Skip to content

Commit

Permalink
Child trie storage proof (paritytech#2433)
Browse files Browse the repository at this point in the history
* proof on child trie

* higher level api for child storage proof

* boilerplate for proof from light fetch

* actually check proof on light fetch

* Do not break former encoding

* tabify

* tabify2

* Add child trie root tx to full_storage_root transaction.

* Shorten long lines.

* Temp rename for audit

* Make full_storage a trait method

* Name back and replace some code with full_storage where it looks fine.

* fix indentations, remove unused import

* flush child root to top when calculated

* impl +1
  • Loading branch information
cheme authored and gavofyork committed May 10, 2019
1 parent 0f24ba9 commit f87a493
Show file tree
Hide file tree
Showing 14 changed files with 475 additions and 103 deletions.
23 changes: 10 additions & 13 deletions core/client/db/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,29 +324,26 @@ where Block: BlockT<Hash=H256>,
Ok(())
}

fn reset_storage(&mut self, mut top: StorageOverlay, children: ChildrenStorageOverlay) -> Result<H256, client::error::Error> {
fn reset_storage(&mut self, top: StorageOverlay, children: ChildrenStorageOverlay) -> Result<H256, client::error::Error> {

if top.iter().any(|(k, _)| well_known_keys::is_child_storage_key(k)) {
return Err(client::error::Error::GenesisInvalid.into());
}

let mut transaction: PrefixedMemoryDB<Blake2Hasher> = Default::default();

for (child_key, child_map) in children {
for child_key in children.keys() {
if !well_known_keys::is_child_storage_key(&child_key) {
return Err(client::error::Error::GenesisInvalid.into());
}

let (root, is_default, update) = self.old_state.child_storage_root(&child_key, child_map.into_iter().map(|(k, v)| (k, Some(v))));
transaction.consolidate(update);

if !is_default {
top.insert(child_key, root);
}
}

let (root, update) = self.old_state.storage_root(top.into_iter().map(|(k, v)| (k, Some(v))));
transaction.consolidate(update);
let child_delta = children.into_iter()
.map(|(storage_key, child_overlay)|
(storage_key, child_overlay.into_iter().map(|(k, v)| (k, Some(v)))));

let (root, transaction) = self.old_state.full_storage_root(
top.into_iter().map(|(k, v)| (k, Some(v))),
child_delta
);

self.db_updates = transaction;
Ok(root)
Expand Down
16 changes: 15 additions & 1 deletion core/client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ use primitives::storage::well_known_keys;
use parity_codec::{Encode, Decode};
use state_machine::{
DBValue, Backend as StateBackend, CodeExecutor, ChangesTrieAnchorBlockId,
ExecutionStrategy, ExecutionManager, prove_read,
ExecutionStrategy, ExecutionManager, prove_read, prove_child_read,
ChangesTrieRootsStorage, ChangesTrieStorage,
key_changes, key_changes_proof, OverlayedChanges, NeverOffchainExt,
};
Expand Down Expand Up @@ -374,6 +374,20 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
.map_err(Into::into))
}

/// Reads child storage value at a given block + storage_key + key, returning
/// read proof.
pub fn read_child_proof(
&self,
id: &BlockId<Block>,
storage_key: &[u8],
key: &[u8]
) -> error::Result<Vec<Vec<u8>>> {
self.state_at(id)
.and_then(|state| prove_child_read(state, storage_key, key)
.map(|(_, proof)| proof)
.map_err(Into::into))
}

/// Execute a call to a contract on top of state in a block of given hash
/// AND returning execution proof.
///
Expand Down
23 changes: 9 additions & 14 deletions core/client/src/in_mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use runtime_primitives::generic::BlockId;
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Zero,
NumberFor, As, Digest, DigestItem};
use runtime_primitives::{Justification, StorageOverlay, ChildrenStorageOverlay};
use state_machine::backend::{Backend as StateBackend, InMemory, Consolidate};
use state_machine::backend::{Backend as StateBackend, InMemory};
use state_machine::{self, InMemoryChangesTrieStorage, ChangesTrieAnchorBlockId};
use hash_db::Hasher;
use trie::MemoryDB;
Expand Down Expand Up @@ -482,22 +482,17 @@ where
Ok(())
}

fn reset_storage(&mut self, mut top: StorageOverlay, children: ChildrenStorageOverlay) -> error::Result<H::Out> {
fn reset_storage(&mut self, top: StorageOverlay, children: ChildrenStorageOverlay) -> error::Result<H::Out> {
check_genesis_storage(&top, &children)?;

let mut transaction: Vec<(Option<Vec<u8>>, Vec<u8>, Option<Vec<u8>>)> = Default::default();
let child_delta = children.into_iter()
.map(|(storage_key, child_overlay)|
(storage_key, child_overlay.into_iter().map(|(k, v)| (k, Some(v)))));

for (child_key, child_map) in children {
let (root, is_default, update) = self.old_state.child_storage_root(&child_key, child_map.into_iter().map(|(k, v)| (k, Some(v))));
transaction.consolidate(update);

if !is_default {
top.insert(child_key, root);
}
}

let (root, update) = self.old_state.storage_root(top.into_iter().map(|(k, v)| (k, Some(v))));
transaction.consolidate(update);
let (root, transaction) = self.old_state.full_storage_root(
top.into_iter().map(|(k, v)| (k, Some(v))),
child_delta
);

self.new_state = Some(InMemory::from(transaction));
Ok(root)
Expand Down
51 changes: 49 additions & 2 deletions core/client/src/light/fetcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ use hash_db::{HashDB, Hasher};
use primitives::{ChangesTrieConfiguration, convert_hash};
use runtime_primitives::traits::{As, Block as BlockT, Header as HeaderT, NumberFor};
use state_machine::{CodeExecutor, ChangesTrieRootsStorage, ChangesTrieAnchorBlockId,
TrieBackend, read_proof_check, key_changes_proof_check, create_proof_check_backend_storage};
TrieBackend, read_proof_check, key_changes_proof_check,
create_proof_check_backend_storage, read_child_proof_check};

use crate::cht;
use crate::error::{Error as ClientError, Result as ClientResult};
Expand Down Expand Up @@ -71,6 +72,21 @@ pub struct RemoteReadRequest<Header: HeaderT> {
pub retry_count: Option<usize>,
}

/// Remote storage read child request.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct RemoteReadChildRequest<Header: HeaderT> {
/// Read at state of given block.
pub block: Header::Hash,
/// Header of block at which read is performed.
pub header: Header,
/// Storage key for child.
pub storage_key: Vec<u8>,
/// Child storage key to read.
pub key: Vec<u8>,
/// Number of times to retry request. None means that default RETRY_COUNT is used.
pub retry_count: Option<usize>,
}

