Merge branch 'master' into rh-grandpa-dynamic2

This commit is contained in:
Robert Habermeier
2018-10-22 16:06:42 +02:00
87 changed files with 2757 additions and 993 deletions
+5 -5
View File
@@ -91,7 +91,7 @@ pub struct Fork<Block: BlockT, T> {
head: Entry<Block, T>,
}
/// Outcome of Fork::try_append_or_fork.
/// Outcome of Fork::try_append_or_fork.
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
pub enum ForkAppendResult<Block: BlockT> {
@@ -356,7 +356,7 @@ impl<Block: BlockT, T: CacheItemT, S: Storage<Block, T>> ListCache<Block, T, S>
// if there's an entry at this block:
// - remove reference from this entry to the previous entry
// - destroy fork starting with previous entry
// - destroy fork starting with previous entry
let current_entry = match self.storage.read_entry(&ancient_block)? {
Some(current_entry) => current_entry,
None => return Ok(()),
@@ -583,12 +583,12 @@ fn read_forks<Block: BlockT, T: CacheItemT, S: Storage<Block, T>>(
#[cfg(test)]
pub mod tests {
use runtime_primitives::testing::{Header, Block as RawBlock};
use runtime_primitives::testing::{Header, Block as RawBlock, ExtrinsicWrapper};
use runtime_primitives::traits::Header as HeaderT;
use cache::list_storage::tests::{DummyStorage, FaultyStorage, DummyTransaction};
use super::*;
type Block = RawBlock<u64>;
type Block = RawBlock<ExtrinsicWrapper<u64>>;
pub fn test_id(number: u64) -> ComplexBlockId<Block> {
ComplexBlockId::new(From::from(number), number)
@@ -834,7 +834,7 @@ pub mod tests {
// when trying to insert block @ finalized number
assert!(ListCache::new(DummyStorage::new(), 1024, test_id(100))
.on_block_insert(&mut DummyTransaction::new(), test_id(99), test_id(100), Some(100), false).unwrap().is_none());
// when trying to insert non-final block AND it appends to the best block of unfinalized fork
// AND new value is the same as in the fork' best block
let mut cache = ListCache::new(
+3 -3
View File
@@ -23,7 +23,7 @@ use kvdb::{KeyValueDB, DBTransaction};
use client::error::{Error as ClientError, ErrorKind as ClientErrorKind, Result as ClientResult};
use codec::{Encode, Decode};
use runtime_primitives::generic::BlockId;
use runtime_primitives::traits::{Block as BlockT, NumberFor};
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor};
use utils::{self, db_err, meta_keys};
use cache::{CacheItemT, ComplexBlockId};
@@ -126,7 +126,8 @@ impl DbStorage {
impl<Block: BlockT, T: CacheItemT> Storage<Block, T> for DbStorage {
fn read_id(&self, at: NumberFor<Block>) -> ClientResult<Option<Block::Hash>> {
utils::read_id::<Block>(&*self.db, self.columns.hash_lookup, BlockId::Number(at))
utils::read_header::<Block>(&*self.db, self.columns.hash_lookup, self.columns.header, BlockId::Number(at))
.map(|maybe_header| maybe_header.map(|header| header.hash()))
}
fn read_header(&self, at: &Block::Hash) -> ClientResult<Option<Block::Header>> {
@@ -246,7 +247,6 @@ mod meta {
#[cfg(test)]
pub mod tests {
use std::collections::{HashMap, HashSet};
use runtime_primitives::traits::Header as HeaderT;
use super::*;
pub struct FaultyStorage;
+3 -2
View File
@@ -194,10 +194,11 @@ impl<Block: BlockT> BlockchainCache<Block> for DbCacheSync<Block> {
ComplexBlockId::new(hash, *header.number())
},
BlockId::Number(number) => {
let hash = utils::read_id::<Block>(
let hash = utils::read_header::<Block>(
&**db,
columns.hash_lookup,
BlockId::Number(number.clone())).ok()??;
columns.header,
BlockId::Number(number.clone())).ok()??.hash();
ComplexBlockId::new(hash, number)
},
};
+101 -42
View File
@@ -75,7 +75,7 @@ use runtime_primitives::BuildStorage;
use state_machine::backend::Backend as StateBackend;
use executor::RuntimeInfo;
use state_machine::{CodeExecutor, DBValue, ExecutionStrategy};
use utils::{Meta, db_err, meta_keys, open_database, read_db, read_id, read_meta};
use utils::{Meta, db_err, meta_keys, open_database, read_db, block_id_to_lookup_key, read_meta};
use client::LeafSet;
use state_db::StateDb;
pub use state_db::PruningMode;
@@ -118,6 +118,7 @@ mod columns {
pub const META: Option<u32> = ::utils::COLUMN_META;
pub const STATE: Option<u32> = Some(1);
pub const STATE_META: Option<u32> = Some(2);
/// maps hashes to lookup keys
pub const HASH_LOOKUP: Option<u32> = Some(3);
pub const HEADER: Option<u32> = Some(4);
pub const BODY: Option<u32> = Some(5);
@@ -219,15 +220,20 @@ impl<Block: BlockT> client::blockchain::HeaderBackend<Block> for BlockchainDb<Bl
}
}
fn number(&self, hash: Block::Hash) -> Result<Option<<Block::Header as HeaderT>::Number>, client::error::Error> {
self.header(BlockId::Hash(hash)).and_then(|key| match key {
Some(hdr) => Ok(Some(hdr.number().clone())),
None => Ok(None),
})
fn number(&self, hash: Block::Hash) -> Result<Option<NumberFor<Block>>, client::error::Error> {
if let Some(lookup_key) = block_id_to_lookup_key::<Block>(&*self.db, columns::HASH_LOOKUP, BlockId::Hash(hash))? {
let number = utils::lookup_key_to_number(&lookup_key)?;
Ok(Some(number))
} else {
Ok(None)
}
}
fn hash(&self, number: <Block::Header as HeaderT>::Number) -> Result<Option<Block::Hash>, client::error::Error> {
read_id::<Block>(&*self.db, columns::HASH_LOOKUP, BlockId::Number(number))
fn hash(&self, number: NumberFor<Block>) -> Result<Option<Block::Hash>, client::error::Error> {
self.header(BlockId::Number(number)).and_then(|maybe_header| match maybe_header {
Some(header) => Ok(Some(header.hash().clone())),
None => Ok(None),
})
}
}
@@ -495,12 +501,9 @@ impl<Block: BlockT> Backend<Block> {
let hash = if new_canonical == number_u64 {
hash
} else {
read_id::<Block>(
&*self.blockchain.db,
columns::HASH_LOOKUP,
BlockId::Number(As::sa(new_canonical))
)?.expect("existence of block with number `new_canonical` \
implies existence of blocks with all nubmers before it; qed")
::client::blockchain::HeaderBackend::hash(&self.blockchain, As::sa(new_canonical))?
.expect("existence of block with number `new_canonical` \
implies existence of blocks with all numbers before it; qed")
};
trace!(target: "db", "Canonicalize block #{} ({:?})", new_canonical, hash);
@@ -588,14 +591,6 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
let parent_hash = *pending_block.header.parent_hash();
let number = pending_block.header.number().clone();
transaction.put(columns::HEADER, hash.as_ref(), &pending_block.header.encode());
if let Some(body) = pending_block.body {
transaction.put(columns::BODY, hash.as_ref(), &body.encode());
}
if let Some(justification) = pending_block.justification {
transaction.put(columns::JUSTIFICATION, hash.as_ref(), &justification.encode());
}
if pending_block.leaf_state.is_best() {
let meta = self.blockchain.meta.read();
@@ -607,7 +602,7 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
BlockId::Hash(parent_hash),
)?;
// update block number to hash lookup entries.
// uncanonicalize
for retracted in tree_route.retracted() {
if retracted.hash == meta.finalized_hash {
warn!("Potential safety failure: reverting finalized block {:?}",
@@ -616,30 +611,94 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
return Err(::client::error::ErrorKind::NotInFinalizedChain.into());
}
transaction.delete(
columns::HASH_LOOKUP,
&::utils::number_to_lookup_key(retracted.number)
);
let prev_lookup_key = ::utils::number_to_lookup_key(retracted.number);
let new_lookup_key = ::utils::number_and_hash_to_lookup_key(retracted.number, retracted.hash);
// change mapping from `number -> header`
// to `number + hash -> header`
let retracted_header = if let Some(header) = ::client::blockchain::HeaderBackend::<Block>::header(&self.blockchain, BlockId::Number(retracted.number))? {
header
} else {
return Err(client::error::ErrorKind::UnknownBlock(format!("retracted {:?}", retracted)).into());
};
transaction.delete(columns::HEADER, &prev_lookup_key);
transaction.put(columns::HEADER, &new_lookup_key, &retracted_header.encode());
// if body is stored
// change mapping from `number -> body`
// to `number + hash -> body`
if let Some(retracted_body) = ::client::blockchain::Backend::<Block>::body(&self.blockchain, BlockId::Number(retracted.number))? {
transaction.delete(columns::BODY, &prev_lookup_key);
transaction.put(columns::BODY, &new_lookup_key, &retracted_body.encode());
}
// if justification is stored
// change mapping from `number -> justification`
// to `number + hash -> justification`
if let Some(retracted_justification) = ::client::blockchain::Backend::<Block>::justification(&self.blockchain, BlockId::Number(retracted.number))? {
transaction.delete(columns::JUSTIFICATION, &prev_lookup_key);
transaction.put(columns::JUSTIFICATION, &new_lookup_key, &retracted_justification.encode());
}
transaction.put(columns::HASH_LOOKUP, retracted.hash.as_ref(), &new_lookup_key);
}
// canonicalize
for enacted in tree_route.enacted() {
let hash: &Block::Hash = &enacted.hash;
transaction.put(
columns::HASH_LOOKUP,
&::utils::number_to_lookup_key(enacted.number),
hash.as_ref(),
)
let prev_lookup_key = ::utils::number_and_hash_to_lookup_key(enacted.number, enacted.hash);
let new_lookup_key = ::utils::number_to_lookup_key(enacted.number);
// change mapping from `number + hash -> header`
// to `number -> header`
let enacted_header = if let Some(header) = ::client::blockchain::HeaderBackend::<Block>::header(&self.blockchain, BlockId::Number(enacted.number))? {
header
} else {
return Err(client::error::ErrorKind::UnknownBlock(format!("enacted {:?}", enacted)).into());
};
transaction.delete(columns::HEADER, &prev_lookup_key);
transaction.put(columns::HEADER, &new_lookup_key, &enacted_header.encode());
// if body is stored
// change mapping from `number + hash -> body`
// to `number -> body`
if let Some(enacted_body) = ::client::blockchain::Backend::<Block>::body(&self.blockchain, BlockId::Number(enacted.number))? {
transaction.delete(columns::BODY, &prev_lookup_key);
transaction.put(columns::BODY, &new_lookup_key, &enacted_body.encode());
}
// if justification is stored
// change mapping from `number -> justification`
// to `number + hash -> justification`
if let Some(enacted_justification) = ::client::blockchain::Backend::<Block>::justification(&self.blockchain, BlockId::Number(enacted.number))? {
transaction.delete(columns::JUSTIFICATION, &prev_lookup_key);
transaction.put(columns::JUSTIFICATION, &new_lookup_key, &enacted_justification.encode());
}
transaction.put(columns::HASH_LOOKUP, enacted.hash.as_ref(), &new_lookup_key);
}
}
transaction.put(
columns::HASH_LOOKUP,
&::utils::number_to_lookup_key(number),
hash.as_ref()
);
transaction.put(columns::META, meta_keys::BEST_BLOCK, hash.as_ref());
}
// blocks in longest chain are keyed by number
let lookup_key = if pending_block.leaf_state.is_best() {
::utils::number_to_lookup_key(number).to_vec()
} else {
// other blocks are keyed by number + hash
::utils::number_and_hash_to_lookup_key(number, hash)
};
transaction.put(columns::HEADER, &lookup_key, &pending_block.header.encode());
if let Some(body) = pending_block.body {
transaction.put(columns::BODY, &lookup_key, &body.encode());
}
if let Some(justification) = pending_block.justification {
transaction.put(columns::JUSTIFICATION, &lookup_key, &justification.encode());
}
transaction.put(columns::HASH_LOOKUP, hash.as_ref(), &lookup_key);
if number == Zero::zero() {
transaction.put(columns::META, meta_keys::FINALIZED_BLOCK, hash.as_ref());
transaction.put(columns::META, meta_keys::GENESIS_HASH, hash.as_ref());
@@ -732,14 +791,14 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
match self.storage.state_db.revert_one() {
Some(commit) => {
apply_state_commit(&mut transaction, commit);
let removed = best.clone();
let _removed = best.clone();
best -= As::sa(1);
let header = self.blockchain.header(BlockId::Number(best))?.ok_or_else(
|| client::error::ErrorKind::UnknownBlock(
format!("Error reverting to {}. Block header not found.", best)))?;
transaction.put(columns::META, meta_keys::BEST_BLOCK, header.hash().as_ref());
transaction.delete(columns::HASH_LOOKUP, &::utils::number_to_lookup_key(removed));
transaction.delete(columns::HASH_LOOKUP, header.hash().as_ref());
self.storage.db.write(transaction).map_err(db_err)?;
self.blockchain.update_meta(header.hash().clone(), best.clone(), true, false);
self.blockchain.leaves.write().revert(header.hash().clone(), header.number().clone(), header.parent_hash().clone());
@@ -806,11 +865,11 @@ mod tests {
use client::backend::Backend as BTrait;
use client::backend::BlockImportOperation as Op;
use client::blockchain::HeaderBackend as BlockchainHeaderBackend;
use runtime_primitives::testing::{Header, Block as RawBlock};
use runtime_primitives::testing::{Header, Block as RawBlock, ExtrinsicWrapper};
use state_machine::{TrieMut, TrieDBMut, ChangesTrieRootsStorage, ChangesTrieStorage};
use test_client;
type Block = RawBlock<u64>;
type Block = RawBlock<ExtrinsicWrapper<u64>>;
fn prepare_changes(changes: Vec<(Vec<u8>, Vec<u8>)>) -> (H256, MemoryDB<Blake2Hasher>) {
let mut changes_root = H256::default();
+59 -27
View File
@@ -34,7 +34,7 @@ use runtime_primitives::traits::{Block as BlockT, Header as HeaderT,
Zero, One, As, NumberFor};
use cache::{DbCacheSync, DbCache, ComplexBlockId};
use utils::{meta_keys, Meta, db_err, number_to_lookup_key, open_database,
read_db, read_id, read_meta};
read_db, block_id_to_lookup_key, read_meta};
use DatabaseSettings;
pub(crate) mod columns {
@@ -168,14 +168,16 @@ impl<Block> BlockchainHeaderBackend<Block> for LightStorage<Block>
}
fn number(&self, hash: Block::Hash) -> ClientResult<Option<<<Block as BlockT>::Header as HeaderT>::Number>> {
self.header(BlockId::Hash(hash)).and_then(|key| match key {
Some(hdr) => Ok(Some(hdr.number().clone())),
None => Ok(None),
})
if let Some(lookup_key) = block_id_to_lookup_key::<Block>(&*self.db, columns::HASH_LOOKUP, BlockId::Hash(hash))? {
let number = ::utils::lookup_key_to_number(&lookup_key)?;
Ok(Some(number))
} else {
Ok(None)
}
}
fn hash(&self, number: <<Block as BlockT>::Header as HeaderT>::Number) -> ClientResult<Option<Block::Hash>> {
read_id::<Block>(&*self.db, columns::HASH_LOOKUP, BlockId::Number(number))
Ok(self.header(BlockId::Number(number))?.map(|header| header.hash().clone()))
}
}
@@ -212,13 +214,13 @@ impl<Block: BlockT> LightStorage<Block> {
trace!(target: "db", "Replacing blocks [{}..{}] with CHT#{}", new_cht_start, new_cht_end, new_cht_number);
while prune_block <= new_cht_end {
let id = read_id::<Block>(&*self.db, columns::HASH_LOOKUP, BlockId::Number(prune_block))?;
if let Some(hash) = id {
let lookup_key = number_to_lookup_key(prune_block);
transaction.delete(columns::HASH_LOOKUP, &lookup_key);
transaction.delete(columns::HEADER, hash.as_ref());
if let Some(hash) = self.hash(prune_block)? {
let lookup_key = block_id_to_lookup_key::<Block>(&*self.db, columns::HASH_LOOKUP, BlockId::Number(prune_block))?
.expect("retrieved hash for `prune_block` right above. therefore retrieving lookup key must succeed. q.e.d.");
transaction.delete(columns::HASH_LOOKUP, hash.as_ref());
transaction.delete(columns::HEADER, &lookup_key);
}
prune_block += <<Block as BlockT>::Header as HeaderT>::Number::one();
prune_block += NumberFor::<Block>::one();
}
}
}
@@ -242,8 +244,6 @@ impl<Block> LightBlockchainStorage<Block> for LightStorage<Block>
let number = *header.number();
let parent_hash = *header.parent_hash();
transaction.put(columns::HEADER, hash.as_ref(), &header.encode());
if leaf_state.is_best() {
// handle reorg.
{
@@ -263,27 +263,55 @@ impl<Block> LightBlockchainStorage<Block> for LightStorage<Block>
(&retracted.number, &retracted.hash));
}
transaction.delete(
columns::HASH_LOOKUP,
&::utils::number_to_lookup_key(retracted.number)
);
let prev_lookup_key = ::utils::number_to_lookup_key(retracted.number);
let new_lookup_key = ::utils::number_and_hash_to_lookup_key(retracted.number, retracted.hash);
// change mapping from `number -> header`
// to `number + hash -> header`
let retracted_header = if let Some(header) = self.header(BlockId::Number(retracted.number))? {
header
} else {
return Err(::client::error::ErrorKind::UnknownBlock(format!("retracted {:?}", retracted)).into());
};
transaction.delete(columns::HEADER, &prev_lookup_key);
transaction.put(columns::HEADER, &new_lookup_key, &retracted_header.encode());
transaction.put(columns::HASH_LOOKUP, retracted.hash.as_ref(), &new_lookup_key);
}
for enacted in tree_route.enacted() {
let hash: &Block::Hash = &enacted.hash;
transaction.put(
columns::HASH_LOOKUP,
&::utils::number_to_lookup_key(enacted.number),
hash.as_ref(),
)
let prev_lookup_key = ::utils::number_and_hash_to_lookup_key(enacted.number, enacted.hash);
let new_lookup_key = ::utils::number_to_lookup_key(enacted.number);
// change mapping from `number + hash -> header`
// to `number -> header`
let enacted_header = if let Some(header) = self.header(BlockId::Number(enacted.number))? {
header
} else {
return Err(::client::error::ErrorKind::UnknownBlock(format!("enacted {:?}", enacted)).into());
};
transaction.delete(columns::HEADER, &prev_lookup_key);
transaction.put(columns::HEADER, &new_lookup_key, &enacted_header.encode());
transaction.put(columns::HASH_LOOKUP, enacted.hash.as_ref(), &new_lookup_key);
}
}
}
transaction.put(columns::META, meta_keys::BEST_BLOCK, hash.as_ref());
transaction.put(columns::HASH_LOOKUP, &number_to_lookup_key(number), hash.as_ref());
}
// blocks in longest chain are keyed by number
let lookup_key = if leaf_state.is_best() {
::utils::number_to_lookup_key(number).to_vec()
} else {
// other blocks are keyed by number + hash
::utils::number_and_hash_to_lookup_key(number, hash)
};
transaction.put(columns::HEADER, &lookup_key, &header.encode());
transaction.put(columns::HASH_LOOKUP, hash.as_ref(), &lookup_key);
let finalized = match leaf_state {
NewBlockState::Final => true,
_ => false,
@@ -374,10 +402,10 @@ impl<Block> LightBlockchainStorage<Block> for LightStorage<Block>
#[cfg(test)]
pub(crate) mod tests {
use client::cht;
use runtime_primitives::testing::{H256 as Hash, Header, Block as RawBlock};
use runtime_primitives::testing::{H256 as Hash, Header, Block as RawBlock, ExtrinsicWrapper};
use super::*;
type Block = RawBlock<u32>;
type Block = RawBlock<ExtrinsicWrapper<u32>>;
fn prepare_header(parent: &Hash, number: u64, extrinsics_root: Hash) -> Header {
Header {
@@ -512,6 +540,7 @@ pub(crate) mod tests {
prev_hash = insert_block(&db, &prev_hash, 1 + number, None);
}
assert_eq!(db.db.iter(columns::HEADER).count(), (1 + cht::SIZE) as usize);
assert_eq!(db.db.iter(columns::HASH_LOOKUP).count(), (1 + cht::SIZE) as usize);
assert_eq!(db.db.iter(columns::CHT).count(), 0);
// insert next SIZE blocks && ensure that nothing is pruned
@@ -519,12 +548,14 @@ pub(crate) mod tests {
prev_hash = insert_block(&db, &prev_hash, 1 + cht::SIZE + number, None);
}
assert_eq!(db.db.iter(columns::HEADER).count(), (1 + cht::SIZE + cht::SIZE) as usize);
assert_eq!(db.db.iter(columns::HASH_LOOKUP).count(), (1 + cht::SIZE + cht::SIZE) as usize);
assert_eq!(db.db.iter(columns::CHT).count(), 0);
// insert block #{2 * cht::SIZE + 1} && check that new CHT is created + headers of this CHT are pruned
// nothing is yet finalized, so nothing is pruned.
prev_hash = insert_block(&db, &prev_hash, 1 + cht::SIZE + cht::SIZE, None);
assert_eq!(db.db.iter(columns::HEADER).count(), (2 + cht::SIZE + cht::SIZE) as usize);
assert_eq!(db.db.iter(columns::HASH_LOOKUP).count(), (2 + cht::SIZE + cht::SIZE) as usize);
assert_eq!(db.db.iter(columns::CHT).count(), 0);
// now finalize the block.
@@ -533,6 +564,7 @@ pub(crate) mod tests {
}
db.finalize_header(BlockId::Hash(prev_hash)).unwrap();
assert_eq!(db.db.iter(columns::HEADER).count(), (1 + cht::SIZE + 1) as usize);
assert_eq!(db.db.iter(columns::HASH_LOOKUP).count(), (1 + cht::SIZE + 1) as usize);
assert_eq!(db.db.iter(columns::CHT).count(), 1);
assert!((0..cht::SIZE).all(|i| db.db.get(columns::HEADER, &number_to_lookup_key(1 + i)).unwrap().is_none()));
}
+48 -25
View File
@@ -67,10 +67,11 @@ pub struct Meta<N, H> {
}
/// A block lookup key: used for canonical lookup from block number to hash
pub type BlockLookupKey = [u8; 4];
pub type ShortBlockLookupKey = [u8; 4];
/// Convert block number into lookup key (LE representation).
pub fn number_to_lookup_key<N>(n: N) -> BlockLookupKey where N: As<u64> {
/// Convert block number into short lookup key (LE representation) for
/// blocks that are in the canonical chain.
pub fn number_to_lookup_key<N>(n: N) -> ShortBlockLookupKey where N: As<u64> {
let n: u64 = n.as_();
assert!(n & 0xffffffff00000000 == 0);
@@ -82,6 +83,49 @@ pub fn number_to_lookup_key<N>(n: N) -> BlockLookupKey where N: As<u64> {
]
}
/// Convert number and hash into long lookup key for blocks that are
/// not in the canonical chain.
pub fn number_and_hash_to_lookup_key<N, H>(number: N, hash: H) -> Vec<u8> where
N: As<u64>,
H: AsRef<[u8]>
{
let mut lookup_key = number_to_lookup_key(number).to_vec();
lookup_key.extend_from_slice(hash.as_ref());
lookup_key
}
/// Convert block lookup key into block number.
/// all block lookup keys start with the block number.
pub fn lookup_key_to_number<N>(key: &[u8]) -> client::error::Result<N> where N: As<u64> {
if key.len() < 4 {
return Err(client::error::ErrorKind::Backend("Invalid block key".into()).into());
}
Ok((key[0] as u64) << 24
| (key[1] as u64) << 16
| (key[2] as u64) << 8
| (key[3] as u64)).map(As::sa)
}
/// Convert block id to block lookup key.
/// block lookup key is the DB-key header, block and justification are stored under.
/// looks up lookup key by hash from DB as necessary.
pub fn block_id_to_lookup_key<Block>(
db: &KeyValueDB,
hash_lookup_col: Option<u32>,
id: BlockId<Block>
) -> Result<Option<Vec<u8>>, client::error::Error> where
Block: BlockT,
{
match id {
// numbers are solely looked up in canonical chain
BlockId::Number(n) => Ok(Some(number_to_lookup_key(n).to_vec())),
BlockId::Hash(h) => db.get(hash_lookup_col, h.as_ref()).map(|v|
v.map(|v| { v.into_vec() })
).map_err(db_err),
}
}
/// Maps database error to client error
pub fn db_err(err: io::Error) -> client::error::Error {
use std::error::Error;
@@ -113,33 +157,12 @@ pub fn open_database(config: &DatabaseSettings, col_meta: Option<u32>, db_type:
Ok(Arc::new(db))
}
/// Convert block id to block key, looking up canonical hash by number from DB as necessary.
pub fn read_id<Block>(db: &KeyValueDB, col_index: Option<u32>, id: BlockId<Block>) -> Result<Option<Block::Hash>, client::error::Error>
where
Block: BlockT,
{
match id {
BlockId::Hash(h) => Ok(Some(h)),
BlockId::Number(n) => db.get(col_index, &number_to_lookup_key(n)).map(|v|
v.map(|v| {
let mut h = <Block::Hash>::default();
{
let h = h.as_mut();
let len = ::std::cmp::min(v.len(), h.len());
h.as_mut().copy_from_slice(&v[..len]);
}
h
})
).map_err(db_err),
}
}
/// Read database column entry for the given block.
pub fn read_db<Block>(db: &KeyValueDB, col_index: Option<u32>, col: Option<u32>, id: BlockId<Block>) -> client::error::Result<Option<DBValue>>
where
Block: BlockT,
{
read_id(db, col_index, id).and_then(|key| match key {
block_id_to_lookup_key(db, col_index, id).and_then(|key| match key {
Some(key) => db.get(col, key.as_ref()).map_err(db_err),
None => Ok(None),
})
+2 -2
View File
@@ -89,7 +89,7 @@ pub fn build_proof<Header, Hasher, I>(
{
let transaction = build_pairs::<Header, I>(cht_size, cht_num, hashes)?
.into_iter()
.map(|(k, v)| (k, Some(v)))
.map(|(k, v)| (None, k, Some(v)))
.collect::<Vec<_>>();
let storage = InMemoryState::<Hasher>::default().update(transaction);
let (value, proof) = prove_read(storage, &encode_cht_key(block_num)).ok()?;
@@ -205,7 +205,7 @@ pub fn decode_cht_value(value: &[u8]) -> Option<H256> {
32 => Some(H256::from_slice(&value[0..32])),
_ => None,
}
}
#[cfg(test)]
+3 -3
View File
@@ -574,7 +574,7 @@ impl<B, E, Block> Client<B, E, Block> where
for tx in extrinsics {
let tx = api::TaggedTransactionQueue::validate_transaction(self, &id, &tx)?;
match tx {
TransactionValidity::Valid(_, _, mut provides, ..) => {
TransactionValidity::Valid { mut provides, .. } => {
tags.append(&mut provides);
},
// silently ignore invalid extrinsics,
@@ -1128,12 +1128,12 @@ impl<B, E, Block> api::BlockBuilder<Block> for Client<B, E, Block> where
self.call_api_at(at, "inherent_extrinsics", &(inherent))
}
fn check_inherents<InherentData: Encode + Decode>(
fn check_inherents<InherentData: Encode + Decode, InherentError: Encode + Decode>(
&self,
at: &BlockId<Block>,
block: &Block,
data: &InherentData
) -> Result<Result<(), api::BlockBuilderError>, Self::Error> {
) -> Result<Result<(), InherentError>, Self::Error> {
self.call_api_at(at, "check_inherents", &(block, data))
}
+19 -2
View File
@@ -201,7 +201,7 @@ impl<Block, S, F, H> StateBackend<H> for OnDemandState<Block, S, F>
S: BlockchainStorage<Block>,
F: Fetcher<Block>,
H: Hasher,
{
type Error = ClientError;
type Transaction = ();
@@ -227,15 +227,32 @@ impl<Block, S, F, H> StateBackend<H> for OnDemandState<Block, S, F>
.into_future().wait()
}
fn child_storage(&self, _storage_key: &[u8], _key: &[u8]) -> ClientResult<Option<Vec<u8>>> {
Err(ClientErrorKind::NotAvailableOnLightClient.into())
}
fn for_keys_with_prefix<A: FnMut(&[u8])>(&self, _prefix: &[u8], _action: A) {
// whole state is not available on light node
}
fn for_keys_in_child_storage<A: FnMut(&[u8])>(&self, _storage_key: &[u8], _action: A) {
// whole state is not available on light node
}
fn storage_root<I>(&self, _delta: I) -> (H::Out, Self::Transaction)
where I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)> {
where
I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>
{
(H::Out::default(), ())
}
fn child_storage_root<I>(&self, _key: &[u8], _delta: I) -> (Vec<u8>, Self::Transaction)
where
I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>
{
(H::Out::default().as_ref().to_vec(), ())
}
fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
// whole state is not available on light node
Vec::new()
+2 -3
View File
@@ -179,11 +179,10 @@ impl<Block: BlockT> StorageNotifications<Block> {
#[cfg(test)]
mod tests {
use runtime_primitives::testing::{H256 as Hash, Block as RawBlock};
use runtime_primitives::testing::{H256 as Hash, Block as RawBlock, ExtrinsicWrapper};
use super::*;
use futures::Stream;
#[cfg(test)]
impl From<Vec<(StorageKey, Option<StorageData>)>> for StorageChangeSet {
fn from(changes: Vec<(StorageKey, Option<StorageData>)>) -> Self {
@@ -201,7 +200,7 @@ mod tests {
}
}
type Block = RawBlock<Hash>;
type Block = RawBlock<ExtrinsicWrapper<Hash>>;
#[test]
fn triggering_change_should_notify_wildcard_listeners() {
+155 -7
View File
@@ -181,6 +181,46 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
this.ext.set_storage(key, value);
Ok(())
},
ext_set_child_storage(storage_key_data: *const u8, storage_key_len: u32, key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32) => {
let storage_key = this.memory.get(storage_key_data, storage_key_len as usize).map_err(|_| UserError("Invalid attempt to determine storage_key in ext_set_child_storage"))?;
let key = this.memory.get(key_data, key_len as usize).map_err(|_| UserError("Invalid attempt to determine key in ext_set_child_storage"))?;
let value = this.memory.get(value_data, value_len as usize).map_err(|_| UserError("Invalid attempt to determine value in ext_set_child_storage"))?;
if let Some(_preimage) = this.hash_lookup.get(&key) {
debug_trace!(
target: "wasm-trace", "*** Setting child storage: {} -> %{} -> {} [k={}]",
::primitives::hexdisplay::ascii_format(&storage_key),
::primitives::hexdisplay::ascii_format(&_preimage),
HexDisplay::from(&value),
HexDisplay::from(&key)
);
} else {
debug_trace!(
target: "wasm-trace", "*** Setting child storage: {} -> {} -> {} [k={}]",
::primitives::hexdisplay::ascii_format(&storage_key),
::primitives::hexdisplay::ascii_format(&key),
HexDisplay::from(&value),
HexDisplay::from(&key)
);
}
this.ext.set_child_storage(storage_key, key, value);
Ok(())
},
ext_clear_child_storage(storage_key_data: *const u8, storage_key_len: u32, key_data: *const u8, key_len: u32) => {
let storage_key = this.memory.get(
storage_key_data,
storage_key_len as usize
).map_err(|_| UserError("Invalid attempt to determine storage_key in ext_clear_child_storage"))?;
let key = this.memory.get(key_data, key_len as usize).map_err(|_| UserError("Invalid attempt to determine key in ext_clear_child_storage"))?;
debug_trace!(target: "wasm-trace", "*** Clearing child storage: {} -> {} [k={}]",
::primitives::hexdisplay::ascii_format(&storage_key),
if let Some(_preimage) = this.hash_lookup.get(&key) {
format!("%{}", ::primitives::hexdisplay::ascii_format(&_preimage))
} else {
format!(" {}", ::primitives::hexdisplay::ascii_format(&key))
}, HexDisplay::from(&key));
this.ext.clear_child_storage(&storage_key, &key);
Ok(())
},
ext_clear_storage(key_data: *const u8, key_len: u32) => {
let key = this.memory.get(key_data, key_len as usize).map_err(|_| UserError("Invalid attempt to determine key in ext_clear_storage"))?;
debug_trace!(target: "wasm-trace", "*** Clearing storage: {} [k={}]",
@@ -196,14 +236,33 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
let key = this.memory.get(key_data, key_len as usize).map_err(|_| UserError("Invalid attempt to determine key in ext_exists_storage"))?;
Ok(if this.ext.exists_storage(&key) { 1 } else { 0 })
},
ext_exists_child_storage(storage_key_data: *const u8, storage_key_len: u32, key_data: *const u8, key_len: u32) -> u32 => {
let storage_key = this.memory.get(
storage_key_data,
storage_key_len as usize
).map_err(|_| UserError("Invalid attempt to determine storage_key in ext_exists_child_storage"))?;
let key = this.memory.get(key_data, key_len as usize).map_err(|_| UserError("Invalid attempt to determine key in ext_exists_child_storage"))?;
Ok(if this.ext.exists_child_storage(&storage_key, &key) { 1 } else { 0 })
},
ext_clear_prefix(prefix_data: *const u8, prefix_len: u32) => {
let prefix = this.memory.get(prefix_data, prefix_len as usize).map_err(|_| UserError("Invalid attempt to determine prefix in ext_clear_prefix"))?;
this.ext.clear_prefix(&prefix);
Ok(())
},
ext_kill_child_storage(storage_key_data: *const u8, storage_key_len: u32) => {
let storage_key = this.memory.get(
storage_key_data,
storage_key_len as usize
).map_err(|_| UserError("Invalid attempt to determine storage_key in ext_kill_child_storage"))?;
this.ext.kill_child_storage(&storage_key);
Ok(())
},
// 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(|_| UserError("Invalid attempt to determine key in ext_get_allocated_storage"))?;
let key = this.memory.get(
key_data,
key_len as usize
).map_err(|_| UserError("Invalid attempt to determine key in ext_get_allocated_storage"))?;
let maybe_value = this.ext.storage(&key);
debug_trace!(target: "wasm-trace", "*** Getting storage: {} == {} [k={}]",
@@ -213,9 +272,9 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
format!(" {}", ::primitives::hexdisplay::ascii_format(&key))
},
if let Some(ref b) = maybe_value {
format!("{}", HexDisplay::from(b))
&format!("{}", HexDisplay::from(b))
} else {
"<empty>".to_owned()
"<empty>"
},
HexDisplay::from(&key)
);
@@ -232,6 +291,45 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
Ok(0)
}
},
// return 0 and place u32::max_value() into written_out if no value exists for the key.
ext_get_allocated_child_storage(storage_key_data: *const u8, storage_key_len: u32, key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8 => {
let storage_key = this.memory.get(
storage_key_data,
storage_key_len as usize
).map_err(|_| UserError("Invalid attempt to determine storage_key in ext_get_allocated_child_storage"))?;
let key = this.memory.get(
key_data,
key_len as usize
).map_err(|_| UserError("Invalid attempt to determine key in ext_get_allocated_child_storage"))?;
let maybe_value = this.ext.child_storage(&storage_key, &key);
debug_trace!(target: "wasm-trace", "*** Getting child storage: {} -> {} == {} [k={}]",
::primitives::hexdisplay::ascii_format(&storage_key),
if let Some(_preimage) = this.hash_lookup.get(&key) {
format!("%{}", ::primitives::hexdisplay::ascii_format(&_preimage))
} else {
format!(" {}", ::primitives::hexdisplay::ascii_format(&key))
},
if let Some(ref b) = maybe_value {
&format!("{}", HexDisplay::from(b))
} else {
"<empty>"
},
HexDisplay::from(&key)
);
if let Some(value) = maybe_value {
let offset = this.heap.allocate(value.len() as u32) as u32;
this.memory.set(offset, &value).map_err(|_| UserError("Invalid attempt to set memory in ext_get_allocated_child_storage"))?;
this.memory.write_primitive(written_out, value.len() as u32)
.map_err(|_| UserError("Invalid attempt to write written_out in ext_get_allocated_child_storage"))?;
Ok(offset)
} else {
this.memory.write_primitive(written_out, u32::max_value())
.map_err(|_| UserError("Invalid attempt to write failed written_out in ext_get_allocated_child_storage"))?;
Ok(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(|_| UserError("Invalid attempt to get key in ext_get_storage_into"))?;
@@ -243,9 +341,9 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
format!(" {}", ::primitives::hexdisplay::ascii_format(&key))
},
if let Some(ref b) = maybe_value {
format!("{}", HexDisplay::from(b))
&format!("{}", HexDisplay::from(b))
} else {
"<empty>".to_owned()
"<empty>"
},
HexDisplay::from(&key)
);
@@ -259,11 +357,61 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
Ok(u32::max_value())
}
},
// return u32::max_value() if no value exists for the key.
ext_get_child_storage_into(storage_key_data: *const u8, storage_key_len: u32, key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32, value_offset: u32) -> u32 => {
let storage_key = this.memory.get(
storage_key_data,
storage_key_len as usize
).map_err(|_| UserError("Invalid attempt to determine storage_key in ext_get_child_storage_into"))?;
let key = this.memory.get(
key_data,
key_len as usize
).map_err(|_| UserError("Invalid attempt to get key in ext_get_child_storage_into"))?;
let maybe_value = this.ext.child_storage(&storage_key, &key);
debug_trace!(target: "wasm-trace", "*** Getting storage: {} -> {} == {} [k={}]",
::primitives::hexdisplay::ascii_format(&storage_key),
if let Some(_preimage) = this.hash_lookup.get(&key) {
format!("%{}", ::primitives::hexdisplay::ascii_format(&_preimage))
} else {
format!(" {}", ::primitives::hexdisplay::ascii_format(&key))
},
if let Some(ref b) = maybe_value {
&format!("{}", HexDisplay::from(b))
} else {
"<empty>"
},
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(|_| UserError("Invalid attempt to set value in ext_get_child_storage_into"))?;
Ok(written as u32)
} else {
Ok(u32::max_value())
}
},
ext_storage_root(result: *mut u8) => {
let r = this.ext.storage_root();
this.memory.set(result, r.as_ref()).map_err(|_| UserError("Invalid attempt to set memory in ext_storage_root"))?;
Ok(())
},
ext_child_storage_root(storage_key_data: *const u8, storage_key_len: u32, written_out: *mut u32) -> *mut u8 => {
let storage_key = this.memory.get(storage_key_data, storage_key_len as usize).map_err(|_| UserError("Invalid attempt to determine storage_key in ext_child_storage_root"))?;
let r = this.ext.child_storage_root(&storage_key);
if let Some(value) = r {
let offset = this.heap.allocate(value.len() as u32) as u32;
this.memory.set(offset, &value).map_err(|_| UserError("Invalid attempt to set memory in ext_child_storage_root"))?;
this.memory.write_primitive(written_out, value.len() as u32)
.map_err(|_| UserError("Invalid attempt to write written_out in ext_child_storage_root"))?;
Ok(offset)
} else {
this.memory.write_primitive(written_out, u32::max_value())
.map_err(|_| UserError("Invalid attempt to write failed written_out in ext_child_storage_root"))?;
Ok(0)
}
},
ext_storage_changes_root(block: u64, result: *mut u8) -> u32 => {
let r = this.ext.storage_changes_root(block);
if let Some(ref r) = r {
@@ -300,9 +448,9 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
let hashed_key = twox_128(&key);
debug_trace!(target: "xxhash", "XXhash: {} -> {}",
if let Ok(_skey) = ::std::str::from_utf8(&key) {
_skey.to_owned()
_skey
} else {
format!("{}", HexDisplay::from(&key))
&format!("{}", HexDisplay::from(&key))
},
HexDisplay::from(&hashed_key)
);
@@ -161,7 +161,7 @@ where TSubstream: AsyncRead + AsyncWrite,
// Note that `inner` is wrapped in a `Fuse`, therefore we can poll it forever.
loop {
match self.inner.poll()? {
Async::Ready(Some(mut data)) =>
Async::Ready(Some(data)) =>
return Ok(Async::Ready(Some(data.freeze()))),
Async::Ready(None) =>
if !self.requires_poll_complete && self.send_queue.is_empty() {
+2 -2
View File
@@ -195,10 +195,10 @@ impl<B: BlockT> BlockCollection<B> {
mod test {
use super::{BlockCollection, BlockData, BlockRangeState};
use message;
use runtime_primitives::testing::Block as RawBlock;
use runtime_primitives::testing::{Block as RawBlock, ExtrinsicWrapper};
use primitives::H256;
type Block = RawBlock<u64>;
type Block = RawBlock<ExtrinsicWrapper<u64>>;
fn is_empty(bc: &BlockCollection<Block>) -> bool {
bc.blocks.is_empty() &&
@@ -310,11 +310,11 @@ impl<Block: BlockT> Specialization<Block> for ConsensusGossip<Block> where
#[cfg(test)]
mod tests {
use runtime_primitives::testing::{H256, Block as RawBlock};
use runtime_primitives::testing::{H256, Block as RawBlock, ExtrinsicWrapper};
use std::time::Instant;
use super::*;
type Block = RawBlock<u64>;
type Block = RawBlock<ExtrinsicWrapper<u64>>;
#[test]
fn collects_garbage() {
+1 -1
View File
@@ -516,8 +516,8 @@ impl<B: BlockT, S: Specialization<B>, H: ExHashT> Protocol<B, S, H> {
for (who, ref mut peer) in peers.iter_mut() {
let (hashes, to_send): (Vec<_>, Vec<_>) = extrinsics
.iter()
.cloned()
.filter(|&(ref hash, _)| peer.known_extrinsics.insert(hash.clone()))
.cloned()
.unzip();
if !to_send.is_empty() {
+9
View File
@@ -72,4 +72,13 @@ pub mod well_known_keys {
/// Changes trie configuration is stored under this key.
pub const CHANGES_TRIE_CONFIG: &'static [u8] = b":changes_trie";
/// Prefix of child storage keys.
pub const CHILD_STORAGE_KEY_PREFIX: &'static [u8] = b":child_storage:";
/// Whether a key is a child storage key.
pub fn is_child_storage_key(key: &[u8]) -> bool {
key.starts_with(CHILD_STORAGE_KEY_PREFIX)
}
}
+1 -1
View File
@@ -127,7 +127,7 @@ impl<B, E, P> AuthorApi<ExHash<P>, BlockHash<P>, ExtrinsicFor<P>, Vec<ExtrinsicF
}
fn pending_extrinsics(&self) -> Result<Vec<ExtrinsicFor<P>>> {
Ok(self.pool.all(usize::max_value()))
Ok(self.pool.ready().map(|tx| tx.data.clone()).collect())
}
fn watch_extrinsic(&self, _metadata: Self::Metadata, subscriber: pubsub::Subscriber<Status<ExHash<P>, BlockHash<P>>>, xt: Bytes) {
+4 -7
View File
@@ -16,7 +16,6 @@
//! Substrate service components.
use std::fmt;
use std::sync::Arc;
use std::marker::PhantomData;
use std::ops::Deref;
@@ -39,7 +38,7 @@ use primitives::{Blake2Hasher};
pub type NetworkService<F> = network::Service<
<F as ServiceFactory>::Block,
<F as ServiceFactory>::NetworkProtocol,
<F as ServiceFactory>::ExtrinsicHash,
<<F as ServiceFactory>::Block as BlockT>::Hash,
>;
/// Code executor type for a factory.
@@ -121,16 +120,14 @@ impl<T: Serialize + DeserializeOwned + BuildStorage> RuntimeGenesis for T {}
pub trait ServiceFactory: 'static + Sized {
/// Block type.
type Block: BlockT;
/// Extrinsic hash type.
type ExtrinsicHash: ::std::hash::Hash + Eq + Copy + fmt::Debug + fmt::LowerHex + Serialize + DeserializeOwned + ::std::str::FromStr + Send + Sync + Default + 'static;
/// Network protocol extensions.
type NetworkProtocol: network::specialization::Specialization<Self::Block>;
/// Chain runtime.
type RuntimeDispatch: NativeExecutionDispatch + Send + Sync + 'static;
/// Extrinsic pool backend type for the full client.
type FullTransactionPoolApi: txpool::ChainApi<Hash = Self::ExtrinsicHash, Block = Self::Block> + Send + 'static;
type FullTransactionPoolApi: txpool::ChainApi<Hash = <Self::Block as BlockT>::Hash, Block = Self::Block> + Send + 'static;
/// Extrinsic pool backend type for the light client.
type LightTransactionPoolApi: txpool::ChainApi<Hash = Self::ExtrinsicHash, Block = Self::Block> + 'static;
type LightTransactionPoolApi: txpool::ChainApi<Hash = <Self::Block as BlockT>::Hash, Block = Self::Block> + 'static;
/// Genesis configuration for the runtime.
type Genesis: RuntimeGenesis;
/// Other configuration for service members.
@@ -202,7 +199,7 @@ pub trait Components: 'static {
type Executor: 'static + client::CallExecutor<FactoryBlock<Self::Factory>, Blake2Hasher> + Send + Sync;
/// Extrinsic pool type.
type TransactionPoolApi: 'static + txpool::ChainApi<
Hash = <Self::Factory as ServiceFactory>::ExtrinsicHash,
Hash = <<Self::Factory as ServiceFactory>::Block as BlockT>::Hash,
Block = FactoryBlock<Self::Factory>
>;
+166 -4
View File
@@ -59,8 +59,9 @@ pub mod chain_ops;
use std::io;
use std::net::SocketAddr;
use std::sync::Arc;
use std::collections::HashMap;
#[doc(hidden)]
pub use std::{ops::Deref, result::Result, sync::Arc};
use futures::prelude::*;
use parking_lot::Mutex;
use keystore::Store as Keystore;
@@ -68,7 +69,8 @@ use client::BlockchainEvents;
use runtime_primitives::traits::{Header, As};
use runtime_primitives::generic::BlockId;
use exit_future::Signal;
use tokio::runtime::TaskExecutor;
#[doc(hidden)]
pub use tokio::runtime::TaskExecutor;
use substrate_executor::NativeExecutor;
use codec::{Encode, Decode};
@@ -393,13 +395,13 @@ impl<C: Components> TransactionPoolAdapter<C> {
impl<C: Components> network::TransactionPool<ComponentExHash<C>, ComponentBlock<C>> for TransactionPoolAdapter<C> {
fn transactions(&self) -> Vec<(ComponentExHash<C>, ComponentExtrinsic<C>)> {
self.pool.ready(|pending| pending
self.pool.ready()
.map(|t| {
let hash = t.hash.clone();
let ex: ComponentExtrinsic<C> = t.data.clone();
(hash, ex)
})
.collect())
.collect()
}
fn import(&self, transaction: &ComponentExtrinsic<C>) -> Option<ComponentExHash<C>> {
@@ -438,3 +440,163 @@ impl<C: Components> network::TransactionPool<ComponentExHash<C>, ComponentBlock<
self.pool.on_broadcasted(propagations)
}
}
/// Creates a simple `Service` implementation.
/// This `Service` just holds an instance to a `service::Service` and implements `Deref`.
/// It also provides a `new` function that takes a `config` and a `TaskExecutor`.
#[macro_export]
macro_rules! construct_simple_service {
(
$name: ident
) => {
pub struct $name<C: $crate::Components> {
inner: $crate::Service<C>,
}
impl<C: $crate::Components> $name<C> {
fn new(
config: FactoryFullConfiguration<C::Factory>,
executor: $crate::TaskExecutor
) -> $crate::Result<Self, $crate::Error> {
Ok(
Self {
inner: $crate::Service::new(config, executor)?
}
)
}
}
impl<C: $crate::Components> $crate::Deref for $name<C> {
type Target = $crate::Service<C>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
}
}
/// Constructs a service factory with the given name that implements the `ServiceFactory` trait.
/// The required parameters are required to be given in the exact order. Some parameters are followed
/// by `{}` blocks. These blocks are required and used to initialize the given parameter.
/// In these block it is required to write a closure that takes the same number of arguments,
/// the corresponding function in the `ServiceFactory` trait provides.
///
/// # Example
///
/// ```nocompile
/// construct_service_factory! {
/// struct Factory {
/// // Declare the block type
/// Block = Block,
/// // Declare the network protocol and give an initializer.
/// NetworkProtocol = NodeProtocol { |config| Ok(NodeProtocol::new()) },
/// RuntimeDispatch = node_executor::Executor,
/// FullTransactionPoolApi = transaction_pool::ChainApi<FullBackend<Self>, FullExecutor<Self>, Block>
/// { |config, client| Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client))) },
/// LightTransactionPoolApi = transaction_pool::ChainApi<LightBackend<Self>, LightExecutor<Self>, Block>
/// { |config, client| Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client))) },
/// Genesis = GenesisConfig,
/// Configuration = (),
/// FullService = Service<FullComponents<Self>>
/// { |config, executor| Service::<FullComponents<Factory>>::new(config, executor) },
/// LightService = Service<LightComponents<Self>>
/// { |config, executor| Service::<LightComponents<Factory>>::new(config, executor) },
/// // Declare the import queue. The import queue is special as it takes two initializers.
/// // The first one is for the initializing the full import queue and the second for the
/// // light import queue.
/// ImportQueue = BasicQueue<Block, NoneVerifier>
/// { |_, _| Ok(BasicQueue::new(Arc::new(NoneVerifier {}))) }
/// { |_, _| Ok(BasicQueue::new(Arc::new(NoneVerifier {}))) },
/// }
/// }
/// ```
#[macro_export]
macro_rules! construct_service_factory {
(
$(#[$attr:meta])*
struct $name:ident {
Block = $block:ty,
NetworkProtocol = $protocol:ty { $( $protocol_init:tt )* },
RuntimeDispatch = $dispatch:ty,
FullTransactionPoolApi = $full_transaction:ty { $( $full_transaction_init:tt )* },
LightTransactionPoolApi = $light_transaction:ty { $( $light_transaction_init:tt )* },
Genesis = $genesis:ty,
Configuration = $config:ty,
FullService = $full_service:ty { $( $full_service_init:tt )* },
LightService = $light_service:ty { $( $light_service_init:tt )* },
ImportQueue = $import_queue:ty
{ $( $full_import_queue_init:tt )* }
{ $( $light_import_queue_init:tt )* },
}
) => {
$( #[$attr] )*
pub struct $name {}
#[allow(unused_variables)]
impl $crate::ServiceFactory for $name {
type Block = $block;
type NetworkProtocol = $protocol;
type RuntimeDispatch = $dispatch;
type FullTransactionPoolApi = $full_transaction;
type LightTransactionPoolApi = $light_transaction;
type Genesis = $genesis;
type Configuration = $config;
type FullService = $full_service;
type LightService = $light_service;
type ImportQueue = $import_queue;
fn build_full_transaction_pool(
config: $crate::TransactionPoolOptions,
client: $crate::Arc<$crate::FullClient<Self>>
) -> $crate::Result<$crate::TransactionPool<Self::FullTransactionPoolApi>, $crate::Error>
{
( $( $full_transaction_init )* ) (config, client)
}
fn build_light_transaction_pool(
config: $crate::TransactionPoolOptions,
client: $crate::Arc<$crate::LightClient<Self>>
) -> $crate::Result<$crate::TransactionPool<Self::LightTransactionPoolApi>, $crate::Error>
{
( $( $light_transaction_init )* ) (config, client)
}
fn build_network_protocol(config: &$crate::FactoryFullConfiguration<Self>)
-> $crate::Result<Self::NetworkProtocol, $crate::Error>
{
( $( $protocol_init )* ) (config)
}
fn build_full_import_queue(
config: &$crate::FactoryFullConfiguration<Self>,
client: $crate::Arc<$crate::FullClient<Self>>,
) -> $crate::Result<Self::ImportQueue, $crate::Error> {
( $( $full_import_queue_init )* ) (config, client)
}
fn build_light_import_queue(
config: &FactoryFullConfiguration<Self>,
client: Arc<$crate::LightClient<Self>>,
) -> Result<Self::ImportQueue, $crate::Error> {
( $( $light_import_queue_init )* ) (config, client)
}
fn new_light(
config: $crate::FactoryFullConfiguration<Self>,
executor: $crate::TaskExecutor
) -> $crate::Result<Self::LightService, $crate::Error>
{
( $( $light_service_init )* ) (config, executor)
}
fn new_full(
config: $crate::FactoryFullConfiguration<Self>,
executor: $crate::TaskExecutor
) -> Result<Self::FullService, $crate::Error>
{
( $( $full_service_init )* ) (config, executor)
}
}
}
}
+4 -4
View File
@@ -185,7 +185,7 @@ pub fn connectivity<F: ServiceFactory>(spec: FactoryChainSpec<F>) {
{
let mut network = TestNet::<F>::new(&temp, spec.clone(), NUM_NODES, 0, vec![], 30400);
info!("Checking star topology");
let first_address = network.full_nodes[0].1.network().node_id().unwrap();
let first_address = network.full_nodes[0].1.network().node_id().expect("No node address");
for (_, service) in network.full_nodes.iter().skip(1) {
service.network().add_reserved_peer(first_address.clone()).expect("Error adding reserved peer");
}
@@ -200,10 +200,10 @@ pub fn connectivity<F: ServiceFactory>(spec: FactoryChainSpec<F>) {
{
let mut network = TestNet::<F>::new(&temp, spec, NUM_NODES as u32, 0, vec![], 30400);
info!("Checking linked topology");
let mut address = network.full_nodes[0].1.network().node_id().unwrap();
let mut address = network.full_nodes[0].1.network().node_id().expect("No node address");
for (_, service) in network.full_nodes.iter().skip(1) {
service.network().add_reserved_peer(address.clone()).expect("Error adding reserved peer");
address = service.network().node_id().unwrap();
address = service.network().node_id().expect("No node address");
}
network.run_until_all_full(|_index, service| {
service.network().status().num_peers == NUM_NODES as usize - 1
@@ -247,7 +247,7 @@ where
let best_block = BlockId::number(first_service.client().info().unwrap().chain.best_number);
first_service.transaction_pool().submit_one(&best_block, extrinsic_factory(&first_service)).unwrap();
network.run_until_all_full(|_index, service|
service.transaction_pool().all(usize::max_value()).len() == 1
service.transaction_pool().ready().count() == 1
);
}
-2
View File
@@ -5,7 +5,6 @@ authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
parity-codec = { version = "2.1", default-features = false }
parity-codec-derive = { version = "2.1", default-features = false }
sr-std = { path = "../sr-std", default-features = false }
sr-primitives = { path = "../sr-primitives", default-features = false }
sr-version = { path = "../sr-version", default-features = false }
@@ -15,7 +14,6 @@ default = ["std"]
std = [
"sr-std/std",
"parity-codec/std",
"parity-codec-derive/std",
"sr-primitives/std",
"sr-version/std",
]
+1 -14
View File
@@ -22,8 +22,6 @@ extern crate sr_std as rstd;
extern crate sr_primitives as primitives;
#[doc(hidden)]
pub extern crate parity_codec as codec;
#[macro_use]
extern crate parity_codec_derive;
extern crate sr_version as runtime_version;
#[doc(hidden)]
@@ -429,17 +427,6 @@ macro_rules! decl_apis {
};
}
//TODO: Move into runtime!
#[derive(Encode)]
#[cfg_attr(feature = "std", derive(Debug, Decode))]
pub enum BlockBuilderError {
#[cfg(not(feature = "std"))]
Generic(&'static str),
#[cfg(feature = "std")]
Generic(String),
TimestampInFuture(u64),
}
decl_apis! {
/// The `Core` api trait that is mandantory for each runtime.
pub trait Core<Block: BlockT, AuthorityId> {
@@ -482,7 +469,7 @@ decl_apis! {
/// Generate inherent extrinsics.
fn inherent_extrinsics<InherentExtrinsic, UncheckedExtrinsic>(inherent: InherentExtrinsic) -> Vec<UncheckedExtrinsic>;
/// Check that the inherents are valid.
fn check_inherents<InherentData>(block: Block, data: InherentData) -> Result<(), BlockBuilderError>;
fn check_inherents<InherentData, Error>(block: Block, data: InherentData) -> Result<(), Error>;
/// Generate a random seed.
fn random_seed() -> <Block as BlockT>::Hash;
}
+55 -1
View File
@@ -47,6 +47,12 @@ pub fn storage(key: &[u8]) -> Option<Vec<u8>> {
.expect("storage cannot be called outside of an Externalities-provided environment.")
}
/// Get `key` from child storage and return a `Vec`, empty if there's a problem.
pub fn child_storage(storage_key: &[u8], key: &[u8]) -> Option<Vec<u8>> {
ext::with(|ext| ext.child_storage(storage_key, key).map(|s| s.to_vec()))
.expect("storage cannot be called outside of an Externalities-provided environment.")
}
/// Get `key` from storage, placing the value into `value_out` (as much of it as possible) and return
/// the number of bytes that the entry in storage had beyond the offset or None if the storage entry
/// doesn't exist at all. Note that if the buffer is smaller than the storage entry length, the returned
@@ -55,7 +61,20 @@ pub fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> Op
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_out[..written].copy_from_slice(&value[..written]);
value.len()
})).expect("read_storage cannot be called outside of an Externalities-provided environment.")
}
/// Get `key` from child storage, placing the value into `value_out` (as much of it as possible) and return
/// the number of bytes that the entry in storage had beyond the offset or None if the storage entry
/// doesn't exist at all. Note that if the buffer is smaller than the storage entry length, the returned
/// number of bytes is not equal to the number of bytes written to the `value_out`.
pub fn read_child_storage(storage_key: &[u8], key: &[u8], value_out: &mut [u8], value_offset: usize) -> Option<usize> {
ext::with(|ext| ext.child_storage(storage_key, key).map(|value| {
let value = &value[value_offset..];
let written = ::std::cmp::min(value.len(), value_out.len());
value_out[..written].copy_from_slice(&value[..written]);
value.len()
})).expect("read_storage cannot be called outside of an Externalities-provided environment.")
}
@@ -67,6 +86,13 @@ pub fn set_storage(key: &[u8], value: &[u8]) {
);
}
/// Set the child storage of a key to some value.
pub fn set_child_storage(storage_key: &[u8], key: &[u8], value: &[u8]) {
ext::with(|ext|
ext.set_child_storage(storage_key.to_vec(), key.to_vec(), value.to_vec())
);
}
/// Clear the storage of a key.
pub fn clear_storage(key: &[u8]) {
ext::with(|ext|
@@ -74,6 +100,13 @@ pub fn clear_storage(key: &[u8]) {
);
}
/// Clear the storage of a key.
pub fn clear_child_storage(storage_key: &[u8], key: &[u8]) {
ext::with(|ext|
ext.clear_child_storage(storage_key, key)
);
}
/// Check whether a given `key` exists in storage.
pub fn exists_storage(key: &[u8]) -> bool {
ext::with(|ext|
@@ -81,6 +114,13 @@ pub fn exists_storage(key: &[u8]) -> bool {
).unwrap_or(false)
}
/// Check whether a given `key` exists in storage.
pub fn exists_child_storage(storage_key: &[u8], key: &[u8]) -> bool {
ext::with(|ext|
ext.exists_child_storage(storage_key, key)
).unwrap_or(false)
}
/// Clear the storage entries with a key that starts with the given prefix.
pub fn clear_prefix(prefix: &[u8]) {
ext::with(|ext|
@@ -88,6 +128,13 @@ pub fn clear_prefix(prefix: &[u8]) {
);
}
/// Clear an entire child storage.
pub fn kill_child_storage(storage_key: &[u8]) {
ext::with(|ext|
ext.kill_child_storage(storage_key)
);
}
/// The current relay chain identifier.
pub fn chain_id() -> u64 {
ext::with(|ext|
@@ -102,6 +149,13 @@ pub fn storage_root() -> H256 {
).unwrap_or(H256::new())
}
/// "Commit" all existing operations and compute the resultant child storage root.
pub fn child_storage_root(storage_key: &[u8]) -> Option<Vec<u8>> {
ext::with(|ext|
ext.child_storage_root(storage_key)
).unwrap_or(None)
}
/// "Commit" all existing operations and get the resultant storage change root.
pub fn storage_changes_root(block: u64) -> Option<H256> {
ext::with(|ext|
+98 -1
View File
@@ -54,16 +54,24 @@ pub extern fn oom(_: ::core::alloc::Layout) -> ! {
}
extern "C" {
fn ext_free(addr: *mut u8);
fn ext_print_utf8(utf8_data: *const u8, utf8_len: u32);
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_set_child_storage(storage_key_data: *const u8, storage_key_len: u32, 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_clear_child_storage(storage_key_data: *const u8, storage_key_len: u32, key_data: *const u8, key_len: u32);
fn ext_exists_storage(key_data: *const u8, key_len: u32) -> u32;
fn ext_exists_child_storage(storage_key_data: *const u8, storage_key_len: u32, key_data: *const u8, key_len: u32) -> u32;
fn ext_clear_prefix(prefix_data: *const u8, prefix_len: u32);
fn ext_kill_child_storage(storage_key_data: *const u8, storage_key_len: u32);
fn ext_get_allocated_storage(key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8;
fn ext_get_allocated_child_storage(storage_key_data: *const u8, storage_key_len: u32, 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_get_child_storage_into(storage_key_data: *const u8, storage_key_len: u32, 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);
fn ext_child_storage_root(storage_key_data: *const u8, storage_key_len: u32, written_out: *mut u32) -> *mut u8;
fn ext_storage_changes_root(block: u64, result: *mut u8) -> u32;
fn ext_blake2_256_enumerated_trie_root(values_data: *const u8, lens_data: *const u32, lens_len: u32, result: *mut u8);
fn ext_chain_id() -> u64;
@@ -104,7 +112,24 @@ pub fn storage(key: &[u8]) -> Option<Vec<u8>> {
if length == u32::max_value() {
None
} else {
Some(Vec::from_raw_parts(ptr, length as usize, length as usize))
let ret = slice::from_raw_parts(ptr, length as usize).to_vec();
ext_free(ptr);
Some(ret)
}
}
}
/// Get `key` from child storage and return a `Vec`, empty if there's a problem.
pub fn child_storage(storage_key: &[u8], key: &[u8]) -> Option<Vec<u8>> {
let mut length: u32 = 0;
unsafe {
let ptr = ext_get_allocated_child_storage(storage_key.as_ptr(), storage_key.len() as u32, key.as_ptr(), key.len() as u32, &mut length);
if length == u32::max_value() {
None
} else {
let ret = slice::from_raw_parts(ptr, length as usize).to_vec();
ext_free(ptr);
Some(ret)
}
}
}
@@ -119,6 +144,17 @@ pub fn set_storage(key: &[u8], value: &[u8]) {
}
}
/// Set the child storage of some particular key to Some value.
pub fn set_child_storage(storage_key: &[u8], key: &[u8], value: &[u8]) {
unsafe {
ext_set_child_storage(
storage_key.as_ptr(), key.len() as u32,
key.as_ptr(), key.len() as u32,
value.as_ptr(), value.len() as u32
);
}
}
/// Clear the storage of some particular key.
pub fn clear_storage(key: &[u8]) {
unsafe {
@@ -128,6 +164,16 @@ pub fn clear_storage(key: &[u8]) {
}
}
/// Clear the storage of some particular key.
pub fn clear_child_storage(storage_key: &[u8], key: &[u8]) {
unsafe {
ext_clear_child_storage(
storage_key.as_ptr(), storage_key.len() as u32,
key.as_ptr(), key.len() as u32
);
}
}
/// Determine whether a particular key exists in storage.
pub fn exists_storage(key: &[u8]) -> bool {
unsafe {
@@ -137,6 +183,16 @@ pub fn exists_storage(key: &[u8]) -> bool {
}
}
/// Determine whether a particular key exists in storage.
pub fn exists_child_storage(storage_key: &[u8], key: &[u8]) -> bool {
unsafe {
ext_exists_child_storage(
storage_key.as_ptr(), storage_key.len() as u32,
key.as_ptr(), key.len() as u32
) != 0
}
}
/// Clear the storage entries key of which starts with the given prefix.
pub fn clear_prefix(prefix: &[u8]) {
unsafe {
@@ -147,6 +203,16 @@ pub fn clear_prefix(prefix: &[u8]) {
}
}
/// Clear an entire child storage.
pub fn kill_child_storage(storage_key: &[u8]) {
unsafe {
ext_kill_child_storage(
storage_key.as_ptr(),
storage_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) -> Option<usize> {
@@ -162,6 +228,22 @@ pub fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> Op
}
}
/// Get `key` from child 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_child_storage(storage_key: &[u8], key: &[u8], value_out: &mut [u8], value_offset: usize) -> Option<usize> {
unsafe {
match ext_get_child_storage_into(
storage_key.as_ptr(), storage_key.len() as u32,
key.as_ptr(), key.len() as u32,
value_out.as_mut_ptr(), value_out.len() as u32,
value_offset as u32
) {
none if none == u32::max_value() => None,
length => Some(length as usize),
}
}
}
/// The current storage's root.
pub fn storage_root() -> [u8; 32] {
let mut result: [u8; 32] = Default::default();
@@ -171,6 +253,21 @@ pub fn storage_root() -> [u8; 32] {
result
}
/// "Commit" all existing operations and compute the resultant child storage root.
pub fn child_storage_root(storage_key: &[u8]) -> Option<Vec<u8>> {
let mut length: u32 = 0;
unsafe {
let ptr = ext_child_storage_root(storage_key.as_ptr(), storage_key.len() as u32, &mut length);
if length == u32::max_value() {
None
} else {
let ret = slice::from_raw_parts(ptr, length as usize).to_vec();
ext_free(ptr);
Some(ret)
}
}
}
/// The current storage' changes root.
pub fn storage_changes_root(block: u64) -> Option<[u8; 32]> {
let mut result: [u8; 32] = Default::default();
-2
View File
@@ -13,7 +13,6 @@ parity-codec-derive = { version = "2.1", default-features = false }
substrate-primitives = { path = "../primitives", default-features = false }
sr-std = { path = "../sr-std", default-features = false }
sr-io = { path = "../sr-io", default-features = false }
sr-version = { path = "../sr-version", default-features = false }
log = {version = "0.4", optional = true }
[dev-dependencies]
@@ -28,7 +27,6 @@ std = [
"log",
"sr-std/std",
"sr-io/std",
"sr-version/std",
"parity-codec/std",
"substrate-primitives/std",
]
@@ -72,7 +72,7 @@ pub struct Block<Header, Extrinsic> {
impl<Header, Extrinsic> traits::Block for Block<Header, Extrinsic>
where
Header: HeaderT,
Extrinsic: Member + Codec,
Extrinsic: Member + Codec + traits::Extrinsic,
{
type Extrinsic = Extrinsic;
type Header = Header;
@@ -102,4 +102,4 @@ pub struct SignedBlock<H, E> {
pub block: Block<H, E>,
/// Block justification.
pub justification: Justification,
}
}
@@ -113,17 +113,11 @@ impl<Hash: Codec + Member, AuthorityId: Codec + Member> traits::DigestItem for D
type AuthorityId = AuthorityId;
fn as_authorities_change(&self) -> Option<&[Self::AuthorityId]> {
match *self {
DigestItem::AuthoritiesChange(ref authorities) => Some(authorities),
_ => None,
}
self.dref().as_authorities_change()
}
fn as_changes_trie_root(&self) -> Option<&Hash> {
match *self {
DigestItem::ChangesTrieRoot(ref changes_trie_root) => Some(changes_trie_root),
_ => None,
}
self.dref().as_changes_trie_root()
}
}
@@ -150,6 +144,22 @@ impl<Hash: Decode, AuthorityId: Decode> Decode for DigestItem<Hash, AuthorityId>
}
}
impl<'a, Hash: Codec + Member, AuthorityId: Codec + Member> DigestItemRef<'a, Hash, AuthorityId> {
pub fn as_authorities_change(&self) -> Option<&'a [AuthorityId]> {
match *self {
DigestItemRef::AuthoritiesChange(ref authorities) => Some(authorities),
_ => None,
}
}
pub fn as_changes_trie_root(&self) -> Option<&'a Hash> {
match *self {
DigestItemRef::ChangesTrieRoot(ref changes_trie_root) => Some(changes_trie_root),
_ => None,
}
}
}
impl<'a, Hash: Encode, AuthorityId: Encode> Encode for DigestItemRef<'a, Hash, AuthorityId> {
fn encode(&self) -> Vec<u8> {
let mut v = Vec::new();
@@ -22,7 +22,7 @@ use std::fmt;
use rstd::prelude::*;
use codec::{Decode, Encode, Input};
use traits::{self, Member, SimpleArithmetic, MaybeDisplay, CurrentHeight, BlockNumberToHash, Lookup,
Checkable};
Checkable, Extrinsic};
use super::{CheckedExtrinsic, Era};
const TRANSACTION_VERSION: u8 = 1;
@@ -56,10 +56,11 @@ impl<Address, Index, Call, Signature> UncheckedMortalExtrinsic<Address, Index, C
function,
}
}
}
/// `true` if there is a signature.
pub fn is_signed(&self) -> bool {
self.signature.is_some()
impl<Address, Index, Call, Signature> Extrinsic for UncheckedMortalExtrinsic<Address, Index, Call, Signature> {
fn is_signed(&self) -> Option<bool> {
Some(self.signature.is_some())
}
}
@@ -221,49 +222,49 @@ mod tests {
#[test]
fn unsigned_check_should_work() {
let ux = Ex::new_unsigned(DUMMY_FUNCTION);
assert!(!ux.is_signed());
assert!(!ux.is_signed().unwrap_or(false));
assert!(<Ex as Checkable<TestContext>>::check(ux, &TestContext).is_ok());
}
#[test]
fn badly_signed_check_should_fail() {
let ux = Ex::new_signed(0, DUMMY_FUNCTION, DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, vec![0u8]), Era::immortal());
assert!(ux.is_signed());
assert!(ux.is_signed().unwrap_or(false));
assert_eq!(<Ex as Checkable<TestContext>>::check(ux, &TestContext), Err("bad signature in extrinsic"));
}
#[test]
fn immortal_signed_check_should_work() {
let ux = Ex::new_signed(0, DUMMY_FUNCTION, DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, DUMMY_FUNCTION, Era::immortal(), 0u64).encode()), Era::immortal());
assert!(ux.is_signed());
assert!(ux.is_signed().unwrap_or(false));
assert_eq!(<Ex as Checkable<TestContext>>::check(ux, &TestContext), Ok(CEx { signed: Some((DUMMY_ACCOUNTID, 0)), function: DUMMY_FUNCTION }));
}
#[test]
fn mortal_signed_check_should_work() {
let ux = Ex::new_signed(0, DUMMY_FUNCTION, DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, DUMMY_FUNCTION, Era::mortal(32, 42), 42u64).encode()), Era::mortal(32, 42));
assert!(ux.is_signed());
assert!(ux.is_signed().unwrap_or(false));
assert_eq!(<Ex as Checkable<TestContext>>::check(ux, &TestContext), Ok(CEx { signed: Some((DUMMY_ACCOUNTID, 0)), function: DUMMY_FUNCTION }));
}
#[test]
fn later_mortal_signed_check_should_work() {
let ux = Ex::new_signed(0, DUMMY_FUNCTION, DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, DUMMY_FUNCTION, Era::mortal(32, 11), 11u64).encode()), Era::mortal(32, 11));
assert!(ux.is_signed());
assert!(ux.is_signed().unwrap_or(false));
assert_eq!(<Ex as Checkable<TestContext>>::check(ux, &TestContext), Ok(CEx { signed: Some((DUMMY_ACCOUNTID, 0)), function: DUMMY_FUNCTION }));
}
#[test]
fn too_late_mortal_signed_check_should_fail() {
let ux = Ex::new_signed(0, DUMMY_FUNCTION, DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, DUMMY_FUNCTION, Era::mortal(32, 10), 10u64).encode()), Era::mortal(32, 10));
assert!(ux.is_signed());
assert!(ux.is_signed().unwrap_or(false));
assert_eq!(<Ex as Checkable<TestContext>>::check(ux, &TestContext), Err("bad signature in extrinsic"));
}
#[test]
fn too_early_mortal_signed_check_should_fail() {
let ux = Ex::new_signed(0, DUMMY_FUNCTION, DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, DUMMY_FUNCTION, Era::mortal(32, 43), 43u64).encode()), Era::mortal(32, 43));
assert!(ux.is_signed());
assert!(ux.is_signed().unwrap_or(false));
assert_eq!(<Ex as Checkable<TestContext>>::check(ux, &TestContext), Err("bad signature in extrinsic"));
}
+73 -35
View File
@@ -37,7 +37,6 @@ extern crate num_traits;
extern crate integer_sqrt;
extern crate sr_std as rstd;
extern crate sr_io as runtime_io;
extern crate sr_version as runtime_version;
#[doc(hidden)]
pub extern crate parity_codec as codec;
extern crate substrate_primitives;
@@ -65,6 +64,12 @@ pub type Justification = Vec<u8>;
use traits::{Verify, Lazy};
/// A String that is a `&'static str` on `no_std` and a `String` on `std`.
#[cfg(not(feature = "std"))]
pub type RuntimeString = &'static str;
#[cfg(feature = "std")]
pub type RuntimeString = ::std::borrow::Cow<'static, str>;
#[cfg(feature = "std")]
pub use serde::{Serialize, de::DeserializeOwned};
@@ -264,19 +269,32 @@ pub fn verify_encoded_lazy<V: Verify, T: codec::Encode>(sig: &V, item: &T, signe
#[macro_export]
macro_rules! __impl_outer_config_types {
($concrete:ident $config:ident $snake:ident $($rest:ident)*) => {
(
$concrete:ident $config:ident $snake:ident < $ignore:ident > $( $rest:tt )*
) => {
#[cfg(any(feature = "std", test))]
pub type $config = $snake::GenesisConfig<$concrete>;
__impl_outer_config_types! {$concrete $($rest)*}
};
(
$concrete:ident $config:ident $snake:ident $( $rest:tt )*
) => {
#[cfg(any(feature = "std", test))]
pub type $config = $snake::GenesisConfig;
__impl_outer_config_types! {$concrete $($rest)*}
};
($concrete:ident) => ()
}
#[macro_export]
/// Implement the output "meta" module configuration struct.
macro_rules! impl_outer_config {
( pub struct $main:ident for $concrete:ident { $( $config:ident => $snake:ident, )* } ) => {
__impl_outer_config_types! { $concrete $( $config $snake )* }
(
pub struct $main:ident for $concrete:ident {
$( $config:ident => $snake:ident $( < $generic:ident > )*, )*
}
) => {
__impl_outer_config_types! { $concrete $( $config $snake $( < $generic > )* )* }
#[cfg(any(feature = "std", test))]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
@@ -321,7 +339,7 @@ macro_rules! impl_outer_log {
(
$(#[$attr:meta])*
pub enum $name:ident ($internal:ident: DigestItem<$( $genarg:ty ),*>) for $trait:ident {
$( $module:ident($( $item:ident ),*) ),*
$( $module:ident( $( $sitem:ident ),* ) ),*
}
) => {
/// Wrapper for all possible log entries for the `$trait` runtime. Provides binary-compatible
@@ -338,7 +356,7 @@ macro_rules! impl_outer_log {
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
$(#[$attr])*
#[allow(non_camel_case_types)]
enum $internal {
pub enum InternalLog {
$(
$module($module::Log<$trait>),
)*
@@ -352,14 +370,27 @@ macro_rules! impl_outer_log {
fn dref<'a>(&'a self) -> Option<$crate::generic::DigestItemRef<'a, $($genarg),*>> {
match self.0 {
$($(
$internal::$module($module::RawLog::$item(ref v)) =>
Some($crate::generic::DigestItemRef::$item(v)),
$internal::$module($module::RawLog::$sitem(ref v)) =>
Some($crate::generic::DigestItemRef::$sitem(v)),
)*)*
_ => None,
}
}
}
impl $crate::traits::DigestItem for $name {
type Hash = <$crate::generic::DigestItem<$($genarg),*> as $crate::traits::DigestItem>::Hash;
type AuthorityId = <$crate::generic::DigestItem<$($genarg),*> as $crate::traits::DigestItem>::AuthorityId;
fn as_authorities_change(&self) -> Option<&[Self::AuthorityId]> {
self.dref().and_then(|dref| dref.as_authorities_change())
}
fn as_changes_trie_root(&self) -> Option<&Self::Hash> {
self.dref().and_then(|dref| dref.as_changes_trie_root())
}
}
impl From<$crate::generic::DigestItem<$($genarg),*>> for $name {
/// Converts `generic::DigestItem` into `$name`. If `generic::DigestItem` represents
/// a system item which is supported by the runtime, it is returned.
@@ -370,8 +401,8 @@ macro_rules! impl_outer_log {
fn from(gen: $crate::generic::DigestItem<$($genarg),*>) -> Self {
match gen {
$($(
$crate::generic::DigestItem::$item(value) =>
$name($internal::$module($module::RawLog::$item(value))),
$crate::generic::DigestItem::$sitem(value) =>
$name($internal::$module($module::RawLog::$sitem(value))),
)*)*
_ => gen.as_other()
.and_then(|value| $crate::codec::Decode::decode(&mut &value[..]))
@@ -412,10 +443,10 @@ macro_rules! impl_outer_log {
}
}
impl From<$module::Log<$trait>> for $internal {
impl From<$module::Log<$trait>> for InternalLog {
/// Converts single module log item into `$internal`.
fn from(x: $module::Log<$trait>) -> Self {
$internal::$module(x)
InternalLog::$module(x)
}
}
)*
@@ -426,6 +457,7 @@ macro_rules! impl_outer_log {
mod tests {
use substrate_primitives::hash::H256;
use codec::{Encode as EncodeHidden, Decode as DecodeHidden};
use traits::DigestItem;
pub trait RuntimeT {
type AuthorityId;
@@ -437,31 +469,31 @@ mod tests {
type AuthorityId = u64;
}
mod a {
use super::RuntimeT;
pub type Log<R> = RawLog<<R as RuntimeT>::AuthorityId>;
#[derive(Serialize, Deserialize, Debug, Encode, Decode, PartialEq, Eq, Clone)]
pub enum RawLog<AuthorityId> { A1(AuthorityId), AuthoritiesChange(Vec<AuthorityId>), A3(AuthorityId) }
}
mod b {
use super::RuntimeT;
pub type Log<R> = RawLog<<R as RuntimeT>::AuthorityId>;
#[derive(Serialize, Deserialize, Debug, Encode, Decode, PartialEq, Eq, Clone)]
pub enum RawLog<AuthorityId> { B1(AuthorityId), B2(AuthorityId) }
}
// TODO try to avoid redundant brackets: a(AuthoritiesChange), b
impl_outer_log! {
pub enum Log(InternalLog: DigestItem<H256, u64>) for Runtime {
a(AuthoritiesChange), b()
}
}
#[test]
fn impl_outer_log_works() {
mod a {
use super::RuntimeT;
pub type Log<R> = RawLog<<R as RuntimeT>::AuthorityId>;
#[derive(Serialize, Deserialize, Debug, Encode, Decode, PartialEq, Eq, Clone)]
pub enum RawLog<AuthorityId> { A1(AuthorityId), AuthoritiesChange(Vec<AuthorityId>), A3(AuthorityId) }
}
mod b {
use super::RuntimeT;
pub type Log<R> = RawLog<<R as RuntimeT>::AuthorityId>;
#[derive(Serialize, Deserialize, Debug, Encode, Decode, PartialEq, Eq, Clone)]
pub enum RawLog<AuthorityId> { B1(AuthorityId), B2(AuthorityId) }
}
// TODO try to avoid redundant brackets: a(AuthoritiesChange), b
impl_outer_log! {
pub enum Log(InternalLog: DigestItem<H256, u64>) for Runtime {
a(AuthoritiesChange), b()
}
}
// encode/decode regular item
let b1: Log = b::RawLog::B1::<u64>(777).into();
let encoded_b1 = b1.encode();
@@ -487,5 +519,11 @@ mod tests {
super::generic::DigestItem::AuthoritiesChange::<H256, u64>(authorities) => assert_eq!(authorities, vec![100, 200, 300]),
_ => panic!("unexpected generic_auth_change: {:?}", generic_auth_change),
}
// check that as-style methods are working with system items
assert!(auth_change.as_authorities_change().is_some());
// check that as-style methods are not working with regular items
assert!(b1.as_authorities_change().is_none());
}
}
+30 -2
View File
@@ -17,7 +17,7 @@
//! Testing utilities.
use serde::{Serialize, de::DeserializeOwned};
use std::fmt::Debug;
use std::{fmt::Debug, ops::Deref};
use codec::Codec;
use traits::{self, Checkable, Applyable, BlakeTwo256};
use generic::DigestItem as GenDigestItem;
@@ -93,13 +93,36 @@ impl traits::Header for Header {
}
}
#[derive(PartialEq, Eq, Clone, Serialize, Deserialize, Debug, Encode, Decode)]
pub struct ExtrinsicWrapper<Xt>(Xt);
impl<Xt> traits::Extrinsic for ExtrinsicWrapper<Xt> {
fn is_signed(&self) -> Option<bool> {
None
}
}
impl<Xt> From<Xt> for ExtrinsicWrapper<Xt> {
fn from(xt: Xt) -> Self {
ExtrinsicWrapper(xt)
}
}
impl<Xt> Deref for ExtrinsicWrapper<Xt> {
type Target = Xt;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(PartialEq, Eq, Clone, Serialize, Deserialize, Debug, Encode, Decode)]
pub struct Block<Xt> {
pub header: Header,
pub extrinsics: Vec<Xt>,
}
impl<Xt: 'static + Codec + Sized + Send + Sync + Serialize + DeserializeOwned + Clone + Eq + Debug> traits::Block for Block<Xt> {
impl<Xt: 'static + Codec + Sized + Send + Sync + Serialize + DeserializeOwned + Clone + Eq + Debug + traits::Extrinsic> traits::Block for Block<Xt> {
type Extrinsic = Xt;
type Header = Header;
type Hash = <Header as traits::Header>::Hash;
@@ -125,6 +148,11 @@ impl<Call: Codec + Sync + Send + Serialize, Context> Checkable<Context> for Test
type Checked = Self;
fn check(self, _: &Context) -> Result<Self::Checked, &'static str> { Ok(self) }
}
impl<Call: Codec + Sync + Send + Serialize> traits::Extrinsic for TestXt<Call> {
fn is_signed(&self) -> Option<bool> {
None
}
}
impl<Call> Applyable for TestXt<Call> where
Call: 'static + Sized + Send + Sync + Clone + Eq + Codec + Debug + Serialize + DeserializeOwned,
{
+43 -7
View File
@@ -383,6 +383,17 @@ pub trait MaybeDisplay {}
#[cfg(not(feature = "std"))]
impl<T> MaybeDisplay for T {}
#[cfg(feature = "std")]
pub trait MaybeDecode: ::codec::Decode {}
#[cfg(feature = "std")]
impl<T: ::codec::Decode> MaybeDecode for T {}
#[cfg(not(feature = "std"))]
pub trait MaybeDecode {}
#[cfg(not(feature = "std"))]
impl<T> MaybeDecode for T {}
pub trait Member: Send + Sync + Sized + MaybeSerializeDebug + Eq + PartialEq + Clone + 'static {}
impl<T: Send + Sync + Sized + MaybeSerializeDebug + Eq + PartialEq + Clone + 'static> Member for T {}
@@ -430,7 +441,7 @@ pub trait Header: Clone + Send + Sync + Codec + Eq + MaybeSerializeDebug + 'stat
///
/// You can get an iterator over each of the `extrinsics` and retrieve the `header`.
pub trait Block: Clone + Send + Sync + Codec + Eq + MaybeSerializeDebug + 'static {
type Extrinsic: Member + Codec;
type Extrinsic: Member + Codec + Extrinsic;
type Header: Header<Hash=Self::Hash>;
type Hash: Member + ::rstd::hash::Hash + Copy + MaybeDisplay + Default + SimpleBitOps + Codec + AsRef<[u8]> + AsMut<[u8]>;
@@ -527,12 +538,37 @@ pub trait DigestItem: Codec + Member {
type AuthorityId: Member;
/// Returns Some if the entry is the `AuthoritiesChange` entry.
fn as_authorities_change(&self) -> Option<&[Self::AuthorityId]> {
None
}
fn as_authorities_change(&self) -> Option<&[Self::AuthorityId]>;
/// Returns Some if the entry is the `ChangesTrieRoot` entry.
fn as_changes_trie_root(&self) -> Option<&Self::Hash> {
None
}
fn as_changes_trie_root(&self) -> Option<&Self::Hash>;
}
/// Something that provides an inherent for a runtime.
pub trait ProvideInherent {
/// The inherent that is provided.
type Inherent: Encode + MaybeDecode;
/// The error used by this trait.
type Error: Encode + MaybeDecode;
/// The call for setting the inherent.
type Call: Encode + MaybeDecode;
/// Create the inherent extrinsics.
///
/// # Return
///
/// Returns a vector with tuples containing the index for the extrinsic and the extrinsic itself.
fn create_inherent_extrinsics(data: Self::Inherent) -> Vec<(u32, Self::Call)>;
/// Check that the given inherent is valid.
fn check_inherent<Block: self::Block, F: Fn(&Block::Extrinsic) -> Option<&Self::Call>>(
block: &Block, data: Self::Inherent, extract_function: &F
) -> Result<(), Self::Error>;
}
/// Something that acts like an `Extrinsic`.
pub trait Extrinsic {
/// Is this `Extrinsic` signed?
/// If no information are available about signed/unsigned, `None` should be returned.
fn is_signed(&self) -> Option<bool> { None }
}
@@ -33,11 +33,11 @@ pub type TransactionTag = Vec<u8>;
#[cfg_attr(feature = "std", derive(Debug))]
pub enum TransactionValidity {
Invalid,
Valid(
/* priority: */TransactionPriority,
/* requires: */Vec<TransactionTag>,
/* provides: */Vec<TransactionTag>,
/* longevity: */TransactionLongevity
),
Valid {
priority: TransactionPriority,
requires: Vec<TransactionTag>,
provides: Vec<TransactionTag>,
longevity: TransactionLongevity
},
Unknown,
}
+2 -1
View File
@@ -15,7 +15,8 @@
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
#[cfg(feature = "nightly")]
extern crate alloc;
#[doc(hidden)]
pub extern crate alloc;
extern "C" {
fn ext_malloc(size: usize) -> *mut u8;
+2
View File
@@ -9,6 +9,7 @@ serde_derive = { version = "1.0", optional = true }
parity-codec = { version = "2.1", default-features = false }
parity-codec-derive = { version = "2.1", default-features = false }
sr-std = { path = "../sr-std", default-features = false }
sr-primitives = { path = "../sr-primitives", default-features = false }
[features]
default = ["std"]
@@ -17,4 +18,5 @@ std = [
"serde_derive",
"parity-codec/std",
"sr-std/std",
"sr-primitives/std",
]
+5 -6
View File
@@ -29,15 +29,14 @@ extern crate sr_std as rstd;
#[macro_use]
extern crate parity_codec_derive;
extern crate sr_primitives as runtime_primitives;
#[cfg(feature = "std")]
use std::fmt;
#[cfg(feature = "std")]
use std::collections::HashSet;
#[cfg(feature = "std")]
pub type VersionString = ::std::borrow::Cow<'static, str>;
#[cfg(not(feature = "std"))]
pub type VersionString = &'static str;
use runtime_primitives::RuntimeString;
/// The identity of a particular API interface that the runtime might provide.
pub type ApiId = [u8; 8];
@@ -80,14 +79,14 @@ pub struct RuntimeVersion {
/// Identifies the different Substrate runtimes. There'll be at least polkadot and node.
/// A different on-chain spec_name to that of the native runtime would normally result
/// in node not attempting to sync or author blocks.
pub spec_name: VersionString,
pub spec_name: RuntimeString,
/// Name of the implementation of the spec. This is of little consequence for the node
/// and serves only to differentiate code of different implementation teams. For this
/// codebase, it will be parity-polkadot. If there were a non-Rust implementation of the
/// Polkadot runtime (e.g. C++), then it would identify itself with an accordingly different
/// `impl_name`.
pub impl_name: VersionString,
pub impl_name: RuntimeString,
/// `authoring_version` is the version of the authorship interface. An authoring node
/// will not attempt to author blocks unless this is equal to its native runtime.
+113 -16
View File
@@ -23,7 +23,7 @@ use std::marker::PhantomData;
use hash_db::Hasher;
use trie_backend::TrieBackend;
use trie_backend_essence::TrieBackendStorage;
use substrate_trie::{TrieDBMut, TrieMut, MemoryDB, trie_root};
use substrate_trie::{TrieDBMut, TrieMut, MemoryDB, trie_root, child_trie_root};
use heapsize::HeapSizeOf;
/// A state backend is used to read state data and can have changes committed
@@ -35,7 +35,7 @@ pub trait Backend<H: Hasher> {
type Error: super::Error;
/// Storage changes to be applied if committing
type Transaction;
type Transaction: Consolidate + Default;
/// Type of trie backend storage.
type TrieBackendStorage: TrieBackendStorage<H>;
@@ -43,11 +43,22 @@ pub trait Backend<H: Hasher> {
/// Get keyed storage associated with specific address, or None if there is nothing associated.
fn storage(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error>;
/// Get keyed child storage associated with specific address, or None if there is nothing associated.
fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error>;
/// true if a key exists in storage.
fn exists_storage(&self, key: &[u8]) -> Result<bool, Self::Error> {
Ok(self.storage(key)?.is_some())
}
/// true if a key exists in child storage.
fn exists_child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result<bool, Self::Error> {
Ok(self.child_storage(storage_key, key)?.is_some())
}
/// Retrieve all entries keys of child storage and call `f` for each of those keys.
fn for_keys_in_child_storage<F: FnMut(&[u8])>(&self, storage_key: &[u8], f: F);
/// Retrieve all entries keys of which start with the given prefix and
/// call `f` for each of those keys.
fn for_keys_with_prefix<F: FnMut(&[u8])>(&self, prefix: &[u8], f: F);
@@ -59,6 +70,13 @@ pub trait Backend<H: Hasher> {
I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>,
H::Out: Ord;
/// Calculate the child storage root, with given delta over what is already stored in
/// the backend, and produce a "transaction" that can be used to commit.
fn child_storage_root<I>(&self, storage_key: &[u8], delta: I) -> (Vec<u8>, Self::Transaction)
where
I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>,
H::Out: Ord;
/// Get all key/value pairs into a Vec.
fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)>;
@@ -66,6 +84,30 @@ pub trait Backend<H: Hasher> {
fn try_into_trie_backend(self) -> Option<TrieBackend<Self::TrieBackendStorage, H>>;
}
/// Trait that allows consolidate two transactions together.
pub trait Consolidate {
/// Consolidate two transactions into one.
fn consolidate(&mut self, other: Self);
}
impl Consolidate for () {
fn consolidate(&mut self, _: Self) {
()
}
}
impl Consolidate for Vec<(Option<Vec<u8>>, Vec<u8>, Option<Vec<u8>>)> {
fn consolidate(&mut self, mut other: Self) {
self.append(&mut other);
}
}
impl<H: Hasher> Consolidate for MemoryDB<H> {
fn consolidate(&mut self, other: Self) {
MemoryDB::consolidate(self, other)
}
}
/// Error impossible.
// TODO: use `!` type when stabilized.
#[derive(Debug)]
@@ -85,7 +127,7 @@ impl error::Error for Void {
/// tests.
#[derive(Eq)]
pub struct InMemory<H> {
inner: HashMap<Vec<u8>, Vec<u8>>,
inner: HashMap<Option<Vec<u8>>, HashMap<Vec<u8>, Vec<u8>>>,
_hasher: PhantomData<H>,
}
@@ -117,10 +159,10 @@ impl<H: Hasher> InMemory<H> where H::Out: HeapSizeOf {
/// Copy the state, with applied updates
pub fn update(&self, changes: <Self as Backend<H>>::Transaction) -> Self {
let mut inner: HashMap<_, _> = self.inner.clone();
for (key, val) in changes {
for (storage_key, key, val) in changes {
match val {
Some(v) => { inner.insert(key, v); },
None => { inner.remove(&key); },
Some(v) => { inner.entry(storage_key).or_default().insert(key, v); },
None => { inner.entry(storage_key).or_default().remove(&key); },
}
}
@@ -128,8 +170,8 @@ impl<H: Hasher> InMemory<H> where H::Out: HeapSizeOf {
}
}
impl<H> From<HashMap<Vec<u8>, Vec<u8>>> for InMemory<H> {
fn from(inner: HashMap<Vec<u8>, Vec<u8>>) -> Self {
impl<H> From<HashMap<Option<Vec<u8>>, HashMap<Vec<u8>, Vec<u8>>>> for InMemory<H> {
fn from(inner: HashMap<Option<Vec<u8>>, HashMap<Vec<u8>, Vec<u8>>>) -> Self {
InMemory {
inner: inner,
_hasher: PhantomData,
@@ -137,23 +179,42 @@ impl<H> From<HashMap<Vec<u8>, Vec<u8>>> for InMemory<H> {
}
}
impl<H> From<HashMap<Vec<u8>, Vec<u8>>> for InMemory<H> {
fn from(inner: HashMap<Vec<u8>, Vec<u8>>) -> Self {
let mut expanded = HashMap::new();
expanded.insert(None, inner);
InMemory {
inner: expanded,
_hasher: PhantomData,
}
}
}
impl super::Error for Void {}
impl<H: Hasher> Backend<H> for InMemory<H> where H::Out: HeapSizeOf {
type Error = Void;
type Transaction = Vec<(Vec<u8>, Option<Vec<u8>>)>;
type Transaction = Vec<(Option<Vec<u8>>, Vec<u8>, Option<Vec<u8>>)>;
type TrieBackendStorage = MemoryDB<H>;
fn storage(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
Ok(self.inner.get(key).map(Clone::clone))
Ok(self.inner.get(&None).and_then(|map| map.get(key).map(Clone::clone)))
}
fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
Ok(self.inner.get(&Some(storage_key.to_vec())).and_then(|map| map.get(key).map(Clone::clone)))
}
fn exists_storage(&self, key: &[u8]) -> Result<bool, Self::Error> {
Ok(self.inner.get(key).is_some())
Ok(self.inner.get(&None).map(|map| map.get(key).is_some()).unwrap_or(false))
}
fn for_keys_with_prefix<F: FnMut(&[u8])>(&self, prefix: &[u8], f: F) {
self.inner.keys().filter(|key| key.starts_with(prefix)).map(|k| &**k).for_each(f);
self.inner.get(&None).map(|map| map.keys().filter(|key| key.starts_with(prefix)).map(|k| &**k).for_each(f));
}
fn for_keys_in_child_storage<F: FnMut(&[u8])>(&self, storage_key: &[u8], mut f: F) {
self.inner.get(&Some(storage_key.to_vec())).map(|map| map.keys().for_each(|k| f(&k)));
}
fn storage_root<I>(&self, delta: I) -> (H::Out, Self::Transaction)
@@ -161,7 +222,7 @@ impl<H: Hasher> Backend<H> for InMemory<H> where H::Out: HeapSizeOf {
I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>,
<H as Hasher>::Out: Ord,
{
let existing_pairs = self.inner.iter().map(|(k, v)| (k.clone(), Some(v.clone())));
let existing_pairs = self.inner.get(&None).into_iter().flat_map(|map| map.iter().map(|(k, v)| (k.clone(), Some(v.clone()))));
let transaction: Vec<_> = delta.into_iter().collect();
let root = trie_root::<H, _, _, _>(existing_pairs.chain(transaction.iter().cloned())
@@ -170,16 +231,52 @@ impl<H: Hasher> Backend<H> for InMemory<H> where H::Out: HeapSizeOf {
.filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val)))
);
(root, transaction)
let full_transaction = transaction.into_iter().map(|(k, v)| (None, k, v)).collect();
(root, full_transaction)
}
fn child_storage_root<I>(&self, storage_key: &[u8], delta: I) -> (Vec<u8>, Self::Transaction)
where
I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>,
H::Out: Ord
{
let storage_key = storage_key.to_vec();
let existing_pairs = self.inner.get(&Some(storage_key.clone())).into_iter().flat_map(|map| map.iter().map(|(k, v)| (k.clone(), Some(v.clone()))));
let transaction: Vec<_> = delta.into_iter().collect();
let root = child_trie_root::<H, _, _, _>(
&storage_key,
existing_pairs.chain(transaction.iter().cloned())
.collect::<HashMap<_, _>>()
.into_iter()
.filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val)))
);
let full_transaction = transaction.into_iter().map(|(k, v)| (Some(storage_key.clone()), k, v)).collect();
(root, full_transaction)
}
fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
self.inner.iter().map(|(k, v)| (k.clone(), v.clone())).collect()
self.inner.get(&None).into_iter().flat_map(|map| map.iter().map(|(k, v)| (k.clone(), v.clone()))).collect()
}
fn try_into_trie_backend(self) -> Option<TrieBackend<Self::TrieBackendStorage, H>> {
let mut mdb = MemoryDB::default(); // TODO: should be more correct and use ::new()
let root = insert_into_memory_db::<H, _>(&mut mdb, self.inner.into_iter())?;
let mut root = None;
for (storage_key, map) in self.inner {
if storage_key != None {
let _ = insert_into_memory_db::<H, _>(&mut mdb, map.into_iter())?;
} else {
root = Some(insert_into_memory_db::<H, _>(&mut mdb, map.into_iter())?);
}
}
let root = match root {
Some(root) => root,
None => insert_into_memory_db::<H, _>(&mut mdb, ::std::iter::empty())?,
};
Some(TrieBackend::new(mdb, root))
}
}
@@ -73,10 +73,10 @@ fn prepare_extrinsics_input<B, H>(
where
B: Backend<H>,
H: Hasher,
{
let mut extrinsic_map = BTreeMap::<Vec<u8>, BTreeSet<u32>>::new();
for (key, val) in changes.prospective.iter().chain(changes.committed.iter()) {
for (key, val) in changes.prospective.top.iter().chain(changes.committed.top.iter()) {
let extrinsics = match val.extrinsics {
Some(ref extrinsics) => extrinsics,
None => continue,
@@ -274,7 +274,7 @@ mod test {
let (backend, storage, mut changes) = prepare_for_build();
// 110: missing from backend, set to None in overlay
changes.prospective.insert(vec![110], OverlayedValue {
changes.prospective.top.insert(vec![110], OverlayedValue {
value: None,
extrinsics: Some(vec![1].into_iter().collect())
});
@@ -31,6 +31,9 @@
//! block }, containing entries for every storage key that has been changed in
//! the last N*digest_level-1 blocks (except for genesis block), mapping these keys
//! to the set of lower-level digest blocks.
//!
//! Changes trie only contains the top level storage changes. Sub-level changes
//! are propogated through its storage root on the top level storage.
mod build;
mod build_iterator;
+102 -10
View File
@@ -17,11 +17,12 @@
//! Conrete externalities implementation.
use std::{error, fmt, cmp::Ord};
use backend::Backend;
use backend::{Backend, Consolidate};
use changes_trie::{Storage as ChangesTrieStorage, compute_changes_trie_root};
use {Externalities, OverlayedChanges};
use hash_db::Hasher;
use substrate_trie::{MemoryDB, TrieDBMut, TrieMut};
use primitives::storage::well_known_keys::is_child_storage_key;
use substrate_trie::{MemoryDB, TrieDBMut, TrieMut, default_child_trie_root, is_child_trie_key_valid};
use heapsize::HeapSizeOf;
const EXT_NOT_ALLOWED_TO_FAIL: &'static str = "Externalities not allowed to fail within runtime";
@@ -122,6 +123,31 @@ where
fn mark_dirty(&mut self) {
self.storage_transaction = None;
}
/// Fetch child storage root together with its transaction.
fn child_storage_root_transaction(&mut self, storage_key: &[u8]) -> (Vec<u8>, B::Transaction) {
self.mark_dirty();
let (root, transaction) = {
let delta = self.overlay.committed.children.get(storage_key)
.into_iter()
.flat_map(|map| map.1.iter().map(|(k, v)| (k.clone(), v.clone())))
.chain(self.overlay.prospective.children.get(storage_key)
.into_iter()
.flat_map(|map| map.1.iter().map(|(k, v)| (k.clone(), v.clone()))));
self.backend.child_storage_root(storage_key, delta)
};
let root_val = if root == default_child_trie_root::<H>(storage_key) {
None
} else {
Some(root.clone())
};
self.overlay.sync_child_storage_root(storage_key, root_val);
(root, transaction)
}
}
#[cfg(test)]
@@ -137,8 +163,8 @@ where
self.backend.pairs().iter()
.map(|&(ref k, ref v)| (k.to_vec(), Some(v.to_vec())))
.chain(self.overlay.committed.clone().into_iter().map(|(k, v)| (k, v.value)))
.chain(self.overlay.prospective.clone().into_iter().map(|(k, v)| (k, v.value)))
.chain(self.overlay.committed.top.clone().into_iter().map(|(k, v)| (k, v.value)))
.chain(self.overlay.prospective.top.clone().into_iter().map(|(k, v)| (k, v.value)))
.collect::<HashMap<_, _>>()
.into_iter()
.filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val)))
@@ -158,6 +184,11 @@ where
self.backend.storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL))
}
fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Option<Vec<u8>> {
self.overlay.child_storage(storage_key, key).map(|x| x.map(|x| x.to_vec())).unwrap_or_else(||
self.backend.child_storage(storage_key, key).expect(EXT_NOT_ALLOWED_TO_FAIL))
}
fn exists_storage(&self, key: &[u8]) -> bool {
match self.overlay.storage(key) {
Some(x) => x.is_some(),
@@ -165,12 +196,52 @@ where
}
}
fn exists_child_storage(&self, storage_key: &[u8], key: &[u8]) -> bool {
match self.overlay.child_storage(storage_key, key) {
Some(x) => x.is_some(),
_ => self.backend.exists_child_storage(storage_key, key).expect(EXT_NOT_ALLOWED_TO_FAIL),
}
}
fn place_storage(&mut self, key: Vec<u8>, value: Option<Vec<u8>>) {
if is_child_storage_key(&key) {
warn!(target: "trie", "Refuse to directly set child storage key");
return;
}
self.mark_dirty();
self.overlay.set_storage(key, value);
}
fn place_child_storage(&mut self, storage_key: Vec<u8>, key: Vec<u8>, value: Option<Vec<u8>>) -> bool {
if !is_child_storage_key(&storage_key) || !is_child_trie_key_valid::<H>(&storage_key) {
return false;
}
self.mark_dirty();
self.overlay.set_child_storage(storage_key, key, value);
true
}
fn kill_child_storage(&mut self, storage_key: &[u8]) {
if !is_child_storage_key(storage_key) || !is_child_trie_key_valid::<H>(storage_key) {
return;
}
self.mark_dirty();
self.overlay.clear_child_storage(storage_key);
self.backend.for_keys_in_child_storage(storage_key, |key| {
self.overlay.set_child_storage(storage_key.to_vec(), key.to_vec(), None);
});
}
fn clear_prefix(&mut self, prefix: &[u8]) {
if is_child_storage_key(prefix) {
warn!(target: "trie", "Refuse to directly clear prefix that is part of child storage key");
return;
}
self.mark_dirty();
self.overlay.clear_prefix(prefix);
self.backend.for_keys_with_prefix(prefix, |key| {
@@ -183,19 +254,40 @@ where
}
fn storage_root(&mut self) -> H::Out {
if let Some((_, ref root)) = self.storage_transaction {
if let Some((_, ref root)) = self.storage_transaction {
return root.clone();
}
// compute and memoize
let delta = self.overlay.committed.iter().map(|(k, v)| (k.clone(), v.value.clone()))
.chain(self.overlay.prospective.iter().map(|(k, v)| (k.clone(), v.value.clone())));
let mut transaction = B::Transaction::default();
let child_storage_keys: Vec<_> = self.overlay.prospective.children.keys().cloned().collect();
let (root, transaction) = self.backend.storage_root(delta);
for key in child_storage_keys {
let (_, t) = self.child_storage_root_transaction(&key);
transaction.consolidate(t);
}
// compute and memoize
let delta = self.overlay.committed.top.iter().map(|(k, v)| (k.clone(), v.value.clone()))
.chain(self.overlay.prospective.top.iter().map(|(k, v)| (k.clone(), v.value.clone())));
let (root, t) = self.backend.storage_root(delta);
transaction.consolidate(t);
self.storage_transaction = Some((transaction, root));
root
}
fn child_storage_root(&mut self, storage_key: &[u8]) -> Option<Vec<u8>> {
if !is_child_storage_key(storage_key) || !is_child_trie_key_valid::<H>(storage_key) {
return None;
}
if self.storage_transaction.is_some() {
return Some(self.storage(storage_key).unwrap_or(default_child_trie_root::<H>(storage_key)));
}
Some(self.child_storage_root_transaction(storage_key).0)
}
fn storage_changes_root(&mut self, block: u64) -> Option<H::Out> {
let root_and_tx = compute_changes_trie_root::<_, T, H>(
self.backend,
@@ -287,7 +379,7 @@ mod tests {
#[test]
fn storage_changes_root_is_some_when_extrinsic_changes_are_empty() {
let mut overlay = prepare_overlay_with_changes();
overlay.prospective.get_mut(&vec![1]).unwrap().value = None;
overlay.prospective.top.get_mut(&vec![1]).unwrap().value = None;
let storage = TestChangesTrieStorage::new();
let backend = TestBackend::default();
let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage));
+49 -6
View File
@@ -97,33 +97,62 @@ pub trait Externalities<H: Hasher> {
/// Read storage of current contract being called.
fn storage(&self, key: &[u8]) -> Option<Vec<u8>>;
/// Read child storage of current contract being called.
fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Option<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));
}
/// Set child storage entry `key` of current contract being called (effective immediately).
fn set_child_storage(&mut self, storage_key: Vec<u8>, key: Vec<u8>, value: Vec<u8>) -> bool {
self.place_child_storage(storage_key, 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);
}
/// Clear a storage entry (`key`) of current contract being called (effective immediately).
/// Clear a child storage entry (`key`) of current contract being called (effective immediately).
fn clear_child_storage(&mut self, storage_key: &[u8], key: &[u8]) -> bool {
self.place_child_storage(storage_key.to_vec(), key.to_vec(), None)
}
/// Whether a storage entry exists.
fn exists_storage(&self, key: &[u8]) -> bool {
self.storage(key).is_some()
}
/// Whether a child storage entry exists.
fn exists_child_storage(&self, storage_key: &[u8], key: &[u8]) -> bool {
self.child_storage(storage_key, key).is_some()
}
/// Clear an entire child storage.
fn kill_child_storage(&mut self, storage_key: &[u8]);
/// Clear storage entries which keys are start with the given prefix.
fn clear_prefix(&mut self, prefix: &[u8]);
/// 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>>);
/// Set or clear a child storage entry. Return whether the operation succeeds.
fn place_child_storage(&mut self, storage_key: Vec<u8>, key: Vec<u8>, value: Option<Vec<u8>>) -> bool;
/// Get the identity of the chain.
fn chain_id(&self) -> u64;
/// Get the trie root of the current storage map.
/// Get the trie root of the current storage map. This will also update all child storage keys in the top-level storage map.
fn storage_root(&mut self) -> H::Out where H::Out: Ord;
/// Get the trie root of a child storage map. This will also update the value of the child storage keys in the top-level storage map. If the storage root equals default hash as defined by trie, the key in top-level storage map will be removed.
///
/// Returns None if key provided is not a storage key. This can due to not being started with CHILD_STORAGE_KEY_PREFIX, or the trie implementation regards the key as invalid.
fn child_storage_root(&mut self, storage_key: &[u8]) -> Option<Vec<u8>>;
/// Get the change trie root of the current storage overlay at given block.
fn storage_changes_root(&mut self, block: u64) -> Option<H::Out> where H::Out: Ord;
}
@@ -477,6 +506,7 @@ where
mod tests {
use std::collections::HashMap;
use codec::Encode;
use overlayed_changes::OverlayedValue;
use super::*;
use super::backend::InMemory;
use super::ext::Ext;
@@ -600,12 +630,12 @@ mod tests {
let backend = InMemory::<Blake2Hasher>::from(initial).try_into_trie_backend().unwrap();
let mut overlay = OverlayedChanges {
committed: map![
b"aba".to_vec() => Some(b"1312".to_vec()).into(),
b"bab".to_vec() => Some(b"228".to_vec()).into()
b"aba".to_vec() => OverlayedValue::from(Some(b"1312".to_vec())),
b"bab".to_vec() => OverlayedValue::from(Some(b"228".to_vec()))
],
prospective: map![
b"abd".to_vec() => Some(b"69".to_vec()).into(),
b"bbd".to_vec() => Some(b"42".to_vec()).into()
b"abd".to_vec() => OverlayedValue::from(Some(b"69".to_vec())),
b"bbd".to_vec() => OverlayedValue::from(Some(b"42".to_vec()))
],
..Default::default()
};
@@ -631,6 +661,19 @@ mod tests {
);
}
#[test]
fn set_child_storage_works() {
let backend = InMemory::<Blake2Hasher>::default().try_into_trie_backend().unwrap();
let changes_trie_storage = InMemoryChangesTrieStorage::new();
let mut overlay = OverlayedChanges::default();
let mut ext = Ext::new(&mut overlay, &backend, Some(&changes_trie_storage));
assert!(ext.set_child_storage(b":child_storage:testchild".to_vec(), b"abc".to_vec(), b"def".to_vec()));
assert_eq!(ext.child_storage(b":child_storage:testchild", b"abc"), Some(b"def".to_vec()));
ext.kill_child_storage(b":child_storage:testchild");
assert_eq!(ext.child_storage(b":child_storage:testchild", b"abc"), None);
}
#[test]
fn prove_read_and_proof_check_works() {
// fetch read proof from 'remote' full node
@@ -16,6 +16,7 @@
//! The overlayed changes to state.
#[cfg(test)] use std::iter::FromIterator;
use std::collections::{HashMap, HashSet};
use codec::Decode;
use changes_trie::{NO_EXTRINSIC_INDEX, Configuration as ChangesTrieConfig};
@@ -28,9 +29,9 @@ use primitives::storage::well_known_keys::EXTRINSIC_INDEX;
#[derive(Debug, Default, Clone)]
pub struct OverlayedChanges {
/// Changes that are not yet committed.
pub(crate) prospective: HashMap<Vec<u8>, OverlayedValue>,
pub(crate) prospective: OverlayedChangeSet,
/// Committed changes.
pub(crate) committed: HashMap<Vec<u8>, OverlayedValue>,
pub(crate) committed: OverlayedChangeSet,
/// Changes trie configuration. None by default, but could be installed by the
/// runtime if it supports change tries.
pub(crate) changes_trie_config: Option<ChangesTrieConfig>,
@@ -47,6 +48,39 @@ pub struct OverlayedValue {
pub extrinsics: Option<HashSet<u32>>,
}
/// Prospective or committed overlayed change set.
#[derive(Debug, Default, Clone)]
#[cfg_attr(test, derive(PartialEq))]
pub struct OverlayedChangeSet {
/// Top level storage changes.
pub top: HashMap<Vec<u8>, OverlayedValue>,
/// Child storage changes.
pub children: HashMap<Vec<u8>, (Option<HashSet<u32>>, HashMap<Vec<u8>, Option<Vec<u8>>>)>,
}
#[cfg(test)]
impl FromIterator<(Vec<u8>, OverlayedValue)> for OverlayedChangeSet {
fn from_iter<T: IntoIterator<Item = (Vec<u8>, OverlayedValue)>>(iter: T) -> Self {
Self {
top: iter.into_iter().collect(),
children: Default::default(),
}
}
}
impl OverlayedChangeSet {
/// Whether the change set is empty.
pub fn is_empty(&self) -> bool {
self.top.is_empty() && self.children.is_empty()
}
/// Clear the change set.
pub fn clear(&mut self) {
self.top.clear();
self.children.clear();
}
}
impl OverlayedChanges {
/// Sets the changes trie configuration.
///
@@ -68,17 +102,36 @@ impl OverlayedChanges {
/// 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))
self.prospective.top.get(key)
.or_else(|| self.committed.top.get(key))
.map(|x| x.value.as_ref().map(AsRef::as_ref))
}
/// 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 child_storage(&self, storage_key: &[u8], key: &[u8]) -> Option<Option<&[u8]>> {
if let Some(map) = self.prospective.children.get(storage_key) {
if let Some(val) = map.1.get(key) {
return Some(val.as_ref().map(AsRef::as_ref));
}
}
if let Some(map) = self.committed.children.get(storage_key) {
if let Some(val) = map.1.get(key) {
return Some(val.as_ref().map(AsRef::as_ref));
}
}
None
}
/// Inserts the given key-value pair into the prospective change set.
///
/// `None` can be used to delete a value specified by the given key.
pub(crate) fn set_storage(&mut self, key: Vec<u8>, val: Option<Vec<u8>>) {
let extrinsic_index = self.extrinsic_index();
let entry = self.prospective.entry(key).or_default();
let entry = self.prospective.top.entry(key).or_default();
entry.value = val;
if let Some(extrinsic) = extrinsic_index {
@@ -87,6 +140,57 @@ impl OverlayedChanges {
}
}
/// Inserts the given key-value pair into the prospective child change set.
///
/// `None` can be used to delete a value specified by the given key.
pub(crate) fn set_child_storage(&mut self, storage_key: Vec<u8>, key: Vec<u8>, val: Option<Vec<u8>>) {
let extrinsic_index = self.extrinsic_index();
let map_entry = self.prospective.children.entry(storage_key).or_default();
map_entry.1.insert(key, val);
if let Some(extrinsic) = extrinsic_index {
map_entry.0.get_or_insert_with(Default::default)
.insert(extrinsic);
}
}
/// Sync the child storage root.
pub(crate) fn sync_child_storage_root(&mut self, storage_key: &[u8], root: Option<Vec<u8>>) {
let entry = self.prospective.top.entry(storage_key.to_vec()).or_default();
entry.value = root;
if let Some((Some(extrinsics), _)) = self.prospective.children.get(storage_key) {
for extrinsic in extrinsics {
entry.extrinsics.get_or_insert_with(Default::default)
.insert(*extrinsic);
}
}
}
/// Clear child storage of given storage key.
///
/// NOTE that this doesn't take place immediately but written into the prospective
/// change set, and still can be reverted by [`discard_prospective`].
///
/// [`discard_prospective`]: #method.discard_prospective
pub(crate) fn clear_child_storage(&mut self, storage_key: &[u8]) {
let extrinsic_index = self.extrinsic_index();
let map_entry = self.prospective.children.entry(storage_key.to_vec()).or_default();
if let Some(extrinsic) = extrinsic_index {
map_entry.0.get_or_insert_with(Default::default)
.insert(extrinsic);
}
map_entry.1.values_mut().for_each(|e| *e = None);
if let Some((_, committed_map)) = self.committed.children.get(storage_key) {
for (key, _) in committed_map.iter() {
map_entry.1.insert(key.clone(), None);
}
}
}
/// Removes all key-value pairs which keys share the given prefix.
///
/// NOTE that this doesn't take place immediately but written into the prospective
@@ -98,7 +202,7 @@ impl OverlayedChanges {
// Iterate over all prospective and mark all keys that share
// the given prefix as removed (None).
for (key, entry) in self.prospective.iter_mut() {
for (key, entry) in self.prospective.top.iter_mut() {
if key.starts_with(prefix) {
entry.value = None;
@@ -111,9 +215,9 @@ impl OverlayedChanges {
// Then do the same with keys from commited changes.
// NOTE that we are making changes in the prospective change set.
for key in self.committed.keys() {
for key in self.committed.top.keys() {
if key.starts_with(prefix) {
let entry = self.prospective.entry(key.clone()).or_default();
let entry = self.prospective.top.entry(key.clone()).or_default();
entry.value = None;
if let Some(extrinsic) = extrinsic_index {
@@ -134,8 +238,8 @@ impl OverlayedChanges {
if self.committed.is_empty() {
::std::mem::swap(&mut self.prospective, &mut self.committed);
} else {
for (key, val) in self.prospective.drain() {
let entry = self.committed.entry(key).or_default();
for (key, val) in self.prospective.top.drain() {
let entry = self.committed.top.entry(key).or_default();
entry.value = val.value;
if let Some(prospective_extrinsics) = val.extrinsics {
@@ -143,16 +247,16 @@ impl OverlayedChanges {
.extend(prospective_extrinsics);
}
}
}
}
for (storage_key, map) in self.prospective.children.drain() {
let entry = self.committed.children.entry(storage_key).or_default();
entry.1.extend(map.1.iter().map(|(k, v)| (k.clone(), v.clone())));
/// Drain committed changes to an iterator.
///
/// Panics:
/// Will panic if there are any uncommitted prospective changes.
pub fn drain<'a>(&'a mut self) -> impl Iterator<Item=(Vec<u8>, OverlayedValue)> + 'a {
assert!(self.prospective.is_empty());
self.committed.drain()
if let Some(prospective_extrinsics) = map.0 {
entry.0.get_or_insert_with(Default::default)
.extend(prospective_extrinsics);
}
}
}
}
/// Consume `OverlayedChanges` and take committed set.
@@ -161,14 +265,14 @@ impl OverlayedChanges {
/// Will panic if there are any uncommitted prospective changes.
pub fn into_committed(self) -> impl Iterator<Item=(Vec<u8>, Option<Vec<u8>>)> {
assert!(self.prospective.is_empty());
self.committed.into_iter().map(|(k, v)| (k, v.value))
self.committed.top.into_iter().map(|(k, v)| (k, v.value))
}
/// Inserts storage entry responsible for current extrinsic index.
#[cfg(test)]
pub(crate) fn set_extrinsic_index(&mut self, extrinsic_index: u32) {
use codec::Encode;
self.prospective.insert(EXTRINSIC_INDEX.to_vec(), OverlayedValue {
self.prospective.top.insert(EXTRINSIC_INDEX.to_vec(), OverlayedValue {
value: Some(extrinsic_index.encode()),
extrinsics: None,
});
@@ -293,7 +397,7 @@ mod tests {
digest_interval: 4, digest_levels: 1,
}), true);
assert_eq!(
strip_extrinsic_index(&overlay.prospective),
strip_extrinsic_index(&overlay.prospective.top),
vec![
(vec![1], OverlayedValue { value: Some(vec![2]), extrinsics: Some(vec![0].into_iter().collect()) }),
].into_iter().collect(),
@@ -329,7 +433,7 @@ mod tests {
overlay.set_extrinsic_index(2);
overlay.set_storage(vec![1], Some(vec![6]));
assert_eq!(strip_extrinsic_index(&overlay.prospective),
assert_eq!(strip_extrinsic_index(&overlay.prospective.top),
vec![
(vec![1], OverlayedValue { value: Some(vec![6]), extrinsics: Some(vec![0, 2].into_iter().collect()) }),
(vec![3], OverlayedValue { value: Some(vec![4]), extrinsics: Some(vec![1].into_iter().collect()) }),
@@ -344,14 +448,14 @@ mod tests {
overlay.set_extrinsic_index(4);
overlay.set_storage(vec![1], Some(vec![8]));
assert_eq!(strip_extrinsic_index(&overlay.committed),
assert_eq!(strip_extrinsic_index(&overlay.committed.top),
vec![
(vec![1], OverlayedValue { value: Some(vec![6]), extrinsics: Some(vec![0, 2].into_iter().collect()) }),
(vec![3], OverlayedValue { value: Some(vec![4]), extrinsics: Some(vec![1].into_iter().collect()) }),
(vec![100], OverlayedValue { value: Some(vec![101]), extrinsics: Some(vec![NO_EXTRINSIC_INDEX].into_iter().collect()) }),
].into_iter().collect());
assert_eq!(strip_extrinsic_index(&overlay.prospective),
assert_eq!(strip_extrinsic_index(&overlay.prospective.top),
vec![
(vec![1], OverlayedValue { value: Some(vec![8]), extrinsics: Some(vec![4].into_iter().collect()) }),
(vec![3], OverlayedValue { value: Some(vec![7]), extrinsics: Some(vec![3].into_iter().collect()) }),
@@ -359,7 +463,7 @@ mod tests {
overlay.commit_prospective();
assert_eq!(strip_extrinsic_index(&overlay.committed),
assert_eq!(strip_extrinsic_index(&overlay.committed.top),
vec![
(vec![1], OverlayedValue { value: Some(vec![8]), extrinsics: Some(vec![0, 2, 4].into_iter().collect()) }),
(vec![3], OverlayedValue { value: Some(vec![7]), extrinsics: Some(vec![1, 3].into_iter().collect()) }),
@@ -20,7 +20,7 @@ use std::cell::RefCell;
use hash_db::Hasher;
use heapsize::HeapSizeOf;
use hash_db::HashDB;
use trie::{TrieDB, Trie, Recorder, MemoryDB, TrieError};
use trie::{Recorder, MemoryDB, TrieError, default_child_trie_root, read_trie_value_with, read_child_trie_value_with, record_all_keys};
use trie_backend::TrieBackend;
use trie_backend_essence::{Ephemeral, TrieBackendEssence, TrieBackendStorage};
use {Error, ExecutionError, Backend};
@@ -47,10 +47,21 @@ impl<'a, S, H> ProvingBackendEssence<'a, S, H>
let map_e = |e| format!("Trie lookup error: {}", e);
TrieDB::<H>::new(&eph, self.backend.root()).map_err(map_e)?
.get_with(key, &mut *self.proof_recorder)
.map(|x| x.map(|val| val.to_vec()))
.map_err(map_e)
read_trie_value_with(&eph, self.backend.root(), key, &mut *self.proof_recorder).map_err(map_e)
}
pub fn child_storage(&mut self, storage_key: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, String> {
let root = self.storage(storage_key)?.unwrap_or(default_child_trie_root::<H>(storage_key));
let mut read_overlay = MemoryDB::default();
let eph = Ephemeral::new(
self.backend.backend_storage(),
&mut read_overlay,
);
let map_e = |e| format!("Trie lookup error: {}", e);
read_child_trie_value_with(storage_key, &eph, &root, key, &mut *self.proof_recorder).map_err(map_e)
}
pub fn record_all_keys(&mut self) {
@@ -62,20 +73,7 @@ impl<'a, S, H> ProvingBackendEssence<'a, S, H>
let mut iter = move || -> Result<(), Box<TrieError<H::Out>>> {
let root = self.backend.root();
let trie = TrieDB::<H>::new(&eph, root)?;
let iter = trie.iter()?;
for x in iter {
let (key, _) = x?;
// there's currently no API like iter_with()
// => use iter to enumerate all keys AND lookup each
// key using get_with
trie.get_with(&key, &mut *self.proof_recorder)
.map(|x| x.map(|val| val.to_vec()))?;
}
Ok(())
record_all_keys::<H>(&eph, root, &mut *self.proof_recorder)
};
if let Err(e) = iter() {
@@ -128,6 +126,18 @@ impl<S, H> Backend<H> for ProvingBackend<S, H>
}.storage(key)
}
fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
ProvingBackendEssence {
backend: self.backend.essence(),
proof_recorder: &mut *self.proof_recorder.try_borrow_mut()
.expect("only fails when already borrowed; child_storage() is non-reentrant; qed"),
}.child_storage(storage_key, key)
}
fn for_keys_in_child_storage<F: FnMut(&[u8])>(&self, storage_key: &[u8], f: F) {
self.backend.for_keys_in_child_storage(storage_key, f)
}
fn for_keys_with_prefix<F: FnMut(&[u8])>(&self, prefix: &[u8], f: F) {
self.backend.for_keys_with_prefix(prefix, f)
}
@@ -142,6 +152,14 @@ impl<S, H> Backend<H> for ProvingBackend<S, H>
self.backend.storage_root(delta)
}
fn child_storage_root<I>(&self, storage_key: &[u8], delta: I) -> (Vec<u8>, Self::Transaction)
where
I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>,
H::Out: Ord
{
self.backend.child_storage_root(storage_key, delta)
}
fn try_into_trie_backend(self) -> Option<TrieBackend<Self::TrieBackendStorage, H>> {
None
}
@@ -211,7 +229,7 @@ mod tests {
#[test]
fn proof_recorded_and_checked() {
let contents = (0..64).map(|i| (vec![i], Some(vec![i]))).collect::<Vec<_>>();
let contents = (0..64).map(|i| (None, vec![i], Some(vec![i]))).collect::<Vec<_>>();
let in_memory = InMemory::<Blake2Hasher>::default();
let in_memory = in_memory.update(contents);
let in_memory_root = in_memory.storage_root(::std::iter::empty()).0;
@@ -103,6 +103,10 @@ impl<H: Hasher> Externalities<H> for TestExternalities<H> where H::Out: Ord + He
self.inner.get(key).map(|x| x.to_vec())
}
fn child_storage(&self, _storage_key: &[u8], _key: &[u8]) -> Option<Vec<u8>> {
None
}
fn place_storage(&mut self, key: Vec<u8>, maybe_value: Option<Vec<u8>>) {
self.changes.set_storage(key.clone(), maybe_value.clone());
match maybe_value {
@@ -111,6 +115,12 @@ impl<H: Hasher> Externalities<H> for TestExternalities<H> where H::Out: Ord + He
}
}
fn place_child_storage(&mut self, _storage_key: Vec<u8>, _key: Vec<u8>, _value: Option<Vec<u8>>) -> bool {
false
}
fn kill_child_storage(&mut self, _storage_key: &[u8]) { }
fn clear_prefix(&mut self, prefix: &[u8]) {
self.changes.clear_prefix(prefix);
self.inner.retain(|key, _| !key.starts_with(prefix));
@@ -122,6 +132,10 @@ impl<H: Hasher> Externalities<H> for TestExternalities<H> where H::Out: Ord + He
trie_root::<H, _, _, _>(self.inner.clone())
}
fn child_storage_root(&mut self, _storage_key: &[u8]) -> Option<Vec<u8>> {
None
}
fn storage_changes_root(&mut self, block: u64) -> Option<H::Out> {
compute_changes_trie_root::<_, _, H>(
&InMemory::default(),
@@ -18,7 +18,7 @@
use hash_db::Hasher;
use heapsize::HeapSizeOf;
use trie::{TrieDB, TrieDBMut, TrieError, Trie, TrieMut, MemoryDB};
use trie::{TrieDB, TrieError, Trie, MemoryDB, delta_trie_root, default_child_trie_root, child_delta_trie_root};
use trie_backend_essence::{TrieBackendEssence, TrieBackendStorage, Ephemeral};
use {Backend};
@@ -64,10 +64,18 @@ impl<S: TrieBackendStorage<H>, H: Hasher> Backend<H> for TrieBackend<S, H> where
self.essence.storage(key)
}
fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
self.essence.child_storage(storage_key, key)
}
fn for_keys_with_prefix<F: FnMut(&[u8])>(&self, prefix: &[u8], f: F) {
self.essence.for_keys_with_prefix(prefix, f)
}
fn for_keys_in_child_storage<F: FnMut(&[u8])>(&self, storage_key: &[u8], f: F) {
self.essence.for_keys_in_child_storage(storage_key, f)
}
fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
let mut read_overlay = MemoryDB::default(); // TODO: use new for correctness
let eph = Ephemeral::new(self.essence.backend_storage(), &mut read_overlay);
@@ -97,22 +105,45 @@ impl<S: TrieBackendStorage<H>, H: Hasher> Backend<H> for TrieBackend<S, H> where
{
let mut write_overlay = MemoryDB::default();
let mut root = *self.essence.root();
{
let mut eph = Ephemeral::new(
self.essence.backend_storage(),
&mut write_overlay,
);
let mut trie = TrieDBMut::<H>::from_existing(&mut eph, &mut root).expect("prior state root to exist"); // TODO: handle gracefully
for (key, change) in delta {
let result = match change {
Some(val) => trie.insert(&key, &val),
None => trie.remove(&key), // TODO: archive mode
};
match delta_trie_root::<H, _, _, _>(&mut eph, root, delta) {
Ok(ret) => root = ret,
Err(e) => warn!(target: "trie", "Failed to write to trie: {}", e),
}
}
if let Err(e) = result {
warn!(target: "trie", "Failed to write to trie: {}", e);
}
(root, write_overlay)
}
fn child_storage_root<I>(&self, storage_key: &[u8], delta: I) -> (Vec<u8>, Self::Transaction)
where
I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>,
H::Out: Ord
{
let mut write_overlay = MemoryDB::default();
let mut root = match self.storage(storage_key) {
Ok(value) => value.unwrap_or(default_child_trie_root::<H>(storage_key)),
Err(e) => {
warn!(target: "trie", "Failed to read child storage root: {}", e);
default_child_trie_root::<H>(storage_key)
},
};
{
let mut eph = Ephemeral::new(
self.essence.backend_storage(),
&mut write_overlay,
);
match child_delta_trie_root::<H, _, _, _>(storage_key, &mut eph, root.clone(), delta) {
Ok(ret) => root = ret,
Err(e) => warn!(target: "trie", "Failed to write to trie: {}", e),
}
}
@@ -128,6 +159,7 @@ impl<S: TrieBackendStorage<H>, H: Hasher> Backend<H> for TrieBackend<S, H> where
pub mod tests {
use std::collections::HashSet;
use primitives::{Blake2Hasher, H256};
use trie::{TrieMut, TrieDBMut};
use super::*;
fn test_db() -> (MemoryDB<Blake2Hasher>, H256) {
@@ -22,7 +22,7 @@ use std::ops::Deref;
use std::sync::Arc;
use hash_db::{self, Hasher};
use heapsize::HeapSizeOf;
use trie::{TrieDB, Trie, MemoryDB, DBValue, TrieError};
use trie::{TrieDB, Trie, MemoryDB, DBValue, TrieError, default_child_trie_root, read_trie_value, read_child_trie_value, for_keys_in_child_trie};
use changes_trie::Storage as ChangesTrieStorage;
/// Patricia trie-based storage trait.
@@ -66,8 +66,43 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
let map_e = |e| format!("Trie lookup error: {}", e);
TrieDB::<H>::new(&eph, &self.root).map_err(map_e)?
.get(key).map(|x| x.map(|val| val.to_vec())).map_err(map_e)
read_trie_value(&eph, &self.root, key).map_err(map_e)
}
/// Get the value of child storage at given key.
pub fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, String> {
let root = self.storage(storage_key)?.unwrap_or(default_child_trie_root::<H>(storage_key));
let mut read_overlay = MemoryDB::default();
let eph = Ephemeral {
storage: &self.storage,
overlay: &mut read_overlay,
};
let map_e = |e| format!("Trie lookup error: {}", e);
read_child_trie_value(storage_key, &eph, &root, key).map_err(map_e)
}
/// Retrieve all entries keys of child storage and call `f` for each of those keys.
pub fn for_keys_in_child_storage<F: FnMut(&[u8])>(&self, storage_key: &[u8], f: F) {
let root = match self.storage(storage_key) {
Ok(v) => v.unwrap_or(default_child_trie_root::<H>(storage_key)),
Err(e) => {
debug!(target: "trie", "Error while iterating child storage: {}", e);
return;
}
};
let mut read_overlay = MemoryDB::default();
let eph = Ephemeral {
storage: &self.storage,
overlay: &mut read_overlay,
};
if let Err(e) = for_keys_in_child_trie::<H, _>(storage_key, &eph, &root, f) {
debug!(target: "trie", "Error while iterating child storage: {}", e);
}
}
/// Execute given closure for all keys starting with prefix.
+9 -3
View File
@@ -54,7 +54,7 @@ use rstd::prelude::*;
use codec::{Encode, Decode};
use runtime_api::runtime::*;
use runtime_primitives::traits::{BlindCheckable, BlakeTwo256, Block as BlockT};
use runtime_primitives::traits::{BlindCheckable, BlakeTwo256, Block as BlockT, Extrinsic as ExtrinsicT};
use runtime_primitives::{ApplyResult, Ed25519Signature, transaction_validity::TransactionValidity};
use runtime_version::RuntimeVersion;
pub use primitives::hash::H256;
@@ -115,6 +115,12 @@ impl BlindCheckable for Extrinsic {
}
}
impl ExtrinsicT for Extrinsic {
fn is_signed(&self) -> Option<bool> {
Some(true)
}
}
/// An identifier for an account on this system.
pub type AccountId = H256;
/// A simple hash type for all our hashing.
@@ -185,7 +191,7 @@ impl_apis! {
}
}
impl BlockBuilder<Block, u32, u32, u32> for Runtime {
impl BlockBuilder<Block, u32, u32, u32, u32> for Runtime {
fn initialise_block(header: <Block as BlockT>::Header) {
system::initialise_block(header)
}
@@ -202,7 +208,7 @@ impl_apis! {
unimplemented!()
}
fn check_inherents(_block: Block, _data: u32) -> Result<(), runtime_api::BlockBuilderError> {
fn check_inherents(_block: Block, _data: u32) -> Result<(), u32> {
unimplemented!()
}
+4 -4
View File
@@ -131,12 +131,12 @@ pub fn validate_transaction(utx: Extrinsic) -> TransactionValidity {
p
};
TransactionValidity::Valid(
/* priority: */tx.amount,
TransactionValidity::Valid {
priority: tx.amount,
requires,
provides,
/* longevity: */64
)
longevity: 64
}
}
+1 -2
View File
@@ -470,7 +470,6 @@ name = "sr-api"
version = "0.1.0"
dependencies = [
"parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-primitives 0.1.0",
"sr-std 0.1.0",
"sr-version 0.1.0",
@@ -503,7 +502,6 @@ dependencies = [
"serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-io 0.1.0",
"sr-std 0.1.0",
"sr-version 0.1.0",
"substrate-primitives 0.1.0",
]
@@ -522,6 +520,7 @@ dependencies = [
"parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-primitives 0.1.0",
"sr-std 0.1.0",
]
@@ -216,7 +216,7 @@ impl<Hash: hash::Hash + Member, Ex: ::std::fmt::Debug> BasePool<Hash, Ex> {
}
/// Returns an iterator over ready transactions in the pool.
pub fn ready<'a, 'b: 'a>(&'b self) -> impl Iterator<Item=Arc<Transaction<Hash, Ex>>> + 'a {
pub fn ready(&self) -> impl Iterator<Item=Arc<Transaction<Hash, Ex>>> {
self.ready.get()
}
@@ -105,7 +105,7 @@ impl<B: ChainApi> Pool<B> {
}
match self.api.validate_transaction(at, &xt)? {
TransactionValidity::Valid(priority, requires, provides, longevity) => {
TransactionValidity::Valid { priority, requires, provides, longevity } => {
Ok(base::Transaction {
data: xt,
hash,
@@ -197,11 +197,12 @@ impl<B: ChainApi> Pool<B> {
.ok_or_else(|| error::ErrorKind::Msg(format!("Invalid block id: {:?}", at)).into())?
.as_();
let now = time::Instant::now();
let to_remove = self.ready(|pending| pending
.filter(|tx| self.rotator.ban_if_stale(&now, block_number, &tx))
.map(|tx| tx.hash.clone())
.collect::<Vec<_>>()
);
let to_remove = {
self.ready()
.filter(|tx| self.rotator.ban_if_stale(&now, block_number, &tx))
.map(|tx| tx.hash.clone())
.collect::<Vec<_>>()
};
let futures_to_remove: Vec<ExHash<B>> = {
let p = self.pool.read();
let mut hashes = Vec::new();
@@ -266,20 +267,9 @@ impl<B: ChainApi> Pool<B> {
invalid
}
/// Get ready transactions ordered by priority
pub fn ready<F, X>(&self, f: F) -> X where
F: FnOnce(&mut Iterator<Item=TransactionFor<B>>) -> X,
{
let pool = self.pool.read();
let mut ready = pool.ready();
f(&mut ready)
}
/// Returns all transactions in the pool.
///
/// Be careful with large limit values, as querying the entire pool might be time consuming.
pub fn all(&self, limit: usize) -> Vec<ExtrinsicFor<B>> {
self.ready(|it| it.take(limit).map(|ex| ex.data.clone()).collect())
/// Get an iterator for ready transactions ordered by priority
pub fn ready(&self) -> impl Iterator<Item=TransactionFor<B>> {
self.pool.read().ready()
}
/// Returns pool status.
@@ -341,12 +331,12 @@ mod tests {
if nonce < block_number {
Ok(TransactionValidity::Invalid)
} else {
Ok(TransactionValidity::Valid(
4,
if nonce > block_number { vec![vec![nonce as u8 - 1]] } else { vec![] },
vec![vec![nonce as u8]],
3,
))
Ok(TransactionValidity::Valid {
priority: 4,
requires: if nonce > block_number { vec![vec![nonce as u8 - 1]] } else { vec![] },
provides: vec![vec![nonce as u8]],
longevity: 3,
})
}
}
@@ -398,7 +388,7 @@ mod tests {
})).unwrap();
// then
assert_eq!(pool.ready(|pending| pending.map(|tx| tx.hash.clone()).collect::<Vec<_>>()), vec![hash]);
assert_eq!(pool.ready().map(|v| v.hash).collect::<Vec<_>>(), vec![hash]);
}
#[test]
@@ -489,7 +479,7 @@ mod tests {
pool.clear_stale(&BlockId::Number(5)).unwrap();
// then
assert_eq!(pool.all(3).len(), 0);
assert_eq!(pool.ready().count(), 0);
assert_eq!(pool.status().future, 0);
assert_eq!(pool.status().ready, 0);
// make sure they are temporarily banned as well
@@ -21,6 +21,7 @@ use std::{
sync::Arc,
};
use parking_lot::RwLock;
use sr_primitives::traits::Member;
use sr_primitives::transaction_validity::{
TransactionTag as Tag,
@@ -79,6 +80,16 @@ struct ReadyTx<Hash, Ex> {
pub requires_offset: usize,
}
impl<Hash: Clone, Ex> Clone for ReadyTx<Hash, Ex> {
fn clone(&self) -> Self {
ReadyTx {
transaction: self.transaction.clone(),
unlocks: self.unlocks.clone(),
requires_offset: self.requires_offset,
}
}
}
const HASH_READY: &str = r#"
Every time transaction is imported its hash is placed in `ready` map and tags in `provided_tags`;
Every time transaction is removed from the queue we remove the hash from `ready` map and from `provided_tags`;
@@ -93,8 +104,7 @@ pub struct ReadyTransactions<Hash: hash::Hash + Eq, Ex> {
/// tags that are provided by Ready transactions
provided_tags: HashMap<Tag, Hash>,
/// Transactions that are ready (i.e. don't have any requirements external to the pool)
ready: HashMap<Hash, ReadyTx<Hash, Ex>>,
// ^^ TODO [ToDr] Consider wrapping this into `Arc<RwLock<>>` and allow multiple concurrent iterators
ready: Arc<RwLock<HashMap<Hash, ReadyTx<Hash, Ex>>>>,
/// Best transactions that are ready to be included to the block without any other previous transaction.
best: BTreeSet<TransactionRef<Hash, Ex>>,
}
@@ -127,9 +137,9 @@ impl<Hash: hash::Hash + Member, Ex> ReadyTransactions<Hash, Ex> {
/// - transactions that are valid for a shorter time go first
/// 4. Lastly we sort by the time in the queue
/// - transactions that are longer in the queue go first
pub fn get<'a>(&'a self) -> impl Iterator<Item=Arc<Transaction<Hash, Ex>>> + 'a {
pub fn get(&self) -> impl Iterator<Item=Arc<Transaction<Hash, Ex>>> {
BestIterator {
all: &self.ready,
all: self.ready.clone(),
best: self.best.clone(),
awaiting: Default::default(),
}
@@ -144,7 +154,7 @@ impl<Hash: hash::Hash + Member, Ex> ReadyTransactions<Hash, Ex> {
tx: WaitingTransaction<Hash, Ex>,
) -> error::Result<Vec<Arc<Transaction<Hash, Ex>>>> {
assert!(tx.is_ready(), "Only ready transactions can be imported.");
assert!(!self.ready.contains_key(&tx.transaction.hash), "Transaction is already imported.");
assert!(!self.ready.read().contains_key(&tx.transaction.hash), "Transaction is already imported.");
self.insertion_id += 1;
let insertion_id = self.insertion_id;
@@ -154,11 +164,12 @@ impl<Hash: hash::Hash + Member, Ex> ReadyTransactions<Hash, Ex> {
let replaced = self.replace_previous(&tx)?;
let mut goes_to_best = true;
let mut ready = self.ready.write();
// Add links to transactions that unlock the current one
for tag in &tx.requires {
// Check if the transaction that satisfies the tag is still in the queue.
if let Some(other) = self.provided_tags.get(tag) {
let mut tx = self.ready.get_mut(other).expect(HASH_READY);
let mut tx = ready.get_mut(other).expect(HASH_READY);
tx.unlocks.push(hash.clone());
// this transaction depends on some other, so it doesn't go to best directly.
goes_to_best = false;
@@ -181,7 +192,7 @@ impl<Hash: hash::Hash + Member, Ex> ReadyTransactions<Hash, Ex> {
}
// insert to Ready
self.ready.insert(hash, ReadyTx {
ready.insert(hash, ReadyTx {
transaction,
unlocks: vec![],
requires_offset: 0,
@@ -192,7 +203,7 @@ impl<Hash: hash::Hash + Member, Ex> ReadyTransactions<Hash, Ex> {
/// Returns true if given hash is part of the queue.
pub fn contains(&self, hash: &Hash) -> bool {
self.ready.contains_key(hash)
self.ready.read().contains_key(hash)
}
/// Removes invalid transactions from the ready pool.
@@ -204,13 +215,14 @@ impl<Hash: hash::Hash + Member, Ex> ReadyTransactions<Hash, Ex> {
let mut removed = vec![];
let mut to_remove = hashes.iter().cloned().collect::<Vec<_>>();
let mut ready = self.ready.write();
loop {
let hash = match to_remove.pop() {
Some(hash) => hash,
None => return removed,
};
if let Some(mut tx) = self.ready.remove(&hash) {
if let Some(mut tx) = ready.remove(&hash) {
// remove entries from provided_tags
for tag in &tx.transaction.transaction.provides {
self.provided_tags.remove(tag);
@@ -218,7 +230,7 @@ impl<Hash: hash::Hash + Member, Ex> ReadyTransactions<Hash, Ex> {
// remove from unlocks
for tag in &tx.transaction.transaction.requires {
if let Some(hash) = self.provided_tags.get(tag) {
if let Some(tx) = self.ready.get_mut(hash) {
if let Some(tx) = ready.get_mut(hash) {
remove_item(&mut tx.unlocks, &hash);
}
}
@@ -253,7 +265,7 @@ impl<Hash: hash::Hash + Member, Ex> ReadyTransactions<Hash, Ex> {
};
let res = self.provided_tags.remove(&tag)
.and_then(|hash| self.ready.remove(&hash));
.and_then(|hash| self.ready.write().remove(&hash));
if let Some(tx) = res {
let unlocks = tx.unlocks;
@@ -262,9 +274,10 @@ impl<Hash: hash::Hash + Member, Ex> ReadyTransactions<Hash, Ex> {
// prune previous transactions as well
{
let hash = &tx.hash;
let mut ready = self.ready.write();
let mut find_previous = |tag| -> Option<Vec<Tag>> {
let prev_hash = self.provided_tags.get(tag)?;
let tx2 = self.ready.get_mut(&prev_hash)?;
let tx2 = ready.get_mut(&prev_hash)?;
remove_item(&mut tx2.unlocks, hash);
// We eagerly prune previous transactions as well.
// But it might not always be good.
@@ -292,7 +305,7 @@ impl<Hash: hash::Hash + Member, Ex> ReadyTransactions<Hash, Ex> {
// add the transactions that just got unlocked to `best`
for hash in unlocks {
if let Some(tx) = self.ready.get_mut(&hash) {
if let Some(tx) = self.ready.write().get_mut(&hash) {
tx.requires_offset += 1;
// this transaction is ready
if tx.requires_offset == tx.transaction.transaction.requires.len() {
@@ -328,10 +341,13 @@ impl<Hash: hash::Hash + Member, Ex> ReadyTransactions<Hash, Ex> {
}
// now check if collective priority is lower than the replacement transaction.
let old_priority = replace_hashes
.iter()
.filter_map(|hash| self.ready.get(hash))
.fold(0u64, |total, tx| total.saturating_add(tx.transaction.transaction.priority));
let old_priority = {
let ready = self.ready.read();
replace_hashes
.iter()
.filter_map(|hash| ready.get(hash))
.fold(0u64, |total, tx| total.saturating_add(tx.transaction.transaction.priority))
};
// bail - the transaction has too low priority to replace the old ones
if old_priority >= tx.priority {
@@ -349,7 +365,7 @@ impl<Hash: hash::Hash + Member, Ex> ReadyTransactions<Hash, Ex> {
None => return Ok(removed),
};
let tx = self.ready.remove(&hash).expect(HASH_READY);
let tx = self.ready.write().remove(&hash).expect(HASH_READY);
// check if this transaction provides stuff that is not provided by the new one.
let (mut unlocks, tx) = (tx.unlocks, tx.transaction.transaction);
{
@@ -371,18 +387,18 @@ impl<Hash: hash::Hash + Member, Ex> ReadyTransactions<Hash, Ex> {
/// Returns number of transactions in this queue.
pub fn len(&self) -> usize {
self.ready.len()
self.ready.read().len()
}
}
pub struct BestIterator<'a, Hash: 'a, Ex: 'a> {
all: &'a HashMap<Hash, ReadyTx<Hash, Ex>>,
pub struct BestIterator<Hash, Ex> {
all: Arc<RwLock<HashMap<Hash, ReadyTx<Hash, Ex>>>>,
awaiting: HashMap<Hash, (usize, TransactionRef<Hash, Ex>)>,
best: BTreeSet<TransactionRef<Hash, Ex>>,
}
impl<'a, Hash: 'a + hash::Hash + Member, Ex: 'a> BestIterator<'a, Hash, Ex> {
impl<Hash: hash::Hash + Member, Ex> BestIterator<Hash, Ex> {
/// Depending on number of satisfied requirements insert given ref
/// either to awaiting set or to best set.
fn best_or_awaiting(&mut self, satisfied: usize, tx_ref: TransactionRef<Hash, Ex>) {
@@ -397,32 +413,41 @@ impl<'a, Hash: 'a + hash::Hash + Member, Ex: 'a> BestIterator<'a, Hash, Ex> {
}
}
impl<'a, Hash: 'a + hash::Hash + Member, Ex: 'a> Iterator for BestIterator<'a, Hash, Ex> {
impl<Hash: hash::Hash + Member, Ex> Iterator for BestIterator<Hash, Ex> {
type Item = Arc<Transaction<Hash, Ex>>;
fn next(&mut self) -> Option<Self::Item> {
let best = self.best.iter().next_back()?.clone();
let best = self.best.take(&best)?;
loop {
let best = self.best.iter().next_back()?.clone();
let best = self.best.take(&best)?;
let ready = match self.all.get(&best.transaction.hash) {
Some(ready) => ready,
// The transaction is not in all, maybe it was removed in the meantime?
None => return self.next(),
};
let next = self.all.read().get(&best.transaction.hash).cloned();
let ready = match next {
Some(ready) => ready,
// The transaction is not in all, maybe it was removed in the meantime?
None => continue,
};
// Insert transactions that just got unlocked.
for hash in &ready.unlocks {
// first check local awaiting transactions
if let Some((mut satisfied, tx_ref)) = self.awaiting.remove(hash) {
satisfied += 1;
self.best_or_awaiting(satisfied, tx_ref);
// then get from the pool
} else if let Some(next) = self.all.get(hash) {
self.best_or_awaiting(next.requires_offset + 1, next.transaction.clone());
// Insert transactions that just got unlocked.
for hash in &ready.unlocks {
// first check local awaiting transactions
let res = if let Some((mut satisfied, tx_ref)) = self.awaiting.remove(hash) {
satisfied += 1;
Some((satisfied, tx_ref))
// then get from the pool
} else if let Some(next) = self.all.read().get(hash) {
Some((next.requires_offset + 1, next.transaction.clone()))
} else {
None
};
if let Some((satisfied, tx_ref)) = res {
self.best_or_awaiting(satisfied, tx_ref)
}
}
}
Some(best.transaction.clone())
return Some(best.transaction.clone())
}
}
}
+12 -12
View File
@@ -49,12 +49,12 @@ impl txpool::ChainApi for TestApi {
};
let provides = vec![vec![uxt.transfer.nonce as u8]];
Ok(TransactionValidity::Valid(
/* priority: */1,
Ok(TransactionValidity::Valid {
priority: 1,
requires,
provides,
/* longevity: */64
))
longevity: 64
})
}
fn block_id_to_number(&self, at: &BlockId<Self::Block>) -> error::Result<Option<txpool::NumberFor<Self>>> {
@@ -109,7 +109,7 @@ fn submission_should_work() {
assert_eq!(209, index(&BlockId::number(0)));
pool.submit_one(&BlockId::number(0), uxt(Alice, 209)).unwrap();
let pending: Vec<_> = pool.ready(|p| p.map(|a| a.data.transfer.nonce).collect());
let pending: Vec<_> = pool.ready().map(|a| a.data.transfer.nonce).collect();
assert_eq!(pending, vec![209]);
}
@@ -119,7 +119,7 @@ fn multiple_submission_should_work() {
pool.submit_one(&BlockId::number(0), uxt(Alice, 209)).unwrap();
pool.submit_one(&BlockId::number(0), uxt(Alice, 210)).unwrap();
let pending: Vec<_> = pool.ready(|p| p.map(|a| a.data.transfer.nonce).collect());
let pending: Vec<_> = pool.ready().map(|a| a.data.transfer.nonce).collect();
assert_eq!(pending, vec![209, 210]);
}
@@ -128,7 +128,7 @@ fn early_nonce_should_be_culled() {
let pool = pool();
pool.submit_one(&BlockId::number(0), uxt(Alice, 208)).unwrap();
let pending: Vec<_> = pool.ready(|p| p.map(|a| a.data.transfer.nonce).collect());
let pending: Vec<_> = pool.ready().map(|a| a.data.transfer.nonce).collect();
assert_eq!(pending, Vec::<Index>::new());
}
@@ -137,11 +137,11 @@ fn late_nonce_should_be_queued() {
let pool = pool();
pool.submit_one(&BlockId::number(0), uxt(Alice, 210)).unwrap();
let pending: Vec<_> = pool.ready(|p| p.map(|a| a.data.transfer.nonce).collect());
let pending: Vec<_> = pool.ready().map(|a| a.data.transfer.nonce).collect();
assert_eq!(pending, Vec::<Index>::new());
pool.submit_one(&BlockId::number(0), uxt(Alice, 209)).unwrap();
let pending: Vec<_> = pool.ready(|p| p.map(|a| a.data.transfer.nonce).collect());
let pending: Vec<_> = pool.ready().map(|a| a.data.transfer.nonce).collect();
assert_eq!(pending, vec![209, 210]);
}
@@ -151,12 +151,12 @@ fn prune_tags_should_work() {
pool.submit_one(&BlockId::number(0), uxt(Alice, 209)).unwrap();
pool.submit_one(&BlockId::number(0), uxt(Alice, 210)).unwrap();
let pending: Vec<_> = pool.ready(|p| p.map(|a| a.data.transfer.nonce).collect());
let pending: Vec<_> = pool.ready().map(|a| a.data.transfer.nonce).collect();
assert_eq!(pending, vec![209, 210]);
pool.prune_tags(&BlockId::number(1), vec![vec![209]]).unwrap();
let pending: Vec<_> = pool.ready(|p| p.map(|a| a.data.transfer.nonce).collect());
let pending: Vec<_> = pool.ready().map(|a| a.data.transfer.nonce).collect();
assert_eq!(pending, vec![210]);
}
@@ -169,7 +169,7 @@ fn should_ban_invalid_transactions() {
pool.submit_one(&BlockId::number(0), uxt.clone()).unwrap_err();
// when
let pending: Vec<_> = pool.ready(|p| p.map(|a| a.data.transfer.nonce).collect());
let pending: Vec<_> = pool.ready().map(|a| a.data.transfer.nonce).collect();
assert_eq!(pending, Vec::<Index>::new());
// then
+129 -3
View File
@@ -45,7 +45,7 @@ pub use trie_stream::TrieStream;
/// The Substrate format implementation of `NodeCodec`.
pub use node_codec::NodeCodec;
/// Various re-exports from the `trie-db` crate.
pub use trie_db::{Trie, TrieMut, DBValue, Recorder};
pub use trie_db::{Trie, TrieMut, DBValue, Recorder, Query};
/// As in `trie_db`, but less generic, error type for the crate.
pub type TrieError<H> = trie_db::TrieError<H, Error>;
@@ -53,7 +53,7 @@ pub type TrieError<H> = trie_db::TrieError<H, Error>;
pub trait AsHashDB<H: Hasher>: hash_db::AsHashDB<H, trie_db::DBValue> {}
impl<H: Hasher, T: hash_db::AsHashDB<H, trie_db::DBValue>> AsHashDB<H> for T {}
/// As in `hash_db`, but less generic, trait exposed.
pub type HashDB<H> = hash_db::HashDB<H, trie_db::DBValue>;
pub type HashDB<'a, H> = hash_db::HashDB<H, trie_db::DBValue> + 'a;
/// As in `memory_db`, but less generic, trait exposed.
pub type MemoryDB<H> = memory_db::MemoryDB<H, trie_db::DBValue>;
@@ -73,6 +73,36 @@ pub fn trie_root<H: Hasher, I, A, B>(input: I) -> H::Out where
trie_root::trie_root::<H, TrieStream, _, _, _>(input)
}
/// Determine a trie root given a hash DB and delta values.
pub fn delta_trie_root<H: Hasher, I, A, B>(db: &mut HashDB<H>, mut root: H::Out, delta: I) -> Result<H::Out, Box<TrieError<H::Out>>> where
I: IntoIterator<Item = (A, Option<B>)>,
A: AsRef<[u8]> + Ord,
B: AsRef<[u8]>,
{
{
let mut trie = TrieDBMut::<H>::from_existing(db, &mut root)?;
for (key, change) in delta {
match change {
Some(val) => trie.insert(key.as_ref(), val.as_ref())?,
None => trie.remove(key.as_ref())?, // TODO: archive mode
};
}
}
Ok(root)
}
/// Read a value from the trie.
pub fn read_trie_value<H: Hasher>(db: &HashDB<H>, root: &H::Out, key: &[u8]) -> Result<Option<Vec<u8>>, Box<TrieError<H::Out>>> {
Ok(TrieDB::<H>::new(db, root)?.get(key).map(|x| x.map(|val| val.to_vec()))?)
}
/// Read a value from the trie with given Query.
pub fn read_trie_value_with<H: Hasher, Q: Query<H, Item=DBValue>>(db: &HashDB<H>, root: &H::Out, key: &[u8], query: Q) -> Result<Option<Vec<u8>>, Box<TrieError<H::Out>>> {
Ok(TrieDB::<H>::new(db, root)?.get_with(key, query).map(|x| x.map(|val| val.to_vec()))?)
}
/// Determine a trie root node's data given its ordered contents, closed form.
pub fn unhashed_trie<H: Hasher, I, A, B>(input: I) -> Vec<u8> where
I: IntoIterator<Item = (A, B)>,
@@ -95,6 +125,102 @@ where
)
}
/// Determine whether a child trie key is valid. `child_trie_root` and `child_delta_trie_root` can panic if invalid value is provided to them.
pub fn is_child_trie_key_valid<H: Hasher>(_storage_key: &[u8]) -> bool {
true
}
/// Determine the default child trie root.
pub fn default_child_trie_root<H: Hasher>(_storage_key: &[u8]) -> Vec<u8> {
let mut db = MemoryDB::default();
let mut root = H::Out::default();
let mut empty = TrieDBMut::<H>::new(&mut db, &mut root);
empty.commit();
empty.root().as_ref().to_vec()
}
/// Determine a child trie root given its ordered contents, closed form. H is the default hasher, but a generic
/// implementation may ignore this type parameter and use other hashers.
pub fn child_trie_root<H: Hasher, I, A, B>(_storage_key: &[u8], input: I) -> Vec<u8> where
I: IntoIterator<Item = (A, B)>,
A: AsRef<[u8]> + Ord,
B: AsRef<[u8]>,
{
trie_root::<H, _, _, _>(input).as_ref().iter().cloned().collect()
}
/// Determine a child trie root given a hash DB and delta values. H is the default hasher, but a generic implementation may ignore this type parameter and use other hashers.
pub fn child_delta_trie_root<H: Hasher, I, A, B>(_storage_key: &[u8], db: &mut HashDB<H>, root_vec: Vec<u8>, delta: I) -> Result<Vec<u8>, Box<TrieError<H::Out>>> where
I: IntoIterator<Item = (A, Option<B>)>,
A: AsRef<[u8]> + Ord,
B: AsRef<[u8]>,
{
let mut root = H::Out::default();
root.as_mut().copy_from_slice(&root_vec); // root is fetched from DB, not writable by runtime, so it's always valid.
{
let mut trie = TrieDBMut::<H>::from_existing(db, &mut root)?;
for (key, change) in delta {
match change {
Some(val) => trie.insert(key.as_ref(), val.as_ref())?,
None => trie.remove(key.as_ref())?, // TODO: archive mode
};
}
}
Ok(root.as_ref().to_vec())
}
/// Call `f` for all keys in a child trie.
pub fn for_keys_in_child_trie<H: Hasher, F: FnMut(&[u8])>(_storage_key: &[u8], db: &HashDB<H>, root_slice: &[u8], mut f: F) -> Result<(), Box<TrieError<H::Out>>> {
let mut root = H::Out::default();
root.as_mut().copy_from_slice(root_slice); // root is fetched from DB, not writable by runtime, so it's always valid.
let trie = TrieDB::<H>::new(db, &root)?;
let iter = trie.iter()?;
for x in iter {
let (key, _) = x?;
f(&key);
}
Ok(())
}
/// Record all keys for a given root.
pub fn record_all_keys<H: Hasher>(db: &HashDB<H>, root: &H::Out, recorder: &mut Recorder<H::Out>) -> Result<(), Box<TrieError<H::Out>>> {
let trie = TrieDB::<H>::new(db, root)?;
let iter = trie.iter()?;
for x in iter {
let (key, _) = x?;
// there's currently no API like iter_with()
// => use iter to enumerate all keys AND lookup each
// key using get_with
trie.get_with(&key, &mut *recorder)?;
}
Ok(())
}
/// Read a value from the child trie.
pub fn read_child_trie_value<H: Hasher>(_storage_key: &[u8], db: &HashDB<H>, root_slice: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, Box<TrieError<H::Out>>> {
let mut root = H::Out::default();
root.as_mut().copy_from_slice(root_slice); // root is fetched from DB, not writable by runtime, so it's always valid.
Ok(TrieDB::<H>::new(db, &root)?.get(key).map(|x| x.map(|val| val.to_vec()))?)
}
/// Read a value from the child trie with given query.
pub fn read_child_trie_value_with<H: Hasher, Q: Query<H, Item=DBValue>>(_storage_key: &[u8], db: &HashDB<H>, root_slice: &[u8], key: &[u8], query: Q) -> Result<Option<Vec<u8>>, Box<TrieError<H::Out>>> {
let mut root = H::Out::default();
root.as_mut().copy_from_slice(root_slice); // root is fetched from DB, not writable by runtime, so it's always valid.
Ok(TrieDB::<H>::new(db, &root)?.get_with(key, query).map(|x| x.map(|val| val.to_vec()))?)
}
// Utilities (not exported):
const EMPTY_TRIE: u8 = 0;
@@ -418,4 +544,4 @@ mod tests {
assert_eq!(pairs, iter_pairs);
}
}
}