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

Commit 7599a68

Browse files
committed
TrieBasedBackend
1 parent 6ba747c commit 7599a68

File tree

10 files changed

+577
-200
lines changed

10 files changed

+577
-200
lines changed

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

substrate/client/db/src/lib.rs

Lines changed: 9 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,11 @@
1717
//! Client backend that uses RocksDB database as storage. State is still kept in memory.
1818
1919
extern crate substrate_client as client;
20-
extern crate ethereum_types;
2120
extern crate kvdb_rocksdb;
2221
extern crate kvdb;
2322
extern crate hashdb;
2423
extern crate memorydb;
2524
extern crate parking_lot;
26-
extern crate patricia_trie;
2725
extern crate substrate_state_machine as state_machine;
2826
extern crate substrate_primitives as primitives;
2927
extern crate substrate_runtime_support as runtime_support;
@@ -37,21 +35,20 @@ extern crate kvdb_memorydb;
3735

3836
use std::sync::Arc;
3937
use std::path::PathBuf;
40-
use std::collections::HashMap;
4138

4239
use codec::Slicable;
43-
use ethereum_types::H256 as TrieH256;
44-
use hashdb::{DBValue, HashDB};
40+
use hashdb::DBValue;
4541
use kvdb_rocksdb::{Database, DatabaseConfig};
4642
use kvdb::{KeyValueDB, DBTransaction};
4743
use memorydb::MemoryDB;
4844
use parking_lot::RwLock;
49-
use patricia_trie::{TrieDB, TrieDBMut, TrieError, Trie, TrieMut};
5045
use primitives::blake2_256;
5146
use primitives::block::{self, Id as BlockId, HeaderHash};
5247
use runtime_support::Hashable;
53-
use state_machine::backend::Backend as StateBackend;
54-
use state_machine::CodeExecutor;
48+
use state_machine::{CodeExecutor, Backend as StateBackend};
49+
50+
/// DB-backed patricia trie state, transaction type is an overlay of changes to commit.
51+
pub type DbState = state_machine::TrieBackend;
5552

5653
/// Database settings.
5754
pub struct DatabaseSettings {
@@ -294,133 +291,6 @@ impl client::backend::BlockImportOperation for BlockImportOperation {
294291
}
295292
}
296293

