@btc-vision/transaction 1.4.0 → 1.5.1
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/buffer/BinaryReader.d.ts +1 -1
- package/browser/generators/builders/DeploymentGenerator.d.ts +2 -0
- package/browser/index.js +1 -1
- package/browser/index.js.LICENSE.txt +1 -3
- package/browser/keypair/Address.d.ts +1 -0
- package/browser/keypair/AddressVerificator.d.ts +1 -0
- package/browser/keypair/EcKeyPair.d.ts +4 -1
- package/browser/transaction/TransactionFactory.d.ts +0 -1
- package/browser/transaction/browser/Web3Provider.d.ts +2 -0
- package/browser/transaction/builders/CustomScriptTransaction.d.ts +9 -6
- package/browser/transaction/builders/DeploymentTransaction.d.ts +6 -3
- package/browser/transaction/builders/MultiSignTransaction.d.ts +5 -5
- package/browser/transaction/builders/SharedInteractionTransaction.d.ts +5 -5
- package/browser/transaction/shared/TweakedTransaction.d.ts +5 -5
- package/build/buffer/BinaryReader.d.ts +1 -1
- package/build/buffer/BinaryReader.js +1 -1
- package/build/buffer/BinaryWriter.js +2 -2
- package/build/generators/builders/DeploymentGenerator.d.ts +2 -0
- package/build/generators/builders/DeploymentGenerator.js +2 -0
- package/build/keypair/Address.d.ts +1 -0
- package/build/keypair/Address.js +15 -2
- package/build/keypair/AddressVerificator.d.ts +1 -0
- package/build/keypair/AddressVerificator.js +4 -0
- package/build/keypair/EcKeyPair.d.ts +4 -1
- package/build/keypair/EcKeyPair.js +48 -21
- package/build/transaction/TransactionFactory.d.ts +0 -1
- package/build/transaction/TransactionFactory.js +18 -4
- package/build/transaction/browser/Web3Provider.d.ts +2 -0
- package/build/transaction/builders/CustomScriptTransaction.d.ts +9 -6
- package/build/transaction/builders/CustomScriptTransaction.js +29 -6
- package/build/transaction/builders/DeploymentTransaction.d.ts +6 -3
- package/build/transaction/builders/DeploymentTransaction.js +16 -7
- package/build/transaction/builders/MultiSignTransaction.d.ts +5 -5
- package/build/transaction/builders/MultiSignTransaction.js +5 -1
- package/build/transaction/builders/SharedInteractionTransaction.d.ts +5 -5
- package/build/transaction/builders/SharedInteractionTransaction.js +5 -1
- package/build/transaction/builders/TransactionBuilder.js +3 -2
- package/build/transaction/shared/TweakedTransaction.d.ts +5 -5
- package/build/transaction/shared/TweakedTransaction.js +5 -3
- package/build/verification/TapscriptVerificator.js +0 -1
- package/package.json +18 -18
- package/src/buffer/BinaryReader.ts +2 -2
- package/src/buffer/BinaryWriter.ts +2 -2
- package/src/generators/builders/DeploymentGenerator.ts +4 -0
- package/src/keypair/Address.ts +21 -0
- package/src/keypair/AddressVerificator.ts +6 -1
- package/src/keypair/EcKeyPair.ts +91 -34
- package/src/transaction/TransactionFactory.ts +21 -7
- package/src/transaction/browser/Web3Provider.ts +6 -0
- package/src/transaction/builders/CustomScriptTransaction.ts +51 -14
- package/src/transaction/builders/DeploymentTransaction.ts +36 -14
- package/src/transaction/builders/MultiSignTransaction.ts +10 -6
- package/src/transaction/builders/SharedInteractionTransaction.ts +9 -5
- package/src/transaction/builders/TransactionBuilder.ts +4 -3
- package/src/transaction/shared/TweakedTransaction.ts +10 -6
- package/src/verification/TapscriptVerificator.ts +1 -1
package/src/keypair/EcKeyPair.ts
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import * as ecc from '@bitcoinerlab/secp256k1';
|
|
2
2
|
import bip32, { BIP32API, BIP32Factory, BIP32Interface } from 'bip32';
|
|
3
|
-
import {
|
|
3
|
+
import bitcoin, {
|
|
4
4
|
address,
|
|
5
|
+
fromOutputScript,
|
|
5
6
|
initEccLib,
|
|
6
7
|
Network,
|
|
7
8
|
networks,
|
|
9
|
+
opcodes,
|
|
8
10
|
payments,
|
|
11
|
+
script,
|
|
9
12
|
Signer,
|
|
10
|
-
taggedHash,
|
|
11
13
|
toXOnly,
|
|
12
14
|
} from '@btc-vision/bitcoin';
|
|
13
15
|
import { ECPairAPI, ECPairFactory, ECPairInterface } from 'ecpair';
|
|
14
16
|
import { IWallet } from './interfaces/IWallet.js';
|
|
15
|
-
import {
|
|
17
|
+
import { secp256k1 } from '@noble/curves/secp256k1';
|
|
18
|
+
import { bytesToNumberBE, concatBytes, utf8ToBytes } from '@noble/curves/abstract/utils';
|
|
19
|
+
import { mod } from '@noble/curves/abstract/modular';
|
|
20
|
+
import { sha256 } from '@noble/hashes/sha2';
|
|
16
21
|
|
|
17
22
|
initEccLib(ecc);
|
|
18
23
|
|
|
@@ -21,10 +26,16 @@ if (!BIP32factory) {
|
|
|
21
26
|
throw new Error('Failed to load BIP32 library');
|
|
22
27
|
}
|
|
23
28
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
secp256k1.utils.precompute(8);
|
|
30
|
+
|
|
31
|
+
const { ProjectivePoint: Point, CURVE } = secp256k1;
|
|
32
|
+
|
|
33
|
+
const TAP_TAG = utf8ToBytes('TapTweak');
|
|
34
|
+
const TAP_TAG_HASH = sha256(TAP_TAG);
|
|
35
|
+
|
|
36
|
+
function tapTweakHash(x: Uint8Array): Uint8Array {
|
|
37
|
+
return sha256(concatBytes(TAP_TAG_HASH, TAP_TAG_HASH, x));
|
|
38
|
+
}
|
|
28
39
|
|
|
29
40
|
/**
|
|
30
41
|
* Class for handling EC key pairs
|
|
@@ -195,6 +206,32 @@ export class EcKeyPair {
|
|
|
195
206
|
return address;
|
|
196
207
|
}
|
|
197
208
|
|
|
209
|
+
/**
|
|
210
|
+
* Generate a P2OP address
|
|
211
|
+
* @param bytes - The bytes to use for the P2OP address
|
|
212
|
+
* @param network - The network to use
|
|
213
|
+
* @param deploymentVersion - The deployment version (default is 0)
|
|
214
|
+
* @returns {string} - The generated P2OP address
|
|
215
|
+
*/
|
|
216
|
+
public static p2op(
|
|
217
|
+
bytes: Buffer | Uint8Array,
|
|
218
|
+
network: Network = networks.bitcoin,
|
|
219
|
+
deploymentVersion: number = 0,
|
|
220
|
+
): string {
|
|
221
|
+
// custom opnet contract addresses
|
|
222
|
+
const witnessProgram = Buffer.concat([
|
|
223
|
+
Buffer.from([deploymentVersion]),
|
|
224
|
+
bitcoin.crypto.hash160(Buffer.from(bytes)),
|
|
225
|
+
]);
|
|
226
|
+
|
|
227
|
+
if (witnessProgram.length < 2 || witnessProgram.length > 40) {
|
|
228
|
+
throw new Error('Witness program must be 2-40 bytes.');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const scriptData = script.compile([opcodes.OP_16, witnessProgram]);
|
|
232
|
+
return fromOutputScript(scriptData, network);
|
|
233
|
+
}
|
|
234
|
+
|
|
198
235
|
/**
|
|
199
236
|
* Get the address of a xOnly tweaked public key
|
|
200
237
|
* @param {string} tweakedPubKeyHex - The xOnly tweaked public key hex string
|
|
@@ -225,42 +262,44 @@ export class EcKeyPair {
|
|
|
225
262
|
|
|
226
263
|
/**
|
|
227
264
|
* Tweak a public key
|
|
228
|
-
* @param {
|
|
265
|
+
* @param {Buffer | Uint8Array | string} pub - The public key to tweak
|
|
229
266
|
* @returns {Buffer} - The tweaked public key hex string
|
|
230
267
|
* @throws {Error} - If the public key cannot be tweaked
|
|
231
268
|
*/
|
|
232
|
-
public static tweakPublicKey(
|
|
233
|
-
if (typeof
|
|
234
|
-
compressedPubKeyHex = compressedPubKeyHex.slice(2);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
if (typeof compressedPubKeyHex !== 'string') {
|
|
238
|
-
compressedPubKeyHex = compressedPubKeyHex.toString('hex');
|
|
239
|
-
}
|
|
269
|
+
public static tweakPublicKey(pub: Uint8Array | Buffer | string): Buffer {
|
|
270
|
+
if (typeof pub === 'string' && pub.startsWith('0x')) pub = pub.slice(2);
|
|
240
271
|
|
|
241
|
-
|
|
242
|
-
|
|
272
|
+
const P = Point.fromHex(pub);
|
|
273
|
+
const Peven = (P.y & 1n) === 0n ? P : P.negate();
|
|
243
274
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
P = P.negate();
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Get the x-coordinate (32 bytes) of the point
|
|
251
|
-
const x = P.toRawBytes(true).slice(1); // Remove the prefix byte
|
|
252
|
-
|
|
253
|
-
// Compute the tweak t = H_tapTweak(x)
|
|
254
|
-
const tHash = taggedHash('TapTweak', Buffer.from(x));
|
|
255
|
-
const t = mod(BigInt('0x' + Buffer.from(tHash).toString('hex')), CURVE.n);
|
|
275
|
+
const xBytes = Peven.toRawBytes(true).subarray(1);
|
|
276
|
+
const tBytes = tapTweakHash(xBytes);
|
|
277
|
+
const t = mod(bytesToNumberBE(tBytes), CURVE.n);
|
|
256
278
|
|
|
257
|
-
|
|
258
|
-
const Q = P.add(Point.BASE.mul(t));
|
|
259
|
-
|
|
260
|
-
// Return the tweaked public key in compressed form (hex string)
|
|
279
|
+
const Q = Peven.add(Point.BASE.multiply(t));
|
|
261
280
|
return Buffer.from(Q.toRawBytes(true));
|
|
262
281
|
}
|
|
263
282
|
|
|
283
|
+
/**
|
|
284
|
+
* Tweak a batch of public keys
|
|
285
|
+
* @param {readonly Uint8Array[]} pubkeys - The public keys to tweak
|
|
286
|
+
* @param {bigint} tweakScalar - The scalar to use for tweaking
|
|
287
|
+
* @returns {Uint8Array[]} - The tweaked public keys
|
|
288
|
+
*/
|
|
289
|
+
public static tweakBatchSharedT(
|
|
290
|
+
pubkeys: readonly Uint8Array[],
|
|
291
|
+
tweakScalar: bigint,
|
|
292
|
+
): Uint8Array[] {
|
|
293
|
+
const T = Point.BASE.multiply(tweakScalar);
|
|
294
|
+
|
|
295
|
+
return pubkeys.map((bytes) => {
|
|
296
|
+
const P = Point.fromHex(bytes);
|
|
297
|
+
const P_even = P.hasEvenY() ? P : P.negate();
|
|
298
|
+
const Q = P_even.add(T);
|
|
299
|
+
return Q.toRawBytes(true);
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
264
303
|
/**
|
|
265
304
|
* Generate a random wallet
|
|
266
305
|
* @param {Network} network - The network to use
|
|
@@ -337,6 +376,24 @@ export class EcKeyPair {
|
|
|
337
376
|
return wallet.address;
|
|
338
377
|
}
|
|
339
378
|
|
|
379
|
+
/**
|
|
380
|
+
* Get the legacy address from a keypair
|
|
381
|
+
* @param publicKey
|
|
382
|
+
* @param {Network} network - The network to use
|
|
383
|
+
* @returns {string} - The legacy address
|
|
384
|
+
*/
|
|
385
|
+
public static getP2PKH(
|
|
386
|
+
publicKey: Buffer | Uint8Array,
|
|
387
|
+
network: Network = networks.bitcoin,
|
|
388
|
+
): string {
|
|
389
|
+
const wallet = payments.p2pkh({ pubkey: Buffer.from(publicKey), network: network });
|
|
390
|
+
if (!wallet.address) {
|
|
391
|
+
throw new Error('Failed to generate wallet');
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return wallet.address;
|
|
395
|
+
}
|
|
396
|
+
|
|
340
397
|
/**
|
|
341
398
|
* Get the legacy address from a keypair
|
|
342
399
|
* @param {ECPairInterface} keyPair - The keypair to get the address for
|
|
@@ -30,8 +30,6 @@ export interface DeploymentResult {
|
|
|
30
30
|
|
|
31
31
|
readonly contractAddress: string;
|
|
32
32
|
readonly contractPubKey: string;
|
|
33
|
-
readonly p2trAddress: string;
|
|
34
|
-
|
|
35
33
|
readonly preimage: string;
|
|
36
34
|
|
|
37
35
|
readonly utxos: UTXO[];
|
|
@@ -89,13 +87,22 @@ export class TransactionFactory {
|
|
|
89
87
|
throw new Error('Field "from" not provided.');
|
|
90
88
|
}
|
|
91
89
|
|
|
90
|
+
if (!interactionParameters.utxos[0]) {
|
|
91
|
+
throw new Error('Missing at least one UTXO.');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (!('signer' in interactionParameters)) {
|
|
95
|
+
throw new Error('Field "signer" not provided, OP_WALLET not detected.');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const inputs = this.parseOptionalInputs(interactionParameters.optionalInputs);
|
|
92
99
|
const preTransaction: CustomScriptTransaction = new CustomScriptTransaction({
|
|
93
100
|
...interactionParameters,
|
|
94
101
|
utxos: [interactionParameters.utxos[0]], // we simulate one input here.
|
|
102
|
+
optionalInputs: inputs,
|
|
95
103
|
});
|
|
96
104
|
|
|
97
105
|
// we don't sign that transaction, we just need the parameters.
|
|
98
|
-
|
|
99
106
|
await preTransaction.generateTransactionMinimalSignatures();
|
|
100
107
|
|
|
101
108
|
const parameters: IFundingTransactionParameters =
|
|
@@ -119,7 +126,12 @@ export class TransactionFactory {
|
|
|
119
126
|
|
|
120
127
|
parameters.estimatedFees = feeEstimationFundingTransaction.estimatedFees;
|
|
121
128
|
|
|
122
|
-
const signedTransaction = await this.createFundTransaction(
|
|
129
|
+
const signedTransaction = await this.createFundTransaction({
|
|
130
|
+
...parameters,
|
|
131
|
+
optionalOutputs: [],
|
|
132
|
+
optionalInputs: [],
|
|
133
|
+
});
|
|
134
|
+
|
|
123
135
|
if (!signedTransaction) {
|
|
124
136
|
throw new Error('Could not sign funding transaction.');
|
|
125
137
|
}
|
|
@@ -132,10 +144,13 @@ export class TransactionFactory {
|
|
|
132
144
|
|
|
133
145
|
const newParams: ICustomTransactionParameters = {
|
|
134
146
|
...interactionParameters,
|
|
135
|
-
utxos:
|
|
147
|
+
utxos: [
|
|
148
|
+
...this.getUTXOAsTransaction(signedTransaction.tx, interactionParameters.to, 0),
|
|
149
|
+
], // always 0
|
|
136
150
|
randomBytes: preTransaction.getRndBytes(),
|
|
137
151
|
nonWitnessUtxo: signedTransaction.tx.toBuffer(),
|
|
138
152
|
estimatedFees: preTransaction.estimatedFees,
|
|
153
|
+
optionalInputs: inputs,
|
|
139
154
|
};
|
|
140
155
|
|
|
141
156
|
const finalTransaction: CustomScriptTransaction = new CustomScriptTransaction(newParams);
|
|
@@ -352,9 +367,8 @@ export class TransactionFactory {
|
|
|
352
367
|
|
|
353
368
|
return {
|
|
354
369
|
transaction: [signedTransaction.toHex(), outTx.toHex()],
|
|
355
|
-
contractAddress: finalTransaction.contractAddress.p2tr(deploymentParameters.network),
|
|
370
|
+
contractAddress: finalTransaction.getContractAddress(), //finalTransaction.contractAddress.p2tr(deploymentParameters.network),
|
|
356
371
|
contractPubKey: finalTransaction.contractPubKey,
|
|
357
|
-
p2trAddress: finalTransaction.p2trAddress,
|
|
358
372
|
utxos: [refundUTXO],
|
|
359
373
|
preimage: preTransaction.getPreimage().toString('hex'),
|
|
360
374
|
};
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
} from '../interfaces/ITransactionParameters.js';
|
|
5
5
|
import { UTXO } from '../../utxo/interfaces/IUTXO.js';
|
|
6
6
|
import { DeploymentResult, InteractionResponse } from '../TransactionFactory';
|
|
7
|
+
import { ICustomTransactionParameters } from '../builders/CustomScriptTransaction.js';
|
|
7
8
|
|
|
8
9
|
export type InteractionParametersWithoutSigner = Omit<
|
|
9
10
|
IInteractionParameters,
|
|
@@ -15,6 +16,11 @@ export type IDeploymentParametersWithoutSigner = Omit<
|
|
|
15
16
|
'signer' | 'network' | 'preimage'
|
|
16
17
|
>;
|
|
17
18
|
|
|
19
|
+
export type CustomTransactionWithoutSigner = Omit<
|
|
20
|
+
ICustomTransactionParameters,
|
|
21
|
+
'signer' | 'preimage'
|
|
22
|
+
>;
|
|
23
|
+
|
|
18
24
|
export interface BroadcastTransactionOptions {
|
|
19
25
|
raw: string;
|
|
20
26
|
psbt: boolean;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
crypto as bitCrypto,
|
|
3
|
+
P2TRPayment,
|
|
3
4
|
Payment,
|
|
5
|
+
PaymentType,
|
|
4
6
|
Psbt,
|
|
5
7
|
PsbtInput,
|
|
6
8
|
Signer,
|
|
@@ -19,10 +21,13 @@ import { AddressGenerator } from '../../generators/AddressGenerator.js';
|
|
|
19
21
|
import { ECPairInterface } from 'ecpair';
|
|
20
22
|
|
|
21
23
|
export interface ICustomTransactionParameters extends SharedInteractionParameters {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
script: (Buffer | Stack)[];
|
|
25
|
+
witnesses: Buffer[];
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
/** optional Taproot annex payload (without the 0x50 prefix) */
|
|
28
|
+
annex?: Buffer;
|
|
29
|
+
|
|
30
|
+
to: string;
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
/**
|
|
@@ -46,12 +51,12 @@ export class CustomScriptTransaction extends TransactionBuilder<TransactionType.
|
|
|
46
51
|
* The target script redeem
|
|
47
52
|
* @private
|
|
48
53
|
*/
|
|
49
|
-
private targetScriptRedeem:
|
|
54
|
+
private targetScriptRedeem: P2TRPayment | null = null;
|
|
50
55
|
/**
|
|
51
56
|
* The left over funds script redeem
|
|
52
57
|
* @private
|
|
53
58
|
*/
|
|
54
|
-
private leftOverFundsScriptRedeem:
|
|
59
|
+
private leftOverFundsScriptRedeem: P2TRPayment | null = null;
|
|
55
60
|
/**
|
|
56
61
|
* The compiled target script
|
|
57
62
|
* @private
|
|
@@ -91,6 +96,7 @@ export class CustomScriptTransaction extends TransactionBuilder<TransactionType.
|
|
|
91
96
|
* @private
|
|
92
97
|
*/
|
|
93
98
|
private readonly witnesses: Buffer[];
|
|
99
|
+
private readonly annexData?: Buffer;
|
|
94
100
|
|
|
95
101
|
public constructor(parameters: ICustomTransactionParameters) {
|
|
96
102
|
super(parameters);
|
|
@@ -154,7 +160,7 @@ export class CustomScriptTransaction extends TransactionBuilder<TransactionType.
|
|
|
154
160
|
this.to = this.getScriptAddress();
|
|
155
161
|
}
|
|
156
162
|
|
|
157
|
-
const selectedRedeem = this.contractSigner
|
|
163
|
+
const selectedRedeem: Payment | null = this.contractSigner
|
|
158
164
|
? this.targetScriptRedeem
|
|
159
165
|
: this.leftOverFundsScriptRedeem;
|
|
160
166
|
|
|
@@ -202,7 +208,10 @@ export class CustomScriptTransaction extends TransactionBuilder<TransactionType.
|
|
|
202
208
|
for (let i = 0; i < transaction.data.inputs.length; i++) {
|
|
203
209
|
if (i === 0) {
|
|
204
210
|
// multi sig input
|
|
205
|
-
|
|
211
|
+
try {
|
|
212
|
+
transaction.signInput(0, this.contractSigner);
|
|
213
|
+
} catch (e) {}
|
|
214
|
+
|
|
206
215
|
transaction.signInput(0, this.getSignerKey());
|
|
207
216
|
|
|
208
217
|
transaction.finalizeInput(0, this.customFinalizer);
|
|
@@ -217,11 +226,12 @@ export class CustomScriptTransaction extends TransactionBuilder<TransactionType.
|
|
|
217
226
|
* Get the tap output
|
|
218
227
|
* @protected
|
|
219
228
|
*/
|
|
220
|
-
protected override generateScriptAddress():
|
|
229
|
+
protected override generateScriptAddress(): P2TRPayment {
|
|
221
230
|
return {
|
|
222
231
|
internalPubkey: this.internalPubKeyToXOnly(),
|
|
223
232
|
network: this.network,
|
|
224
233
|
scriptTree: this.scriptTree,
|
|
234
|
+
name: PaymentType.P2TR,
|
|
225
235
|
};
|
|
226
236
|
}
|
|
227
237
|
|
|
@@ -229,7 +239,7 @@ export class CustomScriptTransaction extends TransactionBuilder<TransactionType.
|
|
|
229
239
|
* Generate the tap data
|
|
230
240
|
* @protected
|
|
231
241
|
*/
|
|
232
|
-
protected override generateTapData():
|
|
242
|
+
protected override generateTapData(): P2TRPayment {
|
|
233
243
|
const selectedRedeem = this.contractSigner
|
|
234
244
|
? this.targetScriptRedeem
|
|
235
245
|
: this.leftOverFundsScriptRedeem;
|
|
@@ -247,9 +257,25 @@ export class CustomScriptTransaction extends TransactionBuilder<TransactionType.
|
|
|
247
257
|
network: this.network,
|
|
248
258
|
scriptTree: this.scriptTree,
|
|
249
259
|
redeem: selectedRedeem,
|
|
260
|
+
name: PaymentType.P2TR,
|
|
250
261
|
};
|
|
251
262
|
}
|
|
252
263
|
|
|
264
|
+
protected getScriptSolution(input: PsbtInput): Buffer[] {
|
|
265
|
+
if (!input.tapScriptSig) {
|
|
266
|
+
throw new Error('Tap script signature is required');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const witnesses: Buffer[] = [...this.witnesses];
|
|
270
|
+
if (input.tapScriptSig) {
|
|
271
|
+
for (const sig of input.tapScriptSig) {
|
|
272
|
+
witnesses.push(sig.signature);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return witnesses;
|
|
277
|
+
}
|
|
278
|
+
|
|
253
279
|
/**
|
|
254
280
|
* Generate the contract seed for the deployment
|
|
255
281
|
* @private
|
|
@@ -261,18 +287,27 @@ export class CustomScriptTransaction extends TransactionBuilder<TransactionType.
|
|
|
261
287
|
/**
|
|
262
288
|
* Finalize the transaction
|
|
263
289
|
* @param _inputIndex
|
|
264
|
-
* @param
|
|
290
|
+
* @param input
|
|
265
291
|
*/
|
|
266
|
-
private customFinalizer = (_inputIndex: number,
|
|
292
|
+
private customFinalizer = (_inputIndex: number, input: PsbtInput) => {
|
|
267
293
|
if (!this.tapLeafScript) {
|
|
268
294
|
throw new Error('Tap leaf script is required');
|
|
269
295
|
}
|
|
270
296
|
|
|
271
|
-
const scriptSolution = this.
|
|
297
|
+
const scriptSolution = this.getScriptSolution(input);
|
|
272
298
|
const witness = scriptSolution
|
|
273
299
|
.concat(this.tapLeafScript.script)
|
|
274
300
|
.concat(this.tapLeafScript.controlBlock);
|
|
275
301
|
|
|
302
|
+
if (this.annexData && this.annexData.length > 0) {
|
|
303
|
+
const annex =
|
|
304
|
+
this.annexData[0] === 0x50
|
|
305
|
+
? this.annexData
|
|
306
|
+
: Buffer.concat([Buffer.from([0x50]), this.annexData]);
|
|
307
|
+
|
|
308
|
+
witness.push(annex);
|
|
309
|
+
}
|
|
310
|
+
|
|
276
311
|
return {
|
|
277
312
|
finalScriptWitness: TransactionBuilder.witnessStackToScriptWitness(witness),
|
|
278
313
|
};
|
|
@@ -298,13 +333,15 @@ export class CustomScriptTransaction extends TransactionBuilder<TransactionType.
|
|
|
298
333
|
*/
|
|
299
334
|
private generateRedeemScripts(): void {
|
|
300
335
|
this.targetScriptRedeem = {
|
|
301
|
-
|
|
336
|
+
name: PaymentType.P2TR,
|
|
337
|
+
//pubkeys: this.getPubKeys(),
|
|
302
338
|
output: this.compiledTargetScript,
|
|
303
339
|
redeemVersion: 192,
|
|
304
340
|
};
|
|
305
341
|
|
|
306
342
|
this.leftOverFundsScriptRedeem = {
|
|
307
|
-
|
|
343
|
+
name: PaymentType.P2TR,
|
|
344
|
+
//pubkeys: this.getPubKeys(),
|
|
308
345
|
output: this.getLeafScript(),
|
|
309
346
|
redeemVersion: 192,
|
|
310
347
|
};
|
|
@@ -2,7 +2,8 @@ import { TransactionType } from '../enums/TransactionType.js';
|
|
|
2
2
|
import { IDeploymentParameters } from '../interfaces/ITransactionParameters.js';
|
|
3
3
|
import {
|
|
4
4
|
crypto as bitCrypto,
|
|
5
|
-
|
|
5
|
+
P2TRPayment,
|
|
6
|
+
PaymentType,
|
|
6
7
|
Psbt,
|
|
7
8
|
PsbtInput,
|
|
8
9
|
Signer,
|
|
@@ -15,7 +16,10 @@ import {
|
|
|
15
16
|
TransactionBuilder,
|
|
16
17
|
} from './TransactionBuilder.js';
|
|
17
18
|
import { TapLeafScript } from '../interfaces/Tap.js';
|
|
18
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
DeploymentGenerator,
|
|
21
|
+
versionBuffer,
|
|
22
|
+
} from '../../generators/builders/DeploymentGenerator.js';
|
|
19
23
|
import { EcKeyPair } from '../../keypair/EcKeyPair.js';
|
|
20
24
|
import { BitcoinUtils } from '../../utils/BitcoinUtils.js';
|
|
21
25
|
import { Compressor } from '../../bytecode/Compressor.js';
|
|
@@ -25,15 +29,13 @@ import { Address } from '../../keypair/Address.js';
|
|
|
25
29
|
import { UnisatSigner } from '../browser/extensions/UnisatSigner.js';
|
|
26
30
|
import { ChallengeGenerator, IMineableReward } from '../mineable/ChallengeGenerator.js';
|
|
27
31
|
|
|
28
|
-
const p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fn;
|
|
29
|
-
|
|
30
32
|
export class DeploymentTransaction extends TransactionBuilder<TransactionType.DEPLOYMENT> {
|
|
31
33
|
public static readonly MAXIMUM_CONTRACT_SIZE = 128 * 1024;
|
|
34
|
+
|
|
32
35
|
public type: TransactionType.DEPLOYMENT = TransactionType.DEPLOYMENT;
|
|
33
36
|
|
|
34
37
|
protected readonly preimage: Buffer; // ALWAYS 128 bytes for the preimage
|
|
35
38
|
protected readonly rewardChallenge: IMineableReward;
|
|
36
|
-
|
|
37
39
|
/**
|
|
38
40
|
* The contract address
|
|
39
41
|
* @protected
|
|
@@ -44,16 +46,17 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
|
|
|
44
46
|
* @private
|
|
45
47
|
*/
|
|
46
48
|
protected tapLeafScript: TapLeafScript | null = null;
|
|
49
|
+
private readonly deploymentVersion: number = 0x00;
|
|
47
50
|
/**
|
|
48
51
|
* The target script redeem
|
|
49
52
|
* @private
|
|
50
53
|
*/
|
|
51
|
-
private targetScriptRedeem:
|
|
54
|
+
private targetScriptRedeem: P2TRPayment | null = null;
|
|
52
55
|
/**
|
|
53
56
|
* The left over funds script redeem
|
|
54
57
|
* @private
|
|
55
58
|
*/
|
|
56
|
-
private leftOverFundsScriptRedeem:
|
|
59
|
+
private leftOverFundsScriptRedeem: P2TRPayment | null = null;
|
|
57
60
|
/**
|
|
58
61
|
* The compiled target script
|
|
59
62
|
* @private
|
|
@@ -105,12 +108,13 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
|
|
|
105
108
|
* @private
|
|
106
109
|
*/
|
|
107
110
|
private readonly randomBytes: Buffer;
|
|
111
|
+
private _computedAddress: string | undefined;
|
|
108
112
|
|
|
109
113
|
public constructor(parameters: IDeploymentParameters) {
|
|
110
|
-
// TODO: Add legacy deployment, this is only p2tr.
|
|
111
114
|
super(parameters);
|
|
112
115
|
|
|
113
|
-
this.bytecode = Compressor.compress(parameters.bytecode);
|
|
116
|
+
this.bytecode = Compressor.compress(Buffer.concat([versionBuffer, parameters.bytecode]));
|
|
117
|
+
|
|
114
118
|
this.verifyBytecode();
|
|
115
119
|
|
|
116
120
|
if (parameters.calldata) {
|
|
@@ -190,6 +194,20 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
|
|
|
190
194
|
return this.preimage;
|
|
191
195
|
}
|
|
192
196
|
|
|
197
|
+
public getContractAddress(): string {
|
|
198
|
+
if (this._computedAddress) {
|
|
199
|
+
return this._computedAddress;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
this._computedAddress = EcKeyPair.p2op(
|
|
203
|
+
this.contractSeed,
|
|
204
|
+
this.network,
|
|
205
|
+
this.deploymentVersion,
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
return this._computedAddress;
|
|
209
|
+
}
|
|
210
|
+
|
|
193
211
|
/**
|
|
194
212
|
* Get the contract signer public key
|
|
195
213
|
* @protected
|
|
@@ -243,7 +261,7 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
|
|
|
243
261
|
// ALWAYS THE FIRST INPUT.
|
|
244
262
|
this.addOutput({
|
|
245
263
|
value: Number(amountToCA),
|
|
246
|
-
address: this.
|
|
264
|
+
address: this.getContractAddress(),
|
|
247
265
|
});
|
|
248
266
|
|
|
249
267
|
// ALWAYS SECOND.
|
|
@@ -323,8 +341,9 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
|
|
|
323
341
|
* Get the tap output
|
|
324
342
|
* @protected
|
|
325
343
|
*/
|
|
326
|
-
protected override generateScriptAddress():
|
|
344
|
+
protected override generateScriptAddress(): P2TRPayment {
|
|
327
345
|
return {
|
|
346
|
+
name: PaymentType.P2TR,
|
|
328
347
|
internalPubkey: this.internalPubKeyToXOnly(),
|
|
329
348
|
network: this.network,
|
|
330
349
|
scriptTree: this.scriptTree,
|
|
@@ -335,7 +354,7 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
|
|
|
335
354
|
* Generate the tap data
|
|
336
355
|
* @protected
|
|
337
356
|
*/
|
|
338
|
-
protected override generateTapData():
|
|
357
|
+
protected override generateTapData(): P2TRPayment {
|
|
339
358
|
const selectedRedeem = this.contractSigner
|
|
340
359
|
? this.targetScriptRedeem
|
|
341
360
|
: this.leftOverFundsScriptRedeem;
|
|
@@ -349,6 +368,7 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
|
|
|
349
368
|
}
|
|
350
369
|
|
|
351
370
|
return {
|
|
371
|
+
name: PaymentType.P2TR,
|
|
352
372
|
internalPubkey: this.internalPubKeyToXOnly(),
|
|
353
373
|
network: this.network,
|
|
354
374
|
scriptTree: this.scriptTree,
|
|
@@ -440,13 +460,15 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
|
|
|
440
460
|
*/
|
|
441
461
|
private generateRedeemScripts(): void {
|
|
442
462
|
this.targetScriptRedeem = {
|
|
443
|
-
|
|
463
|
+
name: PaymentType.P2TR,
|
|
464
|
+
//pubkeys: this.getPubKeys(),
|
|
444
465
|
output: this.compiledTargetScript,
|
|
445
466
|
redeemVersion: 192,
|
|
446
467
|
};
|
|
447
468
|
|
|
448
469
|
this.leftOverFundsScriptRedeem = {
|
|
449
|
-
|
|
470
|
+
name: PaymentType.P2TR,
|
|
471
|
+
//pubkeys: this.getPubKeys(),
|
|
450
472
|
output: this.getLeafScript(),
|
|
451
473
|
redeemVersion: 192,
|
|
452
474
|
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
crypto as bitcoinCrypto,
|
|
3
3
|
opcodes,
|
|
4
|
-
|
|
4
|
+
P2TRPayment,
|
|
5
|
+
PaymentType,
|
|
5
6
|
Psbt,
|
|
6
7
|
PsbtInput,
|
|
7
8
|
PsbtInputExtended,
|
|
@@ -58,8 +59,8 @@ export class MultiSignTransaction extends TransactionBuilder<TransactionType.MUL
|
|
|
58
59
|
|
|
59
60
|
public type: TransactionType.MULTI_SIG = TransactionType.MULTI_SIG;
|
|
60
61
|
|
|
61
|
-
protected targetScriptRedeem:
|
|
62
|
-
protected leftOverFundsScriptRedeem:
|
|
62
|
+
protected targetScriptRedeem: P2TRPayment | null = null;
|
|
63
|
+
protected leftOverFundsScriptRedeem: P2TRPayment | null = null;
|
|
63
64
|
|
|
64
65
|
protected readonly compiledTargetScript: Buffer;
|
|
65
66
|
protected readonly scriptTree: Taptree;
|
|
@@ -545,16 +546,16 @@ export class MultiSignTransaction extends TransactionBuilder<TransactionType.MUL
|
|
|
545
546
|
*/
|
|
546
547
|
protected override async signInputs(_transaction: Psbt): Promise<void> {}
|
|
547
548
|
|
|
548
|
-
protected override generateScriptAddress():
|
|
549
|
+
protected override generateScriptAddress(): P2TRPayment {
|
|
549
550
|
return {
|
|
550
551
|
internalPubkey: toXOnly(MultiSignTransaction.numsPoint), //this.internalPubKeyToXOnly(),
|
|
551
552
|
network: this.network,
|
|
552
553
|
scriptTree: this.scriptTree,
|
|
553
|
-
|
|
554
|
+
name: PaymentType.P2TR,
|
|
554
555
|
};
|
|
555
556
|
}
|
|
556
557
|
|
|
557
|
-
protected override generateTapData():
|
|
558
|
+
protected override generateTapData(): P2TRPayment {
|
|
558
559
|
const selectedRedeem = this.targetScriptRedeem;
|
|
559
560
|
if (!selectedRedeem) {
|
|
560
561
|
throw new Error('Left over funds script redeem is required');
|
|
@@ -569,6 +570,7 @@ export class MultiSignTransaction extends TransactionBuilder<TransactionType.MUL
|
|
|
569
570
|
network: this.network,
|
|
570
571
|
scriptTree: this.scriptTree,
|
|
571
572
|
redeem: selectedRedeem,
|
|
573
|
+
name: PaymentType.P2TR,
|
|
572
574
|
};
|
|
573
575
|
}
|
|
574
576
|
|
|
@@ -662,11 +664,13 @@ export class MultiSignTransaction extends TransactionBuilder<TransactionType.MUL
|
|
|
662
664
|
*/
|
|
663
665
|
private generateRedeemScripts(): void {
|
|
664
666
|
this.targetScriptRedeem = {
|
|
667
|
+
name: PaymentType.P2TR,
|
|
665
668
|
output: this.compiledTargetScript,
|
|
666
669
|
redeemVersion: 192,
|
|
667
670
|
};
|
|
668
671
|
|
|
669
672
|
this.leftOverFundsScriptRedeem = {
|
|
673
|
+
name: PaymentType.P2TR,
|
|
670
674
|
output: MultiSignTransaction.LOCK_LEAF_SCRIPT,
|
|
671
675
|
redeemVersion: 192,
|
|
672
676
|
};
|