3babbcb487
Some people keep thinking that MAX_BLOCK_BASE_SIZE is a separate size limit from the weight limit when it fact it is superfluous, and used in early tests before the witness data has been validated or just to compute worst case sizes. The size checks that use it would not behave any differently consensus wise if they were eliminated completely. Its correct value is not independently settable but is a function of the weight limit and weight formula. This patch just eliminates it and uses the scale factor as required to compute the worse case constants. It also moves the weight factor out of primitives into consensus, which is a more logical place for it.
232 lines
9.9 KiB
C++
232 lines
9.9 KiB
C++
// Copyright (c) 2012-2016 The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include "consensus/tx_verify.h"
|
|
#include "consensus/validation.h"
|
|
#include "pubkey.h"
|
|
#include "key.h"
|
|
#include "script/script.h"
|
|
#include "script/standard.h"
|
|
#include "uint256.h"
|
|
#include "test/test_bitcoin.h"
|
|
|
|
#include <vector>
|
|
|
|
#include <boost/test/unit_test.hpp>
|
|
|
|
// Helpers:
|
|
static std::vector<unsigned char>
|
|
Serialize(const CScript& s)
|
|
{
|
|
std::vector<unsigned char> sSerialized(s.begin(), s.end());
|
|
return sSerialized;
|
|
}
|
|
|
|
BOOST_FIXTURE_TEST_SUITE(sigopcount_tests, BasicTestingSetup)
|
|
|
|
BOOST_AUTO_TEST_CASE(GetSigOpCount)
|
|
{
|
|
// Test CScript::GetSigOpCount()
|
|
CScript s1;
|
|
BOOST_CHECK_EQUAL(s1.GetSigOpCount(false), 0U);
|
|
BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 0U);
|
|
|
|
uint160 dummy;
|
|
s1 << OP_1 << ToByteVector(dummy) << ToByteVector(dummy) << OP_2 << OP_CHECKMULTISIG;
|
|
BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 2U);
|
|
s1 << OP_IF << OP_CHECKSIG << OP_ENDIF;
|
|
BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 3U);
|
|
BOOST_CHECK_EQUAL(s1.GetSigOpCount(false), 21U);
|
|
|
|
CScript p2sh = GetScriptForDestination(CScriptID(s1));
|
|
CScript scriptSig;
|
|
scriptSig << OP_0 << Serialize(s1);
|
|
BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(scriptSig), 3U);
|
|
|
|
std::vector<CPubKey> keys;
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
CKey k;
|
|
k.MakeNewKey(true);
|
|
keys.push_back(k.GetPubKey());
|
|
}
|
|
CScript s2 = GetScriptForMultisig(1, keys);
|
|
BOOST_CHECK_EQUAL(s2.GetSigOpCount(true), 3U);
|
|
BOOST_CHECK_EQUAL(s2.GetSigOpCount(false), 20U);
|
|
|
|
p2sh = GetScriptForDestination(CScriptID(s2));
|
|
BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(true), 0U);
|
|
BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(false), 0U);
|
|
CScript scriptSig2;
|
|
scriptSig2 << OP_1 << ToByteVector(dummy) << ToByteVector(dummy) << Serialize(s2);
|
|
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, &inputi.vin[0].scriptWitness, 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
|
|
* and witness 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, const CScriptWitness& witness)
|
|
{
|
|
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.vin[0].scriptWitness = witness;
|
|
spendingTx.vout.resize(1);
|
|
spendingTx.vout[0].nValue = 1;
|
|
spendingTx.vout[0].scriptPubKey = CScript();
|
|
|
|
AddCoins(coins, 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_WITNESS | SCRIPT_VERIFY_P2SH;
|
|
|
|
// Multisig script (legacy counting)
|
|
{
|
|
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, CScriptWitness());
|
|
// 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(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 0);
|
|
// creationTx contains two signature operations in its scriptPubKey, but legacy counting
|
|
// is not accurate.
|
|
assert(GetTransactionSigOpCost(CTransaction(creationTx), coins, flags) == MAX_PUBKEYS_PER_MULTISIG * WITNESS_SCALE_FACTOR);
|
|
// 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, CScriptWitness());
|
|
assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 2 * WITNESS_SCALE_FACTOR);
|
|
assert(VerifyWithFlag(creationTx, spendingTx, flags) == SCRIPT_ERR_CHECKMULTISIGVERIFY);
|
|
}
|
|
|
|
// P2WPKH witness program
|
|
{
|
|
CScript p2pk = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;
|
|
CScript scriptPubKey = GetScriptForWitness(p2pk);
|
|
CScript scriptSig = CScript();
|
|
CScriptWitness scriptWitness;
|
|
scriptWitness.stack.push_back(std::vector<unsigned char>(0));
|
|
scriptWitness.stack.push_back(std::vector<unsigned char>(0));
|
|
|
|
|
|
BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness);
|
|
assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 1);
|
|
// No signature operations if we don't verify the witness.
|
|
assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags & ~SCRIPT_VERIFY_WITNESS) == 0);
|
|
assert(VerifyWithFlag(creationTx, spendingTx, flags) == SCRIPT_ERR_EQUALVERIFY);
|
|
|
|
// The sig op cost for witness version != 0 is zero.
|
|
assert(scriptPubKey[0] == 0x00);
|
|
scriptPubKey[0] = 0x51;
|
|
BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness);
|
|
assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 0);
|
|
scriptPubKey[0] = 0x00;
|
|
BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness);
|
|
|
|
// The witness of a coinbase transaction is not taken into account.
|
|
spendingTx.vin[0].prevout.SetNull();
|
|
assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 0);
|
|
}
|
|
|
|
// P2WPKH nested in P2SH
|
|
{
|
|
CScript p2pk = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;
|
|
CScript scriptSig = GetScriptForWitness(p2pk);
|
|
CScript scriptPubKey = GetScriptForDestination(CScriptID(scriptSig));
|
|
scriptSig = CScript() << ToByteVector(scriptSig);
|
|
CScriptWitness scriptWitness;
|
|
scriptWitness.stack.push_back(std::vector<unsigned char>(0));
|
|
scriptWitness.stack.push_back(std::vector<unsigned char>(0));
|
|
|
|
BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness);
|
|
assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 1);
|
|
assert(VerifyWithFlag(creationTx, spendingTx, flags) == SCRIPT_ERR_EQUALVERIFY);
|
|
}
|
|
|
|
// P2WSH witness program
|
|
{
|
|
CScript witnessScript = CScript() << 1 << ToByteVector(pubkey) << ToByteVector(pubkey) << 2 << OP_CHECKMULTISIGVERIFY;
|
|
CScript scriptPubKey = GetScriptForWitness(witnessScript);
|
|
CScript scriptSig = CScript();
|
|
CScriptWitness scriptWitness;
|
|
scriptWitness.stack.push_back(std::vector<unsigned char>(0));
|
|
scriptWitness.stack.push_back(std::vector<unsigned char>(0));
|
|
scriptWitness.stack.push_back(std::vector<unsigned char>(witnessScript.begin(), witnessScript.end()));
|
|
|
|
BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness);
|
|
assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 2);
|
|
assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags & ~SCRIPT_VERIFY_WITNESS) == 0);
|
|
assert(VerifyWithFlag(creationTx, spendingTx, flags) == SCRIPT_ERR_CHECKMULTISIGVERIFY);
|
|
}
|
|
|
|
// P2WSH nested in P2SH
|
|
{
|
|
CScript witnessScript = CScript() << 1 << ToByteVector(pubkey) << ToByteVector(pubkey) << 2 << OP_CHECKMULTISIGVERIFY;
|
|
CScript redeemScript = GetScriptForWitness(witnessScript);
|
|
CScript scriptPubKey = GetScriptForDestination(CScriptID(redeemScript));
|
|
CScript scriptSig = CScript() << ToByteVector(redeemScript);
|
|
CScriptWitness scriptWitness;
|
|
scriptWitness.stack.push_back(std::vector<unsigned char>(0));
|
|
scriptWitness.stack.push_back(std::vector<unsigned char>(0));
|
|
scriptWitness.stack.push_back(std::vector<unsigned char>(witnessScript.begin(), witnessScript.end()));
|
|
|
|
BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness);
|
|
assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 2);
|
|
assert(VerifyWithFlag(creationTx, spendingTx, flags) == SCRIPT_ERR_CHECKMULTISIGVERIFY);
|
|
}
|
|
}
|
|
|
|
BOOST_AUTO_TEST_SUITE_END()
|