MWEB: Included MWEB weight in feerate
This commit is contained in:
parent
9a9dfec9ac
commit
147ad5aefd
@ -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;
|
||||
|
||||
@ -235,10 +235,11 @@ public:
|
||||
virtual std::vector<WalletTxOut> getCoins(const std::vector<COutPoint>& 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;
|
||||
|
||||
@ -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<int64_t> BlockAssembler::m_last_block_num_txs{nullopt};
|
||||
Optional<int64_t> BlockAssembler::m_last_block_weight{nullopt};
|
||||
Optional<int64_t> BlockAssembler::m_last_block_mweb_weight{nullopt};
|
||||
|
||||
std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn)
|
||||
{
|
||||
@ -153,6 +155,7 @@ std::unique_ptr<CBlockTemplate> 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<CBlockTemplate> 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
|
||||
|
||||
@ -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<int64_t> m_last_block_num_txs;
|
||||
static Optional<int64_t> m_last_block_weight;
|
||||
static Optional<int64_t> 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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -7,15 +7,26 @@
|
||||
|
||||
#include <tinyformat.h>
|
||||
|
||||
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<int64_t>::max()));
|
||||
int64_t nSize = int64_t(nBytes_);
|
||||
assert(mweb_weight <= uint64_t(std::numeric_limits<int64_t>::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<int64_t>::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) {
|
||||
|
||||
@ -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; }
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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");
|
||||
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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<size_t>::max() >> 1).GetFeePerK();
|
||||
CFeeRate(MAX_MONEY, std::numeric_limits<size_t>::max() >> 1, std::numeric_limits<size_t>::max() >> 1).GetFeePerK();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(BinaryOperatorTest)
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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<CTransactionRef> block;
|
||||
|
||||
@ -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<uint256>& 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<TxMempoolInfo> 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<COutPoint>* 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);
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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<uint256> 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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -9,15 +9,15 @@
|
||||
#include <wallet/wallet.h>
|
||||
|
||||
|
||||
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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()) {
|
||||
|
||||
@ -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<int, std::string> input_errors;
|
||||
return SignTransaction(tx, coins, SIGHASH_ALL, input_errors);
|
||||
@ -3956,7 +3956,7 @@ std::shared_ptr<CWallet> 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=<amount>: '%s' (must be at least %s)"),
|
||||
gArgs.GetArg("-paytxfee", ""), chain.relayMinFee().ToString());
|
||||
@ -3973,7 +3973,7 @@ std::shared_ptr<CWallet> 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=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"),
|
||||
gArgs.GetArg("-maxtxfee", ""), chain.relayMinFee().ToString());
|
||||
return nullptr;
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user