@btc-vision/btc-runtime 1.3.17 → 1.3.19
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/contracts/DeployableOP_20.ts +3 -2
- package/runtime/exports/index.ts +1 -0
- package/runtime/index.ts +15 -10
- package/runtime/storage/{StoredAddressArray.ts → arrays/StoredAddressArray.ts} +10 -6
- package/runtime/storage/{StoredBooleanArray.ts → arrays/StoredBooleanArray.ts} +9 -5
- package/runtime/storage/{StoredU128Array.ts → arrays/StoredU128Array.ts} +7 -9
- package/runtime/storage/{StoredU16Array.ts → arrays/StoredU16Array.ts} +8 -10
- package/runtime/storage/{StoredU256Array.ts → arrays/StoredU256Array.ts} +7 -9
- package/runtime/storage/arrays/StoredU32Array.ts +470 -0
- package/runtime/storage/arrays/StoredU8Array.ts +515 -0
- package/runtime/storage/{StorageLayout.ts → utils/StorageLayout.ts} +1 -1
- package/runtime/storage/{StorageSlot.ts → utils/StorageSlot.ts} +4 -4
- package/runtime/types/SafeMath.ts +8 -0
- /package/runtime/storage/{StorageBacked.ts → utils/StorageBacked.ts} +0 -0
- /package/runtime/storage/{StorageStruct.ts → utils/StorageStruct.ts} +0 -0
- /package/runtime/storage/{StorageValue.ts → utils/StorageValue.ts} +0 -0
package/package.json
CHANGED
|
@@ -309,10 +309,11 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
|
|
|
309
309
|
@unsafe
|
|
310
310
|
protected _unsafeTransferFrom(from: Address, to: Address, value: u256): boolean {
|
|
311
311
|
const balance: u256 = this.balanceOfMap.get(from);
|
|
312
|
-
if (balance < value)
|
|
312
|
+
if (balance < value) {
|
|
313
313
|
throw new Revert(
|
|
314
|
-
`TransferFrom insufficient balance of ${from} is ${balance} and value is ${value}`,
|
|
314
|
+
`TransferFrom insufficient balance of ${from.toHex()} is ${balance} and value is ${value}`,
|
|
315
315
|
);
|
|
316
|
+
}
|
|
316
317
|
|
|
317
318
|
const newBalance: u256 = SafeMath.sub(balance, value);
|
|
318
319
|
this.balanceOfMap.set(from, newBalance);
|
package/runtime/exports/index.ts
CHANGED
|
@@ -18,6 +18,7 @@ export function onDeploy(data: Uint8Array): void {
|
|
|
18
18
|
const calldata: Calldata = new BytesReader(data);
|
|
19
19
|
|
|
20
20
|
Blockchain.contract.onDeployment(calldata);
|
|
21
|
+
Blockchain.contract.onExecutionCompleted();
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
export function setEnvironment(data: Uint8Array): void {
|
package/runtime/index.ts
CHANGED
|
@@ -62,21 +62,26 @@ export * from './memory/Uint8ArrayMerger';
|
|
|
62
62
|
/** Storage */
|
|
63
63
|
export * from './storage/StoredU256';
|
|
64
64
|
export * from './storage/StoredU64';
|
|
65
|
-
export * from './storage/StoredU16Array';
|
|
66
|
-
export * from './storage/StoredBooleanArray';
|
|
67
|
-
export * from './storage/StoredU128Array';
|
|
68
|
-
export * from './storage/StoredU256Array';
|
|
69
65
|
export * from './storage/StoredString';
|
|
70
66
|
export * from './storage/StoredAddress';
|
|
71
67
|
export * from './storage/StoredBoolean';
|
|
72
68
|
export * from './storage/Serializable';
|
|
73
|
-
export * from './storage/StoredAddressArray';
|
|
74
69
|
|
|
75
|
-
|
|
76
|
-
export * from './storage/
|
|
77
|
-
export * from './storage/
|
|
78
|
-
export * from './storage/
|
|
79
|
-
export * from './storage/
|
|
70
|
+
/** Arrays */
|
|
71
|
+
export * from './storage/arrays/StoredAddressArray';
|
|
72
|
+
export * from './storage/arrays/StoredU8Array';
|
|
73
|
+
export * from './storage/arrays/StoredU16Array';
|
|
74
|
+
export * from './storage/arrays/StoredU32Array';
|
|
75
|
+
export * from './storage/arrays/StoredBooleanArray';
|
|
76
|
+
export * from './storage/arrays/StoredU128Array';
|
|
77
|
+
export * from './storage/arrays/StoredU256Array';
|
|
78
|
+
|
|
79
|
+
/** Storage Utils */
|
|
80
|
+
export * from './storage/utils/StorageBacked';
|
|
81
|
+
export * from './storage/utils/StorageSlot';
|
|
82
|
+
export * from './storage/utils/StorageStruct';
|
|
83
|
+
export * from './storage/utils/StorageLayout';
|
|
84
|
+
export * from './storage/utils/StorageValue';
|
|
80
85
|
|
|
81
86
|
/** Shared libraries */
|
|
82
87
|
export * from './shared-libraries/TransferHelper';
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { u256 } from '@btc-vision/as-bignum/assembly';
|
|
2
|
-
import { BytesWriter } from '
|
|
3
|
-
import { Blockchain } from '
|
|
4
|
-
import { Address } from '
|
|
5
|
-
import { Revert } from '
|
|
6
|
-
import { SafeMath } from '
|
|
2
|
+
import { BytesWriter } from '../../buffer/BytesWriter';
|
|
3
|
+
import { Blockchain } from '../../env';
|
|
4
|
+
import { Address } from '../../types/Address';
|
|
5
|
+
import { Revert } from '../../types/Revert';
|
|
6
|
+
import { SafeMath } from '../../types/SafeMath';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* @class StoredAddressArray
|
|
@@ -35,7 +35,11 @@ export class StoredAddressArray {
|
|
|
35
35
|
* @param {Uint8Array} subPointer - The sub-pointer for memory slot addressing.
|
|
36
36
|
* @param {Address} defaultValue - The default Address value if storage is uninitialized.
|
|
37
37
|
*/
|
|
38
|
-
constructor(
|
|
38
|
+
constructor(
|
|
39
|
+
public pointer: u16,
|
|
40
|
+
public subPointer: Uint8Array,
|
|
41
|
+
private defaultValue: Address,
|
|
42
|
+
) {
|
|
39
43
|
// Initialize the base pointer
|
|
40
44
|
const writer = new BytesWriter(32);
|
|
41
45
|
writer.writeU16(pointer);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { u256 } from '@btc-vision/as-bignum/assembly';
|
|
2
|
-
import { BytesWriter } from '
|
|
3
|
-
import { Blockchain } from '
|
|
4
|
-
import { Revert } from '
|
|
5
|
-
import { SafeMath } from '
|
|
2
|
+
import { BytesWriter } from '../../buffer/BytesWriter';
|
|
3
|
+
import { Blockchain } from '../../env';
|
|
4
|
+
import { Revert } from '../../types/Revert';
|
|
5
|
+
import { SafeMath } from '../../types/SafeMath';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @class StoredBooleanArray
|
|
@@ -33,7 +33,11 @@ export class StoredBooleanArray {
|
|
|
33
33
|
* @param {Uint8Array} subPointer - The sub-pointer for memory slot addressing.
|
|
34
34
|
* @param {u256} defaultValue - The default u256 value if storage is uninitialized.
|
|
35
35
|
*/
|
|
36
|
-
constructor(
|
|
36
|
+
constructor(
|
|
37
|
+
public pointer: u16,
|
|
38
|
+
public subPointer: Uint8Array,
|
|
39
|
+
private defaultValue: u256,
|
|
40
|
+
) {
|
|
37
41
|
// Initialize the base u256 pointer using the primary pointer and subPointer
|
|
38
42
|
const writer = new BytesWriter(32);
|
|
39
43
|
writer.writeU16(pointer);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { u128, u256 } from '@btc-vision/as-bignum/assembly';
|
|
2
|
-
import { Blockchain } from '
|
|
3
|
-
import { BytesWriter } from '
|
|
4
|
-
import { SafeMath } from '
|
|
5
|
-
import { Revert } from '
|
|
2
|
+
import { Blockchain } from '../../env';
|
|
3
|
+
import { BytesWriter } from '../../buffer/BytesWriter';
|
|
4
|
+
import { SafeMath } from '../../types/SafeMath';
|
|
5
|
+
import { Revert } from '../../types/Revert';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @class StoredU128Array
|
|
@@ -363,11 +363,9 @@ export class StoredU128Array {
|
|
|
363
363
|
throw new Revert('SetLength operation failed: Length exceeds maximum allowed value.');
|
|
364
364
|
}
|
|
365
365
|
|
|
366
|
-
if (newLength
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
this.delete(i);
|
|
370
|
-
}
|
|
366
|
+
if (newLength > this._startIndex) {
|
|
367
|
+
this._startIndex = newLength;
|
|
368
|
+
this._isChangedStartIndex = true;
|
|
371
369
|
}
|
|
372
370
|
|
|
373
371
|
this._length = newLength;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { u256 } from '@btc-vision/as-bignum/assembly';
|
|
2
|
-
import { Blockchain } from '
|
|
3
|
-
import { BytesWriter } from '
|
|
4
|
-
import { SafeMath } from '
|
|
5
|
-
import { Revert } from '
|
|
2
|
+
import { Blockchain } from '../../env';
|
|
3
|
+
import { BytesWriter } from '../../buffer/BytesWriter';
|
|
4
|
+
import { SafeMath } from '../../types/SafeMath';
|
|
5
|
+
import { Revert } from '../../types/Revert';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @class StoredU16Array
|
|
@@ -108,7 +108,7 @@ export class StoredU16Array {
|
|
|
108
108
|
const newIndex: u64 = this._length;
|
|
109
109
|
const wrappedIndex: u64 =
|
|
110
110
|
newIndex < this.MAX_LENGTH ? newIndex : newIndex % this.MAX_LENGTH;
|
|
111
|
-
|
|
111
|
+
|
|
112
112
|
const slotIndex: u64 = wrappedIndex / 16;
|
|
113
113
|
const subIndex: u8 = <u8>(wrappedIndex % 16);
|
|
114
114
|
|
|
@@ -354,11 +354,9 @@ export class StoredU16Array {
|
|
|
354
354
|
throw new Revert('SetLength operation failed: Length exceeds maximum allowed value.');
|
|
355
355
|
}
|
|
356
356
|
|
|
357
|
-
if (newLength
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
this.delete(i);
|
|
361
|
-
}
|
|
357
|
+
if (newLength > this._startIndex) {
|
|
358
|
+
this._startIndex = newLength;
|
|
359
|
+
this._isChangedStartIndex = true;
|
|
362
360
|
}
|
|
363
361
|
|
|
364
362
|
this._length = newLength;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { u256 } from '@btc-vision/as-bignum/assembly';
|
|
2
|
-
import { Blockchain } from '
|
|
3
|
-
import { BytesWriter } from '
|
|
4
|
-
import { SafeMath } from '
|
|
5
|
-
import { Revert } from '
|
|
2
|
+
import { Blockchain } from '../../env';
|
|
3
|
+
import { BytesWriter } from '../../buffer/BytesWriter';
|
|
4
|
+
import { SafeMath } from '../../types/SafeMath';
|
|
5
|
+
import { Revert } from '../../types/Revert';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @class StoredU256Array
|
|
@@ -364,11 +364,9 @@ export class StoredU256Array {
|
|
|
364
364
|
throw new Revert('SetLength operation failed: Length exceeds maximum allowed value.');
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
-
if (newLength
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
this.delete(i);
|
|
371
|
-
}
|
|
367
|
+
if (newLength > this._startIndex) {
|
|
368
|
+
this._startIndex = newLength;
|
|
369
|
+
this._isChangedStartIndex = true;
|
|
372
370
|
}
|
|
373
371
|
|
|
374
372
|
this._length = newLength;
|
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
import { 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';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @class StoredU32Array
|
|
9
|
+
* @description Manages an array of u32 values across multiple storage slots.
|
|
10
|
+
* Each slot holds **eight** u32 values packed into a single u256.
|
|
11
|
+
*/
|
|
12
|
+
@final
|
|
13
|
+
export class StoredU32Array {
|
|
14
|
+
private readonly baseU256Pointer: u256;
|
|
15
|
+
private readonly lengthPointer: u256;
|
|
16
|
+
|
|
17
|
+
// Internal cache for storage slots
|
|
18
|
+
private _values: Map<u64, u32[]> = new Map(); // Map from slotIndex to array of eight u32s
|
|
19
|
+
private _isLoaded: Set<u64> = new Set(); // Set of slotIndexes that are loaded
|
|
20
|
+
private _isChanged: Set<u64> = new Set(); // Set of slotIndexes that are modified
|
|
21
|
+
|
|
22
|
+
// Internal variables for length and startIndex management
|
|
23
|
+
private _length: u64 = 0; // Current length of the array
|
|
24
|
+
private _startIndex: u64 = 0; // Starting index of the array
|
|
25
|
+
private _isChangedLength: bool = false; // Indicates if the length has been modified
|
|
26
|
+
private _isChangedStartIndex: bool = false; // Indicates if the startIndex has been modified
|
|
27
|
+
|
|
28
|
+
// Define a maximum allowed length to prevent excessive storage usage
|
|
29
|
+
private readonly MAX_LENGTH: u64 = u64(u32.MAX_VALUE - 1);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @constructor
|
|
33
|
+
* @param {u16} pointer - The primary pointer identifier.
|
|
34
|
+
* @param {Uint8Array} subPointer - The sub-pointer for memory slot addressing.
|
|
35
|
+
* @param {u256} defaultValue - The default u256 value if storage is uninitialized.
|
|
36
|
+
*/
|
|
37
|
+
constructor(
|
|
38
|
+
public pointer: u16,
|
|
39
|
+
public subPointer: Uint8Array,
|
|
40
|
+
private defaultValue: u256,
|
|
41
|
+
) {
|
|
42
|
+
// Initialize the base pointer using the primary pointer and subPointer
|
|
43
|
+
const writer = new BytesWriter(32);
|
|
44
|
+
writer.writeU16(pointer);
|
|
45
|
+
writer.writeBytes(subPointer);
|
|
46
|
+
|
|
47
|
+
const baseU256Pointer = u256.fromBytes(writer.getBuffer(), true);
|
|
48
|
+
this.baseU256Pointer = baseU256Pointer;
|
|
49
|
+
|
|
50
|
+
// We’ll reuse the same pointer for length & startIndex
|
|
51
|
+
const lengthPointer = baseU256Pointer.clone();
|
|
52
|
+
this.lengthPointer = lengthPointer;
|
|
53
|
+
|
|
54
|
+
// Load current length + startIndex from storage
|
|
55
|
+
const storedLengthAndStartIndex: u256 = Blockchain.getStorageAt(lengthPointer, u256.Zero);
|
|
56
|
+
this._length = storedLengthAndStartIndex.lo1; // Bytes 0-7: length (u64)
|
|
57
|
+
this._startIndex = storedLengthAndStartIndex.lo2; // Bytes 8-15: startIndex (u64)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @method get
|
|
62
|
+
* @description Retrieves the u32 value at the specified global index.
|
|
63
|
+
* @param {u64} index - The global index (0 to ∞) of the u32 value to retrieve.
|
|
64
|
+
* @returns {u32} - The u32 value at the specified index.
|
|
65
|
+
*/
|
|
66
|
+
@inline
|
|
67
|
+
public get(index: u64): u32 {
|
|
68
|
+
assert(index < this._length, 'Index out of bounds');
|
|
69
|
+
|
|
70
|
+
const slotIndex: u64 = index / 8; // Each slot holds 8 u32s
|
|
71
|
+
const subIndex: u8 = <u8>(index % 8); // 0..7
|
|
72
|
+
this.ensureValues(slotIndex);
|
|
73
|
+
const slotValues = this._values.get(slotIndex);
|
|
74
|
+
return slotValues ? slotValues[subIndex] : 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @method set
|
|
79
|
+
* @description Sets the u32 value at the specified global index.
|
|
80
|
+
* @param {u64} index - The global index (0 to ∞) of the u32 value to set.
|
|
81
|
+
* @param {u32} value - The u32 value to assign.
|
|
82
|
+
*/
|
|
83
|
+
@inline
|
|
84
|
+
public set(index: u64, value: u32): void {
|
|
85
|
+
assert(index < this._length, 'Index exceeds current array length');
|
|
86
|
+
const slotIndex: u64 = index / 8;
|
|
87
|
+
const subIndex: u8 = <u8>(index % 8);
|
|
88
|
+
|
|
89
|
+
this.ensureValues(slotIndex);
|
|
90
|
+
const slotValues = this._values.get(slotIndex);
|
|
91
|
+
if (slotValues && slotValues[subIndex] !== value) {
|
|
92
|
+
slotValues[subIndex] = value;
|
|
93
|
+
this._isChanged.add(slotIndex);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @method push
|
|
99
|
+
* @description Appends a new u32 value to the end of the array.
|
|
100
|
+
* @param {u32} value - The u32 value to append.
|
|
101
|
+
*/
|
|
102
|
+
public push(value: u32): void {
|
|
103
|
+
if (this._length >= this.MAX_LENGTH) {
|
|
104
|
+
throw new Revert(
|
|
105
|
+
'Push operation failed: Array has reached its maximum allowed length.',
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const newIndex: u64 = this._length;
|
|
110
|
+
const wrappedIndex: u64 =
|
|
111
|
+
newIndex < this.MAX_LENGTH ? newIndex : newIndex % this.MAX_LENGTH;
|
|
112
|
+
|
|
113
|
+
const slotIndex: u64 = wrappedIndex / 8;
|
|
114
|
+
const subIndex: u8 = <u8>(wrappedIndex % 8);
|
|
115
|
+
|
|
116
|
+
// Ensure the slot is loaded
|
|
117
|
+
this.ensureValues(slotIndex);
|
|
118
|
+
|
|
119
|
+
// Set the new value
|
|
120
|
+
const slotValues = this._values.get(slotIndex);
|
|
121
|
+
if (slotValues) {
|
|
122
|
+
slotValues[subIndex] = value;
|
|
123
|
+
this._isChanged.add(slotIndex);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Increment the length
|
|
127
|
+
this._length += 1;
|
|
128
|
+
this._isChangedLength = true;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* @method delete
|
|
133
|
+
* @description Deletes the u32 value at the specified index by setting it to zero (does not reorder).
|
|
134
|
+
* @param {u64} index - The global index of the u32 value to delete.
|
|
135
|
+
*/
|
|
136
|
+
public delete(index: u64): void {
|
|
137
|
+
if (index >= this._length) {
|
|
138
|
+
throw new Revert('Delete operation failed: Index out of bounds.');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const slotIndex: u64 = index / 8;
|
|
142
|
+
const subIndex: u8 = <u8>(index % 8);
|
|
143
|
+
this.ensureValues(slotIndex);
|
|
144
|
+
|
|
145
|
+
const slotValues = this._values.get(slotIndex);
|
|
146
|
+
if (slotValues && slotValues[subIndex] !== 0) {
|
|
147
|
+
slotValues[subIndex] = 0;
|
|
148
|
+
this._isChanged.add(slotIndex);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* @method shift
|
|
154
|
+
* @description Removes the first element of the array by zeroing it out, decrementing length,
|
|
155
|
+
* and incrementing the startIndex (with wrap-around).
|
|
156
|
+
*/
|
|
157
|
+
public shift(): void {
|
|
158
|
+
if (this._length === 0) {
|
|
159
|
+
throw new Revert('Shift operation failed: Array is empty.');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const currentStartIndex: u64 = this._startIndex;
|
|
163
|
+
const slotIndex: u64 = currentStartIndex / 8;
|
|
164
|
+
const subIndex: u8 = <u8>(currentStartIndex % 8);
|
|
165
|
+
|
|
166
|
+
this.ensureValues(slotIndex);
|
|
167
|
+
const slotValues = this._values.get(slotIndex);
|
|
168
|
+
if (slotValues && slotValues[subIndex] !== 0) {
|
|
169
|
+
slotValues[subIndex] = 0;
|
|
170
|
+
this._isChanged.add(slotIndex);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Decrement length
|
|
174
|
+
this._length -= 1;
|
|
175
|
+
this._isChangedLength = true;
|
|
176
|
+
|
|
177
|
+
// Increment startIndex with wrap-around
|
|
178
|
+
if (this._startIndex < this.MAX_LENGTH - 1) {
|
|
179
|
+
this._startIndex += 1;
|
|
180
|
+
} else {
|
|
181
|
+
this._startIndex = 0;
|
|
182
|
+
}
|
|
183
|
+
this._isChangedStartIndex = true;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* @method save
|
|
188
|
+
* @description Persists all modified slots and the current length/startIndex into storage.
|
|
189
|
+
*/
|
|
190
|
+
public save(): void {
|
|
191
|
+
// Save changed slots
|
|
192
|
+
const changedSlots = this._isChanged.values();
|
|
193
|
+
for (let i = 0; i < changedSlots.length; i++) {
|
|
194
|
+
const slotIndex = changedSlots[i];
|
|
195
|
+
const packed = this.packValues(slotIndex);
|
|
196
|
+
const storagePointer = this.calculateStoragePointer(slotIndex);
|
|
197
|
+
Blockchain.setStorageAt(storagePointer, packed);
|
|
198
|
+
}
|
|
199
|
+
this._isChanged.clear();
|
|
200
|
+
|
|
201
|
+
// Save length + startIndex if changed
|
|
202
|
+
if (this._isChangedLength || this._isChangedStartIndex) {
|
|
203
|
+
const packedLengthAndStartIndex = new u256();
|
|
204
|
+
packedLengthAndStartIndex.lo1 = this._length;
|
|
205
|
+
packedLengthAndStartIndex.lo2 = this._startIndex;
|
|
206
|
+
|
|
207
|
+
Blockchain.setStorageAt(this.lengthPointer, packedLengthAndStartIndex);
|
|
208
|
+
this._isChangedLength = false;
|
|
209
|
+
this._isChangedStartIndex = false;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* @method deleteAll
|
|
215
|
+
* @description Deletes all storage slots and resets length/startIndex to zero.
|
|
216
|
+
*/
|
|
217
|
+
public deleteAll(): void {
|
|
218
|
+
// Clear loaded slots from storage
|
|
219
|
+
const keys = this._values.keys();
|
|
220
|
+
for (let i = 0; i < keys.length; i++) {
|
|
221
|
+
const slotIndex = keys[i];
|
|
222
|
+
const storagePointer = this.calculateStoragePointer(slotIndex);
|
|
223
|
+
Blockchain.setStorageAt(storagePointer, u256.Zero);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Reset length + startIndex
|
|
227
|
+
Blockchain.setStorageAt(this.lengthPointer, u256.Zero);
|
|
228
|
+
this._length = 0;
|
|
229
|
+
this._startIndex = 0;
|
|
230
|
+
this._isChangedLength = false;
|
|
231
|
+
this._isChangedStartIndex = false;
|
|
232
|
+
|
|
233
|
+
// Clear caches
|
|
234
|
+
this._values.clear();
|
|
235
|
+
this._isLoaded.clear();
|
|
236
|
+
this._isChanged.clear();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* @method setMultiple
|
|
241
|
+
* @description Sets multiple u32 values starting at a given global index.
|
|
242
|
+
* @param {u64} startIndex - The starting global index.
|
|
243
|
+
* @param {u32[]} values - The array of u32 values to set.
|
|
244
|
+
*/
|
|
245
|
+
@inline
|
|
246
|
+
public setMultiple(startIndex: u64, values: u32[]): void {
|
|
247
|
+
for (let i: u64 = 0; i < values.length; i++) {
|
|
248
|
+
this.set(startIndex + i, values[i]);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* @method getAll
|
|
254
|
+
* @description Retrieves a consecutive range of u32 values starting at a given global index.
|
|
255
|
+
* @param {u64} startIndex - The starting global index.
|
|
256
|
+
* @param {u64} count - The number of u32 values to retrieve.
|
|
257
|
+
* @returns {u32[]} - The requested slice of the array.
|
|
258
|
+
*/
|
|
259
|
+
@inline
|
|
260
|
+
public getAll(startIndex: u64, count: u64): u32[] {
|
|
261
|
+
assert(startIndex + count <= this._length, 'Requested range exceeds array length');
|
|
262
|
+
|
|
263
|
+
if (u32.MAX_VALUE < count) {
|
|
264
|
+
throw new Revert('Requested range exceeds maximum allowed value.');
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const result: u32[] = new Array<u32>(count as u32);
|
|
268
|
+
for (let i: u64 = 0; i < count; i++) {
|
|
269
|
+
result[i as u32] = this.get(startIndex + i);
|
|
270
|
+
}
|
|
271
|
+
return result;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* @method toString
|
|
276
|
+
* @description Returns a string representation of all cached u32 values.
|
|
277
|
+
* @returns {string} - A string in the format "[val0, val1, ..., valN]".
|
|
278
|
+
*/
|
|
279
|
+
@inline
|
|
280
|
+
public toString(): string {
|
|
281
|
+
let str = '[';
|
|
282
|
+
for (let i: u64 = 0; i < this._length; i++) {
|
|
283
|
+
const value = this.get(i);
|
|
284
|
+
str += value.toString();
|
|
285
|
+
if (i !== this._length - 1) {
|
|
286
|
+
str += ', ';
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
str += ']';
|
|
290
|
+
return str;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* @method toBytes
|
|
295
|
+
* @description Packs all cached slots into u256 and returns them as a byte array.
|
|
296
|
+
* @returns {u8[]} - The packed u256 values in byte form.
|
|
297
|
+
*/
|
|
298
|
+
@inline
|
|
299
|
+
public toBytes(): u8[] {
|
|
300
|
+
const bytes: u8[] = new Array<u8>();
|
|
301
|
+
const slotCount: u64 = (this._length + 7) / 8; // each slot has 8 values
|
|
302
|
+
|
|
303
|
+
for (let slotIndex: u64 = 0; slotIndex < slotCount; slotIndex++) {
|
|
304
|
+
this.ensureValues(slotIndex);
|
|
305
|
+
const packed = this.packValues(slotIndex);
|
|
306
|
+
const slotBytes = packed.toBytes();
|
|
307
|
+
for (let i: u32 = 0; i < slotBytes.length; i++) {
|
|
308
|
+
bytes.push(slotBytes[i]);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return bytes;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* @method reset
|
|
316
|
+
* @description Zeros out the entire array and resets length/startIndex to zero, persisting changes immediately.
|
|
317
|
+
*/
|
|
318
|
+
@inline
|
|
319
|
+
public reset(): void {
|
|
320
|
+
this._length = 0;
|
|
321
|
+
this._startIndex = 0;
|
|
322
|
+
this._isChangedLength = true;
|
|
323
|
+
this._isChangedStartIndex = true;
|
|
324
|
+
this.save();
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* @method getLength
|
|
329
|
+
* @description Returns the current length of the array.
|
|
330
|
+
* @returns {u64} - The length.
|
|
331
|
+
*/
|
|
332
|
+
@inline
|
|
333
|
+
public getLength(): u64 {
|
|
334
|
+
return this._length;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* @method startingIndex
|
|
339
|
+
* @description Returns the current starting index of the array.
|
|
340
|
+
* @returns {u64} - The startIndex.
|
|
341
|
+
*/
|
|
342
|
+
@inline
|
|
343
|
+
public startingIndex(): u64 {
|
|
344
|
+
return this._startIndex;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* @method setLength
|
|
349
|
+
* @description Adjusts the length of the array (may truncate if newLength < currentLength).
|
|
350
|
+
* @param {u64} newLength - The new length to set.
|
|
351
|
+
*/
|
|
352
|
+
public setLength(newLength: u64): void {
|
|
353
|
+
if (newLength > this.MAX_LENGTH) {
|
|
354
|
+
throw new Revert('SetLength operation failed: Length exceeds maximum allowed value.');
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (newLength > this._startIndex) {
|
|
358
|
+
this._startIndex = newLength;
|
|
359
|
+
this._isChangedStartIndex = true;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
this._length = newLength;
|
|
363
|
+
this._isChangedLength = true;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* @method deleteLast
|
|
368
|
+
* @description Deletes the last element of the array by setting it to zero and decrementing the length.
|
|
369
|
+
*/
|
|
370
|
+
public deleteLast(): void {
|
|
371
|
+
if (this._length === 0) {
|
|
372
|
+
throw new Revert('DeleteLast operation failed: Array is empty.');
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const index = this._length - 1;
|
|
376
|
+
this.delete(index);
|
|
377
|
+
|
|
378
|
+
this._length -= 1;
|
|
379
|
+
this._isChangedLength = true;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* @private
|
|
384
|
+
* @method ensureValues
|
|
385
|
+
* @description Loads the slot data from storage if not already in cache.
|
|
386
|
+
* @param {u64} slotIndex - The slot index.
|
|
387
|
+
*/
|
|
388
|
+
private ensureValues(slotIndex: u64): void {
|
|
389
|
+
if (!this._isLoaded.has(slotIndex)) {
|
|
390
|
+
const storagePointer = this.calculateStoragePointer(slotIndex);
|
|
391
|
+
const storedU256: u256 = Blockchain.getStorageAt(storagePointer, this.defaultValue);
|
|
392
|
+
const slotValues = this.unpackU256(storedU256);
|
|
393
|
+
this._values.set(slotIndex, slotValues);
|
|
394
|
+
this._isLoaded.add(slotIndex);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* @private
|
|
400
|
+
* @method packValues
|
|
401
|
+
* @description Packs eight u32 values into a single u256 (lo1, lo2, hi1, hi2).
|
|
402
|
+
* @param {u64} slotIndex - The slot index.
|
|
403
|
+
* @returns {u256} - The packed u256.
|
|
404
|
+
*/
|
|
405
|
+
private packValues(slotIndex: u64): u256 {
|
|
406
|
+
const values = this._values.get(slotIndex);
|
|
407
|
+
if (!values) {
|
|
408
|
+
return u256.Zero;
|
|
409
|
+
}
|
|
410
|
+
const packed = new u256();
|
|
411
|
+
|
|
412
|
+
// Each 64 bits can store two u32:
|
|
413
|
+
// lo1 = (values[0], values[1])
|
|
414
|
+
// lo2 = (values[2], values[3])
|
|
415
|
+
// hi1 = (values[4], values[5])
|
|
416
|
+
// hi2 = (values[6], values[7])
|
|
417
|
+
|
|
418
|
+
// Pack into lo1
|
|
419
|
+
packed.lo1 = (u64(values[0]) << 32) | (u64(values[1]) & 0xffffffff);
|
|
420
|
+
|
|
421
|
+
// Pack into lo2
|
|
422
|
+
packed.lo2 = (u64(values[2]) << 32) | (u64(values[3]) & 0xffffffff);
|
|
423
|
+
|
|
424
|
+
// Pack into hi1
|
|
425
|
+
packed.hi1 = (u64(values[4]) << 32) | (u64(values[5]) & 0xffffffff);
|
|
426
|
+
|
|
427
|
+
// Pack into hi2
|
|
428
|
+
packed.hi2 = (u64(values[6]) << 32) | (u64(values[7]) & 0xffffffff);
|
|
429
|
+
|
|
430
|
+
return packed;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* @private
|
|
435
|
+
* @method unpackU256
|
|
436
|
+
* @description Unpacks a u256 into an array of eight u32 values.
|
|
437
|
+
* @param {u256} storedU256 - The stored u256 data.
|
|
438
|
+
* @returns {u32[]} - The array of eight u32s.
|
|
439
|
+
*/
|
|
440
|
+
private unpackU256(storedU256: u256): u32[] {
|
|
441
|
+
const values: u32[] = new Array<u32>(8);
|
|
442
|
+
|
|
443
|
+
// Extract each pair of u32 from lo1, lo2, hi1, hi2
|
|
444
|
+
values[0] = u32(storedU256.lo1 >> 32);
|
|
445
|
+
values[1] = u32(storedU256.lo1 & 0xffffffff);
|
|
446
|
+
|
|
447
|
+
values[2] = u32(storedU256.lo2 >> 32);
|
|
448
|
+
values[3] = u32(storedU256.lo2 & 0xffffffff);
|
|
449
|
+
|
|
450
|
+
values[4] = u32(storedU256.hi1 >> 32);
|
|
451
|
+
values[5] = u32(storedU256.hi1 & 0xffffffff);
|
|
452
|
+
|
|
453
|
+
values[6] = u32(storedU256.hi2 >> 32);
|
|
454
|
+
values[7] = u32(storedU256.hi2 & 0xffffffff);
|
|
455
|
+
|
|
456
|
+
return values;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* @private
|
|
461
|
+
* @method calculateStoragePointer
|
|
462
|
+
* @description Derives the storage pointer for a slot index by adding (slotIndex + 1) to the base pointer.
|
|
463
|
+
* @param {u64} slotIndex - The slot index.
|
|
464
|
+
* @returns {u256} - The resulting storage pointer.
|
|
465
|
+
*/
|
|
466
|
+
private calculateStoragePointer(slotIndex: u64): u256 {
|
|
467
|
+
// We offset by +1 so we don't collide with the length pointer (stored at basePointer itself).
|
|
468
|
+
return SafeMath.add(this.baseU256Pointer, u256.fromU64(slotIndex + 1));
|
|
469
|
+
}
|
|
470
|
+
}
|
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
import { u256 } from '@btc-vision/as-bignum/assembly';
|
|
2
|
+
import { BytesWriter } from '../../buffer/BytesWriter';
|
|
3
|
+
import { Blockchain } from '../../env';
|
|
4
|
+
import { Revert } from '../../types/Revert';
|
|
5
|
+
import { SafeMath } from '../../types/SafeMath';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @class StoredU8Array
|
|
9
|
+
* @description Manages an array of u8 values across multiple storage slots. Each slot holds thirty-two u8 values packed into a u256.
|
|
10
|
+
*/
|
|
11
|
+
@final
|
|
12
|
+
export class StoredU8Array {
|
|
13
|
+
private readonly baseU256Pointer: u256;
|
|
14
|
+
private readonly lengthPointer: u256;
|
|
15
|
+
|
|
16
|
+
// Internal cache for storage slots
|
|
17
|
+
private _values: Map<u64, u8[]> = new Map(); // Map from slotIndex to array of thirty-two u8s
|
|
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;
|
|
25
|
+
private _isChangedStartIndex: bool = false;
|
|
26
|
+
|
|
27
|
+
// Define a maximum allowed length to prevent excessive storage usage
|
|
28
|
+
private readonly MAX_LENGTH: u64 = u64(u32.MAX_VALUE - 1);
|
|
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
|
+
const baseU256Pointer = u256.fromBytes(writer.getBuffer(), true);
|
|
47
|
+
const lengthPointer = baseU256Pointer.clone();
|
|
48
|
+
|
|
49
|
+
// Load the current length and startIndex from storage
|
|
50
|
+
const storedLengthAndStartIndex: u256 = Blockchain.getStorageAt(lengthPointer, u256.Zero);
|
|
51
|
+
this.lengthPointer = lengthPointer;
|
|
52
|
+
this.baseU256Pointer = baseU256Pointer;
|
|
53
|
+
|
|
54
|
+
this._length = storedLengthAndStartIndex.lo1; // Bytes 0-7: length
|
|
55
|
+
this._startIndex = storedLengthAndStartIndex.lo2; // Bytes 8-15: startIndex
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @method get
|
|
60
|
+
* @description Retrieves the u8 value at the specified global index.
|
|
61
|
+
* @param {u64} index - The global index (0 to ∞) of the u8 value to retrieve.
|
|
62
|
+
* @returns {u8} - The u8 value at the specified index.
|
|
63
|
+
*/
|
|
64
|
+
@inline
|
|
65
|
+
public get(index: u64): u8 {
|
|
66
|
+
assert(index < this._length, 'Index out of bounds');
|
|
67
|
+
|
|
68
|
+
const slotIndex: u64 = index / 32; // Each slot holds thirty-two u8s
|
|
69
|
+
const subIndex: u8 = <u8>(index % 32);
|
|
70
|
+
this.ensureValues(slotIndex);
|
|
71
|
+
const slotValues = this._values.get(slotIndex);
|
|
72
|
+
return slotValues ? slotValues[subIndex] : 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @method set
|
|
77
|
+
* @description Sets the u8 value at the specified global index.
|
|
78
|
+
* @param {u64} index - The global index (0 to ∞) of the u8 value to set.
|
|
79
|
+
* @param {u8} value - The u8 value to assign.
|
|
80
|
+
*/
|
|
81
|
+
@inline
|
|
82
|
+
public set(index: u64, value: u8): void {
|
|
83
|
+
assert(index < this._length, 'Index exceeds current array length');
|
|
84
|
+
|
|
85
|
+
const slotIndex: u64 = index / 32;
|
|
86
|
+
const subIndex: u8 = <u8>(index % 32);
|
|
87
|
+
this.ensureValues(slotIndex);
|
|
88
|
+
|
|
89
|
+
const slotValues = this._values.get(slotIndex);
|
|
90
|
+
if (slotValues && slotValues[subIndex] !== value) {
|
|
91
|
+
slotValues[subIndex] = value;
|
|
92
|
+
this._isChanged.add(slotIndex);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @method push
|
|
98
|
+
* @description Appends a new u8 value to the end of the array.
|
|
99
|
+
* @param {u8} value - The u8 value to append.
|
|
100
|
+
*/
|
|
101
|
+
public push(value: u8): void {
|
|
102
|
+
if (this._length >= this.MAX_LENGTH) {
|
|
103
|
+
throw new Revert(
|
|
104
|
+
'Push operation failed: Array has reached its maximum allowed length.',
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const newIndex: u64 = this._length;
|
|
109
|
+
const wrappedIndex: u64 =
|
|
110
|
+
newIndex < this.MAX_LENGTH ? newIndex : newIndex % this.MAX_LENGTH;
|
|
111
|
+
|
|
112
|
+
const slotIndex: u64 = wrappedIndex / 32;
|
|
113
|
+
const subIndex: u8 = <u8>(wrappedIndex % 32);
|
|
114
|
+
|
|
115
|
+
this.ensureValues(slotIndex);
|
|
116
|
+
|
|
117
|
+
const slotValues = this._values.get(slotIndex);
|
|
118
|
+
if (slotValues) {
|
|
119
|
+
slotValues[subIndex] = value;
|
|
120
|
+
this._isChanged.add(slotIndex);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
this._length += 1;
|
|
124
|
+
this._isChangedLength = true;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @method delete
|
|
129
|
+
* @description Deletes the u8 value at the specified index by setting it to zero. Does not reorder the array.
|
|
130
|
+
* @param {u64} index - The global index of the u8 value to delete.
|
|
131
|
+
*/
|
|
132
|
+
public delete(index: u64): void {
|
|
133
|
+
if (index >= this._length) {
|
|
134
|
+
throw new Revert('Delete operation failed: Index out of bounds.');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const slotIndex: u64 = index / 32;
|
|
138
|
+
const subIndex: u8 = <u8>(index % 32);
|
|
139
|
+
this.ensureValues(slotIndex);
|
|
140
|
+
|
|
141
|
+
const slotValues = this._values.get(slotIndex);
|
|
142
|
+
if (slotValues && slotValues[subIndex] !== 0) {
|
|
143
|
+
slotValues[subIndex] = 0;
|
|
144
|
+
this._isChanged.add(slotIndex);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* @method shift
|
|
150
|
+
* @description Removes the first element of the array by setting it to zero, decrementing the length,
|
|
151
|
+
* and incrementing the startIndex (with wrap-around if needed).
|
|
152
|
+
*/
|
|
153
|
+
public shift(): void {
|
|
154
|
+
if (this._length === 0) {
|
|
155
|
+
throw new Revert('Shift operation failed: Array is empty.');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const currentStartIndex: u64 = this._startIndex;
|
|
159
|
+
const slotIndex: u64 = currentStartIndex / 32;
|
|
160
|
+
const subIndex: u8 = <u8>(currentStartIndex % 32);
|
|
161
|
+
this.ensureValues(slotIndex);
|
|
162
|
+
|
|
163
|
+
const slotValues = this._values.get(slotIndex);
|
|
164
|
+
if (slotValues && slotValues[subIndex] !== 0) {
|
|
165
|
+
slotValues[subIndex] = 0;
|
|
166
|
+
this._isChanged.add(slotIndex);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
this._length -= 1;
|
|
170
|
+
this._isChangedLength = true;
|
|
171
|
+
|
|
172
|
+
if (this._startIndex < this.MAX_LENGTH - 1) {
|
|
173
|
+
this._startIndex += 1;
|
|
174
|
+
} else {
|
|
175
|
+
this._startIndex = 0;
|
|
176
|
+
}
|
|
177
|
+
this._isChangedStartIndex = true;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* @method save
|
|
182
|
+
* @description Persists all cached u8 values, the length, and the startIndex to their respective storage slots if any have been modified.
|
|
183
|
+
*/
|
|
184
|
+
public save(): void {
|
|
185
|
+
// Save all changed slots
|
|
186
|
+
const changedSlots = this._isChanged.values();
|
|
187
|
+
for (let i = 0; i < changedSlots.length; i++) {
|
|
188
|
+
const slotIndex = changedSlots[i];
|
|
189
|
+
const packed = this.packValues(slotIndex);
|
|
190
|
+
const storagePointer = this.calculateStoragePointer(slotIndex);
|
|
191
|
+
Blockchain.setStorageAt(storagePointer, packed);
|
|
192
|
+
}
|
|
193
|
+
this._isChanged.clear();
|
|
194
|
+
|
|
195
|
+
// Save length and startIndex if changed
|
|
196
|
+
if (this._isChangedLength || this._isChangedStartIndex) {
|
|
197
|
+
const packedLengthAndStartIndex = new u256();
|
|
198
|
+
packedLengthAndStartIndex.lo1 = this._length;
|
|
199
|
+
packedLengthAndStartIndex.lo2 = this._startIndex;
|
|
200
|
+
Blockchain.setStorageAt(this.lengthPointer, packedLengthAndStartIndex);
|
|
201
|
+
this._isChangedLength = false;
|
|
202
|
+
this._isChangedStartIndex = false;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* @method deleteAll
|
|
208
|
+
* @description Deletes all storage slots by setting them to zero, including the length and startIndex slots.
|
|
209
|
+
*/
|
|
210
|
+
public deleteAll(): void {
|
|
211
|
+
// Iterate over all loaded slots and clear them
|
|
212
|
+
const keys = this._values.keys();
|
|
213
|
+
for (let i = 0; i < keys.length; i++) {
|
|
214
|
+
const slotIndex = keys[i];
|
|
215
|
+
const storagePointer = this.calculateStoragePointer(slotIndex);
|
|
216
|
+
Blockchain.setStorageAt(storagePointer, u256.Zero);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Reset length and startIndex
|
|
220
|
+
Blockchain.setStorageAt(this.lengthPointer, u256.Zero);
|
|
221
|
+
this._length = 0;
|
|
222
|
+
this._startIndex = 0;
|
|
223
|
+
this._isChangedLength = false;
|
|
224
|
+
this._isChangedStartIndex = false;
|
|
225
|
+
|
|
226
|
+
// Clear internal caches
|
|
227
|
+
this._values.clear();
|
|
228
|
+
this._isLoaded.clear();
|
|
229
|
+
this._isChanged.clear();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* @method setMultiple
|
|
234
|
+
* @description Sets multiple u8 values starting from a specific global index.
|
|
235
|
+
* @param {u64} startIndex - The starting global index.
|
|
236
|
+
* @param {u8[]} values - An array of u8 values to set.
|
|
237
|
+
*/
|
|
238
|
+
@inline
|
|
239
|
+
public setMultiple(startIndex: u64, values: u8[]): void {
|
|
240
|
+
for (let i: u64 = 0; i < values.length; i++) {
|
|
241
|
+
this.set(startIndex + i, values[i]);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* @method getAll
|
|
247
|
+
* @description Retrieves a range of u8 values starting from a specific global index.
|
|
248
|
+
* @param {u64} startIndex - The starting global index.
|
|
249
|
+
* @param {u64} count - The number of u8 values to retrieve.
|
|
250
|
+
* @returns {u8[]} - An array containing the retrieved u8 values.
|
|
251
|
+
*/
|
|
252
|
+
@inline
|
|
253
|
+
public getAll(startIndex: u64, count: u64): u8[] {
|
|
254
|
+
assert(startIndex + count <= this._length, 'Requested range exceeds array length');
|
|
255
|
+
if (u32.MAX_VALUE < count) {
|
|
256
|
+
throw new Revert('Requested range exceeds maximum allowed value.');
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const result: u8[] = new Array<u8>(count as u32);
|
|
260
|
+
for (let i: u64 = 0; i < count; i++) {
|
|
261
|
+
result[i as u32] = this.get(startIndex + i);
|
|
262
|
+
}
|
|
263
|
+
return result;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* @method toString
|
|
268
|
+
* @description Returns a string representation of all cached u8 values.
|
|
269
|
+
* @returns {string} - A string in the format "[value0, value1, ..., valueN]".
|
|
270
|
+
*/
|
|
271
|
+
@inline
|
|
272
|
+
public toString(): string {
|
|
273
|
+
let str = '[';
|
|
274
|
+
for (let i: u64 = 0; i < this._length; i++) {
|
|
275
|
+
const value = this.get(i);
|
|
276
|
+
str += value.toString();
|
|
277
|
+
if (i !== this._length - 1) {
|
|
278
|
+
str += ', ';
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
str += ']';
|
|
282
|
+
return str;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* @method toBytes
|
|
287
|
+
* @description Returns the packed u256 values as a byte array.
|
|
288
|
+
* @returns {u8[]} - The packed u256 values in byte form.
|
|
289
|
+
*/
|
|
290
|
+
@inline
|
|
291
|
+
public toBytes(): u8[] {
|
|
292
|
+
const bytes: u8[] = new Array<u8>();
|
|
293
|
+
const slotCount: u64 = (this._length + 31) / 32;
|
|
294
|
+
|
|
295
|
+
for (let slotIndex: u64 = 0; slotIndex < slotCount; slotIndex++) {
|
|
296
|
+
this.ensureValues(slotIndex);
|
|
297
|
+
const packed = this.packValues(slotIndex);
|
|
298
|
+
const slotBytes = packed.toBytes();
|
|
299
|
+
for (let i: u32 = 0; i < slotBytes.length; i++) {
|
|
300
|
+
bytes.push(slotBytes[i]);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return bytes;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* @method reset
|
|
308
|
+
* @description Resets all cached u8 values to zero and marks them as changed, including resetting the length and startIndex.
|
|
309
|
+
*/
|
|
310
|
+
@inline
|
|
311
|
+
public reset(): void {
|
|
312
|
+
this._length = 0;
|
|
313
|
+
this._startIndex = 0;
|
|
314
|
+
this._isChangedLength = true;
|
|
315
|
+
this._isChangedStartIndex = true;
|
|
316
|
+
this.save();
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* @method getLength
|
|
321
|
+
* @description Retrieves the current length of the array.
|
|
322
|
+
* @returns {u64} - The current length.
|
|
323
|
+
*/
|
|
324
|
+
@inline
|
|
325
|
+
public getLength(): u64 {
|
|
326
|
+
return this._length;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* @method startingIndex
|
|
331
|
+
* @description Retrieves the current starting index of the array.
|
|
332
|
+
* @returns {u64} - The starting index.
|
|
333
|
+
*/
|
|
334
|
+
public startingIndex(): u64 {
|
|
335
|
+
return this._startIndex;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* @method setLength
|
|
340
|
+
* @description Sets the length of the array.
|
|
341
|
+
* @param {u64} newLength - The new length to set.
|
|
342
|
+
*/
|
|
343
|
+
public setLength(newLength: u64): void {
|
|
344
|
+
if (newLength > this.MAX_LENGTH) {
|
|
345
|
+
throw new Revert('SetLength operation failed: Length exceeds maximum allowed value.');
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// If newLength is bigger than _startIndex, adjust startIndex
|
|
349
|
+
if (newLength > this._startIndex) {
|
|
350
|
+
this._startIndex = newLength;
|
|
351
|
+
this._isChangedStartIndex = true;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
this._length = newLength;
|
|
355
|
+
this._isChangedLength = true;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* @method deleteLast
|
|
360
|
+
* @description Removes the last element of the array by setting it to zero and decrementing the length.
|
|
361
|
+
*/
|
|
362
|
+
public deleteLast(): void {
|
|
363
|
+
if (this._length === 0) {
|
|
364
|
+
throw new Revert('DeleteLast operation failed: Array is empty.');
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const index = this._length - 1;
|
|
368
|
+
this.delete(index);
|
|
369
|
+
|
|
370
|
+
this._length -= 1;
|
|
371
|
+
this._isChangedLength = true;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* @private
|
|
376
|
+
* @method ensureValues
|
|
377
|
+
* @description Loads and caches the u8 values from the specified storage slot if not already loaded.
|
|
378
|
+
* @param {u64} slotIndex - The index of the storage slot.
|
|
379
|
+
*/
|
|
380
|
+
private ensureValues(slotIndex: u64): void {
|
|
381
|
+
if (!this._isLoaded.has(slotIndex)) {
|
|
382
|
+
const storagePointer = this.calculateStoragePointer(slotIndex);
|
|
383
|
+
const storedU256: u256 = Blockchain.getStorageAt(storagePointer, this.defaultValue);
|
|
384
|
+
const slotValues = this.unpackU256(storedU256);
|
|
385
|
+
this._values.set(slotIndex, slotValues);
|
|
386
|
+
this._isLoaded.add(slotIndex);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* @private
|
|
392
|
+
* @method packValues
|
|
393
|
+
* @description Packs the thirty-two cached u8 values into a single u256 for storage.
|
|
394
|
+
* @param {u64} slotIndex - The index of the storage slot.
|
|
395
|
+
* @returns {u256} - The packed u256 value.
|
|
396
|
+
*/
|
|
397
|
+
private packValues(slotIndex: u64): u256 {
|
|
398
|
+
const values = this._values.get(slotIndex);
|
|
399
|
+
if (!values) {
|
|
400
|
+
return u256.Zero;
|
|
401
|
+
}
|
|
402
|
+
const packed = new u256();
|
|
403
|
+
|
|
404
|
+
// Pack values[0..7] into lo1
|
|
405
|
+
packed.lo1 =
|
|
406
|
+
(u64(values[0]) << 56) |
|
|
407
|
+
(u64(values[1]) << 48) |
|
|
408
|
+
(u64(values[2]) << 40) |
|
|
409
|
+
(u64(values[3]) << 32) |
|
|
410
|
+
(u64(values[4]) << 24) |
|
|
411
|
+
(u64(values[5]) << 16) |
|
|
412
|
+
(u64(values[6]) << 8) |
|
|
413
|
+
u64(values[7]);
|
|
414
|
+
|
|
415
|
+
// Pack values[8..15] into lo2
|
|
416
|
+
packed.lo2 =
|
|
417
|
+
(u64(values[8]) << 56) |
|
|
418
|
+
(u64(values[9]) << 48) |
|
|
419
|
+
(u64(values[10]) << 40) |
|
|
420
|
+
(u64(values[11]) << 32) |
|
|
421
|
+
(u64(values[12]) << 24) |
|
|
422
|
+
(u64(values[13]) << 16) |
|
|
423
|
+
(u64(values[14]) << 8) |
|
|
424
|
+
u64(values[15]);
|
|
425
|
+
|
|
426
|
+
// Pack values[16..23] into hi1
|
|
427
|
+
packed.hi1 =
|
|
428
|
+
(u64(values[16]) << 56) |
|
|
429
|
+
(u64(values[17]) << 48) |
|
|
430
|
+
(u64(values[18]) << 40) |
|
|
431
|
+
(u64(values[19]) << 32) |
|
|
432
|
+
(u64(values[20]) << 24) |
|
|
433
|
+
(u64(values[21]) << 16) |
|
|
434
|
+
(u64(values[22]) << 8) |
|
|
435
|
+
u64(values[23]);
|
|
436
|
+
|
|
437
|
+
// Pack values[24..31] into hi2
|
|
438
|
+
packed.hi2 =
|
|
439
|
+
(u64(values[24]) << 56) |
|
|
440
|
+
(u64(values[25]) << 48) |
|
|
441
|
+
(u64(values[26]) << 40) |
|
|
442
|
+
(u64(values[27]) << 32) |
|
|
443
|
+
(u64(values[28]) << 24) |
|
|
444
|
+
(u64(values[29]) << 16) |
|
|
445
|
+
(u64(values[30]) << 8) |
|
|
446
|
+
u64(values[31]);
|
|
447
|
+
|
|
448
|
+
return packed;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* @private
|
|
453
|
+
* @method unpackU256
|
|
454
|
+
* @description Unpacks a u256 value into an array of thirty-two u8s.
|
|
455
|
+
* @param {u256} storedU256 - The u256 value to unpack.
|
|
456
|
+
* @returns {u8[]} - An array of thirty-two u8 values.
|
|
457
|
+
*/
|
|
458
|
+
private unpackU256(storedU256: u256): u8[] {
|
|
459
|
+
const values: u8[] = new Array<u8>(32);
|
|
460
|
+
|
|
461
|
+
// Unpack lo1 into values[0..7]
|
|
462
|
+
values[0] = u8(storedU256.lo1 >> 56);
|
|
463
|
+
values[1] = u8((storedU256.lo1 >> 48) & 0xff);
|
|
464
|
+
values[2] = u8((storedU256.lo1 >> 40) & 0xff);
|
|
465
|
+
values[3] = u8((storedU256.lo1 >> 32) & 0xff);
|
|
466
|
+
values[4] = u8((storedU256.lo1 >> 24) & 0xff);
|
|
467
|
+
values[5] = u8((storedU256.lo1 >> 16) & 0xff);
|
|
468
|
+
values[6] = u8((storedU256.lo1 >> 8) & 0xff);
|
|
469
|
+
values[7] = u8(storedU256.lo1 & 0xff);
|
|
470
|
+
|
|
471
|
+
// Unpack lo2 into values[8..15]
|
|
472
|
+
values[8] = u8(storedU256.lo2 >> 56);
|
|
473
|
+
values[9] = u8((storedU256.lo2 >> 48) & 0xff);
|
|
474
|
+
values[10] = u8((storedU256.lo2 >> 40) & 0xff);
|
|
475
|
+
values[11] = u8((storedU256.lo2 >> 32) & 0xff);
|
|
476
|
+
values[12] = u8((storedU256.lo2 >> 24) & 0xff);
|
|
477
|
+
values[13] = u8((storedU256.lo2 >> 16) & 0xff);
|
|
478
|
+
values[14] = u8((storedU256.lo2 >> 8) & 0xff);
|
|
479
|
+
values[15] = u8(storedU256.lo2 & 0xff);
|
|
480
|
+
|
|
481
|
+
// Unpack hi1 into values[16..23]
|
|
482
|
+
values[16] = u8(storedU256.hi1 >> 56);
|
|
483
|
+
values[17] = u8((storedU256.hi1 >> 48) & 0xff);
|
|
484
|
+
values[18] = u8((storedU256.hi1 >> 40) & 0xff);
|
|
485
|
+
values[19] = u8((storedU256.hi1 >> 32) & 0xff);
|
|
486
|
+
values[20] = u8((storedU256.hi1 >> 24) & 0xff);
|
|
487
|
+
values[21] = u8((storedU256.hi1 >> 16) & 0xff);
|
|
488
|
+
values[22] = u8((storedU256.hi1 >> 8) & 0xff);
|
|
489
|
+
values[23] = u8(storedU256.hi1 & 0xff);
|
|
490
|
+
|
|
491
|
+
// Unpack hi2 into values[24..31]
|
|
492
|
+
values[24] = u8(storedU256.hi2 >> 56);
|
|
493
|
+
values[25] = u8((storedU256.hi2 >> 48) & 0xff);
|
|
494
|
+
values[26] = u8((storedU256.hi2 >> 40) & 0xff);
|
|
495
|
+
values[27] = u8((storedU256.hi2 >> 32) & 0xff);
|
|
496
|
+
values[28] = u8((storedU256.hi2 >> 24) & 0xff);
|
|
497
|
+
values[29] = u8((storedU256.hi2 >> 16) & 0xff);
|
|
498
|
+
values[30] = u8((storedU256.hi2 >> 8) & 0xff);
|
|
499
|
+
values[31] = u8(storedU256.hi2 & 0xff);
|
|
500
|
+
|
|
501
|
+
return values;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* @private
|
|
506
|
+
* @method calculateStoragePointer
|
|
507
|
+
* @description Calculates the storage pointer for a given slot index by incrementing the base pointer.
|
|
508
|
+
* @param {u64} slotIndex - The index of the storage slot.
|
|
509
|
+
* @returns {u256} - The calculated storage pointer.
|
|
510
|
+
*/
|
|
511
|
+
private calculateStoragePointer(slotIndex: u64): u256 {
|
|
512
|
+
// Each slot is identified by baseU256Pointer + slotIndex + 1
|
|
513
|
+
return SafeMath.add(this.baseU256Pointer, u256.fromU64(slotIndex + 1));
|
|
514
|
+
}
|
|
515
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { u256 } from '@btc-vision/as-bignum/assembly';
|
|
2
|
-
import { BlockchainEnvironment } from '
|
|
3
|
-
import { Sha256 } from '
|
|
4
|
-
import { MemorySlotPointer } from '
|
|
5
|
-
import { Box, concat, fromArrayBuffer, toArrayBuffer } from '
|
|
2
|
+
import { BlockchainEnvironment } from '../../env/BlockchainEnvironment';
|
|
3
|
+
import { Sha256 } from '../../math/sha256';
|
|
4
|
+
import { MemorySlotPointer } from '../../memory/MemorySlotPointer';
|
|
5
|
+
import { Box, concat, fromArrayBuffer, toArrayBuffer } from '../../utils';
|
|
6
6
|
|
|
7
7
|
export function toBuffer<T>(v: T): ArrayBuffer {
|
|
8
8
|
const result = new ArrayBuffer(sizeof<T>());
|
|
@@ -411,4 +411,12 @@ export class SafeMath {
|
|
|
411
411
|
|
|
412
412
|
return result;
|
|
413
413
|
}
|
|
414
|
+
|
|
415
|
+
public static pow10(exponent: u8): u256 {
|
|
416
|
+
let result: u256 = u256.One;
|
|
417
|
+
for (let i: u8 = 0; i < exponent; i++) {
|
|
418
|
+
result = SafeMath.mul(result, u256.fromU32(10));
|
|
419
|
+
}
|
|
420
|
+
return result;
|
|
421
|
+
}
|
|
414
422
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|