diff --git a/TransactionBuilder.js b/TransactionBuilder.js index ebb3a6f..9e741ab 100644 --- a/TransactionBuilder.js +++ b/TransactionBuilder.js @@ -94,12 +94,7 @@ var FEE_PER_1000B_SAT = parseInt(0.0001 * util.COIN); function TransactionBuilder(opts) { opts = opts || {}; - this.txobj = {}; - this.txobj.version = 1; - this.txobj.lock_time = opts.lockTime || 0; - this.txobj.ins = []; - this.txobj.outs = []; - + this.lockTime = opts.lockTime || 0; this.spendUnconfirmed = opts.spendUnconfirmed || false; if (opts.fee || opts.feeSat) { @@ -265,12 +260,12 @@ TransactionBuilder.prototype._selectUnspent = function(neededAmountSat) { return this; }; -TransactionBuilder.prototype._setInputs = function() { +TransactionBuilder.prototype._setInputs = function(txobj) { var ins = this.selectedUtxos; var l = ins.length; var valueInSat = bignum(0); - this.txobj.ins=[]; + txobj.ins=[]; for (var i = 0; i < l; i++) { valueInSat = valueInSat.add(util.parseValue(ins[i].amount)); @@ -286,7 +281,7 @@ TransactionBuilder.prototype._setInputs = function() { voutBuf.writeUInt32LE(vout, 0); txin.o = Buffer.concat([hashReversed, voutBuf]); - this.txobj.ins.push(txin); + txobj.ins.push(txin); } this.valueInSat = valueInSat; return this; @@ -309,7 +304,7 @@ TransactionBuilder.prototype._setFee = function(feeSat) { return this; }; -TransactionBuilder.prototype._setRemainder = function(remainderIndex) { +TransactionBuilder.prototype._setRemainder = function(txobj, remainderIndex) { if ( typeof this.valueInSat === 'undefined' || typeof this.valueOutSat === 'undefined') @@ -317,12 +312,12 @@ TransactionBuilder.prototype._setRemainder = function(remainderIndex) { // add remainder (without modifying outs[]) var remainderSat = this.valueInSat.sub(this.valueOutSat).sub(this.feeSat); - var l =this.txobj.outs.length; + var l =txobj.outs.length; this.remainderSat = bignum(0); //remove old remainder? if (l > remainderIndex) { - this.txobj.outs.pop(); + txobj.outs.pop(); } if (remainderSat.cmp(0) > 0) { @@ -333,18 +328,17 @@ TransactionBuilder.prototype._setRemainder = function(remainderIndex) { v: value, s: script.getBuffer(), }; - this.txobj.outs.push(txout); + txobj.outs.push(txout); this.remainderSat = remainderSat; } return this; }; -TransactionBuilder.prototype._setFeeAndRemainder = function() { +TransactionBuilder.prototype._setFeeAndRemainder = function(txobj) { //starting size estimation - var size = 500, maxSizeK, remainderIndex = this.txobj.outs.length; - + var size = 500, maxSizeK, remainderIndex = txobj.outs.length; do { // based on https://en.bitcoin.it/wiki/Transaction_fees maxSizeK = parseInt(size / 1000) + 1; @@ -355,12 +349,12 @@ TransactionBuilder.prototype._setFeeAndRemainder = function() { var neededAmountSat = this.valueOutSat.add(feeSat); this._selectUnspent(neededAmountSat) - ._setInputs() + ._setInputs(txobj) ._setFee(feeSat) - ._setRemainder(remainderIndex); + ._setRemainder(txobj, remainderIndex); - size = new Transaction(this.txobj).getSize(); + size = new Transaction(txobj).getSize(); } while (size > (maxSizeK + 1) * 1000); return this; }; @@ -368,9 +362,13 @@ TransactionBuilder.prototype._setFeeAndRemainder = function() { TransactionBuilder.prototype.setOutputs = function(outs) { var valueOutSat = bignum(0); - this.txobj.outs = []; - var l =outs.length; + var txobj = {}; + txobj.version = 1; + txobj.lock_time = this.lockTime || 0; + txobj.ins = []; + txobj.outs = []; + var l =outs.length; for (var i = 0; i < l; i++) { var amountSat = outs[i].amountSat || util.parseValue(outs[i].amount); var value = util.bigIntToValue(amountSat); @@ -379,7 +377,7 @@ TransactionBuilder.prototype.setOutputs = function(outs) { v: value, s: script.getBuffer(), }; - this.txobj.outs.push(txout); + txobj.outs.push(txout); var sat = outs[i].amountSat || util.parseValue(outs[i].amount); valueOutSat = valueOutSat.add(sat); @@ -387,9 +385,9 @@ TransactionBuilder.prototype.setOutputs = function(outs) { this.valueOutSat = valueOutSat; - this._setFeeAndRemainder(); + this._setFeeAndRemainder(txobj); - this.tx = new Transaction(this.txobj); + this.tx = new Transaction(txobj); return this; }; @@ -506,12 +504,12 @@ TransactionBuilder.prototype._signPubKeyHash = function(walletKeyMap, input, txS }; // FOR TESTING -// var _dumpChunks = function (scriptSig, label) { -// console.log('## DUMP: ' + label + ' ##'); -// for(var i=0; i this fails: no way to check signatures, since PRIV Keys are not stored + b = getBuilder3([{ + address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', + amount: 16 + }]) + .sign(testdata.dataUnspentSign.keyStrings); + // merge simple + b2 = getBuilder3([{ + address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', + amount: 16 + }]) + .sign(testdata.dataUnspentSign.keyStrings); + (function() {b2.merge(b);}).should.throw(); + b2.merge(b, true); + }); + + it('#merge p2sh/steps', function() { + var b = getP2shBuilder(1); + var k1 = testdata.dataUnspentSign.keyStringsP2sh.slice(0,1); + var k2 = testdata.dataUnspentSign.keyStringsP2sh.slice(1,2); + var k3 = testdata.dataUnspentSign.keyStringsP2sh.slice(2,3); + b.isFullySigned().should.equal(false); + b.signaturesAdded.should.equal(0); + b.sign(k1); + b.signaturesAdded.should.equal(1); + b.isFullySigned().should.equal(false); + var tx = b.build(); + tx.isComplete().should.equal(false); + + var b2 = getP2shBuilder(1); + b2.sign(k2); + b2.signaturesAdded.should.equal(1); + b2.merge(b); + b2.signaturesAdded.should.equal(2); + tx = b2.build(); + tx.isComplete().should.equal(false); + + var b3 = getP2shBuilder(1); + b3.sign(k3); + b3.signaturesAdded.should.equal(1); + b3.merge(b2); + b3.signaturesAdded.should.equal(3); + tx = b3.build(); + tx.isComplete().should.equal(true); + + b2.merge(b3); + b2.signaturesAdded.should.equal(3); + tx = b2.build(); + tx.isComplete().should.equal(true); + }); });