Skip to content

Commit b5c92a4

Browse files
committed
Handle bisection sketch
If a peer responded to our request with a bisection sketch, attempt to decode it to find missing transactions by combining it with the initial sketch. If success, share/request missing transactions. If failure, send all the transactions we had for the peer. In case of a partial failure, apply this logic accordingly to the parts.
1 parent 152e9bc commit b5c92a4

File tree

1 file changed

+149
-23
lines changed

1 file changed

+149
-23
lines changed

src/net_processing.cpp

+149-23
Original file line numberDiff line numberDiff line change
@@ -774,7 +774,12 @@ struct Peer {
774774
// When reconciliation initialized by us is done, update local q for future reconciliations.
775775
if (action == LocalQAction::Q_RECOMPUTE) {
776776
assert(m_outgoing_recon != ReconPhase::NONE);
777-
uint8_t local_set_size = m_local_set.size();
777+
uint8_t local_set_size;
778+
if (m_outgoing_recon == ReconPhase::BISEC_REQUESTED) {
779+
local_set_size = m_local_set_snapshot.size();
780+
} else {
781+
local_set_size = m_local_set.size();
782+
}
778783
uint8_t remote_set_size = local_set_size + actual_local_missing - actual_remote_missing;
779784
uint8_t set_size_diff = std::abs(local_set_size - remote_set_size);
780785
uint8_t min_size = std::min(local_set_size, remote_set_size);
@@ -4273,12 +4278,13 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
42734278
return;
42744279
}
42754280

4276-
// Received a response to the reconciliation request.
4281+
// Received a response to the reconciliation request (initial request or request for bisection if initial failed).
42774282
// May leak tx-related privacy if we announce local transactions right away, if a peer is strategic about sending
42784283
// sketches to us via different connections (requires attacker to occupy multiple outgoing connections).
42794284
if (msg_type == NetMsgType::SKETCH) {
42804285
if (peer->m_recon_state == nullptr) return;
4281-
if (peer->m_recon_state->m_outgoing_recon != Peer::ReconState::ReconPhase::INIT_REQUESTED) return;
4286+
if (peer->m_recon_state->m_outgoing_recon != Peer::ReconState::ReconPhase::INIT_REQUESTED &&
4287+
peer->m_recon_state->m_outgoing_recon != Peer::ReconState::ReconPhase::BISEC_REQUESTED) return;
42824288

42834289
std::vector<uint8_t> skdata;
42844290
vRecv >> skdata;
@@ -4294,7 +4300,11 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
42944300

42954301
minisketch* working_sketch; // Contains sketch of the set difference (full or low chunk)
42964302
minisketch* local_sketch; // Stores local sketch (full or low chunk)
4297-
local_sketch = peer->m_recon_state->ComputeSketch(Peer::ReconState::BisectionChunk::BISECTION_NONE, remote_sketch_capacity, false);
4303+
if (peer->m_recon_state->m_outgoing_recon == Peer::ReconState::ReconPhase::INIT_REQUESTED) {
4304+
local_sketch = peer->m_recon_state->ComputeSketch(Peer::ReconState::BisectionChunk::BISECTION_NONE, remote_sketch_capacity, false);
4305+
} else {
4306+
local_sketch = peer->m_recon_state->ComputeSketch(Peer::ReconState::BisectionChunk::BISECTION_LOW, remote_sketch_capacity, false);
4307+
}
42984308
if (local_sketch != nullptr) {
42994309
working_sketch = minisketch_clone(local_sketch);
43004310
minisketch_merge(working_sketch, remote_sketch);
@@ -4311,29 +4321,145 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
43114321
std::vector<uint32_t> local_missing;
43124322
if (decoded) {
43134323
// Reconciliation over the current working chunk succeeded
4314-
// Initial reconciliation succeeded
4315-
// Send/request transactions which found to be missing
4316-
LogPrint(BCLog::NET, "Outgoing reconciliation with peer=%i succeeded without bisection\n", pfrom.GetId());
4317-
std::vector<uint256> remote_missing = peer->m_recon_state->GetRelevantIDsFromShortIDs(differences, num_differences, local_missing);
4318-
AnnounceTxs(remote_missing, &pfrom, msgMaker, &m_connman);
4319-
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::RECONCILDIFF, uint8_t(RECON_SUCCESS), local_missing));
4320-
peer->m_recon_state->FinalizeReconciliation(true, Peer::ReconState::LocalQAction::Q_RECOMPUTE, local_missing.size(), remote_missing.size());
4324+
if (peer->m_recon_state->m_outgoing_recon == Peer::ReconState::ReconPhase::INIT_REQUESTED) {
4325+
// Initial reconciliation succeeded
4326+
// Send/request transactions which found to be missing
4327+
LogPrint(BCLog::NET, "Outgoing reconciliation with peer=%I succeeded without bisection\n", pfrom.GetId());
4328+
std::vector<uint256> remote_missing = peer->m_recon_state->GetRelevantIDsFromShortIDs(differences, num_differences, local_missing);
4329+
AnnounceTxs(remote_missing, &pfrom, msgMaker, &m_connman);
4330+
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::RECONCILDIFF, uint8_t(RECON_SUCCESS), local_missing));
4331+
peer->m_recon_state->FinalizeReconciliation(true, Peer::ReconState::LocalQAction::Q_RECOMPUTE, local_missing.size(), remote_missing.size());
4332+
} else {
4333+
// Reconciliation over the low chunk succeeded.
4334+
// Attempt to reconcile over the high chunks.
4335+
4336+
LogPrint(BCLog::NET, "Outgoing reconciliation with peer=%I succeeded after bisection (low chunk)\n", pfrom.GetId());
4337+
// Compute missing transactions in the low chunk.
4338+
std::vector<uint256> remote_missing = peer->m_recon_state->GetRelevantIDsFromShortIDs(differences, num_differences, local_missing);
4339+
4340+
// Attempt to find the difference over the high chunk, which can be derived from the low.
4341+
assert(peer->m_recon_state->m_remote_sketch_snapshot != nullptr); // Otherwise reconciliation should have been cancelled.
4342+
minisketch* working_sketch_high = minisketch_clone(peer->m_recon_state->m_remote_sketch_snapshot);
4343+
minisketch_merge(working_sketch_high, remote_sketch); // Working sketch now stores high chunk of the remote set.
4344+
4345+
if (peer->m_recon_state->m_local_sketch_snapshot != nullptr) {
4346+
// Computes sketch of the local high chunk.
4347+
minisketch* local_sketch_high = minisketch_clone(peer->m_recon_state->m_local_sketch_snapshot);
4348+
if (local_sketch != nullptr) {
4349+
minisketch_merge(local_sketch_high, local_sketch);
4350+
}
4351+
4352+
// Combine high chunks to compute the high chunk diff.
4353+
minisketch_merge(working_sketch_high, local_sketch_high);
4354+
}
4355+
4356+
// Attempt to decode the set difference (high chunk).
4357+
uint64_t differences_high_chunk[RECONCIL_MAX_DIFF];
4358+
num_differences = minisketch_decode(working_sketch_high, RECONCIL_MAX_DIFF, differences_high_chunk);
4359+
max_possible_differences = minisketch_compute_max_elements(RECON_FIELD_SIZE, remote_sketch_capacity, RECON_FALSE_POSITIVE_COEF);
4360+
bool high_chunk_decoded = (0 <= num_differences) && (num_differences <= ssize_t(max_possible_differences));
4361+
4362+
if (high_chunk_decoded) {
4363+
// High chunk decoded and low chunk decoded. Bisection fully succeeded.
4364+
// Request and send only transactions which were identified to be missing.
4365+
4366+
LogPrint(BCLog::NET, "Outgoing reconciliation with peer=%I succeeded after bisection (high chunk)\n", pfrom.GetId());
4367+
std::vector<uint32_t> local_missing_high;
4368+
std::vector<uint256> remote_missing_high = peer->m_recon_state->GetRelevantIDsFromShortIDs(differences_high_chunk, num_differences, local_missing_high);
4369+
local_missing.insert(local_missing.end(), local_missing_high.begin(), local_missing_high.end());
4370+
remote_missing.insert(remote_missing.end(), remote_missing_high.begin(), remote_missing_high.end());
4371+
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::RECONCILDIFF, uint8_t(RECON_SUCCESS), local_missing));
4372+
AnnounceTxs(remote_missing, &pfrom, msgMaker, &m_connman);
4373+
peer->m_recon_state->FinalizeReconciliation(false, Peer::ReconState::LocalQAction::Q_RECOMPUTE, local_missing.size(), remote_missing.size());
4374+
} else {
4375+
// High chunk failed to decode and Low chunk decoded. Bisection partially succeeded.
4376+
// From the high chunk, send all transactions. From the low chunk, send and request those identified to be missing.
4377+
// The peer will announce their transactions from the high chunk to us.
4378+
4379+
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::RECONCILDIFF, uint8_t(RECON_HIGH_FAILED), local_missing));
4380+
for (uint256 wtxid : peer->m_recon_state->m_local_set_snapshot) {
4381+
uint32_t short_txid = peer->m_recon_state->ComputeShortID(wtxid);
4382+
if (short_txid > BISECTION_MEDIAN) // use only high chunk, low chunk transactions are already in the vector
4383+
remote_missing.push_back(wtxid);
4384+
}
4385+
AnnounceTxs(remote_missing, &pfrom, msgMaker, &m_connman);
4386+
LogPrint(BCLog::NET, "Outgoing reconciliation with peer=%I failed after bisection (high chunk)\n", pfrom.GetId());
4387+
peer->m_recon_state->FinalizeReconciliation(false, Peer::ReconState::LocalQAction::Q_INCREASE, 0, 0);
4388+
}
4389+
}
43214390
return;
43224391
} else {
43234392
// Reconciliation over the current working chunk failed.
4324-
// Initial reconciliation failed.
4325-
// Store the received sketch and the local sketch, request bisection.
4326-
4327-
assert(remote_sketch != nullptr);
4328-
// Prepare to request bisection.
4329-
peer->m_recon_state->m_remote_sketch_snapshot = minisketch_clone(remote_sketch);
4330-
if (local_sketch != nullptr) {
4331-
peer->m_recon_state->m_local_sketch_snapshot = minisketch_clone(local_sketch);
4393+
if (peer->m_recon_state->m_outgoing_recon == Peer::ReconState::ReconPhase::INIT_REQUESTED) {
4394+
// Initial reconciliation failed.
4395+
// Store the received sketch and the local sketch, request bisection.
4396+
4397+
assert(remote_sketch != nullptr);
4398+
// Prepare to request bisection.
4399+
peer->m_recon_state->m_remote_sketch_snapshot = minisketch_clone(remote_sketch);
4400+
if (local_sketch != nullptr) {
4401+
peer->m_recon_state->m_local_sketch_snapshot = minisketch_clone(local_sketch);
4402+
}
4403+
peer->m_recon_state->m_local_set_snapshot = peer->m_recon_state->m_local_set;
4404+
peer->m_recon_state->m_local_set.clear();
4405+
LogPrint(BCLog::NET, "Outgoing reconciliation with peer=%I initially failed, requesting bisection\n", pfrom.GetId());
4406+
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::REQBISEC));
4407+
peer->m_recon_state->m_outgoing_recon = Peer::ReconState::ReconPhase::BISEC_REQUESTED;
4408+
} else {
4409+
// Reconciliation over the low chunk failed during bisection.
4410+
// Announce all local transactions from the low chunk.
4411+
// Attempt to reconstruct the difference from locally computed high chunk.
4412+
4413+
LogPrint(BCLog::NET, "Outgoing reconciliation with peer=%I failed after bisection (low chunk)\n", pfrom.GetId());
4414+
assert(peer->m_recon_state->m_remote_sketch_snapshot != nullptr); // Otherwise should have been cancelled
4415+
minisketch* working_sketch_high = minisketch_clone(peer->m_recon_state->m_remote_sketch_snapshot);
4416+
minisketch_merge(working_sketch_high, remote_sketch); // Sketch of the remote high chunk
4417+
4418+
// Empty snapshot implies there were no transactions locally
4419+
if (peer->m_recon_state->m_local_sketch_snapshot != nullptr) {
4420+
minisketch* local_sketch_high = minisketch_clone(peer->m_recon_state->m_local_sketch_snapshot);
4421+
if (local_sketch != nullptr) {
4422+
minisketch_merge(local_sketch_high, local_sketch); // Sketch of the local high chunk
4423+
}
4424+
minisketch_merge(working_sketch_high, local_sketch_high); // Sketch of the difference in high chunks
4425+
}
4426+
4427+
// Attempt to find difference in high chunks
4428+
uint64_t differences_high_chunk[RECONCIL_MAX_DIFF];
4429+
num_differences = minisketch_decode(working_sketch_high, RECONCIL_MAX_DIFF, differences_high_chunk);
4430+
max_possible_differences = minisketch_compute_max_elements(RECON_FIELD_SIZE, remote_sketch_capacity, RECON_FALSE_POSITIVE_COEF);
4431+
bool high_chunk_decoded = (0 <= num_differences) && (num_differences <= ssize_t(max_possible_differences));
4432+
4433+
std::vector<uint256> remote_missing;
4434+
if (high_chunk_decoded) {
4435+
// Reconciliation over the high chunks succeeded.
4436+
// Send/request only missing transactions from the high chunk, and send all from the low (because low failed).
4437+
// Can’t request missing low because we have no idea which those are, but peer will send them after finding out
4438+
// that low chunk decoding failed.
4439+
4440+
LogPrint(BCLog::NET, "Outgoing reconciliation with peer=%I succeeded after bisection (high chunk)\n", pfrom.GetId());
4441+
std::vector<uint32_t> local_missing_high;
4442+
remote_missing = peer->m_recon_state->GetRelevantIDsFromShortIDs(differences_high_chunk, num_differences, local_missing_high);
4443+
for (uint256 local_wtxid : peer->m_recon_state->m_local_set_snapshot) {
4444+
uint32_t short_id = peer->m_recon_state->ComputeShortID(local_wtxid);
4445+
if (short_id <= BISECTION_MEDIAN) {
4446+
remote_missing.push_back(local_wtxid);
4447+
}
4448+
}
4449+
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::RECONCILDIFF, uint8_t(RECON_LOW_FAILED), local_missing_high));
4450+
peer->m_recon_state->FinalizeReconciliation(false, Peer::ReconState::LocalQAction::Q_INCREASE, 0, 0);
4451+
} else {
4452+
// Reconciliation over the high chunks also failed.
4453+
// Announce all local transactions.
4454+
// All remote transactions will be announced by peer due to the reconciliation failure flag.
4455+
4456+
LogPrint(BCLog::NET, "Outgoing reconciliation with peer=%I failed after bisection (high chunk)\n", pfrom.GetId());
4457+
remote_missing.insert(remote_missing.end(), peer->m_recon_state->m_local_set_snapshot.begin(), peer->m_recon_state->m_local_set_snapshot.end());
4458+
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::RECONCILDIFF, uint8_t(RECON_FAILED), std::vector<uint32_t>()));
4459+
peer->m_recon_state->FinalizeReconciliation(false, Peer::ReconState::LocalQAction::Q_SET_DEFAULT, 0, 0);
4460+
}
4461+
AnnounceTxs(remote_missing, &pfrom, msgMaker, &m_connman);
43324462
}
4333-
peer->m_recon_state->m_local_set.clear();
4334-
LogPrint(BCLog::NET, "Outgoing reconciliation with peer=%i initially failed, requesting bisection\n", pfrom.GetId());
4335-
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::REQBISEC));
4336-
peer->m_recon_state->m_outgoing_recon = Peer::ReconState::ReconPhase::BISEC_REQUESTED;
43374463
}
43384464
return;
43394465
}

0 commit comments

Comments
 (0)