diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp index f68016b55..9087155d7 100644 --- a/src/interfaces/wallet.cpp +++ b/src/interfaces/wallet.cpp @@ -426,15 +426,16 @@ public: } return result; } - CAmount getRequiredFee(unsigned int tx_bytes) override { return GetRequiredFee(*m_wallet, tx_bytes); } + CAmount getRequiredFee(unsigned int tx_bytes, uint64_t mweb_weight) override { return GetRequiredFee(*m_wallet, tx_bytes, mweb_weight); } CAmount getMinimumFee(unsigned int tx_bytes, + uint64_t mweb_weight, const CCoinControl& coin_control, int* returned_target, FeeReason* reason) override { FeeCalculation fee_calc; CAmount result; - result = GetMinimumFee(*m_wallet, tx_bytes, coin_control, &fee_calc); + result = GetMinimumFee(*m_wallet, tx_bytes, mweb_weight, coin_control, &fee_calc); if (returned_target) *returned_target = fee_calc.returnedTarget; if (reason) *reason = fee_calc.reason; return result; diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h index 6ccfd7fc2..07c0689c2 100644 --- a/src/interfaces/wallet.h +++ b/src/interfaces/wallet.h @@ -235,10 +235,11 @@ public: virtual std::vector getCoins(const std::vector& outputs) = 0; //! Get required fee. - virtual CAmount getRequiredFee(unsigned int tx_bytes) = 0; + virtual CAmount getRequiredFee(unsigned int tx_bytes, uint64_t mweb_weight) = 0; //! Get minimum fee. virtual CAmount getMinimumFee(unsigned int tx_bytes, + uint64_t mweb_weight, const CCoinControl& coin_control, int* returned_target, FeeReason* reason) = 0; diff --git a/src/miner.cpp b/src/miner.cpp index 41a835f70..434e20a61 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -89,6 +89,7 @@ void BlockAssembler::resetBlock() // Reserve space for coinbase tx nBlockWeight = 4000; nBlockSigOpsCost = 400; + nBlockMWEBWeight = 0; fIncludeWitness = false; // These counters do not include coinbase tx @@ -98,6 +99,7 @@ void BlockAssembler::resetBlock() Optional BlockAssembler::m_last_block_num_txs{nullopt}; Optional BlockAssembler::m_last_block_weight{nullopt}; +Optional BlockAssembler::m_last_block_mweb_weight{nullopt}; std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn) { @@ -153,6 +155,7 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc m_last_block_num_txs = nBlockTx; m_last_block_weight = nBlockWeight; + m_last_block_mweb_weight = nBlockMWEBWeight; // Create coinbase transaction. CMutableTransaction coinbaseTx; @@ -166,7 +169,7 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc pblocktemplate->vchCoinbaseCommitment = GenerateCoinbaseCommitment(*pblock, pindexPrev, chainparams.GetConsensus()); pblocktemplate->vTxFees[0] = -nFees; - LogPrintf("CreateNewBlock(): block weight: %u txs: %u fees: %ld sigops %d\n", GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost); + LogPrintf("CreateNewBlock(): block weight: %u txs: %u fees: %ld sigops: %d MWEB weight: %u\n", GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost, nBlockMWEBWeight); // Fill in header pblock->hashPrevBlock = pindexPrev->GetBlockHash(); @@ -199,13 +202,15 @@ void BlockAssembler::onlyUnconfirmed(CTxMemPool::setEntries& testSet) } } -bool BlockAssembler::TestPackage(uint64_t packageSize, int64_t packageSigOpsCost) const +bool BlockAssembler::TestPackage(uint64_t packageSize, int64_t packageSigOpsCost, int64_t packageMWEBWeight) const { // TODO: switch to weight-based accounting for packages instead of vsize-based accounting. if (nBlockWeight + WITNESS_SCALE_FACTOR * packageSize >= nBlockMaxWeight) return false; if (nBlockSigOpsCost + packageSigOpsCost >= MAX_BLOCK_SIGOPS_COST) return false; + if (nBlockMWEBWeight + packageMWEBWeight >= mw::MAX_BLOCK_WEIGHT) + return false; return true; } @@ -232,13 +237,14 @@ void BlockAssembler::AddToBlock(CTxMemPool::txiter iter) nBlockWeight += iter->GetTxWeight(); ++nBlockTx; nBlockSigOpsCost += iter->GetSigOpCost(); + nBlockMWEBWeight += iter->GetMWEBWeight(); nFees += iter->GetFee(); inBlock.insert(iter); bool fPrintPriority = gArgs.GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY); if (fPrintPriority) { LogPrintf("fee %s txid %s\n", - CFeeRate(iter->GetModifiedFee(), iter->GetTxSize()).ToString(), + CFeeRate(iter->GetModifiedFee(), iter->GetTxSize(), iter->GetMWEBWeight()).ToString(), iter->GetTx().GetHash().ToString()); } } @@ -261,6 +267,7 @@ int BlockAssembler::UpdatePackagesForAdded(const CTxMemPool::setEntries& already modEntry.nSizeWithAncestors -= it->GetTxSize(); modEntry.nModFeesWithAncestors -= it->GetModifiedFee(); modEntry.nSigOpCostWithAncestors -= it->GetSigOpCost(); + modEntry.nMWEBWeightWithAncestors -= it->GetMWEBWeight(); mapModifiedTx.insert(modEntry); } else { mapModifiedTx.modify(mit, update_for_parent_inclusion(it)); @@ -368,18 +375,20 @@ void BlockAssembler::addPackageTxs(int &nPackagesSelected, int &nDescendantsUpda uint64_t packageSize = iter->GetSizeWithAncestors(); CAmount packageFees = iter->GetModFeesWithAncestors(); int64_t packageSigOpsCost = iter->GetSigOpCostWithAncestors(); + int64_t packageMWEBWeight = iter->GetMWEBWeightWithAncestors(); if (fUsingModified) { packageSize = modit->nSizeWithAncestors; packageFees = modit->nModFeesWithAncestors; packageSigOpsCost = modit->nSigOpCostWithAncestors; + packageMWEBWeight = modit->nMWEBWeightWithAncestors; } - if (packageFees < blockMinFeeRate.GetFee(packageSize)) { + if (packageFees < blockMinFeeRate.GetTotalFee(packageSize, packageMWEBWeight)) { // Everything else we might consider has a lower fee rate return; } - if (!TestPackage(packageSize, packageSigOpsCost)) { + if (!TestPackage(packageSize, packageSigOpsCost, packageMWEBWeight)) { if (fUsingModified) { // Since we always look at the best entry in mapModifiedTx, // we must erase failed entries so that we can consider the diff --git a/src/miner.h b/src/miner.h index bb7a30b18..544ac09c6 100644 --- a/src/miner.h +++ b/src/miner.h @@ -42,18 +42,22 @@ struct CTxMemPoolModifiedEntry { nSizeWithAncestors = entry->GetSizeWithAncestors(); nModFeesWithAncestors = entry->GetModFeesWithAncestors(); nSigOpCostWithAncestors = entry->GetSigOpCostWithAncestors(); + nMWEBWeightWithAncestors = entry->GetMWEBWeightWithAncestors(); } int64_t GetModifiedFee() const { return iter->GetModifiedFee(); } uint64_t GetSizeWithAncestors() const { return nSizeWithAncestors; } CAmount GetModFeesWithAncestors() const { return nModFeesWithAncestors; } + int64_t GetMWEBWeightWithAncestors() const { return nMWEBWeightWithAncestors; } size_t GetTxSize() const { return iter->GetTxSize(); } + uint64_t GetMWEBWeight() const { return iter->GetMWEBWeight(); } const CTransaction& GetTx() const { return iter->GetTx(); } CTxMemPool::txiter iter; uint64_t nSizeWithAncestors; CAmount nModFeesWithAncestors; int64_t nSigOpCostWithAncestors; + int64_t nMWEBWeightWithAncestors; }; /** Comparator for CTxMemPool::txiter objects. @@ -117,6 +121,7 @@ struct update_for_parent_inclusion e.nModFeesWithAncestors -= iter->GetFee(); e.nSizeWithAncestors -= iter->GetTxSize(); e.nSigOpCostWithAncestors -= iter->GetSigOpCost(); + e.nMWEBWeightWithAncestors -= iter->GetMWEBWeight(); } CTxMemPool::txiter iter; @@ -138,6 +143,7 @@ private: uint64_t nBlockWeight; uint64_t nBlockTx; uint64_t nBlockSigOpsCost; + uint64_t nBlockMWEBWeight; CAmount nFees; CTxMemPool::setEntries inBlock; @@ -162,6 +168,7 @@ public: static Optional m_last_block_num_txs; static Optional m_last_block_weight; + static Optional m_last_block_mweb_weight; private: // utility functions @@ -180,7 +187,7 @@ private: /** Remove confirmed (inBlock) entries from given set */ void onlyUnconfirmed(CTxMemPool::setEntries& testSet); /** Test if a new package would "fit" in the block */ - bool TestPackage(uint64_t packageSize, int64_t packageSigOpsCost) const; + bool TestPackage(uint64_t packageSize, int64_t packageSigOpsCost, int64_t packageMWEBWeight) const; /** Perform checks on each transaction in a package: * locktime, premature-witness, serialized size (if necessary) * These checks should always succeed, and they're here diff --git a/src/net_processing.cpp b/src/net_processing.cpp index b33c311f5..8ee530608 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -4420,7 +4420,7 @@ bool PeerManager::SendMessages(CNode* pto) CInv inv(state.m_wtxid_relay ? MSG_WTX : MSG_TX, hash); pto->m_tx_relay->setInventoryTxToSend.erase(hash); // Don't send transactions that peers will not put into their mempool - if (txinfo.fee < filterrate.GetFee(txinfo.vsize)) { + if (txinfo.fee < filterrate.GetTotalFee(txinfo.vsize, txinfo.mweb_weight)) { continue; } if (pto->m_tx_relay->pfilter) { @@ -4479,7 +4479,7 @@ bool PeerManager::SendMessages(CNode* pto) auto txid = txinfo.tx->GetHash(); auto wtxid = txinfo.tx->GetWitnessHash(); // Peer told you to not send transactions at that feerate? Don't bother sending it. - if (txinfo.fee < filterrate.GetFee(txinfo.vsize)) { + if (txinfo.fee < filterrate.GetTotalFee(txinfo.vsize, txinfo.mweb_weight)) { continue; } if (pto->m_tx_relay->pfilter && !pto->m_tx_relay->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue; diff --git a/src/node/psbt.cpp b/src/node/psbt.cpp index c18901826..b395af12d 100644 --- a/src/node/psbt.cpp +++ b/src/node/psbt.cpp @@ -137,7 +137,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx) size_t size = GetVirtualTransactionSize(ctx, GetTransactionSigOpCost(ctx, view, STANDARD_SCRIPT_VERIFY_FLAGS)); result.estimated_vsize = size; // Estimate fee rate - CFeeRate feerate(fee, size); + CFeeRate feerate(fee, size, ctx.mweb_tx.GetMWEBWeight()); result.estimated_feerate = feerate; } diff --git a/src/policy/feerate.cpp b/src/policy/feerate.cpp index 04e0e117a..7963cc5a9 100644 --- a/src/policy/feerate.cpp +++ b/src/policy/feerate.cpp @@ -7,15 +7,26 @@ #include -CFeeRate::CFeeRate(const CAmount& nFeePaid, size_t nBytes_) +static const CAmount BASE_MWEB_FEE = 100'000; + +CFeeRate::CFeeRate(const CAmount& nFeePaid, size_t nBytes_, uint64_t mweb_weight) + : m_nFeePaid(nFeePaid), m_nBytes(nBytes_), m_weight(mweb_weight) { assert(nBytes_ <= uint64_t(std::numeric_limits::max())); - int64_t nSize = int64_t(nBytes_); + assert(mweb_weight <= uint64_t(std::numeric_limits::max())); - if (nSize > 0) - nSatoshisPerK = nFeePaid * 1000 / nSize; - else + CAmount mweb_fee = CAmount(mweb_weight) * BASE_MWEB_FEE; + if (mweb_fee > 0 && nFeePaid < mweb_fee) { nSatoshisPerK = 0; + } else { + CAmount ltc_fee = (nFeePaid - mweb_fee); + + int64_t nSize = int64_t(nBytes_); + if (nSize > 0) + nSatoshisPerK = ltc_fee * 1000 / nSize; + else + nSatoshisPerK = 0; + } } CAmount CFeeRate::GetFee(size_t nBytes_) const @@ -35,6 +46,35 @@ CAmount CFeeRate::GetFee(size_t nBytes_) const return nFee; } +CAmount CFeeRate::GetMWEBFee(uint64_t mweb_weight) const +{ + assert(mweb_weight <= uint64_t(std::numeric_limits::max())); + return CAmount(mweb_weight) * BASE_MWEB_FEE; +} + +CAmount CFeeRate::GetTotalFee(size_t nBytes, uint64_t mweb_weight) const +{ + return GetFee(nBytes) + GetMWEBFee(mweb_weight); +} + +bool CFeeRate::MeetsFeePerK(const CAmount& min_fee_per_k) const +{ + // (mweb_weight * BASE_MWEB_FEE) litoshis are required as fee for MWEB transactions. + // Anything beyond that can be used to calculate nSatoshisPerK. + CAmount mweb_fee = CAmount(m_weight) * BASE_MWEB_FEE; + if (m_weight > 0 && m_nFeePaid < mweb_fee) { + return false; + } + + // MWEB-to-MWEB transactions don't have a size to calculate nSatoshisPerK. + // Since we got this far, we know the transaction meets the minimum MWEB fee, so return true. + if (m_nBytes == 0 && m_weight > 0) { + return true; + } + + return nSatoshisPerK >= min_fee_per_k; +} + std::string CFeeRate::ToString(const FeeEstimateMode& fee_estimate_mode) const { switch (fee_estimate_mode) { diff --git a/src/policy/feerate.h b/src/policy/feerate.h index 88bc39209..21fd71c60 100644 --- a/src/policy/feerate.h +++ b/src/policy/feerate.h @@ -30,6 +30,9 @@ class CFeeRate { private: CAmount nSatoshisPerK; // unit is satoshis-per-1,000-bytes + CAmount m_nFeePaid; + size_t m_nBytes; + uint64_t m_weight; public: /** Fee rate of 0 satoshis per kB */ @@ -49,15 +52,26 @@ public: * @param[in] nBytes size_t bytes (units) to construct with * @returns fee rate */ - CFeeRate(const CAmount& nFeePaid, size_t nBytes); + CFeeRate(const CAmount& nFeePaid, size_t nBytes_, uint64_t mweb_weight); /** * Return the fee in satoshis for the given size in bytes. */ - CAmount GetFee(size_t nBytes) const; + CAmount GetFee(size_t nBytes_) const; + /** + * Return the fee in satoshis for the given MWEB weight. + */ + CAmount GetMWEBFee(uint64_t mweb_weight) const; + /** + * Return the fee in satoshis for the given size in bytes & MWEB weight. + */ + CAmount GetTotalFee(size_t nBytes, uint64_t mweb_weight) const; /** * Return the fee in satoshis for a size of 1000 bytes */ CAmount GetFeePerK() const { return GetFee(1000); } + + bool MeetsFeePerK(const CAmount& min_fee_per_k) const; + friend bool operator<(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK < b.nSatoshisPerK; } friend bool operator>(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK > b.nSatoshisPerK; } friend bool operator==(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK == b.nSatoshisPerK; } diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index 0f31093db..7ed9f319b 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -534,7 +534,7 @@ void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, boo trackedTxs++; // Feerates are stored and reported as BTC-per-kb: - CFeeRate feeRate(entry.GetFee(), entry.GetTxSize()); + CFeeRate feeRate(entry.GetFee(), entry.GetTxSize(), entry.GetMWEBWeight()); mapMemPoolTxs[hash].blockHeight = txHeight; unsigned int bucketIndex = feeStats->NewTx(txHeight, (double)feeRate.GetFeePerK()); @@ -564,7 +564,7 @@ bool CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxM } // Feerates are stored and reported as BTC-per-kb: - CFeeRate feeRate(entry->GetFee(), entry->GetTxSize()); + CFeeRate feeRate(entry->GetFee(), entry->GetTxSize(), entry->GetMWEBWeight()); feeStats->Record(blocksToConfirm, (double)feeRate.GetFeePerK()); shortStats->Record(blocksToConfirm, (double)feeRate.GetFeePerK()); diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 7c7285850..cde31204d 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -469,6 +469,9 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel * // calculation if (nQuantity > 0) { + // MW: TODO - Implement byte & fee estimation for MWEB + uint64_t mweb_weight = 0; + // Bytes nBytes = nBytesInputs + ((CoinControlDialog::payAmounts.size() > 0 ? CoinControlDialog::payAmounts.size() + 1 : 2) * 34) + 10; // always assume +1 output for change here if (fWitness) @@ -486,7 +489,7 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel * nBytes -= 34; // Fee - nPayFee = model->wallet().getMinimumFee(nBytes, m_coin_control, nullptr /* returned_target */, nullptr /* reason */); + nPayFee = model->wallet().getMinimumFee(nBytes, mweb_weight, m_coin_control, nullptr /* returned_target */, nullptr /* reason */); if (nPayAmount > 0) { diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index b22c71520..bc7361abf 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -179,7 +179,7 @@ void SendCoinsDialog::setModel(WalletModel *_model) // Litecoin: Disable RBF // connect(ui->optInRBF, &QCheckBox::stateChanged, this, &SendCoinsDialog::updateSmartFeeLabel); // connect(ui->optInRBF, &QCheckBox::stateChanged, this, &SendCoinsDialog::coinControlUpdateLabels); - CAmount requiredFee = model->wallet().getRequiredFee(1000); + CAmount requiredFee = model->wallet().getRequiredFee(1000, 0); ui->customFee->SetMinValue(requiredFee); if (ui->customFee->value() < requiredFee) { ui->customFee->setValue(requiredFee); @@ -765,7 +765,7 @@ void SendCoinsDialog::updateSmartFeeLabel() m_coin_control->m_feerate.reset(); // Explicitly use only fee estimation rate for smart fee labels int returned_target; FeeReason reason; - CFeeRate feeRate = CFeeRate(model->wallet().getMinimumFee(1000, *m_coin_control, &returned_target, &reason)); + CFeeRate feeRate = CFeeRate(model->wallet().getMinimumFee(1000, 0, *m_coin_control, &returned_target, &reason)); ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK()) + "/kB"); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 35d68ff94..cbad14bba 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -429,6 +429,7 @@ static RPCHelpMan getmininginfo() UniValue obj(UniValue::VOBJ); obj.pushKV("blocks", (int)::ChainActive().Height()); if (BlockAssembler::m_last_block_weight) obj.pushKV("currentblockweight", *BlockAssembler::m_last_block_weight); + if (BlockAssembler::m_last_block_mweb_weight) obj.pushKV("currentblockmwebweight", *BlockAssembler::m_last_block_mweb_weight); if (BlockAssembler::m_last_block_num_txs) obj.pushKV("currentblocktx", *BlockAssembler::m_last_block_num_txs); obj.pushKV("difficulty", (double)GetDifficulty(::ChainActive().Tip())); obj.pushKV("networkhashps", getnetworkhashps().HandleRequest(request)); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 395e8562a..32eb7700d 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -858,7 +858,7 @@ static RPCHelpMan sendrawtransaction() CFeeRate(AmountFromValue(request.params[1])); int64_t virtual_size = GetVirtualTransactionSize(*tx); - CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size); + CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetTotalFee(virtual_size, tx->mweb_tx.GetMWEBWeight()); std::string err_string; AssertLockNotHeld(cs_main); @@ -939,7 +939,7 @@ static RPCHelpMan testmempoolaccept() CTxMemPool& mempool = EnsureMemPool(request.context); int64_t virtual_size = GetVirtualTransactionSize(*tx); - CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size); + CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetTotalFee(virtual_size, tx->mweb_tx.GetMWEBWeight()); UniValue result(UniValue::VARR); UniValue result_0(UniValue::VOBJ); diff --git a/src/test/amount_tests.cpp b/src/test/amount_tests.cpp index 8d515cda3..e22ad7a17 100644 --- a/src/test/amount_tests.cpp +++ b/src/test/amount_tests.cpp @@ -69,21 +69,21 @@ BOOST_AUTO_TEST_CASE(GetFeeTest) BOOST_CHECK_EQUAL(feeRate.GetFee(100), altFeeRate.GetFee(100)); // Check full constructor - BOOST_CHECK(CFeeRate(CAmount(-1), 0) == CFeeRate(0)); - BOOST_CHECK(CFeeRate(CAmount(0), 0) == CFeeRate(0)); - BOOST_CHECK(CFeeRate(CAmount(1), 0) == CFeeRate(0)); + BOOST_CHECK(CFeeRate(CAmount(-1), 0, 0) == CFeeRate(0)); + BOOST_CHECK(CFeeRate(CAmount(0), 0, 0) == CFeeRate(0)); + BOOST_CHECK(CFeeRate(CAmount(1), 0, 0) == CFeeRate(0)); // default value - BOOST_CHECK(CFeeRate(CAmount(-1), 1000) == CFeeRate(-1)); - BOOST_CHECK(CFeeRate(CAmount(0), 1000) == CFeeRate(0)); - BOOST_CHECK(CFeeRate(CAmount(1), 1000) == CFeeRate(1)); + BOOST_CHECK(CFeeRate(CAmount(-1), 1000, 0) == CFeeRate(-1)); + BOOST_CHECK(CFeeRate(CAmount(0), 1000, 0) == CFeeRate(0)); + BOOST_CHECK(CFeeRate(CAmount(1), 1000, 0) == CFeeRate(1)); // lost precision (can only resolve satoshis per kB) - BOOST_CHECK(CFeeRate(CAmount(1), 1001) == CFeeRate(0)); - BOOST_CHECK(CFeeRate(CAmount(2), 1001) == CFeeRate(1)); + BOOST_CHECK(CFeeRate(CAmount(1), 1001, 0) == CFeeRate(0)); + BOOST_CHECK(CFeeRate(CAmount(2), 1001, 0) == CFeeRate(1)); // some more integer checks - BOOST_CHECK(CFeeRate(CAmount(26), 789) == CFeeRate(32)); - BOOST_CHECK(CFeeRate(CAmount(27), 789) == CFeeRate(34)); + BOOST_CHECK(CFeeRate(CAmount(26), 789, 0) == CFeeRate(32)); + BOOST_CHECK(CFeeRate(CAmount(27), 789, 0) == CFeeRate(34)); // Maximum size in bytes, should not crash - CFeeRate(MAX_MONEY, std::numeric_limits::max() >> 1).GetFeePerK(); + CFeeRate(MAX_MONEY, std::numeric_limits::max() >> 1, std::numeric_limits::max() >> 1).GetFeePerK(); } BOOST_AUTO_TEST_CASE(BinaryOperatorTest) diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp index 38fed51af..e10d558b1 100644 --- a/src/test/mempool_tests.cpp +++ b/src/test/mempool_tests.cpp @@ -471,7 +471,7 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) BOOST_CHECK(!pool.exists(tx2.GetHash())); BOOST_CHECK(!pool.exists(tx3.GetHash())); - CFeeRate maxFeeRateRemoved(25000, GetVirtualTransactionSize(CTransaction(tx3)) + GetVirtualTransactionSize(CTransaction(tx2))); + CFeeRate maxFeeRateRemoved(25000, GetVirtualTransactionSize(CTransaction(tx3)) + GetVirtualTransactionSize(CTransaction(tx2)), 0); BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000); CMutableTransaction tx4 = CMutableTransaction(); diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp index 06877898a..fd5699ee7 100644 --- a/src/test/policyestimator_tests.cpp +++ b/src/test/policyestimator_tests.cpp @@ -44,7 +44,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) tx.vin[0].scriptSig = garbage; tx.vout.resize(1); tx.vout[0].nValue=0LL; - CFeeRate baseRate(basefee, GetVirtualTransactionSize(CTransaction(tx))); + CFeeRate baseRate(basefee, GetVirtualTransactionSize(CTransaction(tx)), 0); // Create a fake block std::vector block; diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 55f0234da..bdff9be7b 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -22,12 +22,13 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee, int64_t _nTime, unsigned int _entryHeight, bool _spendsCoinbase, int64_t _sigOpsCost, LockPoints lp) - : tx(_tx), nFee(_nFee), nTxWeight(GetTransactionWeight(*tx)), nUsageSize(RecursiveDynamicUsage(tx)), nTime(_nTime), entryHeight(_entryHeight), + : tx(_tx), nFee(_nFee), nTxWeight(GetTransactionWeight(*tx)), mweb_weight(tx->mweb_tx.GetMWEBWeight()), nUsageSize(RecursiveDynamicUsage(tx)), nTime(_nTime), entryHeight(_entryHeight), spendsCoinbase(_spendsCoinbase), sigOpCost(_sigOpsCost), lockPoints(lp), m_epoch(0) { nCountWithDescendants = 1; nSizeWithDescendants = GetTxSize(); nModFeesWithDescendants = nFee; + nMWEBWeightWithDescendants = mweb_weight; feeDelta = 0; @@ -35,6 +36,7 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFe nSizeWithAncestors = GetTxSize(); nModFeesWithAncestors = nFee; nSigOpCostWithAncestors = sigOpCost; + nMWEBWeightWithAncestors = mweb_weight; } void CTxMemPoolEntry::UpdateFeeDelta(int64_t newFeeDelta) @@ -86,17 +88,19 @@ void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendan int64_t modifySize = 0; CAmount modifyFee = 0; int64_t modifyCount = 0; + int64_t modifyMWEBWeight = 0; for (const CTxMemPoolEntry& descendant : descendants) { if (!setExclude.count(descendant.GetTx().GetHash())) { modifySize += descendant.GetTxSize(); modifyFee += descendant.GetModifiedFee(); + modifyMWEBWeight += descendant.GetMWEBWeight(); modifyCount++; cachedDescendants[updateIt].insert(mapTx.iterator_to(descendant)); // Update ancestor state for each descendant - mapTx.modify(mapTx.iterator_to(descendant), update_ancestor_state(updateIt->GetTxSize(), updateIt->GetModifiedFee(), 1, updateIt->GetSigOpCost())); + mapTx.modify(mapTx.iterator_to(descendant), update_ancestor_state(updateIt->GetTxSize(), updateIt->GetModifiedFee(), 1, updateIt->GetSigOpCost(), updateIt->GetMWEBWeight())); } } - mapTx.modify(updateIt, update_descendant_state(modifySize, modifyFee, modifyCount)); + mapTx.modify(updateIt, update_descendant_state(modifySize, modifyFee, modifyCount, modifyMWEBWeight)); } // vHashesToUpdate is the set of transaction hashes from a disconnected block @@ -224,8 +228,9 @@ void CTxMemPool::UpdateAncestorsOf(bool add, txiter it, setEntries &setAncestors const int64_t updateCount = (add ? 1 : -1); const int64_t updateSize = updateCount * it->GetTxSize(); const CAmount updateFee = updateCount * it->GetModifiedFee(); + const int64_t updateMWEBWeight = updateCount * it->GetMWEBWeight(); for (txiter ancestorIt : setAncestors) { - mapTx.modify(ancestorIt, update_descendant_state(updateSize, updateFee, updateCount)); + mapTx.modify(ancestorIt, update_descendant_state(updateSize, updateFee, updateCount, updateMWEBWeight)); } } @@ -235,12 +240,14 @@ void CTxMemPool::UpdateEntryForAncestors(txiter it, const setEntries &setAncesto int64_t updateSize = 0; CAmount updateFee = 0; int64_t updateSigOpsCost = 0; + int64_t updateMWEBWeight = 0; for (txiter ancestorIt : setAncestors) { updateSize += ancestorIt->GetTxSize(); updateFee += ancestorIt->GetModifiedFee(); updateSigOpsCost += ancestorIt->GetSigOpCost(); + updateMWEBWeight += ancestorIt->GetMWEBWeight(); } - mapTx.modify(it, update_ancestor_state(updateSize, updateFee, updateCount, updateSigOpsCost)); + mapTx.modify(it, update_ancestor_state(updateSize, updateFee, updateCount, updateSigOpsCost, updateMWEBWeight)); } void CTxMemPool::UpdateChildrenForRemoval(txiter it) @@ -270,8 +277,9 @@ void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove, b int64_t modifySize = -((int64_t)removeIt->GetTxSize()); CAmount modifyFee = -removeIt->GetModifiedFee(); int modifySigOps = -removeIt->GetSigOpCost(); + int64_t modifyMWEBWeight = -((int64_t)removeIt->GetMWEBWeight()); for (txiter dit : setDescendants) { - mapTx.modify(dit, update_ancestor_state(modifySize, modifyFee, -1, modifySigOps)); + mapTx.modify(dit, update_ancestor_state(modifySize, modifyFee, -1, modifySigOps, modifyMWEBWeight)); } } } @@ -311,24 +319,28 @@ void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove, b } } -void CTxMemPoolEntry::UpdateDescendantState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount) +void CTxMemPoolEntry::UpdateDescendantState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount, int64_t modifyMWEBWeight) { nSizeWithDescendants += modifySize; assert(int64_t(nSizeWithDescendants) > 0); nModFeesWithDescendants += modifyFee; nCountWithDescendants += modifyCount; assert(int64_t(nCountWithDescendants) > 0); + nMWEBWeightWithDescendants += modifyMWEBWeight; + assert(int64_t(nMWEBWeightWithDescendants) >= 0); } -void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount, int64_t modifySigOps) +void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount, int64_t modifySigOps, int64_t modifyMWEBWeight) { nSizeWithAncestors += modifySize; - assert(int64_t(nSizeWithAncestors) > 0); + assert(int64_t(nSizeWithAncestors) >= 0); nModFeesWithAncestors += modifyFee; nCountWithAncestors += modifyCount; assert(int64_t(nCountWithAncestors) > 0); nSigOpCostWithAncestors += modifySigOps; assert(int(nSigOpCostWithAncestors) >= 0); + nMWEBWeightWithAncestors += modifyMWEBWeight; + assert(int64_t(nMWEBWeightWithAncestors) >= 0); } CTxMemPool::CTxMemPool(CBlockPolicyEstimator* estimator) @@ -800,7 +812,7 @@ void CTxMemPool::queryHashes(std::vector& vtxid) const } static TxMempoolInfo GetInfo(CTxMemPool::indexed_transaction_set::const_iterator it) { - return TxMempoolInfo{it->GetSharedTx(), it->GetTime(), it->GetFee(), it->GetTxSize(), it->GetModifiedFee() - it->GetFee()}; + return TxMempoolInfo{it->GetSharedTx(), it->GetTime(), it->GetFee(), it->GetTxSize(), it->GetMWEBWeight(), it->GetModifiedFee() - it->GetFee()}; } std::vector CTxMemPool::infoAll() const @@ -852,14 +864,14 @@ void CTxMemPool::PrioritiseTransaction(const uint256& hash, const CAmount& nFeeD std::string dummy; CalculateMemPoolAncestors(*it, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy, false); for (txiter ancestorIt : setAncestors) { - mapTx.modify(ancestorIt, update_descendant_state(0, nFeeDelta, 0)); + mapTx.modify(ancestorIt, update_descendant_state(0, nFeeDelta, 0, 0)); } // Now update all descendants' modified fees with ancestors setEntries setDescendants; CalculateDescendants(it, setDescendants); setDescendants.erase(it); for (txiter descendantIt : setDescendants) { - mapTx.modify(descendantIt, update_ancestor_state(0, nFeeDelta, 0, 0)); + mapTx.modify(descendantIt, update_ancestor_state(0, nFeeDelta, 0, 0, 0)); } ++nTransactionsUpdated; } @@ -1047,7 +1059,7 @@ void CTxMemPool::TrimToSize(size_t sizelimit, std::vector* pvNoSpends // "minimum reasonable fee rate" (ie some value under which we consider txn // to have 0 fee). This way, we don't allow txn to enter mempool with feerate // equal to txn which were removed with no block in between. - CFeeRate removed(it->GetModFeesWithDescendants(), it->GetSizeWithDescendants()); + CFeeRate removed(it->GetModFeesWithDescendants(), it->GetSizeWithDescendants(), it->GetMWEBWeightWithDescendants()); removed += incrementalRelayFee; trackPackageRemoved(removed); maxFeeRateRemoved = std::max(maxFeeRateRemoved, removed); diff --git a/src/txmempool.h b/src/txmempool.h index f513f14af..a0e109028 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -89,6 +89,7 @@ private: mutable Children m_children; const CAmount nFee; //!< Cached to avoid expensive parent-transaction lookups const size_t nTxWeight; //!< ... and avoid recomputing tx weight (also used for GetTxSize()) + const uint64_t mweb_weight; const size_t nUsageSize; //!< ... and total memory usage const int64_t nTime; //!< Local time when entering the mempool const unsigned int entryHeight; //!< Chain height when entering the mempool @@ -103,12 +104,14 @@ private: uint64_t nCountWithDescendants; //!< number of descendant transactions uint64_t nSizeWithDescendants; //!< ... and size CAmount nModFeesWithDescendants; //!< ... and total fees (all including us) + uint64_t nMWEBWeightWithDescendants; // Analogous statistics for ancestor transactions uint64_t nCountWithAncestors; uint64_t nSizeWithAncestors; CAmount nModFeesWithAncestors; int64_t nSigOpCostWithAncestors; + int64_t nMWEBWeightWithAncestors; public: CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee, @@ -121,6 +124,7 @@ public: const CAmount& GetFee() const { return nFee; } size_t GetTxSize() const; size_t GetTxWeight() const { return nTxWeight; } + size_t GetMWEBWeight() const { return mweb_weight; } std::chrono::seconds GetTime() const { return std::chrono::seconds{nTime}; } unsigned int GetHeight() const { return entryHeight; } int64_t GetSigOpCost() const { return sigOpCost; } @@ -129,9 +133,9 @@ public: const LockPoints& GetLockPoints() const { return lockPoints; } // Adjusts the descendant state. - void UpdateDescendantState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount); + void UpdateDescendantState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount, int64_t modifyMWEBWeight); // Adjusts the ancestor state - void UpdateAncestorState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount, int64_t modifySigOps); + void UpdateAncestorState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount, int64_t modifySigOps, int64_t modifyMWEBWeight); // Updates the fee delta used for mining priority score, and the // modified fees with descendants. void UpdateFeeDelta(int64_t feeDelta); @@ -141,6 +145,7 @@ public: uint64_t GetCountWithDescendants() const { return nCountWithDescendants; } uint64_t GetSizeWithDescendants() const { return nSizeWithDescendants; } CAmount GetModFeesWithDescendants() const { return nModFeesWithDescendants; } + uint64_t GetMWEBWeightWithDescendants() const { return nMWEBWeightWithDescendants; } bool GetSpendsCoinbase() const { return spendsCoinbase; } @@ -148,6 +153,7 @@ public: uint64_t GetSizeWithAncestors() const { return nSizeWithAncestors; } CAmount GetModFeesWithAncestors() const { return nModFeesWithAncestors; } int64_t GetSigOpCostWithAncestors() const { return nSigOpCostWithAncestors; } + uint64_t GetMWEBWeightWithAncestors() const { return nMWEBWeightWithAncestors; } const Parents& GetMemPoolParentsConst() const { return m_parents; } const Children& GetMemPoolChildrenConst() const { return m_children; } @@ -161,33 +167,35 @@ public: // Helpers for modifying CTxMemPool::mapTx, which is a boost multi_index. struct update_descendant_state { - update_descendant_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount) : - modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount) + update_descendant_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount, int64_t _modifyMWEBWeight) : + modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount), modifyMWEBWeight(_modifyMWEBWeight) {} void operator() (CTxMemPoolEntry &e) - { e.UpdateDescendantState(modifySize, modifyFee, modifyCount); } + { e.UpdateDescendantState(modifySize, modifyFee, modifyCount, modifyMWEBWeight); } private: int64_t modifySize; CAmount modifyFee; int64_t modifyCount; + int64_t modifyMWEBWeight; }; struct update_ancestor_state { - update_ancestor_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount, int64_t _modifySigOpsCost) : - modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount), modifySigOpsCost(_modifySigOpsCost) + update_ancestor_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount, int64_t _modifySigOpsCost, int64_t _modifyMWEBWeight) : + modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount), modifySigOpsCost(_modifySigOpsCost), modifyMWEBWeight(_modifyMWEBWeight) {} void operator() (CTxMemPoolEntry &e) - { e.UpdateAncestorState(modifySize, modifyFee, modifyCount, modifySigOpsCost); } + { e.UpdateAncestorState(modifySize, modifyFee, modifyCount, modifySigOpsCost, modifyMWEBWeight); } private: int64_t modifySize; CAmount modifyFee; int64_t modifyCount; int64_t modifySigOpsCost; + int64_t modifyMWEBWeight; }; struct update_fee_delta @@ -382,6 +390,9 @@ struct TxMempoolInfo /** Virtual size of the transaction. */ size_t vsize; + /** MWEB weight */ + uint64_t mweb_weight; + /** The fee delta. */ int64_t nFeeDelta; }; diff --git a/src/validation.cpp b/src/validation.cpp index 59c86260e..4c2e302c0 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -522,15 +522,15 @@ private: bool Finalize(ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs); // Compare a package's feerate against minimum allowed. - bool CheckFeeRate(size_t package_size, CAmount package_fee, TxValidationState& state) + bool CheckFeeRate(size_t package_size, uint64_t mweb_weight, CAmount package_fee, TxValidationState& state) { - CAmount mempoolRejectFee = m_pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(package_size); + CAmount mempoolRejectFee = m_pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetTotalFee(package_size, mweb_weight); if (mempoolRejectFee > 0 && package_fee < mempoolRejectFee) { return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool min fee not met", strprintf("%d < %d", package_fee, mempoolRejectFee)); } - if (package_fee < ::minRelayTxFee.GetFee(package_size)) { - return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "min relay fee not met", strprintf("%d < %d", package_fee, ::minRelayTxFee.GetFee(package_size))); + if (package_fee < ::minRelayTxFee.GetTotalFee(package_size, mweb_weight)) { + return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "min relay fee not met", strprintf("%d < %d", package_fee, ::minRelayTxFee.GetTotalFee(package_size, mweb_weight))); } return true; } @@ -729,6 +729,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) entry.reset(new CTxMemPoolEntry(ptx, nFees, nAcceptTime, ::ChainActive().Height(), fSpendsCoinbase, nSigOpsCost, lp)); unsigned int nSize = entry->GetTxSize(); + uint64_t mweb_weight = entry->GetMWEBWeight(); if (nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST) return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "bad-txns-too-many-sigops", @@ -736,7 +737,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) // No transactions are allowed below minRelayTxFee except from disconnected // blocks - if (!bypass_limits && !CheckFeeRate(nSize, nModifiedFees, state)) return false; + if (!bypass_limits && !CheckFeeRate(nSize, mweb_weight, nModifiedFees, state)) return false; const CTxMemPool::setEntries setIterConflicting = m_pool.GetIterSet(setConflicts); // Calculate in-mempool ancestors, up to a limit. @@ -825,7 +826,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) fReplacementTransaction = setConflicts.size(); if (fReplacementTransaction) { - CFeeRate newFeeRate(nModifiedFees, nSize); + CFeeRate newFeeRate(nModifiedFees, nSize, mweb_weight); std::set setConflictsParents; const int maxDescendantsToVisit = 100; for (const auto& mi : setIterConflicting) { @@ -843,7 +844,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) // mean high feerate children are ignored when deciding whether // or not to replace, we do require the replacement to pay more // overall fees too, mitigating most cases. - CFeeRate oldFeeRate(mi->GetModifiedFee(), mi->GetTxSize()); + CFeeRate oldFeeRate(mi->GetModifiedFee(), mi->GetTxSize(), mi->GetMWEBWeight()); if (newFeeRate <= oldFeeRate) { return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "insufficient fee", @@ -918,13 +919,13 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) // Finally in addition to paying more fees than the conflicts the // new transaction must pay for its own bandwidth. CAmount nDeltaFees = nModifiedFees - nConflictingFees; - if (nDeltaFees < ::incrementalRelayFee.GetFee(nSize)) + if (nDeltaFees < ::incrementalRelayFee.GetTotalFee(nSize, mweb_weight)) { return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "insufficient fee", strprintf("rejecting replacement %s, not enough additional fees to relay; %s < %s", hash.ToString(), FormatMoney(nDeltaFees), - FormatMoney(::incrementalRelayFee.GetFee(nSize)))); + FormatMoney(::incrementalRelayFee.GetTotalFee(nSize, mweb_weight)))); } } return true; diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index 6cbad14de..00bef9c1a 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -66,6 +66,7 @@ static feebumper::Result CheckFeeRate(const CWallet& wallet, const CWalletTx& wt // in a rare situation where the mempool minimum fee increased significantly since the fee estimation just a // moment earlier. In this case, we report an error to the user, who may adjust the fee. CFeeRate minMempoolFeeRate = wallet.chain().mempoolMinFee(); + const uint64_t mweb_weight = wtx.tx->mweb_tx.GetMWEBWeight(); if (newFeerate.GetFeePerK() < minMempoolFeeRate.GetFeePerK()) { errors.push_back(strprintf( @@ -75,7 +76,7 @@ static feebumper::Result CheckFeeRate(const CWallet& wallet, const CWalletTx& wt return feebumper::Result::WALLET_ERROR; } - CAmount new_total_fee = newFeerate.GetFee(maxTxSize); + CAmount new_total_fee = newFeerate.GetTotalFee(maxTxSize, mweb_weight); CFeeRate incrementalRelayFee = std::max(wallet.chain().relayIncrementalFee(), CFeeRate(WALLET_INCREMENTAL_RELAY_FEE)); @@ -83,17 +84,17 @@ static feebumper::Result CheckFeeRate(const CWallet& wallet, const CWalletTx& wt isminefilter filter = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE; CAmount old_fee = wtx.GetDebit(filter) - wtx.tx->GetValueOut(); const int64_t txSize = GetVirtualTransactionSize(*(wtx.tx)); - CFeeRate nOldFeeRate(old_fee, txSize); + CFeeRate nOldFeeRate(old_fee, txSize, mweb_weight); // Min total fee is old fee + relay fee - CAmount minTotalFee = nOldFeeRate.GetFee(maxTxSize) + incrementalRelayFee.GetFee(maxTxSize); + CAmount minTotalFee = nOldFeeRate.GetTotalFee(maxTxSize, mweb_weight) + incrementalRelayFee.GetFee(maxTxSize); if (new_total_fee < minTotalFee) { errors.push_back(strprintf(Untranslated("Insufficient total fee %s, must be at least %s (oldFee %s + incrementalFee %s)"), - FormatMoney(new_total_fee), FormatMoney(minTotalFee), FormatMoney(nOldFeeRate.GetFee(maxTxSize)), FormatMoney(incrementalRelayFee.GetFee(maxTxSize)))); + FormatMoney(new_total_fee), FormatMoney(minTotalFee), FormatMoney(nOldFeeRate.GetTotalFee(maxTxSize, mweb_weight)), FormatMoney(incrementalRelayFee.GetFee(maxTxSize)))); return feebumper::Result::INVALID_PARAMETER; } - CAmount requiredFee = GetRequiredFee(wallet, maxTxSize); + CAmount requiredFee = GetRequiredFee(wallet, maxTxSize, mweb_weight); if (new_total_fee < requiredFee) { errors.push_back(strprintf(Untranslated("Insufficient total fee (cannot be less than required fee %s)"), FormatMoney(requiredFee))); @@ -117,7 +118,7 @@ static CFeeRate EstimateFeeRate(const CWallet& wallet, const CWalletTx& wtx, con // the tx fee/vsize, so it may have been rounded down. Add 1 satoshi to the // result. int64_t txSize = GetVirtualTransactionSize(*(wtx.tx)); - CFeeRate feerate(old_fee, txSize); + CFeeRate feerate(old_fee, txSize, wtx.tx->mweb_tx.GetMWEBWeight()); feerate += CFeeRate(1); // The node has a configurable incremental relay fee. Increment the fee by diff --git a/src/wallet/fees.cpp b/src/wallet/fees.cpp index 249bc833c..79a48bebc 100644 --- a/src/wallet/fees.cpp +++ b/src/wallet/fees.cpp @@ -9,15 +9,15 @@ #include -CAmount GetRequiredFee(const CWallet& wallet, unsigned int nTxBytes) +CAmount GetRequiredFee(const CWallet& wallet, unsigned int nTxBytes, uint64_t mweb_weight) { - return GetRequiredFeeRate(wallet).GetFee(nTxBytes); + return GetRequiredFeeRate(wallet).GetTotalFee(nTxBytes, mweb_weight); } -CAmount GetMinimumFee(const CWallet& wallet, unsigned int nTxBytes, const CCoinControl& coin_control, FeeCalculation* feeCalc) +CAmount GetMinimumFee(const CWallet& wallet, unsigned int nTxBytes, uint64_t mweb_weight, const CCoinControl& coin_control, FeeCalculation* feeCalc) { - return GetMinimumFeeRate(wallet, coin_control, feeCalc).GetFee(nTxBytes); + return GetMinimumFeeRate(wallet, coin_control, feeCalc).GetTotalFee(nTxBytes, mweb_weight); } CFeeRate GetRequiredFeeRate(const CWallet& wallet) diff --git a/src/wallet/fees.h b/src/wallet/fees.h index 434f211dc..e03c19620 100644 --- a/src/wallet/fees.h +++ b/src/wallet/fees.h @@ -17,13 +17,13 @@ struct FeeCalculation; * Return the minimum required absolute fee for this size * based on the required fee rate */ -CAmount GetRequiredFee(const CWallet& wallet, unsigned int nTxBytes); +CAmount GetRequiredFee(const CWallet& wallet, unsigned int nTxBytes, uint64_t mweb_weight); /** * Estimate the minimum fee considering user set parameters * and the required fee */ -CAmount GetMinimumFee(const CWallet& wallet, unsigned int nTxBytes, const CCoinControl& coin_control, FeeCalculation* feeCalc); +CAmount GetMinimumFee(const CWallet& wallet, unsigned int nTxBytes, uint64_t mweb_weight, const CCoinControl& coin_control, FeeCalculation* feeCalc); /** * Return the minimum required feerate taking into account the diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 4a79dea3d..79898a353 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -216,7 +216,7 @@ static void SetFeeEstimateMode(const CWallet& wallet, CCoinControl& cc, const Un if (!estimate_mode.isNull() && estimate_mode.get_str() != "unset") { throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both estimate_mode and fee_rate"); } - cc.m_feerate = CFeeRate(AmountFromValue(fee_rate), COIN); + cc.m_feerate = CFeeRate(AmountFromValue(fee_rate), COIN, 0); if (override_min_fee) cc.fOverrideFeeRate = true; // Default RBF to true for explicit fee_rate, if unset. if (cc.m_signal_bip125_rbf == nullopt) cc.m_signal_bip125_rbf = true; @@ -2338,8 +2338,8 @@ static RPCHelpMan settxfee() LOCK(pwallet->cs_wallet); CAmount nAmount = AmountFromValue(request.params[0]); - CFeeRate tx_fee_rate(nAmount, 1000); - CFeeRate max_tx_fee_rate(pwallet->m_default_max_tx_fee, 1000); + CFeeRate tx_fee_rate(nAmount, 1000, 0); + CFeeRate max_tx_fee_rate(pwallet->m_default_max_tx_fee, 1000, 0); if (tx_fee_rate == CFeeRate(0)) { // automatic selection } else if (tx_fee_rate < pwallet->chain().relayMinFee()) { diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index a583c82c4..2adb28f8f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2509,7 +2509,7 @@ bool CWallet::SignTransaction(CMutableTransaction& tx) const return false; } const CWalletTx& wtx = mi->second; - coins[input.prevout] = Coin(wtx.tx->vout[input.prevout.n], wtx.m_confirm.block_height, wtx.IsCoinBase()); + coins[input.prevout] = Coin(wtx.tx->vout[input.prevout.n], wtx.m_confirm.block_height, wtx.IsCoinBase(), wtx.IsHogEx()); } std::map input_errors; return SignTransaction(tx, coins, SIGHASH_ALL, input_errors); @@ -3956,7 +3956,7 @@ std::shared_ptr CWallet::Create(interfaces::Chain& chain, const std::st warnings.push_back(AmountHighWarn("-paytxfee") + Untranslated(" ") + _("This is the transaction fee you will pay if you send a transaction.")); } - walletInstance->m_pay_tx_fee = CFeeRate(nFeePerK, 1000); + walletInstance->m_pay_tx_fee = CFeeRate(nFeePerK, 1000, 0); if (walletInstance->m_pay_tx_fee < chain.relayMinFee()) { error = strprintf(_("Invalid amount for -paytxfee=: '%s' (must be at least %s)"), gArgs.GetArg("-paytxfee", ""), chain.relayMinFee().ToString()); @@ -3973,7 +3973,7 @@ std::shared_ptr CWallet::Create(interfaces::Chain& chain, const std::st if (nMaxFee > HIGH_MAX_TX_FEE) { warnings.push_back(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction.")); } - if (CFeeRate(nMaxFee, 1000) < chain.relayMinFee()) { + if (CFeeRate(nMaxFee, 1000, 0) < chain.relayMinFee()) { error = strprintf(_("Invalid amount for -maxtxfee=: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"), gArgs.GetArg("-maxtxfee", ""), chain.relayMinFee().ToString()); return nullptr; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 6fde2fa79..cc8af8bf8 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -549,6 +549,7 @@ public: const uint256& GetHash() const { return tx->GetHash(); } bool IsCoinBase() const { return tx->IsCoinBase(); } bool IsImmatureCoinBase() const; + bool IsHogEx() const { return tx->IsHogEx(); } // Disable copying of CWalletTx objects to prevent bugs where instances get // copied in and out of the mapWallet map, and fields are updated in the