@btc-vision/transaction 1.0.111 → 1.0.112
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/generators/Generator.d.ts +2 -0
- package/browser/index.js +1 -1
- package/browser/keypair/EcKeyPair.d.ts +2 -0
- package/browser/transaction/builders/TransactionBuilder.d.ts +1 -0
- package/browser/verification/TapscriptVerificator.d.ts +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/EcKeyPair.d.ts +2 -0
- package/build/keypair/EcKeyPair.js +17 -0
- package/build/transaction/builders/DeploymentTransaction.js +1 -1
- package/build/transaction/builders/FundingTransaction.js +18 -4
- package/build/transaction/builders/SharedInteractionTransaction.js +1 -1
- package/build/transaction/builders/TransactionBuilder.d.ts +1 -0
- package/build/transaction/builders/TransactionBuilder.js +7 -2
- package/build/transaction/builders/UnwrapTransaction.js +1 -1
- package/build/verification/TapscriptVerificator.d.ts +1 -1
- package/build/verification/TapscriptVerificator.js +2 -2
- package/package.json +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/EcKeyPair.ts +40 -1
- package/src/transaction/builders/DeploymentTransaction.ts +1 -1
- package/src/transaction/builders/FundingTransaction.ts +16 -4
- package/src/transaction/builders/SharedInteractionTransaction.ts +1 -1
- package/src/transaction/builders/TransactionBuilder.ts +18 -2
- package/src/transaction/builders/UnwrapTransaction.ts +1 -1
- package/src/verification/TapscriptVerificator.ts +3 -3
|
@@ -14,7 +14,9 @@ export declare class EcKeyPair {
|
|
|
14
14
|
static getP2WPKHAddress(keyPair: ECPairInterface, network?: Network): Address;
|
|
15
15
|
static generateWallet(network?: Network): IWallet;
|
|
16
16
|
static verifyContractAddress(contractAddress: Address, network?: Network): boolean;
|
|
17
|
+
static getLegacySegwitAddress(keyPair: ECPairInterface, network?: Network): Address;
|
|
17
18
|
static getLegacyAddress(keyPair: ECPairInterface, network?: Network): Address;
|
|
19
|
+
static getP2PKAddress(keyPair: ECPairInterface, network?: Network): Address;
|
|
18
20
|
static generateRandomKeyPair(network?: Network): ECPairInterface;
|
|
19
21
|
static fromSeed(seed: Buffer, network?: Network): BIP32Interface;
|
|
20
22
|
static getTaprootAddress(keyPair: ECPairInterface, network?: Network): Address;
|
|
@@ -28,6 +28,7 @@ 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
33
|
static getFrom(from: string | undefined, keypair: ECPairInterface, network: Network): Address;
|
|
33
34
|
static witnessStackToScriptWitness(witness: Buffer[]): Buffer;
|
|
@@ -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;
|
|
@@ -3,9 +3,11 @@ export declare abstract class Generator {
|
|
|
3
3
|
static readonly DATA_CHUNK_SIZE: number;
|
|
4
4
|
static readonly MAGIC: Buffer;
|
|
5
5
|
protected readonly senderPubKey: Buffer;
|
|
6
|
+
protected readonly xSenderPubKey: Buffer;
|
|
6
7
|
protected readonly contractSaltPubKey?: Buffer;
|
|
7
8
|
protected readonly network: Network;
|
|
8
9
|
protected constructor(senderPubKey: Buffer, contractSaltPubKey?: Buffer, network?: Network);
|
|
10
|
+
get senderFirstByte(): Buffer;
|
|
9
11
|
abstract compile(...args: unknown[]): Buffer;
|
|
10
12
|
protected splitBufferIntoChunks(buffer: Buffer, chunkSize?: number): Array<Buffer[]>;
|
|
11
13
|
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { networks } from 'bitcoinjs-lib';
|
|
2
|
+
import { toXOnly } from 'bitcoinjs-lib/src/psbt/bip371.js';
|
|
2
3
|
export class Generator {
|
|
3
4
|
constructor(senderPubKey, contractSaltPubKey, network = networks.bitcoin) {
|
|
4
5
|
this.network = networks.bitcoin;
|
|
5
6
|
this.senderPubKey = senderPubKey;
|
|
6
7
|
this.contractSaltPubKey = contractSaltPubKey;
|
|
7
8
|
this.network = network;
|
|
9
|
+
this.xSenderPubKey = toXOnly(senderPubKey);
|
|
10
|
+
}
|
|
11
|
+
get senderFirstByte() {
|
|
12
|
+
return Buffer.from([this.senderPubKey[0], 0, 0, 0]);
|
|
8
13
|
}
|
|
9
14
|
splitBufferIntoChunks(buffer, chunkSize = Generator.DATA_CHUNK_SIZE) {
|
|
10
15
|
const chunks = [];
|
|
@@ -32,12 +32,14 @@ export class CalldataGenerator extends Generator {
|
|
|
32
32
|
if (!dataChunks.length)
|
|
33
33
|
throw new Error('No data chunks found');
|
|
34
34
|
let compiledData = [
|
|
35
|
-
this.
|
|
35
|
+
this.senderFirstByte,
|
|
36
|
+
opcodes.OP_TOALTSTACK,
|
|
37
|
+
this.xSenderPubKey,
|
|
36
38
|
opcodes.OP_CHECKSIGVERIFY,
|
|
37
39
|
this.contractSaltPubKey,
|
|
38
40
|
opcodes.OP_CHECKSIGVERIFY,
|
|
39
41
|
opcodes.OP_HASH160,
|
|
40
|
-
crypto.hash160(this.
|
|
42
|
+
crypto.hash160(this.xSenderPubKey),
|
|
41
43
|
opcodes.OP_EQUALVERIFY,
|
|
42
44
|
opcodes.OP_HASH160,
|
|
43
45
|
crypto.hash160(contractSecret),
|
|
@@ -18,13 +18,15 @@ export class DeploymentGenerator extends Generator {
|
|
|
18
18
|
throw new Error('Contract salt public key not set');
|
|
19
19
|
const dataChunks = this.splitBufferIntoChunks(contractBytecode);
|
|
20
20
|
const calldataChunks = calldata ? this.splitBufferIntoChunks(calldata) : [];
|
|
21
|
-
|
|
22
|
-
this.
|
|
21
|
+
const compiledData = [
|
|
22
|
+
this.senderFirstByte,
|
|
23
|
+
opcodes.OP_TOALTSTACK,
|
|
24
|
+
this.xSenderPubKey,
|
|
23
25
|
opcodes.OP_CHECKSIGVERIFY,
|
|
24
26
|
this.contractSaltPubKey,
|
|
25
27
|
opcodes.OP_CHECKSIGVERIFY,
|
|
26
28
|
opcodes.OP_HASH160,
|
|
27
|
-
crypto.hash160(this.
|
|
29
|
+
crypto.hash160(this.xSenderPubKey),
|
|
28
30
|
opcodes.OP_EQUALVERIFY,
|
|
29
31
|
opcodes.OP_HASH256,
|
|
30
32
|
crypto.hash256(contractSalt),
|
|
@@ -41,6 +43,7 @@ export class DeploymentGenerator extends Generator {
|
|
|
41
43
|
opcodes.OP_ELSE,
|
|
42
44
|
opcodes.OP_1,
|
|
43
45
|
opcodes.OP_ENDIF,
|
|
44
|
-
]
|
|
46
|
+
];
|
|
47
|
+
return compiledData.flat();
|
|
45
48
|
}
|
|
46
49
|
}
|
|
@@ -14,7 +14,9 @@ export declare class EcKeyPair {
|
|
|
14
14
|
static getP2WPKHAddress(keyPair: ECPairInterface, network?: Network): Address;
|
|
15
15
|
static generateWallet(network?: Network): IWallet;
|
|
16
16
|
static verifyContractAddress(contractAddress: Address, network?: Network): boolean;
|
|
17
|
+
static getLegacySegwitAddress(keyPair: ECPairInterface, network?: Network): Address;
|
|
17
18
|
static getLegacyAddress(keyPair: ECPairInterface, network?: Network): Address;
|
|
19
|
+
static getP2PKAddress(keyPair: ECPairInterface, network?: Network): Address;
|
|
18
20
|
static generateRandomKeyPair(network?: Network): ECPairInterface;
|
|
19
21
|
static fromSeed(seed: Buffer, network?: Network): BIP32Interface;
|
|
20
22
|
static getTaprootAddress(keyPair: ECPairInterface, network?: Network): Address;
|
|
@@ -67,6 +67,16 @@ export class EcKeyPair {
|
|
|
67
67
|
static verifyContractAddress(contractAddress, network = networks.bitcoin) {
|
|
68
68
|
return !!address.toOutputScript(contractAddress, network);
|
|
69
69
|
}
|
|
70
|
+
static getLegacySegwitAddress(keyPair, network = networks.bitcoin) {
|
|
71
|
+
const wallet = payments.p2sh({
|
|
72
|
+
redeem: payments.p2wpkh({ pubkey: keyPair.publicKey, network: network }),
|
|
73
|
+
network: network,
|
|
74
|
+
});
|
|
75
|
+
if (!wallet.address) {
|
|
76
|
+
throw new Error('Failed to generate wallet');
|
|
77
|
+
}
|
|
78
|
+
return wallet.address;
|
|
79
|
+
}
|
|
70
80
|
static getLegacyAddress(keyPair, network = networks.bitcoin) {
|
|
71
81
|
const wallet = payments.p2pkh({ pubkey: keyPair.publicKey, network: network });
|
|
72
82
|
if (!wallet.address) {
|
|
@@ -74,6 +84,13 @@ export class EcKeyPair {
|
|
|
74
84
|
}
|
|
75
85
|
return wallet.address;
|
|
76
86
|
}
|
|
87
|
+
static getP2PKAddress(keyPair, network = networks.bitcoin) {
|
|
88
|
+
const wallet = payments.p2pk({ pubkey: keyPair.publicKey, network: network });
|
|
89
|
+
if (!wallet.output) {
|
|
90
|
+
throw new Error('Failed to generate wallet');
|
|
91
|
+
}
|
|
92
|
+
return '0x' + wallet.output.toString('hex');
|
|
93
|
+
}
|
|
77
94
|
static generateRandomKeyPair(network = networks.bitcoin) {
|
|
78
95
|
return this.ECPair.makeRandom({
|
|
79
96
|
network: network,
|
|
@@ -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(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();
|
|
@@ -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() {
|
|
@@ -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(this.signer.publicKey, this.scriptSignerXOnlyPubKey(), this.network);
|
|
36
36
|
}
|
|
37
37
|
getContractSecret() {
|
|
38
38
|
return this.contractSecret;
|
|
@@ -28,6 +28,7 @@ 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
33
|
static getFrom(from: string | undefined, keypair: ECPairInterface, network: Network): Address;
|
|
33
34
|
static witnessStackToScriptWitness(witness: Buffer[]): Buffer;
|
|
@@ -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(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();
|
|
@@ -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,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
|
}
|
package/src/keypair/EcKeyPair.ts
CHANGED
|
@@ -181,6 +181,28 @@ export class EcKeyPair {
|
|
|
181
181
|
return !!address.toOutputScript(contractAddress, network);
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
+
/**
|
|
185
|
+
* Get the legacy segwit address from a keypair
|
|
186
|
+
* @param {ECPairInterface} keyPair - The keypair to get the address for
|
|
187
|
+
* @param {Network} network - The network to use
|
|
188
|
+
* @returns {Address} - The legacy address
|
|
189
|
+
*/
|
|
190
|
+
public static getLegacySegwitAddress(
|
|
191
|
+
keyPair: ECPairInterface,
|
|
192
|
+
network: Network = networks.bitcoin,
|
|
193
|
+
): Address {
|
|
194
|
+
const wallet = payments.p2sh({
|
|
195
|
+
redeem: payments.p2wpkh({ pubkey: keyPair.publicKey, network: network }),
|
|
196
|
+
network: network,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
if (!wallet.address) {
|
|
200
|
+
throw new Error('Failed to generate wallet');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return wallet.address;
|
|
204
|
+
}
|
|
205
|
+
|
|
184
206
|
/**
|
|
185
207
|
* Get the legacy address from a keypair
|
|
186
208
|
* @param {ECPairInterface} keyPair - The keypair to get the address for
|
|
@@ -192,7 +214,6 @@ export class EcKeyPair {
|
|
|
192
214
|
network: Network = networks.bitcoin,
|
|
193
215
|
): Address {
|
|
194
216
|
const wallet = payments.p2pkh({ pubkey: keyPair.publicKey, network: network });
|
|
195
|
-
|
|
196
217
|
if (!wallet.address) {
|
|
197
218
|
throw new Error('Failed to generate wallet');
|
|
198
219
|
}
|
|
@@ -200,6 +221,24 @@ export class EcKeyPair {
|
|
|
200
221
|
return wallet.address;
|
|
201
222
|
}
|
|
202
223
|
|
|
224
|
+
/**
|
|
225
|
+
* Get the legacy address from a keypair
|
|
226
|
+
* @param {ECPairInterface} keyPair - The keypair to get the address for
|
|
227
|
+
* @param {Network} network - The network to use
|
|
228
|
+
* @returns {Address} - The legacy address
|
|
229
|
+
*/
|
|
230
|
+
public static getP2PKAddress(
|
|
231
|
+
keyPair: ECPairInterface,
|
|
232
|
+
network: Network = networks.bitcoin,
|
|
233
|
+
): Address {
|
|
234
|
+
const wallet = payments.p2pk({ pubkey: keyPair.publicKey, network: network });
|
|
235
|
+
if (!wallet.output) {
|
|
236
|
+
throw new Error('Failed to generate wallet');
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return '0x' + wallet.output.toString('hex');
|
|
240
|
+
}
|
|
241
|
+
|
|
203
242
|
/**
|
|
204
243
|
* Generate a random keypair
|
|
205
244
|
* @param {Network} network - The network to use
|
|
@@ -100,7 +100,7 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
|
|
|
100
100
|
this.contractSigner = EcKeyPair.fromSeedKeyPair(this.contractSeed, this.network);
|
|
101
101
|
|
|
102
102
|
this.deploymentGenerator = new DeploymentGenerator(
|
|
103
|
-
this.
|
|
103
|
+
this.signer.publicKey,
|
|
104
104
|
this.contractSignerXOnlyPubKey(),
|
|
105
105
|
this.network,
|
|
106
106
|
);
|
|
@@ -27,6 +27,11 @@ export class FundingTransaction extends TransactionBuilder<TransactionType.FUNDI
|
|
|
27
27
|
|
|
28
28
|
if (this.splitInputsInto > 1) {
|
|
29
29
|
this.splitInputs(this.amount);
|
|
30
|
+
} else if (this.isPubKeyDestination) {
|
|
31
|
+
this.addOutput({
|
|
32
|
+
value: Number(this.amount),
|
|
33
|
+
script: Buffer.from(this.to.slice(2), 'hex'),
|
|
34
|
+
});
|
|
30
35
|
} else {
|
|
31
36
|
this.addOutput({
|
|
32
37
|
value: Number(this.amount),
|
|
@@ -45,10 +50,17 @@ export class FundingTransaction extends TransactionBuilder<TransactionType.FUNDI
|
|
|
45
50
|
const splitAmount = amountSpent / BigInt(this.splitInputsInto);
|
|
46
51
|
|
|
47
52
|
for (let i = 0; i < this.splitInputsInto; i++) {
|
|
48
|
-
this.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
53
|
+
if (this.isPubKeyDestination) {
|
|
54
|
+
this.addOutput({
|
|
55
|
+
value: Number(splitAmount),
|
|
56
|
+
script: Buffer.from(this.to.slice(2), 'hex'),
|
|
57
|
+
});
|
|
58
|
+
} else {
|
|
59
|
+
this.addOutput({
|
|
60
|
+
value: Number(splitAmount),
|
|
61
|
+
address: this.to,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
52
64
|
}
|
|
53
65
|
}
|
|
54
66
|
|
|
@@ -73,7 +73,7 @@ export abstract class SharedInteractionTransaction<
|
|
|
73
73
|
this.scriptSigner = this.generateKeyPairFromSeed();
|
|
74
74
|
|
|
75
75
|
this.calldataGenerator = new CalldataGenerator(
|
|
76
|
-
this.
|
|
76
|
+
this.signer.publicKey,
|
|
77
77
|
this.scriptSignerXOnlyPubKey(),
|
|
78
78
|
this.network,
|
|
79
79
|
);
|
|
@@ -107,6 +107,12 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
|
|
|
107
107
|
*/
|
|
108
108
|
protected _maximumFeeRate: number = 100000000;
|
|
109
109
|
|
|
110
|
+
/**
|
|
111
|
+
* @description Is the destionation P2PK
|
|
112
|
+
* @protected
|
|
113
|
+
*/
|
|
114
|
+
protected isPubKeyDestination: boolean;
|
|
115
|
+
|
|
110
116
|
protected constructor(parameters: ITransactionParameters) {
|
|
111
117
|
super(parameters);
|
|
112
118
|
|
|
@@ -121,6 +127,8 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
|
|
|
121
127
|
this.utxos = parameters.utxos;
|
|
122
128
|
this.to = parameters.to || undefined;
|
|
123
129
|
|
|
130
|
+
this.isPubKeyDestination = this.to ? this.to.startsWith('0x') : false;
|
|
131
|
+
|
|
124
132
|
this.optionalOutputs = parameters.optionalOutputs;
|
|
125
133
|
|
|
126
134
|
this.from = TransactionBuilder.getFrom(
|
|
@@ -231,7 +239,11 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
|
|
|
231
239
|
throw new Error('No UTXOs specified');
|
|
232
240
|
}
|
|
233
241
|
|
|
234
|
-
if (
|
|
242
|
+
if (
|
|
243
|
+
this.to &&
|
|
244
|
+
!this.isPubKeyDestination &&
|
|
245
|
+
!EcKeyPair.verifyContractAddress(this.to, this.network)
|
|
246
|
+
) {
|
|
235
247
|
throw new Error(
|
|
236
248
|
'Invalid contract address. The contract address must be a taproot address.',
|
|
237
249
|
);
|
|
@@ -261,7 +273,11 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
|
|
|
261
273
|
public async generateTransactionMinimalSignatures(
|
|
262
274
|
checkPartialSigs: boolean = false,
|
|
263
275
|
): Promise<void> {
|
|
264
|
-
if (
|
|
276
|
+
if (
|
|
277
|
+
this.to &&
|
|
278
|
+
!this.isPubKeyDestination &&
|
|
279
|
+
!EcKeyPair.verifyContractAddress(this.to, this.network)
|
|
280
|
+
) {
|
|
265
281
|
throw new Error(
|
|
266
282
|
'Invalid contract address. The contract address must be a taproot address.',
|
|
267
283
|
);
|
|
@@ -109,7 +109,7 @@ export class UnwrapTransaction extends SharedInteractionTransaction<TransactionT
|
|
|
109
109
|
this.contractSecret = this.generateSecret();
|
|
110
110
|
|
|
111
111
|
this.calldataGenerator = new CalldataGenerator(
|
|
112
|
-
|
|
112
|
+
this.signer.publicKey,
|
|
113
113
|
this.scriptSignerXOnlyPubKey(),
|
|
114
114
|
this.network,
|
|
115
115
|
);
|
|
@@ -6,7 +6,7 @@ import { TransactionBuilder } from '../transaction/builders/TransactionBuilder.j
|
|
|
6
6
|
import { AddressGenerator } from '../generators/AddressGenerator.js';
|
|
7
7
|
|
|
8
8
|
export interface ContractAddressVerificationParams {
|
|
9
|
-
readonly
|
|
9
|
+
readonly deployerPubKey: Buffer;
|
|
10
10
|
readonly contractSaltPubKey: Buffer;
|
|
11
11
|
readonly originalSalt: Buffer;
|
|
12
12
|
readonly bytecode: Buffer;
|
|
@@ -22,7 +22,7 @@ export class TapscriptVerificator {
|
|
|
22
22
|
): string | undefined {
|
|
23
23
|
const network = params.network || networks.bitcoin;
|
|
24
24
|
const scriptBuilder: DeploymentGenerator = new DeploymentGenerator(
|
|
25
|
-
params.
|
|
25
|
+
params.deployerPubKey,
|
|
26
26
|
toXOnly(params.contractSaltPubKey),
|
|
27
27
|
network,
|
|
28
28
|
);
|
|
@@ -80,7 +80,7 @@ export class TapscriptVerificator {
|
|
|
80
80
|
const network = params.network || networks.bitcoin;
|
|
81
81
|
|
|
82
82
|
const transactionData: Payment = {
|
|
83
|
-
internalPubkey: params.
|
|
83
|
+
internalPubkey: toXOnly(params.deployerPubKey),
|
|
84
84
|
network: network,
|
|
85
85
|
scriptTree: scriptTree,
|
|
86
86
|
};
|