@btc-vision/btc-runtime 1.4.7 → 1.5.1

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 +4 -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 +33 -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 +83 -175
  35. package/runtime/storage/arrays/StoredBooleanArray.ts +146 -271
  36. package/runtime/storage/arrays/StoredPackedArray.ts +313 -0
  37. package/runtime/storage/arrays/StoredU128Array.ts +38 -374
  38. package/runtime/storage/arrays/StoredU16Array.ts +34 -420
  39. package/runtime/storage/arrays/StoredU256Array.ts +21 -347
  40. package/runtime/storage/arrays/StoredU32Array.ts +37 -440
  41. package/runtime/storage/arrays/StoredU64Array.ts +66 -0
  42. package/runtime/storage/arrays/StoredU8Array.ts +29 -453
  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,93 +1,82 @@
1
- import { u256 } from '@btc-vision/as-bignum/assembly';
1
+ import { BytesReader } from '../../buffer/BytesReader';
2
2
  import { BytesWriter } from '../../buffer/BytesWriter';
3
3
  import { Blockchain } from '../../env';
4
+ import { addUint8ArraysBE, u64ToBE32Bytes } from '../../math/bytes';
4
5
  import { Address } from '../../types/Address';
5
6
  import { Revert } from '../../types/Revert';
6
- import { SafeMath } from '../../types/SafeMath';
7
7
 
8
8
  /**
9
9
  * @class StoredAddressArray
10
10
  * @description Manages an array of Address values across multiple storage slots.
11
- * Each slot holds one Address (stored as u256 in storage).
11
+ * Each slot holds one Address (stored as a raw Uint8Array in storage).
12
12
  */
13
13
  @final