297-
struct Ephemeral<'a> {
298-
backing: &'a KeyValueDB,
299-
overlay: &'a mut MemoryDB,
300-
}
301-
302-
impl<'a> HashDB for Ephemeral<'a> {
303-
fn keys(&self) -> HashMap<TrieH256, i32> {
304-
self.overlay.keys() // TODO: iterate backing
305-
}
306-
307-
fn get(&self, key: &TrieH256) -> Option<DBValue> {
308-
match self.overlay.raw(key) {
309-
Some((val, i)) => {
310-
if i <= 0 {
311-
None
312-
} else {
313-
Some(val)
314-
}
315-
}
316-
None => {
317-
match self.backing.get(::columns::STATE, &key.0[..]) {
318-
Ok(x) => x,
319-
Err(e) => {
320-
warn!("Failed to read from DB: {}", e);
321-
None
322-
}
323-
}
324-
}
325-
}
326-
}
327-
328-
fn contains(&self, key: &TrieH256) -> bool {
329-
self.get(key).is_some()
330-
}
331-
332-
fn insert(&mut self, value: &[u8]) -> TrieH256 {
333-
self.overlay.insert(value)
334-
}
335-
336-
fn emplace(&mut self, key: TrieH256, value: DBValue) {
337-
self.overlay.emplace(key, value)
338-
}
339-
340-
fn remove(&mut self, key: &TrieH256) {
341-
self.overlay.remove(key)
342-
}
343-
}
344-
345-
/// DB-backed patricia trie state, transaction type is an overlay of changes to commit.
346-
#[derive(Clone)]
347-
pub struct DbState {
348-
db: Arc<KeyValueDB>,
349-
root: TrieH256,
350-
}
351-
352-
impl state_machine::Backend for DbState {
353-
type Error = client::error::Error;
354-
type Transaction = MemoryDB;
355-
356-
fn storage(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
357-
let mut read_overlay = MemoryDB::default();
358-
let eph = Ephemeral {
359-
backing: &*self.db,
360-
overlay: &mut read_overlay,
361-
};
362-
363-
let map_e = |e: Box<TrieError>| ::client::error::Error::from(format!("Trie lookup error: {}", e));
364-
365-
TrieDB::new(&eph, &self.root).map_err(map_e)?
366-
.get(key).map(|x| x.map(|val| val.to_vec())).map_err(map_e)
367-
}
368-
369-
fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
370-
let mut read_overlay = MemoryDB::default();
371-
let eph = Ephemeral {
372-
backing: &*self.db,
373-
overlay: &mut read_overlay,
374-
};
375-
376-
let collect_all = || -> Result<_, Box<TrieError>> {
377-
let trie = TrieDB::new(&eph, &self.root)?;
378-
let mut v = Vec::new();
379-
for x in trie.iter()? {
380-
let (key, value) = x?;
381-
v.push((key.to_vec(), value.to_vec()));
382-
}
383-
384-
Ok(v)
385-
};
386-
387-
match collect_all() {
388-
Ok(v) => v,
389-
Err(e) => {
390-
debug!("Error extracting trie values: {}", e);
391-
Vec::new()
392-
}
393-
}
394-
}
395-
396-
fn storage_root<I>(&self, delta: I) -> ([u8; 32], MemoryDB)
397-
where I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>
398-
{
399-
let mut write_overlay = MemoryDB::default();
400-
let mut root = self.root;
401-
{
402-
let mut eph = Ephemeral {
403-
backing: &*self.db,
404-
overlay: &mut write_overlay,
405-
};
406-
407-
let mut trie = TrieDBMut::from_existing(&mut eph, &mut root).expect("prior state root to exist"); // TODO: handle gracefully
408-
for (key, change) in delta {
409-
let result = match change {
410-
Some(val) => trie.insert(&key, &val),
411-
None => trie.remove(&key), // TODO: archive mode
412-
};
413-
414-
if let Err(e) = result {
415-
warn!("Failed to write to trie: {}", e);
416-
}
417-
}
418-
}
419-
420-
(root.0.into(), write_overlay)
421-
}
422-
}
423-
424294
/// Disk backend. Keeps data in a key-value store. In archive mode, trie nodes are kept from all blocks.
425295
/// Otherwise, trie nodes are kept only from the most recent block.
426296
pub struct Backend {
@@ -513,24 +383,13 @@ impl client::backend::Backend for Backend {
513383

514384
// special case for genesis initialization
515385
match block {
516-
BlockId::Hash(h) if h == Default::default() => {
517-
let mut root = TrieH256::default();
518-
let mut db = MemoryDB::default();
519-
TrieDBMut::new(&mut db, &mut root);
520-
521-
return Ok(DbState {
522-
db: self.db.clone(),
523-
root,
524-
})
525-
}
386+
BlockId::Hash(h) if h == Default::default() =>
387+
return Ok(DbState::with_kvdb_for_genesis(self.db.clone(), ::columns::STATE)),
526388
_ => {}
527389
}
528390

529391
self.blockchain.header(block).and_then(|maybe_hdr| maybe_hdr.map(|hdr| {
530-
DbState {
531-
db: self.db.clone(),
532-
root: hdr.state_root.0.into(),
533-
}
392+
DbState::with_kvdb(self.db.clone(), ::columns::STATE, hdr.state_root.0.into())
534393
}).ok_or_else(|| client::error::ErrorKind::UnknownBlock(block).into()))
535394
}
536395
}
@@ -539,6 +398,7 @@ impl client::backend::LocalBackend for Backend {}
539398

