|
@ -11,8 +11,12 @@ var Parser = imports.Parser || require('./util/BinaryParser'); |
|
|
var Step = imports.Step || require('step'); |
|
|
var Step = imports.Step || require('step'); |
|
|
var buffertools = imports.buffertools || require('buffertools'); |
|
|
var buffertools = imports.buffertools || require('buffertools'); |
|
|
var error = imports.error || require('./util/error'); |
|
|
var error = imports.error || require('./util/error'); |
|
|
|
|
|
var networks = imports.networks || require('./networks'); |
|
|
|
|
|
var WalletKey = imports.WalletKey || require('./WalletKey'); |
|
|
|
|
|
var PrivateKey = imports.PrivateKey || require('./PrivateKey'); |
|
|
|
|
|
|
|
|
var COINBASE_OP = Buffer.concat([util.NULL_HASH, new Buffer('FFFFFFFF', 'hex')]); |
|
|
var COINBASE_OP = Buffer.concat([util.NULL_HASH, new Buffer('FFFFFFFF', 'hex')]); |
|
|
|
|
|
var FEE_PER_1000B_SAT = parseInt(0.0001 * util.COIN); |
|
|
|
|
|
|
|
|
function TransactionIn(data) { |
|
|
function TransactionIn(data) { |
|
|
if ("object" !== typeof data) { |
|
|
if ("object" !== typeof data) { |
|
@ -679,6 +683,445 @@ Transaction.prototype.parse = function (parser) { |
|
|
this.calcHash(); |
|
|
this.calcHash(); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
|
* selectUnspent |
|
|
|
|
|
* |
|
|
|
|
|
* Selects some unspent outputs for later usage in tx inputs |
|
|
|
|
|
* |
|
|
|
|
|
* @utxos |
|
|
|
|
|
* @totalNeededAmount: output transaction amount in BTC, including fee |
|
|
|
|
|
* @allowUnconfirmed: false (allow selecting unconfirmed utxos) |
|
|
|
|
|
* |
|
|
|
|
|
* Note that the sum of the selected unspent is >= the desired amount. |
|
|
|
|
|
* Returns the selected unspent outputs if the totalNeededAmount was reach. |
|
|
|
|
|
* 'null' if not. |
|
|
|
|
|
* |
|
|
|
|
|
* TODO: utxo selection is not optimized to minimize mempool usage. |
|
|
|
|
|
* |
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
Transaction.selectUnspent = function (utxos, totalNeededAmount, allowUnconfirmed) { |
|
|
|
|
|
|
|
|
|
|
|
var minConfirmationSteps = [6,1]; |
|
|
|
|
|
if (allowUnconfirmed) minConfirmationSteps.push(0); |
|
|
|
|
|
|
|
|
|
|
|
var ret = []; |
|
|
|
|
|
var l = utxos.length; |
|
|
|
|
|
var totalSat = bignum(0); |
|
|
|
|
|
var totalNeededAmountSat = util.parseValue(totalNeededAmount); |
|
|
|
|
|
var fulfill = false; |
|
|
|
|
|
var maxConfirmations = null; |
|
|
|
|
|
|
|
|
|
|
|
do { |
|
|
|
|
|
var minConfirmations = minConfirmationSteps.shift(); |
|
|
|
|
|
for(var i = 0; i<l; i++) { |
|
|
|
|
|
var u = 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); |
|
|
|
|
|
ret.push(u); |
|
|
|
|
|
if(totalSat.cmp(totalNeededAmountSat) >= 0) { |
|
|
|
|
|
fulfill = true; |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
maxConfirmations = minConfirmations; |
|
|
|
|
|
} while( !fulfill && minConfirmationSteps.length); |
|
|
|
|
|
|
|
|
|
|
|
//TODO(?): sort ret 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.
|
|
|
|
|
|
|
|
|
|
|
|
return fulfill ? ret : null; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
|
* _scriptForAddress |
|
|
|
|
|
* |
|
|
|
|
|
* Returns a scriptPubKey for the given address type |
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
Transaction._scriptForAddress = function (addressString) { |
|
|
|
|
|
|
|
|
|
|
|
var livenet = networks.livenet; |
|
|
|
|
|
var testnet = networks.testnet; |
|
|
|
|
|
var address = new Address(addressString); |
|
|
|
|
|
|
|
|
|
|
|
var version = address.version(); |
|
|
|
|
|
var script; |
|
|
|
|
|
if (version == livenet.addressPubkey || version == testnet.addressPubkey) |
|
|
|
|
|
script = Script.createPubKeyHashOut(address.payload()); |
|
|
|
|
|
else if (version == livenet.addressScript || version == testnet.addressScript) |
|
|
|
|
|
script = Script.createP2SH(address.payload()); |
|
|
|
|
|
else |
|
|
|
|
|
throw new Error('invalid output address'); |
|
|
|
|
|
|
|
|
|
|
|
return script; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Transaction._sumOutputs = function(outs) { |
|
|
|
|
|
var valueOutSat = bignum(0); |
|
|
|
|
|
var l = outs.length; |
|
|
|
|
|
|
|
|
|
|
|
for(var i=0;i<outs.length;i++) { |
|
|
|
|
|
var sat = outs[i].amountSat || util.parseValue(outs[i].amount); |
|
|
|
|
|
valueOutSat = valueOutSat.add(sat); |
|
|
|
|
|
} |
|
|
|
|
|
return valueOutSat; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
|
* createWithFee |
|
|
|
|
|
* Create a TX given ins (selected already), outs, and a FIXED fee |
|
|
|
|
|
* details on the input on .create |
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
Transaction.createWithFee = function (ins, outs, feeSat, opts) { |
|
|
|
|
|
opts = opts || {}; |
|
|
|
|
|
feeSat = feeSat || 0; |
|
|
|
|
|
|
|
|
|
|
|
var txobj = {}; |
|
|
|
|
|
txobj.version = 1; |
|
|
|
|
|
txobj.lock_time = opts.lockTime || 0; |
|
|
|
|
|
txobj.ins = []; |
|
|
|
|
|
txobj.outs = []; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var l = ins.length; |
|
|
|
|
|
var valueInSat = bignum(0); |
|
|
|
|
|
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]); |
|
|
|
|
|
txobj.ins.push(txin); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var valueOutSat = Transaction._sumOutputs(outs); |
|
|
|
|
|
valueOutSat = valueOutSat.add(feeSat); |
|
|
|
|
|
|
|
|
|
|
|
if (valueInSat.cmp(valueOutSat)<0) { |
|
|
|
|
|
var inv = valueInSat.toString(); |
|
|
|
|
|
var ouv = valueOutSat.toString(); |
|
|
|
|
|
throw new Error('transaction input amount is less than outputs: ' + |
|
|
|
|
|
inv + ' < '+ouv + ' [SAT]'); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for(var i=0;i<outs.length;i++) { |
|
|
|
|
|
var amountSat = outs[i].amountSat || util.parseValue(outs[i].amount); |
|
|
|
|
|
var value = util.bigIntToValue(amountSat); |
|
|
|
|
|
var script = Transaction._scriptForAddress(outs[i].address); |
|
|
|
|
|
var txout = { |
|
|
|
|
|
v: value, |
|
|
|
|
|
s: script.getBuffer(), |
|
|
|
|
|
}; |
|
|
|
|
|
txobj.outs.push(txout); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// add remainder (without modifiying outs[])
|
|
|
|
|
|
var remainderSat = valueInSat.sub(valueOutSat); |
|
|
|
|
|
if (remainderSat.cmp(0)>0) { |
|
|
|
|
|
var remainderAddress = opts.remainderAddress || ins[0].address; |
|
|
|
|
|
var value = util.bigIntToValue(remainderSat); |
|
|
|
|
|
var script = Transaction._scriptForAddress(remainderAddress); |
|
|
|
|
|
var txout = { |
|
|
|
|
|
v: value, |
|
|
|
|
|
s: script.getBuffer(), |
|
|
|
|
|
}; |
|
|
|
|
|
txobj.outs.push(txout); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return new Transaction(txobj); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Transaction.prototype.calcSize = function () { |
|
|
|
|
|
var totalSize = 8; // version + lock_time
|
|
|
|
|
|
totalSize += util.getVarIntSize(this.ins.length); // tx_in count
|
|
|
|
|
|
this.ins.forEach(function (txin) { |
|
|
|
|
|
totalSize += 36 + util.getVarIntSize(txin.s.length) + |
|
|
|
|
|
txin.s.length + 4; // outpoint + script_len + script + sequence
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
totalSize += util.getVarIntSize(this.outs.length); |
|
|
|
|
|
this.outs.forEach(function (txout) { |
|
|
|
|
|
totalSize += util.getVarIntSize(txout.s.length) + |
|
|
|
|
|
txout.s.length + 8; // script_len + script + value
|
|
|
|
|
|
}); |
|
|
|
|
|
this.size = totalSize; |
|
|
|
|
|
return totalSize; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Transaction.prototype.getSize = function getHash() { |
|
|
|
|
|
if (!this.size) { |
|
|
|
|
|
this.size = this.calcSize(); |
|
|
|
|
|
} |
|
|
|
|
|
return this.size; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Transaction.prototype.isComplete = function () { |
|
|
|
|
|
var l = this.ins.length; |
|
|
|
|
|
|
|
|
|
|
|
var ret = true; |
|
|
|
|
|
for (var i=0; i<l; i++) { |
|
|
|
|
|
if ( buffertools.compare(this.ins[i].s,util.EMPTY_BUFFER)===0 ) { |
|
|
|
|
|
ret = false; |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
return ret; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
|
* sign |
|
|
|
|
|
* |
|
|
|
|
|
* signs the transaction |
|
|
|
|
|
* |
|
|
|
|
|
* @ utxos |
|
|
|
|
|
* @keypairs |
|
|
|
|
|
* @opts |
|
|
|
|
|
* signhash: Transaction.SIGHASH_ALL |
|
|
|
|
|
* |
|
|
|
|
|
* Return the 'completeness' status of the tx (i.e, if all inputs are signed). |
|
|
|
|
|
* |
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
Transaction.prototype.sign = function (selectedUtxos, keys, opts) { |
|
|
|
|
|
var self = this; |
|
|
|
|
|
var complete = false; |
|
|
|
|
|
var m = keys.length; |
|
|
|
|
|
opts = opts || {}; |
|
|
|
|
|
var signhash = opts.signhash || SIGHASH_ALL; |
|
|
|
|
|
|
|
|
|
|
|
if (selectedUtxos.length !== self.ins.length) |
|
|
|
|
|
throw new Error('given selectedUtxos do not match tx inputs'); |
|
|
|
|
|
|
|
|
|
|
|
var inputMap = []; |
|
|
|
|
|
var l = selectedUtxos.length; |
|
|
|
|
|
for(var i=0; i<l; i++) { |
|
|
|
|
|
inputMap[i]= { |
|
|
|
|
|
address: selectedUtxos[i].address, |
|
|
|
|
|
scriptPubKey: selectedUtxos[i].scriptPubKey |
|
|
|
|
|
}; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//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; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var inputSigned = 0; |
|
|
|
|
|
l = self.ins.length; |
|
|
|
|
|
for(var i=0;i<l;i++) { |
|
|
|
|
|
var aIn = self.ins[i]; |
|
|
|
|
|
var wk = walletKeyMap[inputMap[i].address]; |
|
|
|
|
|
|
|
|
|
|
|
if (typeof wk === 'undefined') { |
|
|
|
|
|
if ( buffertools.compare(aIn.s,util.EMPTY_BUFFER)!==0 ) |
|
|
|
|
|
inputSigned++; |
|
|
|
|
|
continue; |
|
|
|
|
|
} |
|
|
|
|
|
var scriptBuf = new Buffer(inputMap[i].scriptPubKey, 'hex'); |
|
|
|
|
|
var s = new Script(scriptBuf); |
|
|
|
|
|
if (s.classify() !== Script.TX_PUBKEYHASH) { |
|
|
|
|
|
throw new Error('input:'+i+' script type:'+ s.getRawOutType() +' not supported yet'); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var txSigHash = self.hashForSignature(s, i, signhash); |
|
|
|
|
|
|
|
|
|
|
|
var sigRaw; |
|
|
|
|
|
var triesLeft = 10; |
|
|
|
|
|
do { |
|
|
|
|
|
sigRaw = wk.privKey.signSync(txSigHash); |
|
|
|
|
|
} while ( wk.privKey.verifySignatureSync(txSigHash, sigRaw) === false && triesLeft-- ); |
|
|
|
|
|
|
|
|
|
|
|
if (!triesLeft) { |
|
|
|
|
|
log.debug('could not sign input:'+i +' verification failed'); |
|
|
|
|
|
continue; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var sigType = new Buffer(1); |
|
|
|
|
|
sigType[0] = signhash; |
|
|
|
|
|
var sig = Buffer.concat([sigRaw, sigType]); |
|
|
|
|
|
|
|
|
|
|
|
var scriptSig = new Script(); |
|
|
|
|
|
scriptSig.chunks.push(sig); |
|
|
|
|
|
scriptSig.chunks.push(wk.privKey.public); |
|
|
|
|
|
scriptSig.updateBuffer(); |
|
|
|
|
|
self.ins[i].s = scriptSig.getBuffer(); |
|
|
|
|
|
inputSigned++; |
|
|
|
|
|
} |
|
|
|
|
|
var complete = inputSigned === l; |
|
|
|
|
|
return complete; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
|
* create |
|
|
|
|
|
* |
|
|
|
|
|
* creates a transaction without signing it. |
|
|
|
|
|
* |
|
|
|
|
|
* @utxos |
|
|
|
|
|
* @outs |
|
|
|
|
|
* @opts |
|
|
|
|
|
* |
|
|
|
|
|
* See createAndSign for documentation on the inputs |
|
|
|
|
|
* |
|
|
|
|
|
* Returns: |
|
|
|
|
|
* { tx: {}, selectedUtxos: []} |
|
|
|
|
|
* see createAndSign for details |
|
|
|
|
|
* |
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
Transaction.create = function (utxos, outs, opts) { |
|
|
|
|
|
|
|
|
|
|
|
//starting size estimation
|
|
|
|
|
|
var size = 500; |
|
|
|
|
|
var opts = opts || {}; |
|
|
|
|
|
|
|
|
|
|
|
var givenFeeSat; |
|
|
|
|
|
if (opts.fee || opts.feeSat) { |
|
|
|
|
|
givenFeeSat = opts.fee ? opts.fee * util.COIN : opts.feeSat; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var selectedUtxos; |
|
|
|
|
|
do { |
|
|
|
|
|
// based on https://en.bitcoin.it/wiki/Transaction_fees
|
|
|
|
|
|
maxSizeK = parseInt(size/1000) + 1; |
|
|
|
|
|
var feeSat = givenFeeSat |
|
|
|
|
|
? givenFeeSat : maxSizeK * FEE_PER_1000B_SAT ; |
|
|
|
|
|
|
|
|
|
|
|
var valueOutSat = Transaction |
|
|
|
|
|
._sumOutputs(outs) |
|
|
|
|
|
.add(feeSat); |
|
|
|
|
|
|
|
|
|
|
|
selectedUtxos = Transaction |
|
|
|
|
|
.selectUnspent(utxos,valueOutSat / util.COIN, opts.allowUnconfirmed); |
|
|
|
|
|
|
|
|
|
|
|
if (!selectedUtxos) { |
|
|
|
|
|
throw new Error( |
|
|
|
|
|
'the given UTXOs dont sum up the given outputs: ' |
|
|
|
|
|
+ valueOutSat.toString() |
|
|
|
|
|
+ ' (fee is ' + feeSat |
|
|
|
|
|
+ ' )SAT' |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
var tx = Transaction.createWithFee(selectedUtxos, outs, feeSat, { |
|
|
|
|
|
remainderAddress: opts.remainderAddress, |
|
|
|
|
|
lockTime: opts.lockTime, |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
size = tx.getSize(); |
|
|
|
|
|
} while (size > (maxSizeK+1)*1000 ); |
|
|
|
|
|
|
|
|
|
|
|
return {tx: tx, selectedUtxos: selectedUtxos}; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
|
* createAndSign |
|
|
|
|
|
* |
|
|
|
|
|
* creates and signs a transaction |
|
|
|
|
|
* |
|
|
|
|
|
* @utxos |
|
|
|
|
|
* 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 |
|
|
|
|
|
* { |
|
|
|
|
|
* remainderAddress: null, |
|
|
|
|
|
* fee: 0.001, |
|
|
|
|
|
* lockTime: null, |
|
|
|
|
|
* allowUnconfirmed: false, |
|
|
|
|
|
* signhash: SIGHASH_ALL |
|
|
|
|
|
* } |
|
|
|
|
|
* |
|
|
|
|
|
* |
|
|
|
|
|
* Retuns: |
|
|
|
|
|
* { |
|
|
|
|
|
* tx: The new created transaction, |
|
|
|
|
|
* selectedUtxos: The UTXOs selected as inputs for this transaction |
|
|
|
|
|
* } |
|
|
|
|
|
* |
|
|
|
|
|
* Amounts are in BTC. instead of fee and amount; feeSat and amountSat can be given, |
|
|
|
|
|
* repectively, to provide amounts in satoshis. |
|
|
|
|
|
* |
|
|
|
|
|
* If no remainderAddress is given, and there are remainder coins, the |
|
|
|
|
|
* first IN address will be used to return the coins. (TODO: is this is reasonable?) |
|
|
|
|
|
* |
|
|
|
|
|
* The Transaction creation is handled in 2 steps: |
|
|
|
|
|
* .create |
|
|
|
|
|
* .selectUnspent |
|
|
|
|
|
* .createWithFee |
|
|
|
|
|
* .sign |
|
|
|
|
|
* |
|
|
|
|
|
* If you need just to create a TX and not sign it, use .create |
|
|
|
|
|
* |
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
Transaction.createAndSign = function (utxos, outs, keys, opts) { |
|
|
|
|
|
var ret = Transaction.create(utxos, outs, opts); |
|
|
|
|
|
ret.tx.sign(ret.selectedUtxos, keys); |
|
|
|
|
|
return ret; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
var TransactionInputsCache = exports.TransactionInputsCache = |
|
|
var TransactionInputsCache = exports.TransactionInputsCache = |
|
|
function TransactionInputsCache(tx) |
|
|
function TransactionInputsCache(tx) |
|
|
{ |
|
|
{ |
|
|