Merge pull request #170 from MattFaus/fix_transaction_tests2
Hook up testdata/tx_valid.json and tx_invalid.json test cases
This commit is contained in:
commit
d9cbe13d93
9
Key.js
9
Key.js
@ -78,6 +78,15 @@ if (process.versions) {
|
|||||||
return new Buffer(signature);
|
return new Buffer(signature);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
kSpec.prototype.verifySignature = function(hash, sig, callback) {
|
||||||
|
try {
|
||||||
|
var result = this.verifySignatureSync(hash, sig);
|
||||||
|
callback(null, result);
|
||||||
|
} catch (e) {
|
||||||
|
callback(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
kSpec.prototype.verifySignatureSync = function(hash, sig) {
|
kSpec.prototype.verifySignatureSync = function(hash, sig) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
|||||||
@ -505,7 +505,7 @@ Script.stringToBuffer = function(s) {
|
|||||||
//console.log('hex value');
|
//console.log('hex value');
|
||||||
buf.put(new Buffer(word.substring(2, word.length), 'hex'));
|
buf.put(new Buffer(word.substring(2, word.length), 'hex'));
|
||||||
} else {
|
} else {
|
||||||
var opcode = Opcode.map['OP_' + word];
|
var opcode = Opcode.map['OP_' + word] || Opcode.map[word];
|
||||||
if (typeof opcode !== 'undefined') {
|
if (typeof opcode !== 'undefined') {
|
||||||
// op code in string form
|
// op code in string form
|
||||||
//console.log('opcode');
|
//console.log('opcode');
|
||||||
|
|||||||
@ -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;
|
||||||
@ -21,7 +22,8 @@ for (var i in Opcode.map) {
|
|||||||
var intToBufferSM = Util.intToBufferSM
|
var intToBufferSM = Util.intToBufferSM
|
||||||
var bufferSMToInt = Util.bufferSMToInt;
|
var bufferSMToInt = Util.bufferSMToInt;
|
||||||
|
|
||||||
function ScriptInterpreter() {
|
function ScriptInterpreter(opts) {
|
||||||
|
this.opts = opts || {};
|
||||||
this.stack = [];
|
this.stack = [];
|
||||||
this.disableUnsafeOpcodes = true;
|
this.disableUnsafeOpcodes = true;
|
||||||
};
|
};
|
||||||
@ -60,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.)
|
||||||
@ -98,8 +99,7 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
|
|||||||
|
|
||||||
if (exec && Buffer.isBuffer(opcode)) {
|
if (exec && Buffer.isBuffer(opcode)) {
|
||||||
this.stack.push(opcode);
|
this.stack.push(opcode);
|
||||||
}
|
} else if (exec || (OP_IF <= opcode && opcode <= OP_ENDIF))
|
||||||
else if (exec || (OP_IF <= opcode && opcode <= OP_ENDIF))
|
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
case OP_0:
|
case OP_0:
|
||||||
this.stack.push(new Buffer([]));
|
this.stack.push(new Buffer([]));
|
||||||
@ -620,12 +620,11 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
|
|||||||
// Remove signature if present (a signature can't sign itself)
|
// Remove signature if present (a signature can't sign itself)
|
||||||
scriptCode.findAndDelete(sig);
|
scriptCode.findAndDelete(sig);
|
||||||
|
|
||||||
//
|
// check canonical signature
|
||||||
isCanonicalSignature(new Buffer(sig));
|
this.isCanonicalSignature(new Buffer(sig));
|
||||||
|
|
||||||
// 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) {
|
||||||
@ -650,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
|
||||||
@ -695,8 +691,9 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
|
|||||||
var scriptCode = Script.fromChunks(scriptChunks);
|
var scriptCode = Script.fromChunks(scriptChunks);
|
||||||
|
|
||||||
// Drop the signatures, since a signature can't sign itself
|
// Drop the signatures, since a signature can't sign itself
|
||||||
|
var that = this;
|
||||||
sigs.forEach(function(sig) {
|
sigs.forEach(function(sig) {
|
||||||
isCanonicalSignature(new Buffer(sig));
|
that.isCanonicalSignature(new Buffer(sig));
|
||||||
scriptCode.findAndDelete(sig);
|
scriptCode.findAndDelete(sig);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -706,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--;
|
||||||
@ -728,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]));
|
||||||
@ -745,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
|
||||||
@ -764,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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -845,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));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -882,6 +866,7 @@ ScriptInterpreter.prototype.getResult = function getResult() {
|
|||||||
return castBool(this.stack[this.stack.length - 1]);
|
return castBool(this.stack[this.stack.length - 1]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Use ScriptInterpreter.verifyFull instead
|
||||||
ScriptInterpreter.verify =
|
ScriptInterpreter.verify =
|
||||||
function verify(scriptSig, scriptPubKey, txTo, n, hashType, callback) {
|
function verify(scriptSig, scriptPubKey, txTo, n, hashType, callback) {
|
||||||
if ("function" !== typeof callback) {
|
if ("function" !== typeof callback) {
|
||||||
@ -899,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);
|
||||||
});
|
});
|
||||||
@ -912,8 +892,8 @@ ScriptInterpreter.verify =
|
|||||||
return si;
|
return si;
|
||||||
};
|
};
|
||||||
|
|
||||||
function verifyStep4(scriptSig, scriptPubKey, txTo, nIn,
|
ScriptInterpreter.prototype.verifyStep4 = function(scriptSig, scriptPubKey,
|
||||||
hashType, opts, callback, si, siCopy) {
|
txTo, nIn, hashType, callback, siCopy) {
|
||||||
if (siCopy.stack.length == 0) {
|
if (siCopy.stack.length == 0) {
|
||||||
callback(null, false);
|
callback(null, false);
|
||||||
return;
|
return;
|
||||||
@ -922,19 +902,19 @@ function verifyStep4(scriptSig, scriptPubKey, txTo, nIn,
|
|||||||
callback(null, castBool(siCopy.stackBack()));
|
callback(null, castBool(siCopy.stackBack()));
|
||||||
}
|
}
|
||||||
|
|
||||||
function verifyStep3(scriptSig, scriptPubKey, txTo, nIn,
|
ScriptInterpreter.prototype.verifyStep3 = function(scriptSig,
|
||||||
hashType, opts, callback, si, siCopy) {
|
scriptPubKey, txTo, nIn, hashType, callback, siCopy) {
|
||||||
if (si.stack.length == 0) {
|
if (this.stack.length == 0) {
|
||||||
callback(null, false);
|
callback(null, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (castBool(si.stackBack()) == false) {
|
if (castBool(this.stackBack()) == false) {
|
||||||
callback(null, false);
|
callback(null, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if not P2SH, we're done
|
// if not P2SH, we're done
|
||||||
if (!opts.verifyP2SH || !scriptPubKey.isP2SH()) {
|
if (!this.opts.verifyP2SH || !scriptPubKey.isP2SH()) {
|
||||||
callback(null, true);
|
callback(null, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -949,46 +929,50 @@ function verifyStep3(scriptSig, scriptPubKey, txTo, nIn,
|
|||||||
|
|
||||||
var subscript = new Script(siCopy.stackPop());
|
var subscript = new Script(siCopy.stackPop());
|
||||||
|
|
||||||
ok = true;
|
var that = this;
|
||||||
siCopy.eval(subscript, txTo, nIn, hashType, function(err) {
|
siCopy.eval(subscript, txTo, nIn, hashType, function(err) {
|
||||||
if (err)
|
if (err) callback(err);
|
||||||
callback(err);
|
else that.verifyStep4(scriptSig, scriptPubKey, txTo, nIn,
|
||||||
else
|
hashType, callback, siCopy);
|
||||||
verifyStep4(scriptSig, scriptPubKey, txTo, nIn,
|
|
||||||
hashType, opts, callback, si, siCopy);
|
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
function verifyStep2(scriptSig, scriptPubKey, txTo, nIn,
|
ScriptInterpreter.prototype.verifyStep2 = function(scriptSig, scriptPubKey,
|
||||||
hashType, opts, callback, si, siCopy) {
|
txTo, nIn, hashType, callback, siCopy) {
|
||||||
if (opts.verifyP2SH) {
|
if (this.opts.verifyP2SH) {
|
||||||
si.stack.forEach(function(item) {
|
this.stack.forEach(function(item) {
|
||||||
siCopy.stack.push(item);
|
siCopy.stack.push(item);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
si.eval(scriptPubKey, txTo, nIn, hashType, function(err) {
|
var that = this;
|
||||||
if (err)
|
this.eval(scriptPubKey, txTo, nIn, hashType, function(err) {
|
||||||
callback(err);
|
if (err) callback(err);
|
||||||
else
|
else that.verifyStep3(scriptSig, scriptPubKey, txTo, nIn,
|
||||||
verifyStep3(scriptSig, scriptPubKey, txTo, nIn,
|
hashType, callback, siCopy);
|
||||||
hashType, opts, callback, si, siCopy);
|
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
ScriptInterpreter.verifyFull =
|
ScriptInterpreter.verifyFull =
|
||||||
function verifyFull(scriptSig, scriptPubKey, txTo, nIn, hashType,
|
function verifyFull(scriptSig, scriptPubKey, txTo, nIn, hashType,
|
||||||
opts, callback) {
|
opts, callback) {
|
||||||
var si = new ScriptInterpreter();
|
var si = new ScriptInterpreter(opts);
|
||||||
var siCopy = new ScriptInterpreter();
|
si.verifyFull(scriptSig, scriptPubKey,
|
||||||
|
txTo, nIn, hashType, callback);
|
||||||
|
};
|
||||||
|
|
||||||
si.eval(scriptSig, txTo, nIn, hashType, function(err) {
|
ScriptInterpreter.prototype.verifyFull = function(scriptSig, scriptPubKey,
|
||||||
if (err)
|
txTo, nIn, hashType, callback) {
|
||||||
callback(err);
|
var siCopy = new ScriptInterpreter(this.opts);
|
||||||
else
|
var that = this;
|
||||||
verifyStep2(scriptSig, scriptPubKey, txTo, nIn,
|
this.eval(scriptSig, txTo, nIn, hashType, function(err) {
|
||||||
hashType, opts, callback, si, siCopy);
|
if (err) callback(err);
|
||||||
|
else {
|
||||||
|
that.verifyStep2(scriptSig, scriptPubKey, txTo, nIn,
|
||||||
|
hashType, callback, siCopy);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var checkSig = ScriptInterpreter.checkSig =
|
var checkSig = ScriptInterpreter.checkSig =
|
||||||
@ -1006,20 +990,16 @@ 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);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var isCanonicalSignature = ScriptInterpreter.isCanonicalSignature = function(sig, opts) {
|
ScriptInterpreter.prototype.isCanonicalSignature = function(sig) {
|
||||||
// See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623
|
// See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623
|
||||||
// A canonical signature exists of: <30> <total len> <02> <len R> <R> <02> <len S> <S> <hashtype>
|
// A canonical signature exists of: <30> <total len> <02> <len R> <R> <02> <len S> <S> <hashtype>
|
||||||
// Where R and S are not negative (their first byte has its highest bit not set), and not
|
// Where R and S are not negative (their first byte has its highest bit not set), and not
|
||||||
@ -1029,7 +1009,9 @@ var isCanonicalSignature = ScriptInterpreter.isCanonicalSignature = function(sig
|
|||||||
if (!Buffer.isBuffer(sig))
|
if (!Buffer.isBuffer(sig))
|
||||||
throw new Error("arg should be a Buffer");
|
throw new Error("arg should be a Buffer");
|
||||||
|
|
||||||
opts = opts || {};
|
// TODO: change to opts.verifyStrictEnc to make the default
|
||||||
|
// behavior not verify, as in bitcoin core
|
||||||
|
if (this.opts.dontVerifyStrictEnc) return true;
|
||||||
|
|
||||||
var l = sig.length;
|
var l = sig.length;
|
||||||
if (l < 9) throw new Error("Non-canonical signature: too short");
|
if (l < 9) throw new Error("Non-canonical signature: too short");
|
||||||
@ -1076,7 +1058,7 @@ var isCanonicalSignature = ScriptInterpreter.isCanonicalSignature = function(sig
|
|||||||
if (nLenS > 1 && (S[0] == 0x00) && !(S[1] & 0x80))
|
if (nLenS > 1 && (S[0] == 0x00) && !(S[1] & 0x80))
|
||||||
throw new Error("Non-canonical signature: S value excessively padded");
|
throw new Error("Non-canonical signature: S value excessively padded");
|
||||||
|
|
||||||
if (opts.verifyEvenS) {
|
if (this.opts.verifyEvenS) {
|
||||||
if (S[nLenS - 1] & 1)
|
if (S[nLenS - 1] & 1)
|
||||||
throw new Error("Non-canonical signature: S value odd");
|
throw new Error("Non-canonical signature: S value odd");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -204,7 +204,7 @@ Transaction.prototype.inputs = function inputs() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load and cache transaction inputs.
|
* Load and cache transaction inputs.
|
||||||
@ -259,10 +259,10 @@ Transaction.prototype.verify = function verify(txCache, blockChain, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return txout;
|
return txout;
|
||||||
};
|
}
|
||||||
|
|
||||||
Step(
|
Step(
|
||||||
function verifyInputs() {
|
function verifyInputs(opts) {
|
||||||
var group = this.group();
|
var group = this.group();
|
||||||
|
|
||||||
if (self.isCoinBase()) {
|
if (self.isCoinBase()) {
|
||||||
@ -278,7 +278,7 @@ Transaction.prototype.verify = function verify(txCache, blockChain, callback) {
|
|||||||
|
|
||||||
outpoints.push(txin.o);
|
outpoints.push(txin.o);
|
||||||
|
|
||||||
self.verifyInput(n, txout.getScript(), group());
|
self.verifyInput(n, txout.getScript(), opts, group());
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -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 " +
|
||||||
@ -351,10 +348,13 @@ Transaction.prototype.verify = function verify(txCache, blockChain, callback) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Transaction.prototype.verifyInput = function verifyInput(n, scriptPubKey, callback) {
|
Transaction.prototype.verifyInput = function verifyInput(n, scriptPubKey, opts, callback) {
|
||||||
return ScriptInterpreter.verify(this.ins[n].getScript(),
|
var scriptSig = this.ins[n].getScript();
|
||||||
|
return ScriptInterpreter.verifyFull(
|
||||||
|
scriptSig,
|
||||||
scriptPubKey,
|
scriptPubKey,
|
||||||
this, n, 0,
|
this, n, 0,
|
||||||
|
opts,
|
||||||
callback);
|
callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -372,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();
|
||||||
|
|
||||||
@ -380,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;
|
||||||
@ -415,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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -934,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;
|
||||||
@ -1023,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)
|
||||||
@ -1035,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, {
|
||||||
@ -1049,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
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -1123,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 = {};
|
||||||
@ -1153,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;
|
||||||
@ -1224,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'));
|
||||||
}
|
}
|
||||||
@ -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);
|
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
|
||||||
@ -1249,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);
|
||||||
|
|||||||
@ -7,16 +7,11 @@ var buffertools = require('buffertools');
|
|||||||
var should = chai.should();
|
var should = chai.should();
|
||||||
var testdata = testdata || require('./testdata');
|
var testdata = testdata || require('./testdata');
|
||||||
|
|
||||||
var ScriptInterpreterModule = bitcore.ScriptInterpreter;
|
|
||||||
var Script = bitcore.Script;
|
var Script = bitcore.Script;
|
||||||
var ScriptInterpreter;
|
var ScriptInterpreter = bitcore.ScriptInterpreter;
|
||||||
|
|
||||||
describe('ScriptInterpreter', function() {
|
describe('ScriptInterpreter', function() {
|
||||||
it('should initialze the main object', function() {
|
it('should initialze the main object', function() {
|
||||||
should.exist(ScriptInterpreterModule);
|
|
||||||
});
|
|
||||||
it('should be able to create class', function() {
|
|
||||||
ScriptInterpreter = ScriptInterpreterModule;
|
|
||||||
should.exist(ScriptInterpreter);
|
should.exist(ScriptInterpreter);
|
||||||
});
|
});
|
||||||
it('should be able to create instance', function() {
|
it('should be able to create instance', function() {
|
||||||
@ -24,7 +19,6 @@ describe('ScriptInterpreter', function() {
|
|||||||
should.exist(si);
|
should.exist(si);
|
||||||
});
|
});
|
||||||
var testScripts = function(data, valid) {
|
var testScripts = function(data, valid) {
|
||||||
var i = 0;
|
|
||||||
data.forEach(function(datum) {
|
data.forEach(function(datum) {
|
||||||
if (datum.length < 2) throw new Error('Invalid test data');
|
if (datum.length < 2) throw new Error('Invalid test data');
|
||||||
var scriptSig = datum[0]; // script inputs
|
var scriptSig = datum[0]; // script inputs
|
||||||
@ -68,7 +62,7 @@ describe('ScriptInterpreter', function() {
|
|||||||
|
|
||||||
testdata.dataSigCanonical.forEach(function(datum) {
|
testdata.dataSigCanonical.forEach(function(datum) {
|
||||||
it('should validate valid canonical signatures', function() {
|
it('should validate valid canonical signatures', function() {
|
||||||
ScriptInterpreter.isCanonicalSignature(new Buffer(datum, 'hex')).should.equal(true);
|
new ScriptInterpreter().isCanonicalSignature(new Buffer(datum, 'hex')).should.equal(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
testdata.dataSigNonCanonical.forEach(function(datum) {
|
testdata.dataSigNonCanonical.forEach(function(datum) {
|
||||||
@ -81,9 +75,16 @@ describe('ScriptInterpreter', function() {
|
|||||||
isHex = 1;
|
isHex = 1;
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
if (isHex)
|
// ignore non-hex strings
|
||||||
ScriptInterpreter.isCanonicalSignature.bind(sig).should.
|
if (isHex) {
|
||||||
throw ();
|
var f = function() {
|
||||||
|
var si = new ScriptInterpreter();
|
||||||
|
var r = si.isCanonicalSignature(sig);
|
||||||
|
};
|
||||||
|
// how this test should be
|
||||||
|
// f.should.throw();
|
||||||
|
new ScriptInterpreter().isCanonicalSignature.bind(sig).should.throw();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var chai = chai || require('chai');
|
var chai = chai || require('chai');
|
||||||
|
chai.Assertion.includeStack = true;
|
||||||
var bitcore = bitcore || require('../bitcore');
|
var bitcore = bitcore || require('../bitcore');
|
||||||
|
|
||||||
var should = chai.should();
|
var should = chai.should();
|
||||||
|
|
||||||
var TransactionModule = bitcore.Transaction;
|
var Transaction = bitcore.Transaction;
|
||||||
var Transaction;
|
|
||||||
var In;
|
var In;
|
||||||
var Out;
|
var Out;
|
||||||
var Script = bitcore.Script;
|
var Script = bitcore.Script;
|
||||||
@ -14,12 +14,41 @@ var util = bitcore.util;
|
|||||||
var buffertools = require('buffertools');
|
var buffertools = require('buffertools');
|
||||||
var testdata = testdata || require('./testdata');
|
var testdata = testdata || require('./testdata');
|
||||||
|
|
||||||
|
// Read tests from test/data/tx_valid.json and tx_invalid.json
|
||||||
|
// Format is an array of arrays
|
||||||
|
// Inner arrays are either [ "comment" ]
|
||||||
|
// or [[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"], serializedTransaction, enforceP2SH
|
||||||
|
// ... where all scripts are stringified scripts.
|
||||||
|
// Returns an object with the Transaction object, and an array of input objects
|
||||||
|
function parse_test_transaction(entry) {
|
||||||
|
// Ignore comments
|
||||||
|
if (entry.length !== 3) return;
|
||||||
|
|
||||||
|
var inputs = {};
|
||||||
|
entry[0].forEach(function(vin) {
|
||||||
|
var hash = (vin[0]);
|
||||||
|
var index = vin[1];
|
||||||
|
var scriptPubKey = Script.fromHumanReadable(vin[2]);
|
||||||
|
|
||||||
|
var mapKey = [hash, index];
|
||||||
|
inputs[mapKey] = scriptPubKey;
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
var raw = new Buffer(entry[1], 'hex');
|
||||||
|
var tx = new Transaction();
|
||||||
|
tx.parse(raw);
|
||||||
|
|
||||||
|
// Sanity check transaction has been parsed correctly
|
||||||
|
buffertools.toHex(tx.serialize()).should.equal(buffertools.toHex(raw));
|
||||||
|
return {
|
||||||
|
'transaction': tx,
|
||||||
|
'inputs': inputs
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
describe('Transaction', function() {
|
describe('Transaction', function() {
|
||||||
it('should initialze the main object', function() {
|
it('should initialze the main object', function() {
|
||||||
should.exist(TransactionModule);
|
|
||||||
});
|
|
||||||
it('should be able to create class', function() {
|
|
||||||
Transaction = TransactionModule;
|
|
||||||
should.exist(Transaction);
|
should.exist(Transaction);
|
||||||
In = Transaction.In;
|
In = Transaction.In;
|
||||||
Out = Transaction.Out;
|
Out = Transaction.Out;
|
||||||
@ -68,12 +97,12 @@ describe('Transaction', function() {
|
|||||||
it('#selectUnspent should check confirmations', function() {
|
it('#selectUnspent should check confirmations', function() {
|
||||||
var u = Transaction.selectUnspent(testdata.dataUnspent, 0.9);
|
var u = Transaction.selectUnspent(testdata.dataUnspent, 0.9);
|
||||||
should.not.exist(u);
|
should.not.exist(u);
|
||||||
var u = Transaction.selectUnspent(testdata.dataUnspent,0.9,true);
|
u = Transaction.selectUnspent(testdata.dataUnspent, 0.9, true);
|
||||||
u.length.should.equal(3);
|
u.length.should.equal(3);
|
||||||
|
|
||||||
var u = Transaction.selectUnspent(testdata.dataUnspent,0.11);
|
u = Transaction.selectUnspent(testdata.dataUnspent, 0.11);
|
||||||
u.length.should.equal(2);
|
u.length.should.equal(2);
|
||||||
var u = Transaction.selectUnspent(testdata.dataUnspent,0.111);
|
u = Transaction.selectUnspent(testdata.dataUnspent, 0.111);
|
||||||
should.not.exist(u);
|
should.not.exist(u);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -85,7 +114,10 @@ describe('Transaction', function() {
|
|||||||
|
|
||||||
it('#create should be able to create instance', function() {
|
it('#create should be able to create instance', function() {
|
||||||
var utxos = testdata.dataUnspent;
|
var utxos = testdata.dataUnspent;
|
||||||
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.08}];
|
var outs = [{
|
||||||
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
|
||||||
|
amount: 0.08
|
||||||
|
}];
|
||||||
|
|
||||||
var ret = Transaction.create(utxos, outs, opts);
|
var ret = Transaction.create(utxos, outs, opts);
|
||||||
should.exist(ret.tx);
|
should.exist(ret.tx);
|
||||||
@ -107,23 +139,34 @@ describe('Transaction', function() {
|
|||||||
|
|
||||||
it('#create should fail if not enough inputs ', function() {
|
it('#create should fail if not enough inputs ', function() {
|
||||||
var utxos = testdata.dataUnspent;
|
var utxos = testdata.dataUnspent;
|
||||||
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:80}];
|
var outs = [{
|
||||||
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
|
||||||
|
amount: 80
|
||||||
|
}];
|
||||||
Transaction
|
Transaction
|
||||||
.create
|
.create
|
||||||
.bind(utxos, outs, opts)
|
.bind(utxos, outs, opts)
|
||||||
.should.throw();
|
.should.
|
||||||
|
throw ();
|
||||||
|
|
||||||
var outs2 = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.5}];
|
var outs2 = [{
|
||||||
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
|
||||||
|
amount: 0.5
|
||||||
|
}];
|
||||||
should.exist(Transaction.create(utxos, outs2, opts));
|
should.exist(Transaction.create(utxos, outs2, opts));
|
||||||
|
|
||||||
// do not allow unconfirmed
|
// do not allow unconfirmed
|
||||||
Transaction.create.bind(utxos, outs2).should.throw();
|
Transaction.create.bind(utxos, outs2).should.
|
||||||
|
throw ();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('#create should create same output as bitcoind createrawtransaction ', function() {
|
it('#create should create same output as bitcoind createrawtransaction ', function() {
|
||||||
var utxos = testdata.dataUnspent;
|
var utxos = testdata.dataUnspent;
|
||||||
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.08}];
|
var outs = [{
|
||||||
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
|
||||||
|
amount: 0.08
|
||||||
|
}];
|
||||||
var ret = Transaction.create(utxos, outs, opts);
|
var ret = Transaction.create(utxos, outs, opts);
|
||||||
var tx = ret.tx;
|
var tx = ret.tx;
|
||||||
|
|
||||||
@ -135,8 +178,13 @@ describe('Transaction', function() {
|
|||||||
it('#create should create same output as bitcoind createrawtransaction wo remainder', function() {
|
it('#create should create same output as bitcoind createrawtransaction wo remainder', function() {
|
||||||
var utxos = testdata.dataUnspent;
|
var utxos = testdata.dataUnspent;
|
||||||
// no remainder
|
// no remainder
|
||||||
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.08}];
|
var outs = [{
|
||||||
var ret = Transaction.create(utxos, outs, {fee:0.03} );
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
|
||||||
|
amount: 0.08
|
||||||
|
}];
|
||||||
|
var ret = Transaction.create(utxos, outs, {
|
||||||
|
fee: 0.03
|
||||||
|
});
|
||||||
var tx = ret.tx;
|
var tx = ret.tx;
|
||||||
|
|
||||||
// string output generated from: bitcoind createrawtransaction '[{"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1","vout":1},{"txid":"2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc2","vout":0} ]' '{"mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE":0.08}'
|
// string output generated from: bitcoind createrawtransaction '[{"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1","vout":1},{"txid":"2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc2","vout":0} ]' '{"mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE":0.08}'
|
||||||
@ -146,14 +194,20 @@ describe('Transaction', function() {
|
|||||||
|
|
||||||
it('#createAndSign should sign a tx', function() {
|
it('#createAndSign should sign a tx', function() {
|
||||||
var utxos = testdata.dataUnspentSign.unspent;
|
var utxos = testdata.dataUnspentSign.unspent;
|
||||||
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.08}];
|
var outs = [{
|
||||||
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
|
||||||
|
amount: 0.08
|
||||||
|
}];
|
||||||
var ret = Transaction.createAndSign(utxos, outs, testdata.dataUnspentSign.keyStrings, opts);
|
var ret = Transaction.createAndSign(utxos, outs, testdata.dataUnspentSign.keyStrings, opts);
|
||||||
var tx = ret.tx;
|
var tx = ret.tx;
|
||||||
tx.isComplete().should.equal(true);
|
tx.isComplete().should.equal(true);
|
||||||
tx.ins.length.should.equal(1);
|
tx.ins.length.should.equal(1);
|
||||||
tx.outs.length.should.equal(2);
|
tx.outs.length.should.equal(2);
|
||||||
|
|
||||||
var outs2 = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:16}];
|
var outs2 = [{
|
||||||
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
|
||||||
|
amount: 16
|
||||||
|
}];
|
||||||
var ret2 = Transaction.createAndSign(utxos, outs2, testdata.dataUnspentSign.keyStrings, opts);
|
var ret2 = Transaction.createAndSign(utxos, outs2, testdata.dataUnspentSign.keyStrings, opts);
|
||||||
var tx2 = ret2.tx;
|
var tx2 = ret2.tx;
|
||||||
tx2.isComplete().should.equal(true);
|
tx2.isComplete().should.equal(true);
|
||||||
@ -164,7 +218,10 @@ describe('Transaction', function() {
|
|||||||
it('#createAndSign should sign an incomplete tx ', function() {
|
it('#createAndSign should sign an incomplete tx ', function() {
|
||||||
var keys = ['cNpW8B7XPAzCdRR9RBWxZeveSNy3meXgHD8GuhcqUyDuy8ptCDzJ'];
|
var keys = ['cNpW8B7XPAzCdRR9RBWxZeveSNy3meXgHD8GuhcqUyDuy8ptCDzJ'];
|
||||||
var utxos = testdata.dataUnspentSign.unspent;
|
var utxos = testdata.dataUnspentSign.unspent;
|
||||||
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.08}];
|
var outs = [{
|
||||||
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
|
||||||
|
amount: 0.08
|
||||||
|
}];
|
||||||
var ret = Transaction.createAndSign(utxos, outs, keys, opts);
|
var ret = Transaction.createAndSign(utxos, outs, keys, opts);
|
||||||
var tx = ret.tx;
|
var tx = ret.tx;
|
||||||
tx.ins.length.should.equal(1);
|
tx.ins.length.should.equal(1);
|
||||||
@ -173,7 +230,10 @@ describe('Transaction', function() {
|
|||||||
it('#isComplete should return TX signature status', function() {
|
it('#isComplete should return TX signature status', function() {
|
||||||
var keys = ['cNpW8B7XPAzCdRR9RBWxZeveSNy3meXgHD8GuhcqUyDuy8ptCDzJ'];
|
var keys = ['cNpW8B7XPAzCdRR9RBWxZeveSNy3meXgHD8GuhcqUyDuy8ptCDzJ'];
|
||||||
var utxos = testdata.dataUnspentSign.unspent;
|
var utxos = testdata.dataUnspentSign.unspent;
|
||||||
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.08}];
|
var outs = [{
|
||||||
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
|
||||||
|
amount: 0.08
|
||||||
|
}];
|
||||||
var ret = Transaction.createAndSign(utxos, outs, keys, opts);
|
var ret = Transaction.createAndSign(utxos, outs, keys, opts);
|
||||||
var tx = ret.tx;
|
var tx = ret.tx;
|
||||||
tx.isComplete().should.equal(false);
|
tx.isComplete().should.equal(false);
|
||||||
@ -182,7 +242,10 @@ describe('Transaction', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('#sign should sign a tx in multiple steps (case1)', function() {
|
it('#sign should sign a tx in multiple steps (case1)', function() {
|
||||||
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:1.08}];
|
var outs = [{
|
||||||
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
|
||||||
|
amount: 1.08
|
||||||
|
}];
|
||||||
var ret = Transaction.create(testdata.dataUnspentSign.unspent, outs, opts);
|
var ret = Transaction.create(testdata.dataUnspentSign.unspent, outs, opts);
|
||||||
var tx = ret.tx;
|
var tx = ret.tx;
|
||||||
var selectedUtxos = ret.selectedUtxos;
|
var selectedUtxos = ret.selectedUtxos;
|
||||||
@ -199,7 +262,10 @@ describe('Transaction', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('#sign should sign a tx in multiple steps (case2)', function() {
|
it('#sign should sign a tx in multiple steps (case2)', function() {
|
||||||
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:16}];
|
var outs = [{
|
||||||
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
|
||||||
|
amount: 16
|
||||||
|
}];
|
||||||
var ret = Transaction.create(testdata.dataUnspentSign.unspent, outs, opts);
|
var ret = Transaction.create(testdata.dataUnspentSign.unspent, outs, opts);
|
||||||
var tx = ret.tx;
|
var tx = ret.tx;
|
||||||
var selectedUtxos = ret.selectedUtxos;
|
var selectedUtxos = ret.selectedUtxos;
|
||||||
@ -220,7 +286,10 @@ describe('Transaction', function() {
|
|||||||
var outs = [];
|
var outs = [];
|
||||||
var n = 101;
|
var n = 101;
|
||||||
for (var i = 0; i < n; i++) {
|
for (var i = 0; i < n; i++) {
|
||||||
outs.push({address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.01});
|
outs.push({
|
||||||
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
|
||||||
|
amount: 0.01
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var ret = Transaction.createAndSign(utxos, outs, testdata.dataUnspentSign.keyStrings, opts);
|
var ret = Transaction.createAndSign(utxos, outs, testdata.dataUnspentSign.keyStrings, opts);
|
||||||
@ -238,15 +307,18 @@ describe('Transaction', function() {
|
|||||||
|
|
||||||
|
|
||||||
//this is the complementary case, it does not trigger a new utxo
|
//this is the complementary case, it does not trigger a new utxo
|
||||||
var utxos =testdata.dataUnspentSign.unspent;
|
utxos = testdata.dataUnspentSign.unspent;
|
||||||
var outs = [];
|
outs = [];
|
||||||
var n =100;
|
n = 100;
|
||||||
for (var i=0; i<n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
outs.push({address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.01});
|
outs.push({
|
||||||
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
|
||||||
|
amount: 0.01
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var ret = Transaction.createAndSign(utxos, outs, testdata.dataUnspentSign.keyStrings, opts);
|
ret = Transaction.createAndSign(utxos, outs, testdata.dataUnspentSign.keyStrings, opts);
|
||||||
var tx = ret.tx;
|
tx = ret.tx;
|
||||||
tx.getSize().should.equal(3485);
|
tx.getSize().should.equal(3485);
|
||||||
|
|
||||||
// ins = 1.0101 BTC (1 inputs: 1.0101);
|
// ins = 1.0101 BTC (1 inputs: 1.0101);
|
||||||
@ -260,39 +332,49 @@ describe('Transaction', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// Read tests from test/data/tx_valid.json
|
|
||||||
// Format is an array of arrays
|
|
||||||
// Inner arrays are either [ "comment" ]
|
/*
|
||||||
// or [[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"], serializedTransaction, enforceP2SH
|
* Bitcoin core transaction tests
|
||||||
// ... where all scripts are stringified scripts.
|
*/
|
||||||
testdata.dataTxValid.forEach(function(datum) {
|
// Verify that known valid transactions are intepretted correctly
|
||||||
if (datum.length === 3) {
|
var coreTest = function(data, valid) {
|
||||||
it.skip('valid tx=' + datum[1], function(done) {
|
data.forEach(function(datum) {
|
||||||
var inputs = datum[0];
|
if (datum.length < 3) return;
|
||||||
var map = {};
|
var raw = datum[1];
|
||||||
inputs.forEach(function(vin) {
|
var verifyP2SH = datum[2];
|
||||||
var hash = vin[0];
|
|
||||||
var index = vin[1];
|
it.skip((valid ? '' : 'in') + 'valid tx=' + raw, function(done) {
|
||||||
var scriptPubKey = new Script(new Buffer(vin[2]));
|
var cb = function(err, results) {
|
||||||
map[[hash, index]] = scriptPubKey; //Script.fromStringContent(scriptPubKey);
|
should.not.exist(err);
|
||||||
console.log(scriptPubKey.getStringContent());
|
should.exist(results);
|
||||||
console.log('********************************');
|
results.should.equal(valid);
|
||||||
done();
|
done();
|
||||||
|
};
|
||||||
|
|
||||||
});
|
var testTx = parse_test_transaction(datum);
|
||||||
var raw = new Buffer(datum[1], 'hex');
|
buffertools.toHex(testTx.transaction.serialize()).should.equal(raw);
|
||||||
var tx = new Transaction();
|
var inputs = testTx.transaction.inputs();
|
||||||
tx.parse(raw);
|
for (var i = 0; i < inputs.length; i++) {
|
||||||
|
var input = inputs[i];
|
||||||
buffertools.toHex(tx.serialize()).should.equal(buffertools.toHex(raw));
|
buffertools.reverse(input[0]);
|
||||||
|
input[0] = buffertools.toHex(input[0]);
|
||||||
var i = 0;
|
var mapKey = [input];
|
||||||
var stx = tx.getStandardizedObject();
|
var scriptPubKey = testTx.inputs[mapKey];
|
||||||
tx.ins.forEach(function(txin) {
|
if (!scriptPubKey) throw new Error('Bad test: '+datum);
|
||||||
var scriptPubKey = map[[stx. in [i].prev_out.hash, stx. in [i].prev_out.n]];
|
testTx.transaction.verifyInput(
|
||||||
i += 1;
|
i,
|
||||||
});
|
scriptPubKey, {
|
||||||
});
|
verifyP2SH: verifyP2SH,
|
||||||
|
dontVerifyStrictEnc: true
|
||||||
|
},
|
||||||
|
cb);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
coreTest(testdata.dataTxValid, true);
|
||||||
|
coreTest(testdata.dataTxInvalid, false);
|
||||||
|
|
||||||
|
});
|
||||||
|
|||||||
48
util/util.js
48
util/util.js
@ -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,9 +17,7 @@ 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(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;
|
||||||
@ -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();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user