Use cmpctblock type 2 for segwit-enabled transfer

Contains version negotiation logic by Matt Corallo and bugfixes by
Suhas Daftuar.

Github-Pull: #8393
Rebased-From: 6aa28abf53
This commit is contained in:
Pieter Wuille 2016-06-25 19:17:45 +02:00 committed by Wladimir J. van der Laan
parent 611cc5096e
commit fe1975a974
6 changed files with 64 additions and 30 deletions

View File

@ -17,7 +17,7 @@
#define MIN_TRANSACTION_BASE_SIZE (::GetSerializeSize(CTransaction(), SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS)) #define MIN_TRANSACTION_BASE_SIZE (::GetSerializeSize(CTransaction(), SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS))
CBlockHeaderAndShortTxIDs::CBlockHeaderAndShortTxIDs(const CBlock& block) : CBlockHeaderAndShortTxIDs::CBlockHeaderAndShortTxIDs(const CBlock& block, bool fUseWTXID) :
nonce(GetRand(std::numeric_limits<uint64_t>::max())), nonce(GetRand(std::numeric_limits<uint64_t>::max())),
shorttxids(block.vtx.size() - 1), prefilledtxn(1), header(block) { shorttxids(block.vtx.size() - 1), prefilledtxn(1), header(block) {
FillShortTxIDSelector(); FillShortTxIDSelector();
@ -25,7 +25,7 @@ CBlockHeaderAndShortTxIDs::CBlockHeaderAndShortTxIDs(const CBlock& block) :
prefilledtxn[0] = {0, block.vtx[0]}; prefilledtxn[0] = {0, block.vtx[0]};
for (size_t i = 1; i < block.vtx.size(); i++) { for (size_t i = 1; i < block.vtx.size(); i++) {
const CTransaction& tx = block.vtx[i]; const CTransaction& tx = block.vtx[i];
shorttxids[i - 1] = GetShortID(tx.GetHash()); shorttxids[i - 1] = GetShortID(fUseWTXID ? tx.GetWitnessHash() : tx.GetHash());
} }
} }

View File

@ -146,7 +146,7 @@ public:
// Dummy for deserialization // Dummy for deserialization
CBlockHeaderAndShortTxIDs() {} CBlockHeaderAndShortTxIDs() {}
CBlockHeaderAndShortTxIDs(const CBlock& block); CBlockHeaderAndShortTxIDs(const CBlock& block, bool fUseWTXID);
uint64_t GetShortID(const uint256& txhash) const; uint64_t GetShortID(const uint256& txhash) const;

View File

