Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use wtxid for transaction relay #18044

Merged
merged 18 commits into from Jul 22, 2020
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/consensus/validation.h
Expand Up @@ -30,11 +30,15 @@ enum class TxValidationResult {
TX_MISSING_INPUTS, //!< transaction was missing some of its inputs
TX_PREMATURE_SPEND, //!< transaction spends a coinbase too early, or violates locktime/sequence locks
/**
* Transaction might be missing a witness, have a witness prior to SegWit
* Transaction might have a witness prior to SegWit
* activation, or witness may have been malleated (which includes
* non-standard witnesses).
*/
TX_WITNESS_MUTATED,
/**
* Transaction is missing a witness.
*/
TX_WITNESS_STRIPPED,
/**
* Tx already in mempool or conflicts with a tx in the chain
* (if it conflicts with another tx in mempool, we use MEMPOOL_POLICY as it failed to reach the RBF threshold)
Expand Down
4 changes: 2 additions & 2 deletions src/net.h
Expand Up @@ -965,11 +965,11 @@ class CNode
}


void AddInventoryKnown(const CInv& inv)
void AddKnownTx(const uint256& hash)
{
if (m_tx_relay != nullptr) {
LOCK(m_tx_relay->cs_tx_inventory);
m_tx_relay->filterInventoryKnown.insert(inv.hash);
m_tx_relay->filterInventoryKnown.insert(hash);
}
}

Expand Down
257 changes: 196 additions & 61 deletions src/net_processing.cpp

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/net_processing.h
Expand Up @@ -100,6 +100,6 @@ struct CNodeStateStats {
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats);

/** Relay transaction to every node */
void RelayTransaction(const uint256&, const CConnman& connman);
void RelayTransaction(const uint256& txid, const uint256& wtxid, const CConnman& connman) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
sdaftuar marked this conversation as resolved.
Show resolved Hide resolved

#endif // BITCOIN_NET_PROCESSING_H
5 changes: 3 additions & 2 deletions src/node/transaction.cpp
Expand Up @@ -80,9 +80,10 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
if (relay) {
// the mempool tracks locally submitted transactions to make a
// best-effort of initial broadcast
node.mempool->AddUnbroadcastTx(hashTx);
node.mempool->AddUnbroadcastTx(hashTx, tx->GetWitnessHash());

RelayTransaction(hashTx, *node.connman);
LOCK(cs_main);
RelayTransaction(hashTx, tx->GetWitnessHash(), *node.connman);
}

return TransactionError::OK;
Expand Down
4 changes: 4 additions & 0 deletions src/protocol.cpp
Expand Up @@ -46,6 +46,7 @@ const char *GETCFHEADERS="getcfheaders";
const char *CFHEADERS="cfheaders";
const char *GETCFCHECKPT="getcfcheckpt";
const char *CFCHECKPT="cfcheckpt";
const char *WTXIDRELAY="wtxidrelay";
} // namespace NetMsgType

/** All known message types. Keep this in the same order as the list of
Expand Down Expand Up @@ -83,6 +84,7 @@ const static std::string allNetMessageTypes[] = {
NetMsgType::CFHEADERS,
NetMsgType::GETCFCHECKPT,
NetMsgType::CFCHECKPT,
NetMsgType::WTXIDRELAY,
};
const static std::vector<std::string> allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes));

Expand Down Expand Up @@ -177,6 +179,8 @@ std::string CInv::GetCommand() const
switch (masked)
{
case MSG_TX: return cmd.append(NetMsgType::TX);
// WTX is not a message type, just an inv type
case MSG_WTX: return cmd.append("wtx");
case MSG_BLOCK: return cmd.append(NetMsgType::BLOCK);
case MSG_FILTERED_BLOCK: return cmd.append(NetMsgType::MERKLEBLOCK);
case MSG_CMPCT_BLOCK: return cmd.append(NetMsgType::CMPCTBLOCK);
Expand Down
11 changes: 9 additions & 2 deletions src/protocol.h
Expand Up @@ -261,6 +261,12 @@ extern const char* GETCFCHECKPT;
* evenly spaced filter headers for blocks on the requested chain.
*/
extern const char* CFCHECKPT;
/**
* Indicates that a node prefers to relay transactions via wtxid, rather than
* txid.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

d0f03e9

Is this rule a best-effort right now? At tx reception, we may have to request parent based only on the input. If so is the BIP clear enough on the 5) ? Precise also there is no current sanction of the rule by receiver as of today

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you made a good observation -- our fetching of parents of orphan transactions must be by txid, rather than wtxid. I did try to carefully word the BIP to avoid referencing this situation:

After a node has sent and received a "wtxidrelay" message to/from a given peer, the node is required to use the MSG_WTX inv-type when announcing transactions to that peer, or requesting announced transactions from that peer.

So the BIP says that newly announced transactions must be by wtxid. And if your peer announces a transaction by wtxid, you must fetch it by wtxid (and not txid). The BIP is silent on requests for transactions that are not announced -- we could make this explicit but this has never been documented behavior, and I'm not sure we should commit to a certain handling of this kind of thing anyway... But open to suggestions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would amend the BIP to make its scope explicitly restrained. Add to 5) "Note: this rule doesn't cover cases where a peer needs to request transaction for validation purposes but don't know its wtxid yet (e.g missing parents of a wtxid-announced child)" ? At least to avoid someone raising same concern in the future and saying we don't commit on anything wrt to this issue.

* @since protocol version 70016 as described by BIP 339.
*/
extern const char *WTXIDRELAY;
}; // namespace NetMsgType

