Merge pull request #1476 from bitcoinjs/removeTxb

Tests cleanup
This commit is contained in:
d-yokoi 2019-09-12 17:53:39 +09:00 committed by GitHub
commit 41bf2cd03d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
51 changed files with 1284 additions and 1556 deletions

View file

@ -19,6 +19,8 @@ matrix:
env: TEST_SUITE=gitdiff:ci env: TEST_SUITE=gitdiff:ci
- node_js: "lts/*" - node_js: "lts/*"
env: TEST_SUITE=lint env: TEST_SUITE=lint
- node_js: "lts/*"
env: TEST_SUITE=lint:tests
- node_js: "lts/*" - node_js: "lts/*"
env: TEST_SUITE=coverage env: TEST_SUITE=coverage
env: env:

View file

@ -85,14 +85,6 @@ The below examples are implemented as integration tests, they should be very eas
Otherwise, pull requests are appreciated. Otherwise, pull requests are appreciated.
Some examples interact (via HTTPS) with a 3rd Party Blockchain Provider (3PBP). Some examples interact (via HTTPS) with a 3rd Party Blockchain Provider (3PBP).
### Warning: Currently the tests use TransactionBuilder, which will be removed in the future (v6.x.x or higher)
We will move towards replacing all instances of TransactionBuilder in the tests with the new Psbt.
Currently we have a few examples on how to use the newer Psbt class at the following link:
- [Psbt examples](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions-psbt.spec.ts)
The rest of the examples are below (using TransactionBuilder for Transaction creation)
- [Generate a random address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts) - [Generate a random address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts)
- [Import an address via WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts) - [Import an address via WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts)
- [Generate a 2-of-3 P2SH multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts) - [Generate a 2-of-3 P2SH multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts)
@ -104,7 +96,6 @@ The rest of the examples are below (using TransactionBuilder for Transaction cre
- [Generate a Testnet address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts) - [Generate a Testnet address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts)
- [Generate a Litecoin address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts) - [Generate a Litecoin address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts)
- [Create a 1-to-1 Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) - [Create a 1-to-1 Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts)
- [Create a 2-to-2 Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts)
- [Create (and broadcast via 3PBP) a typical Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) - [Create (and broadcast via 3PBP) a typical Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts)
- [Create (and broadcast via 3PBP) a Transaction with an OP\_RETURN output](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) - [Create (and broadcast via 3PBP) a Transaction with an OP\_RETURN output](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts)
- [Create (and broadcast via 3PBP) a Transaction with a 2-of-4 P2SH(multisig) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) - [Create (and broadcast via 3PBP) a Transaction with a 2-of-4 P2SH(multisig) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts)
@ -112,7 +103,7 @@ The rest of the examples are below (using TransactionBuilder for Transaction cre
- [Create (and broadcast via 3PBP) a Transaction with a SegWit P2WPKH input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) - [Create (and broadcast via 3PBP) a Transaction with a SegWit P2WPKH input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts)
- [Create (and broadcast via 3PBP) a Transaction with a SegWit P2PK input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) - [Create (and broadcast via 3PBP) a Transaction with a SegWit P2PK input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts)
- [Create (and broadcast via 3PBP) a Transaction with a SegWit 3-of-4 P2SH(P2WSH(multisig)) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) - [Create (and broadcast via 3PBP) a Transaction with a SegWit 3-of-4 P2SH(P2WSH(multisig)) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts)
- [Verify a Transaction signature](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) - [Create (and broadcast via 3PBP) a Transaction and sign with an HDSigner interface (bip32)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts)
- [Import a BIP32 testnet xpriv and export to WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.spec.ts) - [Import a BIP32 testnet xpriv and export to WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.spec.ts)
- [Export a BIP32 xpriv, then import it](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.spec.ts) - [Export a BIP32 xpriv, then import it](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.spec.ts)
- [Export a BIP32 xpub](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.spec.ts) - [Export a BIP32 xpub](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.spec.ts)

13
package-lock.json generated
View file

@ -156,9 +156,9 @@
"dev": true "dev": true
}, },
"@types/node": { "@types/node": {
"version": "10.12.18", "version": "12.7.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.5.tgz",
"integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==" "integrity": "sha512-9fq4jZVhPNW8r+UYKnxF1e2HkDWOWKM5bC2/7c9wPV835I0aOrVbS/Hw/pWPk2uKrNXQqg9Z959Kz+IYDd5p3w=="
}, },
"@types/proxyquire": { "@types/proxyquire": {
"version": "1.3.28", "version": "1.3.28",
@ -261,6 +261,13 @@
"tiny-secp256k1": "^1.1.0", "tiny-secp256k1": "^1.1.0",
"typeforce": "^1.11.5", "typeforce": "^1.11.5",
"wif": "^2.0.6" "wif": "^2.0.6"
},
"dependencies": {
"@types/node": {
"version": "10.12.18",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz",
"integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ=="
}
} }
}, },
"bip39": { "bip39": {

View file

@ -28,6 +28,7 @@
"gitdiff:ci": "npm run build && git diff --exit-code", "gitdiff:ci": "npm run build && git diff --exit-code",
"integration": "npm run build && npm run nobuild:integration", "integration": "npm run build && npm run nobuild:integration",
"lint": "tslint -p tsconfig.json -c tslint.json", "lint": "tslint -p tsconfig.json -c tslint.json",
"lint:tests": "tslint -p test/tsconfig.json -c tslint.json",
"mocha:ts": "mocha --recursive --require test/ts-node-register", "mocha:ts": "mocha --recursive --require test/ts-node-register",
"nobuild:coverage-report": "nyc report --reporter=lcov", "nobuild:coverage-report": "nyc report --reporter=lcov",
"nobuild:coverage-html": "nyc report --reporter=html", "nobuild:coverage-html": "nyc report --reporter=html",
@ -48,7 +49,7 @@
"types" "types"
], ],
"dependencies": { "dependencies": {
"@types/node": "10.12.18", "@types/node": "12.7.5",
"bech32": "^1.1.2", "bech32": "^1.1.2",
"bip174": "^1.0.1", "bip174": "^1.0.1",
"bip32": "^2.0.4", "bip32": "^2.0.4",

View file

@ -7,8 +7,8 @@ import * as base58KeysInvalid from './fixtures/core/base58_keys_invalid.json';
import * as base58KeysValid from './fixtures/core/base58_keys_valid.json'; import * as base58KeysValid from './fixtures/core/base58_keys_valid.json';
import * as blocksValid from './fixtures/core/blocks.json'; import * as blocksValid from './fixtures/core/blocks.json';
import * as sigCanonical from './fixtures/core/sig_canonical.json'; import * as sigCanonical from './fixtures/core/sig_canonical.json';
import * as sigHash from './fixtures/core/sighash.json';
import * as sigNoncanonical from './fixtures/core/sig_noncanonical.json'; import * as sigNoncanonical from './fixtures/core/sig_noncanonical.json';
import * as sigHash from './fixtures/core/sighash.json';
import * as txValid from './fixtures/core/tx_valid.json'; import * as txValid from './fixtures/core/tx_valid.json';
describe('Bitcoin-core', () => { describe('Bitcoin-core', () => {
@ -72,11 +72,11 @@ describe('Bitcoin-core', () => {
]; ];
base58KeysInvalid.forEach(f => { base58KeysInvalid.forEach(f => {
const string = f[0]; const strng = f[0];
it('throws on ' + string, () => { it('throws on ' + strng, () => {
assert.throws(() => { assert.throws(() => {
const address = bitcoin.address.fromBase58Check(string); const address = bitcoin.address.fromBase58Check(strng);
assert.notStrictEqual( assert.notStrictEqual(
allowedNetworks.indexOf(address.version), allowedNetworks.indexOf(address.version),
@ -121,11 +121,11 @@ describe('Bitcoin-core', () => {
]; ];
base58KeysInvalid.forEach(f => { base58KeysInvalid.forEach(f => {
const string = f[0]; const strng = f[0];
it('throws on ' + string, () => { it('throws on ' + strng, () => {
assert.throws(() => { assert.throws(() => {
bitcoin.ECPair.fromWIF(string, allowedNetworks); bitcoin.ECPair.fromWIF(strng, allowedNetworks);
}, /(Invalid|Unknown) (checksum|compression flag|network version|WIF length)/); }, /(Invalid|Unknown) (checksum|compression flag|network version|WIF length)/);
}); });
}); });
@ -242,9 +242,14 @@ describe('Bitcoin-core', () => {
const buffer = Buffer.from(hex, 'hex'); const buffer = Buffer.from(hex, 'hex');
it('throws on ' + description, () => { it('throws on ' + description, () => {
const reg = new RegExp(
'Expected DER (integer|sequence)|(R|S) value (excessively ' +
'padded|is negative)|(R|S|DER sequence) length is (zero|too ' +
'short|too long|invalid)|Invalid hashType',
);
assert.throws(() => { assert.throws(() => {
bitcoin.script.signature.decode(buffer); bitcoin.script.signature.decode(buffer);
}, /Expected DER (integer|sequence)|(R|S) value (excessively padded|is negative)|(R|S|DER sequence) length is (zero|too short|too long|invalid)|Invalid hashType/); }, reg);
}); });
}); });
}); });

View file

@ -8,7 +8,9 @@ describe('Block', () => {
describe('version', () => { describe('version', () => {
it('should be interpreted as an int32le', () => { it('should be interpreted as an int32le', () => {
const blockHex = const blockHex =
'ffffffff0000000000000000000000000000000000000000000000000000000000000000414141414141414141414141414141414141414141414141414141414141414101000000020000000300000000'; 'ffffffff000000000000000000000000000000000000000000000000000000000000' +
'00004141414141414141414141414141414141414141414141414141414141414141' +
'01000000020000000300000000';
const block = Block.fromHex(blockHex); const block = Block.fromHex(blockHex);
assert.strictEqual(-1, block.version); assert.strictEqual(-1, block.version);
assert.strictEqual(1, block.timestamp); assert.strictEqual(1, block.timestamp);

View file

@ -9,9 +9,9 @@ describe('bufferutils', () => {
fixtures.valid.forEach(f => { fixtures.valid.forEach(f => {
it('decodes ' + f.hex, () => { it('decodes ' + f.hex, () => {
const buffer = Buffer.from(f.hex, 'hex'); const buffer = Buffer.from(f.hex, 'hex');
const number = bufferutils.readUInt64LE(buffer, 0); const num = bufferutils.readUInt64LE(buffer, 0);
assert.strictEqual(number, f.dec); assert.strictEqual(num, f.dec);
}); });
}); });

View file

@ -1,7 +1,7 @@
import * as assert from 'assert'; import * as assert from 'assert';
import { describe, it } from 'mocha'; import { describe, it } from 'mocha';
import * as bscript from '../src/script';
import * as classify from '../src/classify'; import * as classify from '../src/classify';
import * as bscript from '../src/script';
import * as fixtures from './fixtures/templates.json'; import * as fixtures from './fixtures/templates.json';
@ -10,9 +10,9 @@ import * as nullData from '../src/templates/nulldata';
import * as pubKey from '../src/templates/pubkey'; import * as pubKey from '../src/templates/pubkey';
import * as pubKeyHash from '../src/templates/pubkeyhash'; import * as pubKeyHash from '../src/templates/pubkeyhash';
import * as scriptHash from '../src/templates/scripthash'; import * as scriptHash from '../src/templates/scripthash';
import * as witnessCommitment from '../src/templates/witnesscommitment';
import * as witnessPubKeyHash from '../src/templates/witnesspubkeyhash'; import * as witnessPubKeyHash from '../src/templates/witnesspubkeyhash';
import * as witnessScriptHash from '../src/templates/witnessscripthash'; import * as witnessScriptHash from '../src/templates/witnessscripthash';
import * as witnessCommitment from '../src/templates/witnesscommitment';
const tmap = { const tmap = {
pubKey, pubKey,

View file

@ -6,7 +6,10 @@ const dhttp = regtestUtils.dhttp;
const TESTNET = bitcoin.networks.testnet; const TESTNET = bitcoin.networks.testnet;
describe('bitcoinjs-lib (addresses)', () => { describe('bitcoinjs-lib (addresses)', () => {
it('can generate a random address [and support the retrieval of transactions for that address (via 3PBP)', async () => { it(
'can generate a random address [and support the retrieval of ' +
'transactions for that address (via 3PBP)]',
async () => {
const keyPair = bitcoin.ECPair.makeRandom(); const keyPair = bitcoin.ECPair.makeRandom();
const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }); const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey });
@ -22,7 +25,8 @@ describe('bitcoinjs-lib (addresses)', () => {
assert.strictEqual((result as any).n_tx, 0); assert.strictEqual((result as any).n_tx, 0);
assert.strictEqual((result as any).total_received, 0); assert.strictEqual((result as any).total_received, 0);
assert.strictEqual((result as any).total_sent, 0); assert.strictEqual((result as any).total_sent, 0);
}); },
);
it('can import an address via WIF', () => { it('can import an address via WIF', () => {
const keyPair = bitcoin.ECPair.fromWIF( const keyPair = bitcoin.ECPair.fromWIF(

View file

@ -1,7 +1,7 @@
import * as assert from 'assert'; import * as assert from 'assert';
import { describe, it } from 'mocha';
import * as bip32 from 'bip32'; import * as bip32 from 'bip32';
import * as bip39 from 'bip39'; import * as bip39 from 'bip39';
import { describe, it } from 'mocha';
import * as bitcoin from '../..'; import * as bitcoin from '../..';
function getAddress(node: any, network?: any): string { function getAddress(node: any, network?: any): string {
@ -25,8 +25,8 @@ describe('bitcoinjs-lib (BIP32)', () => {
'praise you muffin lion enable neck grocery crumble super myself license ghost'; 'praise you muffin lion enable neck grocery crumble super myself license ghost';
const seed = bip39.mnemonicToSeedSync(mnemonic); const seed = bip39.mnemonicToSeedSync(mnemonic);
const node = bip32.fromSeed(seed); const node = bip32.fromSeed(seed);
const string = node.toBase58(); const strng = node.toBase58();
const restored = bip32.fromBase58(string); const restored = bip32.fromBase58(strng);
assert.strictEqual(getAddress(node), getAddress(restored)); // same public key assert.strictEqual(getAddress(node), getAddress(restored)); // same public key
assert.strictEqual(node.toWIF(), restored.toWIF()); // same private key assert.strictEqual(node.toWIF(), restored.toWIF()); // same private key
@ -37,10 +37,10 @@ describe('bitcoinjs-lib (BIP32)', () => {
'praise you muffin lion enable neck grocery crumble super myself license ghost'; 'praise you muffin lion enable neck grocery crumble super myself license ghost';
const seed = bip39.mnemonicToSeedSync(mnemonic); const seed = bip39.mnemonicToSeedSync(mnemonic);
const node = bip32.fromSeed(seed); const node = bip32.fromSeed(seed);
const string = node.neutered().toBase58(); const strng = node.neutered().toBase58();
assert.strictEqual( assert.strictEqual(
string, strng,
'xpub661MyMwAqRbcGhVeaVfEBA25e3cP9DsJQZoE8iep5fZSxy3TnPBNBgWnMZx56oreNc48ZoTkQfatNJ9VWnQ7ZcLZcVStpaXLTeG8bGrzX3n', 'xpub661MyMwAqRbcGhVeaVfEBA25e3cP9DsJQZoE8iep5fZSxy3TnPBNBgWnMZx56oreNc48ZoTkQfatNJ9VWnQ7ZcLZcVStpaXLTeG8bGrzX3n',
); );
}); });

View file

@ -6,7 +6,14 @@ describe('bitcoinjs-lib (blocks)', () => {
it('can extract a height from a CoinBase transaction', () => { it('can extract a height from a CoinBase transaction', () => {
// from 00000000000000000097669cdca131f24d40c4cc7d80eaa65967a2d09acf6ce6 // from 00000000000000000097669cdca131f24d40c4cc7d80eaa65967a2d09acf6ce6
const txHex = const txHex =
'010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff50037f9a07174d696e656420627920416e74506f6f6c685b205a2b1f7bfabe6d6d36afe1910eca9405b66f97750940a656e38e2c0312958190ff8e98fd16761d220400000000000000aa340000d49f0000ffffffff02b07fc366000000001976a9148349212dc27ce3ab4c5b29b85c4dec643d764b1788ac0000000000000000266a24aa21a9ed72d9432948505e3d3062f1307a3f027a5dea846ff85e47159680919c12bf1e400120000000000000000000000000000000000000000000000000000000000000000000000000'; '010000000001010000000000000000000000000000000000000000000000000000000' +
'000000000ffffffff50037f9a07174d696e656420627920416e74506f6f6c685b205a' +
'2b1f7bfabe6d6d36afe1910eca9405b66f97750940a656e38e2c0312958190ff8e98f' +
'd16761d220400000000000000aa340000d49f0000ffffffff02b07fc3660000000019' +
'76a9148349212dc27ce3ab4c5b29b85c4dec643d764b1788ac0000000000000000266' +
'a24aa21a9ed72d9432948505e3d3062f1307a3f027a5dea846ff85e47159680919c12' +
'bf1e40012000000000000000000000000000000000000000000000000000000000000' +
'0000000000000';
const tx = bitcoin.Transaction.fromHex(txHex); const tx = bitcoin.Transaction.fromHex(txHex);
assert.strictEqual(tx.ins.length, 1); assert.strictEqual(tx.ins.length, 1);

View file

@ -5,6 +5,14 @@ import { regtestUtils } from './_regtest';
const regtest = regtestUtils.network; const regtest = regtestUtils.network;
const bip65 = require('bip65'); const bip65 = require('bip65');
function toOutputScript(address: string): Buffer {
return bitcoin.address.toOutputScript(address, regtest);
}
function idToHash(txid: string): Buffer {
return Buffer.from(txid, 'hex').reverse();
}
const alice = bitcoin.ECPair.fromWIF( const alice = bitcoin.ECPair.fromWIF(
'cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe', 'cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe',
regtest, regtest,
@ -13,7 +21,6 @@ const bob = bitcoin.ECPair.fromWIF(
'cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', 'cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x',
regtest, regtest,
); );
console.warn = () => {}; // Silence the Deprecation Warning
describe('bitcoinjs-lib (transactions w/ CLTV)', () => { describe('bitcoinjs-lib (transactions w/ CLTV)', () => {
// force update MTP // force update MTP
@ -23,8 +30,14 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', () => {
const hashType = bitcoin.Transaction.SIGHASH_ALL; const hashType = bitcoin.Transaction.SIGHASH_ALL;
type keyPair = { publicKey: Buffer }; interface KeyPair {
function cltvCheckSigOutput(aQ: keyPair, bQ: keyPair, lockTime: number) { publicKey: Buffer;
}
function cltvCheckSigOutput(
aQ: KeyPair,
bQ: KeyPair,
lockTime: number,
): Buffer {
return bitcoin.script.fromASM( return bitcoin.script.fromASM(
` `
OP_IF OP_IF
@ -43,12 +56,15 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', () => {
); );
} }
function utcNow() { function utcNow(): number {
return Math.floor(Date.now() / 1000); return Math.floor(Date.now() / 1000);
} }
// expiry past, {Alice's signature} OP_TRUE // expiry past, {Alice's signature} OP_TRUE
it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the past)', async () => { it(
'can create (and broadcast via 3PBP) a Transaction where Alice can redeem ' +
'the output after the expiry (in the past)',
async () => {
// 3 hours ago // 3 hours ago
const lockTime = bip65.encode({ utc: utcNow() - 3600 * 3 }); const lockTime = bip65.encode({ utc: utcNow() - 3600 * 3 });
const redeemScript = cltvCheckSigOutput(alice, bob, lockTime); const redeemScript = cltvCheckSigOutput(alice, bob, lockTime);
@ -59,19 +75,21 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', () => {
// fund the P2SH(CLTV) address // fund the P2SH(CLTV) address
const unspent = await regtestUtils.faucet(address!, 1e5); const unspent = await regtestUtils.faucet(address!, 1e5);
const txb = new bitcoin.TransactionBuilder(regtest); const tx = new bitcoin.Transaction();
txb.setLockTime(lockTime); tx.locktime = lockTime;
// Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable.
txb.addInput(unspent.txId, unspent.vout, 0xfffffffe); tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe);
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4); tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4);
// {Alice's signature} OP_TRUE // {Alice's signature} OP_TRUE
const tx = txb.buildIncomplete();
const signatureHash = tx.hashForSignature(0, redeemScript, hashType); const signatureHash = tx.hashForSignature(0, redeemScript, hashType);
const redeemScriptSig = bitcoin.payments.p2sh({ const redeemScriptSig = bitcoin.payments.p2sh({
redeem: { redeem: {
input: bitcoin.script.compile([ input: bitcoin.script.compile([
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), bitcoin.script.signature.encode(
alice.sign(signatureHash),
hashType,
),
bitcoin.opcodes.OP_TRUE, bitcoin.opcodes.OP_TRUE,
]), ]),
output: redeemScript, output: redeemScript,
@ -87,10 +105,14 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', () => {
vout: 0, vout: 0,
value: 7e4, value: 7e4,
}); });
}); },
);
// expiry will pass, {Alice's signature} OP_TRUE // expiry will pass, {Alice's signature} OP_TRUE
it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)', async () => { it(
'can create (and broadcast via 3PBP) a Transaction where Alice can redeem ' +
'the output after the expiry (in the future)',
async () => {
const height = await regtestUtils.height(); const height = await regtestUtils.height();
// 5 blocks from now // 5 blocks from now
const lockTime = bip65.encode({ blocks: height + 5 }); const lockTime = bip65.encode({ blocks: height + 5 });
@ -102,19 +124,21 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', () => {
// fund the P2SH(CLTV) address // fund the P2SH(CLTV) address
const unspent = await regtestUtils.faucet(address!, 1e5); const unspent = await regtestUtils.faucet(address!, 1e5);
const txb = new bitcoin.TransactionBuilder(regtest); const tx = new bitcoin.Transaction();
txb.setLockTime(lockTime); tx.locktime = lockTime;
// Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable.
txb.addInput(unspent.txId, unspent.vout, 0xfffffffe); tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe);
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4); tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4);
// {Alice's signature} OP_TRUE // {Alice's signature} OP_TRUE
const tx = txb.buildIncomplete();
const signatureHash = tx.hashForSignature(0, redeemScript, hashType); const signatureHash = tx.hashForSignature(0, redeemScript, hashType);
const redeemScriptSig = bitcoin.payments.p2sh({ const redeemScriptSig = bitcoin.payments.p2sh({
redeem: { redeem: {
input: bitcoin.script.compile([ input: bitcoin.script.compile([
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), bitcoin.script.signature.encode(
alice.sign(signatureHash),
hashType,
),
bitcoin.opcodes.OP_TRUE, bitcoin.opcodes.OP_TRUE,
]), ]),
output: redeemScript, output: redeemScript,
@ -133,10 +157,14 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', () => {
vout: 0, vout: 0,
value: 7e4, value: 7e4,
}); });
}); },
);
// expiry ignored, {Bob's signature} {Alice's signature} OP_FALSE // expiry ignored, {Bob's signature} {Alice's signature} OP_FALSE
it('can create (and broadcast via 3PBP) a Transaction where Alice and Bob can redeem the output at any time', async () => { it(
'can create (and broadcast via 3PBP) a Transaction where Alice and Bob can ' +
'redeem the output at any time',
async () => {
// two hours ago // two hours ago
const lockTime = bip65.encode({ utc: utcNow() - 3600 * 2 }); const lockTime = bip65.encode({ utc: utcNow() - 3600 * 2 });
const redeemScript = cltvCheckSigOutput(alice, bob, lockTime); const redeemScript = cltvCheckSigOutput(alice, bob, lockTime);
@ -147,19 +175,21 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', () => {
// fund the P2SH(CLTV) address // fund the P2SH(CLTV) address
const unspent = await regtestUtils.faucet(address!, 2e5); const unspent = await regtestUtils.faucet(address!, 2e5);
const txb = new bitcoin.TransactionBuilder(regtest); const tx = new bitcoin.Transaction();
txb.setLockTime(lockTime); tx.locktime = lockTime;
// Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable.
txb.addInput(unspent.txId, unspent.vout, 0xfffffffe); tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe);
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 8e4); tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 8e4);
// {Alice's signature} {Bob's signature} OP_FALSE // {Alice's signature} {Bob's signature} OP_FALSE
const tx = txb.buildIncomplete();
const signatureHash = tx.hashForSignature(0, redeemScript, hashType); const signatureHash = tx.hashForSignature(0, redeemScript, hashType);
const redeemScriptSig = bitcoin.payments.p2sh({ const redeemScriptSig = bitcoin.payments.p2sh({
redeem: { redeem: {
input: bitcoin.script.compile([ input: bitcoin.script.compile([
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), bitcoin.script.signature.encode(
alice.sign(signatureHash),
hashType,
),
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
bitcoin.opcodes.OP_FALSE, bitcoin.opcodes.OP_FALSE,
]), ]),
@ -175,10 +205,14 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', () => {
vout: 0, vout: 0,
value: 8e4, value: 8e4,
}); });
}); },
);
// expiry in the future, {Alice's signature} OP_TRUE // expiry in the future, {Alice's signature} OP_TRUE
it('can create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry', async () => { it(
'can create (but fail to broadcast via 3PBP) a Transaction where Alice ' +
'attempts to redeem before the expiry',
async () => {
// two hours from now // two hours from now
const lockTime = bip65.encode({ utc: utcNow() + 3600 * 2 }); const lockTime = bip65.encode({ utc: utcNow() + 3600 * 2 });
const redeemScript = cltvCheckSigOutput(alice, bob, lockTime); const redeemScript = cltvCheckSigOutput(alice, bob, lockTime);
@ -189,19 +223,21 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', () => {
// fund the P2SH(CLTV) address // fund the P2SH(CLTV) address
const unspent = await regtestUtils.faucet(address!, 2e4); const unspent = await regtestUtils.faucet(address!, 2e4);
const txb = new bitcoin.TransactionBuilder(regtest); const tx = new bitcoin.Transaction();
txb.setLockTime(lockTime); tx.locktime = lockTime;
// Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable.
txb.addInput(unspent.txId, unspent.vout, 0xfffffffe); tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe);
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4); tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 1e4);
// {Alice's signature} OP_TRUE // {Alice's signature} OP_TRUE
const tx = txb.buildIncomplete();
const signatureHash = tx.hashForSignature(0, redeemScript, hashType); const signatureHash = tx.hashForSignature(0, redeemScript, hashType);
const redeemScriptSig = bitcoin.payments.p2sh({ const redeemScriptSig = bitcoin.payments.p2sh({
redeem: { redeem: {
input: bitcoin.script.compile([ input: bitcoin.script.compile([
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), bitcoin.script.signature.encode(
alice.sign(signatureHash),
hashType,
),
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
bitcoin.opcodes.OP_TRUE, bitcoin.opcodes.OP_TRUE,
]), ]),
@ -215,5 +251,6 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', () => {
if (err) throw err; if (err) throw err;
}, /Error: non-final \(code 64\)/); }, /Error: non-final \(code 64\)/);
}); });
}); },
);
}); });

View file

@ -5,6 +5,14 @@ import { regtestUtils } from './_regtest';
const regtest = regtestUtils.network; const regtest = regtestUtils.network;
const bip68 = require('bip68'); const bip68 = require('bip68');
function toOutputScript(address: string): Buffer {
return bitcoin.address.toOutputScript(address, regtest);
}
function idToHash(txid: string): Buffer {
return Buffer.from(txid, 'hex').reverse();
}
const alice = bitcoin.ECPair.fromWIF( const alice = bitcoin.ECPair.fromWIF(
'cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe', 'cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe',
regtest, regtest,
@ -21,7 +29,6 @@ const dave = bitcoin.ECPair.fromWIF(
'cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsMwS4pqnx', 'cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsMwS4pqnx',
regtest, regtest,
); );
console.warn = () => {}; // Silence the Deprecation Warning
describe('bitcoinjs-lib (transactions w/ CSV)', () => { describe('bitcoinjs-lib (transactions w/ CSV)', () => {
// force update MTP // force update MTP
@ -31,9 +38,15 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
const hashType = bitcoin.Transaction.SIGHASH_ALL; const hashType = bitcoin.Transaction.SIGHASH_ALL;
type keyPair = { publicKey: Buffer }; interface KeyPair {
publicKey: Buffer;
}
// IF MTP (from when confirmed) > seconds, _alice can redeem // IF MTP (from when confirmed) > seconds, _alice can redeem
function csvCheckSigOutput(_alice: keyPair, _bob: keyPair, sequence: number) { function csvCheckSigOutput(
_alice: KeyPair,
_bob: KeyPair,
sequence: number,
): Buffer {
return bitcoin.script.fromASM( return bitcoin.script.fromASM(
` `
OP_IF OP_IF
@ -55,17 +68,20 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
// 2 of 3 multisig of _bob, _charles, _dave, // 2 of 3 multisig of _bob, _charles, _dave,
// but after sequence1 time, _alice can allow the multisig to become 1 of 3. // but after sequence1 time, _alice can allow the multisig to become 1 of 3.
// but after sequence2 time, _alice can sign for the output all by themself. // but after sequence2 time, _alice can sign for the output all by themself.
/* tslint:disable-next-line */
// Ref: https://github.com/bitcoinbook/bitcoinbook/blob/f8b883dcd4e3d1b9adf40fed59b7e898fbd9241f/ch07.asciidoc#complex-script-example // Ref: https://github.com/bitcoinbook/bitcoinbook/blob/f8b883dcd4e3d1b9adf40fed59b7e898fbd9241f/ch07.asciidoc#complex-script-example
// Note: bitcoinjs-lib will not offer specific support for problems with // Note: bitcoinjs-lib will not offer specific support for problems with
// advanced script usages such as below. Use at your own risk. // advanced script usages such as below. Use at your own risk.
function complexCsvOutput( function complexCsvOutput(
_alice: keyPair, _alice: KeyPair,
_bob: keyPair, _bob: KeyPair,
_charles: keyPair, _charles: KeyPair,
_dave: keyPair, _dave: KeyPair,
sequence1: number, sequence1: number,
sequence2: number, sequence2: number,
) { ): Buffer {
return bitcoin.script.fromASM( return bitcoin.script.fromASM(
` `
OP_IF OP_IF
@ -98,7 +114,10 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
} }
// expiry will pass, {Alice's signature} OP_TRUE // expiry will pass, {Alice's signature} OP_TRUE
it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future) (simple CHECKSEQUENCEVERIFY)', async () => { it(
'can create (and broadcast via 3PBP) a Transaction where Alice can redeem ' +
'the output after the expiry (in the future) (simple CHECKSEQUENCEVERIFY)',
async () => {
// 5 blocks from now // 5 blocks from now
const sequence = bip68.encode({ blocks: 5 }); const sequence = bip68.encode({ blocks: 5 });
const p2sh = bitcoin.payments.p2sh({ const p2sh = bitcoin.payments.p2sh({
@ -111,12 +130,12 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
// fund the P2SH(CSV) address // fund the P2SH(CSV) address
const unspent = await regtestUtils.faucet(p2sh.address!, 1e5); const unspent = await regtestUtils.faucet(p2sh.address!, 1e5);
const txb = new bitcoin.TransactionBuilder(regtest); const tx = new bitcoin.Transaction();
txb.addInput(unspent.txId, unspent.vout, sequence); tx.version = 2;
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4); tx.addInput(idToHash(unspent.txId), unspent.vout, sequence);
tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4);
// {Alice's signature} OP_TRUE // {Alice's signature} OP_TRUE
const tx = txb.buildIncomplete();
const signatureHash = tx.hashForSignature( const signatureHash = tx.hashForSignature(
0, 0,
p2sh.redeem!.output!, p2sh.redeem!.output!,
@ -128,7 +147,10 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
network: regtest, network: regtest,
output: p2sh.redeem!.output, output: p2sh.redeem!.output,
input: bitcoin.script.compile([ input: bitcoin.script.compile([
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), bitcoin.script.signature.encode(
alice.sign(signatureHash),
hashType,
),
bitcoin.opcodes.OP_TRUE, bitcoin.opcodes.OP_TRUE,
]), ]),
}, },
@ -148,10 +170,14 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
vout: 0, vout: 0,
value: 7e4, value: 7e4,
}); });
}); },
);
// expiry in the future, {Alice's signature} OP_TRUE // expiry in the future, {Alice's signature} OP_TRUE
it('can create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry (simple CHECKSEQUENCEVERIFY)', async () => { it(
'can create (but fail to broadcast via 3PBP) a Transaction where Alice ' +
'attempts to redeem before the expiry (simple CHECKSEQUENCEVERIFY)',
async () => {
// two hours after confirmation // two hours after confirmation
const sequence = bip68.encode({ seconds: 7168 }); const sequence = bip68.encode({ seconds: 7168 });
const p2sh = bitcoin.payments.p2sh({ const p2sh = bitcoin.payments.p2sh({
@ -164,12 +190,12 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
// fund the P2SH(CSV) address // fund the P2SH(CSV) address
const unspent = await regtestUtils.faucet(p2sh.address!, 2e4); const unspent = await regtestUtils.faucet(p2sh.address!, 2e4);
const txb = new bitcoin.TransactionBuilder(regtest); const tx = new bitcoin.Transaction();
txb.addInput(unspent.txId, unspent.vout, sequence); tx.version = 2;
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4); tx.addInput(idToHash(unspent.txId), unspent.vout, sequence);
tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 1e4);
// {Alice's signature} OP_TRUE // {Alice's signature} OP_TRUE
const tx = txb.buildIncomplete();
const signatureHash = tx.hashForSignature( const signatureHash = tx.hashForSignature(
0, 0,
p2sh.redeem!.output!, p2sh.redeem!.output!,
@ -181,7 +207,10 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
network: regtest, network: regtest,
output: p2sh.redeem!.output, output: p2sh.redeem!.output,
input: bitcoin.script.compile([ input: bitcoin.script.compile([
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), bitcoin.script.signature.encode(
alice.sign(signatureHash),
hashType,
),
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
bitcoin.opcodes.OP_TRUE, bitcoin.opcodes.OP_TRUE,
]), ]),
@ -194,10 +223,14 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
if (err) throw err; if (err) throw err;
}, /Error: non-BIP68-final \(code 64\)/); }, /Error: non-BIP68-final \(code 64\)/);
}); });
}); },
);
// Check first combination of complex CSV, 2 of 3 // Check first combination of complex CSV, 2 of 3
it('can create (and broadcast via 3PBP) a Transaction where Bob and Charles can send (complex CHECKSEQUENCEVERIFY)', async () => { it(
'can create (and broadcast via 3PBP) a Transaction where Bob and Charles ' +
'can send (complex CHECKSEQUENCEVERIFY)',
async () => {
// 2 blocks from now // 2 blocks from now
const sequence1 = bip68.encode({ blocks: 2 }); const sequence1 = bip68.encode({ blocks: 2 });
// 5 blocks from now // 5 blocks from now
@ -219,12 +252,12 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
// fund the P2SH(CCSV) address // fund the P2SH(CCSV) address
const unspent = await regtestUtils.faucet(p2sh.address!, 1e5); const unspent = await regtestUtils.faucet(p2sh.address!, 1e5);
const txb = new bitcoin.TransactionBuilder(regtest); const tx = new bitcoin.Transaction();
txb.addInput(unspent.txId, unspent.vout); tx.version = 2;
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4); tx.addInput(idToHash(unspent.txId), unspent.vout);
tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4);
// OP_0 {Bob sig} {Charles sig} OP_TRUE OP_TRUE // OP_0 {Bob sig} {Charles sig} OP_TRUE OP_TRUE
const tx = txb.buildIncomplete();
const signatureHash = tx.hashForSignature( const signatureHash = tx.hashForSignature(
0, 0,
p2sh.redeem!.output!, p2sh.redeem!.output!,
@ -257,10 +290,14 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
vout: 0, vout: 0,
value: 7e4, value: 7e4,
}); });
}); },
);
// Check first combination of complex CSV, mediator + 1 of 3 after 2 blocks // Check first combination of complex CSV, mediator + 1 of 3 after 2 blocks
it('can create (and broadcast via 3PBP) a Transaction where Alice (mediator) and Bob can send after 2 blocks (complex CHECKSEQUENCEVERIFY)', async () => { it(
'can create (and broadcast via 3PBP) a Transaction where Alice (mediator) ' +
'and Bob can send after 2 blocks (complex CHECKSEQUENCEVERIFY)',
async () => {
// 2 blocks from now // 2 blocks from now
const sequence1 = bip68.encode({ blocks: 2 }); const sequence1 = bip68.encode({ blocks: 2 });
// 5 blocks from now // 5 blocks from now
@ -282,12 +319,12 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
// fund the P2SH(CCSV) address // fund the P2SH(CCSV) address
const unspent = await regtestUtils.faucet(p2sh.address!, 1e5); const unspent = await regtestUtils.faucet(p2sh.address!, 1e5);
const txb = new bitcoin.TransactionBuilder(regtest); const tx = new bitcoin.Transaction();
txb.addInput(unspent.txId, unspent.vout, sequence1); // Set sequence1 for input tx.version = 2;
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4); tx.addInput(idToHash(unspent.txId), unspent.vout, sequence1); // Set sequence1 for input
tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4);
// OP_0 {Bob sig} {Alice mediator sig} OP_FALSE OP_TRUE // OP_0 {Bob sig} {Alice mediator sig} OP_FALSE OP_TRUE
const tx = txb.buildIncomplete();
const signatureHash = tx.hashForSignature( const signatureHash = tx.hashForSignature(
0, 0,
p2sh.redeem!.output!, p2sh.redeem!.output!,
@ -301,7 +338,10 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
input: bitcoin.script.compile([ input: bitcoin.script.compile([
bitcoin.opcodes.OP_0, bitcoin.opcodes.OP_0,
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), bitcoin.script.signature.encode(
alice.sign(signatureHash),
hashType,
),
bitcoin.opcodes.OP_0, bitcoin.opcodes.OP_0,
bitcoin.opcodes.OP_TRUE, bitcoin.opcodes.OP_TRUE,
]), ]),
@ -320,10 +360,14 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
vout: 0, vout: 0,
value: 7e4, value: 7e4,
}); });
}); },
);
// Check first combination of complex CSV, mediator after 5 blocks // Check first combination of complex CSV, mediator after 5 blocks
it('can create (and broadcast via 3PBP) a Transaction where Alice (mediator) can send after 5 blocks (complex CHECKSEQUENCEVERIFY)', async () => { it(
'can create (and broadcast via 3PBP) a Transaction where Alice (mediator) ' +
'can send after 5 blocks (complex CHECKSEQUENCEVERIFY)',
async () => {
// 2 blocks from now // 2 blocks from now
const sequence1 = bip68.encode({ blocks: 2 }); const sequence1 = bip68.encode({ blocks: 2 });
// 5 blocks from now // 5 blocks from now
@ -345,12 +389,12 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
// fund the P2SH(CCSV) address // fund the P2SH(CCSV) address
const unspent = await regtestUtils.faucet(p2sh.address!, 1e5); const unspent = await regtestUtils.faucet(p2sh.address!, 1e5);
const txb = new bitcoin.TransactionBuilder(regtest); const tx = new bitcoin.Transaction();
txb.addInput(unspent.txId, unspent.vout, sequence2); // Set sequence2 for input tx.version = 2;
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4); tx.addInput(idToHash(unspent.txId), unspent.vout, sequence2); // Set sequence2 for input
tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4);
// {Alice mediator sig} OP_FALSE // {Alice mediator sig} OP_FALSE
const tx = txb.buildIncomplete();
const signatureHash = tx.hashForSignature( const signatureHash = tx.hashForSignature(
0, 0,
p2sh.redeem!.output!, p2sh.redeem!.output!,
@ -362,7 +406,10 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
network: regtest, network: regtest,
output: p2sh.redeem!.output, output: p2sh.redeem!.output,
input: bitcoin.script.compile([ input: bitcoin.script.compile([
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), bitcoin.script.signature.encode(
alice.sign(signatureHash),
hashType,
),
bitcoin.opcodes.OP_0, bitcoin.opcodes.OP_0,
]), ]),
}, },
@ -380,5 +427,6 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
vout: 0, vout: 0,
value: 7e4, value: 7e4,
}); });
}); },
);
}); });

View file

@ -6,46 +6,43 @@ const keyPairs = [
bitcoin.ECPair.makeRandom({ network: NETWORK }), bitcoin.ECPair.makeRandom({ network: NETWORK }),
bitcoin.ECPair.makeRandom({ network: NETWORK }), bitcoin.ECPair.makeRandom({ network: NETWORK }),
]; ];
console.warn = () => {}; // Silence the Deprecation Warning
async function buildAndSign( async function buildAndSign(
depends: any, depends: any,
prevOutput: any, prevOutput: any,
redeemScript: any, redeemScript: any,
witnessScript: any, witnessScript: any,
) { ): Promise<null> {
const unspent = await regtestUtils.faucetComplex(prevOutput, 5e4); const unspent = await regtestUtils.faucetComplex(prevOutput, 5e4);
const utx = await regtestUtils.fetch(unspent.txId);
const txb = new bitcoin.TransactionBuilder(NETWORK); const psbt = new bitcoin.Psbt({ network: NETWORK })
txb.addInput(unspent.txId, unspent.vout, undefined, prevOutput); .addInput({
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4); hash: unspent.txId,
index: unspent.vout,
const posType = depends.prevOutScriptType; nonWitnessUtxo: Buffer.from(utx.txHex, 'hex'),
const needsValue = !!witnessScript || posType.slice(-6) === 'p2wpkh'; ...(redeemScript ? { redeemScript } : {}),
...(witnessScript ? { witnessScript } : {}),
})
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
value: 2e4,
});
if (depends.signatures) { if (depends.signatures) {
keyPairs.forEach(keyPair => { keyPairs.forEach(keyPair => {
txb.sign({ psbt.signInput(0, keyPair);
prevOutScriptType: posType,
vin: 0,
keyPair,
redeemScript,
witnessValue: needsValue ? unspent.value : undefined,
witnessScript,
});
}); });
} else if (depends.signature) { } else if (depends.signature) {
txb.sign({ psbt.signInput(0, keyPairs[0]);
prevOutScriptType: posType,
vin: 0,
keyPair: keyPairs[0],
redeemScript,
witnessValue: needsValue ? unspent.value : undefined,
witnessScript,
});
} }
return regtestUtils.broadcast(txb.build().toHex()); return regtestUtils.broadcast(
psbt
.finalizeAllInputs()
.extractTransaction()
.toHex(),
);
} }
['p2ms', 'p2pk', 'p2pkh', 'p2wpkh'].forEach(k => { ['p2ms', 'p2pk', 'p2pkh', 'p2wpkh'].forEach(k => {

View file

@ -1,669 +0,0 @@
import * as assert from 'assert';
import { describe, it } from 'mocha';
import * as bitcoin from '../..';
import { regtestUtils } from './_regtest';
import * as bip32 from 'bip32';
const rng = require('randombytes');
const regtest = regtestUtils.network;
// See bottom of file for some helper functions used to make the payment objects needed.
describe('bitcoinjs-lib (transactions with psbt)', () => {
it('can create a 1-to-1 Transaction', () => {
const alice = bitcoin.ECPair.fromWIF(
'L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr',
);
const psbt = new bitcoin.Psbt();
psbt.setVersion(2); // These are defaults. This line is not needed.
psbt.setLocktime(0); // These are defaults. This line is not needed.
psbt.addInput({
// if hash is string, txid, if hash is Buffer, is reversed compared to txid
hash: '7d067b4a697a09d2c3cff7d4d9506c9955e93bff41bf82d439da7d030382bc3e',
index: 0,
sequence: 0xffffffff, // These are defaults. This line is not needed.
// non-segwit inputs now require passing the whole previous tx as Buffer
nonWitnessUtxo: Buffer.from(
'0200000001f9f34e95b9d5c8abcd20fc5bd4a825d1517be62f0f775e5f36da944d9' +
'452e550000000006b483045022100c86e9a111afc90f64b4904bd609e9eaed80d48' +
'ca17c162b1aca0a788ac3526f002207bb79b60d4fc6526329bf18a77135dc566020' +
'9e761da46e1c2f1152ec013215801210211755115eabf846720f5cb18f248666fec' +
'631e5e1e66009ce3710ceea5b1ad13ffffffff01' +
// value in satoshis (Int64LE) = 0x015f90 = 90000
'905f010000000000' +
// scriptPubkey length
'19' +
// scriptPubkey
'76a9148bbc95d2709c71607c60ee3f097c1217482f518d88ac' +
// locktime
'00000000',
'hex',
),
// // If this input was segwit, instead of nonWitnessUtxo, you would add
// // a witnessUtxo as follows. The scriptPubkey and the value only are needed.
// witnessUtxo: {
// script: Buffer.from(
// '76a9148bbc95d2709c71607c60ee3f097c1217482f518d88ac',
// 'hex',
// ),
// value: 90000,
// },
// Not featured here:
// redeemScript. A Buffer of the redeemScript for P2SH
// witnessScript. A Buffer of the witnessScript for P2WSH
});
psbt.addOutput({
address: '1KRMKfeZcmosxALVYESdPNez1AP1mEtywp',
value: 80000,
});
psbt.signInput(0, alice);
psbt.validateSignaturesOfInput(0);
psbt.finalizeAllInputs();
assert.strictEqual(
psbt.extractTransaction().toHex(),
'02000000013ebc8203037dda39d482bf41ff3be955996c50d9d4f7cfc3d2097a694a7' +
'b067d000000006b483045022100931b6db94aed25d5486884d83fc37160f37f3368c0' +
'd7f48c757112abefec983802205fda64cff98c849577026eb2ce916a50ea70626a766' +
'9f8596dd89b720a26b4d501210365db9da3f8a260078a7e8f8b708a1161468fb2323f' +
'fda5ec16b261ec1056f455ffffffff0180380100000000001976a914ca0d36044e0dc' +
'08a22724efa6f6a07b0ec4c79aa88ac00000000',
);
});
it('can create (and broadcast via 3PBP) a typical Transaction', async () => {
// these are { payment: Payment; keys: ECPair[] }
const alice1 = createPayment('p2pkh');
const alice2 = createPayment('p2pkh');
// give Alice 2 unspent outputs
const inputData1 = await getInputData(
5e4,
alice1.payment,
false,
'noredeem',
);
const inputData2 = await getInputData(
7e4,
alice2.payment,
false,
'noredeem',
);
{
const {
hash, // string of txid or Buffer of tx hash. (txid and hash are reverse order)
index, // the output index of the txo you are spending
nonWitnessUtxo, // the full previous transaction as a Buffer
} = inputData1;
assert.deepStrictEqual({ hash, index, nonWitnessUtxo }, inputData1);
}
// network is only needed if you pass an address to addOutput
// using script (Buffer of scriptPubkey) instead will avoid needed network.
const psbt = new bitcoin.Psbt({ network: regtest })
.addInput(inputData1) // alice1 unspent
.addInput(inputData2) // alice2 unspent
.addOutput({
address: 'mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf',
value: 8e4,
}) // the actual "spend"
.addOutput({
address: alice2.payment.address, // OR script, which is a Buffer.
value: 1e4,
}); // Alice's change
// (in)(5e4 + 7e4) - (out)(8e4 + 1e4) = (fee)3e4 = 30000, this is the miner fee
// Let's show a new feature with PSBT.
// We can have multiple signers sign in parrallel and combine them.
// (this is not necessary, but a nice feature)
// encode to send out to the signers
const psbtBaseText = psbt.toBase64();
// each signer imports
const signer1 = bitcoin.Psbt.fromBase64(psbtBaseText);
const signer2 = bitcoin.Psbt.fromBase64(psbtBaseText);
// Alice signs each input with the respective private keys
// signInput and signInputAsync are better
// (They take the input index explicitly as the first arg)
signer1.signAllInputs(alice1.keys[0]);
signer2.signAllInputs(alice2.keys[0]);
// If your signer object's sign method returns a promise, use the following
// await signer2.signAllInputsAsync(alice2.keys[0])
// encode to send back to combiner (signer 1 and 2 are not near each other)
const s1text = signer1.toBase64();
const s2text = signer2.toBase64();
const final1 = bitcoin.Psbt.fromBase64(s1text);
const final2 = bitcoin.Psbt.fromBase64(s2text);
// final1.combine(final2) would give the exact same result
psbt.combine(final1, final2);
// Finalizer wants to check all signatures are valid before finalizing.
// If the finalizer wants to check for specific pubkeys, the second arg
// can be passed. See the first multisig example below.
assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
assert.strictEqual(psbt.validateSignaturesOfInput(1), true);
// This step it new. Since we separate the signing operation and
// the creation of the scriptSig and witness stack, we are able to
psbt.finalizeAllInputs();
// build and broadcast our RegTest network
await regtestUtils.broadcast(psbt.extractTransaction().toHex());
// to build and broadcast to the actual Bitcoin network, see https://github.com/bitcoinjs/bitcoinjs-lib/issues/839
});
it('can create (and broadcast via 3PBP) a Transaction with an OP_RETURN output', async () => {
const alice1 = createPayment('p2pkh');
const inputData1 = await getInputData(
2e5,
alice1.payment,
false,
'noredeem',
);
const data = Buffer.from('bitcoinjs-lib', 'utf8');
const embed = bitcoin.payments.embed({ data: [data] });
const psbt = new bitcoin.Psbt({ network: regtest })
.addInput(inputData1)
.addOutput({
script: embed.output!,
value: 1000,
})
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
value: 1e5,
})
.signInput(0, alice1.keys[0]);
assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
psbt.finalizeAllInputs();
// build and broadcast to the RegTest network
await regtestUtils.broadcast(psbt.extractTransaction().toHex());
});
it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2MS(2 of 4)) (multisig) input', async () => {
const multisig = createPayment('p2sh-p2ms(2 of 4)');
const inputData1 = await getInputData(2e4, multisig.payment, false, 'p2sh');
{
const {
hash,
index,
nonWitnessUtxo,
redeemScript, // NEW: P2SH needs to give redeemScript when adding an input.
} = inputData1;
assert.deepStrictEqual(
{ hash, index, nonWitnessUtxo, redeemScript },
inputData1,
);
}
const psbt = new bitcoin.Psbt({ network: regtest })
.addInput(inputData1)
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
value: 1e4,
})
.signInput(0, multisig.keys[0])
.signInput(0, multisig.keys[2]);
assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
assert.strictEqual(
psbt.validateSignaturesOfInput(0, multisig.keys[0].publicKey),
true,
);
assert.throws(() => {
psbt.validateSignaturesOfInput(0, multisig.keys[3].publicKey);
}, new RegExp('No signatures for this pubkey'));
psbt.finalizeAllInputs();
const tx = psbt.extractTransaction();
// build and broadcast to the Bitcoin RegTest network
await regtestUtils.broadcast(tx.toHex());
await regtestUtils.verify({
txId: tx.getId(),
address: regtestUtils.RANDOM_ADDRESS,
vout: 0,
value: 1e4,
});
});
it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WPKH) input', async () => {
const p2sh = createPayment('p2sh-p2wpkh');
const inputData = await getInputData(5e4, p2sh.payment, true, 'p2sh');
const inputData2 = await getInputData(5e4, p2sh.payment, true, 'p2sh');
{
const {
hash,
index,
witnessUtxo, // NEW: this is an object of the output being spent { script: Buffer; value: Satoshis; }
redeemScript,
} = inputData;
assert.deepStrictEqual(
{ hash, index, witnessUtxo, redeemScript },
inputData,
);
}
const keyPair = p2sh.keys[0];
const outputData = {
script: p2sh.payment.output, // sending to myself for fun
value: 2e4,
};
const outputData2 = {
script: p2sh.payment.output, // sending to myself for fun
value: 7e4,
};
const tx = new bitcoin.Psbt()
.addInputs([inputData, inputData2])
.addOutputs([outputData, outputData2])
.signAllInputs(keyPair)
.finalizeAllInputs()
.extractTransaction();
// build and broadcast to the Bitcoin RegTest network
await regtestUtils.broadcast(tx.toHex());
await regtestUtils.verify({
txId: tx.getId(),
address: p2sh.payment.address,
vout: 0,
value: 2e4,
});
});
it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WPKH) input with nonWitnessUtxo', async () => {
// For learning purposes, ignore this test.
// REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData
const p2sh = createPayment('p2sh-p2wpkh');
const inputData = await getInputData(5e4, p2sh.payment, false, 'p2sh');
const inputData2 = await getInputData(5e4, p2sh.payment, false, 'p2sh');
const keyPair = p2sh.keys[0];
const outputData = {
script: p2sh.payment.output,
value: 2e4,
};
const outputData2 = {
script: p2sh.payment.output,
value: 7e4,
};
const tx = new bitcoin.Psbt()
.addInputs([inputData, inputData2])
.addOutputs([outputData, outputData2])
.signAllInputs(keyPair)
.finalizeAllInputs()
.extractTransaction();
await regtestUtils.broadcast(tx.toHex());
await regtestUtils.verify({
txId: tx.getId(),
address: p2sh.payment.address,
vout: 0,
value: 2e4,
});
});
it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input', async () => {
// the only thing that changes is you don't give a redeemscript for input data
const p2wpkh = createPayment('p2wpkh');
const inputData = await getInputData(5e4, p2wpkh.payment, true, 'noredeem');
{
const { hash, index, witnessUtxo } = inputData;
assert.deepStrictEqual({ hash, index, witnessUtxo }, inputData);
}
const psbt = new bitcoin.Psbt({ network: regtest })
.addInput(inputData)
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
value: 2e4,
})
.signInput(0, p2wpkh.keys[0]);
assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
psbt.finalizeAllInputs();
const tx = psbt.extractTransaction();
// build and broadcast to the Bitcoin RegTest network
await regtestUtils.broadcast(tx.toHex());
await regtestUtils.verify({
txId: tx.getId(),
address: regtestUtils.RANDOM_ADDRESS,
vout: 0,
value: 2e4,
});
});
it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input with nonWitnessUtxo', async () => {
// For learning purposes, ignore this test.
// REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData
const p2wpkh = createPayment('p2wpkh');
const inputData = await getInputData(
5e4,
p2wpkh.payment,
false,
'noredeem',
);
const psbt = new bitcoin.Psbt({ network: regtest })
.addInput(inputData)
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
value: 2e4,
})
.signInput(0, p2wpkh.keys[0]);
psbt.finalizeAllInputs();
const tx = psbt.extractTransaction();
await regtestUtils.broadcast(tx.toHex());
await regtestUtils.verify({
txId: tx.getId(),
address: regtestUtils.RANDOM_ADDRESS,
vout: 0,
value: 2e4,
});
});
it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input', async () => {
const p2wsh = createPayment('p2wsh-p2pk');
const inputData = await getInputData(5e4, p2wsh.payment, true, 'p2wsh');
{
const {
hash,
index,
witnessUtxo,
witnessScript, // NEW: A Buffer of the witnessScript
} = inputData;
assert.deepStrictEqual(
{ hash, index, witnessUtxo, witnessScript },
inputData,
);
}
const psbt = new bitcoin.Psbt({ network: regtest })
.addInput(inputData)
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
value: 2e4,
})
.signInput(0, p2wsh.keys[0]);
assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
psbt.finalizeAllInputs();
const tx = psbt.extractTransaction();
// build and broadcast to the Bitcoin RegTest network
await regtestUtils.broadcast(tx.toHex());
await regtestUtils.verify({
txId: tx.getId(),
address: regtestUtils.RANDOM_ADDRESS,
vout: 0,
value: 2e4,
});
});
it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input with nonWitnessUtxo', async () => {
// For learning purposes, ignore this test.
// REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData
const p2wsh = createPayment('p2wsh-p2pk');
const inputData = await getInputData(5e4, p2wsh.payment, false, 'p2wsh');
const psbt = new bitcoin.Psbt({ network: regtest })
.addInput(inputData)
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
value: 2e4,
})
.signInput(0, p2wsh.keys[0]);
psbt.finalizeAllInputs();
const tx = psbt.extractTransaction();
await regtestUtils.broadcast(tx.toHex());
await regtestUtils.verify({
txId: tx.getId(),
address: regtestUtils.RANDOM_ADDRESS,
vout: 0,
value: 2e4,
});
});
it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input', async () => {
const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)');
const inputData = await getInputData(5e4, p2sh.payment, true, 'p2sh-p2wsh');
{
const {
hash,
index,
witnessUtxo,
redeemScript,
witnessScript,
} = inputData;
assert.deepStrictEqual(
{ hash, index, witnessUtxo, redeemScript, witnessScript },
inputData,
);
}
const psbt = new bitcoin.Psbt({ network: regtest })
.addInput(inputData)
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
value: 2e4,
})
.signInput(0, p2sh.keys[0])
.signInput(0, p2sh.keys[2])
.signInput(0, p2sh.keys[3]);
assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
assert.strictEqual(
psbt.validateSignaturesOfInput(0, p2sh.keys[3].publicKey),
true,
);
assert.throws(() => {
psbt.validateSignaturesOfInput(0, p2sh.keys[1].publicKey);
}, new RegExp('No signatures for this pubkey'));
psbt.finalizeAllInputs();
const tx = psbt.extractTransaction();
// build and broadcast to the Bitcoin RegTest network
await regtestUtils.broadcast(tx.toHex());
await regtestUtils.verify({
txId: tx.getId(),
address: regtestUtils.RANDOM_ADDRESS,
vout: 0,
value: 2e4,
});
});
it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input with nonWitnessUtxo', async () => {
// For learning purposes, ignore this test.
// REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData
const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)');
const inputData = await getInputData(
5e4,
p2sh.payment,
false,
'p2sh-p2wsh',
);
const psbt = new bitcoin.Psbt({ network: regtest })
.addInput(inputData)
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
value: 2e4,
})
.signInput(0, p2sh.keys[0])
.signInput(0, p2sh.keys[2])
.signInput(0, p2sh.keys[3]);
psbt.finalizeAllInputs();
const tx = psbt.extractTransaction();
await regtestUtils.broadcast(tx.toHex());
await regtestUtils.verify({
txId: tx.getId(),
address: regtestUtils.RANDOM_ADDRESS,
vout: 0,
value: 2e4,
});
});
it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input using HD', async () => {
const hdRoot = bip32.fromSeed(rng(64));
const masterFingerprint = hdRoot.fingerprint;
const path = "m/84'/0'/0'/0/0";
const childNode = hdRoot.derivePath(path);
const pubkey = childNode.publicKey;
// This information should be added to your input via updateInput
// You can add multiple bip32Derivation objects for multisig, but
// each must have a unique pubkey.
//
// This is useful because as long as you store the masterFingerprint on
// the PSBT Creator's server, you can have the PSBT Creator do the heavy
// lifting with derivation from your m/84'/0'/0' xpub, (deriving only 0/0 )
// and your signer just needs to pass in an HDSigner interface (ie. bip32 library)
const updateData = {
bip32Derivation: [
{
masterFingerprint,
path,
pubkey,
},
],
};
const p2wpkh = createPayment('p2wpkh', [childNode]);
const inputData = await getInputData(5e4, p2wpkh.payment, true, 'noredeem');
{
const { hash, index, witnessUtxo } = inputData;
assert.deepStrictEqual({ hash, index, witnessUtxo }, inputData);
}
// You can add extra attributes for updateData into the addInput(s) object(s)
Object.assign(inputData, updateData);
const psbt = new bitcoin.Psbt({ network: regtest })
.addInput(inputData)
// .updateInput(0, updateData) // if you didn't merge the bip32Derivation with inputData
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
value: 2e4,
})
.signInputHD(0, hdRoot); // must sign with root!!!
assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
assert.strictEqual(
psbt.validateSignaturesOfInput(0, childNode.publicKey),
true,
);
psbt.finalizeAllInputs();
const tx = psbt.extractTransaction();
// build and broadcast to the Bitcoin RegTest network
await regtestUtils.broadcast(tx.toHex());
await regtestUtils.verify({
txId: tx.getId(),
address: regtestUtils.RANDOM_ADDRESS,
vout: 0,
value: 2e4,
});
});
});
function createPayment(_type: string, myKeys?: any[], network?: any) {
network = network || regtest;
const splitType = _type.split('-').reverse();
const isMultisig = splitType[0].slice(0, 4) === 'p2ms';
const keys = myKeys || [];
let m: number | undefined;
if (isMultisig) {
const match = splitType[0].match(/^p2ms\((\d+) of (\d+)\)$/);
m = parseInt(match![1]);
let n = parseInt(match![2]);
if (keys.length > 0 && keys.length !== n) {
throw new Error('Need n keys for multisig');
}
while (!myKeys && n > 1) {
keys.push(bitcoin.ECPair.makeRandom({ network }));
n--;
}
}
if (!myKeys) keys.push(bitcoin.ECPair.makeRandom({ network }));
let payment: any;
splitType.forEach(type => {
if (type.slice(0, 4) === 'p2ms') {
payment = bitcoin.payments.p2ms({
m,
pubkeys: keys.map(key => key.publicKey).sort(),
network,
});
} else if (['p2sh', 'p2wsh'].indexOf(type) > -1) {
payment = (bitcoin.payments as any)[type]({
redeem: payment,
network,
});
} else {
payment = (bitcoin.payments as any)[type]({
pubkey: keys[0].publicKey,
network,
});
}
});
return {
payment,
keys,
};
}
function getWitnessUtxo(out: any) {
delete out.address;
out.script = Buffer.from(out.script, 'hex');
return out;
}
async function getInputData(
amount: number,
payment: any,
isSegwit: boolean,
redeemType: string,
) {
const unspent = await regtestUtils.faucetComplex(payment.output, amount);
const utx = await regtestUtils.fetch(unspent.txId);
// for non segwit inputs, you must pass the full transaction buffer
const nonWitnessUtxo = Buffer.from(utx.txHex, 'hex');
// for segwit inputs, you only need the output script and value as an object.
const witnessUtxo = getWitnessUtxo(utx.outs[unspent.vout]);
const mixin = isSegwit ? { witnessUtxo } : { nonWitnessUtxo };
const mixin2: any = {};
switch (redeemType) {
case 'p2sh':
mixin2.redeemScript = payment.redeem.output;
break;
case 'p2wsh':
mixin2.witnessScript = payment.redeem.output;
break;
case 'p2sh-p2wsh':
mixin2.witnessScript = payment.redeem.redeem.output;
mixin2.redeemScript = payment.redeem.output;
break;
}
return {
hash: unspent.txId,
index: unspent.vout,
...mixin,
...mixin2,
};
}

