@btc-vision/transaction 1.6.1 → 1.6.5
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/epoch/ChallengeSolution.d.ts +3 -3
- package/browser/epoch/validator/EpochValidator.d.ts +5 -6
- package/browser/generators/builders/P2WDAGenerator.d.ts +13 -0
- package/browser/index.js +1 -1
- package/browser/keypair/Address.d.ts +3 -2
- package/browser/keypair/AddressVerificator.d.ts +13 -1
- package/browser/keypair/Wallet.d.ts +3 -0
- package/browser/opnet.d.ts +4 -0
- package/browser/p2wda/P2WDADetector.d.ts +16 -0
- package/browser/transaction/TransactionFactory.d.ts +3 -1
- package/browser/transaction/builders/DeploymentTransaction.d.ts +3 -3
- package/browser/transaction/builders/InteractionTransactionP2WDA.d.ts +37 -0
- package/browser/transaction/builders/SharedInteractionTransaction.d.ts +4 -4
- package/browser/transaction/builders/TransactionBuilder.d.ts +3 -0
- package/browser/transaction/interfaces/ITransactionParameters.d.ts +1 -0
- package/browser/transaction/mineable/IP2WSHAddress.d.ts +4 -0
- package/browser/transaction/mineable/TimelockGenerator.d.ts +2 -5
- package/browser/transaction/shared/TweakedTransaction.d.ts +23 -0
- package/build/_version.d.ts +1 -1
- package/build/_version.js +1 -1
- package/build/epoch/ChallengeSolution.d.ts +3 -3
- package/build/epoch/ChallengeSolution.js +3 -3
- package/build/epoch/validator/EpochValidator.d.ts +5 -6
- package/build/epoch/validator/EpochValidator.js +11 -12
- package/build/generators/builders/P2WDAGenerator.d.ts +13 -0
- package/build/generators/builders/P2WDAGenerator.js +62 -0
- package/build/keypair/Address.d.ts +3 -2
- package/build/keypair/Address.js +28 -2
- package/build/keypair/AddressVerificator.d.ts +13 -1
- package/build/keypair/AddressVerificator.js +82 -1
- package/build/keypair/Wallet.d.ts +3 -0
- package/build/keypair/Wallet.js +4 -0
- package/build/opnet.d.ts +4 -0
- package/build/opnet.js +4 -0
- package/build/p2wda/P2WDADetector.d.ts +16 -0
- package/build/p2wda/P2WDADetector.js +97 -0
- package/build/transaction/TransactionFactory.d.ts +3 -1
- package/build/transaction/TransactionFactory.js +35 -4
- package/build/transaction/builders/DeploymentTransaction.d.ts +3 -3
- package/build/transaction/builders/DeploymentTransaction.js +1 -1
- package/build/transaction/builders/InteractionTransactionP2WDA.d.ts +37 -0
- package/build/transaction/builders/InteractionTransactionP2WDA.js +205 -0
- package/build/transaction/builders/SharedInteractionTransaction.d.ts +4 -4
- package/build/transaction/builders/SharedInteractionTransaction.js +3 -3
- package/build/transaction/builders/TransactionBuilder.d.ts +3 -0
- package/build/transaction/builders/TransactionBuilder.js +18 -3
- package/build/transaction/interfaces/ITransactionParameters.d.ts +1 -0
- package/build/transaction/mineable/IP2WSHAddress.d.ts +4 -0
- package/build/transaction/mineable/IP2WSHAddress.js +1 -0
- package/build/transaction/mineable/TimelockGenerator.d.ts +2 -5
- package/build/transaction/shared/TweakedTransaction.d.ts +23 -0
- package/build/transaction/shared/TweakedTransaction.js +154 -18
- package/doc/README.md +0 -0
- package/doc/addresses/P2OP.md +1 -0
- package/doc/addresses/P2WDA.md +240 -0
- package/package.json +2 -2
- package/src/_version.ts +1 -1
- package/src/epoch/ChallengeSolution.ts +4 -4
- package/src/epoch/validator/EpochValidator.ts +12 -16
- package/src/generators/builders/P2WDAGenerator.ts +174 -0
- package/src/keypair/Address.ts +58 -3
- package/src/keypair/AddressVerificator.ts +147 -2
- package/src/keypair/Wallet.ts +16 -0
- package/src/opnet.ts +4 -0
- package/src/p2wda/P2WDADetector.ts +218 -0
- package/src/transaction/TransactionFactory.ts +79 -5
- package/src/transaction/builders/DeploymentTransaction.ts +4 -3
- package/src/transaction/builders/InteractionTransactionP2WDA.ts +376 -0
- package/src/transaction/builders/SharedInteractionTransaction.ts +7 -6
- package/src/transaction/builders/TransactionBuilder.ts +30 -3
- package/src/transaction/interfaces/ITransactionParameters.ts +1 -0
- package/src/transaction/mineable/IP2WSHAddress.ts +4 -0
- package/src/transaction/mineable/TimelockGenerator.ts +2 -6
- package/src/transaction/shared/TweakedTransaction.ts +246 -23
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { Network, networks } from '@btc-vision/bitcoin';
|
|
2
|
+
import { BinaryWriter } from '../../buffer/BinaryWriter.js';
|
|
3
|
+
import { Feature, Features } from '../Features.js';
|
|
4
|
+
import { Generator } from '../Generator.js';
|
|
5
|
+
import { ChallengeSolution } from '../../epoch/ChallengeSolution.js';
|
|
6
|
+
|
|
7
|
+
export class P2WDAGenerator extends Generator {
|
|
8
|
+
private static readonly P2WDA_VERSION = 0x01;
|
|
9
|
+
|
|
10
|
+
constructor(
|
|
11
|
+
senderPubKey: Buffer,
|
|
12
|
+
contractSaltPubKey: Buffer,
|
|
13
|
+
network: Network = networks.bitcoin,
|
|
14
|
+
) {
|
|
15
|
+
super(senderPubKey, contractSaltPubKey, network);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Validate that operation data will fit in P2WDA witness fields
|
|
20
|
+
*
|
|
21
|
+
* @param dataSize Size of the operation data
|
|
22
|
+
* @param maxWitnessFields Maximum number of witness fields (default 10)
|
|
23
|
+
* @param maxBytesPerWitness Maximum bytes per witness field (default 80)
|
|
24
|
+
* @returns true if data will fit, false otherwise
|
|
25
|
+
*/
|
|
26
|
+
public static validateWitnessSize(
|
|
27
|
+
dataSize: number,
|
|
28
|
+
maxWitnessFields: number = 10,
|
|
29
|
+
maxBytesPerWitness: number = 80,
|
|
30
|
+
): boolean {
|
|
31
|
+
// Account for Schnorr signature (64 bytes) and compression
|
|
32
|
+
// Assume 30% compression ratio (conservative estimate)
|
|
33
|
+
|
|
34
|
+
const signatureSize = 64;
|
|
35
|
+
const compressionRatio = 0.7;
|
|
36
|
+
|
|
37
|
+
const totalSize = dataSize + signatureSize;
|
|
38
|
+
const compressedSize = Math.ceil(totalSize * compressionRatio);
|
|
39
|
+
|
|
40
|
+
const requiredFields = Math.ceil(compressedSize / maxBytesPerWitness);
|
|
41
|
+
|
|
42
|
+
return requiredFields <= maxWitnessFields;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Compile operation data for P2WDA witness embedding
|
|
47
|
+
*
|
|
48
|
+
* This creates a binary structure containing all operation information
|
|
49
|
+
* without Bitcoin script opcodes. The structure is:
|
|
50
|
+
*
|
|
51
|
+
* [version(1)] [header(12)] [contract(32)] [challenge_pubkey(33)] [challenge_solution(32)]
|
|
52
|
+
* [calldata_length(4)] [calldata] [features_length(2)] [features_data]
|
|
53
|
+
*
|
|
54
|
+
* @param calldata The compressed calldata for the contract interaction
|
|
55
|
+
* @param contractSecret The 32-byte contract secret
|
|
56
|
+
* @param challenge The challenge solution for epoch rewards
|
|
57
|
+
* @param maxPriority Maximum priority fee in satoshis
|
|
58
|
+
* @param features Optional features like access lists
|
|
59
|
+
* @returns Raw operation data ready for signing and compression
|
|
60
|
+
*/
|
|
61
|
+
public compile(
|
|
62
|
+
calldata: Buffer,
|
|
63
|
+
contractSecret: Buffer,
|
|
64
|
+
challenge: ChallengeSolution,
|
|
65
|
+
maxPriority: bigint,
|
|
66
|
+
features: Feature<Features>[] = [],
|
|
67
|
+
): Buffer {
|
|
68
|
+
if (!this.contractSaltPubKey) {
|
|
69
|
+
throw new Error('Contract salt public key not set');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (contractSecret.length !== 32) {
|
|
73
|
+
throw new Error('Contract secret must be exactly 32 bytes');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const writer = new BinaryWriter();
|
|
77
|
+
|
|
78
|
+
// Version byte
|
|
79
|
+
writer.writeU8(P2WDAGenerator.P2WDA_VERSION);
|
|
80
|
+
|
|
81
|
+
// Header
|
|
82
|
+
writer.writeBytes(
|
|
83
|
+
this.getHeader(
|
|
84
|
+
maxPriority,
|
|
85
|
+
features.map((f) => f.opcode),
|
|
86
|
+
),
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
// Contract secret
|
|
90
|
+
writer.writeBytes(contractSecret);
|
|
91
|
+
|
|
92
|
+
// Challenge components for epoch rewards
|
|
93
|
+
writer.writeBytes(challenge.publicKey.originalPublicKeyBuffer());
|
|
94
|
+
writer.writeBytes(challenge.solution);
|
|
95
|
+
|
|
96
|
+
// Calldata with length prefix
|
|
97
|
+
writer.writeU32(calldata.length);
|
|
98
|
+
writer.writeBytes(calldata);
|
|
99
|
+
|
|
100
|
+
// Features
|
|
101
|
+
this.writeFeatures(writer, features);
|
|
102
|
+
|
|
103
|
+
return Buffer.from(writer.getBuffer());
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Create a minimal header for P2WDA operations
|
|
108
|
+
*
|
|
109
|
+
* The header contains essential transaction metadata in a compact format:
|
|
110
|
+
* [sender_pubkey_prefix(1)] [feature_flags(3)] [max_priority(8)]
|
|
111
|
+
*
|
|
112
|
+
* @param maxPriority Maximum priority fee
|
|
113
|
+
* @param features Feature opcodes to set in flags
|
|
114
|
+
* @returns 12-byte header
|
|
115
|
+
*/
|
|
116
|
+
public override getHeader(maxPriority: bigint, features: Features[] = []): Buffer {
|
|
117
|
+
return super.getHeader(maxPriority, features);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Write features section to the operation data
|
|
122
|
+
*
|
|
123
|
+
* Features are encoded as:
|
|
124
|
+
* [feature_count(2)] [feature1_opcode(1)] [feature1_length(4)] [feature1_data] ...
|
|
125
|
+
*
|
|
126
|
+
* @param writer Binary writer to write to
|
|
127
|
+
* @param features Array of features to encode
|
|
128
|
+
*/
|
|
129
|
+
private writeFeatures(writer: BinaryWriter, features: Feature<Features>[]): void {
|
|
130
|
+
// Write feature count
|
|
131
|
+
writer.writeU16(features.length);
|
|
132
|
+
|
|
133
|
+
for (const feature of features) {
|
|
134
|
+
// Write feature opcode
|
|
135
|
+
writer.writeU8(feature.opcode);
|
|
136
|
+
|
|
137
|
+
// Encode feature data
|
|
138
|
+
const encodedData = this.encodeFeatureData(feature);
|
|
139
|
+
|
|
140
|
+
// Write feature data with length prefix
|
|
141
|
+
writer.writeU32(encodedData.length);
|
|
142
|
+
writer.writeBytes(encodedData);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Encode a single feature's data
|
|
148
|
+
*
|
|
149
|
+
* Unlike the base Generator class, we don't split into chunks here
|
|
150
|
+
* since P2WDA handles chunking at the witness level
|
|
151
|
+
*
|
|
152
|
+
* @param feature The feature to encode
|
|
153
|
+
* @returns Encoded feature data
|
|
154
|
+
*/
|
|
155
|
+
private encodeFeatureData(feature: Feature<Features>): Buffer {
|
|
156
|
+
switch (feature.opcode) {
|
|
157
|
+
case Features.ACCESS_LIST: {
|
|
158
|
+
// Access lists are already encoded efficiently by the parent class
|
|
159
|
+
const chunks = this.encodeFeature(feature);
|
|
160
|
+
// Flatten chunks since P2WDA doesn't need script-level chunking
|
|
161
|
+
return Buffer.concat(chunks.flat());
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
case Features.EPOCH_SUBMISSION: {
|
|
165
|
+
// Epoch submissions are also handled by parent
|
|
166
|
+
const chunks = this.encodeFeature(feature);
|
|
167
|
+
return Buffer.concat(chunks.flat());
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
default:
|
|
171
|
+
throw new Error(`Unknown feature type: ${feature.opcode}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
package/src/keypair/Address.ts
CHANGED
|
@@ -5,7 +5,9 @@ import { AddressVerificator } from './AddressVerificator.js';
|
|
|
5
5
|
import { EcKeyPair } from './EcKeyPair.js';
|
|
6
6
|
import { ContractAddress } from '../transaction/ContractAddress.js';
|
|
7
7
|
import { BitcoinUtils } from '../utils/BitcoinUtils.js';
|
|
8
|
-
import {
|
|
8
|
+
import { TimeLockGenerator } from '../transaction/mineable/TimelockGenerator.js';
|
|
9
|
+
import { IP2WSHAddress } from '../transaction/mineable/IP2WSHAddress.js';
|
|
10
|
+
import { P2WDADetector } from '../p2wda/P2WDADetector.js';
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* Objects of type "Address" are the representation of tweaked public keys. They can be converted to different address formats.
|
|
@@ -19,6 +21,7 @@ export class Address extends Uint8Array {
|
|
|
19
21
|
#keyPair: ECPairInterface | undefined;
|
|
20
22
|
#uncompressed: UncompressedPublicKey | undefined;
|
|
21
23
|
#tweakedUncompressed: Buffer | undefined;
|
|
24
|
+
#p2wda: IP2WSHAddress | undefined;
|
|
22
25
|
|
|
23
26
|
public constructor(bytes?: ArrayLike<number>) {
|
|
24
27
|
super(ADDRESS_BYTE_LENGTH);
|
|
@@ -318,6 +321,58 @@ export class Address extends Uint8Array {
|
|
|
318
321
|
throw new Error('Public key not set');
|
|
319
322
|
}
|
|
320
323
|
|
|
324
|
+
/**
|
|
325
|
+
* Generate a P2WDA (Pay-to-Witness-Data-Authentication) address
|
|
326
|
+
*
|
|
327
|
+
* P2WDA addresses are a special type of P2WSH address that allows embedding
|
|
328
|
+
* authenticated data directly in the witness field, achieving 75% cost reduction
|
|
329
|
+
* through Bitcoin's witness discount.
|
|
330
|
+
*
|
|
331
|
+
* The witness script pattern is: (OP_2DROP * 5) <pubkey> OP_CHECKSIG
|
|
332
|
+
* This allows up to 10 witness data fields (5 * 2 = 10), where each field
|
|
333
|
+
* can hold up to 80 bytes of data due to relay rules.
|
|
334
|
+
*
|
|
335
|
+
* @param {Network} network - The Bitcoin network to use
|
|
336
|
+
* @returns {IP2WSHAddress} The P2WDA address
|
|
337
|
+
* @throws {Error} If the public key is not set or address generation fails
|
|
338
|
+
*
|
|
339
|
+
* @example
|
|
340
|
+
* ```typescript
|
|
341
|
+
* const address = Address.fromString('0x02...');
|
|
342
|
+
* const p2wdaAddress = address.p2wda(networks.bitcoin);
|
|
343
|
+
* console.log(p2wdaAddress); // bc1q...
|
|
344
|
+
* ```
|
|
345
|
+
*/
|
|
346
|
+
public p2wda(network: Network): IP2WSHAddress {
|
|
347
|
+
if (this.#p2wda && this.#network === network) {
|
|
348
|
+
return this.#p2wda;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (!this.#originalPublicKey) {
|
|
352
|
+
throw new Error('Cannot create P2WDA address: public key not set');
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const publicKeyBuffer = Buffer.from(this.#originalPublicKey);
|
|
356
|
+
|
|
357
|
+
if (publicKeyBuffer.length !== 33) {
|
|
358
|
+
throw new Error('P2WDA requires a compressed public key (33 bytes)');
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
try {
|
|
362
|
+
const p2wdaInfo = P2WDADetector.generateP2WDAAddress(publicKeyBuffer, network);
|
|
363
|
+
|
|
364
|
+
this.#network = network;
|
|
365
|
+
this.#p2wda = p2wdaInfo;
|
|
366
|
+
|
|
367
|
+
return {
|
|
368
|
+
address: p2wdaInfo.address,
|
|
369
|
+
witnessScript: p2wdaInfo.witnessScript,
|
|
370
|
+
};
|
|
371
|
+
} catch (error) {
|
|
372
|
+
throw new Error(`Failed to generate P2WDA address: ${(error as Error).message}`);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
321
376
|
/**
|
|
322
377
|
* Generate a P2WSH address with CSV (CheckSequenceVerify) timelock
|
|
323
378
|
* The resulting address can only be spent after the specified number of blocks
|
|
@@ -325,10 +380,10 @@ export class Address extends Uint8Array {
|
|
|
325
380
|
*
|
|
326
381
|
* @param {bigint | number | string} duration - The number of blocks that must pass before spending (1-65535)
|
|
327
382
|
* @param {Network} network - The Bitcoin network to use
|
|
328
|
-
* @returns {
|
|
383
|
+
* @returns {IP2WSHAddress} The timelocked address and its witness script
|
|
329
384
|
* @throws {Error} If the block number is out of range or public key is not available
|
|
330
385
|
*/
|
|
331
|
-
public toCSV(duration: bigint | number | string, network: Network):
|
|
386
|
+
public toCSV(duration: bigint | number | string, network: Network): IP2WSHAddress {
|
|
332
387
|
const n = Number(duration);
|
|
333
388
|
|
|
334
389
|
// First, let's validate the block number to ensure it's within the valid range
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { address, initEccLib, Network } from '@btc-vision/bitcoin';
|
|
1
|
+
import { address, initEccLib, Network, payments } from '@btc-vision/bitcoin';
|
|
2
2
|
import * as ecc from '@bitcoinerlab/secp256k1';
|
|
3
3
|
import { EcKeyPair } from './EcKeyPair.js';
|
|
4
4
|
import { BitcoinUtils } from '../utils/BitcoinUtils.js';
|
|
5
|
+
import { P2WDADetector } from '../p2wda/P2WDADetector.js';
|
|
5
6
|
|
|
6
7
|
initEccLib(ecc);
|
|
7
8
|
|
|
@@ -12,6 +13,16 @@ export enum AddressTypes {
|
|
|
12
13
|
P2PK = 'P2PK',
|
|
13
14
|
P2TR = 'P2TR',
|
|
14
15
|
P2WPKH = 'P2WPKH',
|
|
16
|
+
P2WSH = 'P2WSH',
|
|
17
|
+
P2WDA = 'P2WDA',
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ValidatedP2WDAAddress {
|
|
21
|
+
readonly isValid: boolean;
|
|
22
|
+
readonly isPotentiallyP2WDA: boolean;
|
|
23
|
+
readonly isDefinitelyP2WDA: boolean;
|
|
24
|
+
readonly publicKey?: Buffer;
|
|
25
|
+
readonly error?: string;
|
|
15
26
|
}
|
|
16
27
|
|
|
17
28
|
export class AddressVerificator {
|
|
@@ -62,6 +73,22 @@ export class AddressVerificator {
|
|
|
62
73
|
return isValidSegWitAddress;
|
|
63
74
|
}
|
|
64
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Check if a given witness script is a P2WDA witness script
|
|
78
|
+
*
|
|
79
|
+
* P2WDA witness scripts have a specific pattern:
|
|
80
|
+
* (OP_2DROP * 5) <pubkey> OP_CHECKSIG
|
|
81
|
+
*
|
|
82
|
+
* This pattern allows for 10 witness data fields (5 * 2 = 10),
|
|
83
|
+
* which can be used to embed authenticated operation data.
|
|
84
|
+
*
|
|
85
|
+
* @param witnessScript The witness script to check
|
|
86
|
+
* @returns true if this is a valid P2WDA witness script
|
|
87
|
+
*/
|
|
88
|
+
public static isP2WDAWitnessScript(witnessScript: Buffer): boolean {
|
|
89
|
+
return P2WDADetector.isP2WDAWitnessScript(witnessScript);
|
|
90
|
+
}
|
|
91
|
+
|
|
65
92
|
/**
|
|
66
93
|
* Checks if the given address is a valid P2PKH or P2SH address.
|
|
67
94
|
* @param addy - The address to check.
|
|
@@ -193,11 +220,16 @@ export class AddressVerificator {
|
|
|
193
220
|
}
|
|
194
221
|
|
|
195
222
|
if (decodedBech32.prefix === network.bech32) {
|
|
196
|
-
// P2WPKH: SegWit address (
|
|
223
|
+
// P2WPKH: SegWit address (20 bytes)
|
|
197
224
|
if (decodedBech32.version === 0 && decodedBech32.data.length === 20) {
|
|
198
225
|
return AddressTypes.P2WPKH;
|
|
199
226
|
}
|
|
200
227
|
|
|
228
|
+
// P2WSH: SegWit script hash (32 bytes)
|
|
229
|
+
if (decodedBech32.version === 0 && decodedBech32.data.length === 32) {
|
|
230
|
+
return AddressTypes.P2WSH;
|
|
231
|
+
}
|
|
232
|
+
|
|
201
233
|
// P2TR: Taproot address (starting with 'bc1p' for mainnet, 'tb1p' for testnet)
|
|
202
234
|
if (decodedBech32.version === 1 && decodedBech32.data.length === 32) {
|
|
203
235
|
return AddressTypes.P2TR;
|
|
@@ -207,4 +239,117 @@ export class AddressVerificator {
|
|
|
207
239
|
|
|
208
240
|
return null; // Not a valid or recognized Bitcoin address type
|
|
209
241
|
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Enhanced detectAddressType that provides hints about P2WDA
|
|
245
|
+
*
|
|
246
|
+
* Note: P2WDA addresses cannot be distinguished from regular P2WSH
|
|
247
|
+
* addresses without the witness script. When a P2WSH address is detected,
|
|
248
|
+
* it could potentially be P2WDA if it has the correct witness script.
|
|
249
|
+
*
|
|
250
|
+
* @param addy The address to analyze
|
|
251
|
+
* @param network The Bitcoin network
|
|
252
|
+
* @param witnessScript Optional witness script for P2WSH addresses
|
|
253
|
+
* @returns The address type, with P2WDA detection if witness script provided
|
|
254
|
+
*/
|
|
255
|
+
public static detectAddressTypeWithWitnessScript(
|
|
256
|
+
addy: string,
|
|
257
|
+
network: Network,
|
|
258
|
+
witnessScript?: Buffer,
|
|
259
|
+
): AddressTypes | null {
|
|
260
|
+
const baseType = AddressVerificator.detectAddressType(addy, network);
|
|
261
|
+
|
|
262
|
+
if (baseType === AddressTypes.P2WSH && witnessScript) {
|
|
263
|
+
if (AddressVerificator.isP2WDAWitnessScript(witnessScript)) {
|
|
264
|
+
return AddressTypes.P2WDA;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return baseType;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Validate a P2WDA address and extract information
|
|
273
|
+
*
|
|
274
|
+
* This method validates that an address is a properly formatted P2WSH
|
|
275
|
+
* address and, if a witness script is provided, verifies it matches
|
|
276
|
+
* the P2WDA pattern and corresponds to the address.
|
|
277
|
+
*
|
|
278
|
+
* @param address The address to validate
|
|
279
|
+
* @param network The Bitcoin network
|
|
280
|
+
* @param witnessScript Optional witness script to verify
|
|
281
|
+
* @returns Validation result with extracted information
|
|
282
|
+
*/
|
|
283
|
+
public static validateP2WDAAddress(
|
|
284
|
+
address: string,
|
|
285
|
+
network: Network,
|
|
286
|
+
witnessScript?: Buffer,
|
|
287
|
+
): ValidatedP2WDAAddress {
|
|
288
|
+
try {
|
|
289
|
+
const addressType = AddressVerificator.detectAddressType(address, network);
|
|
290
|
+
if (addressType !== AddressTypes.P2WSH) {
|
|
291
|
+
return {
|
|
292
|
+
isValid: false,
|
|
293
|
+
isPotentiallyP2WDA: false,
|
|
294
|
+
isDefinitelyP2WDA: false,
|
|
295
|
+
error: 'Not a P2WSH address',
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (!witnessScript) {
|
|
300
|
+
return {
|
|
301
|
+
isValid: true,
|
|
302
|
+
isPotentiallyP2WDA: true,
|
|
303
|
+
isDefinitelyP2WDA: false,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (!AddressVerificator.isP2WDAWitnessScript(witnessScript)) {
|
|
308
|
+
return {
|
|
309
|
+
isValid: true,
|
|
310
|
+
isPotentiallyP2WDA: true,
|
|
311
|
+
isDefinitelyP2WDA: false,
|
|
312
|
+
error: 'Witness script does not match P2WDA pattern',
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const p2wsh = payments.p2wsh({
|
|
317
|
+
redeem: { output: witnessScript },
|
|
318
|
+
network,
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
if (p2wsh.address !== address) {
|
|
322
|
+
return {
|
|
323
|
+
isValid: false,
|
|
324
|
+
isPotentiallyP2WDA: false,
|
|
325
|
+
isDefinitelyP2WDA: false,
|
|
326
|
+
error: 'Witness script does not match address',
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const publicKey = P2WDADetector.extractPublicKeyFromP2WDA(witnessScript);
|
|
331
|
+
if (!publicKey) {
|
|
332
|
+
return {
|
|
333
|
+
isValid: false,
|
|
334
|
+
isPotentiallyP2WDA: false,
|
|
335
|
+
isDefinitelyP2WDA: false,
|
|
336
|
+
error: 'Failed to extract public key from witness script',
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return {
|
|
341
|
+
isValid: true,
|
|
342
|
+
isPotentiallyP2WDA: true,
|
|
343
|
+
isDefinitelyP2WDA: true,
|
|
344
|
+
publicKey,
|
|
345
|
+
};
|
|
346
|
+
} catch (error) {
|
|
347
|
+
return {
|
|
348
|
+
isValid: false,
|
|
349
|
+
isPotentiallyP2WDA: false,
|
|
350
|
+
isDefinitelyP2WDA: false,
|
|
351
|
+
error: (error as Error).message,
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
}
|
|
210
355
|
}
|
package/src/keypair/Wallet.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { EcKeyPair } from './EcKeyPair.js';
|
|
|
3
3
|
import { Network, networks, toXOnly } from '@btc-vision/bitcoin';
|
|
4
4
|
import { Address } from './Address.js';
|
|
5
5
|
import { BitcoinUtils } from '../utils/BitcoinUtils.js';
|
|
6
|
+
import { IP2WSHAddress } from '../transaction/mineable/IP2WSHAddress.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Wallet class
|
|
@@ -26,6 +27,12 @@ export class Wallet {
|
|
|
26
27
|
*/
|
|
27
28
|
private readonly _p2tr: string;
|
|
28
29
|
|
|
30
|
+
/**
|
|
31
|
+
* P2WDA Pay-to-Witness-Data-Authentication
|
|
32
|
+
* @private
|
|
33
|
+
*/
|
|
34
|
+
private readonly _p2wda: IP2WSHAddress;
|
|
35
|
+
|
|
29
36
|
/**
|
|
30
37
|
* Legacy address for the wallet
|
|
31
38
|
* @private
|
|
@@ -80,6 +87,7 @@ export class Wallet {
|
|
|
80
87
|
this._p2wpkh = this._address.p2wpkh(this.network);
|
|
81
88
|
this._legacy = this._address.p2pkh(this.network);
|
|
82
89
|
this._segwitLegacy = this._address.p2wpkh(this.network);
|
|
90
|
+
this._p2wda = this._address.p2wda(this.network);
|
|
83
91
|
|
|
84
92
|
this._tweakedKey = this._address.toBuffer();
|
|
85
93
|
}
|
|
@@ -126,6 +134,14 @@ export class Wallet {
|
|
|
126
134
|
return this._p2tr;
|
|
127
135
|
}
|
|
128
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Get the P2WDA address for the wallet
|
|
139
|
+
* @returns {string}
|
|
140
|
+
*/
|
|
141
|
+
public get p2wda(): IP2WSHAddress {
|
|
142
|
+
return this._p2wda;
|
|
143
|
+
}
|
|
144
|
+
|
|
129
145
|
/**
|
|
130
146
|
* Get the legacy address for the wallet
|
|
131
147
|
* @returns {string}
|
package/src/opnet.ts
CHANGED
|
@@ -11,10 +11,13 @@ export * from './generators/builders/CustomGenerator.js';
|
|
|
11
11
|
export * from './generators/builders/DeploymentGenerator.js';
|
|
12
12
|
export * from './generators/builders/LegacyCalldataGenerator.js';
|
|
13
13
|
export * from './generators/builders/MultiSignGenerator.js';
|
|
14
|
+
export * from './generators/builders/P2WDAGenerator.js';
|
|
14
15
|
export * from './generators/Features.js';
|
|
15
16
|
export * from './generators/Generator.js';
|
|
16
17
|
|
|
17
18
|
export * from './transaction/mineable/TimelockGenerator.js';
|
|
19
|
+
export * from './transaction/mineable/IP2WSHAddress.js';
|
|
20
|
+
export * from './p2wda/P2WDADetector.js';
|
|
18
21
|
|
|
19
22
|
/** Address */
|
|
20
23
|
export * from './generators/AddressGenerator.js';
|
|
@@ -45,6 +48,7 @@ export * from './transaction/builders/CustomScriptTransaction.js';
|
|
|
45
48
|
export * from './transaction/builders/DeploymentTransaction.js';
|
|
46
49
|
export * from './transaction/builders/FundingTransaction.js';
|
|
47
50
|
export * from './transaction/builders/InteractionTransaction.js';
|
|
51
|
+
export * from './transaction/builders/InteractionTransactionP2WDA.js';
|
|
48
52
|
export * from './transaction/builders/MultiSignTransaction.js';
|
|
49
53
|
export * from './transaction/builders/SharedInteractionTransaction.js';
|
|
50
54
|
export * from './transaction/builders/TransactionBuilder.js';
|