2020-09-22 14:53:52 +02:00
|
|
|
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
|
|
|
// This file is part of OpenEthereum.
|
2017-05-17 12:41:33 +02:00
|
|
|
|
2020-09-22 14:53:52 +02:00
|
|
|
// OpenEthereum is free software: you can redistribute it and/or modify
|
2017-05-17 12:41:33 +02:00
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
|
2020-09-22 14:53:52 +02:00
|
|
|
// OpenEthereum is distributed in the hope that it will be useful,
|
2017-05-17 12:41:33 +02:00
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
|
|
|
|
// You should have received a copy of the GNU General Public License
|
2020-09-22 14:53:52 +02:00
|
|
|
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
2017-05-17 12:41:33 +02:00
|
|
|
|
|
|
|
//! Secondary chunk creation and restoration, implementation for proof-of-work
|
|
|
|
//! chains.
|
|
|
|
//!
|
|
|
|
//! The secondary chunks in this instance are 30,000 "abridged blocks" from the head
|
|
|
|
//! of the chain, which serve as an indication of valid chain.
|
|
|
|
|
|
|
|
use super::{ChunkSink, Rebuilder, SnapshotComponents};
|
|
|
|
|
|
|
|
use std::{
|
|
|
|
collections::VecDeque,
|
|
|
|
sync::{
|
|
|
|
atomic::{AtomicBool, Ordering},
|
|
|
|
Arc,
|
2020-08-05 06:08:03 +02:00
|
|
|
},
|
2017-05-17 12:41:33 +02:00
|
|
|
};
|
|
|
|
|
2018-06-20 15:13:07 +02:00
|
|
|
use blockchain::{BlockChain, BlockChainDB, BlockProvider};
|
2017-09-06 20:47:45 +02:00
|
|
|
use bytes::Bytes;
|
2021-03-03 22:44:35 +01:00
|
|
|
use db::KeyValueDB;
|
2017-09-26 14:19:08 +02:00
|
|
|
use engines::EthEngine;
|
2018-01-10 13:35:18 +01:00
|
|
|
use ethereum_types::H256;
|
2021-03-12 10:12:42 +01:00
|
|
|
use rand::rngs::OsRng;
|
2018-04-16 15:52:12 +02:00
|
|
|
use rlp::{Rlp, RlpStream};
|
2018-09-13 12:58:49 +02:00
|
|
|
use snapshot::{block::AbridgedBlock, Error, ManifestData, Progress};
|
2021-06-04 12:12:24 +02:00
|
|
|
use types::{encoded, BlockNumber};
|
2017-05-17 12:41:33 +02:00
|
|
|
|
|
|
|
/// Snapshot creation and restoration for PoW chains.
|
|
|
|
/// This includes blocks from the head of the chain as a
|
|
|
|
/// loose assurance that the chain is valid.
|
|
|
|
#[derive(Clone, Copy, PartialEq)]
|
2017-08-22 11:24:56 +02:00
|
|
|
pub struct PowSnapshot {
|
|
|
|
/// Number of blocks from the head of the chain
|
|
|
|
/// to include in the snapshot.
|
|
|
|
pub blocks: u64,
|
|
|
|
/// Number of to allow in the snapshot when restoring.
|
|
|
|
pub max_restore_blocks: u64,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PowSnapshot {
|
|
|
|
/// Create a new instance.
|
|
|
|
pub fn new(blocks: u64, max_restore_blocks: u64) -> PowSnapshot {
|
|
|
|
PowSnapshot {
|
|
|
|
blocks: blocks,
|
|
|
|
max_restore_blocks: max_restore_blocks,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-05-17 12:41:33 +02:00
|
|
|
|
|
|
|
impl SnapshotComponents for PowSnapshot {
|
|
|
|
fn chunk_all(
|
|
|
|
&mut self,
|
|
|
|
chain: &BlockChain,
|
|
|
|
block_at: H256,
|
|
|
|
chunk_sink: &mut ChunkSink,
|
2018-09-13 12:58:49 +02:00
|
|
|
progress: &Progress,
|
2017-05-17 12:41:33 +02:00
|
|
|
preferred_size: usize,
|
2021-06-04 12:12:24 +02:00
|
|
|
eip1559_transition: BlockNumber,
|
2017-05-17 12:41:33 +02:00
|
|
|
) -> Result<(), Error> {
|
|
|
|
PowWorker {
|
|
|
|
chain: chain,
|
|
|
|
rlps: VecDeque::new(),
|
|
|
|
current_hash: block_at,
|
|
|
|
writer: chunk_sink,
|
2018-09-13 12:58:49 +02:00
|
|
|
progress: progress,
|
2017-05-17 12:41:33 +02:00
|
|
|
preferred_size: preferred_size,
|
2017-08-22 11:24:56 +02:00
|
|
|
}
|
2021-06-04 12:12:24 +02:00
|
|
|
.chunk_all(self.blocks, eip1559_transition)
|
2017-05-17 12:41:33 +02:00
|
|
|
}
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
fn rebuilder(
|
|
|
|
&self,
|
|
|
|
chain: BlockChain,
|
2020-07-29 10:36:15 +02:00
|
|
|
db: Arc<dyn BlockChainDB>,
|
2017-05-17 12:41:33 +02:00
|
|
|
manifest: &ManifestData,
|
2020-07-29 10:36:15 +02:00
|
|
|
) -> Result<Box<dyn Rebuilder>, ::error::Error> {
|
2018-06-20 15:13:07 +02:00
|
|
|
PowRebuilder::new(
|
|
|
|
chain,
|
|
|
|
db.key_value().clone(),
|
|
|
|
manifest,
|
|
|
|
self.max_restore_blocks,
|
2020-08-05 06:08:03 +02:00
|
|
|
)
|
2018-06-20 15:13:07 +02:00
|
|
|
.map(|r| Box::new(r) as Box<_>)
|
2017-05-17 12:41:33 +02:00
|
|
|
}
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
fn min_supported_version(&self) -> u64 {
|
|
|
|
::snapshot::MIN_SUPPORTED_STATE_CHUNK_VERSION
|
|
|
|
}
|
|
|
|
fn current_version(&self) -> u64 {
|
|
|
|
::snapshot::STATE_CHUNK_VERSION
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Used to build block chunks.
|
|
|
|
struct PowWorker<'a> {
|
|
|
|
chain: &'a BlockChain,
|
|
|
|
// block, receipt rlp pairs.
|
|
|
|
rlps: VecDeque<Bytes>,
|
|
|
|
current_hash: H256,
|
|
|
|
writer: &'a mut ChunkSink<'a>,
|
2018-09-13 12:58:49 +02:00
|
|
|
progress: &'a Progress,
|
2017-05-17 12:41:33 +02:00
|
|
|
preferred_size: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> PowWorker<'a> {
|
|
|
|
// Repeatedly fill the buffers and writes out chunks, moving backwards from starting block hash.
|
|
|
|
// Loops until we reach the first desired block, and writes out the remainder.
|
2021-06-04 12:12:24 +02:00
|
|
|
fn chunk_all(
|
|
|
|
&mut self,
|
|
|
|
snapshot_blocks: u64,
|
|
|
|
eip1559_transition: BlockNumber,
|
|
|
|
) -> Result<(), Error> {
|
2017-05-17 12:41:33 +02:00
|
|
|
let mut loaded_size = 0;
|
|
|
|
let mut last = self.current_hash;
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
let genesis_hash = self.chain.genesis_hash();
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
for _ in 0..snapshot_blocks {
|
|
|
|
if self.current_hash == genesis_hash {
|
|
|
|
break;
|
|
|
|
}
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
let (block, receipts) = self
|
|
|
|
.chain
|
|
|
|
.block(&self.current_hash)
|
|
|
|
.and_then(|b| {
|
|
|
|
self.chain
|
|
|
|
.block_receipts(&self.current_hash)
|
|
|
|
.map(|r| (b, r))
|
2020-08-05 06:08:03 +02:00
|
|
|
})
|
2019-03-06 15:30:35 +01:00
|
|
|
.ok_or_else(|| Error::BlockNotFound(self.current_hash))?;
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2021-06-04 12:12:24 +02:00
|
|
|
let abridged_rlp =
|
|
|
|
AbridgedBlock::from_block_view(&block.view(), eip1559_transition).into_inner();
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
let pair = {
|
|
|
|
let mut pair_stream = RlpStream::new_list(2);
|
|
|
|
pair_stream.append_raw(&abridged_rlp, 1).append(&receipts);
|
|
|
|
pair_stream.out()
|
|
|
|
};
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
let new_loaded_size = loaded_size + pair.len();
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
// cut off the chunk if too large.
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
if new_loaded_size > self.preferred_size && !self.rlps.is_empty() {
|
|
|
|
self.write_chunk(last)?;
|
|
|
|
loaded_size = pair.len();
|
|
|
|
} else {
|
|
|
|
loaded_size = new_loaded_size;
|
|
|
|
}
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
self.rlps.push_front(pair);
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
last = self.current_hash;
|
|
|
|
self.current_hash = block.header_view().parent_hash();
|
2018-09-13 12:58:49 +02:00
|
|
|
self.progress.blocks.fetch_add(1, Ordering::SeqCst);
|
2017-05-17 12:41:33 +02:00
|
|
|
}
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
if loaded_size != 0 {
|
|
|
|
self.write_chunk(last)?;
|
|
|
|
}
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
Ok(())
|
|
|
|
}
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
// write out the data in the buffers to a chunk on disk
|
|
|
|
//
|
|
|
|
// we preface each chunk with the parent of the first block's details,
|
|
|
|
// obtained from the details of the last block written.
|
|
|
|
fn write_chunk(&mut self, last: H256) -> Result<(), Error> {
|
|
|
|
trace!(target: "snapshot", "prepared block chunk with {} blocks", self.rlps.len());
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2018-04-03 10:01:28 +02:00
|
|
|
let (last_header, last_details) = self
|
|
|
|
.chain
|
|
|
|
.block_header_data(&last)
|
2017-05-17 12:41:33 +02:00
|
|
|
.and_then(|n| self.chain.block_details(&last).map(|d| (n, d)))
|
2019-03-06 15:30:35 +01:00
|
|
|
.ok_or_else(|| Error::BlockNotFound(last))?;
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
let parent_number = last_header.number() - 1;
|
|
|
|
let parent_hash = last_header.parent_hash();
|
2018-04-03 10:01:28 +02:00
|
|
|
let parent_total_difficulty = last_details.total_difficulty - last_header.difficulty();
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
trace!(target: "snapshot", "parent last written block: {}", parent_hash);
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
let num_entries = self.rlps.len();
|
|
|
|
let mut rlp_stream = RlpStream::new_list(3 + num_entries);
|
2018-04-03 10:01:28 +02:00
|
|
|
rlp_stream
|
|
|
|
.append(&parent_number)
|
|
|
|
.append(&parent_hash)
|
|
|
|
.append(&parent_total_difficulty);
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
for pair in self.rlps.drain(..) {
|
|
|
|
rlp_stream.append_raw(&pair, 1);
|
|
|
|
}
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
let raw_data = rlp_stream.out();
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
(self.writer)(&raw_data)?;
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Rebuilder for proof-of-work chains.
|
|
|
|
/// Does basic verification for all blocks, but `PoW` verification for some.
|
|
|
|
/// Blocks must be fed in-order.
|
|
|
|
///
|
|
|
|
/// The first block in every chunk is disconnected from the last block in the
|
|
|
|
/// chunk before it, as chunks may be submitted out-of-order.
|
|
|
|
///
|
|
|
|
/// After all chunks have been submitted, we "glue" the chunks together.
|
|
|
|
pub struct PowRebuilder {
|
|
|
|
chain: BlockChain,
|
2020-07-29 10:36:15 +02:00
|
|
|
db: Arc<dyn KeyValueDB>,
|
2017-05-17 12:41:33 +02:00
|
|
|
rng: OsRng,
|
|
|
|
disconnected: Vec<(u64, H256)>,
|
|
|
|
best_number: u64,
|
|
|
|
best_hash: H256,
|
|
|
|
best_root: H256,
|
|
|
|
fed_blocks: u64,
|
|
|
|
snapshot_blocks: u64,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PowRebuilder {
|
|
|
|
/// Create a new PowRebuilder.
|
|
|
|
fn new(
|
|
|
|
chain: BlockChain,
|
2020-07-29 10:36:15 +02:00
|
|
|
db: Arc<dyn KeyValueDB>,
|
2017-05-17 12:41:33 +02:00
|
|
|
manifest: &ManifestData,
|
|
|
|
snapshot_blocks: u64,
|
|
|
|
) -> Result<Self, ::error::Error> {
|
|
|
|
Ok(PowRebuilder {
|
|
|
|
chain: chain,
|
|
|
|
db: db,
|
2021-03-12 10:12:42 +01:00
|
|
|
rng: OsRng,
|
2017-05-17 12:41:33 +02:00
|
|
|
disconnected: Vec::new(),
|
|
|
|
best_number: manifest.block_number,
|
|
|
|
best_hash: manifest.block_hash,
|
|
|
|
best_root: manifest.state_root,
|
|
|
|
fed_blocks: 0,
|
|
|
|
snapshot_blocks: snapshot_blocks,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Rebuilder for PowRebuilder {
|
|
|
|
/// Feed the rebuilder an uncompressed block chunk.
|
|
|
|
/// Returns the number of blocks fed or any errors.
|
2017-09-26 14:19:08 +02:00
|
|
|
fn feed(
|
|
|
|
&mut self,
|
|
|
|
chunk: &[u8],
|
2020-07-29 10:36:15 +02:00
|
|
|
engine: &dyn EthEngine,
|
2017-09-26 14:19:08 +02:00
|
|
|
abort_flag: &AtomicBool,
|
|
|
|
) -> Result<(), ::error::Error> {
|
2018-01-10 13:35:18 +01:00
|
|
|
use ethereum_types::U256;
|
2017-05-17 12:41:33 +02:00
|
|
|
use snapshot::verify_old_block;
|
2017-09-03 09:11:14 +02:00
|
|
|
use triehash::ordered_trie_root;
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2018-04-16 15:52:12 +02:00
|
|
|
let rlp = Rlp::new(chunk);
|
2017-05-17 12:41:33 +02:00
|
|
|
let item_count = rlp.item_count()?;
|
|
|
|
let num_blocks = (item_count - 3) as u64;
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2018-05-12 22:46:08 +02:00
|
|
|
trace!(target: "snapshot", "restoring block chunk with {} blocks.", num_blocks);
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
if self.fed_blocks + num_blocks > self.snapshot_blocks {
|
2017-08-22 11:24:56 +02:00
|
|
|
return Err(
|
|
|
|
Error::TooManyBlocks(self.snapshot_blocks, self.fed_blocks + num_blocks).into(),
|
2020-08-05 06:08:03 +02:00
|
|
|
);
|
2017-05-17 12:41:33 +02:00
|
|
|
}
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
// todo: assert here that these values are consistent with chunks being in order.
|
|
|
|
let mut cur_number = rlp.val_at::<u64>(0)? + 1;
|
|
|
|
let mut parent_hash = rlp.val_at::<H256>(1)?;
|
|
|
|
let parent_total_difficulty = rlp.val_at::<U256>(2)?;
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
for idx in 3..item_count {
|
|
|
|
if !abort_flag.load(Ordering::SeqCst) {
|
|
|
|
return Err(Error::RestorationAborted.into());
|
|
|
|
}
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
let pair = rlp.at(idx)?;
|
|
|
|
let abridged_rlp = pair.at(0)?.as_raw().to_owned();
|
|
|
|
let abridged_block = AbridgedBlock::from_raw(abridged_rlp);
|
2020-12-10 16:42:05 +01:00
|
|
|
let receipts = ::types::receipt::TypedReceipt::decode_rlp_list(&pair.at(1)?)?;
|
|
|
|
let receipts_root = ordered_trie_root(pair.at(1)?.iter().map(|r| {
|
|
|
|
if r.is_list() {
|
|
|
|
r.as_raw()
|
|
|
|
} else {
|
|
|
|
// We have allready checked validity by decoding rlp list in line above
|
|
|
|
r.data().expect("Expect for raw receipts list to be valid.")
|
|
|
|
}
|
|
|
|
}));
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2021-06-04 12:12:24 +02:00
|
|
|
let block = abridged_block.to_block(
|
|
|
|
parent_hash,
|
|
|
|
cur_number,
|
|
|
|
receipts_root,
|
|
|
|
engine.params().eip1559_transition,
|
|
|
|
)?;
|
2018-07-30 11:45:10 +02:00
|
|
|
let block_bytes = encoded::Block::new(block.rlp_bytes());
|
2017-05-17 12:41:33 +02:00
|
|
|
let is_best = cur_number == self.best_number;
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
if is_best {
|
|
|
|
if block.header.hash() != self.best_hash {
|
|
|
|
return Err(Error::WrongBlockHash(
|
|
|
|
cur_number,
|
|
|
|
self.best_hash,
|
|
|
|
block.header.hash(),
|
|
|
|
)
|
|
|
|
.into());
|
|
|
|
}
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
if block.header.state_root() != &self.best_root {
|
|
|
|
return Err(
|
|
|
|
Error::WrongStateRoot(self.best_root, *block.header.state_root()).into(),
|
2020-08-05 06:08:03 +02:00
|
|
|
);
|
2017-05-17 12:41:33 +02:00
|
|
|
}
|
2020-08-05 06:08:03 +02:00
|
|
|
}
|
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
verify_old_block(&mut self.rng, &block.header, engine, &self.chain, is_best)?;
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
let mut batch = self.db.transaction();
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
// special-case the first block in each chunk.
|
|
|
|
if idx == 3 {
|
2018-07-30 11:45:10 +02:00
|
|
|
if self.chain.insert_unordered_block(
|
|
|
|
&mut batch,
|
|
|
|
block_bytes,
|
|
|
|
receipts,
|
|
|
|
Some(parent_total_difficulty),
|
|
|
|
is_best,
|
|
|
|
false,
|
|
|
|
) {
|
2017-05-17 12:41:33 +02:00
|
|
|
self.disconnected.push((cur_number, block.header.hash()));
|
|
|
|
}
|
|
|
|
} else {
|
2018-07-30 11:45:10 +02:00
|
|
|
self.chain.insert_unordered_block(
|
|
|
|
&mut batch,
|
|
|
|
block_bytes,
|
|
|
|
receipts,
|
|
|
|
None,
|
|
|
|
is_best,
|
|
|
|
false,
|
|
|
|
);
|
2017-05-17 12:41:33 +02:00
|
|
|
}
|
|
|
|
self.db.write_buffered(batch);
|
|
|
|
self.chain.commit();
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2018-07-30 11:45:10 +02:00
|
|
|
parent_hash = block.header.hash();
|
2017-05-17 12:41:33 +02:00
|
|
|
cur_number += 1;
|
|
|
|
}
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
self.fed_blocks += num_blocks;
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
Ok(())
|
|
|
|
}
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
/// Glue together any disconnected chunks and check that the chain is complete.
|
2020-07-29 10:36:15 +02:00
|
|
|
fn finalize(&mut self, _: &dyn EthEngine) -> Result<(), ::error::Error> {
|
2017-05-17 12:41:33 +02:00
|
|
|
let mut batch = self.db.transaction();
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
for (first_num, first_hash) in self.disconnected.drain(..) {
|
|
|
|
let parent_num = first_num - 1;
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
// check if the parent is even in the chain.
|
|
|
|
// since we don't restore every single block in the chain,
|
|
|
|
// the first block of the first chunks has nothing to connect to.
|
|
|
|
if let Some(parent_hash) = self.chain.block_hash(parent_num) {
|
|
|
|
// if so, add the child to it.
|
|
|
|
self.chain.add_child(&mut batch, parent_hash, first_hash);
|
|
|
|
}
|
|
|
|
}
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
let genesis_hash = self.chain.genesis_hash();
|
2017-06-28 13:17:36 +02:00
|
|
|
self.chain.insert_epoch_transition(
|
|
|
|
&mut batch,
|
|
|
|
0,
|
|
|
|
::engines::EpochTransition {
|
2017-05-17 12:41:33 +02:00
|
|
|
block_number: 0,
|
|
|
|
block_hash: genesis_hash,
|
|
|
|
proof: vec![],
|
|
|
|
},
|
|
|
|
);
|
2020-08-05 06:08:03 +02:00
|
|
|
|
2017-05-17 12:41:33 +02:00
|
|
|
self.db.write_buffered(batch);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|