@btc-vision/btc-runtime 1.4.6 → 1.5.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.
Files changed (59) hide show
  1. package/package.json +44 -53
  2. package/runtime/abort/abort.ts +25 -0
  3. package/runtime/buffer/BytesReader.ts +171 -140
  4. package/runtime/buffer/BytesWriter.ts +120 -152
  5. package/runtime/contracts/DeployableOP_20.ts +29 -15
  6. package/runtime/contracts/OP_NET.ts +1 -1
  7. package/runtime/env/BlockchainEnvironment.ts +79 -137
  8. package/runtime/env/classes/Block.ts +4 -8
  9. package/runtime/env/classes/Transaction.ts +14 -7
  10. package/runtime/env/classes/UTXO.ts +4 -2
  11. package/runtime/env/global.ts +49 -20
  12. package/runtime/events/predefined/MintEvent.ts +1 -1
  13. package/runtime/exports/index.ts +29 -8
  14. package/runtime/generic/AddressMap.ts +7 -5
  15. package/runtime/generic/Map.ts +32 -2
  16. package/runtime/generic/MapU256.ts +7 -5
  17. package/runtime/generic/MapUint8Array.ts +93 -0
  18. package/runtime/index.ts +4 -12
  19. package/runtime/math/abi.ts +71 -11
  20. package/runtime/math/bytes.ts +177 -41
  21. package/runtime/memory/AddressMemoryMap.ts +22 -19
  22. package/runtime/memory/FastUint8Array.ts +122 -0
  23. package/runtime/memory/KeyMerger.ts +25 -23
  24. package/runtime/memory/MultiAddressMemoryMap.ts +11 -8
  25. package/runtime/memory/MultiStringMemoryMap.ts +8 -5
  26. package/runtime/memory/StringMemoryMap.ts +15 -15
  27. package/runtime/memory/Uint8ArrayMerger.ts +22 -15
  28. package/runtime/storage/Serializable.ts +19 -20
  29. package/runtime/storage/StoredAddress.ts +16 -15
  30. package/runtime/storage/StoredBoolean.ts +26 -21
  31. package/runtime/storage/StoredString.ts +158 -102
  32. package/runtime/storage/StoredU256.ts +25 -28
  33. package/runtime/storage/StoredU64.ts +23 -35
  34. package/runtime/storage/arrays/StoredAddressArray.ts +88 -179
  35. package/runtime/storage/arrays/StoredBooleanArray.ts +150 -272
  36. package/runtime/storage/arrays/StoredPackedArray.ts +313 -0
  37. package/runtime/storage/arrays/StoredU128Array.ts +38 -373
  38. package/runtime/storage/arrays/StoredU16Array.ts +34 -418
  39. package/runtime/storage/arrays/StoredU256Array.ts +21 -346
  40. package/runtime/storage/arrays/StoredU32Array.ts +37 -438
  41. package/runtime/storage/arrays/StoredU64Array.ts +66 -0
  42. package/runtime/storage/arrays/StoredU8Array.ts +29 -451
  43. package/runtime/types/Address.ts +72 -5
  44. package/runtime/types/index.ts +1 -4
  45. package/runtime/utils/encodings.ts +5 -6
  46. package/runtime/utils/hex.ts +1 -1
  47. package/runtime/interfaces/DeployContractResponse.ts +0 -12
  48. package/runtime/math/cyrb53.ts +0 -48
  49. package/runtime/math/sha256.ts +0 -12
  50. package/runtime/memory/MemorySlot.ts +0 -1
  51. package/runtime/memory/MemorySlotPointer.ts +0 -3
  52. package/runtime/storage/utils/StorageBacked.ts +0 -5
  53. package/runtime/storage/utils/StorageLayout.ts +0 -7
  54. package/runtime/storage/utils/StorageSlot.ts +0 -106
  55. package/runtime/storage/utils/StorageStruct.ts +0 -23
  56. package/runtime/storage/utils/StorageValue.ts +0 -36
  57. package/runtime/tests/assert.ts +0 -11
  58. package/runtime/tests/env.ts +0 -7
  59. package/runtime/tests/tests.ts +0 -28
