@fluffylabs/anan-as 1.2.0 → 1.3.0-39d3435
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/dist/bin/index.js +138 -5
- package/dist/bin/src/fuzz.js +2 -2
- package/dist/bin/src/test-json.js +2 -6
- package/dist/bin/src/trace-parse.js +1 -0
- package/dist/bin/src/trace-replay.js +14 -9
- package/dist/bin/src/tracer.js +16 -13
- package/dist/bin/src/utils.js +2 -2
- package/dist/build/compiler-inline.js +1 -1
- package/dist/build/compiler.d.ts +17 -13
- package/dist/build/compiler.js +13 -21
- package/dist/build/compiler.wasm +0 -0
- package/dist/build/debug-inline.js +1 -1
- package/dist/build/debug-raw-inline.js +1 -1
- package/dist/build/debug-raw.d.ts +50 -112
- package/dist/build/debug-raw.js +78 -139
- package/dist/build/debug-raw.wasm +0 -0
- package/dist/build/debug.d.ts +50 -112
- package/dist/build/debug.js +81 -147
- package/dist/build/debug.wasm +0 -0
- package/dist/build/js/assembly/api-debugger.d.ts +55 -0
- package/dist/build/js/assembly/api-debugger.js +245 -0
- package/dist/build/js/assembly/api-internal.d.ts +13 -0
- package/dist/build/js/assembly/api-internal.js +191 -0
- package/dist/build/js/assembly/api-types.d.ts +45 -0
- package/dist/build/js/assembly/api-types.js +52 -0
- package/dist/build/js/assembly/api-utils.d.ts +79 -0
- package/dist/build/js/assembly/api-utils.js +221 -0
- package/dist/build/js/assembly/arguments.d.ts +44 -0
- package/dist/build/js/assembly/arguments.js +164 -0
- package/dist/build/js/assembly/codec.d.ts +24 -0
- package/dist/build/js/assembly/codec.js +139 -0
- package/dist/build/js/assembly/gas.d.ts +11 -0
- package/dist/build/js/assembly/gas.js +33 -0
- package/dist/build/js/assembly/index-shared.d.ts +4 -0
- package/dist/build/js/assembly/index-shared.js +4 -0
- package/dist/build/js/assembly/instructions/bit.d.ts +11 -0
- package/dist/build/js/assembly/instructions/bit.js +53 -0
- package/dist/build/js/assembly/instructions/branch.d.ts +17 -0
- package/dist/build/js/assembly/instructions/branch.js +120 -0
- package/dist/build/js/assembly/instructions/jump.d.ts +5 -0
- package/dist/build/js/assembly/instructions/jump.js +21 -0
- package/dist/build/js/assembly/instructions/load.d.ts +17 -0
- package/dist/build/js/assembly/instructions/load.js +134 -0
- package/dist/build/js/assembly/instructions/logic.d.ts +10 -0
- package/dist/build/js/assembly/instructions/logic.js +47 -0
- package/dist/build/js/assembly/instructions/math.d.ts +28 -0
- package/dist/build/js/assembly/instructions/math.js +225 -0
- package/dist/build/js/assembly/instructions/misc.d.ts +6 -0
- package/dist/build/js/assembly/instructions/misc.js +22 -0
- package/dist/build/js/assembly/instructions/mov.d.ts +6 -0
- package/dist/build/js/assembly/instructions/mov.js +35 -0
- package/dist/build/js/assembly/instructions/outcome.d.ts +30 -0
- package/dist/build/js/assembly/instructions/outcome.js +88 -0
- package/dist/build/js/assembly/instructions/rot.d.ts +15 -0
- package/dist/build/js/assembly/instructions/rot.js +66 -0
- package/dist/build/js/assembly/instructions/set.d.ts +7 -0
- package/dist/build/js/assembly/instructions/set.js +36 -0
- package/dist/build/js/assembly/instructions/shift.d.ts +19 -0
- package/dist/build/js/assembly/instructions/shift.js +121 -0
- package/dist/build/js/assembly/instructions/store.d.ts +17 -0
- package/dist/build/js/assembly/instructions/store.js +101 -0
- package/dist/build/js/assembly/instructions/utils.d.ts +25 -0
- package/dist/build/js/assembly/instructions/utils.js +91 -0
- package/dist/build/js/assembly/instructions-exe.d.ts +2 -0
- package/dist/build/js/assembly/instructions-exe.js +245 -0
- package/dist/build/js/assembly/instructions.d.ts +10 -0
- package/dist/build/js/assembly/instructions.js +252 -0
- package/dist/build/js/assembly/interpreter.d.ts +28 -0
- package/dist/build/js/assembly/interpreter.js +221 -0
- package/dist/build/js/assembly/math.d.ts +6 -0
- package/dist/build/js/assembly/math.js +22 -0
- package/dist/build/js/assembly/memory-page.d.ts +36 -0
- package/dist/build/js/assembly/memory-page.js +74 -0
- package/dist/build/js/assembly/memory.d.ts +83 -0
- package/dist/build/js/assembly/memory.js +482 -0
- package/dist/build/js/assembly/portable.d.ts +24 -0
- package/dist/build/js/assembly/portable.js +363 -0
- package/dist/build/js/assembly/program-build.d.ts +2 -0
- package/dist/build/js/assembly/program-build.js +104 -0
- package/dist/build/js/assembly/program.d.ts +85 -0
- package/dist/build/js/assembly/program.js +340 -0
- package/dist/build/js/assembly/registers.d.ts +6 -0
- package/dist/build/js/assembly/registers.js +9 -0
- package/dist/build/js/assembly/spi.d.ts +92 -0
- package/dist/build/js/assembly/spi.js +152 -0
- package/dist/build/js/portable/bootstrap.d.ts +1 -0
- package/dist/build/js/portable/bootstrap.js +6 -0
- package/dist/build/js/portable/index.d.ts +4 -0
- package/dist/build/js/portable/index.js +6 -0
- package/dist/build/js/portable-bundle.js +4497 -0
- package/dist/build/release-inline.js +1 -1
- package/dist/build/release-mini-inline.js +1 -1
- package/dist/build/release-mini.d.ts +50 -112
- package/dist/build/release-mini.js +81 -147
- package/dist/build/release-mini.wasm +0 -0
- package/dist/build/release-stub-inline.js +1 -1
- package/dist/build/release-stub.d.ts +50 -112
- package/dist/build/release-stub.js +81 -147
- package/dist/build/release-stub.wasm +0 -0
- package/dist/build/release.d.ts +50 -112
- package/dist/build/release.js +81 -147
- package/dist/build/release.wasm +0 -0
- package/dist/build/test-inline.js +1 -1
- package/dist/build/test.wasm +0 -0
- package/dist/test/test-gas-cost.js +2 -3
- package/dist/test/test-trace-format.js +166 -0
- package/dist/test/test-w3f-common.js +125 -0
- package/dist/test/test-w3f-portable.js +5 -0
- package/dist/test/test-w3f.js +3 -120
- package/package.json +22 -11
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import { Args, Arguments, DECODERS, REQUIRED_BYTES } from "./arguments";
|
|
2
|
+
import { Decoder } from "./codec";
|
|
3
|
+
import { INSTRUCTIONS, MISSING_INSTRUCTION } from "./instructions";
|
|
4
|
+
import { Inst } from "./instructions/utils";
|
|
5
|
+
import { portable } from "./portable";
|
|
6
|
+
const MAX_SKIP = 24;
|
|
7
|
+
export class CodeAndMetadata {
|
|
8
|
+
constructor(code, metadata) {
|
|
9
|
+
this.code = code;
|
|
10
|
+
this.metadata = metadata;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/** https://graypaper.fluffylabs.dev/#/cc517d7/109a01109a01?v=0.6.5 */
|
|
14
|
+
export function extractCodeAndMetadata(data) {
|
|
15
|
+
const decoder = new Decoder(data);
|
|
16
|
+
const metadataLength = decoder.varU32();
|
|
17
|
+
const metadata = decoder.bytes(metadataLength);
|
|
18
|
+
const code = decoder.remainingBytes();
|
|
19
|
+
return new CodeAndMetadata(code, metadata);
|
|
20
|
+
}
|
|
21
|
+
/** Convert `u8` to `Uint8Array` */
|
|
22
|
+
export function liftBytes(data) {
|
|
23
|
+
const p = new Uint8Array(data.length);
|
|
24
|
+
p.set(data, 0);
|
|
25
|
+
return p;
|
|
26
|
+
}
|
|
27
|
+
/** Convert `Uint8Array` to `Code` (StaticArray<u8>) */
|
|
28
|
+
export function lowerBytes(data) {
|
|
29
|
+
const r = new StaticArray(data.length);
|
|
30
|
+
for (let i = 0; i < data.length; i++) {
|
|
31
|
+
r[i] = data[i];
|
|
32
|
+
}
|
|
33
|
+
return r;
|
|
34
|
+
}
|
|
35
|
+
/** https://graypaper.fluffylabs.dev/#/cc517d7/234f01234f01?v=0.6.5 */
|
|
36
|
+
export function deblob(program, useBlockGas) {
|
|
37
|
+
const decoder = new Decoder(program);
|
|
38
|
+
// number of items in the jump table
|
|
39
|
+
const jumpTableLength = decoder.varU32();
|
|
40
|
+
// how many bytes are used to encode a single item of the jump table
|
|
41
|
+
const jumpTableItemLength = decoder.u8();
|
|
42
|
+
// the length of the code (in bytes).
|
|
43
|
+
const codeLength = decoder.varU32();
|
|
44
|
+
const jumpTableLengthInBytes = i32(jumpTableLength * jumpTableItemLength);
|
|
45
|
+
const rawJumpTable = decoder.bytes(jumpTableLengthInBytes);
|
|
46
|
+
// NOTE [ToDr] we copy the code here, because indexing a raw
|
|
47
|
+
// assembly script array is faster than going through `Uint8Array` API.
|
|
48
|
+
const rawCode = lowerBytes(decoder.bytes(codeLength));
|
|
49
|
+
const rawMask = decoder.bytes(i32((codeLength + 7) / 8));
|
|
50
|
+
const mask = new Mask(rawMask, codeLength);
|
|
51
|
+
const jumpTable = new JumpTable(jumpTableItemLength, rawJumpTable);
|
|
52
|
+
const basicBlocks = new BasicBlocks(rawCode, mask);
|
|
53
|
+
const gasCosts = new GasCosts(rawCode, mask, basicBlocks, useBlockGas);
|
|
54
|
+
return new Program(rawCode, mask, jumpTable, basicBlocks, gasCosts);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* https://graypaper.fluffylabs.dev/#/cc517d7/236e01236e01?v=0.6.5
|
|
58
|
+
*/
|
|
59
|
+
export class Mask {
|
|
60
|
+
constructor(packedMask, codeLength) {
|
|
61
|
+
this.bytesToSkip = new StaticArray(codeLength);
|
|
62
|
+
let lastInstructionOffset = 0;
|
|
63
|
+
for (let i = packedMask.length - 1; i >= 0; i -= 1) {
|
|
64
|
+
let bits = packedMask[i];
|
|
65
|
+
const index = i * 8;
|
|
66
|
+
for (let b = 7; b >= 0; b--) {
|
|
67
|
+
const isSet = bits & 128;
|
|
68
|
+
bits = bits << 1;
|
|
69
|
+
if (index + b < codeLength) {
|
|
70
|
+
lastInstructionOffset = isSet ? 0 : lastInstructionOffset + 1;
|
|
71
|
+
this.bytesToSkip[index + b] = lastInstructionOffset < MAX_SKIP + 1 ? lastInstructionOffset : MAX_SKIP + 1;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
isInstruction(index) {
|
|
77
|
+
if (index >= u32(this.bytesToSkip.length)) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
return portable.staticArrayAt(this.bytesToSkip, u32(index)) === 0;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Given we are at instruction `i`, how many bytes should be skipped to
|
|
84
|
+
* reach the next instruction (i.e. `skip(i) + 1` from the GP).
|
|
85
|
+
*
|
|
86
|
+
* NOTE: we don't guarantee that `isInstruction()` will return true
|
|
87
|
+
* for the new program counter, since `skip` function is bounded by
|
|
88
|
+
* an upper limit of `24` bytes.
|
|
89
|
+
*/
|
|
90
|
+
skipBytesToNextInstruction(i) {
|
|
91
|
+
if (i + 1 < this.bytesToSkip.length) {
|
|
92
|
+
return portable.staticArrayAt(this.bytesToSkip, i + 1);
|
|
93
|
+
}
|
|
94
|
+
return 0;
|
|
95
|
+
}
|
|
96
|
+
toString() {
|
|
97
|
+
let v = "Mask[";
|
|
98
|
+
for (let i = 0; i < this.bytesToSkip.length; i += 1) {
|
|
99
|
+
v += `${this.bytesToSkip[i]}, `;
|
|
100
|
+
}
|
|
101
|
+
return `${v}]`;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
export class GasCosts {
|
|
105
|
+
constructor(code, mask, blocks, useBlockGasCost) {
|
|
106
|
+
const len = code.length;
|
|
107
|
+
const costs = new StaticArray(len);
|
|
108
|
+
for (let n = 0; n < len; n += 1) {
|
|
109
|
+
const isInstructionInMask = mask.isInstruction(n);
|
|
110
|
+
if (!isInstructionInMask) {
|
|
111
|
+
costs[n] = code[n];
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
const skipArgs = mask.skipBytesToNextInstruction(n);
|
|
115
|
+
const iData = code[n] >= INSTRUCTIONS.length ? MISSING_INSTRUCTION : INSTRUCTIONS[code[n]];
|
|
116
|
+
costs[n] = code[n] | (iData.gas << 8);
|
|
117
|
+
n += skipArgs;
|
|
118
|
+
}
|
|
119
|
+
// sum up costs per block
|
|
120
|
+
if (useBlockGasCost) {
|
|
121
|
+
let previousStart = 0;
|
|
122
|
+
let previousSum = 0;
|
|
123
|
+
for (let n = 0; n < len; n += 1) {
|
|
124
|
+
const currentGas = costs[n] >> 8;
|
|
125
|
+
costs[n] = code[n]; // reset to just opcode (gas=0)
|
|
126
|
+
if (blocks.isStart(n)) {
|
|
127
|
+
costs[previousStart] = code[previousStart] | (previousSum << 8);
|
|
128
|
+
previousSum = currentGas;
|
|
129
|
+
previousStart = n;
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
previousSum += currentGas;
|
|
133
|
+
}
|
|
134
|
+
n += mask.skipBytesToNextInstruction(n);
|
|
135
|
+
}
|
|
136
|
+
// final assignment
|
|
137
|
+
costs[previousStart] = code[previousStart] | (previousSum << 8);
|
|
138
|
+
}
|
|
139
|
+
this.codeAndGas = costs;
|
|
140
|
+
}
|
|
141
|
+
toString() {
|
|
142
|
+
let v = "GasCosts[";
|
|
143
|
+
for (let i = 0; i < this.codeAndGas.length; i += 1) {
|
|
144
|
+
const gas = this.codeAndGas[i] >> 8;
|
|
145
|
+
if (gas !== 0) {
|
|
146
|
+
v += `${i} -> ${gas}, `;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return `${v}]`;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
export var BasicBlock;
|
|
153
|
+
(function (BasicBlock) {
|
|
154
|
+
BasicBlock[BasicBlock["NONE"] = 0] = "NONE";
|
|
155
|
+
BasicBlock[BasicBlock["START"] = 2] = "START";
|
|
156
|
+
BasicBlock[BasicBlock["END"] = 4] = "END";
|
|
157
|
+
})(BasicBlock || (BasicBlock = {}));
|
|
158
|
+
/**
|
|
159
|
+
* https://graypaper.fluffylabs.dev/#/cc517d7/23fe0123fe01?v=0.6.5
|
|
160
|
+
*/
|
|
161
|
+
export class BasicBlocks {
|
|
162
|
+
constructor(code, mask) {
|
|
163
|
+
const len = code.length;
|
|
164
|
+
const isStartOrEnd = new StaticArray(len);
|
|
165
|
+
if (len > 0) {
|
|
166
|
+
isStartOrEnd[0] = BasicBlock.START;
|
|
167
|
+
}
|
|
168
|
+
for (let n = 0; n < len; n += 1) {
|
|
169
|
+
// we only track end-blocks for instructions.
|
|
170
|
+
const isInstructionInMask = mask.isInstruction(n);
|
|
171
|
+
if (!isInstructionInMask) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
const skipArgs = mask.skipBytesToNextInstruction(n);
|
|
175
|
+
const iData = code[n] >= INSTRUCTIONS.length ? MISSING_INSTRUCTION : INSTRUCTIONS[code[n]];
|
|
176
|
+
const isTerminating = iData.isTerminating;
|
|
177
|
+
if (isTerminating) {
|
|
178
|
+
// skip is always 0?
|
|
179
|
+
const newBlockStart = n + 1 + skipArgs;
|
|
180
|
+
// mark the beginning of the next block
|
|
181
|
+
if (newBlockStart < len) {
|
|
182
|
+
isStartOrEnd[newBlockStart] = BasicBlock.START;
|
|
183
|
+
}
|
|
184
|
+
// and mark current instruction as terminating
|
|
185
|
+
isStartOrEnd[n] |= BasicBlock.END;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
this.isStartOrEnd = isStartOrEnd;
|
|
189
|
+
}
|
|
190
|
+
isStart(newPc) {
|
|
191
|
+
if (newPc < this.isStartOrEnd.length) {
|
|
192
|
+
return (portable.staticArrayAt(this.isStartOrEnd, newPc) & BasicBlock.START) > 0;
|
|
193
|
+
}
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
toString() {
|
|
197
|
+
let v = "BasicBlocks[";
|
|
198
|
+
for (let i = 0; i < this.isStartOrEnd.length; i += 1) {
|
|
199
|
+
let t = "";
|
|
200
|
+
const isStart = (this.isStartOrEnd[i] & BasicBlock.START) > 0;
|
|
201
|
+
t += isStart ? "start" : "";
|
|
202
|
+
const isEnd = (this.isStartOrEnd[i] & BasicBlock.END) > 0;
|
|
203
|
+
t += isEnd ? "end" : "";
|
|
204
|
+
if (t.length > 0) {
|
|
205
|
+
v += `${i} -> ${t}, `;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return `${v}]`;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
export class JumpTable {
|
|
212
|
+
constructor(itemBytes, data) {
|
|
213
|
+
const jumps = new StaticArray(itemBytes > 0 ? i32(data.length / itemBytes) : 0);
|
|
214
|
+
for (let i = 0; i < data.length; i += itemBytes) {
|
|
215
|
+
let num = u64(0);
|
|
216
|
+
for (let j = itemBytes - 1; j >= 0; j--) {
|
|
217
|
+
let nextNum = num << u64(8);
|
|
218
|
+
let isOverflow = nextNum < num;
|
|
219
|
+
nextNum = portable.u64_add(nextNum, u64(data[i + j]));
|
|
220
|
+
isOverflow = isOverflow || nextNum < num;
|
|
221
|
+
// handle overflow
|
|
222
|
+
num = isOverflow ? u64.MAX_VALUE : nextNum;
|
|
223
|
+
}
|
|
224
|
+
jumps[i32(i / itemBytes)] = num;
|
|
225
|
+
}
|
|
226
|
+
this.jumps = jumps;
|
|
227
|
+
}
|
|
228
|
+
toString() {
|
|
229
|
+
let v = "JumpTable[";
|
|
230
|
+
for (let i = 0; i < this.jumps.length; i += 1) {
|
|
231
|
+
v += `${i} -> ${this.jumps[i]}, `;
|
|
232
|
+
}
|
|
233
|
+
return `${v}]`;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
export class Program {
|
|
237
|
+
constructor(code, mask, jumpTable, basicBlocks, gasCosts) {
|
|
238
|
+
this.code = code;
|
|
239
|
+
this.mask = mask;
|
|
240
|
+
this.jumpTable = jumpTable;
|
|
241
|
+
this.basicBlocks = basicBlocks;
|
|
242
|
+
this.gasCosts = gasCosts;
|
|
243
|
+
}
|
|
244
|
+
toString() {
|
|
245
|
+
return `Program { code: ${this.code}, mask: ${this.mask}, jumpTable: ${this.jumpTable}, basicBlocks: ${this.basicBlocks}, gasCosts: ${this.gasCosts} }`;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
// Pre-allocated buffer for the rare case when code is shorter than needed.
|
|
249
|
+
// Max REQUIRED_BYTES is 9 (OneRegOneExtImm). We allocate 16 for safety.
|
|
250
|
+
const EXTENDED_BUF = new StaticArray(16);
|
|
251
|
+
export function decodeArguments(args, kind, code, offset, lim) {
|
|
252
|
+
if (code.length < offset + REQUIRED_BYTES[kind]) {
|
|
253
|
+
// in case we have less data than needed we extend the data with zeros.
|
|
254
|
+
const reqBytes = unchecked(REQUIRED_BYTES[kind]);
|
|
255
|
+
for (let i = 0; i < reqBytes; i++) {
|
|
256
|
+
EXTENDED_BUF[i] = 0;
|
|
257
|
+
}
|
|
258
|
+
for (let i = offset; i < code.length; i++) {
|
|
259
|
+
EXTENDED_BUF[i - offset] = unchecked(code[i]);
|
|
260
|
+
}
|
|
261
|
+
return unchecked(DECODERS[kind])(args, EXTENDED_BUF, 0, lim);
|
|
262
|
+
}
|
|
263
|
+
return unchecked(DECODERS[kind])(args, code, offset, offset + lim);
|
|
264
|
+
}
|
|
265
|
+
class ResolvedArguments {
|
|
266
|
+
constructor() {
|
|
267
|
+
this.a = i64(0);
|
|
268
|
+
this.b = i64(0);
|
|
269
|
+
this.c = i64(0);
|
|
270
|
+
this.d = i64(0);
|
|
271
|
+
this.decoded = new Args();
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
export function resolveArguments(argsRes, kind, code, offset, lim, registers) {
|
|
275
|
+
const args = decodeArguments(argsRes, kind, code, offset, lim);
|
|
276
|
+
if (args === null) {
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
const resolved = new ResolvedArguments();
|
|
280
|
+
resolved.decoded = args;
|
|
281
|
+
switch (kind) {
|
|
282
|
+
case Arguments.Zero:
|
|
283
|
+
return resolved;
|
|
284
|
+
case Arguments.OneImm:
|
|
285
|
+
resolved.a = Inst.u32SignExtend(args.a);
|
|
286
|
+
return resolved;
|
|
287
|
+
case Arguments.TwoImm:
|
|
288
|
+
resolved.a = Inst.u32SignExtend(args.a);
|
|
289
|
+
resolved.b = Inst.u32SignExtend(args.b);
|
|
290
|
+
return resolved;
|
|
291
|
+
case Arguments.OneOff:
|
|
292
|
+
resolved.a = Inst.u32SignExtend(args.a);
|
|
293
|
+
return resolved;
|
|
294
|
+
case Arguments.OneRegOneImm:
|
|
295
|
+
resolved.a = registers[Inst.reg(u64(args.a))];
|
|
296
|
+
resolved.b = Inst.u32SignExtend(args.b);
|
|
297
|
+
return resolved;
|
|
298
|
+
case Arguments.OneRegOneExtImm:
|
|
299
|
+
resolved.a = registers[Inst.reg(u64(args.a))];
|
|
300
|
+
resolved.b = portable.u64_add(u64(args.a) << u64(32), u64(args.b));
|
|
301
|
+
return resolved;
|
|
302
|
+
case Arguments.OneRegTwoImm:
|
|
303
|
+
resolved.a = registers[Inst.reg(u64(args.a))];
|
|
304
|
+
resolved.b = Inst.u32SignExtend(args.b);
|
|
305
|
+
resolved.c = Inst.u32SignExtend(args.c);
|
|
306
|
+
return resolved;
|
|
307
|
+
case Arguments.OneRegOneImmOneOff:
|
|
308
|
+
resolved.a = registers[Inst.reg(u64(args.a))];
|
|
309
|
+
resolved.b = Inst.u32SignExtend(args.b);
|
|
310
|
+
resolved.c = Inst.u32SignExtend(args.c);
|
|
311
|
+
return resolved;
|
|
312
|
+
case Arguments.TwoReg:
|
|
313
|
+
resolved.a = registers[Inst.reg(u64(args.a))];
|
|
314
|
+
resolved.b = registers[Inst.reg(u64(args.b))];
|
|
315
|
+
return resolved;
|
|
316
|
+
case Arguments.TwoRegOneImm:
|
|
317
|
+
resolved.a = registers[Inst.reg(u64(args.a))];
|
|
318
|
+
resolved.b = registers[Inst.reg(u64(args.b))];
|
|
319
|
+
resolved.c = Inst.u32SignExtend(args.c);
|
|
320
|
+
return resolved;
|
|
321
|
+
case Arguments.TwoRegOneOff:
|
|
322
|
+
resolved.a = registers[Inst.reg(u64(args.a))];
|
|
323
|
+
resolved.b = registers[Inst.reg(u64(args.b))];
|
|
324
|
+
resolved.c = Inst.u32SignExtend(args.c);
|
|
325
|
+
return resolved;
|
|
326
|
+
case Arguments.TwoRegTwoImm:
|
|
327
|
+
resolved.a = registers[Inst.reg(u64(args.a))];
|
|
328
|
+
resolved.b = registers[Inst.reg(u64(args.b))];
|
|
329
|
+
resolved.c = Inst.u32SignExtend(args.c);
|
|
330
|
+
resolved.d = Inst.u32SignExtend(args.d);
|
|
331
|
+
return resolved;
|
|
332
|
+
case Arguments.ThreeReg:
|
|
333
|
+
resolved.a = registers[Inst.reg(u64(args.a))];
|
|
334
|
+
resolved.b = registers[Inst.reg(u64(args.b))];
|
|
335
|
+
resolved.c = registers[Inst.reg(u64(args.c))];
|
|
336
|
+
return resolved;
|
|
337
|
+
default:
|
|
338
|
+
throw new Error(`Unhandled arguments kind: ${kind}`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Memory } from "./memory";
|
|
2
|
+
import { Program } from "./program";
|
|
3
|
+
import { Registers } from "./registers";
|
|
4
|
+
/** `Z_I`: https://graypaper.fluffylabs.dev/#/ab2cdbd/2daf002daf00?v=0.7.2 */
|
|
5
|
+
export declare const MAX_ARGS_LEN: u32;
|
|
6
|
+
/** https://graypaper.fluffylabs.dev/#/ab2cdbd/2d47022d4702?v=0.7.2 */
|
|
7
|
+
export declare const ARGS_SEGMENT_START: u32;
|
|
8
|
+
/** https://graypaper.fluffylabs.dev/#/ab2cdbd/2d33022d3502?v=0.7.2 */
|
|
9
|
+
export declare const STACK_SEGMENT_END: u32;
|
|
10
|
+
/** https://graypaper.fluffylabs.dev/#/ab2cdbd/2da3002da300?v=0.7.2 */
|
|
11
|
+
export declare function decodeSpi(data: Uint8Array, args: Uint8Array, preallocateMemoryPages?: u32, useBlockGas?: boolean): StandardProgram;
|
|
12
|
+
/**
|
|
13
|
+
* SPI Program with memory and registers.
|
|
14
|
+
*
|
|
15
|
+
* https://graypaper.fluffylabs.dev/#/ab2cdbd/2d13002d1400?v=0.7.2
|
|
16
|
+
*/
|
|
17
|
+
export declare class StandardProgram {
|
|
18
|
+
readonly program: Program;
|
|
19
|
+
readonly memory: Memory;
|
|
20
|
+
readonly registers: Registers;
|
|
21
|
+
metadata: Uint8Array;
|
|
22
|
+
constructor(program: Program, memory: Memory, registers: Registers);
|
|
23
|
+
toString(): string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Standard Program Interface (SPI) Memory Layout
|
|
27
|
+
* ===============================================
|
|
28
|
+
*
|
|
29
|
+
* 32-bit address space (0x0000_0000 to 0xFFFF_FFFF)
|
|
30
|
+
*
|
|
31
|
+
* ```
|
|
32
|
+
* Address Region Access Notes
|
|
33
|
+
* ─────────────────────────────────────────────────────────────────
|
|
34
|
+
* 0x0000_0000 ┌─────────────────────────┐
|
|
35
|
+
* │ │
|
|
36
|
+
* │ Reserved / Guard │ None 64 KB (Z_Z)
|
|
37
|
+
* │ (inaccessible) │
|
|
38
|
+
* │ │
|
|
39
|
+
* 0x0001_0000 ├─────────────────────────┤ ◄─── SEGMENT_SIZE
|
|
40
|
+
* │ │
|
|
41
|
+
* │ Read-Only Data (RO) │ Read Code constants,
|
|
42
|
+
* │ │ string literals
|
|
43
|
+
* │ │
|
|
44
|
+
* 0x0002_0000+ ├─────────────────────────┤ ◄─── 2*SEGMENT_SIZE + align(roLen)
|
|
45
|
+
* │ │
|
|
46
|
+
* │ Read-Write Data (RW) │ Write Initialized globals
|
|
47
|
+
* │ │
|
|
48
|
+
* ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ ◄─── heapStart + align(rwLen)
|
|
49
|
+
* │ │
|
|
50
|
+
* │ Heap (Zero-init) │ Write Dynamic allocation
|
|
51
|
+
* │ (heapPages * 4KB) │ sbrk grows here
|
|
52
|
+
* │ │
|
|
53
|
+
* ├─────────────────────────┤ ◄─── sbrk pointer
|
|
54
|
+
* │ │
|
|
55
|
+
* │ ░░░░░░░ │
|
|
56
|
+
* │ Unmapped / Guard │ None Grows towards each other
|
|
57
|
+
* │ ░░░░░░░ │
|
|
58
|
+
* │ │
|
|
59
|
+
* stackStart ├─────────────────────────┤ ◄─── STACK_SEGMENT_END - stackLen
|
|
60
|
+
* │ │
|
|
61
|
+
* │ Stack │ Write Grows downward (↓)
|
|
62
|
+
* │ (stackSize aligned) │ r1 = stack pointer
|
|
63
|
+
* │ │
|
|
64
|
+
* 0xFEFE_0000 ├─────────────────────────┤ ◄─── STACK_SEGMENT_END
|
|
65
|
+
* │ │
|
|
66
|
+
* │ Guard (64 KB) │ None Separates stack/args
|
|
67
|
+
* │ │
|
|
68
|
+
* 0xFEFF_0000 ├─────────────────────────┤ ◄─── ARGS_SEGMENT_START
|
|
69
|
+
* │ │
|
|
70
|
+
* │ Arguments (RO) │ Read r7 = args pointer
|
|
71
|
+
* │ (up to 16 MB) │ r8 = args length
|
|
72
|
+
* │ │
|
|
73
|
+
* ├─────────────────────────┤ ◄─── ARGS_SEGMENT_START + argsLen
|
|
74
|
+
* │ │
|
|
75
|
+
* │ Guard (64 KB) │ None Top guard region
|
|
76
|
+
* │ │
|
|
77
|
+
* 0xFFFF_FFFF └─────────────────────────┘
|
|
78
|
+
*
|
|
79
|
+
* Initial Register State:
|
|
80
|
+
* ┌──────┬──────────────────┬─────────────────────────┐
|
|
81
|
+
* │ r0 │ 0xFFFF_0000 │ (reserved) │
|
|
82
|
+
* │ r1 │ STACK_SEG_END │ Stack pointer (SP) │
|
|
83
|
+
* │ r7 │ ARGS_SEG_START │ Arguments pointer │
|
|
84
|
+
* │ r8 │ args.length │ Arguments length │
|
|
85
|
+
* └──────┴──────────────────┴─────────────────────────┘
|
|
86
|
+
*
|
|
87
|
+
* Key Constants:
|
|
88
|
+
* Z_Z (SEGMENT_SIZE) = 2^16 = 64 KB
|
|
89
|
+
* Z_P (PAGE_SIZE) = 2^12 = 4 KB
|
|
90
|
+
* Z_I (MAX_ARGS_LEN) = 2^24 = 16 MB
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { Decoder } from "./codec";
|
|
2
|
+
import { MemoryBuilder } from "./memory";
|
|
3
|
+
import { Access, PAGE_SIZE, PAGE_SIZE_SHIFT, SEGMENT_SIZE, SEGMENT_SIZE_SHIFT } from "./memory-page";
|
|
4
|
+
import { deblob } from "./program";
|
|
5
|
+
import { newRegisters } from "./registers";
|
|
6
|
+
/** `Z_I`: https://graypaper.fluffylabs.dev/#/ab2cdbd/2daf002daf00?v=0.7.2 */
|
|
7
|
+
export const MAX_ARGS_LEN = 2 ** 24;
|
|
8
|
+
/** https://graypaper.fluffylabs.dev/#/ab2cdbd/2d47022d4702?v=0.7.2 */
|
|
9
|
+
export const ARGS_SEGMENT_START = 2 ** 32 - SEGMENT_SIZE - MAX_ARGS_LEN;
|
|
10
|
+
/** https://graypaper.fluffylabs.dev/#/ab2cdbd/2d33022d3502?v=0.7.2 */
|
|
11
|
+
export const STACK_SEGMENT_END = ARGS_SEGMENT_START - SEGMENT_SIZE;
|
|
12
|
+
/** https://graypaper.fluffylabs.dev/#/ab2cdbd/2da3002da300?v=0.7.2 */
|
|
13
|
+
export function decodeSpi(data, args, preallocateMemoryPages = 0, useBlockGas = false) {
|
|
14
|
+
const argsLength = args.length;
|
|
15
|
+
if (argsLength > MAX_ARGS_LEN) {
|
|
16
|
+
throw new Error(`Arguments length is too big. Got: ${argsLength}, max: ${MAX_ARGS_LEN}`);
|
|
17
|
+
}
|
|
18
|
+
const decoder = new Decoder(data);
|
|
19
|
+
const roLength = decoder.u24();
|
|
20
|
+
const rwLength = decoder.u24();
|
|
21
|
+
const heapPages = decoder.u16();
|
|
22
|
+
const stackSize = decoder.u24();
|
|
23
|
+
const roMem = decoder.bytes(roLength);
|
|
24
|
+
const rwMem = decoder.bytes(rwLength);
|
|
25
|
+
const codeLength = decoder.u32();
|
|
26
|
+
const code = decoder.bytes(codeLength);
|
|
27
|
+
decoder.finish();
|
|
28
|
+
const program = deblob(code, useBlockGas);
|
|
29
|
+
// building memory
|
|
30
|
+
const builder = new MemoryBuilder(preallocateMemoryPages);
|
|
31
|
+
const heapStart = 2 * SEGMENT_SIZE + alignToSegmentSize(roLength);
|
|
32
|
+
const heapZerosStart = heapStart + alignToPageSize(rwLength);
|
|
33
|
+
const heapZerosLength = heapPages * PAGE_SIZE;
|
|
34
|
+
const stackLength = alignToPageSize(stackSize);
|
|
35
|
+
// stackLength is bounded to `2**24`, so there is no risk of underflow here.
|
|
36
|
+
const stackStart = STACK_SEGMENT_END - stackLength;
|
|
37
|
+
// readable memory
|
|
38
|
+
if (roLength > 0) {
|
|
39
|
+
builder.setData(Access.Read, SEGMENT_SIZE, roMem);
|
|
40
|
+
}
|
|
41
|
+
if (argsLength > 0) {
|
|
42
|
+
builder.setData(Access.Read, ARGS_SEGMENT_START, args);
|
|
43
|
+
}
|
|
44
|
+
// writable memory
|
|
45
|
+
if (rwLength > 0) {
|
|
46
|
+
builder.setData(Access.Write, heapStart, rwMem);
|
|
47
|
+
}
|
|
48
|
+
if (heapZerosLength > 0) {
|
|
49
|
+
builder.setEmpty(Access.Write, heapZerosStart, heapZerosLength);
|
|
50
|
+
}
|
|
51
|
+
if (stackLength > 0) {
|
|
52
|
+
builder.setEmpty(Access.Write, stackStart, stackLength);
|
|
53
|
+
}
|
|
54
|
+
const memory = builder.build(heapZerosStart + heapZerosLength, stackStart);
|
|
55
|
+
// build registers
|
|
56
|
+
const registers = newRegisters();
|
|
57
|
+
registers[0] = 4294901760;
|
|
58
|
+
registers[1] = STACK_SEGMENT_END;
|
|
59
|
+
registers[7] = ARGS_SEGMENT_START;
|
|
60
|
+
registers[8] = argsLength;
|
|
61
|
+
return new StandardProgram(program, memory, registers);
|
|
62
|
+
}
|
|
63
|
+
function alignToPageSize(size) {
|
|
64
|
+
return ((size + PAGE_SIZE - 1) >> PAGE_SIZE_SHIFT) << PAGE_SIZE_SHIFT;
|
|
65
|
+
}
|
|
66
|
+
function alignToSegmentSize(size) {
|
|
67
|
+
return ((size + SEGMENT_SIZE - 1) >> SEGMENT_SIZE_SHIFT) << SEGMENT_SIZE_SHIFT;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* SPI Program with memory and registers.
|
|
71
|
+
*
|
|
72
|
+
* https://graypaper.fluffylabs.dev/#/ab2cdbd/2d13002d1400?v=0.7.2
|
|
73
|
+
*/
|
|
74
|
+
export class StandardProgram {
|
|
75
|
+
constructor(program, memory, registers) {
|
|
76
|
+
this.program = program;
|
|
77
|
+
this.memory = memory;
|
|
78
|
+
this.registers = registers;
|
|
79
|
+
this.metadata = new Uint8Array(0);
|
|
80
|
+
}
|
|
81
|
+
toString() {
|
|
82
|
+
return `StandardProgram { program: ${this.program}, memory_pages: ${this.memory.pages.size}, registers: ${this.registers} }`;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Standard Program Interface (SPI) Memory Layout
|
|
87
|
+
* ===============================================
|
|
88
|
+
*
|
|
89
|
+
* 32-bit address space (0x0000_0000 to 0xFFFF_FFFF)
|
|
90
|
+
*
|
|
91
|
+
* ```
|
|
92
|
+
* Address Region Access Notes
|
|
93
|
+
* ─────────────────────────────────────────────────────────────────
|
|
94
|
+
* 0x0000_0000 ┌─────────────────────────┐
|
|
95
|
+
* │ │
|
|
96
|
+
* │ Reserved / Guard │ None 64 KB (Z_Z)
|
|
97
|
+
* │ (inaccessible) │
|
|
98
|
+
* │ │
|
|
99
|
+
* 0x0001_0000 ├─────────────────────────┤ ◄─── SEGMENT_SIZE
|
|
100
|
+
* │ │
|
|
101
|
+
* │ Read-Only Data (RO) │ Read Code constants,
|
|
102
|
+
* │ │ string literals
|
|
103
|
+
* │ │
|
|
104
|
+
* 0x0002_0000+ ├─────────────────────────┤ ◄─── 2*SEGMENT_SIZE + align(roLen)
|
|
105
|
+
* │ │
|
|
106
|
+
* │ Read-Write Data (RW) │ Write Initialized globals
|
|
107
|
+
* │ │
|
|
108
|
+
* ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ ◄─── heapStart + align(rwLen)
|
|
109
|
+
* │ │
|
|
110
|
+
* │ Heap (Zero-init) │ Write Dynamic allocation
|
|
111
|
+
* │ (heapPages * 4KB) │ sbrk grows here
|
|
112
|
+
* │ │
|
|
113
|
+
* ├─────────────────────────┤ ◄─── sbrk pointer
|
|
114
|
+
* │ │
|
|
115
|
+
* │ ░░░░░░░ │
|
|
116
|
+
* │ Unmapped / Guard │ None Grows towards each other
|
|
117
|
+
* │ ░░░░░░░ │
|
|
118
|
+
* │ │
|
|
119
|
+
* stackStart ├─────────────────────────┤ ◄─── STACK_SEGMENT_END - stackLen
|
|
120
|
+
* │ │
|
|
121
|
+
* │ Stack │ Write Grows downward (↓)
|
|
122
|
+
* │ (stackSize aligned) │ r1 = stack pointer
|
|
123
|
+
* │ │
|
|
124
|
+
* 0xFEFE_0000 ├─────────────────────────┤ ◄─── STACK_SEGMENT_END
|
|
125
|
+
* │ │
|
|
126
|
+
* │ Guard (64 KB) │ None Separates stack/args
|
|
127
|
+
* │ │
|
|
128
|
+
* 0xFEFF_0000 ├─────────────────────────┤ ◄─── ARGS_SEGMENT_START
|
|
129
|
+
* │ │
|
|
130
|
+
* │ Arguments (RO) │ Read r7 = args pointer
|
|
131
|
+
* │ (up to 16 MB) │ r8 = args length
|
|
132
|
+
* │ │
|
|
133
|
+
* ├─────────────────────────┤ ◄─── ARGS_SEGMENT_START + argsLen
|
|
134
|
+
* │ │
|
|
135
|
+
* │ Guard (64 KB) │ None Top guard region
|
|
136
|
+
* │ │
|
|
137
|
+
* 0xFFFF_FFFF └─────────────────────────┘
|
|
138
|
+
*
|
|
139
|
+
* Initial Register State:
|
|
140
|
+
* ┌──────┬──────────────────┬─────────────────────────┐
|
|
141
|
+
* │ r0 │ 0xFFFF_0000 │ (reserved) │
|
|
142
|
+
* │ r1 │ STACK_SEG_END │ Stack pointer (SP) │
|
|
143
|
+
* │ r7 │ ARGS_SEG_START │ Arguments pointer │
|
|
144
|
+
* │ r8 │ args.length │ Arguments length │
|
|
145
|
+
* └──────┴──────────────────┴─────────────────────────┘
|
|
146
|
+
*
|
|
147
|
+
* Key Constants:
|
|
148
|
+
* Z_Z (SEGMENT_SIZE) = 2^16 = 64 KB
|
|
149
|
+
* Z_P (PAGE_SIZE) = 2^12 = 4 KB
|
|
150
|
+
* Z_I (MAX_ARGS_LEN) = 2^24 = 16 MB
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
declare const g: Record<string, unknown>;
|