From 31741b339d51a0857ae9b36ffc8c15830af5bf60 Mon Sep 17 00:00:00 2001 From: Tom Trevethan Date: Fri, 18 Jul 2025 15:40:04 +0100 Subject: [PATCH 1/3] add allow any fee config --- src/chainparams.cpp | 4 ++ src/chainparamsbase.cpp | 1 + src/confidential_validation.cpp | 10 ++-- src/confidential_validation.h | 2 +- src/consensus/params.h | 1 + src/consensus/tx_verify.cpp | 6 +-- src/consensus/tx_verify.h | 2 +- src/test/fuzz/coins_view.cpp | 2 +- src/test/pegin_witness_tests.cpp | 6 +-- src/txmempool.cpp | 2 +- src/validation.cpp | 6 ++- src/wallet/rpc/transactions.cpp | 2 +- src/wallet/wallet.cpp | 2 +- test/functional/test_runner.py | 1 + test/functional/wallet_send_zero_fee.py | 61 +++++++++++++++++++++++++ 15 files changed, 91 insertions(+), 17 deletions(-) create mode 100755 test/functional/wallet_send_zero_fee.py 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 62190a92fd6..a5add93d69d 100644 --- a/src/confidential_validation.cpp +++ b/src/confidential_validation.cpp @@ -24,16 +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; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 0bc17e30fa0..1f4ffb50279 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3024,7 +3024,7 @@ std::shared_ptr CWallet::Create(WalletContext& context, const std::stri if (args.IsArgSet("-mintxfee")) { std::optional min_tx_fee = ParseMoney(args.GetArg("-mintxfee", "")); - if (!min_tx_fee || min_tx_fee.value() == 0) { + if (!min_tx_fee || (!Params().GetConsensus().allow_any_fee && min_tx_fee.value() == 0)) { error = AmountErrMsg("mintxfee", args.GetArg("-mintxfee", "")); return nullptr; } else if (min_tx_fee.value() > HIGH_TX_FEE_PER_KB) { diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index bdebbda19c3..3ff2738016f 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -356,6 +356,7 @@ 'feature_coinstatsindex.py --legacy-wallet', 'feature_coinstatsindex.py --descriptors', 'wallet_orphanedreward.py', + 'wallet_send_zero_fee.py', 'wallet_timelock.py', 'p2p_node_network_limited.py', 'p2p_permissions.py', diff --git a/test/functional/wallet_send_zero_fee.py b/test/functional/wallet_send_zero_fee.py new file mode 100755 index 00000000000..2c534acb910 --- /dev/null +++ b/test/functional/wallet_send_zero_fee.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017-2020 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +from decimal import Decimal + +from test_framework.blocktools import COINBASE_MATURITY +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_equal, +) + +class WalletTest(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = True + self.num_nodes = 3 + self.extra_args = [[ + "-blindedaddresses=1", + "-initialfreecoins=2100000000000000", + "-con_blocksubsidy=0", + "-con_connect_genesis_outputs=1", + "-txindex=1", + "-minrelaytxfee=0", + "-blockmintxfee=0", + "-mintxfee=0", + "-con_allow_any_fee=1" + ]] * self.num_nodes + self.extra_args[0].append("-anyonecanspendaremine=1") + + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + + def run_test(self): + self.generate(self.nodes[0], COINBASE_MATURITY + 1) + self.sync_all() + + self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 10) + self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 20) + self.generate(self.nodes[0], 1) + assert_equal(self.nodes[0].getblockchaininfo()["blocks"], 102) + assert_equal(self.nodes[0].getbalance(), {'bitcoin': Decimal('20999969.99900900')}) + assert_equal(self.nodes[1].getbalance(), {'bitcoin': Decimal('10.00000000')}) + assert_equal(self.nodes[2].getbalance(), {'bitcoin': Decimal('20.00000000')}) + + addr = self.nodes[1].getnewaddress() + txid = self.nodes[0].sendtoaddress(addr, 1, None, None, None, None, None, None, None, None, None, 0, False) + tx = self.nodes[0].gettransaction(txid) + assert_equal(tx["fee"]["bitcoin"], 0) + hex = self.nodes[0].getrawtransaction(txid) + self.generate(self.nodes[0], 1) + assert_equal(self.nodes[0].getblockchaininfo()["blocks"], 103) + + decoded = self.nodes[0].decoderawtransaction(hex) + bitcoin = "b2e15d0d7a0c94e4e2ce0fe6e8691b9e451377f6e46e8045a86f7c4b5d4f0f23" + assert_equal(decoded["fee"][bitcoin], 0) + assert_equal(self.nodes[0].getbalance(), {'bitcoin': Decimal('20999968.99900900')}) + assert_equal(self.nodes[1].getbalance(), {'bitcoin': Decimal('11.00000000')}) + assert_equal(self.nodes[2].getbalance(), {'bitcoin': Decimal('20.00000000')}) + +if __name__ == '__main__': + WalletTest().main() \ No newline at end of file From f1fc61f24883923762f5ac92e1d982daac7bb501 Mon Sep 17 00:00:00 2001 From: Tom Trevethan Date: Tue, 22 Jul 2025 12:34:39 +0100 Subject: [PATCH 2/3] removed mintxfee --- src/wallet/wallet.cpp | 2 +- test/functional/test_runner.py | 1 - test/functional/wallet_send_zero_fee.py | 61 ------------------------- 3 files changed, 1 insertion(+), 63 deletions(-) delete mode 100755 test/functional/wallet_send_zero_fee.py diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 1f4ffb50279..0bc17e30fa0 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3024,7 +3024,7 @@ std::shared_ptr CWallet::Create(WalletContext& context, const std::stri if (args.IsArgSet("-mintxfee")) { std::optional min_tx_fee = ParseMoney(args.GetArg("-mintxfee", "")); - if (!min_tx_fee || (!Params().GetConsensus().allow_any_fee && min_tx_fee.value() == 0)) { + if (!min_tx_fee || min_tx_fee.value() == 0) { error = AmountErrMsg("mintxfee", args.GetArg("-mintxfee", "")); return nullptr; } else if (min_tx_fee.value() > HIGH_TX_FEE_PER_KB) { diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 3ff2738016f..bdebbda19c3 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -356,7 +356,6 @@ 'feature_coinstatsindex.py --legacy-wallet', 'feature_coinstatsindex.py --descriptors', 'wallet_orphanedreward.py', - 'wallet_send_zero_fee.py', 'wallet_timelock.py', 'p2p_node_network_limited.py', 'p2p_permissions.py', diff --git a/test/functional/wallet_send_zero_fee.py b/test/functional/wallet_send_zero_fee.py deleted file mode 100755 index 2c534acb910..00000000000 --- a/test/functional/wallet_send_zero_fee.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2017-2020 The Bitcoin Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. -from decimal import Decimal - -from test_framework.blocktools import COINBASE_MATURITY -from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import ( - assert_equal, -) - -class WalletTest(BitcoinTestFramework): - def set_test_params(self): - self.setup_clean_chain = True - self.num_nodes = 3 - self.extra_args = [[ - "-blindedaddresses=1", - "-initialfreecoins=2100000000000000", - "-con_blocksubsidy=0", - "-con_connect_genesis_outputs=1", - "-txindex=1", - "-minrelaytxfee=0", - "-blockmintxfee=0", - "-mintxfee=0", - "-con_allow_any_fee=1" - ]] * self.num_nodes - self.extra_args[0].append("-anyonecanspendaremine=1") - - def skip_test_if_missing_module(self): - self.skip_if_no_wallet() - - def run_test(self): - self.generate(self.nodes[0], COINBASE_MATURITY + 1) - self.sync_all() - - self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 10) - self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 20) - self.generate(self.nodes[0], 1) - assert_equal(self.nodes[0].getblockchaininfo()["blocks"], 102) - assert_equal(self.nodes[0].getbalance(), {'bitcoin': Decimal('20999969.99900900')}) - assert_equal(self.nodes[1].getbalance(), {'bitcoin': Decimal('10.00000000')}) - assert_equal(self.nodes[2].getbalance(), {'bitcoin': Decimal('20.00000000')}) - - addr = self.nodes[1].getnewaddress() - txid = self.nodes[0].sendtoaddress(addr, 1, None, None, None, None, None, None, None, None, None, 0, False) - tx = self.nodes[0].gettransaction(txid) - assert_equal(tx["fee"]["bitcoin"], 0) - hex = self.nodes[0].getrawtransaction(txid) - self.generate(self.nodes[0], 1) - assert_equal(self.nodes[0].getblockchaininfo()["blocks"], 103) - - decoded = self.nodes[0].decoderawtransaction(hex) - bitcoin = "b2e15d0d7a0c94e4e2ce0fe6e8691b9e451377f6e46e8045a86f7c4b5d4f0f23" - assert_equal(decoded["fee"][bitcoin], 0) - assert_equal(self.nodes[0].getbalance(), {'bitcoin': Decimal('20999968.99900900')}) - assert_equal(self.nodes[1].getbalance(), {'bitcoin': Decimal('11.00000000')}) - assert_equal(self.nodes[2].getbalance(), {'bitcoin': Decimal('20.00000000')}) - -if __name__ == '__main__': - WalletTest().main() \ No newline at end of file From 15d09d09ad115843ff2297f60f4ae57fde093b7f Mon Sep 17 00:00:00 2001 From: Tom Trevethan Date: Wed, 10 Sep 2025 15:21:30 +0100 Subject: [PATCH 3/3] remove config option and apply for policy asset --- src/chainparams.cpp | 4 ---- src/chainparamsbase.cpp | 1 - src/confidential_validation.cpp | 11 ++++++----- src/confidential_validation.h | 2 +- src/consensus/params.h | 1 - src/consensus/tx_verify.cpp | 7 ++++--- src/consensus/tx_verify.h | 2 +- src/test/fuzz/coins_view.cpp | 2 +- src/test/pegin_witness_tests.cpp | 6 +++--- src/txmempool.cpp | 2 +- src/validation.cpp | 6 ++---- src/wallet/rpc/transactions.cpp | 2 +- 12 files changed, 20 insertions(+), 26 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 481a258a92f..02ee9145476 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -898,8 +898,6 @@ 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); @@ -1517,8 +1515,6 @@ 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 91d062e06e7..e3eb7dd45bf 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -45,7 +45,6 @@ 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 a5add93d69d..1d902565f72 100644 --- a/src/confidential_validation.cpp +++ b/src/confidential_validation.cpp @@ -1,4 +1,5 @@ +#include #include #include #include @@ -24,20 +25,20 @@ class CSecp256k1Init { static CSecp256k1Init instance_of_csecp256k1; } -bool HasValidFee(const CTransaction& tx, bool allow_any_fee) { +bool HasValidFee(const CTransaction& tx) { CAmountMap totalFee; for (unsigned int i = 0; i < tx.vout.size(); i++) { CAmount fee = 0; - if (tx.vout[i].IsFee()) { + if (tx.vout[i].IsFee() && tx.vout[i].nAsset.GetAsset() == Params().GetConsensus().pegged_asset) { fee = tx.vout[i].nValue.GetAmount(); - if (!allow_any_fee && (fee == 0 || !MoneyRange(fee))) { + if (fee == 0 || !MoneyRange(fee)) { return false; } totalFee[tx.vout[i].nAsset.GetAsset()] += fee; - if (!allow_any_fee && !MoneyRange(totalFee)) { + if (!MoneyRange(totalFee)) { return false; } - if(allow_any_fee && fee < 0) { + if(fee < 0) { return false; } } diff --git a/src/confidential_validation.h b/src/confidential_validation.h index 724d28ab0a7..36ec2d74556 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 allow_any_fee); +bool HasValidFee(const CTransaction& tx); // 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 52db82dc9df..0162247285e 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -175,7 +175,6 @@ 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 64facf58d57..770ca9a9bbf 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -14,6 +14,7 @@ #include