@btc-vision/btc-runtime 1.6.1 → 1.6.2
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 +1 -1
- package/runtime/env/Atomic.ts +51 -0
- package/runtime/env/global.ts +2 -0
- package/runtime/index.ts +5 -0
- package/runtime/math/bytes.ts +7 -7
- package/runtime/storage/AdvancedStoredString.ts +199 -0
- package/runtime/storage/StoredU32.ts +178 -0
- package/runtime/storage/arrays/StoredAddressArray.ts +20 -279
- package/runtime/storage/arrays/StoredBooleanArray.ts +122 -52
- package/runtime/storage/arrays/StoredPackedArray.ts +125 -49
- package/runtime/storage/arrays/StoredU128Array.ts +6 -6
- package/runtime/storage/arrays/StoredU16Array.ts +6 -6
- package/runtime/storage/arrays/StoredU256Array.ts +5 -5
- package/runtime/storage/arrays/StoredU32Array.ts +5 -5
- package/runtime/storage/arrays/StoredU64Array.ts +14 -7
- package/runtime/storage/arrays/StoredU8Array.ts +5 -5
- package/runtime/storage/maps/StoredMapU256.ts +62 -0
- package/runtime/types/SafeMath.ts +50 -0
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
import { Blockchain } from '../../env';
|
|
9
9
|
import { Revert } from '../../types/Revert';
|
|
10
10
|
|
|
11
|
+
export const DEFAULT_MAX_LENGTH: u32 = u32.MAX_VALUE - 1;
|
|
12
|
+
|
|
11
13
|
/**
|
|
12
14
|
* Abstract base class for an array of T values that are packed
|
|
13
15
|
* in 32-byte (Uint8Array) "slots" in storage.
|
|
@@ -16,6 +18,8 @@ import { Revert } from '../../types/Revert';
|
|
|
16
18
|
* - Maps each global index to (slotIndex, subIndex).
|
|
17
19
|
* - Child classes define how many T items fit per 32-byte slot
|
|
18
20
|
* and how to pack/unpack them.
|
|
21
|
+
*
|
|
22
|
+
* Note: This is designed to wrap around.
|
|
19
23
|
*/
|
|
20
24
|
export abstract class StoredPackedArray<T> {
|
|
21
25
|
/** 32-byte base pointer (used to derive storage keys). */
|
|
@@ -25,10 +29,10 @@ export abstract class StoredPackedArray<T> {
|
|
|
25
29
|
protected readonly lengthPointer: Uint8Array;
|
|
26
30
|
|
|
27
31
|
/** Internal length of the array. */
|
|
28
|
-
protected _length:
|
|
32
|
+
protected _length: u32 = 0;
|
|
29
33
|
|
|
30
34
|
/** Optional "start index" if needed by your logic. */
|
|
31
|
-
protected _startIndex:
|
|
35
|
+
protected _startIndex: u32 = 0;
|
|
32
36
|
|
|
33
37
|
/** Whether length or startIndex changed. */
|
|
34
38
|
protected _isChangedLength: bool = false;
|
|
@@ -38,19 +42,23 @@ export abstract class StoredPackedArray<T> {
|
|
|
38
42
|
* A map from slotIndex => the 32-byte slot data in memory.
|
|
39
43
|
* Child classes will parse that 32 bytes into an array of T, or vice versa.
|
|
40
44
|
*/
|
|
41
|
-
protected _slots: Map<
|
|
45
|
+
protected _slots: Map<u32, Uint8Array> = new Map();
|
|
42
46
|
|
|
43
47
|
/** Track which slotIndexes are changed and need saving. */
|
|
44
|
-
protected _isChanged: Set<
|
|
48
|
+
protected _isChanged: Set<u32> = new Set();
|
|
45
49
|
|
|
46
|
-
|
|
47
|
-
* Maximum length to prevent unbounded usage.
|
|
48
|
-
* Adjust to fit your constraints (e.g. `u64.MAX_VALUE`).
|
|
49
|
-
*/
|
|
50
|
-
protected readonly MAX_LENGTH: u64 = <u64>(u32.MAX_VALUE - 1);
|
|
50
|
+
private nextItemOffset: u32 = 0;
|
|
51
51
|
|
|
52
|
-
protected constructor(
|
|
53
|
-
|
|
52
|
+
protected constructor(
|
|
53
|
+
public pointer: u16,
|
|
54
|
+
public subPointer: Uint8Array,
|
|
55
|
+
protected defaultValue: T,
|
|
56
|
+
protected MAX_LENGTH: u32 = DEFAULT_MAX_LENGTH,
|
|
57
|
+
) {
|
|
58
|
+
assert(
|
|
59
|
+
subPointer.length <= 30,
|
|
60
|
+
`You must pass a 30 bytes sub-pointer. (Array, got ${subPointer.length})`,
|
|
61
|
+
);
|
|
54
62
|
|
|
55
63
|
const basePointer = encodeBasePointer(pointer, subPointer);
|
|
56
64
|
this.lengthPointer = Uint8Array.wrap(basePointer.buffer);
|
|
@@ -63,16 +71,38 @@ export abstract class StoredPackedArray<T> {
|
|
|
63
71
|
this._startIndex = data[1];
|
|
64
72
|
}
|
|
65
73
|
|
|
74
|
+
@inline
|
|
75
|
+
public get previousOffset(): u32 {
|
|
76
|
+
return <u32>(
|
|
77
|
+
((this._startIndex +
|
|
78
|
+
<u64>(this.nextItemOffset === 0 ? this.nextItemOffset : this.nextItemOffset - 1)) %
|
|
79
|
+
<u64>this.MAX_LENGTH)
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Set the maximum length of the array.
|
|
85
|
+
* This is a safety check to prevent unbounded usage.
|
|
86
|
+
*/
|
|
87
|
+
@inline
|
|
88
|
+
public setMaxLength(maxLength: u32): void {
|
|
89
|
+
if (maxLength > this.MAX_LENGTH) {
|
|
90
|
+
throw new Revert('setMaxLength: maxLength exceeds MAX_LENGTH');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
this.MAX_LENGTH = maxLength;
|
|
94
|
+
}
|
|
95
|
+
|
|
66
96
|
@inline
|
|
67
97
|
@operator('[]')
|
|
68
|
-
public get(index:
|
|
98
|
+
public get(index: u32): T {
|
|
69
99
|
// max length used on purpose to prevent unbounded usage
|
|
70
100
|
if (index > this.MAX_LENGTH) {
|
|
71
101
|
throw new Revert('get: out of range');
|
|
72
102
|
}
|
|
73
103
|
|
|
74
|
-
const realIndex =
|
|
75
|
-
const cap = this.getSlotCapacity();
|
|
104
|
+
const realIndex: u32 = this.getRealIndex(index);
|
|
105
|
+
const cap: u32 = this.getSlotCapacity();
|
|
76
106
|
const slotIndex = realIndex / cap;
|
|
77
107
|
const subIndex = <u32>(realIndex % cap);
|
|
78
108
|
|
|
@@ -83,7 +113,7 @@ export abstract class StoredPackedArray<T> {
|
|
|
83
113
|
}
|
|
84
114
|
|
|
85
115
|
@inline
|
|
86
|
-
public get_physical(index:
|
|
116
|
+
public get_physical(index: u32): T {
|
|
87
117
|
if (index > this.MAX_LENGTH) {
|
|
88
118
|
throw new Revert('get: index exceeds MAX_LENGTH (packed array)');
|
|
89
119
|
}
|
|
@@ -100,12 +130,12 @@ export abstract class StoredPackedArray<T> {
|
|
|
100
130
|
|
|
101
131
|
@inline
|
|
102
132
|
@operator('[]=')
|
|
103
|
-
public set(index:
|
|
133
|
+
public set(index: u32, value: T): void {
|
|
104
134
|
if (index > this.MAX_LENGTH) {
|
|
105
135
|
throw new Revert('set: index exceeds MAX_LENGTH (packed array)');
|
|
106
136
|
}
|
|
107
137
|
|
|
108
|
-
const realIndex =
|
|
138
|
+
const realIndex: u32 = this.getRealIndex(index);
|
|
109
139
|
const cap = this.getSlotCapacity();
|
|
110
140
|
const slotIndex = realIndex / cap;
|
|
111
141
|
const subIndex = <u32>(realIndex % cap);
|
|
@@ -122,7 +152,7 @@ export abstract class StoredPackedArray<T> {
|
|
|
122
152
|
}
|
|
123
153
|
|
|
124
154
|
@inline
|
|
125
|
-
public set_physical(index:
|
|
155
|
+
public set_physical(index: u32, value: T): void {
|
|
126
156
|
if (index > this.MAX_LENGTH) {
|
|
127
157
|
throw new Revert('set: index exceeds MAX_LENGTH (packed array)');
|
|
128
158
|
}
|
|
@@ -142,13 +172,48 @@ export abstract class StoredPackedArray<T> {
|
|
|
142
172
|
}
|
|
143
173
|
}
|
|
144
174
|
|
|
175
|
+
/**
|
|
176
|
+
* Get the next item in the array, starting from the current offset.
|
|
177
|
+
* This is useful for iterating through the array.
|
|
178
|
+
*/
|
|
179
|
+
@inline
|
|
180
|
+
public next(): T {
|
|
181
|
+
const value = this.get(this.nextItemOffset);
|
|
182
|
+
this.nextItemOffset += 1;
|
|
183
|
+
|
|
184
|
+
return value;
|
|
185
|
+
}
|
|
186
|
+
|
|
145
187
|
@inline
|
|
146
|
-
public
|
|
188
|
+
public incrementStartingIndex(): void {
|
|
189
|
+
if (this._startIndex >= this.MAX_LENGTH) {
|
|
190
|
+
this._startIndex = 0;
|
|
191
|
+
} else {
|
|
192
|
+
this._startIndex += 1;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
this._isChangedStartIndex = true;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Apply the starting index with n offset.
|
|
200
|
+
*/
|
|
201
|
+
@inline
|
|
202
|
+
public applyNextOffsetToStartingIndex(): void {
|
|
203
|
+
if (!this.nextItemOffset) return;
|
|
204
|
+
|
|
205
|
+
this._startIndex += this.nextItemOffset;
|
|
206
|
+
this._isChangedStartIndex = true;
|
|
207
|
+
this.nextItemOffset = 0;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
@inline
|
|
211
|
+
public push(value: T, isPhysical: bool = false): u32 {
|
|
147
212
|
if (this._length >= this.MAX_LENGTH) {
|
|
148
213
|
throw new Revert('push: array has reached MAX_LENGTH');
|
|
149
214
|
}
|
|
150
215
|
|
|
151
|
-
const realIndex =
|
|
216
|
+
const realIndex: u32 = this.getRealIndex(this._length, isPhysical);
|
|
152
217
|
const cap = this.getSlotCapacity();
|
|
153
218
|
const slotIndex = realIndex / cap;
|
|
154
219
|
const subIndex = <u32>(realIndex % cap);
|
|
@@ -165,6 +230,8 @@ export abstract class StoredPackedArray<T> {
|
|
|
165
230
|
|
|
166
231
|
this._length += 1;
|
|
167
232
|
this._isChangedLength = true;
|
|
233
|
+
|
|
234
|
+
return realIndex;
|
|
168
235
|
}
|
|
169
236
|
|
|
170
237
|
/**
|
|
@@ -207,8 +274,8 @@ export abstract class StoredPackedArray<T> {
|
|
|
207
274
|
* but does not reduce the length.
|
|
208
275
|
*/
|
|
209
276
|
@inline
|
|
210
|
-
public delete(index:
|
|
211
|
-
const realIndex =
|
|
277
|
+
public delete(index: u32): void {
|
|
278
|
+
const realIndex = this.getRealIndex(index);
|
|
212
279
|
const cap = this.getSlotCapacity();
|
|
213
280
|
const slotIndex = realIndex / cap;
|
|
214
281
|
const subIndex = <u32>(realIndex % cap);
|
|
@@ -225,12 +292,22 @@ export abstract class StoredPackedArray<T> {
|
|
|
225
292
|
}
|
|
226
293
|
}
|
|
227
294
|
|
|
295
|
+
@inline
|
|
296
|
+
public removeItemFromLength(): void {
|
|
297
|
+
if (this._length == 0) {
|
|
298
|
+
throw new Revert('delete: array is empty');
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
this._length -= 1;
|
|
302
|
+
this._isChangedLength = true;
|
|
303
|
+
}
|
|
304
|
+
|
|
228
305
|
/**
|
|
229
306
|
* "Delete" by zeroing out the element at `index`,
|
|
230
307
|
* but does not reduce the length.
|
|
231
308
|
*/
|
|
232
309
|
@inline
|
|
233
|
-
public delete_physical(index:
|
|
310
|
+
public delete_physical(index: u32): void {
|
|
234
311
|
const cap = this.getSlotCapacity();
|
|
235
312
|
const slotIndex = index / cap;
|
|
236
313
|
const subIndex = <u32>(index % cap);
|
|
@@ -264,28 +341,25 @@ export abstract class StoredPackedArray<T> {
|
|
|
264
341
|
}
|
|
265
342
|
|
|
266
343
|
@inline
|
|
267
|
-
public setMultiple(startIndex:
|
|
268
|
-
const end = startIndex + <
|
|
344
|
+
public setMultiple(startIndex: u32, values: T[]): void {
|
|
345
|
+
const end = startIndex + <u32>values.length;
|
|
269
346
|
if (end > this._length) {
|
|
270
347
|
throw new Revert('setMultiple: out of range (packed array)');
|
|
271
348
|
}
|
|
272
349
|
|
|
273
350
|
for (let i = 0; i < values.length; i++) {
|
|
274
|
-
this.set(startIndex + <
|
|
351
|
+
this.set(startIndex + <u32>i, values[i]);
|
|
275
352
|
}
|
|
276
353
|
}
|
|
277
354
|
|
|
278
|
-
// -----------------------------------------------------------
|
|
279
|
-
// Public Array-Like Methods
|
|
280
|
-
// -----------------------------------------------------------
|
|
281
355
|
@inline
|
|
282
|
-
public getAll(startIndex:
|
|
283
|
-
if (count > <
|
|
356
|
+
public getAll(startIndex: u32, count: u32): T[] {
|
|
357
|
+
if (count > <u32>u32.MAX_VALUE) {
|
|
284
358
|
throw new Revert('getAll: count too large (packed array)');
|
|
285
359
|
}
|
|
286
360
|
|
|
287
361
|
const out = new Array<T>(<i32>count);
|
|
288
|
-
for (let i:
|
|
362
|
+
for (let i: u32 = 0; i < count; i++) {
|
|
289
363
|
out[<i32>i] = this.get(startIndex + i);
|
|
290
364
|
}
|
|
291
365
|
|
|
@@ -293,17 +367,17 @@ export abstract class StoredPackedArray<T> {
|
|
|
293
367
|
}
|
|
294
368
|
|
|
295
369
|
@inline
|
|
296
|
-
public getLength():
|
|
370
|
+
public getLength(): u32 {
|
|
297
371
|
return this._length;
|
|
298
372
|
}
|
|
299
373
|
|
|
300
374
|
@inline
|
|
301
|
-
public startingIndex():
|
|
375
|
+
public startingIndex(): u32 {
|
|
302
376
|
return this._startIndex;
|
|
303
377
|
}
|
|
304
378
|
|
|
305
379
|
@inline
|
|
306
|
-
public setStartingIndex(index:
|
|
380
|
+
public setStartingIndex(index: u32): void {
|
|
307
381
|
this._startIndex = index;
|
|
308
382
|
this._isChangedStartIndex = true;
|
|
309
383
|
}
|
|
@@ -313,7 +387,7 @@ export abstract class StoredPackedArray<T> {
|
|
|
313
387
|
*/
|
|
314
388
|
public toString(): string {
|
|
315
389
|
let s = '[';
|
|
316
|
-
for (let i:
|
|
390
|
+
for (let i: u32 = 0; i < this._length; i++) {
|
|
317
391
|
s += `${this.get(i)}`;
|
|
318
392
|
if (i < this._length - 1) {
|
|
319
393
|
s += ', ';
|
|
@@ -329,7 +403,7 @@ export abstract class StoredPackedArray<T> {
|
|
|
329
403
|
const slotIndex = changed[i];
|
|
330
404
|
const slotData = this._slots.get(slotIndex);
|
|
331
405
|
if (slotData) {
|
|
332
|
-
const ptr = this.calculateStoragePointer(slotIndex);
|
|
406
|
+
const ptr = this.calculateStoragePointer(<u64>slotIndex);
|
|
333
407
|
Blockchain.setStorageAt(ptr, slotData);
|
|
334
408
|
}
|
|
335
409
|
}
|
|
@@ -348,7 +422,7 @@ export abstract class StoredPackedArray<T> {
|
|
|
348
422
|
const keys = this._slots.keys();
|
|
349
423
|
for (let i = 0; i < keys.length; i++) {
|
|
350
424
|
const slotIndex = keys[i];
|
|
351
|
-
const ptr = this.calculateStoragePointer(slotIndex);
|
|
425
|
+
const ptr = this.calculateStoragePointer(<u64>slotIndex);
|
|
352
426
|
Blockchain.setStorageAt(ptr, GET_EMPTY_BUFFER()); // 32 bytes of zero
|
|
353
427
|
}
|
|
354
428
|
|
|
@@ -379,7 +453,7 @@ export abstract class StoredPackedArray<T> {
|
|
|
379
453
|
}
|
|
380
454
|
|
|
381
455
|
/** Number of T items that fit in one 32-byte slot. */
|
|
382
|
-
protected abstract getSlotCapacity():
|
|
456
|
+
protected abstract getSlotCapacity(): u32;
|
|
383
457
|
|
|
384
458
|
/** Return the "zero" value of type T. */
|
|
385
459
|
protected abstract zeroValue(): T;
|
|
@@ -387,10 +461,6 @@ export abstract class StoredPackedArray<T> {
|
|
|
387
461
|
/** Compare two T values for equality. */
|
|
388
462
|
protected abstract eq(a: T, b: T): bool;
|
|
389
463
|
|
|
390
|
-
// -----------------------------------------------------------
|
|
391
|
-
// Persistence (save, reset, etc.)
|
|
392
|
-
// -----------------------------------------------------------
|
|
393
|
-
|
|
394
464
|
/**
|
|
395
465
|
* Pack an array of T (length = getSlotCapacity()) into a 32-byte buffer.
|
|
396
466
|
*/
|
|
@@ -408,16 +478,12 @@ export abstract class StoredPackedArray<T> {
|
|
|
408
478
|
*/
|
|
409
479
|
protected abstract calculateStoragePointer(slotIndex: u64): Uint8Array;
|
|
410
480
|
|
|
411
|
-
// -----------------------------------------------------------
|
|
412
|
-
// Internal Slot-Loading Helpers
|
|
413
|
-
// -----------------------------------------------------------
|
|
414
|
-
|
|
415
481
|
/**
|
|
416
482
|
* Ensure that slotIndex is loaded into _slots. If missing, read from storage.
|
|
417
483
|
*/
|
|
418
|
-
protected ensureSlot(slotIndex:
|
|
484
|
+
protected ensureSlot(slotIndex: u32): Uint8Array {
|
|
419
485
|
if (!this._slots.has(slotIndex)) {
|
|
420
|
-
const ptr = this.calculateStoragePointer(slotIndex);
|
|
486
|
+
const ptr = this.calculateStoragePointer(<u64>slotIndex);
|
|
421
487
|
const data = Blockchain.getStorageAt(ptr);
|
|
422
488
|
|
|
423
489
|
// Must be exactly 32 bytes; if it's empty, you get 32 zero bytes from GET_EMPTY_BUFFER()
|
|
@@ -426,4 +492,14 @@ export abstract class StoredPackedArray<T> {
|
|
|
426
492
|
|
|
427
493
|
return this._slots.get(slotIndex);
|
|
428
494
|
}
|
|
495
|
+
|
|
496
|
+
private getRealIndex(index: u32, isPhysical: bool = false): u32 {
|
|
497
|
+
const maxLength: u64 = <u64>this.MAX_LENGTH;
|
|
498
|
+
let realIndex: u64 = (isPhysical ? <u64>0 : <u64>this._startIndex) + <u64>index;
|
|
499
|
+
if (!(realIndex < maxLength)) {
|
|
500
|
+
realIndex %= maxLength;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
return <u32>realIndex;
|
|
504
|
+
}
|
|
429
505
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StoredPackedArray } from './StoredPackedArray';
|
|
1
|
+
import { DEFAULT_MAX_LENGTH, StoredPackedArray } from './StoredPackedArray';
|
|
2
2
|
import { u128 } from '@btc-vision/as-bignum/assembly';
|
|
3
3
|
import { bigEndianAdd } from '../../math/bytes';
|
|
4
4
|
|
|
@@ -9,16 +9,16 @@ import { bigEndianAdd } from '../../math/bytes';
|
|
|
9
9
|
*/
|
|
10
10
|
@final
|
|
11
11
|
export class StoredU128Array extends StoredPackedArray<u128> {
|
|
12
|
-
public constructor(pointer: u16, subPointer: Uint8Array) {
|
|
13
|
-
super(pointer, subPointer, u128.Zero);
|
|
12
|
+
public constructor(pointer: u16, subPointer: Uint8Array, maxLength: u32 = DEFAULT_MAX_LENGTH) {
|
|
13
|
+
super(pointer, subPointer, u128.Zero, maxLength);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
protected getSlotCapacity():
|
|
16
|
+
protected getSlotCapacity(): u32 {
|
|
17
17
|
return 2; // 2 x u128 => 32 bytes
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
protected zeroValue(): u128 {
|
|
21
|
-
return u128.Zero;
|
|
21
|
+
return u128.Zero; // from the as-bignum library
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
protected eq(a: u128, b: u128): bool {
|
|
@@ -61,4 +61,4 @@ export class StoredU128Array extends StoredPackedArray<u128> {
|
|
|
61
61
|
protected calculateStoragePointer(slotIndex: u64): Uint8Array {
|
|
62
62
|
return bigEndianAdd(this.basePointer, slotIndex);
|
|
63
63
|
}
|
|
64
|
-
}
|
|
64
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StoredPackedArray } from './StoredPackedArray';
|
|
1
|
+
import { DEFAULT_MAX_LENGTH, StoredPackedArray } from './StoredPackedArray';
|
|
2
2
|
import { bigEndianAdd } from '../../math/bytes';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -6,11 +6,11 @@ import { bigEndianAdd } from '../../math/bytes';
|
|
|
6
6
|
*/
|
|
7
7
|
@final
|
|
8
8
|
export class StoredU16Array extends StoredPackedArray<u16> {
|
|
9
|
-
public constructor(pointer: u16, subPointer: Uint8Array) {
|
|
10
|
-
super(pointer, subPointer, 0);
|
|
9
|
+
public constructor(pointer: u16, subPointer: Uint8Array, maxLength: u32 = DEFAULT_MAX_LENGTH) {
|
|
10
|
+
super(pointer, subPointer, 0, maxLength);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
protected getSlotCapacity():
|
|
13
|
+
protected getSlotCapacity(): u32 {
|
|
14
14
|
return 16; // 16 x u16 = 32 bytes
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -30,8 +30,8 @@ export class StoredU16Array extends StoredPackedArray<u16> {
|
|
|
30
30
|
let offset = 0;
|
|
31
31
|
for (let i = 0; i < 16; i++) {
|
|
32
32
|
const v = values[i];
|
|
33
|
-
out[offset] = <u8>((v >> 8) & 0xff);
|
|
34
|
-
out[offset + 1] = <u8>(v & 0xff);
|
|
33
|
+
out[offset] = <u8>((v >> 8) & 0xff); // high byte
|
|
34
|
+
out[offset + 1] = <u8>(v & 0xff); // low byte
|
|
35
35
|
offset += 2;
|
|
36
36
|
}
|
|
37
37
|
return out;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StoredPackedArray } from './StoredPackedArray';
|
|
1
|
+
import { DEFAULT_MAX_LENGTH, StoredPackedArray } from './StoredPackedArray';
|
|
2
2
|
import { u256 } from '@btc-vision/as-bignum/assembly';
|
|
3
3
|
import { bigEndianAdd } from '../../math/bytes';
|
|
4
4
|
|
|
@@ -8,11 +8,11 @@ import { bigEndianAdd } from '../../math/bytes';
|
|
|
8
8
|
*/
|
|
9
9
|
@final
|
|
10
10
|
export class StoredU256Array extends StoredPackedArray<u256> {
|
|
11
|
-
public constructor(pointer: u16, subPointer: Uint8Array) {
|
|
12
|
-
super(pointer, subPointer, u256.Zero);
|
|
11
|
+
public constructor(pointer: u16, subPointer: Uint8Array, maxLength: u32 = DEFAULT_MAX_LENGTH) {
|
|
12
|
+
super(pointer, subPointer, u256.Zero, maxLength);
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
protected getSlotCapacity():
|
|
15
|
+
protected getSlotCapacity(): u32 {
|
|
16
16
|
return 1; // 1 x u256 => 32 bytes
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -36,4 +36,4 @@ export class StoredU256Array extends StoredPackedArray<u256> {
|
|
|
36
36
|
protected calculateStoragePointer(slotIndex: u64): Uint8Array {
|
|
37
37
|
return bigEndianAdd(this.basePointer, slotIndex);
|
|
38
38
|
}
|
|
39
|
-
}
|
|
39
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StoredPackedArray } from './StoredPackedArray';
|
|
1
|
+
import { DEFAULT_MAX_LENGTH, StoredPackedArray } from './StoredPackedArray';
|
|
2
2
|
import { bigEndianAdd } from '../../math/bytes';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -7,11 +7,11 @@ import { bigEndianAdd } from '../../math/bytes';
|
|
|
7
7
|
*/
|
|
8
8
|
@final
|
|
9
9
|
export class StoredU32Array extends StoredPackedArray<u32> {
|
|
10
|
-
public constructor(pointer: u16, subPointer: Uint8Array) {
|
|
11
|
-
super(pointer, subPointer, 0);
|
|
10
|
+
public constructor(pointer: u16, subPointer: Uint8Array, maxLength: u32 = DEFAULT_MAX_LENGTH) {
|
|
11
|
+
super(pointer, subPointer, 0, maxLength);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
protected getSlotCapacity():
|
|
14
|
+
protected getSlotCapacity(): u32 {
|
|
15
15
|
return 8; // 8 x u32 => 32 bytes
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -45,7 +45,7 @@ export class StoredU32Array extends StoredPackedArray<u32> {
|
|
|
45
45
|
const b1 = slotData[offset + 1];
|
|
46
46
|
const b2 = slotData[offset + 2];
|
|
47
47
|
const b3 = slotData[offset + 3];
|
|
48
|
-
out[i] = ((<u32>b0 << 24) | (<u32>b1 << 16) | (<u32>b2 << 8) | b3) as u32;
|
|
48
|
+
out[i] = (((<u32>b0) << 24) | ((<u32>b1) << 16) | ((<u32>b2) << 8) | b3) as u32;
|
|
49
49
|
offset += 4;
|
|
50
50
|
}
|
|
51
51
|
return out;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StoredPackedArray } from './StoredPackedArray';
|
|
1
|
+
import { DEFAULT_MAX_LENGTH, StoredPackedArray } from './StoredPackedArray';
|
|
2
2
|
import { bigEndianAdd } from '../../math/bytes';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -7,11 +7,11 @@ import { bigEndianAdd } from '../../math/bytes';
|
|
|
7
7
|
*/
|
|
8
8
|
@final
|
|
9
9
|
export class StoredU64Array extends StoredPackedArray<u64> {
|
|
10
|
-
public constructor(pointer: u16, subPointer: Uint8Array) {
|
|
11
|
-
super(pointer, subPointer, 0);
|
|
10
|
+
public constructor(pointer: u16, subPointer: Uint8Array, maxLength: u32 = DEFAULT_MAX_LENGTH) {
|
|
11
|
+
super(pointer, subPointer, 0, maxLength);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
protected getSlotCapacity():
|
|
14
|
+
protected getSlotCapacity(): u32 {
|
|
15
15
|
return 4; // 4 x u64 => 32 bytes
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -53,8 +53,15 @@ export class StoredU64Array extends StoredPackedArray<u64> {
|
|
|
53
53
|
const b5 = <u64>slotData[offset + 5];
|
|
54
54
|
const b6 = <u64>slotData[offset + 6];
|
|
55
55
|
const b7 = <u64>slotData[offset + 7];
|
|
56
|
-
out[i] =
|
|
57
|
-
(
|
|
56
|
+
out[i] =
|
|
57
|
+
(b0 << 56) |
|
|
58
|
+
(b1 << 48) |
|
|
59
|
+
(b2 << 40) |
|
|
60
|
+
(b3 << 32) |
|
|
61
|
+
(b4 << 24) |
|
|
62
|
+
(b5 << 16) |
|
|
63
|
+
(b6 << 8) |
|
|
64
|
+
b7;
|
|
58
65
|
offset += 8;
|
|
59
66
|
}
|
|
60
67
|
return out;
|
|
@@ -63,4 +70,4 @@ export class StoredU64Array extends StoredPackedArray<u64> {
|
|
|
63
70
|
protected calculateStoragePointer(slotIndex: u64): Uint8Array {
|
|
64
71
|
return bigEndianAdd(this.basePointer, slotIndex);
|
|
65
72
|
}
|
|
66
|
-
}
|
|
73
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StoredPackedArray } from './StoredPackedArray';
|
|
1
|
+
import { DEFAULT_MAX_LENGTH, StoredPackedArray } from './StoredPackedArray';
|
|
2
2
|
import { bigEndianAdd } from '../../math/bytes';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -7,11 +7,11 @@ import { bigEndianAdd } from '../../math/bytes';
|
|
|
7
7
|
*/
|
|
8
8
|
@final
|
|
9
9
|
export class StoredU8Array extends StoredPackedArray<u8> {
|
|
10
|
-
public constructor(pointer: u16, subPointer: Uint8Array) {
|
|
11
|
-
super(pointer, subPointer, 0);
|
|
10
|
+
public constructor(pointer: u16, subPointer: Uint8Array, maxLength: u32 = DEFAULT_MAX_LENGTH) {
|
|
11
|
+
super(pointer, subPointer, 0, maxLength);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
protected getSlotCapacity():
|
|
14
|
+
protected getSlotCapacity(): u32 {
|
|
15
15
|
return 32; // 32 bytes => 32 x u8
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -45,4 +45,4 @@ export class StoredU8Array extends StoredPackedArray<u8> {
|
|
|
45
45
|
// basePointer + (slotIndex+1) in big-endian
|
|
46
46
|
return bigEndianAdd(this.basePointer, slotIndex);
|
|
47
47
|
}
|
|
48
|
-
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import {u256} from '@btc-vision/as-bignum/assembly';
|
|
2
|
+
import {Blockchain} from '../../env';
|
|
3
|
+
import {EMPTY_BUFFER} from "../../math/bytes";
|
|
4
|
+
import {BytesWriter} from "../../buffer/BytesWriter";
|
|
5
|
+
import {encodePointerUnknownLength} from "../../math/abi";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* StoredMap<K, V> implementation using u256 as keys.
|
|
9
|
+
*/
|
|
10
|
+
@final
|
|
11
|
+
export class StoredMapU256 {
|
|
12
|
+
private readonly pointer: u16;
|
|
13
|
+
private readonly subPointer: Uint8Array;
|
|
14
|
+
|
|
15
|
+
constructor(pointer: u16, subPointer: Uint8Array = new Uint8Array(30)) {
|
|
16
|
+
this.pointer = pointer;
|
|
17
|
+
this.subPointer = subPointer;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Sets the value for a given key.
|
|
22
|
+
* @param key - The key of type K.
|
|
23
|
+
* @param value - The value of type V.
|
|
24
|
+
*/
|
|
25
|
+
public set(key: u256, value: u256): void {
|
|
26
|
+
const keyPointer = this.getKeyPointer(key);
|
|
27
|
+
Blockchain.setStorageAt(keyPointer, value.toUint8Array(true));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Retrieves the value for a given key.
|
|
32
|
+
* @param key - The key of type K.
|
|
33
|
+
* @returns The value of type V or null if the key does not exist.
|
|
34
|
+
*/
|
|
35
|
+
public get(key: u256): u256 {
|
|
36
|
+
const keyPointer = this.getKeyPointer(key);
|
|
37
|
+
return u256.fromUint8ArrayBE(Blockchain.getStorageAt(keyPointer));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Deletes the value for a given key.
|
|
42
|
+
* @param key - The key of type K.
|
|
43
|
+
*/
|
|
44
|
+
public delete(key: u256): void {
|
|
45
|
+
const keyPointer = this.getKeyPointer(key);
|
|
46
|
+
Blockchain.setStorageAt(keyPointer, EMPTY_BUFFER);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Generates the storage pointer for a given key.
|
|
51
|
+
* @param key - The key of type K.
|
|
52
|
+
* @returns The storage pointer as u256.
|
|
53
|
+
*/
|
|
54
|
+
private getKeyPointer(key: u256): Uint8Array {
|
|
55
|
+
const writer = new BytesWriter(64);
|
|
56
|
+
|
|
57
|
+
writer.writeBytes(this.subPointer);
|
|
58
|
+
writer.writeU256(key);
|
|
59
|
+
|
|
60
|
+
return encodePointerUnknownLength(this.pointer, writer.getBuffer());
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -154,6 +154,40 @@ export class SafeMath {
|
|
|
154
154
|
return c;
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
+
public static mul64(a: u64, b: u64): u64 {
|
|
158
|
+
if (a === 0 || b === 0) {
|
|
159
|
+
return 0;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const c: u64 = a * b;
|
|
163
|
+
|
|
164
|
+
if (c / a !== b) {
|
|
165
|
+
throw new Error('SafeMath: multiplication overflow');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return c;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
public static div64(a: u64, b: u64): u64 {
|
|
172
|
+
if (b === 0) {
|
|
173
|
+
throw new Error('Division by zero');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (a === 0) {
|
|
177
|
+
return 0;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (a < b) {
|
|
181
|
+
return 0; // Return 0 if a < b
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (a === b) {
|
|
185
|
+
return 1; // Return 1 if a == b
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return a / b;
|
|
189
|
+
}
|
|
190
|
+
|
|
157
191
|
public static div128(a: u128, b: u128): u128 {
|
|
158
192
|
if (b.isZero()) {
|
|
159
193
|
throw new Error('Division by zero');
|
|
@@ -226,6 +260,22 @@ export class SafeMath {
|
|
|
226
260
|
return result;
|
|
227
261
|
}
|
|
228
262
|
|
|
263
|
+
public static min64(a: u64, b: u64): u64 {
|
|
264
|
+
return a < b ? a : b;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
public static max64(a: u64, b: u64): u64 {
|
|
268
|
+
return a > b ? a : b;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
public static min128(a: u128, b: u128): u128 {
|
|
272
|
+
return u128.lt(a, b) ? a : b;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
public static max128(a: u128, b: u128): u128 {
|
|
276
|
+
return u128.gt(a, b) ? a : b;
|
|
277
|
+
}
|
|
278
|
+
|
|
229
279
|
public static min(a: u256, b: u256): u256 {
|
|
230
280
|
return u256.lt(a, b) ? a : b;
|
|
231
281
|
}
|