Skip to content

Commit f03b60c

Browse files
committed
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 ea56c0e commit f03b60c

File tree

6 files changed

+58
-93
lines changed

6 files changed

+58
-93
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 & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use std::sync::Arc;
2020
use std::time::Instant;
2121

2222
use ccore::encoded::Header;
23-
use ccore::{BlockChainClient, BlockId};
23+
use ccore::{BlockChainClient, BlockId, BlockStatus};
2424
use ctypes::BlockHash;
2525
use primitives::U256;
2626

@@ -31,65 +31,46 @@ 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 update(&mut self, nonce: U256, best_hash: BlockHash) -> bool {
66+
match self.nonce.cmp(&nonce) {
8367
Ordering::Equal => true,
8468
Ordering::Less => {
85-
self.total_score = total_score;
69+
self.nonce = nonce;
8670
self.best_hash = best_hash;
8771

8872
if self.client.block_header(&BlockId::Hash(best_hash)).is_some() {
89-
self.pivot = Pivot {
90-
hash: best_hash,
91-
total_score,
92-
}
73+
self.pivot = best_hash;
9374
}
9475
true
9576
}
@@ -108,25 +89,28 @@ impl HeaderDownloader {
10889
/// Find header from queued headers, downloaded cache and then from blockchain
10990
/// Panics if header dosn't exist
11091
fn pivot_header(&self) -> Header {
111-
match self.queued.get(&self.pivot.hash) {
92+
match self.queued.get(&self.pivot) {
11293
Some(header) => header.clone(),
113-
None => match self.downloaded.get(&self.pivot.hash) {
94+
None => match self.downloaded.get(&self.pivot) {
11495
Some(header) => header.clone(),
115-
None => self.client.block_header(&BlockId::Hash(self.pivot.hash)).unwrap(),
96+
None => self.client.block_header(&BlockId::Hash(self.pivot)).unwrap(),
11697
},
11798
}
11899
}
119100

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

127104
self.is_valid() && (can_request || self.is_expired())
128105
}
129106

107+
pub fn is_lagging(&self) -> bool {
108+
let peer_status = self.client.block_status(&BlockId::Hash(self.best_hash));
109+
let best_hash = self.client.block_hash(&BlockId::Latest).expect("Best hash always exists");
110+
111+
peer_status == BlockStatus::InChain && self.best_hash != best_hash
112+
}
113+
130114
pub fn create_request(&mut self) -> Option<RequestMessage> {
131115
if !self.is_idle() {
132116
return None
@@ -154,37 +138,30 @@ impl HeaderDownloader {
154138
let pivot_header = self.pivot_header();
155139

156140
// This happens when best_hash is imported by other peer.
157-
if self.best_hash == self.pivot.hash {
141+
if self.best_hash == self.pivot {
158142
ctrace!(SYNC, "Ignore received headers, pivot already reached the best hash");
159-
} else if first_header_hash == self.pivot.hash {
143+
} else if first_header_hash == self.pivot {
160144
for header in headers.iter() {
161145
self.downloaded.insert(header.hash(), header.clone());
162146
}
163147

164148
// 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-
}
149+
self.pivot = headers.last().expect("Last downloaded header must exist").hash();
170150
} else if first_header_number < pivot_header.number() {
171151
ctrace!(
172152
SYNC,
173153
"Ignore received headers, pivot is already updated since headers are imported by other peers"
174154
);
175155
} else if first_header_number == pivot_header.number() {
176156
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-
}
157+
self.pivot = pivot_header.parent_hash();
181158
}
182159
} else {
183160
cerror!(
184161
SYNC,
185-
"Invalid header update state. best_hash: {}, self.pivot.hash: {}, first_header_hash: {}",
162+
"Invalid header update state. best_hash: {}, self.pivot: {}, first_header_hash: {}",
186163
self.best_hash,
187-
self.pivot.hash,
164+
self.pivot,
188165
first_header_hash
189166
);
190167
}
@@ -203,10 +180,7 @@ impl HeaderDownloader {
203180
self.downloaded.remove(&hash);
204181

205182
if self.best_hash == hash {
206-
self.pivot = Pivot {
207-
hash,
208-
total_score: self.total_score,
209-
}
183+
self.pivot = hash;
210184
}
211185
}
212186
self.queued.shrink_to_fit();

sync/src/block/extension.rs

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ pub struct Extension {
7676
client: Arc<Client>,
7777
api: Box<dyn Api>,
7878
last_request: u64,
79+
nonce: u64,
7980
}
8081

8182
impl Extension {
@@ -126,6 +127,7 @@ impl Extension {
126127
client,
127128
api,
128129
last_request: Default::default(),
130+
nonce: Default::default(),
129131
}
130132
}
131133

@@ -243,14 +245,15 @@ impl NetworkExtension<Event> for Extension {
243245
id,
244246
Arc::new(
245247
Message::Status {
246-
total_score: chain_info.best_proposal_score,
248+
nonce: U256::from(self.nonce),
247249
best_hash: chain_info.best_proposal_block_hash,
248250
genesis_hash: chain_info.genesis_hash,
249251
}
250252
.rlp_bytes()
251253
.into_vec(),
252254
),
253255
);
256+
self.nonce += 1;
254257
let t = self.connected_nodes.insert(*id);
255258
debug_assert!(t, "{} is already added to peer list", id);
256259

@@ -299,10 +302,10 @@ impl NetworkExtension<Event> for Extension {
299302
if let Ok(received_message) = UntrustedRlp::new(data).as_val() {
300303
match received_message {
301304
Message::Status {
302-
total_score,
305+
nonce,
303306
best_hash,
304307
genesis_hash,
305-
} => self.on_peer_status(id, total_score, best_hash, genesis_hash),
308+
} => self.on_peer_status(id, nonce, best_hash, genesis_hash),
306309
Message::Request(request_id, request) => self.on_peer_request(id, request_id, request),
307310
Message::Response(request_id, response) => self.on_peer_response(id, request_id, response),
308311
}
@@ -328,7 +331,6 @@ impl NetworkExtension<Event> for Extension {
328331
}
329332
State::SnapshotChunk(..) => unimplemented!(),
330333
State::Full => {
331-
let best_proposal_score = self.client.chain_info().best_proposal_score;
332334
for id in &peer_ids {
333335
let request =
334336
self.header_downloaders.get_mut(id).and_then(HeaderDownloader::create_request);
@@ -339,13 +341,7 @@ impl NetworkExtension<Event> for Extension {
339341
}
340342

341343
for id in peer_ids {
342-
let peer_score = if let Some(peer) = self.header_downloaders.get(&id) {
343-
peer.total_score()
344-
} else {
345-
U256::zero()
346-
};
347-
348-
if peer_score > best_proposal_score {
344+
if self.header_downloaders.get(&id).map_or(false, |d| !d.is_lagging()) {
349345
self.send_body_request(&id);
350346
}
351347
}
@@ -501,20 +497,21 @@ impl Extension {
501497
id,
502498
Arc::new(
503499
Message::Status {
504-
total_score: chain_info.best_proposal_score,
500+
nonce: U256::from(self.nonce),
505501
best_hash: chain_info.best_proposal_block_hash,
506502
genesis_hash: chain_info.genesis_hash,
507503
}
508504
.rlp_bytes()
509505
.into_vec(),
510506
),
511507
);
508+
self.nonce += 1;
512509
}
513510
}
514511
}
515512

516513
impl Extension {
517-
fn on_peer_status(&mut self, from: &NodeId, total_score: U256, best_hash: BlockHash, genesis_hash: BlockHash) {
514+
fn on_peer_status(&mut self, from: &NodeId, nonce: U256, best_hash: BlockHash, genesis_hash: BlockHash) {
518515
// Validity check
519516
if genesis_hash != self.client.chain_info().genesis_hash {
520517
cinfo!(SYNC, "Genesis hash mismatch with peer {}", from);
@@ -523,17 +520,17 @@ impl Extension {
523520

524521
match self.header_downloaders.entry(*from) {
525522
Entry::Occupied(mut peer) => {
526-
if !peer.get_mut().update(total_score, best_hash) {
523+
if !peer.get_mut().update(nonce, best_hash) {
527524
// FIXME: It should be an error level if the consensus is PoW.
528525
cdebug!(SYNC, "Peer #{} status updated but score is less than before", from);
529526
return
530527
}
531528
}
532529
Entry::Vacant(e) => {
533-
e.insert(HeaderDownloader::new(self.client.clone(), total_score, best_hash));
530+
e.insert(HeaderDownloader::new(self.client.clone(), nonce, best_hash));
534531
}
535532
}
536-
cinfo!(SYNC, "Peer #{} status update: total_score: {}, best_hash: {}", from, total_score, best_hash);
533+
cinfo!(SYNC, "Peer #{} status update: nonce: {}, best_hash: {}", from, nonce, best_hash);
537534
}
538535

539536
fn on_peer_request(&self, from: &NodeId, id: u64, request: RequestMessage) {
@@ -759,14 +756,12 @@ impl Extension {
759756
}
760757
State::SnapshotChunk(..) => {}
761758
State::Full => {
762-
let (mut completed, pivot_score_changed) = if let Some(peer) = self.header_downloaders.get_mut(from) {
763-
let before_pivot_score = peer.pivot_score();
759+
let (mut completed, is_lagging) = if let Some(peer) = self.header_downloaders.get_mut(from) {
764760
let encoded: Vec<_> = headers.iter().map(|h| EncodedHeader::new(h.rlp_bytes().to_vec())).collect();
765761
peer.import_headers(&encoded);
766-
let after_pivot_score = peer.pivot_score();
767-
(peer.downloaded(), before_pivot_score != after_pivot_score)
762+
(peer.downloaded(), peer.is_lagging())
768763
} else {
769-
(Vec::new(), false)
764+
(Vec::new(), true)
770765
};
771766
completed.sort_unstable_by_key(EncodedHeader::number);
772767

@@ -792,7 +787,7 @@ impl Extension {
792787
peer.mark_as_imported(exists);
793788
peer.create_request()
794789
});
795-
if pivot_score_changed {
790+
if !is_lagging {
796791
if let Some(request) = request {
797792
self.send_header_request(from, request);
798793
}
@@ -834,18 +829,11 @@ impl Extension {
834829
}
835830
}
836831

837-
let total_score = self.client.chain_info().best_proposal_score;
838832
let mut peer_ids: Vec<_> = self.header_downloaders.keys().cloned().collect();
839833
peer_ids.shuffle(&mut thread_rng());
840834

841835
for id in peer_ids {
842-
let peer_score = if let Some(peer) = self.header_downloaders.get(&id) {
843-
peer.total_score()
844-
} else {
845-
U256::zero()
846-
};
847-
848-
if peer_score > total_score {
836+
if self.header_downloaders.get(&id).map_or(false, |d| !d.is_lagging()) {
849837
self.send_body_request(&id);
850838
}
851839
}

0 commit comments

Comments
 (0)