bitcore/lib/TransactionBuilder.js

722 lines
20 KiB
JavaScript
Raw Normal View History

2014-03-28 17:17:34 -07:00
/*
2014-03-29 00:01:32 -07:00
var tx = (new TransactionBuilder(opts))
2014-03-28 17:17:34 -07:00
.setUnspent(utxos)
.setOutputs(outs)
.sign(keys)
.build();
2014-03-29 00:01:32 -07:00
var builder = (new TransactionBuilder(opts))
2014-03-28 17:17:34 -07:00
.setUnspent(spent)
.setOutputs(outs);
// Uncomplete tx (no signed or partially signed)
var tx = builder.build();
..later..
builder.sign(keys);
while ( builder.isFullySigned() ) {
... get new keys ...
builder.sign(keys);
}
var tx = builder.build();
broadcast(tx.serialize());
To get selected unspent outputs:
var selectedUnspent = builder.getSelectedUnspent();
@unspent
* unspent outputs array (UTXO), using the following format:
* [{
* address: "mqSjTad2TKbPcKQ3Jq4kgCkKatyN44UMgZ",
* hash: "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1",
* scriptPubKey: "76a9146ce4e1163eb18939b1440c42844d5f0261c0338288ac",
* vout: 1,
* amount: 0.01,
* confirmations: 3
* }, ...
* ]
* This is compatible con insight's utxo API.
* That amount is in BTCs (as returned in insight and bitcoind).
* amountSat (instead of amount) can be given to provide amount in satochis.
*
* @outs
* an array of [{
* address: xx,
* amount:0.001
* },...]
*
* @keys
* an array of strings representing private keys to sign the
* transaction in WIF private key format OR WalletKey objects
*
* @opts
* {
* remainderOut: null,
2014-03-28 17:17:34 -07:00
* fee: 0.001,
* lockTime: null,
* spendUnconfirmed: false,
* signhash: SIGHASH_ALL
* }
* Amounts are in BTC. instead of fee and amount; feeSat and amountSat can be given,
* repectively, to provide amounts in satoshis.
*
* If no remainderOut is given, and there are remainder coins, the
* first IN out will be used to return the coins. remainderOut has the form:
* remainderOut = { address: 1xxxxx}
* or
* remainderOut = { pubkeys: ['hex1','hex2',...} for multisig
*
2014-03-28 17:17:34 -07:00
*
*/
'use strict';
var imports = require('soop').imports();
var Address = imports.Address || require('./Address');
var Script = imports.Script || require('./Script');
var util = imports.util || require('./util/util');
var bignum = imports.bignum || require('bignum');
var buffertools = imports.buffertools || require('buffertools');
var networks = imports.networks || require('./networks');
var WalletKey = imports.WalletKey || require('./WalletKey');
var PrivateKey = imports.PrivateKey || require('./PrivateKey');
var Transaction = imports.Transaction || require('./Transaction');
var FEE_PER_1000B_SAT = parseInt(0.0001 * util.COIN);
2014-03-29 00:01:32 -07:00
function TransactionBuilder(opts) {
2014-03-31 10:41:27 -07:00
opts = opts || {};
2014-03-29 00:01:32 -07:00
this.txobj = {};
this.txobj.version = 1;
this.txobj.lock_time = opts.lockTime || 0;
this.txobj.ins = [];
this.txobj.outs = [];
this.spendUnconfirmed = opts.spendUnconfirmed || false;
if (opts.fee || opts.feeSat) {
this.givenFeeSat = opts.fee ? opts.fee * util.COIN : opts.feeSat;
}
this.remainderOut = opts.remainderOut;
2014-03-29 00:01:32 -07:00
this.signhash = opts.signhash || Transaction.SIGHASH_ALL;
this.tx = {};
this.inputsSigned= 0;
2014-04-10 15:43:28 -07:00
this.signaturesAdded= 0;
2014-03-29 00:01:32 -07:00
return this;
2014-03-28 17:17:34 -07:00
}
/*
2014-04-01 20:59:26 -07:00
* scriptForAddress
2014-03-28 17:17:34 -07:00
*
* Returns a scriptPubKey for the given address type
*/
2014-04-01 20:59:26 -07:00
TransactionBuilder.scriptForAddress = function(addressString) {
2014-03-28 17:17:34 -07:00
var livenet = networks.livenet;
var testnet = networks.testnet;
var address = new Address(addressString);
var version = address.version();
var script;
2014-04-07 14:30:49 -07:00
if (version === livenet.addressVersion || version === testnet.addressVersion)
2014-03-28 17:17:34 -07:00
script = Script.createPubKeyHashOut(address.payload());
2014-04-07 14:30:49 -07:00
else if (version === livenet.P2SHVersion || version === testnet.P2SHVersion)
2014-03-28 17:17:34 -07:00
script = Script.createP2SH(address.payload());
else
throw new Error('invalid output address');
return script;
};
TransactionBuilder._scriptForPubkeys = function(out) {
var l = out.pubkeys.length;
var pubKeyBuf=[];
for (var i=0; i<l; i++) {
pubKeyBuf.push(new Buffer(out.pubkeys[i],'hex'));
}
return Script.createMultisig(out.nreq, pubKeyBuf);
};
TransactionBuilder._scriptForOut = function(out) {
2014-03-31 20:07:45 -07:00
var ret;
if (out.address)
2014-04-01 20:59:26 -07:00
ret = this.scriptForAddress(out.address);
else if (out.pubkeys || out.nreq || out.nreq > 1)
ret = this._scriptForPubkeys(out);
2014-03-31 20:07:45 -07:00
else
2014-03-31 12:25:43 -07:00
throw new Error('unknown out type');
return ret;
};
2014-03-31 20:07:45 -07:00
TransactionBuilder.infoForP2sh = function(opts, networkName) {
var script = this._scriptForOut(opts);
var hash = util.sha256ripe160(script.getBuffer());
var version = networkName === 'testnet' ?
2014-04-07 14:30:49 -07:00
networks.testnet.P2SHVersion : networks.livenet.P2SHVersion;
2014-03-31 20:07:45 -07:00
var addr = new Address(version, hash);
var addrStr = addr.as('base58');
return {
script: script,
scriptBufHex: script.getBuffer().toString('hex'),
hash: hash,
address: addrStr,
};
};
2014-03-28 17:17:34 -07:00
TransactionBuilder.prototype.setUnspent = function(utxos) {
this.utxos = utxos;
return this;
};
TransactionBuilder.prototype._setInputMap = function() {
var inputMap = [];
var l = this.selectedUtxos.length;
for (var i = 0; i < l; i++) {
2014-03-31 20:07:45 -07:00
var utxo = this.selectedUtxos[i];
2014-03-31 10:41:27 -07:00
var scriptBuf = new Buffer(utxo.scriptPubKey, 'hex');
var scriptPubKey = new Script(scriptBuf);
var scriptType = scriptPubKey.classify();
if (scriptType === Script.TX_UNKNOWN)
throw new Error('unkown output type at:' + i +
' Type:' + scriptPubKey.getRawOutType());
2014-03-28 17:17:34 -07:00
inputMap.push({
2014-04-01 20:59:26 -07:00
address: utxo.address,
2014-03-31 10:41:27 -07:00
scriptPubKey: scriptPubKey,
scriptType: scriptType,
i: i,
2014-03-28 17:17:34 -07:00
});
}
this.inputMap = inputMap;
return this;
};
2014-03-31 10:41:27 -07:00
TransactionBuilder.prototype.getSelectedUnspent = function() {
2014-03-28 17:17:34 -07:00
return this.selectedUtxos;
};
/* _selectUnspent
* TODO(?): sort sel (at the end) and check is some inputs can be avoided.
* If the initial utxos are sorted, this step would be necesary only if
* utxos were selected from different minConfirmationSteps.
*/
TransactionBuilder.prototype._selectUnspent = function(neededAmountSat) {
if (!this.utxos || !this.utxos.length)
throw new Error('unspent not set');
var minConfirmationSteps = [6, 1];
if (this.spendUnconfirmed) minConfirmationSteps.push(0);
var sel = [],
totalSat = bignum(0),
fulfill = false,
maxConfirmations = null,
l = this.utxos.length;
do {
var minConfirmations = minConfirmationSteps.shift();
for (var i = 0; i < l; i++) {
var u = this.utxos[i];
var c = u.confirmations || 0;
if (c < minConfirmations || (maxConfirmations && c >= maxConfirmations))
continue;
var sat = u.amountSat || util.parseValue(u.amount);
totalSat = totalSat.add(sat);
sel.push(u);
if (totalSat.cmp(neededAmountSat) >= 0) {
fulfill = true;
break;
}
}
maxConfirmations = minConfirmations;
} while (!fulfill && minConfirmationSteps.length);
if (!fulfill)
2014-03-31 12:25:43 -07:00
throw new Error('no enough unspent to fulfill totalNeededAmount [SAT]:' +
neededAmountSat);
2014-03-28 17:17:34 -07:00
this.selectedUtxos = sel;
this._setInputMap();
return this;
};
TransactionBuilder.prototype._setInputs = function() {
var ins = this.selectedUtxos;
var l = ins.length;
var valueInSat = bignum(0);
this.txobj.ins=[];
for (var i = 0; i < l; i++) {
valueInSat = valueInSat.add(util.parseValue(ins[i].amount));
var txin = {};
txin.s = util.EMPTY_BUFFER;
txin.q = 0xffffffff;
var hash = new Buffer(ins[i].txid, 'hex');
var hashReversed = buffertools.reverse(hash);
var vout = parseInt(ins[i].vout);
var voutBuf = new Buffer(4);
voutBuf.writeUInt32LE(vout, 0);
txin.o = Buffer.concat([hashReversed, voutBuf]);
this.txobj.ins.push(txin);
}
this.valueInSat = valueInSat;
return this;
};
TransactionBuilder.prototype._setFee = function(feeSat) {
if ( typeof this.valueOutSat === 'undefined')
throw new Error('valueOutSat undefined');
var valueOutSat = this.valueOutSat.add(feeSat);
if (this.valueInSat.cmp(valueOutSat) < 0) {
var inv = this.valueInSat.toString();
var ouv = valueOutSat.toString();
throw new Error('transaction input amount is less than outputs: ' +
inv + ' < ' + ouv + ' [SAT]');
}
this.feeSat = feeSat;
return this;
};
TransactionBuilder.prototype._setRemainder = function(remainderIndex) {
if ( typeof this.valueInSat === 'undefined' ||
typeof this.valueOutSat === 'undefined')
throw new Error('valueInSat / valueOutSat undefined');
// add remainder (without modifying outs[])
var remainderSat = this.valueInSat.sub(this.valueOutSat).sub(this.feeSat);
var l =this.txobj.outs.length;
this.remainderSat = bignum(0);
//remove old remainder?
if (l > remainderIndex) {
this.txobj.outs.pop();
}
if (remainderSat.cmp(0) > 0) {
var remainderOut = this.remainderOut || this.selectedUtxos[0];
2014-03-28 17:17:34 -07:00
var value = util.bigIntToValue(remainderSat);
var script = TransactionBuilder._scriptForOut(remainderOut);
2014-03-28 17:17:34 -07:00
var txout = {
v: value,
s: script.getBuffer(),
};
this.txobj.outs.push(txout);
this.remainderSat = remainderSat;
}
return this;
};
TransactionBuilder.prototype._setFeeAndRemainder = function() {
//starting size estimation
var size = 500, maxSizeK, remainderIndex = this.txobj.outs.length;
do {
// based on https://en.bitcoin.it/wiki/Transaction_fees
maxSizeK = parseInt(size / 1000) + 1;
var feeSat = this.givenFeeSat ?
this.givenFeeSat : maxSizeK * FEE_PER_1000B_SAT;
var neededAmountSat = this.valueOutSat.add(feeSat);
this._selectUnspent(neededAmountSat)
._setInputs()
._setFee(feeSat)
._setRemainder(remainderIndex);
size = new Transaction(this.txobj).getSize();
} while (size > (maxSizeK + 1) * 1000);
return this;
};
TransactionBuilder.prototype.setOutputs = function(outs) {
var valueOutSat = bignum(0);
this.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);
var script = TransactionBuilder._scriptForOut(outs[i]);
2014-03-28 17:17:34 -07:00
var txout = {
v: value,
s: script.getBuffer(),
};
this.txobj.outs.push(txout);
var sat = outs[i].amountSat || util.parseValue(outs[i].amount);
valueOutSat = valueOutSat.add(sat);
}
this.valueOutSat = valueOutSat;
this._setFeeAndRemainder();
this.tx = new Transaction(this.txobj);
return this;
};
TransactionBuilder._mapKeys = function(keys) {
//prepare keys
var walletKeyMap = {};
var l = keys.length;
var wk;
for (var i = 0; i < l; i++) {
var k = keys[i];
if (typeof k === 'string') {
var pk = new PrivateKey(k);
wk = new WalletKey({ network: pk.network() });
wk.fromObj({ priv: k });
}
else if (k instanceof WalletKey) {
wk = k;
}
else {
throw new Error('argument must be an array of strings (WIF format) or WalletKey objects');
}
walletKeyMap[wk.storeObj().addr] = wk;
}
return walletKeyMap;
};
TransactionBuilder._signHashAndVerify = function(wk, txSigHash) {
var triesLeft = 10, sigRaw;
do {
sigRaw = wk.privKey.signSync(txSigHash);
} while (wk.privKey.verifySignatureSync(txSigHash, sigRaw) === false &&
triesLeft--);
if (triesLeft<0)
throw new Error('could not sign input: verification failed');
return sigRaw;
};
TransactionBuilder.prototype._checkTx = function() {
if (! this.tx || !this.tx.ins.length || !this.tx.outs.length)
throw new Error('tx is not defined');
};
2014-04-01 20:59:26 -07:00
TransactionBuilder.prototype._multiFindKey = function(walletKeyMap,pubKeyHash) {
var wk;
[ networks.livenet, networks.testnet].forEach(function(n) {
2014-04-07 14:30:49 -07:00
[ n.addressVersion, n.P2SHVersion].forEach(function(v) {
2014-04-01 20:59:26 -07:00
var a = new Address(v,pubKeyHash);
if (!wk && walletKeyMap[a]) {
wk = walletKeyMap[a];
}
});
});
return wk;
};
TransactionBuilder.prototype._findWalletKey = function(walletKeyMap, input) {
var wk;
if (input.address) {
wk = walletKeyMap[input.address];
}
else if (input.pubKeyHash) {
wk = this._multiFindKey(walletKeyMap, input.pubKeyHash);
}
else if (input.pubKeyBuf) {
var pubKeyHash = util.sha256ripe160(input.pubKeyBuf);
wk = this._multiFindKey(walletKeyMap, pubKeyHash);
} else {
throw new Error('no infomation at input to find keys');
}
return wk;
};
2014-03-31 10:41:27 -07:00
TransactionBuilder.prototype._signPubKey = function(walletKeyMap, input, txSigHash) {
if (this.tx.ins[input.i].s.length > 0) return {};
2014-04-01 20:59:26 -07:00
var wk = this._findWalletKey(walletKeyMap, input);
2014-03-31 10:41:27 -07:00
if (!wk) return;
var sigRaw = TransactionBuilder._signHashAndVerify(wk, txSigHash);
var sigType = new Buffer(1);
sigType[0] = this.signhash;
var sig = Buffer.concat([sigRaw, sigType]);
var scriptSig = new Script();
scriptSig.chunks.push(sig);
scriptSig.updateBuffer();
2014-04-10 15:43:28 -07:00
return {isFullySigned: true, signaturesAdded: 1, script: scriptSig.getBuffer()};
2014-03-31 10:41:27 -07:00
};
TransactionBuilder.prototype._signPubKeyHash = function(walletKeyMap, input, txSigHash) {
if (this.tx.ins[input.i].s.length > 0) return {};
2014-04-01 20:59:26 -07:00
var wk = this._findWalletKey(walletKeyMap, input);
2014-03-31 10:41:27 -07:00
if (!wk) return;
var sigRaw = TransactionBuilder._signHashAndVerify(wk, txSigHash);
var sigType = new Buffer(1);
sigType[0] = this.signhash;
var sig = Buffer.concat([sigRaw, sigType]);
var scriptSig = new Script();
scriptSig.chunks.push(sig);
scriptSig.chunks.push(wk.privKey.public);
scriptSig.updateBuffer();
2014-04-10 15:43:28 -07:00
return {isFullySigned: true, signaturesAdded: 1, script: scriptSig.getBuffer()};
2014-03-31 10:41:27 -07:00
};
// FOR TESTING
2014-04-01 20:59:26 -07:00
// var _dumpChunks = function (scriptSig, label) {
// console.log('## DUMP: ' + label + ' ##');
// for(var i=0; i<scriptSig.chunks.length; i++) {
// console.log('\tCHUNK ', i, scriptSig.chunks[i]);
// }
// };
2014-03-31 10:41:27 -07:00
TransactionBuilder.prototype._initMultiSig = function(scriptSig, nreq) {
var wasUpdated = false;
if (scriptSig.chunks.length < nreq + 1) {
wasUpdated = true;
scriptSig.writeN(0);
while (scriptSig.chunks.length <= nreq)
scriptSig.chunks.push(util.EMPTY_BUFFER);
}
return wasUpdated;
};
TransactionBuilder.prototype._isSignedWithKey = function(wk, scriptSig, txSigHash, nreq) {
var ret=0;
for(var i=1; i<=nreq; i++) {
var chunk = scriptSig.chunks[i];
if (chunk ===0 || chunk.length === 0) continue;
var sigRaw = new Buffer(chunk.slice(0,chunk.length-1));
if(wk.privKey.verifySignatureSync(txSigHash, sigRaw) === true) {
ret=true;
}
}
return ret;
};
TransactionBuilder.prototype._chunkIsEmpty = function(chunk) {
return chunk === 0 || // when serializing and back, EMPTY_BUFFER becomes 0
buffertools.compare(chunk, util.EMPTY_BUFFER) === 0;
};
2014-04-01 20:59:26 -07:00
2014-03-31 10:41:27 -07:00
TransactionBuilder.prototype._updateMultiSig = function(wk, scriptSig, txSigHash, nreq) {
var wasUpdated = this._initMultiSig(scriptSig, nreq);
if (this._isSignedWithKey(wk,scriptSig, txSigHash, nreq))
return null;
// Find an empty slot and sign
for(var i=1; i<=nreq; i++) {
var chunk = scriptSig.chunks[i];
if (!this._chunkIsEmpty(chunk))
continue;
// Add signature
var sigRaw = TransactionBuilder._signHashAndVerify(wk, txSigHash);
var sigType = new Buffer(1);
sigType[0] = this.signhash;
var sig = Buffer.concat([sigRaw, sigType]);
scriptSig.chunks[i] = sig;
scriptSig.updateBuffer();
wasUpdated=true;
break;
}
return wasUpdated ? scriptSig : null;
};
TransactionBuilder.prototype._signMultiSig = function(walletKeyMap, input, txSigHash) {
var pubkeys = input.scriptPubKey.capture(),
nreq = input.scriptPubKey.chunks[0] - 80, //see OP_2-OP_16
l = pubkeys.length,
originalScriptBuf = this.tx.ins[input.i].s;
var scriptSig = new Script (originalScriptBuf);
2014-04-10 15:43:28 -07:00
var signaturesAdded = 0;
2014-03-31 10:41:27 -07:00
2014-04-01 20:59:26 -07:00
for(var j=0; j<l && scriptSig.countMissingSignatures(); j++) {
var wk = this._findWalletKey(walletKeyMap, {pubKeyBuf: pubkeys[j]});
2014-03-31 10:41:27 -07:00
if (!wk) continue;
var newScriptSig = this._updateMultiSig(wk, scriptSig, txSigHash, nreq);
2014-04-01 20:59:26 -07:00
if (newScriptSig) {
2014-03-31 10:41:27 -07:00
scriptSig = newScriptSig;
2014-04-10 15:43:28 -07:00
signaturesAdded++;
2014-04-01 20:59:26 -07:00
}
2014-03-31 10:41:27 -07:00
}
return {
2014-04-01 20:59:26 -07:00
isFullySigned: scriptSig.countMissingSignatures() === 0,
signaturesAdded: signaturesAdded,
2014-03-31 10:41:27 -07:00
script: scriptSig.getBuffer(),
};
};
2014-03-31 20:07:45 -07:00
var fnToSign = {};
2014-04-01 20:59:26 -07:00
TransactionBuilder.prototype._scriptIsAppended = function(script, scriptToAddBuf) {
var len = script.chunks.length;
if (script.chunks[len-1] === undefined)
return false;
if (typeof script.chunks[len-1] === 'number')
return false;
if (buffertools.compare(script.chunks[len-1] , scriptToAddBuf) !==0 )
return false;
return true;
};
TransactionBuilder.prototype._addScript = function(scriptBuf, scriptToAddBuf) {
var s = new Script(scriptBuf);
if (!this._scriptIsAppended(s, scriptToAddBuf)) {
s.chunks.push(scriptToAddBuf);
s.updateBuffer();
}
return s.getBuffer();
};
TransactionBuilder.prototype._getInputForP2sh = function(script, index) {
var scriptType = script.classify();
// pubKeyHash is needed for TX_PUBKEYHASH and TX_PUBKEY to retrieve the keys.
var pubKeyHash;
switch(scriptType) {
case Script.TX_PUBKEYHASH:
pubKeyHash = script.captureOne();
break;
case Script.TX_PUBKEY:
var chunk = script.captureOne();
pubKeyHash = util.sha256ripe160(chunk);
}
return {
i: index,
pubKeyHash: pubKeyHash,
scriptPubKey: script,
scriptType: scriptType,
};
};
2014-03-31 20:07:45 -07:00
TransactionBuilder.prototype._signScriptHash = function(walletKeyMap, input, txSigHash) {
2014-03-31 20:07:45 -07:00
var originalScriptBuf = this.tx.ins[input.i].s;
if (!this.hashToScriptMap)
throw new Error('hashToScriptMap not set');
2014-03-31 20:07:45 -07:00
var scriptHex = this.hashToScriptMap[input.address];
if (!scriptHex) return;
2014-03-31 10:41:27 -07:00
2014-04-01 20:59:26 -07:00
var scriptBuf = new Buffer(scriptHex,'hex');
var script = new Script(scriptBuf);
var scriptType = script.classify();
2014-03-31 10:41:27 -07:00
2014-04-01 20:59:26 -07:00
if (!fnToSign[scriptType] || scriptType === Script.TX_SCRIPTHASH)
throw new Error('dont know how to sign p2sh script type:'+ script.getRawOutType());
2014-03-31 20:07:45 -07:00
2014-04-01 20:59:26 -07:00
var newInput = this._getInputForP2sh(script, input.i);
var newTxSigHash = this.tx.hashForSignature( script, newInput.i, this.signhash);
var ret = fnToSign[scriptType].call(this, walletKeyMap, newInput, newTxSigHash);
2014-03-31 20:07:45 -07:00
2014-04-01 20:59:26 -07:00
if (ret && ret.script && ret.signaturesAdded) {
ret.script = this._addScript(ret.script, scriptBuf);
2014-03-31 20:07:45 -07:00
}
return ret;
};
2014-03-31 10:41:27 -07:00
fnToSign[Script.TX_PUBKEYHASH] = TransactionBuilder.prototype._signPubKeyHash;
fnToSign[Script.TX_PUBKEY] = TransactionBuilder.prototype._signPubKey;
fnToSign[Script.TX_MULTISIG] = TransactionBuilder.prototype._signMultiSig;
fnToSign[Script.TX_SCRIPTHASH] = TransactionBuilder.prototype._signScriptHash;
2014-03-28 17:17:34 -07:00
TransactionBuilder.prototype.sign = function(keys) {
this._checkTx();
var tx = this.tx,
ins = tx.ins,
2014-03-31 10:41:27 -07:00
l = ins.length,
walletKeyMap = TransactionBuilder._mapKeys(keys);
2014-03-28 17:17:34 -07:00
2014-03-31 20:07:45 -07:00
2014-03-28 17:17:34 -07:00
for (var i = 0; i < l; i++) {
2014-03-31 10:41:27 -07:00
var input = this.inputMap[i];
2014-03-28 17:17:34 -07:00
2014-03-31 10:41:27 -07:00
var txSigHash = this.tx.hashForSignature(
input.scriptPubKey, i, this.signhash);
2014-03-28 17:17:34 -07:00
2014-03-31 10:41:27 -07:00
var ret = fnToSign[input.scriptType].call(this, walletKeyMap, input, txSigHash);
if (ret && ret.script) {
2014-04-01 20:59:26 -07:00
tx.ins[i].s = ret.script;
2014-03-31 10:41:27 -07:00
if (ret.isFullySigned) this.inputsSigned++;
2014-04-10 15:43:28 -07:00
if (ret.signaturesAdded) this.signaturesAdded +=ret.signaturesAdded;
2014-03-31 10:41:27 -07:00
}
2014-03-28 17:17:34 -07:00
}
return this;
};
2014-04-01 20:59:26 -07:00
// [ { address:scriptHex }]
2014-03-31 10:41:27 -07:00
TransactionBuilder.prototype.setHashToScriptMap = function(hashToScriptMap) {
this.hashToScriptMap= hashToScriptMap;
2014-03-31 20:07:45 -07:00
return this;
2014-03-31 10:41:27 -07:00
};
2014-03-28 17:17:34 -07:00
TransactionBuilder.prototype.isFullySigned = function() {
return this.inputsSigned === this.tx.ins.length;
};
TransactionBuilder.prototype.build = function() {
this._checkTx();
return this.tx;
};
module.exports = require('soop')(TransactionBuilder);