@btc-vision/transaction 1.1.16 → 1.2.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/_version.d.ts +1 -1
- package/browser/crypto/crypto-browser.d.ts +2 -2
- package/browser/index.js +1 -1
- package/browser/index.js.LICENSE.txt +2 -0
- package/browser/keypair/Address.d.ts +10 -0
- package/browser/keypair/Secp256k1PointDeriver.d.ts +16 -0
- package/browser/opnet.d.ts +2 -0
- package/browser/signer/SignerUtils.d.ts +5 -0
- package/browser/transaction/ContractAddress.d.ts +6 -0
- package/browser/transaction/builders/CustomScriptTransaction.d.ts +1 -1
- package/browser/transaction/builders/InteractionTransaction.d.ts +1 -1
- package/browser/transaction/builders/MultiSignTransaction.d.ts +3 -4
- package/browser/transaction/builders/SharedInteractionTransaction.d.ts +2 -3
- package/browser/transaction/shared/TweakedTransaction.d.ts +5 -6
- package/browser/utxo/interfaces/IUTXO.d.ts +1 -0
- package/browser/verification/TapscriptVerificator.d.ts +1 -2
- package/build/_version.d.ts +1 -1
- package/build/_version.js +1 -1
- package/build/buffer/BinaryReader.js +2 -2
- package/build/buffer/BinaryWriter.js +1 -1
- package/build/generators/AddressGenerator.js +1 -2
- package/build/generators/Generator.js +1 -2
- package/build/generators/builders/CalldataGenerator.js +1 -1
- package/build/generators/builders/LegacyCalldataGenerator.js +1 -1
- package/build/generators/builders/MultiSignGenerator.js +1 -2
- package/build/keypair/Address.d.ts +10 -0
- package/build/keypair/Address.js +76 -7
- package/build/keypair/AddressVerificator.js +5 -2
- package/build/keypair/EcKeyPair.js +3 -4
- package/build/keypair/MessageSigner.js +1 -2
- package/build/keypair/Secp256k1PointDeriver.d.ts +16 -0
- package/build/keypair/Secp256k1PointDeriver.js +80 -0
- package/build/keypair/Wallet.js +1 -2
- package/build/opnet.d.ts +2 -0
- package/build/opnet.js +2 -0
- package/build/signer/SignerUtils.d.ts +5 -0
- package/build/signer/SignerUtils.js +40 -0
- package/build/signer/TweakedSigner.js +1 -3
- package/build/transaction/ContractAddress.d.ts +6 -0
- package/build/transaction/ContractAddress.js +12 -0
- package/build/transaction/browser/extensions/UnisatSigner.js +5 -33
- package/build/transaction/browser/extensions/XverseSigner.js +5 -49
- package/build/transaction/builders/CustomScriptTransaction.d.ts +1 -1
- package/build/transaction/builders/CustomScriptTransaction.js +1 -2
- package/build/transaction/builders/DeploymentTransaction.js +2 -2
- package/build/transaction/builders/FundingTransaction.js +6 -1
- package/build/transaction/builders/InteractionTransaction.d.ts +1 -1
- package/build/transaction/builders/MultiSignTransaction.d.ts +3 -4
- package/build/transaction/builders/MultiSignTransaction.js +1 -2
- package/build/transaction/builders/SharedInteractionTransaction.d.ts +2 -3
- package/build/transaction/builders/SharedInteractionTransaction.js +1 -2
- package/build/transaction/builders/TransactionBuilder.js +14 -3
- package/build/transaction/shared/TweakedTransaction.d.ts +5 -6
- package/build/transaction/shared/TweakedTransaction.js +121 -94
- package/build/utils/BitcoinUtils.js +4 -4
- package/build/utils/BufferHelper.js +1 -1
- package/build/utxo/OPNetLimitedProvider.js +1 -0
- package/build/utxo/interfaces/IUTXO.d.ts +1 -0
- package/build/verification/TapscriptVerificator.d.ts +1 -2
- package/build/verification/TapscriptVerificator.js +1 -2
- package/package.json +9 -12
- package/src/_version.ts +1 -1
- package/src/buffer/BinaryReader.ts +2 -2
- package/src/buffer/BinaryWriter.ts +1 -1
- package/src/generators/AddressGenerator.ts +1 -2
- package/src/generators/Generator.ts +1 -2
- package/src/generators/builders/CalldataGenerator.ts +5 -1
- package/src/generators/builders/LegacyCalldataGenerator.ts +5 -1
- package/src/generators/builders/MultiSignGenerator.ts +1 -2
- package/src/keypair/Address.ts +106 -10
- package/src/keypair/AddressVerificator.ts +6 -2
- package/src/keypair/EcKeyPair.ts +14 -5
- package/src/keypair/MessageSigner.ts +1 -2
- package/src/keypair/Secp256k1PointDeriver.ts +170 -0
- package/src/keypair/Wallet.ts +1 -2
- package/src/opnet.ts +2 -0
- package/src/signer/SignerUtils.ts +66 -0
- package/src/signer/TweakedSigner.ts +1 -3
- package/src/transaction/ContractAddress.ts +13 -0
- package/src/transaction/TransactionFactory.ts +4 -258
- package/src/transaction/browser/extensions/UnisatSigner.ts +5 -41
- package/src/transaction/browser/extensions/XverseSigner.ts +9 -69
- package/src/transaction/builders/CustomScriptTransaction.ts +10 -3
- package/src/transaction/builders/DeploymentTransaction.ts +13 -3
- package/src/transaction/builders/FundingTransaction.ts +7 -2
- package/src/transaction/builders/InteractionTransaction.ts +1 -1
- package/src/transaction/builders/MultiSignTransaction.ts +2 -2
- package/src/transaction/builders/SharedInteractionTransaction.ts +1 -3
- package/src/transaction/builders/TransactionBuilder.ts +14 -2
- package/src/transaction/shared/TweakedTransaction.ts +186 -123
- package/src/utils/BitcoinUtils.ts +4 -4
- package/src/utils/BufferHelper.ts +1 -1
- package/src/utxo/OPNetLimitedProvider.ts +1 -0
- package/src/utxo/interfaces/IUTXO.ts +2 -0
- package/src/verification/TapscriptVerificator.ts +9 -3
package/src/keypair/Address.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { Network } from '@btc-vision/bitcoin';
|
|
2
|
-
import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
|
|
1
|
+
import { decompressPublicKey, Network, toXOnly, UncompressedPublicKey } from '@btc-vision/bitcoin';
|
|
3
2
|
import { ECPairInterface } from 'ecpair';
|
|
4
3
|
import { ADDRESS_BYTE_LENGTH } from '../utils/lengths.js';
|
|
5
4
|
import { AddressVerificator } from './AddressVerificator.js';
|
|
6
5
|
import { EcKeyPair } from './EcKeyPair.js';
|
|
6
|
+
import { ContractAddress } from '../transaction/ContractAddress.js';
|
|
7
7
|
|
|
8
8
|
const hexPattern = /^[0-9a-fA-F]+$/;
|
|
9
9
|
const isHexadecimal = (input: string): boolean => {
|
|
@@ -19,6 +19,8 @@ export class Address extends Uint8Array {
|
|
|
19
19
|
#network: Network | undefined;
|
|
20
20
|
#originalPublicKey: Uint8Array | undefined;
|
|
21
21
|
#keyPair: ECPairInterface | undefined;
|
|
22
|
+
#uncompressed: UncompressedPublicKey | undefined;
|
|
23
|
+
#tweakedUncompressed: Buffer | undefined;
|
|
22
24
|
|
|
23
25
|
public constructor(bytes?: ArrayLike<number>) {
|
|
24
26
|
super(ADDRESS_BYTE_LENGTH);
|
|
@@ -88,6 +90,19 @@ export class Address extends Uint8Array {
|
|
|
88
90
|
return new Address(bytes);
|
|
89
91
|
}
|
|
90
92
|
|
|
93
|
+
public static uncompressedToCompressed(publicKey: ArrayLike<number>): Buffer {
|
|
94
|
+
const buffer = Uint8Array.from(publicKey);
|
|
95
|
+
|
|
96
|
+
const x = buffer.slice(1, 33);
|
|
97
|
+
const y = buffer.slice(33);
|
|
98
|
+
|
|
99
|
+
const compressed = Buffer.alloc(33);
|
|
100
|
+
compressed[0] = 0x02 + (y[y.length - 1] & 0x01);
|
|
101
|
+
compressed.set(x, 1);
|
|
102
|
+
|
|
103
|
+
return compressed;
|
|
104
|
+
}
|
|
105
|
+
|
|
91
106
|
/**
|
|
92
107
|
* Converts the address to a hex string
|
|
93
108
|
* @returns {string} The hex string
|
|
@@ -104,6 +119,46 @@ export class Address extends Uint8Array {
|
|
|
104
119
|
return Buffer.from(this);
|
|
105
120
|
}
|
|
106
121
|
|
|
122
|
+
public toUncompressedHex(): string {
|
|
123
|
+
if (!this.#uncompressed) {
|
|
124
|
+
throw new Error('Public key not set');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return '0x' + this.#uncompressed.uncompressed.toString('hex');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
public toUncompressedBuffer(): Buffer {
|
|
131
|
+
if (!this.#uncompressed) {
|
|
132
|
+
throw new Error('Public key not set');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return this.#uncompressed.uncompressed;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
public toHybridPublicKeyHex(): string {
|
|
139
|
+
if (!this.#uncompressed) {
|
|
140
|
+
throw new Error('Public key not set');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return '0x' + this.#uncompressed.hybrid.toString('hex');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
public toHybridPublicKeyBuffer(): Buffer {
|
|
147
|
+
if (!this.#uncompressed) {
|
|
148
|
+
throw new Error('Public key not set');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return this.#uncompressed.hybrid;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
public originalPublicKeyBuffer(): Buffer {
|
|
155
|
+
if (!this.#originalPublicKey) {
|
|
156
|
+
throw new Error('Public key not set');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return Buffer.from(this.#originalPublicKey);
|
|
160
|
+
}
|
|
161
|
+
|
|
107
162
|
public equals(a: Address): boolean {
|
|
108
163
|
const b: Address = this as Address;
|
|
109
164
|
|
|
@@ -178,16 +233,11 @@ export class Address extends Uint8Array {
|
|
|
178
233
|
const buf = Buffer.alloc(ADDRESS_BYTE_LENGTH);
|
|
179
234
|
buf.set(publicKey);
|
|
180
235
|
|
|
236
|
+
this.#tweakedUncompressed = ContractAddress.generateHybridKeyFromHash(buf);
|
|
237
|
+
|
|
181
238
|
super.set(publicKey);
|
|
182
239
|
} else {
|
|
183
|
-
this
|
|
184
|
-
this.#keyPair = EcKeyPair.fromPublicKey(this.#originalPublicKey);
|
|
185
|
-
|
|
186
|
-
const tweakedBytes = toXOnly(
|
|
187
|
-
EcKeyPair.tweakPublicKey(Buffer.from(this.#originalPublicKey)),
|
|
188
|
-
);
|
|
189
|
-
|
|
190
|
-
super.set(tweakedBytes);
|
|
240
|
+
this.autoFormat(publicKey);
|
|
191
241
|
}
|
|
192
242
|
}
|
|
193
243
|
|
|
@@ -200,6 +250,13 @@ export class Address extends Uint8Array {
|
|
|
200
250
|
return AddressVerificator.isValidPublicKey(Buffer.from(this).toString('hex'), network);
|
|
201
251
|
}
|
|
202
252
|
|
|
253
|
+
/**
|
|
254
|
+
* Get the public key as address
|
|
255
|
+
*/
|
|
256
|
+
public p2pk(): string {
|
|
257
|
+
return this.toHex();
|
|
258
|
+
}
|
|
259
|
+
|
|
203
260
|
/**
|
|
204
261
|
* Get the address in p2wpkh format
|
|
205
262
|
* @param {Network} network The network
|
|
@@ -258,4 +315,43 @@ export class Address extends Uint8Array {
|
|
|
258
315
|
|
|
259
316
|
throw new Error('Public key not set');
|
|
260
317
|
}
|
|
318
|
+
|
|
319
|
+
public toTweakedHybridPublicKeyHex(): string {
|
|
320
|
+
if (!this.#tweakedUncompressed) {
|
|
321
|
+
throw new Error('Public key not set');
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return '0x' + this.#tweakedUncompressed.toString('hex');
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
public toTweakedHybridPublicKeyBuffer(): Buffer {
|
|
328
|
+
if (!this.#tweakedUncompressed) {
|
|
329
|
+
throw new Error('Public key not set');
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return this.#tweakedUncompressed;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
private autoFormat(publicKey: ArrayLike<number>): void {
|
|
336
|
+
const firstByte = publicKey[0];
|
|
337
|
+
|
|
338
|
+
if (firstByte === 0x03 || firstByte === 0x02) {
|
|
339
|
+
// do nothing
|
|
340
|
+
} else if (firstByte === 0x04 || firstByte === 0x06 || firstByte === 0x07) {
|
|
341
|
+
// uncompressed
|
|
342
|
+
publicKey = Address.uncompressedToCompressed(publicKey);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
this.#originalPublicKey = Uint8Array.from(publicKey);
|
|
346
|
+
this.#keyPair = EcKeyPair.fromPublicKey(this.#originalPublicKey);
|
|
347
|
+
this.#uncompressed = decompressPublicKey(this.#originalPublicKey);
|
|
348
|
+
|
|
349
|
+
const tweakedBytes: Buffer = toXOnly(
|
|
350
|
+
EcKeyPair.tweakPublicKey(Buffer.from(this.#originalPublicKey)),
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
this.#tweakedUncompressed = ContractAddress.generateHybridKeyFromHash(tweakedBytes);
|
|
354
|
+
|
|
355
|
+
super.set(tweakedBytes);
|
|
356
|
+
}
|
|
261
357
|
}
|
|
@@ -109,14 +109,18 @@ export class AddressVerificator {
|
|
|
109
109
|
return true;
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
const pubKeyBuffer = Buffer.from(input, 'hex');
|
|
113
|
+
if ((input.length === 130 && pubKeyBuffer[0] === 0x06) || pubKeyBuffer[0] === 0x07) {
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
|
|
112
117
|
if (input.length === 66 || input.length === 130) {
|
|
113
118
|
// Check if the input can be parsed as a valid public key
|
|
114
|
-
const pubKeyBuffer = Buffer.from(input, 'hex');
|
|
115
119
|
EcKeyPair.fromPublicKey(pubKeyBuffer, network);
|
|
116
120
|
|
|
117
121
|
return true;
|
|
118
122
|
}
|
|
119
|
-
} catch {
|
|
123
|
+
} catch (e) {
|
|
120
124
|
return false;
|
|
121
125
|
}
|
|
122
126
|
|
package/src/keypair/EcKeyPair.ts
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import * as ecc from '@bitcoinerlab/secp256k1';
|
|
2
2
|
import bip32, { BIP32API, BIP32Factory, BIP32Interface } from 'bip32';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
address,
|
|
5
|
+
initEccLib,
|
|
6
|
+
Network,
|
|
7
|
+
networks,
|
|
8
|
+
payments,
|
|
9
|
+
Signer,
|
|
10
|
+
taggedHash,
|
|
11
|
+
toXOnly,
|
|
12
|
+
} from '@btc-vision/bitcoin';
|
|
4
13
|
import { ECPairAPI, ECPairFactory, ECPairInterface } from 'ecpair';
|
|
5
14
|
import { IWallet } from './interfaces/IWallet.js';
|
|
6
15
|
import { CURVE, ProjectivePoint as Point } from '@noble/secp256k1';
|
|
7
|
-
import { taggedHash } from '@btc-vision/bitcoin/src/crypto.js';
|
|
8
|
-
import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
|
|
9
16
|
|
|
10
17
|
initEccLib(ecc);
|
|
11
18
|
|
|
@@ -154,8 +161,10 @@ export class EcKeyPair {
|
|
|
154
161
|
}
|
|
155
162
|
|
|
156
163
|
// Convert the tweaked public key hex string to a Buffer
|
|
157
|
-
let tweakedPubKeyBuffer = Buffer.from(tweakedPubKeyHex, 'hex');
|
|
158
|
-
if (tweakedPubKeyBuffer.length !== 32)
|
|
164
|
+
let tweakedPubKeyBuffer: Buffer = Buffer.from(tweakedPubKeyHex, 'hex');
|
|
165
|
+
if (tweakedPubKeyBuffer.length !== 32) {
|
|
166
|
+
tweakedPubKeyBuffer = toXOnly(tweakedPubKeyBuffer);
|
|
167
|
+
}
|
|
159
168
|
|
|
160
169
|
return EcKeyPair.tweakedPubKeyBufferToAddress(tweakedPubKeyBuffer, network);
|
|
161
170
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { ECPairInterface } from 'ecpair';
|
|
2
2
|
import * as ecc from '@bitcoinerlab/secp256k1';
|
|
3
|
-
import { crypto, Network } from '@btc-vision/bitcoin';
|
|
3
|
+
import { crypto, Network, toXOnly } from '@btc-vision/bitcoin';
|
|
4
4
|
import { TweakedSigner } from '../signer/TweakedSigner.js';
|
|
5
5
|
import { EcKeyPair } from './EcKeyPair.js';
|
|
6
|
-
import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
|
|
7
6
|
|
|
8
7
|
export interface SignedMessage {
|
|
9
8
|
readonly signature: Uint8Array;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
const P = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fn;
|
|
2
|
+
|
|
3
|
+
export class Secp256k1PointDeriver {
|
|
4
|
+
/**
|
|
5
|
+
* The maximum increments to try before giving up on deriving a valid x.
|
|
6
|
+
*/
|
|
7
|
+
private maxTries: number;
|
|
8
|
+
|
|
9
|
+
constructor(maxTries = 10000) {
|
|
10
|
+
this.maxTries = maxTries;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Main entry point.
|
|
15
|
+
* Given a 32-byte input, ensures we obtain a "real" x on secp256k1.
|
|
16
|
+
* - If the given x is valid, we use it.
|
|
17
|
+
* - Otherwise, we increment x (mod p) until we find one whose (x^3 + 7) is a quadratic residue.
|
|
18
|
+
* Then we return { x, y1, y2 } for that valid point.
|
|
19
|
+
*
|
|
20
|
+
* @param xBytes A 32-byte Uint8Array for the candidate x
|
|
21
|
+
* @param failOnInvalidX (optional) whether to throw if the given x is not a valid curve point, defaults to true
|
|
22
|
+
* @param maxTries (optional) number of increments to attempt, defaults to this.maxTries
|
|
23
|
+
* @returns An object { x: bigint; y1: bigint; y2: bigint } describing a valid curve point
|
|
24
|
+
* @throws If no valid x found within maxTries
|
|
25
|
+
*/
|
|
26
|
+
public findOrDeriveValidPoint(
|
|
27
|
+
xBytes: Uint8Array,
|
|
28
|
+
failOnInvalidX: boolean = true,
|
|
29
|
+
maxTries: number = this.maxTries,
|
|
30
|
+
): { x: bigint; y1: bigint; y2: bigint } {
|
|
31
|
+
if (xBytes.length !== 32) {
|
|
32
|
+
throw new Error('xBytes must be exactly 32 bytes.');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Convert input to a BigInt in [0, p-1]
|
|
36
|
+
let xCandidate = this.bytesToBigInt(xBytes) % P;
|
|
37
|
+
|
|
38
|
+
// 2. Loop up to maxTries to find a valid x
|
|
39
|
+
let sqrtVal = this.isValidX(xCandidate);
|
|
40
|
+
if (failOnInvalidX && sqrtVal === null) {
|
|
41
|
+
throw new Error(`The given x is not a valid curve point.`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let tries = 0;
|
|
45
|
+
while (sqrtVal === null) {
|
|
46
|
+
xCandidate = (xCandidate + 1n) % P;
|
|
47
|
+
sqrtVal = this.isValidX(xCandidate);
|
|
48
|
+
|
|
49
|
+
tries++;
|
|
50
|
+
if (tries > maxTries) {
|
|
51
|
+
throw new Error(`Could not find a valid X point within ${maxTries} increments.`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Now, sqrtVal is a valid 'r' for alpha = x^3 + 7
|
|
56
|
+
// The two roots for y are: r and p-r
|
|
57
|
+
const y1 = sqrtVal;
|
|
58
|
+
const y2 = (P - y1) % P;
|
|
59
|
+
|
|
60
|
+
return { x: xCandidate, y1, y2 };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Given two candidate y values, returns the one with the smaller y-coordinate.
|
|
65
|
+
* @param {bigint} y
|
|
66
|
+
* @param {bigint} y2
|
|
67
|
+
*/
|
|
68
|
+
public getCanonicalY(y: bigint, y2: bigint): bigint {
|
|
69
|
+
return y < y2 ? y : y2;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Creates a 65-byte "hybrid public key" from the specified x and y.
|
|
74
|
+
* - First byte:
|
|
75
|
+
* - 0x06 if y is even
|
|
76
|
+
* - 0x07 if y is odd
|
|
77
|
+
* - Next 32 bytes: x
|
|
78
|
+
* - Last 32 bytes: y
|
|
79
|
+
*
|
|
80
|
+
* @param x X-coordinate as a bigint
|
|
81
|
+
* @param y Y-coordinate as a bigint
|
|
82
|
+
* @returns A Uint8Array of length 65
|
|
83
|
+
*/
|
|
84
|
+
public getHybridPublicKey(x: bigint, y: bigint): Uint8Array {
|
|
85
|
+
// Determine prefix based on parity of y
|
|
86
|
+
const prefix = y % 2n === 0n ? 0x06 : 0x07;
|
|
87
|
+
|
|
88
|
+
// Convert x and y to 32-byte big-endian arrays
|
|
89
|
+
const xBytes = this.bigIntTo32Bytes(x);
|
|
90
|
+
const yBytes = this.bigIntTo32Bytes(y);
|
|
91
|
+
|
|
92
|
+
// Allocate 65 bytes: 1 for prefix + 32 for x + 32 for y
|
|
93
|
+
const hybrid = new Uint8Array(65);
|
|
94
|
+
hybrid[0] = prefix;
|
|
95
|
+
hybrid.set(xBytes, 1);
|
|
96
|
+
hybrid.set(yBytes, 33);
|
|
97
|
+
|
|
98
|
+
return hybrid;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Checks if (x^3 + 7) is a quadratic residue mod p.
|
|
103
|
+
* Returns the square root if it is, or null if not.
|
|
104
|
+
*/
|
|
105
|
+
private isValidX(x: bigint): bigint | null {
|
|
106
|
+
// alpha = (x^3 + 7) mod p
|
|
107
|
+
const alpha = (this.modPow(x, 3n, P) + 7n) % P;
|
|
108
|
+
return this.sqrtModP(alpha, P);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Computes base^exp (mod m) using exponentiation by squaring.
|
|
113
|
+
*/
|
|
114
|
+
private modPow(base: bigint, exp: bigint, m: bigint): bigint {
|
|
115
|
+
let result = 1n;
|
|
116
|
+
let cur = base % m;
|
|
117
|
+
let e = exp;
|
|
118
|
+
|
|
119
|
+
while (e > 0) {
|
|
120
|
+
if ((e & 1n) === 1n) {
|
|
121
|
+
result = (result * cur) % m;
|
|
122
|
+
}
|
|
123
|
+
cur = (cur * cur) % m;
|
|
124
|
+
e >>= 1n;
|
|
125
|
+
}
|
|
126
|
+
return result;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* sqrtModP(a, p):
|
|
131
|
+
* Attempts to compute the square root of `a` modulo prime `p`.
|
|
132
|
+
* Returns the root if it exists, or null if `a` is not a quadratic residue.
|
|
133
|
+
*
|
|
134
|
+
* Since p ≡ 3 (mod 4), we can do:
|
|
135
|
+
* sqrt(a) = a^((p+1)/4) mod p
|
|
136
|
+
*/
|
|
137
|
+
private sqrtModP(a: bigint, prime: bigint): bigint | null {
|
|
138
|
+
// Candidate root
|
|
139
|
+
const root = this.modPow(a, (prime + 1n) >> 2n, prime);
|
|
140
|
+
|
|
141
|
+
// Check if it's truly a root
|
|
142
|
+
if ((root * root) % prime !== a % prime) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
return root;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Convert a 32-byte Uint8Array (big-endian) to a BigInt.
|
|
150
|
+
*/
|
|
151
|
+
private bytesToBigInt(bytes: Uint8Array): bigint {
|
|
152
|
+
let b = 0n;
|
|
153
|
+
for (const byte of bytes) {
|
|
154
|
+
b = (b << 8n) | BigInt(byte);
|
|
155
|
+
}
|
|
156
|
+
return b;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Convert a BigInt to a 32-byte array (big-endian).
|
|
161
|
+
*/
|
|
162
|
+
private bigIntTo32Bytes(value: bigint): Uint8Array {
|
|
163
|
+
const bytes = new Uint8Array(32);
|
|
164
|
+
for (let i = 31; i >= 0; i--) {
|
|
165
|
+
bytes[i] = Number(value & 0xffn);
|
|
166
|
+
value >>= 8n;
|
|
167
|
+
}
|
|
168
|
+
return bytes;
|
|
169
|
+
}
|
|
170
|
+
}
|
package/src/keypair/Wallet.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { IWallet } from './interfaces/IWallet.js';
|
|
2
2
|
import { ECPairInterface } from 'ecpair';
|
|
3
3
|
import { EcKeyPair } from './EcKeyPair.js';
|
|
4
|
-
import { Network, networks } from '@btc-vision/bitcoin';
|
|
5
|
-
import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
|
|
4
|
+
import { Network, networks, toXOnly } from '@btc-vision/bitcoin';
|
|
6
5
|
import { Address } from './Address.js';
|
|
7
6
|
|
|
8
7
|
/**
|
package/src/opnet.ts
CHANGED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Helper functions
|
|
2
|
+
import { isP2TR, PsbtInput, pubkeyPositionInScript } from '@btc-vision/bitcoin';
|
|
3
|
+
|
|
4
|
+
export function isTaprootInput(input: PsbtInput): boolean {
|
|
5
|
+
return (
|
|
6
|
+
input &&
|
|
7
|
+
!!(
|
|
8
|
+
input.tapInternalKey ||
|
|
9
|
+
input.tapMerkleRoot ||
|
|
10
|
+
(input.tapLeafScript && input.tapLeafScript.length) ||
|
|
11
|
+
(input.tapBip32Derivation && input.tapBip32Derivation.length) ||
|
|
12
|
+
(input.witnessUtxo && isP2TR(input.witnessUtxo.script))
|
|
13
|
+
)
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function getInputRelevantScript(input: PsbtInput): Buffer | null {
|
|
18
|
+
if (input.redeemScript) {
|
|
19
|
+
return input.redeemScript;
|
|
20
|
+
}
|
|
21
|
+
if (input.witnessScript) {
|
|
22
|
+
return input.witnessScript;
|
|
23
|
+
}
|
|
24
|
+
if (input.witnessUtxo) {
|
|
25
|
+
return input.witnessUtxo.script;
|
|
26
|
+
}
|
|
27
|
+
if (input.nonWitnessUtxo) {
|
|
28
|
+
// Parse the full transaction from nonWitnessUtxo
|
|
29
|
+
/*const tx = Transaction.fromBuffer(input.nonWitnessUtxo);
|
|
30
|
+
// Retrieve the output referenced by the input index
|
|
31
|
+
const out = tx.outs[input.index];
|
|
32
|
+
if (!out) {
|
|
33
|
+
throw new Error(`No output at index ${input.index} in nonWitnessUtxo`);
|
|
34
|
+
}
|
|
35
|
+
return out.script;*/
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function canSignNonTaprootInput(input: PsbtInput, publicKey: Buffer): boolean {
|
|
41
|
+
if (
|
|
42
|
+
(input.nonWitnessUtxo &&
|
|
43
|
+
!input.redeemScript &&
|
|
44
|
+
!input.witnessScript &&
|
|
45
|
+
!input.witnessUtxo) ||
|
|
46
|
+
input.redeemScript
|
|
47
|
+
) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const script = getInputRelevantScript(input);
|
|
52
|
+
if (script) {
|
|
53
|
+
return pubkeyInScript(publicKey, script);
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Checks if a public key is present in a script.
|
|
60
|
+
* @param pubkey The public key to check.
|
|
61
|
+
* @param script The script to search in.
|
|
62
|
+
* @returns A boolean indicating whether the public key is present in the script.
|
|
63
|
+
*/
|
|
64
|
+
export function pubkeyInScript(pubkey: Buffer, script: Buffer): boolean {
|
|
65
|
+
return pubkeyPositionInScript(pubkey, script) !== -1;
|
|
66
|
+
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import * as ecc from '@bitcoinerlab/secp256k1';
|
|
2
|
-
import { initEccLib, Network, Signer } from '@btc-vision/bitcoin';
|
|
3
|
-
import { tapTweakHash } from '@btc-vision/bitcoin/src/payments/bip341.js';
|
|
4
|
-
import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
|
|
2
|
+
import { initEccLib, Network, Signer, tapTweakHash, toXOnly } from '@btc-vision/bitcoin';
|
|
5
3
|
import { ECPairInterface } from 'ecpair';
|
|
6
4
|
import { EcKeyPair } from '../keypair/EcKeyPair.js';
|
|
7
5
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Secp256k1PointDeriver } from '../keypair/Secp256k1PointDeriver.js';
|
|
2
|
+
|
|
3
|
+
class ContractAddressBase {
|
|
4
|
+
private readonly deriver: Secp256k1PointDeriver = new Secp256k1PointDeriver();
|
|
5
|
+
|
|
6
|
+
public generateHybridKeyFromHash(input: Buffer): Buffer {
|
|
7
|
+
const p = this.deriver.findOrDeriveValidPoint(input, false);
|
|
8
|
+
const y = this.deriver.getCanonicalY(p.y1, p.y2);
|
|
9
|
+
return Buffer.from(this.deriver.getHybridPublicKey(p.x, y));
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const ContractAddress = new ContractAddressBase();
|