@ -292,10 +292,21 @@ struct CNodeState {
bool fPreferHeaders; bool fPreferHeaders;
//! Whether this peer wants invs or cmpctblocks (when possible) for block announcements. //! Whether this peer wants invs or cmpctblocks (when possible) for block announcements.
bool fPreferHeaderAndIDs; bool fPreferHeaderAndIDs;
//! Whether this peer will send us cmpctblocks if we request them /**
* Whether this peer will send us cmpctblocks if we request them.
* This is not used to gate request logic, as we really only care about fSupportsDesiredCmpctVersion,
* but is used as a flag to "lock in" the version of compact blocks (fWantsCmpctWitness) we send.
*/
bool fProvidesHeaderAndIDs; bool fProvidesHeaderAndIDs;
//! Whether this peer can give us witnesses //! Whether this peer can give us witnesses
bool fHaveWitness; bool fHaveWitness;
//! Whether this peer wants witnesses in cmpctblocks/blocktxns
bool fWantsCmpctWitness;
/**
* 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.
*/
bool fSupportsDesiredCmpctVersion;
CNodeState() { CNodeState() {
fCurrentlyConnected = false; fCurrentlyConnected = false;
@ -316,6 +327,8 @@ struct CNodeState {
fPreferHeaderAndIDs = false; fPreferHeaderAndIDs = false;
fProvidesHeaderAndIDs = false; fProvidesHeaderAndIDs = false;
fHaveWitness = false; fHaveWitness = false;
fWantsCmpctWitness = false;
fSupportsDesiredCmpctVersion = false;
} }
}; };
@ -475,8 +488,8 @@ void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) {
} }
void MaybeSetPeerAsAnnouncingHeaderAndIDs(const CNodeState* nodestate, CNode* pfrom) { void MaybeSetPeerAsAnnouncingHeaderAndIDs(const CNodeState* nodestate, CNode* pfrom) {
if (nLocalServices & NODE_WITNESS) { if (!nodestate->fSupportsDesiredCmpctVersion) {
// Don't ever request compact blocks when segwit is enabled. // Never ask from peers who can't provide witnesses.
return; return;
} }
if (nodestate->fProvidesHeaderAndIDs) { if (nodestate->fProvidesHeaderAndIDs) {
@ -484,7 +497,7 @@ void MaybeSetPeerAsAnnouncingHeaderAndIDs(const CNodeState* nodestate, CNode* pf
if (nodeid == pfrom->GetId()) if (nodeid == pfrom->GetId())
return; return;
bool fAnnounceUsingCMPCTBLOCK = false; bool fAnnounceUsingCMPCTBLOCK = false;
uint64_t nCMPCTBLOCKVersion = 1; uint64_t nCMPCTBLOCKVersion = (pfrom->GetLocalServices() & NODE_WITNESS) ? 2 : 1;
if (lNodesAnnouncingHeaderAndIDs.size() >= 3) { if (lNodesAnnouncingHeaderAndIDs.size() >= 3) {
// As per BIP152, we only get 3 of our peers to announce // As per BIP152, we only get 3 of our peers to announce
// blocks using compact encodings. // blocks using compact encodings.
@ -4832,11 +4845,12 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
// they wont have a useful mempool to match against a compact block, // they wont have a useful mempool to match against a compact block,
// and we don't feel like constructing the object for them, so // and we don't feel like constructing the object for them, so
// instead we respond with the full, non-compact block. // instead we respond with the full, non-compact block.
bool fPeerWantsWitness = State(pfrom->GetId())->fWantsCmpctWitness;
if (mi->second->nHeight >= chainActive.Height() - 10) { if (mi->second->nHeight >= chainActive.Height() - 10) {
CBlockHeaderAndShortTxIDs cmpctblock(block); CBlockHeaderAndShortTxIDs cmpctblock(block, fPeerWantsWitness);
pfrom->PushMessageWithFlag(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::CMPCTBLOCK, cmpctblock); pfrom->PushMessageWithFlag(fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::CMPCTBLOCK, cmpctblock);
} else } else
pfrom->PushMessageWithFlag(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, block); pfrom->PushMessageWithFlag(fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, block);
} }
// Trigger the peer node to send a getblocks request for the next batch of inventory // Trigger the peer node to send a getblocks request for the next batch of inventory
@ -5102,13 +5116,16 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
pfrom->PushMessage(NetMsgType::SENDHEADERS); pfrom->PushMessage(NetMsgType::SENDHEADERS);
} }
if (pfrom->nVersion >= SHORT_IDS_BLOCKS_VERSION) { if (pfrom->nVersion >= SHORT_IDS_BLOCKS_VERSION) {
// Tell our peer we are willing to provide version-1 cmpctblocks // Tell our peer we are willing to provide version 1 or 2 cmpctblocks
// However, we do not request new block announcements using // However, we do not request new block announcements using
// cmpctblock messages. // cmpctblock messages.
// We send this to non-NODE NETWORK peers as well, because // We send this to non-NODE NETWORK peers as well, because
// they may wish to request compact blocks from us // they may wish to request compact blocks from us
bool fAnnounceUsingCMPCTBLOCK = false; bool fAnnounceUsingCMPCTBLOCK = false;
uint64_t nCMPCTBLOCKVersion = 1; uint64_t nCMPCTBLOCKVersion = 2;
if (pfrom->GetLocalServices() & NODE_WITNESS)
pfrom->PushMessage(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion);
nCMPCTBLOCKVersion = 1;
pfrom->PushMessage(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion); pfrom->PushMessage(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion);
} }
} }
@ -5188,12 +5205,23 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
else if (strCommand == NetMsgType::SENDCMPCT) else if (strCommand == NetMsgType::SENDCMPCT)
{ {
bool fAnnounceUsingCMPCTBLOCK = false; bool fAnnounceUsingCMPCTBLOCK = false;
uint64_t nCMPCTBLOCKVersion = 1; uint64_t nCMPCTBLOCKVersion = 0;
vRecv >> fAnnounceUsingCMPCTBLOCK >> nCMPCTBLOCKVersion; vRecv >> fAnnounceUsingCMPCTBLOCK >> nCMPCTBLOCKVersion;
if (nCMPCTBLOCKVersion == 1) { if (nCMPCTBLOCKVersion == 1 || ((pfrom->GetLocalServices() & NODE_WITNESS) && nCMPCTBLOCKVersion == 2)) {
LOCK(cs_main); LOCK(cs_main);
State(pfrom->GetId())->fProvidesHeaderAndIDs = true; // fProvidesHeaderAndIDs is used to "lock in" version of compact blocks we send (fWantsCmpctWitness)
State(pfrom->GetId())->fPreferHeaderAndIDs = fAnnounceUsingCMPCTBLOCK; if (!State(pfrom->GetId())->fProvidesHeaderAndIDs) {
State(pfrom->GetId())->fProvidesHeaderAndIDs = true;
State(pfrom->GetId())->fWantsCmpctWitness = nCMPCTBLOCKVersion == 2;
}
if (State(pfrom->GetId())->fWantsCmpctWitness == (nCMPCTBLOCKVersion == 2)) // ignore later version announces
State(pfrom->GetId())->fPreferHeaderAndIDs = fAnnounceUsingCMPCTBLOCK;
if (!State(pfrom->GetId())->fSupportsDesiredCmpctVersion) {
if (pfrom->GetLocalServices() & NODE_WITNESS)
State(pfrom->GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 2);
else
State(pfrom->GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 1);
}
} }
} }
@ -5251,7 +5279,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER &&
(!IsWitnessEnabled(chainActive.Tip(), chainparams.GetConsensus()) || State(pfrom->GetId())->fHaveWitness)) { (!IsWitnessEnabled(chainActive.Tip(), chainparams.GetConsensus()) || State(pfrom->GetId())->fHaveWitness)) {
inv.type |= nFetchFlags; inv.type |= nFetchFlags;
if (nodestate->fProvidesHeaderAndIDs && !(nLocalServices & NODE_WITNESS)) if (nodestate->fSupportsDesiredCmpctVersion)
vToFetch.push_back(CInv(MSG_CMPCT_BLOCK, inv.hash)); vToFetch.push_back(CInv(MSG_CMPCT_BLOCK, inv.hash));
else else
vToFetch.push_back(inv); vToFetch.push_back(inv);
@ -5379,7 +5407,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
} }
resp.txn[i] = block.vtx[req.indexes[i]]; resp.txn[i] = block.vtx[req.indexes[i]];
} }
pfrom->PushMessageWithFlag(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCKTXN, resp); pfrom->PushMessageWithFlag(State(pfrom->GetId())->fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCKTXN, resp);
} }
@ -5643,7 +5671,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// We requested this block for some reason, but our mempool will probably be useless // We requested this block for some reason, but our mempool will probably be useless
// so we just grab the block via normal getdata // so we just grab the block via normal getdata
std::vector<CInv> vInv(1); std::vector<CInv> vInv(1);
vInv[0] = CInv(MSG_BLOCK, cmpctblock.header.GetHash()); vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom, pindex->pprev, chainparams.GetConsensus()), cmpctblock.header.GetHash());
pfrom->PushMessage(NetMsgType::GETDATA, vInv); pfrom->PushMessage(NetMsgType::GETDATA, vInv);
} }
return true; return true;
@ -5655,6 +5683,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
CNodeState *nodestate = State(pfrom->GetId()); CNodeState *nodestate = State(pfrom->GetId());
if (IsWitnessEnabled(pindex->pprev, chainparams.GetConsensus()) && !nodestate->fSupportsDesiredCmpctVersion) {
// Don't bother trying to process compact blocks from v1 peers
// after segwit activates.
return true;
}
// We want to be a bit conservative just to be extra careful about DoS // We want to be a bit conservative just to be extra careful about DoS
// possibilities in compact block processing... // possibilities in compact block processing...
if (pindex->nHeight <= chainActive.Height() + 2) { if (pindex->nHeight <= chainActive.Height() + 2) {
@ -5681,7 +5715,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
} else if (status == READ_STATUS_FAILED) { } else if (status == READ_STATUS_FAILED) {
// Duplicate txindexes, the block is now in-flight, so just request it // Duplicate txindexes, the block is now in-flight, so just request it
std::vector<CInv> vInv(1); std::vector<CInv> vInv(1);
vInv[0] = CInv(MSG_BLOCK, cmpctblock.header.GetHash()); vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom, pindex->pprev, chainparams.GetConsensus()), cmpctblock.header.GetHash());
pfrom->PushMessage(NetMsgType::GETDATA, vInv); pfrom->PushMessage(NetMsgType::GETDATA, vInv);
return true; return true;
} }
@ -5708,7 +5742,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// We requested this block, but its far into the future, so our // We requested this block, but its far into the future, so our
// mempool will probably be useless - request the block normally // mempool will probably be useless - request the block normally
std::vector<CInv> vInv(1); std::vector<CInv> vInv(1);
vInv[0] = CInv(MSG_BLOCK, cmpctblock.header.GetHash()); vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom, pindex->pprev, chainparams.GetConsensus()), cmpctblock.header.GetHash());
pfrom->PushMessage(NetMsgType::GETDATA, vInv); pfrom->PushMessage(NetMsgType::GETDATA, vInv);
return true; return true;
} else { } else {
@ -5750,7 +5784,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
} else if (status == READ_STATUS_FAILED) { } else if (status == READ_STATUS_FAILED) {
// Might have collided, fall back to getdata now :( // Might have collided, fall back to getdata now :(
std::vector<CInv> invs; std::vector<CInv> invs;
invs.push_back(CInv(MSG_BLOCK, resp.blockhash)); invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(pfrom, chainActive.Tip(), chainparams.GetConsensus()), resp.blockhash));
pfrom->PushMessage(NetMsgType::GETDATA, invs); pfrom->PushMessage(NetMsgType::GETDATA, invs);
} else { } else {
CValidationState state; CValidationState state;
@ -5899,7 +5933,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
pindexLast->GetBlockHash().ToString(), pindexLast->nHeight); pindexLast->GetBlockHash().ToString(), pindexLast->nHeight);
} }
if (vGetData.size() > 0) { if (vGetData.size() > 0) {
if (nodestate->fProvidesHeaderAndIDs && vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN) && !(nLocalServices & NODE_WITNESS)) { if (nodestate->fSupportsDesiredCmpctVersion && vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN)) {
// We seem to be rather well-synced, so it appears pfrom was the first to provide us // We seem to be rather well-synced, so it appears pfrom was the first to provide us
// with this block! Let's get them to announce using compact blocks in the future. // with this block! Let's get them to announce using compact blocks in the future.
MaybeSetPeerAsAnnouncingHeaderAndIDs(nodestate, pfrom); MaybeSetPeerAsAnnouncingHeaderAndIDs(nodestate, pfrom);
@ -6527,8 +6561,8 @@ bool SendMessages(CNode* pto)
//TODO: Shouldn't need to reload block from disk, but requires refactor //TODO: Shouldn't need to reload block from disk, but requires refactor
CBlock block; CBlock block;
assert(ReadBlockFromDisk(block, pBestIndex, consensusParams)); assert(ReadBlockFromDisk(block, pBestIndex, consensusParams));
CBlockHeaderAndShortTxIDs cmpctblock(block); CBlockHeaderAndShortTxIDs cmpctblock(block, state.fWantsCmpctWitness);
pto->PushMessageWithFlag(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::CMPCTBLOCK, cmpctblock); pto->PushMessageWithFlag(state.fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::CMPCTBLOCK, cmpctblock);
state.pindexBestHeaderSent = pBestIndex; state.pindexBestHeaderSent = pBestIndex;
} else if (state.fPreferHeaders) { } else if (state.fPreferHeaders) {
if (vHeaders.size() > 1) { if (vHeaders.size() > 1) {

View File

@ -64,7 +64,7 @@ BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
// Do a simple ShortTxIDs RT // Do a simple ShortTxIDs RT
{ {
CBlockHeaderAndShortTxIDs shortIDs(block); CBlockHeaderAndShortTxIDs shortIDs(block, true);
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << shortIDs; stream << shortIDs;
@ -116,7 +116,7 @@ public:
stream >> *this; stream >> *this;
} }
TestHeaderAndShortIDs(const CBlock& block) : TestHeaderAndShortIDs(const CBlock& block) :
TestHeaderAndShortIDs(CBlockHeaderAndShortTxIDs(block)) {} TestHeaderAndShortIDs(CBlockHeaderAndShortTxIDs(block, true)) {}
uint64_t GetShortID(const uint256& txhash) const { uint64_t GetShortID(const uint256& txhash) const {
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
@ -267,7 +267,7 @@ BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
// Test simple header round-trip with only coinbase // Test simple header round-trip with only coinbase
{ {
CBlockHeaderAndShortTxIDs shortIDs(block); CBlockHeaderAndShortTxIDs shortIDs(block, false);
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << shortIDs; stream << shortIDs;

View File

@ -444,7 +444,7 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
totalTxSize += entry.GetTxSize(); totalTxSize += entry.GetTxSize();
minerPolicyEstimator->processTransaction(entry, fCurrentEstimate); minerPolicyEstimator->processTransaction(entry, fCurrentEstimate);
vTxHashes.emplace_back(hash, newit); vTxHashes.emplace_back(tx.GetWitnessHash(), newit);
newit->vTxHashesIdx = vTxHashes.size() - 1; newit->vTxHashesIdx = vTxHashes.size() - 1;
return true; return true;

View File

@ -462,7 +462,7 @@ public:
indexed_transaction_set mapTx; indexed_transaction_set mapTx;
typedef indexed_transaction_set::nth_index<0>::type::iterator txiter; typedef indexed_transaction_set::nth_index<0>::type::iterator txiter;
std::vector<std::pair<uint256, txiter> > vTxHashes; //!< All tx hashes/entries in mapTx, in random order std::vector<std::pair<uint256, txiter> > vTxHashes; //!< All tx witness hashes/entries in mapTx, in random order
struct CompareIteratorByHash { struct CompareIteratorByHash {
bool operator()(const txiter &a, const txiter &b) const { bool operator()(const txiter &a, const txiter &b) const {