@btc-vision/transaction 1.0.111 → 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/generators/Generator.d.ts +2 -0
- package/browser/index.js +1 -1
- package/browser/keypair/AddressVerificator.d.ts +15 -3
- package/browser/keypair/EcKeyPair.d.ts +9 -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 +3 -2
- package/browser/transaction/shared/TweakedTransaction.d.ts +8 -7
- package/browser/verification/TapscriptVerificator.d.ts +1 -1
- package/build/_version.d.ts +1 -1
- package/build/_version.js +1 -1
- package/build/generators/Generator.d.ts +2 -0
- package/build/generators/Generator.js +5 -0
- package/build/generators/builders/CalldataGenerator.js +4 -2
- package/build/generators/builders/DeploymentGenerator.js +7 -4
- package/build/keypair/AddressVerificator.d.ts +15 -3
- package/build/keypair/AddressVerificator.js +74 -3
- package/build/keypair/EcKeyPair.d.ts +9 -4
- package/build/keypair/EcKeyPair.js +69 -7
- 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/FundingTransaction.js +18 -4
- 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 +3 -2
- package/build/transaction/builders/TransactionBuilder.js +7 -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/build/verification/TapscriptVerificator.d.ts +1 -1
- package/build/verification/TapscriptVerificator.js +2 -2
- package/package.json +2 -1
- package/src/_version.ts +1 -1
- package/src/generators/Generator.ts +12 -0
- package/src/generators/builders/CalldataGenerator.ts +6 -3
- package/src/generators/builders/DeploymentGenerator.ts +9 -4
- package/src/keypair/AddressVerificator.ts +144 -5
- package/src/keypair/EcKeyPair.ts +149 -13
- 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 +18 -5
- package/src/transaction/builders/MultiSignTransaction.ts +6 -2
- package/src/transaction/builders/SharedInteractionTransaction.ts +5 -5
- package/src/transaction/builders/TransactionBuilder.ts +36 -10
- 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
- package/src/verification/TapscriptVerificator.ts +3 -3
|
@@ -49,7 +49,7 @@ export class CustomScriptTransaction extends TransactionBuilder {
|
|
|
49
49
|
return this.randomBytes;
|
|
50
50
|
}
|
|
51
51
|
contractSignerXOnlyPubKey() {
|
|
52
|
-
return toXOnly(this.contractSigner.publicKey);
|
|
52
|
+
return toXOnly(Buffer.from(this.contractSigner.publicKey));
|
|
53
53
|
}
|
|
54
54
|
async buildTransaction() {
|
|
55
55
|
if (!this.to) {
|
|
@@ -125,9 +125,9 @@ export class CustomScriptTransaction extends TransactionBuilder {
|
|
|
125
125
|
return bitCrypto.hash256(this.randomBytes);
|
|
126
126
|
}
|
|
127
127
|
getPubKeys() {
|
|
128
|
-
const pubkeys = [this.signer.publicKey];
|
|
128
|
+
const pubkeys = [Buffer.from(this.signer.publicKey)];
|
|
129
129
|
if (this.contractSigner) {
|
|
130
|
-
pubkeys.push(this.contractSigner.publicKey);
|
|
130
|
+
pubkeys.push(Buffer.from(this.contractSigner.publicKey));
|
|
131
131
|
}
|
|
132
132
|
return pubkeys;
|
|
133
133
|
}
|
|
@@ -44,7 +44,7 @@ export class DeploymentTransaction extends TransactionBuilder {
|
|
|
44
44
|
this.randomBytes = parameters.randomBytes || BitcoinUtils.rndBytes();
|
|
45
45
|
this.contractSeed = this.getContractSeed();
|
|
46
46
|
this.contractSigner = EcKeyPair.fromSeedKeyPair(this.contractSeed, this.network);
|
|
47
|
-
this.deploymentGenerator = new DeploymentGenerator(this.
|
|
47
|
+
this.deploymentGenerator = new DeploymentGenerator(Buffer.from(this.signer.publicKey), this.contractSignerXOnlyPubKey(), this.network);
|
|
48
48
|
this.compiledTargetScript = this.deploymentGenerator.compile(this.bytecode, this.randomBytes, this.calldata);
|
|
49
49
|
this.scriptTree = this.getScriptTree();
|
|
50
50
|
this.internalInit();
|
|
@@ -60,7 +60,7 @@ export class DeploymentTransaction extends TransactionBuilder {
|
|
|
60
60
|
return this.randomBytes;
|
|
61
61
|
}
|
|
62
62
|
contractSignerXOnlyPubKey() {
|
|
63
|
-
return toXOnly(this.contractSigner.publicKey);
|
|
63
|
+
return toXOnly(Buffer.from(this.contractSigner.publicKey));
|
|
64
64
|
}
|
|
65
65
|
async buildTransaction() {
|
|
66
66
|
if (!this.to) {
|
|
@@ -156,9 +156,9 @@ export class DeploymentTransaction extends TransactionBuilder {
|
|
|
156
156
|
return bitCrypto.hash256(buf);
|
|
157
157
|
}
|
|
158
158
|
getPubKeys() {
|
|
159
|
-
const pubkeys = [this.signer.publicKey];
|
|
159
|
+
const pubkeys = [Buffer.from(this.signer.publicKey)];
|
|
160
160
|
if (this.contractSigner) {
|
|
161
|
-
pubkeys.push(this.contractSigner.publicKey);
|
|
161
|
+
pubkeys.push(Buffer.from(this.contractSigner.publicKey));
|
|
162
162
|
}
|
|
163
163
|
return pubkeys;
|
|
164
164
|
}
|
|
@@ -2,6 +2,7 @@ import { TransactionType } from '../enums/TransactionType.js';
|
|
|
2
2
|
import { IFundingTransactionParameters } from '../interfaces/ITransactionParameters.js';
|
|
3
3
|
import { Signer } from 'bitcoinjs-lib';
|
|
4
4
|
import { TransactionBuilder } from './TransactionBuilder.js';
|
|
5
|
+
import { ECPairInterface } from 'ecpair';
|
|
5
6
|
export declare class FundingTransaction extends TransactionBuilder<TransactionType.FUNDING> {
|
|
6
7
|
readonly type: TransactionType.FUNDING;
|
|
7
8
|
protected amount: bigint;
|
|
@@ -9,5 +10,5 @@ export declare class FundingTransaction extends TransactionBuilder<TransactionTy
|
|
|
9
10
|
constructor(parameters: IFundingTransactionParameters);
|
|
10
11
|
protected buildTransaction(): Promise<void>;
|
|
11
12
|
protected splitInputs(amountSpent: bigint): void;
|
|
12
|
-
protected getSignerKey(): Signer;
|
|
13
|
+
protected getSignerKey(): Signer | ECPairInterface;
|
|
13
14
|
}
|
|
@@ -16,6 +16,12 @@ export class FundingTransaction extends TransactionBuilder {
|
|
|
16
16
|
if (this.splitInputsInto > 1) {
|
|
17
17
|
this.splitInputs(this.amount);
|
|
18
18
|
}
|
|
19
|
+
else if (this.isPubKeyDestination) {
|
|
20
|
+
this.addOutput({
|
|
21
|
+
value: Number(this.amount),
|
|
22
|
+
script: Buffer.from(this.to.slice(2), 'hex'),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
19
25
|
else {
|
|
20
26
|
this.addOutput({
|
|
21
27
|
value: Number(this.amount),
|
|
@@ -30,10 +36,18 @@ export class FundingTransaction extends TransactionBuilder {
|
|
|
30
36
|
}
|
|
31
37
|
const splitAmount = amountSpent / BigInt(this.splitInputsInto);
|
|
32
38
|
for (let i = 0; i < this.splitInputsInto; i++) {
|
|
33
|
-
this.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
if (this.isPubKeyDestination) {
|
|
40
|
+
this.addOutput({
|
|
41
|
+
value: Number(splitAmount),
|
|
42
|
+
script: Buffer.from(this.to.slice(2), 'hex'),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
this.addOutput({
|
|
47
|
+
value: Number(splitAmount),
|
|
48
|
+
address: this.to,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
37
51
|
}
|
|
38
52
|
}
|
|
39
53
|
getSignerKey() {
|
|
@@ -5,6 +5,7 @@ import { TransactionBuilder } from './TransactionBuilder.js';
|
|
|
5
5
|
import { TransactionType } from '../enums/TransactionType.js';
|
|
6
6
|
import { ITransactionParameters } from '../interfaces/ITransactionParameters.js';
|
|
7
7
|
import { Address } from '@btc-vision/bsi-binary';
|
|
8
|
+
import { ECPairInterface } from 'ecpair';
|
|
8
9
|
export interface MultiSignParameters extends Omit<ITransactionParameters, 'priorityFee' | 'signer'> {
|
|
9
10
|
readonly pubkeys: Buffer[];
|
|
10
11
|
readonly minimumSignatures: number;
|
|
@@ -37,7 +38,7 @@ export declare class MultiSignTransaction extends TransactionBuilder<Transaction
|
|
|
37
38
|
constructor(parameters: MultiSignParameters);
|
|
38
39
|
static fromBase64(params: MultiSignFromBase64Params): MultiSignTransaction;
|
|
39
40
|
static verifyIfSigned(psbt: Psbt, signerPubKey: Buffer): boolean;
|
|
40
|
-
static signPartial(psbt: Psbt, signer: Signer, originalInputCount: number, minimums: number[]): {
|
|
41
|
+
static signPartial(psbt: Psbt, signer: Signer | ECPairInterface, originalInputCount: number, minimums: number[]): {
|
|
41
42
|
final: boolean;
|
|
42
43
|
signed: boolean;
|
|
43
44
|
};
|
|
@@ -16,7 +16,7 @@ export declare abstract class SharedInteractionTransaction<T extends Transaction
|
|
|
16
16
|
protected calldataGenerator: CalldataGenerator;
|
|
17
17
|
protected readonly calldata: Buffer;
|
|
18
18
|
protected abstract readonly contractSecret: Buffer;
|
|
19
|
-
protected readonly scriptSigner: Signer;
|
|
19
|
+
protected readonly scriptSigner: Signer | ECPairInterface;
|
|
20
20
|
protected readonly disableAutoRefund: boolean;
|
|
21
21
|
protected constructor(parameters: SharedInteractionParameters);
|
|
22
22
|
getContractSecret(): Buffer;
|
|
@@ -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.
|
|
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;
|
|
@@ -28,8 +28,9 @@ export declare abstract class TransactionBuilder<T extends TransactionType> exte
|
|
|
28
28
|
protected to: Address | undefined;
|
|
29
29
|
protected from: Address;
|
|
30
30
|
protected _maximumFeeRate: number;
|
|
31
|
+
protected isPubKeyDestination: boolean;
|
|
31
32
|
protected constructor(parameters: ITransactionParameters);
|
|
32
|
-
static getFrom(from: string | undefined, keypair: ECPairInterface, network: Network): Address;
|
|
33
|
+
static getFrom(from: string | undefined, keypair: ECPairInterface | Signer, network: Network): Address;
|
|
33
34
|
static witnessStackToScriptWitness(witness: Buffer[]): Buffer;
|
|
34
35
|
getFundingTransactionParameters(): Promise<IFundingTransactionParameters>;
|
|
35
36
|
setDestinationAddress(address: Address): void;
|
|
@@ -25,6 +25,7 @@ export class TransactionBuilder extends TweakedTransaction {
|
|
|
25
25
|
this.priorityFee = parameters.priorityFee ?? 0n;
|
|
26
26
|
this.utxos = parameters.utxos;
|
|
27
27
|
this.to = parameters.to || undefined;
|
|
28
|
+
this.isPubKeyDestination = this.to ? this.to.startsWith('0x') : false;
|
|
28
29
|
this.optionalOutputs = parameters.optionalOutputs;
|
|
29
30
|
this.from = TransactionBuilder.getFrom(parameters.from, this.signer, this.network);
|
|
30
31
|
this.totalInputAmount = this.calculateTotalUTXOAmount();
|
|
@@ -87,7 +88,9 @@ export class TransactionBuilder extends TweakedTransaction {
|
|
|
87
88
|
if (!this.utxos.length) {
|
|
88
89
|
throw new Error('No UTXOs specified');
|
|
89
90
|
}
|
|
90
|
-
if (this.to &&
|
|
91
|
+
if (this.to &&
|
|
92
|
+
!this.isPubKeyDestination &&
|
|
93
|
+
!EcKeyPair.verifyContractAddress(this.to, this.network)) {
|
|
91
94
|
throw new Error('Invalid contract address. The contract address must be a taproot address.');
|
|
92
95
|
}
|
|
93
96
|
if (this.signed)
|
|
@@ -104,7 +107,9 @@ export class TransactionBuilder extends TweakedTransaction {
|
|
|
104
107
|
throw new Error('Could not sign transaction');
|
|
105
108
|
}
|
|
106
109
|
async generateTransactionMinimalSignatures(checkPartialSigs = false) {
|
|
107
|
-
if (this.to &&
|
|
110
|
+
if (this.to &&
|
|
111
|
+
!this.isPubKeyDestination &&
|
|
112
|
+
!EcKeyPair.verifyContractAddress(this.to, this.network)) {
|
|
108
113
|
throw new Error('Invalid contract address. The contract address must be a taproot address.');
|
|
109
114
|
}
|
|
110
115
|
await this.buildTransaction();
|
|
@@ -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(
|
|
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());
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Network } from 'bitcoinjs-lib';
|
|
2
2
|
import { Taptree } from 'bitcoinjs-lib/src/types.js';
|
|
3
3
|
export interface ContractAddressVerificationParams {
|
|
4
|
-
readonly
|
|
4
|
+
readonly deployerPubKey: Buffer;
|
|
5
5
|
readonly contractSaltPubKey: Buffer;
|
|
6
6
|
readonly originalSalt: Buffer;
|
|
7
7
|
readonly bytecode: Buffer;
|
|
@@ -6,7 +6,7 @@ import { AddressGenerator } from '../generators/AddressGenerator.js';
|
|
|
6
6
|
export class TapscriptVerificator {
|
|
7
7
|
static getContractAddress(params) {
|
|
8
8
|
const network = params.network || networks.bitcoin;
|
|
9
|
-
const scriptBuilder = new DeploymentGenerator(params.
|
|
9
|
+
const scriptBuilder = new DeploymentGenerator(params.deployerPubKey, toXOnly(params.contractSaltPubKey), network);
|
|
10
10
|
const compiledTargetScript = scriptBuilder.compile(params.bytecode, params.originalSalt, params.calldata);
|
|
11
11
|
const scriptTree = [
|
|
12
12
|
{
|
|
@@ -32,7 +32,7 @@ export class TapscriptVerificator {
|
|
|
32
32
|
static generateAddressFromScript(params, scriptTree) {
|
|
33
33
|
const network = params.network || networks.bitcoin;
|
|
34
34
|
const transactionData = {
|
|
35
|
-
internalPubkey: params.
|
|
35
|
+
internalPubkey: toXOnly(params.deployerPubKey),
|
|
36
36
|
network: network,
|
|
37
37
|
scriptTree: scriptTree,
|
|
38
38
|
};
|
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,4 +1,5 @@
|
|
|
1
1
|
import { Network, networks } from 'bitcoinjs-lib';
|
|
2
|
+
import { toXOnly } from 'bitcoinjs-lib/src/psbt/bip371.js';
|
|
2
3
|
|
|
3
4
|
/** Bitcoin Script Generator */
|
|
4
5
|
export abstract class Generator {
|
|
@@ -18,6 +19,12 @@ export abstract class Generator {
|
|
|
18
19
|
*/
|
|
19
20
|
protected readonly senderPubKey: Buffer;
|
|
20
21
|
|
|
22
|
+
/**
|
|
23
|
+
* The public key of the sender
|
|
24
|
+
* @protected
|
|
25
|
+
*/
|
|
26
|
+
protected readonly xSenderPubKey: Buffer;
|
|
27
|
+
|
|
21
28
|
/**
|
|
22
29
|
* The public key of the contract salt
|
|
23
30
|
* @protected
|
|
@@ -38,6 +45,11 @@ export abstract class Generator {
|
|
|
38
45
|
this.senderPubKey = senderPubKey;
|
|
39
46
|
this.contractSaltPubKey = contractSaltPubKey;
|
|
40
47
|
this.network = network;
|
|
48
|
+
this.xSenderPubKey = toXOnly(senderPubKey);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public get senderFirstByte(): Buffer {
|
|
52
|
+
return Buffer.from([this.senderPubKey[0], 0, 0, 0]);
|
|
41
53
|
}
|
|
42
54
|
|
|
43
55
|
/**
|
|
@@ -75,14 +75,17 @@ export class CalldataGenerator extends Generator {
|
|
|
75
75
|
if (!dataChunks.length) throw new Error('No data chunks found');
|
|
76
76
|
|
|
77
77
|
let compiledData = [
|
|
78
|
-
this.
|
|
78
|
+
this.senderFirstByte,
|
|
79
|
+
opcodes.OP_TOALTSTACK,
|
|
80
|
+
|
|
81
|
+
this.xSenderPubKey,
|
|
79
82
|
opcodes.OP_CHECKSIGVERIFY,
|
|
80
83
|
|
|
81
84
|
this.contractSaltPubKey,
|
|
82
85
|
opcodes.OP_CHECKSIGVERIFY,
|
|
83
86
|
|
|
84
87
|
opcodes.OP_HASH160,
|
|
85
|
-
crypto.hash160(this.
|
|
88
|
+
crypto.hash160(this.xSenderPubKey),
|
|
86
89
|
opcodes.OP_EQUALVERIFY,
|
|
87
90
|
|
|
88
91
|
opcodes.OP_HASH160,
|
|
@@ -96,7 +99,7 @@ export class CalldataGenerator extends Generator {
|
|
|
96
99
|
|
|
97
100
|
Generator.MAGIC,
|
|
98
101
|
];
|
|
99
|
-
|
|
102
|
+
|
|
100
103
|
// write pub keys, when requested.
|
|
101
104
|
if (vaultPublicKeys.length > 0) {
|
|
102
105
|
const pubKeyBuffer = CalldataGenerator.getPubKeyAsBuffer(vaultPublicKeys, this.network);
|
|
@@ -42,15 +42,18 @@ export class DeploymentGenerator extends Generator {
|
|
|
42
42
|
const dataChunks: Buffer[][] = this.splitBufferIntoChunks(contractBytecode);
|
|
43
43
|
const calldataChunks: Buffer[][] = calldata ? this.splitBufferIntoChunks(calldata) : [];
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
this.
|
|
45
|
+
const compiledData = [
|
|
46
|
+
this.senderFirstByte,
|
|
47
|
+
opcodes.OP_TOALTSTACK,
|
|
48
|
+
|
|
49
|
+
this.xSenderPubKey,
|
|
47
50
|
opcodes.OP_CHECKSIGVERIFY,
|
|
48
51
|
|
|
49
52
|
this.contractSaltPubKey,
|
|
50
53
|
opcodes.OP_CHECKSIGVERIFY,
|
|
51
54
|
|
|
52
55
|
opcodes.OP_HASH160,
|
|
53
|
-
crypto.hash160(this.
|
|
56
|
+
crypto.hash160(this.xSenderPubKey),
|
|
54
57
|
opcodes.OP_EQUALVERIFY,
|
|
55
58
|
|
|
56
59
|
opcodes.OP_HASH256,
|
|
@@ -71,6 +74,8 @@ export class DeploymentGenerator extends Generator {
|
|
|
71
74
|
opcodes.OP_ELSE,
|
|
72
75
|
opcodes.OP_1,
|
|
73
76
|
opcodes.OP_ENDIF,
|
|
74
|
-
]
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
return compiledData.flat();
|
|
75
80
|
}
|
|
76
81
|
}
|
|
@@ -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
|
}
|