Merge pull request #777 from maraoz/add/tx_invalid/tests
add tx_invalid.json tests
This commit is contained in:
commit
9f442e8ec4
|
@ -29,6 +29,9 @@ function Block(arg) {
|
|||
return this;
|
||||
}
|
||||
|
||||
// https://github.com/bitcoin/bitcoin/blob/b5fa132329f0377d787a4a21c1686609c2bfaece/src/primitives/block.h#L14
|
||||
Block.MAX_BLOCK_SIZE = 1000000;
|
||||
|
||||
/**
|
||||
* @param {*} - A Buffer, JSON string or Object
|
||||
* @returns {Object} - An object representing block data
|
||||
|
|
|
@ -889,7 +889,7 @@ ScriptInterpreter.prototype.step = function() {
|
|||
try {
|
||||
var sig = Signature.fromTxFormat(bufSig);
|
||||
var pubkey = PublicKey.fromBuffer(bufPubkey, false);
|
||||
fSuccess = this.tx.verify(sig, pubkey, this.nin, subscript);
|
||||
fSuccess = this.tx.verifySignature(sig, pubkey, this.nin, subscript);
|
||||
} catch (e) {
|
||||
//invalid sig or pubkey
|
||||
fSuccess = false;
|
||||
|
@ -978,7 +978,7 @@ ScriptInterpreter.prototype.step = function() {
|
|||
try {
|
||||
var sig = Signature.fromTxFormat(bufSig);
|
||||
var pubkey = PublicKey.fromBuffer(bufPubkey, false);
|
||||
fOk = this.tx.verify(sig, pubkey, this.nin, subscript);
|
||||
fOk = this.tx.verifySignature(sig, pubkey, this.nin, subscript);
|
||||
} catch (e) {
|
||||
//invalid sig or pubkey
|
||||
fOk = false;
|
||||
|
|
|
@ -148,4 +148,12 @@ Input.prototype.isValidSignature = function(transaction, signature) {
|
|||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns true if this is a coinbase input (represents no input)
|
||||
*/
|
||||
Input.prototype.isNull = function() {
|
||||
return this.prevTxId.toString('hex') === '0000000000000000000000000000000000000000000000000000000000000000' &&
|
||||
this.outputIndex === 0xffffffff;
|
||||
};
|
||||
|
||||
module.exports = Input;
|
||||
|
|
|
@ -22,6 +22,8 @@ var MultiSigScriptHashInput = Input.MultiSigScriptHash;
|
|||
var Output = require('./output');
|
||||
var Script = require('../script');
|
||||
var PrivateKey = require('../privatekey');
|
||||
var Block = require('../block');
|
||||
var BN = require('../crypto/bn');
|
||||
|
||||
var CURRENT_VERSION = 1;
|
||||
var DEFAULT_NLOCKTIME = 0;
|
||||
|
@ -57,10 +59,13 @@ function Transaction(serialized) {
|
|||
}
|
||||
}
|
||||
|
||||
// max amount of satoshis in circulation
|
||||
Transaction.MAX_MONEY = 21000000 * 1e8;
|
||||
|
||||
/* Constructors and Serialization */
|
||||
|
||||
/**
|
||||
* Create a "shallow" copy of the transaction, by serializing and deserializing
|
||||
* Create a 'shallow' copy of the transaction, by serializing and deserializing
|
||||
* it dropping any additional information that inputs and outputs may have hold
|
||||
*
|
||||
* @param {Transaction} transaction
|
||||
|
@ -562,8 +567,82 @@ Transaction.prototype.isValidSignature = function(signature) {
|
|||
/**
|
||||
* @returns {bool} whether the signature is valid for this transaction input
|
||||
*/
|
||||
Transaction.prototype.verify = function(sig, pubkey, nin, subscript) {
|
||||
Transaction.prototype.verifySignature = function(sig, pubkey, nin, subscript) {
|
||||
return Sighash.verify(this, sig, pubkey, nin, subscript);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Check that a transaction passes basic sanity tests. If not, return a string
|
||||
* describing the error. This function contains the same logic as
|
||||
* CheckTransaction in bitcoin core.
|
||||
*/
|
||||
Transaction.prototype.verify = function() {
|
||||
// Basic checks that don't depend on any context
|
||||
if (this.inputs.length === 0) {
|
||||
return 'transaction txins empty';
|
||||
}
|
||||
|
||||
if (this.outputs.length === 0) {
|
||||
return 'transaction txouts empty';
|
||||
}
|
||||
|
||||
// Size limits
|
||||
if (this.toBuffer().length > Block.MAX_BLOCK_SIZE) {
|
||||
return 'transaction over the maximum block size';
|
||||
}
|
||||
|
||||
// Check for negative or overflow output values
|
||||
var valueoutbn = BN(0);
|
||||
for (var i = 0; i < this.outputs.length; i++) {
|
||||
var txout = this.outputs[i];
|
||||
var valuebn = BN(txout.satoshis.toString(16));
|
||||
if (valuebn.lt(0)) {
|
||||
return 'transaction txout ' + i + ' negative';
|
||||
}
|
||||
if (valuebn.gt(Transaction.MAX_MONEY)) {
|
||||
return 'transaction txout ' + i + ' greater than MAX_MONEY';
|
||||
}
|
||||
valueoutbn = valueoutbn.add(valuebn);
|
||||
if (valueoutbn.gt(Transaction.MAX_MONEY)) {
|
||||
return 'transaction txout ' + i + ' total output greater than MAX_MONEY';
|
||||
}
|
||||
}
|
||||
|
||||
// Check for duplicate inputs
|
||||
var txinmap = {};
|
||||
for (i = 0; i < this.inputs.length; i++) {
|
||||
var txin = this.inputs[i];
|
||||
|
||||
var inputid = txin.prevTxId + ':' + txin.outputIndex;
|
||||
if (!_.isUndefined(txinmap[inputid])) {
|
||||
return 'transaction input ' + i + ' duplicate input';
|
||||
}
|
||||
txinmap[inputid] = true;
|
||||
}
|
||||
|
||||
var isCoinbase = this.isCoinbase();
|
||||
if (isCoinbase) {
|
||||
var buf = this.inputs[0]._script.toBuffer();
|
||||
if (buf.length < 2 || buf.length > 100) {
|
||||
return 'coinbase trasaction script size invalid';
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < this.inputs.length; i++) {
|
||||
if (this.inputs[i].isNull()) {
|
||||
return 'tranasction input ' + i + ' has null input';
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Analagous to bitcoind's IsCoinBase function in transaction.h
|
||||
*/
|
||||
Transaction.prototype.isCoinbase = function() {
|
||||
return (this.inputs.length === 1 && this.inputs[0].isNull());
|
||||
};
|
||||
|
||||
|
||||
module.exports = Transaction;
|
||||
|
|
|
@ -9,6 +9,7 @@ var BN = bitcore.crypto.BN;
|
|||
var BufferReader = bitcore.encoding.BufferReader;
|
||||
var BufferWriter = bitcore.encoding.BufferWriter;
|
||||
var Opcode = bitcore.Opcode;
|
||||
var _ = require('lodash');
|
||||
|
||||
var script_valid = require('./data/bitcoind/script_valid');
|
||||
var script_invalid = require('./data/bitcoind/script_invalid');
|
||||
|
@ -223,13 +224,14 @@ describe('ScriptInterpreter', function() {
|
|||
|
||||
});
|
||||
describe('bitcoind transaction evaluation fixtures', function() {
|
||||
var test_txs = function(set, expected) {
|
||||
var c = 0;
|
||||
tx_valid.forEach(function(vector) {
|
||||
set.forEach(function(vector) {
|
||||
if (vector.length === 1) {
|
||||
return;
|
||||
}
|
||||
c++;
|
||||
it('should pass tx_valid vector ' + c, function() {
|
||||
it('should pass tx_' + (expected ? '' : 'in') + 'valid vector ' + c, function() {
|
||||
var inputs = vector[0];
|
||||
var txhex = vector[1];
|
||||
var flags = getFlags(vector[2]);
|
||||
|
@ -246,6 +248,7 @@ describe('ScriptInterpreter', function() {
|
|||
});
|
||||
|
||||
var tx = Transaction(txhex);
|
||||
var allInputsVerified = true;
|
||||
tx.inputs.forEach(function(txin, j) {
|
||||
var scriptSig = txin.script;
|
||||
var txidhex = txin.prevTxId.toString('hex');
|
||||
|
@ -255,61 +258,19 @@ describe('ScriptInterpreter', function() {
|
|||
should.exist(scriptSig);
|
||||
var interp = ScriptInterpreter();
|
||||
var verified = interp.verify(scriptSig, scriptPubkey, tx, j, flags);
|
||||
verified.should.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
c = 0;
|
||||
tx_invalid.forEach(function(vector) {
|
||||
if (vector.length === 1) {
|
||||
return;
|
||||
}
|
||||
c++;
|
||||
|
||||
// tests intentionally not performed by the script interpreter:
|
||||
// TODO: check this?
|
||||
/*
|
||||
if (c === 7 || // tests if valuebn is negative
|
||||
c === 8 || // tests if valuebn is greater than MAX_MONEY
|
||||
c === 10 || // tests if two inputs are equal
|
||||
c === 11 || // coinbase
|
||||
c === 12 || // coinbase
|
||||
c === 13 // null input
|
||||
) {
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
it.skip('should pass tx_invalid vector ' + c, function() {
|
||||
var inputs = vector[0];
|
||||
var txhex = vector[1];
|
||||
var flags = getFlags(vector[2]);
|
||||
|
||||
var map = {};
|
||||
inputs.forEach(function(input) {
|
||||
var txoutnum = input[1];
|
||||
if (txoutnum === -1) {
|
||||
txoutnum = 0xffffffff; //bitcoind casts -1 to an unsigned int
|
||||
}
|
||||
map[input[0] + ':' + txoutnum] = Script.fromBitcoindString(input[2]);
|
||||
});
|
||||
|
||||
var tx = Transaction().fromBuffer(new Buffer(txhex, 'hex'));
|
||||
if (tx.txins.length > 0) {
|
||||
tx.txins.some(function(txin, j) {
|
||||
var scriptSig = txin.script;
|
||||
var txidhex = BufferReader(txin.txidbuf).readReverse().toString('hex');
|
||||
var txoutnum = txin.txoutnum;
|
||||
var scriptPubkey = map[txidhex + ':' + txoutnum];
|
||||
should.exist(scriptPubkey);
|
||||
var interp = ScriptInterpreter();
|
||||
var verified = interp.verify(scriptSig, scriptPubkey, tx, j, flags);
|
||||
return verified === false;
|
||||
}).should.equal(true);
|
||||
if (!verified) {
|
||||
allInputsVerified = false;
|
||||
}
|
||||
});
|
||||
var txVerified = tx.verify();
|
||||
txVerified = _.isBoolean(txVerified);
|
||||
allInputsVerified = allInputsVerified && txVerified;
|
||||
allInputsVerified.should.equal(expected);
|
||||
});
|
||||
});
|
||||
};
|
||||
test_txs(tx_valid, true);
|
||||
test_txs(tx_invalid, false);
|
||||
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue