@alephium/web3 1.4.0 → 1.5.0
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/alephium-web3.min.js +1 -1
- package/dist/alephium-web3.min.js.map +1 -1
- package/dist/src/address/address.js +5 -5
- package/dist/src/api/api-alephium.d.ts +1 -1
- package/dist/src/api/api-alephium.js +1 -1
- package/dist/src/codec/array-codec.d.ts +4 -12
- package/dist/src/codec/array-codec.js +15 -28
- package/dist/src/codec/asset-output-codec.d.ts +6 -11
- package/dist/src/codec/asset-output-codec.js +32 -71
- package/dist/src/codec/bigint-codec.d.ts +2 -1
- package/dist/src/codec/bigint-codec.js +14 -2
- package/dist/src/codec/bytestring-codec.d.ts +6 -11
- package/dist/src/codec/bytestring-codec.js +9 -23
- package/dist/src/codec/codec.d.ts +54 -5
- package/dist/src/codec/codec.js +112 -14
- package/dist/src/codec/compact-int-codec.d.ts +65 -44
- package/dist/src/codec/compact-int-codec.js +222 -204
- package/dist/src/codec/contract-codec.d.ts +5 -8
- package/dist/src/codec/contract-codec.js +15 -29
- package/dist/src/codec/contract-output-codec.d.ts +4 -10
- package/dist/src/codec/contract-output-codec.js +20 -40
- package/dist/src/codec/contract-output-ref-codec.d.ts +2 -8
- package/dist/src/codec/contract-output-ref-codec.js +7 -17
- package/dist/src/codec/either-codec.d.ts +8 -15
- package/dist/src/codec/either-codec.js +5 -46
- package/dist/src/codec/index.d.ts +4 -3
- package/dist/src/codec/index.js +7 -4
- package/dist/src/codec/input-codec.d.ts +4 -10
- package/dist/src/codec/input-codec.js +11 -46
- package/dist/src/codec/instr-codec.d.ts +633 -40
- package/dist/src/codec/instr-codec.js +1040 -434
- package/dist/src/codec/int-as-4bytes-codec.d.ts +7 -0
- package/dist/src/codec/{signed-int-codec.js → int-as-4bytes-codec.js} +6 -12
- package/dist/src/codec/lockup-script-codec.d.ts +23 -26
- package/dist/src/codec/lockup-script-codec.js +12 -58
- package/dist/src/codec/method-codec.d.ts +6 -18
- package/dist/src/codec/method-codec.js +20 -48
- package/dist/src/codec/option-codec.d.ts +8 -13
- package/dist/src/codec/option-codec.js +14 -32
- package/dist/src/codec/output-codec.d.ts +2 -2
- package/dist/src/codec/output-codec.js +1 -1
- package/dist/src/codec/reader.d.ts +8 -0
- package/dist/src/codec/reader.js +48 -0
- package/dist/src/codec/script-codec.d.ts +6 -14
- package/dist/src/codec/script-codec.js +6 -22
- package/dist/src/codec/signature-codec.d.ts +4 -12
- package/dist/src/codec/signature-codec.js +3 -15
- package/dist/src/codec/timestamp-codec.d.ts +8 -0
- package/dist/src/codec/timestamp-codec.js +39 -0
- package/dist/src/codec/token-codec.d.ts +3 -10
- package/dist/src/codec/token-codec.js +6 -24
- package/dist/src/codec/transaction-codec.d.ts +6 -11
- package/dist/src/codec/transaction-codec.js +24 -49
- package/dist/src/codec/unlock-script-codec.d.ts +25 -36
- package/dist/src/codec/unlock-script-codec.js +26 -147
- package/dist/src/codec/unsigned-tx-codec.d.ts +8 -14
- package/dist/src/codec/unsigned-tx-codec.js +24 -66
- package/dist/src/codec/val.d.ts +27 -0
- package/dist/src/codec/val.js +33 -0
- package/dist/src/contract/contract.js +6 -6
- package/dist/src/contract/index.d.ts +1 -0
- package/dist/src/contract/index.js +1 -0
- package/dist/src/contract/ralph.d.ts +0 -4
- package/dist/src/contract/ralph.js +50 -179
- package/dist/src/contract/script-simulator.d.ts +27 -0
- package/dist/src/contract/script-simulator.js +757 -0
- package/dist/src/exchange/exchange.js +1 -1
- package/package.json +3 -4
- package/src/address/address.ts +8 -8
- package/src/api/api-alephium.ts +1 -1
- package/src/codec/array-codec.ts +16 -34
- package/src/codec/asset-output-codec.ts +38 -83
- package/src/codec/bigint-codec.ts +16 -2
- package/src/codec/bytestring-codec.ts +10 -28
- package/src/codec/codec.ts +121 -15
- package/src/codec/compact-int-codec.ts +230 -207
- package/src/codec/contract-codec.ts +20 -33
- package/src/codec/contract-output-codec.ts +22 -48
- package/src/codec/contract-output-ref-codec.ts +6 -17
- package/src/codec/either-codec.ts +4 -53
- package/src/codec/index.ts +4 -3
- package/src/codec/input-codec.ts +14 -36
- package/src/codec/instr-codec.ts +1229 -455
- package/src/codec/{signed-int-codec.ts → int-as-4bytes-codec.ts} +6 -10
- package/src/codec/lockup-script-codec.ts +28 -76
- package/src/codec/method-codec.ts +23 -61
- package/src/codec/option-codec.ts +13 -36
- package/src/codec/output-codec.ts +2 -2
- package/src/codec/reader.ts +56 -0
- package/src/codec/script-codec.ts +9 -31
- package/src/codec/signature-codec.ts +3 -18
- package/src/codec/timestamp-codec.ts +42 -0
- package/src/codec/token-codec.ts +7 -26
- package/src/codec/transaction-codec.ts +29 -58
- package/src/codec/unlock-script-codec.ts +44 -171
- package/src/codec/unsigned-tx-codec.ts +34 -63
- package/src/codec/val.ts +40 -0
- package/src/contract/contract.ts +9 -13
- package/src/contract/index.ts +1 -0
- package/src/contract/ralph.ts +76 -172
- package/src/contract/script-simulator.ts +838 -0
- package/src/exchange/exchange.ts +1 -1
- package/dist/src/codec/long-codec.d.ts +0 -8
- package/dist/src/codec/long-codec.js +0 -55
- package/dist/src/codec/signed-int-codec.d.ts +0 -8
- package/src/codec/long-codec.ts +0 -58
|
@@ -0,0 +1,757 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright 2018 - 2022 The Alephium Authors
|
|
4
|
+
This file is part of the alephium project.
|
|
5
|
+
|
|
6
|
+
The library is free software: you can redistribute it and/or modify
|
|
7
|
+
it under the terms of the GNU Lesser General Public License as published by
|
|
8
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
(at your option) any later version.
|
|
10
|
+
|
|
11
|
+
The library is distributed in the hope that it will be useful,
|
|
12
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
GNU Lesser General Public License for more details.
|
|
15
|
+
|
|
16
|
+
You should have received a copy of the GNU Lesser General Public License
|
|
17
|
+
along with the library. If not, see <http://www.gnu.org/licenses/>.
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.ScriptSimulator = void 0;
|
|
21
|
+
const address_1 = require("../address");
|
|
22
|
+
const codec_1 = require("../codec");
|
|
23
|
+
const lockup_script_codec_1 = require("../codec/lockup-script-codec");
|
|
24
|
+
const constants_1 = require("../constants");
|
|
25
|
+
const utils_1 = require("../utils");
|
|
26
|
+
class ScriptSimulator {
|
|
27
|
+
// This function without errors is recommended for now as the simulator does not support all instructions
|
|
28
|
+
static extractContractCalls(unsignedTx) {
|
|
29
|
+
try {
|
|
30
|
+
return this.extractContractCallsWithErrors(unsignedTx);
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
console.debug('Error extracting contract calls from script', e);
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
static extractContractCallsWithErrors(unsignedTx) {
|
|
38
|
+
const unsignedTxBytes = (0, utils_1.hexToBinUnsafe)(unsignedTx);
|
|
39
|
+
const decodedUnsignedTx = codec_1.unsignedTxCodec.decode(unsignedTxBytes);
|
|
40
|
+
const scriptOpt = decodedUnsignedTx.statefulScript;
|
|
41
|
+
switch (scriptOpt.kind) {
|
|
42
|
+
case 'Some': {
|
|
43
|
+
return this.extractContractCallsFromScript(scriptOpt.value);
|
|
44
|
+
}
|
|
45
|
+
case 'None': {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
static extractContractCallsFromScript(script) {
|
|
51
|
+
const methods = script.methods;
|
|
52
|
+
if (methods.length === 0) {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
const mainMethod = methods[0];
|
|
56
|
+
return this.extractContractCallsFromMainMethod(mainMethod);
|
|
57
|
+
}
|
|
58
|
+
static extractContractCallsFromMainMethod(mainMethod) {
|
|
59
|
+
const operandStack = new Stack();
|
|
60
|
+
const localVariables = new LocalVariables();
|
|
61
|
+
const contractCalls = [];
|
|
62
|
+
const callerAddress = {
|
|
63
|
+
kind: 'Address',
|
|
64
|
+
value: { kind: 'P2PKH', value: random32Bytes() }
|
|
65
|
+
};
|
|
66
|
+
const approved = new ApprovedAccumulator();
|
|
67
|
+
for (const instr of mainMethod.instrs) {
|
|
68
|
+
switch (instr.name) {
|
|
69
|
+
case 'ConstTrue':
|
|
70
|
+
operandStack.push({ kind: 'Bool', value: true });
|
|
71
|
+
break;
|
|
72
|
+
case 'ConstFalse':
|
|
73
|
+
operandStack.push({ kind: 'Bool', value: false });
|
|
74
|
+
break;
|
|
75
|
+
case 'I256Const0':
|
|
76
|
+
operandStack.push({ kind: 'I256', value: 0n });
|
|
77
|
+
break;
|
|
78
|
+
case 'I256Const1':
|
|
79
|
+
operandStack.push({ kind: 'I256', value: 1n });
|
|
80
|
+
break;
|
|
81
|
+
case 'I256Const2':
|
|
82
|
+
operandStack.push({ kind: 'I256', value: 2n });
|
|
83
|
+
break;
|
|
84
|
+
case 'I256Const3':
|
|
85
|
+
operandStack.push({ kind: 'I256', value: 3n });
|
|
86
|
+
break;
|
|
87
|
+
case 'I256Const4':
|
|
88
|
+
operandStack.push({ kind: 'I256', value: 4n });
|
|
89
|
+
break;
|
|
90
|
+
case 'I256Const5':
|
|
91
|
+
operandStack.push({ kind: 'I256', value: 5n });
|
|
92
|
+
break;
|
|
93
|
+
case 'I256ConstN1':
|
|
94
|
+
operandStack.push({ kind: 'I256', value: -1n });
|
|
95
|
+
break;
|
|
96
|
+
case 'I256Const':
|
|
97
|
+
operandStack.push({ kind: 'I256', value: instr.value });
|
|
98
|
+
break;
|
|
99
|
+
case 'U256Const0':
|
|
100
|
+
operandStack.push({ kind: 'U256', value: 0n });
|
|
101
|
+
break;
|
|
102
|
+
case 'U256Const1':
|
|
103
|
+
operandStack.push({ kind: 'U256', value: 1n });
|
|
104
|
+
break;
|
|
105
|
+
case 'U256Const2':
|
|
106
|
+
operandStack.push({ kind: 'U256', value: 2n });
|
|
107
|
+
break;
|
|
108
|
+
case 'U256Const3':
|
|
109
|
+
operandStack.push({ kind: 'U256', value: 3n });
|
|
110
|
+
break;
|
|
111
|
+
case 'U256Const4':
|
|
112
|
+
operandStack.push({ kind: 'U256', value: 4n });
|
|
113
|
+
break;
|
|
114
|
+
case 'U256Const5':
|
|
115
|
+
operandStack.push({ kind: 'U256', value: 5n });
|
|
116
|
+
break;
|
|
117
|
+
case 'U256Const':
|
|
118
|
+
operandStack.push({ kind: 'U256', value: instr.value });
|
|
119
|
+
break;
|
|
120
|
+
case 'BytesConst':
|
|
121
|
+
operandStack.push({ kind: 'ByteVec', value: instr.value });
|
|
122
|
+
break;
|
|
123
|
+
case 'AddressConst':
|
|
124
|
+
operandStack.push({ kind: 'Address', value: instr.value });
|
|
125
|
+
break;
|
|
126
|
+
case 'LoadLocal':
|
|
127
|
+
operandStack.push(localVariables.get(instr.index));
|
|
128
|
+
break;
|
|
129
|
+
case 'StoreLocal':
|
|
130
|
+
localVariables.set(instr.index, operandStack.pop());
|
|
131
|
+
break;
|
|
132
|
+
case 'Pop':
|
|
133
|
+
operandStack.pop();
|
|
134
|
+
break;
|
|
135
|
+
case 'Dup':
|
|
136
|
+
const val = operandStack.pop();
|
|
137
|
+
operandStack.push(val);
|
|
138
|
+
operandStack.push(val);
|
|
139
|
+
break;
|
|
140
|
+
case 'Swap':
|
|
141
|
+
const val1 = operandStack.pop();
|
|
142
|
+
const val2 = operandStack.pop();
|
|
143
|
+
operandStack.push(val1);
|
|
144
|
+
operandStack.push(val2);
|
|
145
|
+
break;
|
|
146
|
+
case 'BoolNot':
|
|
147
|
+
const bool = operandStack.popBool();
|
|
148
|
+
const result = unaryOp(bool, (x) => !x);
|
|
149
|
+
operandStack.push(result);
|
|
150
|
+
case 'BoolAnd': {
|
|
151
|
+
const bool1 = operandStack.popBool();
|
|
152
|
+
const bool2 = operandStack.popBool();
|
|
153
|
+
binaryOp(bool1, bool2, (x, y) => x && y, operandStack.push);
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
case 'BoolOr': {
|
|
157
|
+
const bool1 = operandStack.popBool();
|
|
158
|
+
const bool2 = operandStack.popBool();
|
|
159
|
+
binaryOp(bool1, bool2, (x, y) => x || y, operandStack.push);
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
case 'BoolEq': {
|
|
163
|
+
const bool1 = operandStack.popBool();
|
|
164
|
+
const bool2 = operandStack.popBool();
|
|
165
|
+
binaryOp(bool1, bool2, (x, y) => x === y, operandStack.push);
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
case 'BoolNeq': {
|
|
169
|
+
const bool1 = operandStack.popBool();
|
|
170
|
+
const bool2 = operandStack.popBool();
|
|
171
|
+
binaryOp(bool1, bool2, (x, y) => x !== y, operandStack.push);
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
case 'BoolToByteVec': {
|
|
175
|
+
const bool = operandStack.popBool();
|
|
176
|
+
if (bool.kind === 'Symbol-Bool') {
|
|
177
|
+
operandStack.push(bool);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
operandStack.push({ kind: 'ByteVec', value: codec_1.boolCodec.encode(bool.value) });
|
|
181
|
+
}
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
case 'I256Add': {
|
|
185
|
+
// unsafe
|
|
186
|
+
const i256_2 = operandStack.popI256();
|
|
187
|
+
const i256_1 = operandStack.popI256();
|
|
188
|
+
binaryOp(i256_1, i256_2, (x, y) => x + y, operandStack.push);
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
case 'I256Sub': {
|
|
192
|
+
// unsafe
|
|
193
|
+
const i256_2 = operandStack.popI256();
|
|
194
|
+
const i256_1 = operandStack.popI256();
|
|
195
|
+
binaryOp(i256_1, i256_2, (x, y) => x - y, operandStack.push);
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
case 'I256Mul': {
|
|
199
|
+
// unsafe
|
|
200
|
+
const i256_2 = operandStack.popI256();
|
|
201
|
+
const i256_1 = operandStack.popI256();
|
|
202
|
+
binaryOp(i256_1, i256_2, (x, y) => x * y, operandStack.push);
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
case 'I256Div': {
|
|
206
|
+
// unsafe
|
|
207
|
+
const i256_2 = operandStack.popI256();
|
|
208
|
+
const i256_1 = operandStack.popI256();
|
|
209
|
+
binaryOp(i256_1, i256_2, (x, y) => x / y, operandStack.push);
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
case 'I256Eq': {
|
|
213
|
+
// unsafe
|
|
214
|
+
const i256_2 = operandStack.popI256();
|
|
215
|
+
const i256_1 = operandStack.popI256();
|
|
216
|
+
comparisonOp(i256_1, i256_2, (x, y) => x === y, operandStack.push);
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
case 'I256Neq': {
|
|
220
|
+
// unsafe
|
|
221
|
+
const i256_2 = operandStack.popI256();
|
|
222
|
+
const i256_1 = operandStack.popI256();
|
|
223
|
+
comparisonOp(i256_1, i256_2, (x, y) => x !== y, operandStack.push);
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
case 'I256Lt': {
|
|
227
|
+
// unsafe
|
|
228
|
+
const i256_2 = operandStack.popI256();
|
|
229
|
+
const i256_1 = operandStack.popI256();
|
|
230
|
+
comparisonOp(i256_1, i256_2, (x, y) => x < y, operandStack.push);
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
case 'I256Le': {
|
|
234
|
+
// unsafe
|
|
235
|
+
const i256_2 = operandStack.popI256();
|
|
236
|
+
const i256_1 = operandStack.popI256();
|
|
237
|
+
comparisonOp(i256_1, i256_2, (x, y) => x <= y, operandStack.push);
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
case 'I256Gt': {
|
|
241
|
+
// unsafe
|
|
242
|
+
const i256_2 = operandStack.popI256();
|
|
243
|
+
const i256_1 = operandStack.popI256();
|
|
244
|
+
comparisonOp(i256_1, i256_2, (x, y) => x > y, operandStack.push);
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
case 'I256Ge': {
|
|
248
|
+
// unsafe
|
|
249
|
+
const i256_2 = operandStack.popI256();
|
|
250
|
+
const i256_1 = operandStack.popI256();
|
|
251
|
+
comparisonOp(i256_1, i256_2, (x, y) => x >= y, operandStack.push);
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
case 'U256Add': {
|
|
255
|
+
// unsafe
|
|
256
|
+
const u256_2 = operandStack.popU256();
|
|
257
|
+
const u256_1 = operandStack.popU256();
|
|
258
|
+
binaryOp(u256_1, u256_2, (x, y) => x + y, operandStack.push);
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
case 'U256Sub': {
|
|
262
|
+
// unsafe
|
|
263
|
+
const u256_2 = operandStack.popU256();
|
|
264
|
+
const u256_1 = operandStack.popU256();
|
|
265
|
+
binaryOp(u256_1, u256_2, (x, y) => x - y, operandStack.push);
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
case 'U256Mul': {
|
|
269
|
+
// unsafe
|
|
270
|
+
const u256_2 = operandStack.popU256();
|
|
271
|
+
const u256_1 = operandStack.popU256();
|
|
272
|
+
binaryOp(u256_1, u256_2, (x, y) => x * y, operandStack.push);
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
case 'U256Div': {
|
|
276
|
+
// unsafe
|
|
277
|
+
const u256_2 = operandStack.popU256();
|
|
278
|
+
const u256_1 = operandStack.popU256();
|
|
279
|
+
binaryOp(u256_1, u256_2, (x, y) => x / y, operandStack.push);
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
case 'U256Eq': {
|
|
283
|
+
// unsafe
|
|
284
|
+
const u256_2 = operandStack.popU256();
|
|
285
|
+
const u256_1 = operandStack.popU256();
|
|
286
|
+
comparisonOp(u256_1, u256_2, (x, y) => x === y, operandStack.push);
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
case 'U256Neq': {
|
|
290
|
+
// unsafe
|
|
291
|
+
const u256_2 = operandStack.popU256();
|
|
292
|
+
const u256_1 = operandStack.popU256();
|
|
293
|
+
comparisonOp(u256_1, u256_2, (x, y) => x !== y, operandStack.push);
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
case 'U256Lt': {
|
|
297
|
+
// unsafe
|
|
298
|
+
const u256_2 = operandStack.popU256();
|
|
299
|
+
const u256_1 = operandStack.popU256();
|
|
300
|
+
comparisonOp(u256_1, u256_2, (x, y) => x < y, operandStack.push);
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
303
|
+
case 'U256Le': {
|
|
304
|
+
// unsafe
|
|
305
|
+
const u256_2 = operandStack.popU256();
|
|
306
|
+
const u256_1 = operandStack.popU256();
|
|
307
|
+
comparisonOp(u256_1, u256_2, (x, y) => x <= y, operandStack.push);
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
case 'U256Gt': {
|
|
311
|
+
// unsafe
|
|
312
|
+
const u256_2 = operandStack.popU256();
|
|
313
|
+
const u256_1 = operandStack.popU256();
|
|
314
|
+
comparisonOp(u256_1, u256_2, (x, y) => x > y, operandStack.push);
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
case 'U256Ge': {
|
|
318
|
+
// unsafe
|
|
319
|
+
const u256_2 = operandStack.popU256();
|
|
320
|
+
const u256_1 = operandStack.popU256();
|
|
321
|
+
comparisonOp(u256_1, u256_2, (x, y) => x >= y, operandStack.push);
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
case 'ByteVecEq': {
|
|
325
|
+
const byteVec1 = operandStack.popByteVec();
|
|
326
|
+
const byteVec2 = operandStack.popByteVec();
|
|
327
|
+
comparisonOp(byteVec1, byteVec2, (x, y) => arrayEquals(x, y), operandStack.push);
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
case 'ByteVecNeq': {
|
|
331
|
+
const byteVec1 = operandStack.popByteVec();
|
|
332
|
+
const byteVec2 = operandStack.popByteVec();
|
|
333
|
+
comparisonOp(byteVec1, byteVec2, (x, y) => !arrayEquals(x, y), operandStack.push);
|
|
334
|
+
break;
|
|
335
|
+
}
|
|
336
|
+
case 'ByteVecSize': {
|
|
337
|
+
const byteVec = operandStack.popByteVec();
|
|
338
|
+
if (byteVec.kind === 'Symbol-ByteVec') {
|
|
339
|
+
operandStack.push({ kind: 'Symbol-U256', value: undefined });
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
operandStack.push({ kind: 'U256', value: BigInt(byteVec.value.length) });
|
|
343
|
+
}
|
|
344
|
+
break;
|
|
345
|
+
}
|
|
346
|
+
case 'ByteVecConcat': {
|
|
347
|
+
const byteVec2 = operandStack.popByteVec();
|
|
348
|
+
const byteVec1 = operandStack.popByteVec();
|
|
349
|
+
binaryOp(byteVec1, byteVec2, (x, y) => new Uint8Array([...x, ...y]), operandStack.push);
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
case 'ByteVecSlice': {
|
|
353
|
+
const end = operandStack.popU256();
|
|
354
|
+
const start = operandStack.popU256();
|
|
355
|
+
const byteVec = operandStack.popByteVec();
|
|
356
|
+
if (byteVec.kind === 'Symbol-ByteVec' || start.kind === 'Symbol-U256' || end.kind === 'Symbol-U256') {
|
|
357
|
+
operandStack.push({ kind: 'Symbol-ByteVec', value: undefined });
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
operandStack.push({
|
|
361
|
+
kind: 'ByteVec',
|
|
362
|
+
value: byteVec.value.slice(Number(start.value), Number(end.value))
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
case 'AddressEq': {
|
|
368
|
+
const address1 = operandStack.popAddress();
|
|
369
|
+
const address2 = operandStack.popAddress();
|
|
370
|
+
comparisonOp(address1, address2, (x, y) => arrayEquals(lockup_script_codec_1.lockupScriptCodec.encode(x), lockup_script_codec_1.lockupScriptCodec.encode(y)), operandStack.push);
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
case 'AddressNeq': {
|
|
374
|
+
const address1 = operandStack.popAddress();
|
|
375
|
+
const address2 = operandStack.popAddress();
|
|
376
|
+
comparisonOp(address1, address2, (x, y) => !arrayEquals(lockup_script_codec_1.lockupScriptCodec.encode(x), lockup_script_codec_1.lockupScriptCodec.encode(y)), operandStack.push);
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
case 'AddressToByteVec': {
|
|
380
|
+
const address = operandStack.popAddress();
|
|
381
|
+
if (address.kind === 'Symbol-Address') {
|
|
382
|
+
operandStack.push({ kind: 'Symbol-ByteVec', value: undefined });
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
operandStack.push({ kind: 'ByteVec', value: lockup_script_codec_1.lockupScriptCodec.encode(address.value) });
|
|
386
|
+
}
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
case 'Assert': {
|
|
390
|
+
const bool = operandStack.popBool();
|
|
391
|
+
if (!bool) {
|
|
392
|
+
throw new Error('Assertion failed');
|
|
393
|
+
}
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
396
|
+
case 'Blake2b':
|
|
397
|
+
case 'Sha256':
|
|
398
|
+
case 'Sha3':
|
|
399
|
+
case 'Keccak256': {
|
|
400
|
+
dummyImplementation(instr.name);
|
|
401
|
+
operandStack.popByteVec();
|
|
402
|
+
operandStack.push({ kind: 'ByteVec', value: new Uint8Array(32) });
|
|
403
|
+
break;
|
|
404
|
+
}
|
|
405
|
+
case 'ByteVecToAddress': {
|
|
406
|
+
const byteVec = operandStack.popByteVec();
|
|
407
|
+
if (byteVec.kind === 'Symbol-ByteVec') {
|
|
408
|
+
operandStack.push({ kind: 'Symbol-Address', value: undefined });
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
operandStack.push({ kind: 'Address', value: lockup_script_codec_1.lockupScriptCodec.decode(byteVec.value) });
|
|
412
|
+
}
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
case 'Zeros': {
|
|
416
|
+
const size = operandStack.popU256();
|
|
417
|
+
if (size.kind === 'Symbol-U256') {
|
|
418
|
+
operandStack.push({ kind: 'Symbol-ByteVec', value: undefined });
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
if (size.value > 4096) {
|
|
422
|
+
throw new Error('Zeros size is too large');
|
|
423
|
+
}
|
|
424
|
+
operandStack.push({ kind: 'ByteVec', value: new Uint8Array(Number(size.value)) });
|
|
425
|
+
}
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
case 'U256To1Byte':
|
|
429
|
+
case 'U256To2Byte':
|
|
430
|
+
case 'U256To4Byte':
|
|
431
|
+
case 'U256To8Byte':
|
|
432
|
+
case 'U256To16Byte':
|
|
433
|
+
case 'U256To32Byte': {
|
|
434
|
+
dummyImplementation(instr.name);
|
|
435
|
+
operandStack.popU256();
|
|
436
|
+
operandStack.push({ kind: 'Symbol-ByteVec', value: undefined });
|
|
437
|
+
break;
|
|
438
|
+
}
|
|
439
|
+
case 'U256From1Byte':
|
|
440
|
+
case 'U256From2Byte':
|
|
441
|
+
case 'U256From4Byte':
|
|
442
|
+
case 'U256From8Byte':
|
|
443
|
+
case 'U256From16Byte':
|
|
444
|
+
case 'U256From32Byte': {
|
|
445
|
+
dummyImplementation(instr.name);
|
|
446
|
+
operandStack.popByteVec();
|
|
447
|
+
operandStack.push({ kind: 'Symbol-U256', value: undefined });
|
|
448
|
+
break;
|
|
449
|
+
}
|
|
450
|
+
case 'CallExternal':
|
|
451
|
+
case 'CallExternalBySelector': {
|
|
452
|
+
const contractId = operandStack.popByteVec();
|
|
453
|
+
const returnLength = operandStack.popU256(); // method return length
|
|
454
|
+
operandStack.popU256(); // method args length
|
|
455
|
+
if (contractId.kind !== 'Symbol-ByteVec') {
|
|
456
|
+
contractCalls.push({
|
|
457
|
+
contractAddress: (0, address_1.addressFromContractId)((0, utils_1.binToHex)(contractId.value)),
|
|
458
|
+
approvedAttoAlphAmount: approved.getApprovedAttoAlph(),
|
|
459
|
+
approvedTokens: approved.getApprovedTokens()
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
approved.reset();
|
|
463
|
+
if (returnLength.kind !== 'Symbol-U256') {
|
|
464
|
+
for (let i = 0; i < returnLength.value; i++) {
|
|
465
|
+
operandStack.push({ kind: 'Symbol-Any', value: undefined });
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
break;
|
|
469
|
+
}
|
|
470
|
+
case 'ContractIdToAddress': {
|
|
471
|
+
const contractId = operandStack.popByteVec();
|
|
472
|
+
if (contractId.kind === 'Symbol-ByteVec') {
|
|
473
|
+
operandStack.push({ kind: 'Symbol-Address', value: undefined });
|
|
474
|
+
}
|
|
475
|
+
else {
|
|
476
|
+
operandStack.push({ kind: 'Address', value: { kind: 'P2C', value: contractId.value } });
|
|
477
|
+
}
|
|
478
|
+
break;
|
|
479
|
+
}
|
|
480
|
+
case 'LoadLocalByIndex': {
|
|
481
|
+
const index = operandStack.popU256();
|
|
482
|
+
if (index.kind === 'Symbol-U256') {
|
|
483
|
+
throw new Error('LoadLocalByIndex index is a symbol');
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
operandStack.push(localVariables.get(Number(index.value)));
|
|
487
|
+
}
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
490
|
+
case 'StoreLocalByIndex': {
|
|
491
|
+
const index = operandStack.popU256();
|
|
492
|
+
if (index.kind === 'Symbol-U256') {
|
|
493
|
+
throw new Error('StoreLocalByIndex index is a symbol');
|
|
494
|
+
}
|
|
495
|
+
else {
|
|
496
|
+
localVariables.set(Number(index.value), operandStack.pop());
|
|
497
|
+
}
|
|
498
|
+
break;
|
|
499
|
+
}
|
|
500
|
+
case 'CallerAddress': {
|
|
501
|
+
operandStack.push(callerAddress);
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
case 'ApproveAlph': {
|
|
505
|
+
const amount = operandStack.popU256(); // amount
|
|
506
|
+
const spender = operandStack.popAddress(); // spender
|
|
507
|
+
if (spender.kind.startsWith('Symbol')) {
|
|
508
|
+
approved.setUnknown(); // The spender might be the caller
|
|
509
|
+
}
|
|
510
|
+
else if (spender === callerAddress) {
|
|
511
|
+
approved.addApprovedAttoAlph(amount);
|
|
512
|
+
}
|
|
513
|
+
break;
|
|
514
|
+
}
|
|
515
|
+
case 'ApproveToken': {
|
|
516
|
+
const amount = operandStack.popU256(); // amount
|
|
517
|
+
const tokenId = operandStack.popByteVec(); // token
|
|
518
|
+
const spender = operandStack.popAddress(); // spender
|
|
519
|
+
if (spender.kind.startsWith('Symbol')) {
|
|
520
|
+
approved.setUnknown(); // The spender might be the caller
|
|
521
|
+
}
|
|
522
|
+
else if (spender === callerAddress) {
|
|
523
|
+
approved.addApprovedToken(tokenId, amount);
|
|
524
|
+
}
|
|
525
|
+
break;
|
|
526
|
+
}
|
|
527
|
+
case 'CreateContractAndTransferToken': {
|
|
528
|
+
operandStack.popAddress(); // token owner
|
|
529
|
+
}
|
|
530
|
+
case 'CreateContractWithToken': {
|
|
531
|
+
operandStack.popU256(); // token amount
|
|
532
|
+
}
|
|
533
|
+
case 'CreateContract': {
|
|
534
|
+
operandStack.popByteVec(); // mutable fields
|
|
535
|
+
operandStack.popByteVec(); // immutable fields
|
|
536
|
+
operandStack.popByteVec(); // contract code
|
|
537
|
+
operandStack.push({ kind: 'Symbol-ByteVec', value: undefined }); // new contract id
|
|
538
|
+
break;
|
|
539
|
+
}
|
|
540
|
+
case 'TransferAlph': {
|
|
541
|
+
operandStack.popU256(); // amount
|
|
542
|
+
operandStack.popAddress(); // recipient
|
|
543
|
+
operandStack.popAddress(); // sender
|
|
544
|
+
break;
|
|
545
|
+
}
|
|
546
|
+
case 'TransferToken': {
|
|
547
|
+
operandStack.popU256(); // amount
|
|
548
|
+
operandStack.popByteVec(); // token
|
|
549
|
+
operandStack.popAddress(); // recipient
|
|
550
|
+
operandStack.popAddress(); // sender
|
|
551
|
+
break;
|
|
552
|
+
}
|
|
553
|
+
default:
|
|
554
|
+
unimplemented(instr.name);
|
|
555
|
+
break;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
return contractCalls;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
exports.ScriptSimulator = ScriptSimulator;
|
|
562
|
+
function unaryOp(x, op) {
|
|
563
|
+
if (x.kind.startsWith('Symbol')) {
|
|
564
|
+
return x;
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
return { kind: x.kind, value: op(x.value) };
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
function binaryOp(x, y, op, push) {
|
|
571
|
+
const result = x.kind.startsWith('Symbol')
|
|
572
|
+
? x
|
|
573
|
+
: y.kind.startsWith('Symbol')
|
|
574
|
+
? y
|
|
575
|
+
: { kind: x.kind, value: op(x.value, y.value) };
|
|
576
|
+
push(result);
|
|
577
|
+
}
|
|
578
|
+
function comparisonOp(x, y, op, push) {
|
|
579
|
+
const result = x.kind.startsWith('Symbol') || y.kind.startsWith('Symbol')
|
|
580
|
+
? { kind: 'Symbol-Bool', value: undefined }
|
|
581
|
+
: { kind: 'Bool', value: op(x.value, y.value) };
|
|
582
|
+
push(result);
|
|
583
|
+
}
|
|
584
|
+
// implement arrayEquals
|
|
585
|
+
function arrayEquals(x, y) {
|
|
586
|
+
return x.length === y.length && x.every((value, index) => value === y[`${index}`]);
|
|
587
|
+
}
|
|
588
|
+
// generate 32 bytes array with random numbers
|
|
589
|
+
function random32Bytes() {
|
|
590
|
+
const result = new Uint8Array(32);
|
|
591
|
+
for (let i = 0; i < 32; i++) {
|
|
592
|
+
result[`${i}`] = Math.floor(Math.random() * 256);
|
|
593
|
+
}
|
|
594
|
+
return result;
|
|
595
|
+
}
|
|
596
|
+
class Stack {
|
|
597
|
+
constructor() {
|
|
598
|
+
this.stack = [];
|
|
599
|
+
this.push = (val) => {
|
|
600
|
+
this.stack.push(val);
|
|
601
|
+
};
|
|
602
|
+
// TODO
|
|
603
|
+
}
|
|
604
|
+
pop() {
|
|
605
|
+
const result = this.stack.pop();
|
|
606
|
+
if (result === undefined) {
|
|
607
|
+
throw new Error('Stack is empty');
|
|
608
|
+
}
|
|
609
|
+
return result;
|
|
610
|
+
}
|
|
611
|
+
size() {
|
|
612
|
+
return this.stack.length;
|
|
613
|
+
}
|
|
614
|
+
checkedResult(result, expected) {
|
|
615
|
+
if (result.kind.startsWith('Symbol')) {
|
|
616
|
+
if (result.kind !== `Symbol-${expected}`) {
|
|
617
|
+
throw new Error(`Expected a ${expected} value on the stack`);
|
|
618
|
+
}
|
|
619
|
+
return result;
|
|
620
|
+
}
|
|
621
|
+
if (result.kind !== expected) {
|
|
622
|
+
throw new Error(`Expected a ${expected} value on the stack`);
|
|
623
|
+
}
|
|
624
|
+
return result;
|
|
625
|
+
}
|
|
626
|
+
popBool() {
|
|
627
|
+
const result = this.pop();
|
|
628
|
+
return this.checkedResult(result, 'Bool');
|
|
629
|
+
}
|
|
630
|
+
popI256() {
|
|
631
|
+
const result = this.pop();
|
|
632
|
+
return this.checkedResult(result, 'I256');
|
|
633
|
+
}
|
|
634
|
+
popU256() {
|
|
635
|
+
const result = this.pop();
|
|
636
|
+
return this.checkedResult(result, 'U256');
|
|
637
|
+
}
|
|
638
|
+
popByteVec() {
|
|
639
|
+
const result = this.pop();
|
|
640
|
+
return this.checkedResult(result, 'ByteVec');
|
|
641
|
+
}
|
|
642
|
+
popAddress() {
|
|
643
|
+
const result = this.pop();
|
|
644
|
+
return this.checkedResult(result, 'Address');
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
class LocalVariables {
|
|
648
|
+
constructor() {
|
|
649
|
+
this.locals = [];
|
|
650
|
+
// TODO
|
|
651
|
+
}
|
|
652
|
+
get(index) {
|
|
653
|
+
const result = this.locals[`${index}`];
|
|
654
|
+
if (result === undefined) {
|
|
655
|
+
throw new Error(`Local variable at index ${index} is not set`);
|
|
656
|
+
}
|
|
657
|
+
return result;
|
|
658
|
+
}
|
|
659
|
+
set(index, val) {
|
|
660
|
+
this.locals[`${index}`] = val;
|
|
661
|
+
}
|
|
662
|
+
checkedResult(result, index, expected) {
|
|
663
|
+
if (result.kind.startsWith('Symbol')) {
|
|
664
|
+
if (result.kind !== `Symbol-${expected}`) {
|
|
665
|
+
throw new Error(`Local variable at index ${index} is not a ${expected}`);
|
|
666
|
+
}
|
|
667
|
+
return result;
|
|
668
|
+
}
|
|
669
|
+
if (result.kind !== expected) {
|
|
670
|
+
throw new Error(`Local variable at index ${index} is not a ${expected}`);
|
|
671
|
+
}
|
|
672
|
+
return result;
|
|
673
|
+
}
|
|
674
|
+
getBool(index) {
|
|
675
|
+
const result = this.get(index);
|
|
676
|
+
return this.checkedResult(result, index, 'Bool');
|
|
677
|
+
}
|
|
678
|
+
getI256(index) {
|
|
679
|
+
const result = this.get(index);
|
|
680
|
+
return this.checkedResult(result, index, 'I256');
|
|
681
|
+
}
|
|
682
|
+
getU256(index) {
|
|
683
|
+
const result = this.get(index);
|
|
684
|
+
return this.checkedResult(result, index, 'U256');
|
|
685
|
+
}
|
|
686
|
+
getByteVec(index) {
|
|
687
|
+
const result = this.get(index);
|
|
688
|
+
return this.checkedResult(result, index, 'ByteVec');
|
|
689
|
+
}
|
|
690
|
+
getAddress(index) {
|
|
691
|
+
const result = this.get(index);
|
|
692
|
+
return this.checkedResult(result, index, 'Address');
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
function unimplemented(instrName) {
|
|
696
|
+
throw new Error(`Unimplemented instruction: ${instrName}`);
|
|
697
|
+
}
|
|
698
|
+
function dummyImplementation(instrName) {
|
|
699
|
+
console.debug(`Dummy implementation for instruction: ${instrName}`);
|
|
700
|
+
}
|
|
701
|
+
class ApprovedAccumulator {
|
|
702
|
+
constructor() {
|
|
703
|
+
this.approvedTokens = [];
|
|
704
|
+
this.reset();
|
|
705
|
+
}
|
|
706
|
+
reset() {
|
|
707
|
+
this.approvedTokens = [{ id: constants_1.ALPH_TOKEN_ID, amount: 0n }];
|
|
708
|
+
}
|
|
709
|
+
setUnknown() {
|
|
710
|
+
this.approvedTokens = 'unknown';
|
|
711
|
+
}
|
|
712
|
+
getApprovedAttoAlph() {
|
|
713
|
+
if (this.approvedTokens === 'unknown') {
|
|
714
|
+
return 'unknown';
|
|
715
|
+
}
|
|
716
|
+
const approvedAttoAlph = this.approvedTokens[0].amount;
|
|
717
|
+
return approvedAttoAlph === 'unknown' ? 'unknown' : approvedAttoAlph === 0n ? undefined : approvedAttoAlph;
|
|
718
|
+
}
|
|
719
|
+
getApprovedTokens() {
|
|
720
|
+
if (this.approvedTokens === 'unknown') {
|
|
721
|
+
return 'unknown';
|
|
722
|
+
}
|
|
723
|
+
const allTokens = this.approvedTokens.slice(1);
|
|
724
|
+
return allTokens.length === 0 ? undefined : allTokens;
|
|
725
|
+
}
|
|
726
|
+
addApprovedAttoAlph(amount) {
|
|
727
|
+
this.addApprovedToken({ kind: 'ByteVec', value: (0, utils_1.hexToBinUnsafe)(constants_1.ALPH_TOKEN_ID) }, amount);
|
|
728
|
+
}
|
|
729
|
+
addApprovedToken(tokenId, amount) {
|
|
730
|
+
if (this.approvedTokens === 'unknown') {
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
if (tokenId.kind === 'Symbol-ByteVec') {
|
|
734
|
+
this.approvedTokens = 'unknown';
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
const tokenIndex = this.approvedTokens.findIndex((token) => arrayEquals((0, utils_1.hexToBinUnsafe)(token.id), tokenId.value));
|
|
738
|
+
if (tokenIndex === -1) {
|
|
739
|
+
this.approvedTokens.push({
|
|
740
|
+
id: (0, utils_1.binToHex)(tokenId.value),
|
|
741
|
+
amount: amount.kind === 'Symbol-U256' ? 'unknown' : amount.value
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
else {
|
|
745
|
+
const approved = this.approvedTokens[`${tokenIndex}`];
|
|
746
|
+
if (approved.amount === 'unknown') {
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
if (amount.kind === 'Symbol-U256') {
|
|
750
|
+
approved.amount = 'unknown';
|
|
751
|
+
}
|
|
752
|
+
else {
|
|
753
|
+
approved.amount += amount.value;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|