diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 02ee9145476..481a258a92f 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -898,6 +898,8 @@ class CCustomParams : public CRegTestParams { std::vector man_bytes = ParseHex(args.GetArg("-con_mandatorycoinbase", "")); consensus.mandatory_coinbase_destination = CScript(man_bytes.begin(), man_bytes.end()); // Blank script allows any coinbase destination + consensus.allow_any_fee = args.GetBoolArg("-con_allow_any_fee", consensus.allow_any_fee); + // Custom chains connect coinbase outputs to db by default consensus.connect_genesis_outputs = args.GetIntArg("-con_connect_genesis_outputs", true); @@ -1515,6 +1517,8 @@ class CLiquidV1TestParams : public CLiquidV1Params { consensus.mandatory_coinbase_destination = CScript(man_bytes.begin(), man_bytes.end()); // Blank script allows any coinbase destination } + consensus.allow_any_fee = args.GetBoolArg("-con_allow_any_fee", consensus.allow_any_fee); + consensus.connect_genesis_outputs = args.GetIntArg("-con_connect_genesis_outputs", consensus.connect_genesis_outputs); initialFreeCoins = args.GetIntArg("-initialfreecoins", initialFreeCoins); diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index e3eb7dd45bf..91d062e06e7 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -45,6 +45,7 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman) argsman.AddArg("-con_signed_blocks", "Signed blockchain. Uses input of `-signblockscript` to define what signatures are necessary to solve it.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-signblockscript", "Signed blockchain enumberance. Only active when `-con_signed_blocks` set to true.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-con_max_block_sig_size", "Max allowed witness data for the signed block header.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); + argsman.AddArg("-con_allow_any_fee", "Allow any fee value for any asset", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-con_has_parent_chain", "Whether or not there is a parent chain.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-parentgenesisblockhash", "The genesis blockhash of the parent chain.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); diff --git a/src/confidential_validation.cpp b/src/confidential_validation.cpp index 05f65db3f94..a5add93d69d 100644 --- a/src/confidential_validation.cpp +++ b/src/confidential_validation.cpp @@ -24,17 +24,20 @@ class CSecp256k1Init { static CSecp256k1Init instance_of_csecp256k1; } -bool HasValidFee(const CTransaction& tx) { +bool HasValidFee(const CTransaction& tx, bool allow_any_fee) { CAmountMap totalFee; for (unsigned int i = 0; i < tx.vout.size(); i++) { CAmount fee = 0; if (tx.vout[i].IsFee()) { fee = tx.vout[i].nValue.GetAmount(); - if (fee == 0 || !MoneyRange(fee)) { + if (!allow_any_fee && (fee == 0 || !MoneyRange(fee))) { return false; } totalFee[tx.vout[i].nAsset.GetAsset()] += fee; - if (!MoneyRange(totalFee)) { + if (!allow_any_fee && !MoneyRange(totalFee)) { + return false; + } + if(allow_any_fee && fee < 0) { return false; } } diff --git a/src/confidential_validation.h b/src/confidential_validation.h index 36ec2d74556..724d28ab0a7 100644 --- a/src/confidential_validation.h +++ b/src/confidential_validation.h @@ -15,7 +15,7 @@ // Check if explicit TX fees overflow or are negative -bool HasValidFee(const CTransaction& tx); +bool HasValidFee(const CTransaction& tx, bool allow_any_fee); // Compute the fee from the explicit fee outputs. Must call HasValidFee first CAmountMap GetFeeMap(const CTransaction& tx); diff --git a/src/consensus/params.h b/src/consensus/params.h index 0162247285e..52db82dc9df 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -175,6 +175,7 @@ struct Params { size_t total_valid_epochs = 1; bool elements_mode = false; bool start_p2wsh_script = false; + bool allow_any_fee = false; }; } // namespace Consensus diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index afbcfbaaa57..64facf58d57 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -192,7 +192,7 @@ int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& i return nSigOps; } -bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmountMap& fee_map, std::set>& setPeginsSpent, std::vector *pvChecks, const bool cacheStore, bool fScriptChecks, const std::vector>& fedpegscripts) +bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmountMap& fee_map, std::set>& setPeginsSpent, std::vector *pvChecks, const bool cacheStore, bool fScriptChecks, const std::vector>& fedpegscripts, bool allow_any_fee) { // are the actual inputs available? if (!inputs.HaveInputs(tx)) { @@ -245,7 +245,7 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, if (g_con_elementsmode) { // Tally transaction fees - if (!HasValidFee(tx)) { + if (!HasValidFee(tx, allow_any_fee)) { return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-fee-outofrange"); } @@ -254,7 +254,7 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-in-ne-out", "value in != value out"); } fee_map += GetFeeMap(tx); - if (!MoneyRange(fee_map)) { + if (allow_any_fee && !MoneyRange(fee_map)) { return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-block-total-fee-outofrange"); } } else { diff --git a/src/consensus/tx_verify.h b/src/consensus/tx_verify.h index 921389638fd..5f652276f73 100644 --- a/src/consensus/tx_verify.h +++ b/src/consensus/tx_verify.h @@ -28,7 +28,7 @@ namespace Consensus { * @param[out] fee_map Set to the transaction fee if successful. * Preconditions: tx.IsCoinBase() is false. */ -[[nodiscard] ]bool CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmountMap& fee_map, std::set>& setPeginsSpent, std::vector *pvChecks, const bool cacheStore, bool fScriptChecks, const std::vector>& fedpegscripts); +[[nodiscard] ]bool CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmountMap& fee_map, std::set>& setPeginsSpent, std::vector *pvChecks, const bool cacheStore, bool fScriptChecks, const std::vector>& fedpegscripts, bool allow_any_fee); } // namespace Consensus /** Auxiliary functions for transaction validation (ideally should not be exposed) */ diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp index 22b98bf5ec1..fd095784c7b 100644 --- a/src/test/fuzz/coins_view.cpp +++ b/src/test/fuzz/coins_view.cpp @@ -252,7 +252,7 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view) } std::vector> fedpegscripts; // ELEMENTS: we ought to populate this and have a more useful fuzztest std::set > setPeginsSpent; - if (Consensus::CheckTxInputs(transaction, state, coins_view_cache, fuzzed_data_provider.ConsumeIntegralInRange(0, std::numeric_limits::max()), tx_fee_map, setPeginsSpent, NULL, false, true, fedpegscripts)) { + if (Consensus::CheckTxInputs(transaction, state, coins_view_cache, fuzzed_data_provider.ConsumeIntegralInRange(0, std::numeric_limits::max()), tx_fee_map, setPeginsSpent, NULL, false, true, fedpegscripts, false)) { assert(MoneyRange(tx_fee_map)); } }, diff --git a/src/test/pegin_witness_tests.cpp b/src/test/pegin_witness_tests.cpp index 60b011672ec..84d14fd08ca 100644 --- a/src/test/pegin_witness_tests.cpp +++ b/src/test/pegin_witness_tests.cpp @@ -123,7 +123,7 @@ BOOST_AUTO_TEST_CASE(witness_valid) CCoinsViewCache coins(&coinsDummy); // Get the latest block index to look up fedpegscripts // For these tests, should be genesis-block-hardcoded consensus.fedpegscript - BOOST_CHECK(Consensus::CheckTxInputs(tx, state, coins, 0, fee_map, setPeginsSpent, NULL, false, true, fedpegscripts)); + BOOST_CHECK(Consensus::CheckTxInputs(tx, state, coins, 0, fee_map, setPeginsSpent, NULL, false, true, fedpegscripts, false)); BOOST_CHECK(setPeginsSpent.size() == 1); setPeginsSpent.clear(); @@ -131,7 +131,7 @@ BOOST_AUTO_TEST_CASE(witness_valid) CMutableTransaction mtxn(tx); mtxn.witness.vtxinwit[0].m_pegin_witness.SetNull(); CTransaction tx2(mtxn); - BOOST_CHECK(!Consensus::CheckTxInputs(tx2, state, coins, 0, fee_map, setPeginsSpent, NULL, false, true, fedpegscripts)); + BOOST_CHECK(!Consensus::CheckTxInputs(tx2, state, coins, 0, fee_map, setPeginsSpent, NULL, false, true, fedpegscripts, false)); BOOST_CHECK(setPeginsSpent.empty()); // Invalidate peg-in (and spending) authorization by pegin marker. @@ -140,7 +140,7 @@ BOOST_AUTO_TEST_CASE(witness_valid) CMutableTransaction mtxn2(tx); mtxn2.vin[0].m_is_pegin = false; CTransaction tx3(mtxn2); - BOOST_CHECK(!Consensus::CheckTxInputs(tx3, state, coins, 0, fee_map, setPeginsSpent, NULL, false, true, fedpegscripts)); + BOOST_CHECK(!Consensus::CheckTxInputs(tx3, state, coins, 0, fee_map, setPeginsSpent, NULL, false, true, fedpegscripts, false)); BOOST_CHECK(setPeginsSpent.empty()); diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 579259c7a47..eef03b8583a 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -895,7 +895,7 @@ void CTxMemPool::check(const CBlockIndex* active_chain_tip, const CCoinsViewCach const auto& fedpegscripts = GetValidFedpegScripts(active_chain_tip, Params().GetConsensus(), true /* nextblock_validation */); bool cacheStore = true; bool fScriptChecks = true; - assert(Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, fee_map, setPeginsSpent, nullptr, cacheStore, fScriptChecks, fedpegscripts)); + assert(Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, fee_map, setPeginsSpent, nullptr, cacheStore, fScriptChecks, fedpegscripts, Params().GetConsensus().allow_any_fee)); for (const auto& input: tx.vin) mempoolDuplicate.SpendCoin(input.prevout); AddCoins(mempoolDuplicate, tx, std::numeric_limits::max()); } diff --git a/src/validation.cpp b/src/validation.cpp index d62f942720d..afee38fa745 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -855,10 +855,12 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) // The mempool holds txs for the next block, so pass height+1 to CheckTxInputs CAmountMap fee_map; - if (!Consensus::CheckTxInputs(tx, state, m_view, m_active_chainstate.m_chain.Height() + 1, fee_map, setPeginsSpent, NULL, true, true, fedpegscripts)) { + if (!Consensus::CheckTxInputs(tx, state, m_view, m_active_chainstate.m_chain.Height() + 1, fee_map, setPeginsSpent, NULL, true, true, fedpegscripts, Params().GetConsensus().allow_any_fee)) { return false; // state filled in by CheckTxInputs } + LogPrintf("mand coin: %u\n", Params().GetConsensus().allow_any_fee); + // ELEMENTS: extra policy check for consistency between issuances and their rangeproof if (fRequireStandard) { for (unsigned i = 0; i < std::min(tx.witness.vtxinwit.size(), tx.vin.size()); i++) { @@ -2350,7 +2352,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state, TxValidationState tx_state; if (!Consensus::CheckTxInputs(tx, tx_state, view, pindex->nHeight, fee_map, setPeginsSpent == NULL ? setPeginsSpentDummy : *setPeginsSpent, - g_parallel_script_checks ? &vChecks : NULL, fCacheResults, fScriptChecks, fedpegscripts)) { + g_parallel_script_checks ? &vChecks : NULL, fCacheResults, fScriptChecks, fedpegscripts, Params().GetConsensus().allow_any_fee)) { // Any transaction validation failure in ConnectBlock is a block consensus failure state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, tx_state.GetRejectReason(), tx_state.GetDebugMessage()); diff --git a/src/wallet/rpc/transactions.cpp b/src/wallet/rpc/transactions.cpp index 334ee241626..12d32c433ab 100644 --- a/src/wallet/rpc/transactions.cpp +++ b/src/wallet/rpc/transactions.cpp @@ -823,7 +823,7 @@ RPCHelpMan gettransaction() CAmountMap nCredit = CachedTxGetCredit(*pwallet, wtx, filter); CAmountMap nDebit = CachedTxGetDebit(*pwallet, wtx, filter); CAmountMap nNet = nCredit - nDebit; - CHECK_NONFATAL(HasValidFee(*wtx.tx)); + CHECK_NONFATAL(HasValidFee(*wtx.tx,Params().GetConsensus().allow_any_fee)); CAmountMap nFee = CachedTxIsFromMe(*pwallet, wtx, filter) ? CAmountMap() - GetFeeMap(*wtx.tx) : CAmountMap(); if (!g_con_elementsmode) { CAmount total_out = 0;