540399
#[cfg(test)]
541400
mod tests {
401+
use hashdb::HashDB;
542402
use super::*;
543403
use client::backend::Backend as BTrait;
544404
use client::backend::BlockImportOperation as Op;

substrate/client/src/call_executor.rs

Lines changed: 61 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,9 @@
1616

1717
use std::sync::Arc;
1818
use futures::{IntoFuture, Future};
19+
use primitives::Hash;
1920
use primitives::block::Id as BlockId;
2021
use state_machine::{self, OverlayedChanges, Backend as StateBackend, CodeExecutor};
21-
use state_machine::backend::InMemory as InMemoryStateBackend;
22-
use triehash::trie_root;
2322

2423
use backend;
2524
use blockchain::Backend as ChainBackend;
@@ -49,6 +48,11 @@ pub trait CallExecutor {
4948
///
5049
/// No changes are made.
5150
fn call_at_state<S: state_machine::Backend>(&self, state: &S, overlay: &mut OverlayedChanges, method: &str, call_data: &[u8]) -> Result<(Vec<u8>, S::Transaction), error::Error>;
51+
52+
/// Execute a call to a contract on top of given state, gathering execution proof.
53+
///
54+
/// No changes are made.
55+
fn prove_at_state<S: state_machine::Backend>(&self, state: S, overlay: &mut OverlayedChanges, method: &str, call_data: &[u8]) -> Result<(Vec<u8>, Vec<Vec<u8>>), error::Error>;
5256
}
5357

5458
/// Call executor that executes methods locally, querying all required
@@ -104,6 +108,18 @@ impl<B, E> CallExecutor for LocalCallExecutor<B, E>
104108
call_data,
105109
).map_err(Into::into)
106110
}
111+
112+
fn prove_at_state<S: state_machine::Backend>(&self, state: S, changes: &mut OverlayedChanges, method: &str, call_data: &[u8]) -> Result<(Vec<u8>, Vec<Vec<u8>>), error::Error> {
113+
state_machine::prove(
114+
state,
115+
changes,
116+
&self.executor,
117+
method,
118+
call_data,
119+
)
120+
.map(|(result, proof, _)| (result, proof))
121+
.map_err(Into::into)
122+
}
107123
}
108124

109125
impl<B, F> RemoteCallExecutor<B, F> {
@@ -138,36 +154,40 @@ impl<B, F> CallExecutor for RemoteCallExecutor<B, F>
138154
fn call_at_state<S: state_machine::Backend>(&self, _state: &S, _changes: &mut OverlayedChanges, _method: &str, _call_data: &[u8]) -> error::Result<(Vec<u8>, S::Transaction)> {
139155
Err(error::ErrorKind::NotAvailableOnLightClient.into())
140156
}
157+
158+
fn prove_at_state<S: state_machine::Backend>(&self, _state: S, _changes: &mut OverlayedChanges, _method: &str, _call_data: &[u8]) -> Result<(Vec<u8>, Vec<Vec<u8>>), error::Error> {
159+
Err(error::ErrorKind::NotAvailableOnLightClient.into())
160+
}
141161
}
142162

143-
/// Check remote execution proof.
163+
/// Check remote execution proof using given backend.
144164
pub fn check_execution_proof<B, E>(backend: &B, executor: &E, request: &RemoteCallRequest, remote_proof: (Vec<u8>, Vec<Vec<u8>>)) -> Result<CallResult, error::Error>
145165
where
146166
B: backend::RemoteBackend,
147167
E: CodeExecutor,
148168
error::Error: From<<<B as backend::Backend>::State as StateBackend>::Error>,
149169
{
150-
let (remote_result, remote_proof) = remote_proof;
151-
152-
let remote_state = state_from_execution_proof(remote_proof);
153-
let remote_state_root = trie_root(remote_state.pairs().into_iter()).0;
154-
155170
let local_header = backend.blockchain().header(BlockId::Hash(request.block))?;
156171
let local_header = local_header.ok_or_else(|| error::ErrorKind::UnknownBlock(BlockId::Hash(request.block)))?;
157172
let local_state_root = local_header.state_root;
173+
do_check_execution_proof(local_state_root, executor, request, remote_proof)
174+
}
158175

