@typeberry/lib 0.5.10-6923c44 → 0.5.10-7338c21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/packages/jam/block/work-package.d.ts +7 -7
- package/packages/jam/block/work-package.d.ts.map +1 -1
- package/packages/jam/block/work-package.js +12 -12
- package/packages/jam/executor/pvm-executor.d.ts +7 -0
- package/packages/jam/executor/pvm-executor.d.ts.map +1 -1
- package/packages/jam/executor/pvm-executor.js +15 -0
- package/packages/jam/in-core/externalities/refine.d.ts +3 -3
- package/packages/jam/in-core/externalities/refine.d.ts.map +1 -1
- package/packages/jam/in-core/externalities/refine.js +33 -5
- package/packages/jam/in-core/externalities/refine.test.js +65 -0
- package/packages/jam/in-core/in-core.d.ts +7 -22
- package/packages/jam/in-core/in-core.d.ts.map +1 -1
- package/packages/jam/in-core/in-core.js +16 -184
- package/packages/jam/in-core/in-core.test.js +47 -15
- package/packages/jam/in-core/is-authorized.d.ts +33 -0
- package/packages/jam/in-core/is-authorized.d.ts.map +1 -0
- package/packages/jam/in-core/is-authorized.js +72 -0
- package/packages/jam/in-core/is-authorized.test.d.ts +2 -0
- package/packages/jam/in-core/is-authorized.test.d.ts.map +1 -0
- package/packages/jam/in-core/is-authorized.test.js +125 -0
- package/packages/jam/in-core/refine.d.ts +34 -0
- package/packages/jam/in-core/refine.d.ts.map +1 -0
- package/packages/jam/in-core/refine.js +176 -0
- package/packages/jam/in-core/refine.test.d.ts +2 -0
- package/packages/jam/in-core/refine.test.d.ts.map +1 -0
- package/packages/jam/in-core/refine.test.js +6 -0
- package/packages/jam/jam-host-calls/accumulate/bless.js +9 -9
- package/packages/jam/jam-host-calls/externalities/partial-state.d.ts +1 -1
- package/packages/jam/jam-host-calls/general/fetch.d.ts +40 -40
- package/packages/jam/jam-host-calls/general/fetch.d.ts.map +1 -1
- package/packages/jam/jam-host-calls/general/fetch.js +30 -30
- package/packages/jam/jam-host-calls/general/fetch.test.js +17 -14
- package/packages/jam/jamnp-s/protocol/ce-133-work-package-submission.d.ts +2 -2
- package/packages/jam/transition/accumulate/accumulation-result-merge-utils.js +48 -39
- package/packages/jam/transition/externalities/accumulate-externalities.d.ts +2 -2
- package/packages/jam/transition/externalities/accumulate-externalities.d.ts.map +1 -1
- package/packages/jam/transition/externalities/accumulate-externalities.js +20 -7
- package/packages/jam/transition/externalities/accumulate-externalities.test.js +74 -4
- package/packages/jam/transition/externalities/index.d.ts +1 -0
- package/packages/jam/transition/externalities/index.d.ts.map +1 -1
- package/packages/jam/transition/externalities/index.js +1 -0
- package/packages/jam/transition/externalities/is-authorized-fetch-externalities.d.ts +22 -0
- package/packages/jam/transition/externalities/is-authorized-fetch-externalities.d.ts.map +1 -0
- package/packages/jam/transition/externalities/is-authorized-fetch-externalities.js +41 -0
- package/packages/jam/transition/externalities/refine-fetch-externalities.d.ts +7 -7
- package/packages/jam/transition/externalities/refine-fetch-externalities.d.ts.map +1 -1
- package/packages/jam/transition/externalities/refine-fetch-externalities.js +17 -9
|
@@ -1,18 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { W_C } from "#@typeberry/block/gp-constants.js";
|
|
3
|
-
import { WorkPackageInfo, } from "#@typeberry/block/refine-context.js";
|
|
1
|
+
import { WorkPackageInfo } from "#@typeberry/block/refine-context.js";
|
|
4
2
|
import { WorkPackageSpec, WorkReport } from "#@typeberry/block/work-report.js";
|
|
5
|
-
import {
|
|
6
|
-
import { Bytes, BytesBlob } from "#@typeberry/bytes";
|
|
7
|
-
import { codec, Encoder } from "#@typeberry/codec";
|
|
3
|
+
import { Bytes } from "#@typeberry/bytes";
|
|
8
4
|
import { asKnownSize, FixedSizeArray } from "#@typeberry/collections";
|
|
9
|
-
import { PvmExecutor, ReturnStatus } from "#@typeberry/executor";
|
|
10
5
|
import { HASH_SIZE } from "#@typeberry/hash";
|
|
11
6
|
import { Logger } from "#@typeberry/logger";
|
|
12
7
|
import { tryAsU8, tryAsU16, tryAsU32 } from "#@typeberry/numbers";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
8
|
+
import { assertEmpty, Result } from "#@typeberry/utils";
|
|
9
|
+
import { AuthorizationError, IsAuthorized } from "./is-authorized.js";
|
|
10
|
+
import { Refine } from "./refine.js";
|
|
16
11
|
export var RefineError;
|
|
17
12
|
(function (RefineError) {
|
|
18
13
|
/** State for context anchor block or lookup anchor is not found in the DB. */
|
|
@@ -24,39 +19,17 @@ export var RefineError;
|
|
|
24
19
|
/** Authorization error. */
|
|
25
20
|
RefineError[RefineError["AuthorizationError"] = 3] = "AuthorizationError";
|
|
26
21
|
})(RefineError || (RefineError = {}));
|
|
27
|
-
var ServiceCodeError;
|
|
28
|
-
(function (ServiceCodeError) {
|
|
29
|
-
/** Service id is not found in the state. */
|
|
30
|
-
ServiceCodeError[ServiceCodeError["ServiceNotFound"] = 0] = "ServiceNotFound";
|
|
31
|
-
/** Expected service code does not match the state one. */
|
|
32
|
-
ServiceCodeError[ServiceCodeError["ServiceCodeMismatch"] = 1] = "ServiceCodeMismatch";
|
|
33
|
-
/** Code preimage missing. */
|
|
34
|
-
ServiceCodeError[ServiceCodeError["ServiceCodeMissing"] = 2] = "ServiceCodeMissing";
|
|
35
|
-
/** Code blob is too big. */
|
|
36
|
-
ServiceCodeError[ServiceCodeError["ServiceCodeTooBig"] = 3] = "ServiceCodeTooBig";
|
|
37
|
-
})(ServiceCodeError || (ServiceCodeError = {}));
|
|
38
|
-
var AuthorizationError;
|
|
39
|
-
(function (AuthorizationError) {
|
|
40
|
-
})(AuthorizationError || (AuthorizationError = {}));
|
|
41
22
|
const logger = Logger.new(import.meta.filename, "refine");
|
|
42
|
-
/** https://graypaper.fluffylabs.dev/#/ab2cdbd/2ffe002ffe00?v=0.7.2 */
|
|
43
|
-
const ARGS_CODEC = codec.object({
|
|
44
|
-
core: codec.varU32.convert((x) => tryAsU32(x), (x) => tryAsCoreIndex(x)),
|
|
45
|
-
workItemIndex: codec.varU32,
|
|
46
|
-
serviceId: codec.varU32.asOpaque(),
|
|
47
|
-
payloadLength: codec.varU32,
|
|
48
|
-
packageHash: codec.bytes(HASH_SIZE).asOpaque(),
|
|
49
|
-
});
|
|
50
23
|
export class InCore {
|
|
51
24
|
chainSpec;
|
|
52
25
|
states;
|
|
53
|
-
|
|
54
|
-
|
|
26
|
+
isAuthorized;
|
|
27
|
+
refineItem;
|
|
55
28
|
constructor(chainSpec, states, pvmBackend, blake2b) {
|
|
56
29
|
this.chainSpec = chainSpec;
|
|
57
30
|
this.states = states;
|
|
58
|
-
this.
|
|
59
|
-
this.
|
|
31
|
+
this.isAuthorized = new IsAuthorized(chainSpec, pvmBackend, blake2b);
|
|
32
|
+
this.refineItem = new Refine(chainSpec, pvmBackend, blake2b);
|
|
60
33
|
}
|
|
61
34
|
/**
|
|
62
35
|
* Work-report computation function.
|
|
@@ -70,13 +43,14 @@ export class InCore {
|
|
|
70
43
|
*/
|
|
71
44
|
async refine(workPackageAndHash, core, imports, extrinsics) {
|
|
72
45
|
const workPackageHash = workPackageAndHash.hash;
|
|
73
|
-
const { context,
|
|
46
|
+
const { context, authToken, authCodeHash, authCodeHost, authConfiguration, items, ...rest } = workPackageAndHash.data;
|
|
74
47
|
assertEmpty(rest);
|
|
75
48
|
// TODO [ToDr] Verify BEEFY root
|
|
76
49
|
// TODO [ToDr] Verify prerequisites
|
|
77
50
|
logger.log `[core:${core}] Attempting to refine work package with ${items.length} items.`;
|
|
78
|
-
// TODO [ToDr] GP link
|
|
79
51
|
// Verify anchor block
|
|
52
|
+
// https://graypaper.fluffylabs.dev/#/ab2cdbd/15cd0215cd02?v=0.7.2
|
|
53
|
+
// TODO [ToDr] Validation
|
|
80
54
|
const state = this.states.getState(context.anchor);
|
|
81
55
|
if (state === null) {
|
|
82
56
|
return Result.error(RefineError.StateMissing, () => `State at anchor block ${context.anchor} is missing.`);
|
|
@@ -96,7 +70,7 @@ export class InCore {
|
|
|
96
70
|
return Result.error(RefineError.InvalidLookupAnchorSlot, () => `Lookup anchor slot does not match the one is state. Ours: ${lookupState.timeslot}, expected: ${context.lookupAnchorSlot}`);
|
|
97
71
|
}
|
|
98
72
|
// Check authorization
|
|
99
|
-
const authResult = await this.
|
|
73
|
+
const authResult = await this.isAuthorized.invoke(state, core, authToken, authCodeHost, authCodeHash, authConfiguration);
|
|
100
74
|
if (authResult.isError) {
|
|
101
75
|
return Result.error(RefineError.AuthorizationError, () => `Authorization error: ${AuthorizationError[authResult.error]}: ${authResult.details()}.`);
|
|
102
76
|
}
|
|
@@ -106,14 +80,14 @@ export class InCore {
|
|
|
106
80
|
const refineResults = [];
|
|
107
81
|
for (const [idx, item] of items.entries()) {
|
|
108
82
|
logger.info `[core:${core}][i:${idx}] Refining item for service ${item.service}.`;
|
|
109
|
-
const result = await this.refineItem(state, lookupState, idx, item, imports, extrinsics, core, workPackageHash, exportOffset);
|
|
83
|
+
const result = await this.refineItem.invoke(state, lookupState, idx, item, imports, extrinsics, core, workPackageHash, exportOffset);
|
|
110
84
|
refineResults.push(result);
|
|
111
85
|
exportOffset += result.exports.length;
|
|
112
86
|
}
|
|
113
87
|
// amalgamate the work report now
|
|
114
|
-
return Result.ok(
|
|
88
|
+
return Result.ok(InCore.amalgamateWorkReport(asKnownSize(refineResults), authResult.ok, workPackageHash, context, core));
|
|
115
89
|
}
|
|
116
|
-
amalgamateWorkReport(refineResults, authResult, workPackageHash, context, coreIndex) {
|
|
90
|
+
static amalgamateWorkReport(refineResults, authResult, workPackageHash, context, coreIndex) {
|
|
117
91
|
// unzip exports and work results for each work item
|
|
118
92
|
const exports = refineResults.map((x) => x.exports);
|
|
119
93
|
const results = refineResults.map((x) => x.result);
|
|
@@ -156,146 +130,4 @@ export class InCore {
|
|
|
156
130
|
exports: asKnownSize(exports),
|
|
157
131
|
};
|
|
158
132
|
}
|
|
159
|
-
async authorizePackage(_authorization, _authCodeHost, _authCodeHash, _parametrization) {
|
|
160
|
-
// TODO [ToDr] Check authorization?
|
|
161
|
-
const authorizerHash = Bytes.zero(HASH_SIZE).asOpaque();
|
|
162
|
-
const authorizationGasUsed = tryAsServiceGas(0);
|
|
163
|
-
const authorizationOutput = BytesBlob.empty();
|
|
164
|
-
return Result.ok({
|
|
165
|
-
authorizerHash,
|
|
166
|
-
authorizationGasUsed,
|
|
167
|
-
authorizationOutput,
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
async refineItem(state, lookupState, idx, item, allImports, allExtrinsics, coreIndex, workPackageHash, exportOffset) {
|
|
171
|
-
const payloadHash = this.blake2b.hashBytes(item.payload);
|
|
172
|
-
const baseResult = {
|
|
173
|
-
serviceId: item.service,
|
|
174
|
-
codeHash: item.codeHash,
|
|
175
|
-
payloadHash,
|
|
176
|
-
gas: item.refineGasLimit,
|
|
177
|
-
};
|
|
178
|
-
const imports = allImports[idx];
|
|
179
|
-
const extrinsics = allExtrinsics[idx];
|
|
180
|
-
const baseLoad = {
|
|
181
|
-
importedSegments: tryAsU32(imports.length),
|
|
182
|
-
extrinsicCount: tryAsU32(extrinsics.length),
|
|
183
|
-
extrinsicSize: tryAsU32(extrinsics.reduce((acc, x) => acc + x.length, 0)),
|
|
184
|
-
};
|
|
185
|
-
const maybeCode = this.getServiceCode(state, idx, item);
|
|
186
|
-
if (maybeCode.isError) {
|
|
187
|
-
const error = maybeCode.error === ServiceCodeError.ServiceCodeTooBig
|
|
188
|
-
? WorkExecResultKind.codeOversize
|
|
189
|
-
: WorkExecResultKind.badCode;
|
|
190
|
-
return {
|
|
191
|
-
exports: [],
|
|
192
|
-
result: WorkResult.create({
|
|
193
|
-
...baseResult,
|
|
194
|
-
result: WorkExecResult.error(error),
|
|
195
|
-
load: WorkRefineLoad.create({
|
|
196
|
-
...baseLoad,
|
|
197
|
-
gasUsed: tryAsServiceGas(item.refineGasLimit),
|
|
198
|
-
exportedSegments: tryAsU32(0),
|
|
199
|
-
}),
|
|
200
|
-
}),
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
const code = maybeCode.ok;
|
|
204
|
-
const externalities = this.createRefineExternalities({
|
|
205
|
-
payload: item.payload,
|
|
206
|
-
imports: allImports,
|
|
207
|
-
extrinsics: allExtrinsics,
|
|
208
|
-
currentServiceId: item.service,
|
|
209
|
-
lookupState,
|
|
210
|
-
exportOffset,
|
|
211
|
-
});
|
|
212
|
-
const executor = await PvmExecutor.createRefineExecutor(item.service, code, externalities, this.pvmBackend);
|
|
213
|
-
const args = Encoder.encodeObject(ARGS_CODEC, {
|
|
214
|
-
serviceId: item.service,
|
|
215
|
-
core: coreIndex,
|
|
216
|
-
workItemIndex: tryAsU32(idx),
|
|
217
|
-
payloadLength: tryAsU32(item.payload.length),
|
|
218
|
-
packageHash: workPackageHash,
|
|
219
|
-
});
|
|
220
|
-
const execResult = await executor.run(args, item.refineGasLimit);
|
|
221
|
-
const exports = externalities.refine.getExportedSegments();
|
|
222
|
-
if (exports.length !== item.exportCount) {
|
|
223
|
-
return {
|
|
224
|
-
exports,
|
|
225
|
-
result: WorkResult.create({
|
|
226
|
-
...baseResult,
|
|
227
|
-
result: WorkExecResult.error(WorkExecResultKind.incorrectNumberOfExports),
|
|
228
|
-
load: WorkRefineLoad.create({
|
|
229
|
-
...baseLoad,
|
|
230
|
-
gasUsed: tryAsServiceGas(item.refineGasLimit),
|
|
231
|
-
exportedSegments: tryAsU32(0),
|
|
232
|
-
}),
|
|
233
|
-
}),
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
const result = this.extractWorkResult(execResult);
|
|
237
|
-
return {
|
|
238
|
-
exports,
|
|
239
|
-
result: WorkResult.create({
|
|
240
|
-
...baseResult,
|
|
241
|
-
result,
|
|
242
|
-
load: WorkRefineLoad.create({
|
|
243
|
-
...baseLoad,
|
|
244
|
-
gasUsed: tryAsServiceGas(execResult.consumedGas),
|
|
245
|
-
exportedSegments: tryAsU32(exports.length),
|
|
246
|
-
}),
|
|
247
|
-
}),
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
extractWorkResult(execResult) {
|
|
251
|
-
if (execResult.status === ReturnStatus.OK) {
|
|
252
|
-
const slice = execResult.memorySlice;
|
|
253
|
-
// TODO [ToDr] Verify the output size and change digestTooBig?
|
|
254
|
-
return WorkExecResult.ok(BytesBlob.blobFrom(slice));
|
|
255
|
-
}
|
|
256
|
-
switch (execResult.status) {
|
|
257
|
-
case ReturnStatus.OOG:
|
|
258
|
-
return WorkExecResult.error(WorkExecResultKind.outOfGas);
|
|
259
|
-
case ReturnStatus.PANIC:
|
|
260
|
-
return WorkExecResult.error(WorkExecResultKind.panic);
|
|
261
|
-
default:
|
|
262
|
-
assertNever(execResult);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
getServiceCode(state, idx, item) {
|
|
266
|
-
const serviceId = item.service;
|
|
267
|
-
const service = state.getService(serviceId);
|
|
268
|
-
// TODO [ToDr] GP link
|
|
269
|
-
// missing service
|
|
270
|
-
if (service === null) {
|
|
271
|
-
return Result.error(ServiceCodeError.ServiceNotFound, () => `[i:${idx}] Service ${serviceId} is missing in state.`);
|
|
272
|
-
}
|
|
273
|
-
// TODO [ToDr] GP link
|
|
274
|
-
// TODO [ToDr] shall we rather use the old codehash instead
|
|
275
|
-
if (!service.getInfo().codeHash.isEqualTo(item.codeHash)) {
|
|
276
|
-
return Result.error(ServiceCodeError.ServiceCodeMismatch, () => `[i:${idx}] Service ${serviceId} has invalid code hash. Ours: ${service.getInfo().codeHash}, expected: ${item.codeHash}`);
|
|
277
|
-
}
|
|
278
|
-
const code = service.getPreimage(item.codeHash.asOpaque());
|
|
279
|
-
if (code === null) {
|
|
280
|
-
return Result.error(ServiceCodeError.ServiceCodeMissing, () => `[i:${idx}] Code ${item.codeHash} for service ${serviceId} was not found.`);
|
|
281
|
-
}
|
|
282
|
-
if (code.length > W_C) {
|
|
283
|
-
return Result.error(ServiceCodeError.ServiceCodeTooBig, () => `[i:${idx}] Code ${item.codeHash} for service ${serviceId} is too big! ${code.length} bytes vs ${W_C} bytes max.`);
|
|
284
|
-
}
|
|
285
|
-
return Result.ok(code);
|
|
286
|
-
}
|
|
287
|
-
createRefineExternalities(args) {
|
|
288
|
-
// TODO [ToDr] Pass all required fetch data
|
|
289
|
-
const fetchExternalities = new RefineFetchExternalities(this.chainSpec);
|
|
290
|
-
const refine = RefineExternalitiesImpl.create({
|
|
291
|
-
currentServiceId: args.currentServiceId,
|
|
292
|
-
lookupState: args.lookupState,
|
|
293
|
-
exportOffset: args.exportOffset,
|
|
294
|
-
pvmBackend: this.pvmBackend,
|
|
295
|
-
});
|
|
296
|
-
return {
|
|
297
|
-
fetchExternalities,
|
|
298
|
-
refine,
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
133
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
2
4
|
import { before, describe, it } from "node:test";
|
|
3
5
|
import { tryAsCoreIndex, tryAsServiceGas, tryAsServiceId, tryAsTimeSlot } from "#@typeberry/block";
|
|
4
6
|
import { RefineContext } from "#@typeberry/block/refine-context.js";
|
|
@@ -6,21 +8,46 @@ import { WorkItem } from "#@typeberry/block/work-item.js";
|
|
|
6
8
|
import { tryAsWorkItemsCount, WorkPackage } from "#@typeberry/block/work-package.js";
|
|
7
9
|
import { Bytes, BytesBlob } from "#@typeberry/bytes";
|
|
8
10
|
import { Encoder } from "#@typeberry/codec";
|
|
9
|
-
import { asKnownSize, FixedSizeArray } from "#@typeberry/collections";
|
|
11
|
+
import { asKnownSize, FixedSizeArray, HashDictionary } from "#@typeberry/collections";
|
|
10
12
|
import { PvmBackend, tinyChainSpec } from "#@typeberry/config";
|
|
11
13
|
import { InMemoryStates } from "#@typeberry/database";
|
|
12
14
|
import { Blake2b, HASH_SIZE, WithHash } from "#@typeberry/hash";
|
|
13
|
-
import { tryAsU16 } from "#@typeberry/numbers";
|
|
14
|
-
import {
|
|
15
|
+
import { tryAsU16, tryAsU32, tryAsU64 } from "#@typeberry/numbers";
|
|
16
|
+
import { InMemoryService, InMemoryState, PreimageItem, ServiceAccountInfo } from "#@typeberry/state";
|
|
15
17
|
import { InCore, RefineError } from "./in-core.js";
|
|
18
|
+
// Load the authorizer PVM fixture (checks authToken === authConfiguration).
|
|
19
|
+
const AUTHORIZER_PVM = BytesBlob.blobFrom(readFileSync(resolve(import.meta.dirname, "fixtures/authorizer.pvm")));
|
|
20
|
+
const AUTH_SERVICE_ID = tryAsServiceId(1);
|
|
16
21
|
let blake2b;
|
|
17
22
|
before(async () => {
|
|
18
23
|
blake2b = await Blake2b.createHasher();
|
|
19
24
|
});
|
|
20
|
-
function
|
|
25
|
+
function getAuthCodeHash() {
|
|
26
|
+
return blake2b.hashBytes(AUTHORIZER_PVM).asOpaque();
|
|
27
|
+
}
|
|
28
|
+
function createService(serviceId, codeHash, code) {
|
|
29
|
+
return new InMemoryService(serviceId, {
|
|
30
|
+
info: ServiceAccountInfo.create({
|
|
31
|
+
codeHash: codeHash.asOpaque(),
|
|
32
|
+
balance: tryAsU64(10_000_000_000),
|
|
33
|
+
accumulateMinGas: tryAsServiceGas(0n),
|
|
34
|
+
onTransferMinGas: tryAsServiceGas(0n),
|
|
35
|
+
storageUtilisationBytes: tryAsU64(0),
|
|
36
|
+
storageUtilisationCount: tryAsU32(0),
|
|
37
|
+
gratisStorage: tryAsU64(0),
|
|
38
|
+
created: tryAsTimeSlot(0),
|
|
39
|
+
lastAccumulation: tryAsTimeSlot(0),
|
|
40
|
+
parentService: tryAsServiceId(0),
|
|
41
|
+
}),
|
|
42
|
+
preimages: HashDictionary.fromEntries([PreimageItem.create({ hash: codeHash.asOpaque(), blob: code })].map((x) => [x.hash, x])),
|
|
43
|
+
lookupHistory: HashDictionary.fromEntries([]),
|
|
44
|
+
storage: new Map(),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function createWorkItem(codeHash, serviceId = 1) {
|
|
21
48
|
return WorkItem.create({
|
|
22
49
|
service: tryAsServiceId(serviceId),
|
|
23
|
-
codeHash
|
|
50
|
+
codeHash,
|
|
24
51
|
payload: BytesBlob.empty(),
|
|
25
52
|
refineGasLimit: tryAsServiceGas(1_000_000),
|
|
26
53
|
accumulateGasLimit: tryAsServiceGas(1_000_000),
|
|
@@ -29,12 +56,12 @@ function createWorkItem(serviceId = 1) {
|
|
|
29
56
|
exportCount: tryAsU16(0),
|
|
30
57
|
});
|
|
31
58
|
}
|
|
32
|
-
function createWorkPackage(anchorHash, stateRoot, lookupAnchorSlot = 0) {
|
|
59
|
+
function createWorkPackage(anchorHash, stateRoot, authCodeHash, lookupAnchorSlot = 0) {
|
|
33
60
|
return WorkPackage.create({
|
|
34
|
-
|
|
35
|
-
authCodeHost:
|
|
36
|
-
authCodeHash
|
|
37
|
-
|
|
61
|
+
authToken: BytesBlob.empty(),
|
|
62
|
+
authCodeHost: AUTH_SERVICE_ID,
|
|
63
|
+
authCodeHash,
|
|
64
|
+
authConfiguration: BytesBlob.empty(),
|
|
38
65
|
context: RefineContext.create({
|
|
39
66
|
anchor: anchorHash,
|
|
40
67
|
stateRoot,
|
|
@@ -43,7 +70,7 @@ function createWorkPackage(anchorHash, stateRoot, lookupAnchorSlot = 0) {
|
|
|
43
70
|
lookupAnchorSlot: tryAsTimeSlot(lookupAnchorSlot),
|
|
44
71
|
prerequisites: [],
|
|
45
72
|
}),
|
|
46
|
-
items: FixedSizeArray.new([createWorkItem()], tryAsWorkItemsCount(1)),
|
|
73
|
+
items: FixedSizeArray.new([createWorkItem(authCodeHash)], tryAsWorkItemsCount(1)),
|
|
47
74
|
});
|
|
48
75
|
}
|
|
49
76
|
function hashWorkPackage(spec, workPackage) {
|
|
@@ -59,7 +86,8 @@ describe("InCore", () => {
|
|
|
59
86
|
const inCore = new InCore(spec, states, PvmBackend.BuiltIn, blake2b);
|
|
60
87
|
const anchorHash = Bytes.fill(HASH_SIZE, 1).asOpaque();
|
|
61
88
|
const stateRoot = Bytes.zero(HASH_SIZE).asOpaque();
|
|
62
|
-
const
|
|
89
|
+
const authCodeHash = getAuthCodeHash();
|
|
90
|
+
const workPackage = createWorkPackage(anchorHash, stateRoot, authCodeHash);
|
|
63
91
|
const result = await inCore.refine(hashWorkPackage(spec, workPackage), tryAsCoreIndex(0), asKnownSize([[]]), asKnownSize([[]]));
|
|
64
92
|
assert.strictEqual(result.isError, true);
|
|
65
93
|
assert.strictEqual(result.error, RefineError.StateMissing);
|
|
@@ -68,13 +96,17 @@ describe("InCore", () => {
|
|
|
68
96
|
const spec = tinyChainSpec;
|
|
69
97
|
const states = new InMemoryStates(spec);
|
|
70
98
|
const inCore = new InCore(spec, states, PvmBackend.BuiltIn, blake2b);
|
|
99
|
+
const authCodeHash = getAuthCodeHash();
|
|
71
100
|
const anchorHash = Bytes.fill(HASH_SIZE, 1).asOpaque();
|
|
72
|
-
const state =
|
|
101
|
+
const state = InMemoryState.partial(spec, {
|
|
102
|
+
timeslot: tryAsTimeSlot(16),
|
|
103
|
+
services: new Map([[AUTH_SERVICE_ID, createService(AUTH_SERVICE_ID, authCodeHash, AUTHORIZER_PVM)]]),
|
|
104
|
+
});
|
|
73
105
|
await states.insertInitialState(anchorHash, state);
|
|
74
106
|
const correctStateRoot = await states.getStateRoot(state);
|
|
75
|
-
const workPackage = createWorkPackage(anchorHash, correctStateRoot, state.timeslot);
|
|
107
|
+
const workPackage = createWorkPackage(anchorHash, correctStateRoot, authCodeHash, state.timeslot);
|
|
76
108
|
const result = await inCore.refine(hashWorkPackage(spec, workPackage), tryAsCoreIndex(0), asKnownSize([[]]), asKnownSize([[]]));
|
|
77
|
-
assert.strictEqual(result.isOk, true);
|
|
109
|
+
assert.strictEqual(result.isOk, true, `Expected OK but got error: ${result.isError ? result.details() : ""}`);
|
|
78
110
|
assert.strictEqual(result.ok.report.coreIndex, 0);
|
|
79
111
|
assert.strictEqual(result.ok.report.results.length, 1);
|
|
80
112
|
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type CodeHash, type CoreIndex, type ServiceGas, type ServiceId } from "#@typeberry/block";
|
|
2
|
+
import type { AuthorizerHash } from "#@typeberry/block/refine-context.js";
|
|
3
|
+
import { BytesBlob } from "#@typeberry/bytes";
|
|
4
|
+
import type { ChainSpec, PvmBackend } from "#@typeberry/config";
|
|
5
|
+
import type { Blake2b } from "#@typeberry/hash";
|
|
6
|
+
import type { State } from "#@typeberry/state";
|
|
7
|
+
import { Result } from "#@typeberry/utils";
|
|
8
|
+
export declare enum AuthorizationError {
|
|
9
|
+
/** BAD: authorizer code not found (service or preimage missing). */
|
|
10
|
+
CodeNotFound = 0,
|
|
11
|
+
/** BIG: authorizer code exceeds W_A limit. */
|
|
12
|
+
CodeTooBig = 1,
|
|
13
|
+
/** PANIC/OOG: PVM execution failed. */
|
|
14
|
+
PvmFailed = 2
|
|
15
|
+
}
|
|
16
|
+
export type AuthorizationOk = {
|
|
17
|
+
authorizerHash: AuthorizerHash;
|
|
18
|
+
authorizationGasUsed: ServiceGas;
|
|
19
|
+
authorizationOutput: BytesBlob;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* IsAuthorized PVM invocation (Psi_I).
|
|
23
|
+
*
|
|
24
|
+
* https://graypaper.fluffylabs.dev/#/ab2cdbd/2e64002e6400?v=0.7.2
|
|
25
|
+
*/
|
|
26
|
+
export declare class IsAuthorized {
|
|
27
|
+
private readonly chainSpec;
|
|
28
|
+
private readonly pvmBackend;
|
|
29
|
+
private readonly blake2b;
|
|
30
|
+
constructor(chainSpec: ChainSpec, pvmBackend: PvmBackend, blake2b: Blake2b);
|
|
31
|
+
invoke(state: State, coreIndex: CoreIndex, authToken: BytesBlob, authCodeHost: ServiceId, authCodeHash: CodeHash, authConfiguration: BytesBlob): Promise<Result<AuthorizationOk, AuthorizationError>>;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=is-authorized.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is-authorized.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/in-core/is-authorized.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAmB,MAAM,kBAAkB,CAAC;AAEnH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,oBAAY,kBAAkB;IAC5B,oEAAoE;IACpE,YAAY,IAAI;IAChB,8CAA8C;IAC9C,UAAU,IAAI;IACd,uCAAuC;IACvC,SAAS,IAAI;CACd;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,cAAc,EAAE,cAAc,CAAC;IAC/B,oBAAoB,EAAE,UAAU,CAAC;IACjC,mBAAmB,EAAE,SAAS,CAAC;CAChC,CAAC;AAMF;;;;GAIG;AACH,qBAAa,YAAY;IAErB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAFP,SAAS,EAAE,SAAS,EACpB,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,OAAO;IAG7B,MAAM,CACV,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE,SAAS,EACvB,YAAY,EAAE,QAAQ,EACtB,iBAAiB,EAAE,SAAS,GAC3B,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,kBAAkB,CAAC,CAAC;CA+DxD"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { tryAsServiceGas } from "#@typeberry/block";
|
|
2
|
+
import { G_I, W_A } from "#@typeberry/block/gp-constants.js";
|
|
3
|
+
import { BytesBlob } from "#@typeberry/bytes";
|
|
4
|
+
import { codec, Encoder } from "#@typeberry/codec";
|
|
5
|
+
import { PvmExecutor, ReturnStatus } from "#@typeberry/executor";
|
|
6
|
+
import { IsAuthorizedFetchExternalities } from "#@typeberry/transition/externalities/is-authorized-fetch-externalities.js";
|
|
7
|
+
import { Result } from "#@typeberry/utils";
|
|
8
|
+
export var AuthorizationError;
|
|
9
|
+
(function (AuthorizationError) {
|
|
10
|
+
/** BAD: authorizer code not found (service or preimage missing). */
|
|
11
|
+
AuthorizationError[AuthorizationError["CodeNotFound"] = 0] = "CodeNotFound";
|
|
12
|
+
/** BIG: authorizer code exceeds W_A limit. */
|
|
13
|
+
AuthorizationError[AuthorizationError["CodeTooBig"] = 1] = "CodeTooBig";
|
|
14
|
+
/** PANIC/OOG: PVM execution failed. */
|
|
15
|
+
AuthorizationError[AuthorizationError["PvmFailed"] = 2] = "PvmFailed";
|
|
16
|
+
})(AuthorizationError || (AuthorizationError = {}));
|
|
17
|
+
const AUTH_ARGS_CODEC = codec.object({
|
|
18
|
+
coreIndex: codec.u16,
|
|
19
|
+
});
|
|
20
|
+
/**
|
|
21
|
+
* IsAuthorized PVM invocation (Psi_I).
|
|
22
|
+
*
|
|
23
|
+
* https://graypaper.fluffylabs.dev/#/ab2cdbd/2e64002e6400?v=0.7.2
|
|
24
|
+
*/
|
|
25
|
+
export class IsAuthorized {
|
|
26
|
+
chainSpec;
|
|
27
|
+
pvmBackend;
|
|
28
|
+
blake2b;
|
|
29
|
+
constructor(chainSpec, pvmBackend, blake2b) {
|
|
30
|
+
this.chainSpec = chainSpec;
|
|
31
|
+
this.pvmBackend = pvmBackend;
|
|
32
|
+
this.blake2b = blake2b;
|
|
33
|
+
}
|
|
34
|
+
async invoke(state, coreIndex, authToken, authCodeHost, authCodeHash, authConfiguration) {
|
|
35
|
+
// Look up the authorizer code from the auth code host service
|
|
36
|
+
const service = state.getService(authCodeHost);
|
|
37
|
+
// https://graypaper.fluffylabs.dev/#/ab2cdbd/2eca002eca00?v=0.7.2
|
|
38
|
+
if (service === null) {
|
|
39
|
+
return Result.error(AuthorizationError.CodeNotFound, () => `Auth code host service ${authCodeHost} not found in state.`);
|
|
40
|
+
}
|
|
41
|
+
const code = service.getPreimage(authCodeHash.asOpaque());
|
|
42
|
+
if (code === null) {
|
|
43
|
+
return Result.error(AuthorizationError.CodeNotFound, () => `Auth code preimage ${authCodeHash} not found in service ${authCodeHost}.`);
|
|
44
|
+
}
|
|
45
|
+
// BIG: code exceeds W_A
|
|
46
|
+
// https://graypaper.fluffylabs.dev/#/ab2cdbd/2ed6002ed600?v=0.7.2
|
|
47
|
+
if (code.length > W_A) {
|
|
48
|
+
return Result.error(AuthorizationError.CodeTooBig, () => `Auth code is too big: ${code.length} bytes vs ${W_A} max.`);
|
|
49
|
+
}
|
|
50
|
+
// Prepare fetch externalities and executor
|
|
51
|
+
const fetchExternalities = new IsAuthorizedFetchExternalities(this.chainSpec, {
|
|
52
|
+
authToken,
|
|
53
|
+
authConfiguration,
|
|
54
|
+
});
|
|
55
|
+
const executor = await PvmExecutor.createIsAuthorizedExecutor(authCodeHost, code, { fetchExternalities }, this.pvmBackend);
|
|
56
|
+
const args = Encoder.encodeObject(AUTH_ARGS_CODEC, {
|
|
57
|
+
coreIndex,
|
|
58
|
+
});
|
|
59
|
+
// Run PVM with gas budget G_I
|
|
60
|
+
const gasLimit = tryAsServiceGas(G_I);
|
|
61
|
+
const execResult = await executor.run(args, gasLimit);
|
|
62
|
+
if (execResult.status !== ReturnStatus.OK) {
|
|
63
|
+
return Result.error(AuthorizationError.PvmFailed, () => `IsAuthorized PVM ${ReturnStatus[execResult.status]} (gas used: ${execResult.consumedGas}).`);
|
|
64
|
+
}
|
|
65
|
+
// Compute authorizer hash: H(code_hash ++ configuration)
|
|
66
|
+
// https://graypaper.fluffylabs.dev/#/ab2cdbd/1b81011b8401?v=0.7.2
|
|
67
|
+
const authorizerHash = this.blake2b.hashBlobs([authCodeHash, authConfiguration]);
|
|
68
|
+
const authorizationOutput = BytesBlob.blobFrom(execResult.memorySlice);
|
|
69
|
+
const authorizationGasUsed = tryAsServiceGas(execResult.consumedGas);
|
|
70
|
+
return Result.ok({ authorizerHash, authorizationGasUsed, authorizationOutput });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is-authorized.test.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/in-core/is-authorized.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { before, describe, it } from "node:test";
|
|
5
|
+
import { tryAsCoreIndex, tryAsServiceGas, tryAsServiceId, tryAsTimeSlot } from "#@typeberry/block";
|
|
6
|
+
import { Bytes, BytesBlob } from "#@typeberry/bytes";
|
|
7
|
+
import { HashDictionary } from "#@typeberry/collections";
|
|
8
|
+
import { PvmBackend, tinyChainSpec } from "#@typeberry/config";
|
|
9
|
+
import { Blake2b, HASH_SIZE } from "#@typeberry/hash";
|
|
10
|
+
import { tryAsU32, tryAsU64 } from "#@typeberry/numbers";
|
|
11
|
+
import { InMemoryService, InMemoryState, PreimageItem, ServiceAccountInfo } from "#@typeberry/state";
|
|
12
|
+
import { AuthorizationError, IsAuthorized } from "./is-authorized.js";
|
|
13
|
+
let blake2b;
|
|
14
|
+
before(async () => {
|
|
15
|
+
blake2b = await Blake2b.createHasher();
|
|
16
|
+
});
|
|
17
|
+
// Load the authorizer PVM fixture.
|
|
18
|
+
// This authorizer checks that authToken === authConfiguration and returns "Auth=<token>".
|
|
19
|
+
// https://github.com/tomusdrw/as-lan/blob/main/examples/authorizer/assembly/authorize.ts
|
|
20
|
+
const AUTHORIZER_PVM = BytesBlob.blobFrom(readFileSync(resolve(import.meta.dirname, "fixtures/authorizer.pvm")));
|
|
21
|
+
const AUTH_SERVICE_ID = tryAsServiceId(42);
|
|
22
|
+
function createService(serviceId, codeHash, code) {
|
|
23
|
+
return new InMemoryService(serviceId, {
|
|
24
|
+
info: ServiceAccountInfo.create({
|
|
25
|
+
codeHash: codeHash.asOpaque(),
|
|
26
|
+
balance: tryAsU64(10_000_000_000),
|
|
27
|
+
accumulateMinGas: tryAsServiceGas(0n),
|
|
28
|
+
onTransferMinGas: tryAsServiceGas(0n),
|
|
29
|
+
storageUtilisationBytes: tryAsU64(0),
|
|
30
|
+
storageUtilisationCount: tryAsU32(0),
|
|
31
|
+
gratisStorage: tryAsU64(0),
|
|
32
|
+
created: tryAsTimeSlot(0),
|
|
33
|
+
lastAccumulation: tryAsTimeSlot(0),
|
|
34
|
+
parentService: tryAsServiceId(0),
|
|
35
|
+
}),
|
|
36
|
+
preimages: HashDictionary.fromEntries([PreimageItem.create({ hash: codeHash.asOpaque(), blob: code })].map((x) => [x.hash, x])),
|
|
37
|
+
lookupHistory: HashDictionary.fromEntries([]),
|
|
38
|
+
storage: new Map(),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
describe("IsAuthorized", () => {
|
|
42
|
+
const spec = tinyChainSpec;
|
|
43
|
+
function getAuthCodeHash() {
|
|
44
|
+
return blake2b.hashBytes(AUTHORIZER_PVM).asOpaque();
|
|
45
|
+
}
|
|
46
|
+
function createStateWithService(codeHash, code) {
|
|
47
|
+
return InMemoryState.partial(spec, {
|
|
48
|
+
timeslot: tryAsTimeSlot(16),
|
|
49
|
+
services: new Map([[AUTH_SERVICE_ID, createService(AUTH_SERVICE_ID, codeHash, code)]]),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
it("should authorize when token matches configuration", async () => {
|
|
53
|
+
const authCodeHash = getAuthCodeHash();
|
|
54
|
+
const state = createStateWithService(authCodeHash, AUTHORIZER_PVM);
|
|
55
|
+
const isAuthorized = new IsAuthorized(spec, PvmBackend.BuiltIn, blake2b);
|
|
56
|
+
const token = BytesBlob.blobFromString("hello");
|
|
57
|
+
const result = await isAuthorized.invoke(state, tryAsCoreIndex(0), token, AUTH_SERVICE_ID, authCodeHash, token);
|
|
58
|
+
assert.strictEqual(result.isOk, true, `Expected OK but got error: ${result.isError ? result.details() : ""}`);
|
|
59
|
+
// Verify the authorization output starts with "Auth=<hello>"
|
|
60
|
+
const outputStr = Buffer.from(result.ok.authorizationOutput.raw).toString("utf8");
|
|
61
|
+
assert.ok(outputStr.startsWith("Auth=<hello>"), `Expected "Auth=<hello>" prefix but got "${outputStr.slice(0, 30)}"`);
|
|
62
|
+
// Verify the authorizer hash is H(code_hash ++ configuration)
|
|
63
|
+
const expectedHash = blake2b.hashBlobs([authCodeHash, token]);
|
|
64
|
+
assert.ok(result.ok.authorizerHash.isEqualTo(expectedHash), "authorizerHash should be H(code_hash || config)");
|
|
65
|
+
// Verify gas was consumed
|
|
66
|
+
assert.ok(Number(result.ok.authorizationGasUsed) > 0, "should have consumed some gas");
|
|
67
|
+
});
|
|
68
|
+
it("should authorize with empty token and configuration", async () => {
|
|
69
|
+
const authCodeHash = getAuthCodeHash();
|
|
70
|
+
const state = createStateWithService(authCodeHash, AUTHORIZER_PVM);
|
|
71
|
+
const isAuthorized = new IsAuthorized(spec, PvmBackend.BuiltIn, blake2b);
|
|
72
|
+
const result = await isAuthorized.invoke(state, tryAsCoreIndex(0), BytesBlob.empty(), AUTH_SERVICE_ID, authCodeHash, BytesBlob.empty());
|
|
73
|
+
assert.strictEqual(result.isOk, true, `Expected OK but got error: ${result.isError ? result.details() : ""}`);
|
|
74
|
+
const outputStr = Buffer.from(result.ok.authorizationOutput.raw).toString("utf8");
|
|
75
|
+
assert.ok(outputStr.startsWith("Auth=<>"), `Expected "Auth=<>" prefix but got "${outputStr.slice(0, 30)}"`);
|
|
76
|
+
});
|
|
77
|
+
it("should fail when token does not match configuration", async () => {
|
|
78
|
+
const authCodeHash = getAuthCodeHash();
|
|
79
|
+
const state = createStateWithService(authCodeHash, AUTHORIZER_PVM);
|
|
80
|
+
const isAuthorized = new IsAuthorized(spec, PvmBackend.BuiltIn, blake2b);
|
|
81
|
+
const result = await isAuthorized.invoke(state, tryAsCoreIndex(0), BytesBlob.blobFromString("wrong"), AUTH_SERVICE_ID, authCodeHash, BytesBlob.blobFromString("right"));
|
|
82
|
+
assert.strictEqual(result.isError, true);
|
|
83
|
+
assert.strictEqual(result.error, AuthorizationError.PvmFailed);
|
|
84
|
+
});
|
|
85
|
+
it("should fail when auth code host service is missing", async () => {
|
|
86
|
+
const authCodeHash = getAuthCodeHash();
|
|
87
|
+
const state = InMemoryState.partial(spec, {
|
|
88
|
+
timeslot: tryAsTimeSlot(16),
|
|
89
|
+
services: new Map(),
|
|
90
|
+
});
|
|
91
|
+
const isAuthorized = new IsAuthorized(spec, PvmBackend.BuiltIn, blake2b);
|
|
92
|
+
const result = await isAuthorized.invoke(state, tryAsCoreIndex(0), BytesBlob.empty(), AUTH_SERVICE_ID, authCodeHash, BytesBlob.empty());
|
|
93
|
+
assert.strictEqual(result.isError, true);
|
|
94
|
+
assert.strictEqual(result.error, AuthorizationError.CodeNotFound);
|
|
95
|
+
});
|
|
96
|
+
it("should fail when auth code preimage is missing", async () => {
|
|
97
|
+
const authCodeHash = getAuthCodeHash();
|
|
98
|
+
// Service exists but with no preimages
|
|
99
|
+
const emptyService = new InMemoryService(AUTH_SERVICE_ID, {
|
|
100
|
+
info: ServiceAccountInfo.create({
|
|
101
|
+
codeHash: Bytes.zero(HASH_SIZE).asOpaque(),
|
|
102
|
+
balance: tryAsU64(0),
|
|
103
|
+
accumulateMinGas: tryAsServiceGas(0n),
|
|
104
|
+
onTransferMinGas: tryAsServiceGas(0n),
|
|
105
|
+
storageUtilisationBytes: tryAsU64(0),
|
|
106
|
+
storageUtilisationCount: tryAsU32(0),
|
|
107
|
+
gratisStorage: tryAsU64(0),
|
|
108
|
+
created: tryAsTimeSlot(0),
|
|
109
|
+
lastAccumulation: tryAsTimeSlot(0),
|
|
110
|
+
parentService: tryAsServiceId(0),
|
|
111
|
+
}),
|
|
112
|
+
preimages: HashDictionary.fromEntries([]),
|
|
113
|
+
lookupHistory: HashDictionary.fromEntries([]),
|
|
114
|
+
storage: new Map(),
|
|
115
|
+
});
|
|
116
|
+
const state = InMemoryState.partial(spec, {
|
|
117
|
+
timeslot: tryAsTimeSlot(16),
|
|
118
|
+
services: new Map([[AUTH_SERVICE_ID, emptyService]]),
|
|
119
|
+
});
|
|
120
|
+
const isAuthorized = new IsAuthorized(spec, PvmBackend.BuiltIn, blake2b);
|
|
121
|
+
const result = await isAuthorized.invoke(state, tryAsCoreIndex(0), BytesBlob.empty(), AUTH_SERVICE_ID, authCodeHash, BytesBlob.empty());
|
|
122
|
+
assert.strictEqual(result.isError, true);
|
|
123
|
+
assert.strictEqual(result.error, AuthorizationError.CodeNotFound);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type CoreIndex, type Segment, type SegmentIndex, type ServiceGas } from "#@typeberry/block";
|
|
2
|
+
import type { WorkPackageHash } from "#@typeberry/block/refine-context.js";
|
|
3
|
+
import type { WorkItem, WorkItemExtrinsic } from "#@typeberry/block/work-item.js";
|
|
4
|
+
import { WorkExecResult, WorkResult } from "#@typeberry/block/work-result.js";
|
|
5
|
+
import type { KnownSizeArray } from "#@typeberry/collections";
|
|
6
|
+
import type { ChainSpec, PvmBackend } from "#@typeberry/config";
|
|
7
|
+
import { type ReturnValue } from "#@typeberry/executor";
|
|
8
|
+
import { type Blake2b } from "#@typeberry/hash";
|
|
9
|
+
import type { State } from "#@typeberry/state";
|
|
10
|
+
export type RefineItemResult = {
|
|
11
|
+
result: WorkResult;
|
|
12
|
+
exports: readonly Segment[];
|
|
13
|
+
};
|
|
14
|
+
export type PerWorkItem<T> = KnownSizeArray<T, "for each work item">;
|
|
15
|
+
export type ImportedSegment = {
|
|
16
|
+
index: SegmentIndex;
|
|
17
|
+
data: Segment;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Refine PVM invocation (Psi_R).
|
|
21
|
+
*
|
|
22
|
+
* Executes a single work item's refinement logic.
|
|
23
|
+
*/
|
|
24
|
+
export declare class Refine {
|
|
25
|
+
private readonly chainSpec;
|
|
26
|
+
private readonly pvmBackend;
|
|
27
|
+
private readonly blake2b;
|
|
28
|
+
constructor(chainSpec: ChainSpec, pvmBackend: PvmBackend, blake2b: Blake2b);
|
|
29
|
+
invoke(state: State, lookupState: State, idx: number, item: WorkItem, allImports: PerWorkItem<ImportedSegment[]>, allExtrinsics: PerWorkItem<WorkItemExtrinsic[]>, coreIndex: CoreIndex, workPackageHash: WorkPackageHash, exportOffset: number): Promise<RefineItemResult>;
|
|
30
|
+
static extractWorkResult(execResult: ReturnValue<ServiceGas>): WorkExecResult;
|
|
31
|
+
private getServiceCode;
|
|
32
|
+
private createRefineExternalities;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=refine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refine.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/in-core/refine.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,SAAS,EACd,KAAK,OAAO,EACZ,KAAK,YAAY,EACjB,KAAK,UAAU,EAIhB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACjF,OAAO,EAAE,cAAc,EAAsC,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAGjH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAA+D,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACpH,OAAO,EAAE,KAAK,OAAO,EAAa,MAAM,iBAAiB,CAAC;AAE1D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAK9C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,EAAE,SAAS,OAAO,EAAE,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,cAAc,CAAC,CAAC,EAAE,oBAAoB,CAAC,CAAC;AAErE,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,YAAY,CAAC;IACpB,IAAI,EAAE,OAAO,CAAC;CACf,CAAC;AAyBF;;;;GAIG;AACH,qBAAa,MAAM;IAEf,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAFP,SAAS,EAAE,SAAS,EACpB,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,OAAO;IAG7B,MAAM,CACV,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,KAAK,EAClB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,QAAQ,EACd,UAAU,EAAE,WAAW,CAAC,eAAe,EAAE,CAAC,EAC1C,aAAa,EAAE,WAAW,CAAC,iBAAiB,EAAE,CAAC,EAC/C,SAAS,EAAE,SAAS,EACpB,eAAe,EAAE,eAAe,EAChC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,gBAAgB,CAAC;IA0F5B,MAAM,CAAC,iBAAiB,CAAC,UAAU,EAAE,WAAW,CAAC,UAAU,CAAC;IAiB5D,OAAO,CAAC,cAAc;IAyCtB,OAAO,CAAC,yBAAyB;CAsBlC"}
|