@btc-vision/transaction 1.4.0 → 1.5.0
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/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 +7 -4
- package/browser/transaction/builders/DeploymentTransaction.d.ts +3 -0
- package/build/buffer/BinaryReader.d.ts +1 -1
- package/build/buffer/BinaryReader.js +1 -1
- package/build/buffer/BinaryWriter.js +2 -2
- 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 +7 -4
- package/build/transaction/builders/CustomScriptTransaction.js +24 -3
- package/build/transaction/builders/DeploymentTransaction.d.ts +3 -0
- package/build/transaction/builders/DeploymentTransaction.js +9 -1
- package/build/transaction/builders/TransactionBuilder.js +3 -2
- package/build/transaction/shared/TweakedTransaction.js +2 -2
- package/package.json +18 -18
- package/src/buffer/BinaryReader.ts +2 -2
- package/src/buffer/BinaryWriter.ts +2 -2
- 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 +38 -7
- package/src/transaction/builders/DeploymentTransaction.ts +17 -3
- package/src/transaction/builders/TransactionBuilder.ts +4 -3
- package/src/transaction/shared/TweakedTransaction.ts +2 -2
|
@@ -20,9 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
22
22
|
|
|
23
|
-
/*!
|
|
24
|
-
|
|
25
|
-
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
|
|
23
|
+
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */
|
|
26
24
|
|
|
27
25
|
/*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
|
|
28
26
|
|
|
@@ -28,6 +28,7 @@ export declare class Address extends Uint8Array {
|
|
|
28
28
|
toString(): string;
|
|
29
29
|
toJSON(): string;
|
|
30
30
|
p2tr(network: Network): string;
|
|
31
|
+
p2op(network: Network): string;
|
|
31
32
|
toTweakedHybridPublicKeyHex(): string;
|
|
32
33
|
toTweakedHybridPublicKeyBuffer(): Buffer;
|
|
33
34
|
private autoFormat;
|
|
@@ -13,12 +13,15 @@ export declare class EcKeyPair {
|
|
|
13
13
|
static getP2WPKHAddress(keyPair: ECPairInterface, network?: Network): string;
|
|
14
14
|
static tweakedPubKeyToAddress(tweakedPubKeyHex: string, network: Network): string;
|
|
15
15
|
static tweakedPubKeyBufferToAddress(tweakedPubKeyBuffer: Buffer | Uint8Array, network: Network): string;
|
|
16
|
+
static p2op(bytes: Buffer | Uint8Array, network?: Network, deploymentVersion?: number): string;
|
|
16
17
|
static xOnlyTweakedPubKeyToAddress(tweakedPubKeyHex: string, network: Network): string;
|
|
17
|
-
static tweakPublicKey(
|
|
18
|
+
static tweakPublicKey(pub: Uint8Array | Buffer | string): Buffer;
|
|
19
|
+
static tweakBatchSharedT(pubkeys: readonly Uint8Array[], tweakScalar: bigint): Uint8Array[];
|
|
18
20
|
static generateWallet(network?: Network): IWallet;
|
|
19
21
|
static verifyContractAddress(contractAddress: string, network?: Network): boolean;
|
|
20
22
|
static getLegacySegwitAddress(keyPair: ECPairInterface, network?: Network): string;
|
|
21
23
|
static getLegacyAddress(keyPair: ECPairInterface, network?: Network): string;
|
|
24
|
+
static getP2PKH(publicKey: Buffer | Uint8Array, network?: Network): string;
|
|
22
25
|
static getP2PKAddress(keyPair: ECPairInterface, network?: Network): string;
|
|
23
26
|
static generateRandomKeyPair(network?: Network): ECPairInterface;
|
|
24
27
|
static fromSeed(seed: Buffer, network?: Network): BIP32Interface;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { IDeploymentParameters, IInteractionParameters } from '../interfaces/ITransactionParameters.js';
|
|
2
2
|
import { UTXO } from '../../utxo/interfaces/IUTXO.js';
|
|
3
3
|
import { DeploymentResult, InteractionResponse } from '../TransactionFactory';
|
|
4
|
+
import { ICustomTransactionParameters } from '../builders/CustomScriptTransaction.js';
|
|
4
5
|
export type InteractionParametersWithoutSigner = Omit<IInteractionParameters, 'signer' | 'preimage'>;
|
|
5
6
|
export type IDeploymentParametersWithoutSigner = Omit<IDeploymentParameters, 'signer' | 'network' | 'preimage'>;
|
|
7
|
+
export type CustomTransactionWithoutSigner = Omit<ICustomTransactionParameters, 'signer' | 'preimage'>;
|
|
6
8
|
export interface BroadcastTransactionOptions {
|
|
7
9
|
raw: string;
|
|
8
10
|
psbt: boolean;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { Payment, Psbt, Stack } from '@btc-vision/bitcoin';
|
|
1
|
+
import { Payment, Psbt, PsbtInput, Stack } from '@btc-vision/bitcoin';
|
|
2
2
|
import { TransactionType } from '../enums/TransactionType.js';
|
|
3
3
|
import { TapLeafScript } from '../interfaces/Tap.js';
|
|
4
4
|
import { SharedInteractionParameters } from '../interfaces/ITransactionParameters.js';
|
|
5
5
|
import { TransactionBuilder } from './TransactionBuilder.js';
|
|
6
6
|
export interface ICustomTransactionParameters extends SharedInteractionParameters {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
script: (Buffer | Stack)[];
|
|
8
|
+
witnesses: Buffer[];
|
|
9
|
+
annex?: Buffer;
|
|
10
|
+
to: string;
|
|
10
11
|
}
|
|
11
12
|
export declare class CustomScriptTransaction extends TransactionBuilder<TransactionType.CUSTOM_CODE> {
|
|
12
13
|
type: TransactionType.CUSTOM_CODE;
|
|
@@ -21,6 +22,7 @@ export declare class CustomScriptTransaction extends TransactionBuilder<Transact
|
|
|
21
22
|
private readonly contractSigner;
|
|
22
23
|
private readonly randomBytes;
|
|
23
24
|
private readonly witnesses;
|
|
25
|
+
private readonly annexData?;
|
|
24
26
|
constructor(parameters: ICustomTransactionParameters);
|
|
25
27
|
get scriptAddress(): string;
|
|
26
28
|
get p2trAddress(): string;
|
|
@@ -30,6 +32,7 @@ export declare class CustomScriptTransaction extends TransactionBuilder<Transact
|
|
|
30
32
|
protected signInputs(transaction: Psbt): Promise<void>;
|
|
31
33
|
protected generateScriptAddress(): Payment;
|
|
32
34
|
protected generateTapData(): Payment;
|
|
35
|
+
protected getScriptSolution(input: PsbtInput): Buffer[];
|
|
33
36
|
private getContractSeed;
|
|
34
37
|
private customFinalizer;
|
|
35
38
|
private getPubKeys;
|
|
@@ -12,6 +12,7 @@ export declare class DeploymentTransaction extends TransactionBuilder<Transactio
|
|
|
12
12
|
protected readonly rewardChallenge: IMineableReward;
|
|
13
13
|
protected readonly _contractAddress: Address;
|
|
14
14
|
protected tapLeafScript: TapLeafScript | null;
|
|
15
|
+
private readonly deploymentVersion;
|
|
15
16
|
private targetScriptRedeem;
|
|
16
17
|
private leftOverFundsScriptRedeem;
|
|
17
18
|
private readonly compiledTargetScript;
|
|
@@ -23,12 +24,14 @@ export declare class DeploymentTransaction extends TransactionBuilder<Transactio
|
|
|
23
24
|
private readonly contractSigner;
|
|
24
25
|
private readonly _contractPubKey;
|
|
25
26
|
private readonly randomBytes;
|
|
27
|
+
private _computedAddress;
|
|
26
28
|
constructor(parameters: IDeploymentParameters);
|
|
27
29
|
get contractPubKey(): string;
|
|
28
30
|
get contractAddress(): Address;
|
|
29
31
|
get p2trAddress(): string;
|
|
30
32
|
getRndBytes(): Buffer;
|
|
31
33
|
getPreimage(): Buffer;
|
|
34
|
+
getContractAddress(): string;
|
|
32
35
|
protected contractSignerXOnlyPubKey(): Buffer;
|
|
33
36
|
protected buildTransaction(): Promise<void>;
|
|
34
37
|
protected signInputsWalletBased(transaction: Psbt): Promise<void>;
|
|
@@ -21,7 +21,7 @@ export declare class BinaryReader {
|
|
|
21
21
|
readBoolean(): boolean;
|
|
22
22
|
readSelector(): Selector;
|
|
23
23
|
readBytes(length: u32, zeroStop?: boolean): Uint8Array;
|
|
24
|
-
readString(length:
|
|
24
|
+
readString(length: u32): string;
|
|
25
25
|
readStringWithLength(be?: boolean): string;
|
|
26
26
|
readAddress(): Address;
|
|
27
27
|
readBytesWithLength(maxLength?: number, be?: boolean): Uint8Array;
|
|
@@ -119,8 +119,8 @@ export class BinaryWriter {
|
|
|
119
119
|
this.writeBytes(value);
|
|
120
120
|
}
|
|
121
121
|
writeStringWithLength(value) {
|
|
122
|
-
this.allocSafe(
|
|
123
|
-
this.
|
|
122
|
+
this.allocSafe(U32_BYTE_LENGTH + value.length);
|
|
123
|
+
this.writeU32(value.length);
|
|
124
124
|
this.writeString(value);
|
|
125
125
|
}
|
|
126
126
|
getBuffer(clear = true) {
|
|
@@ -28,6 +28,7 @@ export declare class Address extends Uint8Array {
|
|
|
28
28
|
toString(): string;
|
|
29
29
|
toJSON(): string;
|
|
30
30
|
p2tr(network: Network): string;
|
|
31
|
+
p2op(network: Network): string;
|
|
31
32
|
toTweakedHybridPublicKeyHex(): string;
|
|
32
33
|
toTweakedHybridPublicKeyBuffer(): Buffer;
|
|
33
34
|
private autoFormat;
|
package/build/keypair/Address.js
CHANGED
|
@@ -9,7 +9,7 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
|
|
|
9
9
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
10
10
|
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
11
11
|
};
|
|
12
|
-
var _Address_p2tr, _Address_network, _Address_originalPublicKey, _Address_keyPair, _Address_uncompressed, _Address_tweakedUncompressed;
|
|
12
|
+
var _Address_p2tr, _Address_p2op, _Address_network, _Address_originalPublicKey, _Address_keyPair, _Address_uncompressed, _Address_tweakedUncompressed;
|
|
13
13
|
import { decompressPublicKey, toXOnly } from '@btc-vision/bitcoin';
|
|
14
14
|
import { ADDRESS_BYTE_LENGTH } from '../utils/lengths.js';
|
|
15
15
|
import { AddressVerificator } from './AddressVerificator.js';
|
|
@@ -20,6 +20,7 @@ export class Address extends Uint8Array {
|
|
|
20
20
|
constructor(bytes) {
|
|
21
21
|
super(ADDRESS_BYTE_LENGTH);
|
|
22
22
|
_Address_p2tr.set(this, void 0);
|
|
23
|
+
_Address_p2op.set(this, void 0);
|
|
23
24
|
_Address_network.set(this, void 0);
|
|
24
25
|
_Address_originalPublicKey.set(this, void 0);
|
|
25
26
|
_Address_keyPair.set(this, void 0);
|
|
@@ -193,6 +194,18 @@ export class Address extends Uint8Array {
|
|
|
193
194
|
}
|
|
194
195
|
throw new Error('Public key not set');
|
|
195
196
|
}
|
|
197
|
+
p2op(network) {
|
|
198
|
+
if (__classPrivateFieldGet(this, _Address_p2op, "f") && __classPrivateFieldGet(this, _Address_network, "f") === network) {
|
|
199
|
+
return __classPrivateFieldGet(this, _Address_p2op, "f");
|
|
200
|
+
}
|
|
201
|
+
const p2opAddy = EcKeyPair.p2op(this, network);
|
|
202
|
+
if (p2opAddy) {
|
|
203
|
+
__classPrivateFieldSet(this, _Address_network, network, "f");
|
|
204
|
+
__classPrivateFieldSet(this, _Address_p2op, p2opAddy, "f");
|
|
205
|
+
return p2opAddy;
|
|
206
|
+
}
|
|
207
|
+
throw new Error('Public key not set');
|
|
208
|
+
}
|
|
196
209
|
toTweakedHybridPublicKeyHex() {
|
|
197
210
|
if (!__classPrivateFieldGet(this, _Address_tweakedUncompressed, "f")) {
|
|
198
211
|
throw new Error('Public key not set');
|
|
@@ -220,4 +233,4 @@ export class Address extends Uint8Array {
|
|
|
220
233
|
super.set(tweakedBytes);
|
|
221
234
|
}
|
|
222
235
|
}
|
|
223
|
-
_Address_p2tr = new WeakMap(), _Address_network = new WeakMap(), _Address_originalPublicKey = new WeakMap(), _Address_keyPair = new WeakMap(), _Address_uncompressed = new WeakMap(), _Address_tweakedUncompressed = new WeakMap();
|
|
236
|
+
_Address_p2tr = new WeakMap(), _Address_p2op = new WeakMap(), _Address_network = new WeakMap(), _Address_originalPublicKey = new WeakMap(), _Address_keyPair = new WeakMap(), _Address_uncompressed = new WeakMap(), _Address_tweakedUncompressed = new WeakMap();
|
|
@@ -6,6 +6,7 @@ initEccLib(ecc);
|
|
|
6
6
|
export var AddressTypes;
|
|
7
7
|
(function (AddressTypes) {
|
|
8
8
|
AddressTypes["P2PKH"] = "P2PKH";
|
|
9
|
+
AddressTypes["P2OP"] = "P2OP";
|
|
9
10
|
AddressTypes["P2SH_OR_P2SH_P2WPKH"] = "P2SH_OR_P2SH-P2WPKH";
|
|
10
11
|
AddressTypes["P2PK"] = "P2PK";
|
|
11
12
|
AddressTypes["P2TR"] = "P2TR";
|
|
@@ -102,6 +103,9 @@ export class AddressVerificator {
|
|
|
102
103
|
catch { }
|
|
103
104
|
try {
|
|
104
105
|
const decodedBech32 = address.fromBech32(addy);
|
|
106
|
+
if (decodedBech32.prefix === network.bech32Opnet && decodedBech32.version === 16) {
|
|
107
|
+
return AddressTypes.P2OP;
|
|
108
|
+
}
|
|
105
109
|
if (decodedBech32.prefix === network.bech32) {
|
|
106
110
|
if (decodedBech32.version === 0 && decodedBech32.data.length === 20) {
|
|
107
111
|
return AddressTypes.P2WPKH;
|
|
@@ -13,12 +13,15 @@ export declare class EcKeyPair {
|
|
|
13
13
|
static getP2WPKHAddress(keyPair: ECPairInterface, network?: Network): string;
|
|
14
14
|
static tweakedPubKeyToAddress(tweakedPubKeyHex: string, network: Network): string;
|
|
15
15
|
static tweakedPubKeyBufferToAddress(tweakedPubKeyBuffer: Buffer | Uint8Array, network: Network): string;
|
|
16
|
+
static p2op(bytes: Buffer | Uint8Array, network?: Network, deploymentVersion?: number): string;
|
|
16
17
|
static xOnlyTweakedPubKeyToAddress(tweakedPubKeyHex: string, network: Network): string;
|
|
17
|
-
static tweakPublicKey(
|
|
18
|
+
static tweakPublicKey(pub: Uint8Array | Buffer | string): Buffer;
|
|
19
|
+
static tweakBatchSharedT(pubkeys: readonly Uint8Array[], tweakScalar: bigint): Uint8Array[];
|
|
18
20
|
static generateWallet(network?: Network): IWallet;
|
|
19
21
|
static verifyContractAddress(contractAddress: string, network?: Network): boolean;
|
|
20
22
|
static getLegacySegwitAddress(keyPair: ECPairInterface, network?: Network): string;
|
|
21
23
|
static getLegacyAddress(keyPair: ECPairInterface, network?: Network): string;
|
|
24
|
+
static getP2PKH(publicKey: Buffer | Uint8Array, network?: Network): string;
|
|
22
25
|
static getP2PKAddress(keyPair: ECPairInterface, network?: Network): string;
|
|
23
26
|
static generateRandomKeyPair(network?: Network): ECPairInterface;
|
|
24
27
|
static fromSeed(seed: Buffer, network?: Network): BIP32Interface;
|
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
import * as ecc from '@bitcoinerlab/secp256k1';
|
|
2
2
|
import bip32, { BIP32Factory } from 'bip32';
|
|
3
|
-
import { address, initEccLib, networks, payments,
|
|
3
|
+
import bitcoin, { address, fromOutputScript, initEccLib, networks, opcodes, payments, script, toXOnly, } from '@btc-vision/bitcoin';
|
|
4
4
|
import { ECPairFactory } from 'ecpair';
|
|
5
|
-
import {
|
|
5
|
+
import { secp256k1 } from '@noble/curves/secp256k1';
|
|
6
|
+
import { bytesToNumberBE, concatBytes, utf8ToBytes } from '@noble/curves/abstract/utils';
|
|
7
|
+
import { mod } from '@noble/curves/abstract/modular';
|
|
8
|
+
import { sha256 } from '@noble/hashes/sha2';
|
|
6
9
|
initEccLib(ecc);
|
|
7
10
|
const BIP32factory = typeof bip32 === 'function' ? bip32 : BIP32Factory;
|
|
8
11
|
if (!BIP32factory) {
|
|
9
12
|
throw new Error('Failed to load BIP32 library');
|
|
10
13
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
secp256k1.utils.precompute(8);
|
|
15
|
+
const { ProjectivePoint: Point, CURVE } = secp256k1;
|
|
16
|
+
const TAP_TAG = utf8ToBytes('TapTweak');
|
|
17
|
+
const TAP_TAG_HASH = sha256(TAP_TAG);
|
|
18
|
+
function tapTweakHash(x) {
|
|
19
|
+
return sha256(concatBytes(TAP_TAG_HASH, TAP_TAG_HASH, x));
|
|
20
|
+
}
|
|
15
21
|
export class EcKeyPair {
|
|
16
22
|
static fromWIF(wif, network = networks.bitcoin) {
|
|
17
23
|
return this.ECPair.fromWIF(wif, network);
|
|
@@ -77,6 +83,17 @@ export class EcKeyPair {
|
|
|
77
83
|
}
|
|
78
84
|
return address;
|
|
79
85
|
}
|
|
86
|
+
static p2op(bytes, network = networks.bitcoin, deploymentVersion = 0) {
|
|
87
|
+
const witnessProgram = Buffer.concat([
|
|
88
|
+
Buffer.from([deploymentVersion]),
|
|
89
|
+
bitcoin.crypto.hash160(Buffer.from(bytes)),
|
|
90
|
+
]);
|
|
91
|
+
if (witnessProgram.length < 2 || witnessProgram.length > 40) {
|
|
92
|
+
throw new Error('Witness program must be 2-40 bytes.');
|
|
93
|
+
}
|
|
94
|
+
const scriptData = script.compile([opcodes.OP_16, witnessProgram]);
|
|
95
|
+
return fromOutputScript(scriptData, network);
|
|
96
|
+
}
|
|
80
97
|
static xOnlyTweakedPubKeyToAddress(tweakedPubKeyHex, network) {
|
|
81
98
|
if (tweakedPubKeyHex.startsWith('0x')) {
|
|
82
99
|
tweakedPubKeyHex = tweakedPubKeyHex.slice(2);
|
|
@@ -91,23 +108,26 @@ export class EcKeyPair {
|
|
|
91
108
|
}
|
|
92
109
|
return address;
|
|
93
110
|
}
|
|
94
|
-
static tweakPublicKey(
|
|
95
|
-
if (typeof
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
P = P.negate();
|
|
104
|
-
}
|
|
105
|
-
const x = P.toRawBytes(true).slice(1);
|
|
106
|
-
const tHash = taggedHash('TapTweak', Buffer.from(x));
|
|
107
|
-
const t = mod(BigInt('0x' + Buffer.from(tHash).toString('hex')), CURVE.n);
|
|
108
|
-
const Q = P.add(Point.BASE.mul(t));
|
|
111
|
+
static tweakPublicKey(pub) {
|
|
112
|
+
if (typeof pub === 'string' && pub.startsWith('0x'))
|
|
113
|
+
pub = pub.slice(2);
|
|
114
|
+
const P = Point.fromHex(pub);
|
|
115
|
+
const Peven = (P.y & 1n) === 0n ? P : P.negate();
|
|
116
|
+
const xBytes = Peven.toRawBytes(true).subarray(1);
|
|
117
|
+
const tBytes = tapTweakHash(xBytes);
|
|
118
|
+
const t = mod(bytesToNumberBE(tBytes), CURVE.n);
|
|
119
|
+
const Q = Peven.add(Point.BASE.multiply(t));
|
|
109
120
|
return Buffer.from(Q.toRawBytes(true));
|
|
110
121
|
}
|
|
122
|
+
static tweakBatchSharedT(pubkeys, tweakScalar) {
|
|
123
|
+
const T = Point.BASE.multiply(tweakScalar);
|
|
124
|
+
return pubkeys.map((bytes) => {
|
|
125
|
+
const P = Point.fromHex(bytes);
|
|
126
|
+
const P_even = P.hasEvenY() ? P : P.negate();
|
|
127
|
+
const Q = P_even.add(T);
|
|
128
|
+
return Q.toRawBytes(true);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
111
131
|
static generateWallet(network = networks.bitcoin) {
|
|
112
132
|
const keyPair = this.ECPair.makeRandom({
|
|
113
133
|
network: network,
|
|
@@ -142,6 +162,13 @@ export class EcKeyPair {
|
|
|
142
162
|
}
|
|
143
163
|
return wallet.address;
|
|
144
164
|
}
|
|
165
|
+
static getP2PKH(publicKey, network = networks.bitcoin) {
|
|
166
|
+
const wallet = payments.p2pkh({ pubkey: Buffer.from(publicKey), network: network });
|
|
167
|
+
if (!wallet.address) {
|
|
168
|
+
throw new Error('Failed to generate wallet');
|
|
169
|
+
}
|
|
170
|
+
return wallet.address;
|
|
171
|
+
}
|
|
145
172
|
static getP2PKAddress(keyPair, network = networks.bitcoin) {
|
|
146
173
|
const wallet = payments.p2pk({ pubkey: Buffer.from(keyPair.publicKey), network: network });
|
|
147
174
|
if (!wallet.output) {
|
|
@@ -13,9 +13,17 @@ export class TransactionFactory {
|
|
|
13
13
|
if (!interactionParameters.from) {
|
|
14
14
|
throw new Error('Field "from" not provided.');
|
|
15
15
|
}
|
|
16
|
+
if (!interactionParameters.utxos[0]) {
|
|
17
|
+
throw new Error('Missing at least one UTXO.');
|
|
18
|
+
}
|
|
19
|
+
if (!('signer' in interactionParameters)) {
|
|
20
|
+
throw new Error('Field "signer" not provided, OP_WALLET not detected.');
|
|
21
|
+
}
|
|
22
|
+
const inputs = this.parseOptionalInputs(interactionParameters.optionalInputs);
|
|
16
23
|
const preTransaction = new CustomScriptTransaction({
|
|
17
24
|
...interactionParameters,
|
|
18
25
|
utxos: [interactionParameters.utxos[0]],
|
|
26
|
+
optionalInputs: inputs,
|
|
19
27
|
});
|
|
20
28
|
await preTransaction.generateTransactionMinimalSignatures();
|
|
21
29
|
const parameters = await preTransaction.getFundingTransactionParameters();
|
|
@@ -33,17 +41,24 @@ export class TransactionFactory {
|
|
|
33
41
|
throw new Error('Could not sign funding transaction.');
|
|
34
42
|
}
|
|
35
43
|
parameters.estimatedFees = feeEstimationFundingTransaction.estimatedFees;
|
|
36
|
-
const signedTransaction = await this.createFundTransaction(
|
|
44
|
+
const signedTransaction = await this.createFundTransaction({
|
|
45
|
+
...parameters,
|
|
46
|
+
optionalOutputs: [],
|
|
47
|
+
optionalInputs: [],
|
|
48
|
+
});
|
|
37
49
|
if (!signedTransaction) {
|
|
38
50
|
throw new Error('Could not sign funding transaction.');
|
|
39
51
|
}
|
|
40
52
|
interactionParameters.utxos = this.getUTXOAsTransaction(signedTransaction.tx, interactionParameters.to, 0);
|
|
41
53
|
const newParams = {
|
|
42
54
|
...interactionParameters,
|
|
43
|
-
utxos:
|
|
55
|
+
utxos: [
|
|
56
|
+
...this.getUTXOAsTransaction(signedTransaction.tx, interactionParameters.to, 0),
|
|
57
|
+
],
|
|
44
58
|
randomBytes: preTransaction.getRndBytes(),
|
|
45
59
|
nonWitnessUtxo: signedTransaction.tx.toBuffer(),
|
|
46
60
|
estimatedFees: preTransaction.estimatedFees,
|
|
61
|
+
optionalInputs: inputs,
|
|
47
62
|
};
|
|
48
63
|
const finalTransaction = new CustomScriptTransaction(newParams);
|
|
49
64
|
const outTx = await finalTransaction.signTransaction();
|
|
@@ -194,9 +209,8 @@ export class TransactionFactory {
|
|
|
194
209
|
};
|
|
195
210
|
return {
|
|
196
211
|
transaction: [signedTransaction.toHex(), outTx.toHex()],
|
|
197
|
-
contractAddress: finalTransaction.
|
|
212
|
+
contractAddress: finalTransaction.getContractAddress(),
|
|
198
213
|
contractPubKey: finalTransaction.contractPubKey,
|
|
199
|
-
p2trAddress: finalTransaction.p2trAddress,
|
|
200
214
|
utxos: [refundUTXO],
|
|
201
215
|
preimage: preTransaction.getPreimage().toString('hex'),
|
|
202
216
|
};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { IDeploymentParameters, IInteractionParameters } from '../interfaces/ITransactionParameters.js';
|
|
2
2
|
import { UTXO } from '../../utxo/interfaces/IUTXO.js';
|
|
3
3
|
import { DeploymentResult, InteractionResponse } from '../TransactionFactory';
|
|
4
|
+
import { ICustomTransactionParameters } from '../builders/CustomScriptTransaction.js';
|
|
4
5
|
export type InteractionParametersWithoutSigner = Omit<IInteractionParameters, 'signer' | 'preimage'>;
|
|
5
6
|
export type IDeploymentParametersWithoutSigner = Omit<IDeploymentParameters, 'signer' | 'network' | 'preimage'>;
|
|
7
|
+
export type CustomTransactionWithoutSigner = Omit<ICustomTransactionParameters, 'signer' | 'preimage'>;
|
|
6
8
|
export interface BroadcastTransactionOptions {
|
|
7
9
|
raw: string;
|
|
8
10
|
psbt: boolean;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { Payment, Psbt, Stack } from '@btc-vision/bitcoin';
|
|
1
|
+
import { Payment, Psbt, PsbtInput, Stack } from '@btc-vision/bitcoin';
|
|
2
2
|
import { TransactionType } from '../enums/TransactionType.js';
|
|
3
3
|
import { TapLeafScript } from '../interfaces/Tap.js';
|
|
4
4
|
import { SharedInteractionParameters } from '../interfaces/ITransactionParameters.js';
|
|
5
5
|
import { TransactionBuilder } from './TransactionBuilder.js';
|
|
6
6
|
export interface ICustomTransactionParameters extends SharedInteractionParameters {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
script: (Buffer | Stack)[];
|
|
8
|
+
witnesses: Buffer[];
|
|
9
|
+
annex?: Buffer;
|
|
10
|
+
to: string;
|
|
10
11
|
}
|
|
11
12
|
export declare class CustomScriptTransaction extends TransactionBuilder<TransactionType.CUSTOM_CODE> {
|
|
12
13
|
type: TransactionType.CUSTOM_CODE;
|
|
@@ -21,6 +22,7 @@ export declare class CustomScriptTransaction extends TransactionBuilder<Transact
|
|
|
21
22
|
private readonly contractSigner;
|
|
22
23
|
private readonly randomBytes;
|
|
23
24
|
private readonly witnesses;
|
|
25
|
+
private readonly annexData?;
|
|
24
26
|
constructor(parameters: ICustomTransactionParameters);
|
|
25
27
|
get scriptAddress(): string;
|
|
26
28
|
get p2trAddress(): string;
|
|
@@ -30,6 +32,7 @@ export declare class CustomScriptTransaction extends TransactionBuilder<Transact
|
|
|
30
32
|
protected signInputs(transaction: Psbt): Promise<void>;
|
|
31
33
|
protected generateScriptAddress(): Payment;
|
|
32
34
|
protected generateTapData(): Payment;
|
|
35
|
+
protected getScriptSolution(input: PsbtInput): Buffer[];
|
|
33
36
|
private getContractSeed;
|
|
34
37
|
private customFinalizer;
|
|
35
38
|
private getPubKeys;
|
|
@@ -12,14 +12,20 @@ export class CustomScriptTransaction extends TransactionBuilder {
|
|
|
12
12
|
this.tapLeafScript = null;
|
|
13
13
|
this.targetScriptRedeem = null;
|
|
14
14
|
this.leftOverFundsScriptRedeem = null;
|
|
15
|
-
this.customFinalizer = (_inputIndex,
|
|
15
|
+
this.customFinalizer = (_inputIndex, input) => {
|
|
16
16
|
if (!this.tapLeafScript) {
|
|
17
17
|
throw new Error('Tap leaf script is required');
|
|
18
18
|
}
|
|
19
|
-
const scriptSolution = this.
|
|
19
|
+
const scriptSolution = this.getScriptSolution(input);
|
|
20
20
|
const witness = scriptSolution
|
|
21
21
|
.concat(this.tapLeafScript.script)
|
|
22
22
|
.concat(this.tapLeafScript.controlBlock);
|
|
23
|
+
if (this.annexData && this.annexData.length > 0) {
|
|
24
|
+
const annex = this.annexData[0] === 0x50
|
|
25
|
+
? this.annexData
|
|
26
|
+
: Buffer.concat([Buffer.from([0x50]), this.annexData]);
|
|
27
|
+
witness.push(annex);
|
|
28
|
+
}
|
|
23
29
|
return {
|
|
24
30
|
finalScriptWitness: TransactionBuilder.witnessStackToScriptWitness(witness),
|
|
25
31
|
};
|
|
@@ -86,7 +92,10 @@ export class CustomScriptTransaction extends TransactionBuilder {
|
|
|
86
92
|
}
|
|
87
93
|
for (let i = 0; i < transaction.data.inputs.length; i++) {
|
|
88
94
|
if (i === 0) {
|
|
89
|
-
|
|
95
|
+
try {
|
|
96
|
+
transaction.signInput(0, this.contractSigner);
|
|
97
|
+
}
|
|
98
|
+
catch (e) { }
|
|
90
99
|
transaction.signInput(0, this.getSignerKey());
|
|
91
100
|
transaction.finalizeInput(0, this.customFinalizer);
|
|
92
101
|
}
|
|
@@ -120,6 +129,18 @@ export class CustomScriptTransaction extends TransactionBuilder {
|
|
|
120
129
|
redeem: selectedRedeem,
|
|
121
130
|
};
|
|
122
131
|
}
|
|
132
|
+
getScriptSolution(input) {
|
|
133
|
+
if (!input.tapScriptSig) {
|
|
134
|
+
throw new Error('Tap script signature is required');
|
|
135
|
+
}
|
|
136
|
+
const witnesses = [...this.witnesses];
|
|
137
|
+
if (input.tapScriptSig) {
|
|
138
|
+
for (const sig of input.tapScriptSig) {
|
|
139
|
+
witnesses.push(sig.signature);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return witnesses;
|
|
143
|
+
}
|
|
123
144
|
getContractSeed() {
|
|
124
145
|
return bitCrypto.hash256(this.randomBytes);
|
|
125
146
|
}
|
|
@@ -12,6 +12,7 @@ export declare class DeploymentTransaction extends TransactionBuilder<Transactio
|
|
|
12
12
|
protected readonly rewardChallenge: IMineableReward;
|
|
13
13
|
protected readonly _contractAddress: Address;
|
|
14
14
|
protected tapLeafScript: TapLeafScript | null;
|
|
15
|
+
private readonly deploymentVersion;
|
|
15
16
|
private targetScriptRedeem;
|
|
16
17
|
private leftOverFundsScriptRedeem;
|
|
17
18
|
private readonly compiledTargetScript;
|
|
@@ -23,12 +24,14 @@ export declare class DeploymentTransaction extends TransactionBuilder<Transactio
|
|
|
23
24
|
private readonly contractSigner;
|
|
24
25
|
private readonly _contractPubKey;
|
|
25
26
|
private readonly randomBytes;
|
|
27
|
+
private _computedAddress;
|
|
26
28
|
constructor(parameters: IDeploymentParameters);
|
|
27
29
|
get contractPubKey(): string;
|
|
28
30
|
get contractAddress(): Address;
|
|
29
31
|
get p2trAddress(): string;
|
|
30
32
|
getRndBytes(): Buffer;
|
|
31
33
|
getPreimage(): Buffer;
|
|
34
|
+
getContractAddress(): string;
|
|
32
35
|
protected contractSignerXOnlyPubKey(): Buffer;
|
|
33
36
|
protected buildTransaction(): Promise<void>;
|
|
34
37
|
protected signInputsWalletBased(transaction: Psbt): Promise<void>;
|
|
@@ -14,6 +14,7 @@ export class DeploymentTransaction extends TransactionBuilder {
|
|
|
14
14
|
super(parameters);
|
|
15
15
|
this.type = TransactionType.DEPLOYMENT;
|
|
16
16
|
this.tapLeafScript = null;
|
|
17
|
+
this.deploymentVersion = 0x00;
|
|
17
18
|
this.targetScriptRedeem = null;
|
|
18
19
|
this.leftOverFundsScriptRedeem = null;
|
|
19
20
|
this.customFinalizer = (_inputIndex, input) => {
|
|
@@ -70,6 +71,13 @@ export class DeploymentTransaction extends TransactionBuilder {
|
|
|
70
71
|
getPreimage() {
|
|
71
72
|
return this.preimage;
|
|
72
73
|
}
|
|
74
|
+
getContractAddress() {
|
|
75
|
+
if (this._computedAddress) {
|
|
76
|
+
return this._computedAddress;
|
|
77
|
+
}
|
|
78
|
+
this._computedAddress = EcKeyPair.p2op(this.contractSeed, this.network, this.deploymentVersion);
|
|
79
|
+
return this._computedAddress;
|
|
80
|
+
}
|
|
73
81
|
contractSignerXOnlyPubKey() {
|
|
74
82
|
return toXOnly(Buffer.from(this.contractSigner.publicKey));
|
|
75
83
|
}
|
|
@@ -105,7 +113,7 @@ export class DeploymentTransaction extends TransactionBuilder {
|
|
|
105
113
|
}
|
|
106
114
|
this.addOutput({
|
|
107
115
|
value: Number(amountToCA),
|
|
108
|
-
address: this.
|
|
116
|
+
address: this.getContractAddress(),
|
|
109
117
|
});
|
|
110
118
|
if (amountToCA === MINIMUM_AMOUNT_CA &&
|
|
111
119
|
amountSpent - MINIMUM_AMOUNT_CA > MINIMUM_AMOUNT_REWARD) {
|
|
@@ -5,7 +5,7 @@ import { AddressVerificator } from '../../keypair/AddressVerificator.js';
|
|
|
5
5
|
import { TweakedTransaction } from '../shared/TweakedTransaction.js';
|
|
6
6
|
initEccLib(ecc);
|
|
7
7
|
export const MINIMUM_AMOUNT_REWARD = 540n;
|
|
8
|
-
export const MINIMUM_AMOUNT_CA =
|
|
8
|
+
export const MINIMUM_AMOUNT_CA = 297n;
|
|
9
9
|
export class TransactionBuilder extends TweakedTransaction {
|
|
10
10
|
constructor(parameters) {
|
|
11
11
|
super(parameters);
|
|
@@ -386,6 +386,7 @@ export class TransactionBuilder extends TweakedTransaction {
|
|
|
386
386
|
}
|
|
387
387
|
}
|
|
388
388
|
TransactionBuilder.LOCK_LEAF_SCRIPT = script.compile([
|
|
389
|
-
opcodes.
|
|
389
|
+
opcodes.OP_FALSE,
|
|
390
|
+
opcodes.OP_VERIFY,
|
|
390
391
|
]);
|
|
391
392
|
TransactionBuilder.MINIMUM_DUST = 50n;
|
|
@@ -47,8 +47,8 @@ export class TweakedTransaction extends Logger {
|
|
|
47
47
|
}
|
|
48
48
|
function readVarInt() {
|
|
49
49
|
const varint = varuint.decode(Buffer, offset);
|
|
50
|
-
offset +=
|
|
51
|
-
return varint;
|
|
50
|
+
offset += varint.bytes;
|
|
51
|
+
return varint.numberValue || 0;
|
|
52
52
|
}
|
|
53
53
|
function readVarSlice() {
|
|
54
54
|
const len = readVarInt();
|