@@ -774,7 +774,12 @@ struct Peer {
774
774
// When reconciliation initialized by us is done, update local q for future reconciliations.
775
775
if (action == LocalQAction::Q_RECOMPUTE) {
776
776
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
+ }
778
783
uint8_t remote_set_size = local_set_size + actual_local_missing - actual_remote_missing;
779
784
uint8_t set_size_diff = std::abs (local_set_size - remote_set_size);
780
785
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
4273
4278
return ;
4274
4279
}
4275
4280
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) .
4277
4282
// May leak tx-related privacy if we announce local transactions right away, if a peer is strategic about sending
4278
4283
// sketches to us via different connections (requires attacker to occupy multiple outgoing connections).
4279
4284
if (msg_type == NetMsgType::SKETCH) {
4280
4285
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 ;
4282
4288
4283
4289
std::vector<uint8_t > skdata;
4284
4290
vRecv >> skdata;
@@ -4294,7 +4300,11 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
4294
4300
4295
4301
minisketch* working_sketch; // Contains sketch of the set difference (full or low chunk)
4296
4302
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
+ }
4298
4308
if (local_sketch != nullptr ) {
4299
4309
working_sketch = minisketch_clone (local_sketch);
4300
4310
minisketch_merge (working_sketch, remote_sketch);
@@ -4311,29 +4321,145 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
4311
4321
std::vector<uint32_t > local_missing;
4312
4322
if (decoded) {
4313
4323
// 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
+ }
4321
4390
return ;
4322
4391
} else {
4323
4392
// 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);
4332
4462
}
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;
4337
4463
}
4338
4464
return ;
4339
4465
}
0 commit comments