@btc-vision/transaction 1.0.112 → 1.0.113
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/index.js +1 -1
- package/browser/keypair/AddressVerificator.d.ts +15 -3
- package/browser/keypair/EcKeyPair.d.ts +7 -4
- package/browser/keypair/Wallet.d.ts +1 -0
- package/browser/signer/TweakedSigner.d.ts +2 -2
- package/browser/transaction/builders/FundingTransaction.d.ts +2 -1
- package/browser/transaction/builders/MultiSignTransaction.d.ts +2 -1
- package/browser/transaction/builders/SharedInteractionTransaction.d.ts +1 -1
- package/browser/transaction/builders/TransactionBuilder.d.ts +2 -2
- package/browser/transaction/shared/TweakedTransaction.d.ts +8 -7
- package/build/_version.d.ts +1 -1
- package/build/_version.js +1 -1
- package/build/keypair/AddressVerificator.d.ts +15 -3
- package/build/keypair/AddressVerificator.js +74 -3
- package/build/keypair/EcKeyPair.d.ts +7 -4
- package/build/keypair/EcKeyPair.js +54 -9
- package/build/keypair/Wallet.d.ts +1 -0
- package/build/keypair/Wallet.js +5 -4
- package/build/signer/TweakedSigner.d.ts +2 -2
- package/build/signer/TweakedSigner.js +1 -1
- package/build/transaction/builders/CustomScriptTransaction.js +3 -3
- package/build/transaction/builders/DeploymentTransaction.js +4 -4
- package/build/transaction/builders/FundingTransaction.d.ts +2 -1
- package/build/transaction/builders/MultiSignTransaction.d.ts +2 -1
- package/build/transaction/builders/SharedInteractionTransaction.d.ts +1 -1
- package/build/transaction/builders/SharedInteractionTransaction.js +4 -4
- package/build/transaction/builders/TransactionBuilder.d.ts +2 -2
- package/build/transaction/builders/UnwrapTransaction.js +2 -2
- package/build/transaction/shared/TweakedTransaction.d.ts +8 -7
- package/build/transaction/shared/TweakedTransaction.js +1 -1
- package/package.json +2 -1
- package/src/_version.ts +1 -1
- package/src/keypair/AddressVerificator.ts +144 -5
- package/src/keypair/EcKeyPair.ts +111 -14
- package/src/keypair/Wallet.ts +11 -3
- package/src/signer/TweakedSigner.ts +3 -2
- package/src/transaction/builders/CustomScriptTransaction.ts +6 -5
- package/src/transaction/builders/DeploymentTransaction.ts +6 -5
- package/src/transaction/builders/FundingTransaction.ts +2 -1
- package/src/transaction/builders/MultiSignTransaction.ts +6 -2
- package/src/transaction/builders/SharedInteractionTransaction.ts +5 -5
- package/src/transaction/builders/TransactionBuilder.ts +18 -8
- package/src/transaction/builders/UnwrapSegwitTransaction.ts +7 -3
- package/src/transaction/builders/UnwrapTransaction.ts +2 -2
- package/src/transaction/builders/WrapTransaction.ts +1 -6
- package/src/transaction/shared/TweakedTransaction.ts +12 -12
|
@@ -32,7 +32,7 @@ export class SharedInteractionTransaction extends TransactionBuilder {
|
|
|
32
32
|
this.calldata = Compressor.compress(parameters.calldata);
|
|
33
33
|
this.randomBytes = parameters.randomBytes || BitcoinUtils.rndBytes();
|
|
34
34
|
this.scriptSigner = this.generateKeyPairFromSeed();
|
|
35
|
-
this.calldataGenerator = new CalldataGenerator(this.signer.publicKey, this.scriptSignerXOnlyPubKey(), this.network);
|
|
35
|
+
this.calldataGenerator = new CalldataGenerator(Buffer.from(this.signer.publicKey), this.scriptSignerXOnlyPubKey(), this.network);
|
|
36
36
|
}
|
|
37
37
|
getContractSecret() {
|
|
38
38
|
return this.contractSecret;
|
|
@@ -46,7 +46,7 @@ export class SharedInteractionTransaction extends TransactionBuilder {
|
|
|
46
46
|
return address.fromBech32(this.to).data;
|
|
47
47
|
}
|
|
48
48
|
scriptSignerXOnlyPubKey() {
|
|
49
|
-
return toXOnly(this.scriptSigner.publicKey);
|
|
49
|
+
return toXOnly(Buffer.from(this.scriptSigner.publicKey));
|
|
50
50
|
}
|
|
51
51
|
generateKeyPairFromSeed() {
|
|
52
52
|
return EcKeyPair.fromSeedKeyPair(this.randomBytes, this.network);
|
|
@@ -188,9 +188,9 @@ export class SharedInteractionTransaction extends TransactionBuilder {
|
|
|
188
188
|
throw new Error('Failed to sign input');
|
|
189
189
|
}
|
|
190
190
|
getPubKeys() {
|
|
191
|
-
const pubkeys = [this.signer.publicKey];
|
|
191
|
+
const pubkeys = [Buffer.from(this.signer.publicKey)];
|
|
192
192
|
if (this.scriptSigner) {
|
|
193
|
-
pubkeys.push(this.scriptSigner.publicKey);
|
|
193
|
+
pubkeys.push(Buffer.from(this.scriptSigner.publicKey));
|
|
194
194
|
}
|
|
195
195
|
return pubkeys;
|
|
196
196
|
}
|
|
@@ -20,7 +20,7 @@ export declare abstract class TransactionBuilder<T extends TransactionType> exte
|
|
|
20
20
|
protected readonly outputs: PsbtOutputExtended[];
|
|
21
21
|
protected feeOutput: PsbtOutputExtended | null;
|
|
22
22
|
protected totalInputAmount: bigint;
|
|
23
|
-
protected readonly signer: Signer;
|
|
23
|
+
protected readonly signer: Signer | ECPairInterface;
|
|
24
24
|
protected readonly network: Network;
|
|
25
25
|
protected readonly feeRate: number;
|
|
26
26
|
protected priorityFee: bigint;
|
|
@@ -30,7 +30,7 @@ export declare abstract class TransactionBuilder<T extends TransactionType> exte
|
|
|
30
30
|
protected _maximumFeeRate: number;
|
|
31
31
|
protected isPubKeyDestination: boolean;
|
|
32
32
|
protected constructor(parameters: ITransactionParameters);
|
|
33
|
-
static getFrom(from: string | undefined, keypair: ECPairInterface, network: Network): Address;
|
|
33
|
+
static getFrom(from: string | undefined, keypair: ECPairInterface | Signer, network: Network): Address;
|
|
34
34
|
static witnessStackToScriptWitness(witness: Buffer[]): Buffer;
|
|
35
35
|
getFundingTransactionParameters(): Promise<IFundingTransactionParameters>;
|
|
36
36
|
setDestinationAddress(address: Address): void;
|
|
@@ -33,7 +33,7 @@ export class UnwrapTransaction extends SharedInteractionTransaction {
|
|
|
33
33
|
this.estimatedFeeLoss = UnwrapTransaction.preEstimateTaprootTransactionFees(BigInt(this.feeRate), this.calculateNumInputs(this.vaultUTXOs), 2n, this.calculateNumSignatures(this.vaultUTXOs), 65n, this.calculateNumEmptyWitnesses(this.vaultUTXOs));
|
|
34
34
|
this.amount = parameters.amount;
|
|
35
35
|
this.contractSecret = this.generateSecret();
|
|
36
|
-
this.calldataGenerator = new CalldataGenerator(this.signer.publicKey, this.scriptSignerXOnlyPubKey(), this.network);
|
|
36
|
+
this.calldataGenerator = new CalldataGenerator(Buffer.from(this.signer.publicKey), this.scriptSignerXOnlyPubKey(), this.network);
|
|
37
37
|
this.compiledTargetScript = this.calldataGenerator.compile(this.calldata, this.contractSecret, [Features.UNWRAP]);
|
|
38
38
|
this.scriptTree = this.getScriptTree();
|
|
39
39
|
this.internalInit();
|
|
@@ -173,7 +173,7 @@ export class UnwrapTransaction extends SharedInteractionTransaction {
|
|
|
173
173
|
}
|
|
174
174
|
return [
|
|
175
175
|
this.contractSecret,
|
|
176
|
-
toXOnly(this.signer.publicKey),
|
|
176
|
+
toXOnly(Buffer.from(this.signer.publicKey)),
|
|
177
177
|
input.tapScriptSig[0].signature,
|
|
178
178
|
input.tapScriptSig[1].signature,
|
|
179
179
|
];
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Logger } from '@btc-vision/logger';
|
|
2
2
|
import { Network, Payment, Psbt, Signer, Transaction } from 'bitcoinjs-lib';
|
|
3
|
+
import { ECPairInterface } from 'ecpair';
|
|
3
4
|
import { PsbtInput } from 'bip174/src/lib/interfaces.js';
|
|
4
5
|
import { UTXO } from '../../utxo/interfaces/IUTXO.js';
|
|
5
6
|
import { PsbtInputExtended, TapLeafScript } from '../interfaces/Tap.js';
|
|
6
7
|
import { ChainId } from '../../network/ChainId.js';
|
|
7
8
|
export interface ITweakedTransactionData {
|
|
8
|
-
readonly signer: Signer;
|
|
9
|
+
readonly signer: Signer | ECPairInterface;
|
|
9
10
|
readonly network: Network;
|
|
10
11
|
readonly chainId?: ChainId;
|
|
11
12
|
readonly nonWitnessUtxo?: Buffer;
|
|
@@ -17,8 +18,8 @@ export declare enum TransactionSequence {
|
|
|
17
18
|
export declare abstract class TweakedTransaction extends Logger {
|
|
18
19
|
readonly logColor: string;
|
|
19
20
|
finalized: boolean;
|
|
20
|
-
protected signer: Signer;
|
|
21
|
-
protected tweakedSigner?:
|
|
21
|
+
protected signer: Signer | ECPairInterface;
|
|
22
|
+
protected tweakedSigner?: ECPairInterface;
|
|
22
23
|
protected network: Network;
|
|
23
24
|
protected signed: boolean;
|
|
24
25
|
protected abstract readonly transaction: Psbt;
|
|
@@ -35,7 +36,7 @@ export declare abstract class TweakedTransaction extends Logger {
|
|
|
35
36
|
protected constructor(data: ITweakedTransactionData);
|
|
36
37
|
static readScriptWitnessToWitnessStack(Buffer: Buffer): Buffer[];
|
|
37
38
|
static preEstimateTaprootTransactionFees(feeRate: bigint, numInputs: bigint, numOutputs: bigint, numWitnessElements: bigint, witnessElementSize: bigint, emptyWitness: bigint, taprootControlWitnessSize?: bigint, taprootScriptSize?: bigint): bigint;
|
|
38
|
-
protected static signInput(transaction: Psbt, input: PsbtInput, i: number, signer: Signer, sighashTypes: number[]): void;
|
|
39
|
+
protected static signInput(transaction: Psbt, input: PsbtInput, i: number, signer: Signer | ECPairInterface, sighashTypes: number[]): void;
|
|
39
40
|
protected static calculateSignHash(sighashTypes: number[]): number;
|
|
40
41
|
ignoreSignatureError(): void;
|
|
41
42
|
getScriptAddress(): string;
|
|
@@ -46,13 +47,13 @@ export declare abstract class TweakedTransaction extends Logger {
|
|
|
46
47
|
preEstimateTransactionFees(feeRate: bigint, numInputs: bigint, numOutputs: bigint, numSignatures: bigint, numPubkeys: bigint): bigint;
|
|
47
48
|
protected generateTapData(): Payment;
|
|
48
49
|
protected generateScriptAddress(): Payment;
|
|
49
|
-
protected getSignerKey(): Signer;
|
|
50
|
-
protected signInput(transaction: Psbt, input: PsbtInput, i: number, signer?: Signer): Promise<void>;
|
|
50
|
+
protected getSignerKey(): Signer | ECPairInterface;
|
|
51
|
+
protected signInput(transaction: Psbt, input: PsbtInput, i: number, signer?: Signer | ECPairInterface): Promise<void>;
|
|
51
52
|
protected splitArray<T>(arr: T[], chunkSize: number): T[][];
|
|
52
53
|
protected signInputs(transaction: Psbt): Promise<void>;
|
|
53
54
|
protected internalPubKeyToXOnly(): Buffer;
|
|
54
55
|
protected internalInit(): void;
|
|
55
56
|
protected tweakSigner(): void;
|
|
56
|
-
protected getTweakedSigner(useTweakedHash?: boolean, signer?: Signer):
|
|
57
|
+
protected getTweakedSigner(useTweakedHash?: boolean, signer?: Signer | ECPairInterface): ECPairInterface | undefined;
|
|
57
58
|
protected generatePsbtInputExtended(utxo: UTXO, i: number): PsbtInputExtended;
|
|
58
59
|
}
|
|
@@ -232,7 +232,7 @@ export class TweakedTransaction extends Logger {
|
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
234
|
internalPubKeyToXOnly() {
|
|
235
|
-
return toXOnly(this.signer.publicKey);
|
|
235
|
+
return toXOnly(Buffer.from(this.signer.publicKey));
|
|
236
236
|
}
|
|
237
237
|
internalInit() {
|
|
238
238
|
this.scriptData = payments.p2tr(this.generateScriptAddress());
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@btc-vision/transaction",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.113",
|
|
5
5
|
"author": "BlobMaster41",
|
|
6
6
|
"description": "OPNet transaction library allows you to create and sign transactions for the OPNet network.",
|
|
7
7
|
"engines": {
|
|
@@ -95,6 +95,7 @@
|
|
|
95
95
|
"@btc-vision/bsi-bitcoin-rpc": "^1.0.29",
|
|
96
96
|
"@btc-vision/logger": "^1.0.6",
|
|
97
97
|
"@eslint/js": "^9.10.0",
|
|
98
|
+
"@noble/secp256k1": "^1.7.1",
|
|
98
99
|
"assert": "^2.1.0",
|
|
99
100
|
"babel-loader": "^9.1.3",
|
|
100
101
|
"babel-plugin-transform-import-meta": "^2.2.1",
|
package/src/_version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.0.
|
|
1
|
+
export const version = '1.0.113';
|
|
@@ -1,11 +1,29 @@
|
|
|
1
|
-
import { address, initEccLib,
|
|
1
|
+
import { address, initEccLib, Network } from 'bitcoinjs-lib';
|
|
2
2
|
import * as ecc from '@bitcoinerlab/secp256k1';
|
|
3
3
|
import { Address } from '@btc-vision/bsi-binary';
|
|
4
|
+
import { EcKeyPair } from './EcKeyPair.js';
|
|
4
5
|
|
|
5
6
|
initEccLib(ecc);
|
|
6
7
|
|
|
8
|
+
export enum AddressTypes {
|
|
9
|
+
P2PKH = 'P2PKH',
|
|
10
|
+
P2SH = 'P2SH',
|
|
11
|
+
P2SH_P2WPKH = 'P2SH-P2WPKH',
|
|
12
|
+
P2SH_OR_P2SH_P2WPKH = 'P2SH_OR_P2SH-P2WPKH',
|
|
13
|
+
P2PK = 'P2PK',
|
|
14
|
+
P2TR = 'P2TR',
|
|
15
|
+
P2WPKH = 'P2WPKH',
|
|
16
|
+
}
|
|
17
|
+
|
|
7
18
|
export class AddressVerificator {
|
|
8
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Checks if the given address is a valid P2PKH address.
|
|
21
|
+
* @param inAddress - The address to check.
|
|
22
|
+
* @param network - The network to validate against.
|
|
23
|
+
* @returns - True if the address is a valid P2PKH address, false otherwise.
|
|
24
|
+
* @remarks This method is useful for validating legacy addresses (P2PKH) without
|
|
25
|
+
*/
|
|
26
|
+
public static isValidP2TRAddress(inAddress: Address, network: Network): boolean {
|
|
9
27
|
if (!inAddress || inAddress.length < 50) return false;
|
|
10
28
|
|
|
11
29
|
let isValidTapRootAddress: boolean = false;
|
|
@@ -14,12 +32,19 @@ export class AddressVerificator {
|
|
|
14
32
|
|
|
15
33
|
const decodedAddress = address.fromBech32(inAddress);
|
|
16
34
|
isValidTapRootAddress = decodedAddress.version === 1;
|
|
17
|
-
} catch
|
|
35
|
+
} catch {}
|
|
18
36
|
|
|
19
37
|
return isValidTapRootAddress;
|
|
20
38
|
}
|
|
21
39
|
|
|
22
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Checks if the given address is a valid P2PKH address.
|
|
42
|
+
* @param inAddress - The address to check.
|
|
43
|
+
* @param network - The network to validate against.
|
|
44
|
+
* @returns - True if the address is a valid P2PKH address, false otherwise.
|
|
45
|
+
* @remarks This method is useful for validating legacy addresses (P2PKH) without
|
|
46
|
+
*/
|
|
47
|
+
public static isP2WPKHAddress(inAddress: string, network: Network): boolean {
|
|
23
48
|
if (!inAddress || inAddress.length < 20 || inAddress.length > 50) return false;
|
|
24
49
|
|
|
25
50
|
let isValidSegWitAddress: boolean = false;
|
|
@@ -33,8 +58,122 @@ export class AddressVerificator {
|
|
|
33
58
|
// Check if the address is P2WPKH (version 0)
|
|
34
59
|
isValidSegWitAddress =
|
|
35
60
|
decodedAddress.version === 0 && decodedAddress.data.length === 20;
|
|
36
|
-
} catch
|
|
61
|
+
} catch {}
|
|
37
62
|
|
|
38
63
|
return isValidSegWitAddress;
|
|
39
64
|
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Checks if the given address is a valid P2PKH or P2SH address.
|
|
68
|
+
* @param addy - The address to check.
|
|
69
|
+
* @param network - The network to validate against.
|
|
70
|
+
* @returns - True if the address is a valid P2PKH or P2SH address, false otherwise.
|
|
71
|
+
* @remarks This method is useful for validating legacy addresses (P2PKH or P2SH) without
|
|
72
|
+
*/
|
|
73
|
+
public static isP2PKHOrP2SH(addy: string, network: Network): boolean {
|
|
74
|
+
try {
|
|
75
|
+
// First, try to decode as a Base58Check address (P2PKH, P2SH, or P2SH-P2WPKH)
|
|
76
|
+
const decodedBase58 = address.fromBase58Check(addy);
|
|
77
|
+
|
|
78
|
+
if (decodedBase58.version === network.pubKeyHash) {
|
|
79
|
+
// P2PKH: Legacy address (starting with '1' for mainnet, 'm/n' for testnet)
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return decodedBase58.version === network.scriptHash;
|
|
84
|
+
} catch (error) {
|
|
85
|
+
// If decoding fails or version is not valid, it's not a valid legacy address
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Checks if the input is a valid hexadecimal public key (P2PK).
|
|
92
|
+
* Public keys can be compressed (66 characters) or uncompressed (130 characters).
|
|
93
|
+
*
|
|
94
|
+
* @param input - The input string to check.
|
|
95
|
+
* @param network - The Bitcoin network to validate against (mainnet, testnet, etc.).
|
|
96
|
+
* @returns - True if the input is a valid public key, false otherwise.
|
|
97
|
+
*/
|
|
98
|
+
public static isValidPublicKey(input: string, network: Network): boolean {
|
|
99
|
+
try {
|
|
100
|
+
if (input.startsWith('0x')) {
|
|
101
|
+
input = input.slice(2);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Compressed public keys are 66 characters long (0x02 or 0x03 prefix + 32 bytes)
|
|
105
|
+
// Uncompressed public keys are 130 characters long (0x04 prefix + 64 bytes)
|
|
106
|
+
const hexRegex = /^[0-9a-fA-F]+$/;
|
|
107
|
+
|
|
108
|
+
if ((input.length === 66 || input.length === 130) && hexRegex.test(input)) {
|
|
109
|
+
// Check if the input can be parsed as a valid public key
|
|
110
|
+
const pubKeyBuffer = Buffer.from(input, 'hex');
|
|
111
|
+
EcKeyPair.fromPublicKey(pubKeyBuffer, network);
|
|
112
|
+
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.log(error);
|
|
117
|
+
|
|
118
|
+
// If any error occurs (invalid public key, etc.), return false
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return false; // Not a valid public key
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Validates if a given Bitcoin address is of the specified type and network.
|
|
127
|
+
* - P2PKH (Legacy address starting with '1' for mainnet or 'm/n' for testnet)
|
|
128
|
+
* - P2SH (Legacy address starting with '3' for mainnet or '2' for testnet)
|
|
129
|
+
* - P2SH-P2WPKH (Wrapped SegWit)
|
|
130
|
+
* - P2PK (Pay to PubKey, technically treated similarly to P2PKH)
|
|
131
|
+
* - P2WPKH (SegWit address starting with 'bc1q' for mainnet or 'tb1q' for testnet)
|
|
132
|
+
* - P2TR (Taproot address starting with 'bc1p' for mainnet or 'tb1p' for testnet)
|
|
133
|
+
*
|
|
134
|
+
* @param addy - The Bitcoin address to validate.
|
|
135
|
+
* @param network - The Bitcoin network to validate against (mainnet, testnet, etc.).
|
|
136
|
+
* @returns - The type of the valid Bitcoin address, or null if invalid.
|
|
137
|
+
*/
|
|
138
|
+
public static validateBitcoinAddress(addy: string, network: Network): AddressTypes | null {
|
|
139
|
+
if (AddressVerificator.isValidPublicKey(addy, network)) {
|
|
140
|
+
return AddressTypes.P2PK;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
// First, try to decode as a Base58Check address (P2PKH, P2SH, or P2SH-P2WPKH)
|
|
145
|
+
const decodedBase58 = address.fromBase58Check(addy);
|
|
146
|
+
|
|
147
|
+
if (decodedBase58.version === network.pubKeyHash) {
|
|
148
|
+
// P2PKH: Legacy address (starting with '1' for mainnet, 'm/n' for testnet)
|
|
149
|
+
return AddressTypes.P2PKH;
|
|
150
|
+
}
|
|
151
|
+
if (decodedBase58.version === network.scriptHash) {
|
|
152
|
+
// P2SH: Could be P2SH (general) or P2SH-P2WPKH (wrapped SegWit)
|
|
153
|
+
return AddressTypes.P2SH_OR_P2SH_P2WPKH;
|
|
154
|
+
}
|
|
155
|
+
} catch (error) {
|
|
156
|
+
// Ignore errors from Base58 decoding, it could be a Bech32 address
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
// Try to decode as a Bech32 or Bech32m address (P2WPKH or P2TR)
|
|
161
|
+
const decodedBech32 = address.fromBech32(addy);
|
|
162
|
+
|
|
163
|
+
if (decodedBech32.prefix === network.bech32) {
|
|
164
|
+
// P2WPKH: SegWit address (starting with 'bc1q' for mainnet, 'tb1q' for testnet)
|
|
165
|
+
if (decodedBech32.version === 0 && decodedBech32.data.length === 20) {
|
|
166
|
+
return AddressTypes.P2WPKH;
|
|
167
|
+
}
|
|
168
|
+
// P2TR: Taproot address (starting with 'bc1p' for mainnet, 'tb1p' for testnet)
|
|
169
|
+
if (decodedBech32.version === 1 && decodedBech32.data.length === 32) {
|
|
170
|
+
return AddressTypes.P2TR;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
} catch (error) {
|
|
174
|
+
// Ignore errors from Bech32/Bech32m decoding
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return null; // Not a valid or recognized Bitcoin address type
|
|
178
|
+
}
|
|
40
179
|
}
|
package/src/keypair/EcKeyPair.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import * as ecc from '@bitcoinerlab/secp256k1';
|
|
2
2
|
import { Address } from '@btc-vision/bsi-binary';
|
|
3
3
|
import bip32, { BIP32API, BIP32Factory, BIP32Interface } from 'bip32';
|
|
4
|
-
import { address, initEccLib, Network, networks, payments } from 'bitcoinjs-lib';
|
|
4
|
+
import { address, initEccLib, Network, networks, payments, Signer } from 'bitcoinjs-lib';
|
|
5
5
|
import { toXOnly } from 'bitcoinjs-lib/src/psbt/bip371.js';
|
|
6
6
|
import { ECPairAPI, ECPairFactory, ECPairInterface } from 'ecpair';
|
|
7
7
|
import { IWallet } from './interfaces/IWallet.js';
|
|
8
|
+
import { CURVE, Point, utils } from '@noble/secp256k1';
|
|
9
|
+
import { taggedHash } from 'bitcoinjs-lib/src/crypto.js';
|
|
8
10
|
|
|
9
11
|
initEccLib(ecc);
|
|
10
12
|
|
|
@@ -42,23 +44,28 @@ export class EcKeyPair {
|
|
|
42
44
|
* @returns {ECPairInterface} - The generated keypair
|
|
43
45
|
*/
|
|
44
46
|
public static fromPrivateKey(
|
|
45
|
-
privateKey: Buffer,
|
|
47
|
+
privateKey: Buffer | Uint8Array,
|
|
46
48
|
network: Network = networks.bitcoin,
|
|
47
49
|
): ECPairInterface {
|
|
48
|
-
return this.ECPair.fromPrivateKey(
|
|
50
|
+
return this.ECPair.fromPrivateKey(
|
|
51
|
+
!Buffer.isBuffer(privateKey) ? Buffer.from(privateKey) : privateKey,
|
|
52
|
+
{ network },
|
|
53
|
+
);
|
|
49
54
|
}
|
|
50
55
|
|
|
51
56
|
/**
|
|
52
57
|
* Generate a keypair from a public key
|
|
53
|
-
* @param {Buffer} publicKey - The public key to use
|
|
58
|
+
* @param {Buffer | Uint8Array} publicKey - The public key to use
|
|
54
59
|
* @param {Network} network - The network to use
|
|
55
60
|
* @returns {ECPairInterface} - The generated keypair
|
|
56
61
|
*/
|
|
57
62
|
public static fromPublicKey(
|
|
58
|
-
publicKey: Buffer,
|
|
63
|
+
publicKey: Buffer | Uint8Array,
|
|
59
64
|
network: Network = networks.bitcoin,
|
|
60
65
|
): ECPairInterface {
|
|
61
|
-
|
|
66
|
+
const buf = !Buffer.isBuffer(publicKey) ? Buffer.from(publicKey) : publicKey;
|
|
67
|
+
|
|
68
|
+
return this.ECPair.fromPublicKey(buf, { network });
|
|
62
69
|
}
|
|
63
70
|
|
|
64
71
|
/**
|
|
@@ -122,7 +129,7 @@ export class EcKeyPair {
|
|
|
122
129
|
throw new Error('Failed to regenerate key');
|
|
123
130
|
}
|
|
124
131
|
|
|
125
|
-
return key.publicKey;
|
|
132
|
+
return Buffer.from(key.publicKey);
|
|
126
133
|
});
|
|
127
134
|
}
|
|
128
135
|
|
|
@@ -136,7 +143,7 @@ export class EcKeyPair {
|
|
|
136
143
|
keyPair: ECPairInterface,
|
|
137
144
|
network: Network = networks.bitcoin,
|
|
138
145
|
): Address {
|
|
139
|
-
const res = payments.p2wpkh({ pubkey: keyPair.publicKey, network: network });
|
|
146
|
+
const res = payments.p2wpkh({ pubkey: Buffer.from(keyPair.publicKey), network: network });
|
|
140
147
|
|
|
141
148
|
if (!res.address) {
|
|
142
149
|
throw new Error('Failed to generate wallet');
|
|
@@ -145,6 +152,96 @@ export class EcKeyPair {
|
|
|
145
152
|
return res.address;
|
|
146
153
|
}
|
|
147
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Get the address of a tweaked public key
|
|
157
|
+
* @param {string} tweakedPubKeyHex - The tweaked public key hex string
|
|
158
|
+
* @param {Network} network - The network to use
|
|
159
|
+
* @returns {Address} - The address
|
|
160
|
+
* @throws {Error} - If the address cannot be generated
|
|
161
|
+
*/
|
|
162
|
+
public static tweakedPubKeyToAddress(tweakedPubKeyHex: string, network: Network): string {
|
|
163
|
+
if (tweakedPubKeyHex.startsWith('0x')) {
|
|
164
|
+
tweakedPubKeyHex = tweakedPubKeyHex.slice(2);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Convert the tweaked public key hex string to a Buffer
|
|
168
|
+
const tweakedPubKeyBuffer = Buffer.from(tweakedPubKeyHex, 'hex');
|
|
169
|
+
|
|
170
|
+
// Generate the Taproot address using the p2tr payment method
|
|
171
|
+
const { address } = payments.p2tr({
|
|
172
|
+
pubkey: toXOnly(tweakedPubKeyBuffer),
|
|
173
|
+
network: network,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
if (!address) {
|
|
177
|
+
throw new Error('Failed to generate Taproot address');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return address;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Get the address of a xOnly tweaked public key
|
|
185
|
+
* @param {string} tweakedPubKeyHex - The xOnly tweaked public key hex string
|
|
186
|
+
* @param {Network} network - The network to use
|
|
187
|
+
* @returns {Address} - The address
|
|
188
|
+
* @throws {Error} - If the address cannot be generated
|
|
189
|
+
*/
|
|
190
|
+
public static xOnlyTweakedPubKeyToAddress(tweakedPubKeyHex: string, network: Network): string {
|
|
191
|
+
if (tweakedPubKeyHex.startsWith('0x')) {
|
|
192
|
+
tweakedPubKeyHex = tweakedPubKeyHex.slice(2);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Convert the tweaked public key hex string to a Buffer
|
|
196
|
+
const tweakedPubKeyBuffer = Buffer.from(tweakedPubKeyHex, 'hex');
|
|
197
|
+
|
|
198
|
+
// Generate the Taproot address using the p2tr payment method
|
|
199
|
+
const { address } = payments.p2tr({
|
|
200
|
+
pubkey: tweakedPubKeyBuffer,
|
|
201
|
+
network: network,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
if (!address) {
|
|
205
|
+
throw new Error('Failed to generate Taproot address');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return address;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Tweak a public key
|
|
213
|
+
* @param {string} compressedPubKeyHex - The compressed public key hex string
|
|
214
|
+
* @returns {string} - The tweaked public key hex string
|
|
215
|
+
* @throws {Error} - If the public key cannot be tweaked
|
|
216
|
+
*/
|
|
217
|
+
public static tweakPublicKey(compressedPubKeyHex: string): string {
|
|
218
|
+
if (compressedPubKeyHex.startsWith('0x')) {
|
|
219
|
+
compressedPubKeyHex = compressedPubKeyHex.slice(2);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Convert the compressed public key hex string to a Point on the curve
|
|
223
|
+
let P = Point.fromHex(compressedPubKeyHex);
|
|
224
|
+
|
|
225
|
+
// Ensure the point has an even y-coordinate
|
|
226
|
+
if (!P.hasEvenY()) {
|
|
227
|
+
// Negate the point to get an even y-coordinate
|
|
228
|
+
P = P.negate();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Get the x-coordinate (32 bytes) of the point
|
|
232
|
+
const x = P.toRawBytes(true).slice(1); // Remove the prefix byte
|
|
233
|
+
|
|
234
|
+
// Compute the tweak t = H_tapTweak(x)
|
|
235
|
+
const tHash = taggedHash('TapTweak', Buffer.from(x));
|
|
236
|
+
const t = utils.mod(BigInt('0x' + Buffer.from(tHash).toString('hex')), CURVE.n);
|
|
237
|
+
|
|
238
|
+
// Compute Q = P + t*G (where G is the generator point)
|
|
239
|
+
const Q = P.add(Point.BASE.multiply(t));
|
|
240
|
+
|
|
241
|
+
// Return the tweaked public key in compressed form (hex string)
|
|
242
|
+
return Q.toHex(true);
|
|
243
|
+
}
|
|
244
|
+
|
|
148
245
|
/**
|
|
149
246
|
* Generate a random wallet
|
|
150
247
|
* @param {Network} network - The network to use
|
|
@@ -164,7 +261,7 @@ export class EcKeyPair {
|
|
|
164
261
|
return {
|
|
165
262
|
address: wallet,
|
|
166
263
|
privateKey: keyPair.toWIF(),
|
|
167
|
-
publicKey: keyPair.publicKey.toString('hex'),
|
|
264
|
+
publicKey: Buffer.from(keyPair.publicKey).toString('hex'),
|
|
168
265
|
};
|
|
169
266
|
}
|
|
170
267
|
|
|
@@ -192,7 +289,7 @@ export class EcKeyPair {
|
|
|
192
289
|
network: Network = networks.bitcoin,
|
|
193
290
|
): Address {
|
|
194
291
|
const wallet = payments.p2sh({
|
|
195
|
-
redeem: payments.p2wpkh({ pubkey: keyPair.publicKey, network: network }),
|
|
292
|
+
redeem: payments.p2wpkh({ pubkey: Buffer.from(keyPair.publicKey), network: network }),
|
|
196
293
|
network: network,
|
|
197
294
|
});
|
|
198
295
|
|
|
@@ -213,7 +310,7 @@ export class EcKeyPair {
|
|
|
213
310
|
keyPair: ECPairInterface,
|
|
214
311
|
network: Network = networks.bitcoin,
|
|
215
312
|
): Address {
|
|
216
|
-
const wallet = payments.p2pkh({ pubkey: keyPair.publicKey, network: network });
|
|
313
|
+
const wallet = payments.p2pkh({ pubkey: Buffer.from(keyPair.publicKey), network: network });
|
|
217
314
|
if (!wallet.address) {
|
|
218
315
|
throw new Error('Failed to generate wallet');
|
|
219
316
|
}
|
|
@@ -231,7 +328,7 @@ export class EcKeyPair {
|
|
|
231
328
|
keyPair: ECPairInterface,
|
|
232
329
|
network: Network = networks.bitcoin,
|
|
233
330
|
): Address {
|
|
234
|
-
const wallet = payments.p2pk({ pubkey: keyPair.publicKey, network: network });
|
|
331
|
+
const wallet = payments.p2pk({ pubkey: Buffer.from(keyPair.publicKey), network: network });
|
|
235
332
|
if (!wallet.output) {
|
|
236
333
|
throw new Error('Failed to generate wallet');
|
|
237
334
|
}
|
|
@@ -267,11 +364,11 @@ export class EcKeyPair {
|
|
|
267
364
|
* @returns {Address} - The taproot address
|
|
268
365
|
*/
|
|
269
366
|
public static getTaprootAddress(
|
|
270
|
-
keyPair: ECPairInterface,
|
|
367
|
+
keyPair: ECPairInterface | Signer,
|
|
271
368
|
network: Network = networks.bitcoin,
|
|
272
369
|
): Address {
|
|
273
370
|
const { address } = payments.p2tr({
|
|
274
|
-
internalPubkey: toXOnly(keyPair.publicKey),
|
|
371
|
+
internalPubkey: toXOnly(Buffer.from(keyPair.publicKey)),
|
|
275
372
|
network: network,
|
|
276
373
|
});
|
|
277
374
|
|
package/src/keypair/Wallet.ts
CHANGED
|
@@ -33,6 +33,12 @@ export class Wallet {
|
|
|
33
33
|
*/
|
|
34
34
|
private readonly _legacy: Address;
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Buffer public key
|
|
38
|
+
* @private
|
|
39
|
+
*/
|
|
40
|
+
private readonly _bufferPubKey: Buffer;
|
|
41
|
+
|
|
36
42
|
constructor(
|
|
37
43
|
wallet: IWallet,
|
|
38
44
|
public readonly network: Network = networks.bitcoin,
|
|
@@ -42,6 +48,8 @@ export class Wallet {
|
|
|
42
48
|
this._p2wpkh = EcKeyPair.getP2WPKHAddress(this._keypair, this.network);
|
|
43
49
|
this._p2tr = EcKeyPair.getTaprootAddress(this._keypair, this.network);
|
|
44
50
|
this._legacy = EcKeyPair.getLegacyAddress(this._keypair, this.network);
|
|
51
|
+
|
|
52
|
+
this._bufferPubKey = Buffer.from(wallet.publicKey, 'hex');
|
|
45
53
|
}
|
|
46
54
|
|
|
47
55
|
/**
|
|
@@ -92,9 +100,9 @@ export class Wallet {
|
|
|
92
100
|
* @returns {Buffer}
|
|
93
101
|
*/
|
|
94
102
|
public get publicKey(): Buffer {
|
|
95
|
-
if (!this.
|
|
103
|
+
if (!this._bufferPubKey) throw new Error('Public key not set');
|
|
96
104
|
|
|
97
|
-
return this.
|
|
105
|
+
return this._bufferPubKey;
|
|
98
106
|
}
|
|
99
107
|
|
|
100
108
|
/**
|
|
@@ -105,7 +113,7 @@ export class Wallet {
|
|
|
105
113
|
public get xOnly(): Buffer {
|
|
106
114
|
if (!this.keypair) throw new Error('Keypair not set');
|
|
107
115
|
|
|
108
|
-
return toXOnly(this.
|
|
116
|
+
return toXOnly(this._bufferPubKey);
|
|
109
117
|
}
|
|
110
118
|
|
|
111
119
|
/**
|
|
@@ -31,8 +31,9 @@ export class TweakedSigner {
|
|
|
31
31
|
* Tweak a signer
|
|
32
32
|
* @param {Signer} signer - The signer to tweak
|
|
33
33
|
* @param {TweakSettings} opts - The tweak settings
|
|
34
|
+
* @returns {ECPairInterface} - The tweaked signer
|
|
34
35
|
*/
|
|
35
|
-
public static tweakSigner(signer: ECPairInterface, opts: TweakSettings = {}):
|
|
36
|
+
public static tweakSigner(signer: ECPairInterface, opts: TweakSettings = {}): ECPairInterface {
|
|
36
37
|
let privateKey: Uint8Array | undefined = signer.privateKey;
|
|
37
38
|
if (!privateKey) {
|
|
38
39
|
throw new Error('Private key is required for tweaking signer!');
|
|
@@ -44,7 +45,7 @@ export class TweakedSigner {
|
|
|
44
45
|
|
|
45
46
|
const tweakedPrivateKey = ecc.privateAdd(
|
|
46
47
|
privateKey,
|
|
47
|
-
tapTweakHash(toXOnly(signer.publicKey), opts.tweakHash),
|
|
48
|
+
tapTweakHash(toXOnly(Buffer.from(signer.publicKey)), opts.tweakHash),
|
|
48
49
|
);
|
|
49
50
|
|
|
50
51
|
if (!tweakedPrivateKey) {
|
|
@@ -11,6 +11,7 @@ import { EcKeyPair } from '../../keypair/EcKeyPair.js';
|
|
|
11
11
|
import { AddressGenerator } from '../../generators/AddressGenerator.js';
|
|
12
12
|
import { toXOnly } from 'bitcoinjs-lib/src/psbt/bip371.js';
|
|
13
13
|
import { PsbtInput } from 'bip174/src/lib/interfaces.js';
|
|
14
|
+
import { ECPairInterface } from 'ecpair';
|
|
14
15
|
|
|
15
16
|
export interface ICustomTransactionParameters extends SharedInteractionParameters {
|
|
16
17
|
readonly script: (Buffer | Stack)[];
|
|
@@ -72,7 +73,7 @@ export class CustomScriptTransaction extends TransactionBuilder<TransactionType.
|
|
|
72
73
|
* The contract signer
|
|
73
74
|
* @private
|
|
74
75
|
*/
|
|
75
|
-
private readonly contractSigner: Signer;
|
|
76
|
+
private readonly contractSigner: Signer | ECPairInterface;
|
|
76
77
|
|
|
77
78
|
/**
|
|
78
79
|
* The contract salt random bytes
|
|
@@ -136,7 +137,7 @@ export class CustomScriptTransaction extends TransactionBuilder<TransactionType.
|
|
|
136
137
|
* @protected
|
|
137
138
|
*/
|
|
138
139
|
protected contractSignerXOnlyPubKey(): Buffer {
|
|
139
|
-
return toXOnly(this.contractSigner.publicKey);
|
|
140
|
+
return toXOnly(Buffer.from(this.contractSigner.publicKey));
|
|
140
141
|
}
|
|
141
142
|
|
|
142
143
|
/**
|
|
@@ -261,7 +262,7 @@ export class CustomScriptTransaction extends TransactionBuilder<TransactionType.
|
|
|
261
262
|
if (!this.tapLeafScript) {
|
|
262
263
|
throw new Error('Tap leaf script is required');
|
|
263
264
|
}
|
|
264
|
-
|
|
265
|
+
|
|
265
266
|
const scriptSolution = this.witnesses;
|
|
266
267
|
const witness = scriptSolution
|
|
267
268
|
.concat(this.tapLeafScript.script)
|
|
@@ -277,10 +278,10 @@ export class CustomScriptTransaction extends TransactionBuilder<TransactionType.
|
|
|
277
278
|
* @private
|
|
278
279
|
*/
|
|
279
280
|
private getPubKeys(): Buffer[] {
|
|
280
|
-
const pubkeys = [this.signer.publicKey];
|
|
281
|
+
const pubkeys = [Buffer.from(this.signer.publicKey)];
|
|
281
282
|
|
|
282
283
|
if (this.contractSigner) {
|
|
283
|
-
pubkeys.push(this.contractSigner.publicKey);
|
|
284
|
+
pubkeys.push(Buffer.from(this.contractSigner.publicKey));
|
|
284
285
|
}
|
|
285
286
|
|
|
286
287
|
return pubkeys;
|