Add GetTransactionSigOpCount unit tests
This commit is contained in:
24
src/main.cpp
24
src/main.cpp
@@ -1004,8 +1004,7 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool
|
||||
return EvaluateSequenceLocks(index, lockPair);
|
||||
}
|
||||
|
||||
|
||||
unsigned int GetLegacySigOpCount(const CTransaction& tx)
|
||||
unsigned int GetSigOpCountWithoutP2SH(const CTransaction& tx)
|
||||
{
|
||||
unsigned int nSigOps = 0;
|
||||
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
||||
@@ -1034,12 +1033,19 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in
|
||||
return nSigOps;
|
||||
}
|
||||
|
||||
uint64_t GetTransactionSigOpCount(const CTransaction &tx, const CCoinsViewCache &inputs, int flags)
|
||||
{
|
||||
uint64_t nSigOps = GetSigOpCountWithoutP2SH(tx);
|
||||
if (tx.IsCoinBase()) {
|
||||
return nSigOps;
|
||||
}
|
||||
|
||||
if (flags & SCRIPT_VERIFY_P2SH) {
|
||||
nSigOps += GetP2SHSigOpCount(tx, inputs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return nSigOps;
|
||||
}
|
||||
|
||||
bool CheckTransaction(const CTransaction& tx, CValidationState &state)
|
||||
{
|
||||
@@ -1230,7 +1236,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
|
||||
if (fRequireStandard && !AreInputsStandard(tx, view))
|
||||
return state.Invalid(false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs");
|
||||
|
||||
unsigned int nSigOps = GetLegacySigOpCount(tx);
|
||||
unsigned int nSigOps = GetSigOpCountWithoutP2SH(tx);
|
||||
nSigOps += GetP2SHSigOpCount(tx, view);
|
||||
|
||||
CAmount nValueOut = tx.GetValueOut();
|
||||
@@ -2453,7 +2459,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||
const CTransaction &tx = block.vtx[i];
|
||||
|
||||
nInputs += tx.vin.size();
|
||||
nSigOps += GetLegacySigOpCount(tx);
|
||||
nSigOps += GetSigOpCountWithoutP2SH(tx);
|
||||
if (nSigOps > MAX_BLOCK_SIGOPS)
|
||||
return state.DoS(100, error("ConnectBlock(): too many sigops"),
|
||||
REJECT_INVALID, "bad-blk-sigops");
|
||||
@@ -3516,7 +3522,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
|
||||
unsigned int nSigOps = 0;
|
||||
BOOST_FOREACH(const CTransaction& tx, block.vtx)
|
||||
{
|
||||
nSigOps += GetLegacySigOpCount(tx);
|
||||
nSigOps += GetSigOpCountWithoutP2SH(tx);
|
||||
}
|
||||
if (nSigOps > MAX_BLOCK_SIGOPS)
|
||||
return state.DoS(100, error("CheckBlock(): out-of-bounds SigOpCount"),
|
||||
|
||||
10
src/main.h
10
src/main.h
@@ -330,7 +330,7 @@ struct CNodeStateStats {
|
||||
* @return number of sigops this transaction's outputs will produce when spent
|
||||
* @see CTransaction::FetchInputs
|
||||
*/
|
||||
unsigned int GetLegacySigOpCount(const CTransaction& tx);
|
||||
unsigned int GetSigOpCountWithoutP2SH(const CTransaction& tx);
|
||||
|
||||
/**
|
||||
* Count ECDSA signature operations in pay-to-script-hash inputs.
|
||||
@@ -341,6 +341,14 @@ unsigned int GetLegacySigOpCount(const CTransaction& tx);
|
||||
*/
|
||||
unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& mapInputs);
|
||||
|
||||
/**
|
||||
* Compute total signature operation cost of a transaction.
|
||||
* @param[in] tx Transaction for which we are computing the cost
|
||||
* @param[in] inputs Map of previous transactions that have outputs we're spending
|
||||
* @param[out] flags Script verification flags
|
||||
* @return Total signature operation cost of tx
|
||||
*/
|
||||
int64_t GetTransactionSigOpCount(const CTransaction& tx, const CCoinsViewCache& inputs, int flags);
|
||||
|
||||
/**
|
||||
* Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts)
|
||||
|
||||
@@ -185,7 +185,7 @@ CBlockTemplate* BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn, in
|
||||
UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
|
||||
pblock->nBits = GetNextTargetRequired(pindexPrev, pblock, chainparams.GetConsensus(), fProofOfStake);
|
||||
pblock->nNonce = 0;
|
||||
pblocktemplate->vTxSigOpsCost[0] = GetLegacySigOpCount(pblock->vtx[0]);
|
||||
pblocktemplate->vTxSigOpsCost[0] = GetSigOpCountWithoutP2SH(pblock->vtx[0]);
|
||||
|
||||
CValidationState state;
|
||||
if (!fProofOfStake && !TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false, true)) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "main.h"
|
||||
#include "pubkey.h"
|
||||
#include "key.h"
|
||||
#include "script/script.h"
|
||||
@@ -64,4 +65,93 @@ BOOST_AUTO_TEST_CASE(GetSigOpCount)
|
||||
BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(scriptSig2), 3U);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies script execution of the zeroth scriptPubKey of tx output and
|
||||
* zeroth scriptSig and witness of tx input.
|
||||
*/
|
||||
ScriptError VerifyWithFlag(const CTransaction& output, const CMutableTransaction& input, int flags)
|
||||
{
|
||||
ScriptError error;
|
||||
CTransaction inputi(input);
|
||||
bool ret = VerifyScript(inputi.vin[0].scriptSig, output.vout[0].scriptPubKey, flags, TransactionSignatureChecker(&inputi, 0, output.vout[0].nValue), &error);
|
||||
BOOST_CHECK((ret == true) == (error == SCRIPT_ERR_OK));
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a creationTx from scriptPubKey and a spendingTx from scriptSig
|
||||
* such that spendingTx spends output zero of creationTx.
|
||||
* Also inserts creationTx's output into the coins view.
|
||||
*/
|
||||
void BuildTxs(CMutableTransaction& spendingTx, CCoinsViewCache& coins, CMutableTransaction& creationTx, const CScript& scriptPubKey, const CScript& scriptSig)
|
||||
{
|
||||
creationTx.nVersion = 1;
|
||||
creationTx.vin.resize(1);
|
||||
creationTx.vin[0].prevout.SetNull();
|
||||
creationTx.vin[0].scriptSig = CScript();
|
||||
creationTx.vout.resize(1);
|
||||
creationTx.vout[0].nValue = 1;
|
||||
creationTx.vout[0].scriptPubKey = scriptPubKey;
|
||||
|
||||
spendingTx.nVersion = 1;
|
||||
spendingTx.vin.resize(1);
|
||||
spendingTx.vin[0].prevout.hash = creationTx.GetHash();
|
||||
spendingTx.vin[0].prevout.n = 0;
|
||||
spendingTx.vin[0].scriptSig = scriptSig;
|
||||
spendingTx.vout.resize(1);
|
||||
spendingTx.vout[0].nValue = 1;
|
||||
spendingTx.vout[0].scriptPubKey = CScript();
|
||||
|
||||
coins.ModifyCoins(creationTx.GetHash())->FromTx(creationTx, 0);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
|
||||
{
|
||||
// Transaction creates outputs
|
||||
CMutableTransaction creationTx;
|
||||
// Transaction that spends outputs and whose
|
||||
// sig op cost is going to be tested
|
||||
CMutableTransaction spendingTx;
|
||||
|
||||
// Create utxo set
|
||||
CCoinsView coinsDummy;
|
||||
CCoinsViewCache coins(&coinsDummy);
|
||||
// Create key
|
||||
CKey key;
|
||||
key.MakeNewKey(true);
|
||||
CPubKey pubkey = key.GetPubKey();
|
||||
// Default flags
|
||||
int flags = SCRIPT_VERIFY_P2SH;
|
||||
|
||||
// Multisig script
|
||||
{
|
||||
CScript scriptPubKey = CScript() << 1 << ToByteVector(pubkey) << ToByteVector(pubkey) << 2 << OP_CHECKMULTISIGVERIFY;
|
||||
// Do not use a valid signature to avoid using wallet operations.
|
||||
CScript scriptSig = CScript() << OP_0 << OP_0;
|
||||
|
||||
BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig);
|
||||
// Legacy counting only includes signature operations in scriptSigs and scriptPubKeys
|
||||
// of a transaction and does not take the actual executed sig operations into account.
|
||||
// spendingTx in itself does not contain a signature operation.
|
||||
assert(GetTransactionSigOpCount(CTransaction(spendingTx), coins, flags) == 0);
|
||||
// creationTx contains two signature operations in its scriptPubKey, but legacy counting
|
||||
// is not accurate.
|
||||
assert(GetTransactionSigOpCount(CTransaction(creationTx), coins, flags) == MAX_PUBKEYS_PER_MULTISIG);
|
||||
// Sanity check: script verification fails because of an invalid signature.
|
||||
assert(VerifyWithFlag(creationTx, spendingTx, flags) == SCRIPT_ERR_CHECKMULTISIGVERIFY);
|
||||
}
|
||||
|
||||
// Multisig nested in P2SH
|
||||
{
|
||||
CScript redeemScript = CScript() << 1 << ToByteVector(pubkey) << ToByteVector(pubkey) << 2 << OP_CHECKMULTISIGVERIFY;
|
||||
CScript scriptPubKey = GetScriptForDestination(CScriptID(redeemScript));
|
||||
CScript scriptSig = CScript() << OP_0 << OP_0 << ToByteVector(redeemScript);
|
||||
|
||||
BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig));
|
||||
assert(GetTransactionSigOpCount(CTransaction(spendingTx), coins, flags) == 2);
|
||||
assert(VerifyWithFlag(creationTx, spendingTx, flags) == SCRIPT_ERR_CHECKMULTISIGVERIFY);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
Reference in New Issue
Block a user