256 lines
7.5 KiB

import * as bitcoin from '..';
const { describe, it } = require('mocha');
const assert = require('assert');
const base58 = require('bs58');
const base58EncodeDecode = require('../ts_test/fixtures/core/base58_encode_decode.json');
const base58KeysInvalid = require('../ts_test/fixtures/core/base58_keys_invalid.json');
const base58KeysValid = require('../ts_test/fixtures/core/base58_keys_valid.json');
const blocksValid = require('../ts_test/fixtures/core/blocks.json');
const sigCanonical = require('../ts_test/fixtures/core/sig_canonical.json');
const sigHash = require('../ts_test/fixtures/core/sighash.json');
const sigNoncanonical = require('../ts_test/fixtures/core/sig_noncanonical.json');
const txValid = require('../ts_test/fixtures/core/tx_valid.json');
describe('Bitcoin-core', () => {
// base58EncodeDecode
describe('base58', () => {
base58EncodeDecode.forEach(f => {
const fhex = f[0];
const fb58 = f[1];
it('can decode ' + fb58, () => {
const buffer = base58.decode(fb58);
const actual = buffer.toString('hex');
assert.strictEqual(actual, fhex);
});
it('can encode ' + fhex, () => {
const buffer = Buffer.from(fhex, 'hex');
const actual = base58.encode(buffer);
assert.strictEqual(actual, fb58);
});
});
});
// base58KeysValid
describe('address.toBase58Check', () => {
const typeMap = {
pubkey: 'pubKeyHash',
script: 'scriptHash',
};
base58KeysValid.forEach(f => {
const expected = f[0];
const hash = Buffer.from(f[1], 'hex');
const params = f[2];
if (params.isPrivkey) return;
const network = params.isTestnet
? bitcoin.networks.testnet
: bitcoin.networks.bitcoin;
const version = network[typeMap[params.addrType]];
it('can export ' + expected, () => {
assert.strictEqual(
bitcoin.address.toBase58Check(hash, version),
expected,
);
});
});
});
// base58KeysInvalid
describe('address.fromBase58Check', () => {
const allowedNetworks = [
bitcoin.networks.bitcoin.pubKeyHash,
bitcoin.networks.bitcoin.scriptHash,
bitcoin.networks.testnet.pubKeyHash,
bitcoin.networks.testnet.scriptHash,
];
base58KeysInvalid.forEach(f => {
const string0 = f[0];
it('throws on ' + string0, () => {
assert.throws(() => {
const address = bitcoin.address.fromBase58Check(string0);
assert.notStrictEqual(
allowedNetworks.indexOf(address.version),
-1,
'Invalid network',
);
}, /(Invalid (checksum|network))|(too (short|long))/);
});
});
});
// base58KeysValid
describe('ECPair', () => {
base58KeysValid.forEach(f => {
const string0 = f[0];
const hex = f[1];
const params = f[2];
if (!params.isPrivkey) return;
const network = params.isTestnet
? bitcoin.networks.testnet
: bitcoin.networks.bitcoin;
const keyPair = bitcoin.ECPair.fromWIF(string0, network);
it('fromWIF imports ' + string0, () => {
assert.strictEqual(keyPair.privateKey.toString('hex'), hex);
assert.strictEqual(keyPair.compressed, params.isCompressed);
});
it('toWIF exports ' + hex + ' to ' + string0, () => {
assert.strictEqual(keyPair.toWIF(), string0);
});
});
});
// base58KeysInvalid
describe('ECPair.fromWIF', () => {
const allowedNetworks = [
bitcoin.networks.bitcoin,
bitcoin.networks.testnet,
];
base58KeysInvalid.forEach(f => {
const string0 = f[0];
it('throws on ' + string0, () => {
assert.throws(() => {
bitcoin.ECPair.fromWIF(string0, allowedNetworks);
}, /(Invalid|Unknown) (checksum|compression flag|network version|WIF length)/);
});
});
});
describe('Block.fromHex', () => {
blocksValid.forEach(f => {
it('can parse ' + f.id, () => {
const block = bitcoin.Block.fromHex(f.hex);
assert.strictEqual(block.getId(), f.id);
assert.strictEqual(block.transactions.length, f.transactions);
});
});
});
// txValid
describe('Transaction.fromHex', () => {
txValid.forEach(f => {
// Objects that are only a single string are ignored
if (f.length === 1) return;
const inputs = f[0];
const fhex = f[1];
// const verifyFlags = f[2] // TODO: do we need to test this?
it('can decode ' + fhex, () => {
const transaction = bitcoin.Transaction.fromHex(fhex);
transaction.ins.forEach((txIn, i) => {
const input = inputs[i];
// reverse because test data is reversed
const prevOutHash = Buffer.from(input[0], 'hex').reverse();
const prevOutIndex = input[1];
assert.deepStrictEqual(txIn.hash, prevOutHash);
// we read UInt32, not Int32
assert.strictEqual(txIn.index & 0xffffffff, prevOutIndex);
});
});
});
});
// sighash
describe('Transaction', () => {
sigHash.forEach(f => {
// Objects that are only a single string are ignored
if (f.length === 1) return;
const txHex = f[0];
const scriptHex = f[1];
const inIndex = f[2];
const hashType = f[3];
const expectedHash = f[4];
const hashTypes = [];
if ((hashType & 0x1f) === bitcoin.Transaction.SIGHASH_NONE)
hashTypes.push('SIGHASH_NONE');
else if ((hashType & 0x1f) === bitcoin.Transaction.SIGHASH_SINGLE)
hashTypes.push('SIGHASH_SINGLE');
else hashTypes.push('SIGHASH_ALL');
if (hashType & bitcoin.Transaction.SIGHASH_ANYONECANPAY)
hashTypes.push('SIGHASH_ANYONECANPAY');
const hashTypeName = hashTypes.join(' | ');
it(
'should hash ' + txHex.slice(0, 40) + '... (' + hashTypeName + ')',
() => {
const transaction = bitcoin.Transaction.fromHex(txHex);
assert.strictEqual(transaction.toHex(), txHex);
const script = Buffer.from(scriptHex, 'hex');
const scriptChunks = bitcoin.script.decompile(script);
assert.strictEqual(
bitcoin.script.compile(scriptChunks).toString('hex'),
scriptHex,
);
const hash = transaction.hashForSignature(inIndex, script, hashType);
// reverse because test data is reversed
// @ts-ignore
assert.strictEqual(hash.reverse().toString('hex'), expectedHash);
},
);
});
});
describe('script.signature.decode', () => {
sigCanonical.forEach(hex => {
const buffer = Buffer.from(hex, 'hex');
it('can parse ' + hex, () => {
const parsed = bitcoin.script.signature.decode(buffer);
const actual = bitcoin.script.signature.encode(
parsed.signature,
parsed.hashType,
);
assert.strictEqual(actual.toString('hex'), hex);
});
});
sigNoncanonical.forEach((hex, i) => {
if (i === 0) return;
if (i % 2 !== 0) return;
const description = sigNoncanonical[i - 1].slice(0, -1);
const buffer = Buffer.from(hex, 'hex');
it('throws on ' + description, () => {
const regexStatement =
'Expected DER (integer|sequence)|(R|S) value (excessively padded|is' +
' negative)|(R|S|DER sequence) length is (zero|too short|too long|i' +
'nvalid)|Invalid hashType';
assert.throws(() => {
bitcoin.script.signature.decode(buffer);
}, new RegExp(regexStatement));
});
});
});
});
export {};