@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
@@ -1,399 +1,64 @@
1
- import { u128, u256 } from '@btc-vision/as-bignum/assembly';
2
- import { Blockchain } from '../../env';
3
- import { BytesWriter } from '../../buffer/BytesWriter';
4
- import { SafeMath } from '../../types/SafeMath';
5
- import { Revert } from '../../types/Revert';
1
+ import { StoredPackedArray } from './StoredPackedArray';
2
+ import { u128 } from '@btc-vision/as-bignum/assembly';
3
+ import { bigEndianAdd } from '../../math/bytes';
6
4
 
7
5
  /**
8
- * @class StoredU128Array
9
- * @description Manages an array of u128 values across multiple u256 storage slots. Each u256 slot holds two u128 values.
6
+ * StoredU128Array
7
+ * - 2 items of type `u128` fit in one 32-byte slot.
8
+ * (Each u128 is 16 bytes.)
10
9
  */
11
10
  @final
12
- export class StoredU128Array {
13
- private readonly baseU256Pointer: u256;
14
- private readonly lengthPointer: u256;
15
-
16
- // Internal cache for storage slots, each holding two u128 values
17
- private _values: Map<u64, u128[]> = new Map(); // Map from slotIndex to array of two u128s
18
- private _isLoaded: Set<u64> = new Set(); // Set of slotIndexes that are loaded
19
- private _isChanged: Set<u64> = new Set(); // Set of slotIndexes that are modified
20
-
21
- // Internal variables for length and startIndex management
22
- private _length: u64 = 0; // Current length of the array
23
- private _startIndex: u64 = 0; // Starting index of the array
24
- private _isChangedLength: bool = false; // Indicates if the length has been modified
25
- private _isChangedStartIndex: bool = false; // Indicates if the startIndex has been modified
26
-
27
- // Define a maximum allowed length to prevent excessive storage usage
28
- private readonly MAX_LENGTH: u64 = u64(u32.MAX_VALUE - 1); // we need to check what happen in overflow situation to be able to set it to u64.MAX_VALUE
29
-
30
- /**
31
- * @constructor
32
- * @param {u16} pointer - The primary pointer identifier.
33
- * @param {Uint8Array} subPointer - The sub-pointer for memory slot addressing.
34
- * @param {u256} defaultValue - The default u256 value if storage is uninitialized.
35
- */
36
- constructor(
37
- public pointer: u16,
38
- public subPointer: Uint8Array,
39
- private defaultValue: u256,
40
- ) {
41
- // Initialize the base u256 pointer using the primary pointer and subPointer
42
- const writer = new BytesWriter(32);
43
- writer.writeU16(pointer);
44
- writer.writeBytes(subPointer);
45
-
46
- // Initialize the length pointer (slot 0) where both length and startIndex are stored
47
- const baseU256Pointer = u256.fromBytes(writer.getBuffer(), true);
48
- const lengthPointer = baseU256Pointer.clone();
49
-
50
- // Load the current length and startIndex from storage
51
- const storedLengthAndStartIndex: u256 = Blockchain.getStorageAt(lengthPointer, u256.Zero);
52
- this.lengthPointer = lengthPointer;
53
- this.baseU256Pointer = baseU256Pointer;
54
-
55
- this._length = storedLengthAndStartIndex.lo1; // Bytes 0-7: length
56
- this._startIndex = storedLengthAndStartIndex.lo2; // Bytes 8-15: startIndex
11
+ export class StoredU128Array extends StoredPackedArray<u128> {
12
+ public constructor(pointer: u16, subPointer: Uint8Array) {
13
+ super(pointer, subPointer);
57
14
  }
58
15
 
59
- /**
60
- * @method get
61
- * @description Retrieves the u128 value at the specified global index.
62
- * @param {u64} index - The global index (0 to ∞) of the u128 value to retrieve.
63
- * @returns {u128} - The u128 value at the specified index.
64
- */
65
- @inline
66
- public get(index: u64): u128 {
67
- if (index >= this._length) {
68
- return u128.Zero;
69
- }
70
-
71
- const slotIndex: u32 = <u32>(index / 2); // Each slot holds two u128s
72
- const subIndex: u8 = <u8>(index % 2); // 0 or 1
73
- this.ensureValues(slotIndex);
74
- const slotValues = this._values.get(slotIndex);
75
- if (slotValues) {
76
- return slotValues[subIndex];
77
- } else {
78
- return u128.Zero; // Should not happen, as ensureValues should have loaded it
79
- }
16
+ protected getSlotCapacity(): u64 {
17
+ return 2; // 2 x u128 => 32 bytes
80
18
  }
81
19
 
82
- /**
83
- * @method set
84
- * @description Sets the u128 value at the specified global index.
85
- * @param {u64} index - The global index (0 to ∞) of the u128 value to set.
86
- * @param {u128} value - The u128 value to assign.
87
- */
88
- @inline
89
- public set(index: u64, value: u128): void {
90
- if (index > this.MAX_LENGTH) {
91
- throw new Revert('Set operation failed: Index exceeds maximum allowed value.');
92
- }
93
-
94
- const slotIndex: u32 = <u32>(index / 2);
95
- const subIndex: u8 = <u8>(index % 2);
96
- this.ensureValues(slotIndex);
97
- const slotValues = this._values.get(slotIndex);
98
- if (slotValues) {
99
- if (!u128.eq(slotValues[subIndex], value)) {
100
- slotValues[subIndex] = value;
101
- this._isChanged.add(slotIndex);
102
- }
103
- }
20
+ protected zeroValue(): u128 {
21
+ return u128.Zero; // from the as-bignum library
104
22
  }
105
23
 
106
- /**
107
- * @method push
108
- * @description Appends a new u128 value to the end of the array.
109
- * @param {u128} value - The u128 value to append.
110
- */
111
- public push(value: u128): void {
112
- if (this._length > this.MAX_LENGTH) {
113
- throw new Revert(
114
- 'Push operation failed: Array has reached its maximum allowed length.',
115
- );
116
- }
117
-
118
- const newIndex: u64 = this._length;
119
- const wrappedIndex: u64 =
120
- newIndex < this.MAX_LENGTH ? newIndex : newIndex % this.MAX_LENGTH;
121
- const slotIndex: u32 = <u32>(wrappedIndex / 2);
122
- const subIndex: u8 = <u8>(wrappedIndex % 2);
123
-
124
- // Ensure the slot is loaded
125
- this.ensureValues(slotIndex);
126
-
127
- // Set the new value in the appropriate sub-index
128
- const slotValues = this._values.get(slotIndex);
129
- if (slotValues) {
130
- slotValues[subIndex] = value;
131
- this._isChanged.add(slotIndex);
132
- }
133
-
134
- // Increment the length
135
- this._length += 1;
136
- this._isChangedLength = true;
24
+ protected eq(a: u128, b: u128): bool {
25
+ return a == b;
137
26
  }
138
27
 
139
- /**
140
- * @method delete
141
- * @description Deletes the u128 value at the specified index by setting it to zero. Does not reorder the array.
142
- * @param {u64} index - The global index of the u128 value to delete.
143
- */
144
- public delete(index: u64): void {
145
- if (index >= this._length) {
146
- // If the index is out of bounds, revert the transaction
147
- throw new Revert('Delete operation failed: Index out of bounds.');
148
- }
149
-
150
- const slotIndex: u32 = <u32>(index / 2);
151
- const subIndex: u8 = <u8>(index % 2);
152
- this.ensureValues(slotIndex);
153
-
154
- const slotValues = this._values.get(slotIndex);
155
- if (slotValues) {
156
- // Set the targeted u128 to zero
157
- if (!u128.eq(slotValues[subIndex], u128.Zero)) {
158
- slotValues[subIndex] = u128.Zero;
159
- this._isChanged.add(slotIndex);
160
- }
28
+ protected packSlot(values: u128[]): Uint8Array {
29
+ const out = new Uint8Array(32);
30
+ const firstBytes = values[0].toBytes(true);
31
+ const secondBytes = values[1].toBytes(true);
32
+ for (let i = 0; i < 16; i++) {
33
+ out[i] = firstBytes[i];
161
34
  }
162
- }
163
35
 
164
- /**
165
- * @method save
166
- * @description Persists all cached u128 values, the length, and the startIndex to their respective storage slots if any have been modified.
167
- */
168
- public save(): void {
169
- // Save all changed slots
170
- const values = this._isChanged.values();
171
- for (let i: u32 = 0; i < <u32>values.length; i++) {
172
- const slotIndex = values[i];
173
- const packed = this.packValues(slotIndex);
174
- const storagePointer = this.calculateStoragePointer(slotIndex);
175
- Blockchain.setStorageAt(storagePointer, packed);
36
+ for (let i = 0; i < 16; i++) {
37
+ out[16 + i] = secondBytes[i];
176
38
  }
177
- this._isChanged.clear();
178
39
 
179
- // Save length and startIndex if changed
180
- if (this._isChangedLength || this._isChangedStartIndex) {
181
- const packedLengthAndStartIndex = new u256();
182
- packedLengthAndStartIndex.lo1 = this._length;
183
- packedLengthAndStartIndex.lo2 = this._startIndex;
184
- Blockchain.setStorageAt(this.lengthPointer, packedLengthAndStartIndex);
185
- this._isChangedLength = false;
186
- this._isChangedStartIndex = false;
187
- }
40
+ return out;
188
41
  }
189
42
 
190
- /**
191
- * @method deleteAll
192
- * @description Deletes all storage slots by setting them to zero, including the length and startIndex slots.
193
- */
194
- public deleteAll(): void {
195
- // Iterate over all loaded slots and clear them
196
- const keys = this._values.keys();
197
- for (let i = 0; i < keys.length; i++) {
198
- const slotIndex = keys[i];
199
- const storagePointer = this.calculateStoragePointer(slotIndex);
200
- Blockchain.setStorageAt(storagePointer, u256.Zero);
43
+ protected unpackSlot(slotData: Uint8Array): u128[] {
44
+ // slotData.length == 32
45
+ const first = new Uint8Array(16);
46
+ const second = new Uint8Array(16);
47
+ for (let i = 0; i < 16; i++) {
48
+ first[i] = slotData[i];
201
49
  }
202
50
 
203
- // Reset the length and startIndex to zero
204
- const zeroLengthAndStartIndex = u256.Zero;
205
- Blockchain.setStorageAt(this.lengthPointer, zeroLengthAndStartIndex);
206
- this._length = 0;
207
- this._startIndex = 0;
208
- this._isChangedLength = false;
209
- this._isChangedStartIndex = false;
210
-
211
- // Clear internal caches
212
- this._values.clear();
213
- this._isLoaded.clear();
214
- this._isChanged.clear();
215
- }
216
-
217
- public deleteLast(): void {
218
- if (this._length === 0) {
219
- throw new Revert('DeleteLast operation failed: Array is empty.');
51
+ for (let i = 16; i < 32; i++) {
52
+ second[i - 16] = slotData[i];
220
53
  }
221
54
 
222
- const lastIndex = this._length - 1;
223
- this.delete(lastIndex);
224
-
225
- // Decrement the length
226
- this._length -= 1;
227
- this._isChangedLength = true;
228
- }
229
-
230
- /**
231
- * @method setMultiple
232
- * @description Sets multiple u128 values starting from a specific global index.
233
- * @param {u32} startIndex - The starting global index.
234
- * @param {u128[]} values - An array of u128 values to set.
235
- */
236
- @inline
237
- public setMultiple(startIndex: u32, values: u128[]): void {
238
- for (let i: u32 = 0; i < values.length; i++) {
239
- this.set(<u64>(startIndex + i), values[i]);
240
- }
241
- }
242
-
243
- /**
244
- * @method getAll
245
- * @description Retrieves a range of u128 values starting from a specific global index.
246
- * @param {u32} startIndex - The starting global index.
247
- * @param {u32} count - The number of u128 values to retrieve.
248
- * @returns {u128[]} - An array containing the retrieved u128 values.
249
- */
250
- @inline
251
- public getAll(startIndex: u32, count: u32): u128[] {
252
- if ((startIndex + count) > this._length) {
253
- throw new Revert('Requested range exceeds array length');
254
- }
255
-
256
- const result: u128[] = new Array<u128>(count);
257
- for (let i: u32 = 0; i < count; i++) {
258
- result[i] = this.get(<u64>(startIndex + i));
259
- }
260
- return result;
261
- }
262
-
263
- /**
264
- * @method toString
265
- * @description Returns a string representation of all cached u128 values.
266
- * @returns {string} - A string in the format "[value0, value1, ..., valueN]".
267
- */
268
- @inline
269
- public toString(): string {
270
- let str = '[';
271
- for (let i: u32 = 0; i < this._length; i++) {
272
- const value = this.get(<u64>i);
273
- str += value.toString();
274
- if (i !== this._length - 1) {
275
- str += ', ';
276
- }
277
- }
278
- str += ']';
279
- return str;
280
- }
281
-
282
- /**
283
- * @method toBytes
284
- * @description Returns the packed u256 values as a byte array.
285
- * @returns {u8[]} - The packed u256 values in byte form.
286
- */
287
- @inline
288
- public toBytes(): u8[] {
289
- const bytes: u8[] = new Array<u8>();
290
- const slotCount: u32 = <u32>((this._length + 1) / 2);
291
-
292
- for (let i: u32 = 0; i < slotCount; i++) {
293
- this.ensureValues(i);
294
- const packed = this.packValues(i);
295
- const slotBytes = packed.toBytes();
296
- for (let j: u32 = 0; j < slotBytes.length; j++) {
297
- bytes.push(slotBytes[j]);
298
- }
299
- }
300
- return bytes;
301
- }
302
-
303
- /**
304
- * @method reset
305
- * @description Resets all cached u128 values to zero and marks them as changed, including resetting the length and startIndex.
306
- */
307
- @inline
308
- public reset(): void {
309
- // Reset the length and startIndex to zero
310
- this._length = 0;
311
- this._startIndex = 0;
312
- this._isChangedLength = true;
313
- this._isChangedStartIndex = true;
314
-
315
- this.save();
316
- }
317
-
318
- /**
319
- * @method getLength
320
- * @description Retrieves the current length of the array.
321
- * @returns {u64} - The current length.
322
- */
323
- @inline
324
- public getLength(): u64 {
325
- return this._length;
326
- }
327
-
328
- /**
329
- * @private
330
- * @method ensureValues
331
- * @description Loads and caches the u128 values from the specified storage slot.
332
- * @param {u32} slotIndex - The index of the storage slot.
333
- */
334
- private ensureValues(slotIndex: u32): void {
335
- if (!this._isLoaded.has(slotIndex)) {
336
- const storagePointer = this.calculateStoragePointer(slotIndex);
337
- const storedU256: u256 = Blockchain.getStorageAt(storagePointer, this.defaultValue);
338
- const slotValues = this.unpackU256(storedU256);
339
- this._values.set(slotIndex, slotValues);
340
- this._isLoaded.add(slotIndex);
341
- }
342
- }
343
-
344
- /**
345
- * @private
346
- * @method packValues
347
- * @description Packs the two cached u128 values into a single u256 for storage.
348
- * @param {u64} slotIndex - The index of the storage slot.
349
- * @returns {u256} - The packed u256 value.
350
- */
351
- private packValues(slotIndex: u64): u256 {
352
- const values = this._values.get(slotIndex);
353
- if (!values) {
354
- // Should not happen, as ensureValues should have loaded it
355
- return u256.Zero;
356
- }
357
- const packed = new u256();
358
-
359
- // Assign first u128
360
- packed.lo1 = values[0].lo;
361
- packed.lo2 = values[0].hi;
362
-
363
- // Assign second u128
364
- packed.hi1 = values[1].lo;
365
- packed.hi2 = values[1].hi;
366
-
367
- return packed;
368
- }
369
-
370
- /**
371
- * @private
372
- * @method unpackU256
373
- * @description Unpacks a u256 value into an array of two u128s.
374
- * @param {u256} storedU256 - The u256 value to unpack.
375
- * @returns {u128[]} - An array of two u128 values.
376
- */
377
- private unpackU256(storedU256: u256): u128[] {
378
- const values: u128[] = new Array<u128>(2);
379
-
380
- // Unpack first u128 from lo1 and lo2
381
- values[0] = new u128(storedU256.lo1, storedU256.lo2);
382
-
383
- // Unpack second u128 from hi1 and hi2
384
- values[1] = new u128(storedU256.hi1, storedU256.hi2);
55
+ const val0 = u128.fromUint8ArrayBE(first);
56
+ const val1 = u128.fromUint8ArrayBE(second);
385
57
 
386
- return values;
58
+ return [val0, val1];
387
59
  }
388
60
 
389
- /**
390
- * @private
391
- * @method calculateStoragePointer
392
- * @description Calculates the storage pointer for a given slot index by incrementing the base pointer.
393
- * @param {u64} slotIndex - The index of the storage slot.
394
- * @returns {u256} - The calculated storage pointer.
395
- */
396
- private calculateStoragePointer(slotIndex: u64): u256 {
397
- return SafeMath.add(this.baseU256Pointer, u256.fromU64(slotIndex + 1));
61
+ protected calculateStoragePointer(slotIndex: u64): Uint8Array {
62
+ return bigEndianAdd(this.basePointer, slotIndex + 1);
398
63
  }
399
- }
64
+ }