Skip to content

WIP: p2tr_ns #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 74 commits into
base: p2tr-v1
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
574a287
chore: add bn.js to dependencies (previously it was present in devDep…
motorina0 Oct 28, 2021
7ae5368
feat: add liftX() function (first version)
motorina0 Oct 28, 2021
8fb2f0a
feat: update the Payment interface with taproot specific fields
motorina0 Oct 28, 2021
47eab68
feat: add first version of p2tr; basic logic for key path construct/s…
motorina0 Oct 28, 2021
38fe854
test: add first tests for p2tr
motorina0 Oct 28, 2021
1bf104e
feat: add generated files
motorina0 Oct 28, 2021
a598134
feat: compute "taproot output key" when "taoroot internal key" is kno…
motorina0 Oct 29, 2021
33c2bd9
tests: improve test coverage
motorina0 Oct 29, 2021
f04d8b2
feat: add function `computeMastRoot()`
motorina0 Nov 1, 2021
583174d
feat: compute p2tr hash based on the script tree
motorina0 Nov 1, 2021
1b96e6d
feat: add scriptsTree field to Payment interface; export p2tr
motorina0 Nov 1, 2021
fc1f1b2
feat: convert `scriptsTree` output to Buffer
motorina0 Nov 1, 2021
91ccef3
tests: add tests for script tree
motorina0 Nov 1, 2021
d840ab6
feat: add simple type for TaprootLeaf and TaprootNode
motorina0 Nov 1, 2021
fb7df4a
feat: check for hash mismatch between the input hash and the computed…
motorina0 Nov 1, 2021
af639f9
feat: validate witness data (partial)
motorina0 Nov 2, 2021
9947571
refactor: split `computeTweakFromScriptPath()` into `rootHash()` and …
motorina0 Nov 2, 2021
b8f8c91
feat: compute hash from witness control-block
motorina0 Nov 2, 2021
8fd07fc
refactor: extract taproot related logic to taproot.ts file
motorina0 Nov 2, 2021
c987b0c
chore: code format and lint
motorina0 Nov 2, 2021
2feff5d
refactor: compare GROUP_ORDER as buffer (instead of using BN.js)
motorina0 Nov 2, 2021
2f55aad
refactor: rename `rootHash` to `rootHashFromPath` and `computeMastRoo…
motorina0 Nov 3, 2021
2a4e64b
tests: add bib341 tests by @sipa; plus refactoring
motorina0 Nov 3, 2021
f7d01b8
refactor: extract `tapBranchHash()` rename `leafHash()` to `tapBranch…
motorina0 Nov 3, 2021
a3550c1
feat: build control-block as part of witness; update tests
motorina0 Nov 3, 2021
b1fca66
chore: lint & format; fix: discovered bug in findScriptPath() after lint
motorina0 Nov 3, 2021
ad2aec1
chore: code clean-up; fix o.scriptLeaf (needs tests)
motorina0 Nov 3, 2021
dcffed3
chore: update taggedHash() prefix after rebase
motorina0 Nov 12, 2021
d107161
fix: rebase issues
motorina0 Jan 12, 2022
4d2af06
chore: remove the bn.js dependency
motorina0 Jan 13, 2022
6cfbf65
refactor: use injectable ecc lib
motorina0 Jan 13, 2022
e64c2d8
chore: code format
motorina0 Jan 13, 2022
d987d8d
refactor: move taproot utils file
motorina0 Jan 13, 2022
9f51a1a
refactor: move non-exported function to the bottom
motorina0 Jan 13, 2022
bc6358f
fix: lint & gitdiff issues
motorina0 Jan 13, 2022
f61371c
chore: removed un-used exports
motorina0 Jan 13, 2022
4573e6c
chore: add docs, simplify code
motorina0 Jan 13, 2022
ddc1e8d
feat: pass the ECC library as an optional parameter to p2tr
motorina0 Jan 14, 2022
4cad59c
refactor: move eccLib to PaymentOptions
motorina0 Jan 18, 2022
c670698
fix: remove `TinySecp256k1Interface` from `PaymentCreator`
motorina0 Jan 20, 2022
b566dd7
chore: keep ecc test vectors to a minimum
motorina0 Jan 20, 2022
0eb9961
fix: taproot signature is 64 bytes
motorina0 Jan 27, 2022
5aa9345
feat: add Key Spend support for taproot to PSBT (more in commit descr…
motorina0 Jan 27, 2022
3ee59b7
chore: version bump
motorina0 Jan 27, 2022
fdb8698
feat: extract `tiny-secp256k1` out of the Psbt module
motorina0 Jan 28, 2022
b4d7760
chore: take the garbage out. Remove `tweakSigner()` and ecpair dep fr…
motorina0 Feb 10, 2022
6f1e7ea
feat: add taproot check for signInputAsync()
motorina0 Feb 18, 2022
9ef904c
feat: add stricter validation for taproot addresses
motorina0 Feb 18, 2022
d3e9aa6
fix: fix integration test
motorina0 Feb 18, 2022
7989b6a
Merge pull request #2 from bitcoincoretech/p2tr-v1-addr-check
motorina0 Feb 22, 2022
203974d
feat: correctly identify P2TR (pass `eccLib` to `toOutputScript` and …
motorina0 Feb 22, 2022
719e4a4
add integration tests for taproot (#3)
motorina0 Mar 9, 2022
72a0b8f
refactor: move tapscript finalizer check; add unit test
motorina0 Mar 9, 2022
ceea629
fix: fix integration test
motorina0 Mar 9, 2022
11681b3
fix: revert package lock version upgrade
motorina0 Mar 9, 2022
b8c0ba5
refactor: change code to original version
motorina0 Mar 9, 2022
de06357
fix: make `FinalScriptsFunc` backwards compatible by moving `isTaps…
motorina0 Mar 9, 2022
408e073
fix: tap tree branch sorting; improve unit test
motorina0 Mar 10, 2022
f5ee927
refactor: add Taptree interface
motorina0 Mar 18, 2022
a04b9d5
refactor: rename `testecc` to `verifyecc` (avoid confusing it with un…
motorina0 Mar 18, 2022
643e8eb
Declare tapscript version mask like the BIP
reardencode Mar 18, 2022
a80a9b6
Correct Taptree type
reardencode Mar 18, 2022
1a248a2
Consistent capitalization of tapleaf
reardencode Mar 18, 2022
431e38c
Don't use constants for tag prefixes
reardencode Mar 18, 2022
748e0dc
Simplify HashTree processing, remove footgun
reardencode Mar 18, 2022
db23603
Support p2tr with 1 script and no tree
reardencode Mar 18, 2022
e28b964
Remove unnecessary arrays of values
reardencode Mar 18, 2022
c8387c4
Improve tapleah hash parameter name
reardencode Mar 18, 2022
984649d
Fix indentation
reardencode Mar 18, 2022
6bf4ffc
Add validation for redeem in scriptTree
reardencode Mar 21, 2022
6ea2f8b
Improve comments and code clarity
reardencode Mar 21, 2022
bbf5cda
refactor: add explicit initialisation of the ecc library (#5)
motorina0 Mar 28, 2022
68361a1
WIP: p2tr_ns
reardencode Mar 31, 2022
d8ddfae
Have scriptSigFinalizer order sigs properly
reardencode Mar 31, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 7 additions & 13 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "bitcoinjs-lib",
"version": "6.0.1",
"version": "6.0.2",
"description": "Client-side Bitcoin JavaScript library",
"main": "./src/index.js",
"types": "./src/index.d.ts",
Expand Down Expand Up @@ -70,7 +70,6 @@
"bip39": "^3.0.2",
"bip65": "^1.0.1",
"bip68": "^1.0.3",
"bn.js": "^4.11.8",
"bs58": "^4.0.0",
"dhttp": "^3.0.0",
"ecpair": "^2.0.1",
Expand All @@ -84,7 +83,7 @@
"randombytes": "^2.1.0",
"regtest-client": "0.2.0",
"rimraf": "^2.6.3",
"tiny-secp256k1": "^2.1.2",
"tiny-secp256k1": "^2.2.0",
"ts-node": "^8.3.0",
"tslint": "^6.1.3",
"typescript": "^4.4.4"
Expand Down
16 changes: 12 additions & 4 deletions src/address.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@ exports.toOutputScript = exports.fromOutputScript = exports.toBech32 = exports.t
const networks = require('./networks');
const payments = require('./payments');
const bscript = require('./script');
const types = require('./types');
const types_1 = require('./types');
const bech32_1 = require('bech32');
const bs58check = require('bs58check');
const { typeforce } = types;
const FUTURE_SEGWIT_MAX_SIZE = 40;
const FUTURE_SEGWIT_MIN_SIZE = 2;
const FUTURE_SEGWIT_MAX_VERSION = 16;
const FUTURE_SEGWIT_MIN_VERSION = 1;
const FUTURE_SEGWIT_MIN_VERSION = 2;
const FUTURE_SEGWIT_VERSION_DIFF = 0x50;
const FUTURE_SEGWIT_VERSION_WARNING =
'WARNING: Sending to a future segwit version address can lead to loss of funds. ' +
Expand Down Expand Up @@ -69,7 +68,10 @@ function fromBech32(address) {
}
exports.fromBech32 = fromBech32;
function toBase58Check(hash, version) {
typeforce(types.tuple(types.Hash160bit, types.UInt8), arguments);
(0, types_1.typeforce)(
(0, types_1.tuple)(types_1.Hash160bit, types_1.UInt8),
arguments,
);
const payload = Buffer.allocUnsafe(21);
payload.writeUInt8(version, 0);
hash.copy(payload, 1);
Expand Down Expand Up @@ -99,6 +101,9 @@ function fromOutputScript(output, network) {
try {
return payments.p2wsh({ output, network }).address;
} catch (e) {}
try {
return payments.p2tr({ output, network }).address;
} catch (e) {}
try {
return _toFutureSegwitAddress(output, network);
} catch (e) {}
Expand Down Expand Up @@ -129,6 +134,9 @@ function toOutputScript(address, network) {
return payments.p2wpkh({ hash: decodeBech32.data }).output;
if (decodeBech32.data.length === 32)
return payments.p2wsh({ hash: decodeBech32.data }).output;
} else if (decodeBech32.version === 1) {
if (decodeBech32.data.length === 32)
return payments.p2tr({ pubkey: decodeBech32.data }).output;
} else if (
decodeBech32.version >= FUTURE_SEGWIT_MIN_VERSION &&
decodeBech32.version <= FUTURE_SEGWIT_MAX_VERSION &&
Expand Down
3 changes: 3 additions & 0 deletions src/ecc_lib.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { TinySecp256k1Interface } from './types';
export declare function initEccLib(eccLib: TinySecp256k1Interface | undefined): void;
export declare function getEccLib(): TinySecp256k1Interface;
91 changes: 91 additions & 0 deletions src/ecc_lib.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.getEccLib = exports.initEccLib = void 0;
const _ECCLIB_CACHE = {};
function initEccLib(eccLib) {
if (!eccLib) {
// allow clearing the library
_ECCLIB_CACHE.eccLib = eccLib;
} else if (eccLib !== _ECCLIB_CACHE.eccLib) {
// new instance, verify it
verifyEcc(eccLib);
_ECCLIB_CACHE.eccLib = eccLib;
}
}
exports.initEccLib = initEccLib;
function getEccLib() {
if (!_ECCLIB_CACHE.eccLib)
throw new Error(
'No ECC Library provided. You must call initEccLib() with a valid TinySecp256k1Interface instance',
);
return _ECCLIB_CACHE.eccLib;
}
exports.getEccLib = getEccLib;
const h = hex => Buffer.from(hex, 'hex');
function verifyEcc(ecc) {
assert(typeof ecc.isXOnlyPoint === 'function');
assert(
ecc.isXOnlyPoint(
h('79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'),
),
);
assert(
ecc.isXOnlyPoint(
h('fffffffffffffffffffffffffffffffffffffffffffffffffffffffeeffffc2e'),
),
);
assert(
ecc.isXOnlyPoint(
h('f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9'),
),
);
assert(
ecc.isXOnlyPoint(
h('0000000000000000000000000000000000000000000000000000000000000001'),
),
);
assert(
!ecc.isXOnlyPoint(
h('0000000000000000000000000000000000000000000000000000000000000000'),
),
);
assert(
!ecc.isXOnlyPoint(
h('fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f'),
),
);
assert(typeof ecc.xOnlyPointAddTweak === 'function');
tweakAddVectors.forEach(t => {
const r = ecc.xOnlyPointAddTweak(h(t.pubkey), h(t.tweak));
if (t.result === null) {
assert(r === null);
} else {
assert(r !== null);
assert(r.parity === t.parity);
assert(Buffer.from(r.xOnlyPubkey).equals(h(t.result)));
}
});
}
function assert(bool) {
if (!bool) throw new Error('ecc library invalid');
}
const tweakAddVectors = [
{
pubkey: '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798',
tweak: 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140',
parity: -1,
result: null,
},
{
pubkey: '1617d38ed8d8657da4d4761e8057bc396ea9e4b9d29776d4be096016dbd2509b',
tweak: 'a8397a935f0dfceba6ba9618f6451ef4d80637abf4e6af2669fbc9de6a8fd2ac',
parity: 1,
result: 'e478f99dab91052ab39a33ea35fd5e6e4933f4d28023cd597c9a1f6760346adf',
},
{
pubkey: '2c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991',
tweak: '823c3cd2142744b075a87eade7e1b8678ba308d566226a0056ca2b7a76f86b47',
parity: 0,
result: '9534f8dc8c6deda2dc007655981c78b49c5d96c778fbf363462a11ec9dfd948c',
},
];
1 change: 1 addition & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export { Transaction } from './transaction';
export { Network } from './networks';
export { Payment, PaymentCreator, PaymentOpts, Stack, StackElement, } from './payments';
export { Input as TxInput, Output as TxOutput } from './transaction';
export { initEccLib } from './ecc_lib';
9 changes: 8 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.Transaction = exports.opcodes = exports.Psbt = exports.Block = exports.script = exports.payments = exports.networks = exports.crypto = exports.address = void 0;
exports.initEccLib = exports.Transaction = exports.opcodes = exports.Psbt = exports.Block = exports.script = exports.payments = exports.networks = exports.crypto = exports.address = void 0;
const address = require('./address');
exports.address = address;
const crypto = require('./crypto');
Expand Down Expand Up @@ -39,3 +39,10 @@ Object.defineProperty(exports, 'Transaction', {
return transaction_1.Transaction;
},
});
var ecc_lib_1 = require('./ecc_lib');
Object.defineProperty(exports, 'initEccLib', {
enumerable: true,
get: function() {
return ecc_lib_1.initEccLib;
},
});
1 change: 1 addition & 0 deletions src/ops.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ const OPS = {
OP_NOP8: 183,
OP_NOP9: 184,
OP_NOP10: 185,
OP_CHECKSIGADD: 186,
OP_PUBKEYHASH: 253,
OP_PUBKEY: 254,
OP_INVALIDOPCODE: 255,
Expand Down
8 changes: 7 additions & 1 deletion src/payments/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
/// <reference types="node" />
import { Network } from '../networks';
import { Taptree } from '../types';
import { p2data as embed } from './embed';
import { p2ms } from './p2ms';
import { p2pk } from './p2pk';
import { p2pkh } from './p2pkh';
import { p2sh } from './p2sh';
import { p2wpkh } from './p2wpkh';
import { p2wsh } from './p2wsh';
import { p2tr } from './p2tr';
import { p2tr_ns } from './p2tr_ns';
export interface Payment {
name?: string;
network?: Network;
Expand All @@ -17,11 +20,14 @@ export interface Payment {
pubkeys?: Buffer[];
input?: Buffer;
signatures?: Buffer[];
internalPubkey?: Buffer;
pubkey?: Buffer;
signature?: Buffer;
address?: string;
hash?: Buffer;
redeem?: Payment;
redeemVersion?: number;
scriptTree?: Taptree;
witness?: Buffer[];
}
export declare type PaymentCreator = (a: Payment, opts?: PaymentOpts) => Payment;
Expand All @@ -33,4 +39,4 @@ export interface PaymentOpts {
export declare type StackElement = Buffer | number;
export declare type Stack = StackElement[];
export declare type StackFunction = () => Stack;
export { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh };
export { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh, p2tr, p2tr_ns };
16 changes: 15 additions & 1 deletion src/payments/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.p2wsh = exports.p2wpkh = exports.p2sh = exports.p2pkh = exports.p2pk = exports.p2ms = exports.embed = void 0;
exports.p2tr_ns = exports.p2tr = exports.p2wsh = exports.p2wpkh = exports.p2sh = exports.p2pkh = exports.p2pk = exports.p2ms = exports.embed = void 0;
const embed_1 = require('./embed');
Object.defineProperty(exports, 'embed', {
enumerable: true,
Expand Down Expand Up @@ -50,5 +50,19 @@ Object.defineProperty(exports, 'p2wsh', {
return p2wsh_1.p2wsh;
},
});
const p2tr_1 = require('./p2tr');
Object.defineProperty(exports, 'p2tr', {
enumerable: true,
get: function() {
return p2tr_1.p2tr;
},
});
const p2tr_ns_1 = require('./p2tr_ns');
Object.defineProperty(exports, 'p2tr_ns', {
enumerable: true,
get: function() {
return p2tr_ns_1.p2tr_ns;
},
});
// TODO
// witness commitment
2 changes: 2 additions & 0 deletions src/payments/p2tr.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { Payment, PaymentOpts } from './index';
export declare function p2tr(a: Payment, opts?: PaymentOpts): Payment;
Loading