Skip to content
This repository was archived by the owner on Nov 6, 2020. It is now read-only.

Commit 1a5b176

Browse files
rphmeierarkpar
authored andcommitted
Backports for stable (#6116)
* remove chunk to restore from pending set only upon successful import * blacklist bad manifest hashes upon failure * more checks before snapshot syncing * Reverted tests * revert submodule change
1 parent f480587 commit 1a5b176

File tree

3 files changed

+111
-7
lines changed

3 files changed

+111
-7
lines changed

ethcore/src/snapshot/service.rs

+54-2
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ struct Guard(bool, PathBuf);
4646
impl Guard {
4747
fn new(path: PathBuf) -> Self { Guard(true, path) }
4848

49+
#[cfg(test)]
50+
fn benign() -> Self { Guard(false, PathBuf::default()) }
51+
4952
fn disarm(mut self) { self.0 = false }
5053
}
5154

@@ -120,28 +123,32 @@ impl Restoration {
120123

121124
// feeds a state chunk, aborts early if `flag` becomes false.
122125
fn feed_state(&mut self, hash: H256, chunk: &[u8], flag: &AtomicBool) -> Result<(), Error> {
123-
if self.state_chunks_left.remove(&hash) {
126+
if self.state_chunks_left.contains(&hash) {
124127
let len = snappy::decompress_into(chunk, &mut self.snappy_buffer)?;
125128

126129
self.state.feed(&self.snappy_buffer[..len], flag)?;
127130

128131
if let Some(ref mut writer) = self.writer.as_mut() {
129132
writer.write_state_chunk(hash, chunk)?;
130133
}
134+
135+
self.state_chunks_left.remove(&hash);
131136
}
132137

133138
Ok(())
134139
}
135140

136141
// feeds a block chunk
137142
fn feed_blocks(&mut self, hash: H256, chunk: &[u8], engine: &Engine, flag: &AtomicBool) -> Result<(), Error> {
138-
if self.block_chunks_left.remove(&hash) {
143+
if self.block_chunks_left.contains(&hash) {
139144
let len = snappy::decompress_into(chunk, &mut self.snappy_buffer)?;
140145

141146
self.blocks.feed(&self.snappy_buffer[..len], engine, flag)?;
142147
if let Some(ref mut writer) = self.writer.as_mut() {
143148
writer.write_block_chunk(hash, chunk)?;
144149
}
150+
151+
self.block_chunks_left.remove(&hash);
145152
}
146153

147154
Ok(())
@@ -669,4 +676,49 @@ mod tests {
669676
service.restore_state_chunk(Default::default(), vec![]);
670677
service.restore_block_chunk(Default::default(), vec![]);
671678
}
679+
680+
#[test]
681+
fn cannot_finish_with_invalid_chunks() {
682+
use util::{H256, FixedHash};
683+
use util::kvdb::DatabaseConfig;
684+
685+
let spec = get_test_spec();
686+
let dir = RandomTempPath::new();
687+
688+
let state_hashes: Vec<_> = (0..5).map(|_| H256::random()).collect();
689+
let block_hashes: Vec<_> = (0..5).map(|_| H256::random()).collect();
690+
let db_config = DatabaseConfig::with_columns(::db::NUM_COLUMNS);
691+
let gb = spec.genesis_block();
692+
let flag = ::std::sync::atomic::AtomicBool::new(true);
693+
694+
let params = RestorationParams {
695+
manifest: ManifestData {
696+
version: 2,
697+
state_hashes: state_hashes.clone(),
698+
block_hashes: block_hashes.clone(),
699+
state_root: H256::default(),
700+
block_number: 100000,
701+
block_hash: H256::default(),
702+
},
703+
pruning: Algorithm::Archive,
704+
db_path: dir.as_path().to_owned(),
705+
db_config: &db_config,
706+
writer: None,
707+
genesis: &gb,
708+
guard: Guard::benign(),
709+
};
710+
711+
let mut restoration = Restoration::new(params).unwrap();
712+
let definitely_bad_chunk = [1, 2, 3, 4, 5];
713+
714+
for hash in state_hashes {
715+
assert!(restoration.feed_state(hash, &definitely_bad_chunk, &flag).is_err());
716+
assert!(!restoration.is_done());
717+
}
718+
719+
for hash in block_hashes {
720+
assert!(restoration.feed_blocks(hash, &definitely_bad_chunk, &*spec.engine, &flag).is_err());
721+
assert!(!restoration.is_done());
722+
}
723+
}
672724
}

sync/src/chain.rs

+35-5
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ pub const SNAPSHOT_SYNC_PACKET_COUNT: u8 = 0x16;
159159
const MAX_SNAPSHOT_CHUNKS_DOWNLOAD_AHEAD: usize = 3;
160160

161161
const MIN_SUPPORTED_SNAPSHOT_MANIFEST_VERSION: u64 = 1;
162+
const MAX_SUPPORTED_SNAPSHOT_MANIFEST_VERSION: u64 = 2;
162163

163164
const WAIT_PEERS_TIMEOUT_SEC: u64 = 5;
164165
const STATUS_TIMEOUT_SEC: u64 = 5;
@@ -523,7 +524,8 @@ impl ChainSync {
523524
sn > fork_block &&
524525
self.highest_block.map_or(true, |highest| highest >= sn && (highest - sn) <= SNAPSHOT_RESTORE_THRESHOLD)
525526
))
526-
.filter_map(|(p, peer)| peer.snapshot_hash.map(|hash| (p, hash.clone())));
527+
.filter_map(|(p, peer)| peer.snapshot_hash.map(|hash| (p, hash.clone())))
528+
.filter(|&(_, ref hash)| !self.snapshot.is_known_bad(hash));
527529

528530
let mut snapshot_peers = HashMap::new();
529531
let mut max_peers: usize = 0;
@@ -1020,6 +1022,7 @@ impl ChainSync {
10201022
trace!(target: "sync", "Ignoring snapshot manifest from unconfirmed peer {}", peer_id);
10211023
return Ok(());
10221024
}
1025+
10231026
self.clear_peer_download(peer_id);
10241027
if !self.reset_peer_asking(peer_id, PeerAsking::SnapshotManifest) || self.state != SyncState::SnapshotManifest {
10251028
trace!(target: "sync", "{}: Ignored unexpected/expired manifest", peer_id);
@@ -1037,13 +1040,32 @@ impl ChainSync {
10371040
}
10381041
Ok(manifest) => manifest,
10391042
};
1040-
if manifest.version < MIN_SUPPORTED_SNAPSHOT_MANIFEST_VERSION {
1041-
trace!(target: "sync", "{}: Snapshot manifest version too low: {}", peer_id, manifest.version);
1043+
1044+
let manifest_hash = manifest_rlp.as_raw().sha3();
1045+
let is_usable_version = manifest.version >= MIN_SUPPORTED_SNAPSHOT_MANIFEST_VERSION
1046+
&& manifest.version <= MAX_SUPPORTED_SNAPSHOT_MANIFEST_VERSION;
1047+
1048+
if !self.peers.get(&peer_id).map_or(false, |peer| peer.snapshot_hash == Some(manifest_hash)) {
1049+
trace!(target: "sync", "{}: Snapshot manifest hash {} mismatched with advertised", peer_id, manifest_hash);
10421050
io.disable_peer(peer_id);
10431051
self.continue_sync(io);
1052+
10441053
return Ok(());
10451054
}
1046-
self.snapshot.reset_to(&manifest, &manifest_rlp.as_raw().sha3());
1055+
1056+
if !is_usable_version {
1057+
trace!(target: "sync", "{}: Snapshot manifest version incompatible: {}", peer_id, manifest.version);
1058+
self.snapshot.note_bad(manifest_hash);
1059+
1060+
// temporarily disable the peer while we tune our peer set to those
1061+
// with usable snapshots. we don't try and download any rejected manifest
1062+
// again, so when we reconnect we can still full sync.
1063+
io.disable_peer(peer_id);;
1064+
self.continue_sync(io);
1065+
return Ok(());
1066+
}
1067+
1068+
self.snapshot.reset_to(&manifest, &manifest_hash);
10471069
io.snapshot_service().begin_restore(manifest);
10481070
self.state = SyncState::SnapshotData;
10491071

@@ -1068,10 +1090,18 @@ impl ChainSync {
10681090
}
10691091

10701092
// check service status
1071-
match io.snapshot_service().status() {
1093+
let status = io.snapshot_service().status();
1094+
match status {
10721095
RestorationStatus::Inactive | RestorationStatus::Failed => {
10731096
trace!(target: "sync", "{}: Snapshot restoration aborted", peer_id);
10741097
self.state = SyncState::WaitingPeers;
1098+
1099+
// only note bad if restoration failed.
1100+
if let (Some(hash), RestorationStatus::Failed) = (self.snapshot.snapshot_hash(), status) {
1101+
trace!(target: "sync", "Noting snapshot hash {} as bad", hash);
1102+
self.snapshot.note_bad(hash);
1103+
}
1104+
10751105
self.snapshot.clear();
10761106
self.continue_sync(io);
10771107
return Ok(());

sync/src/snapshot.rs

+22
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ pub struct Snapshot {
3131
downloading_chunks: HashSet<H256>,
3232
completed_chunks: HashSet<H256>,
3333
snapshot_hash: Option<H256>,
34+
bad_hashes: HashSet<H256>,
3435
}
3536

3637
impl Snapshot {
@@ -42,6 +43,7 @@ impl Snapshot {
4243
downloading_chunks: HashSet::new(),
4344
completed_chunks: HashSet::new(),
4445
snapshot_hash: None,
46+
bad_hashes: HashSet::new(),
4547
}
4648
}
4749

@@ -104,6 +106,16 @@ impl Snapshot {
104106
self.downloading_chunks.remove(hash);
105107
}
106108

109+
// note snapshot hash as bad.
110+
pub fn note_bad(&mut self, hash: H256) {
111+
self.bad_hashes.insert(hash);
112+
}
113+
114+
// whether snapshot hash is known to be bad.
115+
pub fn is_known_bad(&self, hash: &H256) -> bool {
116+
self.bad_hashes.contains(hash)
117+
}
118+
107119
pub fn snapshot_hash(&self) -> Option<H256> {
108120
self.snapshot_hash
109121
}
@@ -200,5 +212,15 @@ mod test {
200212
assert_eq!(snapshot.done_chunks(), snapshot.total_chunks());
201213
assert_eq!(snapshot.snapshot_hash(), Some(manifest.into_rlp().sha3()));
202214
}
215+
216+
#[test]
217+
fn tracks_known_bad() {
218+
let mut snapshot = Snapshot::new();
219+
let hash = H256::random();
220+
221+
assert_eq!(snapshot.is_known_bad(&hash), false);
222+
snapshot.note_bad(hash);
223+
assert_eq!(snapshot.is_known_bad(&hash), true);
224+
}
203225
}
204226

0 commit comments

Comments
 (0)