/* Get a vector of all valid message types (see above) */
Expand Down Expand Up @@ -397,11 +403,12 @@ const uint32_t MSG_TYPE_MASK = 0xffffffff >> 2;
* These numbers are defined by the protocol. When adding a new value, be sure
* to mention it in the respective BIP.
*/
enum GetDataMsg {
enum GetDataMsg : uint32_t {
UNDEFINED = 0,
MSG_TX = 1,
MSG_BLOCK = 2,
// The following can only occur in getdata. Invs always use TX or BLOCK.
MSG_WTX = 5, //!< Defined in BIP 339
// The following can only occur in getdata. Invs always use TX/WTX or BLOCK.
MSG_FILTERED_BLOCK = 3, //!< Defined in BIP37
MSG_CMPCT_BLOCK = 4, //!< Defined in BIP152
MSG_WITNESS_BLOCK = MSG_BLOCK | MSG_WITNESS_FLAG, //!< Defined in BIP144
Expand Down
14 changes: 7 additions & 7 deletions src/txmempool.cpp
Expand Up @@ -726,12 +726,12 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
assert(innerUsage == cachedInnerUsage);
}

bool CTxMemPool::CompareDepthAndScore(const uint256& hasha, const uint256& hashb)
bool CTxMemPool::CompareDepthAndScore(const uint256& hasha, const uint256& hashb, bool wtxid)
{
LOCK(cs);
indexed_transaction_set::const_iterator i = mapTx.find(hasha);
indexed_transaction_set::const_iterator i = wtxid ? get_iter_from_wtxid(hasha) : mapTx.find(hasha);
if (i == mapTx.end()) return false;
indexed_transaction_set::const_iterator j = mapTx.find(hashb);
indexed_transaction_set::const_iterator j = wtxid ? get_iter_from_wtxid(hashb) : mapTx.find(hashb);
if (j == mapTx.end()) return true;
uint64_t counta = i->GetCountWithAncestors();
uint64_t countb = j->GetCountWithAncestors();
Expand Down Expand Up @@ -811,10 +811,10 @@ CTransactionRef CTxMemPool::get(const uint256& hash) const
return i->GetSharedTx();
}

TxMempoolInfo CTxMemPool::info(const uint256& hash) const
TxMempoolInfo CTxMemPool::info(const uint256& hash, bool wtxid) const
{
LOCK(cs);
indexed_transaction_set::const_iterator i = mapTx.find(hash);
indexed_transaction_set::const_iterator i = (wtxid ? get_iter_from_wtxid(hash) : mapTx.find(hash));
if (i == mapTx.end())
return TxMempoolInfo();
return GetInfo(i);
Expand Down Expand Up @@ -917,8 +917,8 @@ bool CCoinsViewMemPool::GetCoin(const COutPoint &outpoint, Coin &coin) const {

size_t CTxMemPool::DynamicMemoryUsage() const {
LOCK(cs);
// Estimate the overhead of mapTx to be 12 pointers + an allocation, as no exact formula for boost::multi_index_contained is implemented.
return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 12 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(mapLinks) + memusage::DynamicUsage(vTxHashes) + cachedInnerUsage;
// Estimate the overhead of mapTx to be 15 pointers + an allocation, as no exact formula for boost::multi_index_contained is implemented.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

54d58e7

3 new pointers for boost::multi_index::tag<index_by_wtxid>, mempoolentry_wtxid, SaltedTxidHasher, not one for new boost::multi_index::hashed_unique ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ariard I don't understand this comment (1c382b5#r385362270).

I think 3 pointers for an extra index is probably close to correct, but #18086 may make these kinds of heuristics unnecessary, so that's probably a better approach long term.

return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 15 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(mapLinks) + memusage::DynamicUsage(vTxHashes) + cachedInnerUsage;
}

void CTxMemPool::RemoveUnbroadcastTx(const uint256& txid, const bool unchecked) {
Expand Down
55 changes: 45 additions & 10 deletions src/txmempool.h
Expand Up @@ -198,6 +198,22 @@ struct mempoolentry_txid
}
};

// extracts a transaction witness-hash from CTxMemPoolEntry or CTransactionRef
struct mempoolentry_wtxid
{
typedef uint256 result_type;
result_type operator() (const CTxMemPoolEntry &entry) const
{
return entry.GetTx().GetWitnessHash();
}

result_type operator() (const CTransactionRef& tx) const
{
return tx->GetWitnessHash();
}
};


/** \class CompareTxMemPoolEntryByDescendantScore
*
* Sort an entry by max(score/size of entry's tx, score/size with all descendants).
Expand Down Expand Up @@ -318,6 +334,7 @@ class CompareTxMemPoolEntryByAncestorFee
struct descendant_score {};
struct entry_time {};
struct ancestor_score {};
struct index_by_wtxid {};

class CBlockPolicyEstimator;

Expand Down Expand Up @@ -383,8 +400,9 @@ class SaltedTxidHasher
*
* CTxMemPool::mapTx, and CTxMemPoolEntry bookkeeping:
*
* mapTx is a boost::multi_index that sorts the mempool on 4 criteria:
* - transaction hash
* mapTx is a boost::multi_index that sorts the mempool on 5 criteria:
* - transaction hash (txid)
* - witness-transaction hash (wtxid)
* - descendant feerate [we use max(feerate of tx, feerate of tx with all descendants)]
* - time in mempool
* - ancestor feerate [we use min(feerate of tx, feerate of tx with all unconfirmed ancestors)]
Expand Down Expand Up @@ -469,6 +487,12 @@ class CTxMemPool
boost::multi_index::indexed_by<
// sorted by txid
boost::multi_index::hashed_unique<mempoolentry_txid, SaltedTxidHasher>,
// sorted by wtxid
sdaftuar marked this conversation as resolved.
Show resolved Hide resolved
boost::multi_index::hashed_unique<
boost::multi_index::tag<index_by_wtxid>,
mempoolentry_wtxid,
SaltedTxidHasher
>,
// sorted by fee rate
boost::multi_index::ordered_non_unique<
boost::multi_index::tag<descendant_score>,
Expand Down Expand Up @@ -549,8 +573,11 @@ class CTxMemPool

std::vector<indexed_transaction_set::const_iterator> GetSortedDepthAndScore() const EXCLUSIVE_LOCKS_REQUIRED(cs);

/** track locally submitted transactions to periodically retry initial broadcast */
std::set<uint256> m_unbroadcast_txids GUARDED_BY(cs);
/**
* track locally submitted transactions to periodically retry initial broadcast
* map of txid -> wtxid
*/
std::map<uint256, uint256> m_unbroadcast_txids GUARDED_BY(cs);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this change is necessary (or desirable). I guess it was prompted by this comment: #18038 (comment) , but storing this as a map from txid->wtxid is unnecessary and confusing, since it obfuscates the purpose of this data, which is a set of transactions that may need to be rebroadcast.

The only place that the wtxid is read from this map is in ReattemptInitialBroadcast(), at which point we can fetch the transaction from the mempool (we very nearly do that with m_mempool.exists()), and get the wtxid from there.

cc @amitiuttarwar @ariard

Copy link
Member

@ariard ariard Jul 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My mistake here, effectively you need to announce transactions from the unbroadcast set by wtxid to upgraded peers but as you point out it doesn't mean we need to cache them as a solution.

Maybe we should precise unbroadcast set semantic wrt same txid, different wtxids, we only guarantee reattempt to the best mempool candidate known for a given txid at the time we call ReattemptInitialBroadcast, not insertion (AddUnbroadcastTx). #18044 (comment) isn't an issue.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really think this "same txid, different wtxid" thing is a complete red herring. The mempool can only ever be aware of one witness for a transaction, so any attempt to announce the transaction via a different wtxid would fail anyway. As Suhas pointed out earlier (#18044 (comment)), to support tracking multiple witnesses we'd need to make significant changes many of our components. Besides, the unbroadcast set is transactions that we created ourselves, so we're not expecting the witnesses to change.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with @jnewbery. There is no real way that we can reasonable start tracking multiple witnesses for the same transaction (either in the mempool or the wallet, ...), so unless this map's storing of wtxid is needed for efficiency, it seems just a set of txid should be sufficient here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😮 wow that's a great point @jnewbery. thank you!!

I thought during implementation I ran into something that prevented me from exclusively having txids on the set, but that evaluation is simply wrong. I've taken a first pass at reverting unbroadcast to a set here: amitiuttarwar@f51cccd. I'll need to revisit with fresh eyes and then will PR.

I agree set makes more sense than a map- its simpler, communicates the intent better, and there's no noticeable efficiency gain with the map. Since we check the transaction is the mempool, the difference between map vs set is the difference between calling CTxMemPool::get vs CTxMemPool::exists. This boils down to mapTx.find(hash) vs mapTx.count(hash), which both have log(n) complexity (according to the boost docs I found).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@amitiuttarwar mapTX is a multiindex and we use a hashed index not a ordered index so it's O(1) lookup. Not sure exactly how mapTX relates to m_unboardcast_txids though...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@amitiuttarwar that patch looks correct from a quick skim. I'll review fully once you open a PR.

As @JeremyRubin points out, we're using a boost::multi_index::hashed_unique index for the wtxid index, which has constant time search/retrieval.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reverted to set in cb79b9d, #19879


public:
indirectmap<COutPoint, const CTransaction*> mapNextTx GUARDED_BY(cs);
Expand Down Expand Up @@ -586,7 +613,7 @@ class CTxMemPool

void clear();
void _clear() EXCLUSIVE_LOCKS_REQUIRED(cs); //lock free
bool CompareDepthAndScore(const uint256& hasha, const uint256& hashb);
bool CompareDepthAndScore(const uint256& hasha, const uint256& hashb, bool wtxid=false);
void queryHashes(std::vector<uint256>& vtxid) const;
bool isSpent(const COutPoint& outpoint) const;
unsigned int GetTransactionsUpdated() const;
Expand Down Expand Up @@ -689,32 +716,40 @@ class CTxMemPool
return totalTxSize;
}

bool exists(const uint256& hash) const
bool exists(const uint256& hash, bool wtxid=false) const
{
LOCK(cs);
if (wtxid) {
return (mapTx.get<index_by_wtxid>().count(hash) != 0);
}
return (mapTx.count(hash) != 0);
}

CTransactionRef get(const uint256& hash) const;
TxMempoolInfo info(const uint256& hash) const;
txiter get_iter_from_wtxid(const uint256& wtxid) const EXCLUSIVE_LOCKS_REQUIRED(cs)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above, I think it would be clearer if this was combined with GetIter()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In commit "Add a wtxid-index to the mempool"

I think this would be a nice improvement.

{
AssertLockHeld(cs);
return mapTx.project<0>(mapTx.get<index_by_wtxid>().find(wtxid));
}
TxMempoolInfo info(const uint256& hash, bool wtxid=false) const;
std::vector<TxMempoolInfo> infoAll() const;

size_t DynamicMemoryUsage() const;

/** Adds a transaction to the unbroadcast set */
void AddUnbroadcastTx(const uint256& txid) {
void AddUnbroadcastTx(const uint256& txid, const uint256& wtxid) {
LOCK(cs);
// Sanity Check: the transaction should also be in the mempool
if (exists(txid)) {
m_unbroadcast_txids.insert(txid);
m_unbroadcast_txids[txid] = wtxid;
sdaftuar marked this conversation as resolved.
Show resolved Hide resolved
}
}

/** Removes a transaction from the unbroadcast set */
void RemoveUnbroadcastTx(const uint256& txid, const bool unchecked = false);

/** Returns transactions in unbroadcast set */
std::set<uint256> GetUnbroadcastTxs() const {
std::map<uint256, uint256> GetUnbroadcastTxs() const {
LOCK(cs);
return m_unbroadcast_txids;
}
Expand Down
19 changes: 11 additions & 8 deletions src/validation.cpp
Expand Up @@ -939,7 +939,7 @@ bool MemPoolAccept::PolicyScriptChecks(ATMPArgs& args, Workspace& ws, Precompute
if (!tx.HasWitness() && CheckInputScripts(tx, state_dummy, m_view, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, false, txdata) &&
!CheckInputScripts(tx, state_dummy, m_view, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, false, txdata)) {
// Only the witness is missing, so the transaction itself may be fine.
state.Invalid(TxValidationResult::TX_WITNESS_MUTATED,
state.Invalid(TxValidationResult::TX_WITNESS_STRIPPED,
state.GetRejectReason(), state.GetDebugMessage());
}
return false; // state filled in by CheckInputScripts
Expand Down Expand Up @@ -5083,19 +5083,22 @@ bool LoadMempool(CTxMemPool& pool)
}

// TODO: remove this try except in v0.22
std::map<uint256, uint256> unbroadcast_txids;
try {
std::set<uint256> unbroadcast_txids;
file >> unbroadcast_txids;
unbroadcast = unbroadcast_txids.size();

for (const auto& txid : unbroadcast_txids) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarification: does moving this out of the try block have any behavior change? Is it possible that a semi-corrupt unbroadcast_txids would partially deserialize and then have only some entries?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the try block was introduced to specifically catch the error when upgrading around not having an unbroadcast set written to disk. so I think having the initialization & iteration separate actually captures that intent better.

but lmk if you think of any specific ways this logic could be problematic

pool.AddUnbroadcastTx(txid);
}
} catch (const std::exception&) {
// mempool.dat files created prior to v0.21 will not have an
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest: clearing unbroadcast_txids here in case has garbage data.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking into this- is there really a way that the CAutoFile >> operator could cause a silent/partial failure? as I mentioned previously the idea of this try catch is just for a smooth upgrade. To elaborate further: so we don't print Failed to deserialize mempool data on disk: %s. Continuing anyway." when everything actually went fine. the current intention is to remove this in 0.22, so if there's a possibility for unbroadcast_txids to have garbage data written, I'd like to think this through more carefully to find a long term solution.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I believe so? Fortunately for std::map we deserialize an element and then insert it into the map, but is a possibility that we say that there should be 10 elements and there are only 5 elements, or there are 5 elements and their are actually 10. In the event the file is corrupt, we should likely treat it as if the entire thing is corrupt, rather than continuing to process.

Perhaps we should add an exception to throw from Unserialize if there are not the correct amount of entries?

I'm not sure to the degree we need to worry about files on our own local being corrupt, and there are other forms of corruption that can occur. But because it is a behavior change, I'm noting it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to be clear the issue only comes up when you don't have enough elements or when you have a half element presently.

// unbroadcast set. No need to log a failure if parsing fails here.
}

for (const auto& elem : unbroadcast_txids) {
// Don't add unbroadcast transactions that didn't get back into the
// mempool.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this correct? Should this transaction get queued somewhere for rebroadcasting? Or is it a known precondition that because it failed to get into the mempool before this line it will fail again?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't think of why we'd want it in the unbroadcast if it didn't make it back in the mempool, but just double checking.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the unbroadcast set should always be a subset of the mempool. it just maintains txids, so we don't have the capability to do much if its not found.

Should this transaction get queued somewhere for rebroadcasting?

do you mean re-attempt mempool submission? bc we def wouldn't want to broadcast or rebroadcast a transaction we don't have in our mempool!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes. it's possible that we may want to resubmit it to the mempool if it was previously in our mempool. What changed to cause it to no longer be accepted?

const CTransactionRef& added_tx = pool.get(elem.first);
if (added_tx != nullptr) {
pool.AddUnbroadcastTx(elem.first, added_tx->GetWitnessHash());
}
}
} catch (const std::exception& e) {
LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing anyway.\n", e.what());
return false;
Expand All @@ -5111,7 +5114,7 @@ bool DumpMempool(const CTxMemPool& pool)

std::map<uint256, CAmount> mapDeltas;
std::vector<TxMempoolInfo> vinfo;
std::set<uint256> unbroadcast_txids;
std::map<uint256, uint256> unbroadcast_txids;

static Mutex dump_mutex;
LOCK(dump_mutex);
Expand Down
5 changes: 4 additions & 1 deletion src/version.h
Expand Up @@ -9,7 +9,7 @@
* network protocol versioning
*/

static const int PROTOCOL_VERSION = 70015;
static const int PROTOCOL_VERSION = 70016;

//! initial proto version, to be increased after version/verack negotiation
static const int INIT_PROTO_VERSION = 209;
Expand All @@ -35,4 +35,7 @@ static const int SHORT_IDS_BLOCKS_VERSION = 70014;
//! not banning for invalid compact blocks starts with this version
static const int INVALID_CB_NO_BAN_VERSION = 70015;

//! "wtxidrelay" command for wtxid-based relay starts with this version
static const int WTXID_RELAY_VERSION = 70016;

#endif // BITCOIN_VERSION_H