Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Dynamically generate CHT roots on a full client #6944

Merged
merged 7 commits into from
Aug 24, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions client/api/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -536,3 +536,22 @@ pub fn changes_tries_state_at_block<'a, Block: BlockT>(
None => Ok(None),
}
}

/// Provide CHT roots. These are stored on a light client and generated dynamically on a full
/// client.
pub trait ProvideChtRoots<Block: BlockT> {
/// Get headers CHT root for given block. Returns None if the block is not a part of any CHT.
svyatonik marked this conversation as resolved.
Show resolved Hide resolved
fn header_cht_root(
&self,
cht_size: NumberFor<Block>,
block: NumberFor<Block>,
) -> sp_blockchain::Result<Option<Block::Hash>>;

/// Get changes trie CHT root for given block. Returns None if the block is not a part of any
/// CHT.
fn changes_trie_cht_root(
&self,
cht_size: NumberFor<Block>,
block: NumberFor<Block>,
) -> sp_blockchain::Result<Option<Block::Hash>>;
}
4 changes: 2 additions & 2 deletions client/api/src/in_mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use sp_state_machine::{
use sp_blockchain::{CachedHeaderMetadata, HeaderMetadata};

use crate::{
backend::{self, NewBlockState},
backend::{self, NewBlockState, ProvideChtRoots},
blockchain::{
self, BlockStatus, HeaderBackend, well_known_cache_keys::Id as CacheKeyId
},
Expand Down Expand Up @@ -456,7 +456,7 @@ impl<Block: BlockT> light::Storage<Block> for Blockchain<Block>
}
}

