@btc-vision/transaction 1.0.110 → 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/_version.d.ts +1 -1
- package/browser/generators/Generator.d.ts +2 -0
- package/browser/generators/builders/DeploymentGenerator.d.ts +1 -1
- package/browser/index.js +1 -1
- package/browser/keypair/EcKeyPair.d.ts +2 -0
- package/browser/transaction/builders/DeploymentTransaction.d.ts +3 -0
- package/browser/transaction/builders/SharedInteractionTransaction.d.ts +1 -0
- package/browser/transaction/builders/TransactionBuilder.d.ts +1 -0
- package/browser/transaction/interfaces/ITransactionParameters.d.ts +1 -0
- package/browser/verification/TapscriptVerificator.d.ts +2 -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.d.ts +1 -1
- package/build/generators/builders/DeploymentGenerator.js +13 -7
- package/build/keypair/EcKeyPair.d.ts +2 -0
- package/build/keypair/EcKeyPair.js +17 -0
- package/build/transaction/builders/DeploymentTransaction.d.ts +3 -0
- package/build/transaction/builders/DeploymentTransaction.js +21 -6
- package/build/transaction/builders/FundingTransaction.js +18 -4
- package/build/transaction/builders/SharedInteractionTransaction.d.ts +1 -0
- package/build/transaction/builders/SharedInteractionTransaction.js +2 -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/transaction/interfaces/ITransactionParameters.d.ts +1 -0
- package/build/verification/TapscriptVerificator.d.ts +2 -1
- package/build/verification/TapscriptVerificator.js +3 -3
- package/package.json +1 -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 +21 -8
- package/src/keypair/EcKeyPair.ts +40 -1
- package/src/transaction/builders/DeploymentTransaction.ts +31 -4
- package/src/transaction/builders/FundingTransaction.ts +16 -4
- package/src/transaction/builders/SharedInteractionTransaction.ts +3 -1
- package/src/transaction/builders/TransactionBuilder.ts +18 -2
- package/src/transaction/builders/UnwrapTransaction.ts +1 -1
- package/src/transaction/interfaces/ITransactionParameters.ts +2 -0
- package/src/verification/TapscriptVerificator.ts +5 -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;
|
|
@@ -16,6 +16,7 @@ export declare class DeploymentTransaction extends TransactionBuilder<Transactio
|
|
|
16
16
|
private deploymentGenerator;
|
|
17
17
|
private readonly contractSeed;
|
|
18
18
|
private readonly bytecode;
|
|
19
|
+
private readonly calldata?;
|
|
19
20
|
private readonly contractSigner;
|
|
20
21
|
private readonly randomBytes;
|
|
21
22
|
constructor(parameters: IDeploymentParameters);
|
|
@@ -27,6 +28,8 @@ export declare class DeploymentTransaction extends TransactionBuilder<Transactio
|
|
|
27
28
|
protected signInputs(transaction: Psbt): Promise<void>;
|
|
28
29
|
protected generateScriptAddress(): Payment;
|
|
29
30
|
protected generateTapData(): Payment;
|
|
31
|
+
private verifyCalldata;
|
|
32
|
+
private verifyBytecode;
|
|
30
33
|
private getContractSeed;
|
|
31
34
|
private customFinalizer;
|
|
32
35
|
private getPubKeys;
|
|
@@ -7,6 +7,7 @@ import { TransactionType } from '../enums/TransactionType.js';
|
|
|
7
7
|
import { CalldataGenerator } from '../../generators/builders/CalldataGenerator.js';
|
|
8
8
|
import { SharedInteractionParameters } from '../interfaces/ITransactionParameters.js';
|
|
9
9
|
export declare abstract class SharedInteractionTransaction<T extends TransactionType> extends TransactionBuilder<T> {
|
|
10
|
+
static readonly MAXIMUM_CALLDATA_SIZE: number;
|
|
10
11
|
readonly randomBytes: Buffer;
|
|
11
12
|
protected targetScriptRedeem: Payment | null;
|
|
12
13
|
protected leftOverFundsScriptRedeem: Payment | null;
|
|
@@ -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;
|
|
@@ -42,5 +42,6 @@ export interface IUnwrapParameters extends Omit<SharedInteractionParameters, 'op
|
|
|
42
42
|
}
|
|
43
43
|
export interface IDeploymentParameters extends Omit<ITransactionParameters, 'to'> {
|
|
44
44
|
readonly bytecode: Buffer;
|
|
45
|
+
readonly calldata?: Buffer;
|
|
45
46
|
readonly randomBytes?: Buffer;
|
|
46
47
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
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;
|
|
8
|
+
readonly calldata?: Buffer;
|
|
8
9
|
readonly network?: Network;
|
|
9
10
|
}
|
|
10
11
|
export declare class TapscriptVerificator {
|
package/build/_version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "1.0.
|
|
1
|
+
export declare const version = "1.0.111";
|
package/build/_version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.0.
|
|
1
|
+
export const version = '1.0.111';
|
|
@@ -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),
|
|
@@ -2,6 +2,6 @@ import { Network } from 'bitcoinjs-lib';
|
|
|
2
2
|
import { Generator } from '../Generator.js';
|
|
3
3
|
export declare class DeploymentGenerator extends Generator {
|
|
4
4
|
constructor(senderPubKey: Buffer, contractSaltPubKey: Buffer, network?: Network);
|
|
5
|
-
compile(contractBytecode: Buffer, contractSalt: Buffer): Buffer;
|
|
5
|
+
compile(contractBytecode: Buffer, contractSalt: Buffer, calldata?: Buffer): Buffer;
|
|
6
6
|
private getAsm;
|
|
7
7
|
}
|
|
@@ -4,8 +4,8 @@ export class DeploymentGenerator extends Generator {
|
|
|
4
4
|
constructor(senderPubKey, contractSaltPubKey, network = networks.bitcoin) {
|
|
5
5
|
super(senderPubKey, contractSaltPubKey, network);
|
|
6
6
|
}
|
|
7
|
-
compile(contractBytecode, contractSalt) {
|
|
8
|
-
const asm = this.getAsm(contractBytecode, contractSalt);
|
|
7
|
+
compile(contractBytecode, contractSalt, calldata) {
|
|
8
|
+
const asm = this.getAsm(contractBytecode, contractSalt, calldata);
|
|
9
9
|
const compiled = script.compile(asm);
|
|
10
10
|
const decompiled = script.decompile(compiled);
|
|
11
11
|
if (!decompiled) {
|
|
@@ -13,17 +13,20 @@ export class DeploymentGenerator extends Generator {
|
|
|
13
13
|
}
|
|
14
14
|
return compiled;
|
|
15
15
|
}
|
|
16
|
-
getAsm(contractBytecode, contractSalt) {
|
|
16
|
+
getAsm(contractBytecode, contractSalt, calldata) {
|
|
17
17
|
if (!this.contractSaltPubKey)
|
|
18
18
|
throw new Error('Contract salt public key not set');
|
|
19
19
|
const dataChunks = this.splitBufferIntoChunks(contractBytecode);
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
const calldataChunks = calldata ? this.splitBufferIntoChunks(calldata) : [];
|
|
21
|
+
const compiledData = [
|
|
22
|
+
this.senderFirstByte,
|
|
23
|
+
opcodes.OP_TOALTSTACK,
|
|
24
|
+
this.xSenderPubKey,
|
|
22
25
|
opcodes.OP_CHECKSIGVERIFY,
|
|
23
26
|
this.contractSaltPubKey,
|
|
24
27
|
opcodes.OP_CHECKSIGVERIFY,
|
|
25
28
|
opcodes.OP_HASH160,
|
|
26
|
-
crypto.hash160(this.
|
|
29
|
+
crypto.hash160(this.xSenderPubKey),
|
|
27
30
|
opcodes.OP_EQUALVERIFY,
|
|
28
31
|
opcodes.OP_HASH256,
|
|
29
32
|
crypto.hash256(contractSalt),
|
|
@@ -33,11 +36,14 @@ export class DeploymentGenerator extends Generator {
|
|
|
33
36
|
opcodes.OP_NUMEQUAL,
|
|
34
37
|
opcodes.OP_IF,
|
|
35
38
|
Generator.MAGIC,
|
|
39
|
+
opcodes.OP_0,
|
|
40
|
+
...calldataChunks,
|
|
36
41
|
opcodes.OP_1NEGATE,
|
|
37
42
|
...dataChunks,
|
|
38
43
|
opcodes.OP_ELSE,
|
|
39
44
|
opcodes.OP_1,
|
|
40
45
|
opcodes.OP_ENDIF,
|
|
41
|
-
]
|
|
46
|
+
];
|
|
47
|
+
return compiledData.flat();
|
|
42
48
|
}
|
|
43
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,
|
|
@@ -16,6 +16,7 @@ export declare class DeploymentTransaction extends TransactionBuilder<Transactio
|
|
|
16
16
|
private deploymentGenerator;
|
|
17
17
|
private readonly contractSeed;
|
|
18
18
|
private readonly bytecode;
|
|
19
|
+
private readonly calldata?;
|
|
19
20
|
private readonly contractSigner;
|
|
20
21
|
private readonly randomBytes;
|
|
21
22
|
constructor(parameters: IDeploymentParameters);
|
|
@@ -27,6 +28,8 @@ export declare class DeploymentTransaction extends TransactionBuilder<Transactio
|
|
|
27
28
|
protected signInputs(transaction: Psbt): Promise<void>;
|
|
28
29
|
protected generateScriptAddress(): Payment;
|
|
29
30
|
protected generateTapData(): Payment;
|
|
31
|
+
private verifyCalldata;
|
|
32
|
+
private verifyBytecode;
|
|
30
33
|
private getContractSeed;
|
|
31
34
|
private customFinalizer;
|
|
32
35
|
private getPubKeys;
|
|
@@ -7,6 +7,7 @@ import { EcKeyPair } from '../../keypair/EcKeyPair.js';
|
|
|
7
7
|
import { BitcoinUtils } from '../../utils/BitcoinUtils.js';
|
|
8
8
|
import { Compressor } from '../../bytecode/Compressor.js';
|
|
9
9
|
import { AddressGenerator } from '../../generators/AddressGenerator.js';
|
|
10
|
+
import { SharedInteractionTransaction } from './SharedInteractionTransaction.js';
|
|
10
11
|
export class DeploymentTransaction extends TransactionBuilder {
|
|
11
12
|
constructor(parameters) {
|
|
12
13
|
super(parameters);
|
|
@@ -35,15 +36,16 @@ export class DeploymentTransaction extends TransactionBuilder {
|
|
|
35
36
|
};
|
|
36
37
|
};
|
|
37
38
|
this.bytecode = Compressor.compress(parameters.bytecode);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
this.verifyBytecode();
|
|
40
|
+
if (parameters.calldata) {
|
|
41
|
+
this.calldata = parameters.calldata;
|
|
42
|
+
this.verifyCalldata();
|
|
43
|
+
}
|
|
42
44
|
this.randomBytes = parameters.randomBytes || BitcoinUtils.rndBytes();
|
|
43
45
|
this.contractSeed = this.getContractSeed();
|
|
44
46
|
this.contractSigner = EcKeyPair.fromSeedKeyPair(this.contractSeed, this.network);
|
|
45
|
-
this.deploymentGenerator = new DeploymentGenerator(this.
|
|
46
|
-
this.compiledTargetScript = this.deploymentGenerator.compile(this.bytecode, this.randomBytes);
|
|
47
|
+
this.deploymentGenerator = new DeploymentGenerator(this.signer.publicKey, this.contractSignerXOnlyPubKey(), this.network);
|
|
48
|
+
this.compiledTargetScript = this.deploymentGenerator.compile(this.bytecode, this.randomBytes, this.calldata);
|
|
47
49
|
this.scriptTree = this.getScriptTree();
|
|
48
50
|
this.internalInit();
|
|
49
51
|
this._contractAddress = AddressGenerator.generatePKSH(this.contractSeed, this.network);
|
|
@@ -130,6 +132,19 @@ export class DeploymentTransaction extends TransactionBuilder {
|
|
|
130
132
|
redeem: selectedRedeem,
|
|
131
133
|
};
|
|
132
134
|
}
|
|
135
|
+
verifyCalldata() {
|
|
136
|
+
if (this.calldata &&
|
|
137
|
+
this.calldata.length > SharedInteractionTransaction.MAXIMUM_CALLDATA_SIZE) {
|
|
138
|
+
throw new Error('Calldata size overflow.');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
verifyBytecode() {
|
|
142
|
+
if (!this.bytecode)
|
|
143
|
+
throw new Error('Bytecode is required');
|
|
144
|
+
if (this.bytecode.length > DeploymentTransaction.MAXIMUM_CONTRACT_SIZE) {
|
|
145
|
+
throw new Error('Contract size overflow.');
|
|
146
|
+
}
|
|
147
|
+
}
|
|
133
148
|
getContractSeed() {
|
|
134
149
|
if (!this.bytecode) {
|
|
135
150
|
throw new Error('Bytecode is required');
|
|
@@ -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() {
|
|
@@ -7,6 +7,7 @@ import { TransactionType } from '../enums/TransactionType.js';
|
|
|
7
7
|
import { CalldataGenerator } from '../../generators/builders/CalldataGenerator.js';
|
|
8
8
|
import { SharedInteractionParameters } from '../interfaces/ITransactionParameters.js';
|
|
9
9
|
export declare abstract class SharedInteractionTransaction<T extends TransactionType> extends TransactionBuilder<T> {
|
|
10
|
+
static readonly MAXIMUM_CALLDATA_SIZE: number;
|
|
10
11
|
readonly randomBytes: Buffer;
|
|
11
12
|
protected targetScriptRedeem: Payment | null;
|
|
12
13
|
protected leftOverFundsScriptRedeem: Payment | null;
|
|
@@ -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;
|
|
@@ -205,3 +205,4 @@ export class SharedInteractionTransaction extends TransactionBuilder {
|
|
|
205
205
|
};
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
|
+
SharedInteractionTransaction.MAXIMUM_CALLDATA_SIZE = 1024 * 1024;
|
|
@@ -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();
|
|
@@ -42,5 +42,6 @@ export interface IUnwrapParameters extends Omit<SharedInteractionParameters, 'op
|
|
|
42
42
|
}
|
|
43
43
|
export interface IDeploymentParameters extends Omit<ITransactionParameters, 'to'> {
|
|
44
44
|
readonly bytecode: Buffer;
|
|
45
|
+
readonly calldata?: Buffer;
|
|
45
46
|
readonly randomBytes?: Buffer;
|
|
46
47
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
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;
|
|
8
|
+
readonly calldata?: Buffer;
|
|
8
9
|
readonly network?: Network;
|
|
9
10
|
}
|
|
10
11
|
export declare class TapscriptVerificator {
|
|
@@ -6,8 +6,8 @@ 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.
|
|
10
|
-
const compiledTargetScript = scriptBuilder.compile(params.bytecode, params.originalSalt);
|
|
9
|
+
const scriptBuilder = new DeploymentGenerator(params.deployerPubKey, toXOnly(params.contractSaltPubKey), network);
|
|
10
|
+
const compiledTargetScript = scriptBuilder.compile(params.bytecode, params.originalSalt, params.calldata);
|
|
11
11
|
const scriptTree = [
|
|
12
12
|
{
|
|
13
13
|
output: compiledTargetScript,
|
|
@@ -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
package/src/_version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.0.
|
|
1
|
+
export const version = '1.0.111';
|
|
@@ -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);
|
|
@@ -14,10 +14,11 @@ export class DeploymentGenerator extends Generator {
|
|
|
14
14
|
* Compile a bitcoin script representing a contract deployment
|
|
15
15
|
* @param {Buffer} contractBytecode - The contract bytecode
|
|
16
16
|
* @param {Buffer} contractSalt - The contract salt
|
|
17
|
+
* @param {Buffer} [calldata] - The calldata to be passed to the contract
|
|
17
18
|
* @returns {Buffer} - The compiled script
|
|
18
19
|
*/
|
|
19
|
-
public compile(contractBytecode: Buffer, contractSalt: Buffer): Buffer {
|
|
20
|
-
const asm = this.getAsm(contractBytecode, contractSalt);
|
|
20
|
+
public compile(contractBytecode: Buffer, contractSalt: Buffer, calldata?: Buffer): Buffer {
|
|
21
|
+
const asm = this.getAsm(contractBytecode, contractSalt, calldata);
|
|
21
22
|
const compiled = script.compile(asm);
|
|
22
23
|
|
|
23
24
|
/**
|
|
@@ -31,20 +32,28 @@ export class DeploymentGenerator extends Generator {
|
|
|
31
32
|
return compiled;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
private getAsm(
|
|
35
|
+
private getAsm(
|
|
36
|
+
contractBytecode: Buffer,
|
|
37
|
+
contractSalt: Buffer,
|
|
38
|
+
calldata?: Buffer,
|
|
39
|
+
): (number | Buffer)[] {
|
|
35
40
|
if (!this.contractSaltPubKey) throw new Error('Contract salt public key not set');
|
|
36
41
|
|
|
37
|
-
const dataChunks = this.splitBufferIntoChunks(contractBytecode);
|
|
42
|
+
const dataChunks: Buffer[][] = this.splitBufferIntoChunks(contractBytecode);
|
|
43
|
+
const calldataChunks: Buffer[][] = calldata ? this.splitBufferIntoChunks(calldata) : [];
|
|
38
44
|
|
|
39
|
-
|
|
40
|
-
this.
|
|
45
|
+
const compiledData = [
|
|
46
|
+
this.senderFirstByte,
|
|
47
|
+
opcodes.OP_TOALTSTACK,
|
|
48
|
+
|
|
49
|
+
this.xSenderPubKey,
|
|
41
50
|
opcodes.OP_CHECKSIGVERIFY,
|
|
42
51
|
|
|
43
52
|
this.contractSaltPubKey,
|
|
44
53
|
opcodes.OP_CHECKSIGVERIFY,
|
|
45
54
|
|
|
46
55
|
opcodes.OP_HASH160,
|
|
47
|
-
crypto.hash160(this.
|
|
56
|
+
crypto.hash160(this.xSenderPubKey),
|
|
48
57
|
opcodes.OP_EQUALVERIFY,
|
|
49
58
|
|
|
50
59
|
opcodes.OP_HASH256,
|
|
@@ -57,12 +66,16 @@ export class DeploymentGenerator extends Generator {
|
|
|
57
66
|
opcodes.OP_IF,
|
|
58
67
|
|
|
59
68
|
Generator.MAGIC,
|
|
69
|
+
opcodes.OP_0,
|
|
70
|
+
...calldataChunks,
|
|
60
71
|
opcodes.OP_1NEGATE,
|
|
61
72
|
...dataChunks,
|
|
62
73
|
|
|
63
74
|
opcodes.OP_ELSE,
|
|
64
75
|
opcodes.OP_1,
|
|
65
76
|
opcodes.OP_ENDIF,
|
|
66
|
-
]
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
return compiledData.flat();
|
|
67
80
|
}
|
|
68
81
|
}
|