Add tslint to tests
This commit is contained in:
parent
34b0b525fc
commit
9810049f4c
21 changed files with 749 additions and 578 deletions
|
@ -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:
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -6,23 +6,27 @@ 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(
|
||||||
const keyPair = bitcoin.ECPair.makeRandom();
|
'can generate a random address [and support the retrieval of ' +
|
||||||
const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey });
|
'transactions for that address (via 3PBP)]',
|
||||||
|
async () => {
|
||||||
|
const keyPair = bitcoin.ECPair.makeRandom();
|
||||||
|
const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey });
|
||||||
|
|
||||||
// bitcoin P2PKH addresses start with a '1'
|
// bitcoin P2PKH addresses start with a '1'
|
||||||
assert.strictEqual(address!.startsWith('1'), true);
|
assert.strictEqual(address!.startsWith('1'), true);
|
||||||
|
|
||||||
const result = await dhttp({
|
const result = await dhttp({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://blockchain.info/rawaddr/' + address,
|
url: 'https://blockchain.info/rawaddr/' + address,
|
||||||
});
|
});
|
||||||
|
|
||||||
// random private keys [probably!] have no transactions
|
// random private keys [probably!] have no transactions
|
||||||
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(
|
||||||
|
|
|
@ -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',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -30,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
|
||||||
|
@ -50,173 +56,201 @@ 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(
|
||||||
// 3 hours ago
|
'can create (and broadcast via 3PBP) a Transaction where Alice can redeem ' +
|
||||||
const lockTime = bip65.encode({ utc: utcNow() - 3600 * 3 });
|
'the output after the expiry (in the past)',
|
||||||
const redeemScript = cltvCheckSigOutput(alice, bob, lockTime);
|
async () => {
|
||||||
const { address } = bitcoin.payments.p2sh({
|
// 3 hours ago
|
||||||
redeem: { output: redeemScript, network: regtest },
|
const lockTime = bip65.encode({ utc: utcNow() - 3600 * 3 });
|
||||||
network: regtest,
|
const redeemScript = cltvCheckSigOutput(alice, bob, lockTime);
|
||||||
});
|
const { address } = bitcoin.payments.p2sh({
|
||||||
|
redeem: { output: redeemScript, network: regtest },
|
||||||
|
network: regtest,
|
||||||
|
});
|
||||||
|
|
||||||
// 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 tx = new bitcoin.Transaction();
|
const tx = new bitcoin.Transaction();
|
||||||
tx.locktime = 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.
|
||||||
tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe);
|
tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe);
|
||||||
tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4);
|
tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4);
|
||||||
|
|
||||||
// {Alice's signature} OP_TRUE
|
// {Alice's signature} OP_TRUE
|
||||||
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(
|
||||||
bitcoin.opcodes.OP_TRUE,
|
alice.sign(signatureHash),
|
||||||
]),
|
hashType,
|
||||||
output: redeemScript,
|
),
|
||||||
},
|
bitcoin.opcodes.OP_TRUE,
|
||||||
}).input;
|
]),
|
||||||
tx.setInputScript(0, redeemScriptSig!);
|
output: redeemScript,
|
||||||
|
},
|
||||||
|
}).input;
|
||||||
|
tx.setInputScript(0, redeemScriptSig!);
|
||||||
|
|
||||||
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,
|
||||||
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(
|
||||||
const height = await regtestUtils.height();
|
'can create (and broadcast via 3PBP) a Transaction where Alice can redeem ' +
|
||||||
// 5 blocks from now
|
'the output after the expiry (in the future)',
|
||||||
const lockTime = bip65.encode({ blocks: height + 5 });
|
async () => {
|
||||||
const redeemScript = cltvCheckSigOutput(alice, bob, lockTime);
|
const height = await regtestUtils.height();
|
||||||
const { address } = bitcoin.payments.p2sh({
|
// 5 blocks from now
|
||||||
redeem: { output: redeemScript, network: regtest },
|
const lockTime = bip65.encode({ blocks: height + 5 });
|
||||||
network: regtest,
|
const redeemScript = cltvCheckSigOutput(alice, bob, lockTime);
|
||||||
});
|
const { address } = bitcoin.payments.p2sh({
|
||||||
|
redeem: { output: redeemScript, network: regtest },
|
||||||
|
network: regtest,
|
||||||
|
});
|
||||||
|
|
||||||
// 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 tx = new bitcoin.Transaction();
|
const tx = new bitcoin.Transaction();
|
||||||
tx.locktime = 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.
|
||||||
tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe);
|
tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe);
|
||||||
tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4);
|
tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4);
|
||||||
|
|
||||||
// {Alice's signature} OP_TRUE
|
// {Alice's signature} OP_TRUE
|
||||||
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(
|
||||||
bitcoin.opcodes.OP_TRUE,
|
alice.sign(signatureHash),
|
||||||
]),
|
hashType,
|
||||||
output: redeemScript,
|
),
|
||||||
},
|
bitcoin.opcodes.OP_TRUE,
|
||||||
}).input;
|
]),
|
||||||
tx.setInputScript(0, redeemScriptSig!);
|
output: redeemScript,
|
||||||
|
},
|
||||||
|
}).input;
|
||||||
|
tx.setInputScript(0, redeemScriptSig!);
|
||||||
|
|
||||||
// TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently
|
// TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently
|
||||||
// ...
|
// ...
|
||||||
// into the future!
|
// into the future!
|
||||||
await regtestUtils.mine(5);
|
await regtestUtils.mine(5);
|
||||||
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,
|
||||||
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(
|
||||||
// two hours ago
|
'can create (and broadcast via 3PBP) a Transaction where Alice and Bob can ' +
|
||||||
const lockTime = bip65.encode({ utc: utcNow() - 3600 * 2 });
|
'redeem the output at any time',
|
||||||
const redeemScript = cltvCheckSigOutput(alice, bob, lockTime);
|
async () => {
|
||||||
const { address } = bitcoin.payments.p2sh({
|
// two hours ago
|
||||||
redeem: { output: redeemScript, network: regtest },
|
const lockTime = bip65.encode({ utc: utcNow() - 3600 * 2 });
|
||||||
network: regtest,
|
const redeemScript = cltvCheckSigOutput(alice, bob, lockTime);
|
||||||
});
|
const { address } = bitcoin.payments.p2sh({
|
||||||
|
redeem: { output: redeemScript, network: regtest },
|
||||||
|
network: regtest,
|
||||||
|
});
|
||||||
|
|
||||||
// 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 tx = new bitcoin.Transaction();
|
const tx = new bitcoin.Transaction();
|
||||||
tx.locktime = 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.
|
||||||
tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe);
|
tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe);
|
||||||
tx.addOutput(toOutputScript(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 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(
|
||||||
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
|
alice.sign(signatureHash),
|
||||||
bitcoin.opcodes.OP_FALSE,
|
hashType,
|
||||||
]),
|
),
|
||||||
output: redeemScript,
|
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
|
||||||
},
|
bitcoin.opcodes.OP_FALSE,
|
||||||
}).input;
|
]),
|
||||||
tx.setInputScript(0, redeemScriptSig!);
|
output: redeemScript,
|
||||||
|
},
|
||||||
|
}).input;
|
||||||
|
tx.setInputScript(0, redeemScriptSig!);
|
||||||
|
|
||||||
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,
|
||||||
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(
|
||||||
// two hours from now
|
'can create (but fail to broadcast via 3PBP) a Transaction where Alice ' +
|
||||||
const lockTime = bip65.encode({ utc: utcNow() + 3600 * 2 });
|
'attempts to redeem before the expiry',
|
||||||
const redeemScript = cltvCheckSigOutput(alice, bob, lockTime);
|
async () => {
|
||||||
const { address } = bitcoin.payments.p2sh({
|
// two hours from now
|
||||||
redeem: { output: redeemScript, network: regtest },
|
const lockTime = bip65.encode({ utc: utcNow() + 3600 * 2 });
|
||||||
network: regtest,
|
const redeemScript = cltvCheckSigOutput(alice, bob, lockTime);
|
||||||
});
|
const { address } = bitcoin.payments.p2sh({
|
||||||
|
redeem: { output: redeemScript, network: regtest },
|
||||||
|
network: regtest,
|
||||||
|
});
|
||||||
|
|
||||||
// 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 tx = new bitcoin.Transaction();
|
const tx = new bitcoin.Transaction();
|
||||||
tx.locktime = 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.
|
||||||
tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe);
|
tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe);
|
||||||
tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 1e4);
|
tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 1e4);
|
||||||
|
|
||||||
// {Alice's signature} OP_TRUE
|
// {Alice's signature} OP_TRUE
|
||||||
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(
|
||||||
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
|
alice.sign(signatureHash),
|
||||||
bitcoin.opcodes.OP_TRUE,
|
hashType,
|
||||||
]),
|
),
|
||||||
output: redeemScript,
|
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
|
||||||
},
|
bitcoin.opcodes.OP_TRUE,
|
||||||
}).input;
|
]),
|
||||||
tx.setInputScript(0, redeemScriptSig!);
|
output: redeemScript,
|
||||||
|
},
|
||||||
|
}).input;
|
||||||
|
tx.setInputScript(0, redeemScriptSig!);
|
||||||
|
|
||||||
await regtestUtils.broadcast(tx.toHex()).catch(err => {
|
await regtestUtils.broadcast(tx.toHex()).catch(err => {
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
}, /Error: non-final \(code 64\)/);
|
}, /Error: non-final \(code 64\)/);
|
||||||
});
|
});
|
||||||
});
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -38,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
|
||||||
|
@ -62,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
|
||||||
|
@ -105,287 +114,319 @@ 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(
|
||||||
// 5 blocks from now
|
'can create (and broadcast via 3PBP) a Transaction where Alice can redeem ' +
|
||||||
const sequence = bip68.encode({ blocks: 5 });
|
'the output after the expiry (in the future) (simple CHECKSEQUENCEVERIFY)',
|
||||||
const p2sh = bitcoin.payments.p2sh({
|
async () => {
|
||||||
redeem: {
|
// 5 blocks from now
|
||||||
output: csvCheckSigOutput(alice, bob, sequence),
|
const sequence = bip68.encode({ blocks: 5 });
|
||||||
},
|
const p2sh = bitcoin.payments.p2sh({
|
||||||
network: regtest,
|
redeem: {
|
||||||
});
|
output: csvCheckSigOutput(alice, bob, sequence),
|
||||||
|
},
|
||||||
// fund the P2SH(CSV) address
|
|
||||||
const unspent = await regtestUtils.faucet(p2sh.address!, 1e5);
|
|
||||||
|
|
||||||
const tx = new bitcoin.Transaction();
|
|
||||||
tx.version = 2;
|
|
||||||
tx.addInput(idToHash(unspent.txId), unspent.vout, sequence);
|
|
||||||
tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4);
|
|
||||||
|
|
||||||
// {Alice's signature} OP_TRUE
|
|
||||||
const signatureHash = tx.hashForSignature(
|
|
||||||
0,
|
|
||||||
p2sh.redeem!.output!,
|
|
||||||
hashType,
|
|
||||||
);
|
|
||||||
const redeemScriptSig = bitcoin.payments.p2sh({
|
|
||||||
network: regtest,
|
|
||||||
redeem: {
|
|
||||||
network: regtest,
|
network: regtest,
|
||||||
output: p2sh.redeem!.output,
|
});
|
||||||
input: bitcoin.script.compile([
|
|
||||||
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
|
|
||||||
bitcoin.opcodes.OP_TRUE,
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
}).input;
|
|
||||||
tx.setInputScript(0, redeemScriptSig!);
|
|
||||||
|
|
||||||
// TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently
|
// fund the P2SH(CSV) address
|
||||||
// ...
|
const unspent = await regtestUtils.faucet(p2sh.address!, 1e5);
|
||||||
// into the future!
|
|
||||||
await regtestUtils.mine(10);
|
|
||||||
|
|
||||||
await regtestUtils.broadcast(tx.toHex());
|
const tx = new bitcoin.Transaction();
|
||||||
|
tx.version = 2;
|
||||||
|
tx.addInput(idToHash(unspent.txId), unspent.vout, sequence);
|
||||||
|
tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4);
|
||||||
|
|
||||||
await regtestUtils.verify({
|
// {Alice's signature} OP_TRUE
|
||||||
txId: tx.getId(),
|
const signatureHash = tx.hashForSignature(
|
||||||
address: regtestUtils.RANDOM_ADDRESS,
|
0,
|
||||||
vout: 0,
|
p2sh.redeem!.output!,
|
||||||
value: 7e4,
|
hashType,
|
||||||
});
|
);
|
||||||
});
|
const redeemScriptSig = bitcoin.payments.p2sh({
|
||||||
|
network: regtest,
|
||||||
|
redeem: {
|
||||||
|
network: regtest,
|
||||||
|
output: p2sh.redeem!.output,
|
||||||
|
input: bitcoin.script.compile([
|
||||||
|
bitcoin.script.signature.encode(
|
||||||
|
alice.sign(signatureHash),
|
||||||
|
hashType,
|
||||||
|
),
|
||||||
|
bitcoin.opcodes.OP_TRUE,
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
}).input;
|
||||||
|
tx.setInputScript(0, redeemScriptSig!);
|
||||||
|
|
||||||
|
// TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently
|
||||||
|
// ...
|
||||||
|
// into the future!
|
||||||
|
await regtestUtils.mine(10);
|
||||||
|
|
||||||
|
await regtestUtils.broadcast(tx.toHex());
|
||||||
|
|
||||||
|
await regtestUtils.verify({
|
||||||
|
txId: tx.getId(),
|
||||||
|
address: regtestUtils.RANDOM_ADDRESS,
|
||||||
|
vout: 0,
|
||||||
|
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(
|
||||||
// two hours after confirmation
|
'can create (but fail to broadcast via 3PBP) a Transaction where Alice ' +
|
||||||
const sequence = bip68.encode({ seconds: 7168 });
|
'attempts to redeem before the expiry (simple CHECKSEQUENCEVERIFY)',
|
||||||
const p2sh = bitcoin.payments.p2sh({
|
async () => {
|
||||||
network: regtest,
|
// two hours after confirmation
|
||||||
redeem: {
|
const sequence = bip68.encode({ seconds: 7168 });
|
||||||
output: csvCheckSigOutput(alice, bob, sequence),
|
const p2sh = bitcoin.payments.p2sh({
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// fund the P2SH(CSV) address
|
|
||||||
const unspent = await regtestUtils.faucet(p2sh.address!, 2e4);
|
|
||||||
|
|
||||||
const tx = new bitcoin.Transaction();
|
|
||||||
tx.version = 2;
|
|
||||||
tx.addInput(idToHash(unspent.txId), unspent.vout, sequence);
|
|
||||||
tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 1e4);
|
|
||||||
|
|
||||||
// {Alice's signature} OP_TRUE
|
|
||||||
const signatureHash = tx.hashForSignature(
|
|
||||||
0,
|
|
||||||
p2sh.redeem!.output!,
|
|
||||||
hashType,
|
|
||||||
);
|
|
||||||
const redeemScriptSig = bitcoin.payments.p2sh({
|
|
||||||
network: regtest,
|
|
||||||
redeem: {
|
|
||||||
network: regtest,
|
network: regtest,
|
||||||
output: p2sh.redeem!.output,
|
redeem: {
|
||||||
input: bitcoin.script.compile([
|
output: csvCheckSigOutput(alice, bob, sequence),
|
||||||
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
|
},
|
||||||
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
|
});
|
||||||
bitcoin.opcodes.OP_TRUE,
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
}).input;
|
|
||||||
tx.setInputScript(0, redeemScriptSig!);
|
|
||||||
|
|
||||||
await regtestUtils.broadcast(tx.toHex()).catch(err => {
|
// fund the P2SH(CSV) address
|
||||||
assert.throws(() => {
|
const unspent = await regtestUtils.faucet(p2sh.address!, 2e4);
|
||||||
if (err) throw err;
|
|
||||||
}, /Error: non-BIP68-final \(code 64\)/);
|
const tx = new bitcoin.Transaction();
|
||||||
});
|
tx.version = 2;
|
||||||
});
|
tx.addInput(idToHash(unspent.txId), unspent.vout, sequence);
|
||||||
|
tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 1e4);
|
||||||
|
|
||||||
|
// {Alice's signature} OP_TRUE
|
||||||
|
const signatureHash = tx.hashForSignature(
|
||||||
|
0,
|
||||||
|
p2sh.redeem!.output!,
|
||||||
|
hashType,
|
||||||
|
);
|
||||||
|
const redeemScriptSig = bitcoin.payments.p2sh({
|
||||||
|
network: regtest,
|
||||||
|
redeem: {
|
||||||
|
network: regtest,
|
||||||
|
output: p2sh.redeem!.output,
|
||||||
|
input: bitcoin.script.compile([
|
||||||
|
bitcoin.script.signature.encode(
|
||||||
|
alice.sign(signatureHash),
|
||||||
|
hashType,
|
||||||
|
),
|
||||||
|
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
|
||||||
|
bitcoin.opcodes.OP_TRUE,
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
}).input;
|
||||||
|
tx.setInputScript(0, redeemScriptSig!);
|
||||||
|
|
||||||
|
await regtestUtils.broadcast(tx.toHex()).catch(err => {
|
||||||
|
assert.throws(() => {
|
||||||
|
if (err) throw err;
|
||||||
|
}, /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(
|
||||||
// 2 blocks from now
|
'can create (and broadcast via 3PBP) a Transaction where Bob and Charles ' +
|
||||||
const sequence1 = bip68.encode({ blocks: 2 });
|
'can send (complex CHECKSEQUENCEVERIFY)',
|
||||||
// 5 blocks from now
|
async () => {
|
||||||
const sequence2 = bip68.encode({ blocks: 5 });
|
// 2 blocks from now
|
||||||
const p2sh = bitcoin.payments.p2sh({
|
const sequence1 = bip68.encode({ blocks: 2 });
|
||||||
redeem: {
|
// 5 blocks from now
|
||||||
output: complexCsvOutput(
|
const sequence2 = bip68.encode({ blocks: 5 });
|
||||||
alice,
|
const p2sh = bitcoin.payments.p2sh({
|
||||||
bob,
|
redeem: {
|
||||||
charles,
|
output: complexCsvOutput(
|
||||||
dave,
|
alice,
|
||||||
sequence1,
|
bob,
|
||||||
sequence2,
|
charles,
|
||||||
),
|
dave,
|
||||||
},
|
sequence1,
|
||||||
network: regtest,
|
sequence2,
|
||||||
});
|
|
||||||
|
|
||||||
// fund the P2SH(CCSV) address
|
|
||||||
const unspent = await regtestUtils.faucet(p2sh.address!, 1e5);
|
|
||||||
|
|
||||||
const tx = new bitcoin.Transaction();
|
|
||||||
tx.version = 2;
|
|
||||||
tx.addInput(idToHash(unspent.txId), unspent.vout);
|
|
||||||
tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4);
|
|
||||||
|
|
||||||
// OP_0 {Bob sig} {Charles sig} OP_TRUE OP_TRUE
|
|
||||||
const signatureHash = tx.hashForSignature(
|
|
||||||
0,
|
|
||||||
p2sh.redeem!.output!,
|
|
||||||
hashType,
|
|
||||||
);
|
|
||||||
const redeemScriptSig = bitcoin.payments.p2sh({
|
|
||||||
network: regtest,
|
|
||||||
redeem: {
|
|
||||||
network: regtest,
|
|
||||||
output: p2sh.redeem!.output,
|
|
||||||
input: bitcoin.script.compile([
|
|
||||||
bitcoin.opcodes.OP_0,
|
|
||||||
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
|
|
||||||
bitcoin.script.signature.encode(
|
|
||||||
charles.sign(signatureHash),
|
|
||||||
hashType,
|
|
||||||
),
|
),
|
||||||
bitcoin.opcodes.OP_TRUE,
|
},
|
||||||
bitcoin.opcodes.OP_TRUE,
|
network: regtest,
|
||||||
]),
|
});
|
||||||
},
|
|
||||||
}).input;
|
|
||||||
tx.setInputScript(0, redeemScriptSig!);
|
|
||||||
|
|
||||||
await regtestUtils.broadcast(tx.toHex());
|
// fund the P2SH(CCSV) address
|
||||||
|
const unspent = await regtestUtils.faucet(p2sh.address!, 1e5);
|
||||||
|
|
||||||
await regtestUtils.verify({
|
const tx = new bitcoin.Transaction();
|
||||||
txId: tx.getId(),
|
tx.version = 2;
|
||||||
address: regtestUtils.RANDOM_ADDRESS,
|
tx.addInput(idToHash(unspent.txId), unspent.vout);
|
||||||
vout: 0,
|
tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4);
|
||||||
value: 7e4,
|
|
||||||
});
|
// OP_0 {Bob sig} {Charles sig} OP_TRUE OP_TRUE
|
||||||
});
|
const signatureHash = tx.hashForSignature(
|
||||||
|
0,
|
||||||
|
p2sh.redeem!.output!,
|
||||||
|
hashType,
|
||||||
|
);
|
||||||
|
const redeemScriptSig = bitcoin.payments.p2sh({
|
||||||
|
network: regtest,
|
||||||
|
redeem: {
|
||||||
|
network: regtest,
|
||||||
|
output: p2sh.redeem!.output,
|
||||||
|
input: bitcoin.script.compile([
|
||||||
|
bitcoin.opcodes.OP_0,
|
||||||
|
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
|
||||||
|
bitcoin.script.signature.encode(
|
||||||
|
charles.sign(signatureHash),
|
||||||
|
hashType,
|
||||||
|
),
|
||||||
|
bitcoin.opcodes.OP_TRUE,
|
||||||
|
bitcoin.opcodes.OP_TRUE,
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
}).input;
|
||||||
|
tx.setInputScript(0, redeemScriptSig!);
|
||||||
|
|
||||||
|
await regtestUtils.broadcast(tx.toHex());
|
||||||
|
|
||||||
|
await regtestUtils.verify({
|
||||||
|
txId: tx.getId(),
|
||||||
|
address: regtestUtils.RANDOM_ADDRESS,
|
||||||
|
vout: 0,
|
||||||
|
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(
|
||||||
// 2 blocks from now
|
'can create (and broadcast via 3PBP) a Transaction where Alice (mediator) ' +
|
||||||
const sequence1 = bip68.encode({ blocks: 2 });
|
'and Bob can send after 2 blocks (complex CHECKSEQUENCEVERIFY)',
|
||||||
// 5 blocks from now
|
async () => {
|
||||||
const sequence2 = bip68.encode({ blocks: 5 });
|
// 2 blocks from now
|
||||||
const p2sh = bitcoin.payments.p2sh({
|
const sequence1 = bip68.encode({ blocks: 2 });
|
||||||
redeem: {
|
// 5 blocks from now
|
||||||
output: complexCsvOutput(
|
const sequence2 = bip68.encode({ blocks: 5 });
|
||||||
alice,
|
const p2sh = bitcoin.payments.p2sh({
|
||||||
bob,
|
redeem: {
|
||||||
charles,
|
output: complexCsvOutput(
|
||||||
dave,
|
alice,
|
||||||
sequence1,
|
bob,
|
||||||
sequence2,
|
charles,
|
||||||
),
|
dave,
|
||||||
},
|
sequence1,
|
||||||
network: regtest,
|
sequence2,
|
||||||
});
|
),
|
||||||
|
},
|
||||||
// fund the P2SH(CCSV) address
|
|
||||||
const unspent = await regtestUtils.faucet(p2sh.address!, 1e5);
|
|
||||||
|
|
||||||
const tx = new bitcoin.Transaction();
|
|
||||||
tx.version = 2;
|
|
||||||
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
|
|
||||||
const signatureHash = tx.hashForSignature(
|
|
||||||
0,
|
|
||||||
p2sh.redeem!.output!,
|
|
||||||
hashType,
|
|
||||||
);
|
|
||||||
const redeemScriptSig = bitcoin.payments.p2sh({
|
|
||||||
network: regtest,
|
|
||||||
redeem: {
|
|
||||||
network: regtest,
|
network: regtest,
|
||||||
output: p2sh.redeem!.output,
|
});
|
||||||
input: bitcoin.script.compile([
|
|
||||||
bitcoin.opcodes.OP_0,
|
|
||||||
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
|
|
||||||
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
|
|
||||||
bitcoin.opcodes.OP_0,
|
|
||||||
bitcoin.opcodes.OP_TRUE,
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
}).input;
|
|
||||||
tx.setInputScript(0, redeemScriptSig!);
|
|
||||||
|
|
||||||
// Wait 2 blocks
|
// fund the P2SH(CCSV) address
|
||||||
await regtestUtils.mine(2);
|
const unspent = await regtestUtils.faucet(p2sh.address!, 1e5);
|
||||||
|
|
||||||
await regtestUtils.broadcast(tx.toHex());
|
const tx = new bitcoin.Transaction();
|
||||||
|
tx.version = 2;
|
||||||
|
tx.addInput(idToHash(unspent.txId), unspent.vout, sequence1); // Set sequence1 for input
|
||||||
|
tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4);
|
||||||
|
|
||||||
await regtestUtils.verify({
|
// OP_0 {Bob sig} {Alice mediator sig} OP_FALSE OP_TRUE
|
||||||
txId: tx.getId(),
|
const signatureHash = tx.hashForSignature(
|
||||||
address: regtestUtils.RANDOM_ADDRESS,
|
0,
|
||||||
vout: 0,
|
p2sh.redeem!.output!,
|
||||||
value: 7e4,
|
hashType,
|
||||||
});
|
);
|
||||||
});
|
const redeemScriptSig = bitcoin.payments.p2sh({
|
||||||
|
network: regtest,
|
||||||
|
redeem: {
|
||||||
|
network: regtest,
|
||||||
|
output: p2sh.redeem!.output,
|
||||||
|
input: bitcoin.script.compile([
|
||||||
|
bitcoin.opcodes.OP_0,
|
||||||
|
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
|
||||||
|
bitcoin.script.signature.encode(
|
||||||
|
alice.sign(signatureHash),
|
||||||
|
hashType,
|
||||||
|
),
|
||||||
|
bitcoin.opcodes.OP_0,
|
||||||
|
bitcoin.opcodes.OP_TRUE,
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
}).input;
|
||||||
|
tx.setInputScript(0, redeemScriptSig!);
|
||||||
|
|
||||||
|
// Wait 2 blocks
|
||||||
|
await regtestUtils.mine(2);
|
||||||
|
|
||||||
|
await regtestUtils.broadcast(tx.toHex());
|
||||||
|
|
||||||
|
await regtestUtils.verify({
|
||||||
|
txId: tx.getId(),
|
||||||
|
address: regtestUtils.RANDOM_ADDRESS,
|
||||||
|
vout: 0,
|
||||||
|
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(
|
||||||
// 2 blocks from now
|
'can create (and broadcast via 3PBP) a Transaction where Alice (mediator) ' +
|
||||||
const sequence1 = bip68.encode({ blocks: 2 });
|
'can send after 5 blocks (complex CHECKSEQUENCEVERIFY)',
|
||||||
// 5 blocks from now
|
async () => {
|
||||||
const sequence2 = bip68.encode({ blocks: 5 });
|
// 2 blocks from now
|
||||||
const p2sh = bitcoin.payments.p2sh({
|
const sequence1 = bip68.encode({ blocks: 2 });
|
||||||
redeem: {
|
// 5 blocks from now
|
||||||
output: complexCsvOutput(
|
const sequence2 = bip68.encode({ blocks: 5 });
|
||||||
alice,
|
const p2sh = bitcoin.payments.p2sh({
|
||||||
bob,
|
redeem: {
|
||||||
charles,
|
output: complexCsvOutput(
|
||||||
dave,
|
alice,
|
||||||
sequence1,
|
bob,
|
||||||
sequence2,
|
charles,
|
||||||
),
|
dave,
|
||||||
},
|
sequence1,
|
||||||
network: regtest,
|
sequence2,
|
||||||
});
|
),
|
||||||
|
},
|
||||||
// fund the P2SH(CCSV) address
|
|
||||||
const unspent = await regtestUtils.faucet(p2sh.address!, 1e5);
|
|
||||||
|
|
||||||
const tx = new bitcoin.Transaction();
|
|
||||||
tx.version = 2;
|
|
||||||
tx.addInput(idToHash(unspent.txId), unspent.vout, sequence2); // Set sequence2 for input
|
|
||||||
tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4);
|
|
||||||
|
|
||||||
// {Alice mediator sig} OP_FALSE
|
|
||||||
const signatureHash = tx.hashForSignature(
|
|
||||||
0,
|
|
||||||
p2sh.redeem!.output!,
|
|
||||||
hashType,
|
|
||||||
);
|
|
||||||
const redeemScriptSig = bitcoin.payments.p2sh({
|
|
||||||
network: regtest,
|
|
||||||
redeem: {
|
|
||||||
network: regtest,
|
network: regtest,
|
||||||
output: p2sh.redeem!.output,
|
});
|
||||||
input: bitcoin.script.compile([
|
|
||||||
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
|
|
||||||
bitcoin.opcodes.OP_0,
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
}).input;
|
|
||||||
tx.setInputScript(0, redeemScriptSig!);
|
|
||||||
|
|
||||||
// Wait 5 blocks
|
// fund the P2SH(CCSV) address
|
||||||
await regtestUtils.mine(5);
|
const unspent = await regtestUtils.faucet(p2sh.address!, 1e5);
|
||||||
|
|
||||||
await regtestUtils.broadcast(tx.toHex());
|
const tx = new bitcoin.Transaction();
|
||||||
|
tx.version = 2;
|
||||||
|
tx.addInput(idToHash(unspent.txId), unspent.vout, sequence2); // Set sequence2 for input
|
||||||
|
tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4);
|
||||||
|
|
||||||
await regtestUtils.verify({
|
// {Alice mediator sig} OP_FALSE
|
||||||
txId: tx.getId(),
|
const signatureHash = tx.hashForSignature(
|
||||||
address: regtestUtils.RANDOM_ADDRESS,
|
0,
|
||||||
vout: 0,
|
p2sh.redeem!.output!,
|
||||||
value: 7e4,
|
hashType,
|
||||||
});
|
);
|
||||||
});
|
const redeemScriptSig = bitcoin.payments.p2sh({
|
||||||
|
network: regtest,
|
||||||
|
redeem: {
|
||||||
|
network: regtest,
|
||||||
|
output: p2sh.redeem!.output,
|
||||||
|
input: bitcoin.script.compile([
|
||||||
|
bitcoin.script.signature.encode(
|
||||||
|
alice.sign(signatureHash),
|
||||||
|
hashType,
|
||||||
|
),
|
||||||
|
bitcoin.opcodes.OP_0,
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
}).input;
|
||||||
|
tx.setInputScript(0, redeemScriptSig!);
|
||||||
|
|
||||||
|
// Wait 5 blocks
|
||||||
|
await regtestUtils.mine(5);
|
||||||
|
|
||||||
|
await regtestUtils.broadcast(tx.toHex());
|
||||||
|
|
||||||
|
await regtestUtils.verify({
|
||||||
|
txId: tx.getId(),
|
||||||
|
address: regtestUtils.RANDOM_ADDRESS,
|
||||||
|
vout: 0,
|
||||||
|
value: 7e4,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,7 +12,7 @@ async function buildAndSign(
|
||||||
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 utx = await regtestUtils.fetch(unspent.txId);
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
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';
|
||||||
import * as bip32 from 'bip32';
|
|
||||||
const rng = require('randombytes');
|
const rng = require('randombytes');
|
||||||
const regtest = regtestUtils.network;
|
const regtest = regtestUtils.network;
|
||||||
|
|
||||||
|
@ -437,85 +437,98 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input', async () => {
|
it(
|
||||||
const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)');
|
'can create (and broadcast via 3PBP) a Transaction, w/ a ' +
|
||||||
const inputData = await getInputData(5e4, p2sh.payment, true, 'p2sh-p2wsh');
|
'P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input',
|
||||||
{
|
async () => {
|
||||||
const {
|
const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)');
|
||||||
hash,
|
const inputData = await getInputData(
|
||||||
index,
|
5e4,
|
||||||
witnessUtxo,
|
p2sh.payment,
|
||||||
redeemScript,
|
true,
|
||||||
witnessScript,
|
'p2sh-p2wsh',
|
||||||
} = inputData;
|
|
||||||
assert.deepStrictEqual(
|
|
||||||
{ hash, index, witnessUtxo, redeemScript, witnessScript },
|
|
||||||
inputData,
|
|
||||||
);
|
);
|
||||||
}
|
{
|
||||||
|
const {
|
||||||
|
hash,
|
||||||
|
index,
|
||||||
|
witnessUtxo,
|
||||||
|
redeemScript,
|
||||||
|
witnessScript,
|
||||||
|
} = inputData;
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
{ hash, index, witnessUtxo, redeemScript, witnessScript },
|
||||||
|
inputData,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const psbt = new bitcoin.Psbt({ network: regtest })
|
const psbt = new bitcoin.Psbt({ network: regtest })
|
||||||
.addInput(inputData)
|
.addInput(inputData)
|
||||||
.addOutput({
|
.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,
|
address: regtestUtils.RANDOM_ADDRESS,
|
||||||
|
vout: 0,
|
||||||
value: 2e4,
|
value: 2e4,
|
||||||
})
|
});
|
||||||
.signInput(0, p2sh.keys[0])
|
},
|
||||||
.signInput(0, p2sh.keys[2])
|
);
|
||||||
.signInput(0, p2sh.keys[3]);
|
|
||||||
|
|
||||||
assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
|
it(
|
||||||
assert.strictEqual(
|
'can create (and broadcast via 3PBP) a Transaction, w/ a ' +
|
||||||
psbt.validateSignaturesOfInput(0, p2sh.keys[3].publicKey),
|
'P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input with nonWitnessUtxo',
|
||||||
true,
|
async () => {
|
||||||
);
|
// For learning purposes, ignore this test.
|
||||||
assert.throws(() => {
|
// REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData
|
||||||
psbt.validateSignaturesOfInput(0, p2sh.keys[1].publicKey);
|
const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)');
|
||||||
}, new RegExp('No signatures for this pubkey'));
|
const inputData = await getInputData(
|
||||||
psbt.finalizeAllInputs();
|
5e4,
|
||||||
|
p2sh.payment,
|
||||||
const tx = psbt.extractTransaction();
|
false,
|
||||||
|
'p2sh-p2wsh',
|
||||||
// build and broadcast to the Bitcoin RegTest network
|
);
|
||||||
await regtestUtils.broadcast(tx.toHex());
|
const psbt = new bitcoin.Psbt({ network: regtest })
|
||||||
|
.addInput(inputData)
|
||||||
await regtestUtils.verify({
|
.addOutput({
|
||||||
txId: tx.getId(),
|
address: regtestUtils.RANDOM_ADDRESS,
|
||||||
address: regtestUtils.RANDOM_ADDRESS,
|
value: 2e4,
|
||||||
vout: 0,
|
})
|
||||||
value: 2e4,
|
.signInput(0, p2sh.keys[0])
|
||||||
});
|
.signInput(0, p2sh.keys[2])
|
||||||
});
|
.signInput(0, p2sh.keys[3]);
|
||||||
|
psbt.finalizeAllInputs();
|
||||||
it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input with nonWitnessUtxo', async () => {
|
const tx = psbt.extractTransaction();
|
||||||
// For learning purposes, ignore this test.
|
await regtestUtils.broadcast(tx.toHex());
|
||||||
// REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData
|
await regtestUtils.verify({
|
||||||
const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)');
|
txId: tx.getId(),
|
||||||
const inputData = await getInputData(
|
|
||||||
5e4,
|
|
||||||
p2sh.payment,
|
|
||||||
false,
|
|
||||||
'p2sh-p2wsh',
|
|
||||||
);
|
|
||||||
const psbt = new bitcoin.Psbt({ network: regtest })
|
|
||||||
.addInput(inputData)
|
|
||||||
.addOutput({
|
|
||||||
address: regtestUtils.RANDOM_ADDRESS,
|
address: regtestUtils.RANDOM_ADDRESS,
|
||||||
|
vout: 0,
|
||||||
value: 2e4,
|
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 () => {
|
it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input using HD', async () => {
|
||||||
const hdRoot = bip32.fromSeed(rng(64));
|
const hdRoot = bip32.fromSeed(rng(64));
|
||||||
|
@ -581,7 +594,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function createPayment(_type: string, myKeys?: any[], network?: any) {
|
function createPayment(_type: string, myKeys?: any[], network?: any): any {
|
||||||
network = network || regtest;
|
network = network || regtest;
|
||||||
const splitType = _type.split('-').reverse();
|
const splitType = _type.split('-').reverse();
|
||||||
const isMultisig = splitType[0].slice(0, 4) === 'p2ms';
|
const isMultisig = splitType[0].slice(0, 4) === 'p2ms';
|
||||||
|
@ -589,8 +602,8 @@ function createPayment(_type: string, myKeys?: any[], network?: any) {
|
||||||
let m: number | undefined;
|
let m: number | undefined;
|
||||||
if (isMultisig) {
|
if (isMultisig) {
|
||||||
const match = splitType[0].match(/^p2ms\((\d+) of (\d+)\)$/);
|
const match = splitType[0].match(/^p2ms\((\d+) of (\d+)\)$/);
|
||||||
m = parseInt(match![1]);
|
m = parseInt(match![1], 10);
|
||||||
let n = parseInt(match![2]);
|
let n = parseInt(match![2], 10);
|
||||||
if (keys.length > 0 && keys.length !== n) {
|
if (keys.length > 0 && keys.length !== n) {
|
||||||
throw new Error('Need n keys for multisig');
|
throw new Error('Need n keys for multisig');
|
||||||
}
|
}
|
||||||
|
@ -628,7 +641,7 @@ function createPayment(_type: string, myKeys?: any[], network?: any) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getWitnessUtxo(out: any) {
|
function getWitnessUtxo(out: any): any {
|
||||||
delete out.address;
|
delete out.address;
|
||||||
out.script = Buffer.from(out.script, 'hex');
|
out.script = Buffer.from(out.script, 'hex');
|
||||||
return out;
|
return out;
|
||||||
|
@ -639,7 +652,7 @@ async function getInputData(
|
||||||
payment: any,
|
payment: any,
|
||||||
isSegwit: boolean,
|
isSegwit: boolean,
|
||||||
redeemType: string,
|
redeemType: string,
|
||||||
) {
|
): Promise<any> {
|
||||||
const unspent = await regtestUtils.faucetComplex(payment.output, amount);
|
const unspent = await regtestUtils.faucetComplex(payment.output, amount);
|
||||||
const utx = await regtestUtils.fetch(unspent.txId);
|
const utx = await regtestUtils.fetch(unspent.txId);
|
||||||
// for non segwit inputs, you must pass the full transaction buffer
|
// for non segwit inputs, you must pass the full transaction buffer
|
||||||
|
|
|
@ -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];
|
||||||
|
|
||||||
|
|
|
@ -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 || {};
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ const initBuffers = (object: any): typeof preFixtures =>
|
||||||
|
|
||||||
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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -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'),
|
||||||
|
|
|
@ -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 +
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Reference in a new issue