@btc-vision/btc-runtime 1.5.3 → 1.5.6
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/BlockchainEnvironment.ts +2 -0
- package/runtime/env/classes/Transaction.ts +1 -0
- package/runtime/exports/index.ts +1 -1
- package/runtime/math/bytes.ts +2 -18
- package/runtime/storage/arrays/StoredAddressArray.ts +53 -58
- package/runtime/storage/arrays/StoredBooleanArray.ts +76 -47
- package/runtime/storage/arrays/StoredPackedArray.ts +27 -7
- package/runtime/storage/arrays/StoredU128Array.ts +1 -1
- package/runtime/storage/arrays/StoredU16Array.ts +1 -1
- package/runtime/storage/arrays/StoredU256Array.ts +1 -1
- package/runtime/storage/arrays/StoredU32Array.ts +1 -1
- package/runtime/storage/arrays/StoredU64Array.ts +1 -1
- package/runtime/storage/arrays/StoredU8Array.ts +1 -1
package/package.json
CHANGED
|
@@ -148,6 +148,7 @@ export class BlockchainEnvironment {
|
|
|
148
148
|
const blockHash = reader.readBytes(32);
|
|
149
149
|
const blockNumber = reader.readU64();
|
|
150
150
|
const blockMedianTime = reader.readU64();
|
|
151
|
+
const txId = reader.readBytes(32);
|
|
151
152
|
const txHash = reader.readBytes(32);
|
|
152
153
|
const contractAddress = reader.readAddress();
|
|
153
154
|
const contractDeployer = reader.readAddress();
|
|
@@ -157,6 +158,7 @@ export class BlockchainEnvironment {
|
|
|
157
158
|
this._tx = new Transaction(
|
|
158
159
|
caller,
|
|
159
160
|
origin,
|
|
161
|
+
txId,
|
|
160
162
|
txHash,
|
|
161
163
|
);
|
|
162
164
|
|
|
@@ -9,6 +9,7 @@ export class Transaction {
|
|
|
9
9
|
public constructor(
|
|
10
10
|
public readonly sender: Address, // "immediate caller"
|
|
11
11
|
public readonly origin: Address, // "leftmost thing in the call chain"
|
|
12
|
+
public readonly txId: Uint8Array,
|
|
12
13
|
public readonly hash: Uint8Array,
|
|
13
14
|
) {
|
|
14
15
|
}
|
package/runtime/exports/index.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { Selector } from '../math/abi';
|
|
|
5
5
|
import { Calldata } from '../types';
|
|
6
6
|
import { env_exit, getCalldata, getEnvironmentVariables } from '../env/global';
|
|
7
7
|
|
|
8
|
-
const ENVIRONMENT_VARIABLES_BYTE_LENGTH: u32 =
|
|
8
|
+
const ENVIRONMENT_VARIABLES_BYTE_LENGTH: u32 = 240;
|
|
9
9
|
|
|
10
10
|
export function execute(calldataLength: u32): u32 {
|
|
11
11
|
const environmentVariablesBuffer = new ArrayBuffer(ENVIRONMENT_VARIABLES_BYTE_LENGTH);
|
package/runtime/math/bytes.ts
CHANGED
|
@@ -171,23 +171,7 @@ export function encodeBasePointer(pointer: u16, subPointer: Uint8Array): Uint8Ar
|
|
|
171
171
|
|
|
172
172
|
@inline
|
|
173
173
|
export function bigEndianAdd(base: Uint8Array, increment: u64): Uint8Array {
|
|
174
|
-
const
|
|
174
|
+
const add = u64ToBE32Bytes(increment);
|
|
175
175
|
|
|
176
|
-
|
|
177
|
-
for (let i = 0; i < 32; i++) {
|
|
178
|
-
out[i] = base[i];
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// If this is truly big-endian, out[0] is the MSB, out[31] is the LSB.
|
|
182
|
-
// So to add `increment`, we start from out[31] backward.
|
|
183
|
-
let carry: u64 = increment;
|
|
184
|
-
for (let i = 31; i >= 0; i--) {
|
|
185
|
-
const sum = <u64>out[i] + (carry & 0xFF);
|
|
186
|
-
out[i] = <u8>(sum & 0xFF);
|
|
187
|
-
carry = sum >> 8;
|
|
188
|
-
if (carry == 0 || i == 0) {
|
|
189
|
-
break;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
return out;
|
|
176
|
+
return addUint8ArraysBE(base, add);
|
|
193
177
|
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import { BytesReader } from '../../buffer/BytesReader';
|
|
2
1
|
import { BytesWriter } from '../../buffer/BytesWriter';
|
|
3
2
|
import { Blockchain } from '../../env';
|
|
4
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
addUint8ArraysBE,
|
|
5
|
+
bigEndianAdd,
|
|
6
|
+
encodeBasePointer,
|
|
7
|
+
readLengthAndStartIndex,
|
|
8
|
+
u64ToBE32Bytes,
|
|
9
|
+
} from '../../math/bytes';
|
|
5
10
|
import { Address } from '../../types/Address';
|
|
6
11
|
import { Revert } from '../../types/Revert';
|
|
7
12
|
|
|
@@ -38,34 +43,32 @@ export class StoredAddressArray {
|
|
|
38
43
|
`You must pass a 30 bytes sub-pointer. (AddressArray, got ${subPointer.length})`,
|
|
39
44
|
);
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
writer.writeBytes(subPointer);
|
|
46
|
+
const basePointer = encodeBasePointer(pointer, subPointer);
|
|
47
|
+
this.lengthPointer = Uint8Array.wrap(basePointer.buffer);
|
|
48
|
+
this.baseU256Pointer = bigEndianAdd(basePointer, 1);
|
|
45
49
|
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
// For length+startIndex, we'll use the same pointer
|
|
49
|
-
const lengthPointer = Uint8Array.wrap(baseU256Pointer.buffer);
|
|
50
|
+
const storedLenStart = Blockchain.getStorageAt(basePointer);
|
|
51
|
+
const data = readLengthAndStartIndex(storedLenStart);
|
|
50
52
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const reader = new BytesReader(storedLengthAndStartIndex);
|
|
55
|
-
this._length = reader.readU64();
|
|
56
|
-
this._startIndex = reader.readU64();
|
|
53
|
+
this._length = data[0];
|
|
54
|
+
this._startIndex = data[1];
|
|
55
|
+
}
|
|
57
56
|
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
@inline
|
|
58
|
+
public has(index: u64): bool {
|
|
59
|
+
return index < this._length;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/** Get an element by its global index. */
|
|
63
63
|
@inline
|
|
64
|
+
@operator('[]')
|
|
64
65
|
public get(index: u64): Address {
|
|
65
|
-
if (index
|
|
66
|
-
throw new Revert('
|
|
66
|
+
if (index >= this._length) {
|
|
67
|
+
throw new Revert('get: index out of range (address array)');
|
|
67
68
|
}
|
|
68
|
-
|
|
69
|
+
|
|
70
|
+
const physicalIndex = (this._startIndex + index) % this.MAX_LENGTH;
|
|
71
|
+
const slotIndex: u32 = <u32>physicalIndex;
|
|
69
72
|
this.ensureValues(slotIndex);
|
|
70
73
|
|
|
71
74
|
return this._values.get(slotIndex);
|
|
@@ -73,11 +76,14 @@ export class StoredAddressArray {
|
|
|
73
76
|
|
|
74
77
|
/** Set an element by its global index. */
|
|
75
78
|
@inline
|
|
79
|
+
@operator('[]=')
|
|
76
80
|
public set(index: u64, value: Address): void {
|
|
77
|
-
if (index
|
|
78
|
-
throw new Revert('
|
|
81
|
+
if (index >= this._length) {
|
|
82
|
+
throw new Revert('set: index out of range (address array)');
|
|
79
83
|
}
|
|
80
|
-
|
|
84
|
+
|
|
85
|
+
const physicalIndex = (this._startIndex + index) % this.MAX_LENGTH;
|
|
86
|
+
const slotIndex: u32 = <u32>physicalIndex;
|
|
81
87
|
this.ensureValues(slotIndex);
|
|
82
88
|
|
|
83
89
|
const currentValue = this._values.get(slotIndex);
|
|
@@ -87,33 +93,16 @@ export class StoredAddressArray {
|
|
|
87
93
|
}
|
|
88
94
|
}
|
|
89
95
|
|
|
90
|
-
/** Find the first index containing `value`. Returns -1 if not found. */
|
|
91
|
-
@inline
|
|
92
|
-
public indexOf(value: Address): i64 {
|
|
93
|
-
for (let i: u64 = 0; i < this._length; i++) {
|
|
94
|
-
if (this.get(i) == value) {
|
|
95
|
-
return i64(i);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
return -1;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/** Check if the array contains `value`. */
|
|
102
|
-
@inline
|
|
103
|
-
public contains(value: Address): boolean {
|
|
104
|
-
return this.indexOf(value) !== -1;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
96
|
/** Append an address at the end of the array. */
|
|
97
|
+
@inline
|
|
108
98
|
public push(value: Address): void {
|
|
109
99
|
if (this._length >= this.MAX_LENGTH) {
|
|
110
|
-
throw new Revert('
|
|
100
|
+
throw new Revert('push: array reached maximum length (address array)');
|
|
111
101
|
}
|
|
112
102
|
|
|
113
|
-
const
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
const slotIndex: u32 = <u32>wrappedIndex;
|
|
103
|
+
const newLogicalIndex: u64 = this._length;
|
|
104
|
+
const physicalIndex: u64 = (this._startIndex + newLogicalIndex) % this.MAX_LENGTH;
|
|
105
|
+
const slotIndex: u32 = <u32>physicalIndex;
|
|
117
106
|
|
|
118
107
|
this.ensureValues(slotIndex);
|
|
119
108
|
this._values.set(slotIndex, value);
|
|
@@ -126,11 +115,12 @@ export class StoredAddressArray {
|
|
|
126
115
|
/** Delete the last element. */
|
|
127
116
|
public deleteLast(): void {
|
|
128
117
|
if (this._length === 0) {
|
|
129
|
-
throw new Revert('
|
|
118
|
+
throw new Revert('deleteLast: array is empty (address array)');
|
|
130
119
|
}
|
|
131
120
|
|
|
132
|
-
const
|
|
133
|
-
const
|
|
121
|
+
const lastLogicalIndex: u64 = this._length - 1;
|
|
122
|
+
const physicalIndex: u64 = (this._startIndex + lastLogicalIndex) % this.MAX_LENGTH;
|
|
123
|
+
const slotIndex: u32 = <u32>physicalIndex;
|
|
134
124
|
this.ensureValues(slotIndex);
|
|
135
125
|
|
|
136
126
|
const currentValue = this._values.get(slotIndex);
|
|
@@ -150,12 +140,14 @@ export class StoredAddressArray {
|
|
|
150
140
|
}
|
|
151
141
|
|
|
152
142
|
/** Delete a specific element by setting it to `defaultValue`. */
|
|
143
|
+
@inline
|
|
153
144
|
public delete(index: u64): void {
|
|
154
145
|
if (index > this.MAX_LENGTH) {
|
|
155
|
-
throw new Revert('
|
|
146
|
+
throw new Revert('delete: index out of range (address array)');
|
|
156
147
|
}
|
|
157
148
|
|
|
158
|
-
const
|
|
149
|
+
const physicalIndex: u64 = (this._startIndex + index) % this.MAX_LENGTH;
|
|
150
|
+
const slotIndex: u32 = <u32>physicalIndex;
|
|
159
151
|
this.ensureValues(slotIndex);
|
|
160
152
|
|
|
161
153
|
const currentValue = this._values.get(slotIndex);
|
|
@@ -170,6 +162,7 @@ export class StoredAddressArray {
|
|
|
170
162
|
* - Store any changed slotIndex -> Address
|
|
171
163
|
* - Store updated length and startIndex if changed
|
|
172
164
|
*/
|
|
165
|
+
@inline
|
|
173
166
|
public save(): void {
|
|
174
167
|
// 1) Save changed slots
|
|
175
168
|
const changed = this._isChanged.values();
|
|
@@ -196,7 +189,6 @@ export class StoredAddressArray {
|
|
|
196
189
|
|
|
197
190
|
/** Clear entire array content from storage, reset length and startIndex. */
|
|
198
191
|
public deleteAll(): void {
|
|
199
|
-
// Clear all loaded slots
|
|
200
192
|
const keys = this._values.keys();
|
|
201
193
|
for (let i = 0; i < keys.length; i++) {
|
|
202
194
|
const slotIndex = keys[i];
|
|
@@ -204,14 +196,13 @@ export class StoredAddressArray {
|
|
|
204
196
|
Blockchain.setStorageAt(storagePointer, this.defaultValue);
|
|
205
197
|
}
|
|
206
198
|
|
|
207
|
-
|
|
208
|
-
|
|
199
|
+
Blockchain.setStorageAt(this.lengthPointer, new Uint8Array(32));
|
|
200
|
+
|
|
209
201
|
this._length = 0;
|
|
210
202
|
this._startIndex = 0;
|
|
211
203
|
this._isChangedLength = false;
|
|
212
204
|
this._isChangedStartIndex = false;
|
|
213
205
|
|
|
214
|
-
// Clear internal caches
|
|
215
206
|
this._values.clear();
|
|
216
207
|
this._isChanged.clear();
|
|
217
208
|
}
|
|
@@ -228,7 +219,7 @@ export class StoredAddressArray {
|
|
|
228
219
|
@inline
|
|
229
220
|
public getAll(startIndex: u32, count: u32): Address[] {
|
|
230
221
|
if (startIndex + count > this._length) {
|
|
231
|
-
throw new Revert('
|
|
222
|
+
throw new Revert('getAll: index out of range (address array)');
|
|
232
223
|
}
|
|
233
224
|
|
|
234
225
|
const result = new Array<Address>(count);
|
|
@@ -260,6 +251,10 @@ export class StoredAddressArray {
|
|
|
260
251
|
this._startIndex = 0;
|
|
261
252
|
this._isChangedLength = true;
|
|
262
253
|
this._isChangedStartIndex = true;
|
|
254
|
+
|
|
255
|
+
this._values.clear();
|
|
256
|
+
this._isChanged.clear();
|
|
257
|
+
|
|
263
258
|
this.save();
|
|
264
259
|
}
|
|
265
260
|
|
|
@@ -295,8 +290,8 @@ export class StoredAddressArray {
|
|
|
295
290
|
* Compute a 32-byte storage pointer = basePointer + (slotIndex + 1) big-endian.
|
|
296
291
|
*/
|
|
297
292
|
private calculateStoragePointer(slotIndex: u64): Uint8Array {
|
|
298
|
-
// Convert (slotIndex
|
|
299
|
-
const offset = u64ToBE32Bytes(slotIndex
|
|
293
|
+
// Convert (slotIndex) to a 32-byte big-endian offset
|
|
294
|
+
const offset = u64ToBE32Bytes(slotIndex);
|
|
300
295
|
|
|
301
296
|
return addUint8ArraysBE(this.baseU256Pointer, offset);
|
|
302
297
|
}
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import { BytesWriter } from '../../buffer/BytesWriter';
|
|
2
|
-
import { BytesReader } from '../../buffer/BytesReader';
|
|
3
2
|
import { Blockchain } from '../../env';
|
|
4
3
|
import { Revert } from '../../types/Revert';
|
|
5
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
addUint8ArraysBE,
|
|
6
|
+
bigEndianAdd,
|
|
7
|
+
encodeBasePointer,
|
|
8
|
+
GET_EMPTY_BUFFER,
|
|
9
|
+
getBit,
|
|
10
|
+
readLengthAndStartIndex,
|
|
11
|
+
setBit,
|
|
12
|
+
u64ToBE32Bytes,
|
|
13
|
+
} from '../../math/bytes';
|
|
6
14
|
|
|
7
15
|
/**
|
|
8
16
|
* @class StoredBooleanArray
|
|
@@ -42,36 +50,41 @@ export class StoredBooleanArray {
|
|
|
42
50
|
) {
|
|
43
51
|
assert(subPtr.length <= 30, `You must pass a 30 bytes sub-pointer. (StoredBooleanArray, got ${subPtr.length})`);
|
|
44
52
|
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const basePtr = writer.getBuffer();
|
|
50
|
-
|
|
51
|
-
this.basePointer = basePtr;
|
|
52
|
-
this.lengthPointer = basePtr;
|
|
53
|
+
const basePointer = encodeBasePointer(pointer, subPtr);
|
|
54
|
+
this.lengthPointer = Uint8Array.wrap(basePointer.buffer);
|
|
55
|
+
this.basePointer = bigEndianAdd(basePointer, 1);
|
|
53
56
|
|
|
54
|
-
const storedLenStart
|
|
55
|
-
|
|
56
|
-
);
|
|
57
|
+
const storedLenStart = Blockchain.getStorageAt(basePointer);
|
|
58
|
+
const data = readLengthAndStartIndex(storedLenStart);
|
|
57
59
|
|
|
58
|
-
|
|
59
|
-
this.
|
|
60
|
-
this._startIndex = r.readU64();
|
|
60
|
+
this._length = data[0];
|
|
61
|
+
this._startIndex = data[1];
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
// -------------- Public Accessors -------------- //
|
|
64
65
|
|
|
66
|
+
@inline
|
|
67
|
+
public has(index: u64): bool {
|
|
68
|
+
return index < this._length;
|
|
69
|
+
}
|
|
70
|
+
|
|
65
71
|
/**
|
|
66
72
|
* Retrieve boolean at `index`.
|
|
67
73
|
*/
|
|
74
|
+
@operator('[]')
|
|
68
75
|
@inline
|
|
69
76
|
public get(index: u64): bool {
|
|
70
|
-
if (index
|
|
71
|
-
throw new Revert(
|
|
77
|
+
if (index >= this._length) {
|
|
78
|
+
throw new Revert(`get: index out of range (${index} >= ${this._length}, boolean array)`);
|
|
72
79
|
}
|
|
73
|
-
|
|
74
|
-
const
|
|
80
|
+
|
|
81
|
+
const effectiveIndex = this._startIndex + index;
|
|
82
|
+
const wrappedIndex = effectiveIndex < this.MAX_LENGTH
|
|
83
|
+
? effectiveIndex
|
|
84
|
+
: effectiveIndex % this.MAX_LENGTH;
|
|
85
|
+
|
|
86
|
+
const slotIndex = wrappedIndex / 256;
|
|
87
|
+
const bitIndex = <u16>(wrappedIndex % 256);
|
|
75
88
|
|
|
76
89
|
this.ensureSlotLoaded(slotIndex);
|
|
77
90
|
|
|
@@ -82,13 +95,20 @@ export class StoredBooleanArray {
|
|
|
82
95
|
/**
|
|
83
96
|
* Set boolean at `index`.
|
|
84
97
|
*/
|
|
98
|
+
@operator('[]=')
|
|
85
99
|
@inline
|
|
86
100
|
public set(index: u64, value: bool): void {
|
|
87
|
-
if (index
|
|
88
|
-
throw new Revert(
|
|
101
|
+
if (index >= this._length) {
|
|
102
|
+
throw new Revert(`set: index out of range (${index} >= ${this._length}, boolean array)`);
|
|
89
103
|
}
|
|
90
|
-
|
|
91
|
-
const
|
|
104
|
+
|
|
105
|
+
const effectiveIndex = this._startIndex + index;
|
|
106
|
+
const wrappedIndex = effectiveIndex < this.MAX_LENGTH
|
|
107
|
+
? effectiveIndex
|
|
108
|
+
: effectiveIndex % this.MAX_LENGTH;
|
|
109
|
+
|
|
110
|
+
const slotIndex = wrappedIndex / 256;
|
|
111
|
+
const bitIndex = <u16>(wrappedIndex % 256);
|
|
92
112
|
|
|
93
113
|
this.ensureSlotLoaded(slotIndex);
|
|
94
114
|
|
|
@@ -108,7 +128,7 @@ export class StoredBooleanArray {
|
|
|
108
128
|
@inline
|
|
109
129
|
public push(value: bool): void {
|
|
110
130
|
if (this._length >= this.MAX_LENGTH) {
|
|
111
|
-
throw new Revert('push:
|
|
131
|
+
throw new Revert('push: reached max allowed length (boolean array)');
|
|
112
132
|
}
|
|
113
133
|
|
|
114
134
|
const newIndex = this._length;
|
|
@@ -137,11 +157,17 @@ export class StoredBooleanArray {
|
|
|
137
157
|
*/
|
|
138
158
|
@inline
|
|
139
159
|
public delete(index: u64): void {
|
|
140
|
-
if (index
|
|
141
|
-
throw new Revert('delete:
|
|
160
|
+
if (index >= this._length) {
|
|
161
|
+
throw new Revert('delete: index out of range (boolean array)');
|
|
142
162
|
}
|
|
143
|
-
|
|
144
|
-
const
|
|
163
|
+
|
|
164
|
+
const effectiveIndex = this._startIndex + index;
|
|
165
|
+
const wrappedIndex = effectiveIndex < this.MAX_LENGTH
|
|
166
|
+
? effectiveIndex
|
|
167
|
+
: effectiveIndex % this.MAX_LENGTH;
|
|
168
|
+
|
|
169
|
+
const slotIndex = wrappedIndex / 256;
|
|
170
|
+
const bitIndex = <u16>(wrappedIndex % 256);
|
|
145
171
|
|
|
146
172
|
this.ensureSlotLoaded(slotIndex);
|
|
147
173
|
|
|
@@ -161,8 +187,9 @@ export class StoredBooleanArray {
|
|
|
161
187
|
@inline
|
|
162
188
|
public deleteLast(): void {
|
|
163
189
|
if (this._length === 0) {
|
|
164
|
-
throw new Revert('deleteLast:
|
|
190
|
+
throw new Revert('deleteLast: array is empty');
|
|
165
191
|
}
|
|
192
|
+
|
|
166
193
|
const lastIndex = this._length - 1;
|
|
167
194
|
this.delete(lastIndex);
|
|
168
195
|
|
|
@@ -187,12 +214,11 @@ export class StoredBooleanArray {
|
|
|
187
214
|
|
|
188
215
|
// 2) If length or startIndex changed, store them
|
|
189
216
|
if (this._isChangedLength || this._isChangedStartIndex) {
|
|
190
|
-
const w = new BytesWriter(
|
|
217
|
+
const w = new BytesWriter(32);
|
|
191
218
|
w.writeU64(this._length);
|
|
192
219
|
w.writeU64(this._startIndex);
|
|
193
220
|
|
|
194
|
-
const data = w.getBuffer();
|
|
195
|
-
// You could store 32 bytes if you prefer; the leftover bytes can be 0
|
|
221
|
+
const data = w.getBuffer();
|
|
196
222
|
Blockchain.setStorageAt(this.lengthPointer, data);
|
|
197
223
|
|
|
198
224
|
this._isChangedLength = false;
|
|
@@ -201,11 +227,10 @@ export class StoredBooleanArray {
|
|
|
201
227
|
}
|
|
202
228
|
|
|
203
229
|
/**
|
|
204
|
-
* Delete all slots in storage (that are loaded) + reset length +
|
|
230
|
+
* Delete all slots in storage (that are loaded) + reset length + _startIndex.
|
|
205
231
|
*/
|
|
206
232
|
@inline
|
|
207
233
|
public deleteAll(): void {
|
|
208
|
-
// clear all loaded slots
|
|
209
234
|
const keys = this._values.keys();
|
|
210
235
|
const zeroArr = GET_EMPTY_BUFFER();
|
|
211
236
|
for (let i = 0; i < keys.length; i++) {
|
|
@@ -214,17 +239,14 @@ export class StoredBooleanArray {
|
|
|
214
239
|
Blockchain.setStorageAt(storagePointer, zeroArr);
|
|
215
240
|
}
|
|
216
241
|
|
|
217
|
-
|
|
218
|
-
const writer = new BytesWriter(16);
|
|
219
|
-
writer.writeU64(0);
|
|
220
|
-
writer.writeU64(0);
|
|
242
|
+
const writer = new BytesWriter(32);
|
|
221
243
|
Blockchain.setStorageAt(this.lengthPointer, writer.getBuffer());
|
|
222
244
|
|
|
223
|
-
// reset in memory
|
|
224
245
|
this._length = 0;
|
|
225
246
|
this._startIndex = 0;
|
|
226
247
|
this._isChangedLength = false;
|
|
227
248
|
this._isChangedStartIndex = false;
|
|
249
|
+
|
|
228
250
|
this._values.clear();
|
|
229
251
|
this._isChanged.clear();
|
|
230
252
|
}
|
|
@@ -243,18 +265,20 @@ export class StoredBooleanArray {
|
|
|
243
265
|
* Retrieve a batch of bools.
|
|
244
266
|
*/
|
|
245
267
|
@inline
|
|
246
|
-
public getAll(
|
|
247
|
-
if (
|
|
248
|
-
throw new Revert('getAll: range exceeds array length');
|
|
268
|
+
public getAll(start: u64, count: u64): bool[] {
|
|
269
|
+
if (start + count > this._length) {
|
|
270
|
+
throw new Revert('getAll: range exceeds array length (boolean array)');
|
|
249
271
|
}
|
|
272
|
+
|
|
250
273
|
if (count > u64(u32.MAX_VALUE)) {
|
|
251
|
-
throw new Revert('getAll: range exceeds max allowed');
|
|
274
|
+
throw new Revert('getAll: range exceeds max allowed (boolean array)');
|
|
252
275
|
}
|
|
253
276
|
|
|
254
277
|
const result = new Array<bool>(<i32>count);
|
|
255
278
|
for (let i: u64 = 0; i < count; i++) {
|
|
256
|
-
result[<i32>i] = this.get(
|
|
279
|
+
result[<i32>i] = this.get(start + i);
|
|
257
280
|
}
|
|
281
|
+
|
|
258
282
|
return result;
|
|
259
283
|
}
|
|
260
284
|
|
|
@@ -298,6 +322,12 @@ export class StoredBooleanArray {
|
|
|
298
322
|
return this._length;
|
|
299
323
|
}
|
|
300
324
|
|
|
325
|
+
@inline
|
|
326
|
+
public setStartingIndex(index: u64): void {
|
|
327
|
+
this._startIndex = index;
|
|
328
|
+
this._isChangedStartIndex = true;
|
|
329
|
+
}
|
|
330
|
+
|
|
301
331
|
/**
|
|
302
332
|
* Current starting index for the array.
|
|
303
333
|
*/
|
|
@@ -306,7 +336,6 @@ export class StoredBooleanArray {
|
|
|
306
336
|
return this._startIndex;
|
|
307
337
|
}
|
|
308
338
|
|
|
309
|
-
|
|
310
339
|
/**
|
|
311
340
|
* Ensure the 32-byte slot for `slotIndex` is loaded into _values.
|
|
312
341
|
*/
|
|
@@ -322,7 +351,7 @@ export class StoredBooleanArray {
|
|
|
322
351
|
* Convert `slotIndex` -> pointer = basePointer + (slotIndex + 1), as big-endian addition.
|
|
323
352
|
*/
|
|
324
353
|
private calculateStoragePointer(slotIndex: u64): Uint8Array {
|
|
325
|
-
const offset = u64ToBE32Bytes(slotIndex
|
|
354
|
+
const offset = u64ToBE32Bytes(slotIndex);
|
|
326
355
|
return addUint8ArraysBE(this.basePointer, offset);
|
|
327
356
|
}
|
|
328
357
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
bigEndianAdd,
|
|
2
3
|
encodeBasePointer,
|
|
3
4
|
GET_EMPTY_BUFFER,
|
|
4
5
|
readLengthAndStartIndex,
|
|
@@ -53,7 +54,7 @@ export abstract class StoredPackedArray<T> {
|
|
|
53
54
|
|
|
54
55
|
const basePointer = encodeBasePointer(pointer, subPointer);
|
|
55
56
|
this.lengthPointer = Uint8Array.wrap(basePointer.buffer);
|
|
56
|
-
this.basePointer = basePointer;
|
|
57
|
+
this.basePointer = bigEndianAdd(basePointer, 1);
|
|
57
58
|
|
|
58
59
|
const storedLenStart = Blockchain.getStorageAt(basePointer);
|
|
59
60
|
const data = readLengthAndStartIndex(storedLenStart);
|
|
@@ -62,9 +63,11 @@ export abstract class StoredPackedArray<T> {
|
|
|
62
63
|
this._startIndex = data[1];
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
@inline
|
|
67
|
+
@operator('[]')
|
|
65
68
|
public get(index: u64): T {
|
|
66
69
|
if (index > this.MAX_LENGTH) {
|
|
67
|
-
throw new Revert('get: index exceeds MAX_LENGTH');
|
|
70
|
+
throw new Revert('get: index exceeds MAX_LENGTH (packed array)');
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
const cap = this.getSlotCapacity();
|
|
@@ -79,7 +82,13 @@ export abstract class StoredPackedArray<T> {
|
|
|
79
82
|
return arr[subIndex];
|
|
80
83
|
}
|
|
81
84
|
|
|
85
|
+
@inline
|
|
86
|
+
@operator('[]=')
|
|
82
87
|
public set(index: u64, value: T): void {
|
|
88
|
+
if (index > this.MAX_LENGTH) {
|
|
89
|
+
throw new Revert('set: index exceeds MAX_LENGTH (packed array)');
|
|
90
|
+
}
|
|
91
|
+
|
|
83
92
|
const cap = this.getSlotCapacity();
|
|
84
93
|
const slotIndex = index / cap;
|
|
85
94
|
const subIndex = <u32>(index % cap);
|
|
@@ -95,9 +104,10 @@ export abstract class StoredPackedArray<T> {
|
|
|
95
104
|
}
|
|
96
105
|
}
|
|
97
106
|
|
|
107
|
+
@inline
|
|
98
108
|
public push(value: T): void {
|
|
99
109
|
if (this._length >= this.MAX_LENGTH) {
|
|
100
|
-
throw new Revert('push: array has reached MAX_LENGTH');
|
|
110
|
+
throw new Revert('push: array has reached MAX_LENGTH (packed array)');
|
|
101
111
|
}
|
|
102
112
|
|
|
103
113
|
const newIndex = this._length;
|
|
@@ -123,6 +133,7 @@ export abstract class StoredPackedArray<T> {
|
|
|
123
133
|
* "Delete" by zeroing out the element at `index`,
|
|
124
134
|
* but does not reduce the length.
|
|
125
135
|
*/
|
|
136
|
+
@inline
|
|
126
137
|
public delete(index: u64): void {
|
|
127
138
|
const cap = this.getSlotCapacity();
|
|
128
139
|
const slotIndex = index / cap;
|
|
@@ -143,9 +154,10 @@ export abstract class StoredPackedArray<T> {
|
|
|
143
154
|
/**
|
|
144
155
|
* Remove the last element by zeroing it and decrementing length by 1.
|
|
145
156
|
*/
|
|
157
|
+
@inline
|
|
146
158
|
public deleteLast(): void {
|
|
147
159
|
if (this._length == 0) {
|
|
148
|
-
throw new Revert('deleteLast: array is empty');
|
|
160
|
+
throw new Revert('deleteLast: array is empty (packed array)');
|
|
149
161
|
}
|
|
150
162
|
|
|
151
163
|
const lastIndex = this._length - 1;
|
|
@@ -155,11 +167,13 @@ export abstract class StoredPackedArray<T> {
|
|
|
155
167
|
this._isChangedLength = true;
|
|
156
168
|
}
|
|
157
169
|
|
|
170
|
+
@inline
|
|
158
171
|
public setMultiple(startIndex: u64, values: T[]): void {
|
|
159
172
|
const end = startIndex + <u64>values.length;
|
|
160
173
|
if (end > this._length) {
|
|
161
|
-
throw new Revert('setMultiple: out of range');
|
|
174
|
+
throw new Revert('setMultiple: out of range (packed array)');
|
|
162
175
|
}
|
|
176
|
+
|
|
163
177
|
for (let i = 0; i < values.length; i++) {
|
|
164
178
|
this.set(startIndex + <u64>i, values[i]);
|
|
165
179
|
}
|
|
@@ -168,10 +182,10 @@ export abstract class StoredPackedArray<T> {
|
|
|
168
182
|
// -----------------------------------------------------------
|
|
169
183
|
// Public Array-Like Methods
|
|
170
184
|
// -----------------------------------------------------------
|
|
171
|
-
|
|
185
|
+
@inline
|
|
172
186
|
public getAll(startIndex: u64, count: u64): T[] {
|
|
173
187
|
if (count > <u64>u32.MAX_VALUE) {
|
|
174
|
-
throw new Revert('getAll: count too large');
|
|
188
|
+
throw new Revert('getAll: count too large (packed array)');
|
|
175
189
|
}
|
|
176
190
|
|
|
177
191
|
const out = new Array<T>(<i32>count);
|
|
@@ -182,14 +196,17 @@ export abstract class StoredPackedArray<T> {
|
|
|
182
196
|
return out;
|
|
183
197
|
}
|
|
184
198
|
|
|
199
|
+
@inline
|
|
185
200
|
public getLength(): u64 {
|
|
186
201
|
return this._length;
|
|
187
202
|
}
|
|
188
203
|
|
|
204
|
+
@inline
|
|
189
205
|
public startingIndex(): u64 {
|
|
190
206
|
return this._startIndex;
|
|
191
207
|
}
|
|
192
208
|
|
|
209
|
+
@inline
|
|
193
210
|
public setStartingIndex(index: u64): void {
|
|
194
211
|
this._startIndex = index;
|
|
195
212
|
this._isChangedStartIndex = true;
|
|
@@ -250,6 +267,9 @@ export abstract class StoredPackedArray<T> {
|
|
|
250
267
|
this._isChanged.clear();
|
|
251
268
|
}
|
|
252
269
|
|
|
270
|
+
/**
|
|
271
|
+
* Reset the array to its initial state.
|
|
272
|
+
*/
|
|
253
273
|
public reset(): void {
|
|
254
274
|
this._length = 0;
|
|
255
275
|
this._startIndex = 0;
|
|
@@ -43,6 +43,6 @@ export class StoredU8Array extends StoredPackedArray<u8> {
|
|
|
43
43
|
|
|
44
44
|
protected calculateStoragePointer(slotIndex: u64): Uint8Array {
|
|
45
45
|
// basePointer + (slotIndex+1) in big-endian
|
|
46
|
-
return bigEndianAdd(this.basePointer, slotIndex
|
|
46
|
+
return bigEndianAdd(this.basePointer, slotIndex);
|
|
47
47
|
}
|
|
48
48
|
}
|