/// Remote key changes read request.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RemoteChangesRequest<Header: HeaderT> {
Expand Down Expand Up @@ -123,7 +139,15 @@ pub trait Fetcher<Block: BlockT>: Send + Sync {
/// Fetch remote header.
fn remote_header(&self, request: RemoteHeaderRequest<Block::Header>) -> Self::RemoteHeaderResult;
/// Fetch remote storage value.
fn remote_read(&self, request: RemoteReadRequest<Block::Header>) -> Self::RemoteReadResult;
fn remote_read(
&self,
request: RemoteReadRequest<Block::Header>
) -> Self::RemoteReadResult;
/// Fetch remote storage child value.
fn remote_read_child(
&self,
request: RemoteReadChildRequest<Block::Header>
) -> Self::RemoteReadResult;
/// Fetch remote call result.
fn remote_call(&self, request: RemoteCallRequest<Block::Header>) -> Self::RemoteCallResult;
/// Fetch remote changes ((block number, extrinsic index)) where given key has been changed
Expand All @@ -149,6 +173,12 @@ pub trait FetchChecker<Block: BlockT>: Send + Sync {
request: &RemoteReadRequest<Block::Header>,
remote_proof: Vec<Vec<u8>>
) -> ClientResult<Option<Vec<u8>>>;
/// Check remote storage read proof.
fn check_read_child_proof(
&self,
request: &RemoteReadChildRequest<Block::Header>,
remote_proof: Vec<Vec<u8>>
) -> ClientResult<Option<Vec<u8>>>;
/// Check remote method execution proof.
fn check_execution_proof(
&self,
Expand Down Expand Up @@ -338,6 +368,19 @@ impl<E, Block, H, S, F> FetchChecker<Block> for LightDataChecker<E, H, Block, S,
.map_err(Into::into)
}

fn check_read_child_proof(
&self,
request: &RemoteReadChildRequest<Block::Header>,
remote_proof: Vec<Vec<u8>>
) -> ClientResult<Option<Vec<u8>>> {
read_child_proof_check::<H>(
convert_hash(request.header.state_root()),
remote_proof,
&request.storage_key,
&request.key)
.map_err(Into::into)
}

fn check_execution_proof(
&self,
request: &RemoteCallRequest<Block::Header>,
Expand Down Expand Up @@ -425,6 +468,10 @@ pub mod tests {
err("Not implemented on test node".into())
}

fn remote_read_child(&self, _request: RemoteReadChildRequest<Header>) -> Self::RemoteReadResult {
err("Not implemented on test node".into())
}

fn remote_call(&self, _request: RemoteCallRequest<Header>) -> Self::RemoteCallResult {
ok((*self.lock()).clone())
}
Expand Down
21 changes: 18 additions & 3 deletions core/network/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub use self::generic::{
BlockAnnounce, RemoteCallRequest, RemoteReadRequest,
RemoteHeaderRequest, RemoteHeaderResponse,
RemoteChangesRequest, RemoteChangesResponse,
FromBlock
FromBlock, RemoteReadChildRequest,
};

/// A unique ID of a request.
Expand Down Expand Up @@ -129,8 +129,8 @@ pub mod generic {
use runtime_primitives::Justification;
use crate::config::Roles;
use super::{
BlockAttributes, RemoteCallResponse, RemoteReadResponse,
RequestId, Transactions, Direction, ConsensusEngineId,
RemoteReadResponse, Transactions, Direction,
RequestId, BlockAttributes, RemoteCallResponse, ConsensusEngineId,
};
/// Consensus is mostly opaque to us
#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)]
Expand Down Expand Up @@ -198,6 +198,8 @@ pub mod generic {
RemoteChangesRequest(RemoteChangesRequest<Hash>),
/// Remote changes reponse.
RemoteChangesResponse(RemoteChangesResponse<Number, Hash>),
/// Remote child storage read request.
RemoteReadChildRequest(RemoteReadChildRequest<Hash>),
/// Chain-specific message
#[codec(index = "255")]
ChainSpecific(Vec<u8>),
Expand Down Expand Up @@ -291,6 +293,19 @@ pub mod generic {
pub key: Vec<u8>,
}

#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)]
/// Remote storage read child request.
pub struct RemoteReadChildRequest<H> {
/// Unique request id.
pub id: RequestId,
/// Block at which to perform call.
pub block: H,
/// Child Storage key.
pub storage_key: Vec<u8>,
/// Storage key.
pub key: Vec<u8>,
}

#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)]
/// Remote header request.
pub struct RemoteHeaderRequest<N> {
Expand Down
Loading

0 comments on commit f87a493

Please sign in to comment.