@typeberry/lib 0.5.10-6cb1bd5 → 0.5.10-9567cee
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/core/codec/encoder.d.ts +1 -1
- package/packages/core/codec/encoder.d.ts.map +1 -1
- package/packages/core/codec/encoder.js +3 -2
- package/packages/core/pvm-interface/pvm.d.ts +2 -0
- package/packages/core/pvm-interface/pvm.d.ts.map +1 -1
- package/packages/jam/executor/pvm-executor.d.ts +2 -2
- package/packages/jam/executor/pvm-executor.d.ts.map +1 -1
- package/packages/jam/in-core/externalities/refine.d.ts +14 -4
- package/packages/jam/in-core/externalities/refine.d.ts.map +1 -1
- package/packages/jam/in-core/externalities/refine.js +47 -3
- package/packages/jam/in-core/externalities/refine.test.js +49 -2
- package/packages/jam/in-core/in-core.d.ts.map +1 -1
- package/packages/jam/in-core/in-core.js +3 -5
- package/packages/jam/jam-host-calls/general/fetch.d.ts +159 -98
- package/packages/jam/jam-host-calls/general/fetch.d.ts.map +1 -1
- package/packages/jam/jam-host-calls/general/fetch.js +110 -16
- package/packages/jam/jam-host-calls/general/fetch.test.js +87 -56
- package/packages/jam/transition/accumulate/accumulate.js +2 -2
- package/packages/jam/transition/externalities/accumulate-fetch-externalities.d.ts +19 -0
- package/packages/jam/transition/externalities/accumulate-fetch-externalities.d.ts.map +1 -0
- package/packages/jam/transition/externalities/accumulate-fetch-externalities.js +45 -0
- package/packages/jam/transition/externalities/accumulate-fetch-externalities.test.d.ts +2 -0
- package/packages/jam/transition/externalities/accumulate-fetch-externalities.test.d.ts.map +1 -0
- package/packages/jam/transition/externalities/accumulate-fetch-externalities.test.js +192 -0
- package/packages/jam/transition/externalities/fetch-externalities.d.ts +3 -39
- package/packages/jam/transition/externalities/fetch-externalities.d.ts.map +1 -1
- package/packages/jam/transition/externalities/fetch-externalities.js +2 -88
- package/packages/jam/transition/externalities/index.d.ts +2 -0
- package/packages/jam/transition/externalities/index.d.ts.map +1 -1
- package/packages/jam/transition/externalities/index.js +2 -0
- package/packages/jam/transition/externalities/refine-fetch-externalities.d.ts +23 -0
- package/packages/jam/transition/externalities/refine-fetch-externalities.d.ts.map +1 -0
- package/packages/jam/transition/externalities/refine-fetch-externalities.js +48 -0
- package/packages/jam/transition/externalities/refine-fetch-externalities.test.d.ts +2 -0
- package/packages/jam/transition/externalities/refine-fetch-externalities.test.d.ts.map +1 -0
- package/packages/jam/transition/externalities/refine-fetch-externalities.test.js +32 -0
- package/packages/jam/transition/externalities/fetch-externalities.test.d.ts +0 -2
- package/packages/jam/transition/externalities/fetch-externalities.test.d.ts.map +0 -1
- package/packages/jam/transition/externalities/fetch-externalities.test.js +0 -254
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, it } from "node:test";
|
|
3
3
|
import { tryAsServiceId } from "#@typeberry/block";
|
|
4
|
-
import { BytesBlob } from "#@typeberry/bytes";
|
|
4
|
+
import { Bytes, BytesBlob } from "#@typeberry/bytes";
|
|
5
|
+
import { HASH_SIZE } from "#@typeberry/hash";
|
|
5
6
|
import { tryAsU64 } from "#@typeberry/numbers";
|
|
6
7
|
import { HostCallMemory, HostCallRegisters, PvmExecution } from "#@typeberry/pvm-host-calls";
|
|
7
8
|
import { tryAsGas } from "#@typeberry/pvm-interface";
|
|
8
9
|
import { gasCounter, MemoryBuilder, tryAsMemoryIndex, tryAsSbrkIndex } from "#@typeberry/pvm-interpreter";
|
|
9
10
|
import { PAGE_SIZE } from "#@typeberry/pvm-interpreter/memory/memory-consts.js";
|
|
10
11
|
import { emptyRegistersBuffer } from "../utils.js";
|
|
11
|
-
import { Fetch, FetchKind } from "./fetch.js";
|
|
12
|
+
import { Fetch, FetchContext, FetchKind } from "./fetch.js";
|
|
12
13
|
import { HostCallResult } from "./results.js";
|
|
13
14
|
describe("Fetch", () => {
|
|
14
15
|
const IN_OUT_REG = 7;
|
|
@@ -16,7 +17,7 @@ describe("Fetch", () => {
|
|
|
16
17
|
it("should return PvmExecution.Panic if memory write fails", async () => {
|
|
17
18
|
const currentServiceId = tryAsServiceId(10_000);
|
|
18
19
|
const blob = BytesBlob.blobFromNumbers([1, 2, 3]);
|
|
19
|
-
const fetchMock = new
|
|
20
|
+
const fetchMock = new RefineFetchMock();
|
|
20
21
|
fetchMock.constantsResponse = blob;
|
|
21
22
|
const badOffset = tryAsU64(0xfffff);
|
|
22
23
|
const registers = new HostCallRegisters(emptyRegistersBuffer());
|
|
@@ -33,10 +34,10 @@ describe("Fetch", () => {
|
|
|
33
34
|
});
|
|
34
35
|
it("should write empty result and set IN_OUT_REG to NONE if fetch returns null", async () => {
|
|
35
36
|
const currentServiceId = tryAsServiceId(10_000);
|
|
36
|
-
const fetchMock = new
|
|
37
|
-
|
|
37
|
+
const fetchMock = new RefineFetchMock();
|
|
38
|
+
// authorizerTraceResponse is null by default — Kind 2 legitimately returns null
|
|
38
39
|
const blob = BytesBlob.blobFromNumbers([]);
|
|
39
|
-
const { registers, memory, readBack } = prepareRegsAndMemory(blob, FetchKind.
|
|
40
|
+
const { registers, memory, readBack } = prepareRegsAndMemory(blob, FetchKind.AuthorizerTrace);
|
|
40
41
|
const fetch = new Fetch(currentServiceId, fetchMock);
|
|
41
42
|
const result = await fetch.execute(gas, registers, memory);
|
|
42
43
|
assert.strictEqual(result, undefined);
|
|
@@ -47,7 +48,7 @@ describe("Fetch", () => {
|
|
|
47
48
|
it("should write nothing if offset >= blob length", async () => {
|
|
48
49
|
const currentServiceId = tryAsServiceId(10_000);
|
|
49
50
|
const blob = BytesBlob.blobFromNumbers([1, 2, 3]);
|
|
50
|
-
const fetchMock = new
|
|
51
|
+
const fetchMock = new RefineFetchMock();
|
|
51
52
|
fetchMock.constantsResponse = blob;
|
|
52
53
|
const { registers, memory, readBack } = prepareRegsAndMemory(blob, FetchKind.Constants, 5, 2);
|
|
53
54
|
const fetch = new Fetch(currentServiceId, fetchMock);
|
|
@@ -59,7 +60,7 @@ describe("Fetch", () => {
|
|
|
59
60
|
it("should clamp offset + length to blob end", async () => {
|
|
60
61
|
const currentServiceId = tryAsServiceId(10_000);
|
|
61
62
|
const blob = BytesBlob.blobFromNumbers([9, 8, 7, 6, 5]);
|
|
62
|
-
const fetchMock = new
|
|
63
|
+
const fetchMock = new RefineFetchMock();
|
|
63
64
|
fetchMock.constantsResponse = blob;
|
|
64
65
|
const { registers, memory, readBack } = prepareRegsAndMemory(blob, FetchKind.Constants, 3, 10);
|
|
65
66
|
const fetch = new Fetch(currentServiceId, fetchMock);
|
|
@@ -71,7 +72,7 @@ describe("Fetch", () => {
|
|
|
71
72
|
it("should return NONE and write nothing if fetch kind is unknown", async () => {
|
|
72
73
|
const currentServiceId = tryAsServiceId(10_000);
|
|
73
74
|
const blob = BytesBlob.empty();
|
|
74
|
-
const fetchMock = new
|
|
75
|
+
const fetchMock = new RefineFetchMock();
|
|
75
76
|
fetchMock.constantsResponse = blob;
|
|
76
77
|
const { registers, memory, readBack } = prepareRegsAndMemory(blob, FetchKind.Constants);
|
|
77
78
|
registers.set(10, tryAsU64(999));
|
|
@@ -84,7 +85,7 @@ describe("Fetch", () => {
|
|
|
84
85
|
it("should fetch constants and write result to memory", async () => {
|
|
85
86
|
const currentServiceId = tryAsServiceId(10_000);
|
|
86
87
|
const blob = BytesBlob.blobFromNumbers([1, 2, 3, 4, 5]);
|
|
87
|
-
const fetchMock = new
|
|
88
|
+
const fetchMock = new RefineFetchMock();
|
|
88
89
|
fetchMock.constantsResponse = blob;
|
|
89
90
|
const { registers, memory, readBack, expectedLength } = prepareRegsAndMemory(blob, FetchKind.Constants);
|
|
90
91
|
const fetch = new Fetch(currentServiceId, fetchMock);
|
|
@@ -95,8 +96,8 @@ describe("Fetch", () => {
|
|
|
95
96
|
});
|
|
96
97
|
it("should fetch entropy and write result to memory", async () => {
|
|
97
98
|
const currentServiceId = tryAsServiceId(10_000);
|
|
98
|
-
const blob =
|
|
99
|
-
const fetchMock = new
|
|
99
|
+
const blob = Bytes.fill(HASH_SIZE, 10).asOpaque();
|
|
100
|
+
const fetchMock = new RefineFetchMock();
|
|
100
101
|
fetchMock.entropyResponse = blob;
|
|
101
102
|
const { registers, memory, readBack, expectedLength } = prepareRegsAndMemory(blob, FetchKind.Entropy);
|
|
102
103
|
const fetch = new Fetch(currentServiceId, fetchMock);
|
|
@@ -108,7 +109,7 @@ describe("Fetch", () => {
|
|
|
108
109
|
it("should fetch authorizer trace and write result to memory", async () => {
|
|
109
110
|
const currentServiceId = tryAsServiceId(10_000);
|
|
110
111
|
const blob = BytesBlob.blobFromNumbers([9, 9, 9]);
|
|
111
|
-
const fetchMock = new
|
|
112
|
+
const fetchMock = new RefineFetchMock();
|
|
112
113
|
fetchMock.authorizerTraceResponse = blob;
|
|
113
114
|
const { registers, memory, readBack, expectedLength } = prepareRegsAndMemory(blob, FetchKind.AuthorizerTrace);
|
|
114
115
|
const fetch = new Fetch(currentServiceId, fetchMock);
|
|
@@ -120,7 +121,7 @@ describe("Fetch", () => {
|
|
|
120
121
|
it("should fetch other work item extrinsics and write result to memory", async () => {
|
|
121
122
|
const currentServiceId = tryAsServiceId(10_000);
|
|
122
123
|
const blob = BytesBlob.blobFromNumbers([42, 43, 44]);
|
|
123
|
-
const fetchMock = new
|
|
124
|
+
const fetchMock = new RefineFetchMock();
|
|
124
125
|
const workItem = tryAsU64(123);
|
|
125
126
|
const index = tryAsU64(7);
|
|
126
127
|
const key = `${workItem}:${index}`;
|
|
@@ -138,7 +139,7 @@ describe("Fetch", () => {
|
|
|
138
139
|
it("should fetch my extrinsics and write result to memory", async () => {
|
|
139
140
|
const currentServiceId = tryAsServiceId(10_000);
|
|
140
141
|
const blob = BytesBlob.blobFromNumbers([11, 12, 13]);
|
|
141
|
-
const fetchMock = new
|
|
142
|
+
const fetchMock = new RefineFetchMock();
|
|
142
143
|
const index = tryAsU64(5);
|
|
143
144
|
const key = `null:${index}`;
|
|
144
145
|
fetchMock.workItemExtrinsicResponses.set(key, blob);
|
|
@@ -154,7 +155,7 @@ describe("Fetch", () => {
|
|
|
154
155
|
it("should fetch other work item imports and write result to memory", async () => {
|
|
155
156
|
const currentServiceId = tryAsServiceId(10_000);
|
|
156
157
|
const blob = BytesBlob.blobFromNumbers([21, 22, 23]);
|
|
157
|
-
const fetchMock = new
|
|
158
|
+
const fetchMock = new RefineFetchMock();
|
|
158
159
|
const workItem = tryAsU64(42);
|
|
159
160
|
const index = tryAsU64(3);
|
|
160
161
|
const key = `${workItem}:${index}`;
|
|
@@ -172,7 +173,7 @@ describe("Fetch", () => {
|
|
|
172
173
|
it("should fetch my imports and write result to memory", async () => {
|
|
173
174
|
const currentServiceId = tryAsServiceId(10_000);
|
|
174
175
|
const blob = BytesBlob.blobFromNumbers([31, 32, 33]);
|
|
175
|
-
const fetchMock = new
|
|
176
|
+
const fetchMock = new RefineFetchMock();
|
|
176
177
|
const index = tryAsU64(8);
|
|
177
178
|
const key = `null:${index}`;
|
|
178
179
|
fetchMock.workItemImportResponses.set(key, blob);
|
|
@@ -188,7 +189,7 @@ describe("Fetch", () => {
|
|
|
188
189
|
it("should fetch work package and write result to memory", async () => {
|
|
189
190
|
const currentServiceId = tryAsServiceId(10_000);
|
|
190
191
|
const blob = BytesBlob.blobFromNumbers([100, 101, 102]);
|
|
191
|
-
const fetchMock = new
|
|
192
|
+
const fetchMock = new RefineFetchMock();
|
|
192
193
|
fetchMock.workPackageResponse = blob;
|
|
193
194
|
const { registers, memory, readBack, expectedLength } = prepareRegsAndMemory(blob, FetchKind.WorkPackage);
|
|
194
195
|
const fetch = new Fetch(currentServiceId, fetchMock);
|
|
@@ -200,7 +201,7 @@ describe("Fetch", () => {
|
|
|
200
201
|
it("should fetch authorizer and write result to memory", async () => {
|
|
201
202
|
const currentServiceId = tryAsServiceId(10_000);
|
|
202
203
|
const blob = BytesBlob.blobFromNumbers([201, 202, 203]);
|
|
203
|
-
const fetchMock = new
|
|
204
|
+
const fetchMock = new RefineFetchMock();
|
|
204
205
|
fetchMock.authorizerResponse = blob;
|
|
205
206
|
const { registers, memory, readBack, expectedLength } = prepareRegsAndMemory(blob, FetchKind.Authorizer);
|
|
206
207
|
const fetch = new Fetch(currentServiceId, fetchMock);
|
|
@@ -212,7 +213,7 @@ describe("Fetch", () => {
|
|
|
212
213
|
it("should fetch authorization token and write result to memory", async () => {
|
|
213
214
|
const currentServiceId = tryAsServiceId(10_000);
|
|
214
215
|
const blob = BytesBlob.blobFromNumbers([210, 211, 212]);
|
|
215
|
-
const fetchMock = new
|
|
216
|
+
const fetchMock = new RefineFetchMock();
|
|
216
217
|
fetchMock.authorizationTokenResponse = blob;
|
|
217
218
|
const { registers, memory, readBack, expectedLength } = prepareRegsAndMemory(blob, FetchKind.AuthorizationToken);
|
|
218
219
|
const fetch = new Fetch(currentServiceId, fetchMock);
|
|
@@ -224,7 +225,7 @@ describe("Fetch", () => {
|
|
|
224
225
|
it("should fetch refine context and write result to memory", async () => {
|
|
225
226
|
const currentServiceId = tryAsServiceId(10_000);
|
|
226
227
|
const blob = BytesBlob.blobFromNumbers([88, 89, 90]);
|
|
227
|
-
const fetchMock = new
|
|
228
|
+
const fetchMock = new RefineFetchMock();
|
|
228
229
|
fetchMock.refineContextResponse = blob;
|
|
229
230
|
const { registers, memory, readBack, expectedLength } = prepareRegsAndMemory(blob, FetchKind.RefineContext);
|
|
230
231
|
const fetch = new Fetch(currentServiceId, fetchMock);
|
|
@@ -236,7 +237,7 @@ describe("Fetch", () => {
|
|
|
236
237
|
it("should fetch all work items and write result to memory", async () => {
|
|
237
238
|
const currentServiceId = tryAsServiceId(10_000);
|
|
238
239
|
const blob = BytesBlob.blobFromNumbers([70, 71, 72]);
|
|
239
|
-
const fetchMock = new
|
|
240
|
+
const fetchMock = new RefineFetchMock();
|
|
240
241
|
fetchMock.allWorkItemsResponse = blob;
|
|
241
242
|
const { registers, memory, readBack, expectedLength } = prepareRegsAndMemory(blob, FetchKind.AllWorkItems);
|
|
242
243
|
const fetch = new Fetch(currentServiceId, fetchMock);
|
|
@@ -248,7 +249,7 @@ describe("Fetch", () => {
|
|
|
248
249
|
it("should fetch one work item and write result to memory", async () => {
|
|
249
250
|
const currentServiceId = tryAsServiceId(10_000);
|
|
250
251
|
const blob = BytesBlob.blobFromNumbers([33, 34, 35]);
|
|
251
|
-
const fetchMock = new
|
|
252
|
+
const fetchMock = new RefineFetchMock();
|
|
252
253
|
const workItem = tryAsU64(55);
|
|
253
254
|
fetchMock.oneWorkItemResponses.set(workItem.toString(), blob);
|
|
254
255
|
const { registers, memory, readBack, expectedLength } = prepareRegsAndMemory(blob, FetchKind.OneWorkItem);
|
|
@@ -263,7 +264,7 @@ describe("Fetch", () => {
|
|
|
263
264
|
it("should fetch work item payload and write result to memory", async () => {
|
|
264
265
|
const currentServiceId = tryAsServiceId(10_000);
|
|
265
266
|
const blob = BytesBlob.blobFromNumbers([60, 61, 62]);
|
|
266
|
-
const fetchMock = new
|
|
267
|
+
const fetchMock = new RefineFetchMock();
|
|
267
268
|
const workItem = tryAsU64(77);
|
|
268
269
|
fetchMock.workItemPayloadResponses.set(workItem.toString(), blob);
|
|
269
270
|
const { registers, memory, readBack, expectedLength } = prepareRegsAndMemory(blob, FetchKind.WorkItemPayload);
|
|
@@ -278,8 +279,8 @@ describe("Fetch", () => {
|
|
|
278
279
|
it("should fetch all transfers and operands and write result to memory", async () => {
|
|
279
280
|
const currentServiceId = tryAsServiceId(10_000);
|
|
280
281
|
const blob = BytesBlob.blobFromNumbers([101, 102, 103]);
|
|
281
|
-
const fetchMock = new
|
|
282
|
-
fetchMock.
|
|
282
|
+
const fetchMock = new AccumulateFetchMock();
|
|
283
|
+
fetchMock.allTransfersAndOperandsResponse = blob;
|
|
283
284
|
const { registers, memory, readBack, expectedLength } = prepareRegsAndMemory(blob, FetchKind.AllTransfersAndOperands);
|
|
284
285
|
const fetch = new Fetch(currentServiceId, fetchMock);
|
|
285
286
|
const result = await fetch.execute(gas, registers, memory);
|
|
@@ -290,7 +291,7 @@ describe("Fetch", () => {
|
|
|
290
291
|
it("should fetch one operand or transfer and write result to memory", async () => {
|
|
291
292
|
const currentServiceId = tryAsServiceId(10_000);
|
|
292
293
|
const blob = BytesBlob.blobFromNumbers([115, 116, 117]);
|
|
293
|
-
const fetchMock = new
|
|
294
|
+
const fetchMock = new AccumulateFetchMock();
|
|
294
295
|
const index = tryAsU64(9);
|
|
295
296
|
fetchMock.oneTransferOrOperandResponses.set(index.toString(), blob);
|
|
296
297
|
const { registers, memory, readBack, expectedLength } = prepareRegsAndMemory(blob, FetchKind.OneTransferOrOperand);
|
|
@@ -302,6 +303,43 @@ describe("Fetch", () => {
|
|
|
302
303
|
assert.deepStrictEqual(readBack(), blob.raw);
|
|
303
304
|
assert.deepStrictEqual(fetchMock.oneTransferOrOperandData, [[index]]);
|
|
304
305
|
});
|
|
306
|
+
it("should return NONE for refine-only kinds in accumulate context", async () => {
|
|
307
|
+
const currentServiceId = tryAsServiceId(10_000);
|
|
308
|
+
const fetchMock = new AccumulateFetchMock();
|
|
309
|
+
const blob = BytesBlob.empty();
|
|
310
|
+
for (const kind of [
|
|
311
|
+
FetchKind.AuthorizerTrace,
|
|
312
|
+
FetchKind.OtherWorkItemExtrinsics,
|
|
313
|
+
FetchKind.MyExtrinsics,
|
|
314
|
+
FetchKind.OtherWorkItemImports,
|
|
315
|
+
FetchKind.MyImports,
|
|
316
|
+
FetchKind.WorkPackage,
|
|
317
|
+
FetchKind.Authorizer,
|
|
318
|
+
FetchKind.AuthorizationToken,
|
|
319
|
+
FetchKind.RefineContext,
|
|
320
|
+
FetchKind.AllWorkItems,
|
|
321
|
+
FetchKind.OneWorkItem,
|
|
322
|
+
FetchKind.WorkItemPayload,
|
|
323
|
+
]) {
|
|
324
|
+
const { registers, memory } = prepareRegsAndMemory(blob, kind);
|
|
325
|
+
const fetch = new Fetch(currentServiceId, fetchMock);
|
|
326
|
+
const result = await fetch.execute(gas, registers, memory);
|
|
327
|
+
assert.strictEqual(result, undefined, `Expected undefined for kind ${kind}`);
|
|
328
|
+
assert.strictEqual(registers.get(IN_OUT_REG), HostCallResult.NONE, `Expected NONE for kind ${kind}`);
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
it("should return NONE for accumulate-only kinds in refine context", async () => {
|
|
332
|
+
const currentServiceId = tryAsServiceId(10_000);
|
|
333
|
+
const fetchMock = new RefineFetchMock();
|
|
334
|
+
const blob = BytesBlob.empty();
|
|
335
|
+
for (const kind of [FetchKind.AllTransfersAndOperands, FetchKind.OneTransferOrOperand]) {
|
|
336
|
+
const { registers, memory } = prepareRegsAndMemory(blob, kind);
|
|
337
|
+
const fetch = new Fetch(currentServiceId, fetchMock);
|
|
338
|
+
const result = await fetch.execute(gas, registers, memory);
|
|
339
|
+
assert.strictEqual(result, undefined, `Expected undefined for kind ${kind}`);
|
|
340
|
+
assert.strictEqual(registers.get(IN_OUT_REG), HostCallResult.NONE, `Expected NONE for kind ${kind}`);
|
|
341
|
+
}
|
|
342
|
+
});
|
|
305
343
|
function prepareRegsAndMemory(blob, fetchKind, offset = 0, length = blob.length) {
|
|
306
344
|
const pageStart = 2 ** 16;
|
|
307
345
|
const memOffset = tryAsU64(pageStart + 1234);
|
|
@@ -327,14 +365,12 @@ describe("Fetch", () => {
|
|
|
327
365
|
};
|
|
328
366
|
}
|
|
329
367
|
});
|
|
330
|
-
class
|
|
368
|
+
class RefineFetchMock {
|
|
369
|
+
context = FetchContext.Refine;
|
|
331
370
|
workItemExtrinsicData = [];
|
|
332
371
|
workItemImportData = [];
|
|
333
372
|
oneWorkItemData = [];
|
|
334
373
|
workItemPayloadData = [];
|
|
335
|
-
oneOperandData = [];
|
|
336
|
-
oneTransferData = [];
|
|
337
|
-
oneTransferOrOperandData = [];
|
|
338
374
|
constantsResponse = null;
|
|
339
375
|
entropyResponse = null;
|
|
340
376
|
authorizerTraceResponse = null;
|
|
@@ -347,12 +383,6 @@ class FetchMock {
|
|
|
347
383
|
allWorkItemsResponse = null;
|
|
348
384
|
oneWorkItemResponses = new Map();
|
|
349
385
|
workItemPayloadResponses = new Map();
|
|
350
|
-
allOperandsResponse = null;
|
|
351
|
-
oneOperandResponses = new Map();
|
|
352
|
-
allTransfersResponse = null;
|
|
353
|
-
oneTransferResponses = new Map();
|
|
354
|
-
allTransfersAndOperandsResponses = null;
|
|
355
|
-
oneTransferOrOperandResponses = new Map();
|
|
356
386
|
constants() {
|
|
357
387
|
if (this.constantsResponse === null) {
|
|
358
388
|
throw new Error("Unexpected call to constants.");
|
|
@@ -360,6 +390,9 @@ class FetchMock {
|
|
|
360
390
|
return this.constantsResponse;
|
|
361
391
|
}
|
|
362
392
|
entropy() {
|
|
393
|
+
if (this.entropyResponse === null) {
|
|
394
|
+
throw new Error("Unexpected call to entropy.");
|
|
395
|
+
}
|
|
363
396
|
return this.entropyResponse;
|
|
364
397
|
}
|
|
365
398
|
authorizerTrace() {
|
|
@@ -412,30 +445,28 @@ class FetchMock {
|
|
|
412
445
|
}
|
|
413
446
|
return this.workItemPayloadResponses.get(key) ?? null;
|
|
414
447
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
448
|
+
}
|
|
449
|
+
class AccumulateFetchMock {
|
|
450
|
+
context = FetchContext.Accumulate;
|
|
451
|
+
oneTransferOrOperandData = [];
|
|
452
|
+
constantsResponse = null;
|
|
453
|
+
entropyResponse = null;
|
|
454
|
+
allTransfersAndOperandsResponse = null;
|
|
455
|
+
oneTransferOrOperandResponses = new Map();
|
|
456
|
+
constants() {
|
|
457
|
+
if (this.constantsResponse === null) {
|
|
458
|
+
throw new Error("Unexpected call to constants.");
|
|
423
459
|
}
|
|
424
|
-
return this.
|
|
425
|
-
}
|
|
426
|
-
allTransfers() {
|
|
427
|
-
return this.allTransfersResponse;
|
|
460
|
+
return this.constantsResponse;
|
|
428
461
|
}
|
|
429
|
-
|
|
430
|
-
this.
|
|
431
|
-
|
|
432
|
-
if (!this.oneTransferResponses.has(key)) {
|
|
433
|
-
throw new Error(`Missing mock response for oneTransfer(${key})`);
|
|
462
|
+
entropy() {
|
|
463
|
+
if (this.entropyResponse === null) {
|
|
464
|
+
throw new Error("Unexpected call to entropy.");
|
|
434
465
|
}
|
|
435
|
-
return this.
|
|
466
|
+
return this.entropyResponse;
|
|
436
467
|
}
|
|
437
468
|
allTransfersAndOperands() {
|
|
438
|
-
return this.
|
|
469
|
+
return this.allTransfersAndOperandsResponse;
|
|
439
470
|
}
|
|
440
471
|
oneTransferOrOperand(index) {
|
|
441
472
|
this.oneTransferOrOperandData.push([index]);
|
|
@@ -11,7 +11,7 @@ import { MAX_VALUE_U64, sumU64, tryAsU32 } from "#@typeberry/numbers";
|
|
|
11
11
|
import { accumulationOutputComparator, hashComparator, ServiceAccountInfo, tryAsPerCore, } from "#@typeberry/state";
|
|
12
12
|
import { assertEmpty, Compatibility, GpVersion, Result, TestSuite } from "#@typeberry/utils";
|
|
13
13
|
import { AccumulateExternalities } from "../externalities/accumulate-externalities.js";
|
|
14
|
-
import {
|
|
14
|
+
import { AccumulateFetchExternalities } from "../externalities/accumulate-fetch-externalities.js";
|
|
15
15
|
import { AccumulateData } from "./accumulate-data.js";
|
|
16
16
|
import { AccumulateQueue, pruneQueue } from "./accumulate-queue.js";
|
|
17
17
|
import { GAS_TO_INVOKE_WORK_REPORT, } from "./accumulate-state.js";
|
|
@@ -86,7 +86,7 @@ export class Accumulate {
|
|
|
86
86
|
}
|
|
87
87
|
const nextServiceId = generateNextServiceId({ serviceId, entropy, timeslot: slot }, this.chainSpec, this.blake2b);
|
|
88
88
|
const partialState = new AccumulateExternalities(this.chainSpec, this.blake2b, updatedState, serviceId, nextServiceId, slot);
|
|
89
|
-
const fetchExternalities =
|
|
89
|
+
const fetchExternalities = new AccumulateFetchExternalities(entropy, transfers, operands, this.chainSpec);
|
|
90
90
|
const externalities = {
|
|
91
91
|
partialState,
|
|
92
92
|
serviceExternalities: partialState,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { EntropyHash } from "#@typeberry/block";
|
|
2
|
+
import type { BytesBlob } from "#@typeberry/bytes";
|
|
3
|
+
import type { ChainSpec } from "#@typeberry/config";
|
|
4
|
+
import { general, type PendingTransfer } from "#@typeberry/jam-host-calls";
|
|
5
|
+
import type { U64 } from "#@typeberry/numbers";
|
|
6
|
+
import type { Operand } from "../accumulate/operand.js";
|
|
7
|
+
export declare class AccumulateFetchExternalities implements general.IAccumulateFetch {
|
|
8
|
+
private readonly entropyHash;
|
|
9
|
+
private readonly transfers;
|
|
10
|
+
private readonly operands;
|
|
11
|
+
private readonly chainSpec;
|
|
12
|
+
readonly context = general.FetchContext.Accumulate;
|
|
13
|
+
constructor(entropyHash: EntropyHash, transfers: PendingTransfer[], operands: Operand[], chainSpec: ChainSpec);
|
|
14
|
+
constants(): BytesBlob;
|
|
15
|
+
entropy(): EntropyHash;
|
|
16
|
+
allTransfersAndOperands(): BytesBlob | null;
|
|
17
|
+
oneTransferOrOperand(index: U64): BytesBlob | null;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=accumulate-fetch-externalities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accumulate-fetch-externalities.d.ts","sourceRoot":"","sources":["../../../../../../packages/jam/transition/externalities/accumulate-fetch-externalities.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC1E,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AASxD,qBAAa,4BAA6B,YAAW,OAAO,CAAC,gBAAgB;IAIzE,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAN5B,QAAQ,CAAC,OAAO,mCAAmC;gBAGhC,WAAW,EAAE,WAAW,EACxB,SAAS,EAAE,eAAe,EAAE,EAC5B,QAAQ,EAAE,OAAO,EAAE,EACnB,SAAS,EAAE,SAAS;IAGvC,SAAS,IAAI,SAAS;IAItB,OAAO,IAAI,WAAW;IAItB,uBAAuB,IAAI,SAAS,GAAG,IAAI;IAU3C,oBAAoB,CAAC,KAAK,EAAE,GAAG,GAAG,SAAS,GAAG,IAAI;CAqBnD"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Encoder } from "#@typeberry/codec";
|
|
2
|
+
import { general } from "#@typeberry/jam-host-calls";
|
|
3
|
+
import { getEncodedConstants, TRANSFER_OR_OPERAND, TRANSFERS_AND_OPERANDS, TransferOperandKind, } from "./fetch-externalities.js";
|
|
4
|
+
export class AccumulateFetchExternalities {
|
|
5
|
+
entropyHash;
|
|
6
|
+
transfers;
|
|
7
|
+
operands;
|
|
8
|
+
chainSpec;
|
|
9
|
+
context = general.FetchContext.Accumulate;
|
|
10
|
+
constructor(entropyHash, transfers, operands, chainSpec) {
|
|
11
|
+
this.entropyHash = entropyHash;
|
|
12
|
+
this.transfers = transfers;
|
|
13
|
+
this.operands = operands;
|
|
14
|
+
this.chainSpec = chainSpec;
|
|
15
|
+
}
|
|
16
|
+
constants() {
|
|
17
|
+
return getEncodedConstants(this.chainSpec);
|
|
18
|
+
}
|
|
19
|
+
entropy() {
|
|
20
|
+
return this.entropyHash;
|
|
21
|
+
}
|
|
22
|
+
allTransfersAndOperands() {
|
|
23
|
+
const transfersAndOperands = this.transfers
|
|
24
|
+
.map((transfer) => ({ kind: TransferOperandKind.TRANSFER, value: transfer }))
|
|
25
|
+
.concat(this.operands.map((operand) => ({ kind: TransferOperandKind.OPERAND, value: operand })));
|
|
26
|
+
return Encoder.encodeObject(TRANSFERS_AND_OPERANDS, transfersAndOperands, this.chainSpec);
|
|
27
|
+
}
|
|
28
|
+
oneTransferOrOperand(index) {
|
|
29
|
+
if (index >= this.transfers.length + this.operands.length) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
// Transfers-first ordering, consistent with allTransfersAndOperands()
|
|
33
|
+
const kind = index < this.transfers.length ? TransferOperandKind.TRANSFER : TransferOperandKind.OPERAND;
|
|
34
|
+
const transferOrOperand = kind === TransferOperandKind.TRANSFER
|
|
35
|
+
? { kind: TransferOperandKind.TRANSFER, value: this.transfers[Number(index)] }
|
|
36
|
+
: {
|
|
37
|
+
kind: TransferOperandKind.OPERAND,
|
|
38
|
+
value: this.operands[Number(index) - this.transfers.length],
|
|
39
|
+
};
|
|
40
|
+
if (transferOrOperand.value === undefined) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
return Encoder.encodeObject(TRANSFER_OR_OPERAND, transferOrOperand, this.chainSpec);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accumulate-fetch-externalities.test.d.ts","sourceRoot":"","sources":["../../../../../../packages/jam/transition/externalities/accumulate-fetch-externalities.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, it } from "node:test";
|
|
3
|
+
import { tryAsServiceGas, tryAsServiceId } from "#@typeberry/block";
|
|
4
|
+
import { WorkExecResult } from "#@typeberry/block/work-result.js";
|
|
5
|
+
import { Bytes, BytesBlob } from "#@typeberry/bytes";
|
|
6
|
+
import { codec, Encoder } from "#@typeberry/codec";
|
|
7
|
+
import { fullChainSpec, tinyChainSpec } from "#@typeberry/config";
|
|
8
|
+
import { HASH_SIZE } from "#@typeberry/hash";
|
|
9
|
+
import { TRANSFER_MEMO_BYTES } from "#@typeberry/jam-host-calls/externalities/partial-state.js";
|
|
10
|
+
import { PendingTransfer } from "#@typeberry/jam-host-calls/externalities/pending-transfer.js";
|
|
11
|
+
import { tryAsU64 } from "#@typeberry/numbers";
|
|
12
|
+
import { Operand } from "../accumulate/operand.js";
|
|
13
|
+
import { AccumulateFetchExternalities } from "./accumulate-fetch-externalities.js";
|
|
14
|
+
import { TRANSFER_OR_OPERAND, TransferOperandKind } from "./fetch-externalities.js";
|
|
15
|
+
describe("AccumulateFetchExternalities", () => {
|
|
16
|
+
const prepareOperands = (length) => {
|
|
17
|
+
const operands = [];
|
|
18
|
+
for (let i = 0; i < length; i++) {
|
|
19
|
+
operands.push(Operand.create({
|
|
20
|
+
authorizationOutput: BytesBlob.empty(),
|
|
21
|
+
authorizerHash: Bytes.fill(HASH_SIZE, i + 1).asOpaque(),
|
|
22
|
+
exportsRoot: Bytes.fill(HASH_SIZE, i + 2).asOpaque(),
|
|
23
|
+
hash: Bytes.fill(HASH_SIZE, i + 4).asOpaque(),
|
|
24
|
+
payloadHash: Bytes.fill(HASH_SIZE, i + 5).asOpaque(),
|
|
25
|
+
result: WorkExecResult.ok(BytesBlob.empty()),
|
|
26
|
+
gas: tryAsServiceGas(1_000),
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
return operands;
|
|
30
|
+
};
|
|
31
|
+
const prepareTransfers = (length) => {
|
|
32
|
+
const transfers = [];
|
|
33
|
+
for (let i = 0; i < length; i++) {
|
|
34
|
+
transfers.push(PendingTransfer.create({
|
|
35
|
+
amount: tryAsU64(1000),
|
|
36
|
+
source: tryAsServiceId(i),
|
|
37
|
+
destination: tryAsServiceId(i + 1),
|
|
38
|
+
gas: tryAsServiceGas(10),
|
|
39
|
+
memo: Bytes.fill(TRANSFER_MEMO_BYTES, 0),
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
42
|
+
return transfers;
|
|
43
|
+
};
|
|
44
|
+
// allTransfersAndOperands: transfers first, then operands
|
|
45
|
+
const toAllTransfersAndOperands = (operands, transfers) => {
|
|
46
|
+
return [
|
|
47
|
+
...transfers.map((t) => ({ kind: TransferOperandKind.TRANSFER, value: t })),
|
|
48
|
+
...operands.map((o) => ({ kind: TransferOperandKind.OPERAND, value: o })),
|
|
49
|
+
];
|
|
50
|
+
};
|
|
51
|
+
// oneTransferOrOperand: transfers first, then operands (same as allTransfersAndOperands)
|
|
52
|
+
const toOneTransferOrOperandAt = (operands, transfers, index) => {
|
|
53
|
+
if (index >= transfers.length + operands.length) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
if (index < transfers.length) {
|
|
57
|
+
return { kind: TransferOperandKind.TRANSFER, value: transfers[index] };
|
|
58
|
+
}
|
|
59
|
+
return { kind: TransferOperandKind.OPERAND, value: operands[index - transfers.length] };
|
|
60
|
+
};
|
|
61
|
+
const encodeOneTransferOrOperand = (item, chainSpec) => {
|
|
62
|
+
if (item === null) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
return Encoder.encodeObject(TRANSFER_OR_OPERAND, item, chainSpec);
|
|
66
|
+
};
|
|
67
|
+
const prepareAccumulateData = ({ chainSpec, operands, entropy, transfers, }) => {
|
|
68
|
+
const defaultChainSpec = tinyChainSpec;
|
|
69
|
+
const defaultEntropy = Bytes.zero(HASH_SIZE).asOpaque();
|
|
70
|
+
const defaultOperands = [];
|
|
71
|
+
const defaultTransfers = [];
|
|
72
|
+
return new AccumulateFetchExternalities(entropy ?? defaultEntropy, transfers ?? defaultTransfers, operands ?? defaultOperands, chainSpec ?? defaultChainSpec);
|
|
73
|
+
};
|
|
74
|
+
it("should return different constants for different chain specs", () => {
|
|
75
|
+
const tinyFetchExternalities = prepareAccumulateData({ chainSpec: tinyChainSpec });
|
|
76
|
+
const fullFetchExternalities = prepareAccumulateData({ chainSpec: fullChainSpec });
|
|
77
|
+
const tinyConstants = tinyFetchExternalities.constants();
|
|
78
|
+
const fullConstants = fullFetchExternalities.constants();
|
|
79
|
+
assert.notStrictEqual(tinyConstants.length, 0);
|
|
80
|
+
assert.notStrictEqual(fullConstants.length, 0);
|
|
81
|
+
assert.notDeepStrictEqual(tinyConstants, fullConstants);
|
|
82
|
+
});
|
|
83
|
+
it("should return entropy hash", () => {
|
|
84
|
+
const expectedEntropy = Bytes.fill(HASH_SIZE, 5).asOpaque();
|
|
85
|
+
const fetchExternalities = prepareAccumulateData({ entropy: expectedEntropy });
|
|
86
|
+
const entropy = fetchExternalities.entropy();
|
|
87
|
+
assert.deepStrictEqual(entropy, expectedEntropy);
|
|
88
|
+
});
|
|
89
|
+
it("should return all transfers and operands", () => {
|
|
90
|
+
const operands = prepareOperands(3);
|
|
91
|
+
const transfers = prepareTransfers(2);
|
|
92
|
+
const chainSpec = tinyChainSpec;
|
|
93
|
+
const expected = toAllTransfersAndOperands(operands, transfers);
|
|
94
|
+
const encodedExpected = Encoder.encodeObject(codec.sequenceVarLen(TRANSFER_OR_OPERAND), expected, chainSpec);
|
|
95
|
+
const fetchExternalities = prepareAccumulateData({ operands, transfers, chainSpec });
|
|
96
|
+
const result = fetchExternalities.allTransfersAndOperands();
|
|
97
|
+
assert.deepStrictEqual(result, encodedExpected);
|
|
98
|
+
});
|
|
99
|
+
it("should return empty encoded sequence when no transfers and no operands", () => {
|
|
100
|
+
const chainSpec = tinyChainSpec;
|
|
101
|
+
const encodedExpected = Encoder.encodeObject(codec.sequenceVarLen(TRANSFER_OR_OPERAND), [], chainSpec);
|
|
102
|
+
const fetchExternalities = prepareAccumulateData({ operands: [], transfers: [], chainSpec });
|
|
103
|
+
const result = fetchExternalities.allTransfersAndOperands();
|
|
104
|
+
assert.deepStrictEqual(result, encodedExpected);
|
|
105
|
+
});
|
|
106
|
+
it("should return one transfer by index (first range)", () => {
|
|
107
|
+
const operands = prepareOperands(3);
|
|
108
|
+
const transfers = prepareTransfers(2);
|
|
109
|
+
const chainSpec = tinyChainSpec;
|
|
110
|
+
const encodedExpected = encodeOneTransferOrOperand(toOneTransferOrOperandAt(operands, transfers, 0), chainSpec);
|
|
111
|
+
const fetchExternalities = prepareAccumulateData({ operands, transfers, chainSpec });
|
|
112
|
+
// Transfers come first (indices 0..1), then operands (indices 2..4)
|
|
113
|
+
const result = fetchExternalities.oneTransferOrOperand(tryAsU64(0));
|
|
114
|
+
assert.deepStrictEqual(result, encodedExpected);
|
|
115
|
+
});
|
|
116
|
+
it("should return one operand by index (second range)", () => {
|
|
117
|
+
const operands = prepareOperands(3);
|
|
118
|
+
const transfers = prepareTransfers(2);
|
|
119
|
+
const chainSpec = tinyChainSpec;
|
|
120
|
+
const encodedExpected = encodeOneTransferOrOperand(toOneTransferOrOperandAt(operands, transfers, 2), chainSpec);
|
|
121
|
+
const fetchExternalities = prepareAccumulateData({ operands, transfers, chainSpec });
|
|
122
|
+
// Operands start after transfers, so index 2 is the first operand
|
|
123
|
+
const result = fetchExternalities.oneTransferOrOperand(tryAsU64(2));
|
|
124
|
+
assert.deepStrictEqual(result, encodedExpected);
|
|
125
|
+
});
|
|
126
|
+
it("should return null when index is out of bounds", () => {
|
|
127
|
+
const operands = prepareOperands(3);
|
|
128
|
+
const transfers = prepareTransfers(2);
|
|
129
|
+
const chainSpec = tinyChainSpec;
|
|
130
|
+
const fetchExternalities = prepareAccumulateData({ operands, transfers, chainSpec });
|
|
131
|
+
// Total items: 3 operands + 2 transfers = 5, so index 5 is out of bounds
|
|
132
|
+
const result = fetchExternalities.oneTransferOrOperand(tryAsU64(5));
|
|
133
|
+
assert.strictEqual(result, null);
|
|
134
|
+
});
|
|
135
|
+
it("should return null when index is far out of bounds", () => {
|
|
136
|
+
const operands = prepareOperands(3);
|
|
137
|
+
const transfers = prepareTransfers(2);
|
|
138
|
+
const chainSpec = tinyChainSpec;
|
|
139
|
+
const fetchExternalities = prepareAccumulateData({ operands, transfers, chainSpec });
|
|
140
|
+
const result = fetchExternalities.oneTransferOrOperand(tryAsU64(153));
|
|
141
|
+
assert.strictEqual(result, null);
|
|
142
|
+
});
|
|
143
|
+
it("should have consistent encoding between all and one", () => {
|
|
144
|
+
const operands = prepareOperands(2);
|
|
145
|
+
const transfers = prepareTransfers(2);
|
|
146
|
+
const chainSpec = tinyChainSpec;
|
|
147
|
+
const allItems = toAllTransfersAndOperands(operands, transfers);
|
|
148
|
+
const encodedAll = Encoder.encodeObject(codec.sequenceVarLen(TRANSFER_OR_OPERAND), allItems, chainSpec);
|
|
149
|
+
const fetchExternalities = prepareAccumulateData({ operands, transfers, chainSpec });
|
|
150
|
+
const all = fetchExternalities.allTransfersAndOperands();
|
|
151
|
+
assert.deepStrictEqual(all, encodedAll);
|
|
152
|
+
for (let i = 0; i < operands.length + transfers.length; i++) {
|
|
153
|
+
const one = fetchExternalities.oneTransferOrOperand(tryAsU64(i));
|
|
154
|
+
const encodedOne = encodeOneTransferOrOperand(toOneTransferOrOperandAt(operands, transfers, i), chainSpec);
|
|
155
|
+
assert.deepStrictEqual(one, encodedOne, `Mismatch at index ${i}`);
|
|
156
|
+
}
|
|
157
|
+
const outOfRange = fetchExternalities.oneTransferOrOperand(tryAsU64(operands.length + transfers.length));
|
|
158
|
+
assert.strictEqual(outOfRange, null);
|
|
159
|
+
});
|
|
160
|
+
it("should handle only operands without transfers", () => {
|
|
161
|
+
const operands = prepareOperands(5);
|
|
162
|
+
const chainSpec = tinyChainSpec;
|
|
163
|
+
const allItems = toAllTransfersAndOperands(operands, []);
|
|
164
|
+
const encodedAll = Encoder.encodeObject(codec.sequenceVarLen(TRANSFER_OR_OPERAND), allItems, chainSpec);
|
|
165
|
+
const fetchExternalities = prepareAccumulateData({ operands, transfers: [], chainSpec });
|
|
166
|
+
const result = fetchExternalities.allTransfersAndOperands();
|
|
167
|
+
assert.deepStrictEqual(result, encodedAll);
|
|
168
|
+
for (let i = 0; i < operands.length; i++) {
|
|
169
|
+
const one = fetchExternalities.oneTransferOrOperand(tryAsU64(i));
|
|
170
|
+
const encodedOne = encodeOneTransferOrOperand(toOneTransferOrOperandAt(operands, [], i), chainSpec);
|
|
171
|
+
assert.deepStrictEqual(one, encodedOne, `Mismatch at operand index ${i}`);
|
|
172
|
+
}
|
|
173
|
+
const outOfRange = fetchExternalities.oneTransferOrOperand(tryAsU64(operands.length));
|
|
174
|
+
assert.strictEqual(outOfRange, null);
|
|
175
|
+
});
|
|
176
|
+
it("should handle only transfers without operands", () => {
|
|
177
|
+
const transfers = prepareTransfers(5);
|
|
178
|
+
const chainSpec = tinyChainSpec;
|
|
179
|
+
const allItems = toAllTransfersAndOperands([], transfers);
|
|
180
|
+
const encodedAll = Encoder.encodeObject(codec.sequenceVarLen(TRANSFER_OR_OPERAND), allItems, chainSpec);
|
|
181
|
+
const fetchExternalities = prepareAccumulateData({ operands: [], transfers, chainSpec });
|
|
182
|
+
const result = fetchExternalities.allTransfersAndOperands();
|
|
183
|
+
assert.deepStrictEqual(result, encodedAll);
|
|
184
|
+
for (let i = 0; i < transfers.length; i++) {
|
|
185
|
+
const one = fetchExternalities.oneTransferOrOperand(tryAsU64(i));
|
|
186
|
+
const encodedOne = encodeOneTransferOrOperand(toOneTransferOrOperandAt([], transfers, i), chainSpec);
|
|
187
|
+
assert.deepStrictEqual(one, encodedOne, `Mismatch at transfer index ${i}`);
|
|
188
|
+
}
|
|
189
|
+
const outOfRange = fetchExternalities.oneTransferOrOperand(tryAsU64(transfers.length));
|
|
190
|
+
assert.strictEqual(outOfRange, null);
|
|
191
|
+
});
|
|
192
|
+
});
|