refactor and fixes for Transaction, ScriptInterpreter, and Key
This commit is contained in:
parent
b227341c12
commit
ba92a6b1df
4
Key.js
4
Key.js
|
@ -78,6 +78,10 @@ if (process.versions) {
|
||||||
return new Buffer(signature);
|
return new Buffer(signature);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
kSpec.prototype.verifySignature = function(hash, sig, callback) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
kSpec.prototype.verifySignatureSync = function(hash, sig) {
|
kSpec.prototype.verifySignatureSync = function(hash, sig) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ var buffertools = imports.buffertools || require('buffertools');
|
||||||
var bignum = imports.bignum || require('bignum');
|
var bignum = imports.bignum || require('bignum');
|
||||||
var Util = imports.Util || require('./util/util');
|
var Util = imports.Util || require('./util/util');
|
||||||
var Script = require('./Script');
|
var Script = require('./Script');
|
||||||
|
var Key = require('./Key');
|
||||||
|
|
||||||
var SIGHASH_ALL = 1;
|
var SIGHASH_ALL = 1;
|
||||||
var SIGHASH_NONE = 2;
|
var SIGHASH_NONE = 2;
|
||||||
|
@ -61,7 +62,6 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
// The execution bit is true if there are no "false" values in the
|
// The execution bit is true if there are no "false" values in the
|
||||||
// execution stack. (A "false" value indicates that we're in the
|
// execution stack. (A "false" value indicates that we're in the
|
||||||
// inactive branch of an if statement.)
|
// inactive branch of an if statement.)
|
||||||
|
@ -411,13 +411,10 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
|
||||||
this.stackPop();
|
this.stackPop();
|
||||||
this.stackPop();
|
this.stackPop();
|
||||||
this.stack.push(new Buffer([value ? 1 : 0]));
|
this.stack.push(new Buffer([value ? 1 : 0]));
|
||||||
console.log(script.toHumanReadable());
|
|
||||||
if (opcode === OP_EQUALVERIFY) {
|
if (opcode === OP_EQUALVERIFY) {
|
||||||
if (value) {
|
if (value) {
|
||||||
this.stackPop();
|
this.stackPop();
|
||||||
} else {
|
} else {
|
||||||
console.log(v1);
|
|
||||||
console.log(v2);
|
|
||||||
throw new Error("OP_EQUALVERIFY negative");
|
throw new Error("OP_EQUALVERIFY negative");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -628,7 +625,6 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
|
||||||
|
|
||||||
// Verify signature
|
// Verify signature
|
||||||
checkSig(sig, pubkey, scriptCode, tx, inIndex, hashType, function(e, result) {
|
checkSig(sig, pubkey, scriptCode, tx, inIndex, hashType, function(e, result) {
|
||||||
try {
|
|
||||||
var success;
|
var success;
|
||||||
|
|
||||||
if (e) {
|
if (e) {
|
||||||
|
@ -653,9 +649,6 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
|
||||||
|
|
||||||
// Run next step
|
// Run next step
|
||||||
executeStep.call(this, cb);
|
executeStep.call(this, cb);
|
||||||
} catch (e) {
|
|
||||||
cb(e);
|
|
||||||
}
|
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
// Note that for asynchronous opcodes we have to return here to prevent
|
// Note that for asynchronous opcodes we have to return here to prevent
|
||||||
|
@ -710,13 +703,11 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
|
||||||
checkMultiSigStep.call(this);
|
checkMultiSigStep.call(this);
|
||||||
|
|
||||||
function checkMultiSigStep() {
|
function checkMultiSigStep() {
|
||||||
try {
|
|
||||||
if (success && sigsCount > 0) {
|
if (success && sigsCount > 0) {
|
||||||
var sig = sigs[isig];
|
var sig = sigs[isig];
|
||||||
var key = keys[ikey];
|
var key = keys[ikey];
|
||||||
|
|
||||||
checkSig(sig, key, scriptCode, tx, inIndex, hashType, function(e, result) {
|
checkSig(sig, key, scriptCode, tx, inIndex, hashType, function(e, result) {
|
||||||
try {
|
|
||||||
if (!e && result) {
|
if (!e && result) {
|
||||||
isig++;
|
isig++;
|
||||||
sigsCount--;
|
sigsCount--;
|
||||||
|
@ -732,9 +723,6 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
|
||||||
}
|
}
|
||||||
|
|
||||||
checkMultiSigStep.call(this);
|
checkMultiSigStep.call(this);
|
||||||
} catch (e) {
|
|
||||||
cb(e);
|
|
||||||
}
|
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
} else {
|
} else {
|
||||||
this.stack.push(new Buffer([success ? 1 : 0]));
|
this.stack.push(new Buffer([success ? 1 : 0]));
|
||||||
|
@ -749,9 +737,6 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
|
||||||
// Run next step
|
// Run next step
|
||||||
executeStep.call(this, cb);
|
executeStep.call(this, cb);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
cb(e);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Note that for asynchronous opcodes we have to return here to prevent
|
// Note that for asynchronous opcodes we have to return here to prevent
|
||||||
|
@ -768,18 +753,13 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run next step
|
// Run next step
|
||||||
if (pc % 100) {
|
if (false && pc % 100) {
|
||||||
// V8 allows for much deeper stacks than Bitcoin's scripting language,
|
// V8 allows for much deeper stacks than Bitcoin's scripting language,
|
||||||
// but just to be safe, we'll reset the stack every 100 steps
|
// but just to be safe, we'll reset the stack every 100 steps
|
||||||
process.nextTick(executeStep.bind(this, cb));
|
process.nextTick(executeStep.bind(this, cb));
|
||||||
} else {
|
} else {
|
||||||
executeStep.call(this, cb);
|
executeStep.call(this, cb);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
log.debug("Script aborted: " +
|
|
||||||
(e.message ? e.message : e));
|
|
||||||
cb(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -849,15 +829,15 @@ ScriptInterpreter.prototype.stackSwap = function stackSwap(a, b) {
|
||||||
* integer. Any longer Buffer is converted to a hex string.
|
* integer. Any longer Buffer is converted to a hex string.
|
||||||
*/
|
*/
|
||||||
ScriptInterpreter.prototype.getPrimitiveStack = function getPrimitiveStack() {
|
ScriptInterpreter.prototype.getPrimitiveStack = function getPrimitiveStack() {
|
||||||
return this.stack.map(function(entry) {
|
return this.stack.map(function(chunk) {
|
||||||
if (entry.length > 2) {
|
if (chunk.length > 2) {
|
||||||
return buffertools.toHex(entry.slice(0));
|
return buffertools.toHex(chunk.slice(0));
|
||||||
}
|
}
|
||||||
var num = bufferSMToInt(entry);
|
var num = bufferSMToInt(chunk);
|
||||||
if (num.cmp(-128) >= 0 && num.cmp(127) <= 0) {
|
if (num.cmp(-128) >= 0 && num.cmp(127) <= 0) {
|
||||||
return num.toNumber();
|
return num.toNumber();
|
||||||
} else {
|
} else {
|
||||||
return buffertools.toHex(entry.slice(0));
|
return buffertools.toHex(chunk.slice(0));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -904,12 +884,7 @@ ScriptInterpreter.verify =
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cast result to bool
|
// Cast result to bool
|
||||||
try {
|
|
||||||
var result = si.getResult();
|
var result = si.getResult();
|
||||||
} catch (err) {
|
|
||||||
callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, result);
|
callback(null, result);
|
||||||
});
|
});
|
||||||
|
@ -992,8 +967,15 @@ ScriptInterpreter.prototype.verifyFull = function(scriptSig, scriptPubKey,
|
||||||
var that = this;
|
var that = this;
|
||||||
this.eval(scriptSig, txTo, nIn, hashType, function(err) {
|
this.eval(scriptSig, txTo, nIn, hashType, function(err) {
|
||||||
if (err) callback(err);
|
if (err) callback(err);
|
||||||
else that.verifyStep2(scriptSig, scriptPubKey, txTo, nIn,
|
else {
|
||||||
|
var e = new Error('dummy');
|
||||||
|
var stack = e.stack.replace(/^[^\(]+?[\n$]/gm, '')
|
||||||
|
.replace(/^\s+at\s+/gm, '')
|
||||||
|
.replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@')
|
||||||
|
.split('\n');
|
||||||
|
that.verifyStep2(scriptSig, scriptPubKey, txTo, nIn,
|
||||||
hashType, callback, siCopy);
|
hashType, callback, siCopy);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -1013,17 +995,13 @@ var checkSig = ScriptInterpreter.checkSig =
|
||||||
}
|
}
|
||||||
sig = sig.slice(0, sig.length - 1);
|
sig = sig.slice(0, sig.length - 1);
|
||||||
|
|
||||||
try {
|
|
||||||
// Signature verification requires a special hash procedure
|
// Signature verification requires a special hash procedure
|
||||||
var hash = tx.hashForSignature(scriptCode, n, hashType);
|
var hash = tx.hashForSignature(scriptCode, n, hashType);
|
||||||
|
|
||||||
// Verify signature
|
// Verify signature
|
||||||
var key = new Util.BitcoinKey();
|
var key = new Key();
|
||||||
key.public = pubkey;
|
key.public = pubkey;
|
||||||
key.verifySignature(hash, sig, callback);
|
key.verifySignature(hash, sig, callback);
|
||||||
} catch (err) {
|
|
||||||
callback(null, false);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ScriptInterpreter.prototype.isCanonicalSignature = function(sig) {
|
ScriptInterpreter.prototype.isCanonicalSignature = function(sig) {
|
||||||
|
|
223
Transaction.js
223
Transaction.js
|
@ -64,7 +64,7 @@ TransactionIn.prototype.getOutpointHash = function getOutpointHash() {
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionIn.prototype.getOutpointIndex = function getOutpointIndex() {
|
TransactionIn.prototype.getOutpointIndex = function getOutpointIndex() {
|
||||||
return (this.o[32] ) +
|
return (this.o[32]) +
|
||||||
(this.o[33] << 8) +
|
(this.o[33] << 8) +
|
||||||
(this.o[34] << 16) +
|
(this.o[34] << 16) +
|
||||||
(this.o[35] << 24);
|
(this.o[35] << 24);
|
||||||
|
@ -106,14 +106,14 @@ function Transaction(data) {
|
||||||
this.hash = data.hash || null;
|
this.hash = data.hash || null;
|
||||||
this.version = data.version;
|
this.version = data.version;
|
||||||
this.lock_time = data.lock_time;
|
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();
|
var txin = new TransactionIn();
|
||||||
txin.s = data.s;
|
txin.s = data.s;
|
||||||
txin.q = data.q;
|
txin.q = data.q;
|
||||||
txin.o = data.o;
|
txin.o = data.o;
|
||||||
return txin;
|
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();
|
var txout = new TransactionOut();
|
||||||
txout.v = data.v;
|
txout.v = data.v;
|
||||||
txout.s = data.s;
|
txout.s = data.s;
|
||||||
|
@ -125,7 +125,7 @@ this.class = Transaction;
|
||||||
Transaction.In = TransactionIn;
|
Transaction.In = TransactionIn;
|
||||||
Transaction.Out = TransactionOut;
|
Transaction.Out = TransactionOut;
|
||||||
|
|
||||||
Transaction.prototype.isCoinBase = function () {
|
Transaction.prototype.isCoinBase = function() {
|
||||||
return this.ins.length == 1 && this.ins[0].isCoinBase();
|
return this.ins.length == 1 && this.ins[0].isCoinBase();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -152,12 +152,12 @@ Transaction.prototype.serialize = function serialize() {
|
||||||
bufs.push(buf);
|
bufs.push(buf);
|
||||||
|
|
||||||
bufs.push(util.varIntBuf(this.ins.length));
|
bufs.push(util.varIntBuf(this.ins.length));
|
||||||
this.ins.forEach(function (txin) {
|
this.ins.forEach(function(txin) {
|
||||||
bufs.push(txin.serialize());
|
bufs.push(txin.serialize());
|
||||||
});
|
});
|
||||||
|
|
||||||
bufs.push(util.varIntBuf(this.outs.length));
|
bufs.push(util.varIntBuf(this.outs.length));
|
||||||
this.outs.forEach(function (txout) {
|
this.outs.forEach(function(txout) {
|
||||||
bufs.push(txout.serialize());
|
bufs.push(txout.serialize());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -204,7 +204,7 @@ Transaction.prototype.inputs = function inputs() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load and cache transaction inputs.
|
* Load and cache transaction inputs.
|
||||||
|
@ -218,7 +218,7 @@ Transaction.prototype.inputs = function inputs() {
|
||||||
* @param {Function} callback Function to call on completion.
|
* @param {Function} callback Function to call on completion.
|
||||||
*/
|
*/
|
||||||
Transaction.prototype.cacheInputs =
|
Transaction.prototype.cacheInputs =
|
||||||
function cacheInputs(blockChain, txStore, wait, callback) {
|
function cacheInputs(blockChain, txStore, wait, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var txCache = new TransactionInputsCache(this);
|
var txCache = new TransactionInputsCache(this);
|
||||||
|
@ -254,8 +254,8 @@ Transaction.prototype.verify = function verify(txCache, blockChain, callback) {
|
||||||
var txout = fromTxOuts[outIndex];
|
var txout = fromTxOuts[outIndex];
|
||||||
|
|
||||||
if (!txout) {
|
if (!txout) {
|
||||||
throw new Error("Source output index "+outIndex+
|
throw new Error("Source output index " + outIndex +
|
||||||
" for input "+n+" out of bounds");
|
" for input " + n + " out of bounds");
|
||||||
}
|
}
|
||||||
|
|
||||||
return txout;
|
return txout;
|
||||||
|
@ -269,7 +269,7 @@ Transaction.prototype.verify = function verify(txCache, blockChain, callback) {
|
||||||
throw new Error("Coinbase tx are invalid unless part of a block");
|
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);
|
var txout = getTxOut(txin, n);
|
||||||
|
|
||||||
// TODO: Verify coinbase maturity
|
// TODO: Verify coinbase maturity
|
||||||
|
@ -289,9 +289,9 @@ Transaction.prototype.verify = function verify(txCache, blockChain, callback) {
|
||||||
if (!results[i]) {
|
if (!results[i]) {
|
||||||
var txout = getTxOut(self.ins[i]);
|
var txout = getTxOut(self.ins[i]);
|
||||||
log.debug('Script evaluated to false');
|
log.debug('Script evaluated to false');
|
||||||
log.debug('|- scriptSig', ""+self.ins[i].getScript());
|
log.debug('|- scriptSig', "" + self.ins[i].getScript());
|
||||||
log.debug('`- scriptPubKey', ""+txout.getScript());
|
log.debug('`- scriptPubKey', "" + txout.getScript());
|
||||||
throw new VerificationError('Script for input '+i+' evaluated to false');
|
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) {
|
function checkConflicts(err, count) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
|
|
||||||
self.outs.forEach(function (txout) {
|
self.outs.forEach(function(txout) {
|
||||||
valueOut = valueOut.add(util.valueToBigInt(txout.v));
|
valueOut = valueOut.add(util.valueToBigInt(txout.v));
|
||||||
});
|
});
|
||||||
|
|
||||||
if (valueIn.cmp(valueOut) < 0) {
|
if (valueIn.cmp(valueOut) < 0) {
|
||||||
var outValue = util.formatValue(valueOut);
|
var outValue = util.formatValue(valueOut);
|
||||||
var inValue = util.formatValue(valueIn);
|
var inValue = util.formatValue(valueIn);
|
||||||
throw new Error("Tx output value (BTC "+outValue+") "+
|
throw new Error("Tx output value (BTC " + outValue + ") " +
|
||||||
"exceeds input value (BTC "+inValue+")");
|
"exceeds input value (BTC " + inValue + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
var fees = valueIn.sub(valueOut);
|
var fees = valueIn.sub(valueOut);
|
||||||
|
|
||||||
if (count) {
|
if (count) {
|
||||||
// Spent output detected, retrieve transaction that spends it
|
// Spent output detected, retrieve transaction that spends it
|
||||||
blockChain.getConflictingTransactions(outpoints, function (err, results) {
|
blockChain.getConflictingTransactions(outpoints, function(err, results) {
|
||||||
if (results.length) {
|
if (results.length) {
|
||||||
if (buffertools.compare(results[0].getHash(), self.getHash()) === 0) {
|
if (buffertools.compare(results[0].getHash(), self.getHash()) === 0) {
|
||||||
log.warn("Detected tx re-add (recoverable db corruption): "
|
log.warn("Detected tx re-add (recoverable db corruption): " + util.formatHashAlt(results[0].getHash()));
|
||||||
+ util.formatHashAlt(results[0].getHash()));
|
|
||||||
// TODO: Needs to return an error for the memory pool case?
|
// TODO: Needs to return an error for the memory pool case?
|
||||||
callback(null, fees);
|
callback(null, fees);
|
||||||
} else {
|
} else {
|
||||||
callback(new Error("At least one referenced output has"
|
callback(new Error("At least one referenced output has" + " already been spent in tx " + util.formatHashAlt(results[0].getHash())));
|
||||||
+ " already been spent in tx "
|
|
||||||
+ util.formatHashAlt(results[0].getHash())));
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
callback(new Error("Outputs of this transaction are spent, but "+
|
callback(new Error("Outputs of this transaction are spent, but " +
|
||||||
"the transaction(s) that spend them are not "+
|
"the transaction(s) that spend them are not " +
|
||||||
"available. This probably means you need to "+
|
"available. This probably means you need to " +
|
||||||
"reset your database."));
|
"reset your database."));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -352,13 +349,13 @@ Transaction.prototype.verify = function verify(txCache, blockChain, callback) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Transaction.prototype.verifyInput = function verifyInput(n, scriptPubKey, opts, callback) {
|
Transaction.prototype.verifyInput = function verifyInput(n, scriptPubKey, opts, callback) {
|
||||||
var valid = ScriptInterpreter.verifyFull(
|
var scriptSig = this.ins[n].getScript();
|
||||||
this.ins[n].getScript(),
|
return ScriptInterpreter.verifyFull(
|
||||||
|
scriptSig,
|
||||||
scriptPubKey,
|
scriptPubKey,
|
||||||
this, n, 0,
|
this, n, 0,
|
||||||
opts,
|
opts,
|
||||||
callback);
|
callback);
|
||||||
return valid;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -375,7 +372,6 @@ Transaction.prototype.getAffectedKeys = function getAffectedKeys(txCache) {
|
||||||
|
|
||||||
// Index any pubkeys affected by the outputs of this transaction
|
// Index any pubkeys affected by the outputs of this transaction
|
||||||
for (var i = 0, l = this.outs.length; i < l; i++) {
|
for (var i = 0, l = this.outs.length; i < l; i++) {
|
||||||
try {
|
|
||||||
var txout = this.outs[i];
|
var txout = this.outs[i];
|
||||||
var script = txout.getScript();
|
var script = txout.getScript();
|
||||||
|
|
||||||
|
@ -383,18 +379,11 @@ Transaction.prototype.getAffectedKeys = function getAffectedKeys(txCache) {
|
||||||
if (outPubKey) {
|
if (outPubKey) {
|
||||||
this.affects.push(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
|
// Index any pubkeys affected by the inputs of this transaction
|
||||||
var txIndex = txCache.txIndex;
|
var txIndex = txCache.txIndex;
|
||||||
for (var i = 0, l = this.ins.length; i < l; i++) {
|
for (var i = 0, l = this.ins.length; i < l; i++) {
|
||||||
try {
|
|
||||||
var txin = this.ins[i];
|
var txin = this.ins[i];
|
||||||
|
|
||||||
if (txin.isCoinBase()) continue;
|
if (txin.isCoinBase()) continue;
|
||||||
|
@ -418,18 +407,12 @@ Transaction.prototype.getAffectedKeys = function getAffectedKeys(txCache) {
|
||||||
if (outPubKey) {
|
if (outPubKey) {
|
||||||
this.affects.push(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 = {};
|
var affectedKeys = {};
|
||||||
|
|
||||||
this.affects.forEach(function (pubKeyHash) {
|
this.affects.forEach(function(pubKeyHash) {
|
||||||
affectedKeys[pubKeyHash.toString('base64')] = pubKeyHash;
|
affectedKeys[pubKeyHash.toString('base64')] = pubKeyHash;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -443,25 +426,25 @@ var SIGHASH_NONE = 2;
|
||||||
var SIGHASH_SINGLE = 3;
|
var SIGHASH_SINGLE = 3;
|
||||||
var SIGHASH_ANYONECANPAY = 80;
|
var SIGHASH_ANYONECANPAY = 80;
|
||||||
|
|
||||||
Transaction.SIGHASH_ALL=SIGHASH_ALL;
|
Transaction.SIGHASH_ALL = SIGHASH_ALL;
|
||||||
Transaction.SIGHASH_NONE=SIGHASH_NONE;
|
Transaction.SIGHASH_NONE = SIGHASH_NONE;
|
||||||
Transaction.SIGHASH_SINGLE=SIGHASH_SINGLE;
|
Transaction.SIGHASH_SINGLE = SIGHASH_SINGLE;
|
||||||
Transaction.SIGHASH_ANYONECANPAY=SIGHASH_ANYONECANPAY;
|
Transaction.SIGHASH_ANYONECANPAY = SIGHASH_ANYONECANPAY;
|
||||||
|
|
||||||
Transaction.prototype.hashForSignature =
|
Transaction.prototype.hashForSignature =
|
||||||
function hashForSignature(script, inIndex, hashType) {
|
function hashForSignature(script, inIndex, hashType) {
|
||||||
if (+inIndex !== inIndex ||
|
if (+inIndex !== inIndex ||
|
||||||
inIndex < 0 || inIndex >= this.ins.length) {
|
inIndex < 0 || inIndex >= this.ins.length) {
|
||||||
throw new Error("Input index '"+inIndex+"' invalid or out of bounds "+
|
throw new Error("Input index '" + inIndex + "' invalid or out of bounds " +
|
||||||
"("+this.ins.length+" inputs)");
|
"(" + this.ins.length + " inputs)");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clone transaction
|
// Clone transaction
|
||||||
var txTmp = new Transaction();
|
var txTmp = new Transaction();
|
||||||
this.ins.forEach(function (txin, i) {
|
this.ins.forEach(function(txin, i) {
|
||||||
txTmp.ins.push(new TransactionIn(txin));
|
txTmp.ins.push(new TransactionIn(txin));
|
||||||
});
|
});
|
||||||
this.outs.forEach(function (txout) {
|
this.outs.forEach(function(txout) {
|
||||||
txTmp.outs.push(new TransactionOut(txout));
|
txTmp.outs.push(new TransactionOut(txout));
|
||||||
});
|
});
|
||||||
txTmp.version = this.version;
|
txTmp.version = this.version;
|
||||||
|
@ -565,7 +548,7 @@ Transaction.prototype.getStandardizedObject = function getStandardizedObject() {
|
||||||
|
|
||||||
var totalSize = 8; // version + lock_time
|
var totalSize = 8; // version + lock_time
|
||||||
totalSize += util.getVarIntSize(this.ins.length); // tx_in count
|
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 = {
|
var txinObj = {
|
||||||
prev_out: {
|
prev_out: {
|
||||||
hash: buffertools.reverse(new Buffer(txin.getOutpointHash())).toString('hex'),
|
hash: buffertools.reverse(new Buffer(txin.getOutpointHash())).toString('hex'),
|
||||||
|
@ -583,7 +566,7 @@ Transaction.prototype.getStandardizedObject = function getStandardizedObject() {
|
||||||
});
|
});
|
||||||
|
|
||||||
totalSize += util.getVarIntSize(this.outs.length);
|
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) +
|
totalSize += util.getVarIntSize(txout.s.length) +
|
||||||
txout.s.length + 8; // script_len + script + value
|
txout.s.length + 8; // script_len + script + value
|
||||||
return {
|
return {
|
||||||
|
@ -649,7 +632,7 @@ Transaction.prototype.fromObj = function fromObj(obj) {
|
||||||
this.outs = txobj.outs;
|
this.outs = txobj.outs;
|
||||||
}
|
}
|
||||||
|
|
||||||
Transaction.prototype.parse = function (parser) {
|
Transaction.prototype.parse = function(parser) {
|
||||||
if (Buffer.isBuffer(parser)) {
|
if (Buffer.isBuffer(parser)) {
|
||||||
this._buffer = parser;
|
this._buffer = parser;
|
||||||
parser = new Parser(parser);
|
parser = new Parser(parser);
|
||||||
|
@ -705,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);
|
if (allowUnconfirmed) minConfirmationSteps.push(0);
|
||||||
|
|
||||||
var ret = [];
|
var ret = [];
|
||||||
|
@ -719,25 +702,25 @@ Transaction.selectUnspent = function (utxos, totalNeededAmount, allowUnconfirmed
|
||||||
|
|
||||||
do {
|
do {
|
||||||
var minConfirmations = minConfirmationSteps.shift();
|
var minConfirmations = minConfirmationSteps.shift();
|
||||||
for(var i = 0; i<l; i++) {
|
for (var i = 0; i < l; i++) {
|
||||||
var u = utxos[i];
|
var u = utxos[i];
|
||||||
|
|
||||||
var c = u.confirmations || 0;
|
var c = u.confirmations || 0;
|
||||||
|
|
||||||
if ( c < minConfirmations || (maxConfirmations && c >=maxConfirmations) )
|
if (c < minConfirmations || (maxConfirmations && c >= maxConfirmations))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
||||||
var sat = u.amountSat || util.parseValue(u.amount);
|
var sat = u.amountSat || util.parseValue(u.amount);
|
||||||
totalSat = totalSat.add(sat);
|
totalSat = totalSat.add(sat);
|
||||||
ret.push(u);
|
ret.push(u);
|
||||||
if(totalSat.cmp(totalNeededAmountSat) >= 0) {
|
if (totalSat.cmp(totalNeededAmountSat) >= 0) {
|
||||||
fulfill = true;
|
fulfill = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
maxConfirmations = minConfirmations;
|
maxConfirmations = minConfirmations;
|
||||||
} while( !fulfill && minConfirmationSteps.length);
|
} while (!fulfill && minConfirmationSteps.length);
|
||||||
|
|
||||||
//TODO(?): sort ret and check is some inputs can be avoided.
|
//TODO(?): sort ret and check is some inputs can be avoided.
|
||||||
//If the initial utxos are sorted, this step would be necesary only if
|
//If the initial utxos are sorted, this step would be necesary only if
|
||||||
|
@ -752,7 +735,7 @@ Transaction.selectUnspent = function (utxos, totalNeededAmount, allowUnconfirmed
|
||||||
* Returns a scriptPubKey for the given address type
|
* Returns a scriptPubKey for the given address type
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Transaction._scriptForAddress = function (addressString) {
|
Transaction._scriptForAddress = function(addressString) {
|
||||||
|
|
||||||
var livenet = networks.livenet;
|
var livenet = networks.livenet;
|
||||||
var testnet = networks.testnet;
|
var testnet = networks.testnet;
|
||||||
|
@ -774,7 +757,7 @@ Transaction._sumOutputs = function(outs) {
|
||||||
var valueOutSat = bignum(0);
|
var valueOutSat = bignum(0);
|
||||||
var l = outs.length;
|
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);
|
var sat = outs[i].amountSat || util.parseValue(outs[i].amount);
|
||||||
valueOutSat = valueOutSat.add(sat);
|
valueOutSat = valueOutSat.add(sat);
|
||||||
}
|
}
|
||||||
|
@ -787,7 +770,7 @@ Transaction._sumOutputs = function(outs) {
|
||||||
* details on the input on .create
|
* details on the input on .create
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Transaction.createWithFee = function (ins, outs, feeSat, opts) {
|
Transaction.createWithFee = function(ins, outs, feeSat, opts) {
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
feeSat = feeSat || 0;
|
feeSat = feeSat || 0;
|
||||||
|
|
||||||
|
@ -800,7 +783,7 @@ Transaction.createWithFee = function (ins, outs, feeSat, opts) {
|
||||||
|
|
||||||
var l = ins.length;
|
var l = ins.length;
|
||||||
var valueInSat = bignum(0);
|
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));
|
valueInSat = valueInSat.add(util.parseValue(ins[i].amount));
|
||||||
|
|
||||||
var txin = {};
|
var txin = {};
|
||||||
|
@ -821,14 +804,14 @@ Transaction.createWithFee = function (ins, outs, feeSat, opts) {
|
||||||
var valueOutSat = Transaction._sumOutputs(outs);
|
var valueOutSat = Transaction._sumOutputs(outs);
|
||||||
valueOutSat = valueOutSat.add(feeSat);
|
valueOutSat = valueOutSat.add(feeSat);
|
||||||
|
|
||||||
if (valueInSat.cmp(valueOutSat)<0) {
|
if (valueInSat.cmp(valueOutSat) < 0) {
|
||||||
var inv = valueInSat.toString();
|
var inv = valueInSat.toString();
|
||||||
var ouv = valueOutSat.toString();
|
var ouv = valueOutSat.toString();
|
||||||
throw new Error('transaction input amount is less than outputs: ' +
|
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 amountSat = outs[i].amountSat || util.parseValue(outs[i].amount);
|
||||||
var value = util.bigIntToValue(amountSat);
|
var value = util.bigIntToValue(amountSat);
|
||||||
var script = Transaction._scriptForAddress(outs[i].address);
|
var script = Transaction._scriptForAddress(outs[i].address);
|
||||||
|
@ -841,7 +824,7 @@ Transaction.createWithFee = function (ins, outs, feeSat, opts) {
|
||||||
|
|
||||||
// add remainder (without modifiying outs[])
|
// add remainder (without modifiying outs[])
|
||||||
var remainderSat = valueInSat.sub(valueOutSat);
|
var remainderSat = valueInSat.sub(valueOutSat);
|
||||||
if (remainderSat.cmp(0)>0) {
|
if (remainderSat.cmp(0) > 0) {
|
||||||
var remainderAddress = opts.remainderAddress || ins[0].address;
|
var remainderAddress = opts.remainderAddress || ins[0].address;
|
||||||
var value = util.bigIntToValue(remainderSat);
|
var value = util.bigIntToValue(remainderSat);
|
||||||
var script = Transaction._scriptForAddress(remainderAddress);
|
var script = Transaction._scriptForAddress(remainderAddress);
|
||||||
|
@ -856,16 +839,16 @@ Transaction.createWithFee = function (ins, outs, feeSat, opts) {
|
||||||
return new Transaction(txobj);
|
return new Transaction(txobj);
|
||||||
};
|
};
|
||||||
|
|
||||||
Transaction.prototype.calcSize = function () {
|
Transaction.prototype.calcSize = function() {
|
||||||
var totalSize = 8; // version + lock_time
|
var totalSize = 8; // version + lock_time
|
||||||
totalSize += util.getVarIntSize(this.ins.length); // tx_in count
|
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) +
|
totalSize += 36 + util.getVarIntSize(txin.s.length) +
|
||||||
txin.s.length + 4; // outpoint + script_len + script + sequence
|
txin.s.length + 4; // outpoint + script_len + script + sequence
|
||||||
});
|
});
|
||||||
|
|
||||||
totalSize += util.getVarIntSize(this.outs.length);
|
totalSize += util.getVarIntSize(this.outs.length);
|
||||||
this.outs.forEach(function (txout) {
|
this.outs.forEach(function(txout) {
|
||||||
totalSize += util.getVarIntSize(txout.s.length) +
|
totalSize += util.getVarIntSize(txout.s.length) +
|
||||||
txout.s.length + 8; // script_len + script + value
|
txout.s.length + 8; // script_len + script + value
|
||||||
});
|
});
|
||||||
|
@ -881,12 +864,12 @@ Transaction.prototype.getSize = function getHash() {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Transaction.prototype.isComplete = function () {
|
Transaction.prototype.isComplete = function() {
|
||||||
var l = this.ins.length;
|
var l = this.ins.length;
|
||||||
|
|
||||||
var ret = true;
|
var ret = true;
|
||||||
for (var i=0; i<l; i++) {
|
for (var i = 0; i < l; i++) {
|
||||||
if ( buffertools.compare(this.ins[i].s,util.EMPTY_BUFFER)===0 ) {
|
if (buffertools.compare(this.ins[i].s, util.EMPTY_BUFFER) === 0) {
|
||||||
ret = false;
|
ret = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -909,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 self = this;
|
||||||
var complete = false;
|
var complete = false;
|
||||||
var m = keys.length;
|
var m = keys.length;
|
||||||
|
@ -921,8 +904,8 @@ Transaction.prototype.sign = function (selectedUtxos, keys, opts) {
|
||||||
|
|
||||||
var inputMap = [];
|
var inputMap = [];
|
||||||
var l = selectedUtxos.length;
|
var l = selectedUtxos.length;
|
||||||
for(var i=0; i<l; i++) {
|
for (var i = 0; i < l; i++) {
|
||||||
inputMap[i]= {
|
inputMap[i] = {
|
||||||
address: selectedUtxos[i].address,
|
address: selectedUtxos[i].address,
|
||||||
scriptPubKey: selectedUtxos[i].scriptPubKey
|
scriptPubKey: selectedUtxos[i].scriptPubKey
|
||||||
};
|
};
|
||||||
|
@ -932,18 +915,20 @@ Transaction.prototype.sign = function (selectedUtxos, keys, opts) {
|
||||||
var walletKeyMap = {};
|
var walletKeyMap = {};
|
||||||
var l = keys.length;
|
var l = keys.length;
|
||||||
var wk;
|
var wk;
|
||||||
for(var i=0; i<l; i++) {
|
for (var i = 0; i < l; i++) {
|
||||||
var k = keys[i];
|
var k = keys[i];
|
||||||
|
|
||||||
if (typeof k === 'string') {
|
if (typeof k === 'string') {
|
||||||
var pk = new PrivateKey(k);
|
var pk = new PrivateKey(k);
|
||||||
wk = new WalletKey({network: pk.network()});
|
wk = new WalletKey({
|
||||||
wk.fromObj({priv:k});
|
network: pk.network()
|
||||||
}
|
});
|
||||||
else if (k instanceof WalletKey) {
|
wk.fromObj({
|
||||||
|
priv: k
|
||||||
|
});
|
||||||
|
} else if (k instanceof WalletKey) {
|
||||||
wk = k;
|
wk = k;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
throw new Error('argument must be an array of strings (WIF format) or WalletKey objects');
|
throw new Error('argument must be an array of strings (WIF format) or WalletKey objects');
|
||||||
}
|
}
|
||||||
walletKeyMap[wk.storeObj().addr] = wk;
|
walletKeyMap[wk.storeObj().addr] = wk;
|
||||||
|
@ -951,19 +936,19 @@ Transaction.prototype.sign = function (selectedUtxos, keys, opts) {
|
||||||
|
|
||||||
var inputSigned = 0;
|
var inputSigned = 0;
|
||||||
l = self.ins.length;
|
l = self.ins.length;
|
||||||
for(var i=0;i<l;i++) {
|
for (var i = 0; i < l; i++) {
|
||||||
var aIn = self.ins[i];
|
var aIn = self.ins[i];
|
||||||
var wk = walletKeyMap[inputMap[i].address];
|
var wk = walletKeyMap[inputMap[i].address];
|
||||||
|
|
||||||
if (typeof wk === 'undefined') {
|
if (typeof wk === 'undefined') {
|
||||||
if ( buffertools.compare(aIn.s,util.EMPTY_BUFFER)!==0 )
|
if (buffertools.compare(aIn.s, util.EMPTY_BUFFER) !== 0)
|
||||||
inputSigned++;
|
inputSigned++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var scriptBuf = new Buffer(inputMap[i].scriptPubKey, 'hex');
|
var scriptBuf = new Buffer(inputMap[i].scriptPubKey, 'hex');
|
||||||
var s = new Script(scriptBuf);
|
var s = new Script(scriptBuf);
|
||||||
if (s.classify() !== Script.TX_PUBKEYHASH) {
|
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);
|
var txSigHash = self.hashForSignature(s, i, signhash);
|
||||||
|
@ -972,10 +957,10 @@ Transaction.prototype.sign = function (selectedUtxos, keys, opts) {
|
||||||
var triesLeft = 10;
|
var triesLeft = 10;
|
||||||
do {
|
do {
|
||||||
sigRaw = wk.privKey.signSync(txSigHash);
|
sigRaw = wk.privKey.signSync(txSigHash);
|
||||||
} while ( wk.privKey.verifySignatureSync(txSigHash, sigRaw) === false && triesLeft-- );
|
} while (wk.privKey.verifySignatureSync(txSigHash, sigRaw) === false && triesLeft--);
|
||||||
|
|
||||||
if (!triesLeft) {
|
if (!triesLeft) {
|
||||||
log.debug('could not sign input:'+i +' verification failed');
|
log.debug('could not sign input:' + i + ' verification failed');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1011,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
|
//starting size estimation
|
||||||
var size = 500;
|
var size = 500;
|
||||||
|
@ -1025,23 +1010,19 @@ Transaction.create = function (utxos, outs, opts) {
|
||||||
var selectedUtxos;
|
var selectedUtxos;
|
||||||
do {
|
do {
|
||||||
// based on https://en.bitcoin.it/wiki/Transaction_fees
|
// based on https://en.bitcoin.it/wiki/Transaction_fees
|
||||||
maxSizeK = parseInt(size/1000) + 1;
|
maxSizeK = parseInt(size / 1000) + 1;
|
||||||
var feeSat = givenFeeSat
|
var feeSat = givenFeeSat ? givenFeeSat : maxSizeK * FEE_PER_1000B_SAT;
|
||||||
? givenFeeSat : maxSizeK * FEE_PER_1000B_SAT ;
|
|
||||||
|
|
||||||
var valueOutSat = Transaction
|
var valueOutSat = Transaction
|
||||||
._sumOutputs(outs)
|
._sumOutputs(outs)
|
||||||
.add(feeSat);
|
.add(feeSat);
|
||||||
|
|
||||||
selectedUtxos = Transaction
|
selectedUtxos = Transaction
|
||||||
.selectUnspent(utxos,valueOutSat / util.COIN, opts.allowUnconfirmed);
|
.selectUnspent(utxos, valueOutSat / util.COIN, opts.allowUnconfirmed);
|
||||||
|
|
||||||
if (!selectedUtxos) {
|
if (!selectedUtxos) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'the given UTXOs dont sum up the given outputs: '
|
'the given UTXOs dont sum up the given outputs: ' + valueOutSat.toString() + ' (fee is ' + feeSat + ' )SAT'
|
||||||
+ valueOutSat.toString()
|
|
||||||
+ ' (fee is ' + feeSat
|
|
||||||
+ ' )SAT'
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
var tx = Transaction.createWithFee(selectedUtxos, outs, feeSat, {
|
var tx = Transaction.createWithFee(selectedUtxos, outs, feeSat, {
|
||||||
|
@ -1050,9 +1031,12 @@ Transaction.create = function (utxos, outs, opts) {
|
||||||
});
|
});
|
||||||
|
|
||||||
size = tx.getSize();
|
size = tx.getSize();
|
||||||
} while (size > (maxSizeK+1)*1000 );
|
} while (size > (maxSizeK + 1) * 1000);
|
||||||
|
|
||||||
return {tx: tx, selectedUtxos: selectedUtxos};
|
return {
|
||||||
|
tx: tx,
|
||||||
|
selectedUtxos: selectedUtxos
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -1119,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);
|
var ret = Transaction.create(utxos, outs, opts);
|
||||||
ret.tx.sign(ret.selectedUtxos, keys);
|
ret.tx.sign(ret.selectedUtxos, keys);
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
var TransactionInputsCache = exports.TransactionInputsCache =
|
var TransactionInputsCache = exports.TransactionInputsCache =
|
||||||
function TransactionInputsCache(tx)
|
function TransactionInputsCache(tx) {
|
||||||
{
|
|
||||||
var txList = [];
|
var txList = [];
|
||||||
var txList64 = [];
|
var txList64 = [];
|
||||||
var reqOuts = {};
|
var reqOuts = {};
|
||||||
|
|
||||||
// Get list of transactions required for verification
|
// Get list of transactions required for verification
|
||||||
tx.ins.forEach(function (txin) {
|
tx.ins.forEach(function(txin) {
|
||||||
if (txin.isCoinBase()) return;
|
if (txin.isCoinBase()) return;
|
||||||
|
|
||||||
var hash = txin.o.slice(0, 32);
|
var hash = txin.o.slice(0, 32);
|
||||||
|
@ -1156,8 +1139,7 @@ function TransactionInputsCache(tx)
|
||||||
this.callbacks = [];
|
this.callbacks = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionInputsCache.prototype.buffer = function buffer(blockChain, txStore, wait, callback)
|
TransactionInputsCache.prototype.buffer = function buffer(blockChain, txStore, wait, callback) {
|
||||||
{
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var complete = false;
|
var complete = false;
|
||||||
|
@ -1167,7 +1149,7 @@ TransactionInputsCache.prototype.buffer = function buffer(blockChain, txStore, w
|
||||||
}
|
}
|
||||||
|
|
||||||
var missingTx = {};
|
var missingTx = {};
|
||||||
self.txList64.forEach(function (hash64) {
|
self.txList64.forEach(function(hash64) {
|
||||||
missingTx[hash64] = true;
|
missingTx[hash64] = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1176,10 +1158,10 @@ TransactionInputsCache.prototype.buffer = function buffer(blockChain, txStore, w
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
|
|
||||||
// Index memory transactions
|
// Index memory transactions
|
||||||
txs.forEach(function (tx) {
|
txs.forEach(function(tx) {
|
||||||
var hash64 = tx.getHash().toString('base64');
|
var hash64 = tx.getHash().toString('base64');
|
||||||
var obj = {};
|
var obj = {};
|
||||||
Object.keys(self.requiredOuts[hash64]).forEach(function (o) {
|
Object.keys(self.requiredOuts[hash64]).forEach(function(o) {
|
||||||
obj[+o] = tx.outs[+o];
|
obj[+o] = tx.outs[+o];
|
||||||
});
|
});
|
||||||
self.txIndex[hash64] = obj;
|
self.txIndex[hash64] = obj;
|
||||||
|
@ -1206,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
|
// TODO: Major speedup should be possible if we load only the outs and not
|
||||||
// whole transactions.
|
// whole transactions.
|
||||||
var callback = this;
|
var callback = this;
|
||||||
blockChain.getOutputsByHashes(self.txList, function (err, result) {
|
blockChain.getOutputsByHashes(self.txList, function(err, result) {
|
||||||
callback(err, result);
|
callback(err, result);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -1216,7 +1198,7 @@ TransactionInputsCache.prototype.buffer = function buffer(blockChain, txStore, w
|
||||||
|
|
||||||
var missingTxDbg = '';
|
var missingTxDbg = '';
|
||||||
if (Object.keys(missingTx).length) {
|
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'));
|
return util.formatHash(new Buffer(hash64, 'base64'));
|
||||||
}).join(',');
|
}).join(',');
|
||||||
}
|
}
|
||||||
|
@ -1224,11 +1206,10 @@ TransactionInputsCache.prototype.buffer = function buffer(blockChain, txStore, w
|
||||||
if (wait && Object.keys(missingTx).length) {
|
if (wait && Object.keys(missingTx).length) {
|
||||||
// TODO: This might no longer be needed now that saveTransactions uses
|
// TODO: This might no longer be needed now that saveTransactions uses
|
||||||
// the safe=true option.
|
// the safe=true option.
|
||||||
setTimeout(function () {
|
setTimeout(function() {
|
||||||
var missingHashes = Object.keys(missingTx);
|
var missingHashes = Object.keys(missingTx);
|
||||||
if (missingHashes.length) {
|
if (missingHashes.length) {
|
||||||
self.callback(new Error('Missing inputs (timeout while searching): '
|
self.callback(new Error('Missing inputs (timeout while searching): ' + missingTxDbg));
|
||||||
+ missingTxDbg));
|
|
||||||
} else if (!complete) {
|
} else if (!complete) {
|
||||||
self.callback(new Error('Callback failed to trigger'));
|
self.callback(new Error('Callback failed to trigger'));
|
||||||
}
|
}
|
||||||
|
@ -1243,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);
|
var args = Array.prototype.slice.apply(arguments);
|
||||||
|
|
||||||
// Empty the callback array first (because downstream functions could add new
|
// Empty the callback array first (because downstream functions could add new
|
||||||
|
@ -1252,14 +1232,9 @@ TransactionInputsCache.prototype.callback = function callback(err)
|
||||||
var cbs = this.callbacks;
|
var cbs = this.callbacks;
|
||||||
this.callbacks = [];
|
this.callbacks = [];
|
||||||
|
|
||||||
try {
|
cbs.forEach(function(cb) {
|
||||||
cbs.forEach(function (cb) {
|
|
||||||
cb.apply(null, args);
|
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);
|
module.exports = require('soop')(Transaction);
|
||||||
|
|
|
@ -24,17 +24,15 @@ function parse_test_transaction(entry) {
|
||||||
// Ignore comments
|
// Ignore comments
|
||||||
if (entry.length !== 3) return;
|
if (entry.length !== 3) return;
|
||||||
|
|
||||||
var inputs = [];
|
var inputs = {};
|
||||||
entry[0].forEach(function(vin) {
|
entry[0].forEach(function(vin) {
|
||||||
var hash = vin[0];
|
var hash = (vin[0]);
|
||||||
var index = vin[1];
|
var index = vin[1];
|
||||||
var scriptPubKey = Script.fromHumanReadable(vin[2]);
|
var scriptPubKey = Script.fromHumanReadable(vin[2]);
|
||||||
|
|
||||||
inputs.push({
|
var mapKey = [hash, index];
|
||||||
'prev_tx_hash': hash,
|
console.log('mapkey=' + mapKey);
|
||||||
'index': index,
|
inputs[mapKey] = scriptPubKey;
|
||||||
'scriptPubKey': scriptPubKey
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -341,53 +339,39 @@ describe('Transaction', function() {
|
||||||
* Bitcoin core transaction tests
|
* Bitcoin core transaction tests
|
||||||
*/
|
*/
|
||||||
// Verify that known valid transactions are intepretted correctly
|
// Verify that known valid transactions are intepretted correctly
|
||||||
testdata.dataTxValid.forEach(function(datum) {
|
var cb = function(err, results) {
|
||||||
var testTx = parse_test_transaction(datum);
|
|
||||||
if (!testTx) return;
|
|
||||||
var verifyP2SH = datum[2];
|
|
||||||
var transactionString = buffertools.toHex(
|
|
||||||
testTx.transaction.serialize());
|
|
||||||
|
|
||||||
it('valid tx=' + transactionString, function() {
|
|
||||||
// Verify that all inputs are valid
|
|
||||||
testTx.inputs.forEach(function(input) {
|
|
||||||
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;
|
|
||||||
|
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
should.exist(results);
|
should.exist(results);
|
||||||
results.should.equal(true);
|
results.should.equal(true);
|
||||||
});
|
};
|
||||||
});
|
testdata.dataTxValid.forEach(function(datum) {
|
||||||
});
|
if (datum.length < 3) return;
|
||||||
});
|
var raw = datum[1];
|
||||||
|
var verifyP2SH = datum[2];
|
||||||
|
|
||||||
// Verify that known invalid transactions are interpretted correctly
|
it('valid tx=' + raw, function() {
|
||||||
testdata.dataTxInvalid.forEach(function(datum) {
|
// Verify that all inputs are valid
|
||||||
var testTx = parse_test_transaction(datum);
|
var testTx = parse_test_transaction(datum);
|
||||||
if (!testTx) return;
|
console.log(raw);
|
||||||
var transactionString = buffertools.toHex(
|
//buffertools.toHex(testTx.transaction.serialize()).should.equal(raw);
|
||||||
testTx.transaction.serialize());
|
var inputs = testTx.transaction.inputs();
|
||||||
|
for (var i = 0; i < inputs.length; i++) {
|
||||||
|
console.log(' input number #########' + i);
|
||||||
|
var input = inputs[i];
|
||||||
|
buffertools.reverse(input[0]);
|
||||||
|
input[0] = buffertools.toHex(input[0]);
|
||||||
|
var mapKey = [input];
|
||||||
|
var scriptPubKey = testTx.inputs[mapKey];
|
||||||
|
if (!scriptPubKey) throw new Error('asdasdasdasd');
|
||||||
|
testTx.transaction.verifyInput(
|
||||||
|
i,
|
||||||
|
scriptPubKey, {
|
||||||
|
verifyP2SH: verifyP2SH,
|
||||||
|
dontVerifyStrictEnc: true
|
||||||
|
},
|
||||||
|
cb);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
// There should either be an error, or the results should be false.
|
|
||||||
(err !== null || (!err && results === false)).should.equal(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
134
util/util.js
134
util/util.js
|
@ -1,27 +1,24 @@
|
||||||
|
|
||||||
var crypto = require('crypto');
|
var crypto = require('crypto');
|
||||||
var bignum = require('bignum');
|
var bignum = require('bignum');
|
||||||
var Binary = require('binary');
|
var Binary = require('binary');
|
||||||
var Put = require('bufferput');
|
var Put = require('bufferput');
|
||||||
var buffertools = require('buffertools');
|
var buffertools = require('buffertools');
|
||||||
var browser;
|
var browser;
|
||||||
if (!process.versions) {
|
var inBrowser = !process.versions;
|
||||||
// browser version
|
if (inBrowser) {
|
||||||
browser = require('../browser/vendor-bundle.js');
|
browser = require('../browser/vendor-bundle.js');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var sha256 = exports.sha256 = function (data) {
|
var sha256 = exports.sha256 = function(data) {
|
||||||
return new Buffer(crypto.createHash('sha256').update(data).digest('binary'), 'binary');
|
return new Buffer(crypto.createHash('sha256').update(data).digest('binary'), 'binary');
|
||||||
};
|
};
|
||||||
var ripe160 = exports.ripe160 = function (data) {
|
var ripe160 = exports.ripe160 = function(data) {
|
||||||
if (!Buffer.isBuffer(data)) {
|
if (!Buffer.isBuffer(data)) {
|
||||||
throw new Error('arg should be a buffer');
|
throw new Error('arg should be a buffer');
|
||||||
}
|
}
|
||||||
|
if (inBrowser) {
|
||||||
if (!process.versions) {
|
var w = new browser.crypto31.lib.WordArray.init(browser.Crypto.util.bytesToWords(data), data.length);
|
||||||
|
|
||||||
var w = new browser.crypto31.lib.WordArray.init(Crypto.util.bytesToWords(data), data.length);
|
|
||||||
var wordArray = browser.crypto31.RIPEMD160(w);
|
var wordArray = browser.crypto31.RIPEMD160(w);
|
||||||
var words = wordArray.words;
|
var words = wordArray.words;
|
||||||
var answer = [];
|
var answer = [];
|
||||||
|
@ -33,22 +30,22 @@ var ripe160 = exports.ripe160 = function (data) {
|
||||||
return new Buffer(crypto.createHash('rmd160').update(data).digest('binary'), 'binary');
|
return new Buffer(crypto.createHash('rmd160').update(data).digest('binary'), 'binary');
|
||||||
};
|
};
|
||||||
|
|
||||||
var sha1 = exports.sha1 = function (data) {
|
var sha1 = exports.sha1 = function(data) {
|
||||||
return new Buffer(crypto.createHash('sha1').update(data).digest('binary'), 'binary');
|
return new Buffer(crypto.createHash('sha1').update(data).digest('binary'), 'binary');
|
||||||
};
|
};
|
||||||
|
|
||||||
var twoSha256 = exports.twoSha256 = function (data) {
|
var twoSha256 = exports.twoSha256 = function(data) {
|
||||||
return sha256(sha256(data));
|
return sha256(sha256(data));
|
||||||
};
|
};
|
||||||
|
|
||||||
var sha256ripe160 = exports.sha256ripe160 = function (data) {
|
var sha256ripe160 = exports.sha256ripe160 = function(data) {
|
||||||
return ripe160(sha256(data));
|
return ripe160(sha256(data));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format a block hash like the official client does.
|
* Format a block hash like the official client does.
|
||||||
*/
|
*/
|
||||||
var formatHash = exports.formatHash = function (hash) {
|
var formatHash = exports.formatHash = function(hash) {
|
||||||
var hashEnd = new Buffer(10);
|
var hashEnd = new Buffer(10);
|
||||||
hash.copy(hashEnd, 0, 22, 32);
|
hash.copy(hashEnd, 0, 22, 32);
|
||||||
return buffertools.reverse(hashEnd).toString('hex');
|
return buffertools.reverse(hashEnd).toString('hex');
|
||||||
|
@ -57,7 +54,7 @@ var formatHash = exports.formatHash = function (hash) {
|
||||||
/**
|
/**
|
||||||
* Display the whole hash, as hex, in correct endian order.
|
* Display the whole hash, as hex, in correct endian order.
|
||||||
*/
|
*/
|
||||||
var formatHashFull = exports.formatHashFull = function (hash) {
|
var formatHashFull = exports.formatHashFull = function(hash) {
|
||||||
var copy = new Buffer(hash.length);
|
var copy = new Buffer(hash.length);
|
||||||
hash.copy(copy);
|
hash.copy(copy);
|
||||||
var hex = buffertools.toHex(buffertools.reverse(copy));
|
var hex = buffertools.toHex(buffertools.reverse(copy));
|
||||||
|
@ -69,13 +66,13 @@ var formatHashFull = exports.formatHashFull = function (hash) {
|
||||||
*
|
*
|
||||||
* Formats a block hash by removing leading zeros and truncating to 10 characters.
|
* Formats a block hash by removing leading zeros and truncating to 10 characters.
|
||||||
*/
|
*/
|
||||||
var formatHashAlt = exports.formatHashAlt = function (hash) {
|
var formatHashAlt = exports.formatHashAlt = function(hash) {
|
||||||
var hex = formatHashFull(hash);
|
var hex = formatHashFull(hash);
|
||||||
hex = hex.replace(/^0*/, '');
|
hex = hex.replace(/^0*/, '');
|
||||||
return hex.substr(0, 10);
|
return hex.substr(0, 10);
|
||||||
};
|
};
|
||||||
|
|
||||||
var formatBuffer = exports.formatBuffer = function (buffer, maxLen) {
|
var formatBuffer = exports.formatBuffer = function(buffer, maxLen) {
|
||||||
// Calculate amount of bytes to display
|
// Calculate amount of bytes to display
|
||||||
if (maxLen === null) {
|
if (maxLen === null) {
|
||||||
maxLen = 10;
|
maxLen = 10;
|
||||||
|
@ -96,41 +93,47 @@ var formatBuffer = exports.formatBuffer = function (buffer, maxLen) {
|
||||||
return output;
|
return output;
|
||||||
};
|
};
|
||||||
|
|
||||||
var valueToBigInt = exports.valueToBigInt = function (valueBuffer) {
|
var valueToBigInt = exports.valueToBigInt = function(valueBuffer) {
|
||||||
if (Buffer.isBuffer(valueBuffer)) {
|
if (Buffer.isBuffer(valueBuffer)) {
|
||||||
return bignum.fromBuffer(valueBuffer, {endian: 'little', size: 8});
|
return bignum.fromBuffer(valueBuffer, {
|
||||||
|
endian: 'little',
|
||||||
|
size: 8
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
return valueBuffer;
|
return valueBuffer;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var bigIntToValue = exports.bigIntToValue = function (valueBigInt) {
|
var bigIntToValue = exports.bigIntToValue = function(valueBigInt) {
|
||||||
if (Buffer.isBuffer(valueBigInt)) {
|
if (Buffer.isBuffer(valueBigInt)) {
|
||||||
return valueBigInt;
|
return valueBigInt;
|
||||||
} else {
|
} else {
|
||||||
return valueBigInt.toBuffer({endian: 'little', size: 8});
|
return valueBigInt.toBuffer({
|
||||||
|
endian: 'little',
|
||||||
|
size: 8
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var fitsInNBits = function(integer, n) {
|
var fitsInNBits = function(integer, n) {
|
||||||
// TODO: make this efficient!!!
|
// TODO: make this efficient!!!
|
||||||
return integer.toString(2).replace('-','').length < n;
|
return integer.toString(2).replace('-', '').length < n;
|
||||||
};
|
};
|
||||||
exports.bytesNeededToStore = bytesNeededToStore = function(integer) {
|
exports.bytesNeededToStore = bytesNeededToStore = function(integer) {
|
||||||
if (integer === 0) return 0;
|
if (integer === 0) return 0;
|
||||||
return Math.ceil(((integer).toString(2).replace('-','').length + 1)/ 8);
|
return Math.ceil(((integer).toString(2).replace('-', '').length + 1) / 8);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.negativeBuffer = negativeBuffer = function(b) {
|
exports.negativeBuffer = negativeBuffer = function(b) {
|
||||||
// implement two-complement negative
|
// implement two-complement negative
|
||||||
var c = new Buffer(b.length);
|
var c = new Buffer(b.length);
|
||||||
// negate each byte
|
// negate each byte
|
||||||
for (var i=0; i<b.length; i++){
|
for (var i = 0; i < b.length; i++) {
|
||||||
c[i] = ~b[i];
|
c[i] = ~b[i];
|
||||||
if (c[i] < 0) c[i] += 256;
|
if (c[i] < 0) c[i] += 256;
|
||||||
}
|
}
|
||||||
// add one
|
// add one
|
||||||
for (var i=b.length - 1; i>=0; i--){
|
for (var i = b.length - 1; i >= 0; i--) {
|
||||||
c[i] += 1;
|
c[i] += 1;
|
||||||
if (c[i] >= 256) c[i] -= 256;
|
if (c[i] >= 256) c[i] -= 256;
|
||||||
if (c[i] !== 0) break;
|
if (c[i] !== 0) break;
|
||||||
|
@ -149,9 +152,9 @@ exports.intToBuffer2C = function(integer) {
|
||||||
var buf = new Put();
|
var buf = new Put();
|
||||||
var s = integer.toString(16);
|
var s = integer.toString(16);
|
||||||
var neg = s[0] === '-';
|
var neg = s[0] === '-';
|
||||||
s = s.replace('-','');
|
s = s.replace('-', '');
|
||||||
for (var i=0; i<size; i++) {
|
for (var i = 0; i < size; i++) {
|
||||||
var si = s.substring(s.length - 2*(i+1), s.length - 2*(i));
|
var si = s.substring(s.length - 2 * (i + 1), s.length - 2 * (i));
|
||||||
if (si.lenght === 1) {
|
if (si.lenght === 1) {
|
||||||
si = '0' + si;
|
si = '0' + si;
|
||||||
}
|
}
|
||||||
|
@ -234,49 +237,44 @@ exports.bufferSMToInt = function(v) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var formatValue = exports.formatValue = function (valueBuffer) {
|
var formatValue = exports.formatValue = function(valueBuffer) {
|
||||||
var value = valueToBigInt(valueBuffer).toString();
|
var value = valueToBigInt(valueBuffer).toString();
|
||||||
var integerPart = value.length > 8 ? value.substr(0, value.length-8) : '0';
|
var integerPart = value.length > 8 ? value.substr(0, value.length - 8) : '0';
|
||||||
var decimalPart = value.length > 8 ? value.substr(value.length-8) : value;
|
var decimalPart = value.length > 8 ? value.substr(value.length - 8) : value;
|
||||||
while (decimalPart.length < 8) {
|
while (decimalPart.length < 8) {
|
||||||
decimalPart = "0"+decimalPart;
|
decimalPart = "0" + decimalPart;
|
||||||
}
|
}
|
||||||
decimalPart = decimalPart.replace(/0*$/, '');
|
decimalPart = decimalPart.replace(/0*$/, '');
|
||||||
while (decimalPart.length < 2) {
|
while (decimalPart.length < 2) {
|
||||||
decimalPart += "0";
|
decimalPart += "0";
|
||||||
}
|
}
|
||||||
return integerPart+"."+decimalPart;
|
return integerPart + "." + decimalPart;
|
||||||
};
|
};
|
||||||
|
|
||||||
var reFullVal = /^\s*(\d+)\.(\d+)/;
|
var reFullVal = /^\s*(\d+)\.(\d+)/;
|
||||||
var reFracVal = /^\s*\.(\d+)/;
|
var reFracVal = /^\s*\.(\d+)/;
|
||||||
var reWholeVal = /^\s*(\d+)/;
|
var reWholeVal = /^\s*(\d+)/;
|
||||||
|
|
||||||
function padFrac(frac)
|
function padFrac(frac) {
|
||||||
{
|
frac = frac.substr(0, 8); //truncate to 8 decimal places
|
||||||
frac=frac.substr(0,8); //truncate to 8 decimal places
|
|
||||||
while (frac.length < 8)
|
while (frac.length < 8)
|
||||||
frac = frac + '0';
|
frac = frac + '0';
|
||||||
return frac;
|
return frac;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseFullValue(res)
|
function parseFullValue(res) {
|
||||||
{
|
|
||||||
return bignum(res[1]).mul('100000000').add(padFrac(res[2]));
|
return bignum(res[1]).mul('100000000').add(padFrac(res[2]));
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseFracValue(res)
|
function parseFracValue(res) {
|
||||||
{
|
|
||||||
return bignum(padFrac(res[1]));
|
return bignum(padFrac(res[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseWholeValue(res)
|
function parseWholeValue(res) {
|
||||||
{
|
|
||||||
return bignum(res[1]).mul('100000000');
|
return bignum(res[1]).mul('100000000');
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.parseValue = function parseValue(valueStr)
|
exports.parseValue = function parseValue(valueStr) {
|
||||||
{
|
|
||||||
if (typeof valueStr !== 'string')
|
if (typeof valueStr !== 'string')
|
||||||
valueStr = valueStr.toString();
|
valueStr = valueStr.toString();
|
||||||
|
|
||||||
|
@ -296,11 +294,11 @@ exports.parseValue = function parseValue(valueStr)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Utility that synchronizes function calls based on a key
|
// Utility that synchronizes function calls based on a key
|
||||||
var createSynchrotron = exports.createSynchrotron = function (fn) {
|
var createSynchrotron = exports.createSynchrotron = function(fn) {
|
||||||
var table = {};
|
var table = {};
|
||||||
return function (key) {
|
return function(key) {
|
||||||
var args = Array.prototype.slice.call(arguments);
|
var args = Array.prototype.slice.call(arguments);
|
||||||
var run = function () {
|
var run = function() {
|
||||||
// Function fn() will call when it finishes
|
// Function fn() will call when it finishes
|
||||||
args[0] = function next() {
|
args[0] = function next() {
|
||||||
if (table[key]) {
|
if (table[key]) {
|
||||||
|
@ -333,21 +331,23 @@ var createSynchrotron = exports.createSynchrotron = function (fn) {
|
||||||
*
|
*
|
||||||
* @returns Buffer random nonce
|
* @returns Buffer random nonce
|
||||||
*/
|
*/
|
||||||
var generateNonce = exports.generateNonce = function () {
|
var generateNonce = exports.generateNonce = function() {
|
||||||
var b32 = 0x100000000, ff = 0xff;
|
var b32 = 0x100000000,
|
||||||
var b = new Buffer(8), i = 0;
|
ff = 0xff;
|
||||||
|
var b = new Buffer(8),
|
||||||
|
i = 0;
|
||||||
|
|
||||||
// Generate eight random bytes
|
// Generate eight random bytes
|
||||||
r = Math.random()*b32;
|
r = Math.random() * b32;
|
||||||
b[i++] = r & ff;
|
b[i++] = r & ff;
|
||||||
b[i++] = (r=r>>>8) & ff;
|
b[i++] = (r = r >>> 8) & ff;
|
||||||
b[i++] = (r=r>>>8) & ff;
|
b[i++] = (r = r >>> 8) & ff;
|
||||||
b[i++] = (r=r>>>8) & ff;
|
b[i++] = (r = r >>> 8) & ff;
|
||||||
r = Math.random()*b32;
|
r = Math.random() * b32;
|
||||||
b[i++] = r & ff;
|
b[i++] = r & ff;
|
||||||
b[i++] = (r=r>>>8) & ff;
|
b[i++] = (r = r >>> 8) & ff;
|
||||||
b[i++] = (r=r>>>8) & ff;
|
b[i++] = (r = r >>> 8) & ff;
|
||||||
b[i++] = (r=r>>>8) & ff;
|
b[i++] = (r = r >>> 8) & ff;
|
||||||
|
|
||||||
return b;
|
return b;
|
||||||
};
|
};
|
||||||
|
@ -357,10 +357,10 @@ var generateNonce = exports.generateNonce = function () {
|
||||||
*
|
*
|
||||||
* This function calculates the difficulty target given the difficulty bits.
|
* This function calculates the difficulty target given the difficulty bits.
|
||||||
*/
|
*/
|
||||||
var decodeDiffBits = exports.decodeDiffBits = function (diffBits, asBigInt) {
|
var decodeDiffBits = exports.decodeDiffBits = function(diffBits, asBigInt) {
|
||||||
diffBits = +diffBits;
|
diffBits = +diffBits;
|
||||||
var target = bignum(diffBits & 0xffffff);
|
var target = bignum(diffBits & 0xffffff);
|
||||||
target = target.shiftLeft(8*((diffBits >>> 24) - 3));
|
target = target.shiftLeft(8 * ((diffBits >>> 24) - 3));
|
||||||
|
|
||||||
if (asBigInt) {
|
if (asBigInt) {
|
||||||
return target;
|
return target;
|
||||||
|
@ -370,7 +370,7 @@ var decodeDiffBits = exports.decodeDiffBits = function (diffBits, asBigInt) {
|
||||||
var diffBuf = target.toBuffer();
|
var diffBuf = target.toBuffer();
|
||||||
var targetBuf = new Buffer(32);
|
var targetBuf = new Buffer(32);
|
||||||
buffertools.fill(targetBuf, 0);
|
buffertools.fill(targetBuf, 0);
|
||||||
diffBuf.copy(targetBuf, 32-diffBuf.length);
|
diffBuf.copy(targetBuf, 32 - diffBuf.length);
|
||||||
return targetBuf;
|
return targetBuf;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -394,7 +394,7 @@ var encodeDiffBits = exports.encodeDiffBits = function encodeDiffBits(target) {
|
||||||
var compact = size << 24;
|
var compact = size << 24;
|
||||||
if (size >= 1) compact |= mpiBuf[4] << 16;
|
if (size >= 1) compact |= mpiBuf[4] << 16;
|
||||||
if (size >= 2) compact |= mpiBuf[5] << 8;
|
if (size >= 2) compact |= mpiBuf[5] << 8;
|
||||||
if (size >= 3) compact |= mpiBuf[6] ;
|
if (size >= 3) compact |= mpiBuf[6];
|
||||||
|
|
||||||
return compact;
|
return compact;
|
||||||
};
|
};
|
||||||
|
@ -405,16 +405,20 @@ var encodeDiffBits = exports.encodeDiffBits = function encodeDiffBits(target) {
|
||||||
* This function calculates the maximum difficulty target divided by the given
|
* This function calculates the maximum difficulty target divided by the given
|
||||||
* difficulty target.
|
* difficulty target.
|
||||||
*/
|
*/
|
||||||
var calcDifficulty = exports.calcDifficulty = function (target) {
|
var calcDifficulty = exports.calcDifficulty = function(target) {
|
||||||
if (!Buffer.isBuffer(target)) {
|
if (!Buffer.isBuffer(target)) {
|
||||||
target = decodeDiffBits(target);
|
target = decodeDiffBits(target);
|
||||||
}
|
}
|
||||||
var targetBigint = bignum.fromBuffer(target, {order: 'forward'});
|
var targetBigint = bignum.fromBuffer(target, {
|
||||||
var maxBigint = bignum.fromBuffer(MAX_TARGET, {order: 'forward'});
|
order: 'forward'
|
||||||
|
});
|
||||||
|
var maxBigint = bignum.fromBuffer(MAX_TARGET, {
|
||||||
|
order: 'forward'
|
||||||
|
});
|
||||||
return maxBigint.div(targetBigint).toNumber();
|
return maxBigint.div(targetBigint).toNumber();
|
||||||
};
|
};
|
||||||
|
|
||||||
var reverseBytes32 = exports.reverseBytes32 = function (data) {
|
var reverseBytes32 = exports.reverseBytes32 = function(data) {
|
||||||
if (data.length % 4) {
|
if (data.length % 4) {
|
||||||
throw new Error("Util.reverseBytes32(): Data length must be multiple of 4");
|
throw new Error("Util.reverseBytes32(): Data length must be multiple of 4");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue