Skip to content

Commit 49c0b29

Browse files
committed
Make the snapshot header importable without parent
1 parent 4346043 commit 49c0b29

File tree

9 files changed

+102
-10
lines changed

9 files changed

+102
-10
lines changed

core/src/blockchain/blockchain.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,18 @@ impl BlockChain {
9898
}
9999
}
100100

101+
pub fn insert_bootstrap_header(&self, batch: &mut DBTransaction, header: &HeaderView) {
102+
self.headerchain.insert_bootstrap_header(batch, header);
103+
104+
let hash = header.hash();
105+
106+
*self.pending_best_block_hash.write() = Some(hash);
107+
batch.put(db::COL_EXTRA, BEST_BLOCK_KEY, &hash);
108+
109+
*self.pending_best_proposal_block_hash.write() = Some(hash);
110+
batch.put(db::COL_EXTRA, BEST_PROPOSAL_BLOCK_KEY, &hash);
111+
}
112+
101113
pub fn insert_header(
102114
&self,
103115
batch: &mut DBTransaction,

core/src/blockchain/headerchain.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,48 @@ impl HeaderChain {
115115
}
116116
}
117117

118+
/// Inserts a bootstrap header into backing cache database.
119+
/// Makes the imported header the best header.
120+
/// Expects the header to be valid and already verified.
121+
/// If the header is already known, does nothing.
122+
// FIXME: Find better return type. Returning `None` at duplication is not natural
123+
pub fn insert_bootstrap_header(&self, batch: &mut DBTransaction, header: &HeaderView) {
124+
let hash = header.hash();
125+
126+
ctrace!(HEADERCHAIN, "Inserting bootstrap block header #{}({}) to the headerchain.", header.number(), hash);
127+
128+
if self.is_known_header(&hash) {
129+
ctrace!(HEADERCHAIN, "Block header #{}({}) is already known.", header.number(), hash);
130+
return
131+
}
132+
133+
assert!(self.pending_best_header_hash.read().is_none());
134+
assert!(self.pending_best_proposal_block_hash.read().is_none());
135+
136+
let compressed_header = compress(header.rlp().as_raw(), blocks_swapper());
137+
batch.put(db::COL_HEADERS, &hash, &compressed_header);
138+
139+
let mut new_hashes = HashMap::new();
140+
new_hashes.insert(header.number(), hash);
141+
let mut new_details = HashMap::new();
142+
new_details.insert(hash, BlockDetails {
143+
number: header.number(),
144+
total_score: 0.into(),
145+
parent: header.parent_hash(),
146+
});
147+
148+
batch.put(db::COL_EXTRA, BEST_HEADER_KEY, &hash);
149+
*self.pending_best_header_hash.write() = Some(hash);
150+
batch.put(db::COL_EXTRA, BEST_PROPOSAL_HEADER_KEY, &hash);
151+
*self.pending_best_proposal_block_hash.write() = Some(hash);
152+
153+
let mut pending_hashes = self.pending_hashes.write();
154+
let mut pending_details = self.pending_details.write();
155+
156+
batch.extend_with_cache(db::COL_EXTRA, &mut *pending_details, new_details, CacheUpdatePolicy::Overwrite);
157+
batch.extend_with_cache(db::COL_EXTRA, &mut *pending_hashes, new_hashes, CacheUpdatePolicy::Overwrite);
158+
}
159+
118160
/// Inserts the header into backing cache database.
119161
/// Expects the header to be valid and already verified.
120162
/// If the header is already known, does nothing.

core/src/client/client.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use cstate::{
2828
};
2929
use ctimer::{TimeoutHandler, TimerApi, TimerScheduleError, TimerToken};
3030
use ctypes::transaction::{AssetTransferInput, PartialHashing, ShardTransaction};
31-
use ctypes::{BlockHash, BlockNumber, CommonParams, ShardId, Tracker, TxHash};
31+
use ctypes::{BlockHash, BlockNumber, CommonParams, Header, ShardId, Tracker, TxHash};
3232
use cvm::{decode, execute, ChainTimeInfo, ScriptResult, VMConfig};
3333
use kvdb::{DBTransaction, KeyValueDB};
3434
use parking_lot::{Mutex, RwLock, RwLockReadGuard};
@@ -634,6 +634,15 @@ impl ImportBlock for Client {
634634
Ok(self.importer.header_queue.import(unverified)?)
635635
}
636636

637+
fn import_bootstrap_header(&self, header: &Header) -> Result<BlockHash, BlockImportError> {
638+
if self.block_chain().is_known_header(&header.hash()) {
639+
return Err(BlockImportError::Import(ImportError::AlreadyInChain))
640+
}
641+
let import_lock = self.importer.import_lock.lock();
642+
self.importer.import_bootstrap_header(header, self, &import_lock);
643+
Ok(header.hash())
644+
}
645+
637646
fn import_sealed_block(&self, block: &SealedBlock) -> ImportResult {
638647
let h = block.header().hash();
639648
let route = {

core/src/client/importer.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ impl Importer {
100100
}
101101

102102
{
103-
let headers: Vec<&Header> = blocks.iter().map(|block| &block.header).collect();
103+
let headers: Vec<_> = blocks.iter().map(|block| &block.header).collect();
104104
self.import_headers(headers, client, &import_lock);
105105
}
106106

@@ -362,6 +362,23 @@ impl Importer {
362362
imported.len()
363363
}
364364

365+
pub fn import_bootstrap_header<'a>(&'a self, header: &'a Header, client: &Client, _importer_lock: &MutexGuard<()>) {
366+
let hash = header.hash();
367+
ctrace!(CLIENT, "Importing bootstrap header {}-{:?}", header.number(), hash);
368+
369+
{
370+
let chain = client.block_chain();
371+
let mut batch = DBTransaction::new();
372+
chain.insert_bootstrap_header(&mut batch, &HeaderView::new(&header.rlp_bytes()));
373+
client.db().write_buffered(batch);
374+
chain.commit();
375+
}
376+
377+
client.new_headers(&[hash], &[], &[hash], &[], &[], Some(hash));
378+
379+
client.db().flush().expect("DB flush failed.");
380+
}
381+
365382
fn check_header(&self, header: &Header, parent: &Header) -> bool {
366383
// FIXME: self.verifier.verify_block_family
367384
if let Err(e) = self.engine.verify_block_family(&header, &parent) {

core/src/client/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use cmerkle::Result as TrieResult;
3838
use cnetwork::NodeId;
3939
use cstate::{AssetScheme, FindActionHandler, OwnedAsset, StateResult, Text, TopLevelState, TopStateView};
4040
use ctypes::transaction::{AssetTransferInput, PartialHashing, ShardTransaction};
41-
use ctypes::{BlockHash, BlockNumber, CommonParams, ShardId, Tracker, TxHash};
41+
use ctypes::{BlockHash, BlockNumber, CommonParams, Header, ShardId, Tracker, TxHash};
4242
use cvm::ChainTimeInfo;
4343
use kvdb::KeyValueDB;
4444
use primitives::{Bytes, H160, H256, U256};
@@ -196,6 +196,10 @@ pub trait ImportBlock {
196196
/// Import a header into the blockchain
197197
fn import_header(&self, bytes: Bytes) -> Result<BlockHash, BlockImportError>;
198198

199+
/// Import a trusted bootstrap header into the blockchain
200+
/// Bootstrap headers don't execute any verifications
201+
fn import_bootstrap_header(&self, bytes: &Header) -> Result<BlockHash, BlockImportError>;
202+
199203
/// Import sealed block. Skips all verifications.
200204
fn import_sealed_block(&self, block: &SealedBlock) -> ImportResult;
201205

core/src/client/test_client.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,10 @@ impl ImportBlock for TestBlockChainClient {
509509
unimplemented!()
510510
}
511511

512+
fn import_bootstrap_header(&self, _header: &BlockHeader) -> Result<BlockHash, BlockImportError> {
513+
unimplemented!()
514+
}
515+
512516
fn import_sealed_block(&self, _block: &SealedBlock) -> ImportResult {
513517
Ok(H256::default().into())
514518
}

core/src/consensus/tendermint/worker.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ use crate::encoded;
5252
use crate::error::{BlockError, Error};
5353
use crate::snapshot_notify::NotifySender as SnapshotNotifySender;
5454
use crate::transaction::{SignedTransaction, UnverifiedTransaction};
55+
use crate::types::BlockStatus;
5556
use crate::views::BlockView;
5657
use crate::BlockId;
5758
use std::cell::Cell;
@@ -965,7 +966,8 @@ impl Worker {
965966
}
966967

967968
fn on_imported_proposal(&mut self, proposal: &Header) {
968-
if proposal.number() < 1 {
969+
// NOTE: Only the genesis block and the snapshot target don't have the parent in the blockchain
970+
if self.client().block_status(&BlockId::Hash(*proposal.parent_hash())) == BlockStatus::Unknown {
969971
return
970972
}
971973

foundry/run_node.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ pub fn run_node(matches: &ArgMatches<'_>) -> Result<(), String> {
264264
let network_config = config.network_config()?;
265265
// XXX: What should we do if the network id has been changed.
266266
let c = client.client();
267-
let network_id = c.common_params(BlockId::Latest).unwrap().network_id();
267+
let network_id = c.common_params(BlockId::Number(0)).unwrap().network_id();
268268
let routing_table = RoutingTable::new();
269269
let service = network_start(network_id, timer_loop, &network_config, Arc::clone(&routing_table))?;
270270

sync/src/block/extension.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,12 @@ impl Extension {
8989
let mut header = client.best_header();
9090
let mut hollow_headers = vec![header.decode()];
9191
while client.block_body(&BlockId::Hash(header.hash())).is_none() {
92-
header = client
93-
.block_header(&BlockId::Hash(header.parent_hash()))
94-
.expect("Every imported header must have parent");
95-
hollow_headers.push(header.decode());
92+
if let Some(h) = client.block_header(&BlockId::Hash(header.parent_hash())) {
93+
header = h;
94+
hollow_headers.push(header.decode());
95+
} else {
96+
break
97+
}
9698
}
9799
let mut body_downloader = BodyDownloader::default();
98100
for neighbors in hollow_headers.windows(2).rev() {
@@ -765,7 +767,7 @@ impl Extension {
765767
match self.state {
766768
State::SnapshotHeader(hash, _) => match headers {
767769
[header] if header.hash() == hash => {
768-
match self.client.import_header(header.rlp_bytes().to_vec()) {
770+
match self.client.import_bootstrap_header(&header) {
769771
Err(BlockImportError::Import(ImportError::AlreadyInChain)) => {}
770772
Err(BlockImportError::Import(ImportError::AlreadyQueued)) => {}
771773
// FIXME: handle import errors

0 commit comments

Comments
 (0)