@btc-vision/transaction 1.0.111 → 1.0.113

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/browser/_version.d.ts +1 -1
  2. package/browser/generators/Generator.d.ts +2 -0
  3. package/browser/index.js +1 -1
  4. package/browser/keypair/AddressVerificator.d.ts +15 -3
  5. package/browser/keypair/EcKeyPair.d.ts +9 -4
  6. package/browser/keypair/Wallet.d.ts +1 -0
  7. package/browser/signer/TweakedSigner.d.ts +2 -2
  8. package/browser/transaction/builders/FundingTransaction.d.ts +2 -1
  9. package/browser/transaction/builders/MultiSignTransaction.d.ts +2 -1
  10. package/browser/transaction/builders/SharedInteractionTransaction.d.ts +1 -1
  11. package/browser/transaction/builders/TransactionBuilder.d.ts +3 -2
  12. package/browser/transaction/shared/TweakedTransaction.d.ts +8 -7
  13. package/browser/verification/TapscriptVerificator.d.ts +1 -1
  14. package/build/_version.d.ts +1 -1
  15. package/build/_version.js +1 -1
  16. package/build/generators/Generator.d.ts +2 -0
  17. package/build/generators/Generator.js +5 -0
  18. package/build/generators/builders/CalldataGenerator.js +4 -2
  19. package/build/generators/builders/DeploymentGenerator.js +7 -4
  20. package/build/keypair/AddressVerificator.d.ts +15 -3
  21. package/build/keypair/AddressVerificator.js +74 -3
  22. package/build/keypair/EcKeyPair.d.ts +9 -4
  23. package/build/keypair/EcKeyPair.js +69 -7
  24. package/build/keypair/Wallet.d.ts +1 -0
  25. package/build/keypair/Wallet.js +5 -4
  26. package/build/signer/TweakedSigner.d.ts +2 -2
  27. package/build/signer/TweakedSigner.js +1 -1
  28. package/build/transaction/builders/CustomScriptTransaction.js +3 -3
  29. package/build/transaction/builders/DeploymentTransaction.js +4 -4
  30. package/build/transaction/builders/FundingTransaction.d.ts +2 -1
  31. package/build/transaction/builders/FundingTransaction.js +18 -4
  32. package/build/transaction/builders/MultiSignTransaction.d.ts +2 -1
  33. package/build/transaction/builders/SharedInteractionTransaction.d.ts +1 -1
  34. package/build/transaction/builders/SharedInteractionTransaction.js +4 -4
  35. package/build/transaction/builders/TransactionBuilder.d.ts +3 -2
  36. package/build/transaction/builders/TransactionBuilder.js +7 -2
  37. package/build/transaction/builders/UnwrapTransaction.js +2 -2
  38. package/build/transaction/shared/TweakedTransaction.d.ts +8 -7
  39. package/build/transaction/shared/TweakedTransaction.js +1 -1
  40. package/build/verification/TapscriptVerificator.d.ts +1 -1
  41. package/build/verification/TapscriptVerificator.js +2 -2
  42. package/package.json +2 -1
  43. package/src/_version.ts +1 -1
  44. package/src/generators/Generator.ts +12 -0
  45. package/src/generators/builders/CalldataGenerator.ts +6 -3
  46. package/src/generators/builders/DeploymentGenerator.ts +9 -4
  47. package/src/keypair/AddressVerificator.ts +144 -5
  48. package/src/keypair/EcKeyPair.ts +149 -13
  49. package/src/keypair/Wallet.ts +11 -3
  50. package/src/signer/TweakedSigner.ts +3 -2
  51. package/src/transaction/builders/CustomScriptTransaction.ts +6 -5
  52. package/src/transaction/builders/DeploymentTransaction.ts +6 -5
  53. package/src/transaction/builders/FundingTransaction.ts +18 -5
  54. package/src/transaction/builders/MultiSignTransaction.ts +6 -2
  55. package/src/transaction/builders/SharedInteractionTransaction.ts +5 -5
  56. package/src/transaction/builders/TransactionBuilder.ts +36 -10
  57. package/src/transaction/builders/UnwrapSegwitTransaction.ts +7 -3
  58. package/src/transaction/builders/UnwrapTransaction.ts +2 -2
  59. package/src/transaction/builders/WrapTransaction.ts +1 -6
  60. package/src/transaction/shared/TweakedTransaction.ts +12 -12
  61. package/src/verification/TapscriptVerificator.ts +3 -3
