bitcore/test/test.Transaction.js

378 lines
12 KiB
JavaScript
Raw Normal View History

2014-02-06 07:57:47 -08:00
'use strict';
var chai = chai || require('chai');
chai.Assertion.includeStack = true;
var bitcore = bitcore || require('../bitcore');
2014-02-06 07:57:47 -08:00
var should = chai.should();
var Transaction = bitcore.Transaction;
2014-02-27 13:09:02 -08:00
var In;
var Out;
var Script = bitcore.Script;
2014-03-14 23:27:06 -07:00
var util = bitcore.util;
2014-02-27 13:09:02 -08:00
var buffertools = require('buffertools');
2014-03-10 09:44:22 -07:00
var testdata = testdata || require('./testdata');
2014-02-06 07:57:47 -08:00
// Read tests from test/data/tx_valid.json and tx_invalid.json
// Format is an array of arrays
// Inner arrays are either [ "comment" ]
// or [[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"], serializedTransaction, enforceP2SH
// ... where all scripts are stringified scripts.
// Returns an object with the Transaction object, and an array of input objects
function parse_test_transaction(entry) {
// Ignore comments
if (entry.length !== 3) return;
var inputs = {};
entry[0].forEach(function(vin) {
var hash = (vin[0]);
var index = vin[1];
var scriptPubKey = Script.fromHumanReadable(vin[2]);
var mapKey = [hash, index];
console.log('mapkey=' + mapKey);
inputs[mapKey] = scriptPubKey;
2014-03-20 07:20:51 -07:00
});
var raw = new Buffer(entry[1], 'hex');
var tx = new Transaction();
tx.parse(raw);
// Sanity check transaction has been parsed correctly
buffertools.toHex(tx.serialize()).should.equal(buffertools.toHex(raw));
return {
'transaction': tx,
'inputs': inputs
};
}
2014-02-18 06:39:47 -08:00
describe('Transaction', function() {
2014-02-06 07:57:47 -08:00
it('should initialze the main object', function() {
should.exist(Transaction);
2014-02-27 13:09:02 -08:00
In = Transaction.In;
Out = Transaction.Out;
should.exist(In);
should.exist(Out);
2014-02-06 07:57:47 -08:00
});
2014-03-14 13:38:42 -07:00
2014-02-06 07:57:47 -08:00
it('should be able to create instance', function() {
var t = new Transaction();
should.exist(t);
});
2014-03-14 13:38:42 -07:00
it('#selectUnspent should be able to select utxos', function() {
var u = Transaction.selectUnspent(testdata.dataUnspent, 1.0, true);
2014-03-14 13:38:42 -07:00
u.length.should.equal(3);
should.exist(u[0].amount);
should.exist(u[0].txid);
should.exist(u[0].scriptPubKey);
should.exist(u[0].vout);
u = Transaction.selectUnspent(testdata.dataUnspent, 0.5, true);
2014-03-14 13:38:42 -07:00
u.length.should.equal(3);
u = Transaction.selectUnspent(testdata.dataUnspent, 0.1, true);
2014-03-14 13:38:42 -07:00
u.length.should.equal(2);
u = Transaction.selectUnspent(testdata.dataUnspent, 0.05, true);
2014-03-14 13:38:42 -07:00
u.length.should.equal(2);
u = Transaction.selectUnspent(testdata.dataUnspent, 0.015, true);
2014-03-14 13:38:42 -07:00
u.length.should.equal(2);
u = Transaction.selectUnspent(testdata.dataUnspent, 0.01, true);
2014-03-14 13:38:42 -07:00
u.length.should.equal(1);
});
2014-03-15 08:21:59 -07:00
it('#selectUnspent should return null if not enough utxos', function() {
var u = Transaction.selectUnspent(testdata.dataUnspent, 1.12);
should.not.exist(u);
2014-03-14 23:27:06 -07:00
});
2014-03-14 13:38:42 -07:00
2014-03-14 23:27:06 -07:00
2014-03-15 08:21:59 -07:00
it('#selectUnspent should check confirmations', function() {
var u = Transaction.selectUnspent(testdata.dataUnspent, 0.9);
should.not.exist(u);
u = Transaction.selectUnspent(testdata.dataUnspent, 0.9, true);
2014-03-15 08:21:59 -07:00
u.length.should.equal(3);
u = Transaction.selectUnspent(testdata.dataUnspent, 0.11);
2014-03-15 08:21:59 -07:00
u.length.should.equal(2);
u = Transaction.selectUnspent(testdata.dataUnspent, 0.111);
should.not.exist(u);
2014-03-15 08:21:59 -07:00
});
var opts = {
remainderAddress: 'mwZabyZXg8JzUtFX1pkGygsMJjnuqiNhgd',
allowUnconfirmed: true,
};
2014-03-15 08:21:59 -07:00
it('#create should be able to create instance', function() {
var utxos = testdata.dataUnspent;
var outs = [{
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
amount: 0.08
}];
var ret = Transaction.create(utxos, outs, opts);
should.exist(ret.tx);
should.exist(ret.selectedUtxos);
ret.selectedUtxos.length.should.equal(2);
var tx = ret.tx;
2014-03-15 08:21:59 -07:00
2014-03-14 23:27:06 -07:00
tx.version.should.equal(1);
tx.ins.length.should.equal(2);
tx.outs.length.should.equal(2);
2014-03-14 23:27:06 -07:00
util.valueToBigInt(tx.outs[0].v).cmp(8000000).should.equal(0);
2014-03-16 19:47:01 -07:00
2014-03-15 14:21:59 -07:00
// remainder is 0.0299 here because unspent select utxos in order
2014-03-16 19:47:01 -07:00
util.valueToBigInt(tx.outs[1].v).cmp(2990000).should.equal(0);
tx.isComplete().should.equal(false);
2014-03-15 08:21:59 -07:00
});
it('#create should fail if not enough inputs ', function() {
var utxos = testdata.dataUnspent;
var outs = [{
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
amount: 80
}];
2014-03-15 08:21:59 -07:00
Transaction
.create
.bind(utxos, outs, opts)
.should.
throw ();
2014-03-16 19:47:01 -07:00
var outs2 = [{
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
amount: 0.5
}];
should.exist(Transaction.create(utxos, outs2, opts));
2014-03-16 19:47:01 -07:00
// do not allow unconfirmed
Transaction.create.bind(utxos, outs2).should.
throw ();
2014-03-15 08:21:59 -07:00
});
it('#create should create same output as bitcoind createrawtransaction ', function() {
var utxos = testdata.dataUnspent;
var outs = [{
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
amount: 0.08
}];
2014-03-18 17:46:10 -07:00
var ret = Transaction.create(utxos, outs, opts);
var tx = ret.tx;
2014-03-15 13:44:29 -07:00
// string output generated from: bitcoind createrawtransaction '[{"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1","vout":1},{"txid":"2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc2","vout":0} ]' '{"mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE":0.08,"mwZabyZXg8JzUtFX1pkGygsMJjnuqiNhgd":0.0299}'
2014-03-16 19:47:01 -07:00
tx.serialize().toString('hex').should.equal('0100000002c1cf12ab89729d19d3cdec8ae531b5038d56c741006a105d532b3a7afa65c12a0100000000ffffffffc2cf12ab89729d19d3cdec8ae531b5038d56c741006a105d532b3a7afa65c12a0000000000ffffffff0200127a00000000001976a914774e603bafb717bd3f070e68bbcccfd907c77d1388acb09f2d00000000001976a914b00127584485a7cff0949ef0f6bc5575f06ce00d88ac00000000');
2014-03-15 08:21:59 -07:00
});
it('#create should create same output as bitcoind createrawtransaction wo remainder', function() {
var utxos = testdata.dataUnspent;
2014-03-15 08:22:15 -07:00
// no remainder
var outs = [{
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
amount: 0.08
}];
var ret = Transaction.create(utxos, outs, {
fee: 0.03
});
var tx = ret.tx;
2014-03-15 13:44:29 -07:00
// string output generated from: bitcoind createrawtransaction '[{"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1","vout":1},{"txid":"2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc2","vout":0} ]' '{"mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE":0.08}'
//
2014-03-15 08:21:59 -07:00
tx.serialize().toString('hex').should.equal('0100000002c1cf12ab89729d19d3cdec8ae531b5038d56c741006a105d532b3a7afa65c12a0100000000ffffffffc2cf12ab89729d19d3cdec8ae531b5038d56c741006a105d532b3a7afa65c12a0000000000ffffffff0100127a00000000001976a914774e603bafb717bd3f070e68bbcccfd907c77d1388ac00000000');
2014-03-14 13:38:42 -07:00
});
2014-03-18 17:46:10 -07:00
it('#createAndSign should sign a tx', function() {
var utxos = testdata.dataUnspentSign.unspent;
var outs = [{
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
amount: 0.08
}];
2014-03-18 17:46:10 -07:00
var ret = Transaction.createAndSign(utxos, outs, testdata.dataUnspentSign.keyStrings, opts);
var tx = ret.tx;
tx.isComplete().should.equal(true);
2014-03-16 19:47:01 -07:00
tx.ins.length.should.equal(1);
tx.outs.length.should.equal(2);
2014-03-15 13:58:38 -07:00
var outs2 = [{
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
amount: 16
}];
2014-03-18 17:46:10 -07:00
var ret2 = Transaction.createAndSign(utxos, outs2, testdata.dataUnspentSign.keyStrings, opts);
var tx2 = ret2.tx;
tx2.isComplete().should.equal(true);
2014-03-16 19:47:01 -07:00
tx2.ins.length.should.equal(3);
tx2.outs.length.should.equal(2);
});
it('#createAndSign should sign an incomplete tx ', function() {
var keys = ['cNpW8B7XPAzCdRR9RBWxZeveSNy3meXgHD8GuhcqUyDuy8ptCDzJ'];
var utxos = testdata.dataUnspentSign.unspent;
var outs = [{
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
amount: 0.08
}];
2014-03-18 17:46:10 -07:00
var ret = Transaction.createAndSign(utxos, outs, keys, opts);
var tx = ret.tx;
2014-03-16 19:47:01 -07:00
tx.ins.length.should.equal(1);
tx.outs.length.should.equal(2);
});
it('#isComplete should return TX signature status', function() {
var keys = ['cNpW8B7XPAzCdRR9RBWxZeveSNy3meXgHD8GuhcqUyDuy8ptCDzJ'];
var utxos = testdata.dataUnspentSign.unspent;
var outs = [{
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
amount: 0.08
}];
2014-03-18 17:46:10 -07:00
var ret = Transaction.createAndSign(utxos, outs, keys, opts);
var tx = ret.tx;
tx.isComplete().should.equal(false);
tx.sign(ret.selectedUtxos, testdata.dataUnspentSign.keyStrings);
tx.isComplete().should.equal(true);
});
it('#sign should sign a tx in multiple steps (case1)', function() {
var outs = [{
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
amount: 1.08
}];
2014-03-18 17:46:10 -07:00
var ret = Transaction.create(testdata.dataUnspentSign.unspent, outs, opts);
var tx = ret.tx;
var selectedUtxos = ret.selectedUtxos;
2014-03-15 13:58:38 -07:00
var k1 = testdata.dataUnspentSign.keyStrings.slice(0, 1);
tx.isComplete().should.equal(false);
tx.sign(selectedUtxos, k1).should.equal(false);
var k23 = testdata.dataUnspentSign.keyStrings.slice(1, 3);
tx.sign(selectedUtxos, k23).should.equal(true);
tx.isComplete().should.equal(true);
});
it('#sign should sign a tx in multiple steps (case2)', function() {
var outs = [{
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
amount: 16
}];
2014-03-18 17:46:10 -07:00
var ret = Transaction.create(testdata.dataUnspentSign.unspent, outs, opts);
var tx = ret.tx;
var selectedUtxos = ret.selectedUtxos;
var k1 = testdata.dataUnspentSign.keyStrings.slice(0, 1);
var k2 = testdata.dataUnspentSign.keyStrings.slice(1, 2);
var k3 = testdata.dataUnspentSign.keyStrings.slice(2, 3);
tx.sign(selectedUtxos, k1).should.equal(false);
tx.sign(selectedUtxos, k2).should.equal(false);
tx.sign(selectedUtxos, k3).should.equal(true);
2014-03-18 17:46:10 -07:00
});
2014-03-14 13:38:42 -07:00
it('#createAndSign: should generate dynamic fee and readjust (and not) the selected UTXOs', function() {
2014-03-16 19:47:01 -07:00
//this cases exceeds the input by 1mbtc AFTEr calculating the dynamic fee,
//so, it should trigger adding a new 10BTC utxo
var utxos = testdata.dataUnspentSign.unspent;
2014-03-16 19:47:01 -07:00
var outs = [];
var n = 101;
for (var i = 0; i < n; i++) {
outs.push({
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
amount: 0.01
});
2014-03-16 19:47:01 -07:00
}
2014-03-18 17:46:10 -07:00
var ret = Transaction.createAndSign(utxos, outs, testdata.dataUnspentSign.keyStrings, opts);
var tx = ret.tx;
2014-03-16 19:47:01 -07:00
tx.getSize().should.equal(3560);
// ins = 11.0101 BTC (2 inputs: 1.0101 + 10 );
tx.ins.length.should.equal(2);
// outs = 101 outs:
// 101 * 0.01 = 1.01BTC; + 0.0004 fee = 1.0104btc
// remainder = 11.0101-1.0104 = 9.9997
tx.outs.length.should.equal(102);
util.valueToBigInt(tx.outs[n].v).cmp(999970000).should.equal(0);
tx.isComplete().should.equal(true);
//this is the complementary case, it does not trigger a new utxo
utxos = testdata.dataUnspentSign.unspent;
outs = [];
n = 100;
for (i = 0; i < n; i++) {
outs.push({
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
amount: 0.01
});
2014-03-16 19:47:01 -07:00
}
ret = Transaction.createAndSign(utxos, outs, testdata.dataUnspentSign.keyStrings, opts);
tx = ret.tx;
2014-03-16 19:47:01 -07:00
tx.getSize().should.equal(3485);
// ins = 1.0101 BTC (1 inputs: 1.0101);
tx.ins.length.should.equal(1);
// outs = 100 outs:
// 100 * 0.01 = 1BTC; + 0.0004 fee = 1.0004btc
// remainder = 1.0101-1.0004 = 0.0097
tx.outs.length.should.equal(101);
util.valueToBigInt(tx.outs[n].v).cmp(970000).should.equal(0);
tx.isComplete().should.equal(true);
});
/*
* Bitcoin core transaction tests
*/
// Verify that known valid transactions are intepretted correctly
var cb = function(err, results) {
should.not.exist(err);
should.exist(results);
results.should.equal(true);
};
2014-03-10 09:44:22 -07:00
testdata.dataTxValid.forEach(function(datum) {
if (datum.length < 3) return;
var raw = datum[1];
var verifyP2SH = datum[2];
it('valid tx=' + raw, function() {
// Verify that all inputs are valid
var testTx = parse_test_transaction(datum);
console.log(raw);
//buffertools.toHex(testTx.transaction.serialize()).should.equal(raw);
var inputs = testTx.transaction.inputs();
for (var i = 0; i < inputs.length; i++) {
console.log(' input number #########' + i);
var input = inputs[i];
buffertools.reverse(input[0]);
input[0] = buffertools.toHex(input[0]);
var mapKey = [input];
var scriptPubKey = testTx.inputs[mapKey];
if (!scriptPubKey) throw new Error('asdasdasdasd');
testTx.transaction.verifyInput(
i,
scriptPubKey, {
verifyP2SH: verifyP2SH,
dontVerifyStrictEnc: true
},
cb);
}
});
});
2014-02-27 13:09:02 -08:00
});