@btc-vision/btc-runtime 1.9.15 → 1.10.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/package.json +2 -2
- package/runtime/buffer/BytesReader.ts +1 -1
- package/runtime/constants/Exports.ts +11 -11
- package/runtime/contracts/OP20.ts +335 -39
- package/runtime/contracts/OP721.ts +15 -9
- package/runtime/env/BlockchainEnvironment.ts +202 -5
- package/runtime/env/classes/Transaction.ts +36 -3
- package/runtime/env/consensus/ConsensusRules.ts +228 -0
- package/runtime/env/consensus/MLDSAMetadata.ts +181 -0
- package/runtime/env/consensus/Signatures.ts +7 -0
- package/runtime/env/global.ts +111 -7
- package/runtime/exports/index.ts +2 -1
- package/runtime/index.ts +7 -11
- package/runtime/interfaces/as.ts +5 -0
- package/runtime/shared-libraries/TransferHelper.ts +4 -17
- package/runtime/types/Address.ts +230 -46
- package/runtime/types/ExtendedAddress.ts +426 -0
- package/runtime/nested/PointerManager.ts +0 -55
- package/runtime/nested/codecs/AddressCodec.ts +0 -19
- package/runtime/nested/codecs/BooleanCodec.ts +0 -17
- package/runtime/nested/codecs/Ids.ts +0 -10
- package/runtime/nested/codecs/NumericCodec.ts +0 -58
- package/runtime/nested/codecs/StringCodec.ts +0 -24
- package/runtime/nested/codecs/U256Codec.ts +0 -20
- package/runtime/nested/codecs/VariableBytesCodec.ts +0 -134
- package/runtime/nested/interfaces/ICodec.ts +0 -12
- package/runtime/nested/storage/StorageMap.ts +0 -207
- package/runtime/nested/storage/StorageSet.ts +0 -60
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import { ICodec } from '../interfaces/ICodec';
|
|
2
|
-
import { PointerManager } from '../PointerManager';
|
|
3
|
-
import { Blockchain } from '../../env';
|
|
4
|
-
import { bigEndianAdd } from '../../math/bytes';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* A generic codec to store an arbitrary-length byte array ("payload") in chunked 32-byte slots.
|
|
8
|
-
* Layout:
|
|
9
|
-
* - chunk 0 (pointer+0):
|
|
10
|
-
* first 4 bytes = length (u32 big-endian),
|
|
11
|
-
* next 28 bytes = partial data
|
|
12
|
-
* - chunk i>0 (pointer + i): 32 bytes of subsequent data
|
|
13
|
-
*/
|
|
14
|
-
export class IVariableBytesCodec implements ICodec<Uint8Array> {
|
|
15
|
-
public encode(value: Uint8Array): Uint8Array {
|
|
16
|
-
const length = value.length;
|
|
17
|
-
|
|
18
|
-
// Number of bytes that fit in "the first chunk" = 28 (since 4 bytes used by length)
|
|
19
|
-
const firstChunkDataLen = length < 28 ? length : 28;
|
|
20
|
-
let remaining = length - firstChunkDataLen;
|
|
21
|
-
|
|
22
|
-
// If remaining > 0, each chunk is 32 bytes.
|
|
23
|
-
// So total chunks needed = 1 (for first chunk) + ceil(remaining / 32).
|
|
24
|
-
const additionalChunks: u32 = remaining == 0 ? 0 : (remaining + 32 - 1) / 32;
|
|
25
|
-
const totalChunks: u32 = 1 + additionalChunks;
|
|
26
|
-
|
|
27
|
-
// 1) Allocate `totalChunks` from PointerManager
|
|
28
|
-
const pointerBytes = PointerManager.allocateSlots(totalChunks);
|
|
29
|
-
|
|
30
|
-
// 2) Write chunk 0: length + up to 28 bytes
|
|
31
|
-
const chunk0 = new Uint8Array(32);
|
|
32
|
-
|
|
33
|
-
// store length in big-endian (4 bytes)
|
|
34
|
-
chunk0[0] = <u8>((length >> 24) & 0xff);
|
|
35
|
-
chunk0[1] = <u8>((length >> 16) & 0xff);
|
|
36
|
-
chunk0[2] = <u8>((length >> 8) & 0xff);
|
|
37
|
-
chunk0[3] = <u8>(length & 0xff);
|
|
38
|
-
|
|
39
|
-
for (let i = 0; i < firstChunkDataLen; i++) {
|
|
40
|
-
chunk0[4 + i] = value[i];
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// store chunk0
|
|
44
|
-
Blockchain.setStorageAt(pointerBytes, chunk0);
|
|
45
|
-
|
|
46
|
-
// 3) Write subsequent chunks
|
|
47
|
-
let offset: u32 = firstChunkDataLen;
|
|
48
|
-
for (let i: u64 = 1; i < u64(totalChunks); i++) {
|
|
49
|
-
const chunk = new Uint8Array(32);
|
|
50
|
-
const chunkSize: u32 = remaining < 32 ? remaining : 32;
|
|
51
|
-
|
|
52
|
-
for (let j: u32 = 0; j < chunkSize; j++) {
|
|
53
|
-
chunk[j] = value[offset + j];
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
offset += chunkSize;
|
|
57
|
-
remaining -= chunkSize;
|
|
58
|
-
|
|
59
|
-
// compute pointer + i in big-endian
|
|
60
|
-
const chunkPointer = bigEndianAdd(pointerBytes, i);
|
|
61
|
-
Blockchain.setStorageAt(chunkPointer, chunk);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// 4) Return the pointer as the "encoded" data (32 bytes).
|
|
65
|
-
return pointerBytes;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
public decode(buffer: Uint8Array): Uint8Array {
|
|
69
|
-
// If buffer is 0 or all zero => means no data
|
|
70
|
-
if (buffer.length == 0 || isAllZero(buffer)) {
|
|
71
|
-
return new Uint8Array(0);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const pointer = buffer; // the pointer (32 bytes)
|
|
75
|
-
|
|
76
|
-
// chunk0
|
|
77
|
-
const chunk0 = Blockchain.getStorageAt(pointer);
|
|
78
|
-
if (chunk0.length == 0) {
|
|
79
|
-
// No data stored => empty
|
|
80
|
-
return new Uint8Array(32);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// read length from first 4 bytes
|
|
84
|
-
const b0 = <u32>chunk0[0];
|
|
85
|
-
const b1 = <u32>chunk0[1];
|
|
86
|
-
const b2 = <u32>chunk0[2];
|
|
87
|
-
const b3 = <u32>chunk0[3];
|
|
88
|
-
const length = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
|
|
89
|
-
|
|
90
|
-
if (length == 0) {
|
|
91
|
-
return new Uint8Array(0);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// read the data
|
|
95
|
-
const out = new Uint8Array(length);
|
|
96
|
-
const firstChunkLen = length < 28 ? length : 28;
|
|
97
|
-
|
|
98
|
-
// copy from chunk0
|
|
99
|
-
for (let i: u32 = 0; i < firstChunkLen; i++) {
|
|
100
|
-
out[i] = chunk0[4 + i];
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
let offset = firstChunkLen;
|
|
104
|
-
let remaining = length - firstChunkLen;
|
|
105
|
-
|
|
106
|
-
// read subsequent chunks
|
|
107
|
-
let chunkIndex: u64 = 1;
|
|
108
|
-
while (remaining > 0) {
|
|
109
|
-
const chunkPointer = bigEndianAdd(pointer, chunkIndex);
|
|
110
|
-
const chunkData = Blockchain.getStorageAt(chunkPointer);
|
|
111
|
-
const chunkSize: u32 = remaining < 32 ? remaining : 32;
|
|
112
|
-
|
|
113
|
-
for (let j: u32 = 0; j < chunkSize; j++) {
|
|
114
|
-
out[offset + j] = chunkData[j];
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
offset += chunkSize;
|
|
118
|
-
remaining -= chunkSize;
|
|
119
|
-
chunkIndex++;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return out;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function isAllZero(arr: Uint8Array): bool {
|
|
127
|
-
for (let i = 0; i < arr.length; i++) {
|
|
128
|
-
if (arr[i] != 0) return false;
|
|
129
|
-
}
|
|
130
|
-
return true;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export const idOfVariableBytes = idof<IVariableBytesCodec>();
|
|
134
|
-
export const VariableBytesCodec = new IVariableBytesCodec();
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export interface ICodec<T> {
|
|
2
|
-
/**
|
|
3
|
-
* Encode in-memory `value` into a buffer that will be stored in blockchain storage.
|
|
4
|
-
* Possibly a single 32-byte chunk or a pointer if more is needed.
|
|
5
|
-
*/
|
|
6
|
-
encode(value: T): Uint8Array;
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Decode a buffer from storage into an in-memory `T`.
|
|
10
|
-
*/
|
|
11
|
-
decode(buffer: Uint8Array): T;
|
|
12
|
-
}
|
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
import { Blockchain } from '../../env';
|
|
2
|
-
import { BytesWriter } from '../../buffer/BytesWriter';
|
|
3
|
-
import { BytesReader } from '../../buffer/BytesReader';
|
|
4
|
-
import { encodePointerUnknownLength } from '../../math/abi';
|
|
5
|
-
|
|
6
|
-
import { i128, u128, u256 } from '@btc-vision/as-bignum/assembly';
|
|
7
|
-
import { idOfAddress, idOfI128, idOfString, idOfU128, idOfU256, idOfUint8Array, } from '../codecs/Ids';
|
|
8
|
-
|
|
9
|
-
import { AddressCodec } from '../codecs/AddressCodec';
|
|
10
|
-
import { BooleanCodec } from '../codecs/BooleanCodec';
|
|
11
|
-
import { StringCodec } from '../codecs/StringCodec';
|
|
12
|
-
import { VariableBytesCodec } from '../codecs/VariableBytesCodec';
|
|
13
|
-
import { U256Codec } from '../codecs/U256Codec';
|
|
14
|
-
|
|
15
|
-
import { Address } from '../../types/Address';
|
|
16
|
-
import { Revert } from '../../types/Revert';
|
|
17
|
-
import { EMPTY_BUFFER } from '../../math/bytes';
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* A reflection-based StorageMap<K, V>.
|
|
21
|
-
* - `pointer` => a u16 "namespace"
|
|
22
|
-
* - `set(key, value)` => store bytes
|
|
23
|
-
* - `get(key)` => decode bytes
|
|
24
|
-
*
|
|
25
|
-
* This version uses a chain of `if/else` for each possible type T,
|
|
26
|
-
* so the compiler doesn't complain about mismatched returns.
|
|
27
|
-
*/
|
|
28
|
-
@final
|
|
29
|
-
export class StorageMap<K, V> {
|
|
30
|
-
private readonly pointer: u16;
|
|
31
|
-
|
|
32
|
-
constructor(pointer: u16) {
|
|
33
|
-
this.pointer = pointer;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
public set(key: K, value: V): this {
|
|
37
|
-
const storageKey = this.getStorageKey(key);
|
|
38
|
-
this.storeValue<V>(storageKey, value);
|
|
39
|
-
return this;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
public get(key: K): V {
|
|
43
|
-
const storageKey = this.getStorageKey(key);
|
|
44
|
-
return this.decodeValue<V>(storageKey);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
public has(key: K): bool {
|
|
48
|
-
const storageKey = this.getStorageKey(key);
|
|
49
|
-
return Blockchain.hasStorageAt(storageKey);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
@unsafe
|
|
53
|
-
public delete(key: K): bool {
|
|
54
|
-
const storageKey = this.getStorageKey(key);
|
|
55
|
-
if (!Blockchain.hasStorageAt(storageKey)) {
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
Blockchain.setStorageAt(storageKey, EMPTY_BUFFER);
|
|
60
|
-
return true;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
@unsafe
|
|
64
|
-
public clear(): void {
|
|
65
|
-
// Not implemented: we'd need key-tracking to remove them all
|
|
66
|
-
throw new Revert('clear() not implemented; no key-tracking logic here.');
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
private getStorageKey(k: K): Uint8Array {
|
|
70
|
-
const keyBytes = this.encodeValue<K>(k);
|
|
71
|
-
|
|
72
|
-
return encodePointerUnknownLength(this.pointer, keyBytes);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Retrieve the value of type P from the given storage pointer (32 bytes).
|
|
77
|
-
* If it's a variable-length type, we decode from pointer-based approach
|
|
78
|
-
* (like VariableBytesCodec or StringCodec).
|
|
79
|
-
* Otherwise, we get the raw from storage and decode.
|
|
80
|
-
*/
|
|
81
|
-
private decodeValue<P>(pointer: Uint8Array): P {
|
|
82
|
-
// For some types like `Uint8Array` or `string`, we might want
|
|
83
|
-
// to treat the pointer as an allocated "variable" location.
|
|
84
|
-
const typeId = idof<P>();
|
|
85
|
-
|
|
86
|
-
// For variable-length things:
|
|
87
|
-
if (typeId == idOfUint8Array) {
|
|
88
|
-
// decode variable bytes from pointer
|
|
89
|
-
const arr = VariableBytesCodec.decode(pointer);
|
|
90
|
-
return changetype<P>(arr);
|
|
91
|
-
}
|
|
92
|
-
if (typeId == idOfString) {
|
|
93
|
-
const str = StringCodec.decode(pointer);
|
|
94
|
-
return changetype<P>(str);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// For everything else, we read raw from storage
|
|
98
|
-
const raw = Blockchain.getStorageAt(pointer);
|
|
99
|
-
|
|
100
|
-
return this.decodeBytesAsType<P>(raw);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Store a value of type P at the given storage key.
|
|
105
|
-
* - Some types (uint8array, string) -> chunked (pointer-based).
|
|
106
|
-
* - Others -> direct bytes in that slot.
|
|
107
|
-
*/
|
|
108
|
-
private storeValue<P>(storageKey: Uint8Array, value: P): void {
|
|
109
|
-
const raw = this.encodeValue<P>(value);
|
|
110
|
-
Blockchain.setStorageAt(storageKey, raw);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
private decodeBytesAsType<P>(raw: Uint8Array): P {
|
|
114
|
-
// isInteger => built-in numeric types (i32, u32, etc.)
|
|
115
|
-
if (isInteger<P>()) {
|
|
116
|
-
if (raw.length < <i32>sizeof<P>()) {
|
|
117
|
-
return changetype<P>(0);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const reader = new BytesReader(raw);
|
|
121
|
-
return reader.read<P>();
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// isBoolean => decode with BooleanCodec
|
|
125
|
-
if (isBoolean<P>()) {
|
|
126
|
-
const boolVal = BooleanCodec.decode(raw);
|
|
127
|
-
return changetype<P>(boolVal);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const typeId = idof<P>();
|
|
131
|
-
|
|
132
|
-
// u256
|
|
133
|
-
if (typeId == idOfU256) {
|
|
134
|
-
const decoded = U256Codec.decode(raw);
|
|
135
|
-
return changetype<P>(decoded);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// i128
|
|
139
|
-
if (typeId == idOfI128) {
|
|
140
|
-
const val = i128.fromUint8ArrayBE(raw);
|
|
141
|
-
return changetype<P>(val);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// u128
|
|
145
|
-
if (typeId == idOfU128) {
|
|
146
|
-
const val = u128.fromUint8ArrayBE(raw);
|
|
147
|
-
return changetype<P>(val);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Address
|
|
151
|
-
if (typeId == idOfAddress) {
|
|
152
|
-
const addr = AddressCodec.decode(raw);
|
|
153
|
-
return changetype<P>(addr);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
throw new Revert(`Unsupported type ${typeId}`);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
private encodeValue<P>(value: P): Uint8Array {
|
|
160
|
-
// built-in integer => write with BytesWriter
|
|
161
|
-
if (isInteger<P>()) {
|
|
162
|
-
const writer = new BytesWriter(sizeof<P>());
|
|
163
|
-
writer.write<P>(value);
|
|
164
|
-
return writer.getBuffer();
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// bool => BooleanCodec
|
|
168
|
-
if (isBoolean<P>()) {
|
|
169
|
-
return BooleanCodec.encode(changetype<bool>(value));
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Check reflection for bigger types
|
|
173
|
-
if (value instanceof Uint8Array) {
|
|
174
|
-
return VariableBytesCodec.encode(value);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (isString<P>()) {
|
|
178
|
-
const strVal = changetype<string>(value);
|
|
179
|
-
return StringCodec.encode(strVal);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (value instanceof u256) {
|
|
183
|
-
return U256Codec.encode(changetype<u256>(value));
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (value instanceof u128) {
|
|
187
|
-
// store big-endian
|
|
188
|
-
const uval = changetype<u128>(value);
|
|
189
|
-
return uval.toUint8Array(true);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
if (value instanceof i128) {
|
|
193
|
-
const ival = changetype<i128>(value);
|
|
194
|
-
return ival.toUint8Array(true);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (value instanceof Address) {
|
|
198
|
-
return AddressCodec.encode(changetype<Address>(value));
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// If nested map => handle in a custom branch (not shown here):
|
|
202
|
-
// if (value instanceof StorageMap<...>) { ... }
|
|
203
|
-
// TODO
|
|
204
|
-
|
|
205
|
-
throw new Revert('encodeValue: Unsupported type');
|
|
206
|
-
}
|
|
207
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { ICodec } from '../interfaces/ICodec';
|
|
2
|
-
import { BytesWriter } from '../../buffer/BytesWriter';
|
|
3
|
-
import { encodePointerUnknownLength } from '../../math/abi';
|
|
4
|
-
import { Blockchain } from '../../env';
|
|
5
|
-
import { Revert } from '../../types/Revert';
|
|
6
|
-
|
|
7
|
-
const SLOT_BYTES: i32 = 32;
|
|
8
|
-
const PRESENCE_BYTE: u8 = 1;
|
|
9
|
-
|
|
10
|
-
export class StorageSet<T> {
|
|
11
|
-
private readonly pointer: u16;
|
|
12
|
-
private readonly parentKey: Uint8Array;
|
|
13
|
-
|
|
14
|
-
private elementCodec: ICodec<T>;
|
|
15
|
-
|
|
16
|
-
constructor(pointer: u16, parentKey: Uint8Array, elementCodec: ICodec<T>) {
|
|
17
|
-
this.pointer = pointer;
|
|
18
|
-
this.parentKey = parentKey;
|
|
19
|
-
this.elementCodec = elementCodec;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
public add(value: T): void {
|
|
23
|
-
const storageKey = this.getStorageKey(value);
|
|
24
|
-
|
|
25
|
-
// A 1-byte array with a single 0x01 is enough to mark presence
|
|
26
|
-
const flag = new Uint8Array(SLOT_BYTES);
|
|
27
|
-
flag[31] = PRESENCE_BYTE;
|
|
28
|
-
|
|
29
|
-
Blockchain.setStorageAt(storageKey, flag);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
public has(value: T): bool {
|
|
33
|
-
const storageKey = this.getStorageKey(value);
|
|
34
|
-
return Blockchain.hasStorageAt(storageKey);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
public delete(value: T): bool {
|
|
38
|
-
const storageKey = this.getStorageKey(value);
|
|
39
|
-
if (!Blockchain.hasStorageAt(storageKey)) {
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
Blockchain.setStorageAt(storageKey, new Uint8Array(SLOT_BYTES));
|
|
44
|
-
return true;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
@unsafe
|
|
48
|
-
public clear(): void {
|
|
49
|
-
throw new Revert('clear() not implemented.');
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
private getStorageKey(value: T): Uint8Array {
|
|
53
|
-
const encoded = this.elementCodec.encode(value);
|
|
54
|
-
const writer = new BytesWriter(this.parentKey.length + encoded.length);
|
|
55
|
-
writer.writeBytes(this.parentKey);
|
|
56
|
-
writer.writeBytes(encoded);
|
|
57
|
-
|
|
58
|
-
return encodePointerUnknownLength(this.pointer, writer.getBuffer());
|
|
59
|
-
}
|
|
60
|
-
}
|