refactor and fixes for Transaction, ScriptInterpreter, and Key

This commit is contained in:
Manuel Araoz 2014-03-20 19:41:21 -03:00 committed by MattFaus
parent b227341c12
commit ba92a6b1df
5 changed files with 1073 additions and 1128 deletions

4
Key.js
View File

@ -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;

View File

@ -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) {

View File

@ -204,7 +204,7 @@ Transaction.prototype.inputs = function inputs() {
} }
return res; return res;
} };
/** /**
* Load and cache transaction inputs. * Load and cache transaction inputs.
@ -325,14 +325,11 @@ Transaction.prototype.verify = function verify(txCache, blockChain, callback) {
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 " +
@ -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,12 +407,6 @@ 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));
}
} }
} }
@ -937,13 +920,15 @@ Transaction.prototype.sign = function (selectedUtxos, keys, opts) {
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;
@ -1026,8 +1011,7 @@ Transaction.create = function (utxos, outs, opts) {
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)
@ -1038,10 +1022,7 @@ Transaction.create = function (utxos, outs, opts) {
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, {
@ -1052,7 +1033,10 @@ 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
};
}; };
@ -1126,8 +1110,7 @@ Transaction.createAndSign = function (utxos, outs, keys, opts) {
}; };
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 = {};
@ -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;
@ -1227,8 +1209,7 @@ TransactionInputsCache.prototype.buffer = function buffer(blockChain, txStore, w
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);

View File

@ -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);
});
});
});
});
}); });

View File

@ -1,12 +1,11 @@
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');
} }
@ -18,10 +17,8 @@ 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 = [];
@ -98,7 +95,10 @@ var formatBuffer = exports.formatBuffer = function (buffer, maxLen) {
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;
} }
@ -108,7 +108,10 @@ 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
});
} }
}; };
@ -252,31 +255,26 @@ 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();
@ -334,8 +332,10 @@ 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;
@ -409,8 +409,12 @@ 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();
}; };