Skip to content

Commit b3cc277

Browse files
remagpieforiequal0
authored andcommitted
Rename total_score to nonce in sync message
When CodeChain is synchronized by snapshot sync, `total_score` doesn't mean the accumulated score from the genesis block. To handle this, the sync extension is updated to not rely on `total_score` for deciding whether a peer is leading or not, and use the value only as a monotonically increasing nonce.
1 parent ef1a176 commit b3cc277

File tree

6 files changed

+68
-98
lines changed

6 files changed

+68
-98
lines changed

core/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,4 @@ pub use crate::service::ClientService;
9999
pub use crate::transaction::{
100100
LocalizedTransaction, PendingSignedTransactions, SignedTransaction, UnverifiedTransaction,
101101
};
102-
pub use crate::types::{BlockId, TransactionId};
102+
pub use crate::types::{BlockId, BlockStatus, TransactionId};

spec/Block-Synchronization-Extension.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@ Message :=
1919
### Status
2020

2121
```
22-
Status(total_score, best_hash, genesis_hash)
22+
Status(nonce, best_hash, genesis_hash)
2323
```
2424

2525
Send current chain status to peer.
2626

2727
* Identifier: 0x01
28-
* Restriction: None
28+
* Restriction:
29+
* `nonce` SHOULD be monotonically increasing every time the message is sent.
2930

3031
## Request messages
3132

sync/src/block/downloader/header.rs

Lines changed: 28 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -31,65 +31,50 @@ const MAX_HEADER_QUEUE_LENGTH: usize = 1024;
3131
const MAX_RETRY: usize = 3;
3232
const MAX_WAIT: u64 = 15;
3333

34-
#[derive(Clone)]
35-
struct Pivot {
36-
hash: BlockHash,
37-
total_score: U256,
38-
}
39-
4034
#[derive(Clone)]
4135
pub struct HeaderDownloader {
4236
// NOTE: Use this member as minimum as possible.
4337
client: Arc<dyn BlockChainClient>,
4438

45-
total_score: U256,
39+
nonce: U256,
4640
best_hash: BlockHash,
47-
48-
pivot: Pivot,
41+
pivot: BlockHash,
4942
request_time: Option<Instant>,
5043
downloaded: HashMap<BlockHash, Header>,
5144
queued: HashMap<BlockHash, Header>,
5245
trial: usize,
5346
}
5447

