Create opaque struct for StorageProof. (#3834)

Passing around Vec<Vec<u8>> everywhere is gross and confusing and
breaks encapsulation.
This commit is contained in:
Jim Posen
2019-10-31 11:02:29 +01:00
committed by Bastian Köcher
parent 073040a053
commit a167f37b91
20 changed files with 246 additions and 141 deletions
+1
View File
@@ -5256,6 +5256,7 @@ dependencies = [
"substrate-keystore 2.0.0",
"substrate-network 2.0.0",
"substrate-primitives 2.0.0",
"substrate-state-machine 2.0.0",
"substrate-telemetry 2.0.0",
"substrate-test-runtime-client 2.0.0",
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -44,7 +44,7 @@
//! 4. Adds the retrieved external addresses as priority nodes to the peerset.
use authority_discovery_primitives::{AuthorityDiscoveryApi, AuthorityId, Signature};
use client::blockchain::HeaderBackend;
use client::{blockchain::HeaderBackend, runtime_api::StorageProof};
use error::{Error, Result};
use futures::{prelude::*, sync::mpsc::Receiver};
use log::{debug, error, log_enabled, warn};
@@ -528,7 +528,7 @@ mod tests {
unimplemented!("Not required for testing!")
}
fn extract_proof(&mut self) -> Option<Vec<Vec<u8>>> {
fn extract_proof(&mut self) -> Option<StorageProof> {
unimplemented!("Not required for testing!")
}
}
@@ -22,6 +22,7 @@ use sr_primitives::traits::{
Header as HeaderT, Hash, Block as BlockT, One, HashFor, ProvideRuntimeApi, ApiRef, DigestFor,
};
use primitives::{H256, ExecutionContext};
use state_machine::StorageProof;
use crate::blockchain::HeaderBackend;
use crate::runtime_api::{Core, ApiExt};
use crate::error;
@@ -140,7 +141,7 @@ where
///
/// The proof will be `Some(_)`, if proof recording was enabled while creating
/// the block builder.
pub fn bake_and_extract_proof(mut self) -> error::Result<(Block, Option<Vec<Vec<u8>>>)> {
pub fn bake_and_extract_proof(mut self) -> error::Result<(Block, Option<StorageProof>)> {
self.bake_impl()?;
let proof = self.api.extract_proof();
+4 -4
View File
@@ -21,7 +21,7 @@ use sr_primitives::{
};
use state_machine::{
self, OverlayedChanges, Ext, ExecutionManager, StateMachine, ExecutionStrategy,
backend::Backend as _, ChangesTrieTransaction,
backend::Backend as _, ChangesTrieTransaction, StorageProof,
};
use executor::{RuntimeVersion, RuntimeInfo, NativeVersion};
use hash_db::Hasher;
@@ -127,7 +127,7 @@ where
overlay: &mut OverlayedChanges,
method: &str,
call_data: &[u8]
) -> Result<(Vec<u8>, Vec<Vec<u8>>), error::Error> {
) -> Result<(Vec<u8>, StorageProof), error::Error> {
let trie_state = state.as_trie_backend()
.ok_or_else(||
Box::new(state_machine::ExecutionError::UnableToGenerateProof)
@@ -145,7 +145,7 @@ where
overlay: &mut OverlayedChanges,
method: &str,
call_data: &[u8]
) -> Result<(Vec<u8>, Vec<Vec<u8>>), error::Error>;
) -> Result<(Vec<u8>, StorageProof), error::Error>;
/// Get runtime version if supported.
fn native_runtime_version(&self) -> Option<&NativeVersion>;
@@ -377,7 +377,7 @@ where
overlay: &mut OverlayedChanges,
method: &str,
call_data: &[u8]
) -> Result<(Vec<u8>, Vec<Vec<u8>>), error::Error> {
) -> Result<(Vec<u8>, StorageProof), error::Error> {
state_machine::prove_execution_on_trie_backend(
trie_state,
overlay,
+3 -3
View File
@@ -30,7 +30,7 @@ use trie;
use primitives::{H256, convert_hash};
use sr_primitives::traits::{Header as HeaderT, SimpleArithmetic, Zero, One};
use state_machine::backend::InMemory as InMemoryState;
use state_machine::{MemoryDB, TrieBackend, Backend as StateBackend,
use state_machine::{MemoryDB, TrieBackend, Backend as StateBackend, StorageProof,
prove_read_on_trie_backend, read_proof_check, read_proof_check_on_proving_backend};
use crate::error::{Error as ClientError, Result as ClientResult};
@@ -88,7 +88,7 @@ pub fn build_proof<Header, Hasher, BlocksI, HashesI>(
cht_num: Header::Number,
blocks: BlocksI,
hashes: HashesI
) -> ClientResult<Vec<Vec<u8>>>
) -> ClientResult<StorageProof>
where
Header: HeaderT,
Hasher: hash_db::Hasher,
@@ -114,7 +114,7 @@ pub fn check_proof<Header, Hasher>(
local_root: Header::Hash,
local_number: Header::Number,
remote_hash: Header::Hash,
remote_proof: Vec<Vec<u8>>
remote_proof: StorageProof,
) -> ClientResult<()>
where
Header: HeaderT,
+11 -11
View File
@@ -43,7 +43,7 @@ use state_machine::{
DBValue, Backend as StateBackend, ChangesTrieAnchorBlockId, ExecutionStrategy, ExecutionManager,
prove_read, prove_child_read, ChangesTrieRootsStorage, ChangesTrieStorage,
ChangesTrieTransaction, ChangesTrieConfigurationRange, key_changes, key_changes_proof,
OverlayedChanges, BackendTrustLevel,
OverlayedChanges, BackendTrustLevel, StorageProof, merge_storage_proofs,
};
use executor::{RuntimeVersion, RuntimeInfo};
use consensus::{
@@ -421,7 +421,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
}
/// Reads storage value at a given block + key, returning read proof.
pub fn read_proof<I>(&self, id: &BlockId<Block>, keys: I) -> error::Result<Vec<Vec<u8>>> where
pub fn read_proof<I>(&self, id: &BlockId<Block>, keys: I) -> error::Result<StorageProof> where
I: IntoIterator,
I::Item: AsRef<[u8]>,
{
@@ -437,7 +437,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
id: &BlockId<Block>,
storage_key: &[u8],
keys: I,
) -> error::Result<Vec<Vec<u8>>> where
) -> error::Result<StorageProof> where
I: IntoIterator,
I::Item: AsRef<[u8]>,
{
@@ -454,14 +454,14 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
id: &BlockId<Block>,
method: &str,
call_data: &[u8]
) -> error::Result<(Vec<u8>, Vec<Vec<u8>>)> {
) -> error::Result<(Vec<u8>, StorageProof)> {
let state = self.state_at(id)?;
let header = self.prepare_environment_block(id)?;
prove_execution(state, header, &self.executor, method, call_data)
}
/// Reads given header and generates CHT-based header proof.
pub fn header_proof(&self, id: &BlockId<Block>) -> error::Result<(Block::Header, Vec<Vec<u8>>)> {
pub fn header_proof(&self, id: &BlockId<Block>) -> error::Result<(Block::Header, StorageProof)> {
self.header_proof_with_cht_size(id, cht::size())
}
@@ -477,7 +477,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
&self,
id: &BlockId<Block>,
cht_size: NumberFor<Block>,
) -> error::Result<(Block::Header, Vec<Vec<u8>>)> {
) -> error::Result<(Block::Header, StorageProof)> {
let proof_error = || error::Error::Backend(format!("Failed to generate header proof for {:?}", id));
let header = self.backend.blockchain().expect_header(*id)?;
let block_num = *header.number();
@@ -696,18 +696,18 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
&self,
cht_size: NumberFor<Block>,
blocks: I
) -> error::Result<Vec<Vec<u8>>> {
) -> error::Result<StorageProof> {
// most probably we have touched several changes tries that are parts of the single CHT
// => GroupBy changes tries by CHT number and then gather proof for the whole group at once
let mut proof = HashSet::new();
let mut proofs = Vec::new();
cht::for_each_cht_group::<Block::Header, _, _, _>(cht_size, blocks, |_, cht_num, cht_blocks| {
let cht_proof = self.changes_trie_roots_proof_at_cht(cht_size, cht_num, cht_blocks)?;
proof.extend(cht_proof);
proofs.push(cht_proof);
Ok(())
}, ())?;
Ok(proof.into_iter().collect())
Ok(merge_storage_proofs(proofs))
}
/// Generates CHT-based proof for roots of changes tries at given blocks (that are part of single CHT).
@@ -716,7 +716,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
cht_size: NumberFor<Block>,
cht_num: NumberFor<Block>,
blocks: Vec<NumberFor<Block>>
) -> error::Result<Vec<Vec<u8>>> {
) -> error::Result<StorageProof> {
let cht_start = cht::start_number(cht_size, cht_num);
let mut current_num = cht_start;
let cht_range = ::std::iter::from_fn(|| {
+1 -1
View File
@@ -122,7 +122,7 @@ pub use crate::client::{
#[cfg(feature = "std")]
pub use crate::notifications::{StorageEventStream, StorageChangeSet};
#[cfg(feature = "std")]
pub use state_machine::ExecutionStrategy;
pub use state_machine::{ExecutionStrategy, StorageProof};
#[cfg(feature = "std")]
pub use crate::leaves::LeafSet;
#[cfg(feature = "std")]
@@ -16,10 +16,7 @@
//! Methods that light client could use to execute runtime calls.
use std::{
collections::HashSet, sync::Arc, panic::UnwindSafe, result,
cell::RefCell, rc::Rc,
};
use std::{sync::Arc, panic::UnwindSafe, result, cell::RefCell, rc::Rc};
use codec::{Encode, Decode};
use primitives::{
@@ -31,7 +28,8 @@ use sr_primitives::{
};
use state_machine::{
self, Backend as StateBackend, OverlayedChanges, ExecutionStrategy, create_proof_check_backend,
execution_proof_check_on_trie_backend, ExecutionManager, ChangesTrieTransaction,
execution_proof_check_on_trie_backend, ExecutionManager, ChangesTrieTransaction, StorageProof,
merge_storage_proofs,
};
use hash_db::Hasher;
@@ -179,7 +177,7 @@ impl<Block, B, Local> CallExecutor<Block, Blake2Hasher> for
_changes: &mut OverlayedChanges,
_method: &str,
_call_data: &[u8]
) -> ClientResult<(Vec<u8>, Vec<Vec<u8>>)> {
) -> ClientResult<(Vec<u8>, StorageProof)> {
Err(ClientError::NotAvailableOnLightClient)
}
@@ -198,7 +196,7 @@ pub fn prove_execution<Block, S, E>(
executor: &E,
method: &str,
call_data: &[u8],
) -> ClientResult<(Vec<u8>, Vec<Vec<u8>>)>
) -> ClientResult<(Vec<u8>, StorageProof)>
where
Block: BlockT<Hash=H256>,
S: StateBackend<Blake2Hasher>,
@@ -218,11 +216,7 @@ pub fn prove_execution<Block, S, E>(
// execute method + record execution proof
let (result, exec_proof) = executor.prove_at_trie_state(&trie_state, &mut changes, method, call_data)?;
let total_proof = init_proof.into_iter()
.chain(exec_proof.into_iter())
.collect::<HashSet<_>>()
.into_iter()
.collect();
let total_proof = merge_storage_proofs(vec![init_proof, exec_proof]);
Ok((result, total_proof))
}
@@ -234,7 +228,7 @@ pub fn prove_execution<Block, S, E>(
pub fn check_execution_proof<Header, E, H>(
executor: &E,
request: &RemoteCallRequest<Header>,
remote_proof: Vec<Vec<u8>>,
remote_proof: StorageProof,
) -> ClientResult<Vec<u8>>
where
Header: HeaderT,
@@ -258,7 +252,7 @@ pub fn check_execution_proof<Header, E, H>(
fn check_execution_proof_with_make_header<Header, E, H, MakeNextHeader: Fn(&Header) -> Header>(
executor: &E,
request: &RemoteCallRequest<Header>,
remote_proof: Vec<Vec<u8>>,
remote_proof: StorageProof,
make_next_header: MakeNextHeader,
) -> ClientResult<Vec<u8>>
where
@@ -382,7 +376,7 @@ mod tests {
_overlay: &mut OverlayedChanges,
_method: &str,
_call_data: &[u8]
) -> Result<(Vec<u8>, Vec<Vec<u8>>), ClientError> {
) -> Result<(Vec<u8>, StorageProof), ClientError> {
unreachable!()
}
+20 -16
View File
@@ -33,6 +33,7 @@ use state_machine::{
TrieBackend, read_proof_check, key_changes_proof_check, create_proof_check_backend_storage,
read_child_proof_check,
};
pub use state_machine::StorageProof;
use crate::cht;
use crate::error::{Error as ClientError, Result as ClientResult};
@@ -129,7 +130,7 @@ pub struct ChangesProof<Header: HeaderT> {
pub roots: BTreeMap<Header::Number, Header::Hash>,
/// The proofs for all changes tries roots that have been touched AND are
/// missing from the requester' node. It is a map of CHT number => proof.
pub roots_proof: Vec<Vec<u8>>,
pub roots_proof: StorageProof,
}
/// Remote block body request
@@ -186,25 +187,25 @@ pub trait FetchChecker<Block: BlockT>: Send + Sync {
&self,
request: &RemoteHeaderRequest<Block::Header>,
header: Option<Block::Header>,
remote_proof: Vec<Vec<u8>>
remote_proof: StorageProof,
) -> ClientResult<Block::Header>;
/// Check remote storage read proof.
fn check_read_proof(
&self,
request: &RemoteReadRequest<Block::Header>,
remote_proof: Vec<Vec<u8>>
remote_proof: StorageProof,
) -> ClientResult<HashMap<Vec<u8>, Option<Vec<u8>>>>;
/// Check remote storage read proof.
fn check_read_child_proof(
&self,
request: &RemoteReadChildRequest<Block::Header>,
remote_proof: Vec<Vec<u8>>
remote_proof: StorageProof,
) -> ClientResult<HashMap<Vec<u8>, Option<Vec<u8>>>>;
/// Check remote method execution proof.
fn check_execution_proof(
&self,
request: &RemoteCallRequest<Block::Header>,
remote_proof: Vec<Vec<u8>>
remote_proof: StorageProof,
) -> ClientResult<Vec<u8>>;
/// Check remote changes query proof.
fn check_changes_proof(
@@ -318,7 +319,7 @@ impl<E, H, B: BlockT, S: BlockchainStorage<B>> LightDataChecker<E, H, B, S> {
&self,
cht_size: NumberFor<B>,
remote_roots: &BTreeMap<NumberFor<B>, B::Hash>,
remote_roots_proof: Vec<Vec<u8>>,
remote_roots_proof: StorageProof,
) -> ClientResult<()>
where
H: Hasher,
@@ -378,7 +379,7 @@ impl<E, Block, H, S> FetchChecker<Block> for LightDataChecker<E, H, Block, S>
&self,
request: &RemoteHeaderRequest<Block::Header>,
remote_header: Option<Block::Header>,
remote_proof: Vec<Vec<u8>>
remote_proof: StorageProof,
) -> ClientResult<Block::Header> {
let remote_header = remote_header.ok_or_else(||
ClientError::from(ClientError::InvalidCHTProof))?;
@@ -394,7 +395,7 @@ impl<E, Block, H, S> FetchChecker<Block> for LightDataChecker<E, H, Block, S>
fn check_read_proof(
&self,
request: &RemoteReadRequest<Block::Header>,
remote_proof: Vec<Vec<u8>>,
remote_proof: StorageProof,
) -> ClientResult<HashMap<Vec<u8>, Option<Vec<u8>>>> {
read_proof_check::<H, _>(
convert_hash(request.header.state_root()),
@@ -406,7 +407,7 @@ impl<E, Block, H, S> FetchChecker<Block> for LightDataChecker<E, H, Block, S>
fn check_read_child_proof(
&self,
request: &RemoteReadChildRequest<Block::Header>,
remote_proof: Vec<Vec<u8>>
remote_proof: StorageProof,
) -> ClientResult<HashMap<Vec<u8>, Option<Vec<u8>>>> {
read_child_proof_check::<H, _>(
convert_hash(request.header.state_root()),
@@ -419,7 +420,7 @@ impl<E, Block, H, S> FetchChecker<Block> for LightDataChecker<E, H, Block, S>
fn check_execution_proof(
&self,
request: &RemoteCallRequest<Block::Header>,
remote_proof: Vec<Vec<u8>>
remote_proof: StorageProof,
) -> ClientResult<Vec<u8>> {
check_execution_proof::<_, _, H>(&self.executor, request, remote_proof)
}
@@ -572,7 +573,7 @@ pub mod tests {
NativeExecutor::new(WasmExecutionMethod::Interpreted, None)
}
fn prepare_for_read_proof_check() -> (TestChecker, Header, Vec<Vec<u8>>, u32) {
fn prepare_for_read_proof_check() -> (TestChecker, Header, StorageProof, u32) {
// prepare remote client
let remote_client = test_client::new();
let remote_block_id = BlockId::Number(0);
@@ -606,7 +607,7 @@ pub mod tests {
(local_checker, remote_block_header, remote_read_proof, heap_pages)
}
fn prepare_for_read_child_proof_check() -> (TestChecker, Header, Vec<Vec<u8>>, Vec<u8>) {
fn prepare_for_read_child_proof_check() -> (TestChecker, Header, StorageProof, Vec<u8>) {
use test_client::DefaultTestClientBuilderExt;
use test_client::TestClientBuilderExt;
// prepare remote client
@@ -648,7 +649,7 @@ pub mod tests {
(local_checker, remote_block_header, remote_read_proof, child_value)
}
fn prepare_for_header_proof_check(insert_cht: bool) -> (TestChecker, Hash, Header, Vec<Vec<u8>>) {
fn prepare_for_header_proof_check(insert_cht: bool) -> (TestChecker, Hash, Header, StorageProof) {
// prepare remote client
let remote_client = test_client::new();
let mut local_headers_hashes = Vec::new();
@@ -899,13 +900,13 @@ pub mod tests {
max_block: remote_proof.max_block,
proof: remote_proof.proof.clone(),
roots: vec![(begin - 1, Default::default())].into_iter().collect(),
roots_proof: vec![],
roots_proof: StorageProof::empty(),
}).is_err());
assert!(local_checker.check_changes_proof(&request, ChangesProof {
max_block: remote_proof.max_block,
proof: remote_proof.proof.clone(),
roots: vec![(end + 1, Default::default())].into_iter().collect(),
roots_proof: vec![],
roots_proof: StorageProof::empty(),
}).is_err());
}
@@ -945,7 +946,10 @@ pub mod tests {
Arc::new(DummyBlockchain::new(local_storage)),
local_executor(),
);
assert!(local_checker.check_changes_tries_proof(4, &remote_proof.roots, vec![]).is_err());
let result = local_checker.check_changes_tries_proof(
4, &remote_proof.roots, StorageProof::empty()
);
assert!(result.is_err());
}
#[test]
+2 -2
View File
@@ -18,7 +18,7 @@
#[doc(hidden)]
#[cfg(feature = "std")]
pub use state_machine::OverlayedChanges;
pub use state_machine::{OverlayedChanges, StorageProof};
#[doc(hidden)]
#[cfg(feature = "std")]
pub use primitives::NativeOrEncoded;
@@ -106,7 +106,7 @@ pub trait ApiExt<Block: BlockT> {
/// Extract the recorded proof.
/// This stops the proof recording.
fn extract_proof(&mut self) -> Option<Vec<Vec<u8>>>;
fn extract_proof(&mut self) -> Option<StorageProof>;
}
/// Before calling any runtime api function, the runtime need to be initialized
@@ -34,6 +34,7 @@ network = { package = "substrate-network", path = "../network", features = ["tes
keyring = { package = "substrate-keyring", path = "../keyring" }
test-client = { package = "substrate-test-runtime-client", path = "../test-runtime/client"}
babe_primitives = { package = "substrate-consensus-babe-primitives", path = "../consensus/babe/primitives" }
state_machine = { package = "substrate-state-machine", path = "../state-machine" }
env_logger = "0.7.0"
tokio = "0.1.22"
tempfile = "3.1.0"
@@ -40,7 +40,7 @@ use log::{trace, warn};
use client::{
backend::Backend, blockchain::Backend as BlockchainBackend, CallExecutor, Client,
error::{Error as ClientError, Result as ClientResult},
light::fetcher::{FetchChecker, RemoteCallRequest}, ExecutionStrategy,
light::fetcher::{FetchChecker, RemoteCallRequest, StorageProof}, ExecutionStrategy,
};
use codec::{Encode, Decode};
use grandpa::BlockNumberOps;
@@ -62,7 +62,7 @@ pub trait AuthoritySetForFinalityProver<Block: BlockT>: Send + Sync {
/// Call GrandpaApi::grandpa_authorities at given block.
fn authorities(&self, block: &BlockId<Block>) -> ClientResult<Vec<(AuthorityId, u64)>>;
/// Prove call of GrandpaApi::grandpa_authorities at given block.
fn prove_authorities(&self, block: &BlockId<Block>) -> ClientResult<Vec<Vec<u8>>>;
fn prove_authorities(&self, block: &BlockId<Block>) -> ClientResult<StorageProof>;
}
/// Client-based implementation of AuthoritySetForFinalityProver.
@@ -85,7 +85,7 @@ impl<B, E, Block: BlockT<Hash=H256>, RA> AuthoritySetForFinalityProver<Block> fo
)))
}
fn prove_authorities(&self, block: &BlockId<Block>) -> ClientResult<Vec<Vec<u8>>> {
fn prove_authorities(&self, block: &BlockId<Block>) -> ClientResult<StorageProof> {
self.execution_proof(block, "GrandpaApi_grandpa_authorities",&[]).map(|(_, proof)| proof)
}
}
@@ -97,7 +97,7 @@ pub trait AuthoritySetForFinalityChecker<Block: BlockT>: Send + Sync {
&self,
hash: Block::Hash,
header: Block::Header,
proof: Vec<Vec<u8>>,
proof: StorageProof,
) -> ClientResult<Vec<(AuthorityId, u64)>>;
}
@@ -107,7 +107,7 @@ impl<Block: BlockT> AuthoritySetForFinalityChecker<Block> for Arc<dyn FetchCheck
&self,
hash: Block::Hash,
header: Block::Header,
proof: Vec<Vec<u8>>,
proof: StorageProof,
) -> ClientResult<Vec<(AuthorityId, u64)>> {
let request = RemoteCallRequest {
block: hash,
@@ -207,7 +207,7 @@ struct FinalityProofFragment<Header: HeaderT> {
/// The set of headers in the range (U; F] that we believe are unknown to the caller. Ordered.
pub unknown_headers: Vec<Header>,
/// Optional proof of execution of GRANDPA::authorities().
pub authorities_proof: Option<Vec<Vec<u8>>>,
pub authorities_proof: Option<StorageProof>,
}
/// Proof of finality is the ordered set of finality fragments, where:
@@ -582,13 +582,13 @@ pub(crate) mod tests {
impl<GetAuthorities, ProveAuthorities> AuthoritySetForFinalityProver<Block> for (GetAuthorities, ProveAuthorities)
where
GetAuthorities: Send + Sync + Fn(BlockId<Block>) -> ClientResult<Vec<(AuthorityId, u64)>>,
ProveAuthorities: Send + Sync + Fn(BlockId<Block>) -> ClientResult<Vec<Vec<u8>>>,
ProveAuthorities: Send + Sync + Fn(BlockId<Block>) -> ClientResult<StorageProof>,
{
fn authorities(&self, block: &BlockId<Block>) -> ClientResult<Vec<(AuthorityId, u64)>> {
self.0(*block)
}
fn prove_authorities(&self, block: &BlockId<Block>) -> ClientResult<Vec<Vec<u8>>> {
fn prove_authorities(&self, block: &BlockId<Block>) -> ClientResult<StorageProof> {
self.1(*block)
}
}
@@ -597,13 +597,13 @@ pub(crate) mod tests {
impl<Closure> AuthoritySetForFinalityChecker<Block> for ClosureAuthoritySetForFinalityChecker<Closure>
where
Closure: Send + Sync + Fn(H256, Header, Vec<Vec<u8>>) -> ClientResult<Vec<(AuthorityId, u64)>>,
Closure: Send + Sync + Fn(H256, Header, StorageProof) -> ClientResult<Vec<(AuthorityId, u64)>>,
{
fn check_authorities_proof(
&self,
hash: H256,
header: Header,
proof: Vec<Vec<u8>>,
proof: StorageProof,
) -> ClientResult<Vec<(AuthorityId, u64)>> {
self.0(hash, header, proof)
}
@@ -824,8 +824,8 @@ pub(crate) mod tests {
_ => unreachable!("no other authorities should be fetched: {:?}", block_id),
},
|block_id| match block_id {
BlockId::Number(4) => Ok(vec![vec![40]]),
BlockId::Number(6) => Ok(vec![vec![60]]),
BlockId::Number(4) => Ok(StorageProof::new(vec![vec![40]])),
BlockId::Number(6) => Ok(StorageProof::new(vec![vec![60]])),
_ => unreachable!("no other authorities should be proved: {:?}", block_id),
},
),
@@ -841,14 +841,14 @@ pub(crate) mod tests {
block: header(5).hash(),
justification: just5,
unknown_headers: Vec::new(),
authorities_proof: Some(vec![vec![40]]),
authorities_proof: Some(StorageProof::new(vec![vec![40]])),
},
// last fragment provides justification for #7 && unknown#7
FinalityProofFragment {
block: header(7).hash(),
justification: just7,
unknown_headers: vec![header(7)],
authorities_proof: Some(vec![vec![60]]),
authorities_proof: Some(StorageProof::new(vec![vec![60]])),
},
]);
}
@@ -895,7 +895,7 @@ pub(crate) mod tests {
block: header(4).hash(),
justification: TestJustification(true, vec![7]).encode(),
unknown_headers: vec![header(4)],
authorities_proof: Some(vec![vec![42]]),
authorities_proof: Some(StorageProof::new(vec![vec![42]])),
}, FinalityProofFragment {
block: header(5).hash(),
justification: TestJustification(true, vec![8]).encode(),
@@ -942,7 +942,7 @@ pub(crate) mod tests {
block: header(2).hash(),
justification: TestJustification(true, vec![7]).encode(),
unknown_headers: Vec::new(),
authorities_proof: Some(vec![vec![42]]),
authorities_proof: Some(StorageProof::new(vec![vec![42]])),
}, FinalityProofFragment {
block: header(4).hash(),
justification: TestJustification(true, vec![8]).encode(),
+24 -8
View File
@@ -27,7 +27,7 @@ use tokio::runtime::current_thread;
use keyring::Ed25519Keyring;
use client::{
error::Result,
runtime_api::{Core, RuntimeVersion, ApiExt},
runtime_api::{Core, RuntimeVersion, ApiExt, StorageProof},
LongestChain,
};
use test_client::{self, runtime::BlockNumber};
@@ -40,6 +40,7 @@ use sr_primitives::traits::{ApiRef, ProvideRuntimeApi, Header as HeaderT};
use sr_primitives::generic::{BlockId, DigestItem};
use primitives::{NativeOrEncoded, ExecutionContext, crypto::Public};
use fg_primitives::{GRANDPA_ENGINE_ID, AuthorityId};
use state_machine::{backend::InMemory, prove_read, read_proof_check};
use authorities::AuthoritySet;
use finality_proof::{FinalityProofProvider, AuthoritySetForFinalityProver, AuthoritySetForFinalityChecker};
@@ -258,7 +259,7 @@ impl ApiExt<Block> for RuntimeApi {
unimplemented!("Not required for testing!")
}
fn extract_proof(&mut self) -> Option<Vec<Vec<u8>>> {
fn extract_proof(&mut self) -> Option<StorageProof> {
unimplemented!("Not required for testing!")
}
}
@@ -285,8 +286,14 @@ impl AuthoritySetForFinalityProver<Block> for TestApi {
})
}
fn prove_authorities(&self, block: &BlockId<Block>) -> Result<Vec<Vec<u8>>> {
self.authorities(block).map(|auth| vec![auth.encode()])
fn prove_authorities(&self, block: &BlockId<Block>) -> Result<StorageProof> {
let authorities = self.authorities(block)?;
let backend = <InMemory<Blake2Hasher>>::from(vec![
(None, b"authorities".to_vec(), Some(authorities.encode()))
]);
let proof = prove_read(backend, vec![b"authorities"])
.expect("failure proving read from in-memory storage backend");
Ok(proof)
}
}
@@ -294,11 +301,20 @@ impl AuthoritySetForFinalityChecker<Block> for TestApi {
fn check_authorities_proof(
&self,
_hash: <Block as BlockT>::Hash,
_header: <Block as BlockT>::Header,
proof: Vec<Vec<u8>>,
header: <Block as BlockT>::Header,
proof: StorageProof,
) -> Result<Vec<(AuthorityId, u64)>> {
Decode::decode(&mut &proof[0][..])
.map_err(|_| unreachable!("incorrect value is passed as GRANDPA authorities proof"))
let results = read_proof_check::<Blake2Hasher, _>(
*header.state_root(), proof, vec![b"authorities"]
)
.expect("failure checking read proof for authorities");
let encoded = results.get(&b"authorities"[..])
.expect("returned map must contain all proof keys")
.as_ref()
.expect("authorities in proof is None");
let authorities = Decode::decode(&mut &encoded[..])
.expect("failure decoding authorities read from proof");
Ok(authorities)
}
}
+12 -9
View File
@@ -18,7 +18,7 @@
use client::{self, Client as SubstrateClient, ClientInfo, CallExecutor};
use client::error::Error;
use client::light::fetcher::ChangesProof;
use client::light::fetcher::{ChangesProof, StorageProof};
use consensus::{BlockImport, BlockStatus, Error as ConsensusError};
use sr_primitives::traits::{Block as BlockT, Header as HeaderT};
use sr_primitives::generic::{BlockId};
@@ -46,10 +46,11 @@ pub trait Client<Block: BlockT>: Send + Sync {
fn justification(&self, id: &BlockId<Block>) -> Result<Option<Justification>, Error>;
/// Get block header proof.
fn header_proof(&self, block_number: <Block::Header as HeaderT>::Number) -> Result<(Block::Header, Vec<Vec<u8>>), Error>;
fn header_proof(&self, block_number: <Block::Header as HeaderT>::Number)
-> Result<(Block::Header, StorageProof), Error>;
/// Get storage read execution proof.
fn read_proof(&self, block: &Block::Hash, keys: &[Vec<u8>]) -> Result<Vec<Vec<u8>>, Error>;
fn read_proof(&self, block: &Block::Hash, keys: &[Vec<u8>]) -> Result<StorageProof, Error>;
/// Get child storage read execution proof.
fn read_child_proof(
@@ -57,10 +58,10 @@ pub trait Client<Block: BlockT>: Send + Sync {
block: &Block::Hash,
storage_key: &[u8],
keys: &[Vec<u8>],
) -> Result<Vec<Vec<u8>>, Error>;
) -> Result<StorageProof, Error>;
/// Get method execution proof.
fn execution_proof(&self, block: &Block::Hash, method: &str, data: &[u8]) -> Result<(Vec<u8>, Vec<Vec<u8>>), Error>;
fn execution_proof(&self, block: &Block::Hash, method: &str, data: &[u8]) -> Result<(Vec<u8>, StorageProof), Error>;
/// Get key changes proof.
fn key_changes_proof(
@@ -120,11 +121,13 @@ impl<B, E, Block, RA> Client<Block> for SubstrateClient<B, E, Block, RA> where
(self as &SubstrateClient<B, E, Block, RA>).justification(id)
}
fn header_proof(&self, block_number: <Block::Header as HeaderT>::Number) -> Result<(Block::Header, Vec<Vec<u8>>), Error> {
fn header_proof(&self, block_number: <Block::Header as HeaderT>::Number)
-> Result<(Block::Header, StorageProof), Error>
{
(self as &SubstrateClient<B, E, Block, RA>).header_proof(&BlockId::Number(block_number))
}
fn read_proof(&self, block: &Block::Hash, keys: &[Vec<u8>]) -> Result<Vec<Vec<u8>>, Error> {
fn read_proof(&self, block: &Block::Hash, keys: &[Vec<u8>]) -> Result<StorageProof, Error> {
(self as &SubstrateClient<B, E, Block, RA>).read_proof(&BlockId::Hash(block.clone()), keys)
}
@@ -133,12 +136,12 @@ impl<B, E, Block, RA> Client<Block> for SubstrateClient<B, E, Block, RA> where
block: &Block::Hash,
storage_key: &[u8],
keys: &[Vec<u8>],
) -> Result<Vec<Vec<u8>>, Error> {
) -> Result<StorageProof, Error> {
(self as &SubstrateClient<B, E, Block, RA>)
.read_child_proof(&BlockId::Hash(block.clone()), storage_key, keys)
}
fn execution_proof(&self, block: &Block::Hash, method: &str, data: &[u8]) -> Result<(Vec<u8>, Vec<Vec<u8>>), Error> {
fn execution_proof(&self, block: &Block::Hash, method: &str, data: &[u8]) -> Result<(Vec<u8>, StorageProof), Error> {
(self as &SubstrateClient<B, E, Block, RA>).execution_proof(&BlockId::Hash(block.clone()), method, data)
}
+6 -6
View File
@@ -48,7 +48,7 @@ use std::sync::Arc;
use std::{cmp, num::NonZeroUsize, time};
use log::{trace, debug, warn, error};
use crate::chain::{Client, FinalityProofProvider};
use client::light::fetcher::{FetchChecker, ChangesProof};
use client::light::fetcher::{FetchChecker, ChangesProof, StorageProof};
use crate::error;
use util::LruHashSet;
@@ -1226,7 +1226,7 @@ impl<B: BlockT, S: NetworkSpecialization<B>, H: ExHashT> Protocol<B, S, H> {
error
);
self.peerset_handle.report_peer(who.clone(), RPC_FAILED_REPUTATION_CHANGE);
Default::default()
StorageProof::empty()
}
};
@@ -1343,7 +1343,7 @@ impl<B: BlockT, S: NetworkSpecialization<B>, H: ExHashT> Protocol<B, S, H> {
request.block,
error
);
Default::default()
StorageProof::empty()
}
};
self.send_message(
@@ -1386,7 +1386,7 @@ impl<B: BlockT, S: NetworkSpecialization<B>, H: ExHashT> Protocol<B, S, H> {
request.block,
error
);
Default::default()
StorageProof::empty()
}
};
self.send_message(
@@ -1426,7 +1426,7 @@ impl<B: BlockT, S: NetworkSpecialization<B>, H: ExHashT> Protocol<B, S, H> {
request.block,
error
);
(Default::default(), Default::default())
(Default::default(), StorageProof::empty())
}
};
self.send_message(
@@ -1495,7 +1495,7 @@ impl<B: BlockT, S: NetworkSpecialization<B>, H: ExHashT> Protocol<B, S, H> {
max_block: Zero::zero(),
proof: vec![],
roots: BTreeMap::new(),
roots_proof: vec![],
roots_proof: StorageProof::empty(),
}
}
};
@@ -28,7 +28,7 @@ use linked_hash_map::{Entry, LinkedHashMap};
use client::error::Error as ClientError;
use client::light::fetcher::{FetchChecker, RemoteHeaderRequest,
RemoteCallRequest, RemoteReadRequest, RemoteChangesRequest, ChangesProof,
RemoteReadChildRequest, RemoteBodyRequest};
RemoteReadChildRequest, RemoteBodyRequest, StorageProof};
use crate::message::{self, BlockAttributes, Direction, FromBlock, RequestId};
use libp2p::PeerId;
use crate::config::Roles;
@@ -174,7 +174,7 @@ impl<Block: BlockT> FetchChecker<Block> for AlwaysBadChecker {
&self,
_request: &RemoteHeaderRequest<Block::Header>,
_remote_header: Option<Block::Header>,
_remote_proof: Vec<Vec<u8>>
_remote_proof: StorageProof,
) -> Result<Block::Header, ClientError> {
Err(ClientError::Msg("AlwaysBadChecker".into()))
}
@@ -182,7 +182,7 @@ impl<Block: BlockT> FetchChecker<Block> for AlwaysBadChecker {
fn check_read_proof(
&self,
_request: &RemoteReadRequest<Block::Header>,
_remote_proof: Vec<Vec<u8>>
_remote_proof: StorageProof,
) -> Result<HashMap<Vec<u8>,Option<Vec<u8>>>, ClientError> {
Err(ClientError::Msg("AlwaysBadChecker".into()))
}
@@ -190,7 +190,7 @@ impl<Block: BlockT> FetchChecker<Block> for AlwaysBadChecker {
fn check_read_child_proof(
&self,
_request: &RemoteReadChildRequest<Block::Header>,
_remote_proof: Vec<Vec<u8>>
_remote_proof: StorageProof,
) -> Result<HashMap<Vec<u8>, Option<Vec<u8>>>, ClientError> {
Err(ClientError::Msg("AlwaysBadChecker".into()))
}
@@ -198,7 +198,7 @@ impl<Block: BlockT> FetchChecker<Block> for AlwaysBadChecker {
fn check_execution_proof(
&self,
_request: &RemoteCallRequest<Block::Header>,
_remote_proof: Vec<Vec<u8>>
_remote_proof: StorageProof,
) -> Result<Vec<u8>, ClientError> {
Err(ClientError::Msg("AlwaysBadChecker".into()))
}
@@ -684,7 +684,7 @@ pub mod tests {
use crate::config::Roles;
use crate::message::{self, BlockAttributes, Direction, FromBlock, RequestId};
use libp2p::PeerId;
use super::{REQUEST_TIMEOUT, LightDispatch, LightDispatchNetwork, RequestData};
use super::{REQUEST_TIMEOUT, LightDispatch, LightDispatchNetwork, RequestData, StorageProof};
use test_client::runtime::{changes_trie_config, Block, Extrinsic, Header};
struct DummyFetchChecker { ok: bool }
@@ -694,7 +694,7 @@ pub mod tests {
&self,
_request: &RemoteHeaderRequest<Header>,
header: Option<Header>,
_remote_proof: Vec<Vec<u8>>
_remote_proof: StorageProof,
) -> ClientResult<Header> {
match self.ok {
true if header.is_some() => Ok(header.unwrap()),
@@ -705,7 +705,7 @@ pub mod tests {
fn check_read_proof(
&self,
request: &RemoteReadRequest<Header>,
_: Vec<Vec<u8>>,
_: StorageProof,
) -> ClientResult<HashMap<Vec<u8>, Option<Vec<u8>>>> {
match self.ok {
true => Ok(request.keys
@@ -721,7 +721,7 @@ pub mod tests {
fn check_read_child_proof(
&self,
request: &RemoteReadChildRequest<Header>,
_: Vec<Vec<u8>>
_: StorageProof,
) -> ClientResult<HashMap<Vec<u8>, Option<Vec<u8>>>> {
match self.ok {
true => Ok(request.keys
@@ -734,7 +734,7 @@ pub mod tests {
}
}
fn check_execution_proof(&self, _: &RemoteCallRequest<Header>, _: Vec<Vec<u8>>) -> ClientResult<Vec<u8>> {
fn check_execution_proof(&self, _: &RemoteCallRequest<Header>, _: StorageProof) -> ClientResult<Vec<u8>> {
match self.ok {
true => Ok(vec![42]),
false => Err(ClientError::Backend("Test error".into())),
@@ -780,7 +780,7 @@ pub mod tests {
) {
light_dispatch.on_remote_call_response(network_interface, peer, message::RemoteCallResponse {
id: id,
proof: vec![vec![2]],
proof: StorageProof::empty(),
});
}
@@ -943,7 +943,7 @@ pub mod tests {
light_dispatch.on_remote_read_response(&mut network_interface, peer0.clone(), message::RemoteReadResponse {
id: 0,
proof: vec![vec![2]],
proof: StorageProof::empty(),
});
assert_disconnected_peer(&network_interface);
assert_eq!(light_dispatch.pending_requests.len(), 1);
@@ -1013,7 +1013,7 @@ pub mod tests {
light_dispatch.on_remote_read_response(&mut network_interface, peer0.clone(), message::RemoteReadResponse {
id: 0,
proof: vec![vec![2]],
proof: StorageProof::empty(),
});
assert_eq!(response.wait().unwrap().unwrap().remove(b":key".as_ref()).unwrap(), Some(vec![42]));
}
@@ -1037,7 +1037,7 @@ pub mod tests {
light_dispatch.on_remote_read_response(&mut network_interface,
peer0.clone(), message::RemoteReadResponse {
id: 0,
proof: vec![vec![2]],
proof: StorageProof::empty(),
});
assert_eq!(response.wait().unwrap().unwrap().remove(b":key".as_ref()).unwrap(), Some(vec![42]));
}
@@ -1065,7 +1065,7 @@ pub mod tests {
extrinsics_root: Default::default(),
digest: Default::default(),
}),
proof: vec![vec![2]],
proof: StorageProof::empty(),
});
assert_eq!(
response.wait().unwrap().unwrap().hash(),
@@ -1097,7 +1097,7 @@ pub mod tests {
max: 1000,
proof: vec![vec![2]],
roots: vec![],
roots_proof: vec![],
roots_proof: StorageProof::empty(),
});
assert_eq!(response.wait().unwrap().unwrap(), vec![(100, 2)]);
}
@@ -1145,7 +1145,7 @@ pub mod tests {
light_dispatch.on_remote_header_response(&mut network_interface, peer1.clone(), message::RemoteHeaderResponse {
id: 0,
header: Some(dummy_header()),
proof: vec![],
proof: StorageProof::empty(),
});
assert!(!light_dispatch.idle_peers.iter().any(|_| true));
@@ -26,6 +26,7 @@ pub use self::generic::{
FinalityProofRequest, FinalityProofResponse,
FromBlock, RemoteReadChildRequest,
};
use client::light::fetcher::StorageProof;
/// A unique ID of a request.
pub type RequestId = u64;
@@ -122,7 +123,7 @@ pub struct RemoteCallResponse {
/// Id of a request this response was made for.
pub id: RequestId,
/// Execution proof.
pub proof: Vec<Vec<u8>>,
pub proof: StorageProof,
}
#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)]
@@ -131,7 +132,7 @@ pub struct RemoteReadResponse {
/// Id of a request this response was made for.
pub id: RequestId,
/// Read proof.
pub proof: Vec<Vec<u8>>,
pub proof: StorageProof,
}
/// Generic types.
@@ -142,7 +143,7 @@ pub mod generic {
use super::{
RemoteReadResponse, Transactions, Direction,
RequestId, BlockAttributes, RemoteCallResponse, ConsensusEngineId,
BlockState,
BlockState, StorageProof,
};
/// Consensus is mostly opaque to us
#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)]
@@ -359,7 +360,7 @@ pub mod generic {
/// Header. None if proof generation has failed (e.g. header is unknown).
pub header: Option<Header>,
/// Header proof.
pub proof: Vec<Vec<u8>>,
pub proof: StorageProof,
}
#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)]
@@ -395,7 +396,7 @@ pub mod generic {
/// Changes tries roots missing on the requester' node.
pub roots: Vec<(N, H)>,
/// Missing changes tries roots proof.
pub roots_proof: Vec<Vec<u8>>,
pub roots_proof: StorageProof,
}
#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)]
@@ -300,15 +300,17 @@ fn generate_runtime_api_base_structures(impls: &[ItemImpl]) -> Result<TokenStrea
self.recorder = Some(Default::default());
}
fn extract_proof(&mut self) -> Option<Vec<Vec<u8>>> {
fn extract_proof(&mut self) -> Option<#crate_::runtime_api::StorageProof> {
self.recorder
.take()
.map(|r| {
r.borrow_mut()
.map(|recorder| {
let trie_nodes = recorder
.borrow_mut()
.drain()
.into_iter()
.map(|n| n.data.to_vec())
.collect()
.map(|record| record.data)
.collect();
#crate_::runtime_api::StorageProof::new(trie_nodes)
})
}
}
+11 -11
View File
@@ -59,8 +59,8 @@ pub use changes_trie::{
};
pub use overlayed_changes::OverlayedChanges;
pub use proving_backend::{
create_proof_check_backend, create_proof_check_backend_storage,
Recorder as ProofRecorder, ProvingBackend,
create_proof_check_backend, create_proof_check_backend_storage, merge_storage_proofs,
Recorder as ProofRecorder, ProvingBackend, StorageProof,
};
pub use trie_backend_essence::{TrieBackendStorage, Storage};
pub use trie_backend::TrieBackend;
@@ -463,7 +463,7 @@ pub fn prove_execution<B, H, Exec>(
method: &str,
call_data: &[u8],
keystore: Option<KeystoreExt>,
) -> Result<(Vec<u8>, Vec<Vec<u8>>), Box<dyn Error>>
) -> Result<(Vec<u8>, StorageProof), Box<dyn Error>>
where
B: Backend<H>,
H: Hasher<Out=H256>,
@@ -490,7 +490,7 @@ pub fn prove_execution_on_trie_backend<S, H, Exec>(
method: &str,
call_data: &[u8],
keystore: Option<KeystoreExt>,
) -> Result<(Vec<u8>, Vec<Vec<u8>>), Box<dyn Error>>
) -> Result<(Vec<u8>, StorageProof), Box<dyn Error>>
where
S: trie_backend_essence::TrieBackendStorage<H>,
H: Hasher<Out=H256>,
@@ -513,7 +513,7 @@ where
/// Check execution proof, generated by `prove_execution` call.
pub fn execution_proof_check<H, Exec>(
root: H::Out,
proof: Vec<Vec<u8>>,
proof: StorageProof,
overlay: &mut OverlayedChanges,
exec: &Exec,
method: &str,
@@ -557,7 +557,7 @@ where
pub fn prove_read<B, H, I>(
mut backend: B,
keys: I,
) -> Result<Vec<Vec<u8>>, Box<dyn Error>>
) -> Result<StorageProof, Box<dyn Error>>
where
B: Backend<H>,
H: Hasher<Out=H256>,
@@ -577,7 +577,7 @@ pub fn prove_child_read<B, H, I>(
mut backend: B,
storage_key: &[u8],
keys: I,
) -> Result<Vec<Vec<u8>>, Box<dyn Error>>
) -> Result<StorageProof, Box<dyn Error>>
where
B: Backend<H>,
H: Hasher,
@@ -594,7 +594,7 @@ where
pub fn prove_read_on_trie_backend<S, H, I>(
trie_backend: &TrieBackend<S, H>,
keys: I,
) -> Result<Vec<Vec<u8>>, Box<dyn Error>>
) -> Result<StorageProof, Box<dyn Error>>
where
S: trie_backend_essence::TrieBackendStorage<H>,
H: Hasher,
@@ -616,7 +616,7 @@ pub fn prove_child_read_on_trie_backend<S, H, I>(
trie_backend: &TrieBackend<S, H>,
storage_key: &[u8],
keys: I,
) -> Result<Vec<Vec<u8>>, Box<dyn Error>>
) -> Result<StorageProof, Box<dyn Error>>
where
S: trie_backend_essence::TrieBackendStorage<H>,
H: Hasher,
@@ -636,7 +636,7 @@ where
/// Check storage read proof, generated by `prove_read` call.
pub fn read_proof_check<H, I>(
root: H::Out,
proof: Vec<Vec<u8>>,
proof: StorageProof,
keys: I,
) -> Result<HashMap<Vec<u8>, Option<Vec<u8>>>, Box<dyn Error>>
where
@@ -657,7 +657,7 @@ where
/// Check child storage read proof, generated by `prove_child_read` call.
pub fn read_child_proof_check<H, I>(
root: H::Out,
proof: Vec<Vec<u8>>,
proof: StorageProof,
storage_key: &[u8],
keys: I,
) -> Result<HashMap<Vec<u8>, Option<Vec<u8>>>, Box<dyn Error>>
@@ -16,7 +16,8 @@
//! Proving state machine backend.
use std::{cell::RefCell, rc::Rc};
use std::{cell::RefCell, collections::HashSet, rc::Rc};
use codec::{Decode, Encode};
use log::debug;
use hash_db::{Hasher, HashDB, EMPTY_PREFIX};
use trie::{
@@ -29,6 +30,82 @@ use crate::trie_backend::TrieBackend;
use crate::trie_backend_essence::{Ephemeral, TrieBackendEssence, TrieBackendStorage};
use crate::{Error, ExecutionError, Backend};
/// A proof that some set of key-value pairs are included in the storage trie. The proof contains
/// the storage values so that the partial storage backend can be reconstructed by a verifier that
/// does not already have access to the key-value pairs.
///
/// The proof consists of the set of serialized nodes in the storage trie accessed when looking up
/// the keys covered by the proof. Verifying the proof requires constructing the partial trie from
/// the serialized nodes and performing the key lookups.
#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)]
pub struct StorageProof {
trie_nodes: Vec<Vec<u8>>,
}
impl StorageProof {
/// Constructs a storage proof from a subset of encoded trie nodes in a storage backend.
pub fn new(trie_nodes: Vec<Vec<u8>>) -> Self {
StorageProof { trie_nodes }
}
/// Returns a new empty proof.
///
/// An empty proof is capable of only proving trivial statements (ie. that an empty set of
/// key-value pairs exist in storage).
pub fn empty() -> Self {
StorageProof {
trie_nodes: Vec::new(),
}
}
/// Returns whether this is an empty proof.
pub fn is_empty(&self) -> bool {
self.trie_nodes.is_empty()
}
/// Create an iterator over trie nodes constructed from the proof. The nodes are not guaranteed
/// to be traversed in any particular order.
pub fn iter_nodes(self) -> StorageProofNodeIterator {
StorageProofNodeIterator::new(self)
}
}
/// An iterator over trie nodes constructed from a storage proof. The nodes are not guaranteed to
/// be traversed in any particular order.
pub struct StorageProofNodeIterator {
inner: <Vec<Vec<u8>> as IntoIterator>::IntoIter,
}
impl StorageProofNodeIterator {
fn new(proof: StorageProof) -> Self {
StorageProofNodeIterator {
inner: proof.trie_nodes.into_iter(),
}
}
}
impl Iterator for StorageProofNodeIterator {
type Item = Vec<u8>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
/// Merges multiple storage proofs covering potentially different sets of keys into one proof
/// covering all keys. The merged proof output may be smaller than the aggregate size of the input
/// proofs due to deduplication of trie nodes.
pub fn merge_storage_proofs<I>(proofs: I) -> StorageProof
where I: IntoIterator<Item=StorageProof>
{
let trie_nodes = proofs.into_iter()
.flat_map(|proof| proof.iter_nodes())
.collect::<HashSet<_>>()
.into_iter()
.collect();
StorageProof { trie_nodes }
}
/// Patricia trie-based backend essence which also tracks all touched storage trie values.
/// These can be sent to remote node and used as a proof of execution.
pub struct ProvingBackendEssence<'a, S: 'a + TrieBackendStorage<H>, H: 'a + Hasher> {
@@ -129,13 +206,14 @@ impl<'a, S: 'a + TrieBackendStorage<H>, H: 'a + Hasher> ProvingBackend<'a, S, H>
}
/// Consume the backend, extracting the gathered proof in lexicographical order by value.
pub fn extract_proof(&self) -> Vec<Vec<u8>> {
self.proof_recorder
pub fn extract_proof(&self) -> StorageProof {
let trie_nodes = self.proof_recorder
.borrow_mut()
.drain()
.into_iter()
.map(|n| n.data.to_vec())
.collect()
.map(|record| record.data)
.collect();
StorageProof::new(trie_nodes)
}
}
@@ -217,7 +295,7 @@ impl<'a, S, H> Backend<H> for ProvingBackend<'a, S, H>
/// Create proof check backend.
pub fn create_proof_check_backend<H>(
root: H::Out,
proof: Vec<Vec<u8>>
proof: StorageProof,
) -> Result<TrieBackend<MemoryDB<H>, H>, Box<dyn Error>>
where
H: Hasher,
@@ -233,13 +311,13 @@ where
/// Create in-memory storage of proof check backend.
pub fn create_proof_check_backend_storage<H>(
proof: Vec<Vec<u8>>
proof: StorageProof,
) -> MemoryDB<H>
where
H: Hasher,
{
let mut db = MemoryDB::default();
for item in proof {
for item in proof.iter_nodes() {
db.insert(EMPTY_PREFIX, &item);
}
db
@@ -275,7 +353,11 @@ mod tests {
#[test]
fn proof_is_invalid_when_does_not_contains_root() {
use primitives::H256;
assert!(create_proof_check_backend::<Blake2Hasher>(H256::from_low_u64_be(1), vec![]).is_err());
let result = create_proof_check_backend::<Blake2Hasher>(
H256::from_low_u64_be(1),
StorageProof::empty()
);
assert!(result.is_err());
}
#[test]