Remove requirement on Hash = H256, make Proposer return StorageChanges and Proof (#3860)

* Extend `Proposer` to optionally generate a proof of the proposal

* Something

* Refactor sr-api to not depend on client anymore

* Fix benches

* Apply suggestions from code review

Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Apply suggestions from code review

* Introduce new `into_storage_changes` function

* Switch to runtime api for `execute_block` and don't require `H256`
anywhere in the code

* Put the `StorageChanges` into the `Proposal`

* Move the runtime api error to its own trait

* Adds `StorageTransactionCache` to the runtime api

This requires that we add `type NodeBlock = ` to the
`impl_runtime_apis!` macro to work around some bugs in rustc :(

* Remove `type NodeBlock` and switch to a "better" hack

* Start using the transaction cache from the runtime api

* Make it compile

* Move `InMemory` to its own file

* Make all tests work again

* Return block, storage_changes and proof from Blockbuilder::bake()

* Make sure that we use/set `storage_changes` when possible

* Add test

* Fix deadlock

* Remove accidentally added folders

* Introduce `RecordProof` as argument type to be more explicit

* Update client/src/client.rs

Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Update primitives/state-machine/src/ext.rs

Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Integrates review feedback

* Remove `unsafe` usage

* Update client/block-builder/src/lib.rs

Co-Authored-By: Benjamin Kampmann <ben@gnunicorn.org>

* Update client/src/call_executor.rs

* Bump versions

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>
Co-authored-by: Benjamin Kampmann <ben.kampmann@googlemail.com>
This commit is contained in:
Bastian Köcher
2020-01-10 10:48:32 +01:00
committed by GitHub
parent 74d6e660c6
commit fd6b29dd2c
140 changed files with 4860 additions and 3339 deletions
+50 -42
View File
@@ -24,12 +24,12 @@ use parking_lot::RwLock;
use sp_core::storage::{ChildInfo, OwnedChildInfo};
use sp_core::offchain::storage::InMemOffchainStorage;
use sp_state_machine::{
Backend as StateBackend, TrieBackend, backend::InMemory as InMemoryState, ChangesTrieTransaction
Backend as StateBackend, TrieBackend, InMemoryBackend, ChangesTrieTransaction
};
use sp_runtime::{generic::BlockId, Justification, Storage};
use sp_runtime::traits::{Block as BlockT, NumberFor, Zero, Header};
use sp_runtime::traits::{Block as BlockT, NumberFor, Zero, Header, HasherFor};
use crate::in_mem::{self, check_genesis_storage};
use sp_blockchain::{ Error as ClientError, Result as ClientResult };
use sp_blockchain::{Error as ClientError, Result as ClientResult};
use sc_client_api::{
backend::{
AuxStore, Backend as ClientBackend, BlockImportOperation, RemoteBackend, NewBlockState,
@@ -43,33 +43,32 @@ use sc_client_api::{
};
use crate::light::blockchain::Blockchain;
use hash_db::Hasher;
use sp_trie::MemoryDB;
const IN_MEMORY_EXPECT_PROOF: &str = "InMemory state backend has Void error type and always succeeds; qed";
/// Light client backend.
pub struct Backend<S, H: Hasher> {
blockchain: Arc<Blockchain<S>>,
genesis_state: RwLock<Option<InMemoryState<H>>>,
genesis_state: RwLock<Option<InMemoryBackend<H>>>,
import_lock: RwLock<()>,
}
/// Light block (header and justification) import operation.
pub struct ImportOperation<Block: BlockT, S, H: Hasher> {
pub struct ImportOperation<Block: BlockT, S> {
header: Option<Block::Header>,
cache: HashMap<well_known_cache_keys::Id, Vec<u8>>,
leaf_state: NewBlockState,
aux_ops: Vec<(Vec<u8>, Option<Vec<u8>>)>,
finalized_blocks: Vec<BlockId<Block>>,
set_head: Option<BlockId<Block>>,
storage_update: Option<InMemoryState<H>>,
_phantom: ::std::marker::PhantomData<S>,
storage_update: Option<InMemoryBackend<HasherFor<Block>>>,
_phantom: std::marker::PhantomData<S>,
}
/// Either in-memory genesis state, or locally-unavailable state.
pub enum GenesisOrUnavailableState<H: Hasher> {
/// Genesis state - storage values are stored in-memory.
Genesis(InMemoryState<H>),
Genesis(InMemoryBackend<H>),
/// We know that state exists, but all calls will fail with error, because it
/// isn't locally available.
Unavailable,
@@ -107,16 +106,16 @@ impl<S: AuxStore, H: Hasher> AuxStore for Backend<S, H> {
}
}
impl<S, Block, H> ClientBackend<Block, H> for Backend<S, H> where
Block: BlockT,
S: BlockchainStorage<Block>,
H: Hasher<Out=Block::Hash>,
H::Out: Ord,
impl<S, Block> ClientBackend<Block> for Backend<S, HasherFor<Block>>
where
Block: BlockT,
S: BlockchainStorage<Block>,
Block::Hash: Ord,
{
type BlockImportOperation = ImportOperation<Block, S, H>;
type BlockImportOperation = ImportOperation<Block, S>;
type Blockchain = Blockchain<S>;
type State = GenesisOrUnavailableState<H>;
type ChangesTrieStorage = in_mem::ChangesTrieStorage<Block, H>;
type State = GenesisOrUnavailableState<HasherFor<Block>>;
type ChangesTrieStorage = in_mem::ChangesTrieStorage<Block>;
type OffchainStorage = InMemOffchainStorage;
fn begin_operation(&self) -> ClientResult<Self::BlockImportOperation> {
@@ -179,7 +178,11 @@ impl<S, Block, H> ClientBackend<Block, H> for Backend<S, H> where
Ok(())
}
fn finalize_block(&self, block: BlockId<Block>, _justification: Option<Justification>) -> ClientResult<()> {
fn finalize_block(
&self,
block: BlockId<Block>,
_justification: Option<Justification>,
) -> ClientResult<()> {
self.blockchain.storage().finalize_header(block)
}
@@ -227,12 +230,11 @@ impl<S, Block, H> ClientBackend<Block, H> for Backend<S, H> where
}
}
impl<S, Block, H> RemoteBackend<Block, H> for Backend<S, H>
impl<S, Block> RemoteBackend<Block> for Backend<S, HasherFor<Block>>
where
Block: BlockT,
S: BlockchainStorage<Block> + 'static,
H: Hasher<Out=Block::Hash>,
H::Out: Ord,
Block::Hash: Ord,
{
fn is_local_state_available(&self, block: &BlockId<Block>) -> bool {
self.genesis_state.read().is_some()
@@ -246,14 +248,13 @@ where
}
}
impl<S, Block, H> BlockImportOperation<Block, H> for ImportOperation<Block, S, H>
where
Block: BlockT,
S: BlockchainStorage<Block>,
H: Hasher<Out=Block::Hash>,
H::Out: Ord,
impl<S, Block> BlockImportOperation<Block> for ImportOperation<Block, S>
where
Block: BlockT,
S: BlockchainStorage<Block>,
Block::Hash: Ord,
{
type State = GenesisOrUnavailableState<H>;
type State = GenesisOrUnavailableState<HasherFor<Block>>;
fn state(&self) -> ClientResult<Option<&Self::State>> {
// None means 'locally-stateless' backend
@@ -276,17 +277,23 @@ where
self.cache = cache;
}
fn update_db_storage(&mut self, _update: <Self::State as StateBackend<H>>::Transaction) -> ClientResult<()> {
fn update_db_storage(
&mut self,
_update: <Self::State as StateBackend<HasherFor<Block>>>::Transaction,
) -> ClientResult<()> {
// we're not storing anything locally => ignore changes
Ok(())
}
fn update_changes_trie(&mut self, _update: ChangesTrieTransaction<H, NumberFor<Block>>) -> ClientResult<()> {
fn update_changes_trie(
&mut self,
_update: ChangesTrieTransaction<HasherFor<Block>, NumberFor<Block>>,
) -> ClientResult<()> {
// we're not storing anything locally => ignore changes
Ok(())
}
fn reset_storage(&mut self, input: Storage) -> ClientResult<H::Out> {
fn reset_storage(&mut self, input: Storage) -> ClientResult<Block::Hash> {
check_genesis_storage(&input)?;
// this is only called when genesis block is imported => shouldn't be performance bottleneck
@@ -303,8 +310,8 @@ where
storage.insert(Some((child_key, storage_child.child_info)), storage_child.data);
}
let storage_update: InMemoryState<H> = storage.into();
let (storage_root, _) = storage_update.full_storage_root(::std::iter::empty(), child_delta);
let storage_update = InMemoryBackend::from(storage);
let (storage_root, _) = storage_update.full_storage_root(std::iter::empty(), child_delta);
self.storage_update = Some(storage_update);
Ok(storage_root)
@@ -351,8 +358,8 @@ impl<H: Hasher> StateBackend<H> for GenesisOrUnavailableState<H>
H::Out: Ord + codec::Codec,
{
type Error = ClientError;
type Transaction = ();
type TrieBackendStorage = MemoryDB<H>;
type Transaction = <InMemoryBackend<H> as StateBackend<H>>::Transaction;
type TrieBackendStorage = <InMemoryBackend<H> as StateBackend<H>>::TrieBackendStorage;
fn storage(&self, key: &[u8]) -> ClientResult<Option<Vec<u8>>> {
match *self {
@@ -445,8 +452,8 @@ impl<H: Hasher> StateBackend<H> for GenesisOrUnavailableState<H>
{
match *self {
GenesisOrUnavailableState::Genesis(ref state) =>
(state.storage_root(delta).0, ()),
GenesisOrUnavailableState::Unavailable => (H::Out::default(), ()),
state.storage_root(delta),
GenesisOrUnavailableState::Unavailable => Default::default(),
}
}
@@ -462,9 +469,10 @@ impl<H: Hasher> StateBackend<H> for GenesisOrUnavailableState<H>
match *self {
GenesisOrUnavailableState::Genesis(ref state) => {
let (root, is_equal, _) = state.child_storage_root(storage_key, child_info, delta);
(root, is_equal, ())
(root, is_equal, Default::default())
},
GenesisOrUnavailableState::Unavailable => (H::Out::default(), true, ()),
GenesisOrUnavailableState::Unavailable =>
(H::Out::default(), true, Default::default()),
}
}
@@ -528,9 +536,9 @@ mod tests {
#[test]
fn light_aux_store_is_updated_via_non_importing_op() {
let backend = Backend::new(Arc::new(DummyBlockchain::new(DummyStorage::new())));
let mut op = ClientBackend::<Block, Blake2Hasher>::begin_operation(&backend).unwrap();
BlockImportOperation::<Block, Blake2Hasher>::insert_aux(&mut op, vec![(vec![1], Some(vec![2]))]).unwrap();
ClientBackend::<Block, Blake2Hasher>::commit_operation(&backend, op).unwrap();
let mut op = ClientBackend::<Block>::begin_operation(&backend).unwrap();
BlockImportOperation::<Block>::insert_aux(&mut op, vec![(vec![1], Some(vec![2]))]).unwrap();
ClientBackend::<Block>::commit_operation(&backend, op).unwrap();
assert_eq!(AuxStore::get_aux(&backend, &[1]).unwrap(), Some(vec![2]));
}