@btc-vision/btc-runtime 1.9.1 → 1.9.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.
Files changed (31) hide show
  1. package/package.json +13 -4
  2. package/runtime/constants/Exports.ts +86 -0
  3. package/runtime/contracts/OP1155.ts +1042 -0
  4. package/runtime/contracts/OP20.ts +56 -37
  5. package/runtime/contracts/OP721.ts +882 -0
  6. package/runtime/contracts/OP_NET.ts +5 -0
  7. package/runtime/contracts/ReentrancyGuard.ts +136 -0
  8. package/runtime/contracts/interfaces/IOP1155.ts +33 -0
  9. package/runtime/contracts/interfaces/IOP721.ts +29 -0
  10. package/runtime/contracts/interfaces/OP1155InitParameters.ts +11 -0
  11. package/runtime/contracts/interfaces/OP721InitParameters.ts +15 -0
  12. package/runtime/env/BlockchainEnvironment.ts +32 -3
  13. package/runtime/events/predefined/ApprovedForAll.ts +16 -0
  14. package/runtime/events/predefined/TransferredBatchEvent.ts +35 -0
  15. package/runtime/events/predefined/TransferredSingleEvent.ts +18 -0
  16. package/runtime/events/predefined/URIEvent.ts +23 -0
  17. package/runtime/events/predefined/index.ts +4 -0
  18. package/runtime/index.ts +9 -0
  19. package/runtime/math/abi.ts +23 -0
  20. package/runtime/math/bytes.ts +4 -0
  21. package/runtime/memory/AddressMemoryMap.ts +20 -0
  22. package/runtime/nested/storage/StorageMap.ts +3 -23
  23. package/runtime/nested/storage/StorageSet.ts +6 -3
  24. package/runtime/script/Script.ts +1 -1
  25. package/runtime/secp256k1/ECPoint.ts +3 -3
  26. package/runtime/shared-libraries/OP20Utils.ts +1 -2
  27. package/runtime/storage/AdvancedStoredString.ts +8 -189
  28. package/runtime/storage/BaseStoredString.ts +206 -0
  29. package/runtime/storage/StoredString.ts +15 -194
  30. package/runtime/storage/arrays/StoredPackedArray.ts +13 -4
  31. package/runtime/types/SafeMath.ts +125 -94
@@ -50,6 +50,11 @@ export class OP_NET implements IBTC {
50
50
  return this.address === address;
51
51
  }
52
52
 