View file

@ -1,191 +1,231 @@
import * as assert from 'assert'; import * as assert from 'assert';
import * as bip32 from 'bip32';
import { describe, it } from 'mocha'; import { describe, it } from 'mocha';
import * as bitcoin from '../..'; import * as bitcoin from '../..';
import { regtestUtils } from './_regtest'; import { regtestUtils } from './_regtest';
const rng = require('randombytes');
const regtest = regtestUtils.network; const regtest = regtestUtils.network;
console.warn = () => {}; // Silence the Deprecation Warning
function rng() { // See bottom of file for some helper functions used to make the payment objects needed.
return Buffer.from('YT8dAtK4d16A3P1z+TpwB2jJ4aFH3g9M1EioIBkLEV4=', 'base64');
}
describe('bitcoinjs-lib (transactions)', () => { describe('bitcoinjs-lib (transactions with psbt)', () => {
it('can create a 1-to-1 Transaction', () => { it('can create a 1-to-1 Transaction', () => {
const alice = bitcoin.ECPair.fromWIF( const alice = bitcoin.ECPair.fromWIF(
'L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy', 'L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr',
); );
const txb = new bitcoin.TransactionBuilder(); const psbt = new bitcoin.Psbt();
psbt.setVersion(2); // These are defaults. This line is not needed.
psbt.setLocktime(0); // These are defaults. This line is not needed.
psbt.addInput({
// if hash is string, txid, if hash is Buffer, is reversed compared to txid
hash: '7d067b4a697a09d2c3cff7d4d9506c9955e93bff41bf82d439da7d030382bc3e',
index: 0,
sequence: 0xffffffff, // These are defaults. This line is not needed.
txb.setVersion(1); // non-segwit inputs now require passing the whole previous tx as Buffer
txb.addInput( nonWitnessUtxo: Buffer.from(
'61d520ccb74288c96bc1a2b20ea1c0d5a704776dd0164a396efec3ea7040349d', '0200000001f9f34e95b9d5c8abcd20fc5bd4a825d1517be62f0f775e5f36da944d9' +
0, '452e550000000006b483045022100c86e9a111afc90f64b4904bd609e9eaed80d48' +
); // Alice's previous transaction output, has 15000 satoshis 'ca17c162b1aca0a788ac3526f002207bb79b60d4fc6526329bf18a77135dc566020' +
txb.addOutput('1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP', 12000); '9e761da46e1c2f1152ec013215801210211755115eabf846720f5cb18f248666fec' +
// (in)15000 - (out)12000 = (fee)3000, this is the miner fee '631e5e1e66009ce3710ceea5b1ad13ffffffff01' +
// value in satoshis (Int64LE) = 0x015f90 = 90000
'905f010000000000' +
// scriptPubkey length
'19' +
// scriptPubkey
'76a9148bbc95d2709c71607c60ee3f097c1217482f518d88ac' +
// locktime
'00000000',
'hex',
),
txb.sign({ // // If this input was segwit, instead of nonWitnessUtxo, you would add
prevOutScriptType: 'p2pkh', // // a witnessUtxo as follows. The scriptPubkey and the value only are needed.
vin: 0, // witnessUtxo: {
keyPair: alice, // script: Buffer.from(
// '76a9148bbc95d2709c71607c60ee3f097c1217482f518d88ac',
// 'hex',
// ),
// value: 90000,
// },
// Not featured here:
// redeemScript. A Buffer of the redeemScript for P2SH
// witnessScript. A Buffer of the witnessScript for P2WSH
}); });
psbt.addOutput({
// prepare for broadcast to the Bitcoin network, see "can broadcast a Transaction" below address: '1KRMKfeZcmosxALVYESdPNez1AP1mEtywp',
assert.strictEqual( value: 80000,
txb.build().toHex(),
'01000000019d344070eac3fe6e394a16d06d7704a7d5c0a10eb2a2c16bc98842b7cc20d561000000006b48304502210088828c0bdfcdca68d8ae0caeb6ec62cd3fd5f9b2191848edae33feb533df35d302202e0beadd35e17e7f83a733f5277028a9b453d525553e3f5d2d7a7aa8010a81d60121029f50f51d63b345039a290c94bffd3180c99ed659ff6ea6b1242bca47eb93b59fffffffff01e02e0000000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac00000000',
);
}); });
psbt.signInput(0, alice);
it('can create a 2-to-2 Transaction', () => { psbt.validateSignaturesOfInput(0);
const alice = bitcoin.ECPair.fromWIF( psbt.finalizeAllInputs();
'L1Knwj9W3qK3qMKdTvmg3VfzUs3ij2LETTFhxza9LfD5dngnoLG1',
);
const bob = bitcoin.ECPair.fromWIF(
'KwcN2pT3wnRAurhy7qMczzbkpY5nXMW2ubh696UBc1bcwctTx26z',
);
const txb = new bitcoin.TransactionBuilder();
txb.setVersion(1);
txb.addInput(
'b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c',
6,
); // Alice's previous transaction output, has 200000 satoshis
txb.addInput(
'7d865e959b2466918c9863afca942d0fb89d7c9ac0c99bafc3749504ded97730',
0,
); // Bob's previous transaction output, has 300000 satoshis
txb.addOutput('1CUNEBjYrCn2y1SdiUMohaKUi4wpP326Lb', 180000);
txb.addOutput('1JtK9CQw1syfWj1WtFMWomrYdV3W2tWBF9', 170000);
// (in)(200000 + 300000) - (out)(180000 + 170000) = (fee)150000, this is the miner fee
txb.sign({
prevOutScriptType: 'p2pkh',
vin: 1,
keyPair: bob,
}); // Bob signs his input, which was the second input (1th)
txb.sign({
prevOutScriptType: 'p2pkh',
vin: 0,
keyPair: alice,
}); // Alice signs her input, which was the first input (0th)
// prepare for broadcast to the Bitcoin network, see "can broadcast a Transaction" below
assert.strictEqual( assert.strictEqual(
txb.build().toHex(), psbt.extractTransaction().toHex(),
'01000000024c94e48a870b85f41228d33cf25213dfcc8dd796e7211ed6b1f9a014809dbbb5060000006a473044022041450c258ce7cac7da97316bf2ea1ce66d88967c4df94f3e91f4c2a30f5d08cb02203674d516e6bb2b0afd084c3551614bd9cec3c2945231245e891b145f2d6951f0012103e05ce435e462ec503143305feb6c00e06a3ad52fbf939e85c65f3a765bb7baacffffffff3077d9de049574c3af9bc9c09a7c9db80f2d94caaf63988c9166249b955e867d000000006b483045022100aeb5f1332c79c446d3f906e4499b2e678500580a3f90329edf1ba502eec9402e022072c8b863f8c8d6c26f4c691ac9a6610aa4200edc697306648ee844cfbc089d7a012103df7940ee7cddd2f97763f67e1fb13488da3fbdd7f9c68ec5ef0864074745a289ffffffff0220bf0200000000001976a9147dd65592d0ab2fe0d0257d571abf032cd9db93dc88ac10980200000000001976a914c42e7ef92fdb603af844d064faad95db9bcdfd3d88ac00000000', '02000000013ebc8203037dda39d482bf41ff3be955996c50d9d4f7cfc3d2097a694a7' +
'b067d000000006b483045022100931b6db94aed25d5486884d83fc37160f37f3368c0' +
'd7f48c757112abefec983802205fda64cff98c849577026eb2ce916a50ea70626a766' +
'9f8596dd89b720a26b4d501210365db9da3f8a260078a7e8f8b708a1161468fb2323f' +
'fda5ec16b261ec1056f455ffffffff0180380100000000001976a914ca0d36044e0dc' +
'08a22724efa6f6a07b0ec4c79aa88ac00000000',
); );
}); });
it('can create (and broadcast via 3PBP) a typical Transaction', async () => { it('can create (and broadcast via 3PBP) a typical Transaction', async () => {
const alice1 = bitcoin.ECPair.makeRandom({ network: regtest }); // these are { payment: Payment; keys: ECPair[] }
const alice2 = bitcoin.ECPair.makeRandom({ network: regtest }); const alice1 = createPayment('p2pkh');
const aliceChange = bitcoin.ECPair.makeRandom({ const alice2 = createPayment('p2pkh');
network: regtest,
rng: rng,
});
const alice1pkh = bitcoin.payments.p2pkh({
pubkey: alice1.publicKey,
network: regtest,
});
const alice2pkh = bitcoin.payments.p2pkh({
pubkey: alice2.publicKey,
network: regtest,
});
const aliceCpkh = bitcoin.payments.p2pkh({
pubkey: aliceChange.publicKey,
network: regtest,
});
// give Alice 2 unspent outputs // give Alice 2 unspent outputs
const unspent0 = await regtestUtils.faucet(alice1pkh.address!, 5e4); const inputData1 = await getInputData(
5e4,
alice1.payment,
false,
'noredeem',
);
const inputData2 = await getInputData(
7e4,
alice2.payment,
false,
'noredeem',
);
{
const {
hash, // string of txid or Buffer of tx hash. (txid and hash are reverse order)
index, // the output index of the txo you are spending
nonWitnessUtxo, // the full previous transaction as a Buffer
} = inputData1;
assert.deepStrictEqual({ hash, index, nonWitnessUtxo }, inputData1);
}
const unspent1 = await regtestUtils.faucet(alice2pkh.address!, 7e4); // network is only needed if you pass an address to addOutput
// using script (Buffer of scriptPubkey) instead will avoid needed network.
const txb = new bitcoin.TransactionBuilder(regtest); const psbt = new bitcoin.Psbt({ network: regtest })
txb.addInput(unspent0.txId, unspent0.vout); // alice1 unspent .addInput(inputData1) // alice1 unspent
txb.addInput(unspent1.txId, unspent1.vout); // alice2 unspent .addInput(inputData2) // alice2 unspent
txb.addOutput('mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf', 8e4); // the actual "spend" .addOutput({
txb.addOutput(aliceCpkh.address!, 1e4); // Alice's change address: 'mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf',
value: 8e4,
}) // the actual "spend"
.addOutput({
address: alice2.payment.address, // OR script, which is a Buffer.
value: 1e4,
}); // Alice's change
// (in)(5e4 + 7e4) - (out)(8e4 + 1e4) = (fee)3e4 = 30000, this is the miner fee // (in)(5e4 + 7e4) - (out)(8e4 + 1e4) = (fee)3e4 = 30000, this is the miner fee
// Let's show a new feature with PSBT.
// We can have multiple signers sign in parrallel and combine them.
// (this is not necessary, but a nice feature)
// encode to send out to the signers
const psbtBaseText = psbt.toBase64();
// each signer imports
const signer1 = bitcoin.Psbt.fromBase64(psbtBaseText);
const signer2 = bitcoin.Psbt.fromBase64(psbtBaseText);
// Alice signs each input with the respective private keys // Alice signs each input with the respective private keys
txb.sign({ // signInput and signInputAsync are better
prevOutScriptType: 'p2pkh', // (They take the input index explicitly as the first arg)
vin: 0, signer1.signAllInputs(alice1.keys[0]);
keyPair: alice1, signer2.signAllInputs(alice2.keys[0]);
});
txb.sign({ // If your signer object's sign method returns a promise, use the following
prevOutScriptType: 'p2pkh', // await signer2.signAllInputsAsync(alice2.keys[0])
vin: 1,
keyPair: alice2, // encode to send back to combiner (signer 1 and 2 are not near each other)
}); const s1text = signer1.toBase64();
const s2text = signer2.toBase64();
const final1 = bitcoin.Psbt.fromBase64(s1text);
const final2 = bitcoin.Psbt.fromBase64(s2text);
// final1.combine(final2) would give the exact same result
psbt.combine(final1, final2);
// Finalizer wants to check all signatures are valid before finalizing.
// If the finalizer wants to check for specific pubkeys, the second arg
// can be passed. See the first multisig example below.
assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
assert.strictEqual(psbt.validateSignaturesOfInput(1), true);
// This step it new. Since we separate the signing operation and
// the creation of the scriptSig and witness stack, we are able to
psbt.finalizeAllInputs();
// build and broadcast our RegTest network // build and broadcast our RegTest network
await regtestUtils.broadcast(txb.build().toHex()); await regtestUtils.broadcast(psbt.extractTransaction().toHex());
// to build and broadcast to the actual Bitcoin network, see https://github.com/bitcoinjs/bitcoinjs-lib/issues/839 // to build and broadcast to the actual Bitcoin network, see https://github.com/bitcoinjs/bitcoinjs-lib/issues/839
}); });
it('can create (and broadcast via 3PBP) a Transaction with an OP_RETURN output', async () => { it('can create (and broadcast via 3PBP) a Transaction with an OP_RETURN output', async () => {
const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }); const alice1 = createPayment('p2pkh');
const p2pkh = bitcoin.payments.p2pkh({ const inputData1 = await getInputData(
pubkey: keyPair.publicKey, 2e5,
network: regtest, alice1.payment,
}); false,
'noredeem',
);
const unspent = await regtestUtils.faucet(p2pkh.address!, 2e5);
const txb = new bitcoin.TransactionBuilder(regtest);
const data = Buffer.from('bitcoinjs-lib', 'utf8'); const data = Buffer.from('bitcoinjs-lib', 'utf8');
const embed = bitcoin.payments.embed({ data: [data] }); const embed = bitcoin.payments.embed({ data: [data] });
txb.addInput(unspent.txId, unspent.vout);
txb.addOutput(embed.output!, 1000); const psbt = new bitcoin.Psbt({ network: regtest })
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e5); .addInput(inputData1)
txb.sign({ .addOutput({
prevOutScriptType: 'p2pkh', script: embed.output!,
vin: 0, value: 1000,
keyPair, })
}); .addOutput({
address: regtestUtils.RANDOM_ADDRESS,
value: 1e5,
})
.signInput(0, alice1.keys[0]);
assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
psbt.finalizeAllInputs();
// build and broadcast to the RegTest network // build and broadcast to the RegTest network
await regtestUtils.broadcast(txb.build().toHex()); await regtestUtils.broadcast(psbt.extractTransaction().toHex());
}); });
it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2MS(2 of 4)) (multisig) input', async () => { it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2MS(2 of 4)) (multisig) input', async () => {
const keyPairs = [ const multisig = createPayment('p2sh-p2ms(2 of 4)');
bitcoin.ECPair.makeRandom({ network: regtest }), const inputData1 = await getInputData(2e4, multisig.payment, false, 'p2sh');
bitcoin.ECPair.makeRandom({ network: regtest }), {
bitcoin.ECPair.makeRandom({ network: regtest }), const {
bitcoin.ECPair.makeRandom({ network: regtest }), hash,
]; index,
const pubkeys = keyPairs.map(x => x.publicKey); nonWitnessUtxo,
const p2ms = bitcoin.payments.p2ms({ redeemScript, // NEW: P2SH needs to give redeemScript when adding an input.
m: 2, } = inputData1;
pubkeys: pubkeys, assert.deepStrictEqual(
network: regtest, { hash, index, nonWitnessUtxo, redeemScript },
}); inputData1,
const p2sh = bitcoin.payments.p2sh({ redeem: p2ms, network: regtest }); );
}
const unspent = await regtestUtils.faucet(p2sh.address!, 2e4); const psbt = new bitcoin.Psbt({ network: regtest })
.addInput(inputData1)
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
value: 1e4,
})
.signInput(0, multisig.keys[0])
.signInput(0, multisig.keys[2]);
const txb = new bitcoin.TransactionBuilder(regtest); assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
txb.addInput(unspent.txId, unspent.vout); assert.strictEqual(
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4); psbt.validateSignaturesOfInput(0, multisig.keys[0].publicKey),
true,
);
assert.throws(() => {
psbt.validateSignaturesOfInput(0, multisig.keys[3].publicKey);
}, new RegExp('No signatures for this pubkey'));
psbt.finalizeAllInputs();
txb.sign({ const tx = psbt.extractTransaction();
prevOutScriptType: 'p2sh-p2ms',
vin: 0,
keyPair: keyPairs[0],
redeemScript: p2sh.redeem!.output,
});
txb.sign({
prevOutScriptType: 'p2sh-p2ms',
vin: 0,
keyPair: keyPairs[2],
redeemScript: p2sh.redeem!.output,
});
const tx = txb.build();
// build and broadcast to the Bitcoin RegTest network // build and broadcast to the Bitcoin RegTest network
await regtestUtils.broadcast(tx.toHex()); await regtestUtils.broadcast(tx.toHex());
@ -199,27 +239,101 @@ describe('bitcoinjs-lib (transactions)', () => {
}); });
it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WPKH) input', async () => { it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WPKH) input', async () => {
const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }); const p2sh = createPayment('p2sh-p2wpkh');
const p2wpkh = bitcoin.payments.p2wpkh({ const inputData = await getInputData(5e4, p2sh.payment, true, 'p2sh');
pubkey: keyPair.publicKey, const inputData2 = await getInputData(5e4, p2sh.payment, true, 'p2sh');
network: regtest, {
const {
hash,
index,
witnessUtxo, // NEW: this is an object of the output being spent { script: Buffer; value: Satoshis; }
redeemScript,
} = inputData;
assert.deepStrictEqual(
{ hash, index, witnessUtxo, redeemScript },
inputData,
);
}
const keyPair = p2sh.keys[0];
const outputData = {
script: p2sh.payment.output, // sending to myself for fun
value: 2e4,
};
const outputData2 = {
script: p2sh.payment.output, // sending to myself for fun
value: 7e4,
};
const tx = new bitcoin.Psbt()
.addInputs([inputData, inputData2])
.addOutputs([outputData, outputData2])
.signAllInputs(keyPair)
.finalizeAllInputs()
.extractTransaction();
// build and broadcast to the Bitcoin RegTest network
await regtestUtils.broadcast(tx.toHex());
await regtestUtils.verify({
txId: tx.getId(),
address: p2sh.payment.address,
vout: 0,
value: 2e4,
}); });
const p2sh = bitcoin.payments.p2sh({ redeem: p2wpkh, network: regtest });
const unspent = await regtestUtils.faucet(p2sh.address!, 5e4);
const txb = new bitcoin.TransactionBuilder(regtest);
txb.addInput(unspent.txId, unspent.vout);
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4);
txb.sign({
prevOutScriptType: 'p2sh-p2wpkh',
vin: 0,
keyPair: keyPair,
redeemScript: p2sh.redeem!.output,
witnessValue: unspent.value,
}); });
const tx = txb.build(); it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WPKH) input with nonWitnessUtxo', async () => {
// For learning purposes, ignore this test.
// REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData
const p2sh = createPayment('p2sh-p2wpkh');
const inputData = await getInputData(5e4, p2sh.payment, false, 'p2sh');
const inputData2 = await getInputData(5e4, p2sh.payment, false, 'p2sh');
const keyPair = p2sh.keys[0];
const outputData = {
script: p2sh.payment.output,
value: 2e4,
};
const outputData2 = {
script: p2sh.payment.output,
value: 7e4,
};
const tx = new bitcoin.Psbt()
.addInputs([inputData, inputData2])
.addOutputs([outputData, outputData2])
.signAllInputs(keyPair)
.finalizeAllInputs()
.extractTransaction();
await regtestUtils.broadcast(tx.toHex());
await regtestUtils.verify({
txId: tx.getId(),
address: p2sh.payment.address,
vout: 0,
value: 2e4,
});
});
it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input', async () => {
// the only thing that changes is you don't give a redeemscript for input data
const p2wpkh = createPayment('p2wpkh');
const inputData = await getInputData(5e4, p2wpkh.payment, true, 'noredeem');
{
const { hash, index, witnessUtxo } = inputData;
assert.deepStrictEqual({ hash, index, witnessUtxo }, inputData);
}
const psbt = new bitcoin.Psbt({ network: regtest })
.addInput(inputData)
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
value: 2e4,
})
.signInput(0, p2wpkh.keys[0]);
assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
psbt.finalizeAllInputs();
const tx = psbt.extractTransaction();
// build and broadcast to the Bitcoin RegTest network // build and broadcast to the Bitcoin RegTest network
await regtestUtils.broadcast(tx.toHex()); await regtestUtils.broadcast(tx.toHex());
@ -232,30 +346,26 @@ describe('bitcoinjs-lib (transactions)', () => {
}); });
}); });
it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input', async () => { it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input with nonWitnessUtxo', async () => {
const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }); // For learning purposes, ignore this test.
const p2wpkh = bitcoin.payments.p2wpkh({ // REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData
pubkey: keyPair.publicKey, const p2wpkh = createPayment('p2wpkh');
network: regtest, const inputData = await getInputData(
}); 5e4,
p2wpkh.payment,
const unspent = await regtestUtils.faucetComplex(p2wpkh.output!, 5e4); false,
'noredeem',
// XXX: build the Transaction w/ a P2WPKH input );
const txb = new bitcoin.TransactionBuilder(regtest); const psbt = new bitcoin.Psbt({ network: regtest })
txb.addInput(unspent.txId, unspent.vout, undefined, p2wpkh.output); // NOTE: provide the prevOutScript! .addInput(inputData)
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4); .addOutput({
txb.sign({ address: regtestUtils.RANDOM_ADDRESS,
prevOutScriptType: 'p2wpkh', value: 2e4,
vin: 0, })
keyPair: keyPair, .signInput(0, p2wpkh.keys[0]);
witnessValue: unspent.value, psbt.finalizeAllInputs();
}); // NOTE: no redeem script const tx = psbt.extractTransaction();
const tx = txb.build();
// build and broadcast (the P2WPKH transaction) to the Bitcoin RegTest network
await regtestUtils.broadcast(tx.toHex()); await regtestUtils.broadcast(tx.toHex());
await regtestUtils.verify({ await regtestUtils.verify({
txId: tx.getId(), txId: tx.getId(),
address: regtestUtils.RANDOM_ADDRESS, address: regtestUtils.RANDOM_ADDRESS,
@ -265,29 +375,35 @@ describe('bitcoinjs-lib (transactions)', () => {
}); });
it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input', async () => { it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input', async () => {
const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }); const p2wsh = createPayment('p2wsh-p2pk');
const p2pk = bitcoin.payments.p2pk({ const inputData = await getInputData(5e4, p2wsh.payment, true, 'p2wsh');
pubkey: keyPair.publicKey, {
network: regtest, const {
}); hash,
const p2wsh = bitcoin.payments.p2wsh({ redeem: p2pk, network: regtest }); index,
witnessUtxo,
witnessScript, // NEW: A Buffer of the witnessScript
} = inputData;
assert.deepStrictEqual(
{ hash, index, witnessUtxo, witnessScript },
inputData,
);
}
const unspent = await regtestUtils.faucetComplex(p2wsh.output!, 5e4); const psbt = new bitcoin.Psbt({ network: regtest })
.addInput(inputData)
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
value: 2e4,
})
.signInput(0, p2wsh.keys[0]);
// XXX: build the Transaction w/ a P2WSH input assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
const txb = new bitcoin.TransactionBuilder(regtest); psbt.finalizeAllInputs();
txb.addInput(unspent.txId, unspent.vout, undefined, p2wsh.output); // NOTE: provide the prevOutScript!
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4);
txb.sign({
prevOutScriptType: 'p2wsh-p2pk',
vin: 0,
keyPair: keyPair,
witnessValue: 5e4,
witnessScript: p2wsh.redeem!.output,
}); // NOTE: provide a witnessScript!
const tx = txb.build();
// build and broadcast (the P2WSH transaction) to the Bitcoin RegTest network const tx = psbt.extractTransaction();
// build and broadcast to the Bitcoin RegTest network
await regtestUtils.broadcast(tx.toHex()); await regtestUtils.broadcast(tx.toHex());
await regtestUtils.verify({ await regtestUtils.verify({
@ -298,50 +414,75 @@ describe('bitcoinjs-lib (transactions)', () => {
}); });
}); });
it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input', async () => { it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input with nonWitnessUtxo', async () => {
const keyPairs = [ // For learning purposes, ignore this test.
bitcoin.ECPair.makeRandom({ network: regtest }), // REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData
bitcoin.ECPair.makeRandom({ network: regtest }), const p2wsh = createPayment('p2wsh-p2pk');
bitcoin.ECPair.makeRandom({ network: regtest }), const inputData = await getInputData(5e4, p2wsh.payment, false, 'p2wsh');
bitcoin.ECPair.makeRandom({ network: regtest }), const psbt = new bitcoin.Psbt({ network: regtest })
]; .addInput(inputData)
const pubkeys = keyPairs.map(x => x.publicKey); .addOutput({
address: regtestUtils.RANDOM_ADDRESS,
const p2ms = bitcoin.payments.p2ms({ m: 3, pubkeys, network: regtest }); value: 2e4,
const p2wsh = bitcoin.payments.p2wsh({ redeem: p2ms, network: regtest }); })
const p2sh = bitcoin.payments.p2sh({ redeem: p2wsh, network: regtest }); .signInput(0, p2wsh.keys[0]);
psbt.finalizeAllInputs();
const unspent = await regtestUtils.faucet(p2sh.address!, 6e4); const tx = psbt.extractTransaction();
await regtestUtils.broadcast(tx.toHex());
const txb = new bitcoin.TransactionBuilder(regtest); await regtestUtils.verify({
txb.addInput(unspent.txId, unspent.vout, undefined, p2sh.output); txId: tx.getId(),
txb.addOutput(regtestUtils.RANDOM_ADDRESS, 3e4); address: regtestUtils.RANDOM_ADDRESS,
txb.sign({ vout: 0,
prevOutScriptType: 'p2sh-p2wsh-p2ms', value: 2e4,
vin: 0,
keyPair: keyPairs[0],
redeemScript: p2sh.redeem!.output,
witnessValue: unspent.value,
witnessScript: p2wsh.redeem!.output,
}); });
txb.sign({
prevOutScriptType: 'p2sh-p2wsh-p2ms',
vin: 0,
keyPair: keyPairs[2],
redeemScript: p2sh.redeem!.output,
witnessValue: unspent.value,
witnessScript: p2wsh.redeem!.output,
});
txb.sign({
prevOutScriptType: 'p2sh-p2wsh-p2ms',
vin: 0,
keyPair: keyPairs[3],
redeemScript: p2sh.redeem!.output,
witnessValue: unspent.value,
witnessScript: p2wsh.redeem!.output,
}); });
const tx = txb.build(); it(
'can create (and broadcast via 3PBP) a Transaction, w/ a ' +
'P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input',
async () => {
const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)');
const inputData = await getInputData(
5e4,
p2sh.payment,
true,
'p2sh-p2wsh',
);
{
const {
hash,
index,
witnessUtxo,
redeemScript,
witnessScript,
} = inputData;
assert.deepStrictEqual(
{ hash, index, witnessUtxo, redeemScript, witnessScript },
inputData,
);
}
const psbt = new bitcoin.Psbt({ network: regtest })
.addInput(inputData)
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
value: 2e4,
})
.signInput(0, p2sh.keys[0])
.signInput(0, p2sh.keys[2])
.signInput(0, p2sh.keys[3]);
assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
assert.strictEqual(
psbt.validateSignaturesOfInput(0, p2sh.keys[3].publicKey),
true,
);
assert.throws(() => {
psbt.validateSignaturesOfInput(0, p2sh.keys[1].publicKey);
}, new RegExp('No signatures for this pubkey'));
psbt.finalizeAllInputs();
const tx = psbt.extractTransaction();
// build and broadcast to the Bitcoin RegTest network // build and broadcast to the Bitcoin RegTest network
await regtestUtils.broadcast(tx.toHex()); await regtestUtils.broadcast(tx.toHex());
@ -350,72 +491,192 @@ describe('bitcoinjs-lib (transactions)', () => {
txId: tx.getId(), txId: tx.getId(),
address: regtestUtils.RANDOM_ADDRESS, address: regtestUtils.RANDOM_ADDRESS,
vout: 0, vout: 0,
value: 3e4, value: 2e4,
}); });
});
it('can verify Transaction (P2PKH) signatures', () => {
const txHex =
'010000000321c5f7e7bc98b3feda84aad36a5c99a02bcb8823a2f3eccbcd5da209698b5c20000000006b48304502210099e021772830207cf7c55b69948d3b16b4dcbf1f55a9cd80ebf8221a169735f9022064d33f11d62cd28240b3862afc0b901adc9f231c7124dd19bdb30367b61964c50121032b4c06c06c3ec0b7fa29519dfa5aae193ee2cc35ca127f29f14ec605d62fb63dffffffff8a75ce85441ddb3f342708ee33cc8ed418b07d9ba9e0e7c4e1cccfe9f52d8a88000000006946304302207916c23dae212c95a920423902fa44e939fb3d542f4478a7b46e9cde53705800021f0d74e9504146e404c1b8f9cba4dff2d4782e3075491c9ed07ce4a7d1c4461a01210216c92abe433106491bdeb4a261226f20f5a4ac86220cc6e37655aac6bf3c1f2affffffffdfef93f69fe32e944fad79fa8f882b3a155d80383252348caba1a77a5abbf7ef000000006b483045022100faa6e9ca289b46c64764a624c59ac30d9abcf1d4a04c4de9089e67cbe0d300a502206930afa683f6807502de5c2431bf9a1fd333c8a2910a76304df0f3d23d83443f0121039e05da8b8ea4f9868ecebb25998c7701542986233f4401799551fbecf316b18fffffffff01ff4b0000000000001976a9146c86476d1d85cd60116cd122a274e6a570a5a35c88acc96d0700';
const keyPairs = [
'032b4c06c06c3ec0b7fa29519dfa5aae193ee2cc35ca127f29f14ec605d62fb63d',
'0216c92abe433106491bdeb4a261226f20f5a4ac86220cc6e37655aac6bf3c1f2a',
'039e05da8b8ea4f9868ecebb25998c7701542986233f4401799551fbecf316b18f',
].map(q => {
return bitcoin.ECPair.fromPublicKey(Buffer.from(q, 'hex'));
});
const tx = bitcoin.Transaction.fromHex(txHex);
tx.ins.forEach((input, i) => {
const keyPair = keyPairs[i];
const p2pkh = bitcoin.payments.p2pkh({
pubkey: keyPair.publicKey,
input: input.script,
});
const ss = bitcoin.script.signature.decode(p2pkh.signature!);
const hash = tx.hashForSignature(i, p2pkh.output!, ss.hashType);
assert.strictEqual(keyPair.verify(hash, ss.signature), true);
});
});
it('can verify Transaction (P2SH(P2WPKH)) signatures', () => {
const utxos = {
'f72d1d83ac40fcedd01415751556a905844ab5f44bbb7728565ebb91b1590109:0': {
value: 50000,
}, },
};
const txHex =
'02000000000101090159b191bb5e562877bb4bf4b54a8405a95615751514d0edfc40ac831d2df7000000001716001435a179e5516947a39ae9c8a25e9fe62c0fc598edffffffff01204e0000000000001976a91431d43308d3c886d53e9ae8a45728370571ff456988ac0247304402206ec41f685b997a51f325b07ee852e82a535f6b52ef54485cc133e05168aa052a022070bafa86108acb51c77b2b259ae8fb7fd1efa10fef804fcfe9b13c2db719acf5012103fb03e9d0a9af86cbed94225dbb8bb70f6b82109bce0a61ddcf41dab6cbb4871100000000';
const tx = bitcoin.Transaction.fromHex(txHex);
tx.ins.forEach((input, i) => {
const txId = (Buffer.from(input.hash).reverse() as Buffer).toString(
'hex',
); );
const utxo = (utxos as any)[`${txId}:${i}`];
if (!utxo) throw new Error('Missing utxo');
const p2sh = bitcoin.payments.p2sh({ it(
input: input.script, 'can create (and broadcast via 3PBP) a Transaction, w/ a ' +
witness: input.witness, 'P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input with nonWitnessUtxo',
async () => {
// For learning purposes, ignore this test.
// REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData
const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)');
const inputData = await getInputData(
5e4,
p2sh.payment,
false,
'p2sh-p2wsh',
);
const psbt = new bitcoin.Psbt({ network: regtest })
.addInput(inputData)
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
value: 2e4,
})
.signInput(0, p2sh.keys[0])
.signInput(0, p2sh.keys[2])
.signInput(0, p2sh.keys[3]);
psbt.finalizeAllInputs();
const tx = psbt.extractTransaction();
await regtestUtils.broadcast(tx.toHex());
await regtestUtils.verify({
txId: tx.getId(),
address: regtestUtils.RANDOM_ADDRESS,
vout: 0,
value: 2e4,
}); });
const p2wpkh = bitcoin.payments.p2wpkh(p2sh.redeem!); },
const p2pkh = bitcoin.payments.p2pkh({ pubkey: p2wpkh.pubkey }); // because P2WPKH is annoying
const ss = bitcoin.script.signature.decode(p2wpkh.signature!);
const hash = tx.hashForWitnessV0(
i,
p2pkh.output!,
utxo.value,
ss.hashType,
); );
const keyPair = bitcoin.ECPair.fromPublicKey(p2wpkh.pubkey!); // aka, cQ3EtF4mApRcogNGSeyPTKbmfxxn3Yfb1wecfKSws9a8bnYuxoAk
assert.strictEqual(keyPair.verify(hash, ss.signature), true); it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input using HD', async () => {
const hdRoot = bip32.fromSeed(rng(64));
const masterFingerprint = hdRoot.fingerprint;
const path = "m/84'/0'/0'/0/0";
const childNode = hdRoot.derivePath(path);
const pubkey = childNode.publicKey;
// This information should be added to your input via updateInput
// You can add multiple bip32Derivation objects for multisig, but
// each must have a unique pubkey.
//
// This is useful because as long as you store the masterFingerprint on
// the PSBT Creator's server, you can have the PSBT Creator do the heavy
// lifting with derivation from your m/84'/0'/0' xpub, (deriving only 0/0 )
// and your signer just needs to pass in an HDSigner interface (ie. bip32 library)
const updateData = {
bip32Derivation: [
{
masterFingerprint,
path,
pubkey,
},
],
};
const p2wpkh = createPayment('p2wpkh', [childNode]);
const inputData = await getInputData(5e4, p2wpkh.payment, true, 'noredeem');
{
const { hash, index, witnessUtxo } = inputData;
assert.deepStrictEqual({ hash, index, witnessUtxo }, inputData);
}
// You can add extra attributes for updateData into the addInput(s) object(s)
Object.assign(inputData, updateData);
const psbt = new bitcoin.Psbt({ network: regtest })
.addInput(inputData)
// .updateInput(0, updateData) // if you didn't merge the bip32Derivation with inputData
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
value: 2e4,
})
.signInputHD(0, hdRoot); // must sign with root!!!
assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
assert.strictEqual(
psbt.validateSignaturesOfInput(0, childNode.publicKey),
true,
);
psbt.finalizeAllInputs();
const tx = psbt.extractTransaction();
// build and broadcast to the Bitcoin RegTest network
await regtestUtils.broadcast(tx.toHex());
await regtestUtils.verify({
txId: tx.getId(),
address: regtestUtils.RANDOM_ADDRESS,
vout: 0,
value: 2e4,
}); });
}); });
}); });
function createPayment(_type: string, myKeys?: any[], network?: any): any {
network = network || regtest;
const splitType = _type.split('-').reverse();
const isMultisig = splitType[0].slice(0, 4) === 'p2ms';
const keys = myKeys || [];
let m: number | undefined;
if (isMultisig) {
const match = splitType[0].match(/^p2ms\((\d+) of (\d+)\)$/);
m = parseInt(match![1], 10);
let n = parseInt(match![2], 10);
if (keys.length > 0 && keys.length !== n) {
throw new Error('Need n keys for multisig');
}
while (!myKeys && n > 1) {
keys.push(bitcoin.ECPair.makeRandom({ network }));
n--;
}
}
if (!myKeys) keys.push(bitcoin.ECPair.makeRandom({ network }));
let payment: any;
splitType.forEach(type => {
if (type.slice(0, 4) === 'p2ms') {
payment = bitcoin.payments.p2ms({
m,
pubkeys: keys.map(key => key.publicKey).sort(),
network,
});
} else if (['p2sh', 'p2wsh'].indexOf(type) > -1) {
payment = (bitcoin.payments as any)[type]({
redeem: payment,
network,
});
} else {
payment = (bitcoin.payments as any)[type]({
pubkey: keys[0].publicKey,
network,
});
}
});
return {
payment,
keys,
};
}
function getWitnessUtxo(out: any): any {
delete out.address;
out.script = Buffer.from(out.script, 'hex');
return out;
}
async function getInputData(
amount: number,
payment: any,
isSegwit: boolean,
redeemType: string,
): Promise<any> {
const unspent = await regtestUtils.faucetComplex(payment.output, amount);
const utx = await regtestUtils.fetch(unspent.txId);
// for non segwit inputs, you must pass the full transaction buffer
const nonWitnessUtxo = Buffer.from(utx.txHex, 'hex');
// for segwit inputs, you only need the output script and value as an object.
const witnessUtxo = getWitnessUtxo(utx.outs[unspent.vout]);
const mixin = isSegwit ? { witnessUtxo } : { nonWitnessUtxo };
const mixin2: any = {};
switch (redeemType) {
case 'p2sh':
mixin2.redeemScript = payment.redeem.output;
break;
case 'p2wsh':
mixin2.witnessScript = payment.redeem.output;
break;
case 'p2sh-p2wsh':
mixin2.witnessScript = payment.redeem.redeem.output;
mixin2.redeemScript = payment.redeem.output;
break;
}
return {
hash: unspent.txId,
index: unspent.vout,
...mixin,
...mixin2,
};
}

