@btc-vision/btc-runtime 1.9.16 → 1.10.0

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.
@@ -1,14 +1,57 @@
1
1
  import { Potential } from '../lang/Definitions';
2
2
  import { ADDRESS_BYTE_LENGTH, decodeHexArray, encodeHexFromBuffer } from '../utils';
3
3
  import { Revert } from './Revert';
4
- import { BitcoinAddresses } from '../script/BitcoinAddresses';
5
- import { Blockchain } from '../env';
6
- import { Network } from '../script/Networks';
7
-
8
- @final
4
+ import { loadMLDSAPublicKey } from '../env/global';
5
+ import { MLDSASecurityLevel } from '../env/consensus/MLDSAMetadata';
6
+ import { ArrayLike } from '../interfaces/as';
7
+
8
+ /**
9
+ * Represents a 32-byte address in the OPNet system.
10
+ *
11
+ * This class extends Uint8Array to provide a fixed-size 32-byte address with additional
12
+ * functionality for quantum-resistant cryptography. The address stores the SHA256 hash
13
+ * of an ML-DSA public key and provides lazy loading of the full ML-DSA public key when
14
+ * needed for signature verification.
15
+ *
16
+ * @remarks
17
+ * Addresses are immutable once set - attempting to modify an address after construction
18
+ * will throw an error. This ensures address integrity throughout the contract lifecycle.
19
+ *
20
+ * The class provides operator overloading for comparison operations, treating addresses
21
+ * as big-endian 256-bit integers for ordering purposes.
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * // Create from hex string
26
+ * const addr1 = Address.fromString('0x1234...abcd');
27
+ *
28
+ * // Create zero address
29
+ * const zero = Address.zero();
30
+ *
31
+ * // Compare addresses
32
+ * if (addr1 > zero) {
33
+ * // addr1 is greater than zero
34
+ * }
35
+ *
36
+ * // Get ML-DSA public key for verification
37
+ * const mldsaKey = addr1.mldsaPublicKey;
38
+ * ```
39
+ */
9
40
  export class Address extends Uint8Array {
10
- private isDefined: boolean = false;
41
+ /**
42
+ * Indicates whether the address has been initialized with data.
43
+ * Once true, the address becomes immutable.
44
+ */
45
+ protected isDefined: boolean = false;
11
46
 
47
+ /**
48
+ * Creates a new Address instance.
49
+ *
50
+ * @param bytes - Optional 32-byte array representing the address.
51
+ * If empty or not provided, creates a zero address.
52
+ *
53
+ * @throws {Revert} If bytes length is not exactly 32
54
+ */
12
55
  public constructor(bytes: u8[] = []) {
13
56
  super(ADDRESS_BYTE_LENGTH);
14
57
 
@@ -18,17 +61,64 @@ export class Address extends Uint8Array {
18
61
  }
19
62
 
20
63
  /**
21
- * Dead address (284ae4acdb32a99ba3ebfa66a91ddb41a7b7a1d2fef415399922cd8a04485c02)
22
- * generated from 04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f
64
+ * Cached ML-DSA public key, loaded lazily when first accessed.
65
+ */
66
+ protected _mldsaPublicKey: Potential<Uint8Array> = null;
67
+
68
+ /**
69
+ * Gets the ML-DSA public key associated with this address.
70
+ *
71
+ * The full ML-DSA public key is loaded from storage on first access and cached
72
+ * for subsequent uses. This key is used for quantum-resistant signature verification
73
+ * when the consensus transitions away from Schnorr signatures.
74
+ *
75
+ * @returns The ML-DSA Level 2 (ML-DSA-44) public key for this address
76
+ *
77
+ * @remarks
78
+ * The address itself stores only the SHA256 hash of the ML-DSA public key.
79
+ * The full key (which is much larger, typically ~1312 bytes for Level 2)
80
+ * is stored separately and loaded on demand for efficiency.
23
81
  */
24
- public static dead(): Address {
25
- return DEAD_ADDRESS.clone();
82
+ public get mldsaPublicKey(): Uint8Array {
83
+ if (!this._mldsaPublicKey) {
84
+ this._mldsaPublicKey = loadMLDSAPublicKey(this, MLDSASecurityLevel.Level2);
85
+ }
86
+
87
+ return this._mldsaPublicKey as Uint8Array;
26
88
  }
27
89
 
90
+ /**
91
+ * Creates a zero address (all 32 bytes are 0x00).
92
+ *
93
+ * @returns A new Address instance with all bytes set to zero
94
+ *
95
+ * @example
96
+ * ```typescript
97
+ * const zero = Address.zero();
98
+ * console.log(zero.isZero()); // true
99
+ * ```
100
+ */
28
101
  public static zero(): Address {
29
102
  return ZERO_ADDRESS.clone();
30
103
  }
31
104
 
105
+ /**
106
+ * Creates an Address from a hexadecimal string representation.
107
+ *
108
+ * @param pubKey - The 32-byte address as a hexadecimal string.
109
+ * Can be prefixed with '0x' or unprefixed.
110
+ *
111
+ * @returns A new Address instance
112
+ *
113
+ * @throws {Error} If the decoded hex string is not exactly 32 bytes
114
+ * @throws {Error} If the string contains invalid hexadecimal characters
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * const addr1 = Address.fromString('0x' + '00'.repeat(32));
119
+ * const addr2 = Address.fromString('deadbeef'.repeat(8));
120
+ * ```
121
+ */
32
122
  public static fromString(pubKey: string): Address {
33
123
  if (pubKey.startsWith('0x')) {
34
124
  pubKey = pubKey.slice(2);
@@ -37,23 +127,41 @@ export class Address extends Uint8Array {
37
127
  return new Address(decodeHexArray(pubKey));
38
128
  }
39
129
 
130
+ /**
131
+ * Creates an Address from a Uint8Array using direct memory copy.
132
+ *
133
+ * This method is more efficient than the constructor for creating
134
+ * addresses from existing Uint8Array data as it uses direct memory
135
+ * copying instead of element-by-element copying.
136
+ *
137
+ * @param bytes - The source Uint8Array containing exactly 32 bytes
138
+ *
139
+ * @returns A new Address instance with data copied from the input
140
+ *
141
+ * @remarks
142
+ * The input array must be exactly ADDRESS_BYTE_LENGTH (32) bytes.
143
+ * This method performs a raw memory copy, so ensure the input is valid.
144
+ */
40
145
  public static fromUint8Array(bytes: Uint8Array): Address {
41
- const cloned = new Address();
146
+ const cloned = new Address([]);
147
+
42
148
  // Copy the raw memory directly:
43
149
  memory.copy(cloned.dataStart, bytes.dataStart, ADDRESS_BYTE_LENGTH);
44
150
 
45
151
  return cloned;
46
152
  }
47
153
 
48
- public static toCSV(address: Uint8Array, blocks: u32): string {
49
- return BitcoinAddresses.csvP2wshAddress(address, blocks, Network.hrp(Blockchain.network))
50
- .address;
51
- }
52
-
53
- public static p2wpkh(address: Uint8Array): string {
54
- return BitcoinAddresses.p2wpkh(address, Network.hrp(Blockchain.network));
55
- }
56
-
154
+ /**
155
+ * Checks if this address is the zero address.
156
+ *
157
+ * @returns `true` if all 32 bytes are zero, `false` otherwise
158
+ *
159
+ * @example
160
+ * ```typescript
161
+ * const addr = Address.zero();
162
+ * console.log(addr.isZero()); // true
163
+ * ```
164
+ */
57
165
  public isZero(): bool {
58
166
  for (let i = 0; i < this.length; i++) {
59
167
  if (this[i] != 0) {
@@ -64,25 +172,18 @@ export class Address extends Uint8Array {
64
172
  return true;
65
173
  }
66
174
 
67
- public isDead(): bool {
68
- for (let i = 0; i < this.length; i++) {
69
- if (this[i] != DEAD_ADDRESS[i]) {
70
- return false;
71
- }
72
- }
73
- return true;
74
- }
75
-
76
- public p2tr(): string {
77
- return BitcoinAddresses.p2trKeyPathAddress(this, Network.hrp(Blockchain.network));
78
- }
79
-
80
175
  /**
81
- * Create a new Address that is a copy of the current Address.
82
- * @returns {Address}
176
+ * Creates a deep copy of this Address.
177
+ *
178
+ * @returns A new Address instance with the same data and isDefined state
179
+ *
180
+ * @remarks
181
+ * Uses direct memory copy for efficiency. The cloned address maintains
182
+ * the same mutability state as the original.
83
183
  */
84
184
  public clone(): Address {
85
- const cloned = new Address();
185
+ const cloned = new Address([]);
186
+
86
187
  // Copy the raw memory directly:
87
188
  memory.copy(cloned.dataStart, this.dataStart, ADDRESS_BYTE_LENGTH);
88
189
 
@@ -92,10 +193,27 @@ export class Address extends Uint8Array {
92
193
  return cloned;
93
194
  }
94
195
 
196
+ /**
197
+ * Converts the address to a hexadecimal string representation.
198
+ *
199
+ * @returns The address as a lowercase hex string without '0x' prefix
200
+ *
201
+ * @example
202
+ * ```typescript
203
+ * const addr = Address.fromString('0xdead' + '00'.repeat(30));
204
+ * console.log(addr.toHex()); // "dead" + "00".repeat(30)
205
+ * ```
206
+ */
95
207
  public toHex(): string {
96
208
  return encodeHexFromBuffer(this.buffer);
97
209
  }
98
210
 
211
+ /**
212
+ * Checks equality with another address (operator ==).
213
+ *
214
+ * @param a - The address to compare with
215
+ * @returns `true` if both addresses have identical bytes, `false` otherwise
216
+ */
99
217
  @operator('==')
100
218
  public equals(a: Address): bool {
101
219
  if (a.length != this.length) {
@@ -111,6 +229,14 @@ export class Address extends Uint8Array {
111
229
  return true;
112
230
  }
113
231
 
232
+ /**
233
+ * Checks if this address is less than another (operator <).
234
+ *
235
+ * Comparison is done byte-by-byte treating addresses as big-endian 256-bit integers.
236
+ *
237
+ * @param a - The address to compare with
238
+ * @returns `true` if this address is numerically less than `a`
239
+ */
114
240
  @operator('<')
115
241
  public lessThan(a: Address): bool {
116
242
  // Compare the two addresses byte-by-byte, treating them as big-endian uint256
@@ -128,6 +254,14 @@ export class Address extends Uint8Array {
128
254
  return false;
129
255
  }
130
256
 
257
+ /**
258
+ * Checks if this address is greater than another (operator >).
259
+ *
260
+ * Comparison is done byte-by-byte treating addresses as big-endian 256-bit integers.
261
+ *
262
+ * @param a - The address to compare with
263
+ * @returns `true` if this address is numerically greater than `a`
264
+ */
131
265
  @operator('>')
132
266
  public greaterThan(a: Address): bool {
133
267
  // Compare the two addresses byte-by-byte, treating them as big-endian uint256
@@ -145,31 +279,58 @@ export class Address extends Uint8Array {
145
279
  return false;
146
280
  }
147
281
 
282
+ /**
283
+ * Checks if this address is less than or equal to another (operator <=).
284
+ *
285
+ * @param a - The address to compare with
286
+ * @returns `true` if this address is numerically less than or equal to `a`
287
+ */
148
288
  @operator('<=')
149
289
  public lessThanOrEqual(a: Address): bool {
150
290
  return this.lessThan(a) || this.equals(a);
151
291
  }
152
292
 
293
+ /**
294
+ * Checks if this address is greater than or equal to another (operator >=).
295
+ *
296
+ * @param a - The address to compare with
297
+ * @returns `true` if this address is numerically greater than or equal to `a`
298
+ */
153
299
  @operator('>=')
154
300
  public greaterThanOrEqual(a: Address): bool {
155
301
  return this.greaterThan(a) || this.equals(a);
156
302
  }
157
303
 
304
+ /**
305
+ * Checks inequality with another address (operator !=).
306
+ *
307
+ * @param a - The address to compare with
308
+ * @returns `true` if addresses have different bytes, `false` if identical
309
+ */
158
310
  @operator('!=')
159
311
  public notEquals(a: Address): bool {
160
312
  return !this.equals(a);
161
313
  }
162
314
 
315
+ /**
316
+ * Returns the hexadecimal string representation of the address.
317
+ *
318
+ * @returns The address as a hex string (delegates to toHex())
319
+ */
163
320
  public toString(): string {
164
- return this.p2tr();
321
+ return this.toHex();
165
322
  }
166
323
 
167
324
  /**
168
- * Set the public key
169
- * @param {ArrayLike} publicKey The public key
170
- * @returns {void}
325
+ * Sets the address data and marks it as immutable.
326
+ *
327
+ * @param publicKey - The 32-byte public key data
328
+ *
329
+ * @throws {Revert} If publicKey length is not exactly 32 bytes
330
+ *
331
+ * @private
171
332
  */
172
- private newSet(publicKey: u8[]): void {
333
+ private newSet<U extends ArrayLike<number>>(publicKey: U): void {
173
334
  if (publicKey.length !== 32) {
174
335
  throw new Revert(`Invalid public key length (${publicKey.length})`);
175
336
  }
@@ -179,6 +340,16 @@ export class Address extends Uint8Array {
179
340
  this.isDefined = true;
180
341
  }
181
342
 
343
+ /**
344
+ * Array index getter (operator []).
345
+ *
346
+ * @param index - The byte index to access (0-31)
347
+ * @returns The byte value at the specified index
348
+ *
349
+ * @throws {RangeError} If index is out of bounds
350
+ *
351
+ * @private
352
+ */
182
353
  @operator('[]')
183
354
  private ___get(index: i32): u8 {
184
355
  if (u32(index) >= u32(this.length)) {
@@ -188,6 +359,17 @@ export class Address extends Uint8Array {
188
359
  return load<u8>(this.dataStart + <usize>index);
189
360
  }
190
361
 
362
+ /**
363
+ * Array index setter (operator []=).
364
+ *
365
+ * @param index - The byte index to set (0-31)
366
+ * @param value - The byte value to set
367
+ *
368
+ * @throws {Revert} If the address is already defined (immutable)
369
+ * @throws {RangeError} If index is out of bounds
370
+ *
371
+ * @private
372
+ */
191
373
  @operator('[]=')
192
374
  private ___set(index: i32, value: u8): void {
193
375
  if (this.isDefined) {
@@ -202,10 +384,12 @@ export class Address extends Uint8Array {
202
384
  }
203
385
  }
204
386
 
205
- export const ZERO_ADDRESS: Address = new Address();
206
- export const DEAD_ADDRESS: Address = new Address([
207
- 40, 74, 228, 172, 219, 50, 169, 155, 163, 235, 250, 102, 169, 29, 219, 65, 167, 183, 161, 210,
208
- 254, 244, 21, 57, 153, 34, 205, 138, 4, 72, 92, 2,
209
- ]);
387
+ /**
388
+ * Pre-initialized zero address constant for efficiency.
389
+ */
390
+ export const ZERO_ADDRESS: Address = new Address([]);
210
391
 
392
+ /**
393
+ * Type alias for nullable Address references.
394
+ */
211
395
  export declare type PotentialAddress = Potential<Address>;