impl<Block: BlockT> light::ChtRootStorage<Block> for Blockchain<Block> {
impl<Block: BlockT> ProvideChtRoots<Block> for Blockchain<Block> {
fn header_cht_root(
&self,
_cht_size: NumberFor<Block>,
Expand Down
21 changes: 2 additions & 19 deletions client/api/src/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use sp_blockchain::{
HeaderMetadata, well_known_cache_keys, HeaderBackend, Cache as BlockchainCache,
Error as ClientError, Result as ClientResult,
};
use crate::{backend::{AuxStore, NewBlockState}, UsageInfo};
use crate::{backend::{AuxStore, NewBlockState}, UsageInfo, ProvideChtRoots};

/// Remote call request.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -233,7 +233,7 @@ pub trait FetchChecker<Block: BlockT>: Send + Sync {

/// Light client blockchain storage.
pub trait Storage<Block: BlockT>: AuxStore + HeaderBackend<Block>
+ HeaderMetadata<Block, Error=ClientError> + ChtRootStorage<Block>
+ HeaderMetadata<Block, Error=ClientError> + ProvideChtRoots<Block>
{
/// Store new header. Should refuse to revert any finalized blocks.
///
Expand Down Expand Up @@ -263,23 +263,6 @@ pub trait Storage<Block: BlockT>: AuxStore + HeaderBackend<Block>
fn usage_info(&self) -> Option<UsageInfo>;
}

/// Light client CHT root storage.
pub trait ChtRootStorage<Block: BlockT> {
/// Get headers CHT root for given block. Returns None if the block is not pruned (not a part of any CHT).
fn header_cht_root(
&self,
cht_size: NumberFor<Block>,
block: NumberFor<Block>,
) -> ClientResult<Option<Block::Hash>>;

/// Get changes trie CHT root for given block. Returns None if the block is not pruned (not a part of any CHT).
fn changes_trie_cht_root(
&self,
cht_size: NumberFor<Block>,
block: NumberFor<Block>,
) -> ClientResult<Option<Block::Hash>>;
}

/// Remote header.
#[derive(Debug)]
pub enum LocalOrRemote<Data, Request> {
Expand Down
91 changes: 88 additions & 3 deletions client/db/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ use std::collections::{HashMap, HashSet};

use sc_client_api::{
UsageInfo, MemoryInfo, IoInfo, MemorySize,
backend::{NewBlockState, PrunableStateChangesTrieStorage},
leaves::{LeafSet, FinalizationDisplaced},
backend::{NewBlockState, PrunableStateChangesTrieStorage, ProvideChtRoots},
leaves::{LeafSet, FinalizationDisplaced}, cht,
};
use sp_blockchain::{
Result as ClientResult, Error as ClientError,
Expand All @@ -70,7 +70,7 @@ use sp_core::ChangesTrieConfiguration;
use sp_core::offchain::storage::{OffchainOverlayedChange, OffchainOverlayedChanges};
use sp_core::storage::{well_known_keys, ChildInfo};
use sp_arithmetic::traits::Saturating;
use sp_runtime::{generic::BlockId, Justification, Storage};
use sp_runtime::{generic::{DigestItem, BlockId}, Justification, Storage};
use sp_runtime::traits::{
Block as BlockT, Header as HeaderT, NumberFor, Zero, One, SaturatedConversion, HashFor,
};
Expand Down Expand Up @@ -405,6 +405,14 @@ impl<Block: BlockT> BlockchainDb<Block> {
meta.finalized_hash = hash;
}
}

// Get block changes trie root, if available.
fn changes_trie_root(&self, block: BlockId<Block>) -> ClientResult<Option<Block::Hash>> {
self.header(block)
.map(|header| header.and_then(|header|
header.digest().log(DigestItem::as_changes_trie_root)
.cloned()))
}
}

impl<Block: BlockT> sc_client_api::blockchain::HeaderBackend<Block> for BlockchainDb<Block> {
Expand Down Expand Up @@ -525,6 +533,58 @@ impl<Block: BlockT> HeaderMetadata<Block> for BlockchainDb<Block> {
}
}

impl<Block: BlockT> ProvideChtRoots<Block> for BlockchainDb<Block> {
fn header_cht_root(
&self,
cht_size: NumberFor<Block>,
block: NumberFor<Block>,
) -> sp_blockchain::Result<Option<Block::Hash>> {
let cht_number = match cht::block_to_cht_number(cht_size, block) {
Some(number) => number,
None => return Ok(None),
};

let cht_start: NumberFor<Block> = cht::start_number(cht::size(), cht_number);

let mut current_num = cht_start;
let cht_range = ::std::iter::from_fn(|| {
let old_current_num = current_num;
current_num = current_num + One::one();
Some(old_current_num)
});

cht::compute_root::<Block::Header, HashFor<Block>, _>(
cht::size(), cht_number, cht_range.map(|num| self.hash(num))
).map(Some)
}

fn changes_trie_cht_root(
&self,
cht_size: NumberFor<Block>,
block: NumberFor<Block>,
) -> sp_blockchain::Result<Option<Block::Hash>> {
let cht_number = match cht::block_to_cht_number(cht_size, block) {
Some(number) => number,
None => return Ok(None),
};

let cht_start: NumberFor<Block> = cht::start_number(cht::size(), cht_number);

let mut current_num = cht_start;
let cht_range = ::std::iter::from_fn(|| {
let old_current_num = current_num;
current_num = current_num + One::one();
Some(old_current_num)
});

cht::compute_root::<Block::Header, HashFor<Block>, _>(
cht::size(),
cht_number,
cht_range.map(|num| self.changes_trie_root(BlockId::Number(num))),
).map(Some)
}
}

/// Database transaction
pub struct BlockImportOperation<Block: BlockT> {
old_state: SyncingCachingState<RefTrackingState<Block>, Block>,
Expand Down Expand Up @@ -2329,4 +2389,29 @@ pub(crate) mod tests {
backend.commit_operation(op).unwrap_err();
}
}

#[test]
fn header_cht_root_works() {
use sc_client_api::ProvideChtRoots;

let backend = Backend::<Block>::new_test(10, 10);

// insert 1 + SIZE + SIZE + 1 blocks so that CHT#0 is created
let mut prev_hash = insert_header(&backend, 0, Default::default(), None, Default::default());
let cht_size: u64 = cht::size();
for i in 1..1 + cht_size + cht_size + 1 {
prev_hash = insert_header(&backend, i, prev_hash, None, Default::default());
}

let blockchain = backend.blockchain();

let cht_root_1 = blockchain.header_cht_root(cht_size, cht::start_number(cht_size, 0))
.unwrap().unwrap();
let cht_root_2 = blockchain.header_cht_root(cht_size, cht::start_number(cht_size, 0) + cht_size / 2)
.unwrap().unwrap();
let cht_root_3 = blockchain.header_cht_root(cht_size, cht::end_number(cht_size, 0))
.unwrap().unwrap();
assert_eq!(cht_root_1, cht_root_2);
assert_eq!(cht_root_2, cht_root_3);
}
}
6 changes: 3 additions & 3 deletions client/db/src/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ use std::convert::TryInto;
use parking_lot::RwLock;

use sc_client_api::{
cht, backend::{AuxStore, NewBlockState}, UsageInfo,
cht, backend::{AuxStore, NewBlockState, ProvideChtRoots}, UsageInfo,
blockchain::{
BlockStatus, Cache as BlockchainCache, Info as BlockchainInfo,
},
Storage, ChtRootStorage,
Storage,
};
use sp_blockchain::{
CachedHeaderMetadata, HeaderMetadata, HeaderMetadataCache,
Expand Down Expand Up @@ -596,7 +596,7 @@ impl<Block> Storage<Block> for LightStorage<Block>
}
}

impl<Block> ChtRootStorage<Block> for LightStorage<Block>
impl<Block> ProvideChtRoots<Block> for LightStorage<Block>
where Block: BlockT,
{
fn header_cht_root(
Expand Down
20 changes: 19 additions & 1 deletion client/light/src/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use sp_blockchain::{
};
pub use sc_client_api::{
backend::{
AuxStore, NewBlockState
AuxStore, NewBlockState, ProvideChtRoots,
},
blockchain::{
Backend as BlockchainBackend, BlockStatus, Cache as BlockchainCache,
Expand Down Expand Up @@ -173,3 +173,21 @@ impl<S, Block: BlockT> RemoteBlockchain<Block> for Blockchain<S>
}))
}
}