@@ -0,0 +1,313 @@
1
+ import {
2
+ encodeBasePointer,
3
+ GET_EMPTY_BUFFER,
4
+ readLengthAndStartIndex,
5
+ writeLengthAndStartIndex,
6
+ } from '../../math/bytes';
7
+ import { Blockchain } from '../../env';
8
+ import { Revert } from '../../types/Revert';
9
+
10
+ /**
11
+ * Abstract base class for an array of T values that are packed
12
+ * in 32-byte (Uint8Array) "slots" in storage.
13
+ *
14
+ * - Tracks length + startIndex in the first 16 bytes of `lengthPointer`.
15
+ * - Maps each global index to (slotIndex, subIndex).
16
+ * - Child classes define how many T items fit per 32-byte slot
17
+ * and how to pack/unpack them.
18
+ */
19
+ export abstract class StoredPackedArray<T> {
20
+ /** 32-byte base pointer (used to derive storage keys). */
21
+ protected readonly basePointer: Uint8Array;
22
+
23
+ /** Same pointer used to read/write length + startIndex. */
24
+ protected readonly lengthPointer: Uint8Array;
25
+
26
+ /** Internal length of the array. */
27
+ protected _length: u64 = 0;
28
+
29
+ /** Optional "start index" if needed by your logic. */
30
+ protected _startIndex: u64 = 0;
31
+
32
+ /** Whether length or startIndex changed. */
33
+ protected _isChangedLength: bool = false;
34
+ protected _isChangedStartIndex: bool = false;
35
+
36
+ /**
37
+ * A map from slotIndex => the 32-byte slot data in memory.
38
+ * Child classes will parse that 32 bytes into an array of T, or vice versa.
39
+ */
40
+ protected _slots: Map<u64, Uint8Array> = new Map();
41
+
42
+ /** Track which slotIndexes are changed and need saving. */
43
+ protected _isChanged: Set<u64> = new Set();
44
+
45
+ /**
46
+ * Maximum length to prevent unbounded usage.
47
+ * Adjust to fit your constraints (e.g. `u64.MAX_VALUE`).
48
+ */
49
+ protected readonly MAX_LENGTH: u64 = <u64>(u32.MAX_VALUE - 1);
50
+
51
+ protected constructor(public pointer: u16, public subPointer: Uint8Array) {
52
+ assert(subPointer.length <= 30, `You must pass a 30 bytes sub-pointer. (Array, got ${subPointer.length})`);
53
+
54
+ const basePointer = encodeBasePointer(pointer, subPointer);
55
+ this.lengthPointer = Uint8Array.wrap(basePointer.buffer);
56
+ this.basePointer = basePointer;
57
+
58
+ const storedLenStart = Blockchain.getStorageAt(basePointer);
59
+ const data = readLengthAndStartIndex(storedLenStart);
60
+
61
+ this._length = data[0];
62
+ this._startIndex = data[1];
63
+ }
64
+
65
+ public get(index: u64): T {
66
+ if (index > this.MAX_LENGTH) {
67
+ throw new Revert('get: index exceeds MAX_LENGTH');
68
+ }
69
+
70
+ const cap = this.getSlotCapacity();
71
+ const slotIndex = index / cap;
72
+ const subIndex = <u32>(index % cap);
73
+
74
+ // Load the slot if not cached
75
+ const slotData = this.ensureSlot(slotIndex);
76
+
77
+ // Unpack and return the subIndex
78
+ const arr = this.unpackSlot(slotData);
79
+ return arr[subIndex];
80
+ }
81
+
82
+ public set(index: u64, value: T): void {
83
+ const cap = this.getSlotCapacity();
84
+ const slotIndex = index / cap;
85
+ const subIndex = <u32>(index % cap);
86
+
87
+ let slotData = this.ensureSlot(slotIndex);
88
+ const arr = this.unpackSlot(slotData);
89
+
90
+ if (!this.eq(arr[subIndex], value)) {
91
+ arr[subIndex] = value;
92
+ slotData = this.packSlot(arr);
93
+ this._slots.set(slotIndex, slotData);
94
+ this._isChanged.add(slotIndex);
95
+ }
96
+ }
97
+
98
+ public push(value: T): void {
99
+ if (this._length >= this.MAX_LENGTH) {
100
+ throw new Revert('push: array has reached MAX_LENGTH');
101
+ }
102
+
103
+ const newIndex = this._length;
104
+ const cap = this.getSlotCapacity();
105
+ const slotIndex = newIndex / cap;
106
+ const subIndex = <u32>(newIndex % cap);
107
+
108
+ let slotData = this.ensureSlot(slotIndex);
109
+ const arr = this.unpackSlot(slotData);
110
+
111
+ if (!this.eq(arr[subIndex], value)) {
112
+ arr[subIndex] = value;
113
+ slotData = this.packSlot(arr);
114
+ this._slots.set(slotIndex, slotData);
115
+ this._isChanged.add(slotIndex);
116
+ }
117
+
118
+ this._length += 1;
119
+ this._isChangedLength = true;
120
+ }
121
+
122
+ /**
123
+ * "Delete" by zeroing out the element at `index`,
124
+ * but does not reduce the length.
125
+ */
126
+ public delete(index: u64): void {
127
+ const cap = this.getSlotCapacity();
128
+ const slotIndex = index / cap;
129
+ const subIndex = <u32>(index % cap);
130
+
131
+ let slotData = this.ensureSlot(slotIndex);
132
+ const arr = this.unpackSlot(slotData);
133
+
134
+ const zeroVal = this.zeroValue();
135
+ if (!this.eq(arr[subIndex], zeroVal)) {
136
+ arr[subIndex] = zeroVal;
137
+ slotData = this.packSlot(arr);
138
+ this._slots.set(slotIndex, slotData);
139
+ this._isChanged.add(slotIndex);
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Remove the last element by zeroing it and decrementing length by 1.
145
+ */
146
+ public deleteLast(): void {
147
+ if (this._length == 0) {
148
+ throw new Revert('deleteLast: array is empty');
149
+ }
150
+
151
+ const lastIndex = this._length - 1;
152
+ this.delete(lastIndex);
153
+
154
+ this._length -= 1;
155
+ this._isChangedLength = true;
156
+ }
157
+
158
+ public setMultiple(startIndex: u64, values: T[]): void {
159
+ const end = startIndex + <u64>values.length;
160
+ if (end > this._length) {
161
+ throw new Revert('setMultiple: out of range');
162
+ }
163
+ for (let i = 0; i < values.length; i++) {
164
+ this.set(startIndex + <u64>i, values[i]);
165
+ }
166
+ }
167
+
168
+ // -----------------------------------------------------------
169
+ // Public Array-Like Methods
170
+ // -----------------------------------------------------------
171
+
172
+ public getAll(startIndex: u64, count: u64): T[] {
173
+ if (count > <u64>u32.MAX_VALUE) {
174
+ throw new Revert('getAll: count too large');
175
+ }
176
+
177
+ const out = new Array<T>(<i32>count);
178
+ for (let i: u64 = 0; i < count; i++) {
179
+ out[<i32>i] = this.get(startIndex + i);
180
+ }
181
+
182
+ return out;
183
+ }
184
+
185
+ public getLength(): u64 {
186
+ return this._length;
187
+ }
188
+
189
+ public startingIndex(): u64 {
190
+ return this._startIndex;
191
+ }
192
+
193
+ public setStartingIndex(index: u64): void {
194
+ this._startIndex = index;
195
+ this._isChangedStartIndex = true;
196
+ }
197
+
198
+ /**
199
+ * Return string "[v0, v1, ...]"
200
+ */
201
+ public toString(): string {
202
+ let s = '[';
203
+ for (let i: u64 = 0; i < this._length; i++) {
204
+ s += `${this.get(i)}`;
205
+ if (i < this._length - 1) {
206
+ s += ', ';
207
+ }
208
+ }
209
+ s += ']';
210
+ return s;
211
+ }
212
+
213
+ public save(): void {
214
+ const changed = this._isChanged.values();
215
+ for (let i = 0; i < changed.length; i++) {
216
+ const slotIndex = changed[i];
217
+ const slotData = this._slots.get(slotIndex);
218
+ if (slotData) {
219
+ const ptr = this.calculateStoragePointer(slotIndex);
220
+ Blockchain.setStorageAt(ptr, slotData);
221
+ }
222
+ }
223
+
224
+ this._isChanged.clear();
225
+
226
+ if (this._isChangedLength || this._isChangedStartIndex) {
227
+ const encoded = writeLengthAndStartIndex(this._length, this._startIndex);
228
+ Blockchain.setStorageAt(this.lengthPointer, encoded);
229
+ this._isChangedLength = false;
230
+ this._isChangedStartIndex = false;
231
+ }
232
+ }
233
+
234
+ public deleteAll(): void {
235
+ const keys = this._slots.keys();
236
+ for (let i = 0; i < keys.length; i++) {
237
+ const slotIndex = keys[i];
238
+ const ptr = this.calculateStoragePointer(slotIndex);
239
+ Blockchain.setStorageAt(ptr, GET_EMPTY_BUFFER()); // 32 bytes of zero
240
+ }
241
+
242
+ // Reset length + startIndex
243
+ Blockchain.setStorageAt(this.lengthPointer, GET_EMPTY_BUFFER());
244
+
245
+ this._length = 0;
246
+ this._startIndex = 0;
247
+ this._isChangedLength = false;
248
+ this._isChangedStartIndex = false;
249
+ this._slots.clear();
250
+ this._isChanged.clear();
251
+ }
252
+
253
+ public reset(): void {
254
+ this._length = 0;
255
+ this._startIndex = 0;
256
+ this._isChangedLength = true;
257
+ this._isChangedStartIndex = true;
258
+
259
+ this._slots.clear();
260
+ this._isChanged.clear();
261
+
262
+ this.save();
263
+ }
264
+
265
+ /** Number of T items that fit in one 32-byte slot. */
266
+ protected abstract getSlotCapacity(): u64;
267
+
268
+ /** Return the "zero" value of type T. */
269
+ protected abstract zeroValue(): T;
270
+
271
+ /** Compare two T values for equality. */
272
+ protected abstract eq(a: T, b: T): bool;
273
+
274
+ // -----------------------------------------------------------
275
+ // Persistence (save, reset, etc.)
276
+ // -----------------------------------------------------------
277
+
278
+ /**
279
+ * Pack an array of T (length = getSlotCapacity()) into a 32-byte buffer.
280
+ */
281
+ protected abstract packSlot(values: T[]): Uint8Array;
282
+
283
+ /**
284
+ * Unpack a 32-byte buffer into an array of T (length = getSlotCapacity()).
285
+ */
286
+ protected abstract unpackSlot(slotData: Uint8Array): T[];
287
+
288
+ /**
289
+ * Calculate storage pointer for each slot index.
290
+ * Typically "basePointer + (slotIndex+1)" in big-endian addition,
291
+ * but you can do your own approach.
292
+ */
293
+ protected abstract calculateStoragePointer(slotIndex: u64): Uint8Array;
294
+
295
+ // -----------------------------------------------------------
296
+ // Internal Slot-Loading Helpers
297
+ // -----------------------------------------------------------
298
+
299
+ /**
300
+ * Ensure that slotIndex is loaded into _slots. If missing, read from storage.
301
+ */
302
+ protected ensureSlot(slotIndex: u64): Uint8Array {
303
+ if (!this._slots.has(slotIndex)) {
304
+ const ptr = this.calculateStoragePointer(slotIndex);
305
+ const data = Blockchain.getStorageAt(ptr);
306
+
307
+ // Must be exactly 32 bytes; if it's empty, you get 32 zero bytes from GET_EMPTY_BUFFER()
308
+ this._slots.set(slotIndex, data);
309
+ }
310
+
311
+ return this._slots.get(slotIndex);
312
+ }
313
+ }