@@ -49,7 +49,7 @@ export class CustomScriptTransaction extends TransactionBuilder {
49
49
  return this.randomBytes;
50
50
  }
51
51
  contractSignerXOnlyPubKey() {
52
- return toXOnly(this.contractSigner.publicKey);
52
+ return toXOnly(Buffer.from(this.contractSigner.publicKey));
53
53
  }
54
54
  async buildTransaction() {
55
55
  if (!this.to) {
@@ -125,9 +125,9 @@ export class CustomScriptTransaction extends TransactionBuilder {
125
125
  return bitCrypto.hash256(this.randomBytes);
126
126
  }
127
127
  getPubKeys() {
128
- const pubkeys = [this.signer.publicKey];
128
+ const pubkeys = [Buffer.from(this.signer.publicKey)];
129
129
  if (this.contractSigner) {
130
- pubkeys.push(this.contractSigner.publicKey);
130
+ pubkeys.push(Buffer.from(this.contractSigner.publicKey));
131
131
  }
132
132
  return pubkeys;
133
133
  }
@@ -44,7 +44,7 @@ export class DeploymentTransaction extends TransactionBuilder {
44
44
  this.randomBytes = parameters.randomBytes || BitcoinUtils.rndBytes();
45
45
  this.contractSeed = this.getContractSeed();
46
46
  this.contractSigner = EcKeyPair.fromSeedKeyPair(this.contractSeed, this.network);
47
- this.deploymentGenerator = new DeploymentGenerator(this.internalPubKeyToXOnly(), this.contractSignerXOnlyPubKey(), this.network);
47
+ this.deploymentGenerator = new DeploymentGenerator(Buffer.from(this.signer.publicKey), this.contractSignerXOnlyPubKey(), this.network);
48
48
  this.compiledTargetScript = this.deploymentGenerator.compile(this.bytecode, this.randomBytes, this.calldata);
49
49
  this.scriptTree = this.getScriptTree();
50
50
  this.internalInit();
@@ -60,7 +60,7 @@ export class DeploymentTransaction extends TransactionBuilder {
60
60
  return this.randomBytes;
61
61
  }
62
62
  contractSignerXOnlyPubKey() {
63
- return toXOnly(this.contractSigner.publicKey);
63
+ return toXOnly(Buffer.from(this.contractSigner.publicKey));
64
64
  }
65
65
  async buildTransaction() {
66
66
  if (!this.to) {
@@ -156,9 +156,9 @@ export class DeploymentTransaction extends TransactionBuilder {
156
156
  return bitCrypto.hash256(buf);
157
157
  }
158
158
  getPubKeys() {
159
- const pubkeys = [this.signer.publicKey];
159
+ const pubkeys = [Buffer.from(this.signer.publicKey)];
160
160
  if (this.contractSigner) {
161
- pubkeys.push(this.contractSigner.publicKey);
161
+ pubkeys.push(Buffer.from(this.contractSigner.publicKey));
162
162
  }
163
163
  return pubkeys;
164
164
  }
@@ -2,6 +2,7 @@ import { TransactionType } from '../enums/TransactionType.js';
2
2
  import { IFundingTransactionParameters } from '../interfaces/ITransactionParameters.js';
3
3
  import { Signer } from 'bitcoinjs-lib';
4
4
  import { TransactionBuilder } from './TransactionBuilder.js';
5
+ import { ECPairInterface } from 'ecpair';
5
6
  export declare class FundingTransaction extends TransactionBuilder<TransactionType.FUNDING> {
6
7
  readonly type: TransactionType.FUNDING;
7
8
  protected amount: bigint;
@@ -9,5 +10,5 @@ export declare class FundingTransaction extends TransactionBuilder<TransactionTy
9
10
  constructor(parameters: IFundingTransactionParameters);
10
11
  protected buildTransaction(): Promise<void>;
11
12
  protected splitInputs(amountSpent: bigint): void;
12
- protected getSignerKey(): Signer;
13
+ protected getSignerKey(): Signer | ECPairInterface;
13
14
  }
@@ -16,6 +16,12 @@ export class FundingTransaction extends TransactionBuilder {
16
16
  if (this.splitInputsInto > 1) {
17
17
  this.splitInputs(this.amount);
18
18
  }
19
+ else if (this.isPubKeyDestination) {
20
+ this.addOutput({
21
+ value: Number(this.amount),
22
+ script: Buffer.from(this.to.slice(2), 'hex'),
23
+ });
24
+ }
19
25
  else {
20
26
  this.addOutput({
21
27
  value: Number(this.amount),
@@ -30,10 +36,18 @@ export class FundingTransaction extends TransactionBuilder {
30
36
  }
31
37
  const splitAmount = amountSpent / BigInt(this.splitInputsInto);
32
38
  for (let i = 0; i < this.splitInputsInto; i++) {
33
- this.addOutput({
34
- value: Number(splitAmount),
35
- address: this.to,
36
- });
39
+ if (this.isPubKeyDestination) {
40
+ this.addOutput({
41
+ value: Number(splitAmount),
42
+ script: Buffer.from(this.to.slice(2), 'hex'),
43
+ });
44
+ }
45
+ else {
46
+ this.addOutput({
47
+ value: Number(splitAmount),
48
+ address: this.to,
49
+ });
50
+ }
37
51
  }
38
52
  }
39
53
  getSignerKey() {
@@ -5,6 +5,7 @@ import { TransactionBuilder } from './TransactionBuilder.js';
5
5
  import { TransactionType } from '../enums/TransactionType.js';
6
6
  import { ITransactionParameters } from '../interfaces/ITransactionParameters.js';
7
7
  import { Address } from '@btc-vision/bsi-binary';
8
+ import { ECPairInterface } from 'ecpair';
8
9
  export interface MultiSignParameters extends Omit<ITransactionParameters, 'priorityFee' | 'signer'> {
9
10
  readonly pubkeys: Buffer[];
10
11
  readonly minimumSignatures: number;
@@ -37,7 +38,7 @@ export declare class MultiSignTransaction extends TransactionBuilder<Transaction
37
38
  constructor(parameters: MultiSignParameters);
38
39
  static fromBase64(params: MultiSignFromBase64Params): MultiSignTransaction;
39
40
  static verifyIfSigned(psbt: Psbt, signerPubKey: Buffer): boolean;
40
- static signPartial(psbt: Psbt, signer: Signer, originalInputCount: number, minimums: number[]): {
41
+ static signPartial(psbt: Psbt, signer: Signer | ECPairInterface, originalInputCount: number, minimums: number[]): {
41
42
  final: boolean;
42
43
  signed: boolean;
43
44
  };
@@ -16,7 +16,7 @@ export declare abstract class SharedInteractionTransaction<T extends Transaction
16
16
  protected calldataGenerator: CalldataGenerator;
17
17
  protected readonly calldata: Buffer;
18
18
  protected abstract readonly contractSecret: Buffer;
19
- protected readonly scriptSigner: Signer;
19
+ protected readonly scriptSigner: Signer | ECPairInterface;
20
20
  protected readonly disableAutoRefund: boolean;
21
21
  protected constructor(parameters: SharedInteractionParameters);
22
22
  getContractSecret(): Buffer;
@@ -32,7 +32,7 @@ export class SharedInteractionTransaction extends TransactionBuilder {
32
32
  this.calldata = Compressor.compress(parameters.calldata);
33
33
  this.randomBytes = parameters.randomBytes || BitcoinUtils.rndBytes();
34
34
  this.scriptSigner = this.generateKeyPairFromSeed();
35
- this.calldataGenerator = new CalldataGenerator(this.internalPubKeyToXOnly(), this.scriptSignerXOnlyPubKey(), this.network);
35
+ this.calldataGenerator = new CalldataGenerator(Buffer.from(this.signer.publicKey), this.scriptSignerXOnlyPubKey(), this.network);
36
36
  }
37
37
  getContractSecret() {
38
38
  return this.contractSecret;
@@ -46,7 +46,7 @@ export class SharedInteractionTransaction extends TransactionBuilder {
46
46
  return address.fromBech32(this.to).data;
47
47
  }
48
48
  scriptSignerXOnlyPubKey() {
49
- return toXOnly(this.scriptSigner.publicKey);
49
+ return toXOnly(Buffer.from(this.scriptSigner.publicKey));
50
50
  }
51
51
  generateKeyPairFromSeed() {
52
52
  return EcKeyPair.fromSeedKeyPair(this.randomBytes, this.network);
@@ -188,9 +188,9 @@ export class SharedInteractionTransaction extends TransactionBuilder {
188
188
  throw new Error('Failed to sign input');
189
189
  }
190
190
  getPubKeys() {
191
- const pubkeys = [this.signer.publicKey];
191
+ const pubkeys = [Buffer.from(this.signer.publicKey)];
192
192
  if (this.scriptSigner) {
193
- pubkeys.push(this.scriptSigner.publicKey);
193
+ pubkeys.push(Buffer.from(this.scriptSigner.publicKey));
194
194
  }
195
195
  return pubkeys;
196
196
  }
@@ -20,7 +20,7 @@ export declare abstract class TransactionBuilder<T extends TransactionType> exte
20
20
  protected readonly outputs: PsbtOutputExtended[];
21
21
  protected feeOutput: PsbtOutputExtended | null;
22
22
  protected totalInputAmount: bigint;
23
- protected readonly signer: Signer;
23
+ protected readonly signer: Signer | ECPairInterface;
24
24
  protected readonly network: Network;
25
25
  protected readonly feeRate: number;
26
26
  protected priorityFee: bigint;
@@ -28,8 +28,9 @@ export declare abstract class TransactionBuilder<T extends TransactionType> exte
28
28
  protected to: Address | undefined;
29
29
  protected from: Address;
30
30
  protected _maximumFeeRate: number;
31
+ protected isPubKeyDestination: boolean;
31
32
  protected constructor(parameters: ITransactionParameters);
32
- static getFrom(from: string | undefined, keypair: ECPairInterface, network: Network): Address;
33
+ static getFrom(from: string | undefined, keypair: ECPairInterface | Signer, network: Network): Address;
33
34
  static witnessStackToScriptWitness(witness: Buffer[]): Buffer;
34
35
  getFundingTransactionParameters(): Promise<IFundingTransactionParameters>;
35
36
  setDestinationAddress(address: Address): void;
@@ -25,6 +25,7 @@ export class TransactionBuilder extends TweakedTransaction {
25
25
  this.priorityFee = parameters.priorityFee ?? 0n;
26
26
  this.utxos = parameters.utxos;
27
27
  this.to = parameters.to || undefined;
28
+ this.isPubKeyDestination = this.to ? this.to.startsWith('0x') : false;
28
29
  this.optionalOutputs = parameters.optionalOutputs;
29
30
  this.from = TransactionBuilder.getFrom(parameters.from, this.signer, this.network);
30
31
  this.totalInputAmount = this.calculateTotalUTXOAmount();
@@ -87,7 +88,9 @@ export class TransactionBuilder extends TweakedTransaction {
87
88
  if (!this.utxos.length) {
88
89
  throw new Error('No UTXOs specified');
89
90
  }
90
- if (this.to && !EcKeyPair.verifyContractAddress(this.to, this.network)) {
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 && !EcKeyPair.verifyContractAddress(this.to, this.network)) {
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(toXOnly(this.signer.publicKey), this.scriptSignerXOnlyPubKey(), this.network);
36
+ this.calldataGenerator = new CalldataGenerator(Buffer.from(this.signer.publicKey), this.scriptSignerXOnlyPubKey(), this.network);
37
37
  this.compiledTargetScript = this.calldataGenerator.compile(this.calldata, this.contractSecret, [Features.UNWRAP]);
38
38
  this.scriptTree = this.getScriptTree();
39
39
  this.internalInit();
@@ -173,7 +173,7 @@ export class UnwrapTransaction extends SharedInteractionTransaction {
173
173
  }
174
174
  return [
175
175
  this.contractSecret,
176
- toXOnly(this.signer.publicKey),
176
+ toXOnly(Buffer.from(this.signer.publicKey)),
177
177
  input.tapScriptSig[0].signature,
178
178
  input.tapScriptSig[1].signature,
179
179
  ];
@@ -1,11 +1,12 @@
1
1
  import { Logger } from '@btc-vision/logger';
2
2
  import { Network, Payment, Psbt, Signer, Transaction } from 'bitcoinjs-lib';
3
+ import { ECPairInterface } from 'ecpair';
3
4
  import { PsbtInput } from 'bip174/src/lib/interfaces.js';
4
5
  import { UTXO } from '../../utxo/interfaces/IUTXO.js';
5
6
  import { PsbtInputExtended, TapLeafScript } from '../interfaces/Tap.js';
6
7
  import { ChainId } from '../../network/ChainId.js';
7
8
  export interface ITweakedTransactionData {
8
- readonly signer: Signer;
9
+ readonly signer: Signer | ECPairInterface;
9
10
  readonly network: Network;
10
11
  readonly chainId?: ChainId;
11
12
  readonly nonWitnessUtxo?: Buffer;
@@ -17,8 +18,8 @@ export declare enum TransactionSequence {
17
18
  export declare abstract class TweakedTransaction extends Logger {
18
19
  readonly logColor: string;
19
20
  finalized: boolean;
20
- protected signer: Signer;
21
- protected tweakedSigner?: Signer;
21
+ protected signer: Signer | ECPairInterface;
22
+ protected tweakedSigner?: ECPairInterface;
22
23
  protected network: Network;
23
24
  protected signed: boolean;
24
25
  protected abstract readonly transaction: Psbt;
@@ -35,7 +36,7 @@ export declare abstract class TweakedTransaction extends Logger {
35
36
  protected constructor(data: ITweakedTransactionData);
36
37
  static readScriptWitnessToWitnessStack(Buffer: Buffer): Buffer[];
37
38
  static preEstimateTaprootTransactionFees(feeRate: bigint, numInputs: bigint, numOutputs: bigint, numWitnessElements: bigint, witnessElementSize: bigint, emptyWitness: bigint, taprootControlWitnessSize?: bigint, taprootScriptSize?: bigint): bigint;
38
- protected static signInput(transaction: Psbt, input: PsbtInput, i: number, signer: Signer, sighashTypes: number[]): void;
39
+ protected static signInput(transaction: Psbt, input: PsbtInput, i: number, signer: Signer | ECPairInterface, sighashTypes: number[]): void;
39
40
  protected static calculateSignHash(sighashTypes: number[]): number;
40
41
  ignoreSignatureError(): void;
41
42
  getScriptAddress(): string;
@@ -46,13 +47,13 @@ export declare abstract class TweakedTransaction extends Logger {
46
47
  preEstimateTransactionFees(feeRate: bigint, numInputs: bigint, numOutputs: bigint, numSignatures: bigint, numPubkeys: bigint): bigint;
47
48
  protected generateTapData(): Payment;
48
49
  protected generateScriptAddress(): Payment;
49
- protected getSignerKey(): Signer;
50
- protected signInput(transaction: Psbt, input: PsbtInput, i: number, signer?: Signer): Promise<void>;
50
+ protected getSignerKey(): Signer | ECPairInterface;
51
+ protected signInput(transaction: Psbt, input: PsbtInput, i: number, signer?: Signer | ECPairInterface): Promise<void>;
51
52
  protected splitArray<T>(arr: T[], chunkSize: number): T[][];
52
53
  protected signInputs(transaction: Psbt): Promise<void>;
53
54
  protected internalPubKeyToXOnly(): Buffer;
54
55
  protected internalInit(): void;
55
56
  protected tweakSigner(): void;
56
- protected getTweakedSigner(useTweakedHash?: boolean, signer?: Signer): Signer | undefined;
57
+ protected getTweakedSigner(useTweakedHash?: boolean, signer?: Signer | ECPairInterface): ECPairInterface | undefined;
57
58
  protected generatePsbtInputExtended(utxo: UTXO, i: number): PsbtInputExtended;
58
59
  }
@@ -232,7 +232,7 @@ export class TweakedTransaction extends Logger {
232
232
  }
233
233
  }
234
234
  internalPubKeyToXOnly() {
235
- return toXOnly(this.signer.publicKey);
235
+ return toXOnly(Buffer.from(this.signer.publicKey));
236
236
  }
237
237
  internalInit() {
238
238
  this.scriptData = payments.p2tr(this.generateScriptAddress());
@@ -1,7 +1,7 @@
1
1
  import { Network } from 'bitcoinjs-lib';
2
2
  import { Taptree } from 'bitcoinjs-lib/src/types.js';
3
3
  export interface ContractAddressVerificationParams {
4
- readonly deployerPubKeyXOnly: Buffer;
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.deployerPubKeyXOnly, toXOnly(params.contractSaltPubKey), network);
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.deployerPubKeyXOnly,
35
+ internalPubkey: toXOnly(params.deployerPubKey),
36
36
  network: network,
37
37
  scriptTree: scriptTree,
38
38
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@btc-vision/transaction",
3
3
  "type": "module",
4
- "version": "1.0.111",
4
+ "version": "1.0.113",
5
5
  "author": "BlobMaster41",
6
6
  "description": "OPNet transaction library allows you to create and sign transactions for the OPNet network.",
7
7
  "engines": {
@@ -95,6 +95,7 @@
95
95
  "@btc-vision/bsi-bitcoin-rpc": "^1.0.29",
96
96
  "@btc-vision/logger": "^1.0.6",
97
97
  "@eslint/js": "^9.10.0",
98
+ "@noble/secp256k1": "^1.7.1",
98
99
  "assert": "^2.1.0",
99
100
  "babel-loader": "^9.1.3",
100
101
  "babel-plugin-transform-import-meta": "^2.2.1",
package/src/_version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '1.0.111';
1
+ export const version = '1.0.113';
@@ -1,4 +1,5 @@
1
1
  import { Network, networks } from 'bitcoinjs-lib';
2
+ import { toXOnly } from 'bitcoinjs-lib/src/psbt/bip371.js';
2
3
 
3
4
  /** Bitcoin Script Generator */
4
5
  export abstract class Generator {
@@ -18,6 +19,12 @@ export abstract class Generator {
18
19
  */
19
20
  protected readonly senderPubKey: Buffer;
20
21
 
22
+ /**
23
+ * The public key of the sender
24
+ * @protected
25
+ */
26
+ protected readonly xSenderPubKey: Buffer;
27
+
21
28
  /**
22
29
  * The public key of the contract salt
23
30
  * @protected
@@ -38,6 +45,11 @@ export abstract class Generator {
38
45
  this.senderPubKey = senderPubKey;
39
46
  this.contractSaltPubKey = contractSaltPubKey;
40
47
  this.network = network;
48
+ this.xSenderPubKey = toXOnly(senderPubKey);
49
+ }
50
+
51
+ public get senderFirstByte(): Buffer {
52
+ return Buffer.from([this.senderPubKey[0], 0, 0, 0]);
41
53
  }
42
54
 
43
55
  /**
@@ -75,14 +75,17 @@ export class CalldataGenerator extends Generator {
75
75
  if (!dataChunks.length) throw new Error('No data chunks found');
76
76
 
77
77
  let compiledData = [
78
- this.senderPubKey,
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.senderPubKey),
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
- return [
46
- this.senderPubKey,
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.senderPubKey),
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
- ].flat();
77
+ ];
78
+
79
+ return compiledData.flat();
75
80
  }
76
81
  }
@@ -1,11 +1,29 @@
1
- import { address, initEccLib, networks } from 'bitcoinjs-lib';
1
+ import { address, initEccLib, Network } from 'bitcoinjs-lib';
2
2
  import * as ecc from '@bitcoinerlab/secp256k1';
3
3
  import { Address } from '@btc-vision/bsi-binary';
4
+ import { EcKeyPair } from './EcKeyPair.js';
4
5
 
5
6
  initEccLib(ecc);
6
7
 
8
+ export enum AddressTypes {
9
+ P2PKH = 'P2PKH',
10
+ P2SH = 'P2SH',
11
+ P2SH_P2WPKH = 'P2SH-P2WPKH',
12
+ P2SH_OR_P2SH_P2WPKH = 'P2SH_OR_P2SH-P2WPKH',
13
+ P2PK = 'P2PK',
14
+ P2TR = 'P2TR',
15
+ P2WPKH = 'P2WPKH',
16
+ }
17
+
7
18
  export class AddressVerificator {
8
- public static isValidP2TRAddress(inAddress: Address, network: networks.Network): boolean {
19
+ /**
20
+ * Checks if the given address is a valid P2PKH address.
21
+ * @param inAddress - The address to check.
22
+ * @param network - The network to validate against.
23
+ * @returns - True if the address is a valid P2PKH address, false otherwise.
24
+ * @remarks This method is useful for validating legacy addresses (P2PKH) without
25
+ */
26
+ public static isValidP2TRAddress(inAddress: Address, network: Network): boolean {
9
27
  if (!inAddress || inAddress.length < 50) return false;
10
28
 
11
29
  let isValidTapRootAddress: boolean = false;
@@ -14,12 +32,19 @@ export class AddressVerificator {
14
32
 
15
33
  const decodedAddress = address.fromBech32(inAddress);
16
34
  isValidTapRootAddress = decodedAddress.version === 1;
17
- } catch (e) {}
35
+ } catch {}
18
36
 
19
37
  return isValidTapRootAddress;
20
38
  }
21
39
 
22
- public static validatePKHAddress(inAddress: string, network: networks.Network): boolean {
40
+ /**
41
+ * Checks if the given address is a valid P2PKH address.
42
+ * @param inAddress - The address to check.
43
+ * @param network - The network to validate against.
44
+ * @returns - True if the address is a valid P2PKH address, false otherwise.
45
+ * @remarks This method is useful for validating legacy addresses (P2PKH) without
46
+ */
47
+ public static isP2WPKHAddress(inAddress: string, network: Network): boolean {
23
48
  if (!inAddress || inAddress.length < 20 || inAddress.length > 50) return false;
24
49
 
25
50
  let isValidSegWitAddress: boolean = false;
@@ -33,8 +58,122 @@ export class AddressVerificator {
33
58
  // Check if the address is P2WPKH (version 0)
34
59
  isValidSegWitAddress =
35
60
  decodedAddress.version === 0 && decodedAddress.data.length === 20;
36
- } catch (e) {}
61
+ } catch {}
37
62
 
38
63
  return isValidSegWitAddress;
39
64
  }
65
+
66
+ /**
67
+ * Checks if the given address is a valid P2PKH or P2SH address.
68
+ * @param addy - The address to check.
69
+ * @param network - The network to validate against.
70
+ * @returns - True if the address is a valid P2PKH or P2SH address, false otherwise.
71
+ * @remarks This method is useful for validating legacy addresses (P2PKH or P2SH) without
72
+ */
73
+ public static isP2PKHOrP2SH(addy: string, network: Network): boolean {
74
+ try {
75
+ // First, try to decode as a Base58Check address (P2PKH, P2SH, or P2SH-P2WPKH)
76
+ const decodedBase58 = address.fromBase58Check(addy);
77
+
78
+ if (decodedBase58.version === network.pubKeyHash) {
79
+ // P2PKH: Legacy address (starting with '1' for mainnet, 'm/n' for testnet)
80
+ return true;
81
+ }
82
+
83
+ return decodedBase58.version === network.scriptHash;
84
+ } catch (error) {
85
+ // If decoding fails or version is not valid, it's not a valid legacy address
86
+ return false;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Checks if the input is a valid hexadecimal public key (P2PK).
92
+ * Public keys can be compressed (66 characters) or uncompressed (130 characters).
93
+ *
94
+ * @param input - The input string to check.
95
+ * @param network - The Bitcoin network to validate against (mainnet, testnet, etc.).
96
+ * @returns - True if the input is a valid public key, false otherwise.
97
+ */
98
+ public static isValidPublicKey(input: string, network: Network): boolean {
99
+ try {
100
+ if (input.startsWith('0x')) {
101
+ input = input.slice(2);
102
+ }
103
+
104
+ // Compressed public keys are 66 characters long (0x02 or 0x03 prefix + 32 bytes)
105
+ // Uncompressed public keys are 130 characters long (0x04 prefix + 64 bytes)
106
+ const hexRegex = /^[0-9a-fA-F]+$/;
107
+
108
+ if ((input.length === 66 || input.length === 130) && hexRegex.test(input)) {
109
+ // Check if the input can be parsed as a valid public key
110
+ const pubKeyBuffer = Buffer.from(input, 'hex');
111
+ EcKeyPair.fromPublicKey(pubKeyBuffer, network);
112
+
113
+ return true;
114
+ }
115
+ } catch (error) {
116
+ console.log(error);
117
+
118
+ // If any error occurs (invalid public key, etc.), return false
119
+ return false;
120
+ }
121
+
122
+ return false; // Not a valid public key
123
+ }
124
+
125
+ /**
126
+ * Validates if a given Bitcoin address is of the specified type and network.
127
+ * - P2PKH (Legacy address starting with '1' for mainnet or 'm/n' for testnet)
128
+ * - P2SH (Legacy address starting with '3' for mainnet or '2' for testnet)
129
+ * - P2SH-P2WPKH (Wrapped SegWit)
130
+ * - P2PK (Pay to PubKey, technically treated similarly to P2PKH)
131
+ * - P2WPKH (SegWit address starting with 'bc1q' for mainnet or 'tb1q' for testnet)
132
+ * - P2TR (Taproot address starting with 'bc1p' for mainnet or 'tb1p' for testnet)
133
+ *
134
+ * @param addy - The Bitcoin address to validate.
135
+ * @param network - The Bitcoin network to validate against (mainnet, testnet, etc.).
136
+ * @returns - The type of the valid Bitcoin address, or null if invalid.
137
+ */
138
+ public static validateBitcoinAddress(addy: string, network: Network): AddressTypes | null {
139
+ if (AddressVerificator.isValidPublicKey(addy, network)) {
140
+ return AddressTypes.P2PK;
141
+ }
142
+
143
+ try {
144
+ // First, try to decode as a Base58Check address (P2PKH, P2SH, or P2SH-P2WPKH)
145
+ const decodedBase58 = address.fromBase58Check(addy);
146
+
147
+ if (decodedBase58.version === network.pubKeyHash) {
148
+ // P2PKH: Legacy address (starting with '1' for mainnet, 'm/n' for testnet)
149
+ return AddressTypes.P2PKH;
150
+ }
151
+ if (decodedBase58.version === network.scriptHash) {
152
+ // P2SH: Could be P2SH (general) or P2SH-P2WPKH (wrapped SegWit)
153
+ return AddressTypes.P2SH_OR_P2SH_P2WPKH;
154
+ }
155
+ } catch (error) {
156
+ // Ignore errors from Base58 decoding, it could be a Bech32 address
157
+ }
158
+
159
+ try {
160
+ // Try to decode as a Bech32 or Bech32m address (P2WPKH or P2TR)
161
+ const decodedBech32 = address.fromBech32(addy);
162
+
163
+ if (decodedBech32.prefix === network.bech32) {
164
+ // P2WPKH: SegWit address (starting with 'bc1q' for mainnet, 'tb1q' for testnet)
165
+ if (decodedBech32.version === 0 && decodedBech32.data.length === 20) {
166
+ return AddressTypes.P2WPKH;
167
+ }
168
+ // P2TR: Taproot address (starting with 'bc1p' for mainnet, 'tb1p' for testnet)
169
+ if (decodedBech32.version === 1 && decodedBech32.data.length === 32) {
170
+ return AddressTypes.P2TR;
171
+ }
172
+ }
173
+ } catch (error) {
174
+ // Ignore errors from Bech32/Bech32m decoding
175
+ }
176
+
177
+ return null; // Not a valid or recognized Bitcoin address type
178
+ }
40
179
  }