14
14
  export class StoredAddressArray {
15
- private readonly baseU256Pointer: u256;
16
- private readonly lengthPointer: u256;
15
+ private readonly baseU256Pointer: Uint8Array;
16
+ private readonly lengthPointer: Uint8Array;
17
17
 
18
- // Internal cache for storage slots
19
- private _values: Map<u64, Address> = new Map(); // Map from slotIndex to Address value
20
- private _isLoaded: Set<u64> = new Set(); // Set of slotIndexes that are loaded
21
- private _isChanged: Set<u64> = new Set(); // Set of slotIndexes that are modified
18
+ private _values: Map<u64, Address> = new Map(); // slotIndex -> Address
19
+ private _isChanged: Set<u64> = new Set(); // track changed slotIndexes
22
20
 
23
- // Internal variables for length and startIndex management
24
- private _length: u64 = 0; // Current length of the array
25
- private _startIndex: u64 = 0; // Starting index of the array
26
- private _isChangedLength: bool = false; // Indicates if the length has been modified
27
- private _isChangedStartIndex: bool = false; // Indicates if the startIndex has been modified
21
+ private _length: u64 = 0;
22
+ private _startIndex: u64 = 0;
23
+ private _isChangedLength: bool = false;
24
+ private _isChangedStartIndex: bool = false;
28
25
 
29
- // Define a maximum allowed length to prevent excessive storage usage
30
26
  private readonly MAX_LENGTH: u64 = u64(u32.MAX_VALUE - 1);
31
27
 
28
+ private readonly defaultValue: Address = Address.zero();
29
+
32
30
  /**
33
31
  * @constructor
34
32
  * @param {u16} pointer - The primary pointer identifier.
35
33
  * @param {Uint8Array} subPointer - The sub-pointer for memory slot addressing.
36
- * @param {Address} defaultValue - The default Address value if storage is uninitialized.
37
34
  */
38
- constructor(
39
- public pointer: u16,
40
- public subPointer: Uint8Array,
41
- private defaultValue: Address,
42
- ) {
43
- // Initialize the base pointer
35
+ constructor(public pointer: u16, public subPointer: Uint8Array) {
36
+ assert(
37
+ subPointer.length <= 30,
38
+ `You must pass a 30 bytes sub-pointer. (AddressArray, got ${subPointer.length})`,
39
+ );
40
+
41
+ // Construct base pointer as a 32-byte array
44
42
  const writer = new BytesWriter(32);
45
43
  writer.writeU16(pointer);
46
44
  writer.writeBytes(subPointer);
47
45
 
48
- const baseU256Pointer = u256.fromBytes(writer.getBuffer(), true);
49
- const lengthPointer = baseU256Pointer.clone();
46
+ // We'll reuse the same bytes as the "base pointer" for offsets
47
+ const baseU256Pointer = writer.getBuffer(); // 32 bytes
48
+ // For length+startIndex, we'll use the same pointer
49
+ const lengthPointer = Uint8Array.wrap(baseU256Pointer.buffer);
50
+
51
+ // Load length + startIndex from storage (16 bytes: 8 for length, 8 for startIndex).
52
+ const storedLengthAndStartIndex: Uint8Array = Blockchain.getStorageAt(lengthPointer);
53
+
54
+ const reader = new BytesReader(storedLengthAndStartIndex);
55
+ this._length = reader.readU64();
56
+ this._startIndex = reader.readU64();
50
57
 
51
- // Load the current length and startIndex from storage
52
- const storedLengthAndStartIndex: u256 = Blockchain.getStorageAt(lengthPointer, u256.Zero);
53
58
  this.lengthPointer = lengthPointer;
54
59
  this.baseU256Pointer = baseU256Pointer;
55
-
56
- this._length = storedLengthAndStartIndex.lo1; // Bytes 0-7: length
57
- this._startIndex = storedLengthAndStartIndex.lo2; // Bytes 8-15: startIndex
58
60
  }
59
61
 
60
- /**
61
- * @method get
62
- * @description Retrieves the Address value at the specified global index.
63
- * @param {u64} index - The global index of the Address to retrieve.
64
- * @returns {Address} - The Address value at the specified index.
65
- */
62
+ /** Get an element by its global index. */
66
63
  @inline
67
64
  public get(index: u64): Address {
68
65
  if (index > this.MAX_LENGTH) {
69
66
  throw new Revert('Operation failed: Index exceeds maximum allowed value.');
70
67
  }
71
-
72
68
  const slotIndex: u32 = <u32>index;
73
69
  this.ensureValues(slotIndex);
74
70
 
75
- const value = this._values.get(slotIndex);
76
- return value ? value : this.defaultValue;
71
+ return this._values.get(slotIndex);
77
72
  }
78
73
 
79
- /**
80
- * @method set
81
- * @description Sets the Address value at the specified global index.
82
- * @param {u64} index - The global index of the Address to set.
83
- * @param {Address} value - The Address value to assign.
84
- */
74
+ /** Set an element by its global index. */
85
75
  @inline
86
76
  public set(index: u64, value: Address): void {
87
77
  if (index > this.MAX_LENGTH) {
88
- throw new Revert('Set operation failed: Index exceeds maximum allowed value.');
78
+ throw new Revert('Set failed: Index exceeds maximum allowed value.');
89
79
  }
90
-
91
80
  const slotIndex: u32 = <u32>index;
92
81
  this.ensureValues(slotIndex);
93
82
 
@@ -98,44 +87,27 @@ export class StoredAddressArray {
98
87
  }
99
88
  }
100
89
 
101
- /**
102
- * @method indexOf
103
- * @description Searches for the first occurrence of the specified Address value and returns its index.
104
- * @param {Address} value - The Address to locate.
105
- * @returns {i64} - The index of the first occurrence, or -1 if not found.
106
- */
90
+ /** Find the first index containing `value`. Returns -1 if not found. */
107
91
  @inline
108
92
  public indexOf(value: Address): i64 {
109
93
  for (let i: u64 = 0; i < this._length; i++) {
110
- const currentValue = this.get(i);
111
- if (currentValue == value) {
94
+ if (this.get(i) == value) {
112
95
  return i64(i);
113
96
  }
114
97
  }
115
98
  return -1;
116
99
  }
117
100
 
118
- /**
119
- * @method contains
120
- * @description Determines whether the array contains the specified Address value.
121
- * @param {Address} value - The Address to locate.
122
- * @returns {boolean} - True if found; otherwise, false.
123
- */
101
+ /** Check if the array contains `value`. */
124
102
  @inline
125
103
  public contains(value: Address): boolean {
126
104
  return this.indexOf(value) !== -1;
127
105
  }
128
106
 
129
- /**
130
- * @method push
131
- * @description Appends a new Address value to the end of the array.
132
- * @param {Address} value - The Address to append.
133
- */
107
+ /** Append an address at the end of the array. */
134
108
  public push(value: Address): void {
135
- if (this._length > this.MAX_LENGTH) {
136
- throw new Revert(
137
- 'Push operation failed: Array has reached its maximum allowed length.',
138
- );
109
+ if (this._length >= this.MAX_LENGTH) {
110
+ throw new Revert('Push failed: Array has reached its maximum length.');
139
111
  }
140
112
 
141
113
  const newIndex: u64 = this._length;
@@ -151,13 +123,10 @@ export class StoredAddressArray {
151
123
  this._isChangedLength = true;
152
124
  }
153
125
 
154
- /**
155
- * @method deleteLast
156
- * @description Deletes the last element from the array.
157
- */
126
+ /** Delete the last element. */
158
127
  public deleteLast(): void {
159
128
  if (this._length === 0) {
160
- throw new Revert('Delete operation failed: Array is empty.');
129
+ throw new Revert('Delete failed: Array is empty.');
161
130
  }
162
131
 
163
132
  const lastIndex: u64 = this._length - 1;
@@ -174,21 +143,13 @@ export class StoredAddressArray {
174
143
  this._isChangedLength = true;
175
144
  }
176
145
 
177
- /**
178
- * @method setStartingIndex
179
- * @description Sets the starting index of the array.
180
- * @param {u64} index - The new starting index.
181
- */
146
+ /** Adjust the starting index. */
182
147
  public setStartingIndex(index: u64): void {
183
148
  this._startIndex = index;
184
149
  this._isChangedStartIndex = true;
185
150
  }
186
151
 
187
- /**
188
- * @method delete
189
- * @description Deletes the Address value at the specified index by setting it to defaultValue.
190
- * @param {u64} index - The global index of the Address value to delete.
191
- */
152
+ /** Delete a specific element by setting it to `defaultValue`. */
192
153
  public delete(index: u64): void {
193
154
  if (index > this.MAX_LENGTH) {
194
155
  throw new Revert('Operation failed: Index exceeds maximum allowed value.');
@@ -205,47 +166,46 @@ export class StoredAddressArray {
205
166
  }
206
167
 
207
168
  /**
208
- * @method save
209
- * @description Persists all changes to storage.
169
+ * Persist changes to storage.
170
+ * - Store any changed slotIndex -> Address
171
+ * - Store updated length and startIndex if changed
210
172
  */
211
173
  public save(): void {
212
- // Save all changed slots
174
+ // 1) Save changed slots
213
175
  const changed = this._isChanged.values();
214
176
  for (let i = 0; i < changed.length; i++) {
215
177
  const slotIndex = changed[i];
216
178
  const storagePointer = this.calculateStoragePointer(slotIndex);
179
+
217
180
  const value = this._values.get(slotIndex);
218
- Blockchain.setStorageAt(storagePointer, u256.fromBytes(value));
181
+ Blockchain.setStorageAt(storagePointer, value);
219
182
  }
220
183
  this._isChanged.clear();
221
184
 
222
- // Save length and startIndex if changed
185
+ // 2) Save length and startIndex if changed
223
186
  if (this._isChangedLength || this._isChangedStartIndex) {
224
- const packedLengthAndStartIndex = new u256();
225
- packedLengthAndStartIndex.lo1 = this._length;
226
- packedLengthAndStartIndex.lo2 = this._startIndex;
227
- Blockchain.setStorageAt(this.lengthPointer, packedLengthAndStartIndex);
187
+ const writer = new BytesWriter(16);
188
+ writer.writeU64(this._length);
189
+ writer.writeU64(this._startIndex);
190
+
191
+ Blockchain.setStorageAt(this.lengthPointer, writer.getBuffer());
228
192
  this._isChangedLength = false;
229
193
  this._isChangedStartIndex = false;
230
194
  }
231
195
  }
232
196
 
233
- /**
234
- * @method deleteAll
235
- * @description Deletes the entire array and resets length and startIndex.
236
- */
197
+ /** Clear entire array content from storage, reset length and startIndex. */
237
198
  public deleteAll(): void {
238
199
  // Clear all loaded slots
239
200
  const keys = this._values.keys();
240
201
  for (let i = 0; i < keys.length; i++) {
241
202
  const slotIndex = keys[i];
242
203
  const storagePointer = this.calculateStoragePointer(slotIndex);
243
- Blockchain.setStorageAt(storagePointer, u256.fromBytes(this.defaultValue));
204
+ Blockchain.setStorageAt(storagePointer, this.defaultValue);
244
205
  }
245
206
 
246
- // Reset the length and startIndex
247
- const zeroLengthAndStartIndex = u256.Zero;
248
- Blockchain.setStorageAt(this.lengthPointer, zeroLengthAndStartIndex);
207
+ // Reset length and startIndex in storage
208
+ Blockchain.setStorageAt(this.lengthPointer, new Uint8Array(0)); // or a 16-byte zero array if preferred
249
209
  this._length = 0;
250
210
  this._startIndex = 0;
251
211
  this._isChangedLength = false;
@@ -253,16 +213,10 @@ export class StoredAddressArray {
253
213
 
254
214
  // Clear internal caches
255
215
  this._values.clear();
256
- this._isLoaded.clear();
257
216
  this._isChanged.clear();
258
217
  }
259
218
 
260
- /**
261
- * @method setMultiple
262
- * @description Sets multiple Address values starting from a specific global index.
263
- * @param {u32} startIndex - The starting global index.
264
- * @param {Address[]} values - An array of Address values to set.
265
- */
219
+ /** Bulk-set multiple addresses starting at `startIndex`. */
266
220
  @inline
267
221
  public setMultiple(startIndex: u32, values: Address[]): void {
268
222
  for (let i: u32 = 0; i < values.length; i++) {
@@ -270,31 +224,21 @@ export class StoredAddressArray {
270
224
  }
271
225
  }
272
226
 
273
- /**
274
- * @method getAll
275
- * @description Retrieves a range of Address values.
276
- * @param {u32} startIndex - The start index.
277
- * @param {u32} count - The number of items to get.
278
- * @returns {Address[]} - The requested Address values.
279
- */
227
+ /** Retrieve a batch of addresses (range). */
280
228
  @inline
281
229
  public getAll(startIndex: u32, count: u32): Address[] {
282
- if ((startIndex + count) > this._length) {
230
+ if (startIndex + count > this._length) {
283
231
  throw new Revert('Requested range exceeds array length');
284
232
  }
285
233
 
286
- const result: Address[] = new Array<Address>(count);
234
+ const result = new Array<Address>(count);
287
235
  for (let i: u32 = 0; i < count; i++) {
288
236
  result[i] = this.get(<u64>(startIndex + i));
289
237
  }
290
238
  return result;
291
239
  }
292
240
 
293
- /**
294
- * @method toString
295
- * @description Returns a string representation of the array.
296
- * @returns {string} - A string of the form "[addr0, addr1, ...]".
297
- */
241
+ /** Returns a string of the form "[addr0, addr1, ...]". */
298
242
  @inline
299
243
  public toString(): string {
300
244
  let str = '[';
@@ -309,31 +253,7 @@ export class StoredAddressArray {
309
253
  return str;
310
254
  }
311
255
 
312
- /**
313
- * @method toBytes
314
- * @description Returns the packed Address values as a byte array.
315
- * @returns {u8[]} - The packed byte array.
316
- */
317
- @inline
318
- public toBytes(): u8[] {
319
- const bytes: u8[] = new Array<u8>();
320
- for (let i: u32 = 0; i < this._length; i++) {
321
- this.ensureValues(i);
322
- const value = this._values.get(i);
323
- if (value) {
324
- const valueBytes = value; // Address is assumed to be or contain a Uint8Array
325
- for (let j: u32 = 0; j < valueBytes.length; j++) {
326
- bytes.push(valueBytes[j]);
327
- }
328
- }
329
- }
330
- return bytes;
331
- }
332
-
333
- /**
334
- * @method reset
335
- * @description Resets the array by clearing all elements and resetting length and startIndex to zero.
336
- */
256
+ /** Reset in-memory and persist. */
337
257
  @inline
338
258
  public reset(): void {
339
259
  this._length = 0;
@@ -343,53 +263,41 @@ export class StoredAddressArray {
343
263
  this.save();
344
264
  }
345
265
 
346
- /**
347
- * @method getLength
348
- * @description Retrieves the current length of the array.
349
- * @returns {u64} - The current length.
350
- */
266
+ /** Current array length. */
351
267
  @inline
352
268
  public getLength(): u64 {
353
269
  return this._length;
354
270
  }
355
271
 
356
- /**
357
- * @method startingIndex
358
- * @description Retrieves the current starting index of the array.
359
- * @returns {u64} - The starting index.
360
- */
272
+ /** Current starting index. */
361
273
  public startingIndex(): u64 {
362
274
  return this._startIndex;
363
275
  }
364
276
 
365
277
  /**
366
- * @private
367
- * @method ensureValues
368
- * @description Loads and caches the Address from the specified storage slot if not already loaded.
369
- * @param {u32} slotIndex - The index of the storage slot.
278
+ * Ensure the given slot index is loaded into `_values`.
370
279
  */
371
280
  private ensureValues(slotIndex: u32): void {
372
- if (!this._isLoaded.has(slotIndex)) {
281
+ if (!this._values.has(slotIndex)) {
373
282
  const storagePointer = this.calculateStoragePointer(slotIndex);
374
- const storedU256: u256 = Blockchain.getStorageAt(storagePointer, u256.Zero);
283
+
284
+ // Load raw bytes from storage
285
+ const stored: Uint8Array = Blockchain.getStorageAt(storagePointer);
286
+
375
287
  const storedAddress: Address =
376
- storedU256 == u256.Zero ? this.defaultValue : new Address(storedU256.toBytes());
288
+ stored.length == 0 ? this.defaultValue : Address.fromUint8Array(stored);
289
+
377
290
  this._values.set(slotIndex, storedAddress);
378
- this._isLoaded.add(slotIndex);
379
291
  }
380
292
  }
381
293
 
382
294
  /**
383
- * @private
384
- * @method calculateStoragePointer
385
- * @description Calculates the storage pointer for a given slot index.
386
- * @param {u64} slotIndex - The index of the storage slot.
387
- * @returns {u256} - The calculated storage pointer.
295
+ * Compute a 32-byte storage pointer = basePointer + (slotIndex + 1) big-endian.
388
296
  */
389
- private calculateStoragePointer(slotIndex: u64): u256 {
390
- // Each slot is identified by baseU256Pointer + slotIndex + 1
391
- // Slot 0: baseU256Pointer + 1
392
- // Slot 1: baseU256Pointer + 2, etc.
393
- return SafeMath.add(this.baseU256Pointer, u256.fromU64(slotIndex + 1));
297
+ private calculateStoragePointer(slotIndex: u64): Uint8Array {
298
+ // Convert (slotIndex + 1) to a 32-byte big-endian offset
299
+ const offset = u64ToBE32Bytes(slotIndex + 1);
300
+
301
+ return addUint8ArraysBE(this.baseU256Pointer, offset);
394
302
  }
395
303
  }