@btc-vision/btc-runtime 1.2.6 → 1.3.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 +73 -37
- package/package.json +7 -4
- package/runtime/buffer/BytesReader.ts +11 -6
- package/runtime/buffer/BytesWriter.ts +9 -17
- package/runtime/contracts/DeployableOP_20.ts +4 -16
- package/runtime/contracts/OP_NET.ts +2 -2
- package/runtime/contracts/interfaces/IOP_20.ts +0 -2
- package/runtime/env/BlockchainEnvironment.ts +13 -10
- package/runtime/generic/AddressMap.ts +62 -0
- package/runtime/generic/Map.ts +9 -8
- package/runtime/index.ts +18 -4
- package/runtime/memory/AddressMemoryMap.ts +8 -7
- package/runtime/memory/MultiAddressMemoryMap.ts +7 -7
- package/runtime/memory/MultiStringMemoryMap.ts +62 -0
- package/runtime/memory/StringMemoryMap.ts +57 -0
- package/runtime/memory/Uint8ArrayMerger.ts +67 -0
- package/runtime/secp256k1/ECPoint.ts +121 -0
- package/runtime/storage/StorageBacked.ts +5 -0
- package/runtime/storage/StorageLayout.ts +7 -0
- package/runtime/storage/StorageSlot.ts +106 -0
- package/runtime/storage/StorageStruct.ts +23 -0
- package/runtime/storage/StorageValue.ts +36 -0
- package/runtime/storage/StoredAddress.ts +47 -0
- package/runtime/storage/StoredU256.ts +5 -0
- package/runtime/tests/assert.ts +6 -3
- package/runtime/tests/env.ts +2 -2
- package/runtime/tests/tests.ts +18 -16
- package/runtime/types/Address.ts +121 -2
- package/runtime/types/SafeMath.ts +24 -0
- package/runtime/utils/b32.ts +243 -0
- package/runtime/utils/box.ts +126 -107
- package/runtime/utils/encodings.ts +46 -0
- package/runtime/utils/hex.ts +50 -47
- package/runtime/utils/index.ts +3 -2
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { MemorySlotData } from './MemorySlot';
|
|
2
2
|
import { u256 } from 'as-bignum/assembly';
|
|
3
|
-
import {
|
|
3
|
+
import { Uint8ArrayMerger } from './Uint8ArrayMerger';
|
|
4
4
|
|
|
5
5
|
@final
|
|
6
6
|
export class MultiAddressMemoryMap<
|
|
7
|
-
K extends
|
|
8
|
-
K2 extends
|
|
7
|
+
K extends Uint8Array,
|
|
8
|
+
K2 extends Uint8Array,
|
|
9
9
|
V extends MemorySlotData<u256>,
|
|
10
|
-
> extends Map<K,
|
|
10
|
+
> extends Map<K, Uint8ArrayMerger<V>> {
|
|
11
11
|
public pointer: u16;
|
|
12
12
|
|
|
13
13
|
constructor(
|
|
@@ -19,7 +19,7 @@ export class MultiAddressMemoryMap<
|
|
|
19
19
|
this.pointer = pointer;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
public get(key: K):
|
|
22
|
+
public get(key: K): Uint8ArrayMerger<V> {
|
|
23
23
|
this.createKeyMerger(key);
|
|
24
24
|
|
|
25
25
|
return super.get(key);
|
|
@@ -36,7 +36,7 @@ export class MultiAddressMemoryMap<
|
|
|
36
36
|
return this;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
public set(key: K, value:
|
|
39
|
+
public set(key: K, value: Uint8ArrayMerger<V>): this {
|
|
40
40
|
this.createKeyMerger(key);
|
|
41
41
|
|
|
42
42
|
return <this>super.set(key, value);
|
|
@@ -56,7 +56,7 @@ export class MultiAddressMemoryMap<
|
|
|
56
56
|
|
|
57
57
|
private createKeyMerger(key: K): void {
|
|
58
58
|
if (!super.has(key)) {
|
|
59
|
-
super.set(key, new
|
|
59
|
+
super.set(key, new Uint8ArrayMerger<V>(key, this.pointer, this.defaultValue));
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { MemorySlotData } from './MemorySlot';
|
|
2
|
+
import { u256 } from 'as-bignum/assembly';
|
|
3
|
+
import { KeyMerger } from './KeyMerger';
|
|
4
|
+
|
|
5
|
+
@final
|
|
6
|
+
export class MultiStringMemoryMap<
|
|
7
|
+
K extends string,
|
|
8
|
+
K2 extends string,
|
|
9
|
+
V extends MemorySlotData<u256>,
|
|
10
|
+
> extends Map<K, KeyMerger<K, K2, V>> {
|
|
11
|
+
public pointer: u16;
|
|
12
|
+
|
|
13
|
+
constructor(
|
|
14
|
+
pointer: u16,
|
|
15
|
+
private readonly defaultValue: V,
|
|
16
|
+
) {
|
|
17
|
+
super();
|
|
18
|
+
|
|
19
|
+
this.pointer = pointer;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public get(key: K): KeyMerger<K, K2, V> {
|
|
23
|
+
this.createKeyMerger(key);
|
|
24
|
+
|
|
25
|
+
return super.get(key);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public setUpperKey(key: K, key2: K2, value: V): this {
|
|
29
|
+
this.createKeyMerger(key);
|
|
30
|
+
|
|
31
|
+
const subMap = super.get(key);
|
|
32
|
+
if (subMap) {
|
|
33
|
+
subMap.set(key2, value);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public set(key: K, value: KeyMerger<K, K2, V>): this {
|
|
40
|
+
this.createKeyMerger(key);
|
|
41
|
+
|
|
42
|
+
return <this>super.set(key, value);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public has(key: K): bool {
|
|
46
|
+
return super.has(key);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public delete(key: K): bool {
|
|
50
|
+
return super.delete(key);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public clear(): void {
|
|
54
|
+
super.clear();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private createKeyMerger(key: K): void {
|
|
58
|
+
if (!super.has(key)) {
|
|
59
|
+
super.set(key, new KeyMerger<K, K2, V>(key, this.pointer, this.defaultValue));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { MemorySlotPointer } from './MemorySlotPointer';
|
|
2
|
+
import { Blockchain } from '../env';
|
|
3
|
+
import { encodePointer } from '../math/abi';
|
|
4
|
+
import { MemorySlotData } from './MemorySlot';
|
|
5
|
+
import { u256 } from 'as-bignum/assembly';
|
|
6
|
+
import { BytesWriter } from '../buffer/BytesWriter';
|
|
7
|
+
|
|
8
|
+
@final
|
|
9
|
+
export class StringMemoryMap<K extends string, V extends MemorySlotData<u256>> {
|
|
10
|
+
public pointer: u16;
|
|
11
|
+
|
|
12
|
+
constructor(
|
|
13
|
+
pointer: u16,
|
|
14
|
+
private readonly defaultValue: V,
|
|
15
|
+
) {
|
|
16
|
+
this.pointer = pointer;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public set(key: K, value: V): this {
|
|
20
|
+
const keyHash: MemorySlotPointer = this.encodePointer(key);
|
|
21
|
+
Blockchain.setStorageAt(keyHash, value);
|
|
22
|
+
|
|
23
|
+
return this;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public get(key: K): MemorySlotData<u256> {
|
|
27
|
+
const keyHash: MemorySlotPointer = this.encodePointer(key);
|
|
28
|
+
|
|
29
|
+
return Blockchain.getStorageAt(keyHash, this.defaultValue);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public has(key: K): bool {
|
|
33
|
+
const keyHash: MemorySlotPointer = this.encodePointer(key);
|
|
34
|
+
|
|
35
|
+
return Blockchain.hasStorageAt(keyHash);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@unsafe
|
|
39
|
+
public delete(key: K): bool {
|
|
40
|
+
this.set(key, this.defaultValue);
|
|
41
|
+
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@unsafe
|
|
46
|
+
public clear(): void {
|
|
47
|
+
throw new Error('Method not implemented.');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private encodePointer(key: K): MemorySlotPointer {
|
|
51
|
+
const writer = new BytesWriter(key.length + 2);
|
|
52
|
+
writer.writeU16(this.pointer);
|
|
53
|
+
writer.writeString(key);
|
|
54
|
+
|
|
55
|
+
return encodePointer(writer.getBuffer());
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { MemorySlotData } from './MemorySlot';
|
|
2
|
+
import { u256 } from 'as-bignum/assembly';
|
|
3
|
+
import { Blockchain } from '../env';
|
|
4
|
+
import { MemorySlotPointer } from './MemorySlotPointer';
|
|
5
|
+
import { encodePointer } from '../math/abi';
|
|
6
|
+
import { BytesWriter } from '../buffer/BytesWriter';
|
|
7
|
+
|
|
8
|
+
@final
|
|
9
|
+
export class Uint8ArrayMerger<V extends MemorySlotData<u256>> {
|
|
10
|
+
public parentKey: Uint8Array;
|
|
11
|
+
|
|
12
|
+
public pointer: u16;
|
|
13
|
+
|
|
14
|
+
constructor(
|
|
15
|
+
parent: Uint8Array,
|
|
16
|
+
pointer: u16,
|
|
17
|
+
private readonly defaultValue: V,
|
|
18
|
+
) {
|
|
19
|
+
this.pointer = pointer;
|
|
20
|
+
|
|
21
|
+
this.parentKey = parent;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public set(key: Uint8Array, value: V): this {
|
|
25
|
+
const keyHash: MemorySlotPointer = this.getKeyHash(key);
|
|
26
|
+
Blockchain.setStorageAt(keyHash, value);
|
|
27
|
+
|
|
28
|
+
return this;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public get(key: Uint8Array): MemorySlotData<u256> {
|
|
32
|
+
const keyHash: MemorySlotPointer = this.getKeyHash(key);
|
|
33
|
+
|
|
34
|
+
return Blockchain.getStorageAt(keyHash, this.defaultValue);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public has(key: Uint8Array): bool {
|
|
38
|
+
const mergedKey: MemorySlotPointer = this.getKeyHash(key);
|
|
39
|
+
|
|
40
|
+
return Blockchain.hasStorageAt(mergedKey);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@unsafe
|
|
44
|
+
public delete(_key: Uint8Array): bool {
|
|
45
|
+
throw new Error('Method not implemented.');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@unsafe
|
|
49
|
+
public clear(): void {
|
|
50
|
+
throw new Error('Clear method not implemented.');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private getKeyHash(key: Uint8Array): MemorySlotPointer {
|
|
54
|
+
const writer: BytesWriter = new BytesWriter(key.byteLength + 2 + this.parentKey.byteLength);
|
|
55
|
+
|
|
56
|
+
writer.writeBytes(this.parentKey);
|
|
57
|
+
writer.writeBytes(key);
|
|
58
|
+
|
|
59
|
+
return this.encodePointer(writer);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private encodePointer(writer: BytesWriter): MemorySlotPointer {
|
|
63
|
+
writer.writeU16(this.pointer);
|
|
64
|
+
|
|
65
|
+
return encodePointer(writer.getBuffer());
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { u256 } from 'as-bignum/assembly';
|
|
2
|
+
import { SafeMath } from '../types/SafeMath';
|
|
3
|
+
|
|
4
|
+
// secp256k1 curve parameters (using little-endian byte arrays)
|
|
5
|
+
const P_BYTES: u8[] = [
|
|
6
|
+
0x2f, 0xfc, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
7
|
+
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
8
|
+
]; // P in little-endian
|
|
9
|
+
|
|
10
|
+
const GX_BYTES: u8[] = [
|
|
11
|
+
0x98, 0x17, 0xf8, 0x16, 0xb1, 0x5b, 0x28, 0xd9, 0x59, 0x28, 0xce, 0x2d, 0xdb, 0xfc, 0x9b, 0x02,
|
|
12
|
+
0x70, 0xb0, 0x87, 0xce, 0x95, 0xa0, 0x62, 0x55, 0xac, 0xbb, 0xdc, 0xf9, 0xef, 0x66, 0xbe, 0x79,
|
|
13
|
+
]; // Gx in little-endian
|
|
14
|
+
|
|
15
|
+
const GY_BYTES: u8[] = [
|
|
16
|
+
0xb8, 0xd4, 0x10, 0xfb, 0xff, 0x08, 0x7d, 0xc4, 0x19, 0x54, 0x85, 0xa6, 0x48, 0x44, 0x17, 0xfd,
|
|
17
|
+
0xa8, 0x08, 0xe1, 0x0e, 0xfc, 0x4b, 0xa4, 0x5d, 0x65, 0xc4, 0xa3, 0xa6, 0x77, 0xda, 0x3a, 0x48,
|
|
18
|
+
]; // Gy in little-endian
|
|
19
|
+
|
|
20
|
+
// Converting byte arrays to u256 using fromBytesLe (little-endian)
|
|
21
|
+
const P = u256.fromBytesLE(P_BYTES);
|
|
22
|
+
const GX = u256.fromBytesLE(GX_BYTES);
|
|
23
|
+
const GY = u256.fromBytesLE(GY_BYTES);
|
|
24
|
+
|
|
25
|
+
// Define a point on the elliptic curve
|
|
26
|
+
class ECPoint {
|
|
27
|
+
x: u256;
|
|
28
|
+
y: u256;
|
|
29
|
+
|
|
30
|
+
constructor(x: u256, y: u256) {
|
|
31
|
+
this.x = x;
|
|
32
|
+
this.y = y;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Point doubling: P + P = 2P
|
|
36
|
+
static double(p: ECPoint): ECPoint {
|
|
37
|
+
if (p.y == u256.Zero) {
|
|
38
|
+
return new ECPoint(u256.Zero, u256.Zero); // Point at infinity
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const two = u256.fromU64(2);
|
|
42
|
+
const three = u256.fromU64(3);
|
|
43
|
+
|
|
44
|
+
// lambda = (3 * p.x^2) / (2 * p.y) mod P
|
|
45
|
+
const numerator = SafeMath.mod(SafeMath.mul(three, SafeMath.pow(p.x, two)), P);
|
|
46
|
+
const denominator = SafeMath.modInverse(SafeMath.mul(two, p.y), P);
|
|
47
|
+
const lambda = SafeMath.mod(SafeMath.mul(numerator, denominator), P);
|
|
48
|
+
|
|
49
|
+
// xr = lambda^2 - 2 * p.x mod P
|
|
50
|
+
const xr = SafeMath.mod(SafeMath.sub(SafeMath.pow(lambda, two), SafeMath.mul(two, p.x)), P);
|
|
51
|
+
|
|
52
|
+
// yr = lambda * (p.x - xr) - p.y mod P
|
|
53
|
+
const yr = SafeMath.mod(SafeMath.sub(SafeMath.mul(lambda, SafeMath.sub(p.x, xr)), p.y), P);
|
|
54
|
+
|
|
55
|
+
return new ECPoint(xr, yr);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Point addition: P + Q = R
|
|
59
|
+
static add(p: ECPoint, q: ECPoint): ECPoint {
|
|
60
|
+
if (p.x == q.x && p.y == q.y) {
|
|
61
|
+
return this.double(p); // If P == Q, perform doubling
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const lambda = SafeMath.mod(
|
|
65
|
+
SafeMath.mul(SafeMath.sub(q.y, p.y), SafeMath.modInverse(SafeMath.sub(q.x, p.x), P)),
|
|
66
|
+
P,
|
|
67
|
+
);
|
|
68
|
+
const xr = SafeMath.mod(
|
|
69
|
+
SafeMath.sub(SafeMath.pow(lambda, u256.fromU64(2)), SafeMath.add(p.x, q.x)),
|
|
70
|
+
P,
|
|
71
|
+
);
|
|
72
|
+
const yr = SafeMath.mod(SafeMath.sub(SafeMath.mul(lambda, SafeMath.sub(p.x, xr)), p.y), P);
|
|
73
|
+
|
|
74
|
+
return new ECPoint(xr, yr);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Scalar multiplication: k * P
|
|
78
|
+
static scalarMultiply(p: ECPoint, k: u256): ECPoint {
|
|
79
|
+
let result = new ECPoint(u256.Zero, u256.Zero); // Point at infinity
|
|
80
|
+
let addend = p;
|
|
81
|
+
|
|
82
|
+
while (!k.isZero()) {
|
|
83
|
+
if (!SafeMath.isEven(k)) {
|
|
84
|
+
result = this.add(result, addend);
|
|
85
|
+
}
|
|
86
|
+
addend = this.double(addend);
|
|
87
|
+
k = SafeMath.div(k, u256.fromU64(2)); // Right shift by 1
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Generate a valid elliptic curve point (public key) from a hash
|
|
95
|
+
export function generatePublicKeyFromHash(scalar: u256): u8[] {
|
|
96
|
+
// Convert hash to u256 scalar
|
|
97
|
+
//const scalar = u256.fromBytes(hash);
|
|
98
|
+
|
|
99
|
+
// Define the generator point on secp256k1 curve
|
|
100
|
+
const G = new ECPoint(GX, GY);
|
|
101
|
+
|
|
102
|
+
// Perform scalar multiplication to get public key point
|
|
103
|
+
const publicKeyPoint = ECPoint.scalarMultiply(G, scalar);
|
|
104
|
+
|
|
105
|
+
// Convert the point to bytes (compressed format)
|
|
106
|
+
return pointToBytes(publicKeyPoint);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Convert elliptic curve point to compressed byte array
|
|
110
|
+
function pointToBytes(point: ECPoint): u8[] {
|
|
111
|
+
const prefix: u8 = SafeMath.isEven(point.y) ? 0x02 : 0x03; // Compressed format prefix
|
|
112
|
+
const xBytes = point.x.toUint8Array(); // Convert X coordinate to bytes
|
|
113
|
+
|
|
114
|
+
const result = new Array<u8>(33); // 1 byte prefix + 32 bytes X coordinate
|
|
115
|
+
result[0] = prefix;
|
|
116
|
+
for (let i = 0; i < 32; i++) {
|
|
117
|
+
result[i + 1] = xBytes[i];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { u256 } from 'as-bignum/assembly';
|
|
2
|
+
import { BlockchainEnvironment } from '../env/BlockchainEnvironment';
|
|
3
|
+
import { Sha256 } from '../math/sha256';
|
|
4
|
+
import { MemorySlotPointer } from '../memory/MemorySlotPointer';
|
|
5
|
+
import { Box, concat, fromArrayBuffer, toArrayBuffer } from '../utils';
|
|
6
|
+
|
|
7
|
+
export function toBuffer<T>(v: T): ArrayBuffer {
|
|
8
|
+
const result = new ArrayBuffer(sizeof<T>());
|
|
9
|
+
store<T>(changetype<usize>(result), v);
|
|
10
|
+
return result;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class StorageSlot {
|
|
14
|
+
public pointer: u16;
|
|
15
|
+
public subPointer: MemorySlotPointer;
|
|
16
|
+
|
|
17
|
+
constructor(pointer: u16, subPointer: MemorySlotPointer) {
|
|
18
|
+
this.pointer = pointer;
|
|
19
|
+
this.subPointer = subPointer;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public get pointerHash(): u256 {
|
|
23
|
+
return fromArrayBuffer(
|
|
24
|
+
Sha256.hash(
|
|
25
|
+
Uint8Array.wrap(
|
|
26
|
+
Box.concat([
|
|
27
|
+
Box.from(toBuffer(this.pointer)),
|
|
28
|
+
Box.from(toArrayBuffer(this.subPointer)),
|
|
29
|
+
]),
|
|
30
|
+
),
|
|
31
|
+
),
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public static wrap(v: ArrayBuffer): StorageSlot {
|
|
36
|
+
return new StorageSlot(0, fromArrayBuffer(Sha256.hash(Uint8Array.wrap(v))));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public static for(keyword: string): StorageSlot {
|
|
40
|
+
return StorageSlot.wrap(String.UTF8.encode(keyword));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public static at(pointer: u16): StorageSlot {
|
|
44
|
+
return new StorageSlot(pointer, u256.Zero);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public select(v: ArrayBuffer): StorageSlot {
|
|
48
|
+
return new StorageSlot(
|
|
49
|
+
this.pointer,
|
|
50
|
+
fromArrayBuffer(
|
|
51
|
+
Sha256.hash(
|
|
52
|
+
Uint8Array.wrap(
|
|
53
|
+
concat(toArrayBuffer(this.subPointer), Sha256.hash(Uint8Array.wrap(v))),
|
|
54
|
+
),
|
|
55
|
+
),
|
|
56
|
+
),
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public keyword(key: string): StorageSlot {
|
|
61
|
+
return this.select(String.UTF8.encode(key));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public get(): u256 {
|
|
65
|
+
return changetype<BlockchainEnvironment>(0).getStorageAt(this.pointerHash, u256.Zero);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public set(v: u256): void {
|
|
69
|
+
changetype<BlockchainEnvironment>(0).setStorageAt(this.pointerHash, v);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public lengthKey(): StorageSlot {
|
|
73
|
+
return this.keyword('/length');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public length(): u32 {
|
|
77
|
+
return this.lengthKey().get().toU32();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public getList(): Array<u256> {
|
|
81
|
+
const result = new Array<u256>(<i32>this.length());
|
|
82
|
+
for (let i: i32 = 0; i < result.length; i++) {
|
|
83
|
+
result[i] = this.selectIndex(i).get();
|
|
84
|
+
}
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public extend(): StorageSlot {
|
|
89
|
+
const lengthKey = this.lengthKey();
|
|
90
|
+
const length = lengthKey.get().toU32();
|
|
91
|
+
lengthKey.set(u256.from(length + 1));
|
|
92
|
+
return this.selectIndex(length);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public selectIndex(index: u32): StorageSlot {
|
|
96
|
+
return this.keyword('/' + index.toString(10));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public nullify(): void {
|
|
100
|
+
this.set(u256.Zero);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
public append(v: u256): void {
|
|
104
|
+
this.extend().set(v);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { StorageSlot } from './StorageSlot';
|
|
2
|
+
import { StorageBacked } from './StorageBacked';
|
|
3
|
+
|
|
4
|
+
export class StorageStruct<T extends StorageBacked> {
|
|
5
|
+
public inner: T;
|
|
6
|
+
public slot: StorageSlot;
|
|
7
|
+
|
|
8
|
+
constructor(slot: StorageSlot, inner: T) {
|
|
9
|
+
this.slot = slot;
|
|
10
|
+
this.inner = inner;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
public static load<T extends StorageBacked>(slot: StorageSlot): StorageStruct<T> {
|
|
14
|
+
return new StorageStruct<T>(slot, instantiate<T>(slot.getList()));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public save(): void {
|
|
18
|
+
const packed = this.inner.serialize();
|
|
19
|
+
for (let i = 0; i < packed.length; i++) {
|
|
20
|
+
this.slot.selectIndex(i).set(packed[i]);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { u256 } from 'as-bignum/assembly';
|
|
2
|
+
import { StorageSlot } from './StorageSlot';
|
|
3
|
+
|
|
4
|
+
export class StorageValue<T> {
|
|
5
|
+
public value: u256;
|
|
6
|
+
public slot: StorageSlot;
|
|
7
|
+
|
|
8
|
+
constructor(slot: StorageSlot) {
|
|
9
|
+
this.slot = slot;
|
|
10
|
+
this.value = u256.Zero;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
public static at<T>(slot: StorageSlot): StorageValue<T> {
|
|
14
|
+
return new StorageValue<T>(slot);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public save(): this {
|
|
18
|
+
this.slot.set(u256.from(this.value));
|
|
19
|
+
return this;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public set(v: T): this {
|
|
23
|
+
this.value = u256.from(v);
|
|
24
|
+
this.save();
|
|
25
|
+
return this;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public load(): this {
|
|
29
|
+
this.value = this.slot.get();
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public unwrap(): T {
|
|
34
|
+
return load<T>(changetype<usize>(this.value.toUint8Array().buffer));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { u256 } from 'as-bignum/assembly';
|
|
2
|
+
import { Blockchain } from '../env';
|
|
3
|
+
import { encodePointer } from '../math/abi';
|
|
4
|
+
import { BytesWriter } from '../buffer/BytesWriter';
|
|
5
|
+
import { Address } from '../types/Address';
|
|
6
|
+
|
|
7
|
+
@final
|
|
8
|
+
export class StoredAddress {
|
|
9
|
+
private readonly addressPointer: u256;
|
|
10
|
+
private readonly defaultValue: u256;
|
|
11
|
+
|
|
12
|
+
constructor(
|
|
13
|
+
public pointer: u16,
|
|
14
|
+
defaultValue: Address,
|
|
15
|
+
) {
|
|
16
|
+
const writer = new BytesWriter(32);
|
|
17
|
+
writer.writeU16(pointer);
|
|
18
|
+
|
|
19
|
+
this.defaultValue = u256.fromBytes(defaultValue);
|
|
20
|
+
this.addressPointer = encodePointer(writer.getBuffer());
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
private _value: Address = new Address();
|
|
24
|
+
|
|
25
|
+
@inline
|
|
26
|
+
public get value(): Address {
|
|
27
|
+
this.ensureValue();
|
|
28
|
+
|
|
29
|
+
return this._value;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@inline
|
|
33
|
+
public set value(value: Address) {
|
|
34
|
+
if (value === this.value) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
this._value = value;
|
|
39
|
+
|
|
40
|
+
Blockchain.setStorageAt(this.addressPointer, u256.fromBytes(this._value));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private ensureValue(): void {
|
|
44
|
+
const value = Blockchain.getStorageAt(this.addressPointer, this.defaultValue);
|
|
45
|
+
this._value.set(value.toBytes());
|
|
46
|
+
}
|
|
47
|
+
}
|
package/runtime/tests/assert.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
export function assert(condition: boolean, e: string): void {
|
|
2
|
-
|
|
2
|
+
if (!condition) throw new Error(e);
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
interface WithToString {
|
|
6
|
+
toString(): string;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
export function assertEq<T extends WithToString>(a: T, b: T): void {
|
|
10
|
+
assert(a === b, 'expected ' + a.toString() + ' to equal ' + b.toString());
|
|
11
|
+
}
|
package/runtime/tests/env.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @ts-ignore
|
|
2
|
-
@external(
|
|
2
|
+
@external('env', 'logStatic')
|
|
3
3
|
export declare function __logStatic(ptr: ArrayBuffer): void;
|
|
4
4
|
|
|
5
5
|
export function log(v: string): void {
|
|
6
|
-
|
|
6
|
+
return __logStatic(String.UTF8.encode(v));
|
|
7
7
|
}
|
package/runtime/tests/tests.ts
CHANGED
|
@@ -1,25 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { Blockchain } from
|
|
4
|
-
import { Box } from
|
|
5
|
-
import {
|
|
6
|
-
import { assertEq } from "./assert";
|
|
1
|
+
import { u256 } from 'as-bignum/assembly';
|
|
2
|
+
import { BytesWriter } from '../buffer/BytesWriter';
|
|
3
|
+
import { Blockchain } from '../env';
|
|
4
|
+
import { Box } from '../utils/box';
|
|
5
|
+
import { assertEq } from './assert';
|
|
7
6
|
|
|
8
7
|
export function test_encode(): void {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
const writer = new BytesWriter(64);
|
|
9
|
+
writer.writeU256(u256.from(10));
|
|
10
|
+
writer.writeU256(u256.from(20));
|
|
11
|
+
const buffer = writer.getBuffer().buffer;
|
|
12
|
+
assertEq(
|
|
13
|
+
Box.from(buffer).toHexString(),
|
|
14
|
+
'0x000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014',
|
|
15
|
+
);
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export function test_log(): void {
|
|
17
|
-
|
|
19
|
+
Blockchain.log('test logging test: OK!');
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
export function test_writeStringWithLength(): void {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
const s = 'test write';
|
|
24
|
+
const writer = new BytesWriter(s.length + 2);
|
|
25
|
+
writer.writeStringWithLength(s);
|
|
26
|
+
assertEq(Box.from(writer.getBuffer().buffer).toHexString(), '0x0a0074657374207772697465');
|
|
25
27
|
}
|