5548
impl HeaderDownloader {
56-
pub fn total_score(&self) -> U256 {
57-
self.total_score
58-
}
59-
60-
pub fn new(client: Arc<dyn BlockChainClient>, total_score: U256, best_hash: BlockHash) -> Self {
49+
pub fn new(client: Arc<dyn BlockChainClient>, nonce: U256, best_hash: BlockHash) -> Self {
6150
let best_header_hash = client.best_block_header().hash();
62-
let best_score = client.block_total_score(&BlockId::Latest).expect("Best block always exist");
6351

6452
Self {
6553
client,
6654

67-
total_score,
55+
nonce,
6856
best_hash,
69-
70-
pivot: Pivot {
71-
hash: best_header_hash,
72-
total_score: best_score,
73-
},
57+
pivot: best_header_hash,
7458
request_time: None,
7559
downloaded: HashMap::new(),
7660
queued: HashMap::new(),
7761
trial: 0,
7862
}
7963
}
8064

81-
pub fn update(&mut self, total_score: U256, best_hash: BlockHash) -> bool {
82-
match self.total_score.cmp(&total_score) {
65+
pub fn best_hash(&self) -> BlockHash {
66+
self.best_hash
67+
}
68+
69+
pub fn update(&mut self, nonce: U256, best_hash: BlockHash) -> bool {
70+
match self.nonce.cmp(&nonce) {
8371
Ordering::Equal => true,
8472
Ordering::Less => {
85-
self.total_score = total_score;
73+
self.nonce = nonce;
8674
self.best_hash = best_hash;
8775

8876
if self.client.block_header(&BlockId::Hash(best_hash)).is_some() {
89-
self.pivot = Pivot {
90-
hash: best_hash,
91-
total_score,
92-
}
77+
self.pivot = best_hash;
9378
}
9479
true
9580
}
@@ -108,25 +93,25 @@ impl HeaderDownloader {
10893
/// Find header from queued headers, downloaded cache and then from blockchain
10994
/// Panics if header dosn't exist
11095
fn pivot_header(&self) -> Header {
111-
match self.queued.get(&self.pivot.hash) {
96+
match self.queued.get(&self.pivot) {
11297
Some(header) => header.clone(),
113-
None => match self.downloaded.get(&self.pivot.hash) {
98+
None => match self.downloaded.get(&self.pivot) {
11499
Some(header) => header.clone(),
115-
None => self.client.block_header(&BlockId::Hash(self.pivot.hash)).unwrap(),
100+
None => self.client.block_header(&BlockId::Hash(self.pivot)).unwrap(),
116101
},
117102
}
118103
}
119104

120-
pub fn pivot_score(&self) -> U256 {
121-
self.pivot.total_score
122-
}
123-
124105
pub fn is_idle(&self) -> bool {
125-
let can_request = self.request_time.is_none() && self.total_score > self.pivot.total_score;
106+
let can_request = self.request_time.is_none() && self.best_hash != self.pivot;
126107

127108
self.is_valid() && (can_request || self.is_expired())
128109
}
129110

111+
pub fn is_caught_up(&self) -> bool {
112+
self.pivot == self.best_hash
113+
}
114+
130115
pub fn create_request(&mut self) -> Option<RequestMessage> {
131116
if !self.is_idle() {
132117
return None
@@ -154,37 +139,30 @@ impl HeaderDownloader {
154139
let pivot_header = self.pivot_header();
155140

156141
// This happens when best_hash is imported by other peer.
157-
if self.best_hash == self.pivot.hash {
142+
if self.best_hash == self.pivot {
158143
ctrace!(SYNC, "Ignore received headers, pivot already reached the best hash");
159-
} else if first_header_hash == self.pivot.hash {
144+
} else if first_header_hash == self.pivot {
160145
for header in headers.iter() {
161146
self.downloaded.insert(header.hash(), header.clone());
162147
}
163148

164149
// FIXME: skip known headers
165-
let new_scores = headers[1..].iter().fold(U256::zero(), |acc, header| acc + header.score());
166-
self.pivot = Pivot {
167-
hash: headers.last().expect("Last downloaded header must exist").hash(),
168-
total_score: self.pivot.total_score + new_scores,
169-
}
150+
self.pivot = headers.last().expect("Last downloaded header must exist").hash();
170151
} else if first_header_number < pivot_header.number() {
171152
ctrace!(
172153
SYNC,
173154
"Ignore received headers, pivot is already updated since headers are imported by other peers"
174155
);
175156
} else if first_header_number == pivot_header.number() {
176157
if pivot_header.number() != 0 {
177-
self.pivot = Pivot {
178-
hash: pivot_header.parent_hash(),
179-
total_score: self.pivot.total_score - pivot_header.score(),
180-
}
158+
self.pivot = pivot_header.parent_hash();
181159
}
182160
} else {
183161
cerror!(
184162
SYNC,
185-
"Invalid header update state. best_hash: {}, self.pivot.hash: {}, first_header_hash: {}",
163+
"Invalid header update state. best_hash: {}, self.pivot: {}, first_header_hash: {}",
186164
self.best_hash,
187-
self.pivot.hash,
165+
self.pivot,
188166
first_header_hash
189167
);
190168
}
@@ -203,10 +181,7 @@ impl HeaderDownloader {
203181
self.downloaded.remove(&hash);
204182

205183
if self.best_hash == hash {
206-
self.pivot = Pivot {
207-
hash,
208-
total_score: self.total_score,
209-
}
184+
self.pivot = hash;
210185
}
211186
}
212187
self.queued.shrink_to_fit();

sync/src/block/extension.rs

Lines changed: 28 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ use std::time::Duration;
2121

2222
use ccore::encoded::Header as EncodedHeader;
2323
use ccore::{
24-
Block, BlockChainClient, BlockChainTrait, BlockId, BlockImportError, ChainNotify, Client, ImportBlock, ImportError,
25-
UnverifiedTransaction,
24+
Block, BlockChainClient, BlockChainTrait, BlockId, BlockImportError, BlockStatus, ChainNotify, Client, ImportBlock,
25+
ImportError, UnverifiedTransaction,
2626
};
2727
use cmerkle::TrieFactory;
2828
use cnetwork::{Api, EventSender, NetworkExtension, NodeId};
@@ -74,6 +74,7 @@ pub struct Extension {
7474
client: Arc<Client>,
7575
api: Box<dyn Api>,
7676
last_request: u64,
77+
nonce: u64,
7778
}
7879

7980
impl Extension {
@@ -125,6 +126,7 @@ impl Extension {
125126
client,
126127
api,
127128
last_request: Default::default(),
129+
nonce: Default::default(),
128130
}
129131
}
130132

@@ -140,13 +142,14 @@ impl Extension {
140142
id,
141143
Arc::new(
142144
Message::Status {
143-
total_score: chain_info.best_proposal_score,
145+
nonce: U256::from(self.nonce),
144146
best_hash: chain_info.best_proposal_block_hash,
145147
genesis_hash: chain_info.genesis_hash,
146148
}
147149
.rlp_bytes(),
148150
),
149151
);
152+
self.nonce += 1;
150153
}
151154

152155
fn send_status_broadcast(&mut self) {
@@ -156,13 +159,14 @@ impl Extension {
156159
id,
157160
Arc::new(
158161
Message::Status {
159-
total_score: chain_info.best_proposal_score,
162+
nonce: U256::from(self.nonce),
160163
best_hash: chain_info.best_proposal_block_hash,
161164
genesis_hash: chain_info.genesis_hash,
162165
}
163166
.rlp_bytes(),
164167
),
165168
);
169+
self.nonce += 1;
166170
}
167171
}
168172

@@ -177,6 +181,14 @@ impl Extension {
177181
}
178182

179183
fn send_body_request(&mut self, id: &NodeId) {
184+
if let Some(downloader) = self.header_downloaders.get(&id) {
185+
if self.client.block_status(&BlockId::Hash(downloader.best_hash())) == BlockStatus::InChain {
186+
// Peer is lagging behind the local blockchain.
187+
// We don't need to request block bodies to this peer
188+
return
189+
}
190+
}
191+
180192
self.check_sync_variable();
181193
if let Some(requests) = self.requests.get_mut(id) {
182194
let have_body_request = {
@@ -319,10 +331,10 @@ impl NetworkExtension<Event> for Extension {
319331
if let Ok(received_message) = Rlp::new(data).as_val() {
320332
match received_message {
321333
Message::Status {
322-
total_score,
334+
nonce,
323335
best_hash,
324336
genesis_hash,
325-
} => self.on_peer_status(id, total_score, best_hash, genesis_hash),
337+
} => self.on_peer_status(id, nonce, best_hash, genesis_hash),
326338
Message::Request(request_id, request) => self.on_peer_request(id, request_id, request),
327339
Message::Response(request_id, response) => self.on_peer_response(id, request_id, response),
328340
}
@@ -348,7 +360,6 @@ impl NetworkExtension<Event> for Extension {
348360
}
349361
State::SnapshotChunk(..) => unimplemented!(),
350362
State::Full => {
351-
let best_proposal_score = self.client.chain_info().best_proposal_score;
352363
for id in &peer_ids {
353364
let request =
354365
self.header_downloaders.get_mut(id).and_then(HeaderDownloader::create_request);
@@ -359,15 +370,7 @@ impl NetworkExtension<Event> for Extension {
359370
}
360371

361372
for id in peer_ids {
362-
let peer_score = if let Some(peer) = self.header_downloaders.get(&id) {
363-
peer.total_score()
364-
} else {
365-
U256::zero()
366-
};
367-
368-
if peer_score > best_proposal_score {
369-
self.send_body_request(&id);
370-
}
373+
self.send_body_request(&id);
371374
}
372375
}
373376
}
@@ -516,7 +519,7 @@ impl Extension {
516519
}
517520

518521
impl Extension {
519-
fn on_peer_status(&mut self, from: &NodeId, total_score: U256, best_hash: BlockHash, genesis_hash: BlockHash) {
522+
fn on_peer_status(&mut self, from: &NodeId, nonce: U256, best_hash: BlockHash, genesis_hash: BlockHash) {
520523
// Validity check
521524
if genesis_hash != self.client.chain_info().genesis_hash {
522525
cinfo!(SYNC, "Genesis hash mismatch with peer {}", from);
@@ -525,17 +528,17 @@ impl Extension {
525528

526529
match self.header_downloaders.entry(*from) {
527530
Entry::Occupied(mut peer) => {
528-
if !peer.get_mut().update(total_score, best_hash) {
531+
if !peer.get_mut().update(nonce, best_hash) {
529532
// FIXME: It should be an error level if the consensus is PoW.
530533
cdebug!(SYNC, "Peer #{} status updated but score is less than before", from);
531534
return
532535
}
533536
}
534537
Entry::Vacant(e) => {
535-
e.insert(HeaderDownloader::new(self.client.clone(), total_score, best_hash));
538+
e.insert(HeaderDownloader::new(self.client.clone(), nonce, best_hash));
536539
}
537540
}
538-
cinfo!(SYNC, "Peer #{} status update: total_score: {}, best_hash: {}", from, total_score, best_hash);
541+
cinfo!(SYNC, "Peer #{} status update: nonce: {}, best_hash: {}", from, nonce, best_hash);
539542
}
540543

541544
fn on_peer_request(&self, from: &NodeId, id: u64, request: RequestMessage) {
@@ -750,14 +753,12 @@ impl Extension {
750753
},
751754
State::SnapshotChunk(..) => {}
752755
State::Full => {
753-
let (mut completed, pivot_score_changed) = if let Some(peer) = self.header_downloaders.get_mut(from) {
754-
let before_pivot_score = peer.pivot_score();
756+
let (mut completed, peer_is_caught_up) = if let Some(peer) = self.header_downloaders.get_mut(from) {
755757
let encoded: Vec<_> = headers.iter().map(|h| EncodedHeader::new(h.rlp_bytes().to_vec())).collect();
756758
peer.import_headers(&encoded);
757-
let after_pivot_score = peer.pivot_score();
758-
(peer.downloaded(), before_pivot_score != after_pivot_score)
759+
(peer.downloaded(), peer.is_caught_up())
759760
} else {
760-
(Vec::new(), false)
761+
(Vec::new(), true)
761762
};
762763
completed.sort_unstable_by_key(EncodedHeader::number);
763764

@@ -783,7 +784,7 @@ impl Extension {
783784
peer.mark_as_imported(exists);
784785
peer.create_request()
785786
});
786-
if pivot_score_changed {
787+
if !peer_is_caught_up {
787788
if let Some(request) = request {
788789
self.send_header_request(from, request);
789790
}
@@ -825,20 +826,11 @@ impl Extension {
825826
}
826827
}
827828

828-
let total_score = self.client.chain_info().best_proposal_score;
829829
let mut peer_ids: Vec<_> = self.header_downloaders.keys().cloned().collect();
830830
peer_ids.shuffle(&mut thread_rng());
831831

832832
for id in peer_ids {
833-
let peer_score = if let Some(peer) = self.header_downloaders.get(&id) {
834-
peer.total_score()
835-
} else {
836-
U256::zero()
837-
};
838-
839-
if peer_score > total_score {
840-
self.send_body_request(&id);
841-
}
833+
self.send_body_request(&id);
842834
}
843835
}
844836
}

0 commit comments

Comments
 (0)