MWEB: P2P: Compact blocks

pull/816/head
David Burkett 2 years ago committed by Loshan T
parent 27245a547a
commit 99b9df737d

@ -17,14 +17,22 @@
#include <unordered_map>
CBlockHeaderAndShortTxIDs::CBlockHeaderAndShortTxIDs(const CBlock& block, bool fUseWTXID) :
nonce(GetRand(std::numeric_limits<uint64_t>::max())),
shorttxids(block.vtx.size() - 1), prefilledtxn(1), header(block) {
nonce(GetRand(std::numeric_limits<uint64_t>::max())), header(block), mweb_block(block.mweb_block) {
FillShortTxIDSelector();
//TODO: Use our mempool prior to block acceptance to predictively fill more than just the coinbase
prefilledtxn[0] = {0, block.vtx[0]};
prefilledtxn.push_back({0, block.vtx[0]});
// MWEB: Include HogEx transaction
if (!mweb_block.IsNull()) {
prefilledtxn.push_back({(uint16_t)(block.vtx.size() - 2), block.vtx.back()});
}
shorttxids.reserve(block.vtx.size() - 1);
for (size_t i = 1; i < block.vtx.size(); i++) {
const CTransaction& tx = *block.vtx[i];
shorttxids[i - 1] = GetShortID(fUseWTXID ? tx.GetWitnessHash() : tx.GetHash());
if (!tx.IsHogEx()) {
shorttxids.push_back(GetShortID(fUseWTXID ? tx.GetWitnessHash() : tx.GetHash()));
}
}
}
@ -54,6 +62,7 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c
assert(header.IsNull() && txn_available.empty());
header = cmpctblock.header;
mweb_block = cmpctblock.mweb_block;
txn_available.resize(cmpctblock.BlockTxCount());
int32_t lastprefilledindex = -1;
@ -177,6 +186,7 @@ ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<
assert(!header.IsNull());
uint256 hash = header.GetHash();
block = header;
block.mweb_block = mweb_block;
block.vtx.resize(txn_available.size());
size_t tx_missing_offset = 0;

@ -100,6 +100,7 @@ public:
static constexpr int SHORTTXIDS_LENGTH = 6;
CBlockHeader header;
MWEB::Block mweb_block;
// Dummy for deserialization
CBlockHeaderAndShortTxIDs() {}
@ -112,7 +113,13 @@ public:
SERIALIZE_METHODS(CBlockHeaderAndShortTxIDs, obj)
{
const bool fAllowMWEB = !(s.GetVersion() & SERIALIZE_NO_MWEB);
READWRITE(obj.header, obj.nonce, Using<VectorFormatter<CustomUintFormatter<SHORTTXIDS_LENGTH>>>(obj.shorttxids), obj.prefilledtxn);
if (fAllowMWEB) {
READWRITE(obj.mweb_block);
}
if (ser_action.ForRead()) {
if (obj.BlockTxCount() > std::numeric_limits<uint16_t>::max()) {
throw std::ios_base::failure("indexes overflowed 16 bits");
@ -129,7 +136,8 @@ protected:
const CTxMemPool* pool;
public:
CBlockHeader header;
explicit PartiallyDownloadedBlock(CTxMemPool* poolIn) : pool(poolIn) {}
MWEB::Block mweb_block;
explicit PartiallyDownloadedBlock(CTxMemPool* poolIn, const MWEB::Block& mweb_blockIn) : pool(poolIn), mweb_block(mweb_blockIn) {}
// extra_txn is a list of extra transactions to look at, in <witness hash, reference> form
ReadStatus InitData(const CBlockHeaderAndShortTxIDs& cmpctblock, const std::vector<std::pair<uint256, CTransactionRef>>& extra_txn);

@ -1201,6 +1201,15 @@ public:
bool SupportsMWEB() const noexcept { return (nLocalServices & NODE_MWEB); }
uint64_t GetCmpctBlockVersion() const noexcept
{
if (nLocalServices & NODE_MWEB) {
return 3;
}
return (nLocalServices & NODE_WITNESS) ? 2 : 1;
}
std::string GetAddrName() const;
//! Sets the addrName only if it was not previously set
void MaybeSetAddrName(const std::string& addrNameIn);

@ -331,6 +331,8 @@ struct CNodeState {
bool fHaveMWEB;
//! Whether this peer wants witnesses in cmpctblocks/blocktxns
bool fWantsCmpctWitness;
//! Whether this peer wants MWEB transactions in cmpctblocks/blocktxns
bool fWantsCmpctMWEB;
/**
* If we've announced NODE_WITNESS to this peer: whether the peer sends witnesses in cmpctblocks/blocktxns,
* otherwise: whether this peer sends non-witnesses in cmpctblocks/blocktxns.
@ -411,6 +413,7 @@ struct CNodeState {
fHaveWitness = false;
fHaveMWEB = false;
fWantsCmpctWitness = false;
fWantsCmpctMWEB = false;
fSupportsDesiredCmpctVersion = false;
m_chain_sync = { 0, nullptr, false, false };
m_last_block_announcement = 0;
@ -558,9 +561,12 @@ static bool MarkBlockAsInFlight(CTxMemPool& mempool, NodeId nodeid, const uint25
// Make sure it's not listed somewhere already.
MarkBlockAsReceived(hash);
MWEB::Block mweb_block;
if (pit && (*pit)) {
mweb_block = (*(*pit))->partialBlock->mweb_block;
}
std::list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(),
{hash, pindex, pindex != nullptr, std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock(&mempool) : nullptr)});
{hash, pindex, pindex != nullptr, std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock(&mempool, mweb_block) : nullptr)});
state->nBlocksInFlight++;
state->nBlocksInFlightValidHeaders += it->fValidatedHeaders;
if (state->nBlocksInFlight == 1) {
@ -635,7 +641,7 @@ static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman& connma
}
connman.ForNode(nodeid, [&connman](CNode* pfrom) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
AssertLockHeld(::cs_main);
uint64_t nCMPCTBLOCKVersion = (pfrom->GetLocalServices() & NODE_WITNESS) ? 2 : 1;
uint64_t nCMPCTBLOCKVersion = pfrom->GetCmpctBlockVersion();
if (lNodesAnnouncingHeaderAndIDs.size() >= 3) {
// As per BIP152, we only get 3 of our peers to announce
// blocks using compact encodings.
@ -1278,6 +1284,7 @@ static std::shared_ptr<const CBlock> most_recent_block GUARDED_BY(cs_most_recent
static std::shared_ptr<const CBlockHeaderAndShortTxIDs> most_recent_compact_block GUARDED_BY(cs_most_recent_block);
static uint256 most_recent_block_hash GUARDED_BY(cs_most_recent_block);
static bool fWitnessesPresentInMostRecentCompactBlock GUARDED_BY(cs_most_recent_block);
static bool fMWEBPresentInMostRecentCompactBlock GUARDED_BY(cs_most_recent_block);
/**
* Maintain state about the best-seen block and fast-announce a compact block
@ -1295,6 +1302,7 @@ void PeerManager::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_
nHighestFastAnnounce = pindex->nHeight;
bool fWitnessEnabled = IsWitnessEnabled(pindex->pprev, m_chainparams.GetConsensus());
bool mweb_enabled = IsMWEBEnabled(pindex->pprev, m_chainparams.GetConsensus());
uint256 hashBlock(pblock->GetHash());
{
@ -1303,9 +1311,10 @@ void PeerManager::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_
most_recent_block = pblock;
most_recent_compact_block = pcmpctblock;
fWitnessesPresentInMostRecentCompactBlock = fWitnessEnabled;
fMWEBPresentInMostRecentCompactBlock = mweb_enabled;
}
m_connman.ForEachNode([this, &pcmpctblock, pindex, &msgMaker, fWitnessEnabled, &hashBlock](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
m_connman.ForEachNode([this, &pcmpctblock, pindex, &msgMaker, fWitnessEnabled, mweb_enabled, &hashBlock](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
AssertLockHeld(::cs_main);
// TODO: Avoid the repeated-serialization here
@ -1316,11 +1325,17 @@ void PeerManager::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_
// If the peer has, or we announced to them the previous block already,
// but we don't think they have this one, go ahead and announce it
if (state.fPreferHeaderAndIDs && (!fWitnessEnabled || state.fWantsCmpctWitness) &&
(!mweb_enabled || state.fWantsCmpctMWEB) &&
!PeerHasHeader(&state, pindex) && PeerHasHeader(&state, pindex->pprev)) {
bool fPeerWantsWitness = State(pnode->GetId())->fWantsCmpctWitness;
bool fPeerWantsMWEB = State(pnode->GetId())->fWantsCmpctMWEB;
int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
nSendFlags |= fPeerWantsMWEB ? 0 : SERIALIZE_NO_MWEB;
LogPrint(BCLog::NET, "%s sending header-and-ids %s to peer=%d\n", "PeerManager::NewPoWValidBlock",
hashBlock.ToString(), pnode->GetId());
m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::CMPCTBLOCK, *pcmpctblock));
m_connman.PushMessage(pnode, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *pcmpctblock));
state.pindexBestHeaderSent = pindex;
}
});
@ -1497,12 +1512,14 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c
std::shared_ptr<const CBlock> a_recent_block;
std::shared_ptr<const CBlockHeaderAndShortTxIDs> a_recent_compact_block;
bool fWitnessesPresentInARecentCompactBlock;
bool fMWEBPresentInARecentCompactBlock;
const Consensus::Params& consensusParams = chainparams.GetConsensus();
{
LOCK(cs_most_recent_block);
a_recent_block = most_recent_block;
a_recent_compact_block = most_recent_compact_block;
fWitnessesPresentInARecentCompactBlock = fWitnessesPresentInMostRecentCompactBlock;
fMWEBPresentInARecentCompactBlock = fMWEBPresentInMostRecentCompactBlock;
}
bool need_activate_chain = false;
@ -1617,9 +1634,12 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c
// and we don't feel like constructing the object for them, so
// instead we respond with the full, non-compact block.
bool fPeerWantsWitness = State(pfrom.GetId())->fWantsCmpctWitness;
bool fPeerWantsMWEB = State(pfrom.GetId())->fWantsCmpctMWEB;
int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
nSendFlags |= fPeerWantsMWEB ? 0 : SERIALIZE_NO_MWEB;
if (CanDirectFetch(consensusParams) && pindex->nHeight >= ::ChainActive().Height() - MAX_CMPCTBLOCK_DEPTH) {
if ((fPeerWantsWitness || !fWitnessesPresentInARecentCompactBlock) && a_recent_compact_block && a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) {
if ((fPeerWantsWitness || !fWitnessesPresentInARecentCompactBlock) && (fPeerWantsMWEB || !fMWEBPresentInARecentCompactBlock) && a_recent_compact_block && a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) {
connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block));
} else {
CBlockHeaderAndShortTxIDs cmpctblock(*pblock, fPeerWantsWitness);
@ -1787,6 +1807,8 @@ void PeerManager::SendBlockTransactions(CNode& pfrom, const CBlock& block, const
LOCK(cs_main);
const CNetMsgMaker msgMaker(pfrom.GetCommonVersion());
int nSendFlags = State(pfrom.GetId())->fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
nSendFlags |= State(pfrom.GetId())->fWantsCmpctMWEB ? 0 : SERIALIZE_NO_MWEB;
m_connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp));
}
@ -2532,7 +2554,10 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
// We send this to non-NODE NETWORK peers as well, because
// they may wish to request compact blocks from us
bool fAnnounceUsingCMPCTBLOCK = false;
uint64_t nCMPCTBLOCKVersion = 2;
uint64_t nCMPCTBLOCKVersion = 3;
if (pfrom.GetLocalServices() & NODE_MWEB)
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
nCMPCTBLOCKVersion = 2;
if (pfrom.GetLocalServices() & NODE_WITNESS)
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
nCMPCTBLOCKVersion = 1;
@ -2649,17 +2674,20 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
bool fAnnounceUsingCMPCTBLOCK = false;
uint64_t nCMPCTBLOCKVersion = 0;
vRecv >> fAnnounceUsingCMPCTBLOCK >> nCMPCTBLOCKVersion;
if (nCMPCTBLOCKVersion == 1 || ((pfrom.GetLocalServices() & NODE_WITNESS) && nCMPCTBLOCKVersion == 2)) {
if (nCMPCTBLOCKVersion == 1 || ((pfrom.GetLocalServices() & NODE_WITNESS) && nCMPCTBLOCKVersion == 2) || ((pfrom.GetLocalServices() & NODE_MWEB) && nCMPCTBLOCKVersion == 3)) {
LOCK(cs_main);
// fProvidesHeaderAndIDs is used to "lock in" version of compact blocks we send (fWantsCmpctWitness)
if (!State(pfrom.GetId())->fProvidesHeaderAndIDs) {
State(pfrom.GetId())->fProvidesHeaderAndIDs = true;
State(pfrom.GetId())->fWantsCmpctWitness = nCMPCTBLOCKVersion == 2;
State(pfrom.GetId())->fWantsCmpctWitness = nCMPCTBLOCKVersion >= 2;
State(pfrom.GetId())->fWantsCmpctMWEB = nCMPCTBLOCKVersion >= 3;
}
if (State(pfrom.GetId())->fWantsCmpctWitness == (nCMPCTBLOCKVersion == 2)) // ignore later version announces
if (State(pfrom.GetId())->fWantsCmpctWitness == (nCMPCTBLOCKVersion >= 2) && State(pfrom.GetId())->fWantsCmpctMWEB == (nCMPCTBLOCKVersion >= 3))
State(pfrom.GetId())->fPreferHeaderAndIDs = fAnnounceUsingCMPCTBLOCK;
if (!State(pfrom.GetId())->fSupportsDesiredCmpctVersion) {
if (pfrom.GetLocalServices() & NODE_WITNESS)
if (pfrom.GetLocalServices() & NODE_MWEB)
State(pfrom.GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 3);
else if (pfrom.GetLocalServices() & NODE_WITNESS)
State(pfrom.GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 2);
else
State(pfrom.GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 1);
@ -2879,6 +2907,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block > %i deep\n", pfrom.GetId(), MAX_BLOCKTXN_DEPTH);
CInv inv;
WITH_LOCK(cs_main, inv.type = State(pfrom.GetId())->fWantsCmpctWitness ? MSG_WITNESS_BLOCK : MSG_BLOCK);
WITH_LOCK(cs_main, inv.type = State(pfrom.GetId())->fWantsCmpctMWEB ? MSG_MWEB_BLOCK : inv.type);
inv.hash = req.blockhash;
WITH_LOCK(peer->m_getdata_requests_mutex, peer->m_getdata_requests.push_back(inv));
// The message processing loop will go around again (without pausing) and we'll respond then
@ -3175,6 +3204,15 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
LogPrint(BCLog::NET, "Unexpected cmpctblock message received from peer %d\n", pfrom.GetId());
return;
}
{
LOCK(cs_main);
CBlockIndex* pTip = ::ChainActive().Tip();
assert(pTip);
if (!IsMWEBEnabled(pTip->pprev, m_chainparams.GetConsensus()) && !State(pfrom.GetId())->fWantsCmpctMWEB) {
vRecv.SetVersion(vRecv.GetVersion() | SERIALIZE_NO_MWEB);
}
}
CBlockHeaderAndShortTxIDs cmpctblock;
vRecv >> cmpctblock;
@ -3263,6 +3301,12 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
return;
}
if (IsMWEBEnabled(pindex->pprev, m_chainparams.GetConsensus()) && !nodestate->fWantsCmpctMWEB) {
// Don't bother trying to process compact blocks from v1/v2 peers
// after MWEB activates.
return;
}
// We want to be a bit conservative just to be extra careful about DoS
// possibilities in compact block processing...
if (pindex->nHeight <= ::ChainActive().Height() + 2) {
@ -3271,7 +3315,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
std::list<QueuedBlock>::iterator* queuedBlockIt = nullptr;
if (!MarkBlockAsInFlight(m_mempool, pfrom.GetId(), pindex->GetBlockHash(), pindex, &queuedBlockIt)) {
if (!(*queuedBlockIt)->partialBlock)
(*queuedBlockIt)->partialBlock.reset(new PartiallyDownloadedBlock(&m_mempool));
(*queuedBlockIt)->partialBlock.reset(new PartiallyDownloadedBlock(&m_mempool, cmpctblock.mweb_block));
else {
// The block was already in flight using compact blocks from the same peer
LogPrint(BCLog::NET, "Peer sent us compact block we were already syncing!\n");
@ -3314,7 +3358,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
// download from.
// Optimistically try to reconstruct anyway since we might be
// able to without any round trips.
PartiallyDownloadedBlock tempBlock(&m_mempool);
PartiallyDownloadedBlock tempBlock(&m_mempool, cmpctblock.mweb_block);
ReadStatus status = tempBlock.InitData(cmpctblock, vExtraTxnForCompact);
if (status != READ_STATUS_OK) {
// TODO: don't ignore failures
@ -4250,6 +4294,7 @@ bool PeerManager::SendMessages(CNode* pto)
vHeaders.front().GetHash().ToString(), pto->GetId());
int nSendFlags = state.fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
nSendFlags |= state.fWantsCmpctMWEB ? 0 : SERIALIZE_NO_MWEB;
bool fGotBlockFromCache = false;
{

@ -5,6 +5,7 @@
#include <protocol.h>
#include <chainparams.h>
#include <util/strencodings.h>
#include <util/system.h>
@ -162,6 +163,8 @@ bool operator<(const CInv& a, const CInv& b)
std::string CInv::GetCommand() const
{
std::string cmd;
if (type & MSG_MWEB_FLAG)
cmd.append("mweb-");
if (type & MSG_WITNESS_FLAG)
cmd.append("witness-");
int masked = type & MSG_TYPE_MASK;

@ -405,6 +405,7 @@ public:
/** getdata message type flags */
const uint32_t MSG_WITNESS_FLAG = 1 << 30;
const uint32_t MSG_MWEB_FLAG = 1 << 29;
const uint32_t MSG_TYPE_MASK = 0xffffffff >> 2;
/** getdata / inv message types.
@ -424,6 +425,8 @@ enum GetDataMsg : uint32_t {
// MSG_FILTERED_WITNESS_BLOCK is defined in BIP144 as reserved for future
// use and remains unused.
// MSG_FILTERED_WITNESS_BLOCK = MSG_FILTERED_BLOCK | MSG_WITNESS_FLAG,
MSG_MWEB_BLOCK = MSG_WITNESS_BLOCK | MSG_MWEB_FLAG,
MSG_MWEB_TX = MSG_WITNESS_TX | MSG_MWEB_FLAG,
};
/** inv message data */
@ -447,15 +450,16 @@ public:
bool IsMsgFilteredBlk() const { return type == MSG_FILTERED_BLOCK; }
bool IsMsgCmpctBlk() const { return type == MSG_CMPCT_BLOCK; }
bool IsMsgWitnessBlk() const { return type == MSG_WITNESS_BLOCK; }
bool IsMsgMWEBBlk() const { return type == MSG_MWEB_BLOCK; }
// Combined-message helper methods
bool IsGenTxMsg() const
{
return type == MSG_TX || type == MSG_WTX || type == MSG_WITNESS_TX;
return type == MSG_TX || type == MSG_WTX || type == MSG_WITNESS_TX || type == MSG_MWEB_TX;
}
bool IsGenBlkMsg() const
{
return type == MSG_BLOCK || type == MSG_FILTERED_BLOCK || type == MSG_CMPCT_BLOCK || type == MSG_WITNESS_BLOCK;
return type == MSG_BLOCK || type == MSG_FILTERED_BLOCK || type == MSG_CMPCT_BLOCK || type == MSG_WITNESS_BLOCK || type == MSG_MWEB_BLOCK;
}
uint32_t type;

Loading…
Cancel
Save