@btc-vision/btc-runtime 1.6.2 → 1.7.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/README.md +2 -2
- package/package.json +1 -1
- package/runtime/buffer/BytesReader.ts +4 -33
- package/runtime/buffer/BytesWriter.ts +3 -3
- package/runtime/contracts/DeployableOP_20.ts +5 -5
- package/runtime/contracts/OP_NET.ts +1 -1
- package/runtime/env/BlockchainEnvironment.ts +10 -16
- package/runtime/env/classes/Transaction.ts +6 -4
- package/runtime/env/classes/UTXO.ts +23 -3
- package/runtime/env/decoders/TransactionDecoder.ts +66 -0
- package/runtime/env/enums/TransactionFlags.ts +9 -0
package/README.md
CHANGED
|
@@ -132,9 +132,9 @@ export class MyToken extends DeployableOP_20 {
|
|
|
132
132
|
|
|
133
133
|
public override execute(method: Selector, calldata: Calldata): BytesWriter {
|
|
134
134
|
switch (method) {
|
|
135
|
-
case encodeSelector('airdrop'):
|
|
135
|
+
case encodeSelector('airdrop()'):
|
|
136
136
|
return this.airdrop(calldata);
|
|
137
|
-
case encodeSelector('airdropWithAmount'):
|
|
137
|
+
case encodeSelector('airdropWithAmount()'):
|
|
138
138
|
return this.airdropWithAmount(calldata);
|
|
139
139
|
default:
|
|
140
140
|
return super.execute(method, calldata);
|
package/package.json
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { i128, u128, u256 } from '@btc-vision/as-bignum/assembly';
|
|
2
|
-
import { TransactionInput, TransactionOutput } from '../env/classes/UTXO';
|
|
3
2
|
import { AddressMap } from '../generic/AddressMap';
|
|
4
3
|
import { Selector } from '../math/abi';
|
|
5
4
|
import { Address } from '../types/Address';
|
|
@@ -207,7 +206,7 @@ export class BytesReader {
|
|
|
207
206
|
* Reads a string of `length` raw bytes, zeroStop = true for convenience.
|
|
208
207
|
* (Or the writer may not have used zeroStop.)
|
|
209
208
|
*/
|
|
210
|
-
public readString(length:
|
|
209
|
+
public readString(length: u32): string {
|
|
211
210
|
const bytes = this.readBytes(length, true);
|
|
212
211
|
return String.UTF8.decode(bytes.buffer);
|
|
213
212
|
}
|
|
@@ -217,7 +216,7 @@ export class BytesReader {
|
|
|
217
216
|
* The AS writer calls `writeStringWithLength(value: string)` => writes length big-endian by default.
|
|
218
217
|
*/
|
|
219
218
|
public readStringWithLength(be: boolean = true): string {
|
|
220
|
-
const length = this.
|
|
219
|
+
const length = this.readU32(be);
|
|
221
220
|
return this.readString(length);
|
|
222
221
|
}
|
|
223
222
|
|
|
@@ -242,7 +241,7 @@ export class BytesReader {
|
|
|
242
241
|
return addr;
|
|
243
242
|
}
|
|
244
243
|
|
|
245
|
-
// ------------------- Arrays ------------------- //
|
|
244
|
+
// ------------------- Arrays ------------------- //
|
|
246
245
|
|
|
247
246
|
/**
|
|
248
247
|
* The AS writer does `writeU32(length)` for U256 arrays, so we read a u32.
|
|
@@ -330,34 +329,6 @@ export class BytesReader {
|
|
|
330
329
|
return result;
|
|
331
330
|
}
|
|
332
331
|
|
|
333
|
-
public readTransactionInputs(): TransactionInput[] {
|
|
334
|
-
const length = this.readU16();
|
|
335
|
-
const result = new Array<TransactionInput>(length);
|
|
336
|
-
|
|
337
|
-
for (let i: u16 = 0; i < length; i++) {
|
|
338
|
-
const txId = this.readBytes(32);
|
|
339
|
-
const outputIndex = this.readU16();
|
|
340
|
-
const scriptSig = this.readBytesWithLength();
|
|
341
|
-
result[i] = new TransactionInput(txId, outputIndex, scriptSig);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
return result;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
public readTransactionOutputs(): TransactionOutput[] {
|
|
348
|
-
const length = this.readU16();
|
|
349
|
-
const result = new Array<TransactionOutput>(length);
|
|
350
|
-
|
|
351
|
-
for (let i: u16 = 0; i < length; i++) {
|
|
352
|
-
const index = this.readU16();
|
|
353
|
-
const scriptPubKey = this.readStringWithLength();
|
|
354
|
-
const value = this.readU64();
|
|
355
|
-
result[i] = new TransactionOutput(index, scriptPubKey, value);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
return result;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
332
|
public getOffset(): i32 {
|
|
362
333
|
return this.currentOffset;
|
|
363
334
|
}
|
|
@@ -373,7 +344,7 @@ export class BytesReader {
|
|
|
373
344
|
if (size > this.buffer.byteLength) {
|
|
374
345
|
throw new Error(
|
|
375
346
|
`Attempt to read beyond buffer length. Requested up to offset ${size}, ` +
|
|
376
|
-
|
|
347
|
+
`but buffer is only ${this.buffer.byteLength} bytes.`,
|
|
377
348
|
);
|
|
378
349
|
}
|
|
379
350
|
}
|
|
@@ -286,7 +286,7 @@ export class BytesWriter {
|
|
|
286
286
|
}
|
|
287
287
|
|
|
288
288
|
public writeStringWithLength(value: string): void {
|
|
289
|
-
this.
|
|
289
|
+
this.writeU32(u32(value.length));
|
|
290
290
|
this.writeString(value);
|
|
291
291
|
}
|
|
292
292
|
|
|
@@ -352,8 +352,8 @@ export class BytesWriter {
|
|
|
352
352
|
private resize(size: u32): void {
|
|
353
353
|
throw new Revert(
|
|
354
354
|
`Buffer is getting resized. This is bad for performance. ` +
|
|
355
|
-
|
|
356
|
-
|
|
355
|
+
`Expected size: ${this.buffer.byteLength + size} - ` +
|
|
356
|
+
`Current size: ${this.buffer.byteLength}`,
|
|
357
357
|
);
|
|
358
358
|
}
|
|
359
359
|
}
|
|
@@ -205,23 +205,23 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
|
|
|
205
205
|
let response: BytesWriter;
|
|
206
206
|
|
|
207
207
|
switch (method) {
|
|
208
|
-
case encodeSelector('decimals'):
|
|
208
|
+
case encodeSelector('decimals()'):
|
|
209
209
|
response = new BytesWriter(BOOLEAN_BYTE_LENGTH);
|
|
210
210
|
response.writeU8(this.decimals);
|
|
211
211
|
break;
|
|
212
|
-
case encodeSelector('name'):
|
|
212
|
+
case encodeSelector('name()'):
|
|
213
213
|
response = new BytesWriter(this.name.length + 2);
|
|
214
214
|
response.writeStringWithLength(this.name);
|
|
215
215
|
break;
|
|
216
|
-
case encodeSelector('symbol'):
|
|
216
|
+
case encodeSelector('symbol()'):
|
|
217
217
|
response = new BytesWriter(this.symbol.length + 2);
|
|
218
218
|
response.writeStringWithLength(this.symbol);
|
|
219
219
|
break;
|
|
220
|
-
case encodeSelector('totalSupply'):
|
|
220
|
+
case encodeSelector('totalSupply()'):
|
|
221
221
|
response = new BytesWriter(U256_BYTE_LENGTH);
|
|
222
222
|
response.writeU256(this.totalSupply);
|
|
223
223
|
break;
|
|
224
|
-
case encodeSelector('maximumSupply'):
|
|
224
|
+
case encodeSelector('maximumSupply()'):
|
|
225
225
|
response = new BytesWriter(U256_BYTE_LENGTH);
|
|
226
226
|
response.writeU256(this.maxSupply);
|
|
227
227
|
break;
|
|
@@ -27,7 +27,7 @@ export class OP_NET implements IBTC {
|
|
|
27
27
|
let response: BytesWriter;
|
|
28
28
|
|
|
29
29
|
switch (method) {
|
|
30
|
-
case encodeSelector('deployer'):
|
|
30
|
+
case encodeSelector('deployer()'):
|
|
31
31
|
response = new BytesWriter(ADDRESS_BYTE_LENGTH);
|
|
32
32
|
response.writeAddress(this.contractDeployer);
|
|
33
33
|
break;
|
|
@@ -35,8 +35,6 @@ export * from '../env/global';
|
|
|
35
35
|
|
|
36
36
|
@final
|
|
37
37
|
export class BlockchainEnvironment {
|
|
38
|
-
private static readonly MAX_U16: u16 = 65535;
|
|
39
|
-
|
|
40
38
|
public readonly DEAD_ADDRESS: Address = Address.dead();
|
|
41
39
|
|
|
42
40
|
private storage: MapUint8Array = new MapUint8Array();
|
|
@@ -81,7 +79,7 @@ export class BlockchainEnvironment {
|
|
|
81
79
|
private _nextPointer: u16 = 0;
|
|
82
80
|
|
|
83
81
|
public get nextPointer(): u16 {
|
|
84
|
-
if (this._nextPointer ===
|
|
82
|
+
if (this._nextPointer === u16.MAX_VALUE) {
|
|
85
83
|
throw new Revert(`Out of storage pointer.`);
|
|
86
84
|
}
|
|
87
85
|
|
|
@@ -157,12 +155,7 @@ export class BlockchainEnvironment {
|
|
|
157
155
|
const caller = reader.readAddress();
|
|
158
156
|
const origin = reader.readAddress();
|
|
159
157
|
|
|
160
|
-
this._tx = new Transaction(
|
|
161
|
-
caller,
|
|
162
|
-
origin,
|
|
163
|
-
txId,
|
|
164
|
-
txHash,
|
|
165
|
-
);
|
|
158
|
+
this._tx = new Transaction(caller, origin, txId, txHash);
|
|
166
159
|
|
|
167
160
|
this._contractDeployer = contractDeployer;
|
|
168
161
|
this._contractAddress = contractAddress;
|
|
@@ -178,7 +171,12 @@ export class BlockchainEnvironment {
|
|
|
178
171
|
}
|
|
179
172
|
|
|
180
173
|
const resultLengthBuffer = new ArrayBuffer(32);
|
|
181
|
-
const status = callContract(
|
|
174
|
+
const status = callContract(
|
|
175
|
+
destinationContract.buffer,
|
|
176
|
+
calldata.getBuffer().buffer,
|
|
177
|
+
calldata.bufferLength(),
|
|
178
|
+
resultLengthBuffer,
|
|
179
|
+
);
|
|
182
180
|
|
|
183
181
|
const reader = new BytesReader(Uint8Array.wrap(resultLengthBuffer));
|
|
184
182
|
const resultLength = reader.readU32(true);
|
|
@@ -244,9 +242,7 @@ export class BlockchainEnvironment {
|
|
|
244
242
|
return contractAddressReader.readAddress();
|
|
245
243
|
}
|
|
246
244
|
|
|
247
|
-
public getStorageAt(
|
|
248
|
-
pointerHash: Uint8Array,
|
|
249
|
-
): Uint8Array {
|
|
245
|
+
public getStorageAt(pointerHash: Uint8Array): Uint8Array {
|
|
250
246
|
this.hasPointerStorageHash(pointerHash);
|
|
251
247
|
if (this.storage.has(pointerHash)) {
|
|
252
248
|
return this.storage.get(pointerHash);
|
|
@@ -255,9 +251,7 @@ export class BlockchainEnvironment {
|
|
|
255
251
|
return new Uint8Array(32);
|
|
256
252
|
}
|
|
257
253
|
|
|
258
|
-
public getTransientStorageAt(
|
|
259
|
-
pointerHash: Uint8Array,
|
|
260
|
-
): Uint8Array {
|
|
254
|
+
public getTransientStorageAt(pointerHash: Uint8Array): Uint8Array {
|
|
261
255
|
if (this.hasPointerTransientStorageHash(pointerHash)) {
|
|
262
256
|
return this.transientStorage.get(pointerHash);
|
|
263
257
|
}
|
|
@@ -3,16 +3,18 @@ import { TransactionInput, TransactionOutput } from './UTXO';
|
|
|
3
3
|
import { Potential } from '../../lang/Definitions';
|
|
4
4
|
import { BytesReader } from '../../buffer/BytesReader';
|
|
5
5
|
import { getInputsSize, getOutputsSize, inputs, outputs } from '../global';
|
|
6
|
+
import { TransactionDecoder } from '../decoders/TransactionDecoder';
|
|
6
7
|
|
|
7
8
|
@final
|
|
8
9
|
export class Transaction {
|
|
10
|
+
private readonly transactionDecoder: TransactionDecoder = new TransactionDecoder();
|
|
11
|
+
|
|
9
12
|
public constructor(
|
|
10
13
|
public readonly sender: Address, // "immediate caller"
|
|
11
14
|
public readonly origin: Address, // "leftmost thing in the call chain"
|
|
12
15
|
public readonly txId: Uint8Array,
|
|
13
16
|
public readonly hash: Uint8Array,
|
|
14
|
-
) {
|
|
15
|
-
}
|
|
17
|
+
) {}
|
|
16
18
|
|
|
17
19
|
private _inputs: Potential<TransactionInput[]> = null;
|
|
18
20
|
|
|
@@ -46,7 +48,7 @@ export class Transaction {
|
|
|
46
48
|
inputs(resultBuffer);
|
|
47
49
|
|
|
48
50
|
const reader = new BytesReader(Uint8Array.wrap(resultBuffer));
|
|
49
|
-
return
|
|
51
|
+
return this.transactionDecoder.readTransactionInputs(reader);
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
private loadOutputs(): TransactionOutput[] {
|
|
@@ -55,6 +57,6 @@ export class Transaction {
|
|
|
55
57
|
outputs(resultBuffer);
|
|
56
58
|
|
|
57
59
|
const reader = new BytesReader(Uint8Array.wrap(resultBuffer));
|
|
58
|
-
return
|
|
60
|
+
return this.transactionDecoder.readTransactionOutputs(reader);
|
|
59
61
|
}
|
|
60
62
|
}
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
+
import { TransactionInputFlags, TransactionOutputFlags } from '../enums/TransactionFlags';
|
|
2
|
+
|
|
1
3
|
@final
|
|
2
4
|
export class TransactionInput {
|
|
3
5
|
public constructor(
|
|
6
|
+
public readonly flags: u8,
|
|
4
7
|
public readonly txId: Uint8Array,
|
|
5
8
|
public readonly outputIndex: u16,
|
|
6
9
|
public readonly scriptSig: Uint8Array,
|
|
7
|
-
|
|
10
|
+
public readonly coinbase: Uint8Array | null,
|
|
11
|
+
) {}
|
|
12
|
+
|
|
13
|
+
public get isCoinbase(): boolean {
|
|
14
|
+
return (this.flags & TransactionInputFlags.hasCoinbase) !== 0;
|
|
8
15
|
}
|
|
9
16
|
}
|
|
10
17
|
|
|
@@ -12,8 +19,21 @@ export class TransactionInput {
|
|
|
12
19
|
export class TransactionOutput {
|
|
13
20
|
public constructor(
|
|
14
21
|
public readonly index: u16,
|
|
15
|
-
public readonly
|
|
22
|
+
public readonly flags: u8,
|
|
23
|
+
public readonly scriptPublicKey: Uint8Array | null,
|
|
24
|
+
public readonly to: string | null,
|
|
16
25
|
public readonly value: u64,
|
|
17
|
-
) {
|
|
26
|
+
) {}
|
|
27
|
+
|
|
28
|
+
public get hasTo(): boolean {
|
|
29
|
+
return (this.flags & TransactionOutputFlags.hasTo) !== 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public get hasScriptPubKey(): boolean {
|
|
33
|
+
return (this.flags & TransactionOutputFlags.hasScriptPubKey) !== 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public get isOPReturn(): boolean {
|
|
37
|
+
return (this.flags & TransactionOutputFlags.OP_RETURN) !== 0;
|
|
18
38
|
}
|
|
19
39
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { TransactionInput, TransactionOutput } from '../classes/UTXO';
|
|
2
|
+
import { TransactionInputFlags, TransactionOutputFlags } from '../enums/TransactionFlags';
|
|
3
|
+
import { BytesReader } from '../../buffer/BytesReader';
|
|
4
|
+
|
|
5
|
+
export class TransactionDecoder {
|
|
6
|
+
public readTransactionInputs(buffer: BytesReader): TransactionInput[] {
|
|
7
|
+
const length = buffer.readU16();
|
|
8
|
+
const result = new Array<TransactionInput>(length);
|
|
9
|
+
|
|
10
|
+
for (let i: u16 = 0; i < length; i++) {
|
|
11
|
+
result[i] = this.decodeInput(buffer);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public readTransactionOutputs(buffer: BytesReader): TransactionOutput[] {
|
|
18
|
+
const length = buffer.readU16();
|
|
19
|
+
const result = new Array<TransactionOutput>(length);
|
|
20
|
+
|
|
21
|
+
for (let i: u16 = 0; i < length; i++) {
|
|
22
|
+
result[i] = this.decodeOutput(buffer);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private decodeInput(buffer: BytesReader): TransactionInput {
|
|
29
|
+
const flags = buffer.readU8();
|
|
30
|
+
const txId = buffer.readBytes(32);
|
|
31
|
+
const outputIndex = buffer.readU16();
|
|
32
|
+
const scriptSig = buffer.readBytesWithLength();
|
|
33
|
+
|
|
34
|
+
const coinbase: Uint8Array | null = this.hasFlag(flags, TransactionInputFlags.hasCoinbase)
|
|
35
|
+
? buffer.readBytesWithLength()
|
|
36
|
+
: null;
|
|
37
|
+
|
|
38
|
+
return new TransactionInput(flags, txId, outputIndex, scriptSig, coinbase);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private decodeOutput(buffer: BytesReader): TransactionOutput {
|
|
42
|
+
const flags = buffer.readU8();
|
|
43
|
+
const index = buffer.readU16();
|
|
44
|
+
|
|
45
|
+
let scriptPubKey: Uint8Array | null = null;
|
|
46
|
+
if (this.hasFlag(flags, TransactionOutputFlags.hasScriptPubKey)) {
|
|
47
|
+
scriptPubKey = buffer.readBytesWithLength();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let to: string | null = null;
|
|
51
|
+
if (this.hasFlag(flags, TransactionOutputFlags.hasTo)) {
|
|
52
|
+
to = buffer.readStringWithLength();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const value = buffer.readU64();
|
|
56
|
+
|
|
57
|
+
return new TransactionOutput(index, flags, scriptPubKey, to, value);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Checks if the given flag is set in the flags byte.
|
|
62
|
+
*/
|
|
63
|
+
private hasFlag<T extends u8>(flags: u8, flag: T): boolean {
|
|
64
|
+
return (flags & flag) !== 0;
|
|
65
|
+
}
|
|
66
|
+
}
|