diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 45bfdf7c93..cdcb786d8e 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -138,7 +138,8 @@ BITCOIN_TESTS =\ test/versionbits_tests.cpp \ test/uint256_tests.cpp \ test/univalue_tests.cpp \ - test/util_tests.cpp + test/util_tests.cpp \ + test/verify_amounts_tests.cpp if ENABLE_WALLET BITCOIN_TESTS += \ diff --git a/src/test/verify_amounts_tests.cpp b/src/test/verify_amounts_tests.cpp new file mode 100644 index 0000000000..e7ba23bf8b --- /dev/null +++ b/src/test/verify_amounts_tests.cpp @@ -0,0 +1,93 @@ +// Copyright (c) 2017-2017 Blockstream Inc. +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "arith_uint256.h" +#include "blind.h" +#include "uint256.h" +#include "validation.h" + +#include "test/test_bitcoin.h" + +#include + + +BOOST_FIXTURE_TEST_SUITE(amount_tests, TestingSetup) + +BOOST_AUTO_TEST_CASE(verify_coinbase_test) +{ + // Generate some asset types and feemap + std::vector types; + CAmountMap mapFees; + for (unsigned int i = 1; i < 4; i++) { + types.emplace_back(GetRandHash()); + mapFees[types.back()] = i; + } + + // Make coinbase transaction + CMutableTransaction tx; + tx.vin.push_back(CTxIn(COutPoint(), CScript(), 0)); + + // Blank amount check + BOOST_CHECK(VerifyCoinbaseAmount(tx, CAmountMap())); + + // Check against non-null fee, surplus is ok + BOOST_CHECK(VerifyCoinbaseAmount(tx, mapFees)); + + // Add non-fee outputs to coinbase + for (unsigned int i = 1; i < 4; i++) { + BOOST_CHECK(VerifyCoinbaseAmount(tx, mapFees)); + tx.vout.push_back(CTxOut(types[i-1], i, CScript() << OP_TRUE)); + } + // All outputs added, should still balance + BOOST_CHECK(VerifyCoinbaseAmount(tx, mapFees)); + + // Adding 0-value unspendable output should not change balance + tx.vout.push_back(CTxOut(types.back(), 0, CScript() << OP_RETURN)); + BOOST_CHECK(VerifyCoinbaseAmount(tx, mapFees)); + + // But you cannot add spendable 0-value output + tx.vout.push_back(CTxOut(types.back(), 0, CScript() << OP_TRUE)); + BOOST_CHECK(!VerifyCoinbaseAmount(tx, mapFees)); + tx.vout.pop_back(); + BOOST_CHECK(VerifyCoinbaseAmount(tx, mapFees)); + + // Adding values outside MAX_MONEY also causes failure + mapFees[CAsset()] = MAX_MONEY+1; + tx.vout.push_back(CTxOut(CAsset(), MAX_MONEY+1, CScript() << OP_RETURN)); + BOOST_CHECK(!VerifyCoinbaseAmount(tx, mapFees)); + tx.vout.pop_back(); + mapFees[CAsset()] = 0; + BOOST_CHECK(VerifyCoinbaseAmount(tx, mapFees)); + + // Change the unspendable 0-value output to have asset commitment + secp256k1_generator gen; + const unsigned char blind[32] = {0}; + CAsset asset = tx.vout.back().nAsset.GetAsset(); + + BlindAsset(tx.vout.back().nAsset, gen, asset, blind); + + BOOST_CHECK(!VerifyCoinbaseAmount(tx, mapFees)); + tx.vout.back().nAsset = types.back(); + BOOST_CHECK(VerifyCoinbaseAmount(tx, mapFees)); + + // Change the same output's value to an unblinded commitment to non-zero value (unblinded zero is blank commitment) + secp256k1_pedersen_commitment commit; + CreateValueCommitment(tx.vout.back().nValue, commit, blind, gen, 1); + BOOST_CHECK(!VerifyCoinbaseAmount(tx, mapFees)); + + tx.vout.pop_back(); + BOOST_CHECK(VerifyCoinbaseAmount(tx, mapFees)); + + // Negative output + tx.vout.push_back(CTxOut(CAsset(), -1, CScript() << OP_RETURN)); + BOOST_CHECK(!VerifyCoinbaseAmount(tx, mapFees)); + tx.vout.pop_back(); + + // Transaction claiming too much fees + tx.vout.push_back(CTxOut(CAsset(), 1, CScript() << OP_RETURN)); + BOOST_CHECK(!VerifyCoinbaseAmount(tx, mapFees)); + tx.vout.pop_back(); +} + +BOOST_AUTO_TEST_SUITE_END()