@btc-vision/bitcoin 6.4.0 → 6.4.2
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/address.d.ts +1 -0
- package/browser/index.js +1 -1
- package/browser/payments/index.d.ts +4 -0
- package/browser/payments/p2op.d.ts +2 -0
- package/browser/psbt/psbtutils.d.ts +1 -0
- package/build/address.d.ts +1 -0
- package/build/address.js +56 -8
- package/build/networks.js +6 -6
- package/build/payments/index.d.ts +4 -0
- package/build/payments/index.js +1 -0
- package/build/payments/p2op.d.ts +2 -0
- package/build/payments/p2op.js +108 -0
- package/build/psbt/psbtutils.d.ts +1 -0
- package/build/psbt/psbtutils.js +2 -0
- package/package.json +1 -1
- package/src/address.ts +68 -9
- package/src/block.ts +233 -233
- package/src/bufferutils.ts +188 -188
- package/src/networks.ts +6 -6
- package/src/payments/index.ts +68 -62
- package/src/payments/p2op.ts +136 -0
- package/src/psbt/psbtutils.ts +2 -0
- package/src/psbt.ts +2187 -2187
- package/test/address.spec.ts +155 -155
- package/test/bitcoin.core.spec.ts +212 -212
- package/test/block.spec.ts +171 -171
- package/test/bufferutils.spec.ts +450 -450
- package/test/crypto.spec.ts +49 -49
- package/test/integration/addresses.spec.ts +142 -142
- package/test/integration/bip32.spec.ts +130 -130
- package/test/integration/blocks.spec.ts +28 -28
- package/test/integration/cltv.spec.ts +241 -241
- package/test/integration/csv.spec.ts +452 -452
- package/test/integration/payments.spec.ts +110 -110
- package/test/integration/taproot.spec.ts +663 -663
- package/test/integration/transactions.spec.ts +668 -668
- package/test/payments.spec.ts +114 -114
- package/test/payments.utils.ts +165 -165
- package/test/psbt.spec.ts +1285 -1285
- package/test/script.spec.ts +186 -186
- package/test/script_number.spec.ts +26 -26
- package/test/script_signature.spec.ts +66 -66
- package/test/transaction.spec.ts +337 -337
- package/test/ts-node-register.js +7 -7
- package/test/types.spec.ts +53 -53
|
@@ -10,6 +10,7 @@ export * from './p2sh.js';
|
|
|
10
10
|
export * from './p2tr.js';
|
|
11
11
|
export * from './p2wpkh.js';
|
|
12
12
|
export * from './p2wsh.js';
|
|
13
|
+
export * from './p2op.js';
|
|
13
14
|
export interface Payment {
|
|
14
15
|
name?: string;
|
|
15
16
|
network?: Network;
|
|
@@ -31,6 +32,9 @@ export interface Payment {
|
|
|
31
32
|
witness?: Buffer[];
|
|
32
33
|
useHybrid?: boolean;
|
|
33
34
|
useUncompressed?: boolean;
|
|
35
|
+
program?: Buffer;
|
|
36
|
+
deploymentVersion?: number;
|
|
37
|
+
hash160?: Buffer;
|
|
34
38
|
}
|
|
35
39
|
export type PaymentCreator = (a: Payment, opts?: PaymentOpts) => Payment;
|
|
36
40
|
export type PaymentFunction = () => Payment;
|
|
@@ -6,6 +6,7 @@ export declare const isP2WPKH: (script: Buffer) => boolean;
|
|
|
6
6
|
export declare const isP2WSHScript: (script: Buffer) => boolean;
|
|
7
7
|
export declare const isP2SHScript: (script: Buffer) => boolean;
|
|
8
8
|
export declare const isP2TR: (script: Buffer) => boolean;
|
|
9
|
+
export declare const isP2OP: (script: Buffer) => boolean;
|
|
9
10
|
export declare function witnessStackToScriptWitness(witness: Buffer[]): Buffer;
|
|
10
11
|
export interface UncompressedPublicKey {
|
|
11
12
|
hybrid: Buffer;
|
package/build/address.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export interface Bech32Result {
|
|
|
8
8
|
prefix: string;
|
|
9
9
|
data: Buffer;
|
|
10
10
|
}
|
|
11
|
+
export declare function toFutureOPNetAddress(output: Buffer, network: Network): string;
|
|
11
12
|
export declare function fromBase58Check(address: string): Base58CheckResult;
|
|
12
13
|
export declare function fromBech32(address: string): Bech32Result;
|
|
13
14
|
export declare function toBase58Check(hash: Buffer, version: number): string;
|
package/build/address.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { bech32, bech32m } from 'bech32';
|
|
2
2
|
import * as bs58check from 'bs58check';
|
|
3
|
-
import { payments } from './index.js';
|
|
3
|
+
import { opcodes, payments } from './index.js';
|
|
4
4
|
import * as networks from './networks.js';
|
|
5
5
|
import * as bscript from './script.js';
|
|
6
6
|
import { Hash160bit, tuple, typeforce, UInt8 } from './types.js';
|
|
7
7
|
const FUTURE_SEGWIT_MAX_SIZE = 40;
|
|
8
8
|
const FUTURE_SEGWIT_MIN_SIZE = 2;
|
|
9
|
-
const FUTURE_SEGWIT_MAX_VERSION =
|
|
9
|
+
const FUTURE_SEGWIT_MAX_VERSION = 15;
|
|
10
|
+
const FUTURE_MAX_VERSION = 16;
|
|
10
11
|
const FUTURE_OPNET_VERSION = 16;
|
|
11
12
|
const FUTURE_SEGWIT_MIN_VERSION = 2;
|
|
12
13
|
const FUTURE_SEGWIT_VERSION_DIFF = 0x50;
|
|
@@ -14,15 +15,49 @@ const FUTURE_SEGWIT_VERSION_WARNING = 'WARNING: Sending to a future segwit versi
|
|
|
14
15
|
'End users MUST be warned carefully in the GUI and asked if they wish to proceed ' +
|
|
15
16
|
'with caution. Wallets should verify the segwit version from the output of fromBech32, ' +
|
|
16
17
|
'then decide when it is safe to use which version of segwit.';
|
|
18
|
+
export function toFutureOPNetAddress(output, network) {
|
|
19
|
+
if (!Buffer.isBuffer(output))
|
|
20
|
+
throw new TypeError('output must be a Buffer');
|
|
21
|
+
if (!network.bech32Opnet)
|
|
22
|
+
throw new Error('Network does not support opnet');
|
|
23
|
+
const opcode = output[0];
|
|
24
|
+
let pushPos = 1, progLen;
|
|
25
|
+
if (output[1] < 0x4c) {
|
|
26
|
+
progLen = output[1];
|
|
27
|
+
pushPos = 2;
|
|
28
|
+
}
|
|
29
|
+
else if (output[1] === 0x4c) {
|
|
30
|
+
progLen = output[2];
|
|
31
|
+
pushPos = 3;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
throw new TypeError('Unsupported push opcode in script');
|
|
35
|
+
}
|
|
36
|
+
const program = output.subarray(pushPos, pushPos + progLen);
|
|
37
|
+
if (program.length < FUTURE_SEGWIT_MIN_SIZE || program.length > FUTURE_SEGWIT_MAX_SIZE)
|
|
38
|
+
throw new TypeError('Invalid program length for segwit address');
|
|
39
|
+
const version = opcode === opcodes.OP_0
|
|
40
|
+
? 0
|
|
41
|
+
: opcode >= opcodes.OP_1 && opcode <= opcodes.OP_16
|
|
42
|
+
? opcode - (opcodes.OP_1 - 1)
|
|
43
|
+
: -1;
|
|
44
|
+
if (version < FUTURE_SEGWIT_MAX_VERSION || version > FUTURE_MAX_VERSION)
|
|
45
|
+
throw new TypeError(`Invalid segwit version ${version}`);
|
|
46
|
+
const words = [version, ...bech32m.toWords(program)];
|
|
47
|
+
return bech32m.encode(network.bech32Opnet, words);
|
|
48
|
+
}
|
|
17
49
|
function _toFutureSegwitAddress(output, network) {
|
|
18
|
-
const data = output.
|
|
19
|
-
if (data.length < FUTURE_SEGWIT_MIN_SIZE || data.length > FUTURE_SEGWIT_MAX_SIZE)
|
|
50
|
+
const data = output.subarray(2);
|
|
51
|
+
if (data.length < FUTURE_SEGWIT_MIN_SIZE || data.length > FUTURE_SEGWIT_MAX_SIZE) {
|
|
20
52
|
throw new TypeError('Invalid program length for segwit address');
|
|
53
|
+
}
|
|
21
54
|
const version = output[0] - FUTURE_SEGWIT_VERSION_DIFF;
|
|
22
|
-
if (version < FUTURE_SEGWIT_MIN_VERSION || version > FUTURE_SEGWIT_MAX_VERSION)
|
|
55
|
+
if (version < FUTURE_SEGWIT_MIN_VERSION || version > FUTURE_SEGWIT_MAX_VERSION) {
|
|
23
56
|
throw new TypeError('Invalid version for segwit address');
|
|
24
|
-
|
|
25
|
-
|
|
57
|
+
}
|
|
58
|
+
if (output[1] !== data.length) {
|
|
59
|
+
throw new TypeError(`Invalid script for segwit address ${output[1]} !== ${data.length}`);
|
|
60
|
+
}
|
|
26
61
|
return toBech32(data, version, network.bech32, network.bech32Opnet);
|
|
27
62
|
}
|
|
28
63
|
export function fromBase58Check(address) {
|
|
@@ -32,7 +67,7 @@ export function fromBase58Check(address) {
|
|
|
32
67
|
if (payload.length > 21)
|
|
33
68
|
throw new TypeError(address + ' is too long');
|
|
34
69
|
const version = payload.readUInt8(0);
|
|
35
|
-
const hash = payload.
|
|
70
|
+
const hash = payload.subarray(1);
|
|
36
71
|
return { version, hash };
|
|
37
72
|
}
|
|
38
73
|
export function fromBech32(address) {
|
|
@@ -97,6 +132,10 @@ export function fromOutputScript(output, network) {
|
|
|
97
132
|
return payments.p2tr({ output, network }).address;
|
|
98
133
|
}
|
|
99
134
|
catch (e) { }
|
|
135
|
+
try {
|
|
136
|
+
return toFutureOPNetAddress(output, network);
|
|
137
|
+
}
|
|
138
|
+
catch (e) { }
|
|
100
139
|
try {
|
|
101
140
|
return _toFutureSegwitAddress(output, network);
|
|
102
141
|
}
|
|
@@ -137,6 +176,15 @@ export function toOutputScript(address, network) {
|
|
|
137
176
|
if (decodeBech32.data.length === 32)
|
|
138
177
|
return payments.p2tr({ pubkey: decodeBech32.data }).output;
|
|
139
178
|
}
|
|
179
|
+
else if (decodeBech32.version === FUTURE_OPNET_VERSION) {
|
|
180
|
+
if (!network.bech32Opnet)
|
|
181
|
+
throw new Error(address + ' has an invalid prefix');
|
|
182
|
+
return payments.p2op({
|
|
183
|
+
program: decodeBech32.data,
|
|
184
|
+
deploymentVersion: decodeBech32.version,
|
|
185
|
+
network,
|
|
186
|
+
}).output;
|
|
187
|
+
}
|
|
140
188
|
else if (decodeBech32.version >= FUTURE_SEGWIT_MIN_VERSION &&
|
|
141
189
|
decodeBech32.version <= FUTURE_SEGWIT_MAX_VERSION &&
|
|
142
190
|
decodeBech32.data.length >= FUTURE_SEGWIT_MIN_SIZE &&
|
package/build/networks.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const bitcoin = {
|
|
2
2
|
messagePrefix: '\x18Bitcoin Signed Message:\n',
|
|
3
3
|
bech32: 'bc',
|
|
4
|
-
bech32Opnet: '
|
|
4
|
+
bech32Opnet: 'op',
|
|
5
5
|
bip32: {
|
|
6
6
|
public: 0x0488b21e,
|
|
7
7
|
private: 0x0488ade4,
|
|
@@ -13,7 +13,7 @@ export const bitcoin = {
|
|
|
13
13
|
export const regtest = {
|
|
14
14
|
messagePrefix: '\x18Bitcoin Signed Message:\n',
|
|
15
15
|
bech32: 'bcrt',
|
|
16
|
-
bech32Opnet: '
|
|
16
|
+
bech32Opnet: 'opr',
|
|
17
17
|
bip32: {
|
|
18
18
|
public: 0x043587cf,
|
|
19
19
|
private: 0x04358394,
|
|
@@ -25,7 +25,7 @@ export const regtest = {
|
|
|
25
25
|
export const testnet = {
|
|
26
26
|
messagePrefix: '\x18Bitcoin Signed Message:\n',
|
|
27
27
|
bech32: 'tb',
|
|
28
|
-
bech32Opnet: '
|
|
28
|
+
bech32Opnet: 'opt',
|
|
29
29
|
bip32: {
|
|
30
30
|
public: 0x043587cf,
|
|
31
31
|
private: 0x04358394,
|
|
@@ -61,7 +61,7 @@ export const dogecoinTestnet = {
|
|
|
61
61
|
export const litecoin = {
|
|
62
62
|
messagePrefix: '\x19Litecoin Signed Message:\n',
|
|
63
63
|
bech32: 'ltc',
|
|
64
|
-
bech32Opnet: '
|
|
64
|
+
bech32Opnet: 'opl',
|
|
65
65
|
bip32: {
|
|
66
66
|
public: 0x019da462,
|
|
67
67
|
private: 0x019d9cfe,
|
|
@@ -73,7 +73,7 @@ export const litecoin = {
|
|
|
73
73
|
export const litecoinTestnet = {
|
|
74
74
|
messagePrefix: '\x19Litecoin Signed Message:\n',
|
|
75
75
|
bech32: 'tltc',
|
|
76
|
-
bech32Opnet: '
|
|
76
|
+
bech32Opnet: 'oplt',
|
|
77
77
|
bip32: {
|
|
78
78
|
public: 0x0436ef7d,
|
|
79
79
|
private: 0x0436f6e1,
|
|
@@ -97,7 +97,7 @@ export const bitcoinCash = {
|
|
|
97
97
|
export const bitcoinCashTestnet = {
|
|
98
98
|
messagePrefix: '\x18Bitcoin Signed Message:\n',
|
|
99
99
|
bech32: 'bchtest',
|
|
100
|
-
bech32Opnet: '
|
|
100
|
+
bech32Opnet: 'opbcht',
|
|
101
101
|
bip32: {
|
|
102
102
|
public: 0x043587cf,
|
|
103
103
|
private: 0x04358394,
|
|
@@ -10,6 +10,7 @@ export * from './p2sh.js';
|
|
|
10
10
|
export * from './p2tr.js';
|
|
11
11
|
export * from './p2wpkh.js';
|
|
12
12
|
export * from './p2wsh.js';
|
|
13
|
+
export * from './p2op.js';
|
|
13
14
|
export interface Payment {
|
|
14
15
|
name?: string;
|
|
15
16
|
network?: Network;
|
|
@@ -31,6 +32,9 @@ export interface Payment {
|
|
|
31
32
|
witness?: Buffer[];
|
|
32
33
|
useHybrid?: boolean;
|
|
33
34
|
useUncompressed?: boolean;
|
|
35
|
+
program?: Buffer;
|
|
36
|
+
deploymentVersion?: number;
|
|
37
|
+
hash160?: Buffer;
|
|
34
38
|
}
|
|
35
39
|
export type PaymentCreator = (a: Payment, opts?: PaymentOpts) => Payment;
|
|
36
40
|
export type PaymentFunction = () => Payment;
|
package/build/payments/index.js
CHANGED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { bech32m } from 'bech32';
|
|
2
|
+
import { Buffer as NBuffer } from 'buffer';
|
|
3
|
+
import { fromBech32 } from '../address.js';
|
|
4
|
+
import { bitcoin as BITCOIN_NETWORK } from '../networks.js';
|
|
5
|
+
import * as bscript from '../script.js';
|
|
6
|
+
import { typeforce as typef } from '../types.js';
|
|
7
|
+
import * as lazy from './lazy.js';
|
|
8
|
+
const OPS = bscript.OPS;
|
|
9
|
+
const P2OP_WITNESS_VERSION = 0x10;
|
|
10
|
+
const MIN_SIZE = 2;
|
|
11
|
+
const MAX_SIZE = 40;
|
|
12
|
+
export function p2op(a, opts) {
|
|
13
|
+
if (!a.address && !a.output && !a.program)
|
|
14
|
+
throw new TypeError('Not enough data for p2op');
|
|
15
|
+
opts = Object.assign({ validate: true }, opts || {});
|
|
16
|
+
typef({
|
|
17
|
+
address: typef.maybe(typef.String),
|
|
18
|
+
output: typef.maybe(typef.Buffer),
|
|
19
|
+
program: typef.maybe(typef.Buffer),
|
|
20
|
+
network: typef.maybe(typef.Object),
|
|
21
|
+
deploymentVersion: typef.maybe(typef.Number),
|
|
22
|
+
hash160: typef.maybe(typef.BufferN(20)),
|
|
23
|
+
}, a);
|
|
24
|
+
const _address = lazy.value(() => fromBech32(a.address));
|
|
25
|
+
const network = a.network || BITCOIN_NETWORK;
|
|
26
|
+
const o = { name: 'p2op', network };
|
|
27
|
+
lazy.prop(o, 'program', () => {
|
|
28
|
+
if (a.program)
|
|
29
|
+
return a.program;
|
|
30
|
+
if (a.output) {
|
|
31
|
+
if (a.output[0] !== OPS.OP_16)
|
|
32
|
+
throw new TypeError('Invalid P2OP script');
|
|
33
|
+
let pushPos = 1, progLen;
|
|
34
|
+
if (a.output[1] < 0x4c) {
|
|
35
|
+
progLen = a.output[1];
|
|
36
|
+
pushPos = 2;
|
|
37
|
+
}
|
|
38
|
+
else if (a.output[1] === 0x4c) {
|
|
39
|
+
progLen = a.output[2];
|
|
40
|
+
pushPos = 3;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
throw new TypeError('Unsupported push opcode in P2OP script');
|
|
44
|
+
}
|
|
45
|
+
return a.output.slice(pushPos, pushPos + progLen);
|
|
46
|
+
}
|
|
47
|
+
if (a.address) {
|
|
48
|
+
const dec = _address();
|
|
49
|
+
return dec.data;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
lazy.prop(o, 'deploymentVersion', () => {
|
|
53
|
+
if (!o.program)
|
|
54
|
+
return;
|
|
55
|
+
return o.program[0];
|
|
56
|
+
});
|
|
57
|
+
lazy.prop(o, 'hash160', () => {
|
|
58
|
+
if (!o.program)
|
|
59
|
+
return;
|
|
60
|
+
return o.program.slice(1);
|
|
61
|
+
});
|
|
62
|
+
lazy.prop(o, 'output', () => {
|
|
63
|
+
if (!o.program)
|
|
64
|
+
return;
|
|
65
|
+
return bscript.compile([OPS.OP_16, o.program]);
|
|
66
|
+
});
|
|
67
|
+
lazy.prop(o, 'address', () => {
|
|
68
|
+
if (!o.program)
|
|
69
|
+
return;
|
|
70
|
+
if (!network.bech32Opnet) {
|
|
71
|
+
throw new TypeError('Network does not support opnet');
|
|
72
|
+
}
|
|
73
|
+
const words = bech32m.toWords(o.program);
|
|
74
|
+
words.unshift(P2OP_WITNESS_VERSION);
|
|
75
|
+
return bech32m.encode(network.bech32Opnet, words);
|
|
76
|
+
});
|
|
77
|
+
if (opts.validate) {
|
|
78
|
+
let prog = NBuffer.alloc(0);
|
|
79
|
+
if (a.address) {
|
|
80
|
+
const dec = _address();
|
|
81
|
+
if (network.bech32Opnet !== dec.prefix)
|
|
82
|
+
throw new TypeError('Invalid prefix or network mismatch');
|
|
83
|
+
if (dec.version !== P2OP_WITNESS_VERSION)
|
|
84
|
+
throw new TypeError('Invalid witness version for p2op');
|
|
85
|
+
if (dec.data.length < MIN_SIZE || dec.data.length > MAX_SIZE)
|
|
86
|
+
throw new TypeError('Invalid witness program length');
|
|
87
|
+
prog = dec.data;
|
|
88
|
+
}
|
|
89
|
+
if (a.program) {
|
|
90
|
+
if (prog.length && !prog.equals(a.program))
|
|
91
|
+
throw new TypeError('Program mismatch');
|
|
92
|
+
prog = a.program;
|
|
93
|
+
}
|
|
94
|
+
if (a.output) {
|
|
95
|
+
const outProg = o.program;
|
|
96
|
+
if (prog.length && !prog.equals(outProg))
|
|
97
|
+
throw new TypeError('Program mismatch (output vs other source)');
|
|
98
|
+
prog = outProg;
|
|
99
|
+
}
|
|
100
|
+
if (prog.length < MIN_SIZE || prog.length > MAX_SIZE)
|
|
101
|
+
throw new TypeError('Witness program must be 2–40 bytes');
|
|
102
|
+
if (a.deploymentVersion !== undefined && a.deploymentVersion !== prog[0])
|
|
103
|
+
throw new TypeError('deploymentVersion mismatch');
|
|
104
|
+
if (a.hash160 && !a.hash160.equals(prog.slice(1)))
|
|
105
|
+
throw new TypeError('hash160 mismatch');
|
|
106
|
+
}
|
|
107
|
+
return Object.assign(o, a);
|
|
108
|
+
}
|
|
@@ -6,6 +6,7 @@ export declare const isP2WPKH: (script: Buffer) => boolean;
|
|
|
6
6
|
export declare const isP2WSHScript: (script: Buffer) => boolean;
|
|
7
7
|
export declare const isP2SHScript: (script: Buffer) => boolean;
|
|
8
8
|
export declare const isP2TR: (script: Buffer) => boolean;
|
|
9
|
+
export declare const isP2OP: (script: Buffer) => boolean;
|
|
9
10
|
export declare function witnessStackToScriptWitness(witness: Buffer[]): Buffer;
|
|
10
11
|
export interface UncompressedPublicKey {
|
|
11
12
|
hybrid: Buffer;
|
package/build/psbt/psbtutils.js
CHANGED
|
@@ -11,6 +11,7 @@ import { p2wsh } from '../payments/p2wsh.js';
|
|
|
11
11
|
import * as bscript from '../script.js';
|
|
12
12
|
import { Transaction } from '../transaction.js';
|
|
13
13
|
import { toXOnly } from './bip371.js';
|
|
14
|
+
import { p2op } from '../payments/p2op.js';
|
|
14
15
|
function isPaymentFactory(payment) {
|
|
15
16
|
return (script) => {
|
|
16
17
|
try {
|
|
@@ -29,6 +30,7 @@ export const isP2WPKH = isPaymentFactory(p2wpkh);
|
|
|
29
30
|
export const isP2WSHScript = isPaymentFactory(p2wsh);
|
|
30
31
|
export const isP2SHScript = isPaymentFactory(p2sh);
|
|
31
32
|
export const isP2TR = isPaymentFactory(p2tr);
|
|
33
|
+
export const isP2OP = isPaymentFactory(p2op);
|
|
32
34
|
export function witnessStackToScriptWitness(witness) {
|
|
33
35
|
let buffer = Buffer.allocUnsafe(0);
|
|
34
36
|
function writeSlice(slice) {
|
package/package.json
CHANGED
package/src/address.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { bech32, bech32m } from 'bech32';
|
|
11
11
|
import * as bs58check from 'bs58check';
|
|
12
|
-
import { payments } from './index.js';
|
|
12
|
+
import { opcodes, payments } from './index.js';
|
|
13
13
|
import * as networks from './networks.js';
|
|
14
14
|
import { Network } from './networks.js';
|
|
15
15
|
import * as bscript from './script.js';
|
|
@@ -35,7 +35,8 @@ export interface Bech32Result {
|
|
|
35
35
|
|
|
36
36
|
const FUTURE_SEGWIT_MAX_SIZE: number = 40;
|
|
37
37
|
const FUTURE_SEGWIT_MIN_SIZE: number = 2;
|
|
38
|
-
const FUTURE_SEGWIT_MAX_VERSION: number =
|
|
38
|
+
const FUTURE_SEGWIT_MAX_VERSION: number = 15;
|
|
39
|
+
const FUTURE_MAX_VERSION: number = 16;
|
|
39
40
|
const FUTURE_OPNET_VERSION: number = 16;
|
|
40
41
|
const FUTURE_SEGWIT_MIN_VERSION: number = 2;
|
|
41
42
|
const FUTURE_SEGWIT_VERSION_DIFF: number = 0x50;
|
|
@@ -45,18 +46,66 @@ const FUTURE_SEGWIT_VERSION_WARNING: string =
|
|
|
45
46
|
'with caution. Wallets should verify the segwit version from the output of fromBech32, ' +
|
|
46
47
|
'then decide when it is safe to use which version of segwit.';
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Encode a future Taproot-style segwit address (SegWit v2 - v16) using bech32m.
|
|
51
|
+
* Only for versions not yet assigned specific meanings (future use).
|
|
52
|
+
*
|
|
53
|
+
* @param output - Output script buffer containing the version and witness program
|
|
54
|
+
* @param network - Network object containing bech32 and optional bech32Opnet prefix
|
|
55
|
+
* @returns Bech32m-encoded future Taproot-style address
|
|
56
|
+
*/
|
|
57
|
+
export function toFutureOPNetAddress(output: Buffer, network: Network): string {
|
|
58
|
+
if (!Buffer.isBuffer(output)) throw new TypeError('output must be a Buffer');
|
|
59
|
+
if (!network.bech32Opnet) throw new Error('Network does not support opnet');
|
|
60
|
+
|
|
61
|
+
const opcode = output[0];
|
|
62
|
+
|
|
63
|
+
// work out where the push-data really starts
|
|
64
|
+
let pushPos = 1,
|
|
65
|
+
progLen: number;
|
|
66
|
+
if (output[1] < 0x4c) {
|
|
67
|
+
progLen = output[1];
|
|
68
|
+
pushPos = 2;
|
|
69
|
+
} else if (output[1] === 0x4c) {
|
|
70
|
+
progLen = output[2];
|
|
71
|
+
pushPos = 3;
|
|
72
|
+
} else {
|
|
73
|
+
throw new TypeError('Unsupported push opcode in script');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const program = output.subarray(pushPos, pushPos + progLen);
|
|
50
77
|
|
|
51
|
-
if (
|
|
78
|
+
if (program.length < FUTURE_SEGWIT_MIN_SIZE || program.length > FUTURE_SEGWIT_MAX_SIZE)
|
|
52
79
|
throw new TypeError('Invalid program length for segwit address');
|
|
53
80
|
|
|
54
|
-
const version =
|
|
81
|
+
const version =
|
|
82
|
+
opcode === opcodes.OP_0
|
|
83
|
+
? 0
|
|
84
|
+
: opcode >= opcodes.OP_1 && opcode <= opcodes.OP_16
|
|
85
|
+
? opcode - (opcodes.OP_1 - 1)
|
|
86
|
+
: -1;
|
|
87
|
+
|
|
88
|
+
if (version < FUTURE_SEGWIT_MAX_VERSION || version > FUTURE_MAX_VERSION)
|
|
89
|
+
throw new TypeError(`Invalid segwit version ${version}`);
|
|
90
|
+
|
|
91
|
+
const words = [version, ...bech32m.toWords(program)];
|
|
92
|
+
return bech32m.encode(network.bech32Opnet, words);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function _toFutureSegwitAddress(output: Buffer, network: Network): string {
|
|
96
|
+
const data = output.subarray(2);
|
|
97
|
+
if (data.length < FUTURE_SEGWIT_MIN_SIZE || data.length > FUTURE_SEGWIT_MAX_SIZE) {
|
|
98
|
+
throw new TypeError('Invalid program length for segwit address');
|
|
99
|
+
}
|
|
55
100
|
|
|
56
|
-
|
|
101
|
+
const version = output[0] - FUTURE_SEGWIT_VERSION_DIFF;
|
|
102
|
+
if (version < FUTURE_SEGWIT_MIN_VERSION || version > FUTURE_SEGWIT_MAX_VERSION) {
|
|
57
103
|
throw new TypeError('Invalid version for segwit address');
|
|
104
|
+
}
|
|
58
105
|
|
|
59
|
-
if (output[1] !== data.length)
|
|
106
|
+
if (output[1] !== data.length) {
|
|
107
|
+
throw new TypeError(`Invalid script for segwit address ${output[1]} !== ${data.length}`);
|
|
108
|
+
}
|
|
60
109
|
|
|
61
110
|
return toBech32(data, version, network.bech32, network.bech32Opnet);
|
|
62
111
|
}
|
|
@@ -72,7 +121,7 @@ export function fromBase58Check(address: string): Base58CheckResult {
|
|
|
72
121
|
if (payload.length > 21) throw new TypeError(address + ' is too long');
|
|
73
122
|
|
|
74
123
|
const version = payload.readUInt8(0);
|
|
75
|
-
const hash = payload.
|
|
124
|
+
const hash = payload.subarray(1);
|
|
76
125
|
|
|
77
126
|
return { version, hash };
|
|
78
127
|
}
|
|
@@ -159,6 +208,9 @@ export function fromOutputScript(output: Buffer, network?: Network): string {
|
|
|
159
208
|
try {
|
|
160
209
|
return payments.p2tr({ output, network }).address as string;
|
|
161
210
|
} catch (e) {}
|
|
211
|
+
try {
|
|
212
|
+
return toFutureOPNetAddress(output, network);
|
|
213
|
+
} catch (e) {}
|
|
162
214
|
try {
|
|
163
215
|
return _toFutureSegwitAddress(output, network);
|
|
164
216
|
} catch (e) {}
|
|
@@ -203,6 +255,13 @@ export function toOutputScript(address: string, network?: Network): Buffer {
|
|
|
203
255
|
} else if (decodeBech32.version === 1) {
|
|
204
256
|
if (decodeBech32.data.length === 32)
|
|
205
257
|
return payments.p2tr({ pubkey: decodeBech32.data }).output as Buffer;
|
|
258
|
+
} else if (decodeBech32.version === FUTURE_OPNET_VERSION) {
|
|
259
|
+
if (!network.bech32Opnet) throw new Error(address + ' has an invalid prefix');
|
|
260
|
+
return payments.p2op({
|
|
261
|
+
program: decodeBech32.data,
|
|
262
|
+
deploymentVersion: decodeBech32.version,
|
|
263
|
+
network,
|
|
264
|
+
}).output as Buffer;
|
|
206
265
|
} else if (
|
|
207
266
|
decodeBech32.version >= FUTURE_SEGWIT_MIN_VERSION &&
|
|
208
267
|
decodeBech32.version <= FUTURE_SEGWIT_MAX_VERSION &&
|