|
|
@ -6,8 +6,7 @@ var bitcore = bitcore || require('../bitcore'); |
|
|
|
|
|
|
|
var should = chai.should(); |
|
|
|
|
|
|
|
var TransactionModule = bitcore.Transaction; |
|
|
|
var Transaction; |
|
|
|
var Transaction = bitcore.Transaction; |
|
|
|
var In; |
|
|
|
var Out; |
|
|
|
var Script = bitcore.Script; |
|
|
@ -40,7 +39,7 @@ function parse_test_transaction(entry) { |
|
|
|
}); |
|
|
|
|
|
|
|
var raw = new Buffer(entry[1], 'hex'); |
|
|
|
var tx = new TransactionModule(); |
|
|
|
var tx = new Transaction(); |
|
|
|
tx.parse(raw); |
|
|
|
|
|
|
|
// Sanity check transaction has been parsed correctly
|
|
|
@ -53,10 +52,6 @@ function parse_test_transaction(entry) { |
|
|
|
|
|
|
|
describe('Transaction', function() { |
|
|
|
it('should initialze the main object', function() { |
|
|
|
should.exist(TransactionModule); |
|
|
|
}); |
|
|
|
it('should be able to create class', function() { |
|
|
|
Transaction = TransactionModule; |
|
|
|
should.exist(Transaction); |
|
|
|
In = Transaction.In; |
|
|
|
Out = Transaction.Out; |
|
|
@ -72,7 +67,7 @@ describe('Transaction', function() { |
|
|
|
|
|
|
|
|
|
|
|
it('#selectUnspent should be able to select utxos', function() { |
|
|
|
var u = Transaction.selectUnspent(testdata.dataUnspent,1.0, true); |
|
|
|
var u = Transaction.selectUnspent(testdata.dataUnspent, 1.0, true); |
|
|
|
u.length.should.equal(3); |
|
|
|
|
|
|
|
should.exist(u[0].amount); |
|
|
@ -80,37 +75,37 @@ describe('Transaction', function() { |
|
|
|
should.exist(u[0].scriptPubKey); |
|
|
|
should.exist(u[0].vout); |
|
|
|
|
|
|
|
u = Transaction.selectUnspent(testdata.dataUnspent,0.5, true); |
|
|
|
u = Transaction.selectUnspent(testdata.dataUnspent, 0.5, true); |
|
|
|
u.length.should.equal(3); |
|
|
|
|
|
|
|
u = Transaction.selectUnspent(testdata.dataUnspent,0.1, true); |
|
|
|
u = Transaction.selectUnspent(testdata.dataUnspent, 0.1, true); |
|
|
|
u.length.should.equal(2); |
|
|
|
|
|
|
|
u = Transaction.selectUnspent(testdata.dataUnspent,0.05, true); |
|
|
|
u = Transaction.selectUnspent(testdata.dataUnspent, 0.05, true); |
|
|
|
u.length.should.equal(2); |
|
|
|
|
|
|
|
u = Transaction.selectUnspent(testdata.dataUnspent,0.015, true); |
|
|
|
u = Transaction.selectUnspent(testdata.dataUnspent, 0.015, true); |
|
|
|
u.length.should.equal(2); |
|
|
|
|
|
|
|
u = Transaction.selectUnspent(testdata.dataUnspent,0.01, true); |
|
|
|
u = Transaction.selectUnspent(testdata.dataUnspent, 0.01, true); |
|
|
|
u.length.should.equal(1); |
|
|
|
}); |
|
|
|
|
|
|
|
it('#selectUnspent should return null if not enough utxos', function() { |
|
|
|
var u = Transaction.selectUnspent(testdata.dataUnspent,1.12); |
|
|
|
var u = Transaction.selectUnspent(testdata.dataUnspent, 1.12); |
|
|
|
should.not.exist(u); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
it('#selectUnspent should check confirmations', function() { |
|
|
|
var u = Transaction.selectUnspent(testdata.dataUnspent,0.9); |
|
|
|
var u = Transaction.selectUnspent(testdata.dataUnspent, 0.9); |
|
|
|
should.not.exist(u); |
|
|
|
var u = Transaction.selectUnspent(testdata.dataUnspent,0.9,true); |
|
|
|
u = Transaction.selectUnspent(testdata.dataUnspent, 0.9, true); |
|
|
|
u.length.should.equal(3); |
|
|
|
|
|
|
|
var u = Transaction.selectUnspent(testdata.dataUnspent,0.11); |
|
|
|
u = Transaction.selectUnspent(testdata.dataUnspent, 0.11); |
|
|
|
u.length.should.equal(2); |
|
|
|
var u = Transaction.selectUnspent(testdata.dataUnspent,0.111); |
|
|
|
u = Transaction.selectUnspent(testdata.dataUnspent, 0.111); |
|
|
|
should.not.exist(u); |
|
|
|
}); |
|
|
|
|
|
|
@ -121,8 +116,11 @@ describe('Transaction', function() { |
|
|
|
}; |
|
|
|
|
|
|
|
it('#create should be able to create instance', function() { |
|
|
|
var utxos =testdata.dataUnspent; |
|
|
|
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.08}]; |
|
|
|
var utxos = testdata.dataUnspent; |
|
|
|
var outs = [{ |
|
|
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', |
|
|
|
amount: 0.08 |
|
|
|
}]; |
|
|
|
|
|
|
|
var ret = Transaction.create(utxos, outs, opts); |
|
|
|
should.exist(ret.tx); |
|
|
@ -143,24 +141,35 @@ describe('Transaction', function() { |
|
|
|
}); |
|
|
|
|
|
|
|
it('#create should fail if not enough inputs ', function() { |
|
|
|
var utxos =testdata.dataUnspent; |
|
|
|
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:80}]; |
|
|
|
var utxos = testdata.dataUnspent; |
|
|
|
var outs = [{ |
|
|
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', |
|
|
|
amount: 80 |
|
|
|
}]; |
|
|
|
Transaction |
|
|
|
.create |
|
|
|
.bind(utxos, outs, opts) |
|
|
|
.should.throw(); |
|
|
|
.should. |
|
|
|
throw (); |
|
|
|
|
|
|
|
var outs2 = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.5}]; |
|
|
|
should.exist( Transaction.create(utxos, outs2, opts)); |
|
|
|
var outs2 = [{ |
|
|
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', |
|
|
|
amount: 0.5 |
|
|
|
}]; |
|
|
|
should.exist(Transaction.create(utxos, outs2, opts)); |
|
|
|
|
|
|
|
// do not allow unconfirmed
|
|
|
|
Transaction.create.bind(utxos, outs2).should.throw(); |
|
|
|
Transaction.create.bind(utxos, outs2).should. |
|
|
|
throw (); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
it('#create should create same output as bitcoind createrawtransaction ', function() { |
|
|
|
var utxos =testdata.dataUnspent; |
|
|
|
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.08}]; |
|
|
|
var utxos = testdata.dataUnspent; |
|
|
|
var outs = [{ |
|
|
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', |
|
|
|
amount: 0.08 |
|
|
|
}]; |
|
|
|
var ret = Transaction.create(utxos, outs, opts); |
|
|
|
var tx = ret.tx; |
|
|
|
|
|
|
@ -170,10 +179,15 @@ describe('Transaction', function() { |
|
|
|
}); |
|
|
|
|
|
|
|
it('#create should create same output as bitcoind createrawtransaction wo remainder', function() { |
|
|
|
var utxos =testdata.dataUnspent; |
|
|
|
var utxos = testdata.dataUnspent; |
|
|
|
// no remainder
|
|
|
|
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.08}]; |
|
|
|
var ret = Transaction.create(utxos, outs, {fee:0.03} ); |
|
|
|
var outs = [{ |
|
|
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', |
|
|
|
amount: 0.08 |
|
|
|
}]; |
|
|
|
var ret = Transaction.create(utxos, outs, { |
|
|
|
fee: 0.03 |
|
|
|
}); |
|
|
|
var tx = ret.tx; |
|
|
|
|
|
|
|
// string output generated from: bitcoind createrawtransaction '[{"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1","vout":1},{"txid":"2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc2","vout":0} ]' '{"mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE":0.08}'
|
|
|
@ -182,15 +196,21 @@ describe('Transaction', function() { |
|
|
|
}); |
|
|
|
|
|
|
|
it('#createAndSign should sign a tx', function() { |
|
|
|
var utxos =testdata.dataUnspentSign.unspent; |
|
|
|
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.08}]; |
|
|
|
var utxos = testdata.dataUnspentSign.unspent; |
|
|
|
var outs = [{ |
|
|
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', |
|
|
|
amount: 0.08 |
|
|
|
}]; |
|
|
|
var ret = Transaction.createAndSign(utxos, outs, testdata.dataUnspentSign.keyStrings, opts); |
|
|
|
var tx = ret.tx; |
|
|
|
tx.isComplete().should.equal(true); |
|
|
|
tx.ins.length.should.equal(1); |
|
|
|
tx.outs.length.should.equal(2); |
|
|
|
|
|
|
|
var outs2 = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:16}]; |
|
|
|
var outs2 = [{ |
|
|
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', |
|
|
|
amount: 16 |
|
|
|
}]; |
|
|
|
var ret2 = Transaction.createAndSign(utxos, outs2, testdata.dataUnspentSign.keyStrings, opts); |
|
|
|
var tx2 = ret2.tx; |
|
|
|
tx2.isComplete().should.equal(true); |
|
|
@ -200,8 +220,11 @@ describe('Transaction', function() { |
|
|
|
|
|
|
|
it('#createAndSign should sign an incomplete tx ', function() { |
|
|
|
var keys = ['cNpW8B7XPAzCdRR9RBWxZeveSNy3meXgHD8GuhcqUyDuy8ptCDzJ']; |
|
|
|
var utxos =testdata.dataUnspentSign.unspent; |
|
|
|
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.08}]; |
|
|
|
var utxos = testdata.dataUnspentSign.unspent; |
|
|
|
var outs = [{ |
|
|
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', |
|
|
|
amount: 0.08 |
|
|
|
}]; |
|
|
|
var ret = Transaction.createAndSign(utxos, outs, keys, opts); |
|
|
|
var tx = ret.tx; |
|
|
|
tx.ins.length.should.equal(1); |
|
|
@ -209,8 +232,11 @@ describe('Transaction', function() { |
|
|
|
}); |
|
|
|
it('#isComplete should return TX signature status', function() { |
|
|
|
var keys = ['cNpW8B7XPAzCdRR9RBWxZeveSNy3meXgHD8GuhcqUyDuy8ptCDzJ']; |
|
|
|
var utxos =testdata.dataUnspentSign.unspent; |
|
|
|
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.08}]; |
|
|
|
var utxos = testdata.dataUnspentSign.unspent; |
|
|
|
var outs = [{ |
|
|
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', |
|
|
|
amount: 0.08 |
|
|
|
}]; |
|
|
|
var ret = Transaction.createAndSign(utxos, outs, keys, opts); |
|
|
|
var tx = ret.tx; |
|
|
|
tx.isComplete().should.equal(false); |
|
|
@ -219,31 +245,37 @@ describe('Transaction', function() { |
|
|
|
}); |
|
|
|
|
|
|
|
it('#sign should sign a tx in multiple steps (case1)', function() { |
|
|
|
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:1.08}]; |
|
|
|
var outs = [{ |
|
|
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', |
|
|
|
amount: 1.08 |
|
|
|
}]; |
|
|
|
var ret = Transaction.create(testdata.dataUnspentSign.unspent, outs, opts); |
|
|
|
var tx = ret.tx; |
|
|
|
var tx = ret.tx; |
|
|
|
var selectedUtxos = ret.selectedUtxos; |
|
|
|
|
|
|
|
var k1 = testdata.dataUnspentSign.keyStrings.slice(0,1); |
|
|
|
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); |
|
|
|
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}]; |
|
|
|
var outs = [{ |
|
|
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', |
|
|
|
amount: 16 |
|
|
|
}]; |
|
|
|
var ret = Transaction.create(testdata.dataUnspentSign.unspent, outs, opts); |
|
|
|
var tx = ret.tx; |
|
|
|
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); |
|
|
|
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); |
|
|
@ -253,11 +285,14 @@ describe('Transaction', function() { |
|
|
|
it('#createAndSign: should generate dynamic fee and readjust (and not) the selected UTXOs', function() { |
|
|
|
//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; |
|
|
|
var utxos = testdata.dataUnspentSign.unspent; |
|
|
|
var outs = []; |
|
|
|
var n =101; |
|
|
|
for (var i=0; i<n; i++) { |
|
|
|
outs.push({address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.01}); |
|
|
|
var n = 101; |
|
|
|
for (var i = 0; i < n; i++) { |
|
|
|
outs.push({ |
|
|
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', |
|
|
|
amount: 0.01 |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
var ret = Transaction.createAndSign(utxos, outs, testdata.dataUnspentSign.keyStrings, opts); |
|
|
@ -275,15 +310,18 @@ describe('Transaction', function() { |
|
|
|
|
|
|
|
|
|
|
|
//this is the complementary case, it does not trigger a new utxo
|
|
|
|
var utxos =testdata.dataUnspentSign.unspent; |
|
|
|
var outs = []; |
|
|
|
var n =100; |
|
|
|
for (var i=0; i<n; i++) { |
|
|
|
outs.push({address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.01}); |
|
|
|
utxos = testdata.dataUnspentSign.unspent; |
|
|
|
outs = []; |
|
|
|
n = 100; |
|
|
|
for (i = 0; i < n; i++) { |
|
|
|
outs.push({ |
|
|
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', |
|
|
|
amount: 0.01 |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
var ret = Transaction.createAndSign(utxos, outs, testdata.dataUnspentSign.keyStrings, opts); |
|
|
|
var tx = ret.tx; |
|
|
|
ret = Transaction.createAndSign(utxos, outs, testdata.dataUnspentSign.keyStrings, opts); |
|
|
|
tx = ret.tx; |
|
|
|
tx.getSize().should.equal(3485); |
|
|
|
|
|
|
|
// ins = 1.0101 BTC (1 inputs: 1.0101);
|
|
|
@ -297,21 +335,30 @@ describe('Transaction', function() { |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
* Bitcoin core transaction tests |
|
|
|
*/ |
|
|
|
// Verify that known valid transactions are intepretted correctly
|
|
|
|
testdata.dataTxValid.forEach(function(datum) { |
|
|
|
var testTx = parse_test_transaction(datum); |
|
|
|
if (!testTx) return; |
|
|
|
var verifyP2SH = datum[2]; |
|
|
|
var transactionString = buffertools.toHex( |
|
|
|
testTx.transaction.serialize()); |
|
|
|
|
|
|
|
it.skip('valid tx=' + transactionString, function() { |
|
|
|
it('valid tx=' + transactionString, function() { |
|
|
|
// Verify that all inputs are valid
|
|
|
|
testTx.inputs.forEach(function(input) { |
|
|
|
testTx.transaction.verifyInput(input.index, input.scriptPubKey, |
|
|
|
testTx.transaction.verifyInput( |
|
|
|
input.index, |
|
|
|
input.scriptPubKey, |
|
|
|
{ verifyP2SH: verifyP2SH, dontVerifyStrictEnc: true}, |
|
|
|
function(err, results) { |
|
|
|
// Exceptions raised inside this function will be handled
|
|
|
|
// ...by this function, so ignore if that is the case
|
|
|
|
if (err && err.constructor.name === "AssertionError") return; |
|
|
|
if (err && err.constructor.name === 'AssertionError') return; |
|
|
|
|
|
|
|
should.not.exist(err); |
|
|
|
should.exist(results); |
|
|
@ -328,14 +375,14 @@ describe('Transaction', function() { |
|
|
|
var transactionString = buffertools.toHex( |
|
|
|
testTx.transaction.serialize()); |
|
|
|
|
|
|
|
it.skip('valid tx=' + transactionString, function() { |
|
|
|
it('valid tx=' + transactionString, function() { |
|
|
|
// Verify that all inputs are invalid
|
|
|
|
testTx.inputs.forEach(function(input) { |
|
|
|
testTx.transaction.verifyInput(input.index, input.scriptPubKey, |
|
|
|
function(err, results) { |
|
|
|
// Exceptions raised inside this function will be handled
|
|
|
|
// ...by this function, so ignore if that is the case
|
|
|
|
if (err && err.constructor.name === "AssertionError") return; |
|
|
|
if (err && err.constructor.name === 'AssertionError') return; |
|
|
|
|
|
|
|
// There should either be an error, or the results should be false.
|
|
|
|
(err !== null || (!err && results === false)).should.equal(true); |
|
|
|