@btc-vision/btc-runtime 1.5.1 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/runtime/buffer/BytesReader.ts +72 -1
- package/runtime/buffer/BytesWriter.ts +74 -0
- package/runtime/contracts/DeployableOP_20.ts +6 -5
- package/runtime/env/BlockchainEnvironment.ts +37 -0
- package/runtime/exports/index.ts +5 -5
- package/runtime/index.ts +12 -9
- package/runtime/memory/{MultiAddressMemoryMap.ts → MapOfMap.ts} +6 -6
- package/runtime/memory/Nested.ts +113 -0
- package/runtime/nested/PointerManager.ts +55 -0
- package/runtime/nested/codecs/AddressCodec.ts +19 -0
- package/runtime/nested/codecs/BooleanCodec.ts +17 -0
- package/runtime/nested/codecs/Ids.ts +10 -0
- package/runtime/nested/codecs/NumericCodec.ts +58 -0
- package/runtime/nested/codecs/StringCodec.ts +24 -0
- package/runtime/nested/codecs/U256Codec.ts +20 -0
- package/runtime/nested/codecs/VariableBytesCodec.ts +134 -0
- package/runtime/nested/interfaces/ICodec.ts +12 -0
- package/runtime/nested/storage/StorageMap.ts +227 -0
- package/runtime/nested/storage/StorageSet.ts +57 -0
- package/runtime/plugins/Plugin.ts +12 -0
- package/runtime/memory/FastUint8Array.ts +0 -122
- package/runtime/memory/MultiStringMemoryMap.ts +0 -65
- package/runtime/memory/StringMemoryMap.ts +0 -56
- package/runtime/memory/Uint8ArrayMerger.ts +0 -72
- package/runtime/storage/Serializable.ts +0 -113
package/package.json
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
U64_BYTE_LENGTH,
|
|
16
16
|
U8_BYTE_LENGTH,
|
|
17
17
|
} from '../utils';
|
|
18
|
+
import { sizeof } from 'builtins';
|
|
18
19
|
|
|
19
20
|
@final
|
|
20
21
|
export class BytesReader {
|
|
@@ -29,6 +30,76 @@ export class BytesReader {
|
|
|
29
30
|
return this.buffer.byteLength;
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
public read<T>(): T {
|
|
34
|
+
const id = idof<T>();
|
|
35
|
+
|
|
36
|
+
if (isBoolean<T>()) {
|
|
37
|
+
return this.readBoolean() as T;
|
|
38
|
+
} else if (isString<T>()) {
|
|
39
|
+
return this.readStringWithLength() as T;
|
|
40
|
+
} else if (isInteger<T>()) {
|
|
41
|
+
if (isSigned<T>()) {
|
|
42
|
+
const size = sizeof<T>();
|
|
43
|
+
|
|
44
|
+
switch (size) {
|
|
45
|
+
case 8:
|
|
46
|
+
return this.readU8() as T;
|
|
47
|
+
case 16:
|
|
48
|
+
return this.readI16() as T;
|
|
49
|
+
case 32:
|
|
50
|
+
return this.readI32() as T;
|
|
51
|
+
case 64:
|
|
52
|
+
return this.readI64() as T;
|
|
53
|
+
default:
|
|
54
|
+
throw new Error(`Invalid size ${size}`);
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
const size = sizeof<T>();
|
|
58
|
+
|
|
59
|
+
switch (size) {
|
|
60
|
+
case 8:
|
|
61
|
+
return this.readU8() as T;
|
|
62
|
+
case 16:
|
|
63
|
+
return this.readU16() as T;
|
|
64
|
+
case 32:
|
|
65
|
+
return this.readU32() as T;
|
|
66
|
+
case 64:
|
|
67
|
+
return this.readU64() as T;
|
|
68
|
+
default:
|
|
69
|
+
throw new Error(`Invalid size ${size}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
} else if (id === idof<u256>()) {
|
|
73
|
+
return this.readU256() as T;
|
|
74
|
+
} else if (id === idof<u128>()) {
|
|
75
|
+
return this.readU128() as T;
|
|
76
|
+
} else if (id === idof<i128>()) {
|
|
77
|
+
return this.readI128() as T;
|
|
78
|
+
} else if (id === idof<Address>()) {
|
|
79
|
+
return this.readAddress() as T;
|
|
80
|
+
} else if (id === idof<Uint8Array>()) {
|
|
81
|
+
return this.readBytesWithLength() as T;
|
|
82
|
+
} else if (id === idof<string>()) {
|
|
83
|
+
return this.readStringWithLength() as T;
|
|
84
|
+
} else {
|
|
85
|
+
throw new Error(`Unsupported type ${id}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
public readI16(): i16 {
|
|
90
|
+
this.verifyEnd(this.currentOffset + 2);
|
|
91
|
+
const value = this.buffer.getInt16(this.currentOffset, true);
|
|
92
|
+
this.currentOffset += 2;
|
|
93
|
+
return value;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public readI32(): i32 {
|
|
97
|
+
this.verifyEnd(this.currentOffset + 4);
|
|
98
|
+
const value = this.buffer.getInt32(this.currentOffset, true);
|
|
99
|
+
this.currentOffset += 4;
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
|
|
32
103
|
public readU8(): u8 {
|
|
33
104
|
this.verifyEnd(this.currentOffset + U8_BYTE_LENGTH);
|
|
34
105
|
const value = this.buffer.getUint8(this.currentOffset);
|
|
@@ -169,7 +240,7 @@ export class BytesReader {
|
|
|
169
240
|
return addr;
|
|
170
241
|
}
|
|
171
242
|
|
|
172
|
-
|
|
243
|
+
// ------------------- Arrays ------------------- //
|
|
173
244
|
|
|
174
245
|
/**
|
|
175
246
|
* The AS writer does `writeU32(length)` for U256 arrays, so we read a u32.
|
|
@@ -30,6 +30,62 @@ export class BytesWriter {
|
|
|
30
30
|
return this.buffer.byteLength;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
public write<T>(value: T): void {
|
|
34
|
+
if (isInteger<T>()) {
|
|
35
|
+
const size = sizeof<T>();
|
|
36
|
+
if (size === 1) {
|
|
37
|
+
this.writeU8(<u8>value);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (isSigned<T>()) {
|
|
42
|
+
switch (size) {
|
|
43
|
+
case 2:
|
|
44
|
+
this.writeI16(<i16>value);
|
|
45
|
+
break;
|
|
46
|
+
case 4:
|
|
47
|
+
this.writeI32(<i32>value);
|
|
48
|
+
break;
|
|
49
|
+
case 8:
|
|
50
|
+
this.writeI64(<i64>value);
|
|
51
|
+
break;
|
|
52
|
+
default:
|
|
53
|
+
throw new Revert(`Unsupported integer size: ${size}`);
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
switch (size) {
|
|
57
|
+
case 2:
|
|
58
|
+
this.writeU16(<u16>value);
|
|
59
|
+
break;
|
|
60
|
+
case 4:
|
|
61
|
+
this.writeU32(<u32>value);
|
|
62
|
+
break;
|
|
63
|
+
case 8:
|
|
64
|
+
this.writeU64(<u64>value);
|
|
65
|
+
break;
|
|
66
|
+
default:
|
|
67
|
+
throw new Revert(`Unsupported integer size: ${size}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} else if (isBoolean<T>()) {
|
|
71
|
+
this.writeBoolean(<boolean>value);
|
|
72
|
+
} else if (isString<T>()) {
|
|
73
|
+
this.writeStringWithLength(<string>value);
|
|
74
|
+
} else if (value instanceof Uint8Array) {
|
|
75
|
+
this.writeBytesWithLength(<Uint8Array>value);
|
|
76
|
+
} else if (value instanceof Address) {
|
|
77
|
+
this.writeAddress(<Address>value);
|
|
78
|
+
} else if (value instanceof u128) {
|
|
79
|
+
this.writeU128(<u128>value);
|
|
80
|
+
} else if (value instanceof u256) {
|
|
81
|
+
this.writeU256(<u256>value);
|
|
82
|
+
} else if (value instanceof i128) {
|
|
83
|
+
this.writeI128(<i128>value);
|
|
84
|
+
} else {
|
|
85
|
+
throw new Revert(`Unsupported type: ${typeof value}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
33
89
|
public writeU8(value: u8): void {
|
|
34
90
|
this.allocSafe(U8_BYTE_LENGTH);
|
|
35
91
|
this.buffer.setUint8(this.currentOffset, value);
|
|
@@ -64,6 +120,24 @@ export class BytesWriter {
|
|
|
64
120
|
this.currentOffset += U64_BYTE_LENGTH;
|
|
65
121
|
}
|
|
66
122
|
|
|
123
|
+
public writeI64(value: i64, be: boolean = true): void {
|
|
124
|
+
this.allocSafe(U64_BYTE_LENGTH);
|
|
125
|
+
this.buffer.setInt64(this.currentOffset, value, !be);
|
|
126
|
+
this.currentOffset += U64_BYTE_LENGTH;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
public writeI32(value: i32, be: boolean = true): void {
|
|
130
|
+
this.allocSafe(U32_BYTE_LENGTH);
|
|
131
|
+
this.buffer.setInt32(this.currentOffset, value, !be);
|
|
132
|
+
this.currentOffset += U32_BYTE_LENGTH;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
public writeI16(value: i16, be: boolean = true): void {
|
|
136
|
+
this.allocSafe(U16_BYTE_LENGTH);
|
|
137
|
+
this.buffer.setInt16(this.currentOffset, value, !be);
|
|
138
|
+
this.currentOffset += U16_BYTE_LENGTH;
|
|
139
|
+
}
|
|
140
|
+
|
|
67
141
|
/**
|
|
68
142
|
* Writes a 32-bit selector.
|
|
69
143
|
* @param value
|
|
@@ -3,8 +3,6 @@ import { BytesWriter } from '../buffer/BytesWriter';
|
|
|
3
3
|
import { Blockchain } from '../env';
|
|
4
4
|
import { ApproveEvent, BurnEvent, MintEvent, TransferEvent } from '../events/predefined';
|
|
5
5
|
import { encodeSelector, Selector } from '../math/abi';
|
|
6
|
-
import { AddressMemoryMap } from '../memory/AddressMemoryMap';
|
|
7
|
-
import { MultiAddressMemoryMap } from '../memory/MultiAddressMemoryMap';
|
|
8
6
|
import { StoredString } from '../storage/StoredString';
|
|
9
7
|
import { StoredU256 } from '../storage/StoredU256';
|
|
10
8
|
import { Address } from '../types/Address';
|
|
@@ -19,6 +17,8 @@ import { OP20InitParameters } from './interfaces/OP20InitParameters';
|
|
|
19
17
|
import { OP_NET } from './OP_NET';
|
|
20
18
|
import { sha256 } from '../env/global';
|
|
21
19
|
import { EMPTY_POINTER } from '../math/bytes';
|
|
20
|
+
import { MapOfMap } from '../memory/MapOfMap';
|
|
21
|
+
import { AddressMemoryMap } from '../memory/AddressMemoryMap';
|
|
22
22
|
|
|
23
23
|
const nonceMapPointer: u16 = Blockchain.nextPointer;
|
|
24
24
|
const maxSupplyPointer: u16 = Blockchain.nextPointer;
|
|
@@ -29,7 +29,7 @@ const allowanceMapPointer: u16 = Blockchain.nextPointer;
|
|
|
29
29
|
const balanceOfMapPointer: u16 = Blockchain.nextPointer;
|
|
30
30
|
|
|
31
31
|
export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
|
|
32
|
-
protected readonly allowanceMap:
|
|
32
|
+
protected readonly allowanceMap: MapOfMap<u256>;
|
|
33
33
|
protected readonly balanceOfMap: AddressMemoryMap;
|
|
34
34
|
|
|
35
35
|
protected readonly _maxSupply: StoredU256;
|
|
@@ -43,14 +43,15 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
|
|
|
43
43
|
super();
|
|
44
44
|
|
|
45
45
|
// Initialize main storage structures
|
|
46
|
-
this.allowanceMap = new
|
|
46
|
+
this.allowanceMap = new MapOfMap(allowanceMapPointer);
|
|
47
47
|
this.balanceOfMap = new AddressMemoryMap(balanceOfMapPointer);
|
|
48
|
+
this._nonceMap = new AddressMemoryMap(nonceMapPointer);
|
|
49
|
+
|
|
48
50
|
this._totalSupply = new StoredU256(totalSupplyPointer, EMPTY_POINTER);
|
|
49
51
|
this._maxSupply = new StoredU256(maxSupplyPointer, EMPTY_POINTER);
|
|
50
52
|
this._decimals = new StoredU256(decimalsPointer, EMPTY_POINTER);
|
|
51
53
|
this._name = new StoredString(stringPointer, 0);
|
|
52
54
|
this._symbol = new StoredString(stringPointer, 1);
|
|
53
|
-
this._nonceMap = new AddressMemoryMap(nonceMapPointer);
|
|
54
55
|
|
|
55
56
|
if (params && this._maxSupply.value.isZero()) {
|
|
56
57
|
this.instantiate(params, true);
|
|
@@ -23,6 +23,8 @@ import {
|
|
|
23
23
|
} from './global';
|
|
24
24
|
import { eqUint, MapUint8Array } from '../generic/MapUint8Array';
|
|
25
25
|
import { EMPTY_BUFFER } from '../math/bytes';
|
|
26
|
+
import { Plugin } from '../plugins/Plugin';
|
|
27
|
+
import { Calldata } from '../types';
|
|
26
28
|
|
|
27
29
|
export * from '../env/global';
|
|
28
30
|
|
|
@@ -38,6 +40,7 @@ export class BlockchainEnvironment {
|
|
|
38
40
|
|
|
39
41
|
private storage: MapUint8Array = new MapUint8Array();
|
|
40
42
|
private _selfContract: Potential<OP_NET> = null;
|
|
43
|
+
private _plugins: Plugin[] = [];
|
|
41
44
|
|
|
42
45
|
private _block: Potential<Block> = null;
|
|
43
46
|
|
|
@@ -105,6 +108,40 @@ export class BlockchainEnvironment {
|
|
|
105
108
|
return this._contractAddress as Address;
|
|
106
109
|
}
|
|
107
110
|
|
|
111
|
+
public registerPlugin(plugin: Plugin): void {
|
|
112
|
+
this._plugins.push(plugin);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
public onDeployment(calldata: Calldata): void {
|
|
116
|
+
for (let i: i32 = 0; i < this._plugins.length; i++) {
|
|
117
|
+
const plugin = this._plugins[i];
|
|
118
|
+
|
|
119
|
+
plugin.onDeployment(calldata);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
this.contract.onDeployment(calldata);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
public onExecutionStarted(): void {
|
|
126
|
+
for (let i: i32 = 0; i < this._plugins.length; i++) {
|
|
127
|
+
const plugin = this._plugins[i];
|
|
128
|
+
|
|
129
|
+
plugin.onExecutionStarted();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
this.contract.onExecutionStarted();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
public onExecutionCompleted(): void {
|
|
136
|
+
for (let i: i32 = 0; i < this._plugins.length; i++) {
|
|
137
|
+
const plugin = this._plugins[i];
|
|
138
|
+
|
|
139
|
+
plugin.onExecutionCompleted();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
this.contract.onExecutionCompleted();
|
|
143
|
+
}
|
|
144
|
+
|
|
108
145
|
public setEnvironmentVariables(data: Uint8Array): void {
|
|
109
146
|
const reader: BytesReader = new BytesReader(data);
|
|
110
147
|
|
package/runtime/exports/index.ts
CHANGED
|
@@ -18,11 +18,11 @@ export function execute(calldataLength: u32): u32 {
|
|
|
18
18
|
const calldata: Calldata = new BytesReader(Uint8Array.wrap(calldataBuffer));
|
|
19
19
|
const selector: Selector = calldata.readSelector();
|
|
20
20
|
|
|
21
|
-
Blockchain.
|
|
21
|
+
Blockchain.onExecutionStarted();
|
|
22
22
|
|
|
23
23
|
const result: BytesWriter = Blockchain.contract.execute(selector, calldata);
|
|
24
24
|
|
|
25
|
-
Blockchain.
|
|
25
|
+
Blockchain.onExecutionCompleted();
|
|
26
26
|
|
|
27
27
|
const resultBuffer = result.getBuffer().buffer;
|
|
28
28
|
const resultLength = resultBuffer.byteLength;
|
|
@@ -43,9 +43,9 @@ export function onDeploy(calldataLength: u32): u32 {
|
|
|
43
43
|
|
|
44
44
|
const calldata: Calldata = new BytesReader(Uint8Array.wrap(calldataBuffer));
|
|
45
45
|
|
|
46
|
-
Blockchain.
|
|
47
|
-
Blockchain.
|
|
48
|
-
Blockchain.
|
|
46
|
+
Blockchain.onExecutionStarted();
|
|
47
|
+
Blockchain.onDeployment(calldata);
|
|
48
|
+
Blockchain.onExecutionCompleted();
|
|
49
49
|
|
|
50
50
|
return 0;
|
|
51
51
|
}
|
package/runtime/index.ts
CHANGED
|
@@ -45,12 +45,18 @@ export * from './math/bytes';
|
|
|
45
45
|
export * from './secp256k1/ECPoint';
|
|
46
46
|
|
|
47
47
|
/** Memory */
|
|
48
|
-
export * from './memory/
|
|
49
|
-
export * from './
|
|
50
|
-
export * from './
|
|
51
|
-
export * from './
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
export * from './memory/Nested';
|
|
49
|
+
export * from './nested/PointerManager';
|
|
50
|
+
export * from './nested/storage/StorageMap';
|
|
51
|
+
export * from './nested/storage/StorageSet';
|
|
52
|
+
|
|
53
|
+
/** Codecs */
|
|
54
|
+
export * from './nested/codecs/U256Codec';
|
|
55
|
+
export * from './nested/codecs/AddressCodec';
|
|
56
|
+
export * from './nested/codecs/NumericCodec';
|
|
57
|
+
export * from './nested/codecs/BooleanCodec';
|
|
58
|
+
export * from './nested/codecs/StringCodec';
|
|
59
|
+
export * from './nested/codecs/VariableBytesCodec';
|
|
54
60
|
|
|
55
61
|
/** Storage */
|
|
56
62
|
export * from './storage/StoredU256';
|
|
@@ -58,7 +64,6 @@ export * from './storage/StoredU64';
|
|
|
58
64
|
export * from './storage/StoredString';
|
|
59
65
|
export * from './storage/StoredAddress';
|
|
60
66
|
export * from './storage/StoredBoolean';
|
|
61
|
-
export * from './storage/Serializable';
|
|
62
67
|
|
|
63
68
|
/** Arrays */
|
|
64
69
|
export * from './storage/arrays/StoredAddressArray';
|
|
@@ -71,8 +76,6 @@ export * from './storage/arrays/StoredU64Array';
|
|
|
71
76
|
export * from './storage/arrays/StoredU128Array';
|
|
72
77
|
export * from './storage/arrays/StoredU256Array';
|
|
73
78
|
|
|
74
|
-
export * from './memory/FastUint8Array';
|
|
75
|
-
|
|
76
79
|
/** Shared libraries */
|
|
77
80
|
export * from './shared-libraries/TransferHelper';
|
|
78
81
|
export * from './shared-libraries/OP20Utils';
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Uint8ArrayMerger } from './Uint8ArrayMerger';
|
|
2
1
|
import { Address } from '../types/Address';
|
|
2
|
+
import { Nested } from './Nested';
|
|
3
3
|
|
|
4
4
|
@final
|
|
5
|
-
export class
|
|
5
|
+
export class MapOfMap<T> extends Map<
|
|
6
6
|
Address,
|
|
7
|
-
|
|
7
|
+
Nested<T>
|
|
8
8
|
> {
|
|
9
9
|
public pointer: u16;
|
|
10
10
|
|
|
@@ -17,14 +17,14 @@ export class MultiAddressMemoryMap extends Map<
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
@inline
|
|
20
|
-
public get(key: Address):
|
|
20
|
+
public get(key: Address): Nested<T> {
|
|
21
21
|
this.createKeyMerger(key);
|
|
22
22
|
|
|
23
23
|
return super.get(key);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
@inline
|
|
27
|
-
public set(key: Address, value:
|
|
27
|
+
public set(key: Address, value: Nested<T>): this {
|
|
28
28
|
this.createKeyMerger(key);
|
|
29
29
|
|
|
30
30
|
return <this>super.set(key, value);
|
|
@@ -48,7 +48,7 @@ export class MultiAddressMemoryMap extends Map<
|
|
|
48
48
|
@inline
|
|
49
49
|
private createKeyMerger(key: Address): void {
|
|
50
50
|
if (!super.has(key)) {
|
|
51
|
-
super.set(key, new
|
|
51
|
+
super.set(key, new Nested<T>(key, this.pointer));
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Blockchain } from '../env';
|
|
2
|
+
import { encodePointerUnknownLength } from '../math/abi';
|
|
3
|
+
import { BytesWriter } from '../buffer/BytesWriter';
|
|
4
|
+
import { u256 } from '@btc-vision/as-bignum/assembly';
|
|
5
|
+
import { Address } from '../types/Address';
|
|
6
|
+
|
|
7
|
+
@final
|
|
8
|
+
export class Nested<T> {
|
|
9
|
+
public parentKey: Uint8Array;
|
|
10
|
+
|
|
11
|
+
public pointer: u16;
|
|
12
|
+
|
|
13
|
+
constructor(
|
|
14
|
+
parent: Uint8Array,
|
|
15
|
+
pointer: u16,
|
|
16
|
+
) {
|
|
17
|
+
this.pointer = pointer;
|
|
18
|
+
|
|
19
|
+
this.parentKey = parent;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public set(key: Uint8Array, value: T): this {
|
|
23
|
+
const keyHash: Uint8Array = this.getKeyHash(key);
|
|
24
|
+
Blockchain.setStorageAt(keyHash, this.from(value));
|
|
25
|
+
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public get(key: Uint8Array): T {
|
|
30
|
+
const keyHash: Uint8Array = this.getKeyHash(key);
|
|
31
|
+
|
|
32
|
+
return this.toValue(Blockchain.getStorageAt(keyHash));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public has(key: Uint8Array): bool {
|
|
36
|
+
const mergedKey: Uint8Array = this.getKeyHash(key);
|
|
37
|
+
|
|
38
|
+
return Blockchain.hasStorageAt(mergedKey);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@unsafe
|
|
42
|
+
public delete(_key: Uint8Array): bool {
|
|
43
|
+
throw new Error('Method not implemented.');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@unsafe
|
|
47
|
+
public clear(): void {
|
|
48
|
+
throw new Error('Clear method not implemented.');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Converts raw bytes from storage into type T.
|
|
53
|
+
*/
|
|
54
|
+
private toValue(value: Uint8Array): T {
|
|
55
|
+
// Check T's compile-time type ID
|
|
56
|
+
if (idof<T>() == idof<u256>()) {
|
|
57
|
+
// We know T is u256
|
|
58
|
+
return changetype<T>(u256.fromUint8ArrayBE(value));
|
|
59
|
+
} else if (idof<T>() == idof<Uint8Array>()) {
|
|
60
|
+
// We know T is Uint8Array
|
|
61
|
+
return changetype<T>(value);
|
|
62
|
+
} else if (idof<T>() == idof<Address>()) {
|
|
63
|
+
// We know T is Address
|
|
64
|
+
return changetype<T>(value);
|
|
65
|
+
} else if (isInteger<T>()) {
|
|
66
|
+
// For a simple integer, just pull out the first byte
|
|
67
|
+
return value[0] as T;
|
|
68
|
+
} else if (isString<T>()) {
|
|
69
|
+
// T is a string
|
|
70
|
+
return changetype<T>(String.UTF8.decode(value.buffer));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
throw new Error('Unsupported type');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Converts type T into raw bytes for storage.
|
|
78
|
+
*/
|
|
79
|
+
private from(value: T): Uint8Array {
|
|
80
|
+
if (idof<T>() == idof<u256>()) {
|
|
81
|
+
// Cast T to u256, then convert to bytes
|
|
82
|
+
return changetype<u256>(value).toUint8Array(true);
|
|
83
|
+
} else if (idof<T>() == idof<Uint8Array>()) {
|
|
84
|
+
// Just return it
|
|
85
|
+
return changetype<Uint8Array>(value);
|
|
86
|
+
} else if (idof<T>() == idof<Address>()) {
|
|
87
|
+
// Address is already bytes
|
|
88
|
+
return changetype<Uint8Array>(value);
|
|
89
|
+
} else if (isInteger<T>()) {
|
|
90
|
+
const writer = new BytesWriter(sizeof<T>());
|
|
91
|
+
writer.write<T>(value);
|
|
92
|
+
return writer.getBuffer();
|
|
93
|
+
} else if (isString<T>()) {
|
|
94
|
+
const str = changetype<string>(value);
|
|
95
|
+
return Uint8Array.wrap(String.UTF8.encode(str));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
throw new Error('Unsupported type');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private getKeyHash(key: Uint8Array): Uint8Array {
|
|
102
|
+
const writer: BytesWriter = new BytesWriter(key.byteLength + this.parentKey.byteLength);
|
|
103
|
+
|
|
104
|
+
writer.writeBytes(this.parentKey);
|
|
105
|
+
writer.writeBytes(key);
|
|
106
|
+
|
|
107
|
+
return this.encodePointer(writer);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private encodePointer(writer: BytesWriter): Uint8Array {
|
|
111
|
+
return encodePointerUnknownLength(this.pointer, writer.getBuffer());
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Blockchain } from '../env';
|
|
2
|
+
import { Potential } from '../lang/Definitions';
|
|
3
|
+
import { bigEndianAdd } from '../math/bytes';
|
|
4
|
+
import { Plugin } from '../plugins/Plugin';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A unique key in storage that holds the next free "offset" as a 256-bit counter.
|
|
8
|
+
* You can change this to any 32-byte constant you like.
|
|
9
|
+
*/
|
|
10
|
+
const ALLOCATOR_KEY = new Uint8Array(32);
|
|
11
|
+
for (let i = 0; i < 32; i++) {
|
|
12
|
+
ALLOCATOR_KEY[i] = 0xff;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* PointerManager: ensures we never collide while allocating variable-length data.
|
|
17
|
+
* - We store a global "offset" in ALLOCATOR_KEY (as a big-endian u256).
|
|
18
|
+
* - Each time we allocate N slots (N * 32 bytes), we do:
|
|
19
|
+
* oldOffset = currentGlobalOffset
|
|
20
|
+
* newOffset = oldOffset + N
|
|
21
|
+
* store newOffset back
|
|
22
|
+
* return oldOffset as the base pointer
|
|
23
|
+
*/
|
|
24
|
+
class _PointerManager extends Plugin {
|
|
25
|
+
private _cachedOffset: Potential<Uint8Array> = null;
|
|
26
|
+
|
|
27
|
+
private get cachedOffset(): Uint8Array {
|
|
28
|
+
if (!this._cachedOffset) {
|
|
29
|
+
this._cachedOffset = Blockchain.getStorageAt(ALLOCATOR_KEY);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return this._cachedOffset as Uint8Array;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Allocates `numSlots` (each 32 bytes). Returns a 32-byte pointer (u256 in big-endian).
|
|
37
|
+
*
|
|
38
|
+
* Each slot is conceptually:
|
|
39
|
+
* slot0 = pointer + 0
|
|
40
|
+
* slot1 = pointer + 1
|
|
41
|
+
* ...
|
|
42
|
+
* and so forth in big-endian arithmetic.
|
|
43
|
+
*/
|
|
44
|
+
public allocateSlots(numSlots: u64): Uint8Array {
|
|
45
|
+
this._cachedOffset = bigEndianAdd(this.cachedOffset, numSlots);
|
|
46
|
+
|
|
47
|
+
const val = this.cachedOffset;
|
|
48
|
+
Blockchain.setStorageAt(ALLOCATOR_KEY, val);
|
|
49
|
+
|
|
50
|
+
return val;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const PointerManager = new _PointerManager();
|
|
55
|
+
Blockchain.registerPlugin(PointerManager);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ICodec } from '../interfaces/ICodec';
|
|
2
|
+
import { Address } from '../../types/Address';
|
|
3
|
+
|
|
4
|
+
class _AddressCodec implements ICodec<Address> {
|
|
5
|
+
public encode(value: Address): Uint8Array {
|
|
6
|
+
return value;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
public decode(buffer: Uint8Array): Address {
|
|
10
|
+
if (buffer.length == 0) {
|
|
11
|
+
return Address.zero();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return Address.fromUint8Array(buffer);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const idOfAddressCodec = idof<_AddressCodec>();
|
|
19
|
+
export const AddressCodec = new _AddressCodec();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ICodec } from '../interfaces/ICodec';
|
|
2
|
+
|
|
3
|
+
class _BooleanCodec implements ICodec<bool> {
|
|
4
|
+
public encode(value: bool): Uint8Array {
|
|
5
|
+
const out = new Uint8Array(1);
|
|
6
|
+
out[0] = value ? 1 : 0;
|
|
7
|
+
return out;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
public decode(buffer: Uint8Array): bool {
|
|
11
|
+
if (buffer.length == 0) return false;
|
|
12
|
+
return buffer[0] == 1;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const idOfBoolCodec = idof<_BooleanCodec>();
|
|
17
|
+
export const BooleanCodec = new _BooleanCodec();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { i128, u128, u256 } from '@btc-vision/as-bignum/assembly';
|
|
2
|
+
import { Uint8Array } from 'typedarray';
|
|
3
|
+
import { Address } from '../../types/Address';
|
|
4
|
+
|
|
5
|
+
export const idOfU256 = idof<u256>();
|
|
6
|
+
export const idOfU128 = idof<u128>();
|
|
7
|
+
export const idOfI128 = idof<i128>();
|
|
8
|
+
export const idOfUint8Array = idof<Uint8Array>();
|
|
9
|
+
export const idOfString = idof<string>();
|
|
10
|
+
export const idOfAddress = idof<Address>();
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { i128, u128, u256 } from '@btc-vision/as-bignum/assembly';
|
|
2
|
+
import { ICodec } from '../interfaces/ICodec';
|
|
3
|
+
import { BytesWriter } from '../../buffer/BytesWriter';
|
|
4
|
+
import { BytesReader } from '../../buffer/BytesReader';
|
|
5
|
+
import { idOfI128, idOfU128, idOfU256 } from './Ids';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A generic NumericCodec<T> that handles:
|
|
9
|
+
* - `u256` from @btc-vision/as-bignum (big-endian)
|
|
10
|
+
* - Any built-in integer type (i32, u32, i64, etc.) also stored big-endian
|
|
11
|
+
*/
|
|
12
|
+
export class NumericCodec<T> implements ICodec<T> {
|
|
13
|
+
public encode(value: T): Uint8Array {
|
|
14
|
+
const id = idof<T>();
|
|
15
|
+
switch (id) {
|
|
16
|
+
case idOfU256: {
|
|
17
|
+
// T is `u256`
|
|
18
|
+
const val = changetype<u256>(value);
|
|
19
|
+
return val.toUint8Array(true); // big-endian
|
|
20
|
+
}
|
|
21
|
+
case idOfU128: {
|
|
22
|
+
// T is `u128`
|
|
23
|
+
const val = changetype<u128>(value);
|
|
24
|
+
return val.toUint8Array(true); // big-endian
|
|
25
|
+
}
|
|
26
|
+
case idOfI128: {
|
|
27
|
+
// T is `i128`
|
|
28
|
+
const val = changetype<i128>(value);
|
|
29
|
+
return val.toUint8Array(true); // big-endian
|
|
30
|
+
}
|
|
31
|
+
default: {
|
|
32
|
+
const writer = new BytesWriter(sizeof<T>());
|
|
33
|
+
writer.write<T>(value);
|
|
34
|
+
|
|
35
|
+
return writer.getBuffer();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public decode(buffer: Uint8Array): T {
|
|
41
|
+
const id = idof<T>();
|
|
42
|
+
switch (id) {
|
|
43
|
+
case idOfU256:
|
|
44
|
+
// T is `u256`
|
|
45
|
+
return changetype<T>(u256.fromBytes(buffer, true));
|
|
46
|
+
case idOfU128:
|
|
47
|
+
// T is `u128`
|
|
48
|
+
return changetype<T>(u128.fromBytes(buffer, true));
|
|
49
|
+
case idOfI128:
|
|
50
|
+
// T is `i128`
|
|
51
|
+
return changetype<T>(i128.fromBytes(buffer, true));
|
|
52
|
+
default: {
|
|
53
|
+
const writer = new BytesReader(buffer);
|
|
54
|
+
return writer.read<T>();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|