-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Snapshot creation and restoration #1679
Changes from all commits
317a3bf
575de78
aec9c9e
0ebd69a
a0f9759
02d052d
e8f7c4f
9f96512
a8da992
7b3b494
51fc3d3
b77d8e2
910b77f
039decc
2c68467
dbc15c3
f3ad832
b588feb
4ecf7b9
fea41ad
5ea7324
13852b9
257b8d3
1573acd
edf75dc
d7a1f6e
de0236b
9ca9d0d
0bebec3
5c67073
4187c54
0ec4da7
a119416
d756943
8cf8ea9
df0f566
3468cfa
207e38a
f6ca5ff
1e573fb
456c122
504a5ac
b04c065
3e8dbb4
b58806d
f155fa4
4bdbeb5
200a2af
2878a39
944f13a
80dab3c
04ccbdf
53ef687
eeb143f
2784d83
fd566ff
22ed490
64275fa
472c9c6
3b8c4da
200a063
c880ab6
80bf077
b8d6986
1339ed6
8967db7
0037aeb
d1d1727
70bcf86
3520fa7
49d52e1
72d8352
6368917
82a58c0
01835c5
ae45db8
758107e
917d1f3
64ea9d1
90aa435
19612ec
d36d5a8
d48e262
2802734
30ea380
96ef106
ad02931
57d4014
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -533,6 +533,116 @@ impl BlockChain { | |
} | ||
} | ||
|
||
/// Inserts a verified, known block from the canonical chain. | ||
/// | ||
/// Can be performed out-of-order, but care must be taken that the final chain is in a correct state. | ||
/// This is used by snapshot restoration. | ||
/// | ||
/// Supply a dummy parent total difficulty when the parent block may not be in the chain. | ||
/// Returns true if the block is disconnected. | ||
pub fn insert_snapshot_block(&self, bytes: &[u8], receipts: Vec<Receipt>, parent_td: Option<U256>, is_best: bool) -> bool { | ||
let block = BlockView::new(bytes); | ||
let header = block.header_view(); | ||
let hash = header.sha3(); | ||
|
||
if self.is_known(&hash) { | ||
return false; | ||
} | ||
|
||
assert!(self.pending_best_block.read().is_none()); | ||
|
||
let batch = self.db.transaction(); | ||
|
||
let block_rlp = UntrustedRlp::new(bytes); | ||
let compressed_header = block_rlp.at(0).unwrap().compress(RlpType::Blocks); | ||
let compressed_body = UntrustedRlp::new(&Self::block_to_body(bytes)).compress(RlpType::Blocks); | ||
|
||
// store block in db | ||
batch.put(DB_COL_HEADERS, &hash, &compressed_header).unwrap(); | ||
batch.put(DB_COL_BODIES, &hash, &compressed_body).unwrap(); | ||
|
||
let maybe_parent = self.block_details(&header.parent_hash()); | ||
|
||
if let Some(parent_details) = maybe_parent { | ||
// parent known to be in chain. | ||
let info = BlockInfo { | ||
hash: hash, | ||
number: header.number(), | ||
total_difficulty: parent_details.total_difficulty + header.difficulty(), | ||
location: BlockLocation::CanonChain, | ||
}; | ||
|
||
self.prepare_update(&batch, ExtrasUpdate { | ||
block_hashes: self.prepare_block_hashes_update(bytes, &info), | ||
block_details: self.prepare_block_details_update(bytes, &info), | ||
block_receipts: self.prepare_block_receipts_update(receipts, &info), | ||
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info), | ||
blocks_blooms: self.prepare_block_blooms_update(bytes, &info), | ||
info: info, | ||
block: bytes | ||
}, is_best); | ||
self.db.write(batch).unwrap(); | ||
|
||
false | ||
} else { | ||
// parent not in the chain yet. we need the parent difficulty to proceed. | ||
let d = parent_td | ||
.expect("parent total difficulty always supplied for first block in chunk. only first block can have missing parent; qed"); | ||
|
||
let info = BlockInfo { | ||
hash: hash, | ||
number: header.number(), | ||
total_difficulty: d + header.difficulty(), | ||
location: BlockLocation::CanonChain, | ||
}; | ||
|
||
let block_details = BlockDetails { | ||
number: header.number(), | ||
total_difficulty: info.total_difficulty, | ||
parent: header.parent_hash(), | ||
children: Vec::new(), | ||
}; | ||
|
||
let mut update = HashMap::new(); | ||
update.insert(hash, block_details); | ||
|
||
self.prepare_update(&batch, ExtrasUpdate { | ||
block_hashes: self.prepare_block_hashes_update(bytes, &info), | ||
block_details: update, | ||
block_receipts: self.prepare_block_receipts_update(receipts, &info), | ||
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info), | ||
blocks_blooms: self.prepare_block_blooms_update(bytes, &info), | ||
info: info, | ||
block: bytes, | ||
}, is_best); | ||
self.db.write(batch).unwrap(); | ||
|
||
true | ||
} | ||
} | ||
|
||
/// Add a child to a given block. Assumes that the block hash is in | ||
/// the chain and the child's parent is this block. | ||
/// | ||
/// Used in snapshots to glue the chunks together at the end. | ||
pub fn add_child(&self, block_hash: H256, child_hash: H256) { | ||
let mut parent_details = self.block_details(&block_hash) | ||
.unwrap_or_else(|| panic!("Invalid block hash: {:?}", block_hash)); | ||
|
||
let batch = self.db.transaction(); | ||
parent_details.children.push(child_hash); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a guarantee that the same child won't be pushed twice? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this function is called at the very end to glue all disconnected chunks together. see in the absence of a bad snapshot i think this can be guaranteed to not push the same child twice. |
||
|
||
let mut update = HashMap::new(); | ||
update.insert(block_hash, parent_details); | ||
|
||
self.note_used(CacheID::BlockDetails(block_hash)); | ||
|
||
let mut write_details = self.block_details.write(); | ||
batch.extend_with_cache(DB_COL_EXTRA, &mut *write_details, update, CacheUpdatePolicy::Overwrite); | ||
|
||
self.db.write(batch).unwrap(); | ||
} | ||
|
||
#[cfg_attr(feature="dev", allow(similar_names))] | ||
/// Inserts the block into backing cache database. | ||
/// Expects the block to be valid and already verified. | ||
|
@@ -572,7 +682,7 @@ impl BlockChain { | |
blocks_blooms: self.prepare_block_blooms_update(bytes, &info), | ||
info: info.clone(), | ||
block: bytes, | ||
}); | ||
}, true); | ||
|
||
ImportRoute::from(info) | ||
} | ||
|
@@ -618,7 +728,7 @@ impl BlockChain { | |
} | ||
|
||
/// Prepares extras update. | ||
fn prepare_update(&self, batch: &DBTransaction, update: ExtrasUpdate) { | ||
fn prepare_update(&self, batch: &DBTransaction, update: ExtrasUpdate, is_best: bool) { | ||
{ | ||
for hash in update.block_details.keys().cloned() { | ||
self.note_used(CacheID::BlockDetails(hash)); | ||
|
@@ -645,17 +755,16 @@ impl BlockChain { | |
// update best block | ||
match update.info.location { | ||
BlockLocation::Branch => (), | ||
_ => { | ||
_ => if is_best { | ||
batch.put(DB_COL_EXTRA, b"best", &update.info.hash).unwrap(); | ||
*best_block = Some(BestBlock { | ||
hash: update.info.hash, | ||
number: update.info.number, | ||
total_difficulty: update.info.total_difficulty, | ||
block: update.block.to_vec(), | ||
}); | ||
} | ||
}, | ||
} | ||
|
||
let mut write_hashes = self.pending_block_hashes.write(); | ||
let mut write_txs = self.pending_transaction_addresses.write(); | ||
|
||
|
@@ -745,6 +854,7 @@ impl BlockChain { | |
} | ||
|
||
/// This function returns modified block details. | ||
/// Uses the given parent details or attempts to load them from the database. | ||
fn prepare_block_details_update(&self, block_bytes: &[u8], info: &BlockInfo) -> HashMap<H256, BlockDetails> { | ||
let block = BlockView::new(block_bytes); | ||
let header = block.header_view(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is
parent_diff
parent's difficulty or total difficulty? If it is just difficulty then this is incorrectThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is the total difficulty.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe rename to
parent_total_diff
to make clear?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i added documentation in my last commit which specifies it.