mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 05:21:08 +00:00
Don't include :code by default in storage proofs (#5060)
* Adds test to verify that the runtime currently is always contained in the proof * Start passing the runtime wasm code from the outside * Fix compilation * More build fixes * Make the test work as expected now :) * Last fixes * Fixes benchmarks * Review feedback * Apply suggestions from code review Co-Authored-By: Sergei Pepyakin <sergei@parity.io> * Review feedback * Fix compilation Co-authored-by: Sergei Pepyakin <s.pepyakin@gmail.com>
This commit is contained in:
committed by
GitHub
parent
67837c6233
commit
6ee39261c8
@@ -24,6 +24,7 @@ rustversion = "1.0.0"
|
||||
[dev-dependencies]
|
||||
criterion = "0.3.0"
|
||||
substrate-test-runtime-client = { version = "2.0.0-dev", path = "../../../test-utils/runtime/client" }
|
||||
sp-core = { version = "2.0.0-alpha.1", path = "../../core" }
|
||||
|
||||
[[bench]]
|
||||
name = "bench"
|
||||
|
||||
@@ -164,6 +164,12 @@ fn record_proof_works() {
|
||||
let block_id = BlockId::Number(client.chain_info().best_number);
|
||||
let storage_root = longest_chain.best_chain().unwrap().state_root().clone();
|
||||
|
||||
let runtime_code = sp_core::traits::RuntimeCode {
|
||||
code: client.code_at(&block_id).unwrap(),
|
||||
hash: vec![1],
|
||||
heap_pages: None,
|
||||
};
|
||||
|
||||
let transaction = Transfer {
|
||||
amount: 1000,
|
||||
nonce: 0,
|
||||
@@ -192,5 +198,6 @@ fn record_proof_works() {
|
||||
&executor,
|
||||
"Core_execute_block",
|
||||
&block.encode(),
|
||||
&runtime_code,
|
||||
).expect("Executes block while using the proof backend");
|
||||
}
|
||||
|
||||
@@ -98,6 +98,7 @@ pub trait CodeExecutor: Sized + Send + Sync + CallInWasm + Clone + 'static {
|
||||
>(
|
||||
&self,
|
||||
ext: &mut E,
|
||||
runtime_code: &RuntimeCode,
|
||||
method: &str,
|
||||
data: &[u8],
|
||||
use_native: bool,
|
||||
@@ -105,6 +106,70 @@ pub trait CodeExecutor: Sized + Send + Sync + CallInWasm + Clone + 'static {
|
||||
) -> (Result<crate::NativeOrEncoded<R>, Self::Error>, bool);
|
||||
}
|
||||
|
||||
|
||||
/// The Wasm code of a Substrate runtime.
|
||||
#[derive(Debug, Clone, codec::Encode, codec::Decode)]
|
||||
pub struct RuntimeCode {
|
||||
/// The actual Wasm code as binary blob.
|
||||
pub code: Vec<u8>,
|
||||
/// The optional heap pages this `code` should be executed with.
|
||||
///
|
||||
/// If `None` are given, the default value of the executor will be used.
|
||||
pub heap_pages: Option<u64>,
|
||||
/// The SCALE encoded hash of `code`.
|
||||
///
|
||||
/// The hashing algorithm isn't that important, as long as all runtime
|
||||
/// code instances use the same.
|
||||
pub hash: Vec<u8>,
|
||||
}
|
||||
|
||||
impl PartialEq for RuntimeCode {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.hash == other.hash
|
||||
}
|
||||
}
|
||||
|
||||
impl RuntimeCode {
|
||||
/// Create an `RuntimeCode` instance from the given `Externalities`.
|
||||
///
|
||||
/// Extracts the code and the heap pages using the well known keys.
|
||||
///
|
||||
/// Returns an error if the code could not be found.
|
||||
pub fn from_externalities(ext: &dyn Externalities) -> Result<Self, CodeNotFound> {
|
||||
let code = ext.storage(sp_storage::well_known_keys::CODE).ok_or(CodeNotFound)?;
|
||||
let hash = ext.storage_hash(sp_storage::well_known_keys::CODE).ok_or(CodeNotFound)?;
|
||||
let heap_pages = ext.storage(sp_storage::well_known_keys::HEAP_PAGES)
|
||||
.and_then(|hp| codec::Decode::decode(&mut &hp[..]).ok());
|
||||
|
||||
Ok(Self {
|
||||
code,
|
||||
hash,
|
||||
heap_pages,
|
||||
})
|
||||
}
|
||||
|
||||
/// Create an empty instance.
|
||||
///
|
||||
/// This is only useful for tests that don't want to execute any code.
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
code: Vec::new(),
|
||||
hash: Vec::new(),
|
||||
heap_pages: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Could not find the `:code` in the externalities while initializing the [`RuntimeCode`].
|
||||
#[derive(Debug)]
|
||||
pub struct CodeNotFound;
|
||||
|
||||
impl std::fmt::Display for CodeNotFound {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
||||
write!(f, "the storage entry `:code` doesn't have any code")
|
||||
}
|
||||
}
|
||||
|
||||
/// Something that can call a method in a WASM blob.
|
||||
pub trait CallInWasm: Send + Sync {
|
||||
/// Call the given `method` in the given `wasm_blob` using `call_data` (SCALE encoded arguments)
|
||||
|
||||
@@ -25,7 +25,10 @@ use serde::{Deserialize, Serialize};
|
||||
use sp_std::prelude::*;
|
||||
use sp_core::RuntimeDebug;
|
||||
use crate::codec::{Codec, Encode, Decode};
|
||||
use crate::traits::{self, Member, Block as BlockT, Header as HeaderT, MaybeSerialize, MaybeMallocSizeOf};
|
||||
use crate::traits::{
|
||||
self, Member, Block as BlockT, Header as HeaderT, MaybeSerialize, MaybeMallocSizeOf,
|
||||
NumberFor,
|
||||
};
|
||||
use crate::Justification;
|
||||
|
||||
/// Something to identify a block.
|
||||
@@ -35,9 +38,9 @@ use crate::Justification;
|
||||
#[cfg_attr(feature = "std", serde(deny_unknown_fields))]
|
||||
pub enum BlockId<Block: BlockT> {
|
||||
/// Identify by block header hash.
|
||||
Hash(<<Block as BlockT>::Header as HeaderT>::Hash),
|
||||
Hash(Block::Hash),
|
||||
/// Identify by block number.
|
||||
Number(<<Block as BlockT>::Header as HeaderT>::Number),
|
||||
Number(NumberFor<Block>),
|
||||
}
|
||||
|
||||
impl<Block: BlockT> BlockId<Block> {
|
||||
@@ -47,7 +50,7 @@ impl<Block: BlockT> BlockId<Block> {
|
||||
}
|
||||
|
||||
/// Create a block ID from a number.
|
||||
pub fn number(number: <Block::Header as HeaderT>::Number) -> Self {
|
||||
pub fn number(number: NumberFor<Block>) -> Self {
|
||||
BlockId::Number(number)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
|
||||
use log::warn;
|
||||
use hash_db::Hasher;
|
||||
use codec::Encode;
|
||||
use codec::{Decode, Encode};
|
||||
|
||||
use sp_core::storage::{ChildInfo, OwnedChildInfo};
|
||||
use sp_core::{traits::RuntimeCode, storage::{ChildInfo, OwnedChildInfo, well_known_keys}};
|
||||
use sp_trie::{TrieMut, MemoryDB, trie_types::TrieDBMut};
|
||||
|
||||
use crate::{
|
||||
@@ -359,3 +359,22 @@ pub(crate) fn insert_into_memory_db<H, I>(mdb: &mut MemoryDB<H>, input: I) -> Op
|
||||
|
||||
Some(root)
|
||||
}
|
||||
|
||||
/// Get the runtime code from the given `backend`.
|
||||
///
|
||||
/// Returns an error if the `:code` could not be found.
|
||||
pub fn get_runtime_code<H: Hasher, B: Backend<H>>(backend: &B) -> Result<RuntimeCode, &'static str>
|
||||
where H::Out: Encode,
|
||||
{
|
||||
let code = backend.storage(well_known_keys::CODE)
|
||||
.ok()
|
||||
.flatten()
|
||||
.ok_or("`:code` not found")?;
|
||||
let hash = H::hash(&code).encode();
|
||||
let heap_pages = backend.storage(well_known_keys::HEAP_PAGES)
|
||||
.ok()
|
||||
.flatten()
|
||||
.and_then(|d| Decode::decode(&mut &d[..]).ok());
|
||||
|
||||
Ok(RuntimeCode { code, hash, heap_pages })
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ use log::{warn, trace};
|
||||
use hash_db::Hasher;
|
||||
use codec::{Decode, Encode, Codec};
|
||||
use sp_core::{
|
||||
storage::ChildInfo, NativeOrEncoded, NeverNativeValue,
|
||||
traits::{CodeExecutor, CallInWasmExt}, hexdisplay::HexDisplay,
|
||||
storage::ChildInfo, NativeOrEncoded, NeverNativeValue, hexdisplay::HexDisplay,
|
||||
traits::{CodeExecutor, CallInWasmExt, RuntimeCode},
|
||||
};
|
||||
use overlayed_changes::OverlayedChangeSet;
|
||||
use sp_externalities::Extensions;
|
||||
@@ -42,7 +42,7 @@ mod trie_backend;
|
||||
mod trie_backend_essence;
|
||||
mod stats;
|
||||
|
||||
pub use sp_trie::{trie_types::{Layout, TrieDBMut}, TrieMut, DBValue, MemoryDB};
|
||||
pub use sp_trie::{trie_types::{Layout, TrieDBMut}, StorageProof, TrieMut, DBValue, MemoryDB};
|
||||
pub use testing::TestExternalities;
|
||||
pub use basic::BasicExternalities;
|
||||
pub use ext::Ext;
|
||||
@@ -67,8 +67,7 @@ pub use overlayed_changes::{
|
||||
StorageCollection, ChildStorageCollection,
|
||||
};
|
||||
pub use proving_backend::{
|
||||
create_proof_check_backend, create_proof_check_backend_storage, merge_storage_proofs,
|
||||
ProofRecorder, ProvingBackend, ProvingBackendRecorder, StorageProof,
|
||||
create_proof_check_backend, ProofRecorder, ProvingBackend, ProvingBackendRecorder,
|
||||
};
|
||||
pub use trie_backend_essence::{TrieBackendStorage, Storage};
|
||||
pub use trie_backend::TrieBackend;
|
||||
@@ -191,6 +190,7 @@ pub struct StateMachine<'a, B, H, N, Exec>
|
||||
changes_trie_state: Option<ChangesTrieState<'a, H, N>>,
|
||||
_marker: PhantomData<(H, N)>,
|
||||
storage_transaction_cache: Option<&'a mut StorageTransactionCache<B::Transaction, H, N>>,
|
||||
runtime_code: &'a RuntimeCode,
|
||||
}
|
||||
|
||||
impl<'a, B, H, N, Exec> StateMachine<'a, B, H, N, Exec> where
|
||||
@@ -209,6 +209,7 @@ impl<'a, B, H, N, Exec> StateMachine<'a, B, H, N, Exec> where
|
||||
method: &'a str,
|
||||
call_data: &'a [u8],
|
||||
mut extensions: Extensions,
|
||||
runtime_code: &'a RuntimeCode,
|
||||
) -> Self {
|
||||
extensions.register(CallInWasmExt::new(exec.clone()));
|
||||
|
||||
@@ -222,6 +223,7 @@ impl<'a, B, H, N, Exec> StateMachine<'a, B, H, N, Exec> where
|
||||
changes_trie_state,
|
||||
_marker: PhantomData,
|
||||
storage_transaction_cache: None,
|
||||
runtime_code,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,6 +294,7 @@ impl<'a, B, H, N, Exec> StateMachine<'a, B, H, N, Exec> where
|
||||
|
||||
let (result, was_native) = self.exec.call(
|
||||
&mut ext,
|
||||
self.runtime_code,
|
||||
self.method,
|
||||
self.call_data,
|
||||
use_native,
|
||||
@@ -436,6 +439,7 @@ pub fn prove_execution<B, H, N, Exec>(
|
||||
exec: &Exec,
|
||||
method: &str,
|
||||
call_data: &[u8],
|
||||
runtime_code: &RuntimeCode,
|
||||
) -> Result<(Vec<u8>, StorageProof), Box<dyn Error>>
|
||||
where
|
||||
B: Backend<H>,
|
||||
@@ -446,7 +450,14 @@ where
|
||||
{
|
||||
let trie_backend = backend.as_trie_backend()
|
||||
.ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box<dyn Error>)?;
|
||||
prove_execution_on_trie_backend::<_, _, N, _>(trie_backend, overlay, exec, method, call_data)
|
||||
prove_execution_on_trie_backend::<_, _, N, _>(
|
||||
trie_backend,
|
||||
overlay,
|
||||
exec,
|
||||
method,
|
||||
call_data,
|
||||
runtime_code,
|
||||
)
|
||||
}
|
||||
|
||||
/// Prove execution using the given trie backend, overlayed changes, and call executor.
|
||||
@@ -464,6 +475,7 @@ pub fn prove_execution_on_trie_backend<S, H, N, Exec>(
|
||||
exec: &Exec,
|
||||
method: &str,
|
||||
call_data: &[u8],
|
||||
runtime_code: &RuntimeCode,
|
||||
) -> Result<(Vec<u8>, StorageProof), Box<dyn Error>>
|
||||
where
|
||||
S: trie_backend_essence::TrieBackendStorage<H>,
|
||||
@@ -474,7 +486,14 @@ where
|
||||
{
|
||||
let proving_backend = proving_backend::ProvingBackend::new(trie_backend);
|
||||
let mut sm = StateMachine::<_, H, N, Exec>::new(
|
||||
&proving_backend, None, overlay, exec, method, call_data, Extensions::default(),
|
||||
&proving_backend,
|
||||
None,
|
||||
overlay,
|
||||
exec,
|
||||
method,
|
||||
call_data,
|
||||
Extensions::default(),
|
||||
runtime_code,
|
||||
);
|
||||
|
||||
let result = sm.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>(
|
||||
@@ -493,6 +512,7 @@ pub fn execution_proof_check<H, N, Exec>(
|
||||
exec: &Exec,
|
||||
method: &str,
|
||||
call_data: &[u8],
|
||||
runtime_code: &RuntimeCode,
|
||||
) -> Result<Vec<u8>, Box<dyn Error>>
|
||||
where
|
||||
H: Hasher,
|
||||
@@ -501,7 +521,14 @@ where
|
||||
N: crate::changes_trie::BlockNumber,
|
||||
{
|
||||
let trie_backend = create_proof_check_backend::<H>(root.into(), proof)?;
|
||||
execution_proof_check_on_trie_backend::<_, N, _>(&trie_backend, overlay, exec, method, call_data)
|
||||
execution_proof_check_on_trie_backend::<_, N, _>(
|
||||
&trie_backend,
|
||||
overlay,
|
||||
exec,
|
||||
method,
|
||||
call_data,
|
||||
runtime_code,
|
||||
)
|
||||
}
|
||||
|
||||
/// Check execution proof on proving backend, generated by `prove_execution` call.
|
||||
@@ -511,6 +538,7 @@ pub fn execution_proof_check_on_trie_backend<H, N, Exec>(
|
||||
exec: &Exec,
|
||||
method: &str,
|
||||
call_data: &[u8],
|
||||
runtime_code: &RuntimeCode,
|
||||
) -> Result<Vec<u8>, Box<dyn Error>>
|
||||
where
|
||||
H: Hasher,
|
||||
@@ -519,7 +547,14 @@ where
|
||||
N: crate::changes_trie::BlockNumber,
|
||||
{
|
||||
let mut sm = StateMachine::<_, H, N, Exec>::new(
|
||||
trie_backend, None, overlay, exec, method, call_data, Extensions::default(),
|
||||
trie_backend,
|
||||
None,
|
||||
overlay,
|
||||
exec,
|
||||
method,
|
||||
call_data,
|
||||
Extensions::default(),
|
||||
runtime_code,
|
||||
);
|
||||
|
||||
sm.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>(
|
||||
@@ -692,7 +727,9 @@ mod tests {
|
||||
use super::*;
|
||||
use super::ext::Ext;
|
||||
use super::changes_trie::Configuration as ChangesTrieConfig;
|
||||
use sp_core::{Blake2Hasher, map, traits::Externalities, storage::ChildStorageKey};
|
||||
use sp_core::{
|
||||
Blake2Hasher, map, traits::{Externalities, RuntimeCode}, storage::ChildStorageKey,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct DummyCodeExecutor {
|
||||
@@ -714,6 +751,7 @@ mod tests {
|
||||
>(
|
||||
&self,
|
||||
ext: &mut E,
|
||||
_: &RuntimeCode,
|
||||
_method: &str,
|
||||
_data: &[u8],
|
||||
use_native: bool,
|
||||
@@ -767,6 +805,7 @@ mod tests {
|
||||
fn execute_works() {
|
||||
let backend = trie_backend::tests::test_trie();
|
||||
let mut overlayed_changes = Default::default();
|
||||
let wasm_code = RuntimeCode::empty();
|
||||
|
||||
let mut state_machine = StateMachine::new(
|
||||
&backend,
|
||||
@@ -781,6 +820,7 @@ mod tests {
|
||||
"test",
|
||||
&[],
|
||||
Default::default(),
|
||||
&wasm_code,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
@@ -794,6 +834,7 @@ mod tests {
|
||||
fn execute_works_with_native_else_wasm() {
|
||||
let backend = trie_backend::tests::test_trie();
|
||||
let mut overlayed_changes = Default::default();
|
||||
let wasm_code = RuntimeCode::empty();
|
||||
|
||||
let mut state_machine = StateMachine::new(
|
||||
&backend,
|
||||
@@ -808,6 +849,7 @@ mod tests {
|
||||
"test",
|
||||
&[],
|
||||
Default::default(),
|
||||
&wasm_code,
|
||||
);
|
||||
|
||||
assert_eq!(state_machine.execute(ExecutionStrategy::NativeElseWasm).unwrap(), vec![66]);
|
||||
@@ -818,6 +860,7 @@ mod tests {
|
||||
let mut consensus_failed = false;
|
||||
let backend = trie_backend::tests::test_trie();
|
||||
let mut overlayed_changes = Default::default();
|
||||
let wasm_code = RuntimeCode::empty();
|
||||
|
||||
let mut state_machine = StateMachine::new(
|
||||
&backend,
|
||||
@@ -832,6 +875,7 @@ mod tests {
|
||||
"test",
|
||||
&[],
|
||||
Default::default(),
|
||||
&wasm_code,
|
||||
);
|
||||
|
||||
assert!(
|
||||
@@ -864,6 +908,7 @@ mod tests {
|
||||
&executor,
|
||||
"test",
|
||||
&[],
|
||||
&RuntimeCode::empty(),
|
||||
).unwrap();
|
||||
|
||||
// check proof locally
|
||||
@@ -874,6 +919,7 @@ mod tests {
|
||||
&executor,
|
||||
"test",
|
||||
&[],
|
||||
&RuntimeCode::empty(),
|
||||
).unwrap();
|
||||
|
||||
// check that both results are correct
|
||||
|
||||
@@ -18,19 +18,19 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
use parking_lot::RwLock;
|
||||
use codec::{Decode, Encode, Codec};
|
||||
use codec::{Decode, Codec};
|
||||
use log::debug;
|
||||
use hash_db::{Hasher, HashDB, EMPTY_PREFIX, Prefix};
|
||||
use sp_trie::{
|
||||
MemoryDB, default_child_trie_root, read_trie_value_with, read_child_trie_value_with,
|
||||
record_all_keys
|
||||
record_all_keys, StorageProof,
|
||||
};
|
||||
pub use sp_trie::Recorder;
|
||||
pub use sp_trie::trie_types::{Layout, TrieError};
|
||||
use crate::trie_backend::TrieBackend;
|
||||
use crate::trie_backend_essence::{Ephemeral, TrieBackendEssence, TrieBackendStorage};
|
||||
use crate::{Error, ExecutionError, Backend};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::collections::HashMap;
|
||||
use crate::DBValue;
|
||||
use sp_core::storage::ChildInfo;
|
||||
|
||||
@@ -40,82 +40,6 @@ pub struct ProvingBackendRecorder<'a, S: 'a + TrieBackendStorage<H>, H: 'a + Has
|
||||
pub(crate) proof_recorder: &'a mut Recorder<H::Out>,
|
||||
}
|
||||
|
||||
/// 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 }
|
||||
}
|
||||
|
||||
impl<'a, S, H> ProvingBackendRecorder<'a, S, H>
|
||||
where
|
||||
S: TrieBackendStorage<H>,
|
||||
@@ -222,7 +146,7 @@ impl<'a, S: 'a + TrieBackendStorage<H>, H: 'a + Hasher> ProvingBackend<'a, S, H>
|
||||
let root = essence.root().clone();
|
||||
let recorder = ProofRecorderBackend {
|
||||
backend: essence.backend_storage(),
|
||||
proof_recorder: proof_recorder,
|
||||
proof_recorder,
|
||||
};
|
||||
ProvingBackend(TrieBackend::new(recorder, root))
|
||||
}
|
||||
@@ -370,7 +294,7 @@ where
|
||||
H: Hasher,
|
||||
H::Out: Codec,
|
||||
{
|
||||
let db = create_proof_check_backend_storage(proof);
|
||||
let db = proof.into_memory_db();
|
||||
|
||||
if db.contains(&root, EMPTY_PREFIX) {
|
||||
Ok(TrieBackend::new(db, root))
|
||||
@@ -379,20 +303,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Create in-memory storage of proof check backend.
|
||||
pub fn create_proof_check_backend_storage<H>(
|
||||
proof: StorageProof,
|
||||
) -> MemoryDB<H>
|
||||
where
|
||||
H: Hasher,
|
||||
{
|
||||
let mut db = MemoryDB::default();
|
||||
for item in proof.iter_nodes() {
|
||||
db.insert(EMPTY_PREFIX, &item);
|
||||
}
|
||||
db
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::InMemoryBackend;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
mod error;
|
||||
mod node_header;
|
||||
mod node_codec;
|
||||
mod storage_proof;
|
||||
mod trie_stream;
|
||||
|
||||
use sp_std::boxed::Box;
|
||||
@@ -35,6 +36,7 @@ pub use error::Error;
|
||||
pub use trie_stream::TrieStream;
|
||||
/// The Substrate format implementation of `NodeCodec`.
|
||||
pub use node_codec::NodeCodec;
|
||||
pub use storage_proof::StorageProof;
|
||||
/// Various re-exports from the `trie-db` crate.
|
||||
pub use trie_db::{
|
||||
Trie, TrieMut, DBValue, Recorder, CError, Query, TrieLayout, TrieConfiguration, nibble_ops, TrieDBIterator,
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
// Copyright 2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use sp_std::vec::Vec;
|
||||
use codec::{Encode, Decode};
|
||||
use hash_db::{Hasher, HashDB};
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// Creates a `MemoryDB` from `Self`.
|
||||
pub fn into_memory_db<H: Hasher>(self) -> crate::MemoryDB<H> {
|
||||
self.into()
|
||||
}
|
||||
|
||||
/// 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<I>(proofs: I) -> Self where I: IntoIterator<Item=Self> {
|
||||
let trie_nodes = proofs.into_iter()
|
||||
.flat_map(|proof| proof.iter_nodes())
|
||||
.collect::<sp_std::collections::btree_set::BTreeSet<_>>()
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
Self { trie_nodes }
|
||||
}
|
||||
}
|
||||
|
||||
/// 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()
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Hasher> From<StorageProof> for crate::MemoryDB<H> {
|
||||
fn from(proof: StorageProof) -> Self {
|
||||
let mut db = crate::MemoryDB::default();
|
||||
for item in proof.iter_nodes() {
|
||||
db.insert(crate::EMPTY_PREFIX, &item);
|
||||
}
|
||||
db
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user