@btc-vision/transaction 1.8.0-alpha.1 → 1.8.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/browser/_version.d.ts +1 -1
- package/browser/abi/ABICoder.d.ts +8 -0
- package/browser/buffer/BinaryReader.d.ts +16 -1
- package/browser/buffer/BinaryWriter.d.ts +11 -1
- package/browser/deterministic/ExtendedAddressMap.d.ts +3 -1
- package/browser/index.js +1102 -861
- package/browser/keypair/Address.d.ts +1 -0
- package/browser/utils/lengths.d.ts +3 -1
- package/browser/utils/types.d.ts +3 -0
- package/build/_version.d.ts +1 -1
- package/build/_version.js +1 -1
- package/build/abi/ABICoder.d.ts +8 -0
- package/build/abi/ABICoder.js +32 -0
- package/build/buffer/BinaryReader.d.ts +16 -1
- package/build/buffer/BinaryReader.js +66 -1
- package/build/buffer/BinaryWriter.d.ts +11 -1
- package/build/buffer/BinaryWriter.js +66 -1
- package/build/deterministic/ExtendedAddressMap.d.ts +3 -1
- package/build/deterministic/ExtendedAddressMap.js +44 -17
- package/build/keypair/Address.d.ts +1 -0
- package/build/keypair/Address.js +19 -4
- package/build/transaction/builders/MultiSignTransaction.js +2 -2
- package/build/transaction/shared/TweakedTransaction.js +3 -3
- package/build/tsconfig.build.tsbuildinfo +1 -1
- package/build/utils/lengths.d.ts +3 -1
- package/build/utils/lengths.js +3 -1
- package/build/utils/types.d.ts +3 -0
- package/package.json +1 -1
- package/src/_version.ts +1 -1
- package/src/abi/ABICoder.ts +43 -0
- package/src/buffer/BinaryReader.ts +158 -2
- package/src/buffer/BinaryWriter.ts +143 -1
- package/src/deterministic/ExtendedAddressMap.ts +59 -20
- package/src/keypair/Address.ts +27 -6
- package/src/transaction/builders/MultiSignTransaction.ts +2 -2
- package/src/transaction/shared/TweakedTransaction.ts +3 -3
- package/src/utils/lengths.ts +3 -1
- package/src/utils/types.ts +4 -1
- package/test/binary-reader-writer.test.ts +457 -0
- package/test/derivePath.test.ts +4 -4
package/build/utils/lengths.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export const ADDRESS_BYTE_LENGTH = 32;
|
|
2
|
+
export const EXTENDED_ADDRESS_BYTE_LENGTH = 64;
|
|
3
|
+
export const SCHNORR_SIGNATURE_BYTE_LENGTH = 64;
|
|
2
4
|
export const SELECTOR_BYTE_LENGTH = 4;
|
|
3
5
|
export const U256_BYTE_LENGTH = 32;
|
|
4
6
|
export const U128_BYTE_LENGTH = 16;
|
|
@@ -9,7 +11,7 @@ export const U8_BYTE_LENGTH = 1;
|
|
|
9
11
|
export const I256_BYTE_LENGTH = 32;
|
|
10
12
|
export const I128_BYTE_LENGTH = 16;
|
|
11
13
|
export const I64_BYTE_LENGTH = 8;
|
|
12
|
-
export const
|
|
14
|
+
export const I32_BYTE_LENGTH = 4;
|
|
13
15
|
export const I16_BYTE_LENGTH = 2;
|
|
14
16
|
export const I8_BYTE_LENGTH = 1;
|
|
15
17
|
export const BOOLEAN_BYTE_LENGTH = 1;
|
package/build/utils/types.d.ts
CHANGED
|
@@ -4,7 +4,10 @@ export type BufferLike = Uint8Array | Buffer;
|
|
|
4
4
|
export type MemorySlotData<T> = T;
|
|
5
5
|
export type PointerStorage = DeterministicMap<MemorySlotPointer, MemorySlotData<bigint>>;
|
|
6
6
|
export type BlockchainStorage = DeterministicMap<string, PointerStorage>;
|
|
7
|
+
export type i8 = number;
|
|
8
|
+
export type i16 = number;
|
|
7
9
|
export type i32 = number;
|
|
10
|
+
export type i64 = bigint;
|
|
8
11
|
export type u8 = number;
|
|
9
12
|
export type u16 = number;
|
|
10
13
|
export type u32 = number;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@btc-vision/transaction",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.8.0-alpha.
|
|
4
|
+
"version": "1.8.0-alpha.2",
|
|
5
5
|
"author": "BlobMaster41",
|
|
6
6
|
"description": "OPNet transaction library allows you to create and sign transactions for the OPNet network.",
|
|
7
7
|
"engines": {
|
package/src/_version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.
|
|
1
|
+
export const version = '1.8.0-alpha.2';
|
package/src/abi/ABICoder.ts
CHANGED
|
@@ -4,21 +4,40 @@ import { BinaryReader } from '../buffer/BinaryReader.js';
|
|
|
4
4
|
import { BufferHelper } from '../utils/BufferHelper.js';
|
|
5
5
|
|
|
6
6
|
export enum ABIDataTypes {
|
|
7
|
+
// Unsigned integers
|
|
7
8
|
UINT8 = 'UINT8',
|
|
8
9
|
UINT16 = 'UINT16',
|
|
9
10
|
UINT32 = 'UINT32',
|
|
10
11
|
UINT64 = 'UINT64',
|
|
11
12
|
UINT128 = 'UINT128',
|
|
12
13
|
UINT256 = 'UINT256',
|
|
14
|
+
|
|
15
|
+
// Signed integers
|
|
16
|
+
INT8 = 'INT8',
|
|
17
|
+
INT16 = 'INT16',
|
|
18
|
+
INT32 = 'INT32',
|
|
19
|
+
INT64 = 'INT64',
|
|
13
20
|
INT128 = 'INT128',
|
|
21
|
+
|
|
22
|
+
// Basic types
|
|
14
23
|
BOOL = 'BOOL',
|
|
15
24
|
ADDRESS = 'ADDRESS',
|
|
25
|
+
EXTENDED_ADDRESS = 'EXTENDED_ADDRESS',
|
|
16
26
|
STRING = 'STRING',
|
|
17
27
|
BYTES4 = 'BYTES4',
|
|
18
28
|
BYTES32 = 'BYTES32',
|
|
19
29
|
BYTES = 'BYTES',
|
|
30
|
+
|
|
31
|
+
// Tuples/Maps
|
|
20
32
|
ADDRESS_UINT256_TUPLE = 'ADDRESS_UINT256_TUPLE',
|
|
33
|
+
EXTENDED_ADDRESS_UINT256_TUPLE = 'EXTENDED_ADDRESS_UINT256_TUPLE',
|
|
34
|
+
|
|
35
|
+
// Signatures
|
|
36
|
+
SCHNORR_SIGNATURE = 'SCHNORR_SIGNATURE',
|
|
37
|
+
|
|
38
|
+
// Arrays
|
|
21
39
|
ARRAY_OF_ADDRESSES = 'ARRAY_OF_ADDRESSES',
|
|
40
|
+
ARRAY_OF_EXTENDED_ADDRESSES = 'ARRAY_OF_EXTENDED_ADDRESSES',
|
|
22
41
|
ARRAY_OF_UINT256 = 'ARRAY_OF_UINT256',
|
|
23
42
|
ARRAY_OF_UINT128 = 'ARRAY_OF_UINT128',
|
|
24
43
|
ARRAY_OF_UINT64 = 'ARRAY_OF_UINT64',
|
|
@@ -68,12 +87,33 @@ export class ABICoder {
|
|
|
68
87
|
case ABIDataTypes.UINT256:
|
|
69
88
|
result.push(byteReader.readU256());
|
|
70
89
|
break;
|
|
90
|
+
case ABIDataTypes.INT8:
|
|
91
|
+
result.push(byteReader.readI8());
|
|
92
|
+
break;
|
|
93
|
+
case ABIDataTypes.INT16:
|
|
94
|
+
result.push(byteReader.readI16());
|
|
95
|
+
break;
|
|
96
|
+
case ABIDataTypes.INT32:
|
|
97
|
+
result.push(byteReader.readI32());
|
|
98
|
+
break;
|
|
99
|
+
case ABIDataTypes.INT64:
|
|
100
|
+
result.push(byteReader.readI64());
|
|
101
|
+
break;
|
|
71
102
|
case ABIDataTypes.INT128:
|
|
72
103
|
result.push(byteReader.readI128());
|
|
73
104
|
break;
|
|
105
|
+
case ABIDataTypes.EXTENDED_ADDRESS:
|
|
106
|
+
result.push(byteReader.readExtendedAddress());
|
|
107
|
+
break;
|
|
74
108
|
case ABIDataTypes.ADDRESS_UINT256_TUPLE:
|
|
75
109
|
result.push(byteReader.readAddressValueTuple());
|
|
76
110
|
break;
|
|
111
|
+
case ABIDataTypes.EXTENDED_ADDRESS_UINT256_TUPLE:
|
|
112
|
+
result.push(byteReader.readExtendedAddressMapU256());
|
|
113
|
+
break;
|
|
114
|
+
case ABIDataTypes.SCHNORR_SIGNATURE:
|
|
115
|
+
result.push(byteReader.readSchnorrSignature());
|
|
116
|
+
break;
|
|
77
117
|
case ABIDataTypes.BYTES:
|
|
78
118
|
result.push(byteReader.readBytesWithLength());
|
|
79
119
|
break;
|
|
@@ -83,6 +123,9 @@ export class ABICoder {
|
|
|
83
123
|
case ABIDataTypes.ARRAY_OF_ADDRESSES:
|
|
84
124
|
result.push(byteReader.readAddressArray());
|
|
85
125
|
break;
|
|
126
|
+
case ABIDataTypes.ARRAY_OF_EXTENDED_ADDRESSES:
|
|
127
|
+
result.push(byteReader.readExtendedAddressArray());
|
|
128
|
+
break;
|
|
86
129
|
case ABIDataTypes.ARRAY_OF_UINT256:
|
|
87
130
|
result.push(byteReader.readU256Array());
|
|
88
131
|
break;
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { AddressMap } from '../deterministic/AddressMap.js';
|
|
2
|
+
import { ExtendedAddressMap } from '../deterministic/ExtendedAddressMap.js';
|
|
2
3
|
import { Address } from '../keypair/Address.js';
|
|
3
4
|
import {
|
|
4
5
|
ADDRESS_BYTE_LENGTH,
|
|
6
|
+
EXTENDED_ADDRESS_BYTE_LENGTH,
|
|
5
7
|
I128_BYTE_LENGTH,
|
|
8
|
+
I16_BYTE_LENGTH,
|
|
9
|
+
I32_BYTE_LENGTH,
|
|
10
|
+
I64_BYTE_LENGTH,
|
|
11
|
+
I8_BYTE_LENGTH,
|
|
12
|
+
SCHNORR_SIGNATURE_BYTE_LENGTH,
|
|
6
13
|
U128_BYTE_LENGTH,
|
|
7
14
|
U16_BYTE_LENGTH,
|
|
8
15
|
U256_BYTE_LENGTH,
|
|
@@ -10,7 +17,20 @@ import {
|
|
|
10
17
|
U64_BYTE_LENGTH,
|
|
11
18
|
U8_BYTE_LENGTH,
|
|
12
19
|
} from '../utils/lengths.js';
|
|
13
|
-
import { BufferLike, Selector, u16, u32, u8 } from '../utils/types.js';
|
|
20
|
+
import { BufferLike, i16, i32, i64, i8, Selector, u16, u32, u8 } from '../utils/types.js';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Represents a Schnorr signature paired with the signer's Address.
|
|
24
|
+
* This class bundles a 64-byte Schnorr signature with its associated
|
|
25
|
+
* full Address (both MLDSA and tweaked keys), providing a complete signed data structure
|
|
26
|
+
* for Bitcoin Taproot-compatible signatures.
|
|
27
|
+
*/
|
|
28
|
+
export interface SchnorrSignature {
|
|
29
|
+
/** The signer's Address containing both MLDSA and tweaked public keys */
|
|
30
|
+
readonly address: Address;
|
|
31
|
+
/** The 64-byte Schnorr signature */
|
|
32
|
+
readonly signature: Uint8Array;
|
|
33
|
+
}
|
|
14
34
|
|
|
15
35
|
export class BinaryReader {
|
|
16
36
|
private buffer: DataView;
|
|
@@ -42,6 +62,10 @@ export class BinaryReader {
|
|
|
42
62
|
this.currentOffset = 0;
|
|
43
63
|
}
|
|
44
64
|
|
|
65
|
+
public get byteLength(): number {
|
|
66
|
+
return this.buffer.byteLength;
|
|
67
|
+
}
|
|
68
|
+
|
|
45
69
|
public length(): number {
|
|
46
70
|
return this.buffer.byteLength;
|
|
47
71
|
}
|
|
@@ -50,6 +74,53 @@ export class BinaryReader {
|
|
|
50
74
|
return this.buffer.byteLength - this.currentOffset;
|
|
51
75
|
}
|
|
52
76
|
|
|
77
|
+
// ------------------- Signed Integer Readers ------------------- //
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Reads a single signed byte (i8).
|
|
81
|
+
*/
|
|
82
|
+
public readI8(): i8 {
|
|
83
|
+
this.verifyEnd(this.currentOffset + I8_BYTE_LENGTH);
|
|
84
|
+
const value = this.buffer.getInt8(this.currentOffset);
|
|
85
|
+
this.currentOffset += I8_BYTE_LENGTH;
|
|
86
|
+
return value;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Reads a signed 16-bit integer. By default, big-endian.
|
|
91
|
+
* @param be - Endianness; true means big-endian (the default).
|
|
92
|
+
*/
|
|
93
|
+
public readI16(be: boolean = true): i16 {
|
|
94
|
+
this.verifyEnd(this.currentOffset + I16_BYTE_LENGTH);
|
|
95
|
+
const value = this.buffer.getInt16(this.currentOffset, !be);
|
|
96
|
+
this.currentOffset += I16_BYTE_LENGTH;
|
|
97
|
+
return value;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Reads a signed 32-bit integer. By default, big-endian.
|
|
102
|
+
* @param be - Endianness; true means big-endian (the default).
|
|
103
|
+
*/
|
|
104
|
+
public readI32(be: boolean = true): i32 {
|
|
105
|
+
this.verifyEnd(this.currentOffset + I32_BYTE_LENGTH);
|
|
106
|
+
const value = this.buffer.getInt32(this.currentOffset, !be);
|
|
107
|
+
this.currentOffset += I32_BYTE_LENGTH;
|
|
108
|
+
return value;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Reads a signed 64-bit integer. By default, big-endian.
|
|
113
|
+
* @param be - Endianness; true means big-endian (the default).
|
|
114
|
+
*/
|
|
115
|
+
public readI64(be: boolean = true): i64 {
|
|
116
|
+
this.verifyEnd(this.currentOffset + I64_BYTE_LENGTH);
|
|
117
|
+
const value = this.buffer.getBigInt64(this.currentOffset, !be);
|
|
118
|
+
this.currentOffset += I64_BYTE_LENGTH;
|
|
119
|
+
return value;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ------------------- Unsigned Integer Readers ------------------- //
|
|
123
|
+
|
|
53
124
|
/**
|
|
54
125
|
* Reads a single unsigned byte (u8).
|
|
55
126
|
*/
|
|
@@ -197,13 +268,62 @@ export class BinaryReader {
|
|
|
197
268
|
}
|
|
198
269
|
|
|
199
270
|
/**
|
|
200
|
-
* Reads an address.
|
|
271
|
+
* Reads an address (32 bytes MLDSA key hash only).
|
|
201
272
|
*/
|
|
202
273
|
public readAddress(): Address {
|
|
203
274
|
const bytes: u8[] = Array.from(this.readBytes(ADDRESS_BYTE_LENGTH));
|
|
204
275
|
return new Address(bytes);
|
|
205
276
|
}
|
|
206
277
|
|
|
278
|
+
/**
|
|
279
|
+
* Reads the tweaked public key portion (32 bytes) and returns it as a raw Uint8Array.
|
|
280
|
+
* Use this when you only need the tweaked key without the full Address object.
|
|
281
|
+
*/
|
|
282
|
+
public readTweakedPublicKey(): Uint8Array {
|
|
283
|
+
this.verifyEnd(this.currentOffset + ADDRESS_BYTE_LENGTH);
|
|
284
|
+
return this.readBytes(ADDRESS_BYTE_LENGTH);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Reads a full address with both MLDSA key hash and tweaked public key (64 bytes total).
|
|
289
|
+
* Format: [32 bytes tweakedPublicKey][32 bytes MLDSA key hash]
|
|
290
|
+
*
|
|
291
|
+
* This is the equivalent of btc-runtime's readExtendedAddress().
|
|
292
|
+
*
|
|
293
|
+
* @returns An Address instance with both keys set
|
|
294
|
+
*/
|
|
295
|
+
public readExtendedAddress(): Address {
|
|
296
|
+
this.verifyEnd(this.currentOffset + EXTENDED_ADDRESS_BYTE_LENGTH);
|
|
297
|
+
|
|
298
|
+
// Read tweaked public key first (32 bytes)
|
|
299
|
+
const tweakedPublicKey: u8[] = Array.from(this.readBytes(ADDRESS_BYTE_LENGTH));
|
|
300
|
+
|
|
301
|
+
// Read MLDSA key hash (32 bytes)
|
|
302
|
+
const mldsaKeyHash: u8[] = Array.from(this.readBytes(ADDRESS_BYTE_LENGTH));
|
|
303
|
+
|
|
304
|
+
return new Address(mldsaKeyHash, tweakedPublicKey);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Reads a Schnorr signature with its associated full Address.
|
|
309
|
+
* Format: [64 bytes full Address][64 bytes signature]
|
|
310
|
+
*
|
|
311
|
+
* Used for deserializing signed data where both the signer's address
|
|
312
|
+
* and their Schnorr signature are stored together.
|
|
313
|
+
*
|
|
314
|
+
* @returns A SchnorrSignature containing the address and signature
|
|
315
|
+
*/
|
|
316
|
+
public readSchnorrSignature(): SchnorrSignature {
|
|
317
|
+
this.verifyEnd(
|
|
318
|
+
this.currentOffset + EXTENDED_ADDRESS_BYTE_LENGTH + SCHNORR_SIGNATURE_BYTE_LENGTH,
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
const address = this.readExtendedAddress();
|
|
322
|
+
const signature = this.readBytes(SCHNORR_SIGNATURE_BYTE_LENGTH);
|
|
323
|
+
|
|
324
|
+
return { address, signature };
|
|
325
|
+
}
|
|
326
|
+
|
|
207
327
|
/**
|
|
208
328
|
* Reads bytes written as [u32 length][bytes].
|
|
209
329
|
* @param maxLength if > 0, enforces an upper bound
|
|
@@ -329,6 +449,42 @@ export class BinaryReader {
|
|
|
329
449
|
return result;
|
|
330
450
|
}
|
|
331
451
|
|
|
452
|
+
/**
|
|
453
|
+
* Reads an array of full addresses (64 bytes each).
|
|
454
|
+
* Format: [u16 length][FullAddress 0][FullAddress 1]...
|
|
455
|
+
*/
|
|
456
|
+
public readExtendedAddressArray(be: boolean = true): Address[] {
|
|
457
|
+
const length = this.readU16(be);
|
|
458
|
+
const result: Address[] = new Array<Address>(length);
|
|
459
|
+
for (let i = 0; i < length; i++) {
|
|
460
|
+
result[i] = this.readExtendedAddress();
|
|
461
|
+
}
|
|
462
|
+
return result;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Reads a map of full Address -> u256 using the tweaked key for map lookup.
|
|
467
|
+
* Format: [u16 length][FullAddress key][u256 value]...
|
|
468
|
+
*
|
|
469
|
+
* This is the equivalent of btc-runtime's readExtendedAddressMapU256().
|
|
470
|
+
*/
|
|
471
|
+
public readExtendedAddressMapU256(be: boolean = true): ExtendedAddressMap<bigint> {
|
|
472
|
+
const length = this.readU16(be);
|
|
473
|
+
const result = new ExtendedAddressMap<bigint>();
|
|
474
|
+
|
|
475
|
+
for (let i = 0; i < length; i++) {
|
|
476
|
+
const address = this.readExtendedAddress();
|
|
477
|
+
const value = this.readU256(be);
|
|
478
|
+
|
|
479
|
+
if (result.has(address)) {
|
|
480
|
+
throw new Error('Duplicate tweaked address found in map');
|
|
481
|
+
}
|
|
482
|
+
result.set(address, value);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return result;
|
|
486
|
+
}
|
|
487
|
+
|
|
332
488
|
// --------------------------------------------------- //
|
|
333
489
|
|
|
334
490
|
public getOffset(): u16 {
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { AddressMap } from '../deterministic/AddressMap.js';
|
|
2
|
+
import { ExtendedAddressMap } from '../deterministic/ExtendedAddressMap.js';
|
|
2
3
|
import { Address } from '../keypair/Address.js';
|
|
3
4
|
import { BufferHelper } from '../utils/BufferHelper.js';
|
|
4
5
|
import {
|
|
5
6
|
ADDRESS_BYTE_LENGTH,
|
|
7
|
+
EXTENDED_ADDRESS_BYTE_LENGTH,
|
|
6
8
|
I128_BYTE_LENGTH,
|
|
9
|
+
I16_BYTE_LENGTH,
|
|
10
|
+
I32_BYTE_LENGTH,
|
|
11
|
+
I64_BYTE_LENGTH,
|
|
12
|
+
I8_BYTE_LENGTH,
|
|
13
|
+
SCHNORR_SIGNATURE_BYTE_LENGTH,
|
|
7
14
|
U128_BYTE_LENGTH,
|
|
8
15
|
U16_BYTE_LENGTH,
|
|
9
16
|
U256_BYTE_LENGTH,
|
|
@@ -11,7 +18,7 @@ import {
|
|
|
11
18
|
U64_BYTE_LENGTH,
|
|
12
19
|
U8_BYTE_LENGTH,
|
|
13
20
|
} from '../utils/lengths.js';
|
|
14
|
-
import { Selector, u16, u32, u64, u8 } from '../utils/types.js';
|
|
21
|
+
import { i16, i32, i64, i8, Selector, u16, u32, u64, u8 } from '../utils/types.js';
|
|
15
22
|
import { BinaryReader } from './BinaryReader.js';
|
|
16
23
|
|
|
17
24
|
export class BinaryWriter {
|
|
@@ -64,6 +71,56 @@ export class BinaryWriter {
|
|
|
64
71
|
this.currentOffset += 8;
|
|
65
72
|
}
|
|
66
73
|
|
|
74
|
+
// ------------------- Signed Integer Writers ------------------- //
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Writes a signed 8-bit integer.
|
|
78
|
+
*/
|
|
79
|
+
public writeI8(value: i8): void {
|
|
80
|
+
if (value < -128 || value > 127) throw new Error('i8 value is out of range.');
|
|
81
|
+
|
|
82
|
+
this.allocSafe(I8_BYTE_LENGTH);
|
|
83
|
+
this.buffer.setInt8(this.currentOffset, value);
|
|
84
|
+
this.currentOffset += I8_BYTE_LENGTH;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Writes a signed 16-bit integer. By default big-endian (be = true).
|
|
89
|
+
*/
|
|
90
|
+
public writeI16(value: i16, be: boolean = true): void {
|
|
91
|
+
if (value < -32768 || value > 32767) throw new Error('i16 value is out of range.');
|
|
92
|
+
|
|
93
|
+
this.allocSafe(I16_BYTE_LENGTH);
|
|
94
|
+
this.buffer.setInt16(this.currentOffset, value, !be);
|
|
95
|
+
this.currentOffset += I16_BYTE_LENGTH;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Writes a signed 32-bit integer. By default big-endian (be = true).
|
|
100
|
+
*/
|
|
101
|
+
public writeI32(value: i32, be: boolean = true): void {
|
|
102
|
+
if (value < -2147483648 || value > 2147483647) throw new Error('i32 value is out of range.');
|
|
103
|
+
|
|
104
|
+
this.allocSafe(I32_BYTE_LENGTH);
|
|
105
|
+
this.buffer.setInt32(this.currentOffset, value, !be);
|
|
106
|
+
this.currentOffset += I32_BYTE_LENGTH;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Writes a signed 64-bit integer. By default big-endian (be = true).
|
|
111
|
+
*/
|
|
112
|
+
public writeI64(value: i64, be: boolean = true): void {
|
|
113
|
+
if (value < -9223372036854775808n || value > 9223372036854775807n) {
|
|
114
|
+
throw new Error('i64 value is out of range.');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
this.allocSafe(I64_BYTE_LENGTH);
|
|
118
|
+
this.buffer.setBigInt64(this.currentOffset, value, !be);
|
|
119
|
+
this.currentOffset += I64_BYTE_LENGTH;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ---------------------------------------------------------------- //
|
|
123
|
+
|
|
67
124
|
public writeSelector(value: Selector): void {
|
|
68
125
|
this.writeU32(value, true);
|
|
69
126
|
}
|
|
@@ -173,12 +230,65 @@ export class BinaryWriter {
|
|
|
173
230
|
this.writeBytes(bytes);
|
|
174
231
|
}
|
|
175
232
|
|
|
233
|
+
/**
|
|
234
|
+
* Writes an address (32 bytes MLDSA key hash only).
|
|
235
|
+
*/
|
|
176
236
|
public writeAddress(value: Address): void {
|
|
177
237
|
this.verifyAddress(value);
|
|
178
238
|
|
|
179
239
|
this.writeBytes(value);
|
|
180
240
|
}
|
|
181
241
|
|
|
242
|
+
/**
|
|
243
|
+
* Writes the tweaked public key from an Address (32 bytes).
|
|
244
|
+
* @param value - The Address containing the tweaked public key
|
|
245
|
+
*/
|
|
246
|
+
public writeTweakedPublicKey(value: Address): void {
|
|
247
|
+
const tweakedKey = value.tweakedPublicKeyToBuffer();
|
|
248
|
+
this.allocSafe(ADDRESS_BYTE_LENGTH);
|
|
249
|
+
this.writeBytes(tweakedKey);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Writes a full address with both tweaked public key and MLDSA key hash (64 bytes total).
|
|
254
|
+
* Format: [32 bytes tweakedPublicKey][32 bytes MLDSA key hash]
|
|
255
|
+
*
|
|
256
|
+
* This is the equivalent of btc-runtime's writeExtendedAddress().
|
|
257
|
+
*
|
|
258
|
+
* @param value - The Address containing both keys
|
|
259
|
+
*/
|
|
260
|
+
public writeExtendedAddress(value: Address): void {
|
|
261
|
+
this.allocSafe(EXTENDED_ADDRESS_BYTE_LENGTH);
|
|
262
|
+
|
|
263
|
+
// Write tweaked public key first (32 bytes)
|
|
264
|
+
this.writeTweakedPublicKey(value);
|
|
265
|
+
|
|
266
|
+
// Write MLDSA key hash (32 bytes)
|
|
267
|
+
this.writeBytes(value);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Writes a Schnorr signature with its associated full Address.
|
|
272
|
+
* Format: [64 bytes full Address][64 bytes signature]
|
|
273
|
+
*
|
|
274
|
+
* Used for serializing signed data where both the signer's address
|
|
275
|
+
* and their Schnorr signature need to be stored together.
|
|
276
|
+
*
|
|
277
|
+
* @param address - The signer's Address (with both MLDSA and tweaked keys)
|
|
278
|
+
* @param signature - The 64-byte Schnorr signature
|
|
279
|
+
* @throws {Error} If signature is not exactly 64 bytes
|
|
280
|
+
*/
|
|
281
|
+
public writeSchnorrSignature(address: Address, signature: Uint8Array): void {
|
|
282
|
+
if (signature.length !== SCHNORR_SIGNATURE_BYTE_LENGTH) {
|
|
283
|
+
throw new Error(
|
|
284
|
+
`Invalid Schnorr signature length: expected ${SCHNORR_SIGNATURE_BYTE_LENGTH}, got ${signature.length}`,
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
this.allocSafe(EXTENDED_ADDRESS_BYTE_LENGTH + SCHNORR_SIGNATURE_BYTE_LENGTH);
|
|
288
|
+
this.writeExtendedAddress(address);
|
|
289
|
+
this.writeBytes(signature);
|
|
290
|
+
}
|
|
291
|
+
|
|
182
292
|
public getBuffer(clear: boolean = true): Uint8Array {
|
|
183
293
|
const buf = new Uint8Array(this.buffer.byteLength);
|
|
184
294
|
for (let i: u32 = 0; i < this.buffer.byteLength; i++) {
|
|
@@ -235,6 +345,23 @@ export class BinaryWriter {
|
|
|
235
345
|
}
|
|
236
346
|
}
|
|
237
347
|
|
|
348
|
+
/**
|
|
349
|
+
* Writes a map of full Address -> u256 using the tweaked key for map lookup.
|
|
350
|
+
* Format: [u16 length][FullAddress key][u256 value]...
|
|
351
|
+
*
|
|
352
|
+
* This is the equivalent of btc-runtime's writeExtendedAddressMapU256().
|
|
353
|
+
*/
|
|
354
|
+
public writeExtendedAddressMapU256(map: ExtendedAddressMap<bigint>, be: boolean = true): void {
|
|
355
|
+
if (map.size > 65535) throw new Error('Map size is too large');
|
|
356
|
+
|
|
357
|
+
this.writeU16(map.size, be);
|
|
358
|
+
|
|
359
|
+
for (const [key, value] of map.entries()) {
|
|
360
|
+
this.writeExtendedAddress(key);
|
|
361
|
+
this.writeU256(value, be);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
238
365
|
public writeBytesWithLength(value: Uint8Array): void {
|
|
239
366
|
this.writeU32(value.length);
|
|
240
367
|
this.writeBytes(value);
|
|
@@ -262,6 +389,21 @@ export class BinaryWriter {
|
|
|
262
389
|
}
|
|
263
390
|
}
|
|
264
391
|
|
|
392
|
+
/**
|
|
393
|
+
* Writes an array of full addresses (64 bytes each).
|
|
394
|
+
* Format: [u16 length][FullAddress 0][FullAddress 1]...
|
|
395
|
+
*/
|
|
396
|
+
public writeExtendedAddressArray(value: Address[]): void {
|
|
397
|
+
if (value.length > 65535) throw new Error('Array size is too large');
|
|
398
|
+
|
|
399
|
+
this.allocSafe(U16_BYTE_LENGTH + value.length * EXTENDED_ADDRESS_BYTE_LENGTH);
|
|
400
|
+
this.writeU16(value.length);
|
|
401
|
+
|
|
402
|
+
for (let i = 0; i < value.length; i++) {
|
|
403
|
+
this.writeExtendedAddress(value[i]);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
265
407
|
public writeU32Array(value: u32[], be: boolean = true): void {
|
|
266
408
|
if (value.length > 65535) throw new Error('Array size is too large');
|
|
267
409
|
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import { Address } from '../keypair/Address.js';
|
|
2
2
|
import { FastMap } from './FastMap.js';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* A map implementation using Address with both MLDSA and tweaked keys.
|
|
6
|
+
* Uses the tweaked public key for lookup/indexing, but stores the full Address.
|
|
7
|
+
*/
|
|
4
8
|
export class ExtendedAddressMap<V> {
|
|
5
|
-
|
|
9
|
+
// Store tweaked bigint -> index mapping for fast lookup
|
|
10
|
+
private indexMap: FastMap<bigint, number>;
|
|
11
|
+
// Store actual addresses and values
|
|
12
|
+
private _keys: Address[] = [];
|
|
13
|
+
private _values: V[] = [];
|
|
6
14
|
|
|
7
15
|
constructor(iterable?: ReadonlyArray<readonly [Address, V]> | null) {
|
|
8
|
-
this.
|
|
16
|
+
this.indexMap = new FastMap();
|
|
9
17
|
|
|
10
18
|
if (iterable) {
|
|
11
19
|
for (const [key, value] of iterable) {
|
|
@@ -15,54 +23,86 @@ export class ExtendedAddressMap<V> {
|
|
|
15
23
|
}
|
|
16
24
|
|
|
17
25
|
public get size(): number {
|
|
18
|
-
return this.
|
|
26
|
+
return this._keys.length;
|
|
19
27
|
}
|
|
20
28
|
|
|
21
29
|
public set(key: Address, value: V): this {
|
|
22
30
|
const keyBigInt = key.tweakedToBigInt();
|
|
23
|
-
this.
|
|
31
|
+
const existingIndex = this.indexMap.get(keyBigInt);
|
|
32
|
+
|
|
33
|
+
if (existingIndex !== undefined) {
|
|
34
|
+
// Update existing entry
|
|
35
|
+
this._values[existingIndex] = value;
|
|
36
|
+
} else {
|
|
37
|
+
// Add new entry
|
|
38
|
+
const newIndex = this._keys.length;
|
|
39
|
+
this._keys.push(key);
|
|
40
|
+
this._values.push(value);
|
|
41
|
+
this.indexMap.set(keyBigInt, newIndex);
|
|
42
|
+
}
|
|
24
43
|
|
|
25
44
|
return this;
|
|
26
45
|
}
|
|
27
46
|
|
|
28
47
|
public get(key: Address): V | undefined {
|
|
29
|
-
|
|
48
|
+
const keyBigInt = key.tweakedToBigInt();
|
|
49
|
+
const index = this.indexMap.get(keyBigInt);
|
|
50
|
+
if (index === undefined) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
return this._values[index];
|
|
30
54
|
}
|
|
31
55
|
|
|
32
56
|
public has(key: Address): boolean {
|
|
33
|
-
return this.
|
|
57
|
+
return this.indexMap.has(key.tweakedToBigInt());
|
|
34
58
|
}
|
|
35
59
|
|
|
36
60
|
public delete(key: Address): boolean {
|
|
37
61
|
const keyBigInt = key.tweakedToBigInt();
|
|
38
|
-
|
|
62
|
+
const index = this.indexMap.get(keyBigInt);
|
|
63
|
+
|
|
64
|
+
if (index === undefined) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Remove from arrays
|
|
69
|
+
this._keys.splice(index, 1);
|
|
70
|
+
this._values.splice(index, 1);
|
|
71
|
+
|
|
72
|
+
// Rebuild index map (indices shifted after splice)
|
|
73
|
+
this.indexMap.clear();
|
|
74
|
+
for (let i = 0; i < this._keys.length; i++) {
|
|
75
|
+
this.indexMap.set(this._keys[i].tweakedToBigInt(), i);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return true;
|
|
39
79
|
}
|
|
40
80
|
|
|
41
81
|
public clear(): void {
|
|
42
|
-
this.
|
|
82
|
+
this.indexMap.clear();
|
|
83
|
+
this._keys = [];
|
|
84
|
+
this._values = [];
|
|
43
85
|
}
|
|
44
86
|
|
|
45
87
|
public indexOf(address: Address): number {
|
|
46
|
-
|
|
88
|
+
const index = this.indexMap.get(address.tweakedToBigInt());
|
|
89
|
+
return index !== undefined ? index : -1;
|
|
47
90
|
}
|
|
48
91
|
|
|
49
|
-
/**
|
|
50
|
-
* WARNING, THIS RETURN NEW COPY OF THE KEYS
|
|
51
|
-
*/
|
|
52
92
|
*entries(): IterableIterator<[Address, V]> {
|
|
53
|
-
for (
|
|
54
|
-
yield [
|
|
93
|
+
for (let i = 0; i < this._keys.length; i++) {
|
|
94
|
+
yield [this._keys[i], this._values[i]];
|
|
55
95
|
}
|
|
56
96
|
}
|
|
57
97
|
|
|
58
98
|
*keys(): IterableIterator<Address> {
|
|
59
|
-
for (const
|
|
60
|
-
yield
|
|
99
|
+
for (const key of this._keys) {
|
|
100
|
+
yield key;
|
|
61
101
|
}
|
|
62
102
|
}
|
|
63
103
|
|
|
64
104
|
*values(): IterableIterator<V> {
|
|
65
|
-
for (const value of this.
|
|
105
|
+
for (const value of this._values) {
|
|
66
106
|
yield value;
|
|
67
107
|
}
|
|
68
108
|
}
|
|
@@ -71,9 +111,8 @@ export class ExtendedAddressMap<V> {
|
|
|
71
111
|
callback: (value: V, key: Address, map: ExtendedAddressMap<V>) => void,
|
|
72
112
|
thisArg?: unknown,
|
|
73
113
|
): void {
|
|
74
|
-
for (
|
|
75
|
-
|
|
76
|
-
callback.call(thisArg, value, key, this);
|
|
114
|
+
for (let i = 0; i < this._keys.length; i++) {
|
|
115
|
+
callback.call(thisArg, this._values[i], this._keys[i], this);
|
|
77
116
|
}
|
|
78
117
|
}
|
|
79
118
|
|