Skip to content

Commit f202713

Browse files
committed
improve sync handling
1 parent c3d0651 commit f202713

File tree

3 files changed

+184
-46
lines changed

3 files changed

+184
-46
lines changed

src/server/p2p/network.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1705,9 +1705,13 @@ where S: ShareChain
17051705
// }
17061706

17071707
// let tx = self.inner_request_tx.clone();
1708-
let i_have_blocks = share_chain
1709-
.create_catchup_sync_blocks(CATCH_UP_SYNC_BLOCKS_IN_I_HAVE)
1710-
.await;
1708+
let i_have_blocks = if last_block_from_them.is_none() {
1709+
share_chain
1710+
.create_catchup_sync_blocks(CATCH_UP_SYNC_BLOCKS_IN_I_HAVE)
1711+
.await
1712+
} else {
1713+
vec![]
1714+
};
17111715

17121716
info!(target: SYNC_REQUEST_LOG_TARGET, "[{:?}] Sending catch up sync to {} for blocks {}, last block received {}. Their height:{}",
17131717
algo,

src/sharechain/in_memory.rs

Lines changed: 49 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -332,34 +332,29 @@ impl InMemoryShareChain {
332332
};
333333

334334
loop {
335-
for block in level.blocks.values() {
336-
if main_chain_only {
337-
if block.hash == level.chain_block {
338-
for uncle in &block.uncles {
339-
// Always include all the uncles, if we have them
340-
if let Some(uncle_block) =
341-
p2_chain.level_at_height(uncle.0).and_then(|l| l.blocks.get(&uncle.1))
342-
{
343-
// Uncles should never exist in the main chain, so we don't need to worry about
344-
// duplicates
345-
res.push(uncle_block.clone());
346-
}
335+
if main_chain_only {
336+
if let Some(block) = level.block_in_main_chain() {
337+
for uncle in &block.uncles {
338+
// Always include all the uncles, if we have them
339+
if let Some(uncle_block) = p2_chain.get_block_at_height(uncle.0, &uncle.1) {
340+
// Uncles should never exist in the main chain, so we don't need to worry about
341+
// duplicates
342+
res.push(uncle_block.clone());
347343
}
348-
349-
num_actual_blocks += 1;
350-
res.push(block.clone());
351344
}
352-
} else {
353345
num_actual_blocks += 1;
354346
res.push(block.clone());
355347
}
356-
// Always include at least 2 main chain blocks so that if we called
357-
// this function with the starting mainchain block we can continue asking for more
358-
// blocks
359-
if num_actual_blocks > page_size {
360-
return Ok(res);
348+
} else {
349+
for block in level.blocks.values() {
350+
num_actual_blocks += 1;
351+
res.push(block.clone());
361352
}
362353
}
354+
if num_actual_blocks > page_size {
355+
return Ok(res);
356+
}
357+
363358
level = if let Some(new_level) = p2_chain.level_at_height(level.height + 1) {
364359
new_level
365360
} else {
@@ -680,21 +675,17 @@ impl ShareChain for InMemoryShareChain {
680675
) -> Result<(Vec<Arc<P2Block>>, Option<(u64, FixedHash)>, AccumulatedDifficulty), ShareChainError> {
681676
let p2_chain_read = self.p2_chain.read().await;
682677

683-
// Assume their blocks are in order highest first.
684678
let mut split_height = 0;
685679

686680
if let Some(last_block_received) = last_block_received {
687-
if let Some(level) = p2_chain_read.level_at_height(last_block_received.0) {
688-
if let Some(block) = level.blocks.get(&last_block_received.1) {
689-
split_height = block.height.saturating_add(1);
690-
}
681+
if let Some(block) = p2_chain_read.get_block_at_height(last_block_received.0, &last_block_received.1) {
682+
split_height = block.height.saturating_add(1);
691683
}
692684
}
693685

694686
let mut their_blocks = their_blocks.to_vec();
695687
// Highest to lowest
696688
their_blocks.sort_by(|a, b| b.0.cmp(&a.0));
697-
// their_blocks.reverse();
698689

699690
let mut split_height2 = 0;
700691
// Go back and find the split in the chain
@@ -711,7 +702,7 @@ impl ShareChain for InMemoryShareChain {
711702
info!(target: LOG_TARGET, "[{:?}] Requesting sync, split_height1 {} splitheight2 {} last block {}", self.pow_algo, split_height, split_height2, last_block_received.as_ref().map(|(h, _)| h.to_string()).unwrap_or("None".to_string()));
712703

713704
let blocks =
714-
self.all_blocks_with_lock(&p2_chain_read, Some(cmp::max(split_height, split_height2)), limit, true)?;
705+
self.all_blocks_with_lock(&p2_chain_read, Some(cmp::max(split_height, split_height2)), limit, true)?; // potential issue here, maybe this should be min
715706
let tip_level = p2_chain_read
716707
.get_tip()
717708
.map(|tip_level| (tip_level.height, tip_level.chain_block));
@@ -755,33 +746,51 @@ impl ShareChain for InMemoryShareChain {
755746
let mut i_have_blocks = Vec::with_capacity(size);
756747
if let Some(tip) = p2_chain_read_lock.get_tip() {
757748
let tip_height = tip.height;
758-
let tip_hash = tip.chain_block;
759749
let mut height = tip_height;
760-
let mut hash = tip_hash;
761750
for _ in 0..size {
762751
if let Some(level) = p2_chain_read_lock.level_at_height(height) {
763-
let block = if let Some(block) = level.blocks.get(&hash) {
752+
// if sync requestee only sees their behind on tip, they will fill in fixedhash::zero(), so it
753+
// wont find this hash, so we return the current chain block
754+
let block = if let Some(block) = level.block_in_main_chain() {
764755
block.clone()
765756
} else {
766-
// if sync requestee only sees their behind on tip, they will fill in fixedhash::zero(), so it
767-
// wont find this hash, so we return the curent chain block
768-
if let Some(block) = level.block_in_main_chain() {
769-
block.clone()
770-
} else {
771-
break;
772-
}
757+
break;
773758
};
774759
i_have_blocks.push((height, block.hash));
775760
if height == 0 {
776761
break;
777762
}
778-
height = block.height - 1;
779-
hash = block.hash;
763+
height = block.height.saturating_sub(1);
780764
} else {
781765
break;
782766
}
783767
}
768+
if i_have_blocks.len() < size {
769+
// we have less blocks to fill up the request, not worth filling in older blocks
770+
return i_have_blocks;
771+
}
772+
// lets replace some blocks with older ones so that it does not neet to sync the entire chain
773+
let mut counter = 0;
774+
let mut count_back = 3000;
775+
while count_back > 100 {
776+
let height = match tip_height.checked_sub(count_back) {
777+
Some(h) => h,
778+
None => {
779+
count_back -= 250;
780+
continue;
781+
},
782+
};
783+
if let Some(level) = p2_chain_read_lock.level_at_height(height) {
784+
if let Some(block) = level.block_in_main_chain() {
785+
let index = i_have_blocks.len() - counter;
786+
i_have_blocks[index] = (height, block.hash);
787+
counter += 1;
788+
}
789+
}
790+
count_back -= 250;
791+
}
784792
}
793+
785794
i_have_blocks
786795
}
787796
}

src/sharechain/p2chain.rs

Lines changed: 128 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ impl P2Chain {
157157
.get(usize::try_from(index?).expect("32 bit systems not supported"))
158158
}
159159

160-
fn get_block_at_height(&self, height: u64, hash: &FixedHash) -> Option<&Arc<P2Block>> {
160+
pub fn get_block_at_height(&self, height: u64, hash: &FixedHash) -> Option<&Arc<P2Block>> {
161161
let level = self.level_at_height(height)?;
162162
level.blocks.get(hash)
163163
}
@@ -201,11 +201,11 @@ impl P2Chain {
201201
self.levels.len() as u64 >= self.total_size + SAFETY_MARGIN + MAX_EXTRA_SYNC
202202
}
203203

204-
fn cleanup_chain(&mut self) -> Result<(), ShareChainError>{
204+
fn cleanup_chain(&mut self) -> Result<(), ShareChainError> {
205205
let mut first_index = self.levels.back().map(|level| level.height).unwrap_or(0);
206206
let mut current_chain_length = self.current_tip.saturating_sub(first_index);
207207
// let see if we are the limit for the current chain
208-
while current_chain_length > self.total_size + SAFETY_MARGIN{
208+
while current_chain_length > self.total_size + SAFETY_MARGIN {
209209
self.levels.pop_back().ok_or(ShareChainError::BlockLevelNotFound)?;
210210
first_index = self.levels.back().map(|level| level.height).unwrap_or(0);
211211
current_chain_length = self.current_tip.saturating_sub(first_index);
@@ -1368,6 +1368,131 @@ mod test {
13681368
chain.assert_share_window_verified();
13691369
}
13701370

1371+
#[test]
1372+
fn add_blocks_missing_block() {
1373+
// this test will verify that we reorg to a completely new chain
1374+
let mut chain = P2Chain::new_empty(50, 25, 20);
1375+
1376+
let mut prev_block = None;
1377+
let mut tari_block = Block::new(BlockHeader::new(0), AggregateBody::empty());
1378+
let mut blocks = Vec::new();
1379+
for i in 0..50 {
1380+
tari_block.header.nonce = i;
1381+
let address = new_random_address();
1382+
let block = P2BlockBuilder::new(prev_block.as_ref())
1383+
.with_timestamp(EpochTime::now())
1384+
.with_height(i)
1385+
.with_tari_block(tari_block.clone())
1386+
.unwrap()
1387+
.with_miner_wallet_address(address.clone())
1388+
.with_target_difficulty(Difficulty::from_u64(10).unwrap())
1389+
.unwrap()
1390+
.build()
1391+
.unwrap();
1392+
prev_block = Some(block.clone());
1393+
blocks.push(block);
1394+
}
1395+
1396+
for (i, block) in blocks.iter().enumerate().take(50) {
1397+
if i != 25 {
1398+
chain.add_block_to_chain(block.clone()).unwrap();
1399+
}
1400+
}
1401+
assert_eq!(chain.current_tip, 24);
1402+
chain.add_block_to_chain(blocks[25].clone()).unwrap();
1403+
1404+
assert_eq!(chain.current_tip, 49);
1405+
assert_eq!(chain.get_tip().unwrap().chain_block, prev_block.unwrap().hash);
1406+
1407+
chain.assert_share_window_verified();
1408+
}
1409+
1410+
#[test]
1411+
fn reorg_with_missing_uncle() {
1412+
// this test will verify that we reorg to a completely new chain
1413+
let mut chain = P2Chain::new_empty(50, 25, 20);
1414+
1415+
let mut prev_block = None;
1416+
let mut tari_block = Block::new(BlockHeader::new(0), AggregateBody::empty());
1417+
for i in 0..50 {
1418+
tari_block.header.nonce = i;
1419+
let address = new_random_address();
1420+
let block = P2BlockBuilder::new(prev_block.as_ref())
1421+
.with_timestamp(EpochTime::now())
1422+
.with_height(i)
1423+
.with_tari_block(tari_block.clone())
1424+
.unwrap()
1425+
.with_miner_wallet_address(address.clone())
1426+
.with_target_difficulty(Difficulty::from_u64(10).unwrap())
1427+
.unwrap()
1428+
.build()
1429+
.unwrap();
1430+
prev_block = Some(block.clone());
1431+
chain.add_block_to_chain(block).unwrap();
1432+
}
1433+
1434+
assert_eq!(chain.current_tip, 49);
1435+
assert_eq!(chain.get_tip().unwrap().chain_block, prev_block.unwrap().hash);
1436+
1437+
let mut prev_block = None;
1438+
let mut tari_block = Block::new(BlockHeader::new(0), AggregateBody::empty());
1439+
let mut uncle_parent = None;
1440+
let mut uncle_block = None;
1441+
for i in 0..50 {
1442+
tari_block.header.nonce = i + 100;
1443+
let address = new_random_address();
1444+
let uncles = if i == 25 {
1445+
let uncle = P2BlockBuilder::new(uncle_parent.as_ref())
1446+
.with_timestamp(EpochTime::now())
1447+
.with_height(24)
1448+
.with_tari_block(tari_block.clone())
1449+
.unwrap()
1450+
.with_miner_wallet_address(address.clone())
1451+
.build()
1452+
.unwrap();
1453+
uncle_block = Some(uncle.clone());
1454+
vec![uncle]
1455+
} else {
1456+
vec![]
1457+
};
1458+
let block = P2BlockBuilder::new(prev_block.as_ref())
1459+
.with_timestamp(EpochTime::now())
1460+
.with_height(i)
1461+
.with_tari_block(tari_block.clone())
1462+
.unwrap()
1463+
.with_miner_wallet_address(address.clone())
1464+
.with_uncles(&uncles)
1465+
.unwrap()
1466+
.with_target_difficulty(Difficulty::from_u64(11).unwrap())
1467+
.unwrap()
1468+
.build()
1469+
.unwrap();
1470+
if i == 23 {
1471+
uncle_parent = Some(block.clone());
1472+
}
1473+
prev_block = Some(block.clone());
1474+
chain.add_block_to_chain(block).unwrap();
1475+
}
1476+
1477+
assert_eq!(chain.current_tip, 49);
1478+
let hash = prev_block.unwrap().hash;
1479+
assert_ne!(chain.get_tip().unwrap().chain_block, hash);
1480+
chain.add_block_to_chain(uncle_block.unwrap()).unwrap();
1481+
assert_eq!(chain.get_tip().unwrap().chain_block, hash);
1482+
assert_eq!(
1483+
chain
1484+
.get_tip()
1485+
.unwrap()
1486+
.block_in_main_chain()
1487+
.unwrap()
1488+
.original_header
1489+
.nonce,
1490+
149
1491+
);
1492+
1493+
chain.assert_share_window_verified();
1494+
}
1495+
13711496
#[test]
13721497
fn add_blocks_to_chain_super_large_reorg_only_window() {
13731498
// this test will verify that we reorg to a completely new chain

0 commit comments

Comments
 (0)