@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.
@@ -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
- }