View file

@ -1,11 +1,11 @@
import * as assert from 'assert'; import * as assert from 'assert';
import { describe, it } from 'mocha'; import { describe, it } from 'mocha';
import * as u from './payments.utils';
import { PaymentCreator } from '../src/payments'; import { PaymentCreator } from '../src/payments';
import * as u from './payments.utils';
['embed', 'p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh'].forEach(p => { ['embed', 'p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh'].forEach(p => {
describe(p, () => { describe(p, () => {
let fn: PaymentCreator; let fn: PaymentCreator;
let payment = require('../src/payments/' + p); const payment = require('../src/payments/' + p);
if (p === 'embed') { if (p === 'embed') {
fn = payment.p2data; fn = payment.p2data;
} else { } else {
@ -83,7 +83,7 @@ import { PaymentCreator } from '../src/payments';
disabled[k] = true; disabled[k] = true;
}); });
for (let key in depends) { for (const key in depends) {
if (key in disabled) continue; if (key in disabled) continue;
const dependencies = depends[key]; const dependencies = depends[key];

View file

@ -1,6 +1,6 @@
import * as t from 'assert'; import * as t from 'assert';
import * as bscript from '../src/script';
import * as BNETWORKS from '../src/networks'; import * as BNETWORKS from '../src/networks';
import * as bscript from '../src/script';
function tryHex(x: Buffer | Buffer[]): string | string[] { function tryHex(x: Buffer | Buffer[]): string | string[] {
if (Buffer.isBuffer(x)) return x.toString('hex'); if (Buffer.isBuffer(x)) return x.toString('hex');
@ -17,12 +17,13 @@ function tryASM(x: Buffer): string {
if (Buffer.isBuffer(x)) return bscript.toASM(x); if (Buffer.isBuffer(x)) return bscript.toASM(x);
return x; return x;
} }
function asmToBuffer(x: string) { function asmToBuffer(x: string): Buffer {
if (x === '') return Buffer.alloc(0); if (x === '') return Buffer.alloc(0);
return bscript.fromASM(x); return bscript.fromASM(x);
} }
function carryOver(a: any, b: any) { function carryOver(a: any, b: any): void {
for (let k in b) { for (const k in b) {
if (!k) continue;
if (k in a && k === 'redeem') { if (k in a && k === 'redeem') {
carryOver(a[k], b[k]); carryOver(a[k], b[k]);
continue; continue;
@ -36,7 +37,7 @@ function carryOver(a: any, b: any) {
} }
} }
function equateBase(a: any, b: any, context: string) { function equateBase(a: any, b: any, context: string): void {
if ('output' in b) if ('output' in b)
t.strictEqual( t.strictEqual(
tryASM(a.output), tryASM(a.output),
@ -53,7 +54,7 @@ function equateBase(a: any, b: any, context: string) {
); );
} }
export function equate(a: any, b: any, args?: any) { export function equate(a: any, b: any, args?: any): void {
b = Object.assign({}, b); b = Object.assign({}, b);
carryOver(b, args); carryOver(b, args);
@ -108,7 +109,7 @@ export function equate(a: any, b: any, args?: any) {
t.deepStrictEqual(tryHex(a.data), tryHex(b.data), 'Inequal *.data'); t.deepStrictEqual(tryHex(a.data), tryHex(b.data), 'Inequal *.data');
} }
export function preform(x: any) { export function preform(x: any): any {
x = Object.assign({}, x); x = Object.assign({}, x);
if (x.network) x.network = (BNETWORKS as any)[x.network]; if (x.network) x.network = (BNETWORKS as any)[x.network];
@ -148,7 +149,7 @@ export function preform(x: any) {
return x; return x;
} }
export function from(path: string, object: any, result?: any) { export function from(path: string, object: any, result?: any): any {
const paths = path.split('.'); const paths = path.split('.');
result = result || {}; result = result || {};

View file

@ -14,12 +14,12 @@ const initBuffers = (object: any): typeof preFixtures =>
const data = result[1]; const data = result[1];
const encoding = result[2]; const encoding = result[2];
return Buffer.from(data, encoding); return Buffer.from(data, encoding as BufferEncoding);
}); });
const fixtures = initBuffers(preFixtures); const fixtures = initBuffers(preFixtures);
const upperCaseFirstLetter = (str: string) => const upperCaseFirstLetter = (str: string): string =>
str.replace(/^./, s => s.toUpperCase()); str.replace(/^./, s => s.toUpperCase());
// const b = (hex: string) => Buffer.from(hex, 'hex'); // const b = (hex: string) => Buffer.from(hex, 'hex');
@ -662,7 +662,7 @@ describe(`Psbt`, () => {
const psbt = Psbt.fromBuffer( const psbt = Psbt.fromBuffer(
Buffer.from( Buffer.from(
'70736274ff01000a01000000000000000000000000', '70736274ff01000a01000000000000000000000000',
'hex', //cHNidP8BAAoBAAAAAAAAAAAAAAAA 'hex', // cHNidP8BAAoBAAAAAAAAAAAAAAAA
), ),
); );
assert.strictEqual(psbt instanceof Psbt, true); assert.strictEqual(psbt instanceof Psbt, true);

View file

@ -12,7 +12,7 @@ describe('script', () => {
assert.strictEqual(false, bscript.isCanonicalPubKey(0 as any)); assert.strictEqual(false, bscript.isCanonicalPubKey(0 as any));
}); });
it('rejects smaller than 33', () => { it('rejects smaller than 33', () => {
for (var i = 0; i < 33; i++) { for (let i = 0; i < 33; i++) {
assert.strictEqual( assert.strictEqual(
false, false,
bscript.isCanonicalPubKey(Buffer.allocUnsafe(i)), bscript.isCanonicalPubKey(Buffer.allocUnsafe(i)),
@ -20,7 +20,9 @@ describe('script', () => {
} }
}); });
}); });
describe.skip('isCanonicalScriptSignature', () => {}); describe.skip('isCanonicalScriptSignature', () => {
assert.ok(true);
});
describe('fromASM/toASM', () => { describe('fromASM/toASM', () => {
fixtures.valid.forEach(f => { fixtures.valid.forEach(f => {
@ -157,19 +159,19 @@ describe('script', () => {
}); });
}); });
function testEncodingForSize(i: number) { function testEncodingForSize(num: number): void {
it('compliant for data PUSH of length ' + i, () => { it('compliant for data PUSH of length ' + num, () => {
const buffer = Buffer.alloc(i); const buffer = Buffer.alloc(num);
const script = bscript.compile([buffer]); const script = bscript.compile([buffer]);
assert( assert(
minimalData(script), minimalData(script),
'Failed for ' + i + ' length script: ' + script.toString('hex'), 'Failed for ' + num + ' length script: ' + script.toString('hex'),
); );
}); });
} }
for (var i = 0; i < 520; ++i) { for (let i = 0; i < 520; ++i) {
testEncodingForSize(i); testEncodingForSize(i);
} }
}); });

View file

@ -4,14 +4,19 @@ import { signature as bscriptSig } from '../src/script';
import * as fixtures from './fixtures/signature.json'; import * as fixtures from './fixtures/signature.json';
describe('Script Signatures', () => { describe('Script Signatures', () => {
function fromRaw(signature: { r: string; s: string }) { function fromRaw(signature: { r: string; s: string }): Buffer {
return Buffer.concat( return Buffer.concat(
[Buffer.from(signature.r, 'hex'), Buffer.from(signature.s, 'hex')], [Buffer.from(signature.r, 'hex'), Buffer.from(signature.s, 'hex')],
64, 64,
); );
} }
function toRaw(signature: Buffer) { function toRaw(
signature: Buffer,
): {
r: string;
s: string;
} {
return { return {
r: signature.slice(0, 32).toString('hex'), r: signature.slice(0, 32).toString('hex'),
s: signature.slice(32, 64).toString('hex'), s: signature.slice(32, 64).toString('hex'),

View file

@ -5,7 +5,7 @@ import * as bscript from '../src/script';
import * as fixtures from './fixtures/transaction.json'; import * as fixtures from './fixtures/transaction.json';
describe('Transaction', () => { describe('Transaction', () => {
function fromRaw(raw: any, noWitness?: boolean) { function fromRaw(raw: any, noWitness?: boolean): Transaction {
const tx = new Transaction(); const tx = new Transaction();
tx.version = raw.version; tx.version = raw.version;
tx.locktime = raw.locktime; tx.locktime = raw.locktime;
@ -47,7 +47,7 @@ describe('Transaction', () => {
} }
describe('fromBuffer/fromHex', () => { describe('fromBuffer/fromHex', () => {
function importExport(f: any) { function importExport(f: any): void {
const id = f.id || f.hash; const id = f.id || f.hash;
const txHex = f.hex || f.txHex; const txHex = f.hex || f.txHex;
@ -218,7 +218,7 @@ describe('Transaction', () => {
}); });
describe('getHash/getId', () => { describe('getHash/getId', () => {
function verify(f: any) { function verify(f: any): void {
it('should return the id for ' + f.id + '(' + f.description + ')', () => { it('should return the id for ' + f.id + '(' + f.description + ')', () => {
const tx = Transaction.fromHex(f.whex || f.hex); const tx = Transaction.fromHex(f.whex || f.hex);
@ -231,7 +231,7 @@ describe('Transaction', () => {
}); });
describe('isCoinbase', () => { describe('isCoinbase', () => {
function verify(f: any) { function verify(f: any): void {
it( it(
'should return ' + 'should return ' +
f.coinbase + f.coinbase +

View file

@ -1,16 +1,18 @@
import * as assert from 'assert'; import * as assert from 'assert';
import { beforeEach, describe, it } from 'mocha'; import { beforeEach, describe, it } from 'mocha';
import * as baddress from '../src/address';
import * as bscript from '../src/script';
import * as payments from '../src/payments';
import { import {
ECPair, ECPair,
networks as NETWORKS, networks as NETWORKS,
Transaction, Transaction,
TransactionBuilder, TransactionBuilder,
} from '..'; } from '..';
import * as baddress from '../src/address';
import * as payments from '../src/payments';
import * as bscript from '../src/script';
console.warn = () => {}; // Silence the Deprecation Warning console.warn = (): void => {
return;
}; // Silence the Deprecation Warning
import * as fixtures from './fixtures/transaction_builder.json'; import * as fixtures from './fixtures/transaction_builder.json';
@ -128,7 +130,7 @@ for (const useOldSignArgs of [false, true]) {
if (useOldSignArgs) { if (useOldSignArgs) {
consoleWarn = console.warn; consoleWarn = console.warn;
// Silence console.warn during these tests // Silence console.warn during these tests
console.warn = () => undefined; console.warn = (): undefined => undefined;
} }
describe(`TransactionBuilder: useOldSignArgs === ${useOldSignArgs}`, () => { describe(`TransactionBuilder: useOldSignArgs === ${useOldSignArgs}`, () => {
// constants // constants
@ -425,13 +427,13 @@ for (const useOldSignArgs of [false, true]) {
describe('sign', () => { describe('sign', () => {
it('supports the alternative abstract interface { publicKey, sign }', () => { it('supports the alternative abstract interface { publicKey, sign }', () => {
const keyPair = { const innerKeyPair = {
publicKey: ECPair.makeRandom({ publicKey: ECPair.makeRandom({
rng: () => { rng: (): Buffer => {
return Buffer.alloc(32, 1); return Buffer.alloc(32, 1);
}, },
}).publicKey, }).publicKey,
sign: () => { sign: (): Buffer => {
return Buffer.alloc(64, 0x5f); return Buffer.alloc(64, 0x5f);
}, },
}; };
@ -446,11 +448,16 @@ for (const useOldSignArgs of [false, true]) {
txb.sign({ txb.sign({
prevOutScriptType: 'p2pkh', prevOutScriptType: 'p2pkh',
vin: 0, vin: 0,
keyPair, keyPair: innerKeyPair,
}); });
assert.strictEqual( assert.strictEqual(
txb.build().toHex(), txb.build().toHex(),
'0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a47304402205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f02205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f0121031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078fffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000', '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffff' +
'ffffffff010000006a47304402205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f' +
'5f5f5f5f5f5f5f5f5f5f5f5f5f02205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f' +
'5f5f5f5f5f5f5f5f5f5f5f5f5f5f0121031b84c5567b126440995d3ed5aaba0565' +
'd71e1834604819ff9c17f5e9d5dd078fffffffff01a0860100000000001976a914' +
'000000000000000000000000000000000000000088ac00000000',
); );
}); });
@ -470,7 +477,12 @@ for (const useOldSignArgs of [false, true]) {
// high R // high R
assert.strictEqual( assert.strictEqual(
txb.build().toHex(), txb.build().toHex(),
'0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006b483045022100b872677f35c9c14ad9c41d83649fb049250f32574e0b2547d67e209ed14ff05d022059b36ad058be54e887a1a311d5c393cb4941f6b93a0b090845ec67094de8972b01210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000', '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffff' +
'ffffffff010000006b483045022100b872677f35c9c14ad9c41d83649fb049250f' +
'32574e0b2547d67e209ed14ff05d022059b36ad058be54e887a1a311d5c393cb49' +
'41f6b93a0b090845ec67094de8972b01210279be667ef9dcbbac55a06295ce870b' +
'07029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a9' +
'14000000000000000000000000000000000000000088ac00000000',
); );
txb = new TransactionBuilder(); txb = new TransactionBuilder();
@ -489,12 +501,17 @@ for (const useOldSignArgs of [false, true]) {
// low R // low R
assert.strictEqual( assert.strictEqual(
txb.build().toHex(), txb.build().toHex(),
'0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a473044022012a601efa8756ebe83e9ac7a7db061c3147e3b49d8be67685799fe51a4c8c62f02204d568d301d5ce14af390d566d4fd50e7b8ee48e71ec67786c029e721194dae3601210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000', '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffff' +
'ffffffff010000006a473044022012a601efa8756ebe83e9ac7a7db061c3147e3b' +
'49d8be67685799fe51a4c8c62f02204d568d301d5ce14af390d566d4fd50e7b8ee' +
'48e71ec67786c029e721194dae3601210279be667ef9dcbbac55a06295ce870b07' +
'029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a914' +
'000000000000000000000000000000000000000088ac00000000',
); );
}); });
it('fails when missing required arguments', () => { it('fails when missing required arguments', () => {
let txb = new TransactionBuilder(); const txb = new TransactionBuilder();
txb.setVersion(1); txb.setVersion(1);
txb.addInput( txb.addInput(
'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
@ -663,7 +680,12 @@ for (const useOldSignArgs of [false, true]) {
it('for incomplete with 0 signatures', () => { it('for incomplete with 0 signatures', () => {
const randomTxData = const randomTxData =
'0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100aa5d8aa40a90f23ce2c3d11bc845ca4a12acd99cbea37de6b9f6d86edebba8cb022022dedc2aa0a255f74d04c0b76ece2d7c691f9dd11a64a8ac49f62a99c3a05f9d01232103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac00000000'; '010000000001010001000000000000000000000000000000000000000000000000' +
'0000000000000000000000ffffffff01e8030000000000001976a9144c9c3dfac4' +
'207d5d8cb89df5722cb3d712385e3f88ac02483045022100aa5d8aa40a90f23ce2' +
'c3d11bc845ca4a12acd99cbea37de6b9f6d86edebba8cb022022dedc2aa0a255f7' +
'4d04c0b76ece2d7c691f9dd11a64a8ac49f62a99c3a05f9d01232103596d345102' +
'5c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac00000000';
const randomAddress = '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH'; const randomAddress = '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH';
const randomTx = Transaction.fromHex(randomTxData); const randomTx = Transaction.fromHex(randomTxData);
@ -676,7 +698,9 @@ for (const useOldSignArgs of [false, true]) {
it('for incomplete P2SH with 0 signatures', () => { it('for incomplete P2SH with 0 signatures', () => {
const inp = Buffer.from( const inp = Buffer.from(
'010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a80400000017a91471a8ec07ff69c6c4fee489184c462a9b1b9237488700000000', '010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be9' +
'59391c100000000000ffffffff0100c817a80400000017a91471a8ec07ff69c6c4' +
'fee489184c462a9b1b9237488700000000',
'hex', 'hex',
); // arbitrary P2SH input ); // arbitrary P2SH input
const inpTx = Transaction.fromBuffer(inp); const inpTx = Transaction.fromBuffer(inp);
@ -690,7 +714,9 @@ for (const useOldSignArgs of [false, true]) {
it('for incomplete P2WPKH with 0 signatures', () => { it('for incomplete P2WPKH with 0 signatures', () => {
const inp = Buffer.from( const inp = Buffer.from(
'010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a8040000001600141a15805e1f4040c9f68ccc887fca2e63547d794b00000000', '010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be9' +
'59391c100000000000ffffffff0100c817a8040000001600141a15805e1f4040c9' +
'f68ccc887fca2e63547d794b00000000',
'hex', 'hex',
); );
const inpTx = Transaction.fromBuffer(inp); const inpTx = Transaction.fromBuffer(inp);
@ -705,7 +731,9 @@ for (const useOldSignArgs of [false, true]) {
it('for incomplete P2WSH with 0 signatures', () => { it('for incomplete P2WSH with 0 signatures', () => {
const inpTx = Transaction.fromBuffer( const inpTx = Transaction.fromBuffer(
Buffer.from( Buffer.from(
'010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a80400000022002072df76fcc0b231b94bdf7d8c25d7eef4716597818d211e19ade7813bff7a250200000000', '010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80b' +
'e959391c100000000000ffffffff0100c817a80400000022002072df76fcc0b2' +
'31b94bdf7d8c25d7eef4716597818d211e19ade7813bff7a250200000000',
'hex', 'hex',
), ),
); );
@ -800,12 +828,14 @@ for (const useOldSignArgs of [false, true]) {
}); });
it('should classify witness inputs with witness = true during multisigning', () => { it('should classify witness inputs with witness = true during multisigning', () => {
const keyPair = ECPair.fromWIF( const innerKeyPair = ECPair.fromWIF(
'cRAwuVuVSBZMPu7hdrYvMCZ8eevzmkExjFbaBLhqnDdrezxN3nTS', 'cRAwuVuVSBZMPu7hdrYvMCZ8eevzmkExjFbaBLhqnDdrezxN3nTS',
network, network,
); );
const witnessScript = Buffer.from( const witnessScript = Buffer.from(
'522102bbbd6eb01efcbe4bd9664b886f26f69de5afcb2e479d72596c8bf21929e352e22102d9c3f7180ef13ec5267723c9c2ffab56a4215241f837502ea8977c8532b9ea1952ae', '522102bbbd6eb01efcbe4bd9664b886f26f69de5afcb2e479d72596c8bf21929e3' +
'52e22102d9c3f7180ef13ec5267723c9c2ffab56a4215241f837502ea8977c8532' +
'b9ea1952ae',
'hex', 'hex',
); );
const redeemScript = Buffer.from( const redeemScript = Buffer.from(
@ -828,7 +858,7 @@ for (const useOldSignArgs of [false, true]) {
txb.sign({ txb.sign({
prevOutScriptType: 'p2sh-p2wsh-p2ms', prevOutScriptType: 'p2sh-p2wsh-p2ms',
vin: 0, vin: 0,
keyPair, keyPair: innerKeyPair,
redeemScript, redeemScript,
witnessValue: 100000, witnessValue: 100000,
witnessScript, witnessScript,
@ -850,10 +880,24 @@ for (const useOldSignArgs of [false, true]) {
it('should handle badly pre-filled OP_0s', () => { it('should handle badly pre-filled OP_0s', () => {
// OP_0 is used where a signature is missing // OP_0 is used where a signature is missing
const redeemScripSig = bscript.fromASM( const redeemScripSig = bscript.fromASM(
'OP_0 OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae', 'OP_0 OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17' +
'be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621eb' +
'd9691d6b48c0d4283d7d01 52410479be667ef9dcbbac55a06295ce870b07029bf' +
'cdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b44' +
'8a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778' +
'e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f6326' +
'53266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c' +
'845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99' +
'934c2231b6cb9fd7584b8e67253ae',
); );
const redeemScript = bscript.fromASM( const redeemScript = bscript.fromASM(
'OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG', 'OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f' +
'81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d' +
'4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c70' +
'9ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe5' +
'2a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce03' +
'6f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67' +
'2 OP_3 OP_CHECKMULTISIG',
); );
const tx = new Transaction(); const tx = new Transaction();
@ -895,7 +939,17 @@ for (const useOldSignArgs of [false, true]) {
); );
assert.strictEqual( assert.strictEqual(
bscript.toASM(tx2.ins[0].script), bscript.toASM(tx2.ins[0].script),
'OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae', 'OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b' +
'63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691' +
'd6b48c0d4283d7d01 3045022100a346c61738304eac5e7702188764d19cdf68f4' +
'466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881' +
'd7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870' +
'b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a' +
'8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07' +
'cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceae' +
'ef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5' +
'229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f35' +
'66500a99934c2231b6cb9fd7584b8e67253ae',
); );
}); });
@ -908,7 +962,7 @@ for (const useOldSignArgs of [false, true]) {
); );
const incomplete = txb.buildIncomplete().toHex(); const incomplete = txb.buildIncomplete().toHex();
const keyPair = ECPair.fromWIF( const innerKeyPair = ECPair.fromWIF(
'L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy', 'L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy',
); );
@ -917,7 +971,7 @@ for (const useOldSignArgs of [false, true]) {
txb.sign({ txb.sign({
prevOutScriptType: 'p2pkh', prevOutScriptType: 'p2pkh',
vin: 0, vin: 0,
keyPair, keyPair: innerKeyPair,
}); });
const txId = txb.build().getId(); const txId = txb.build().getId();
assert.strictEqual( assert.strictEqual(
@ -933,7 +987,7 @@ for (const useOldSignArgs of [false, true]) {
txb.sign({ txb.sign({
prevOutScriptType: 'p2pkh', prevOutScriptType: 'p2pkh',
vin: 0, vin: 0,
keyPair, keyPair: innerKeyPair,
}); });
const txId2 = txb.build().getId(); const txId2 = txb.build().getId();
assert.strictEqual(txId, txId2); assert.strictEqual(txId, txId2);

View file

@ -21,7 +21,7 @@
"no-var-requires": false, "no-var-requires": false,
"no-unused-expression": false, "no-unused-expression": false,
"object-literal-sort-keys": false, "object-literal-sort-keys": false,
"quotemark": [true, "single"], "quotemark": [true, "single", "avoid-escape"],
"typedef": [ "typedef": [
true, true,
"call-signature", "call-signature",

1
types/address.d.ts vendored
View file

@ -1,4 +1,3 @@
/// <reference types="node" />
import { Network } from './networks'; import { Network } from './networks';
export interface Base58CheckResult { export interface Base58CheckResult {
hash: Buffer; hash: Buffer;

1
types/block.d.ts vendored
View file

@ -1,4 +1,3 @@
/// <reference types="node" />
import { Transaction } from './transaction'; import { Transaction } from './transaction';
export declare class Block { export declare class Block {
static fromBuffer(buffer: Buffer): Block; static fromBuffer(buffer: Buffer): Block;

View file

@ -1,4 +1,3 @@
/// <reference types="node" />
export declare function readUInt64LE(buffer: Buffer, offset: number): number; export declare function readUInt64LE(buffer: Buffer, offset: number): number;
export declare function writeUInt64LE(buffer: Buffer, value: number, offset: number): number; export declare function writeUInt64LE(buffer: Buffer, value: number, offset: number): number;
export declare function reverseBuffer(buffer: Buffer): Buffer; export declare function reverseBuffer(buffer: Buffer): Buffer;

1
types/classify.d.ts vendored
View file

@ -1,4 +1,3 @@
/// <reference types="node" />
declare const types: { declare const types: {
P2MS: string; P2MS: string;
NONSTANDARD: string; NONSTANDARD: string;

1
types/crypto.d.ts vendored
View file

@ -1,4 +1,3 @@
/// <reference types="node" />
export declare function ripemd160(buffer: Buffer): Buffer; export declare function ripemd160(buffer: Buffer): Buffer;
export declare function sha1(buffer: Buffer): Buffer; export declare function sha1(buffer: Buffer): Buffer;
export declare function sha256(buffer: Buffer): Buffer; export declare function sha256(buffer: Buffer): Buffer;

1
types/ecpair.d.ts vendored
View file

@ -1,4 +1,3 @@
/// <reference types="node" />
import { Network } from './networks'; import { Network } from './networks';
interface ECPairOptions { interface ECPairOptions {
compressed?: boolean; compressed?: boolean;

View file

@ -1,4 +1,3 @@
/// <reference types="node" />
import { Network } from '../networks'; import { Network } from '../networks';
import { p2data as embed } from './embed'; import { p2data as embed } from './embed';
import { p2ms } from './p2ms'; import { p2ms } from './p2ms';

1
types/psbt.d.ts vendored
View file

@ -1,4 +1,3 @@
/// <reference types="node" />
import { Psbt as PsbtBase } from 'bip174'; import { Psbt as PsbtBase } from 'bip174';
import { KeyValue, PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, PsbtOutput, PsbtOutputUpdate, TransactionInput } from 'bip174/src/lib/interfaces'; import { KeyValue, PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, PsbtOutput, PsbtOutputUpdate, TransactionInput } from 'bip174/src/lib/interfaces';
import { Signer, SignerAsync } from './ecpair'; import { Signer, SignerAsync } from './ecpair';

1
types/script.d.ts vendored
View file

@ -1,4 +1,3 @@
/// <reference types="node" />
import { Stack } from './payments'; import { Stack } from './payments';
import * as scriptNumber from './script_number'; import * as scriptNumber from './script_number';
import * as scriptSignature from './script_signature'; import * as scriptSignature from './script_signature';

View file

@ -1,3 +1,2 @@
/// <reference types="node" />
export declare function decode(buffer: Buffer, maxLength?: number, minimal?: boolean): number; export declare function decode(buffer: Buffer, maxLength?: number, minimal?: boolean): number;
export declare function encode(_number: number): Buffer; export declare function encode(_number: number): Buffer;

View file

@ -1,4 +1,3 @@
/// <reference types="node" />
interface ScriptSignature { interface ScriptSignature {
signature: Buffer; signature: Buffer;
hashType: number; hashType: number;

View file

@ -1,4 +1,3 @@
/// <reference types="node" />
import { Stack } from '../../payments'; import { Stack } from '../../payments';
export declare function check(script: Buffer | Stack, allowIncomplete?: boolean): boolean; export declare function check(script: Buffer | Stack, allowIncomplete?: boolean): boolean;
export declare namespace check { export declare namespace check {

View file

@ -1,4 +1,3 @@
/// <reference types="node" />
import { Stack } from '../../payments'; import { Stack } from '../../payments';
export declare function check(script: Buffer | Stack, allowIncomplete?: boolean): boolean; export declare function check(script: Buffer | Stack, allowIncomplete?: boolean): boolean;
export declare namespace check { export declare namespace check {

View file

@ -1,4 +1,3 @@
/// <reference types="node" />
export declare function check(script: Buffer | Array<number | Buffer>): boolean; export declare function check(script: Buffer | Array<number | Buffer>): boolean;
export declare namespace check { export declare namespace check {
var toJSON: () => string; var toJSON: () => string;

View file

@ -1,4 +1,3 @@
/// <reference types="node" />
import { Stack } from '../../payments'; import { Stack } from '../../payments';
export declare function check(script: Buffer | Stack): boolean; export declare function check(script: Buffer | Stack): boolean;
export declare namespace check { export declare namespace check {

View file

@ -1,4 +1,3 @@
/// <reference types="node" />
import { Stack } from '../../payments'; import { Stack } from '../../payments';
export declare function check(script: Buffer | Stack): boolean; export declare function check(script: Buffer | Stack): boolean;
export declare namespace check { export declare namespace check {

View file

@ -1,4 +1,3 @@
/// <reference types="node" />
import { Stack } from '../../payments'; import { Stack } from '../../payments';
export declare function check(script: Buffer | Stack): boolean; export declare function check(script: Buffer | Stack): boolean;
export declare namespace check { export declare namespace check {

View file

@ -1,4 +1,3 @@
/// <reference types="node" />
export declare function check(script: Buffer | Array<number | Buffer>): boolean; export declare function check(script: Buffer | Array<number | Buffer>): boolean;
export declare namespace check { export declare namespace check {
var toJSON: () => string; var toJSON: () => string;

View file

@ -1,4 +1,3 @@
/// <reference types="node" />
export declare function check(script: Buffer | Array<number | Buffer>, allowIncomplete?: boolean): boolean; export declare function check(script: Buffer | Array<number | Buffer>, allowIncomplete?: boolean): boolean;
export declare namespace check { export declare namespace check {
var toJSON: () => string; var toJSON: () => string;

View file

@ -1,4 +1,3 @@
/// <reference types="node" />
export declare function check(script: Buffer | Array<number | Buffer>): boolean; export declare function check(script: Buffer | Array<number | Buffer>): boolean;
export declare namespace check { export declare namespace check {
var toJSON: () => string; var toJSON: () => string;

View file

@ -1,4 +1,3 @@
/// <reference types="node" />
export declare function check(script: Buffer | Array<number | Buffer>): boolean; export declare function check(script: Buffer | Array<number | Buffer>): boolean;
export declare namespace check { export declare namespace check {
var toJSON: () => string; var toJSON: () => string;

View file

@ -1,4 +1,3 @@
/// <reference types="node" />
import { Stack } from '../../payments'; import { Stack } from '../../payments';
export declare function check(script: Buffer | Stack): boolean; export declare function check(script: Buffer | Stack): boolean;
export declare namespace check { export declare namespace check {

View file

@ -1,4 +1,3 @@
/// <reference types="node" />
export declare function check(script: Buffer | Array<number | Buffer>): boolean; export declare function check(script: Buffer | Array<number | Buffer>): boolean;
export declare namespace check { export declare namespace check {
var toJSON: () => string; var toJSON: () => string;

View file

@ -1,4 +1,3 @@
/// <reference types="node" />
export declare function check(chunks: Buffer[], allowIncomplete?: boolean): boolean; export declare function check(chunks: Buffer[], allowIncomplete?: boolean): boolean;
export declare namespace check { export declare namespace check {
var toJSON: () => string; var toJSON: () => string;

View file

@ -1,4 +1,3 @@
/// <reference types="node" />
export declare function check(script: Buffer | Array<number | Buffer>): boolean; export declare function check(script: Buffer | Array<number | Buffer>): boolean;
export declare namespace check { export declare namespace check {
var toJSON: () => string; var toJSON: () => string;

View file

@ -1,4 +1,3 @@
/// <reference types="node" />
export interface BlankOutput { export interface BlankOutput {
script: Buffer; script: Buffer;
valueBuffer: Buffer; valueBuffer: Buffer;

View file

@ -1,4 +1,3 @@
/// <reference types="node" />
import { Signer } from './ecpair'; import { Signer } from './ecpair';
import { Network } from './networks'; import { Network } from './networks';
import { Transaction } from './transaction'; import { Transaction } from './transaction';