53
+ protected _buildDomainSeparator(): Uint8Array {
54
+ // This method should be overridden in derived classes to provide the domain separator
55
+ throw new Error('Method not implemented.');
56
+ }
57
+
53
58
  protected onlyDeployer(caller: Address): void {
54
59
  if (this.contractDeployer !== caller) {
55
60
  throw new Revert('Only deployer can call this method');
@@ -0,0 +1,136 @@
1
+ import { Blockchain } from '../env';
2
+ import { OP_NET } from './OP_NET';
3
+ import { StoredBoolean } from '../storage/StoredBoolean';
4
+ import { StoredU256 } from '../storage/StoredU256';
5
+ import { Selector } from '../math/abi';
6
+ import { Calldata } from '../types';
7
+ import { Revert } from '../types/Revert';
8
+ import { u256 } from '@btc-vision/as-bignum/assembly';
9
+ import { SafeMath } from '../types/SafeMath';
10
+ import {
11
+ ON_OP1155_BATCH_RECEIVED_MAGIC,
12
+ ON_OP1155_RECEIVED_MAGIC,
13
+ ON_OP20_RECEIVED_SELECTOR,
14
+ ON_OP721_RECEIVED_SELECTOR,
15
+ } from '../constants/Exports';
16
+ import { EMPTY_POINTER } from '../math/bytes';
17
+
18
+ const statusPointer: u16 = Blockchain.nextPointer;
19
+ const depthPointer: u16 = Blockchain.nextPointer;
20
+
21
+ /**
22
+ * @enum ReentrancyLevel
23
+ * @description
24
+ * Defines the level of reentrancy protection.
25
+ *
26
+ * STANDARD: Strict single entry, no reentrancy allowed.
27
+ * CALLBACK: Allows one level of reentrancy for callbacks (e.g., token transfers).
28
+ */
29
+ export enum ReentrancyLevel {
30
+ STANDARD = 0,
31
+ CALLBACK = 1,
32
+ }
33
+
34
+ export class ReentrancyGuard extends OP_NET {
35
+ protected readonly _locked: StoredBoolean;
36
+ protected readonly _reentrancyDepth: StoredU256;
37
+
38
+ // Override this in derived contracts to set protection level
39
+ protected readonly reentrancyLevel: ReentrancyLevel = ReentrancyLevel.STANDARD;
40
+
41
+ protected constructor() {
42
+ super();
43
+ this._locked = new StoredBoolean(statusPointer, false);
44
+ this._reentrancyDepth = new StoredU256(depthPointer, EMPTY_POINTER);
45
+ }
46
+
47
+ public override onExecutionCompleted(selector: Selector, calldata: Calldata): void {
48
+ super.onExecutionCompleted(selector, calldata);
49
+
50
+ if (this.isSelectorExcluded(selector)) {
51
+ return;
52
+ }
53
+
54
+ this.nonReentrantAfter();
55
+ }
56
+
57
+ public override onExecutionStarted(selector: Selector, calldata: Calldata): void {
58
+ super.onExecutionStarted(selector, calldata);
59
+
60
+ if (this.isSelectorExcluded(selector)) {
61
+ return;
62
+ }
63
+
64
+ this.nonReentrantBefore();
65
+ }
66
+
67
+ public nonReentrantBefore(): void {
68
+ if (this.reentrancyLevel === ReentrancyLevel.STANDARD) {
69
+ // Standard behavior - strict single entry
70
+ if (this._locked.value) {
71
+ this.reentrancyGuardReentrantCall();
72
+ }
73
+ this._locked.value = true;
74
+ } else if (this.reentrancyLevel === ReentrancyLevel.CALLBACK) {
75
+ // Allow one level of reentrancy for callbacks
76
+ const currentDepth = this._reentrancyDepth.value;
77
+
78
+ // Maximum depth of 2 (original call + one callback reentry)
79
+ if (currentDepth >= u256.One) {
80
+ throw new Revert('ReentrancyGuard: Max depth exceeded');
81
+ }
82
+
83
+ this._reentrancyDepth.value = SafeMath.add(currentDepth, u256.One);
84
+
85
+ // Use locked flag for first entry
86
+ if (currentDepth.isZero()) {
87
+ this._locked.value = true;
88
+ }
89
+ }
90
+ }
91
+
92
+ public nonReentrantAfter(): void {
93
+ if (this.reentrancyLevel === ReentrancyLevel.STANDARD) {
94
+ // Standard behavior
95
+ this._locked.value = false;
96
+ } else if (this.reentrancyLevel === ReentrancyLevel.CALLBACK) {
97
+ // Decrement depth
98
+ const currentDepth = this._reentrancyDepth.value;
99
+ if (currentDepth.isZero()) {
100
+ throw new Revert('ReentrancyGuard: Depth underflow');
101
+ }
102
+
103
+ const newDepth = SafeMath.sub(currentDepth, u256.One);
104
+ this._reentrancyDepth.value = newDepth;
105
+
106
+ // Clear locked flag when fully exited
107
+ if (newDepth.isZero()) {
108
+ this._locked.value = false;
109
+ }
110
+ }
111
+ }
112
+
113
+ public reentrancyGuardEntered(): boolean {
114
+ return this._locked.value === true;
115
+ }
116
+
117
+ public getCurrentDepth(): u256 {
118
+ return this._reentrancyDepth.value;
119
+ }
120
+
121
+ /**
122
+ * @dev Unauthorized reentrant call.
123
+ */
124
+ protected reentrancyGuardReentrantCall(): void {
125
+ throw new Revert('ReentrancyGuard: LOCKED');
126
+ }
127
+
128
+ protected isSelectorExcluded(selector: Selector): boolean {
129
+ return (
130
+ selector === ON_OP20_RECEIVED_SELECTOR ||
131
+ selector === ON_OP721_RECEIVED_SELECTOR ||
132
+ selector === ON_OP1155_RECEIVED_MAGIC ||
133
+ selector === ON_OP1155_BATCH_RECEIVED_MAGIC
134
+ );
135
+ }
136
+ }
@@ -0,0 +1,33 @@
1
+ import { BytesWriter } from '../../buffer/BytesWriter';
2
+ import { Calldata } from '../../types';
3
+
4
+ export interface IOP1155 {
5
+ // Core properties
6
+ fn_name(calldata: Calldata): BytesWriter;
7
+ fn_symbol(calldata: Calldata): BytesWriter;
8
+ uri(calldata: Calldata): BytesWriter;
9
+
10
+ // Balance and supply
11
+ balanceOf(calldata: Calldata): BytesWriter;
12
+ balanceOfBatch(calldata: Calldata): BytesWriter;
13
+ totalSupply(calldata: Calldata): BytesWriter;
14
+ exists(calldata: Calldata): BytesWriter;
15
+
16
+ // Transfer functions
17
+ safeTransferFrom(calldata: Calldata): BytesWriter;
18
+ safeBatchTransferFrom(calldata: Calldata): BytesWriter;
19
+
20
+ // Approval functions
21
+ setApprovalForAll(calldata: Calldata): BytesWriter;
22
+ isApprovedForAll(calldata: Calldata): BytesWriter;
23
+
24
+ // Advanced functions
25
+ burn(calldata: Calldata): BytesWriter;
26
+ burnBatch(calldata: Calldata): BytesWriter;
27
+ transferBySignature(calldata: Calldata): BytesWriter;
28
+ batchTransferBySignature(calldata: Calldata): BytesWriter;
29
+ domainSeparator(calldata: Calldata): BytesWriter;
30
+
31
+ // Interface support
32
+ supportsInterface(calldata: Calldata): BytesWriter;
33
+ }
@@ -0,0 +1,29 @@
1
+ import { BytesWriter } from '../../buffer/BytesWriter';
2
+ import { Calldata } from '../../types';
3
+
4
+ export interface IOP721 {
5
+ // Core NFT properties
6
+ fn_name(calldata: Calldata): BytesWriter;
7
+ fn_symbol(calldata: Calldata): BytesWriter;
8
+ tokenURI(calldata: Calldata): BytesWriter;
9
+ fn_totalSupply(calldata: Calldata): BytesWriter;
10
+
11
+ // Balance and ownership
12
+ balanceOf(calldata: Calldata): BytesWriter;
13
+ ownerOf(calldata: Calldata): BytesWriter;
14
+
15
+ // Transfer functions
16
+ transferFrom(calldata: Calldata): BytesWriter;
17
+ safeTransferFrom(calldata: Calldata): BytesWriter;
18
+
19
+ // Approval functions
20
+ approve(calldata: Calldata): BytesWriter;
21
+ getApproved(calldata: Calldata): BytesWriter;
22
+ setApprovalForAll(calldata: Calldata): BytesWriter;
23
+ isApprovedForAll(calldata: Calldata): BytesWriter;
24
+
25
+ // Advanced functions
26
+ burn(calldata: Calldata): BytesWriter;
27
+ transferBySignature(calldata: Calldata): BytesWriter;
28
+ domainSeparator(calldata: Calldata): BytesWriter;
29
+ }
@@ -0,0 +1,11 @@
1
+ export class OP1155InitParameters {
2
+ public name: string;
3
+ public symbol: string;
4
+ public baseUri: string;
5
+
6
+ constructor(name: string, symbol: string, baseUri: string) {
7
+ this.name = name;
8
+ this.symbol = symbol;
9
+ this.baseUri = baseUri;
10
+ }
11
+ }
@@ -0,0 +1,15 @@
1
+ import { u256 } from '@btc-vision/as-bignum/assembly';
2
+
3
+ export class OP721InitParameters {
4
+ public name: string;
5
+ public symbol: string;
6
+ public baseURI: string;
7
+ public maxSupply: u256;
8
+
9
+ constructor(name: string, symbol: string, baseURI: string, maxSupply: u256) {
10
+ this.name = name;
11
+ this.symbol = symbol;
12
+ this.baseURI = baseURI;
13
+ this.maxSupply = maxSupply;
14
+ }
15
+ }
@@ -35,6 +35,14 @@ import { Network, Networks } from '../script/Networks';
35
35
 
36
36
  export * from '../env/global';
37
37
 
38
+ @final
39
+ export class CallResult {
40
+ constructor(
41
+ public readonly success: boolean,
42
+ public readonly data: BytesReader,
43
+ ) {}
44
+ }
45
+
38
46
  @final
39
47
  export class BlockchainEnvironment {
40
48
  public readonly DEAD_ADDRESS: Address = Address.dead();
@@ -204,7 +212,11 @@ export class BlockchainEnvironment {
204
212
  this.createContractIfNotExists();
205
213
  }
206
214
 
207
- public call(destinationContract: Address, calldata: BytesWriter): BytesReader {
215
+ public call(
216
+ destinationContract: Address,
217
+ calldata: BytesWriter,
218
+ stopExecutionOnFailure: boolean = true,
219
+ ): CallResult {
208
220
  if (!destinationContract) {
209
221
  throw new Revert('Destination contract is required');
210
222
  }
@@ -222,11 +234,11 @@ export class BlockchainEnvironment {
222
234
  const resultBuffer = new ArrayBuffer(resultLength);
223
235
  getCallResult(0, resultLength, resultBuffer);
224
236
 
225
- if (status !== 0) {
237
+ if (status !== 0 && stopExecutionOnFailure) {
226
238
  env_exit(status, resultBuffer, resultLength);
227
239
  }
228
240
 
229
- return new BytesReader(Uint8Array.wrap(resultBuffer));
241
+ return new CallResult(status === 0, new BytesReader(Uint8Array.wrap(resultBuffer)));
230
242
  }
231
243
 
232
244
  public log(data: string): void {
@@ -285,6 +297,7 @@ export class BlockchainEnvironment {
285
297
 
286
298
  public getStorageAt(pointerHash: Uint8Array): Uint8Array {
287
299
  this.hasPointerStorageHash(pointerHash);
300
+
288
301
  if (this.storage.has(pointerHash)) {
289
302
  return this.storage.get(pointerHash);
290
303
  }
@@ -367,12 +380,20 @@ export class BlockchainEnvironment {
367
380
  private _internalSetStorageAt(pointerHash: Uint8Array, value: Uint8Array): void {
368
381
  this.storage.set(pointerHash, value);
369
382
 
383
+ if (pointerHash.buffer.byteLength !== 32 || value.buffer.byteLength !== 32) {
384
+ throw new Revert('Pointer and value must be 32 bytes long');
385
+ }
386
+
370
387
  storePointer(pointerHash.buffer, value.buffer);
371
388
  }
372
389
 
373
390
  private _internalSetTransientStorageAt(pointerHash: Uint8Array, value: Uint8Array): void {
374
391
  this.transientStorage.set(pointerHash, value);
375
392
 
393
+ if (pointerHash.buffer.byteLength !== 32 || value.buffer.byteLength !== 32) {
394
+ throw new Revert('Transient pointer and value must be 32 bytes long');
395
+ }
396
+
376
397
  tStorePointer(pointerHash.buffer, value.buffer);
377
398
  }
378
399
 
@@ -381,6 +402,10 @@ export class BlockchainEnvironment {
381
402
  return true;
382
403
  }
383
404
 
405
+ if (pointer.buffer.byteLength !== 32) {
406
+ throw new Revert('Pointer must be 32 bytes long');
407
+ }
408
+
384
409
  // we attempt to load the requested pointer.
385
410
  const resultBuffer = new ArrayBuffer(32);
386
411
  loadPointer(pointer.buffer, resultBuffer);
@@ -396,6 +421,10 @@ export class BlockchainEnvironment {
396
421
  return true;
397
422
  }
398
423
 
424
+ if (pointer.buffer.byteLength !== 32) {
425
+ throw new Revert('Transient pointer must be 32 bytes long');
426
+ }
427
+
399
428
  // we attempt to load the requested pointer.
400
429
  const resultBuffer = new ArrayBuffer(32);
401
430
  tLoadPointer(pointer.buffer, resultBuffer);
@@ -0,0 +1,16 @@
1
+ import { NetEvent } from '../NetEvent';
2
+ import { BytesWriter } from '../../buffer/BytesWriter';
3
+ import { Address } from '../../types/Address';
4
+ import { ADDRESS_BYTE_LENGTH } from '../../utils';
5
+
6
+ @final
7
+ export class ApprovedForAllEvent extends NetEvent {
8
+ constructor(account: Address, operator: Address, approved: boolean) {
9
+ const writer = new BytesWriter(ADDRESS_BYTE_LENGTH * 2 + 1);
10
+ writer.writeAddress(account);
11
+ writer.writeAddress(operator);
12
+ writer.writeBoolean(approved);
13
+
14
+ super('ApprovedForAll', writer);
15
+ }
16
+ }
@@ -0,0 +1,35 @@
1
+ import { NetEvent } from '../NetEvent';
2
+ import { Address } from '../../types/Address';
3
+ import { u256 } from '@btc-vision/as-bignum/assembly';
4
+ import { ADDRESS_BYTE_LENGTH, U256_BYTE_LENGTH, U32_BYTE_LENGTH } from '../../utils';
5
+ import { BytesWriter } from '../../buffer/BytesWriter';
6
+ import { Revert } from '../../types/Revert';
7
+
8
+ @final
9
+ export class TransferredBatchEvent extends NetEvent {
10
+ constructor(operator: Address, from: Address, to: Address, ids: u256[], values: u256[]) {
11
+ // Check max array size to avoid exceeding event data limit
12
+ // 3 addresses (32*3) + 2 lengths (4*2) = 104 bytes overhead
13
+ // Each id+value pair = 64 bytes
14
+ // Max pairs = (352 - 104) / 64 = 3.875, so max 3 items
15
+ if (ids.length > 3) {
16
+ throw new Revert('TransferBatch event exceeds max data size');
17
+ }
18
+
19
+ const size =
20
+ ADDRESS_BYTE_LENGTH * 3 + U32_BYTE_LENGTH * 2 + ids.length * U256_BYTE_LENGTH * 2;
21
+ const writer = new BytesWriter(size);
22
+ writer.writeAddress(operator);
23
+ writer.writeAddress(from);
24
+ writer.writeAddress(to);
25
+ writer.writeU32(u32(ids.length));
26
+ for (let i = 0; i < ids.length; i++) {
27
+ writer.writeU256(ids[i]);
28
+ }
29
+ writer.writeU32(u32(values.length));
30
+ for (let i = 0; i < values.length; i++) {
31
+ writer.writeU256(values[i]);
32
+ }
33
+ super('TransferredBatch', writer);
34
+ }
35
+ }
@@ -0,0 +1,18 @@
1
+ import { NetEvent } from '../NetEvent';
2
+ import { Address } from '../../types/Address';
3
+ import { u256 } from '@btc-vision/as-bignum/assembly';
4
+ import { BytesWriter } from '../../buffer/BytesWriter';
5
+ import { ADDRESS_BYTE_LENGTH, U256_BYTE_LENGTH } from '../../utils';
6
+
7
+ @final
8
+ export class TransferredSingleEvent extends NetEvent {
9
+ constructor(operator: Address, from: Address, to: Address, id: u256, value: u256) {
10
+ const writer = new BytesWriter(ADDRESS_BYTE_LENGTH * 3 + U256_BYTE_LENGTH * 2);
11
+ writer.writeAddress(operator);
12
+ writer.writeAddress(from);
13
+ writer.writeAddress(to);
14
+ writer.writeU256(id);
15
+ writer.writeU256(value);
16
+ super('TransferredSingle', writer);
17
+ }
18
+ }
@@ -0,0 +1,23 @@
1
+ import { NetEvent } from '../NetEvent';
2
+ import { u256 } from '@btc-vision/as-bignum/assembly';
3
+ import { BytesWriter } from '../../buffer/BytesWriter';
4
+ import { U256_BYTE_LENGTH, U32_BYTE_LENGTH } from '../../utils';
5
+ import { Revert } from '../../types/Revert';
6
+
7
+ export const MAX_URI_LENGTH: u32 = 200;
8
+
9
+ @final
10
+ export class URIEvent extends NetEvent {
11
+ constructor(value: string, id: u256) {
12
+ const valueBytes: i32 = String.UTF8.byteLength(value);
13
+
14
+ if (valueBytes > MAX_URI_LENGTH) {
15
+ throw new Revert('URI event exceeds max data size');
16
+ }
17
+
18
+ const writer = new BytesWriter(U32_BYTE_LENGTH + valueBytes + U256_BYTE_LENGTH);
19
+ writer.writeStringWithLength(value);
20
+ writer.writeU256(id);
21
+ super('URI', writer);
22
+ }
23
+ }
@@ -2,3 +2,7 @@ export * from './ApprovedEvent';
2
2
  export * from './BurnedEvent';
3
3
  export * from './MintedEvent';
4
4
  export * from './TransferredEvent';
5
+ export * from './ApprovedForAll';
6
+ export * from './URIEvent';
7
+ export * from './TransferredBatchEvent';
8
+ export * from './TransferredSingleEvent';
package/runtime/index.ts CHANGED
@@ -97,3 +97,12 @@ export * from './script/BitcoinAddresses';
97
97
  export * from './script/Networks';
98
98
  export * from './script/Opcodes';
99
99
  export * from './script/Segwit';
100
+
101
+ export * from './constants/Exports';
102
+ export * from './contracts/OP721';
103
+ export * from './contracts/interfaces/IOP721';
104
+ export * from './contracts/OP1155';
105
+ export * from './contracts/interfaces/IOP1155';
106
+ export * from './contracts/interfaces/OP721InitParameters';
107
+ export * from './contracts/ReentrancyGuard';
108
+ export * from './contracts/interfaces/OP1155InitParameters';
@@ -68,11 +68,34 @@ export function u256To30Bytes(value: u256): Uint8Array {
68
68
 
69
69
  /**
70
70
  * Optimized pointer encoding, see encodePointerUnknownLength for a more generic version.
71
+ *
72
+ * ⚠️ CRITICAL SECURITY NOTICE ⚠️
73
+ * ================================================================================================
74
+ * THIS FUNCTION WILL OVERWRITE THE FIRST 2 BYTES OF YOUR DATA!
75
+ *
76
+ * If you pass a 32-byte buffer, the first 2 bytes WILL BE DESTROYED and replaced with the
77
+ * uniqueIdentifier. This is BY DESIGN for pointer encoding.
78
+ *
79
+ * CORRECT USAGE:
80
+ * - For incremental pointers: You MUST provide exactly 30 bytes of data, NOT 32 bytes
81
+ * - The function will create a new 32-byte buffer with:
82
+ * - Bytes 0-1: uniqueIdentifier (split into two bytes)
83
+ * - Bytes 2-31: Your 30 bytes of data
84
+ *
85
+ * INCORRECT USAGE (DATA LOSS):
86
+ * - DO NOT pass 32 bytes expecting them to be preserved
87
+ * - DO NOT assume this function appends the identifier - it PREPENDS and shifts your data
88
+ *
89
+ * Example:
90
+ * - Input: uniqueIdentifier=0x1234, data=[30 bytes]
91
+ * - Output: [0x34, 0x12, ...your 30 bytes...] = 32 bytes total
92
+ *
71
93
  * @param uniqueIdentifier
72
94
  * @param typed
73
95
  * @param enforce30Bytes
74
96
  * @param {string} context Optional debug context.
75
97
  */
98
+ @unsafe
76
99
  export function encodePointer(uniqueIdentifier: u16, typed: Uint8Array, enforce30Bytes: boolean = true, context: string = ''): Uint8Array {
77
100
  const array = ensureAtLeast30Bytes(typed);
78
101
 
@@ -159,6 +159,10 @@ export function writeLengthAndStartIndex(length: u32, startIndex: u32): Uint8Arr
159
159
 
160
160
  @inline
161
161
  export function bigEndianAdd(base: Uint8Array, increment: u64): Uint8Array {
162
+ if(base.length !== 32) {
163
+ throw new Revert('bigEndianAdd: base must be 32 bytes');
164
+ }
165
+
162
166
  const add = u64ToBE32Bytes(increment);
163
167
 
164
168
  return addUint8ArraysBE(base, add);
@@ -5,6 +5,26 @@ import { u256 } from '@btc-vision/as-bignum/assembly';
5
5
  import { EMPTY_BUFFER } from '../math/bytes';
6
6
  import { Revert } from '../types/Revert';
7
7
 
8
+ /**
9
+ * SECURITY NOTICE:
10
+ *
11
+ * This class uses a 30-byte truncation of addresses for storage pointer generation.
12
+ * While this may appear to introduce collision risks, it is secure within the OP_NET
13
+ * protocol context because:
14
+ *
15
+ * 1. All addresses in OP_NET are tweaked public keys (32-byte elliptic curve points)
16
+ *
17
+ * 2. Tweaked public keys are uniformly distributed across the secp256k1 curve space
18
+ *
19
+ * 3. Finding two public keys with identical 30-byte prefixes (240 bits) requires
20
+ * approximately 2^120 operations due to the birthday paradox
21
+ *
22
+ * 4. The probability of accidentally generating colliding addresses through normal
23
+ * key generation is cryptographically negligible
24
+ *
25
+ * The truncation from 32 to 30 bytes is a space optimization that does not
26
+ * meaningfully impact security given the uniform distribution of elliptic curve points.
27
+ */
8
28
  @final
9
29
  export class AddressMemoryMap {
10
30
  public pointer: u16;
@@ -14,6 +14,7 @@ import { U256Codec } from '../codecs/U256Codec';
14
14
 
15
15
  import { Address } from '../../types/Address';
16
16
  import { Revert } from '../../types/Revert';
17
+ import { EMPTY_BUFFER } from '../../math/bytes';
17
18
 
18
19
  /**
19
20
  * A reflection-based StorageMap<K, V>.
@@ -32,10 +33,6 @@ export class StorageMap<K, V> {
32
33
  this.pointer = pointer;
33
34
  }
34
35
 
35
- // ----------------------------------------------------------
36
- // PUBLIC API
37
- // ----------------------------------------------------------
38
-
39
36
  public set(key: K, value: V): this {
40
37
  const storageKey = this.getStorageKey(key);
41
38
  this.storeValue<V>(storageKey, value);
@@ -59,7 +56,7 @@ export class StorageMap<K, V> {
59
56
  return false;
60
57
  }
61
58
 
62
- Blockchain.setStorageAt(storageKey, new Uint8Array(0));
59
+ Blockchain.setStorageAt(storageKey, EMPTY_BUFFER);
63
60
  return true;
64
61
  }
65
62
 
@@ -69,22 +66,12 @@ export class StorageMap<K, V> {
69
66
  throw new Revert('clear() not implemented; no key-tracking logic here.');
70
67
  }
71
68
 
72
- // ----------------------------------------------------------
73
- // INTERNAL: Derive the final storage key
74
- // ----------------------------------------------------------
75
-
76
69
  private getStorageKey(k: K): Uint8Array {
77
- // 1) encode the key
78
70
  const keyBytes = this.encodeValue<K>(k);
79
71
 
80
- // 2) transform with pointer
81
72
  return encodePointerUnknownLength(this.pointer, keyBytes);
82
73
  }
83
74
 
84
- // ----------------------------------------------------------
85
- // ENCODE / DECODE
86
- // ----------------------------------------------------------
87
-
88
75
  /**
89
76
  * Retrieve the value of type P from the given storage pointer (32 bytes).
90
77
  * If it's a variable-length type, we decode from pointer-based approach
@@ -123,10 +110,6 @@ export class StorageMap<K, V> {
123
110
  Blockchain.setStorageAt(storageKey, raw);
124
111
  }
125
112
 
126
- // ----------------------------------------------------------
127
- // decodeBytesAsType: interpret raw bytes as type P
128
- // ----------------------------------------------------------
129
-
130
113
  private decodeBytesAsType<P>(raw: Uint8Array): P {
131
114
  // isInteger => built-in numeric types (i32, u32, etc.)
132
115
  if (isInteger<P>()) {
@@ -173,10 +156,6 @@ export class StorageMap<K, V> {
173
156
  throw new Revert(`Unsupported type ${typeId}`);
174
157
  }
175
158
 
176
- // ----------------------------------------------------------
177
- // encodeValue<P>: convert a value of type P into raw bytes
178
- // ----------------------------------------------------------
179
-
180
159
  private encodeValue<P>(value: P): Uint8Array {
181
160
  // built-in integer => write with BytesWriter
182
161
  if (isInteger<P>()) {
@@ -221,6 +200,7 @@ export class StorageMap<K, V> {
221
200
 
222
201
  // If nested map => handle in a custom branch (not shown here):
223
202
  // if (value instanceof StorageMap<...>) { ... }
203
+ // TODO
224
204
 
225
205
  throw new Revert('encodeValue: Unsupported type');
226
206
  }
@@ -4,6 +4,9 @@ import { encodePointerUnknownLength } from '../../math/abi';
4
4
  import { Blockchain } from '../../env';
5
5
  import { Revert } from '../../types/Revert';
6
6
 
7
+ const SLOT_BYTES: i32 = 32;
8
+ const PRESENCE_BYTE: u8 = 1;
9
+
7
10
  export class StorageSet<T> {
8
11
  private readonly pointer: u16;
9
12
  private readonly parentKey: Uint8Array;
@@ -20,8 +23,8 @@ export class StorageSet<T> {
20
23
  const storageKey = this.getStorageKey(value);
21
24
 
22
25
  // A 1-byte array with a single 0x01 is enough to mark presence
23
- const flag = new Uint8Array(1);
24
- flag[31] = 1;
26
+ const flag = new Uint8Array(SLOT_BYTES);
27
+ flag[31] = PRESENCE_BYTE;
25
28
 
26
29
  Blockchain.setStorageAt(storageKey, flag);
27
30
  }
@@ -37,7 +40,7 @@ export class StorageSet<T> {
37
40
  return false;
38
41
  }
39
42
 
40
- Blockchain.setStorageAt(storageKey, new Uint8Array(32));
43
+ Blockchain.setStorageAt(storageKey, new Uint8Array(SLOT_BYTES));
41
44
  return true;
42
45
  }
43
46
 
@@ -64,7 +64,7 @@ export class ScriptNumber {
64
64
  /**
65
65
  * Decode result type for safe error handling
66
66
  */
67
- static decodeResult(data: Uint8Array, minimal: bool = true): DecodeNumberResult {
67
+ public static decodeResult(data: Uint8Array, minimal: bool = true): DecodeNumberResult {
68
68
  const L = data.length;
69
69
  if (L == 0) return DecodeNumberResult.ok(0);
70
70
 
@@ -41,7 +41,7 @@ export class ECPoint {
41
41
  // x3 = λ^2 - 2x mod P
42
42
  // y3 = λ*(x - x3) - y mod P
43
43
  // ----------------------------
44
- static double(p: ECPoint): ECPoint {
44
+ public static double(p: ECPoint): ECPoint {
45
45
  // If y=0, return infinity
46
46
  if (p.y == u256.Zero) {
47
47
  return new ECPoint(u256.Zero, u256.Zero); // "Point at infinity" convention
@@ -80,7 +80,7 @@ export class ECPoint {
80
80
  // x3 = λ^2 - x1 - x2 mod P
81
81
  // y3 = λ*(x1 - x3) - y1 mod P
82
82
  // ----------------------------
83
- static add(p: ECPoint, q: ECPoint): ECPoint {
83
+ public static add(p: ECPoint, q: ECPoint): ECPoint {
84
84
  // 1) Check for infinity cases
85
85
  const isPInfinity = p.x.isZero() && p.y.isZero();
86
86
  const isQInfinity = q.x.isZero() && q.y.isZero();
@@ -122,7 +122,7 @@ export class ECPoint {
122
122
  // Scalar Multiplication: k*P
123
123
  // Double-and-add approach
124
124
  // ----------------------------
125
- static scalarMultiply(p: ECPoint, k: u256): ECPoint {
125
+ public static scalarMultiply(p: ECPoint, k: u256): ECPoint {
126
126
  let result = new ECPoint(u256.Zero, u256.Zero); // ∞
127
127
  let addend = p;
128
128
  const two = u256.fromU64(2);