impl<S: Storage<Block>, Block: BlockT> ProvideChtRoots<Block> for Blockchain<S> {
fn header_cht_root(
&self,
cht_size: NumberFor<Block>,
block: NumberFor<Block>,
) -> sp_blockchain::Result<Option<Block::Hash>> {
self.storage().header_cht_root(cht_size, block)
svyatonik marked this conversation as resolved.
Show resolved Hide resolved
}

fn changes_trie_cht_root(
&self,
cht_size: NumberFor<Block>,
block: NumberFor<Block>,
) -> sp_blockchain::Result<Option<Block::Hash>> {
self.storage().changes_trie_cht_root(cht_size, block)
}
}
31 changes: 5 additions & 26 deletions client/service/src/chain_ops/build_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,9 @@

use sp_runtime::traits::{Block as BlockT, NumberFor, Saturating, One};
use sp_blockchain::HeaderBackend;
use crate::{TFullBackend, TLightBackend};
use std::sync::Arc;
use sp_runtime::generic::BlockId;

/// An error for if this function is being called on a full node.
pub const CHT_ROOT_ERROR: &str =
"Backend doesn't store CHT roots. Make sure you're calling this on a light client.";

/// Something that might allow access to a `ChtRootStorage`.
pub trait MaybeChtRootStorageProvider<Block> {
/// Potentially get a reference to a `ChtRootStorage`.
fn cht_root_storage(&self) -> Option<&dyn sc_client_api::light::ChtRootStorage<Block>>;
}

impl<Block: BlockT> MaybeChtRootStorageProvider<Block> for TFullBackend<Block> {
fn cht_root_storage(&self) -> Option<&dyn sc_client_api::light::ChtRootStorage<Block>> {
None
}
}

impl<Block: BlockT> MaybeChtRootStorageProvider<Block> for TLightBackend<Block> {
fn cht_root_storage(&self) -> Option<&dyn sc_client_api::light::ChtRootStorage<Block>> {
Some(self.blockchain().storage())
}
}
use sc_client_api::ProvideChtRoots;

/// Build a `LightSyncState` from the CHT roots stored in a backend.
pub fn build_light_sync_state<TBl, TCl, TBackend>(
Expand All @@ -50,9 +28,10 @@ pub fn build_light_sync_state<TBl, TCl, TBackend>(
where
TBl: BlockT,
TCl: HeaderBackend<TBl>,
TBackend: MaybeChtRootStorageProvider<TBl>,
TBackend: sc_client_api::Backend<TBl>,
<TBackend as sc_client_api::Backend<TBl>>::Blockchain: ProvideChtRoots<TBl>,
{
let storage = backend.cht_root_storage().ok_or(CHT_ROOT_ERROR)?;
let cht_root_provider = backend.blockchain();

let finalized_hash = client.info().finalized_hash;
let finalized_number = client.info().finalized_number;
Expand All @@ -67,7 +46,7 @@ pub fn build_light_sync_state<TBl, TCl, TBackend>(
let mut number = NumberFor::<TBl>::one();

while number <= finalized_number.saturating_sub(cht_size_x_2) {
match storage.header_cht_root(cht::size(), number)? {
match cht_root_provider.header_cht_root(cht::size(), number)? {
Some(cht_root) => chts.push(cht_root),
None => log::error!("No CHT found for block {}", number),
}
Expand Down