159-
if remote_state_root != *local_state_root {
160-
return Err(error::ErrorKind::InvalidExecutionProof.into());
161-
}
176+
/// Check remote execution proof using given state root.
177+
fn do_check_execution_proof<E>(local_state_root: Hash, executor: &E, request: &RemoteCallRequest, remote_proof: (Vec<u8>, Vec<Vec<u8>>)) -> Result<CallResult, error::Error>
178+
where
179+
E: CodeExecutor,
180+
{
181+
let (remote_result, remote_proof) = remote_proof;
162182

163183
let mut changes = OverlayedChanges::default();
164-
let (local_result, _) = state_machine::execute(
165-
&remote_state,
184+
let (local_result, _) = state_machine::proof_check(
185+
local_state_root.into(),
186+
remote_proof,
166187
&mut changes,
167188
executor,
168189
&request.method,
169-
&request.call_data,
170-
)?;
190+
&request.call_data)?;
171191

172192
if local_result != remote_result {
173193
return Err(error::ErrorKind::InvalidExecutionProof.into());
@@ -176,28 +196,31 @@ pub fn check_execution_proof<B, E>(backend: &B, executor: &E, request: &RemoteCa
176196
Ok(CallResult { return_data: local_result, changes })
177197
}
178198

179-
/// Convert state to execution proof. Proof is simple the whole state (temporary).
180-
// TODO [light]: this method must be removed after trie-based proofs are landed.
181-
pub fn state_to_execution_proof<B: state_machine::Backend>(state: &B) -> Vec<Vec<u8>> {
182-
state.pairs().into_iter()
183-
.flat_map(|(k, v)| ::std::iter::once(k).chain(::std::iter::once(v)))
184-
.collect()
185-
}
186-
187-
/// Convert execution proof to in-memory state for check. Reverse function for state_to_execution_proof.
188-
// TODO [light]: this method must be removed after trie-based proofs are landed.
189-
fn state_from_execution_proof(proof: Vec<Vec<u8>>) -> InMemoryStateBackend {
190-
let mut changes = Vec::new();
191-
let mut proof_iter = proof.into_iter();
192-
loop {
193-
let key = proof_iter.next();
194-
let value = proof_iter.next();
195-
if let (Some(key), Some(value)) = (key, value) {
196-
changes.push((key, Some(value)));
197-
} else {
198-
break;
199-
}
199+
#[cfg(test)]
200+
mod tests {
201+
use primitives::block::Id as BlockId;
202+
use state_machine::Backend;
203+
use test_client;
204+
use light::RemoteCallRequest;
205+
use super::do_check_execution_proof;
206+
207+
#[test]
208+
fn execution_proof_is_generated_and_checked() {
209+
// prepare remote client
210+
let remote_client = test_client::new();
211+
let remote_block_id = BlockId::Number(0);
212+
let remote_block_storage_root = remote_client.state_at(&remote_block_id)
213+
.unwrap().storage_root(::std::iter::empty()).0;
214+
215+
// 'fetch' execution proof from remote node
216+
let remote_execution_proof = remote_client.execution_proof(&remote_block_id, "authorities", &[]).unwrap();
217+
218+
// check remote execution proof locally
219+
let local_executor = test_client::NativeExecutor::new();
220+
do_check_execution_proof(remote_block_storage_root.into(), &local_executor, &RemoteCallRequest {
221+
block: Default::default(),
222+
method: "authorities".into(),
223+
call_data: vec![],
224+
}, remote_execution_proof).unwrap();
200225
}
201-
202-
InMemoryStateBackend::default().update(changes)
203226
}

substrate/client/src/client.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -226,12 +226,7 @@ impl<B, E> Client<B, E> where
226226
///
227227
/// No changes are made.
228228
pub fn execution_proof(&self, id: &BlockId, method: &str, call_data: &[u8]) -> error::Result<(Vec<u8>, Vec<Vec<u8>>)> {
229-
use call_executor::state_to_execution_proof;
230-
231-
let result = self.executor.call(id, method, call_data);
232-
let result = result?.return_data;
233-
let proof = self.backend.state_at(*id).map(|state| state_to_execution_proof(&state))?;
234-
Ok((result, proof))
229+
self.state_at(id).and_then(|state| self.executor.prove_at_state(state, &mut Default::default(), method, call_data))
235230
}
236231

237232
/// Set up the native execution environment to call into a native runtime code.

0 commit comments

Comments
 (0)