|
|
@ -64,7 +64,7 @@ TransactionIn.prototype.getOutpointHash = function getOutpointHash() { |
|
|
|
}; |
|
|
|
|
|
|
|
TransactionIn.prototype.getOutpointIndex = function getOutpointIndex() { |
|
|
|
return (this.o[32] ) + |
|
|
|
return (this.o[32]) + |
|
|
|
(this.o[33] << 8) + |
|
|
|
(this.o[34] << 16) + |
|
|
|
(this.o[35] << 24); |
|
|
@ -106,14 +106,14 @@ function Transaction(data) { |
|
|
|
this.hash = data.hash || null; |
|
|
|
this.version = data.version; |
|
|
|
this.lock_time = data.lock_time; |
|
|
|
this.ins = Array.isArray(data.ins) ? data.ins.map(function (data) { |
|
|
|
this.ins = Array.isArray(data.ins) ? data.ins.map(function(data) { |
|
|
|
var txin = new TransactionIn(); |
|
|
|
txin.s = data.s; |
|
|
|
txin.q = data.q; |
|
|
|
txin.o = data.o; |
|
|
|
return txin; |
|
|
|
}) : []; |
|
|
|
this.outs = Array.isArray(data.outs) ? data.outs.map(function (data) { |
|
|
|
this.outs = Array.isArray(data.outs) ? data.outs.map(function(data) { |
|
|
|
var txout = new TransactionOut(); |
|
|
|
txout.v = data.v; |
|
|
|
txout.s = data.s; |
|
|
@ -125,7 +125,7 @@ this.class = Transaction; |
|
|
|
Transaction.In = TransactionIn; |
|
|
|
Transaction.Out = TransactionOut; |
|
|
|
|
|
|
|
Transaction.prototype.isCoinBase = function () { |
|
|
|
Transaction.prototype.isCoinBase = function() { |
|
|
|
return this.ins.length == 1 && this.ins[0].isCoinBase(); |
|
|
|
}; |
|
|
|
|
|
|
@ -152,12 +152,12 @@ Transaction.prototype.serialize = function serialize() { |
|
|
|
bufs.push(buf); |
|
|
|
|
|
|
|
bufs.push(util.varIntBuf(this.ins.length)); |
|
|
|
this.ins.forEach(function (txin) { |
|
|
|
this.ins.forEach(function(txin) { |
|
|
|
bufs.push(txin.serialize()); |
|
|
|
}); |
|
|
|
|
|
|
|
bufs.push(util.varIntBuf(this.outs.length)); |
|
|
|
this.outs.forEach(function (txout) { |
|
|
|
this.outs.forEach(function(txout) { |
|
|
|
bufs.push(txout.serialize()); |
|
|
|
}); |
|
|
|
|
|
|
@ -204,7 +204,7 @@ Transaction.prototype.inputs = function inputs() { |
|
|
|
} |
|
|
|
|
|
|
|
return res; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
* Load and cache transaction inputs. |
|
|
@ -218,7 +218,7 @@ Transaction.prototype.inputs = function inputs() { |
|
|
|
* @param {Function} callback Function to call on completion. |
|
|
|
*/ |
|
|
|
Transaction.prototype.cacheInputs = |
|
|
|
function cacheInputs(blockChain, txStore, wait, callback) { |
|
|
|
function cacheInputs(blockChain, txStore, wait, callback) { |
|
|
|
var self = this; |
|
|
|
|
|
|
|
var txCache = new TransactionInputsCache(this); |
|
|
@ -254,22 +254,22 @@ Transaction.prototype.verify = function verify(txCache, blockChain, callback) { |
|
|
|
var txout = fromTxOuts[outIndex]; |
|
|
|
|
|
|
|
if (!txout) { |
|
|
|
throw new Error("Source output index "+outIndex+ |
|
|
|
" for input "+n+" out of bounds"); |
|
|
|
throw new Error("Source output index " + outIndex + |
|
|
|
" for input " + n + " out of bounds"); |
|
|
|
} |
|
|
|
|
|
|
|
return txout; |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
Step( |
|
|
|
function verifyInputs() { |
|
|
|
function verifyInputs(opts) { |
|
|
|
var group = this.group(); |
|
|
|
|
|
|
|
if (self.isCoinBase()) { |
|
|
|
throw new Error("Coinbase tx are invalid unless part of a block"); |
|
|
|
} |
|
|
|
|
|
|
|
self.ins.forEach(function (txin, n) { |
|
|
|
self.ins.forEach(function(txin, n) { |
|
|
|
var txout = getTxOut(txin, n); |
|
|
|
|
|
|
|
// TODO: Verify coinbase maturity
|
|
|
@ -278,7 +278,7 @@ Transaction.prototype.verify = function verify(txCache, blockChain, callback) { |
|
|
|
|
|
|
|
outpoints.push(txin.o); |
|
|
|
|
|
|
|
self.verifyInput(n, txout.getScript(), group()); |
|
|
|
self.verifyInput(n, txout.getScript(), opts, group()); |
|
|
|
}); |
|
|
|
}, |
|
|
|
|
|
|
@ -289,9 +289,9 @@ Transaction.prototype.verify = function verify(txCache, blockChain, callback) { |
|
|
|
if (!results[i]) { |
|
|
|
var txout = getTxOut(self.ins[i]); |
|
|
|
log.debug('Script evaluated to false'); |
|
|
|
log.debug('|- scriptSig', ""+self.ins[i].getScript()); |
|
|
|
log.debug('`- scriptPubKey', ""+txout.getScript()); |
|
|
|
throw new VerificationError('Script for input '+i+' evaluated to false'); |
|
|
|
log.debug('|- scriptSig', "" + self.ins[i].getScript()); |
|
|
|
log.debug('`- scriptPubKey', "" + txout.getScript()); |
|
|
|
throw new VerificationError('Script for input ' + i + ' evaluated to false'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -307,37 +307,34 @@ Transaction.prototype.verify = function verify(txCache, blockChain, callback) { |
|
|
|
function checkConflicts(err, count) { |
|
|
|
if (err) throw err; |
|
|
|
|
|
|
|
self.outs.forEach(function (txout) { |
|
|
|
self.outs.forEach(function(txout) { |
|
|
|
valueOut = valueOut.add(util.valueToBigInt(txout.v)); |
|
|
|
}); |
|
|
|
|
|
|
|
if (valueIn.cmp(valueOut) < 0) { |
|
|
|
var outValue = util.formatValue(valueOut); |
|
|
|
var inValue = util.formatValue(valueIn); |
|
|
|
throw new Error("Tx output value (BTC "+outValue+") "+ |
|
|
|
"exceeds input value (BTC "+inValue+")"); |
|
|
|
throw new Error("Tx output value (BTC " + outValue + ") " + |
|
|
|
"exceeds input value (BTC " + inValue + ")"); |
|
|
|
} |
|
|
|
|
|
|
|
var fees = valueIn.sub(valueOut); |
|
|
|
|
|
|
|
if (count) { |
|
|
|
// Spent output detected, retrieve transaction that spends it
|
|
|
|
blockChain.getConflictingTransactions(outpoints, function (err, results) { |
|
|
|
blockChain.getConflictingTransactions(outpoints, function(err, results) { |
|
|
|
if (results.length) { |
|
|
|
if (buffertools.compare(results[0].getHash(), self.getHash()) === 0) { |
|
|
|
log.warn("Detected tx re-add (recoverable db corruption): " |
|
|
|
+ util.formatHashAlt(results[0].getHash())); |
|
|
|
log.warn("Detected tx re-add (recoverable db corruption): " + util.formatHashAlt(results[0].getHash())); |
|
|
|
// TODO: Needs to return an error for the memory pool case?
|
|
|
|
callback(null, fees); |
|
|
|
} else { |
|
|
|
callback(new Error("At least one referenced output has" |
|
|
|
+ " already been spent in tx " |
|
|
|
+ util.formatHashAlt(results[0].getHash()))); |
|
|
|
callback(new Error("At least one referenced output has" + " already been spent in tx " + util.formatHashAlt(results[0].getHash()))); |
|
|
|
} |
|
|
|
} else { |
|
|
|
callback(new Error("Outputs of this transaction are spent, but "+ |
|
|
|
"the transaction(s) that spend them are not "+ |
|
|
|
"available. This probably means you need to "+ |
|
|
|
callback(new Error("Outputs of this transaction are spent, but " + |
|
|
|
"the transaction(s) that spend them are not " + |
|
|
|
"available. This probably means you need to " + |
|
|
|
"reset your database.")); |
|
|
|
} |
|
|
|
}); |
|
|
@ -351,10 +348,13 @@ Transaction.prototype.verify = function verify(txCache, blockChain, callback) { |
|
|
|
); |
|
|
|
}; |
|
|
|
|
|
|
|
Transaction.prototype.verifyInput = function verifyInput(n, scriptPubKey, callback) { |
|
|
|
return ScriptInterpreter.verify(this.ins[n].getScript(), |
|
|
|
Transaction.prototype.verifyInput = function verifyInput(n, scriptPubKey, opts, callback) { |
|
|
|
var scriptSig = this.ins[n].getScript(); |
|
|
|
return ScriptInterpreter.verifyFull( |
|
|
|
scriptSig, |
|
|
|
scriptPubKey, |
|
|
|
this, n, 0, |
|
|
|
opts, |
|
|
|
callback); |
|
|
|
}; |
|
|
|
|
|
|
@ -372,7 +372,6 @@ Transaction.prototype.getAffectedKeys = function getAffectedKeys(txCache) { |
|
|
|
|
|
|
|
// Index any pubkeys affected by the outputs of this transaction
|
|
|
|
for (var i = 0, l = this.outs.length; i < l; i++) { |
|
|
|
try { |
|
|
|
var txout = this.outs[i]; |
|
|
|
var script = txout.getScript(); |
|
|
|
|
|
|
@ -380,18 +379,11 @@ Transaction.prototype.getAffectedKeys = function getAffectedKeys(txCache) { |
|
|
|
if (outPubKey) { |
|
|
|
this.affects.push(outPubKey); |
|
|
|
} |
|
|
|
} catch (err) { |
|
|
|
// It's not our job to validate, so we just ignore any errors and issue
|
|
|
|
// a very low level log message.
|
|
|
|
log.debug("Unable to determine affected pubkeys: " + |
|
|
|
(err.stack ? err.stack : ""+err)); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
// Index any pubkeys affected by the inputs of this transaction
|
|
|
|
var txIndex = txCache.txIndex; |
|
|
|
for (var i = 0, l = this.ins.length; i < l; i++) { |
|
|
|
try { |
|
|
|
var txin = this.ins[i]; |
|
|
|
|
|
|
|
if (txin.isCoinBase()) continue; |
|
|
@ -415,18 +407,12 @@ Transaction.prototype.getAffectedKeys = function getAffectedKeys(txCache) { |
|
|
|
if (outPubKey) { |
|
|
|
this.affects.push(outPubKey); |
|
|
|
} |
|
|
|
} catch (err) { |
|
|
|
// It's not our job to validate, so we just ignore any errors and issue
|
|
|
|
// a very low level log message.
|
|
|
|
log.debug("Unable to determine affected pubkeys: " + |
|
|
|
(err.stack ? err.stack : ""+err)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
var affectedKeys = {}; |
|
|
|
|
|
|
|
this.affects.forEach(function (pubKeyHash) { |
|
|
|
this.affects.forEach(function(pubKeyHash) { |
|
|
|
affectedKeys[pubKeyHash.toString('base64')] = pubKeyHash; |
|
|
|
}); |
|
|
|
|
|
|
@ -440,25 +426,25 @@ var SIGHASH_NONE = 2; |
|
|
|
var SIGHASH_SINGLE = 3; |
|
|
|
var SIGHASH_ANYONECANPAY = 80; |
|
|
|
|
|
|
|
Transaction.SIGHASH_ALL=SIGHASH_ALL; |
|
|
|
Transaction.SIGHASH_NONE=SIGHASH_NONE; |
|
|
|
Transaction.SIGHASH_SINGLE=SIGHASH_SINGLE; |
|
|
|
Transaction.SIGHASH_ANYONECANPAY=SIGHASH_ANYONECANPAY; |
|
|
|
Transaction.SIGHASH_ALL = SIGHASH_ALL; |
|
|
|
Transaction.SIGHASH_NONE = SIGHASH_NONE; |
|
|
|
Transaction.SIGHASH_SINGLE = SIGHASH_SINGLE; |
|
|
|
Transaction.SIGHASH_ANYONECANPAY = SIGHASH_ANYONECANPAY; |
|
|
|
|
|
|
|
Transaction.prototype.hashForSignature = |
|
|
|
function hashForSignature(script, inIndex, hashType) { |
|
|
|
function hashForSignature(script, inIndex, hashType) { |
|
|
|
if (+inIndex !== inIndex || |
|
|
|
inIndex < 0 || inIndex >= this.ins.length) { |
|
|
|
throw new Error("Input index '"+inIndex+"' invalid or out of bounds "+ |
|
|
|
"("+this.ins.length+" inputs)"); |
|
|
|
throw new Error("Input index '" + inIndex + "' invalid or out of bounds " + |
|
|
|
"(" + this.ins.length + " inputs)"); |
|
|
|
} |
|
|
|
|
|
|
|
// Clone transaction
|
|
|
|
var txTmp = new Transaction(); |
|
|
|
this.ins.forEach(function (txin, i) { |
|
|
|
this.ins.forEach(function(txin, i) { |
|
|
|
txTmp.ins.push(new TransactionIn(txin)); |
|
|
|
}); |
|
|
|
this.outs.forEach(function (txout) { |
|
|
|
this.outs.forEach(function(txout) { |
|
|
|
txTmp.outs.push(new TransactionOut(txout)); |
|
|
|
}); |
|
|
|
txTmp.version = this.version; |
|
|
@ -562,7 +548,7 @@ Transaction.prototype.getStandardizedObject = function getStandardizedObject() { |
|
|
|
|
|
|
|
var totalSize = 8; // version + lock_time
|
|
|
|
totalSize += util.getVarIntSize(this.ins.length); // tx_in count
|
|
|
|
var ins = this.ins.map(function (txin) { |
|
|
|
var ins = this.ins.map(function(txin) { |
|
|
|
var txinObj = { |
|
|
|
prev_out: { |
|
|
|
hash: buffertools.reverse(new Buffer(txin.getOutpointHash())).toString('hex'), |
|
|
@ -580,7 +566,7 @@ Transaction.prototype.getStandardizedObject = function getStandardizedObject() { |
|
|
|
}); |
|
|
|
|
|
|
|
totalSize += util.getVarIntSize(this.outs.length); |
|
|
|
var outs = this.outs.map(function (txout) { |
|
|
|
var outs = this.outs.map(function(txout) { |
|
|
|
totalSize += util.getVarIntSize(txout.s.length) + |
|
|
|
txout.s.length + 8; // script_len + script + value
|
|
|
|
return { |
|
|
@ -646,7 +632,7 @@ Transaction.prototype.fromObj = function fromObj(obj) { |
|
|
|
this.outs = txobj.outs; |
|
|
|
} |
|
|
|
|
|
|
|
Transaction.prototype.parse = function (parser) { |
|
|
|
Transaction.prototype.parse = function(parser) { |
|
|
|
if (Buffer.isBuffer(parser)) { |
|
|
|
this._buffer = parser; |
|
|
|
parser = new Parser(parser); |
|
|
@ -702,9 +688,9 @@ Transaction.prototype.parse = function (parser) { |
|
|
|
* |
|
|
|
*/ |
|
|
|
|
|
|
|
Transaction.selectUnspent = function (utxos, totalNeededAmount, allowUnconfirmed) { |
|
|
|
Transaction.selectUnspent = function(utxos, totalNeededAmount, allowUnconfirmed) { |
|
|
|
|
|
|
|
var minConfirmationSteps = [6,1]; |
|
|
|
var minConfirmationSteps = [6, 1]; |
|
|
|
if (allowUnconfirmed) minConfirmationSteps.push(0); |
|
|
|
|
|
|
|
var ret = []; |
|
|
@ -716,25 +702,25 @@ Transaction.selectUnspent = function (utxos, totalNeededAmount, allowUnconfirmed |
|
|
|
|
|
|
|
do { |
|
|
|
var minConfirmations = minConfirmationSteps.shift(); |
|
|
|
for(var i = 0; i<l; i++) { |
|
|
|
for (var i = 0; i < l; i++) { |
|
|
|
var u = utxos[i]; |
|
|
|
|
|
|
|
var c = u.confirmations || 0; |
|
|
|
|
|
|
|
if ( c < minConfirmations || (maxConfirmations && c >=maxConfirmations) ) |
|
|
|
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) { |
|
|
|
if (totalSat.cmp(totalNeededAmountSat) >= 0) { |
|
|
|
fulfill = true; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
maxConfirmations = minConfirmations; |
|
|
|
} while( !fulfill && minConfirmationSteps.length); |
|
|
|
} 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
|
|
|
@ -749,7 +735,7 @@ Transaction.selectUnspent = function (utxos, totalNeededAmount, allowUnconfirmed |
|
|
|
* Returns a scriptPubKey for the given address type |
|
|
|
*/ |
|
|
|
|
|
|
|
Transaction._scriptForAddress = function (addressString) { |
|
|
|
Transaction._scriptForAddress = function(addressString) { |
|
|
|
|
|
|
|
var livenet = networks.livenet; |
|
|
|
var testnet = networks.testnet; |
|
|
@ -771,7 +757,7 @@ Transaction._sumOutputs = function(outs) { |
|
|
|
var valueOutSat = bignum(0); |
|
|
|
var l = outs.length; |
|
|
|
|
|
|
|
for(var i=0;i<outs.length;i++) { |
|
|
|
for (var i = 0; i < outs.length; i++) { |
|
|
|
var sat = outs[i].amountSat || util.parseValue(outs[i].amount); |
|
|
|
valueOutSat = valueOutSat.add(sat); |
|
|
|
} |
|
|
@ -784,7 +770,7 @@ Transaction._sumOutputs = function(outs) { |
|
|
|
* details on the input on .create |
|
|
|
*/ |
|
|
|
|
|
|
|
Transaction.createWithFee = function (ins, outs, feeSat, opts) { |
|
|
|
Transaction.createWithFee = function(ins, outs, feeSat, opts) { |
|
|
|
opts = opts || {}; |
|
|
|
feeSat = feeSat || 0; |
|
|
|
|
|
|
@ -797,7 +783,7 @@ Transaction.createWithFee = function (ins, outs, feeSat, opts) { |
|
|
|
|
|
|
|
var l = ins.length; |
|
|
|
var valueInSat = bignum(0); |
|
|
|
for(var i=0; i<l; i++) { |
|
|
|
for (var i = 0; i < l; i++) { |
|
|
|
valueInSat = valueInSat.add(util.parseValue(ins[i].amount)); |
|
|
|
|
|
|
|
var txin = {}; |
|
|
@ -818,14 +804,14 @@ Transaction.createWithFee = function (ins, outs, feeSat, opts) { |
|
|
|
var valueOutSat = Transaction._sumOutputs(outs); |
|
|
|
valueOutSat = valueOutSat.add(feeSat); |
|
|
|
|
|
|
|
if (valueInSat.cmp(valueOutSat)<0) { |
|
|
|
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]'); |
|
|
|
inv + ' < ' + ouv + ' [SAT]'); |
|
|
|
} |
|
|
|
|
|
|
|
for(var i=0;i<outs.length;i++) { |
|
|
|
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); |
|
|
@ -838,7 +824,7 @@ Transaction.createWithFee = function (ins, outs, feeSat, opts) { |
|
|
|
|
|
|
|
// add remainder (without modifiying outs[])
|
|
|
|
var remainderSat = valueInSat.sub(valueOutSat); |
|
|
|
if (remainderSat.cmp(0)>0) { |
|
|
|
if (remainderSat.cmp(0) > 0) { |
|
|
|
var remainderAddress = opts.remainderAddress || ins[0].address; |
|
|
|
var value = util.bigIntToValue(remainderSat); |
|
|
|
var script = Transaction._scriptForAddress(remainderAddress); |
|
|
@ -853,16 +839,16 @@ Transaction.createWithFee = function (ins, outs, feeSat, opts) { |
|
|
|
return new Transaction(txobj); |
|
|
|
}; |
|
|
|
|
|
|
|
Transaction.prototype.calcSize = function () { |
|
|
|
Transaction.prototype.calcSize = function() { |
|
|
|
var totalSize = 8; // version + lock_time
|
|
|
|
totalSize += util.getVarIntSize(this.ins.length); // tx_in count
|
|
|
|
this.ins.forEach(function (txin) { |
|
|
|
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) { |
|
|
|
this.outs.forEach(function(txout) { |
|
|
|
totalSize += util.getVarIntSize(txout.s.length) + |
|
|
|
txout.s.length + 8; // script_len + script + value
|
|
|
|
}); |
|
|
@ -878,12 +864,12 @@ Transaction.prototype.getSize = function getHash() { |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Transaction.prototype.isComplete = function () { |
|
|
|
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 ) { |
|
|
|
for (var i = 0; i < l; i++) { |
|
|
|
if (buffertools.compare(this.ins[i].s, util.EMPTY_BUFFER) === 0) { |
|
|
|
ret = false; |
|
|
|
break; |
|
|
|
} |
|
|
@ -906,7 +892,7 @@ Transaction.prototype.isComplete = function () { |
|
|
|
* |
|
|
|
*/ |
|
|
|
|
|
|
|
Transaction.prototype.sign = function (selectedUtxos, keys, opts) { |
|
|
|
Transaction.prototype.sign = function(selectedUtxos, keys, opts) { |
|
|
|
var self = this; |
|
|
|
var complete = false; |
|
|
|
var m = keys.length; |
|
|
@ -918,8 +904,8 @@ Transaction.prototype.sign = function (selectedUtxos, keys, opts) { |
|
|
|
|
|
|
|
var inputMap = []; |
|
|
|
var l = selectedUtxos.length; |
|
|
|
for(var i=0; i<l; i++) { |
|
|
|
inputMap[i]= { |
|
|
|
for (var i = 0; i < l; i++) { |
|
|
|
inputMap[i] = { |
|
|
|
address: selectedUtxos[i].address, |
|
|
|
scriptPubKey: selectedUtxos[i].scriptPubKey |
|
|
|
}; |
|
|
@ -929,18 +915,20 @@ Transaction.prototype.sign = function (selectedUtxos, keys, opts) { |
|
|
|
var walletKeyMap = {}; |
|
|
|
var l = keys.length; |
|
|
|
var wk; |
|
|
|
for(var i=0; i<l; i++) { |
|
|
|
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 = new WalletKey({ |
|
|
|
network: pk.network() |
|
|
|
}); |
|
|
|
wk.fromObj({ |
|
|
|
priv: k |
|
|
|
}); |
|
|
|
} else if (k instanceof WalletKey) { |
|
|
|
wk = k; |
|
|
|
} |
|
|
|
else { |
|
|
|
} else { |
|
|
|
throw new Error('argument must be an array of strings (WIF format) or WalletKey objects'); |
|
|
|
} |
|
|
|
walletKeyMap[wk.storeObj().addr] = wk; |
|
|
@ -948,19 +936,19 @@ Transaction.prototype.sign = function (selectedUtxos, keys, opts) { |
|
|
|
|
|
|
|
var inputSigned = 0; |
|
|
|
l = self.ins.length; |
|
|
|
for(var i=0;i<l;i++) { |
|
|
|
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 ) |
|
|
|
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'); |
|
|
|
throw new Error('input:' + i + ' script type:' + s.getRawOutType() + ' not supported yet'); |
|
|
|
} |
|
|
|
|
|
|
|
var txSigHash = self.hashForSignature(s, i, signhash); |
|
|
@ -969,10 +957,10 @@ Transaction.prototype.sign = function (selectedUtxos, keys, opts) { |
|
|
|
var triesLeft = 10; |
|
|
|
do { |
|
|
|
sigRaw = wk.privKey.signSync(txSigHash); |
|
|
|
} while ( wk.privKey.verifySignatureSync(txSigHash, sigRaw) === false && triesLeft-- ); |
|
|
|
} while (wk.privKey.verifySignatureSync(txSigHash, sigRaw) === false && triesLeft--); |
|
|
|
|
|
|
|
if (!triesLeft) { |
|
|
|
log.debug('could not sign input:'+i +' verification failed'); |
|
|
|
log.debug('could not sign input:' + i + ' verification failed'); |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
@ -1008,7 +996,7 @@ Transaction.prototype.sign = function (selectedUtxos, keys, opts) { |
|
|
|
* |
|
|
|
*/ |
|
|
|
|
|
|
|
Transaction.create = function (utxos, outs, opts) { |
|
|
|
Transaction.create = function(utxos, outs, opts) { |
|
|
|
|
|
|
|
//starting size estimation
|
|
|
|
var size = 500; |
|
|
@ -1022,23 +1010,19 @@ Transaction.create = function (utxos, outs, opts) { |
|
|
|
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 ; |
|
|
|
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); |
|
|
|
.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' |
|
|
|
'the given UTXOs dont sum up the given outputs: ' + valueOutSat.toString() + ' (fee is ' + feeSat + ' )SAT' |
|
|
|
); |
|
|
|
} |
|
|
|
var tx = Transaction.createWithFee(selectedUtxos, outs, feeSat, { |
|
|
@ -1047,9 +1031,12 @@ Transaction.create = function (utxos, outs, opts) { |
|
|
|
}); |
|
|
|
|
|
|
|
size = tx.getSize(); |
|
|
|
} while (size > (maxSizeK+1)*1000 ); |
|
|
|
} while (size > (maxSizeK + 1) * 1000); |
|
|
|
|
|
|
|
return {tx: tx, selectedUtxos: selectedUtxos}; |
|
|
|
return { |
|
|
|
tx: tx, |
|
|
|
selectedUtxos: selectedUtxos |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@ -1116,21 +1103,20 @@ Transaction.create = function (utxos, outs, opts) { |
|
|
|
* |
|
|
|
*/ |
|
|
|
|
|
|
|
Transaction.createAndSign = function (utxos, outs, keys, opts) { |
|
|
|
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 = |
|
|
|
function TransactionInputsCache(tx) |
|
|
|
{ |
|
|
|
function TransactionInputsCache(tx) { |
|
|
|
var txList = []; |
|
|
|
var txList64 = []; |
|
|
|
var reqOuts = {}; |
|
|
|
|
|
|
|
// Get list of transactions required for verification
|
|
|
|
tx.ins.forEach(function (txin) { |
|
|
|
tx.ins.forEach(function(txin) { |
|
|
|
if (txin.isCoinBase()) return; |
|
|
|
|
|
|
|
var hash = txin.o.slice(0, 32); |
|
|
@ -1153,8 +1139,7 @@ function TransactionInputsCache(tx) |
|
|
|
this.callbacks = []; |
|
|
|
}; |
|
|
|
|
|
|
|
TransactionInputsCache.prototype.buffer = function buffer(blockChain, txStore, wait, callback) |
|
|
|
{ |
|
|
|
TransactionInputsCache.prototype.buffer = function buffer(blockChain, txStore, wait, callback) { |
|
|
|
var self = this; |
|
|
|
|
|
|
|
var complete = false; |
|
|
@ -1164,7 +1149,7 @@ TransactionInputsCache.prototype.buffer = function buffer(blockChain, txStore, w |
|
|
|
} |
|
|
|
|
|
|
|
var missingTx = {}; |
|
|
|
self.txList64.forEach(function (hash64) { |
|
|
|
self.txList64.forEach(function(hash64) { |
|
|
|
missingTx[hash64] = true; |
|
|
|
}); |
|
|
|
|
|
|
@ -1173,10 +1158,10 @@ TransactionInputsCache.prototype.buffer = function buffer(blockChain, txStore, w |
|
|
|
if (err) throw err; |
|
|
|
|
|
|
|
// Index memory transactions
|
|
|
|
txs.forEach(function (tx) { |
|
|
|
txs.forEach(function(tx) { |
|
|
|
var hash64 = tx.getHash().toString('base64'); |
|
|
|
var obj = {}; |
|
|
|
Object.keys(self.requiredOuts[hash64]).forEach(function (o) { |
|
|
|
Object.keys(self.requiredOuts[hash64]).forEach(function(o) { |
|
|
|
obj[+o] = tx.outs[+o]; |
|
|
|
}); |
|
|
|
self.txIndex[hash64] = obj; |
|
|
@ -1203,7 +1188,7 @@ TransactionInputsCache.prototype.buffer = function buffer(blockChain, txStore, w |
|
|
|
// TODO: Major speedup should be possible if we load only the outs and not
|
|
|
|
// whole transactions.
|
|
|
|
var callback = this; |
|
|
|
blockChain.getOutputsByHashes(self.txList, function (err, result) { |
|
|
|
blockChain.getOutputsByHashes(self.txList, function(err, result) { |
|
|
|
callback(err, result); |
|
|
|
}); |
|
|
|
}, |
|
|
@ -1213,7 +1198,7 @@ TransactionInputsCache.prototype.buffer = function buffer(blockChain, txStore, w |
|
|
|
|
|
|
|
var missingTxDbg = ''; |
|
|
|
if (Object.keys(missingTx).length) { |
|
|
|
missingTxDbg = Object.keys(missingTx).map(function (hash64) { |
|
|
|
missingTxDbg = Object.keys(missingTx).map(function(hash64) { |
|
|
|
return util.formatHash(new Buffer(hash64, 'base64')); |
|
|
|
}).join(','); |
|
|
|
} |
|
|
@ -1221,11 +1206,10 @@ TransactionInputsCache.prototype.buffer = function buffer(blockChain, txStore, w |
|
|
|
if (wait && Object.keys(missingTx).length) { |
|
|
|
// TODO: This might no longer be needed now that saveTransactions uses
|
|
|
|
// the safe=true option.
|
|
|
|
setTimeout(function () { |
|
|
|
setTimeout(function() { |
|
|
|
var missingHashes = Object.keys(missingTx); |
|
|
|
if (missingHashes.length) { |
|
|
|
self.callback(new Error('Missing inputs (timeout while searching): ' |
|
|
|
+ missingTxDbg)); |
|
|
|
self.callback(new Error('Missing inputs (timeout while searching): ' + missingTxDbg)); |
|
|
|
} else if (!complete) { |
|
|
|
self.callback(new Error('Callback failed to trigger')); |
|
|
|
} |
|
|
@ -1240,8 +1224,7 @@ TransactionInputsCache.prototype.buffer = function buffer(blockChain, txStore, w |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
TransactionInputsCache.prototype.callback = function callback(err) |
|
|
|
{ |
|
|
|
TransactionInputsCache.prototype.callback = function callback(err) { |
|
|
|
var args = Array.prototype.slice.apply(arguments); |
|
|
|
|
|
|
|
// Empty the callback array first (because downstream functions could add new
|
|
|
@ -1249,14 +1232,9 @@ TransactionInputsCache.prototype.callback = function callback(err) |
|
|
|
var cbs = this.callbacks; |
|
|
|
this.callbacks = []; |
|
|
|
|
|
|
|
try { |
|
|
|
cbs.forEach(function (cb) { |
|
|
|
cbs.forEach(function(cb) { |
|
|
|
cb.apply(null, args); |
|
|
|
}); |
|
|
|
} catch (err) { |
|
|
|
log.err("Callback error after connecting tx inputs: "+ |
|
|
|
(err.stack ? err.stack : err.toString())); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
module.exports = require('soop')(Transaction); |
|
|
|