Optional storage entries (#75)

* Block builder (substrate)

* Fix wasm build

* Bulid on any block

* Test for block builder.

* Block import tests for client.

* Tidy ups

* Repotted client

* Avoid pointless work

* All backend stuff now manages optional storage.

Also simplified a lot of the backend.

* Native runtime-io now supports empty storage items.

* Finish up the API transition.

* Build fix.

* Fix tests.

* Remerge in changes to client.

* Final fixes.

* Unrevert typos

* Remove accidentally committed change

* Bring back zero copy

* Fix merge.
This commit is contained in:
Gav Wood
2018-02-19 20:05:30 +01:00
committed by Robert Habermeier
parent 1d0ad42230
commit ec9060460c
32 changed files with 856 additions and 737 deletions
+14 -14
View File
@@ -73,9 +73,9 @@ mod tests {
#[test]
fn panic_execution_with_foreign_code_gives_error() {
let one = Keyring::One.to_raw_public();
let mut t = TestExternalities { storage: map![
let mut t: TestExternalities = map![
twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0]
], };
];
let r = Executor::new().call(&mut t, BLOATY_CODE, "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx()));
assert!(r.is_err());
@@ -84,9 +84,9 @@ mod tests {
#[test]
fn panic_execution_with_native_equivalent_code_gives_error() {
let one = Keyring::One.to_raw_public();
let mut t = TestExternalities { storage: map![
let mut t: TestExternalities = map![
twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0]
], };
];
let r = Executor::new().call(&mut t, COMPACT_CODE, "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx()));
assert!(r.is_err());
@@ -97,9 +97,9 @@ mod tests {
let one = Keyring::One.to_raw_public();
let two = Keyring::Two.to_raw_public();
let mut t = TestExternalities { storage: map![
let mut t: TestExternalities = map![
twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0]
], };
];
let r = Executor::new().call(&mut t, COMPACT_CODE, "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx()));
assert!(r.is_ok());
@@ -115,9 +115,9 @@ mod tests {
let one = Keyring::One.to_raw_public();
let two = Keyring::Two.to_raw_public();
let mut t = TestExternalities { storage: map![
let mut t: TestExternalities = map![
twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0]
], };
];
let r = Executor::new().call(&mut t, BLOATY_CODE, "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx()));
assert!(r.is_ok());
@@ -133,7 +133,7 @@ mod tests {
let two = Keyring::Two.to_raw_public();
let three = [3u8; 32];
TestExternalities { storage: map![
map![
twox_128(&0u64.to_keyed_vec(b"sys:old:")).to_vec() => [69u8; 32].encode(),
twox_128(b"gov:apr").to_vec() => vec![].and(&667u32),
twox_128(b"ses:len").to_vec() => vec![].and(&2u64),
@@ -149,7 +149,7 @@ mod tests {
twox_128(b"sta:vac").to_vec() => vec![].and(&3u64),
twox_128(b"sta:era").to_vec() => vec![].and(&0u64),
twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0]
], }
]
}
fn construct_block(number: BlockNumber, parent_hash: Hash, state_root: Hash, txs: Vec<Transaction>) -> (Vec<u8>, Hash) {
@@ -250,9 +250,9 @@ mod tests {
#[test]
fn panic_execution_gives_error() {
let one = Keyring::One.to_raw_public();
let mut t = TestExternalities { storage: map![
let mut t: TestExternalities = map![
twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0]
], };
];
let foreign_code = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm");
let r = WasmExecutor.call(&mut t, &foreign_code[..], "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx()));
@@ -264,9 +264,9 @@ mod tests {
let one = Keyring::One.to_raw_public();
let two = Keyring::Two.to_raw_public();
let mut t = TestExternalities { storage: map![
let mut t: TestExternalities = map![
twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0]
], };
];
let foreign_code = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm");
let r = WasmExecutor.call(&mut t, &foreign_code[..], "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx()));
@@ -157,7 +157,7 @@ mod tests {
let two = Keyring::Two.to_raw_public();
let three = [3u8; 32];
TestExternalities { storage: map![
map![
twox_128(APPROVALS_REQUIRED).to_vec() => vec![].and(&667u32),
twox_128(b"ses:len").to_vec() => vec![].and(&1u64),
twox_128(b"ses:val:len").to_vec() => vec![].and(&3u32),
@@ -171,7 +171,7 @@ mod tests {
twox_128(b"sta:spe").to_vec() => vec![].and(&1u64),
twox_128(b"sta:vac").to_vec() => vec![].and(&3u64),
twox_128(b"sta:era").to_vec() => vec![].and(&1u64)
], }
]
}
#[test]
@@ -83,10 +83,10 @@ mod tests {
use runtime::{consensus, session};
fn simple_setup() -> TestExternalities {
TestExternalities { storage: map![
map![
twox_128(b"ses:val:len").to_vec() => vec![].and(&8u32),
twox_128(b"par:cou").to_vec() => vec![].and(&2u32)
], }
]
}
#[test]
@@ -145,7 +145,7 @@ mod tests {
use runtime::{consensus, session};
fn simple_setup() -> TestExternalities {
TestExternalities { storage: map![
map![
twox_128(SESSION_LENGTH).to_vec() => vec![].and(&2u64),
// the validators (10, 20, ...)
twox_128(b"ses:val:len").to_vec() => vec![].and(&2u32),
@@ -155,7 +155,7 @@ mod tests {
b":auth:len".to_vec() => vec![].and(&2u32),
0u32.to_keyed_vec(b":auth:") => vec![11; 32],
1u32.to_keyed_vec(b":auth:") => vec![21; 32]
], }
]
}
#[test]
@@ -227,7 +227,7 @@ mod tests {
let three = [3u8; 32];
let four = [4u8; 32];
let mut t = TestExternalities { storage: map![
let mut t: TestExternalities = map![
twox_128(b"ses:len").to_vec() => vec![].and(&1u64),
twox_128(b"ses:val:len").to_vec() => vec![].and(&2u32),
twox_128(&0u32.to_keyed_vec(b"ses:val:")).to_vec() => vec![10; 32],
@@ -239,7 +239,7 @@ mod tests {
twox_128(&two.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&20u64),
twox_128(&three.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&30u64),
twox_128(&four.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&40u64)
], };
];
with_externalities(&mut t, || {
assert_eq!(era_length(), 2u64);
@@ -296,10 +296,10 @@ mod tests {
#[test]
fn staking_eras_work() {
let mut t = TestExternalities { storage: map![
let mut t: TestExternalities = map![
twox_128(b"ses:len").to_vec() => vec![].and(&1u64),
twox_128(SESSIONS_PER_ERA).to_vec() => vec![].and(&2u64)
], };
];
with_externalities(&mut t, || {
assert_eq!(era_length(), 2u64);
assert_eq!(sessions_per_era(), 2u64);
@@ -363,9 +363,9 @@ mod tests {
let one = Keyring::One.to_raw_public();
let two = Keyring::Two.to_raw_public();
let mut t = TestExternalities { storage: map![
let mut t: TestExternalities = map![
twox_128(&one.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&42u64)
], };
];
with_externalities(&mut t, || {
assert_eq!(balance(&one), 42);
@@ -378,9 +378,9 @@ mod tests {
let one = Keyring::One.to_raw_public();
let two = Keyring::Two.to_raw_public();
let mut t = TestExternalities { storage: map![
let mut t: TestExternalities = map![
twox_128(&one.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&111u64)
], };
];
with_externalities(&mut t, || {
transfer(&one, &two, 69);
@@ -395,9 +395,9 @@ mod tests {
let one = Keyring::One.to_raw_public();
let two = Keyring::Two.to_raw_public();
let mut t = TestExternalities { storage: map![
let mut t: TestExternalities = map![
twox_128(&one.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&111u64)
], };
];
with_externalities(&mut t, || {
stake(&one);
@@ -247,9 +247,9 @@ mod tests {
let one = Keyring::One.to_raw_public();
let two = Keyring::Two.to_raw_public();
let mut t = TestExternalities { storage: map![
let mut t: TestExternalities = map![
twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0]
], };
];
let tx = UncheckedTransaction {
transaction: Transaction {
@@ -272,7 +272,7 @@ mod tests {
let two = Keyring::Two.to_raw_public();
let three = [3u8; 32];
TestExternalities { storage: map![
map![
twox_128(&0u64.to_keyed_vec(b"sys:old:")).to_vec() => [69u8; 32].encode(),
twox_128(b"gov:apr").to_vec() => vec![].and(&667u32),
twox_128(b"ses:len").to_vec() => vec![].and(&2u64),
@@ -288,7 +288,7 @@ mod tests {
twox_128(b"sta:vac").to_vec() => vec![].and(&3u64),
twox_128(b"sta:era").to_vec() => vec![].and(&0u64),
twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0]
], }
]
}
#[test]
@@ -47,9 +47,9 @@ mod tests {
#[test]
fn timestamp_works() {
let mut t = TestExternalities { storage: map![
let mut t: TestExternalities = map![
twox_128(CURRENT_TIMESTAMP).to_vec() => vec![].and(&42u64)
], };
];
with_externalities(&mut t, || {
assert_eq!(get(), 42);
Binary file not shown.
+1 -1
View File
@@ -31,7 +31,7 @@ pub trait BlockImportOperation {
/// Append block data to the transaction.
fn set_block_data(&mut self, header: block::Header, body: Option<block::Body>, justification: Option<primitives::bft::Justification>, is_new_best: bool) -> error::Result<()>;
/// Inject storage data into the database.
fn set_storage<I: Iterator<Item=(Vec<u8>, Vec<u8>)>>(&mut self, iter: I) -> error::Result<()>;
fn set_storage<I: Iterator<Item=(Vec<u8>, Option<Vec<u8>>)>>(&mut self, changes: I) -> error::Result<()>;
/// Inject storage data into the database.
fn reset_storage<I: Iterator<Item=(Vec<u8>, Vec<u8>)>>(&mut self, iter: I) -> error::Result<()>;
}
+504
View File
@@ -0,0 +1,504 @@
// Copyright 2017 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate 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.
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Substrate Client
use primitives::{self, block, AuthorityId};
use primitives::block::Id as BlockId;
use primitives::storage::{StorageKey, StorageData};
use runtime_support::Hashable;
use codec::{KeyedVec, Slicable};
use state_machine::{self, Ext, OverlayedChanges, Backend as StateBackend, CodeExecutor};
use backend::{self, BlockImportOperation};
use blockchain::{self, Info as ChainInfo, Backend as ChainBackend};
use {error, in_mem, block_builder, runtime_io, bft};
/// Polkadot Client
#[derive(Debug)]
pub struct Client<B, E> where B: backend::Backend {
backend: B,
executor: E,
}
/// Client info
// TODO: split queue info from chain info and amalgamate into single struct.
#[derive(Debug)]
pub struct ClientInfo {
/// Best block hash.
pub chain: ChainInfo,
/// Best block number in the queue.
pub best_queued_number: Option<block::Number>,
/// Best queued block hash.
pub best_queued_hash: Option<block::HeaderHash>,
}
/// Information regarding the result of a call.
pub struct CallResult {
/// The data that was returned from the call.
pub return_data: Vec<u8>,
/// The changes made to the state by the call.
pub changes: OverlayedChanges,
}
/// Block import result.
#[derive(Debug)]
pub enum ImportResult {
/// Added to the import queue.
Queued,
/// Already in the import queue.
AlreadyQueued,
/// Already in the blockchain.
AlreadyInChain,
/// Block or parent is known to be bad.
KnownBad,
/// Block parent is not in the chain.
UnknownParent,
}
/// Block status.
#[derive(Debug, PartialEq, Eq)]
pub enum BlockStatus {
/// Added to the import queue.
Queued,
/// Already in the blockchain.
InChain,
/// Block or parent is known to be bad.
KnownBad,
/// Not in the queue or the blockchain.
Unknown,
}
/// A header paired with a justification which has already been checked.
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct JustifiedHeader {
header: block::Header,
justification: bft::Justification,
}
impl JustifiedHeader {
/// Deconstruct the justified header into parts.
pub fn into_inner(self) -> (block::Header, bft::Justification) {
(self.header, self.justification)
}
}
/// Create an instance of in-memory client.
pub fn new_in_mem<E, F>(
executor: E,
build_genesis: F
) -> error::Result<Client<in_mem::Backend, E>>
where
E: CodeExecutor,
F: FnOnce() -> (block::Header, Vec<(Vec<u8>, Vec<u8>)>)
{
Client::new(in_mem::Backend::new(), executor, build_genesis)
}
impl<B, E> Client<B, E> where
B: backend::Backend,
E: CodeExecutor,
error::Error: From<<<B as backend::Backend>::State as StateBackend>::Error>,
{
/// Creates new Polkadot Client with given blockchain and code executor.
pub fn new<F>(
backend: B,
executor: E,
build_genesis: F
) -> error::Result<Self>
where
F: FnOnce() -> (block::Header, Vec<(Vec<u8>, Vec<u8>)>)
{
if backend.blockchain().header(BlockId::Number(0))?.is_none() {
trace!("Empty database, writing genesis block");
let (genesis_header, genesis_store) = build_genesis();
let mut op = backend.begin_operation(BlockId::Hash(block::HeaderHash::default()))?;
op.reset_storage(genesis_store.into_iter())?;
op.set_block_data(genesis_header, Some(vec![]), None, true)?;
backend.commit_operation(op)?;
}
Ok(Client {
backend,
executor,
})
}
/// Get a reference to the state at a given block.
pub fn state_at(&self, block: &BlockId) -> error::Result<B::State> {
self.backend.state_at(*block)
}
/// Expose backend reference. To be used in tests only
pub fn backend(&self) -> &B {
&self.backend
}
/// Return single storage entry of contract under given address in state in a block of given hash.
pub fn storage(&self, id: &BlockId, key: &StorageKey) -> error::Result<StorageData> {
Ok(StorageData(self.state_at(id)?
.storage(&key.0)?
.ok_or_else(|| error::ErrorKind::NoValueForKey(key.0.clone()))?
.to_vec()))
}
/// Get the code at a given block.
pub fn code_at(&self, id: &BlockId) -> error::Result<Vec<u8>> {
self.storage(id, &StorageKey(b":code".to_vec())).map(|data| data.0)
}
/// Clone a new instance of Executor.
pub fn clone_executor(&self) -> E where E: Clone {
self.executor.clone()
}
/// Get the current set of authorities from storage.
pub fn authorities_at(&self, id: &BlockId) -> error::Result<Vec<AuthorityId>> {
let state = self.state_at(id)?;
(0..u32::decode(&mut state.storage(b":auth:len")?.ok_or(error::ErrorKind::AuthLenEmpty)?).ok_or(error::ErrorKind::AuthLenInvalid)?)
.map(|i| state.storage(&i.to_keyed_vec(b":auth:"))
.map_err(|_| error::ErrorKind::Backend)
.and_then(|v| v.ok_or(error::ErrorKind::AuthEmpty(i)))
.and_then(|mut s| AuthorityId::decode(&mut s).ok_or(error::ErrorKind::AuthInvalid(i)))
.map_err(Into::into)
).collect()
}
/// Execute a call to a contract on top of state in a block of given hash.
///
/// No changes are made.
pub fn call(&self, id: &BlockId, method: &str, call_data: &[u8]) -> error::Result<CallResult> {
let mut changes = OverlayedChanges::default();
let return_data = state_machine::execute(
&self.state_at(id)?,
&mut changes,
&self.executor,
method,
call_data,
)?;
Ok(CallResult { return_data, changes })
}
/// Set up the native execution environment to call into a native runtime code.
pub fn using_environment<F: FnOnce() -> T, T>(
&self, f: F
) -> error::Result<T> {
self.using_environment_at(&BlockId::Number(self.info()?.chain.best_number), &mut Default::default(), f)
}
/// Set up the native execution environment to call into a native runtime code.
pub fn using_environment_at<F: FnOnce() -> T, T>(
&self,
id: &BlockId,
overlay: &mut OverlayedChanges,
f: F
) -> error::Result<T> {
Ok(runtime_io::with_externalities(&mut Ext { backend: &self.state_at(id)?, overlay }, f))
}
/// Create a new block, built on the head of the chain.
pub fn new_block(&self) -> error::Result<block_builder::BlockBuilder<B, E>> where E: Clone {
block_builder::BlockBuilder::new(self)
}
/// Create a new block, built on top of `parent`.
pub fn new_block_at(&self, parent: &BlockId) -> error::Result<block_builder::BlockBuilder<B, E>> where E: Clone {
block_builder::BlockBuilder::at_block(parent, &self)
}
/// Check a header's justification.
pub fn check_justification(
&self,
header: block::Header,
justification: bft::UncheckedJustification,
) -> error::Result<JustifiedHeader> {
let authorities = self.authorities_at(&BlockId::Hash(header.parent_hash))?;
let just = bft::check_justification(&authorities[..], header.parent_hash, justification)
.map_err(|_| error::ErrorKind::BadJustification(BlockId::Hash(header.hash())))?;
Ok(JustifiedHeader {
header,
justification: just,
})
}
/// Queue a block for import.
pub fn import_block(
&self,
header: JustifiedHeader,
body: Option<block::Body>,
) -> error::Result<ImportResult> {
// TODO: import lock
// TODO: validate block
// TODO: import justification.
let (header, justification) = header.into_inner();
match self.backend.blockchain().status(BlockId::Hash(header.parent_hash))? {
blockchain::BlockStatus::InChain => (),
blockchain::BlockStatus::Unknown => return Ok(ImportResult::UnknownParent),
}
let mut transaction = self.backend.begin_operation(BlockId::Hash(header.parent_hash))?;
let mut overlay = OverlayedChanges::default();
state_machine::execute(
transaction.state()?,
&mut overlay,
&self.executor,
"execute_block",
&block::Block { header: header.clone(), transactions: body.clone().unwrap_or_default().clone() }.encode()
)?;
let is_new_best = header.number == self.backend.blockchain().info()?.best_number + 1;
trace!("Imported {}, (#{}), best={}", block::HeaderHash::from(header.blake2_256()), header.number, is_new_best);
transaction.set_block_data(header, body, Some(justification.uncheck().into()), is_new_best)?;
transaction.set_storage(overlay.drain())?;
self.backend.commit_operation(transaction)?;
Ok(ImportResult::Queued)
}
/// Get blockchain info.
pub fn info(&self) -> error::Result<ClientInfo> {
let info = self.backend.blockchain().info().map_err(|e| error::Error::from_blockchain(Box::new(e)))?;
Ok(ClientInfo {
chain: info,
best_queued_hash: None,
best_queued_number: None,
})
}
/// Get block status.
pub fn block_status(&self, id: &BlockId) -> error::Result<BlockStatus> {
// TODO: more efficient implementation
match self.backend.blockchain().header(*id).map_err(|e| error::Error::from_blockchain(Box::new(e)))?.is_some() {
true => Ok(BlockStatus::InChain),
false => Ok(BlockStatus::Unknown),
}
}
/// Get block hash by number.
pub fn block_hash(&self, block_number: block::Number) -> error::Result<Option<block::HeaderHash>> {
self.backend.blockchain().hash(block_number)
}
/// Convert an arbitrary block ID into a block hash.
pub fn block_hash_from_id(&self, id: &BlockId) -> error::Result<Option<block::HeaderHash>> {
match *id {
BlockId::Hash(h) => Ok(Some(h)),
BlockId::Number(n) => self.block_hash(n),
}
}
/// Convert an arbitrary block ID into a block hash.
pub fn block_number_from_id(&self, id: &BlockId) -> error::Result<Option<block::Number>> {
match *id {
BlockId::Hash(_) => Ok(self.header(id)?.map(|h| h.number)),
BlockId::Number(n) => Ok(Some(n)),
}
}
/// Get block header by id.
pub fn header(&self, id: &BlockId) -> error::Result<Option<block::Header>> {
self.backend.blockchain().header(*id)
}
/// Get block body by id.
pub fn body(&self, id: &BlockId) -> error::Result<Option<block::Body>> {
self.backend.blockchain().body(*id)
}
/// Get block justification set by id.
pub fn justification(&self, id: &BlockId) -> error::Result<Option<primitives::bft::Justification>> {
self.backend.blockchain().justification(*id)
}
}
impl<B, E> bft::BlockImport for Client<B, E>
where
B: backend::Backend,
E: state_machine::CodeExecutor,
error::Error: From<<B::State as state_machine::backend::Backend>::Error>
{
fn import_block(&self, block: block::Block, justification: bft::Justification) {
let justified_header = JustifiedHeader {
header: block.header,
justification,
};
let _ = self.import_block(justified_header, Some(block.transactions));
}
}
impl<B, E> bft::Authorities for Client<B, E>
where
B: backend::Backend,
E: state_machine::CodeExecutor,
error::Error: From<<B::State as state_machine::backend::Backend>::Error>
{
fn authorities(&self, at: &BlockId) -> Result<Vec<AuthorityId>, bft::Error> {
self.authorities_at(at).map_err(|_| bft::ErrorKind::StateUnavailable(*at).into())
}
}
#[cfg(test)]
mod tests {
use super::*;
use codec::Slicable;
use keyring::Keyring;
use {primitives, genesis};
use primitives::block::Transaction as PrimitiveTransaction;
use test_runtime::genesismap::{GenesisConfig, additional_storage_with_genesis};
use test_runtime::{UncheckedTransaction, Transaction};
use test_runtime;
native_executor_instance!(Executor, test_runtime::api::dispatch, include_bytes!("../../test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm"));
fn genesis_config() -> GenesisConfig {
GenesisConfig::new_simple(vec![
Keyring::Alice.to_raw_public(),
Keyring::Bob.to_raw_public(),
Keyring::Charlie.to_raw_public()
], 1000)
}
fn prepare_genesis() -> (primitives::block::Header, Vec<(Vec<u8>, Vec<u8>)>) {
let mut storage = genesis_config().genesis_map();
let block = genesis::construct_genesis_block(&storage);
storage.extend(additional_storage_with_genesis(&block));
(primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect())
}
// since we are in the client module we can create falsely justified
// headers.
// TODO: remove this in favor of custom verification pipelines for the
// client
fn justify(header: &block::Header) -> bft::UncheckedJustification {
let hash = header.hash();
let authorities = vec![
Keyring::Alice.into(),
Keyring::Bob.into(),
Keyring::Charlie.into(),
];
bft::UncheckedJustification {
digest: hash,
signatures: authorities.iter().map(|key| {
bft::sign_message(
bft::generic::Message::Commit(1, hash),
key,
header.parent_hash
).signature
}).collect(),
round_number: 1,
}
}
#[test]
fn client_initialises_from_genesis_ok() {
let client = new_in_mem(Executor::new(), prepare_genesis).unwrap();
let genesis_hash = client.block_hash(0).unwrap().unwrap();
assert_eq!(client.using_environment(|| test_runtime::system::latest_block_hash()).unwrap(), genesis_hash);
assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Alice.to_raw_public())).unwrap(), 1000);
assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Ferdie.to_raw_public())).unwrap(), 0);
}
#[test]
fn authorities_call_works() {
let genesis_config = genesis_config();
let prepare_genesis = || {
let mut storage = genesis_config.genesis_map();
let block = genesis::construct_genesis_block(&storage);
storage.extend(additional_storage_with_genesis(&block));
(primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect())
};
let client = new_in_mem(Executor::new(), prepare_genesis).unwrap();
assert_eq!(client.info().unwrap().chain.best_number, 0);
assert_eq!(client.authorities_at(&BlockId::Number(0)).unwrap(), vec![
Keyring::Alice.to_raw_public(),
Keyring::Bob.to_raw_public(),
Keyring::Charlie.to_raw_public()
]);
}
#[test]
fn block_builder_works_with_no_transactions() {
let genesis_config = genesis_config();
let prepare_genesis = || {
let mut storage = genesis_config.genesis_map();
let block = genesis::construct_genesis_block(&storage);
storage.extend(additional_storage_with_genesis(&block));
(primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect())
};
let client = new_in_mem(Executor::new(), prepare_genesis).unwrap();
let builder = client.new_block().unwrap();
let block = builder.bake().unwrap();
let justification = justify(&block.header);
let justified = client.check_justification(block.header, justification).unwrap();
client.import_block(justified, Some(block.transactions)).unwrap();
assert_eq!(client.info().unwrap().chain.best_number, 1);
assert_eq!(client.using_environment(|| test_runtime::system::latest_block_hash()).unwrap(), client.block_hash(1).unwrap().unwrap());
}
trait Signable {
fn signed(self) -> PrimitiveTransaction;
}
impl Signable for Transaction {
fn signed(self) -> PrimitiveTransaction {
let signature = Keyring::from_raw_public(self.from.clone()).unwrap().sign(&self.encode());
PrimitiveTransaction::decode(&mut UncheckedTransaction { signature, tx: self }.encode().as_ref()).unwrap()
}
}
#[test]
fn block_builder_works_with_transactions() {
let genesis_config = GenesisConfig::new_simple(vec![
Keyring::Alice.to_raw_public(),
Keyring::Bob.to_raw_public(),
Keyring::Charlie.to_raw_public()
], 1000);
let prepare_genesis = || {
let mut storage = genesis_config.genesis_map();
let block = genesis::construct_genesis_block(&storage);
storage.extend(additional_storage_with_genesis(&block));
(primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect())
};
let client = new_in_mem(Executor::new(), prepare_genesis).unwrap();
let mut builder = client.new_block().unwrap();
builder.push(Transaction {
from: Keyring::Alice.to_raw_public(),
to: Keyring::Ferdie.to_raw_public(),
amount: 42,
nonce: 0
}.signed()).unwrap();
let block = builder.bake().unwrap();
let justification = justify(&block.header);
let justified = client.check_justification(block.header, justification).unwrap();
client.import_block(justified, Some(block.transactions)).unwrap();
assert_eq!(client.info().unwrap().chain.best_number, 1);
assert!(client.state_at(&BlockId::Number(1)).unwrap() != client.state_at(&BlockId::Number(0)).unwrap());
assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Alice.to_raw_public())).unwrap(), 958);
assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Ferdie.to_raw_public())).unwrap(), 42);
}
}
+25 -6
View File
@@ -18,6 +18,7 @@
use std;
use state_machine;
use primitives::hexdisplay::HexDisplay;
error_chain! {
errors {
@@ -45,23 +46,41 @@ error_chain! {
display("Blockchain: {}", e),
}
/// Bad justification for header.
BadJustification(h: ::primitives::block::Id) {
description("bad justification for header"),
display("bad justification for header: {}", h),
/// Attempt to read storage yet nothing set for that key.
NoValueForKey(key: Vec<u8>) {
description("storage doesn't contain key"),
display("Storage does not contain the key entry: {}", HexDisplay::from(key)),
}
/// Invalid state data.
AuthLen {
AuthLenEmpty {
description("authority count state error"),
display("Current state of blockchain has no authority count value"),
}
/// Invalid state data.
AuthEmpty(i: u32) {
description("authority value state error"),
display("Current state of blockchain has no authority value for index {}", i),
}
/// Invalid state data.
AuthLenInvalid {
description("authority count state error"),
display("Current state of blockchain has invalid authority count value"),
}
/// Invalid state data.
Auth(i: u32) {
AuthInvalid(i: u32) {
description("authority value state error"),
display("Current state of blockchain has invalid authority value for index {}", i),
}
/// Bad justification for header.
BadJustification(h: ::primitives::block::Id) {
description("bad justification for header"),
display("bad justification for header: {}", h),
}
}
}
+14 -1
View File
@@ -114,7 +114,7 @@ mod tests {
}
#[test]
fn construct_genesis_should_work() {
fn construct_genesis_should_work_with_native() {
let mut storage = GenesisConfig::new_simple(
vec![Keyring::One.to_raw_public(), Keyring::Two.to_raw_public()], 1000
).genesis_map();
@@ -133,6 +133,19 @@ mod tests {
"execute_block",
&b1data
).unwrap();
}
#[test]
fn construct_genesis_should_work_with_wasm() {
let mut storage = GenesisConfig::new_simple(
vec![Keyring::One.to_raw_public(), Keyring::Two.to_raw_public()], 1000
).genesis_map();
let block = construct_genesis_block(&storage);
let genesis_hash = block.header.blake2_256().into();
storage.extend(additional_storage_with_genesis(&block).into_iter());
let backend = InMemory::from(storage);
let (b1data, _b1hash) = block1(genesis_hash, &backend);
let mut overlay = OverlayedChanges::default();
let _ = execute(
+1 -1
View File
@@ -180,7 +180,7 @@ impl backend::BlockImportOperation for BlockImportOperation {
Ok(())
}
fn set_storage<I: Iterator<Item=(Vec<u8>, Vec<u8>)>>(&mut self, changes: I) -> error::Result<()> {
fn set_storage<I: Iterator<Item=(Vec<u8>, Option<Vec<u8>>)>>(&mut self, changes: I) -> error::Result<()> {
self.pending_state.commit(changes);
Ok(())
}
+3 -481
View File
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Substrate Client
//! Substrate Client and associated logic.
#![warn(missing_docs)]
@@ -41,485 +41,7 @@ pub mod backend;
pub mod in_mem;
pub mod genesis;
pub mod block_builder;
mod client;
pub use client::{Client, ClientInfo, CallResult, ImportResult, BlockStatus, new_in_mem};
pub use blockchain::Info as ChainInfo;
use primitives::{block, AuthorityId};
use primitives::block::Id as BlockId;
use primitives::storage::{StorageKey, StorageData};
use codec::{KeyedVec, Slicable};
use blockchain::Backend as BlockchainBackend;
use backend::BlockImportOperation;
use state_machine::backend::Backend as StateBackend;
use state_machine::{Ext, OverlayedChanges};
use runtime_support::Hashable;
/// Polkadot Client
#[derive(Debug)]
pub struct Client<B, E> where B: backend::Backend {
backend: B,
executor: E,
}
/// Client info
// TODO: split queue info from chain info and amalgamate into single struct.
#[derive(Debug)]
pub struct ClientInfo {
/// Best block hash.
pub chain: ChainInfo,
/// Best block number in the queue.
pub best_queued_number: Option<block::Number>,
/// Best queued block hash.
pub best_queued_hash: Option<block::HeaderHash>,
}
/// Information regarding the result of a call.
pub struct CallResult {
/// The data that was returned from the call.
pub return_data: Vec<u8>,
/// The changes made to the state by the call.
pub changes: state_machine::OverlayedChanges,
}
/// Block import result.
#[derive(Debug)]
pub enum ImportResult {
/// Added to the import queue.
Queued,
/// Already in the import queue.
AlreadyQueued,
/// Already in the blockchain.
AlreadyInChain,
/// Block or parent is known to be bad.
KnownBad,
/// Block parent is not in the chain.
UnknownParent,
}
/// Block status.
#[derive(Debug, PartialEq, Eq)]
pub enum BlockStatus {
/// Added to the import queue.
Queued,
/// Already in the blockchain.
InChain,
/// Block or parent is known to be bad.
KnownBad,
/// Not in the queue or the blockchain.
Unknown,
}
/// A header paired with a justification which has already been checked.
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct JustifiedHeader {
header: block::Header,
justification: bft::Justification,
}
impl JustifiedHeader {
/// Deconstruct the justified header into parts.
pub fn into_inner(self) -> (block::Header, bft::Justification) {
(self.header, self.justification)
}
}
/// Create an instance of in-memory client.
pub fn new_in_mem<E, F>(
executor: E,
build_genesis: F
) -> error::Result<Client<in_mem::Backend, E>>
where
E: state_machine::CodeExecutor,
F: FnOnce() -> (block::Header, Vec<(Vec<u8>, Vec<u8>)>)
{
Client::new(in_mem::Backend::new(), executor, build_genesis)
}
impl<B, E> Client<B, E> where
B: backend::Backend,
E: state_machine::CodeExecutor,
error::Error: From<<<B as backend::Backend>::State as state_machine::backend::Backend>::Error>,
{
/// Creates new Polkadot Client with given blockchain and code executor.
pub fn new<F>(
backend: B,
executor: E,
build_genesis: F
) -> error::Result<Self>
where
F: FnOnce() -> (block::Header, Vec<(Vec<u8>, Vec<u8>)>)
{
if backend.blockchain().header(BlockId::Number(0))?.is_none() {
trace!("Empty database, writing genesis block");
let (genesis_header, genesis_store) = build_genesis();
let mut op = backend.begin_operation(BlockId::Hash(block::HeaderHash::default()))?;
op.reset_storage(genesis_store.into_iter())?;
op.set_block_data(genesis_header, Some(vec![]), None, true)?;
backend.commit_operation(op)?;
}
Ok(Client {
backend,
executor,
})
}
/// Get a reference to the state at a given block.
pub fn state_at(&self, block: &BlockId) -> error::Result<B::State> {
self.backend.state_at(*block)
}
/// Expose backend reference. To be used in tests only
pub fn backend(&self) -> &B {
&self.backend
}
/// Return single storage entry of contract under given address in state in a block of given hash.
pub fn storage(&self, id: &BlockId, key: &StorageKey) -> error::Result<StorageData> {
Ok(self.state_at(id)?
.storage(&key.0)
.map(|x| StorageData(x.to_vec()))?)
}
/// Get the code at a given block.
pub fn code_at(&self, id: &BlockId) -> error::Result<Vec<u8>> {
self.storage(id, &StorageKey(b":code".to_vec())).map(|data| data.0)
}
/// Clone a new instance of Executor.
pub fn clone_executor(&self) -> E where E: Clone {
self.executor.clone()
}
/// Get the current set of authorities from storage.
pub fn authorities_at(&self, id: &BlockId) -> error::Result<Vec<AuthorityId>> {
let state = self.state_at(id)?;
(0..u32::decode(&mut state.storage(b":auth:len")?).ok_or(error::ErrorKind::AuthLen)?)
.map(|i| state.storage(&i.to_keyed_vec(b":auth:"))
.map_err(|_| error::ErrorKind::Backend)
.and_then(|mut s| AuthorityId::decode(&mut s).ok_or(error::ErrorKind::Auth(i)))
.map_err(Into::into)
).collect()
}
/// Execute a call to a contract on top of state in a block of given hash.
///
/// No changes are made.
pub fn call(&self, id: &BlockId, method: &str, call_data: &[u8]) -> error::Result<CallResult> {
let mut changes = state_machine::OverlayedChanges::default();
let return_data = state_machine::execute(
&self.state_at(id)?,
&mut changes,
&self.executor,
method,
call_data,
)?;
Ok(CallResult { return_data, changes })
}
/// Set up the native execution environment to call into a native runtime code.
pub fn using_environment<F: FnOnce() -> T, T>(
&self, f: F
) -> error::Result<T> {
self.using_environment_at(&BlockId::Number(self.info()?.chain.best_number), &mut Default::default(), f)
}
/// Set up the native execution environment to call into a native runtime code.
pub fn using_environment_at<F: FnOnce() -> T, T>(
&self,
id: &BlockId,
overlay: &mut OverlayedChanges,
f: F
) -> error::Result<T> {
Ok(runtime_io::with_externalities(&mut Ext { backend: &self.state_at(id)?, overlay }, f))
}
/// Create a new block, built on the head of the chain.
pub fn new_block(&self) -> error::Result<block_builder::BlockBuilder<B, E>> where E: Clone {
block_builder::BlockBuilder::new(self)
}
/// Create a new block, built on top of `parent`.
pub fn new_block_at(&self, parent: &BlockId) -> error::Result<block_builder::BlockBuilder<B, E>> where E: Clone {
block_builder::BlockBuilder::at_block(parent, &self)
}
/// Check a header's justification.
pub fn check_justification(
&self,
header: block::Header,
justification: bft::UncheckedJustification,
) -> error::Result<JustifiedHeader> {
let authorities = self.authorities_at(&BlockId::Hash(header.parent_hash))?;
let just = bft::check_justification(&authorities[..], header.parent_hash, justification)
.map_err(|_| error::ErrorKind::BadJustification(BlockId::Hash(header.hash())))?;
Ok(JustifiedHeader {
header,
justification: just,
})
}
/// Queue a block for import.
pub fn import_block(
&self,
header: JustifiedHeader,
body: Option<block::Body>,
) -> error::Result<ImportResult> {
// TODO: import lock
// TODO: validate block
// TODO: import justification.
let (header, justification) = header.into_inner();
match self.backend.blockchain().status(BlockId::Hash(header.parent_hash))? {
blockchain::BlockStatus::InChain => (),
blockchain::BlockStatus::Unknown => return Ok(ImportResult::UnknownParent),
}
let mut transaction = self.backend.begin_operation(BlockId::Hash(header.parent_hash))?;
let mut overlay = OverlayedChanges::default();
state_machine::execute(
transaction.state()?,
&mut overlay,
&self.executor,
"execute_block",
&block::Block { header: header.clone(), transactions: body.clone().unwrap_or_default().clone() }.encode()
)?;
let is_new_best = header.number == self.backend.blockchain().info()?.best_number + 1;
trace!("Imported {}, (#{}), best={}", block::HeaderHash::from(header.blake2_256()), header.number, is_new_best);
transaction.set_block_data(header, body, Some(justification.uncheck().into()), is_new_best)?;
transaction.set_storage(overlay.drain())?;
self.backend.commit_operation(transaction)?;
Ok(ImportResult::Queued)
}
/// Get blockchain info.
pub fn info(&self) -> error::Result<ClientInfo> {
let info = self.backend.blockchain().info().map_err(|e| error::Error::from_blockchain(Box::new(e)))?;
Ok(ClientInfo {
chain: info,
best_queued_hash: None,
best_queued_number: None,
})
}
/// Get block status.
pub fn block_status(&self, id: &BlockId) -> error::Result<BlockStatus> {
// TODO: more efficient implementation
match self.backend.blockchain().header(*id).map_err(|e| error::Error::from_blockchain(Box::new(e)))?.is_some() {
true => Ok(BlockStatus::InChain),
false => Ok(BlockStatus::Unknown),
}
}
/// Get block hash by number.
pub fn block_hash(&self, block_number: block::Number) -> error::Result<Option<block::HeaderHash>> {
self.backend.blockchain().hash(block_number)
}
/// Convert an arbitrary block ID into a block hash.
pub fn block_hash_from_id(&self, id: &BlockId) -> error::Result<Option<block::HeaderHash>> {
match *id {
BlockId::Hash(h) => Ok(Some(h)),
BlockId::Number(n) => self.block_hash(n),
}
}
/// Convert an arbitrary block ID into a block hash.
pub fn block_number_from_id(&self, id: &BlockId) -> error::Result<Option<block::Number>> {
match *id {
BlockId::Hash(_) => Ok(self.header(id)?.map(|h| h.number)),
BlockId::Number(n) => Ok(Some(n)),
}
}
/// Get block header by id.
pub fn header(&self, id: &BlockId) -> error::Result<Option<block::Header>> {
self.backend.blockchain().header(*id)
}
/// Get block body by id.
pub fn body(&self, id: &BlockId) -> error::Result<Option<block::Body>> {
self.backend.blockchain().body(*id)
}
/// Get block justification set by id.
pub fn justification(&self, id: &BlockId) -> error::Result<Option<primitives::bft::Justification>> {
self.backend.blockchain().justification(*id)
}
}
impl<B, E> bft::BlockImport for Client<B, E>
where
B: backend::Backend,
E: state_machine::CodeExecutor,
error::Error: From<<B::State as state_machine::backend::Backend>::Error>
{
fn import_block(&self, block: block::Block, justification: bft::Justification) {
let justified_header = JustifiedHeader {
header: block.header,
justification,
};
let _ = self.import_block(justified_header, Some(block.transactions));
}
}
impl<B, E> bft::Authorities for Client<B, E>
where
B: backend::Backend,
E: state_machine::CodeExecutor,
error::Error: From<<B::State as state_machine::backend::Backend>::Error>
{
fn authorities(&self, at: &BlockId) -> Result<Vec<AuthorityId>, bft::Error> {
self.authorities_at(at).map_err(|_| bft::ErrorKind::StateUnavailable(*at).into())
}
}
#[cfg(test)]
mod tests {
use super::*;
use codec::Slicable;
use keyring::Keyring;
use primitives::block::Transaction as PrimitiveTransaction;
use test_runtime::genesismap::{GenesisConfig, additional_storage_with_genesis};
use test_runtime::{UncheckedTransaction, Transaction};
use test_runtime;
native_executor_instance!(Executor, test_runtime::api::dispatch, include_bytes!("../../test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm"));
fn genesis_config() -> GenesisConfig {
GenesisConfig::new_simple(vec![
Keyring::Alice.to_raw_public(),
Keyring::Bob.to_raw_public(),
Keyring::Charlie.to_raw_public()
], 1000)
}
fn prepare_genesis() -> (primitives::block::Header, Vec<(Vec<u8>, Vec<u8>)>) {
let mut storage = genesis_config().genesis_map();
let block = genesis::construct_genesis_block(&storage);
storage.extend(additional_storage_with_genesis(&block));
(primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect())
}
// since we are in the client module we can create falsely justified
// headers.
// TODO: remove this in favor of custom verification pipelines for the
// client
fn justify(header: &block::Header) -> bft::UncheckedJustification {
let hash = header.hash();
let authorities = vec![
Keyring::Alice.into(),
Keyring::Bob.into(),
Keyring::Charlie.into(),
];
bft::UncheckedJustification {
digest: hash,
signatures: authorities.iter().map(|key| {
bft::sign_message(
bft::generic::Message::Commit(1, hash),
key,
header.parent_hash
).signature
}).collect(),
round_number: 1,
}
}
#[test]
fn client_initialises_from_genesis_ok() {
let client = new_in_mem(Executor::new(), prepare_genesis).unwrap();
let genesis_hash = client.block_hash(0).unwrap().unwrap();
assert_eq!(client.using_environment(|| test_runtime::system::latest_block_hash()).unwrap(), genesis_hash);
assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Alice.to_raw_public())).unwrap(), 1000);
assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Ferdie.to_raw_public())).unwrap(), 0);
}
#[test]
fn authorities_call_works() {
let genesis_config = genesis_config();
let prepare_genesis = || {
let mut storage = genesis_config.genesis_map();
let block = genesis::construct_genesis_block(&storage);
storage.extend(additional_storage_with_genesis(&block));
(primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect())
};
let client = new_in_mem(Executor::new(), prepare_genesis).unwrap();
assert_eq!(client.info().unwrap().chain.best_number, 0);
assert_eq!(client.authorities_at(&BlockId::Number(0)).unwrap(), vec![
Keyring::Alice.to_raw_public(),
Keyring::Bob.to_raw_public(),
Keyring::Charlie.to_raw_public()
]);
}
#[test]
fn block_builder_works_with_no_transactions() {
let genesis_config = genesis_config();
let prepare_genesis = || {
let mut storage = genesis_config.genesis_map();
let block = genesis::construct_genesis_block(&storage);
storage.extend(additional_storage_with_genesis(&block));
(primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect())
};
let client = new_in_mem(Executor::new(), prepare_genesis).unwrap();
let builder = client.new_block().unwrap();
let block = builder.bake().unwrap();
let justification = justify(&block.header);
let justified = client.check_justification(block.header, justification).unwrap();
client.import_block(justified, Some(block.transactions)).unwrap();
assert_eq!(client.info().unwrap().chain.best_number, 1);
assert_eq!(client.using_environment(|| test_runtime::system::latest_block_hash()).unwrap(), client.block_hash(1).unwrap().unwrap());
}
trait Signable {
fn signed(self) -> PrimitiveTransaction;
}
impl Signable for Transaction {
fn signed(self) -> PrimitiveTransaction {
let signature = Keyring::from_raw_public(self.from.clone()).unwrap().sign(&self.encode());
PrimitiveTransaction::decode(&mut UncheckedTransaction { signature, tx: self }.encode().as_ref()).unwrap()
}
}
#[test]
fn block_builder_works_with_transactions() {
let genesis_config = genesis_config();
let prepare_genesis = || {
let mut storage = genesis_config.genesis_map();
let block = genesis::construct_genesis_block(&storage);
storage.extend(additional_storage_with_genesis(&block));
(primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect())
};
let client = new_in_mem(Executor::new(), prepare_genesis).unwrap();
let mut builder = client.new_block().unwrap();
builder.push(Transaction {
from: Keyring::Alice.to_raw_public(),
to: Keyring::Ferdie.to_raw_public(),
amount: 42,
nonce: 0
}.signed()).unwrap();
let block = builder.bake().unwrap();
let justification = justify(&block.header);
let justified = client.check_justification(block.header, justification).unwrap();
client.import_block(justified, Some(block.transactions)).unwrap();
assert_eq!(client.info().unwrap().chain.best_number, 1);
assert!(client.state_at(&BlockId::Number(1)).unwrap() != client.state_at(&BlockId::Number(0)).unwrap());
assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Alice.to_raw_public())).unwrap(), 958);
assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Ferdie.to_raw_public())).unwrap(), 42);
}
}
@@ -171,34 +171,53 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
}
this.ext.set_storage(key, value);
},
ext_clear_storage(key_data: *const u8, key_len: u32) => {
let key = this.memory.get(key_data, key_len as usize).map_err(|_| DummyUserError)?;
if let Some(preimage) = this.hash_lookup.get(&key) {
info!(target: "wasm-trace", "*** Clearing storage: %{} [k={}]", ascii_format(&preimage), HexDisplay::from(&key));
} else {
info!(target: "wasm-trace", "*** Clearing storage: {} [k={}]", ascii_format(&key), HexDisplay::from(&key));
}
this.ext.clear_storage(&key);
},
// return 0 and place u32::max_value() into written_out if no value exists for the key.
ext_get_allocated_storage(key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8 => {
let key = this.memory.get(key_data, key_len as usize).map_err(|_| DummyUserError)?;
let value = this.ext.storage(&key).map_err(|_| DummyUserError)?;
let maybe_value = this.ext.storage(&key);
if let Some(preimage) = this.hash_lookup.get(&key) {
info!(target: "wasm-trace", " Getting storage: %{} == {} [k={}]", ascii_format(&preimage), HexDisplay::from(&value), HexDisplay::from(&key));
info!(target: "wasm-trace", " Getting storage: %{} == {} [k={}]", ascii_format(&preimage), if let Some(ref b) = maybe_value { format!("{}", HexDisplay::from(b)) } else { "<empty>".to_owned() }, HexDisplay::from(&key));
} else {
info!(target: "wasm-trace", " Getting storage: {} == {} [k={}]", ascii_format(&key), HexDisplay::from(&value), HexDisplay::from(&key));
info!(target: "wasm-trace", " Getting storage: {} == {} [k={}]", ascii_format(&key), if let Some(ref b) = maybe_value { format!("{}", HexDisplay::from(b)) } else { "<empty>".to_owned() }, HexDisplay::from(&key));
}
let offset = this.heap.allocate(value.len() as u32) as u32;
this.memory.set(offset, &value).map_err(|_| DummyUserError)?;
this.memory.write_primitive(written_out, value.len() as u32)?;
offset
if let Some(value) = maybe_value {
let offset = this.heap.allocate(value.len() as u32) as u32;
this.memory.set(offset, &value).map_err(|_| DummyUserError)?;
this.memory.write_primitive(written_out, value.len() as u32)?;
offset
} else {
this.memory.write_primitive(written_out, u32::max_value())?;
0
}
},
// return u32::max_value() if no value exists for the key.
ext_get_storage_into(key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32, value_offset: u32) -> u32 => {
let key = this.memory.get(key_data, key_len as usize).map_err(|_| DummyUserError)?;
let value = this.ext.storage(&key).map_err(|_| DummyUserError)?;
let maybe_value = this.ext.storage(&key);
if let Some(preimage) = this.hash_lookup.get(&key) {
info!(target: "wasm-trace", " Getting storage: %{} == {} [k={}]", ascii_format(&preimage), HexDisplay::from(&value), HexDisplay::from(&key));
info!(target: "wasm-trace", " Getting storage: %{} == {} [k={}]", ascii_format(&preimage), if let Some(ref b) = maybe_value { format!("{}", HexDisplay::from(b)) } else { "<empty>".to_owned() }, HexDisplay::from(&key));
} else {
info!(target: "wasm-trace", " Getting storage: {} == {} [k={}]", ascii_format(&key), HexDisplay::from(&value), HexDisplay::from(&key));
info!(target: "wasm-trace", " Getting storage: {} == {} [k={}]", ascii_format(&key), if let Some(ref b) = maybe_value { format!("{}", HexDisplay::from(b)) } else { "<empty>".to_owned() }, HexDisplay::from(&key));
}
if let Some(value) = maybe_value {
let value = &value[value_offset as usize..];
let written = ::std::cmp::min(value_len as usize, value.len());
this.memory.set(value_data, &value[..written]).map_err(|_| DummyUserError)?;
written as u32
} else {
u32::max_value()
}
let value = &value[value_offset as usize..];
let written = ::std::cmp::min(value_len as usize, value.len());
this.memory.set(value_data, &value[..written]).map_err(|_| DummyUserError)?;
written as u32
},
ext_storage_root(result: *mut u8) => {
let r = this.ext.storage_root();
@@ -369,7 +388,7 @@ mod tests {
b"foo".to_vec() => b"bar".to_vec(),
b"baz".to_vec() => b"bar".to_vec()
];
assert_eq!(expected, ext.storage);
assert_eq!(expected, ext);
}
#[test]
+1 -1
View File
@@ -19,7 +19,7 @@ impl_stubs!(
set_storage(b"input", input);
print("storage");
let foo = storage(b"foo");
let foo = storage(b"foo").unwrap();
print("set_storage");
set_storage(b"baz", &foo);
+1 -1
View File
@@ -35,7 +35,7 @@ fn should_return_storage() {
assert_matches!(
StateApi::storage(&client, StorageKey(vec![10]), genesis_hash),
Ok(ref x) if x.0.is_empty()
Err(Error(ErrorKind::Client(client::error::ErrorKind::NoValueForKey(ref k)), _)) if *k == vec![10]
)
}
+32 -32
View File
@@ -27,45 +27,45 @@ pub extern crate substrate_codec as codec;
// re-export hashing functions.
pub use primitives::{blake2_256, twox_128, twox_256};
pub use substrate_state_machine::{Externalities, ExternalitiesError, TestExternalities};
pub use substrate_state_machine::{Externalities, TestExternalities};
use primitives::hexdisplay::HexDisplay;
// TODO: use the real error, not NoError.
environmental!(ext : trait Externalities);
environmental!(ext: trait Externalities);
/// Get `key` from storage and return a `Vec`, empty if there's a problem.
pub fn storage(key: &[u8]) -> Vec<u8> {
ext::with(|ext| ext.storage(key).ok().map(|s| s.to_vec()))
pub fn storage(key: &[u8]) -> Option<Vec<u8>> {
ext::with(|ext| ext.storage(key).map(|s| s.to_vec()))
.expect("read_storage cannot be called outside of an Externalities-provided environment.")
.unwrap_or_else(Vec::new)
}
/// Get `key` from storage, placing the value into `value_out` (as much as possible) and return
/// the number of bytes that the key in storage was beyond the offset.
pub fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> usize {
ext::with(|ext| {
if let Ok(value) = ext.storage(key) {
let value = &value[value_offset..];
let written = ::std::cmp::min(value.len(), value_out.len());
value_out[0..written].copy_from_slice(&value[0..written]);
value.len()
} else {
// no-entry is treated as an empty vector of bytes.
// TODO: consider allowing empty-vector to exist separately to no-entry (i.e. return
// Option<usize>)
0
}
}).expect("read_storage cannot be called outside of an Externalities-provided environment.")
/// the number of bytes that the key in storage was beyond the offset or None if the storage entry
/// doesn't exist at all.
pub fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> Option<usize> {
ext::with(|ext| ext.storage(key).map(|value| {
let value = &value[value_offset..];
let written = ::std::cmp::min(value.len(), value_out.len());
value_out[0..written].copy_from_slice(&value[0..written]);
value.len()
})).expect("read_storage cannot be called outside of an Externalities-provided environment.")
}
/// Set the storage to some particular key.
/// Set the storage of some particular key to Some value.
pub fn set_storage(key: &[u8], value: &[u8]) {
ext::with(|ext|
ext.set_storage(key.to_vec(), value.to_vec())
);
}
/// Clear the storage of some particular key.
pub fn clear_storage(key: &[u8]) {
ext::with(|ext|
ext.clear_storage(key)
);
}
/// The current relay chain identifier.
pub fn chain_id() -> u64 {
ext::with(|ext|
@@ -164,37 +164,37 @@ mod std_tests {
#[test]
fn storage_works() {
let mut t = TestExternalities { storage: map![], };
let mut t = TestExternalities::new();
assert!(with_externalities(&mut t, || {
assert_eq!(storage(b"hello"), b"".to_vec());
assert_eq!(storage(b"hello"), None);
set_storage(b"hello", b"world");
assert_eq!(storage(b"hello"), b"world".to_vec());
assert_eq!(storage(b"foo"), b"".to_vec());
assert_eq!(storage(b"hello"), Some(b"world".to_vec()));
assert_eq!(storage(b"foo"), None);
set_storage(b"foo", &[1, 2, 3][..]);
true
}));
t.storage = map![b"foo".to_vec() => b"bar".to_vec()];
t = map![b"foo".to_vec() => b"bar".to_vec()];
assert!(!with_externalities(&mut t, || {
assert_eq!(storage(b"hello"), b"".to_vec());
assert_eq!(storage(b"foo"), b"bar".to_vec());
assert_eq!(storage(b"hello"), None);
assert_eq!(storage(b"foo"), Some(b"bar".to_vec()));
false
}));
}
#[test]
fn read_storage_works() {
let mut t = TestExternalities { storage: map![
let mut t: TestExternalities = map![
b":test".to_vec() => b"\x0b\0\0\0Hello world".to_vec()
], };
];
with_externalities(&mut t, || {
let mut v = [0u8; 4];
assert!(read_storage(b":test", &mut v[..], 0) >= 4);
assert!(read_storage(b":test", &mut v[..], 0).unwrap() >= 4);
assert_eq!(v, [11u8, 0, 0, 0]);
let mut w = [0u8; 11];
assert!(read_storage(b":test", &mut w[..], 4) >= 11);
assert!(read_storage(b":test", &mut w[..], 4).unwrap() >= 11);
assert_eq!(&w, b"Hello world");
});
}
+22 -5
View File
@@ -43,6 +43,7 @@ extern "C" {
fn ext_print_hex(data: *const u8, len: u32);
fn ext_print_num(value: u64);
fn ext_set_storage(key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32);
fn ext_clear_storage(key_data: *const u8, key_len: u32);
fn ext_get_allocated_storage(key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8;
fn ext_get_storage_into(key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32, value_offset: u32) -> u32;
fn ext_storage_root(result: *mut u8);
@@ -55,11 +56,15 @@ extern "C" {
}
/// Get `key` from storage and return a `Vec`, empty if there's a problem.
pub fn storage(key: &[u8]) -> Vec<u8> {
pub fn storage(key: &[u8]) -> Option<Vec<u8>> {
let mut length: u32 = 0;
unsafe {
let ptr = ext_get_allocated_storage(key.as_ptr(), key.len() as u32, &mut length);
Vec::from_raw_parts(ptr, length as usize, length as usize)
if length == u32::max_value() {
None
} else {
Some(Vec::from_raw_parts(ptr, length as usize, length as usize))
}
}
}
@@ -73,15 +78,27 @@ pub fn set_storage(key: &[u8], value: &[u8]) {
}
}
/// Set the storage to some particular key.
pub fn clear_storage(key: &[u8]) {
unsafe {
ext_clear_storage(
key.as_ptr(), key.len() as u32
);
}
}
/// Get `key` from storage, placing the value into `value_out` (as much as possible) and return
/// the number of bytes that the key in storage was beyond the offset.
pub fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> usize {
pub fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> Option<usize> {
unsafe {
ext_get_storage_into(
match ext_get_storage_into(
key.as_ptr(), key.len() as u32,
value_out.as_mut_ptr(), value_out.len() as u32,
value_offset as u32
) as usize
) {
none if none == u32::max_value() => None,
length => Some(length as usize),
}
}
}
@@ -18,14 +18,34 @@
use rstd::prelude::*;
use runtime_io::{self, twox_128};
use codec::{Slicable, KeyedVec};
use codec::{Slicable, KeyedVec, Input};
// TODO: consider using blake256 to avoid possible preimage attack.
/// Return the value of the item in storage under `key`, or `None` if there is no explicit entry.
struct IncrementalInput<'a> {
key: &'a [u8],
pos: usize,
}
impl<'a> Input for IncrementalInput<'a> {
fn read(&mut self, into: &mut [u8]) -> usize {
let len = runtime_io::read_storage(self.key, into, self.pos).unwrap_or(0);
let read = ::rstd::cmp::min(len, into.len());
self.pos += read;
read
}
}
/// Return the value of the item in storage under `key`, or `None` if there is no explicit entry.
pub fn get<T: Slicable + Sized>(key: &[u8]) -> Option<T> {
let raw = runtime_io::storage(&twox_128(key)[..]);
Slicable::decode(&mut &raw[..])
let key = twox_128(key);
runtime_io::read_storage(&key[..], &mut [0; 0][..], 0).map(|_| {
let mut input = IncrementalInput {
key: &key[..],
pos: 0,
};
Slicable::decode(&mut input).expect("stroage is not null, therefore must be a valid type")
})
}
/// Return the value of the item in storage under `key`, or the type's default if there is no
@@ -85,17 +105,17 @@ pub fn take_or_else<T: Slicable + Sized, F: FnOnce() -> T>(key: &[u8], default_v
/// Check to see if `key` has an explicit entry in storage.
pub fn exists(key: &[u8]) -> bool {
let mut x = [0u8; 1];
runtime_io::read_storage(&twox_128(key)[..], &mut x[..], 0) >= 1
let mut x = [0u8; 0];
runtime_io::read_storage(&twox_128(key)[..], &mut x[..], 0).is_some()
}
/// Ensure `key` has no explicit entry in storage.
pub fn kill(key: &[u8]) {
runtime_io::set_storage(&twox_128(key)[..], b"");
runtime_io::clear_storage(&twox_128(key)[..]);
}
/// Get a Vec of bytes from storage.
pub fn get_raw(key: &[u8]) -> Vec<u8> {
pub fn get_raw(key: &[u8]) -> Option<Vec<u8>> {
runtime_io::storage(&twox_128(key)[..])
}
@@ -127,12 +147,18 @@ pub trait StorageVec {
}
}
fn clear_item(index: u32) {
if index < Self::count() {
kill(&index.to_keyed_vec(Self::PREFIX));
}
}
fn item(index: u32) -> Self::Item {
get_or_default(&index.to_keyed_vec(Self::PREFIX))
}
fn set_count(count: u32) {
(count..Self::count()).for_each(|i| Self::set_item(i, &Self::Item::default()));
(count..Self::count()).for_each(Self::clear_item);
put(&b"len".to_keyed_vec(Self::PREFIX), &count);
}
@@ -142,12 +168,17 @@ pub trait StorageVec {
}
pub mod unhashed {
use super::{runtime_io, Slicable, KeyedVec, Vec};
use super::{runtime_io, Slicable, KeyedVec, Vec, IncrementalInput};
/// Return the value of the item in storage under `key`, or `None` if there is no explicit entry.
pub fn get<T: Slicable + Sized>(key: &[u8]) -> Option<T> {
let raw = runtime_io::storage(key);
T::decode(&mut &raw[..])
runtime_io::read_storage(key, &mut [0; 0][..], 0).map(|_| {
let mut input = IncrementalInput {
key,
pos: 0,
};
Slicable::decode(&mut input).expect("stroage is not null, therefore must be a valid type")
})
}
/// Return the value of the item in storage under `key`, or the type's default if there is no
@@ -207,17 +238,16 @@ pub mod unhashed {
/// Check to see if `key` has an explicit entry in storage.
pub fn exists(key: &[u8]) -> bool {
let mut x = [0u8; 1];
runtime_io::read_storage(key, &mut x[..], 0) >= 1
runtime_io::read_storage(key, &mut [0;0][..], 0).is_some()
}
/// Ensure `key` has no explicit entry in storage.
pub fn kill(key: &[u8]) {
runtime_io::set_storage(key, b"");
runtime_io::clear_storage(key);
}
/// Get a Vec of bytes from storage.
pub fn get_raw(key: &[u8]) -> Vec<u8> {
pub fn get_raw(key: &[u8]) -> Option<Vec<u8>> {
runtime_io::storage(key)
}
@@ -249,12 +279,18 @@ pub mod unhashed {
}
}
fn clear_item(index: u32) {
if index < Self::count() {
kill(&index.to_keyed_vec(Self::PREFIX));
}
}
fn item(index: u32) -> Self::Item {
get_or_default(&index.to_keyed_vec(Self::PREFIX))
}
fn set_count(count: u32) {
(count..Self::count()).for_each(|i| Self::set_item(i, &Self::Item::default()));
(count..Self::count()).for_each(Self::clear_item);
put(&b"len".to_keyed_vec(Self::PREFIX), &count);
}
@@ -267,13 +303,12 @@ pub mod unhashed {
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
use primitives::hexdisplay::HexDisplay;
use runtime_io::{storage, twox_128, TestExternalities, with_externalities};
#[test]
fn integers_can_be_stored() {
let mut t = TestExternalities { storage: HashMap::new(), };
let mut t = TestExternalities::new();
with_externalities(&mut t, || {
let x = 69u32;
put(b":test", &x);
@@ -290,7 +325,7 @@ mod tests {
#[test]
fn bools_can_be_stored() {
let mut t = TestExternalities { storage: HashMap::new(), };
let mut t = TestExternalities::new();
with_externalities(&mut t, || {
let x = true;
put(b":test", &x);
@@ -308,11 +343,11 @@ mod tests {
#[test]
fn vecs_can_be_retrieved() {
let mut t = TestExternalities { storage: HashMap::new(), };
let mut t = TestExternalities::new();
with_externalities(&mut t, || {
runtime_io::set_storage(&twox_128(b":test"), b"\x0b\0\0\0Hello world");
let x = b"Hello world".to_vec();
println!("Hex: {}", HexDisplay::from(&storage(&twox_128(b":test"))));
println!("Hex: {}", HexDisplay::from(&storage(&twox_128(b":test")).unwrap()));
let y = get::<Vec<u8>>(b":test").unwrap();
assert_eq!(x, y);
@@ -321,7 +356,7 @@ mod tests {
#[test]
fn vecs_can_be_stored() {
let mut t = TestExternalities { storage: HashMap::new(), };
let mut t = TestExternalities::new();
let x = b"Hello world".to_vec();
with_externalities(&mut t, || {
@@ -330,7 +365,7 @@ mod tests {
println!("Ext is {:?}", t);
with_externalities(&mut t, || {
println!("Hex: {}", HexDisplay::from(&storage(&twox_128(b":test"))));
println!("Hex: {}", HexDisplay::from(&storage(&twox_128(b":test")).unwrap()));
let y: Vec<u8> = get(b":test").unwrap();
assert_eq!(x, y);
});
@@ -17,11 +17,7 @@
//! State machine backends. These manage the code and storage of contracts.
use std::{error, fmt};
use super::{Update, MemoryState};
/// Output of a commit.
pub struct Committed {}
use std::collections::HashMap;
/// A state backend is used to read state data and can have changes committed
/// to it.
@@ -29,12 +25,12 @@ pub trait Backend {
/// An error type when fetching data is not possible.
type Error: super::Error;
/// Get keyed storage associated with specific address.
fn storage(&self, key: &[u8]) -> Result<&[u8], Self::Error>;
/// Get keyed storage associated with specific address, or None if there is nothing associated.
fn storage(&self, key: &[u8]) -> Result<Option<&[u8]>, Self::Error>;
/// Commit updates to the backend and get new state.
fn commit<I>(&mut self, changes: I) -> Committed
where I: IntoIterator<Item=Update>;
fn commit<I>(&mut self, changes: I)
where I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>;
/// Get all key/value pairs into a Vec.
fn pairs(&self) -> Vec<(&[u8], &[u8])>;
@@ -57,38 +53,28 @@ impl error::Error for Void {
/// In-memory backend. Fully recomputes tries on each commit but useful for
/// tests.
#[derive(Debug, PartialEq, Default, Clone)]
pub struct InMemory {
inner: MemoryState, // keeps all the state in memory.
}
impl InMemory {
/// Create a new instance from a given storage map.
pub fn from(storage: ::std::collections::HashMap<Vec<u8>, Vec<u8>>) -> Self {
InMemory {
inner: MemoryState {
storage
}
}
}
}
pub type InMemory = HashMap<Vec<u8>, Vec<u8>>;
impl Backend for InMemory {
type Error = Void;
fn storage(&self, key: &[u8]) -> Result<&[u8], Self::Error> {
Ok(self.inner.storage(key).unwrap_or(&[]))
fn storage(&self, key: &[u8]) -> Result<Option<&[u8]>, Self::Error> {
Ok(self.get(key).map(AsRef::as_ref))
}
fn commit<I>(&mut self, changes: I) -> Committed
where I: IntoIterator<Item=Update>
fn commit<I>(&mut self, changes: I)
where I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>
{
self.inner.update(changes);
Committed {}
for (key, val) in changes {
match val {
Some(v) => { self.insert(key, v); },
None => { self.remove(&key); },
}
}
}
fn pairs(&self) -> Vec<(&[u8], &[u8])> {
self.inner.storage.iter().map(|(k, v)| (&k[..], &v[..])).collect()
self.iter().map(|(k, v)| (&k[..], &v[..])).collect()
}
}
+27 -15
View File
@@ -17,9 +17,10 @@
//! Conrete externalities implementation.
use std::{error, fmt};
use std::collections::HashMap;
use triehash::trie_root;
use backend::Backend;
use {Externalities, ExternalitiesError, OverlayedChanges};
use {Externalities, OverlayedChanges};
/// Errors that can occur when interacting with the externalities.
#[derive(Debug, Copy, Clone)]
@@ -58,17 +59,29 @@ pub struct Ext<'a, B: 'a> {
pub backend: &'a B,
}
#[cfg(test)]
impl<'a, B: 'a + Backend> Ext<'a, B> {
pub fn storage_pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
self.backend.pairs().iter()
.map(|&(ref k, ref v)| (k.to_vec(), Some(v.to_vec())))
.chain(self.overlay.committed.clone().into_iter())
.chain(self.overlay.prospective.clone().into_iter())
.collect::<HashMap<_, _>>()
.into_iter()
.filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val)))
.collect()
}
}
impl<'a, B: 'a> Externalities for Ext<'a, B>
where B: Backend
{
fn storage(&self, key: &[u8]) -> Result<&[u8], ExternalitiesError> {
match self.overlay.storage(key) {
Some(x) => Ok(x),
None => self.backend.storage(key).map_err(|_| ExternalitiesError),
}
fn storage(&self, key: &[u8]) -> Option<&[u8]> {
self.overlay.storage(key).unwrap_or_else(||
self.backend.storage(key).expect("Externalities not allowed to fail within runtime"))
}
fn set_storage(&mut self, key: Vec<u8>, value: Vec<u8>) {
fn place_storage(&mut self, key: Vec<u8>, value: Option<Vec<u8>>) {
self.overlay.set_storage(key, value);
}
@@ -77,13 +90,12 @@ impl<'a, B: 'a> Externalities for Ext<'a, B>
}
fn storage_root(&self) -> [u8; 32] {
let mut all_pairs = self.backend.pairs();
all_pairs.extend(
self.overlay.committed.storage.iter()
.chain(self.overlay.prospective.storage.iter())
.map(|(k, v)| (&k[..], &v[..]))
);
trie_root(all_pairs.into_iter().map(|(k, v)| (k.to_vec(), v.to_vec()))).0
trie_root(self.backend.pairs().iter()
.map(|&(ref k, ref v)| (k.to_vec(), Some(v.to_vec())))
.chain(self.overlay.committed.clone().into_iter())
.chain(self.overlay.prospective.clone().into_iter())
.collect::<HashMap<_, _>>()
.into_iter()
.filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val)))).0
}
}
+66 -64
View File
@@ -32,6 +32,7 @@ extern crate triehash;
extern crate byteorder;
use std::collections::HashMap;
use std::collections::hash_map::Drain;
use std::fmt;
pub mod backend;
@@ -40,35 +41,7 @@ mod testing;
pub use testing::TestExternalities;
pub use ext::Ext;
/// Updates to be committed to the state.
pub type Update = (Vec<u8>, Vec<u8>);
// in-memory section of the state.
#[derive(Debug, PartialEq, Default, Clone)]
struct MemoryState {
storage: HashMap<Vec<u8>, Vec<u8>>,
}
impl MemoryState {
fn storage(&self, key: &[u8]) -> Option<&[u8]> {
self.storage.get(key).map(|v| &v[..])
}
fn set_storage(&mut self, key: Vec<u8>, val: Vec<u8>) {
self.storage.insert(key, val);
}
fn update<I>(&mut self, changes: I) where I: IntoIterator<Item=Update> {
for (key, val) in changes {
if val.is_empty() {
self.storage.remove(&key);
} else {
self.storage.insert(key, val);
}
}
}
}
pub use backend::Backend;
/// The overlayed changes to state to be queried on top of the backend.
///
@@ -76,34 +49,41 @@ impl MemoryState {
/// that can be cleared.
#[derive(Default)]
pub struct OverlayedChanges {
prospective: MemoryState,
committed: MemoryState,
prospective: HashMap<Vec<u8>, Option<Vec<u8>>>,
committed: HashMap<Vec<u8>, Option<Vec<u8>>>,
}
impl OverlayedChanges {
fn storage(&self, key: &[u8]) -> Option<&[u8]> {
self.prospective.storage(key)
.or_else(|| self.committed.storage(key))
.and_then(|v| if v.is_empty() { None } else { Some(v) })
/// Returns a double-Option: None if the key is unknown (i.e. and the query should be refered
/// to the backend); Some(None) if the key has been deleted. Some(Some(...)) for a key whose
/// value has been set.
pub fn storage(&self, key: &[u8]) -> Option<Option<&[u8]>> {
self.prospective.get(key)
.or_else(|| self.committed.get(key))
.map(|x| x.as_ref().map(AsRef::as_ref))
}
fn set_storage(&mut self, key: Vec<u8>, val: Vec<u8>) {
self.prospective.set_storage(key, val);
fn set_storage(&mut self, key: Vec<u8>, val: Option<Vec<u8>>) {
self.prospective.insert(key, val);
}
/// Discard prospective changes to state.
pub fn discard_prospective(&mut self) {
self.prospective.storage.clear();
self.prospective.clear();
}
/// Commit prospective changes to state.
pub fn commit_prospective(&mut self) {
self.committed.update(self.prospective.storage.drain());
if self.committed.is_empty() {
::std::mem::swap(&mut self.prospective, &mut self.committed);
} else {
self.committed.extend(self.prospective.drain());
}
}
/// Drain prospective changes to an iterator.
pub fn drain(&mut self) -> ::std::collections::hash_map::Drain<std::vec::Vec<u8>, std::vec::Vec<u8>> {
self.committed.storage.drain()
pub fn drain(&mut self) -> Drain<Vec<u8>, Option<Vec<u8>>> {
self.committed.drain()
}
}
@@ -119,19 +99,32 @@ impl<E> Error for E where E: 'static + fmt::Debug + fmt::Display + Send {}
/// would not be executed unless externalities were available. This is included for completeness,
/// and as a transition away from the pre-existing framework.
#[derive(Debug, Eq, PartialEq)]
pub struct ExternalitiesError;
pub enum ExecutionError {
/// The entry `:code` doesn't exist in storage so there's no way we can execute anything.
CodeEntryDoesNotExist
}
impl fmt::Display for ExternalitiesError {
impl fmt::Display for ExecutionError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Externalities Error") }
}
/// Externalities: pinned to specific active address.
pub trait Externalities {
/// Read storage of current contract being called.
fn storage(&self, key: &[u8]) -> Result<&[u8], ExternalitiesError>;
fn storage(&self, key: &[u8]) -> Option<&[u8]>;
/// Set storage of current contract being called (effective immediately).
fn set_storage(&mut self, key: Vec<u8>, value: Vec<u8>);
/// Set storage entry `key` of current contract being called (effective immediately).
fn set_storage(&mut self, key: Vec<u8>, value: Vec<u8>) {
self.place_storage(key, Some(value));
}
/// Clear a storage entry (`key`) of current contract being called (effective immediately).
fn clear_storage(&mut self, key: &[u8]) {
self.place_storage(key.to_vec(), None);
}
/// Set or clear a storage entry (`key`) of current contract being called (effective immediately).
fn place_storage(&mut self, key: Vec<u8>, value: Option<Vec<u8>>);
/// Get the identity of the chain.
fn chain_id(&self) -> u64;
@@ -167,7 +160,8 @@ pub fn execute<B: backend::Backend, Exec: CodeExecutor>(
exec: &Exec,
method: &str,
call_data: &[u8],
) -> Result<Vec<u8>, Box<Error>> {
) -> Result<Vec<u8>, Box<Error>>
{
let result = {
let mut externalities = ext::Ext {
@@ -175,7 +169,9 @@ pub fn execute<B: backend::Backend, Exec: CodeExecutor>(
overlay: &mut *overlay
};
// make a copy.
let code = externalities.storage(b":code").unwrap_or(&[]).to_vec();
let code = externalities.storage(b":code")
.ok_or(Box::new(ExecutionError::CodeEntryDoesNotExist) as Box<Error>)?
.to_vec();
exec.call(
&mut externalities,
@@ -211,21 +207,24 @@ mod tests {
assert!(overlayed.storage(&key).is_none());
overlayed.set_storage(key.clone(), vec![1, 2, 3]);
assert_eq!(overlayed.storage(&key).unwrap(), &[1, 2, 3]);
overlayed.set_storage(key.clone(), Some(vec![1, 2, 3]));
assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..]));
overlayed.commit_prospective();
assert_eq!(overlayed.storage(&key).unwrap(), &[1, 2, 3]);
assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..]));
overlayed.set_storage(key.clone(), vec![]);
assert!(overlayed.storage(&key).is_none());
overlayed.set_storage(key.clone(), Some(vec![]));
assert_eq!(overlayed.storage(&key).unwrap(), Some(&[][..]));
overlayed.set_storage(key.clone(), None);
assert!(overlayed.storage(&key).unwrap().is_none());
overlayed.discard_prospective();
assert_eq!(overlayed.storage(&key).unwrap(), &[1, 2, 3]);
assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..]));
overlayed.set_storage(key.clone(), vec![]);
overlayed.set_storage(key.clone(), None);
overlayed.commit_prospective();
assert!(overlayed.storage(&key).is_none());
assert!(overlayed.storage(&key).unwrap().is_none());
}
macro_rules! map {
@@ -239,16 +238,19 @@ mod tests {
let mut backend = InMemory::from(map![
b"doe".to_vec() => b"reindeer".to_vec(),
b"dog".to_vec() => b"puppyXXX".to_vec(),
b"dogglesworth".to_vec() => b"catXXX".to_vec()
b"dogglesworth".to_vec() => b"catXXX".to_vec(),
b"doug".to_vec() => b"notadog".to_vec()
]);
let mut overlay = OverlayedChanges {
committed: MemoryState { storage: map![
b"dog".to_vec() => b"puppy".to_vec(),
b"dogglesworth".to_vec() => b"catYYY".to_vec()
], },
prospective: MemoryState { storage: map![
b"dogglesworth".to_vec() => b"cat".to_vec()
], },
committed: map![
b"dog".to_vec() => Some(b"puppy".to_vec()),
b"dogglesworth".to_vec() => Some(b"catYYY".to_vec()),
b"doug".to_vec() => Some(vec![])
],
prospective: map![
b"dogglesworth".to_vec() => Some(b"cat".to_vec()),
b"doug".to_vec() => None
],
};
let ext = Ext {
backend: &mut backend,
@@ -17,38 +17,28 @@
//! Test implementation for Externalities.
use std::collections::HashMap;
use super::{Externalities, ExternalitiesError};
use super::Externalities;
use triehash::trie_root;
/// Simple HashMap based Externalities impl.
#[derive(Debug, Default)]
pub struct TestExternalities {
/// The storage.
pub storage: HashMap<Vec<u8>, Vec<u8>>,
}
impl TestExternalities {
/// Create a new instance with empty storage.
pub fn new() -> Self {
TestExternalities {
storage: HashMap::new(),
}
}
}
pub type TestExternalities = HashMap<Vec<u8>, Vec<u8>>;
impl Externalities for TestExternalities {
fn storage(&self, key: &[u8]) -> Result<&[u8], ExternalitiesError> {
Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice))
fn storage(&self, key: &[u8]) -> Option<&[u8]> {
self.get(key).map(AsRef::as_ref)
}
fn set_storage(&mut self, key: Vec<u8>, value: Vec<u8>) {
self.storage.insert(key, value);
fn place_storage(&mut self, key: Vec<u8>, maybe_value: Option<Vec<u8>>) {
match maybe_value {
Some(value) => { self.insert(key, value); }
None => { self.remove(&key); }
}
}
fn chain_id(&self) -> u64 { 42 }
fn storage_root(&self) -> [u8; 32] {
trie_root(self.storage.clone()).0
trie_root(self.clone()).0
}
}
@@ -136,14 +136,14 @@ mod tests {
use ::{Header, Digest, Transaction, UncheckedTransaction};
fn new_test_ext() -> TestExternalities {
TestExternalities { storage: map![
map![
twox_128(b"latest").to_vec() => vec![69u8; 32],
twox_128(b":auth:len").to_vec() => vec![].and(&3u32),
twox_128(&0u32.to_keyed_vec(b":auth:")).to_vec() => Keyring::Alice.to_raw_public().to_vec(),
twox_128(&1u32.to_keyed_vec(b":auth:")).to_vec() => Keyring::Bob.to_raw_public().to_vec(),
twox_128(&2u32.to_keyed_vec(b":auth:")).to_vec() => Keyring::Charlie.to_raw_public().to_vec(),
twox_128(&Keyring::Alice.to_raw_public().to_keyed_vec(b"balance:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0]
], }
]
}
fn construct_signed_tx(tx: Transaction) -> UncheckedTransaction {
Binary file not shown.