Merge branch 'master' into rh-grandpa-dynamic2

This commit is contained in:
Robert Habermeier
2018-11-14 14:32:30 +01:00
235 changed files with 9413 additions and 3020 deletions
+1
View File
@@ -0,0 +1 @@
Cargo.lock linguist-generated=true -diff
+614 -428
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -33,7 +33,6 @@ members = [
"core/primitives",
"core/rpc",
"core/rpc-servers",
"core/sr-api",
"core/sr-io",
"core/sr-sandbox",
"core/sr-std",
@@ -77,6 +76,7 @@ exclude = [
"core/executor/wasm",
"pwasm-alloc",
"core/test-runtime/wasm",
"test-utils/chain-spec-builder"
]
[badges]
+4 -4
View File
@@ -1,12 +1,12 @@
FROM frolvlad/alpine-glibc AS builder
FROM alpine:edge AS builder
LABEL maintainer="chevdor@gmail.com"
LABEL description="This is the build stage for Substrate. Here we create the binary."
RUN apk add build-base \
cmake \
linux-headers \
openssl-dev && \
apk add --repository http://nl.alpinelinux.org/alpine/edge/community cargo
openssl-dev \
cargo
ARG PROFILE=release
WORKDIR /substrate
@@ -17,7 +17,7 @@ RUN cargo build --$PROFILE
# ===== SECOND STAGE ======
FROM alpine:3.8
FROM alpine:edge
LABEL maintainer="chevdor@gmail.com"
LABEL description="This is the 2nd stage: a very small image where we copy the Substrate binary."
ARG PROFILE=release
+40 -9
View File
@@ -227,7 +227,36 @@ Or you can join the BBQ Birch Testnet with:
[source, shell]
cargo run
include::doc/packages/packages.adoc[]
Detailed logs may be shown by running the node with the following environment variables set: `RUST_LOG=debug RUST_BACKTRACE=1 cargo run -- --dev`.
If you want to see the multi-node consensus algorithm in action locally, then you can create a local testnet with two validator nodes for Alice and Bob, who are the initial authorities of the genesis chain specification that have been endowed with a testnet DOTs. We'll give each node a name and expose them so they are listed on [Telemetry](https://telemetry.polkadot.io/#/Local%20Testnet). You'll need two terminals windows open.
We'll start Alice's substrate node first on default TCP port 30333 with her chain database stored locally at `/tmp/alice`. The Bootnode ID of her node is `QmQZ8TjTqeDj3ciwr93EJ95hxfDsb9pEYDizUAbWpigtQN`, which is generated from the `--node-key` value that we specify below:
[source, shell]
cargo run -- \
--base-path /tmp/alice \
--chain=local \
--key Alice \
--name "ALICE" \
--node-key 0000000000000000000000000000000000000000000000000000000000000001 \
--telemetry-url ws://telemetry.polkadot.io:1024 \
--validator
In the second terminal, we'll run the following to start Bob's substrate node on a different TCP port of 30334, and with his chain database stored locally at `/tmp/bob`. We'll specify a value for the `--bootnodes` option that will connect his node to Alice's Bootnode ID on TCP port 30333:
[source, shell]
cargo run -- \
--base-path /tmp/bob \
--bootnodes /ip4/127.0.0.1/tcp/30333/p2p/QmQZ8TjTqeDj3ciwr93EJ95hxfDsb9pEYDizUAbWpigtQN \
--chain=local \
--key Bob \
--name "BOB" \
--port 30334 \
--telemetry-url ws://telemetry.polkadot.io:1024 \
--validator
Additional Substate CLI usage options are available and may be shown by running `cargo run -- --help`.
== Documentation
@@ -240,13 +269,16 @@ You can generate documentation for a Substrate Rust package and have it automati
cargo doc --package <spec> --open
```
Replacing `<spec>` with one of the following (i.e. `cargo doc --package node-cli --open`):
Replacing `<spec>` with one of the following (i.e. `cargo doc --package substrate --open`):
* All Substrate Packages
[source, shell]
substrate
* Substrate Core
[source, shell]
substrate, substrate-bft, substrate-cli, substrate-client, substrate-client-db,
substrate-executor, substrate-finality-grandpa, substrate-keyring, substrate-keystore,
substrate-metadata, substrate-misbehavior-check, substrate-network,
substrate, substrate-cli, substrate-client, substrate-client-db,
substrate-consensus-common, substrate-consensus-rhd,
substrate-executor, substrate-finality-grandpa, substrate-keyring, substrate-keystore, substrate-network,
substrate-network-libp2p, substrate-primitives, substrate-rpc, substrate-rpc-servers,
substrate-serializer, substrate-service, substrate-service-test, substrate-state-db,
substrate-state-machine, substrate-telemetry, substrate-test-client,
@@ -257,17 +289,16 @@ substrate-trie
sr-api, sr-io, sr-primitives, sr-sandbox, sr-std, sr-version
* Substrate Runtime Module Library (SRML)
[source, shell]
srml-balances, srml-consensus, srml-contract, srml-council, srml-democracy, srml-example,
srml-executive, srml-session, srml-staking, srml-support, srml-system, srml-timestamp,
srml-assets, srml-balances, srml-consensus, srml-contract, srml-council, srml-democracy, srml-example,
srml-executive, srml-metadata, srml-session, srml-staking, srml-support, srml-system, srml-timestamp,
srml-treasury
* Node
[source, shell]
node-cli, node-consensus, node-executor, node-network, node-primitives, node-runtime, node-service
node-cli, node-consensus, node-executor, node-network, node-primitives, node-runtime
* Subkey
[source, shell]
subkey
=== Contributing to documentation for Substrate packages
https://doc.rust-lang.org/1.9.0/book/documentation.html[Document source code] for Substrate packages by annotating the source code with documentation comments.
-1
View File
@@ -25,7 +25,6 @@ exit-future = "0.1"
sysinfo = "0.6.2"
substrate-client = { path = "../../core/client" }
substrate-network = { path = "../../core/network" }
substrate-network-libp2p = { path = "../../core/network-libp2p" }
sr-primitives = { path = "../../core/sr-primitives" }
substrate-primitives = { path = "../../core/primitives" }
substrate-service = { path = "../../core/service" }
-7
View File
@@ -1,13 +1,6 @@
= Substrate CLI
.Summary
[source, toml]
----
include::Cargo.toml[lines=2..5]
----
.Description
Substrate CLI library
include::doc/shell-completion.adoc[]
+2 -4
View File
@@ -30,9 +30,8 @@ use runtime_primitives::traits::{Header, As};
const TIMER_INTERVAL_MS: u64 = 5000;
/// Spawn informant on the event loop
pub fn start<C>(service: &Service<C>, exit: ::exit_future::Exit, handle: TaskExecutor)
where
C: Components,
pub fn start<C>(service: &Service<C>, exit: ::exit_future::Exit, handle: TaskExecutor) where
C: Components,
{
let interval = Interval::new(Instant::now(), Duration::from_millis(TIMER_INTERVAL_MS));
@@ -121,4 +120,3 @@ fn speed(best_number: u64, last_number: Option<u64>) -> String {
format!(" {:4.1} bps", speed / 10.0)
}
}
+1 -5
View File
@@ -14,9 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
// tag::description[]
//! Substrate CLI library.
// end::description[]
#![warn(missing_docs)]
#![warn(unused_extern_crates)]
@@ -36,7 +34,6 @@ extern crate sysinfo;
extern crate substrate_client as client;
extern crate substrate_network as network;
extern crate substrate_network_libp2p as network_libp2p;
extern crate sr_primitives as runtime_primitives;
extern crate substrate_service as service;
extern crate substrate_primitives as primitives;
@@ -58,13 +55,12 @@ pub mod error;
pub mod informant;
mod panic_hook;
use network_libp2p::Protocol;
use runtime_primitives::traits::As;
use service::{
ServiceFactory, FactoryFullConfiguration, RuntimeGenesis,
FactoryGenesis, PruningMode, ChainSpec,
};
use network::NonReservedPeerMode;
use network::{Protocol, config::NonReservedPeerMode};
use primitives::H256;
use std::io::{Write, Read, stdin, stdout};
+48 -20
View File
@@ -4,27 +4,55 @@ version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
error-chain = "0.12"
fnv = "1.0"
log = "0.4"
parking_lot = "0.4"
hex-literal = "0.1"
futures = "0.1.17"
slog = "^2"
heapsize = "0.4"
substrate-consensus-common = { path = "../consensus/common" }
parity-codec = "2.1"
substrate-executor = { path = "../executor" }
substrate-primitives = { path = "../primitives" }
sr-primitives = { path = "../sr-primitives" }
sr-api = { path = "../sr-api" }
substrate-state-machine = { path = "../state-machine" }
substrate-keyring = { path = "../keyring" }
substrate-trie = { path = "../trie" }
substrate-telemetry = { path = "../telemetry" }
hash-db = { git = "https://github.com/paritytech/trie" }
kvdb = "0.1"
error-chain = { version = "0.12", optional = true }
fnv = { version = "1.0", optional = true }
log = { version = "0.4", optional = true }
parking_lot = { version = "0.4", optional = true }
hex-literal = { version = "0.1", optional = true }
futures = { version = "0.1.17", optional = true }
slog = { version = "^2", optional = true }
heapsize = { version = "0.4", optional = true }
substrate-consensus-common = { path = "../consensus/common", optional = true }
substrate-executor = { path = "../executor", optional = true }
substrate-state-machine = { path = "../state-machine", optional = true }
substrate-keyring = { path = "../keyring", optional = true }
substrate-trie = { path = "../trie", optional = true }
substrate-telemetry = { path = "../telemetry", optional = true }
hash-db = { git = "https://github.com/paritytech/trie", optional = true }
kvdb = { version = "0.1", optional = true }
parity-codec = { version = "2.1", default-features = false }
substrate-primitives = { path = "../primitives", default-features = false }
sr-primitives = { path = "../sr-primitives", default-features = false }
sr-version = { path = "../sr-version", default-features = false }
sr-std = { path = "../sr-std", default-features = false }
[dev-dependencies]
substrate-test-client = { path = "../test-client" }
kvdb-memorydb = "0.1"
[features]
default = ["std"]
std = [
"parity-codec/std",
"substrate-consensus-common",
"substrate-primitives/std",
"parking_lot",
"error-chain",
"fnv",
"log",
"hex-literal",
"futures",
"slog",
"heapsize",
"substrate-executor",
"sr-primitives/std",
"sr-version/std",
"sr-std/std",
"substrate-state-machine",
"substrate-keyring",
"substrate-trie",
"substrate-telemetry",
"hash-db",
"kvdb"
]
-13
View File
@@ -1,13 +0,0 @@
= Client
.Summary
[source, toml]
----
include::Cargo.toml[lines=2..5]
----
.Description
----
include::src/lib.rs[tag=description]
----
-13
View File
@@ -1,13 +0,0 @@
= Client DB
.Summary
[source, toml]
----
include::Cargo.toml[lines=2..5]
----
.Description
----
include::src/lib.rs[tag=description]
----
+142 -50
View File
@@ -14,7 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
// tag::description[]
//! Client backend that uses RocksDB database as storage.
//!
//! # Canonicality vs. Finality
@@ -24,8 +23,6 @@
//! having discarded heavy state that will allow a chain reorganization.
//!
//! Finality implies canonicality but not vice-versa.
//!
// end::description[]
extern crate substrate_client as client;
extern crate kvdb_rocksdb;
@@ -67,7 +64,7 @@ use hash_db::Hasher;
use kvdb::{KeyValueDB, DBTransaction};
use trie::MemoryDB;
use parking_lot::RwLock;
use primitives::{H256, AuthorityId, Blake2Hasher, ChangesTrieConfiguration};
use primitives::{H256, AuthorityId, Blake2Hasher, ChangesTrieConfiguration, convert_hash};
use primitives::storage::well_known_keys;
use runtime_primitives::{generic::BlockId, Justification, StorageMap, ChildrenStorageMap};
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, As, NumberFor, Zero, Digest, DigestItem};
@@ -97,17 +94,17 @@ pub struct DatabaseSettings {
}
/// Create an instance of db-backed client.
pub fn new_client<E, S, Block>(
pub fn new_client<E, S, Block, RA>(
settings: DatabaseSettings,
executor: E,
genesis_storage: S,
block_execution_strategy: ExecutionStrategy,
api_execution_strategy: ExecutionStrategy,
) -> Result<client::Client<Backend<Block>, client::LocalCallExecutor<Backend<Block>, E>, Block>, client::error::Error>
where
Block: BlockT<Hash=H256>,
E: CodeExecutor<Blake2Hasher> + RuntimeInfo,
S: BuildStorage,
) -> Result<client::Client<Backend<Block>, client::LocalCallExecutor<Backend<Block>, E>, Block, RA>, client::error::Error>
where
Block: BlockT<Hash=H256>,
E: CodeExecutor<Blake2Hasher> + RuntimeInfo,
S: BuildStorage,
{
let backend = Arc::new(Backend::new(settings, CANONICALIZATION_DELAY)?);
let executor = client::LocalCallExecutor::new(backend.clone(), executor);
@@ -148,7 +145,7 @@ impl<'a> state_db::MetaDb for StateMetaDb<'a> {
/// Block database
pub struct BlockchainDb<Block: BlockT> {
db: Arc<KeyValueDB>,
meta: RwLock<Meta<NumberFor<Block>, Block::Hash>>,
meta: Arc<RwLock<Meta<NumberFor<Block>, Block::Hash>>>,
leaves: RwLock<LeafSet<Block::Hash, NumberFor<Block>>>,
}
@@ -159,7 +156,7 @@ impl<Block: BlockT> BlockchainDb<Block> {
Ok(BlockchainDb {
db,
leaves: RwLock::new(leaves),
meta: RwLock::new(meta),
meta: Arc::new(RwLock::new(meta)),
})
}
@@ -376,7 +373,7 @@ struct StorageDb<Block: BlockT> {
impl<Block: BlockT> state_machine::Storage<Blake2Hasher> for StorageDb<Block> {
fn get(&self, key: &H256) -> Result<Option<DBValue>, String> {
self.state_db.get(&key.0.into(), self).map(|r| r.map(|v| DBValue::from_slice(&v)))
self.state_db.get(key, self).map(|r| r.map(|v| DBValue::from_slice(&v)))
.map_err(|e| format!("Database backend error: {:?}", e))
}
}
@@ -386,7 +383,7 @@ impl<Block: BlockT> state_db::HashDb for StorageDb<Block> {
type Hash = H256;
fn get(&self, key: &H256) -> Result<Option<Vec<u8>>, Self::Error> {
self.db.get(columns::STATE, &key[..]).map(|r| r.map(|v| v.to_vec()))
self.db.get(columns::STATE, key.as_bytes()).map(|r| r.map(|v| v.to_vec()))
}
}
@@ -409,6 +406,7 @@ impl state_machine::Storage<Blake2Hasher> for DbGenesisStorage {
pub struct DbChangesTrieStorage<Block: BlockT> {
db: Arc<KeyValueDB>,
meta: Arc<RwLock<Meta<NumberFor<Block>, Block::Hash>>>,
min_blocks_to_keep: Option<u64>,
_phantom: ::std::marker::PhantomData<Block>,
}
@@ -422,7 +420,7 @@ impl<Block: BlockT> DbChangesTrieStorage<Block> {
}
/// Prune obsolete changes tries.
pub fn prune(&self, config: Option<ChangesTrieConfiguration>, tx: &mut DBTransaction, block: NumberFor<Block>) {
pub fn prune(&self, config: Option<ChangesTrieConfiguration>, tx: &mut DBTransaction, block_hash: Block::Hash, block_num: NumberFor<Block>) {
// never prune on archive nodes
let min_blocks_to_keep = match self.min_blocks_to_keep {
Some(min_blocks_to_keep) => min_blocks_to_keep,
@@ -440,23 +438,54 @@ impl<Block: BlockT> DbChangesTrieStorage<Block> {
&config,
&*self,
min_blocks_to_keep,
block.as_(),
&state_machine::ChangesTrieAnchorBlockId {
hash: convert_hash(&block_hash),
number: block_num.as_(),
},
|node| tx.delete(columns::CHANGES_TRIE, node.as_ref()));
}
}
impl<Block: BlockT> state_machine::ChangesTrieRootsStorage<Blake2Hasher> for DbChangesTrieStorage<Block> {
fn root(&self, block: u64) -> Result<Option<H256>, String> {
Ok(read_db::<Block>(&*self.db, columns::HASH_LOOKUP, columns::HEADER, BlockId::Number(As::sa(block)))
.map_err(|err| format!("{}", err))
.and_then(|header| match header {
Some(header) => Block::Header::decode(&mut &header[..])
.ok_or_else(|| format!("Failed to parse header of block {}", block))
.map(Some),
None => Ok(None)
})?
.and_then(|header| header.digest().log(DigestItem::as_changes_trie_root)
.map(|root| H256::from_slice(root.as_ref()))))
fn root(&self, anchor: &state_machine::ChangesTrieAnchorBlockId<H256>, block: u64) -> Result<Option<H256>, String> {
// check API requirement
assert!(block <= anchor.number, "API requirement");
// we need to get hash of the block to resolve changes trie root
let block_id = if block <= self.meta.read().finalized_number.as_() {
// if block is finalized, we could just read canonical hash
BlockId::Number(As::sa(block))
} else {
// the block is not finalized
let mut current_num = anchor.number;
let mut current_hash: Block::Hash = convert_hash(&anchor.hash);
let maybe_anchor_header: Block::Header = ::utils::require_header::<Block>(
&*self.db, columns::HASH_LOOKUP, columns::HEADER, BlockId::Number(As::sa(current_num))
).map_err(|e| e.to_string())?;
if maybe_anchor_header.hash() == current_hash {
// if anchor is canonicalized, then the block is also canonicalized
BlockId::Number(As::sa(block))
} else {
// else (block is not finalized + anchor is not canonicalized):
// => we should find the required block hash by traversing
// back from the anchor to the block with given number
while current_num != block {
let current_header: Block::Header = ::utils::require_header::<Block>(
&*self.db, columns::HASH_LOOKUP, columns::HEADER, BlockId::Hash(current_hash)
).map_err(|e| e.to_string())?;
current_hash = *current_header.parent_hash();
current_num = current_num - 1;
}
BlockId::Hash(current_hash)
}
};
Ok(::utils::require_header::<Block>(&*self.db, columns::HASH_LOOKUP, columns::HEADER, block_id)
.map_err(|e| e.to_string())?
.digest().log(DigestItem::as_changes_trie_root)
.map(|root| H256::from_slice(root.as_ref())))
}
}
@@ -502,6 +531,7 @@ impl<Block: BlockT> Backend<Block> {
fn from_kvdb(db: Arc<KeyValueDB>, pruning: PruningMode, canonicalization_delay: u64) -> Result<Self, client::error::Error> {
let is_archive_pruning = pruning.is_archive();
let blockchain = BlockchainDb::new(db.clone())?;
let meta = blockchain.meta.clone();
let map_e = |e: state_db::Error<io::Error>| ::client::error::Error::from(format!("State database error: {:?}", e));
let state_db: StateDb<Block::Hash, H256> = StateDb::new(pruning, &StateMetaDb(&*db)).map_err(map_e)?;
let storage_db = StorageDb {
@@ -510,6 +540,7 @@ impl<Block: BlockT> Backend<Block> {
};
let changes_tries_storage = DbChangesTrieStorage {
db,
meta,
min_blocks_to_keep: if is_archive_pruning { None } else { Some(MIN_BLOCKS_TO_KEEP_CHANGES_TRIES_FOR) },
_phantom: Default::default(),
};
@@ -589,7 +620,7 @@ impl<Block: BlockT> Backend<Block> {
let changes_trie_config: Option<ChangesTrieConfiguration> = self.state_at(BlockId::Hash(parent_hash))?
.storage(well_known_keys::CHANGES_TRIE_CONFIG)?
.and_then(|v| Decode::decode(&mut &*v));
self.changes_tries_storage.prune(changes_trie_config, transaction, f_num);
self.changes_tries_storage.prune(changes_trie_config, transaction, f_hash, f_num);
}
Ok(())
@@ -755,9 +786,9 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
let mut changeset: state_db::ChangeSet<H256> = state_db::ChangeSet::default();
for (key, (val, rc)) in operation.updates.drain() {
if rc > 0 {
changeset.inserted.push((key.0.into(), val.to_vec()));
changeset.inserted.push((key, val.to_vec()));
} else if rc < 0 {
changeset.deleted.push(key.0.into());
changeset.deleted.push(key);
}
}
let number_u64 = number.as_();
@@ -1138,7 +1169,7 @@ mod tests {
backend.commit_operation(op).unwrap();
assert_eq!(backend.storage.db.get(::columns::STATE, &key.0[..]).unwrap().unwrap(), &b"hello"[..]);
assert_eq!(backend.storage.db.get(::columns::STATE, key.as_bytes()).unwrap().unwrap(), &b"hello"[..]);
hash
};
@@ -1172,7 +1203,7 @@ mod tests {
backend.commit_operation(op).unwrap();
assert_eq!(backend.storage.db.get(::columns::STATE, &key.0[..]).unwrap().unwrap(), &b"hello"[..]);
assert_eq!(backend.storage.db.get(::columns::STATE, key.as_bytes()).unwrap().unwrap(), &b"hello"[..]);
hash
};
@@ -1204,21 +1235,24 @@ mod tests {
backend.commit_operation(op).unwrap();
assert!(backend.storage.db.get(::columns::STATE, &key.0[..]).unwrap().is_none());
assert!(backend.storage.db.get(::columns::STATE, key.as_bytes()).unwrap().is_none());
}
backend.finalize_block(BlockId::Number(1)).unwrap();
backend.finalize_block(BlockId::Number(2)).unwrap();
assert!(backend.storage.db.get(::columns::STATE, &key.0[..]).unwrap().is_none());
assert!(backend.storage.db.get(::columns::STATE, key.as_bytes()).unwrap().is_none());
}
#[test]
fn changes_trie_storage_works() {
let backend = Backend::<Block>::new_test(1000, 100);
backend.changes_tries_storage.meta.write().finalized_number = 1000;
let check_changes = |backend: &Backend<Block>, block: u64, changes: Vec<(Vec<u8>, Vec<u8>)>| {
let (changes_root, mut changes_trie_update) = prepare_changes(changes);
assert_eq!(backend.changes_tries_storage.root(block), Ok(Some(changes_root)));
let anchor = state_machine::ChangesTrieAnchorBlockId { hash: Default::default(), number: block };
assert_eq!(backend.changes_tries_storage.root(&anchor, block), Ok(Some(changes_root)));
for (key, (val, _)) in changes_trie_update.drain() {
assert_eq!(backend.changes_trie_storage().unwrap().get(&key), Ok(Some(val)));
@@ -1242,9 +1276,66 @@ mod tests {
check_changes(&backend, 2, changes2);
}
#[test]
fn changes_trie_storage_works_with_forks() {
let backend = Backend::<Block>::new_test(1000, 100);
let changes0 = vec![(b"k0".to_vec(), b"v0".to_vec())];
let changes1 = vec![(b"k1".to_vec(), b"v1".to_vec())];
let changes2 = vec![(b"k2".to_vec(), b"v2".to_vec())];
let block0 = insert_header(&backend, 0, Default::default(), changes0.clone(), Default::default());
let block1 = insert_header(&backend, 1, block0, changes1.clone(), Default::default());
let block2 = insert_header(&backend, 2, block1, changes2.clone(), Default::default());
let changes2_1_0 = vec![(b"k3".to_vec(), b"v3".to_vec())];
let changes2_1_1 = vec![(b"k4".to_vec(), b"v4".to_vec())];
let block2_1_0 = insert_header(&backend, 3, block2, changes2_1_0.clone(), Default::default());
let block2_1_1 = insert_header(&backend, 4, block2_1_0, changes2_1_1.clone(), Default::default());
let changes2_2_0 = vec![(b"k5".to_vec(), b"v5".to_vec())];
let changes2_2_1 = vec![(b"k6".to_vec(), b"v6".to_vec())];
let block2_2_0 = insert_header(&backend, 3, block2, changes2_2_0.clone(), Default::default());
let block2_2_1 = insert_header(&backend, 4, block2_2_0, changes2_2_1.clone(), Default::default());
// finalize block1
backend.changes_tries_storage.meta.write().finalized_number = 1;
// branch1: when asking for finalized block hash
let (changes1_root, _) = prepare_changes(changes1);
let anchor = state_machine::ChangesTrieAnchorBlockId { hash: block2_1_1, number: 4 };
assert_eq!(backend.changes_tries_storage.root(&anchor, 1), Ok(Some(changes1_root)));
// branch2: when asking for finalized block hash
let anchor = state_machine::ChangesTrieAnchorBlockId { hash: block2_2_1, number: 4 };
assert_eq!(backend.changes_tries_storage.root(&anchor, 1), Ok(Some(changes1_root)));
// branch1: when asking for non-finalized block hash (search by traversal)
let (changes2_1_0_root, _) = prepare_changes(changes2_1_0);
let anchor = state_machine::ChangesTrieAnchorBlockId { hash: block2_1_1, number: 4 };
assert_eq!(backend.changes_tries_storage.root(&anchor, 3), Ok(Some(changes2_1_0_root)));
// branch2: when asking for non-finalized block hash (search using canonicalized hint)
let (changes2_2_0_root, _) = prepare_changes(changes2_2_0);
let anchor = state_machine::ChangesTrieAnchorBlockId { hash: block2_2_1, number: 4 };
assert_eq!(backend.changes_tries_storage.root(&anchor, 3), Ok(Some(changes2_2_0_root)));
// finalize first block of branch2 (block2_2_0)
backend.changes_tries_storage.meta.write().finalized_number = 3;
// branch2: when asking for finalized block of this branch
assert_eq!(backend.changes_tries_storage.root(&anchor, 3), Ok(Some(changes2_2_0_root)));
// branch1: when asking for finalized block of other branch
// => result is incorrect (returned for the block of branch1), but this is expected,
// because the other fork is abandoned (forked before finalized header)
let anchor = state_machine::ChangesTrieAnchorBlockId { hash: block2_1_1, number: 4 };
assert_eq!(backend.changes_tries_storage.root(&anchor, 3), Ok(Some(changes2_2_0_root)));
}
#[test]
fn changes_tries_are_pruned_on_finalization() {
let mut backend = Backend::<Block>::new_test(1000, 100);
backend.changes_tries_storage.meta.write().finalized_number = 1000;
backend.changes_tries_storage.min_blocks_to_keep = Some(8);
let config = ChangesTrieConfiguration {
digest_interval: 2,
@@ -1267,26 +1358,27 @@ mod tests {
let _ = insert_header(&backend, 12, block11, vec![(b"key_at_12".to_vec(), b"val_at_12".to_vec())], Default::default());
// check that roots of all tries are in the columns::CHANGES_TRIE
let anchor = state_machine::ChangesTrieAnchorBlockId { hash: Default::default(), number: 100 };
fn read_changes_trie_root(backend: &Backend<Block>, num: u64) -> H256 {
backend.blockchain().header(BlockId::Number(num)).unwrap().unwrap().digest().logs().iter()
.find(|i| i.as_changes_trie_root().is_some()).unwrap().as_changes_trie_root().unwrap().clone()
}
let root1 = read_changes_trie_root(&backend, 1); assert_eq!(backend.changes_tries_storage.root(1).unwrap(), Some(root1));
let root2 = read_changes_trie_root(&backend, 2); assert_eq!(backend.changes_tries_storage.root(2).unwrap(), Some(root2));
let root3 = read_changes_trie_root(&backend, 3); assert_eq!(backend.changes_tries_storage.root(3).unwrap(), Some(root3));
let root4 = read_changes_trie_root(&backend, 4); assert_eq!(backend.changes_tries_storage.root(4).unwrap(), Some(root4));
let root5 = read_changes_trie_root(&backend, 5); assert_eq!(backend.changes_tries_storage.root(5).unwrap(), Some(root5));
let root6 = read_changes_trie_root(&backend, 6); assert_eq!(backend.changes_tries_storage.root(6).unwrap(), Some(root6));
let root7 = read_changes_trie_root(&backend, 7); assert_eq!(backend.changes_tries_storage.root(7).unwrap(), Some(root7));
let root8 = read_changes_trie_root(&backend, 8); assert_eq!(backend.changes_tries_storage.root(8).unwrap(), Some(root8));
let root9 = read_changes_trie_root(&backend, 9); assert_eq!(backend.changes_tries_storage.root(9).unwrap(), Some(root9));
let root10 = read_changes_trie_root(&backend, 10); assert_eq!(backend.changes_tries_storage.root(10).unwrap(), Some(root10));
let root11 = read_changes_trie_root(&backend, 11); assert_eq!(backend.changes_tries_storage.root(11).unwrap(), Some(root11));
let root12 = read_changes_trie_root(&backend, 12); assert_eq!(backend.changes_tries_storage.root(12).unwrap(), Some(root12));
let root1 = read_changes_trie_root(&backend, 1); assert_eq!(backend.changes_tries_storage.root(&anchor, 1).unwrap(), Some(root1));
let root2 = read_changes_trie_root(&backend, 2); assert_eq!(backend.changes_tries_storage.root(&anchor, 2).unwrap(), Some(root2));
let root3 = read_changes_trie_root(&backend, 3); assert_eq!(backend.changes_tries_storage.root(&anchor, 3).unwrap(), Some(root3));
let root4 = read_changes_trie_root(&backend, 4); assert_eq!(backend.changes_tries_storage.root(&anchor, 4).unwrap(), Some(root4));
let root5 = read_changes_trie_root(&backend, 5); assert_eq!(backend.changes_tries_storage.root(&anchor, 5).unwrap(), Some(root5));
let root6 = read_changes_trie_root(&backend, 6); assert_eq!(backend.changes_tries_storage.root(&anchor, 6).unwrap(), Some(root6));
let root7 = read_changes_trie_root(&backend, 7); assert_eq!(backend.changes_tries_storage.root(&anchor, 7).unwrap(), Some(root7));
let root8 = read_changes_trie_root(&backend, 8); assert_eq!(backend.changes_tries_storage.root(&anchor, 8).unwrap(), Some(root8));
let root9 = read_changes_trie_root(&backend, 9); assert_eq!(backend.changes_tries_storage.root(&anchor, 9).unwrap(), Some(root9));
let root10 = read_changes_trie_root(&backend, 10); assert_eq!(backend.changes_tries_storage.root(&anchor, 10).unwrap(), Some(root10));
let root11 = read_changes_trie_root(&backend, 11); assert_eq!(backend.changes_tries_storage.root(&anchor, 11).unwrap(), Some(root11));
let root12 = read_changes_trie_root(&backend, 12); assert_eq!(backend.changes_tries_storage.root(&anchor, 12).unwrap(), Some(root12));
// now simulate finalization of block#12, causing prune of tries at #1..#4
let mut tx = DBTransaction::new();
backend.changes_tries_storage.prune(Some(config.clone()), &mut tx, 12);
backend.changes_tries_storage.prune(Some(config.clone()), &mut tx, Default::default(), 12);
backend.storage.db.write(tx).unwrap();
assert!(backend.changes_tries_storage.get(&root1).unwrap().is_none());
assert!(backend.changes_tries_storage.get(&root2).unwrap().is_none());
@@ -1299,7 +1391,7 @@ mod tests {
// now simulate finalization of block#16, causing prune of tries at #5..#8
let mut tx = DBTransaction::new();
backend.changes_tries_storage.prune(Some(config.clone()), &mut tx, 16);
backend.changes_tries_storage.prune(Some(config.clone()), &mut tx, Default::default(), 16);
backend.storage.db.write(tx).unwrap();
assert!(backend.changes_tries_storage.get(&root5).unwrap().is_none());
assert!(backend.changes_tries_storage.get(&root6).unwrap().is_none());
@@ -1310,7 +1402,7 @@ mod tests {
// => no changes tries are pruned, because we never prune in archive mode
backend.changes_tries_storage.min_blocks_to_keep = None;
let mut tx = DBTransaction::new();
backend.changes_tries_storage.prune(Some(config), &mut tx, 20);
backend.changes_tries_storage.prune(Some(config), &mut tx, Default::default(), 20);
backend.storage.db.write(tx).unwrap();
assert!(backend.changes_tries_storage.get(&root9).unwrap().is_some());
assert!(backend.changes_tries_storage.get(&root10).unwrap().is_some());
+11
View File
@@ -187,6 +187,17 @@ pub fn read_header<Block: BlockT>(
}
}
/// Required header from the database.
pub fn require_header<Block: BlockT>(
db: &KeyValueDB,
col_index: Option<u32>,
col: Option<u32>,
id: BlockId<Block>,
) -> client::error::Result<Block::Header> {
read_header(db, col_index, col, id)
.and_then(|header| header.ok_or_else(|| client::error::ErrorKind::UnknownBlock(format!("{}", id)).into()))
}
/// Read meta from the database.
pub fn read_meta<Block>(db: &KeyValueDB, col_meta: Option<u32>, col_header: Option<u32>) -> Result<
Meta<<<Block as BlockT>::Header as HeaderT>::Number, Block::Hash>,
+2 -5
View File
@@ -47,8 +47,7 @@ impl NewBlockState {
}
/// Block insertion operation. Keeps hold if the inserted block state and data.
pub trait BlockImportOperation<Block, H>
where
pub trait BlockImportOperation<Block, H> where
Block: BlockT,
H: Hasher<Out=Block::Hash>,
{
@@ -88,11 +87,9 @@ where
///
/// The same applies for live `BlockImportOperation`s: while an import operation building on a parent `P`
/// is alive, the state for `P` should not be pruned.
pub trait Backend<Block, H>: Send + Sync
where
pub trait Backend<Block, H>: Send + Sync where
Block: BlockT,
H: Hasher<Out=Block::Hash>,
{
/// Associated block insertion operation type.
type BlockImportOperation: BlockImportOperation<Block, H>;
@@ -0,0 +1,40 @@
// Copyright 2018 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! The runtime api for building blocks.
use runtime_primitives::{traits::Block as BlockT, ApplyResult};
use rstd::vec::Vec;
decl_runtime_apis! {
/// The `BlockBuilder` api trait that provides required functions for building a block for a runtime.
pub trait BlockBuilder<Block: BlockT> {
/// The runtime api for building blocks./// Apply the given extrinsics.
fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyResult;
/// Finish the current block.
fn finalise_block() -> <Block as BlockT>::Header;
/// Generate inherent extrinsics.
fn inherent_extrinsics<InherentExtrinsic, UncheckedExtrinsic>(
inherent: InherentExtrinsic
) -> Vec<UncheckedExtrinsic>;
/// Check that the inherents are valid.
fn check_inherents<InherentData, Error>(
block: Block, data: InherentData
) -> Result<(), Error>;
/// Generate a random seed.
fn random_seed() -> <Block as BlockT>::Hash;
}
}
@@ -14,60 +14,48 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Utility struct to build a block.
use super::api::BlockBuilder as BlockBuilderApi;
use std::vec::Vec;
use std::marker::PhantomData;
use codec::Encode;
use state_machine;
use runtime_primitives::traits::{Header as HeaderT, Hash, Block as BlockT, One, HashFor};
use blockchain::HeaderBackend;
use runtime_primitives::traits::{
Header as HeaderT, Hash, Block as BlockT, One, HashFor, ProvideRuntimeApi, ApiRef
};
use primitives::H256;
use runtime_primitives::generic::BlockId;
use runtime_api::BlockBuilder as BlockBuilderAPI;
use {backend, error, Client, CallExecutor};
use runtime_api::Core;
use error;
use runtime_primitives::ApplyOutcome;
use primitives::{Blake2Hasher, H256};
use hash_db::Hasher;
/// Utility for building new (valid) blocks from a stream of extrinsics.
pub struct BlockBuilder<'a, B, E, Block, H>
where
B: backend::Backend<Block, H> + 'a,
E: CallExecutor<Block, H> + Clone + 'a,
Block: BlockT,
H: Hasher<Out=Block::Hash>,
H::Out: Ord,
{
pub struct BlockBuilder<'a, Block, A: ProvideRuntimeApi> where Block: BlockT {
header: <Block as BlockT>::Header,
extrinsics: Vec<<Block as BlockT>::Extrinsic>,
client: &'a Client<B, E, Block>,
api: ApiRef<'a, A::Api>,
block_id: BlockId<Block>,
changes: state_machine::OverlayedChanges,
_marker: PhantomData<H>,
}
impl<'a, B, E, Block> BlockBuilder<'a, B, E, Block, Blake2Hasher>
impl<'a, Block, A> BlockBuilder<'a, Block, A>
where
B: backend::Backend<Block, Blake2Hasher> + 'a,
E: CallExecutor<Block, Blake2Hasher> + Clone + 'a,
Block: BlockT<Hash=H256>,
A: ProvideRuntimeApi + HeaderBackend<Block> + 'a,
A::Api: BlockBuilderApi<Block>,
{
/// Create a new instance of builder from the given client, building on the latest block.
pub fn new(client: &'a Client<B, E, Block>) -> error::Result<Self> {
client.info().and_then(|i| Self::at_block(&BlockId::Hash(i.chain.best_hash), client))
pub fn new(api: &'a A) -> error::Result<Self> {
api.info().and_then(|i| Self::at_block(&BlockId::Hash(i.best_hash), api))
}
/// Create a new instance of builder from the given client using a particular block's ID to
/// build upon.
pub fn at_block(block_id: &BlockId<Block>, client: &'a Client<B, E, Block>) -> error::Result<Self> {
let number = client.block_number_from_id(block_id)?
pub fn at_block(block_id: &BlockId<Block>, api: &'a A) -> error::Result<Self> {
let number = api.block_number_from_id(block_id)?
.ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{}", block_id)))?
+ One::one();
let parent_hash = client.block_hash_from_id(block_id)?
let parent_hash = api.block_hash_from_id(block_id)?
.ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{}", block_id)))?;
let mut changes = Default::default();
let header = <<Block as BlockT>::Header as HeaderT>::new(
number,
Default::default(),
@@ -76,16 +64,14 @@ where
Default::default()
);
client.initialise_block(block_id, &mut changes, &header)?;
changes.commit_prospective();
let api = api.runtime_api();
api.initialise_block(block_id, &header)?;
Ok(BlockBuilder {
header,
extrinsics: Vec::new(),
client,
api,
block_id: *block_id,
changes,
_marker: Default::default(),
})
}
@@ -93,30 +79,32 @@ where
/// can be validly executed (by executing it); if it is invalid, it'll be returned along with
/// the error. Otherwise, it will return a mutable reference to self (in order to chain).
pub fn push(&mut self, xt: <Block as BlockT>::Extrinsic) -> error::Result<()> {
match self.client.apply_extrinsic(&self.block_id, &mut self.changes, &xt) {
Ok(result) => {
match result {
fn impl_push<'a, T, Block: BlockT>(
api: &mut ApiRef<'a, T>,
block_id: &BlockId<Block>,
xt: Block::Extrinsic,
extrinsics: &mut Vec<Block::Extrinsic>
) -> error::Result<()> where T: BlockBuilderApi<Block> {
api.map_api_result(|api| {
match api.apply_extrinsic(block_id, &xt)? {
Ok(ApplyOutcome::Success) | Ok(ApplyOutcome::Fail) => {
self.extrinsics.push(xt);
self.changes.commit_prospective();
extrinsics.push(xt);
Ok(())
}
Err(e) => {
self.changes.discard_prospective();
Err(error::ErrorKind::ApplyExtinsicFailed(e).into())
Err(error::ErrorKind::ApplyExtrinsicFailed(e).into())
}
}
}
Err(e) => {
self.changes.discard_prospective();
Err(e)
}
})
}
//FIXME: Please NLL, help me!
impl_push(&mut self.api, &self.block_id, xt, &mut self.extrinsics)
}
/// Consume the builder to return a valid `Block` containing all pushed extrinsics.
pub fn bake(mut self) -> error::Result<Block> {
self.header = self.client.finalise_block(&self.block_id, &mut self.changes)?;
self.header = self.api.finalise_block(&self.block_id)?;
debug_assert_eq!(
self.header.extrinsics_root().clone(),
@@ -0,0 +1,23 @@
// Copyright 2018 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Utility struct to build a block.
#[cfg(feature = "std")]
mod block_builder;
#[cfg(feature = "std")]
pub use self::block_builder::*;
pub mod api;
+16
View File
@@ -40,6 +40,22 @@ pub trait HeaderBackend<Block: BlockT>: Send + Sync {
fn expect_header(&self, id: BlockId<Block>) -> Result<Block::Header> {
self.header(id)?.ok_or_else(|| ErrorKind::UnknownBlock(format!("{}", id)).into())
}
/// Convert an arbitrary block ID into a block hash.
fn block_hash_from_id(&self, id: &BlockId<Block>) -> Result<Option<Block::Hash>> {
match *id {
BlockId::Hash(h) => Ok(Some(h)),
BlockId::Number(n) => self.hash(n),
}
}
/// Convert an arbitrary block ID into a block hash.
fn block_number_from_id(&self, id: &BlockId<Block>) -> Result<Option<NumberFor<Block>>> {
match *id {
BlockId::Hash(_) => Ok(self.header(*id)?.map(|h| h.number().clone())),
BlockId::Number(n) => Ok(Some(n)),
}
}
}
/// Blockchain database backend. Does not perform any validation.
+2 -3
View File
@@ -27,7 +27,7 @@ use hash_db;
use heapsize::HeapSizeOf;
use trie;
use primitives::H256;
use primitives::{H256, convert_hash};
use runtime_primitives::traits::{As, Header as HeaderT, SimpleArithmetic, One};
use state_machine::backend::InMemory as InMemoryState;
use state_machine::{prove_read, read_proof_check};
@@ -113,8 +113,7 @@ pub fn check_proof<Header, Hasher>(
Hasher: hash_db::Hasher,
Hasher::Out: Ord + HeapSizeOf,
{
let mut root: Hasher::Out = Default::default();
root.as_mut().copy_from_slice(local_root.as_ref());
let root: Hasher::Out = convert_hash(&local_root);
let local_cht_key = encode_cht_key(local_number);
let local_cht_value = read_proof_check::<Hasher>(root, remote_proof,
&local_cht_key).map_err(|e| ClientError::from(e))?;
+219 -241
View File
@@ -16,36 +16,40 @@
//! Substrate Client
use std::sync::Arc;
use error::{Error, ErrorKind};
use std::{marker::PhantomData, sync::Arc};
use error::Error;
use futures::sync::mpsc;
use parking_lot::{Mutex, RwLock};
use primitives::AuthorityId;
use runtime_primitives::{
Justification,
generic::{BlockId, SignedBlock, Block as RuntimeBlock},
generic::{BlockId, SignedBlock},
transaction_validity::{TransactionValidity, TransactionTag},
};
use consensus::{ImportBlock, ImportResult, BlockOrigin};
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Zero, As, NumberFor, CurrentHeight, BlockNumberToHash};
use runtime_primitives::{ApplyResult, BuildStorage};
use runtime_api as api;
use primitives::{Blake2Hasher, H256, ChangesTrieConfiguration};
use runtime_primitives::traits::{
Block as BlockT, Header as HeaderT, Zero, As, NumberFor, CurrentHeight, BlockNumberToHash,
ApiRef, ProvideRuntimeApi
};
use runtime_primitives::BuildStorage;
use runtime_api::{Core as CoreAPI, CallApiAt, TaggedTransactionQueue, ConstructRuntimeApi};
use primitives::{Blake2Hasher, H256, ChangesTrieConfiguration, convert_hash};
use primitives::storage::{StorageKey, StorageData};
use primitives::storage::well_known_keys;
use codec::{Encode, Decode};
use codec::Decode;
use state_machine::{
Backend as StateBackend, CodeExecutor,
Backend as StateBackend, CodeExecutor, ChangesTrieAnchorBlockId,
ExecutionStrategy, ExecutionManager, prove_read,
key_changes, key_changes_proof, OverlayedChanges
};
use codec::Encode;
use backend::{self, BlockImportOperation};
use blockchain::{self, Info as ChainInfo, Backend as ChainBackend, HeaderBackend as ChainHeaderBackend};
use call_executor::{CallExecutor, LocalCallExecutor};
use executor::{RuntimeVersion, RuntimeInfo};
use notifications::{StorageNotifications, StorageEventStream};
use {cht, error, in_mem, block_builder, genesis, consensus};
use {cht, error, in_mem, block_builder::{self, api::BlockBuilder as BlockBuilderAPI}, genesis, consensus};
/// Type that implements `futures::Stream` of block import events.
pub type ImportNotifications<Block> = mpsc::UnboundedReceiver<BlockImportNotification<Block>>;
@@ -54,7 +58,7 @@ pub type ImportNotifications<Block> = mpsc::UnboundedReceiver<BlockImportNotific
pub type FinalityNotifications<Block> = mpsc::UnboundedReceiver<FinalityNotification<Block>>;
/// Substrate Client
pub struct Client<B, E, Block> where Block: BlockT {
pub struct Client<B, E, Block, RA> where Block: BlockT {
backend: Arc<B>,
executor: E,
storage_notifications: Mutex<StorageNotifications<Block>>,
@@ -65,6 +69,7 @@ pub struct Client<B, E, Block> where Block: BlockT {
block_execution_strategy: ExecutionStrategy,
api_execution_strategy: ExecutionStrategy,
changes_trie_config: Option<ChangesTrieConfiguration>,
_phantom: PhantomData<RA>,
}
/// A source of blockchain events.
@@ -180,36 +185,36 @@ impl<H> PrePostHeader<H> {
}
/// Create an instance of in-memory client.
pub fn new_in_mem<E, Block, S>(
pub fn new_in_mem<E, Block, S, RA>(
executor: E,
genesis_storage: S,
) -> error::Result<Client<in_mem::Backend<Block, Blake2Hasher>, LocalCallExecutor<in_mem::Backend<Block, Blake2Hasher>, E>, Block>>
where
E: CodeExecutor<Blake2Hasher> + RuntimeInfo,
S: BuildStorage,
Block: BlockT<Hash=H256>,
) -> error::Result<Client<in_mem::Backend<Block, Blake2Hasher>, LocalCallExecutor<in_mem::Backend<Block, Blake2Hasher>, E>, Block, RA>>
where
E: CodeExecutor<Blake2Hasher> + RuntimeInfo,
S: BuildStorage,
Block: BlockT<Hash=H256>,
{
new_with_backend(Arc::new(in_mem::Backend::new()), executor, genesis_storage)
}
/// Create a client with the explicitely provided backend.
/// This is useful for testing backend implementations.
pub fn new_with_backend<B, E, Block, S>(
pub fn new_with_backend<B, E, Block, S, RA>(
backend: Arc<B>,
executor: E,
build_genesis_storage: S,
) -> error::Result<Client<B, LocalCallExecutor<B, E>, Block>>
where
E: CodeExecutor<Blake2Hasher> + RuntimeInfo,
S: BuildStorage,
Block: BlockT<Hash=H256>,
B: backend::LocalBackend<Block, Blake2Hasher>
) -> error::Result<Client<B, LocalCallExecutor<B, E>, Block, RA>>
where
E: CodeExecutor<Blake2Hasher> + RuntimeInfo,
S: BuildStorage,
Block: BlockT<Hash=H256>,
B: backend::LocalBackend<Block, Blake2Hasher>
{
let call_executor = LocalCallExecutor::new(backend.clone(), executor);
Client::new(backend, call_executor, build_genesis_storage, ExecutionStrategy::NativeWhenPossible, ExecutionStrategy::NativeWhenPossible)
}
impl<B, E, Block> Client<B, E, Block> where
impl<B, E, Block, RA> Client<B, E, Block, RA> where
B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher>,
Block: BlockT<Hash=H256>,
@@ -255,6 +260,7 @@ impl<B, E, Block> Client<B, E, Block> where
block_execution_strategy,
api_execution_strategy,
changes_trie_config,
_phantom: Default::default(),
})
}
@@ -323,6 +329,36 @@ impl<B, E, Block> Client<B, E, Block> where
self.header_proof_with_cht_size(id, cht::SIZE)
}
pub(crate) fn call_at_state(
&self,
at: &BlockId<Block>,
function: &'static str,
args: Vec<u8>,
changes: &mut OverlayedChanges
) -> error::Result<Vec<u8>> {
let state = self.state_at(at)?;
let execution_manager = || match self.api_execution_strategy {
ExecutionStrategy::NativeWhenPossible => ExecutionManager::NativeWhenPossible,
ExecutionStrategy::AlwaysWasm => ExecutionManager::AlwaysWasm,
ExecutionStrategy::Both => ExecutionManager::Both(|wasm_result, native_result| {
warn!("Consensus error between wasm and native runtime execution at block {:?}", at);
warn!(" Function {:?}", function);
warn!(" Native result {:?}", native_result);
warn!(" Wasm result {:?}", wasm_result);
wasm_result
}),
};
self.executor.call_at_state(&state, changes, function, &args, execution_manager())
.map(|res| res.0)
}
/// Get block hash by number.
pub fn block_hash(&self, block_number: <<Block as BlockT>::Header as HeaderT>::Number) -> error::Result<Option<Block::Hash>> {
self.backend.blockchain().hash(block_number)
}
/// Reads given header and generates CHT-based header proof for CHT of given size.
pub fn header_proof_with_cht_size(&self, id: &BlockId<Block>, cht_size: u64) -> error::Result<(Block::Header, Vec<Vec<u8>>)> {
let proof_error = || error::ErrorKind::Backend(format!("Failed to generate header proof for {:?}", id));
@@ -355,7 +391,10 @@ impl<B, E, Block> Client<B, E, Block> where
config,
storage,
self.require_block_number_from_id(&BlockId::Hash(first))?.as_(),
self.require_block_number_from_id(&BlockId::Hash(last))?.as_(),
&ChangesTrieAnchorBlockId {
hash: convert_hash(&last),
number: self.require_block_number_from_id(&BlockId::Hash(last))?.as_(),
},
self.backend.blockchain().info()?.best_number.as_(),
key)
.map_err(|err| error::ErrorKind::ChangesTrieAccessFailed(err).into())
@@ -388,7 +427,10 @@ impl<B, E, Block> Client<B, E, Block> where
config,
storage,
self.require_block_number_from_id(&BlockId::Hash(first))?.as_(),
self.require_block_number_from_id(&BlockId::Hash(last))?.as_(),
&ChangesTrieAnchorBlockId {
hash: convert_hash(&last),
number: self.require_block_number_from_id(&BlockId::Hash(last))?.as_(),
},
max_number.as_(),
key)
.map_err(|err| error::ErrorKind::ChangesTrieAccessFailed(err).into())
@@ -396,88 +438,41 @@ impl<B, E, Block> Client<B, E, Block> where
}
/// Create a new block, built on the head of the chain.
pub fn new_block(&self) -> error::Result<block_builder::BlockBuilder<B, E, Block, Blake2Hasher>>
where E: Clone
pub fn new_block(
&self
) -> error::Result<block_builder::BlockBuilder<Block, Self>> where
E: Clone + Send + Sync,
RA: BlockBuilderAPI<Block>
{
block_builder::BlockBuilder::new(self)
}
/// Create a new block, built on top of `parent`.
pub fn new_block_at(&self, parent: &BlockId<Block>) -> error::Result<block_builder::BlockBuilder<B, E, Block, Blake2Hasher>>
where E: Clone
pub fn new_block_at(
&self, parent: &BlockId<Block>
) -> error::Result<block_builder::BlockBuilder<Block, Self>> where
E: Clone + Send + Sync,
RA: BlockBuilderAPI<Block>
{
block_builder::BlockBuilder::at_block(parent, &self)
}
/// Set up the native execution environment to call into a native runtime code.
pub fn call_api<A, R>(&self, function: &'static str, args: &A) -> error::Result<R>
where A: Encode, R: Decode
{
self.call_api_at(&BlockId::Number(self.info()?.chain.best_number), function, args)
}
/// Call a runtime function at given block.
pub fn call_api_at<A, R>(&self, at: &BlockId<Block>, function: &'static str, args: &A) -> error::Result<R>
where A: Encode, R: Decode
{
let parent = at;
let header = <<Block as BlockT>::Header as HeaderT>::new(
self.block_number_from_id(&parent)?
.ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{:?}", parent)))? + As::sa(1),
Default::default(),
Default::default(),
self.block_hash_from_id(&parent)?
.ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{:?}", parent)))?,
Default::default()
);
let mut overlay = Default::default();
self.call_at_state(at, "initialise_block", &header, &mut overlay)?;
self.call_at_state(at, function, args, &mut overlay)
}
fn call_at_state<A: Encode, R: Decode>(
&self,
at: &BlockId<Block>,
function: &'static str,
args: &A,
changes: &mut OverlayedChanges
) -> error::Result<R> {
let state = self.state_at(at)?;
let execution_manager = || match self.api_execution_strategy {
ExecutionStrategy::NativeWhenPossible => ExecutionManager::NativeWhenPossible,
ExecutionStrategy::AlwaysWasm => ExecutionManager::AlwaysWasm,
ExecutionStrategy::Both => ExecutionManager::Both(|wasm_result, native_result| {
warn!("Consensus error between wasm and native runtime execution at block {:?}", at);
warn!(" Function {:?}", function);
warn!(" Native result {:?}", native_result);
warn!(" Wasm result {:?}", wasm_result);
wasm_result
}),
};
self.executor.call_at_state(
&state,
changes,
function,
&args.encode(),
execution_manager()
).and_then(|res|
R::decode(&mut &res.0[..])
.ok_or_else(|| Error::from(ErrorKind::CallResultDecode(function)))
)
}
// TODO [ToDr] Optimize and re-use tags from the pool.
fn transaction_tags(&self, at: Block::Hash, body: &Option<Vec<Block::Extrinsic>>) -> error::Result<Vec<TransactionTag>> {
fn transaction_tags(
&self,
at: Block::Hash,
body: &Option<Vec<Block::Extrinsic>>
) -> error::Result<Vec<TransactionTag>> where
RA: TaggedTransactionQueue<Block>,
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + Clone,
{
let id = BlockId::Hash(at);
Ok(match body {
None => vec![],
Some(ref extrinsics) => {
let mut tags = vec![];
for tx in extrinsics {
let tx = api::TaggedTransactionQueue::validate_transaction(self, &id, &tx)?;
let tx = self.runtime_api().validate_transaction(&id, &tx)?;
match tx {
TransactionValidity::Valid { mut provides, .. } => {
tags.append(&mut provides);
@@ -503,7 +498,10 @@ impl<B, E, Block> Client<B, E, Block> where
authorities: Option<Vec<AuthorityId>>,
finalized: bool,
aux: Vec<(Vec<u8>, Option<Vec<u8>>)>,
) -> error::Result<ImportResult> {
) -> error::Result<ImportResult> where
RA: TaggedTransactionQueue<Block>,
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + Clone,
{
let parent_hash = import_headers.post().parent_hash().clone();
match self.backend.blockchain().status(BlockId::Hash(hash))? {
blockchain::BlockStatus::InChain => return Ok(ImportResult::AlreadyInChain),
@@ -734,30 +732,9 @@ impl<B, E, Block> Client<B, E, Block> where
}
}
/// Get block hash by number.
pub fn block_hash(&self, block_number: <<Block as BlockT>::Header as HeaderT>::Number) -> error::Result<Option<Block::Hash>> {
self.backend.blockchain().hash(block_number)
}
/// Convert an arbitrary block ID into a block hash.
pub fn block_hash_from_id(&self, id: &BlockId<Block>) -> error::Result<Option<Block::Hash>> {
match *id {
BlockId::Hash(h) => Ok(Some(h)),
BlockId::Number(n) => self.block_hash(n),
}
}
/// Convert an arbitrary block ID into a block hash.
pub fn block_number_from_id(&self, id: &BlockId<Block>) -> error::Result<Option<NumberFor<Block>>> {
match *id {
BlockId::Hash(_) => Ok(self.header(id)?.map(|h| h.number().clone())),
BlockId::Number(n) => Ok(Some(n)),
}
}
/// Convert an arbitrary block ID into a block hash, returning error if the block is unknown.
fn require_block_number_from_id(&self, id: &BlockId<Block>) -> error::Result<NumberFor<Block>> {
self.block_number_from_id(id)
self.backend.blockchain().block_number_from_id(id)
.and_then(|n| n.ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{}", id)).into()))
}
@@ -778,11 +755,11 @@ impl<B, E, Block> Client<B, E, Block> where
/// Get full block by id.
pub fn block(&self, id: &BlockId<Block>)
-> error::Result<Option<SignedBlock<Block::Header, Block::Extrinsic>>>
-> error::Result<Option<SignedBlock<Block>>>
{
Ok(match (self.header(id)?, self.body(id)?, self.justification(id)?) {
(Some(header), Some(extrinsics), Some(justification)) =>
Some(SignedBlock { block: RuntimeBlock { header, extrinsics }, justification }),
Some(SignedBlock { block: Block::new(header, extrinsics), justification }),
_ => None,
})
}
@@ -895,11 +872,87 @@ impl<B, E, Block> Client<B, E, Block> where
}
}
impl<B, E, Block> consensus::BlockImport<Block> for Client<B, E, Block> where
impl<B, E, Block, RA> ChainHeaderBackend<Block> for Client<B, E, Block, RA> where
B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher> + Clone,
E: CallExecutor<Block, Blake2Hasher> + Send + Sync,
Block: BlockT<Hash=H256>,
RA: Send + Sync
{
fn header(&self, id: BlockId<Block>) -> error::Result<Option<Block::Header>> {
self.backend.blockchain().header(id)
}
fn info(&self) -> error::Result<blockchain::Info<Block>> {
self.backend.blockchain().info()
}
fn status(&self, id: BlockId<Block>) -> error::Result<blockchain::BlockStatus> {
self.backend.blockchain().status(id)
}
fn number(&self, hash: Block::Hash) -> error::Result<Option<<<Block as BlockT>::Header as HeaderT>::Number>> {
self.backend.blockchain().number(hash)
}
fn hash(&self, number: NumberFor<Block>) -> error::Result<Option<Block::Hash>> {
self.backend.blockchain().hash(number)
}
}
impl<B, E, Block, RA> ProvideRuntimeApi for Client<B, E, Block, RA> where
B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher> + Clone + Send + Sync,
Block: BlockT<Hash=H256>,
RA: CoreAPI<Block>
{
type Api = RA;
fn runtime_api<'a>(&'a self) -> ApiRef<'a, Self::Api> {
Self::Api::construct_runtime_api(self)
}
}
impl<B, E, Block, RA> CallApiAt<Block> for Client<B, E, Block, RA> where
B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher> + Clone + Send + Sync,
Block: BlockT<Hash=H256>,
RA: Send + Sync,
{
fn call_api_at(
&self,
at: &BlockId<Block>,
function: &'static str,
args: Vec<u8>,
changes: &mut OverlayedChanges,
initialised_block: &mut Option<BlockId<Block>>,
) -> error::Result<Vec<u8>> {
//TODO: Find a better way to prevent double block initialization
if function != "initialise_block" && initialised_block.map(|id| id != *at).unwrap_or(true) {
let parent = at;
let header = <<Block as BlockT>::Header as HeaderT>::new(
self.block_number_from_id(parent)?
.ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{:?}", parent)))?
+ As::sa(1),
Default::default(),
Default::default(),
self.block_hash_from_id(&parent)?
.ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{:?}", parent)))?,
Default::default()
);
self.call_at_state(at, "initialise_block", header.encode(), changes)?;
*initialised_block = Some(*at);
}
self.call_at_state(at, function, args, changes)
}
}
impl<B, E, Block, RA> consensus::BlockImport<Block> for Client<B, E, Block, RA> where
B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher> + Clone + Send + Sync,
Block: BlockT<Hash=H256>,
RA: TaggedTransactionQueue<Block>
{
type Error = Error;
@@ -914,8 +967,8 @@ impl<B, E, Block> consensus::BlockImport<Block> for Client<B, E, Block> where
let ImportBlock {
origin,
header,
external_justification,
post_runtime_digests,
justification,
post_digests,
body,
finalized,
auxiliary,
@@ -927,11 +980,11 @@ impl<B, E, Block> consensus::BlockImport<Block> for Client<B, E, Block> where
blockchain::BlockStatus::Unknown => return Ok(ImportResult::UnknownParent),
}
let import_headers = if post_runtime_digests.is_empty() {
let import_headers = if post_digests.is_empty() {
PrePostHeader::Same(header)
} else {
let mut post_header = header.clone();
for item in post_runtime_digests {
for item in post_digests {
post_header.digest_mut().push(item);
}
PrePostHeader::Different(header, post_header)
@@ -946,7 +999,7 @@ impl<B, E, Block> consensus::BlockImport<Block> for Client<B, E, Block> where
origin,
hash,
import_headers,
external_justification,
justification,
body,
new_authorities,
finalized,
@@ -963,7 +1016,7 @@ impl<B, E, Block> consensus::BlockImport<Block> for Client<B, E, Block> where
}
}
impl<B, E, Block> consensus::Authorities<Block> for Client<B, E, Block> where
impl<B, E, Block, RA> consensus::Authorities<Block> for Client<B, E, Block, RA> where
B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher> + Clone,
Block: BlockT<Hash=H256>,
@@ -974,7 +1027,7 @@ impl<B, E, Block> consensus::Authorities<Block> for Client<B, E, Block> where
}
}
impl<B, E, Block> CurrentHeight for Client<B, E, Block> where
impl<B, E, Block, RA> CurrentHeight for Client<B, E, Block, RA> where
B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher> + Clone,
Block: BlockT<Hash=H256>,
@@ -985,7 +1038,7 @@ impl<B, E, Block> CurrentHeight for Client<B, E, Block> where
}
}
impl<B, E, Block> BlockNumberToHash for Client<B, E, Block> where
impl<B, E, Block, RA> BlockNumberToHash for Client<B, E, Block, RA> where
B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher> + Clone,
Block: BlockT<Hash=H256>,
@@ -998,7 +1051,7 @@ impl<B, E, Block> BlockNumberToHash for Client<B, E, Block> where
}
impl<B, E, Block> BlockchainEvents<Block> for Client<B, E, Block>
impl<B, E, Block, RA> BlockchainEvents<Block> for Client<B, E, Block, RA>
where
E: CallExecutor<Block, Blake2Hasher>,
Block: BlockT<Hash=H256>,
@@ -1022,7 +1075,7 @@ where
}
}
impl<B, E, Block> ChainHead<Block> for Client<B, E, Block>
impl<B, E, Block, RA> ChainHead<Block> for Client<B, E, Block, RA>
where
B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher>,
@@ -1033,116 +1086,17 @@ where
}
}
impl<B, E, Block> BlockBody<Block> for Client<B, E, Block> where
B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher>,
Block: BlockT<Hash=H256>,
impl<B, E, Block, RA> BlockBody<Block> for Client<B, E, Block, RA>
where
B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher>,
Block: BlockT<Hash=H256>,
{
fn block_body(&self, id: &BlockId<Block>) -> error::Result<Option<Vec<<Block as BlockT>::Extrinsic>>> {
self.body(id)
}
}
impl<B, E, Block> api::Core<Block, AuthorityId> for Client<B, E, Block> where
B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher>,
Block: BlockT<Hash=H256>,
{
type Error = Error;
fn version(&self, at: &BlockId<Block>) -> Result<RuntimeVersion, Self::Error> {
self.call_api_at(at, "version", &())
}
fn authorities(&self, at: &BlockId<Block>) -> Result<Vec<AuthorityId>, Self::Error> {
self.authorities_at(at)
}
fn execute_block(&self, at: &BlockId<Block>, block: &Block) -> Result<(), Self::Error> {
self.call_api_at(at, "execute_block", &(block))
}
}
impl<B, E, Block> api::Metadata<Block, Vec<u8>> for Client<B, E, Block> where
B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher>,
Block: BlockT<Hash=H256>,
{
type Error = Error;
fn metadata(&self, at: &BlockId<Block>) -> Result<Vec<u8>, Self::Error> {
self.executor.call(at, "metadata",&[]).map(|v| v.return_data)
}
}
impl<B, E, Block> api::BlockBuilder<Block> for Client<B, E, Block> where
B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher>,
Block: BlockT<Hash=H256>,
{
type Error = Error;
type OverlayedChanges = OverlayedChanges;
fn initialise_block(
&self,
at: &BlockId<Block>,
changes: &mut OverlayedChanges,
header: &<Block as BlockT>::Header
) -> Result<(), Self::Error> {
self.call_at_state(at, "initialise_block", header, changes)
}
fn apply_extrinsic(
&self,
at: &BlockId<Block>,
changes: &mut OverlayedChanges,
extrinsic: &<Block as BlockT>::Extrinsic
) -> Result<ApplyResult, Self::Error> {
self.call_at_state(at, "apply_extrinsic", extrinsic, changes)
}
fn finalise_block(
&self,
at: &BlockId<Block>,
changes: &mut OverlayedChanges
) -> Result<<Block as BlockT>::Header, Self::Error> {
self.call_at_state(at, "finalise_block", &(), changes)
}
fn inherent_extrinsics<InherentExtrinsic: Encode + Decode, UncheckedExtrinsic: Encode + Decode>(
&self, at: &BlockId<Block>, inherent: &InherentExtrinsic
) -> Result<Vec<UncheckedExtrinsic>, Self::Error> {
self.call_api_at(at, "inherent_extrinsics", &(inherent))
}
fn check_inherents<InherentData: Encode + Decode, InherentError: Encode + Decode>(
&self,
at: &BlockId<Block>,
block: &Block,
data: &InherentData
) -> Result<Result<(), InherentError>, Self::Error> {
self.call_api_at(at, "check_inherents", &(block, data))
}
fn random_seed(&self, at: &BlockId<Block>) -> Result<<Block as BlockT>::Hash, Self::Error> {
self.call_api_at(at, "random_seed", &())
}
}
impl<B, E, Block> api::TaggedTransactionQueue<Block> for Client<B, E, Block> where
B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher>,
Block: BlockT<Hash=H256>,
{
type Error = Error;
fn validate_transaction<TransactionValidity: Encode + Decode>(
&self, at: &BlockId<Block>, tx: &<Block as BlockT>::Extrinsic
) -> Result<TransactionValidity, Self::Error> {
self.call_api_at(at, "validate_transaction", &(tx))
}
}
#[cfg(test)]
pub(crate) mod tests {
use std::collections::HashMap;
@@ -1155,14 +1109,14 @@ pub(crate) mod tests {
use consensus::BlockOrigin;
use test_client::client::backend::Backend as TestBackend;
use test_client::BlockBuilderExt;
use test_client::runtime::{self, Block, Transfer};
use test_client::runtime::{self, Block, Transfer, ClientWithApi, test_api::TestAPI};
/// Returns tuple, consisting of:
/// 1) test client pre-filled with blocks changing balances;
/// 2) roots of changes tries for these blocks
/// 3) test cases in form (begin, end, key, vec![(block, extrinsic)]) that are required to pass
pub fn prepare_client_with_key_changes() -> (
test_client::client::Client<test_client::Backend, test_client::Executor, Block>,
test_client::client::Client<test_client::Backend, test_client::Executor, Block, ClientWithApi>,
Vec<H256>,
Vec<(u64, u64, Vec<u8>, Vec<(u64, u32)>)>,
) {
@@ -1234,8 +1188,20 @@ pub(crate) mod tests {
fn client_initialises_from_genesis_ok() {
let client = test_client::new();
assert_eq!(client.call_api::<_, u64>("balance_of", &Keyring::Alice.to_raw_public()).unwrap(), 1000);
assert_eq!(client.call_api::<_, u64>("balance_of", &Keyring::Ferdie.to_raw_public()).unwrap(), 0);
assert_eq!(
client.runtime_api().balance_of(
&BlockId::Number(client.info().unwrap().chain.best_number),
&Keyring::Alice.to_raw_public()
).unwrap(),
1000
);
assert_eq!(
client.runtime_api().balance_of(
&BlockId::Number(client.info().unwrap().chain.best_number),
&Keyring::Ferdie.to_raw_public()
).unwrap(),
0
);
}
#[test]
@@ -1278,8 +1244,20 @@ pub(crate) mod tests {
assert_eq!(client.info().unwrap().chain.best_number, 1);
assert!(client.state_at(&BlockId::Number(1)).unwrap() != client.state_at(&BlockId::Number(0)).unwrap());
assert_eq!(client.call_api::<_, u64>("balance_of", &Keyring::Alice.to_raw_public()).unwrap(), 958);
assert_eq!(client.call_api::<_, u64>("balance_of", &Keyring::Ferdie.to_raw_public()).unwrap(), 42);
assert_eq!(
client.runtime_api().balance_of(
&BlockId::Number(client.info().unwrap().chain.best_number),
&Keyring::Alice.to_raw_public()
).unwrap(),
958
);
assert_eq!(
client.runtime_api().balance_of(
&BlockId::Number(client.info().unwrap().chain.best_number),
&Keyring::Ferdie.to_raw_public()
).unwrap(),
42
);
}
#[test]
+1 -1
View File
@@ -41,7 +41,7 @@ error_chain! {
}
/// Applying extrinsic error.
ApplyExtinsicFailed(e: ApplyError) {
ApplyExtrinsicFailed(e: ApplyError) {
description("Extrinsic error"),
display("Extrinsic error: {:?}", e),
}
+1 -1
View File
@@ -67,7 +67,7 @@ mod tests {
use trie::ordered_trie_root;
let transactions = txs.into_iter().map(|tx| {
let signature = Pair::from(Keyring::from_public(Public::from_raw(tx.from.0)).unwrap())
let signature = Pair::from(Keyring::from_public(Public::from_raw(tx.from.to_fixed_bytes())).unwrap())
.sign(&tx.encode()).into();
Extrinsic { transfer: tx, signature }
+56 -17
View File
@@ -14,64 +14,103 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
// tag::description[]
//! Substrate Client and associated logic.
// end::description[]
#![cfg_attr(not(feature = "std"), no_std)]
#![warn(missing_docs)]
#![recursion_limit="128"]
#[cfg(feature = "std")]
extern crate substrate_trie as trie;
extern crate parity_codec as codec;
extern crate substrate_primitives as primitives;
extern crate sr_primitives as runtime_primitives;
#[cfg(feature = "std")]
extern crate substrate_state_machine as state_machine;
#[cfg(feature = "std")]
extern crate substrate_consensus_common as consensus;
#[cfg(test)] extern crate substrate_keyring as keyring;
#[cfg(test)] extern crate substrate_test_client as test_client;
#[macro_use] extern crate substrate_telemetry;
#[macro_use] extern crate slog; // needed until we can reexport `slog_info` from `substrate_telemetry`
extern crate sr_version as runtime_version;
extern crate sr_std as rstd;
#[cfg(test)]
extern crate substrate_keyring as keyring;
#[cfg(test)]
extern crate substrate_test_client as test_client;
#[cfg(feature = "std")]
#[macro_use]
extern crate substrate_telemetry;
#[cfg(feature = "std")]
#[macro_use]
extern crate slog; // needed until we can reexport `slog_info` from `substrate_telemetry`
#[cfg(feature = "std")]
extern crate fnv;
#[cfg(feature = "std")]
extern crate futures;
#[cfg(feature = "std")]
extern crate parking_lot;
#[cfg(feature = "std")]
extern crate hash_db;
#[cfg(feature = "std")]
extern crate heapsize;
#[cfg(feature = "std")]
extern crate kvdb;
extern crate sr_api;
#[macro_use] extern crate error_chain;
#[macro_use] extern crate log;
#[cfg_attr(test, macro_use)] extern crate substrate_executor as executor;
#[cfg(test)] #[macro_use] extern crate hex_literal;
#[cfg(test)] extern crate kvdb_memorydb;
#[cfg(feature = "std")]
#[macro_use]
extern crate error_chain;
#[cfg(feature = "std")]
#[macro_use]
extern crate log;
#[cfg(feature = "std")]
#[cfg_attr(test, macro_use)]
extern crate substrate_executor as executor;
#[cfg(test)]
#[macro_use]
extern crate hex_literal;
#[cfg(feature = "std")]
#[cfg(test)]
extern crate kvdb_memorydb;
#[macro_use]
pub mod runtime_api;
#[cfg(feature = "std")]
pub mod error;
#[cfg(feature = "std")]
pub mod blockchain;
#[cfg(feature = "std")]
pub mod backend;
#[cfg(feature = "std")]
pub mod cht;
#[cfg(feature = "std")]
pub mod in_mem;
#[cfg(feature = "std")]
pub mod genesis;
pub mod block_builder;
#[cfg(feature = "std")]
pub mod light;
#[cfg(feature = "std")]
mod leaves;
#[cfg(feature = "std")]
mod call_executor;
#[cfg(feature = "std")]
mod client;
#[cfg(feature = "std")]
mod notifications;
#[cfg(feature = "std")]
pub use blockchain::Info as ChainInfo;
#[cfg(feature = "std")]
pub use call_executor::{CallResult, CallExecutor, LocalCallExecutor};
#[cfg(feature = "std")]
pub use client::{
new_with_backend,
new_in_mem,
BlockBody, BlockStatus, ImportNotifications, FinalityNotifications, BlockchainEvents,
Client, ClientInfo, ChainHead,
};
#[cfg(feature = "std")]
pub use notifications::{StorageEventStream, StorageChangeSet};
#[cfg(feature = "std")]
pub use state_machine::ExecutionStrategy;
#[cfg(feature = "std")]
pub use leaves::LeafSet;
/// Traits for interfacing with the runtime from the client.
pub mod runtime_api {
pub use sr_api::*;
}
@@ -21,6 +21,7 @@ use std::marker::PhantomData;
use std::sync::Arc;
use futures::{IntoFuture, Future};
use primitives::convert_hash;
use runtime_primitives::generic::BlockId;
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT};
use state_machine::{Backend as StateBackend, CodeExecutor, OverlayedChanges,
@@ -136,8 +137,7 @@ pub fn check_execution_proof<Header, E, H>(
{
let local_state_root = request.header.state_root();
let mut root: H::Out = Default::default();
root.as_mut().copy_from_slice(local_state_root.as_ref());
let root: H::Out = convert_hash(&local_state_root);
let mut changes = OverlayedChanges::default();
let local_result = execution_proof_check::<H, _>(
+13 -14
View File
@@ -21,10 +21,10 @@ use futures::IntoFuture;
use hash_db::Hasher;
use heapsize::HeapSizeOf;
use primitives::ChangesTrieConfiguration;
use primitives::{ChangesTrieConfiguration, convert_hash};
use runtime_primitives::traits::{As, Block as BlockT, Header as HeaderT, NumberFor};
use state_machine::{CodeExecutor, ChangesTrieRootsStorage, read_proof_check,
key_changes_proof_check};
use state_machine::{CodeExecutor, ChangesTrieRootsStorage, ChangesTrieAnchorBlockId,
read_proof_check, key_changes_proof_check};
use call_executor::CallResult;
use cht;
@@ -192,9 +192,8 @@ impl<E, Block, H> FetchChecker<Block> for LightDataChecker<E, H>
request: &RemoteReadRequest<Block::Header>,
remote_proof: Vec<Vec<u8>>
) -> ClientResult<Option<Vec<u8>>> {
let mut root: H::Out = Default::default();
root.as_mut().copy_from_slice(request.header.state_root().as_ref());
read_proof_check::<H>(root, remote_proof, &request.key).map_err(Into::into)
read_proof_check::<H>(convert_hash(request.header.state_root()), remote_proof, &request.key)
.map_err(Into::into)
}
fn check_execution_proof(
@@ -229,7 +228,10 @@ impl<E, Block, H> FetchChecker<Block> for LightDataChecker<E, H>
},
remote_proof,
first_number,
request.last_block.0.as_(),
&ChangesTrieAnchorBlockId {
hash: convert_hash(&request.last_block.1),
number: request.last_block.0.as_(),
},
remote_max.as_(),
&request.key)
.map(|pairs| pairs.into_iter().map(|(b, x)| (As::sa(b), x)).collect())
@@ -248,15 +250,12 @@ impl<'a, H, Hash> ChangesTrieRootsStorage<H> for RootsStorage<'a, Hash>
H: Hasher,
Hash: 'a + Send + Sync + Clone + AsRef<[u8]>,
{
fn root(&self, block: u64) -> Result<Option<H::Out>, String> {
fn root(&self, _anchor: &ChangesTrieAnchorBlockId<H::Out>, block: u64) -> Result<Option<H::Out>, String> {
// we can't ask for roots from parallel forks here => ignore anchor
Ok(block.checked_sub(self.first)
.and_then(|index| self.roots.get(index as usize))
.cloned()
.map(|root| {
let mut hasher_root: H::Out = Default::default();
hasher_root.as_mut().copy_from_slice(root.as_ref());
hasher_root
}))
.map(|root| convert_hash(&root)))
}
}
@@ -478,7 +477,7 @@ pub mod tests {
assert!(local_checker.check_changes_proof(&request, remote_max + 1, remote_proof.clone()).is_err());
// check proof on local client using broken proof
remote_proof = local_roots_range.into_iter().map(|v| v.to_vec()).collect();
remote_proof = local_roots_range.into_iter().map(|v| v.as_bytes().to_vec()).collect();
assert!(local_checker.check_changes_proof(&request, remote_max, remote_proof).is_err());
}
}
+2 -2
View File
@@ -48,11 +48,11 @@ pub fn new_light_backend<B: BlockT, S: BlockchainStorage<B>, F: Fetcher<B>>(bloc
}
/// Create an instance of light client.
pub fn new_light<B, S, F, GS>(
pub fn new_light<B, S, F, GS, RA>(
backend: Arc<Backend<S, F>>,
fetcher: Arc<F>,
genesis_storage: GS,
) -> ClientResult<Client<Backend<S, F>, RemoteCallExecutor<Blockchain<S, F>, F, Blake2Hasher>, B>>
) -> ClientResult<Client<Backend<S, F>, RemoteCallExecutor<Blockchain<S, F>, F, Blake2Hasher>, B, RA>>
where
B: BlockT<Hash=H256>,
S: BlockchainStorage<B>,
@@ -0,0 +1,62 @@
// Copyright 2018 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
#[cfg(feature = "std")]
use super::{ConstructRuntimeApi, ApiExt};
use runtime_version::RuntimeVersion;
use runtime_primitives::traits::Block as BlockT;
#[cfg(feature = "std")]
use runtime_primitives::generic::BlockId;
use primitives::AuthorityId;
#[cfg(feature = "std")]
use error::Result;
use rstd::vec::Vec;
/// The `Core` api trait that is mandantory for each runtime.
/// This is the side that should be implemented for the `RuntimeApi` that is used by the `Client`.
/// Any modifications at one of these two traits, needs to be done on the other one as well.
#[cfg(feature = "std")]
pub trait Core<Block: BlockT>: 'static + Send + Sync + ConstructRuntimeApi<Block> + ApiExt {
/// Returns the version of the runtime.
fn version(&self, at: &BlockId<Block>) -> Result<RuntimeVersion>;
/// Returns the authorities.
fn authorities(&self, at: &BlockId<Block>) -> Result<Vec<AuthorityId>>;
/// Execute the given block.
fn execute_block(&self, at: &BlockId<Block>, block: &Block) -> Result<()>;
/// Initialise a block with the given header.
fn initialise_block(
&self,
at: &BlockId<Block>,
header: &<Block as BlockT>::Header
) -> Result<()>;
}
pub mod runtime {
use super::*;
/// The `Core` api trait that is mandantory for each runtime.
/// This is the side that should be implemented for the `Runtime`.
pub trait Core<Block: BlockT> {
/// Returns the version of the runtime.
fn version() -> RuntimeVersion;
/// Returns the authorities.
fn authorities() -> Vec<AuthorityId>;
/// Execute the given block.
fn execute_block(block: Block);
/// Initialise a block with the given header.
fn initialise_block(header: <Block as BlockT>::Header);
}
}
@@ -14,25 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
// tag::description[]
//! API's for interfacing with the runtime via native/wasm.
// end::description[]
#![cfg_attr(not(feature = "std"), no_std)]
extern crate sr_std as rstd;
extern crate sr_primitives as primitives;
#[doc(hidden)]
pub extern crate parity_codec as codec;
extern crate sr_version as runtime_version;
use rstd::vec::Vec;
#[doc(hidden)]
pub use primitives::{traits::Block as BlockT, generic::BlockId, transaction_validity::TransactionValidity, ApplyResult};
#[doc(hidden)]
pub use rstd::slice;
#[doc(hidden)]
pub use codec::{Encode, Decode};
//! Macros for declaring and implementing the runtime APIs.
// these are part of the public API, so need to be re-exported
pub use runtime_version::{ApiId, RuntimeVersion};
@@ -42,7 +24,7 @@ pub use runtime_version::{ApiId, RuntimeVersion};
/// # Example:
///
/// ```nocompile
/// decl_apis!{
/// decl_runtime_apis!{
/// pub trait Test<Event> ExtraClientSide<ClientArg> {
/// fn test<AccountId>(event: Event) -> AccountId;
///
@@ -70,11 +52,11 @@ pub use runtime_version::{ApiId, RuntimeVersion};
/// }
/// ```
///
/// The declarations generated in the `runtime` module will be used by `impl_apis!` for implementing
/// The declarations generated in the `runtime` module will be used by `impl_runtime_apis!` for implementing
/// the traits for a runtime. The other declarations should be used for implementing the interface
/// in the client.
#[macro_export]
macro_rules! decl_apis {
macro_rules! decl_runtime_apis {
(
$(
$( #[$attr:meta] )*
@@ -93,7 +75,7 @@ macro_rules! decl_apis {
)*
) => {
$(
decl_apis!(
decl_runtime_apis!(
@ADD_BLOCK_GENERIC
$( #[$attr] )*
pub trait $name $(< $( $generic_param $( : $generic_bound )* ),* >)* {
@@ -111,7 +93,7 @@ macro_rules! decl_apis {
$( $( $generic_param $( : $generic_bound )* ),* )*
);
)*
decl_apis! {
decl_runtime_apis! {
@GENERATE_RUNTIME_TRAITS
$(
$( #[$attr] )*
@@ -140,7 +122,7 @@ macro_rules! decl_apis {
Block: BlockT
$(, $generic_param_rest:ident $( : $generic_bound_rest:ident )* )*
) => {
decl_apis!(
decl_runtime_apis!(
@ADD_BLOCK_GENERIC
$( #[$attr] )*
pub trait $name $(< $( $generic_param_orig $( : $generic_bound_orig )* ),* >)* {
@@ -153,7 +135,7 @@ macro_rules! decl_apis {
)*
};
Found;
$( $generic_param_parsed $( : $generic_bound_parsed )* , )* Block: $crate::BlockT;
$( $generic_param_parsed $( : $generic_bound_parsed )* , )* Block: $crate::runtime_api::BlockT;
$( $generic_param_rest $( : $generic_bound_rest )* ),*
);
};
@@ -173,7 +155,7 @@ macro_rules! decl_apis {
$generic_param:ident $( : $generic_bound:ident )*
$(, $generic_param_rest:ident $( : $generic_bound_rest:ident )* )*
) => {
decl_apis!(
decl_runtime_apis!(
@ADD_BLOCK_GENERIC
$( #[$attr] )*
pub trait $name $(< $( $generic_param_orig $( : $generic_bound_orig )* ),* >)* {
@@ -204,7 +186,7 @@ macro_rules! decl_apis {
Found;
$( $generic_param_parsed:ident $( : $generic_bound_parsed:path )* ),*;
) => {
decl_apis!(
decl_runtime_apis!(
@GENERATE_RETURN_TYPES
$( #[$attr] )*
pub trait $name $(< $( $generic_param_orig $( : $generic_bound_orig )* ),* >)* {
@@ -235,7 +217,7 @@ macro_rules! decl_apis {
;
$( $generic_param_parsed:ident $( : $generic_bound_parsed:ident )* ),*;
) => {
decl_apis!(
decl_runtime_apis!(
@GENERATE_RETURN_TYPES
$( #[$attr] )*
pub trait $name $(< $( $generic_param_orig $( : $generic_bound_orig )* ),* >)* {
@@ -248,7 +230,7 @@ macro_rules! decl_apis {
)*
};
// We need to add the required generic Block parameter
Block: $crate::BlockT $(, $generic_param_parsed $( : $generic_bound_parsed )* )*;
Block: $crate::runtime_api::BlockT $(, $generic_param_parsed $( : $generic_bound_parsed )* )*;
{};
$( $( $return_ty )*; )*
);
@@ -269,7 +251,7 @@ macro_rules! decl_apis {
$return_ty_current:ty;
$( $( $return_ty_rest:ty )*; )*
) => {
decl_apis!(
decl_runtime_apis!(
@GENERATE_RETURN_TYPES
$( #[$attr] )*
pub trait $name $(< $( $generic_param_orig $( : $generic_bound_orig )* ),* >)* {
@@ -282,7 +264,7 @@ macro_rules! decl_apis {
)*
};
$( $generic_param_parsed $( : $generic_bound_parsed )* ),*;
{ $( $result_return_ty; )* Result<$return_ty_current, Self::Error>; };
{ $( $result_return_ty; )* $crate::error::Result<$return_ty_current>; };
$( $( $return_ty_rest )*; )*
);
};
@@ -302,7 +284,7 @@ macro_rules! decl_apis {
;
$( $( $return_ty_rest:ty )*; )*
) => {
decl_apis!(
decl_runtime_apis!(
@GENERATE_RETURN_TYPES
$( #[$attr] )*
pub trait $name $(< $( $generic_param_orig $( : $generic_bound_orig )* ),* >)* {
@@ -315,7 +297,7 @@ macro_rules! decl_apis {
)*
};
$( $generic_param_parsed $( : $generic_bound_parsed )* ),*;
{ $( $result_return_ty; )* Result<(), Self::Error>; };
{ $( $result_return_ty; )* $crate::error::Result<()>; };
$( $( $return_ty_rest )*; )*
);
};
@@ -333,7 +315,7 @@ macro_rules! decl_apis {
$( $generic_param_parsed:ident $( : $generic_bound_parsed:path )* ),*;
{ $( $result_return_ty:ty; )* };
) => {
decl_apis!(
decl_runtime_apis!(
@GENERATE_CLIENT_TRAITS
$( #[$attr] )*
pub trait $name $(< $( $generic_param_orig $( : $generic_bound_orig )* ),* >)* {
@@ -364,15 +346,14 @@ macro_rules! decl_apis {
{ $( $result_return_ty:ty; )* };
) => {
$( #[$attr] )*
pub trait $name < $( $generic_param_parsed $( : $generic_bound_parsed )* ),* > {
/// The Error type returned by this API.
type Error;
#[cfg(feature = "std")]
pub trait $name < $( $generic_param_parsed $( : $generic_bound_parsed )* ),* > : $crate::runtime_api::Core<Block> {
$( type $client_generic_param $( : $client_generic_bound )*; )*
$(
$( #[$fn_attr] )*
fn $fn_name $( < $( $fn_generic: $crate::Encode + $crate::Decode ),* > )* (
&self, at: &$crate::BlockId<Block> $(, $param_name: $param_type )*
fn $fn_name $( < $( $fn_generic: $crate::runtime_api::Encode + $crate::runtime_api::Decode ),* > )* (
&self, at: &$crate::runtime_api::BlockId<Block> $(, $param_name: $param_type )*
) -> $result_return_ty;
)*
}
@@ -390,7 +371,7 @@ macro_rules! decl_apis {
};
)*
) => {
decl_apis! {
decl_runtime_apis! {
@GENERATE_RUNTIME_TRAITS_WITH_JOINED_GENERICS
$(
$( #[$attr] )*
@@ -431,63 +412,8 @@ macro_rules! decl_apis {
};
}
/// The ApiIds for the various standard runtime APIs.
pub mod id {
use super::ApiId;
/// ApiId for the BlockBuilder trait.
pub const BLOCK_BUILDER: ApiId = *b"blkbuild";
/// ApiId for the TaggedTransactionQueue trait.
pub const TAGGED_TRANSACTION_QUEUE: ApiId = *b"validatx";
/// ApiId for the Metadata trait.
pub const METADATA: ApiId = *b"metadata";
}
decl_apis! {
/// The `Core` api trait that is mandantory for each runtime.
pub trait Core<Block: BlockT, AuthorityId> {
fn version() -> RuntimeVersion;
fn authorities() -> Vec<AuthorityId>;
fn execute_block(block: Block);
}
/// The `Metadata` api trait that returns metadata for the runtime.
pub trait Metadata<Data> {
fn metadata() -> Data;
}
/// The `OldTxQueue` api trait for interfering with the old transaction queue.
pub trait OldTxQueue {
fn account_nonce<AccountId, Index>(account: AccountId) -> Index;
fn lookup_address<Address, LookupId>(address: Address) -> Option<LookupId>;
}
/// The `TaggedTransactionQueue` api trait for interfering with the new transaction queue.
pub trait TaggedTransactionQueue<Block: BlockT> {
fn validate_transaction<TransactionValidity>(tx: <Block as BlockT>::Extrinsic) -> TransactionValidity;
}
/// The `BlockBuilder` api trait that provides required functions for building a block for a runtime.
pub trait BlockBuilder<Block: BlockT> ExtraClientSide <OverlayedChanges> {
/// Initialise a block with the given header.
fn initialise_block(header: <Block as BlockT>::Header) ExtraClientSide(changes: &mut Self::OverlayedChanges);
/// Apply the given extrinsics.
fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) ExtraClientSide(changes: &mut Self::OverlayedChanges) -> ApplyResult;
/// Finish the current block.
fn finalise_block() ExtraClientSide(changes: &mut Self::OverlayedChanges) -> <Block as BlockT>::Header;
/// Generate inherent extrinsics.
fn inherent_extrinsics<InherentExtrinsic, UncheckedExtrinsic>(inherent: InherentExtrinsic) -> Vec<UncheckedExtrinsic>;
/// Check that the inherents are valid.
fn check_inherents<InherentData, Error>(block: Block, data: InherentData) -> Result<(), Error>;
/// Generate a random seed.
fn random_seed() -> <Block as BlockT>::Hash;
}
}
/// Implement the given API's for the given runtime.
/// All desired API's need to be implemented in one `impl_apis!` call.
/// All desired API's need to be implemented in one `impl_runtime_apis!` call.
/// Besides generating the implementation for the runtime, there will be also generated an
/// auxiliary module named `api` that contains function for inferring with the API in native/wasm.
/// It is important to use the traits from the `runtime` module with this macro.
@@ -496,18 +422,17 @@ decl_apis! {
///
/// ```nocompile
/// #[macro_use]
/// extern crate sr_api as runtime_api;
/// extern crate substrate_client as client;
///
/// use runtime_api::runtime::{Core, TaggedTransactionQueue};
/// use client::runtime_api::runtime::{Core, TaggedTransactionQueue};
///
/// impl_apis! {
/// impl Core<Block, AccountId> for Runtime {
/// fn version() -> RuntimeVersion { 1 }
/// fn authorities() -> Vec<AuthorityId> { vec![1] }
/// impl_runtime_apis! {
/// impl Core<Block> for Runtime {
/// fn version() -> RuntimeVersion { unimplemented!() }
/// fn authorities() -> Vec<AuthorityId> { unimplemented!() }
/// fn execute_block(block: Block) {
/// //comment
/// let block = call_arbitrary_code(block);
/// execute(block);
/// unimplemented!()
/// }
/// }
///
@@ -521,7 +446,7 @@ decl_apis! {
/// fn main() {}
/// ```
#[macro_export]
macro_rules! impl_apis {
macro_rules! impl_runtime_apis {
(
impl $trait_name:ident $( < $( $generic:ident ),* > )* for $runtime:ident {
$(
@@ -539,7 +464,7 @@ macro_rules! impl_apis {
}
)*
}
impl_apis! {
impl_runtime_apis! {
$runtime;
$( $fn_name ( $( $arg_name: $arg_ty ),* ); )*;
$( $rest )*
@@ -564,7 +489,7 @@ macro_rules! impl_apis {
}
)*
}
impl_apis! {
impl_runtime_apis! {
$runtime;
$( $fn_name_parsed ( $( $arg_name_parsed: $arg_ty_parsed ),* ); )*
$( $fn_name ( $( $arg_name: $arg_ty ),* ); )*;
@@ -583,7 +508,7 @@ macro_rules! impl_apis {
match method {
$(
stringify!($fn_name) => {
Some({impl_apis! {
Some({impl_runtime_apis! {
@GENERATE_IMPL_CALL
$runtime;
$fn_name;
@@ -604,11 +529,11 @@ macro_rules! impl_apis {
&[0u8; 0]
} else {
unsafe {
$crate::slice::from_raw_parts(input_data, input_len)
$crate::runtime_api::slice::from_raw_parts(input_data, input_len)
}
};
let output = { impl_apis! {
let output = { impl_runtime_apis! {
@GENERATE_IMPL_CALL
$runtime;
$fn_name;
@@ -632,13 +557,13 @@ macro_rules! impl_apis {
$arg_name:ident : $arg_ty:ty;
$input:ident;
) => {
let $arg_name : $arg_ty = match $crate::codec::Decode::decode(&mut $input) {
let $arg_name : $arg_ty = match $crate::runtime_api::Decode::decode(&mut $input) {
Some(input) => input,
None => panic!("Bad input data provided to {}", stringify!($fn_name)),
};
let output = $runtime::$fn_name($arg_name);
$crate::codec::Encode::encode(&output)
$crate::runtime_api::Encode::encode(&output)
};
(@GENERATE_IMPL_CALL
$runtime:ident;
@@ -646,12 +571,12 @@ macro_rules! impl_apis {
$( $arg_name:ident : $arg_ty:ty ),*;
$input:ident;
) => {
let ( $( $arg_name ),* ) : ($( $arg_ty ),*) = match $crate::codec::Decode::decode(&mut $input) {
let ( $( $arg_name ),* ) : ($( $arg_ty ),*) = match $crate::runtime_api::Decode::decode(&mut $input) {
Some(input) => input,
None => panic!("Bad input data provided to {}", stringify!($fn_name)),
};
let output = $runtime::$fn_name($( $arg_name ),*);
$crate::codec::Encode::encode(&output)
$crate::runtime_api::Encode::encode(&output)
};
}
@@ -0,0 +1,122 @@
// Copyright 2018 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! All the functionality required for declaring and implementing runtime api's.
//! Core api's are also declared here.
#[doc(hidden)]
#[cfg(feature = "std")]
pub use state_machine::OverlayedChanges;
#[doc(hidden)]
pub use runtime_primitives::{traits::Block as BlockT, generic::BlockId};
#[cfg(feature = "std")]
use runtime_primitives::traits::ApiRef;
pub use runtime_version::ApiId;
#[doc(hidden)]
pub use rstd::slice;
#[cfg(feature = "std")]
use rstd::result;
pub use codec::{Encode, Decode};
#[cfg(feature = "std")]
use error;
mod core;
#[macro_use]
mod macros;
mod traits;
/// Something that can be constructed to a runtime api.
#[cfg(feature = "std")]
pub trait ConstructRuntimeApi<Block: BlockT>: Sized {
/// Construct an instance of the runtime api.
fn construct_runtime_api<'a, T: CallApiAt<Block>>(call: &'a T) -> ApiRef<'a, Self>;
}
/// An extension for the `RuntimeApi`.
#[cfg(feature = "std")]
pub trait ApiExt {
/// The given closure will be called with api instance. Inside the closure any api call is
/// allowed. After doing the api call, the closure is allowed to map the `Result` to a
/// different `Result` type. This can be important, as the internal data structure that keeps
/// track of modifications to the storage, discards changes when the `Result` is an `Err`.
/// On `Ok`, the structure commits the changes to an internal buffer.
fn map_api_result<F: FnOnce(&Self) -> result::Result<R, E>, R, E>(
&self,
map_call: F
) -> result::Result<R, E>;
}
/// Something that can call the runtime api at a given block.
#[cfg(feature = "std")]
pub trait CallApiAt<Block: BlockT> {
/// Calls the given api function with the given encoded arguments at the given block
/// and returns the encoded result.
fn call_api_at(
&self,
at: &BlockId<Block>,
function: &'static str,
args: Vec<u8>,
changes: &mut OverlayedChanges,
initialised_block: &mut Option<BlockId<Block>>,
) -> error::Result<Vec<u8>>;
/// Call the given api function with strong arguments at the given block
/// and returns the decoded result.
fn call_api_at_strong<In: Encode, Out: Decode>(
&self,
at: &BlockId<Block>,
function: &'static str,
args: &In,
changes: &mut OverlayedChanges,
initialised_block: &mut Option<BlockId<Block>>,
) -> error::Result<Out> where Self: Sized {
let raw = self.call_api_at(
at,
function,
args.encode(),
changes,
initialised_block,
)?;
match Out::decode(&mut &raw[..]) {
Some(out) => Ok(out),
None => bail!(error::ErrorKind::CallResultDecode(function)),
}
}
}
/// The ApiIds for the various standard runtime APIs.
pub mod id {
use super::ApiId;
/// ApiId for the BlockBuilder trait.
pub const BLOCK_BUILDER: ApiId = *b"blkbuild";
/// ApiId for the TaggedTransactionQueue trait.
pub const TAGGED_TRANSACTION_QUEUE: ApiId = *b"validatx";
/// ApiId for the Metadata trait.
pub const METADATA: ApiId = *b"metadata";
}
pub use self::core::*;
pub use self::traits::*;
/// The runtime apis that should be implemented for the `Runtime`.
pub mod runtime {
pub use super::core::runtime::Core;
pub use super::traits::runtime::*;
}
@@ -0,0 +1,35 @@
// Copyright 2018 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use primitives::OpaqueMetadata;
use runtime_primitives::{
traits::{Block as BlockT},
transaction_validity::TransactionValidity
};
decl_runtime_apis! {
/// The `Metadata` api trait that returns metadata for the runtime.
pub trait Metadata {
/// Returns the metadata of a runtime.
fn metadata() -> OpaqueMetadata;
}
/// The `TaggedTransactionQueue` api trait for interfering with the new transaction queue.
pub trait TaggedTransactionQueue<Block: BlockT> {
/// Validate the given transaction.
fn validate_transaction(tx: <Block as BlockT>::Extrinsic) -> TransactionValidity;
}
}
+7 -22
View File
@@ -130,22 +130,7 @@ pub trait CompatibleDigestItem: Sized {
fn as_aura_seal(&self) -> Option<(u64, &ed25519::Signature)>;
}
impl CompatibleDigestItem for generic::DigestItem<primitives::H256, u64> {
/// Construct a digest item which is a slot number and a signature on the
/// hash.
fn aura_seal(slot_number: u64, signature: ed25519::Signature) -> Self {
generic::DigestItem::Seal(slot_number, signature)
}
/// If this item is an Aura seal, return the slot number and signature.
fn as_aura_seal(&self) -> Option<(u64, &ed25519::Signature)> {
match self {
generic::DigestItem::Seal(slot, ref sign) => Some((*slot, sign)),
_ => None
}
}
}
impl CompatibleDigestItem for generic::DigestItem<primitives::H256, primitives::AuthorityId> {
impl<Hash, AuthorityId> CompatibleDigestItem for generic::DigestItem<Hash, AuthorityId> {
/// Construct a digest item which is a slot number and a signature on the
/// hash.
fn aura_seal(slot_number: u64, signature: ed25519::Signature) -> Self {
@@ -255,8 +240,8 @@ pub fn start_aura<B, C, E, SO, Error>(
let import_block = ImportBlock {
origin: BlockOrigin::Own,
header,
external_justification: Vec::new(),
post_runtime_digests: vec![item],
justification: Vec::new(),
post_digests: vec![item],
body: Some(body),
finalized: false,
auxiliary: Vec::new(),
@@ -380,8 +365,8 @@ impl<B: Block, C> Verifier<B> for AuraVerifier<C> where
let import_block = ImportBlock {
origin,
header: pre_header,
external_justification: Vec::new(),
post_runtime_digests: vec![item],
justification: Vec::new(),
post_digests: vec![item],
body,
finalized: false,
auxiliary: Vec::new(),
@@ -420,7 +405,7 @@ mod tests {
use network::test::*;
use network::test::{Block as TestBlock, PeersClient};
use runtime_primitives::traits::Block as BlockT;
use network::ProtocolConfig;
use network::config::ProtocolConfig;
use parking_lot::Mutex;
use tokio::runtime::current_thread;
use keyring::Keyring;
@@ -429,7 +414,7 @@ mod tests {
type Error = client::error::Error;
type TestClient = client::Client<test_client::Backend, test_client::Executor, TestBlock>;
type TestClient = client::Client<test_client::Backend, test_client::Executor, TestBlock, test_client::runtime::ClientWithApi>;
struct DummyFactory(Arc<TestClient>);
struct DummyProposer(u64, Arc<TestClient>);
@@ -1,12 +0,0 @@
= Consensus Common
.Summary
[source, toml]
----
include::Cargo.toml[lines=2..5]
----
.Description
----
include::src/lib.rs[tag=description]
----
@@ -44,7 +44,7 @@ pub struct ImportBlock<Block: BlockT> {
///
/// Consensus engines which alter the header (by adding post-runtime digests)
/// should strip those off in the initial verification process and pass them
/// via the `post_runtime_digests` field. During block authorship, they should
/// via the `post_digests` field. During block authorship, they should
/// not be pushed to the header directly.
///
/// The reason for this distinction is so the header can be directly
@@ -52,10 +52,10 @@ pub struct ImportBlock<Block: BlockT> {
/// post-runtime digests are pushed back on after.
pub header: Block::Header,
/// Justification provided for this block from the outside:.
pub external_justification: Justification,
pub justification: Justification,
/// Digest items that have been added after the runtime for external
/// work, like a consensus signature.
pub post_runtime_digests: Vec<DigestItemFor<Block>>,
pub post_digests: Vec<DigestItemFor<Block>>,
/// Block's body
pub body: Option<Vec<Block::Extrinsic>>,
/// Is this block finalized already?
@@ -82,8 +82,8 @@ impl<Block: BlockT> ImportBlock<Block> {
(
self.origin,
self.header,
self.external_justification,
self.post_runtime_digests,
self.justification,
self.post_digests,
self.body,
self.finalized,
self.auxiliary,
+1 -3
View File
@@ -14,9 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Substrate Consensus Common. If not, see <http://www.gnu.org/licenses/>.
// tag::description[]
//! Consensus basics and common features
// end::description[]
//! Tracks offline validators.
// This provides "unused" building blocks to other crates
#![allow(dead_code)]
-12
View File
@@ -1,12 +0,0 @@
= Consensus Rhododendron (RHD)
.Summary
[source, toml]
----
include::Cargo.toml[lines=2..5]
----
.Description
----
include::src/lib.rs[tag=description]
----
+2 -4
View File
@@ -14,7 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
// tag::description[]
//! BFT Agreement based on a rotating proposer in different rounds.
//!
//! Where this crate refers to input stream, should never logically conclude.
@@ -30,7 +29,6 @@
//! conclude without having witnessed the conclusion.
//! In general, this future should be pre-empted by the import of a justification
//! set for this block height.
// end::description[]
#![cfg(feature="rhd")]
// FIXME: doesn't compile - https://github.com/paritytech/substrate/issues/1020
@@ -419,10 +417,10 @@ impl<B, P, I, InStream, OutSink> Future for BftFuture<B, P, I, InStream, OutSink
let import_block = ImportBlock {
origin: BlockOrigin::ConsensusBroadcast,
header: header,
external_justification: just.into(),
justification: just.into(),
body: Some(body),
finalized: true,
post_runtime_digests: Default::default(),
post_digests: Default::default(),
auxiliary: Default::default()
};
-13
View File
@@ -1,13 +0,0 @@
= Executor
.Summary
[source, toml]
----
include::Cargo.toml[lines=2..5]
----
.Description
----
include::src/lib.rs[tag=description]
----
-2
View File
@@ -14,7 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
// tag::description[]
//! Temporary crate for contracts implementations.
//!
//! This will be replaced with WASM contracts stored on-chain.
@@ -25,7 +24,6 @@
//! - init_block(PrevBlock?) -> InProgressBlock
//! - add_transaction(InProgressBlock) -> InProgressBlock
//! It is left as is for now as it might be removed before this is ever done.
// end::description[]
#![warn(missing_docs)]
#![recursion_limit="128"]
+11 -4
View File
@@ -29,7 +29,7 @@ use wasm_utils::UserError;
use primitives::{blake2_256, twox_128, twox_256, ed25519};
use primitives::hexdisplay::HexDisplay;
use primitives::sandbox as sandbox_primitives;
use primitives::Blake2Hasher;
use primitives::{H256, Blake2Hasher};
use trie::ordered_trie_root;
use sandbox;
@@ -412,8 +412,15 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
Ok(0)
}
},
ext_storage_changes_root(block: u64, result: *mut u8) -> u32 => {
let r = this.ext.storage_changes_root(block);
ext_storage_changes_root(parent_hash_data: *const u8, parent_hash_len: u32, parent_number: u64, result: *mut u8) -> u32 => {
let mut parent_hash = H256::default();
if parent_hash_len != parent_hash.as_ref().len() as u32 {
return Err(UserError("Invalid parent_hash_len in ext_storage_changes_root").into());
}
let raw_parent_hash = this.memory.get(parent_hash_data, parent_hash_len as usize)
.map_err(|_| UserError("Invalid attempt to get parent_hash in ext_storage_changes_root"))?;
parent_hash.as_mut().copy_from_slice(&raw_parent_hash[..]);
let r = this.ext.storage_changes_root(parent_hash, parent_number);
if let Some(ref r) = r {
this.memory.set(result, &r[..]).map_err(|_| UserError("Invalid attempt to set memory in ext_storage_changes_root"))?;
}
@@ -845,7 +852,7 @@ mod tests {
let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm");
assert_eq!(
WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_enumerated_trie_root", &[]).unwrap(),
ordered_trie_root::<Blake2Hasher, _, _>(vec![b"zero".to_vec(), b"one".to_vec(), b"two".to_vec()].iter()).0.encode()
ordered_trie_root::<Blake2Hasher, _, _>(vec![b"zero".to_vec(), b"one".to_vec(), b"two".to_vec()].iter()).as_fixed_bytes().encode()
);
}
+24 -9
View File
@@ -17,10 +17,18 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "fixed-hash"
version = "0.2.2"
name = "crunchy"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "fixed-hash"
version = "0.3.0-beta.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "hash-db"
version = "0.9.0"
@@ -141,13 +149,18 @@ dependencies = [
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "static_assertions"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "substrate-primitives"
version = "0.1.0"
dependencies = [
"byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"crunchy 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"fixed-hash 0.3.0-beta.3 (registry+https://github.com/rust-lang/crates.io-index)",
"hash-db 0.9.0 (git+https://github.com/paritytech/trie)",
"hash256-std-hasher 0.9.0 (git+https://github.com/paritytech/trie)",
"parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -155,7 +168,7 @@ dependencies = [
"rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-std 0.1.0",
"uint 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"uint 0.5.0-beta.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -170,11 +183,11 @@ dependencies = [
[[package]]
name = "uint"
version = "0.4.1"
version = "0.5.0-beta.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"crunchy 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -187,7 +200,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef"
"checksum byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "90492c5858dd7d2e78691cfb89f90d273a2800fc11d98f60786e5d87e2f83781"
"checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda"
"checksum fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d5ec8112f00ea8a483e04748a85522184418fd1cf02890b626d8fc28683f7de"
"checksum crunchy 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c240f247c278fa08a6d4820a6a222bfc6e0d999e51ba67be94f44c905b2161f2"
"checksum fixed-hash 0.3.0-beta.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4e71c99c903a9fe54baed1bc701b43daba8c6dc6d4aec89a32f667ab6b3094c4"
"checksum hash-db 0.9.0 (git+https://github.com/paritytech/trie)" = "<none>"
"checksum hash256-std-hasher 0.9.0 (git+https://github.com/paritytech/trie)" = "<none>"
"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2"
@@ -200,6 +214,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
"checksum serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)" = "84257ccd054dc351472528c8587b4de2dbf0dc0fe2e634030c1a90bfdacebaa9"
"checksum static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c19be23126415861cb3a23e501d34a708f7f9b2183c5252d690941c2e69199d5"
"checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741"
"checksum uint 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "754ba11732b9161b94c41798e5197e5e75388d012f760c42adb5000353e98646"
"checksum uint 0.5.0-beta.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4630460173a57c0af94b8306091e018025d988473f641a4af754b6cde980e1e3"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
@@ -1,12 +0,0 @@
= Finality GRANDPA (aka SHAFT)
.Summary
[source, toml]
----
include::Cargo.toml[lines=2..5]
----
.Description
----
include::src/lib.rs[tag=description]
----
@@ -4,18 +4,18 @@ version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
substrate-client = { path = "../../client", default-features = false }
substrate-primitives = { path = "../../primitives", default-features = false }
parity-codec = { version = "2.1", default-features = false }
parity-codec-derive = { version = "2.1", default-features = false }
sr-api = { path = "../../sr-api", default-features = false }
sr-primitives = { path = "../../sr-primitives", default-features = false }
[features]
default = ["std"]
std = [
"substrate-primitives/std",
"substrate-client/std",
"parity-codec/std",
"parity-codec-derive/std",
"sr-api/std",
"sr-primitives/std",
]
@@ -26,7 +26,7 @@ extern crate parity_codec;
extern crate parity_codec_derive;
#[macro_use]
extern crate sr_api;
extern crate substrate_client as client;
use substrate_primitives::AuthorityId;
use sr_primitives::traits::{Block as BlockT, DigestFor, NumberFor};
@@ -48,34 +48,33 @@ pub const AUTHORITIES_CALL: &str = "grandpa_authorities";
/// The ApiIds for GRANDPA API.
pub mod id {
use sr_api::ApiId;
use client::runtime_api::ApiId;
/// ApiId for the GrandpaApi trait.
pub const GRANDPA_API: ApiId = *b"fgrandpa";
}
decl_apis! {
/// APIs for integrating the GRANDPA finality gadget into runtimes.
/// APIs for integrating the GRANDPA finality gadget into runtimes.
/// This should be implemented on the runtime side.
///
/// This is primarily used for negotiating authority-set changes for the
/// gadget. GRANDPA uses a signalling model of changing authority sets:
/// changes should be signalled with a delay of N blocks, and then automatically
/// applied in the runtime after those N blocks have passed.
///
/// The consensus protocol will coordinate the handoff externally.
pub trait GrandpaApi<B: BlockT> {
/// Check a digest for pending changes.
/// Return `None` if there are no pending changes.
///
/// This is primarily used for negotiating authority-set changes for the
/// gadget. GRANDPA uses a signalling model of changing authority sets:
/// changes should be signalled with a delay of N blocks, and then automatically
/// applied in the runtime after those N blocks have passed.
/// Precedence towards earlier or later digest items can be given
/// based on the rules of the chain.
///
/// The consensus protocol will coordinate the handoff externally.
pub trait GrandpaApi<B: BlockT> {
/// Check a digest for pending changes.
/// Return `None` if there are no pending changes.
///
/// Precedence towards earlier or later digest items can be given
/// based on the rules of the chain.
///
/// No change should be scheduled if one is already and the delay has not
/// passed completely.
fn grandpa_pending_change(digest: DigestFor<B>) -> Option<ScheduledChange<NumberFor<B>>>;
/// No change should be scheduled if one is already and the delay has not
/// passed completely.
fn grandpa_pending_change(digest: DigestFor<B>) -> Option<ScheduledChange<NumberFor<B>>>;
/// Get the current GRANDPA authorities and weights. This should not change except
/// for when changes are scheduled and the corresponding delay has passed.
fn grandpa_authorities() -> Vec<(AuthorityId, u64)>;
}
/// Get the current GRANDPA authorities and weights. This should not change except
/// for when changes are scheduled and the corresponding delay has passed.
fn grandpa_authorities() -> Vec<(AuthorityId, u64)>;
}
+35 -23
View File
@@ -83,6 +83,8 @@ use futures::prelude::*;
use futures::stream::Fuse;
use futures::sync::mpsc;
use client::{Client, error::Error as ClientError, ImportNotifications, backend::Backend, CallExecutor};
use client::blockchain::HeaderBackend;
use client::runtime_api::{CallApiAt, TaggedTransactionQueue};
use codec::{Encode, Decode};
use consensus_common::{BlockImport, ImportBlock, ImportResult};
use runtime_primitives::traits::{
@@ -193,9 +195,10 @@ pub trait BlockStatus<Block: BlockT> {
fn block_number(&self, hash: Block::Hash) -> Result<Option<NumberFor<Block>>, Error>;
}
impl<B, E, Block: BlockT<Hash=H256>> BlockStatus<Block> for Arc<Client<B, E, Block>> where
impl<B, E, Block: BlockT<Hash=H256>, RA> BlockStatus<Block> for Arc<Client<B, E, Block, RA>> where
B: Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher> + Send + Sync,
RA: Send + Sync,
NumberFor<Block>: BlockNumberOps,
{
fn block_number(&self, hash: Block::Hash) -> Result<Option<NumberFor<Block>>, Error> {
@@ -468,8 +471,8 @@ fn outgoing_messages<Block: BlockT, N: Network>(
}
/// The environment we run GRANDPA in.
struct Environment<B, E, Block: BlockT, N: Network> {
inner: Arc<Client<B, E, Block>>,
struct Environment<B, E, Block: BlockT, N: Network, RA> {
inner: Arc<Client<B, E, Block, RA>>,
voters: Arc<HashMap<AuthorityId, u64>>,
config: Config,
authority_set: SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
@@ -477,7 +480,7 @@ struct Environment<B, E, Block: BlockT, N: Network> {
set_id: u64,
}
impl<Block: BlockT<Hash=H256>, B, E, N> grandpa::Chain<Block::Hash, NumberFor<Block>> for Environment<B, E, Block, N> where
impl<Block: BlockT<Hash=H256>, B, E, N, RA> grandpa::Chain<Block::Hash, NumberFor<Block>> for Environment<B, E, Block, N, RA> where
Block: 'static,
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static,
@@ -573,12 +576,13 @@ impl<H, N> From<grandpa::Error> for ExitOrError<H, N> {
}
}
impl<B, E, Block: BlockT<Hash=H256>, N> voter::Environment<Block::Hash, NumberFor<Block>> for Environment<B, E, Block, N> where
impl<B, E, Block: BlockT<Hash=H256>, N, RA> voter::Environment<Block::Hash, NumberFor<Block>> for Environment<B, E, Block, N, RA> where
Block: 'static,
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static + Send + Sync,
N: Network + 'static,
N::In: 'static,
RA: 'static + Send + Sync,
NumberFor<Block>: BlockNumberOps,
{
type Timer = Box<Future<Item = (), Error = Self::Error>>;
@@ -771,28 +775,33 @@ pub trait ApiClient<Block: BlockT<Hash=H256>> {
-> Result<Option<ScheduledChange<NumberFor<Block>>>, ClientError>;
}
impl<B, E, Block: BlockT<Hash=H256>> ApiClient<Block> for Arc<Client<B, E, Block>> where
impl<B, E, Block: BlockT<Hash=H256>, RA> ApiClient<Block> for Arc<Client<B, E, Block, RA>> where
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone,
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
DigestFor<Block>: Encode,
RA: Send + Sync,
{
fn genesis_authorities(&self) -> Result<Vec<(AuthorityId, u64)>, ClientError> {
use runtime_primitives::traits::Zero;
self.call_api_at(
self.call_api_at_strong(
&BlockId::Number(NumberFor::<Block>::zero()),
fg_primitives::AUTHORITIES_CALL,
&()
&(),
&mut Default::default(),
&mut None,
)
}
fn scheduled_change(&self, header: &Block::Header)
-> Result<Option<ScheduledChange<NumberFor<Block>>>, ClientError>
{
self.call_api_at(
self.call_api_at_strong(
&BlockId::hash(header.parent_hash().clone()),
::fg_primitives::PENDING_CHANGE_CALL,
header.digest(),
&mut Default::default(),
&mut None,
)
}
}
@@ -802,17 +811,18 @@ impl<B, E, Block: BlockT<Hash=H256>> ApiClient<Block> for Arc<Client<B, E, Block
/// This scans each imported block for signals of changing authority set.
/// When using GRANDPA, the block import worker should be using this block import
/// object.
pub struct GrandpaBlockImport<B, E, Block: BlockT<Hash=H256>, Api> {
inner: Arc<Client<B, E, Block>>,
pub struct GrandpaBlockImport<B, E, Block: BlockT<Hash=H256>, Api, RA> {
inner: Arc<Client<B, E, Block, RA>>,
authority_set: SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
api_client: Api,
}
impl<B, E, Block: BlockT<Hash=H256>, Api> BlockImport<Block> for GrandpaBlockImport<B, E, Block, Api> where
impl<B, E, Block: BlockT<Hash=H256>, Api, RA> BlockImport<Block> for GrandpaBlockImport<B, E, Block, Api, RA> where
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone,
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
DigestFor<Block>: Encode,
Api: ApiClient<Block>,
RA: TaggedTransactionQueue<Block> + Send + Sync, // necessary for client to import `BlockImport`.
{
type Error = ClientError;
@@ -857,19 +867,20 @@ impl<B, E, Block: BlockT<Hash=H256>, Api> BlockImport<Block> for GrandpaBlockImp
/// Half of a link between a block-import worker and a the background voter.
// This should remain non-clone.
pub struct LinkHalf<B, E, Block: BlockT<Hash=H256>> {
client: Arc<Client<B, E, Block>>,
pub struct LinkHalf<B, E, Block: BlockT<Hash=H256>, RA> {
client: Arc<Client<B, E, Block, RA>>,
authority_set: SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
}
/// Make block importer and link half necessary to tie the background voter
/// to it.
pub fn block_import<B, E, Block: BlockT<Hash=H256>, Api>(client: Arc<Client<B, E, Block>>, api_client: Api)
-> Result<(GrandpaBlockImport<B, E, Block, Api>, LinkHalf<B, E, Block>), ClientError>
pub fn block_import<B, E, Block: BlockT<Hash=H256>, Api, RA>(client: Arc<Client<B, E, Block, RA>>, api_client: Api)
-> Result<(GrandpaBlockImport<B, E, Block, Api, RA>, LinkHalf<B, E, Block, RA>), ClientError>
where
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static,
Api: ApiClient<Block>,
RA: Send + Sync,
{
let authority_set = match client.backend().get_aux(AUTHORITY_SET_KEY)? {
None => {
@@ -902,18 +913,19 @@ pub fn block_import<B, E, Block: BlockT<Hash=H256>, Api>(client: Arc<Client<B, E
/// Run a GRANDPA voter as a task. Provide configuration and a link to a
/// block import worker that has already been instantiated with `block_import`.
pub fn run_grandpa<B, E, Block: BlockT<Hash=H256>, N>(
pub fn run_grandpa<B, E, Block: BlockT<Hash=H256>, N, RA>(
config: Config,
link: LinkHalf<B, E, Block>,
link: LinkHalf<B, E, Block, RA>,
network: N,
) -> ::client::error::Result<impl Future<Item=(),Error=()>> where
Block::Hash: Ord,
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + 'static,
N: Network + 'static,
N::In: 'static,
NumberFor<Block>: BlockNumberOps,
DigestFor<Block>: Encode,
RA: Send + Sync + 'static,
{
use futures::future::{self, Loop as FutureLoop};
use runtime_primitives::traits::Zero;
+3 -3
View File
@@ -19,7 +19,7 @@
use super::*;
use network::test::{Block, Hash, TestNetFactory, Peer, PeersClient};
use network::import_queue::{PassThroughVerifier};
use network::ProtocolConfig;
use network::config::{ProtocolConfig, Roles};
use parking_lot::Mutex;
use tokio::runtime::current_thread;
use keyring::Keyring;
@@ -31,7 +31,7 @@ use std::collections::HashSet;
use authorities::AuthoritySet;
type PeerData = Mutex<Option<LinkHalf<test_client::Backend, test_client::Executor, Block>>>;
type PeerData = Mutex<Option<LinkHalf<test_client::Backend, test_client::Executor, Block, test_client::runtime::ClientWithApi>>>;
type GrandpaPeer = Peer<PassThroughVerifier, PeerData>;
struct GrandpaTestNet {
@@ -73,7 +73,7 @@ impl TestNetFactory for GrandpaTestNet {
fn default_config() -> ProtocolConfig {
// the authority role ensures gossip hits all nodes here.
ProtocolConfig {
roles: ::network::Roles::AUTHORITY,
roles: Roles::AUTHORITY,
}
}
-13
View File
@@ -1,13 +0,0 @@
= Keyring
.Summary
[source, toml]
----
include::Cargo.toml[lines=2..5]
----
.Description
----
include::src/lib.rs[tag=description]
----
-2
View File
@@ -14,9 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
// tag::description[]
//! Support code for the runtime. A set of test accounts.
// end::description[]
#[macro_use] extern crate hex_literal;
#[macro_use] extern crate lazy_static;
-13
View File
@@ -1,13 +0,0 @@
= Keystore
.Summary
[source, toml]
----
include::Cargo.toml[lines=2..5]
----
.Description
----
include::src/lib.rs[tag=description]
----
+22 -19
View File
@@ -14,9 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
// tag::description[]
//! Keystore (and session key management) for ed25519 based chains like Polkadot.
// end::description[]
extern crate substrate_primitives;
extern crate parity_crypto as crypto;
@@ -131,6 +129,24 @@ pub struct Store {
additional: HashMap<Public, Seed>,
}
pub fn pad_seed(seed: &str) -> Seed {
let mut s: [u8; 32] = [' ' as u8; 32];
let was_hex = if seed.len() == 66 && &seed[0..2] == "0x" {
if let Ok(d) = hex::decode(&seed[2..]) {
s.copy_from_slice(&d);
true
} else { false }
} else { false };
if !was_hex {
let len = ::std::cmp::min(32, seed.len());
&mut s[..len].copy_from_slice(&seed.as_bytes()[..len]);
}
s
}
impl Store {
/// Create a new store at the given path.
pub fn open(path: PathBuf) -> Result<Self> {
@@ -153,24 +169,11 @@ impl Store {
/// Create a new key from seed. Do not place it into the store.
/// Only the first 32 bytes of the sead are used. This is meant to be used for testing only.
// TODO: Remove this
// FIXME: remove this - https://github.com/paritytech/substrate/issues/1063
pub fn generate_from_seed(&mut self, seed: &str) -> Result<Pair> {
let mut s: [u8; 32] = [' ' as u8; 32];
let was_hex = if seed.len() == 66 && &seed[0..2] == "0x" {
if let Ok(d) = hex::decode(&seed[2..]) {
s.copy_from_slice(&d);
true
} else { false }
} else { false };
if !was_hex {
let len = ::std::cmp::min(32, seed.len());
&mut s[..len].copy_from_slice(&seed.as_bytes()[..len]);
}
let pair = Pair::from_seed(&s);
self.additional.insert(pair.public(), s);
let padded_seed = pad_seed(seed);
let pair = Pair::from_seed(&padded_seed);
self.additional.insert(pair.public(), padded_seed);
Ok(pair)
}
+1 -1
View File
@@ -11,7 +11,7 @@ bytes = "0.4"
error-chain = { version = "0.12", default-features = false }
fnv = "1.0"
futures = "0.1"
libp2p = { git = "https://github.com/tomaka/libp2p-rs", rev = "8111062f0177fd7423626f2db9560273644a4c4d", default-features = false, features = ["libp2p-secio", "libp2p-secio-secp256k1"] }
libp2p = { git = "https://github.com/libp2p/rust-libp2p", rev = "d961e656a74d1bab5366d371a06f9e10d5f4a6c5", default-features = false, features = ["secio-rsa", "secio-secp256k1"] }
parking_lot = "0.5"
libc = "0.2"
log = "0.4"
-13
View File
@@ -1,13 +0,0 @@
= Network libp2p
.Summary
[source, toml]
----
include::Cargo.toml[lines=2..5]
----
.Description
----
include::src/lib.rs[tag=description]
----
@@ -15,7 +15,7 @@
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use bytes::Bytes;
use libp2p::core::{Multiaddr, ConnectionUpgrade, Endpoint};
use libp2p::core::{ConnectionUpgrade, Endpoint};
use libp2p::tokio_codec::Framed;
use std::{collections::VecDeque, io, vec::IntoIter as VecIntoIter};
use futures::{prelude::*, future, stream, task};
@@ -115,7 +115,6 @@ impl<TSubstream> RegisteredProtocolSubstream<TSubstream> {
self.send_queue.push_back(data);
// If the length of the queue goes over a certain arbitrary threshold, we print a warning.
// TODO: figure out a good threshold
if self.send_queue.len() >= 2048 {
warn!(target: "sub-libp2p", "Queue of packets to send over substream is pretty \
large: {}", self.send_queue.len());
@@ -203,8 +202,7 @@ where TSubstream: AsyncRead + AsyncWrite,
self,
socket: TSubstream,
protocol_version: Self::UpgradeIdentifier,
_: Endpoint,
_: &Multiaddr
_: Endpoint
) -> Self::Future {
let framed = Framed::new(socket, UviBytes::default());
@@ -273,13 +271,12 @@ where TSubstream: AsyncRead + AsyncWrite,
self,
socket: TSubstream,
upgrade_identifier: Self::UpgradeIdentifier,
endpoint: Endpoint,
remote_addr: &Multiaddr
endpoint: Endpoint
) -> Self::Future {
let (protocol_index, inner_proto_id) = upgrade_identifier;
self.0.into_iter()
.nth(protocol_index)
.expect("invalid protocol index ; programmer logic error")
.upgrade(socket, inner_proto_id, endpoint, remote_addr)
.upgrade(socket, inner_proto_id, endpoint)
}
}
+4 -6
View File
@@ -14,12 +14,9 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
// tag::description[]
//! TODO: Missing doc
// end::description[]
//! Substrate libp2p implementation of the ethcore network library
#![recursion_limit="128"]
#![type_length_limit = "268435456"]
#![recursion_limit = "128"]
extern crate parking_lot;
extern crate fnv;
@@ -58,7 +55,8 @@ pub use custom_proto::RegisteredProtocol;
pub use error::{Error, ErrorKind, DisconnectReason};
pub use libp2p::{Multiaddr, multiaddr::Protocol, PeerId};
pub use service_task::{start_service, Service, ServiceEvent};
pub use traits::*; // TODO: expand to actual items
pub use traits::{NetworkConfiguration, NodeIndex, NodeId, NonReservedPeerMode};
pub use traits::{ProtocolId, Secret, Severity};
/// Check if node url is valid
pub fn validate_node_url(url: &str) -> Result<(), Error> {
@@ -19,7 +19,6 @@ use custom_proto::{RegisteredProtocols, RegisteredProtocolSubstream};
use futures::{prelude::*, task};
use libp2p::core::{ConnectionUpgrade, Endpoint, PeerId, PublicKey, upgrade};
use libp2p::core::nodes::handled_node::{NodeHandler, NodeHandlerEndpoint, NodeHandlerEvent};
use libp2p::core::nodes::swarm::ConnectedPoint;
use libp2p::kad::{KadConnecConfig, KadFindNodeRespond, KadIncomingRequest, KadConnecController};
use libp2p::{identify, ping};
use parking_lot::Mutex;
@@ -54,20 +53,17 @@ pub struct SubstrateNodeHandler<TSubstream> {
/// Substreams open for "custom" protocols (eg. dot).
custom_protocols_substreams: Vec<RegisteredProtocolSubstream<TSubstream>>,
/// Address of the node.
address: Multiaddr,
/// Substream open for Kademlia, if any.
kademlia_substream: Option<(KadConnecController, Box<Stream<Item = KadIncomingRequest, Error = IoError> + Send>)>,
/// If true, we need to send back a `KadOpen` event on the stream (if Kademlia is open).
need_report_kad_open: bool,
/// Substream open for sending pings, if any.
ping_out_substream: Option<ping::PingDialer<TSubstream, Instant>>,
ping_out_substream: Option<ping::protocol::PingDialer<TSubstream, Instant>>,
/// Active pinging attempt with the moment it expires.
active_ping_out: Option<Delay>,
/// Substreams open for receiving pings.
ping_in_substreams: Vec<ping::PingListener<TSubstream>>,
ping_in_substreams: Vec<ping::protocol::PingListener<TSubstream>>,
/// Future that fires when we need to ping the node again.
///
/// Every time we receive a pong, we reset the timer to the next time.
@@ -249,7 +245,7 @@ macro_rules! listener_upgrade {
upgrade::or(upgrade::or(upgrade::or(
upgrade::map((*$self.registered_custom).clone(), move |c| FinalUpgrade::Custom(c)),
upgrade::map(KadConnecConfig::new(), move |(c, s)| FinalUpgrade::Kad(c, s))),
upgrade::map(ping::Ping::default(), move |p| FinalUpgrade::from(p))),
upgrade::map(ping::protocol::Ping::default(), move |p| FinalUpgrade::from(p))),
upgrade::map(identify::IdentifyProtocolConfig, move |i| FinalUpgrade::from(i)))
// TODO: meh for cloning a Vec here
)
@@ -260,20 +256,14 @@ where TSubstream: AsyncRead + AsyncWrite + Send + 'static,
{
/// Creates a new node handler.
#[inline]
pub fn new(registered_custom: Arc<RegisteredProtocols>, endpoint: ConnectedPoint) -> Self {
pub fn new(registered_custom: Arc<RegisteredProtocols>) -> Self {
let registered_custom_len = registered_custom.len();
let queued_dial_upgrades = registered_custom.0
.iter()
.map(|proto| UpgradePurpose::Custom(proto.id()))
.collect();
let address = match endpoint {
ConnectedPoint::Dialer { address } => address.clone(),
ConnectedPoint::Listener { send_back_addr, .. } => send_back_addr.clone(),
};
SubstrateNodeHandler {
address,
custom_protocols_substreams: Vec::with_capacity(registered_custom_len),
kademlia_substream: None,
need_report_kad_open: false,
@@ -294,18 +284,19 @@ where TSubstream: AsyncRead + AsyncWrite + Send + 'static,
}
}
impl<TSubstream> NodeHandler<TSubstream> for SubstrateNodeHandler<TSubstream>
impl<TSubstream> NodeHandler for SubstrateNodeHandler<TSubstream>
where TSubstream: AsyncRead + AsyncWrite + Send + 'static,
{
type InEvent = SubstrateInEvent;
type OutEvent = SubstrateOutEvent<TSubstream>;
type OutboundOpenInfo = ();
type Substream = TSubstream;
fn inject_substream(&mut self, substream: TSubstream, endpoint: NodeHandlerEndpoint<Self::OutboundOpenInfo>) {
// For listeners, propose all the possible upgrades.
if endpoint == NodeHandlerEndpoint::Listener {
let listener_upgrade = listener_upgrade!(self);
let upgrade = upgrade::apply(substream, listener_upgrade, Endpoint::Listener, &self.address);
let upgrade = upgrade::apply(substream, listener_upgrade, Endpoint::Listener);
self.upgrades_in_progress_listen.push(Box::new(upgrade) as Box<_>);
// Since we pushed to `upgrades_in_progress_listen`, we have to notify the task.
if let Some(task) = self.to_notify.take() {
@@ -338,26 +329,22 @@ where TSubstream: AsyncRead + AsyncWrite + Send + 'static,
return;
};
// TODO: shouldn't be &self.address ; requires a change in libp2p
let upgrade = upgrade::apply(substream, wanted, Endpoint::Dialer, &self.address);
let upgrade = upgrade::apply(substream, wanted, Endpoint::Dialer);
self.upgrades_in_progress_dial.push((purpose, Box::new(upgrade) as Box<_>));
}
UpgradePurpose::Kad => {
let wanted = upgrade::map(KadConnecConfig::new(), move |(c, s)| FinalUpgrade::Kad(c, s));
// TODO: shouldn't be &self.address ; requires a change in libp2p
let upgrade = upgrade::apply(substream, wanted, Endpoint::Dialer, &self.address);
let upgrade = upgrade::apply(substream, wanted, Endpoint::Dialer);
self.upgrades_in_progress_dial.push((purpose, Box::new(upgrade) as Box<_>));
}
UpgradePurpose::Identify => {
let wanted = upgrade::map(identify::IdentifyProtocolConfig, move |i| FinalUpgrade::from(i));
// TODO: shouldn't be &self.address ; requires a change in libp2p
let upgrade = upgrade::apply(substream, wanted, Endpoint::Dialer, &self.address);
let upgrade = upgrade::apply(substream, wanted, Endpoint::Dialer);
self.upgrades_in_progress_dial.push((purpose, Box::new(upgrade) as Box<_>));
}
UpgradePurpose::Ping => {
let wanted = upgrade::map(ping::Ping::default(), move |p| FinalUpgrade::from(p));
// TODO: shouldn't be &self.address ; requires a change in libp2p
let upgrade = upgrade::apply(substream, wanted, Endpoint::Dialer, &self.address);
let wanted = upgrade::map(ping::protocol::Ping::default(), move |p| FinalUpgrade::from(p));
let upgrade = upgrade::apply(substream, wanted, Endpoint::Dialer);
self.upgrades_in_progress_dial.push((purpose, Box::new(upgrade) as Box<_>));
}
};
@@ -733,6 +720,9 @@ where TSubstream: AsyncRead + AsyncWrite + Send + 'static,
},
// We don't care about Kademlia pings, they are unused.
Ok(Async::Ready(Some(KadIncomingRequest::PingPong))) => {},
// Other Kademlia messages are unimplemented.
Ok(Async::Ready(Some(KadIncomingRequest::GetProviders { .. }))) => {},
Ok(Async::Ready(Some(KadIncomingRequest::AddProvider { .. }))) => {},
Ok(Async::NotReady) => {
self.kademlia_substream = Some((controller, stream));
break;
@@ -847,16 +837,16 @@ enum FinalUpgrade<TSubstream> {
Kad(KadConnecController, Box<Stream<Item = KadIncomingRequest, Error = IoError> + Send>),
IdentifyListener(identify::IdentifySender<TSubstream>),
IdentifyDialer(identify::IdentifyInfo, Multiaddr),
PingDialer(ping::PingDialer<TSubstream, Instant>),
PingListener(ping::PingListener<TSubstream>),
PingDialer(ping::protocol::PingDialer<TSubstream, Instant>),
PingListener(ping::protocol::PingListener<TSubstream>),
Custom(RegisteredProtocolSubstream<TSubstream>),
}
impl<TSubstream> From<ping::PingOutput<TSubstream, Instant>> for FinalUpgrade<TSubstream> {
fn from(out: ping::PingOutput<TSubstream, Instant>) -> Self {
impl<TSubstream> From<ping::protocol::PingOutput<TSubstream, Instant>> for FinalUpgrade<TSubstream> {
fn from(out: ping::protocol::PingOutput<TSubstream, Instant>) -> Self {
match out {
ping::PingOutput::Ponger(ponger) => FinalUpgrade::PingListener(ponger),
ping::PingOutput::Pinger(pinger) => FinalUpgrade::PingDialer(pinger),
ping::protocol::PingOutput::Ponger(ponger) => FinalUpgrade::PingListener(ponger),
ping::protocol::PingOutput::Pinger(pinger) => FinalUpgrade::PingDialer(pinger),
}
}
}
@@ -21,7 +21,7 @@ use futures::{prelude::*, task, Stream};
use futures::sync::{oneshot, mpsc};
use libp2p::{Multiaddr, PeerId};
use libp2p::core::{Endpoint, PublicKey};
use libp2p::core::nodes::swarm::ConnectedPoint;
use libp2p::core::nodes::ConnectedPoint;
use libp2p::kad::{KadSystem, KadSystemConfig, KadConnecController, KadPeer};
use libp2p::kad::{KadConnectionType, KadQueryEvent};
use parking_lot::Mutex;
@@ -567,6 +567,8 @@ impl Service {
/// Adds a list of peers to the network topology.
fn add_discovered_peers(&mut self, list: impl IntoIterator<Item = KadPeer>) {
let mut topology_has_changed = false;
for peer in list {
let connected = match peer.connection_ty {
KadConnectionType::NotConnected => false,
@@ -575,15 +577,20 @@ impl Service {
KadConnectionType::CannotConnect => continue,
};
self.topology.add_kademlia_discovered_addrs(
let changed = self.topology.add_kademlia_discovered_addrs(
&peer.node_id,
peer.multiaddrs.iter().map(|a| (a.clone(), connected))
);
if changed {
topology_has_changed = true;
}
}
// Potentially connect to the newly-discovered nodes.
// TODO: only do so if the topology reports that something new has been added
self.connect_to_nodes();
if topology_has_changed {
self.connect_to_nodes();
}
}
/// Handles the swarm opening a connection to the given peer.
+108 -151
View File
@@ -20,15 +20,12 @@ use fnv::FnvHashMap;
use futures::{prelude::*, Stream};
use libp2p::{Multiaddr, multiaddr::Protocol, PeerId};
use libp2p::core::{muxing, Endpoint, PublicKey};
use libp2p::core::nodes::node::Substream;
use libp2p::core::nodes::swarm::{ConnectedPoint, Swarm as Libp2pSwarm, HandlerFactory};
use libp2p::core::nodes::swarm::{SwarmEvent as Libp2pSwarmEvent, Peer as SwarmPeer};
use libp2p::core::nodes::{ConnectedPoint, RawSwarm, RawSwarmEvent, Peer as SwarmPeer, Substream};
use libp2p::core::transport::boxed::Boxed;
use libp2p::kad::{KadConnecController, KadFindNodeRespond};
use libp2p::secio;
use node_handler::{SubstrateOutEvent, SubstrateNodeHandler, SubstrateInEvent, IdentificationRequest};
use std::io::{Error as IoError, ErrorKind as IoErrorKind};
use std::{mem, sync::Arc};
use std::{io, mem, sync::Arc};
use transport;
use {Error, NodeIndex, ProtocolId};
@@ -47,10 +44,11 @@ pub fn start_swarm(
let transport = transport::build_transport(local_private_key);
// Build the underlying libp2p swarm.
let swarm = Libp2pSwarm::with_handler_builder(transport, HandlerBuilder(Arc::new(registered_custom)));
let swarm = RawSwarm::new(transport);
Ok(Swarm {
swarm,
registered_custom: Arc::new(registered_custom),
local_public_key,
local_peer_id,
listening_addrs: Vec::new(),
@@ -60,20 +58,6 @@ pub fn start_swarm(
})
}
/// Dummy structure that exists because we need to be able to express the type. Otherwise we would
/// use a closure.
#[derive(Clone)]
struct HandlerBuilder(Arc<RegisteredProtocols>);
impl HandlerFactory for HandlerBuilder
{
type Handler = SubstrateNodeHandler<Substream<Muxer>>;
#[inline]
fn new_handler(&self, addr: ConnectedPoint) -> Self::Handler {
SubstrateNodeHandler::new(self.0.clone(), addr)
}
}
/// Event produced by the swarm.
pub enum SwarmEvent {
/// We have successfully connected to a node.
@@ -117,7 +101,7 @@ pub enum SwarmEvent {
/// Address that failed.
address: Multiaddr,
/// Reason why we failed.
error: IoError,
error: io::Error,
},
/// Report information about the node.
@@ -197,20 +181,23 @@ pub enum SwarmEvent {
/// Index of the node.
node_index: NodeIndex,
/// Reason why it has been closed. `Ok` means that it's been closed gracefully.
result: Result<(), IoError>,
result: Result<(), io::Error>,
},
}
/// Network swarm. Must be polled regularly in order for the networking to work.
pub struct Swarm {
/// Stream of events of the swarm.
swarm: Libp2pSwarm<
swarm: RawSwarm<
Boxed<(PeerId, Muxer)>,
SubstrateInEvent,
SubstrateOutEvent<Substream<Muxer>>,
HandlerBuilder
SubstrateNodeHandler<Substream<Muxer>>
>,
/// List of registered protocols. Used when we open or receive a new connection.
registered_custom: Arc<RegisteredProtocols>,
/// Public key of the local node.
local_public_key: PublicKey,
@@ -340,7 +327,7 @@ impl Swarm {
SwarmPeer::NotConnected(peer) => {
trace!(target: "sub-libp2p", "Starting to connect to {:?} through {}",
peer_id, addr);
match peer.connect(addr) {
match peer.connect(addr, SubstrateNodeHandler::new(self.registered_custom.clone())) {
Ok(_) => Ok(false),
Err(_) => Err(()),
}
@@ -351,7 +338,7 @@ impl Swarm {
/// Start dialing an address, not knowing which peer ID to expect.
#[inline]
pub fn dial(&mut self, addr: Multiaddr) -> Result<(), Multiaddr> {
self.swarm.dial(addr)
self.swarm.dial(addr, SubstrateNodeHandler::new(self.registered_custom.clone()))
}
/// After receiving a `NodePending` event, you should call either `accept_node` or `drop_node`
@@ -479,124 +466,6 @@ impl Swarm {
);
}
/// Processes an event received by the swarm.
///
/// Optionally returns an event to report back to the outside.
///
/// > **Note**: Must be called from inside `poll()`, otherwise it will panic. This method
/// > shouldn't be made public because of this requirement.
fn process_network_event(
&mut self,
event: Libp2pSwarmEvent<Boxed<(PeerId, Muxer)>, SubstrateOutEvent<Substream<Muxer>>>
) -> Option<SwarmEvent> {
match event {
Libp2pSwarmEvent::Connected { peer_id, endpoint } => {
let node_index = self.next_node_index.clone();
self.next_node_index += 1;
self.node_by_peer.insert(peer_id.clone(), node_index);
self.nodes_info.insert(node_index, NodeInfo {
peer_id: peer_id.clone(),
endpoint: match endpoint {
ConnectedPoint::Listener { .. } => Endpoint::Listener,
ConnectedPoint::Dialer { .. } => Endpoint::Dialer,
},
open_protocols: Vec::new(),
});
return Some(SwarmEvent::NodePending {
node_index,
peer_id,
endpoint
});
}
Libp2pSwarmEvent::Replaced { peer_id, endpoint, .. } => {
let node_index = *self.node_by_peer.get(&peer_id)
.expect("node_by_peer is always kept in sync with the inner swarm");
let infos = self.nodes_info.get_mut(&node_index)
.expect("nodes_info is always kept in sync with the swarm");
debug_assert_eq!(infos.peer_id, peer_id);
infos.endpoint = match endpoint {
ConnectedPoint::Listener { .. } => Endpoint::Listener,
ConnectedPoint::Dialer { .. } => Endpoint::Dialer,
};
let closed_custom_protocols = mem::replace(&mut infos.open_protocols, Vec::new());
return Some(SwarmEvent::Reconnected {
node_index,
endpoint,
closed_custom_protocols,
});
},
Libp2pSwarmEvent::NodeClosed { peer_id, .. } => {
debug!(target: "sub-libp2p", "Connection to {:?} closed gracefully", peer_id);
let node_index = self.node_by_peer.remove(&peer_id)
.expect("node_by_peer is always kept in sync with the inner swarm");
let infos = self.nodes_info.remove(&node_index)
.expect("nodes_info is always kept in sync with the inner swarm");
debug_assert_eq!(infos.peer_id, peer_id);
return Some(SwarmEvent::NodeClosed {
node_index,
peer_id,
closed_custom_protocols: infos.open_protocols,
});
},
Libp2pSwarmEvent::NodeError { peer_id, error, .. } => {
debug!(target: "sub-libp2p", "Closing {:?} because of error: {:?}", peer_id, error);
let node_index = self.node_by_peer.remove(&peer_id)
.expect("node_by_peer is always kept in sync with the inner swarm");
let infos = self.nodes_info.remove(&node_index)
.expect("nodes_info is always kept in sync with the inner swarm");
debug_assert_eq!(infos.peer_id, peer_id);
return Some(SwarmEvent::NodeClosed {
node_index,
peer_id,
closed_custom_protocols: infos.open_protocols,
});
},
Libp2pSwarmEvent::DialError { multiaddr, error, .. } =>
return Some(SwarmEvent::DialFail {
address: multiaddr,
error,
}),
Libp2pSwarmEvent::UnknownPeerDialError { multiaddr, error } =>
return Some(SwarmEvent::DialFail {
address: multiaddr,
error,
}),
Libp2pSwarmEvent::PublicKeyMismatch {
actual_peer_id,
multiaddr,
expected_peer_id,
..
} => {
debug!(target: "sub-libp2p", "When dialing {:?} through {}, public key mismatch, \
actual = {:?}", expected_peer_id, multiaddr, actual_peer_id);
return Some(SwarmEvent::DialFail {
address: multiaddr,
error: IoError::new(IoErrorKind::Other, "Public key mismatch"),
});
},
Libp2pSwarmEvent::ListenerClosed { listen_addr, result, .. } => {
warn!(target: "sub-libp2p", "Listener closed for {}: {:?}", listen_addr, result);
if self.swarm.listeners().count() == 0 {
warn!(target: "sub-libp2p", "No listener left");
}
},
Libp2pSwarmEvent::NodeEvent { peer_id, event } =>
if let Some(event) = self.handle_node_event(peer_id, event) {
return Some(event);
},
Libp2pSwarmEvent::IncomingConnection { listen_addr, send_back_addr } =>
trace!(target: "sub-libp2p", "Incoming connection with {} on listener {}",
send_back_addr, listen_addr),
Libp2pSwarmEvent::IncomingConnectionError { listen_addr, send_back_addr, error } =>
trace!(target: "sub-libp2p", "Incoming connection with {} on listener {} \
errored: {:?}", send_back_addr, listen_addr, error),
}
None
}
/// Processes an event obtained by a node in the swarm.
///
/// Optionally returns an event that the service must emit.
@@ -698,17 +567,105 @@ impl Swarm {
impl Stream for Swarm {
type Item = SwarmEvent;
type Error = IoError;
type Error = io::Error;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
loop {
match self.swarm.poll() {
Async::Ready(Some(event)) =>
if let Some(event) = self.process_network_event(event) {
return Ok(Async::Ready(Some(event)));
}
let (peer_id, node_event) = match self.swarm.poll() {
Async::Ready(RawSwarmEvent::Connected { peer_id, endpoint }) => {
let node_index = self.next_node_index.clone();
self.next_node_index += 1;
self.node_by_peer.insert(peer_id.clone(), node_index);
self.nodes_info.insert(node_index, NodeInfo {
peer_id: peer_id.clone(),
endpoint: match endpoint {
ConnectedPoint::Listener { .. } => Endpoint::Listener,
ConnectedPoint::Dialer { .. } => Endpoint::Dialer,
},
open_protocols: Vec::new(),
});
return Ok(Async::Ready(Some(SwarmEvent::NodePending {
node_index,
peer_id,
endpoint
})));
}
Async::Ready(RawSwarmEvent::Replaced { peer_id, endpoint, .. }) => {
let node_index = *self.node_by_peer.get(&peer_id)
.expect("node_by_peer is always kept in sync with the inner swarm");
let infos = self.nodes_info.get_mut(&node_index)
.expect("nodes_info is always kept in sync with the swarm");
debug_assert_eq!(infos.peer_id, peer_id);
infos.endpoint = match endpoint {
ConnectedPoint::Listener { .. } => Endpoint::Listener,
ConnectedPoint::Dialer { .. } => Endpoint::Dialer,
};
let closed_custom_protocols = mem::replace(&mut infos.open_protocols, Vec::new());
return Ok(Async::Ready(Some(SwarmEvent::Reconnected {
node_index,
endpoint,
closed_custom_protocols,
})));
},
Async::Ready(RawSwarmEvent::NodeClosed { peer_id, .. }) => {
debug!(target: "sub-libp2p", "Connection to {:?} closed gracefully", peer_id);
let node_index = self.node_by_peer.remove(&peer_id)
.expect("node_by_peer is always kept in sync with the inner swarm");
let infos = self.nodes_info.remove(&node_index)
.expect("nodes_info is always kept in sync with the inner swarm");
debug_assert_eq!(infos.peer_id, peer_id);
return Ok(Async::Ready(Some(SwarmEvent::NodeClosed {
node_index,
peer_id,
closed_custom_protocols: infos.open_protocols,
})));
},
Async::Ready(RawSwarmEvent::NodeError { peer_id, error, .. }) => {
debug!(target: "sub-libp2p", "Closing {:?} because of error: {:?}", peer_id, error);
let node_index = self.node_by_peer.remove(&peer_id)
.expect("node_by_peer is always kept in sync with the inner swarm");
let infos = self.nodes_info.remove(&node_index)
.expect("nodes_info is always kept in sync with the inner swarm");
debug_assert_eq!(infos.peer_id, peer_id);
return Ok(Async::Ready(Some(SwarmEvent::NodeClosed {
node_index,
peer_id,
closed_custom_protocols: infos.open_protocols,
})));
},
Async::Ready(RawSwarmEvent::DialError { multiaddr, error, .. }) =>
return Ok(Async::Ready(Some(SwarmEvent::DialFail {
address: multiaddr,
error,
}))),
Async::Ready(RawSwarmEvent::UnknownPeerDialError { multiaddr, error, .. }) =>
return Ok(Async::Ready(Some(SwarmEvent::DialFail {
address: multiaddr,
error,
}))),
Async::Ready(RawSwarmEvent::ListenerClosed { listen_addr, result, .. }) => {
warn!(target: "sub-libp2p", "Listener closed for {}: {:?}", listen_addr, result);
continue;
},
Async::Ready(RawSwarmEvent::NodeEvent { peer_id, event }) => (peer_id, event),
Async::Ready(RawSwarmEvent::IncomingConnection(incoming)) => {
trace!(target: "sub-libp2p", "Incoming connection with {} on listener {}",
incoming.send_back_addr(), incoming.listen_addr());
incoming.accept(SubstrateNodeHandler::new(self.registered_custom.clone()));
continue;
},
Async::Ready(RawSwarmEvent::IncomingConnectionError { listen_addr, send_back_addr, error }) => {
trace!(target: "sub-libp2p", "Incoming connection with {} on listener {} \
errored: {:?}", send_back_addr, listen_addr, error);
continue;
},
Async::NotReady => return Ok(Async::NotReady),
Async::Ready(None) => unreachable!("The Swarm stream never ends"),
};
if let Some(event) = self.handle_node_event(peer_id, node_event) {
return Ok(Async::Ready(Some(event)));
}
}
}
+20 -4
View File
@@ -245,12 +245,16 @@ impl NetTopology {
/// Adds addresses that a node says it is listening on.
///
/// The addresses are most likely to be valid.
///
/// Returns `true` if the topology has changed in some way. Returns `false` if calling this
/// method was a no-op.
#[inline]
pub fn add_self_reported_listen_addrs<I>(
&mut self,
peer_id: &PeerId,
addrs: I,
) where I: Iterator<Item = Multiaddr> {
) -> bool
where I: Iterator<Item = Multiaddr> {
self.add_discovered_addrs(peer_id, addrs.map(|a| (a, true)))
}
@@ -260,21 +264,28 @@ impl NetTopology {
///
/// For each address, incorporates a boolean. If true, that means we have some sort of hint
/// that this address can be reached.
///
/// Returns `true` if the topology has changed in some way. Returns `false` if calling this
/// method was a no-op.
#[inline]
pub fn add_kademlia_discovered_addrs<I>(
&mut self,
peer_id: &PeerId,
addrs: I,
) where I: Iterator<Item = (Multiaddr, bool)> {
) -> bool
where I: Iterator<Item = (Multiaddr, bool)> {
self.add_discovered_addrs(peer_id, addrs)
}
/// Inner implementaiton of the `add_*_discovered_addrs`.
/// Inner implementaiton of the `add_*_discovered_addrs` methods.
/// Returns `true` if the topology has changed in some way. Returns `false` if calling this
/// method was a no-op.
fn add_discovered_addrs<I>(
&mut self,
peer_id: &PeerId,
addrs: I,
) where I: Iterator<Item = (Multiaddr, bool)> {
) -> bool
where I: Iterator<Item = (Multiaddr, bool)> {
let mut addrs: Vec<_> = addrs.collect();
let now_systime = SystemTime::now();
let now = Instant::now();
@@ -291,6 +302,8 @@ impl NetTopology {
true
});
let mut anything_changed = false;
if !addrs.is_empty() {
trace!(
target: "sub-libp2p",
@@ -317,6 +330,7 @@ impl NetTopology {
}
}
anything_changed = true;
peer.addrs.push(Addr {
addr,
expires: now_systime + KADEMLIA_DISCOVERY_EXPIRATION,
@@ -329,6 +343,8 @@ impl NetTopology {
}),
});
}
anything_changed
}
/// Indicates the peer store that we're connected to this given address.
@@ -30,14 +30,14 @@ pub fn build_transport(
let base = libp2p::CommonTransport::new()
.with_upgrade(secio::SecioConfig::new(local_private_key))
.and_then(move |out, endpoint, client_addr| {
.and_then(move |out, endpoint| {
let upgrade = upgrade::or(
upgrade::map(yamux::Config::default(), either::EitherOutput::First),
upgrade::map(mplex_config, either::EitherOutput::Second),
);
let peer_id = out.remote_key.into_peer_id();
let upgrade = upgrade::map(upgrade, move |muxer| (peer_id, muxer));
upgrade::apply(out.stream, upgrade, endpoint, client_addr)
upgrade::apply(out.stream, upgrade, endpoint.into())
})
.map(|(id, muxer), _| (id, StreamMuxerBox::new(muxer)));
-13
View File
@@ -1,13 +0,0 @@
= Network
.Summary
[source, toml]
----
include::Cargo.toml[lines=2..5]
----
.Description
----
include::src/lib.rs[tag=description]
----
+13 -12
View File
@@ -68,52 +68,53 @@ pub trait Client<Block: BlockT>: Send + Sync {
) -> Result<(NumberFor<Block>, Vec<Vec<u8>>), Error>;
}
impl<B, E, Block> Client<Block> for SubstrateClient<B, E, Block> where
impl<B, E, Block, RA> Client<Block> for SubstrateClient<B, E, Block, RA> where
B: client::backend::Backend<Block, Blake2Hasher> + Send + Sync + 'static,
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + 'static,
Self: BlockImport<Block, Error=Error>,
Block: BlockT<Hash=H256>,
RA: Send + Sync
{
fn import(&self, block: ImportBlock<Block>, new_authorities: Option<Vec<AuthorityId>>)
-> Result<ImportResult, Error>
{
(self as &SubstrateClient<B, E, Block>).import_block(block, new_authorities)
(self as &SubstrateClient<B, E, Block, RA>).import_block(block, new_authorities)
}
fn info(&self) -> Result<ClientInfo<Block>, Error> {
(self as &SubstrateClient<B, E, Block>).info()
(self as &SubstrateClient<B, E, Block, RA>).info()
}
fn block_status(&self, id: &BlockId<Block>) -> Result<BlockStatus, Error> {
(self as &SubstrateClient<B, E, Block>).block_status(id)
(self as &SubstrateClient<B, E, Block, RA>).block_status(id)
}
fn block_hash(&self, block_number: <Block::Header as HeaderT>::Number) -> Result<Option<Block::Hash>, Error> {
(self as &SubstrateClient<B, E, Block>).block_hash(block_number)
(self as &SubstrateClient<B, E, Block, RA>).block_hash(block_number)
}
fn header(&self, id: &BlockId<Block>) -> Result<Option<Block::Header>, Error> {
(self as &SubstrateClient<B, E, Block>).header(id)
(self as &SubstrateClient<B, E, Block, RA>).header(id)
}
fn body(&self, id: &BlockId<Block>) -> Result<Option<Vec<Block::Extrinsic>>, Error> {
(self as &SubstrateClient<B, E, Block>).body(id)
(self as &SubstrateClient<B, E, Block, RA>).body(id)
}
fn justification(&self, id: &BlockId<Block>) -> Result<Option<Justification>, Error> {
(self as &SubstrateClient<B, E, Block>).justification(id)
(self as &SubstrateClient<B, E, Block, RA>).justification(id)
}
fn header_proof(&self, block_number: <Block::Header as HeaderT>::Number) -> Result<(Block::Header, Vec<Vec<u8>>), Error> {
(self as &SubstrateClient<B, E, Block>).header_proof(&BlockId::Number(block_number))
(self as &SubstrateClient<B, E, Block, RA>).header_proof(&BlockId::Number(block_number))
}
fn read_proof(&self, block: &Block::Hash, key: &[u8]) -> Result<Vec<Vec<u8>>, Error> {
(self as &SubstrateClient<B, E, Block>).read_proof(&BlockId::Hash(block.clone()), key)
(self as &SubstrateClient<B, E, Block, RA>).read_proof(&BlockId::Hash(block.clone()), key)
}
fn execution_proof(&self, block: &Block::Hash, method: &str, data: &[u8]) -> Result<(Vec<u8>, Vec<Vec<u8>>), Error> {
(self as &SubstrateClient<B, E, Block>).execution_proof(&BlockId::Hash(block.clone()), method, data)
(self as &SubstrateClient<B, E, Block, RA>).execution_proof(&BlockId::Hash(block.clone()), method, data)
}
fn key_changes_proof(
@@ -123,6 +124,6 @@ impl<B, E, Block> Client<Block> for SubstrateClient<B, E, Block> where
max: Block::Hash,
key: &[u8]
) -> Result<(NumberFor<Block>, Vec<Vec<u8>>), Error> {
(self as &SubstrateClient<B, E, Block>).key_changes_proof(first, last, max, key)
(self as &SubstrateClient<B, E, Block, RA>).key_changes_proof(first, last, max, key)
}
}
+53 -2
View File
@@ -14,9 +14,34 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
pub use service::Roles;
//! Configuration for the networking layer of Substrate.
/// Protocol configuration
pub use network_libp2p::{NonReservedPeerMode, NetworkConfiguration};
use chain::Client;
use codec;
use on_demand::OnDemandService;
use runtime_primitives::traits::{Block as BlockT};
use service::{ExHashT, TransactionPool};
use std::sync::Arc;
/// Service initialization parameters.
pub struct Params<B: BlockT, S, H: ExHashT> {
/// Configuration.
pub config: ProtocolConfig,
/// Network layer configuration.
pub network_config: NetworkConfiguration,
/// Substrate relay chain access point.
pub chain: Arc<Client<B>>,
/// On-demand service reference.
pub on_demand: Option<Arc<OnDemandService<B>>>,
/// Transaction pool.
pub transaction_pool: Arc<TransactionPool<H, B>>,
/// Protocol specialization.
pub specialization: S,
}
/// Configuration for the Substrate-specific part of the networking layer.
#[derive(Clone)]
pub struct ProtocolConfig {
/// Assigned roles.
@@ -30,3 +55,29 @@ impl Default for ProtocolConfig {
}
}
}
bitflags! {
/// Bitmask of the roles that a node fulfills.
pub struct Roles: u8 {
/// No network.
const NONE = 0b00000000;
/// Full node, does not participate in consensus.
const FULL = 0b00000001;
/// Light client node.
const LIGHT = 0b00000010;
/// Act as an authority
const AUTHORITY = 0b00000100;
}
}
impl codec::Encode for Roles {
fn encode_to<T: codec::Output>(&self, dest: &mut T) {
dest.push_byte(self.bits())
}
}
impl codec::Decode for Roles {
fn decode<I: codec::Input>(input: &mut I) -> Option<Self> {
Self::from_bits(input.read_byte()?)
}
}
@@ -26,8 +26,8 @@ use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Hash, HashF
use runtime_primitives::generic::BlockId;
use message::generic::{Message, ConsensusMessage};
use protocol::Context;
use service::Roles;
use specialization::Specialization;
use config::Roles;
use specialization::NetworkSpecialization;
use StatusMessage;
use generic_message;
@@ -262,7 +262,7 @@ where
}
}
impl<Block: BlockT> Specialization<Block> for ConsensusGossip<Block> where
impl<Block: BlockT> NetworkSpecialization<Block> for ConsensusGossip<Block> where
Block::Header: HeaderT<Number=u64>
{
fn status(&self) -> Vec<u8> {
+23 -13
View File
@@ -164,8 +164,12 @@ impl<B: BlockT, V: 'static + Verifier<B>> ImportQueue<B> for BasicQueue<B, V> {
fn stop(&self) {
self.clear();
if let Some(handle) = self.handle.lock().take() {
self.data.is_stopping.store(true, Ordering::SeqCst);
self.data.signal.notify_one();
{
// Perform storing the stop flag and signalling under a single lock.
let _queue_lock = self.data.queue.lock();
self.data.is_stopping.store(true, Ordering::SeqCst);
self.data.signal.notify_one();
}
let _ = handle.join();
}
@@ -217,12 +221,15 @@ fn import_thread<B: BlockT, L: Link<B>, V: Verifier<B>>(
) {
trace!(target: "sync", "Starting import thread");
loop {
if qdata.is_stopping.load(Ordering::SeqCst) {
break;
}
let new_blocks = {
let mut queue_lock = qdata.queue.lock();
// We are holding the same lock that `stop` takes so here we either see that stop flag
// is active or wait for the signal. The latter one unlocks the mutex and this gives a chance
// to `stop` to generate the signal.
if qdata.is_stopping.load(Ordering::SeqCst) {
break;
}
if queue_lock.is_empty() {
qdata.signal.wait(&mut queue_lock);
}
@@ -556,8 +563,8 @@ impl<B: BlockT> Verifier<B> for PassThroughVerifier {
header,
body,
finalized: self.0,
external_justification: justification,
post_runtime_digests: vec![],
justification: justification,
post_digests: vec![],
auxiliary: Vec::new(),
}, None))
}
@@ -692,7 +699,7 @@ pub mod tests {
}
}
fn prepare_good_block() -> (client::Client<test_client::Backend, test_client::Executor, Block>, Hash, u64, BlockData<Block>) {
fn prepare_good_block() -> (client::Client<test_client::Backend, test_client::Executor, Block, test_client::runtime::ClientWithApi>, Hash, u64, BlockData<Block>) {
let client = test_client::new();
let block = client.new_block().unwrap().bake().unwrap();
client.justify_and_import(BlockOrigin::File, block).unwrap();
@@ -803,9 +810,12 @@ pub mod tests {
#[test]
fn async_import_queue_drops() {
let verifier = Arc::new(PassThroughVerifier(true));
let queue = BasicQueue::new(verifier, Arc::new(test_client::new()));
queue.start(TestLink::new()).unwrap();
drop(queue);
// Perform this test multiple times since it exhibits non-deterministic behavior.
for _ in 0..100 {
let verifier = Arc::new(PassThroughVerifier(true));
let queue = BasicQueue::new(verifier, Arc::new(test_client::new()));
queue.start(TestLink::new()).unwrap();
drop(queue);
}
}
}
+3 -6
View File
@@ -17,10 +17,8 @@
#![warn(unused_extern_crates)]
#![warn(missing_docs)]
// tag::description[]
//! Substrate-specific P2P networking: synchronizing blocks, propagating BFT messages.
//! Allows attachment of an optional subprotocol for chain-specific requests.
// end::description[]
extern crate linked_hash_map;
extern crate parking_lot;
@@ -53,10 +51,10 @@ mod sync;
#[macro_use]
mod protocol;
mod io;
mod config;
mod chain;
mod blocks;
mod on_demand;
pub mod config;
pub mod import_queue;
pub mod consensus_gossip;
pub mod error;
@@ -67,13 +65,12 @@ pub mod specialization;
pub mod test;
pub use chain::Client as ClientHandle;
pub use service::{Service, FetchFuture, TransactionPool, Params, ManageNetwork, SyncProvider};
pub use service::{Service, FetchFuture, TransactionPool, ManageNetwork, SyncProvider};
pub use protocol::{ProtocolStatus, PeerInfo, Context};
pub use sync::{Status as SyncStatus, SyncState};
pub use network_libp2p::{NonReservedPeerMode, NetworkConfiguration, NodeIndex, ProtocolId, Severity, Protocol};
pub use network_libp2p::{NodeIndex, ProtocolId, Severity, Protocol};
pub use message::{generic as generic_message, RequestId, Status as StatusMessage};
pub use error::Error;
pub use config::{Roles, ProtocolConfig};
pub use on_demand::{OnDemand, OnDemandService, RemoteResponse};
#[doc(hidden)]
pub use runtime_primitives::traits::Block as BlockT;
+1 -1
View File
@@ -125,7 +125,7 @@ pub struct RemoteReadResponse {
/// Generic types.
pub mod generic {
use runtime_primitives::Justification;
use service::Roles;
use config::Roles;
use super::{
BlockAttributes, RemoteCallResponse, RemoteReadResponse,
RequestId, Transactions, Direction
+11 -5
View File
@@ -30,6 +30,7 @@ use client::light::fetcher::{Fetcher, FetchChecker, RemoteHeaderRequest,
use io::SyncIo;
use message;
use network_libp2p::{Severity, NodeIndex};
use config::Roles;
use service;
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor};
@@ -41,7 +42,7 @@ const RETRY_COUNT: usize = 1;
/// On-demand service API.
pub trait OnDemandService<Block: BlockT>: Send + Sync {
/// When new node is connected.
fn on_connect(&self, peer: NodeIndex, role: service::Roles, best_number: NumberFor<Block>);
fn on_connect(&self, peer: NodeIndex, role: Roles, best_number: NumberFor<Block>);
/// When block is announced by the peer.
fn on_block_announce(&self, peer: NodeIndex, best_number: NumberFor<Block>);
@@ -211,8 +212,8 @@ impl<B, E> OnDemandService<B> for OnDemand<B, E> where
E: service::ExecuteInContext<B>,
B::Header: HeaderT,
{
fn on_connect(&self, peer: NodeIndex, role: service::Roles, best_number: NumberFor<B>) {
if !role.intersects(service::Roles::FULL | service::Roles::AUTHORITY) { // TODO: correct?
fn on_connect(&self, peer: NodeIndex, role: Roles, best_number: NumberFor<B>) {
if !role.intersects(Roles::FULL | Roles::AUTHORITY) { // TODO: correct?
return;
}
@@ -511,9 +512,10 @@ pub mod tests {
use client::{self, error::{ErrorKind as ClientErrorKind, Result as ClientResult}};
use client::light::fetcher::{Fetcher, FetchChecker, RemoteHeaderRequest,
RemoteCallRequest, RemoteReadRequest, RemoteChangesRequest};
use config::Roles;
use message;
use network_libp2p::NodeIndex;
use service::{Roles, ExecuteInContext};
use service::ExecuteInContext;
use test::TestIo;
use super::{REQUEST_TIMEOUT, OnDemand, OnDemandService};
use test_client::runtime::{changes_trie_config, Block, Header};
@@ -820,7 +822,11 @@ pub mod tests {
});
let thread = ::std::thread::spawn(move || {
let result = response.wait().unwrap();
assert_eq!(result.hash(), "0x6443a0b46e0412e626363028115a9f2cf963eeed526b8b33e5316f08b50d0dc3".into());
assert_eq!(
result.hash(),
"6443a0b46e0412e626363028115a9f2c\
f963eeed526b8b33e5316f08b50d0dc3".parse().unwrap()
);
});
on_demand.on_remote_header_response(&mut network, 0, message::RemoteHeaderResponse {
+6 -6
View File
@@ -27,11 +27,11 @@ use codec::{Encode, Decode};
use message::{self, Message};
use message::generic::Message as GenericMessage;
use specialization::Specialization;
use specialization::NetworkSpecialization;
use sync::{ChainSync, Status as SyncStatus, SyncState};
use service::{Roles, TransactionPool, ExHashT};
use service::{TransactionPool, ExHashT};
use import_queue::ImportQueue;
use config::ProtocolConfig;
use config::{ProtocolConfig, Roles};
use chain::Client;
use on_demand::OnDemandService;
use io::SyncIo;
@@ -50,7 +50,7 @@ const MAX_BLOCK_DATA_RESPONSE: u32 = 128;
const LIGHT_MAXIMAL_BLOCKS_DIFFERENCE: u64 = 8192;
// Lock must always be taken in order declared here.
pub struct Protocol<B: BlockT, S: Specialization<B>, H: ExHashT> {
pub struct Protocol<B: BlockT, S: NetworkSpecialization<B>, H: ExHashT> {
config: ProtocolConfig,
on_demand: Option<Arc<OnDemandService<B>>>,
genesis_hash: B::Hash,
@@ -184,7 +184,7 @@ pub(crate) struct ContextData<B: BlockT, H: ExHashT> {
pub chain: Arc<Client<B>>,
}
impl<B: BlockT, S: Specialization<B>, H: ExHashT> Protocol<B, S, H> {
impl<B: BlockT, S: NetworkSpecialization<B>, H: ExHashT> Protocol<B, S, H> {
/// Create a new instance.
pub fn new<I: 'static + ImportQueue<B>>(
config: ProtocolConfig,
@@ -761,7 +761,7 @@ macro_rules! construct_simple_protocol {
}
}
impl $crate::specialization::Specialization<$block> for $protocol {
impl $crate::specialization::NetworkSpecialization<$block> for $protocol {
fn status(&self) -> Vec<u8> {
$(
let status = self.$status_protocol_name.status();
+26 -70
View File
@@ -25,11 +25,9 @@ use network_libp2p::{start_service, Service as NetworkService, ServiceEvent as N
use network_libp2p::{RegisteredProtocol, parse_str_addr, Protocol as Libp2pProtocol};
use io::NetSyncIo;
use protocol::{self, Protocol, ProtocolContext, Context, ProtocolStatus};
use config::{ProtocolConfig};
use config::Params;
use error::Error;
use chain::Client;
use specialization::Specialization;
use on_demand::OnDemandService;
use specialization::NetworkSpecialization;
use import_queue::ImportQueue;
use runtime_primitives::traits::{Block as BlockT};
use tokio::{runtime::Runtime, timer::Interval};
@@ -40,38 +38,10 @@ pub type FetchFuture = oneshot::Receiver<Vec<u8>>;
const TICK_TIMEOUT: Duration = Duration::from_millis(1000);
const PROPAGATE_TIMEOUT: Duration = Duration::from_millis(5000);
bitflags! {
/// Node roles bitmask.
pub struct Roles: u8 {
/// No network.
const NONE = 0b00000000;
/// Full node, does not participate in consensus.
const FULL = 0b00000001;
/// Light client node.
const LIGHT = 0b00000010;
/// Act as an authority
const AUTHORITY = 0b00000100;
}
}
impl ::codec::Encode for Roles {
fn encode_to<T: ::codec::Output>(&self, dest: &mut T) {
dest.push_byte(self.bits())
}
}
impl ::codec::Decode for Roles {
fn decode<I: ::codec::Input>(input: &mut I) -> Option<Self> {
Self::from_bits(input.read_byte()?)
}
}
/// Sync status
pub trait SyncProvider<B: BlockT>: Send + Sync {
/// Get sync status
fn status(&self) -> ProtocolStatus<B>;
/// Get this node id if available.
fn node_id(&self) -> Option<String>;
}
pub trait ExHashT: ::std::hash::Hash + Eq + ::std::fmt::Debug + Clone + Send + Sync + 'static {}
@@ -93,24 +63,8 @@ pub trait ExecuteInContext<B: BlockT>: Send + Sync {
fn execute_in_context<F: Fn(&mut Context<B>)>(&self, closure: F);
}
/// Service initialization parameters.
pub struct Params<B: BlockT, S, H: ExHashT> {
/// Configuration.
pub config: ProtocolConfig,
/// Network layer configuration.
pub network_config: NetworkConfiguration,
/// Substrate relay chain access point.
pub chain: Arc<Client<B>>,
/// On-demand service reference.
pub on_demand: Option<Arc<OnDemandService<B>>>,
/// Transaction pool.
pub transaction_pool: Arc<TransactionPool<H, B>>,
/// Protocol specialization.
pub specialization: S,
}
/// Substrate network service. Handles network IO and manages connectivity.
pub struct Service<B: BlockT + 'static, S: Specialization<B>, H: ExHashT> {
pub struct Service<B: BlockT + 'static, S: NetworkSpecialization<B>, H: ExHashT> {
/// Network service
network: Arc<Mutex<NetworkService>>,
/// Protocol handler
@@ -123,7 +77,7 @@ pub struct Service<B: BlockT + 'static, S: Specialization<B>, H: ExHashT> {
bg_thread: Option<(oneshot::Sender<()>, thread::JoinHandle<()>)>,
}
impl<B: BlockT + 'static, S: Specialization<B>, H: ExHashT> Service<B, S, H> {
impl<B: BlockT + 'static, S: NetworkSpecialization<B>, H: ExHashT> Service<B, S, H> {
/// Creates and register protocol with the network service
pub fn new<I: 'static + ImportQueue<B>>(
params: Params<B, S, H>,
@@ -179,13 +133,13 @@ impl<B: BlockT + 'static, S: Specialization<B>, H: ExHashT> Service<B, S, H> {
}
}
impl<B: BlockT + 'static, S: Specialization<B>, H: ExHashT> ::consensus::SyncOracle for Service<B, S, H> {
impl<B: BlockT + 'static, S: NetworkSpecialization<B>, H: ExHashT> ::consensus::SyncOracle for Service<B, S, H> {
fn is_major_syncing(&self) -> bool {
self.handler.sync().read().status().is_major_syncing()
}
}
impl<B: BlockT + 'static, S: Specialization<B>, H:ExHashT> Drop for Service<B, S, H> {
impl<B: BlockT + 'static, S: NetworkSpecialization<B>, H:ExHashT> Drop for Service<B, S, H> {
fn drop(&mut self) {
self.handler.stop();
if let Some((sender, join)) = self.bg_thread.take() {
@@ -197,30 +151,17 @@ impl<B: BlockT + 'static, S: Specialization<B>, H:ExHashT> Drop for Service<B, S
}
}
impl<B: BlockT + 'static, S: Specialization<B>, H: ExHashT> ExecuteInContext<B> for Service<B, S, H> {
impl<B: BlockT + 'static, S: NetworkSpecialization<B>, H: ExHashT> ExecuteInContext<B> for Service<B, S, H> {
fn execute_in_context<F: Fn(&mut ::protocol::Context<B>)>(&self, closure: F) {
closure(&mut ProtocolContext::new(self.handler.context_data(), &mut NetSyncIo::new(&self.network, self.protocol_id)))
}
}
impl<B: BlockT + 'static, S: Specialization<B>, H: ExHashT> SyncProvider<B> for Service<B, S, H> {
impl<B: BlockT + 'static, S: NetworkSpecialization<B>, H: ExHashT> SyncProvider<B> for Service<B, S, H> {
/// Get sync status
fn status(&self) -> ProtocolStatus<B> {
self.handler.status()
}
fn node_id(&self) -> Option<String> {
let network = self.network.lock();
let ret = network
.listeners()
.next()
.map(|addr| {
let mut addr = addr.clone();
addr.append(Libp2pProtocol::P2p(network.peer_id().clone().into()));
addr.to_string()
});
ret
}
}
/// Trait for managing network
@@ -233,9 +174,11 @@ pub trait ManageNetwork: Send + Sync {
fn remove_reserved_peer(&self, peer: PeerId);
/// Add reserved peer
fn add_reserved_peer(&self, peer: String) -> Result<(), String>;
/// Returns a user-friendly identifier of our node.
fn node_id(&self) -> Option<String>;
}
impl<B: BlockT + 'static, S: Specialization<B>, H: ExHashT> ManageNetwork for Service<B, S, H> {
impl<B: BlockT + 'static, S: NetworkSpecialization<B>, H: ExHashT> ManageNetwork for Service<B, S, H> {
fn accept_unreserved_peers(&self) {
self.network.lock().accept_unreserved_peers();
}
@@ -265,10 +208,23 @@ impl<B: BlockT + 'static, S: Specialization<B>, H: ExHashT> ManageNetwork for Se
self.network.lock().add_reserved_peer(addr, peer_id);
Ok(())
}
fn node_id(&self) -> Option<String> {
let network = self.network.lock();
let ret = network
.listeners()
.next()
.map(|addr| {
let mut addr = addr.clone();
addr.append(Libp2pProtocol::P2p(network.peer_id().clone().into()));
addr.to_string()
});
ret
}
}
/// Starts the background thread that handles the networking.
fn start_thread<B: BlockT + 'static, S: Specialization<B>, H: ExHashT>(
fn start_thread<B: BlockT + 'static, S: NetworkSpecialization<B>, H: ExHashT>(
config: NetworkConfiguration,
protocol: Arc<Protocol<B, S, H>>,
registered: RegisteredProtocol,
@@ -309,7 +265,7 @@ fn start_thread<B: BlockT + 'static, S: Specialization<B>, H: ExHashT>(
}
/// Runs the background thread that handles the networking.
fn run_thread<B: BlockT + 'static, S: Specialization<B>, H: ExHashT>(
fn run_thread<B: BlockT + 'static, S: NetworkSpecialization<B>, H: ExHashT>(
network_service: Arc<Mutex<NetworkService>>,
protocol: Arc<Protocol<B, S, H>>,
protocol_id: ProtocolId,
+1 -1
View File
@@ -21,7 +21,7 @@ use runtime_primitives::traits::Block as BlockT;
use protocol::Context;
/// A specialization of the substrate network protocol. Handles events and sends messages.
pub trait Specialization<B: BlockT>: Send + Sync + 'static {
pub trait NetworkSpecialization<B: BlockT>: Send + Sync + 'static {
/// Get the current specialization-status.
fn status(&self) -> Vec<u8>;
+1 -1
View File
@@ -25,7 +25,7 @@ use blocks::{self, BlockCollection};
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, As, NumberFor};
use runtime_primitives::generic::BlockId;
use message::{self, generic::Message as GenericMessage};
use service::Roles;
use config::Roles;
use import_queue::ImportQueue;
// Maximum blocks to request in a single packet.
+5 -6
View File
@@ -29,7 +29,6 @@ use client::block_builder::BlockBuilder;
use runtime_primitives::generic::BlockId;
use io::SyncIo;
use protocol::{Context, Protocol, ProtocolContext};
use primitives::{Blake2Hasher};
use config::ProtocolConfig;
use service::TransactionPool;
use network_libp2p::{NodeIndex, PeerId, Severity};
@@ -37,7 +36,7 @@ use keyring::Keyring;
use codec::Encode;
use import_queue::{SyncImportQueue, PassThroughVerifier, Verifier};
use consensus::BlockOrigin;
use specialization::Specialization;
use specialization::NetworkSpecialization;
use consensus_gossip::ConsensusGossip;
use import_queue::{BlockImport, ImportQueue};
use service::ExecuteInContext;
@@ -64,7 +63,7 @@ pub struct DummySpecialization {
pub gossip: ConsensusGossip<Block>,
}
impl Specialization<Block> for DummySpecialization {
impl NetworkSpecialization<Block> for DummySpecialization {
fn status(&self) -> Vec<u8> { vec![] }
fn on_connect(&mut self, ctx: &mut Context<Block>, peer_id: NodeIndex, status: ::message::Status<Block>) {
@@ -138,7 +137,7 @@ pub struct TestPacket {
recipient: NodeIndex,
}
pub type PeersClient = client::Client<test_client::Backend, test_client::Executor, Block>;
pub type PeersClient = client::Client<test_client::Backend, test_client::Executor, Block, test_client::runtime::ClientWithApi>;
pub struct Peer<V: Verifier<Block>, D> {
client: Arc<PeersClient>,
@@ -241,7 +240,7 @@ impl<V: 'static + Verifier<Block>, D> Peer<V, D> {
/// Add blocks to the peer -- edit the block before adding
pub fn generate_blocks<F>(&self, count: usize, origin: BlockOrigin, mut edit_block: F)
where F: FnMut(BlockBuilder<test_client::Backend, test_client::Executor, Block, Blake2Hasher>) -> Block
where F: FnMut(BlockBuilder<Block, PeersClient>) -> Block
{
use blocks::BlockData;
@@ -280,7 +279,7 @@ impl<V: 'static + Verifier<Block>, D> Peer<V, D> {
amount: 1,
nonce,
};
let signature = Keyring::from_raw_public(transfer.from.0).unwrap().sign(&transfer.encode()).into();
let signature = Keyring::from_raw_public(transfer.from.to_fixed_bytes()).unwrap().sign(&transfer.encode()).into();
builder.push(Extrinsic { transfer, signature }).unwrap();
nonce = nonce + 1;
builder.bake().unwrap()
+1 -1
View File
@@ -16,9 +16,9 @@
use client::backend::Backend;
use client::blockchain::HeaderBackend as BlockchainHeaderBackend;
use config::Roles;
use consensus::BlockOrigin;
use sync::SyncState;
use Roles;
use super::*;
#[test]
+7 -4
View File
@@ -4,15 +4,15 @@ version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
crunchy = "0.1"
crunchy = { version = "0.2", default-features = false }
sr-std = { path = "../sr-std", default-features = false }
parity-codec = { version = "2.1", default-features = false }
parity-codec-derive = { version = "2.1", default-features = false }
fixed-hash = { version = "0.2.2", default-features = false }
fixed-hash = { version = "0.3.0-beta", default-features = false }
rustc-hex = { version = "2.0", default-features = false }
serde = { version = "1.0", default-features = false }
serde_derive = { version = "1.0", optional = true }
uint = { version = "0.4.1", default-features = false }
uint = { version = "0.5.0-beta", default-features = false }
twox-hash = { version = "1.1.0", optional = true }
byteorder = { version = "1.1", default-features = false }
wasmi = { version = "0.4.1", optional = true }
@@ -32,10 +32,13 @@ heapsize = "0.4"
[features]
default = ["std"]
std = [
"crunchy/std",
"wasmi",
"uint/std",
"fixed-hash/std",
"fixed-hash/heapsizeof",
"fixed-hash/heapsize",
"fixed-hash/byteorder",
"fixed-hash/rustc-hex",
"fixed-hash/libc",
"parity-codec/std",
"hash256-std-hasher/std",
-13
View File
@@ -1,13 +0,0 @@
= Primitives
.Summary
[source, toml]
----
include::Cargo.toml[lines=2..5]
----
.Description
----
include::src/lib.rs[tag=description]
----
+3 -3
View File
@@ -241,7 +241,7 @@ impl Pair {
pub fn derive_child_probably_bad(&self, chain_data: &[u8]) -> Pair {
let sig = self.sign(chain_data);
let mut seed = [0u8; 32];
seed.copy_from_slice(&sig.0[..32]);
seed.copy_from_slice(&sig[..32]);
Pair::from_seed(&seed)
}
@@ -251,7 +251,7 @@ impl Pair {
pub fn verify_strong<P: AsRef<Public>>(sig: &Signature, message: &[u8], pubkey: P) -> bool {
let public_key = untrusted::Input::from(&pubkey.as_ref().0[..]);
let msg = untrusted::Input::from(message);
let sig = untrusted::Input::from(&sig.0[..]);
let sig = untrusted::Input::from(&sig.as_bytes());
match signature::verify(&signature::ED25519, public_key, msg, sig) {
Ok(_) => true,
@@ -316,7 +316,7 @@ mod test {
assert_eq!(public, Public::from_raw(hex!("2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee")));
let message = hex!("2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee00000000000000000200d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a4500000000000000");
let signature = pair.sign(&message[..]);
println!("Correct signature: {}", HexDisplay::from(&signature.0));
println!("Correct signature: {}", HexDisplay::from(&signature.as_bytes()));
assert!(verify_strong(&signature, &message[..], &public));
}
+32 -5
View File
@@ -35,7 +35,7 @@ macro_rules! impl_rest {
impl<'de> Deserialize<'de> for $name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
bytes::deserialize_check_len(deserializer, bytes::ExpectedLen::Exact($len))
.map(|x| (&*x).into())
.map(|x| $name::from_slice(&x))
}
}
@@ -49,16 +49,43 @@ macro_rules! impl_rest {
<[u8; $len] as ::codec::Decode>::decode(input).map($name)
}
}
#[cfg(feature = "std")]
impl From<u64> for $name {
fn from(val: u64) -> Self {
Self::from_low_u64_be(val)
}
}
}
}
construct_hash!(H160, 20);
construct_hash!(H256, 32);
construct_hash!(H512, 64);
construct_fixed_hash!{
/// Fixed-size uninterpreted hash type with 20 bytes (160 bits) size.
pub struct H160(20);
}
construct_fixed_hash!{
/// Fixed-size uninterpreted hash type with 32 bytes (256 bits) size.
pub struct H256(32);
}
construct_fixed_hash!{
/// Fixed-size uninterpreted hash type with 64 bytes (512 bits) size.
pub struct H512(64);
}
impl_rest!(H160, 20);
impl_rest!(H256, 32);
impl_rest!(H512, 64);
/// Hash conversion. Used to convert between unbound associated hash types in traits,
/// implemented by the same hash type.
/// Panics if used to convert between different hash types.
pub fn convert_hash<H1: Default + AsMut<[u8]>, H2: AsRef<[u8]>>(src: &H2) -> H1 {
let mut dest = H1::default();
assert_eq!(dest.as_mut().len(), src.as_ref().len());
dest.as_mut().copy_from_slice(src.as_ref());
dest
}
#[cfg(test)]
mod tests {
use super::*;
@@ -113,7 +140,7 @@ mod tests {
#[test]
fn test_heapsizeof() {
use heapsize::HeapSizeOf;
let h = H256::new();
let h = H256::zero();
assert_eq!(h.heap_size_of_children(), 0);
}
}
+24 -3
View File
@@ -14,9 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
// tag::description[]
//! Shareable Substrate types.
// end::description[]
#![warn(missing_docs)]
@@ -109,7 +107,7 @@ mod changes_trie;
#[cfg(test)]
mod tests;
pub use self::hash::{H160, H256, H512};
pub use self::hash::{H160, H256, H512, convert_hash};
pub use self::uint::U256;
pub use authority_id::AuthorityId;
pub use changes_trie::ChangesTrieConfiguration;
@@ -131,7 +129,30 @@ impl From<Vec<u8>> for Bytes {
fn from(s: Vec<u8>) -> Self { Bytes(s) }
}
impl From<OpaqueMetadata> for Bytes {
fn from(s: OpaqueMetadata) -> Self { Bytes(s.0) }
}
impl Deref for Bytes {
type Target = [u8];
fn deref(&self) -> &[u8] { &self.0[..] }
}
/// Stores the encoded `RuntimeMetadata` for the native side as opaque type.
#[derive(Encode, Decode)]
pub struct OpaqueMetadata(Vec<u8>);
impl OpaqueMetadata {
/// Creates a new instance with the given metadata blob.
pub fn new(metadata: Vec<u8>) -> Self {
OpaqueMetadata(metadata)
}
}
impl rstd::ops::Deref for OpaqueMetadata {
type Target = Vec<u8>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
-14
View File
@@ -1,14 +0,0 @@
= RPC Server
.Summary
[source, toml]
----
include::Cargo.toml[lines=2..5]
----
.Description
----
include::src/lib.rs[tag=description]
----
+5 -7
View File
@@ -14,9 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
// tag::description[]
//! Substrate RPC servers.
// end::description[]
#[warn(missing_docs)]
@@ -32,7 +30,7 @@ extern crate sr_primitives;
extern crate log;
use std::io;
use sr_primitives::traits::{Block as BlockT, NumberFor};
use sr_primitives::{traits::{Block as BlockT, NumberFor}, generic::SignedBlock};
type Metadata = apis::metadata::Metadata;
type RpcHandler = pubsub::PubSubHandler<Metadata>;
@@ -40,7 +38,7 @@ pub type HttpServer = http::Server;
pub type WsServer = ws::Server;
/// Construct rpc `IoHandler`
pub fn rpc_handler<Block: BlockT, ExHash, PendingExtrinsics, S, C, A, Y>(
pub fn rpc_handler<Block: BlockT, ExHash, S, C, A, Y>(
state: S,
chain: C,
author: A,
@@ -48,10 +46,10 @@ pub fn rpc_handler<Block: BlockT, ExHash, PendingExtrinsics, S, C, A, Y>(
) -> RpcHandler where
Block: BlockT + 'static,
ExHash: Send + Sync + 'static + sr_primitives::Serialize + sr_primitives::DeserializeOwned,
PendingExtrinsics: serde::Serialize + serde::de::DeserializeOwned + Send + Sync + 'static,
SignedBlock<Block>: serde::Serialize + sr_primitives::DeserializeOwned,
S: apis::state::StateApi<Block::Hash, Metadata=Metadata>,
C: apis::chain::ChainApi<Block::Hash, Block::Header, NumberFor<Block>, Block::Extrinsic, Metadata=Metadata>,
A: apis::author::AuthorApi<ExHash, Block::Hash, Block::Extrinsic, PendingExtrinsics, Metadata=Metadata>,
C: apis::chain::ChainApi<Block::Hash, Block::Header, NumberFor<Block>, SignedBlock<Block>, Metadata=Metadata>,
A: apis::author::AuthorApi<ExHash, Block::Hash, Metadata=Metadata>,
Y: apis::system::SystemApi,
{
let mut io = pubsub::PubSubHandler::default();
+1
View File
@@ -11,6 +11,7 @@ jsonrpc-pubsub = { git="https://github.com/paritytech/jsonrpc.git" }
log = "0.4"
parking_lot = "0.4"
parity-codec = "2.1"
serde_json = "1.0"
substrate-client = { path = "../client" }
substrate-executor = { path = "../executor" }
substrate-transaction-pool = { path = "../transaction-pool" }
-13
View File
@@ -1,13 +0,0 @@
= RPC
.Summary
[source, toml]
----
include::Cargo.toml[lines=2..5]
----
.Description
----
include::src/lib.rs[tag=description]
----
+13 -24
View File
@@ -19,13 +19,12 @@
use std::sync::Arc;
use client::{self, Client};
use codec::Decode;
use codec::{Encode, Decode};
use transaction_pool::{
txpool::{
ChainApi as PoolChainApi,
BlockHash,
ExHash,
ExtrinsicFor,
IntoPoolError,
Pool,
watcher::Status,
@@ -47,19 +46,16 @@ use self::error::Result;
build_rpc_trait! {
/// Substrate authoring RPC API
pub trait AuthorApi<Hash, BlockHash, Extrinsic, PendingExtrinsics> {
pub trait AuthorApi<Hash, BlockHash> {
type Metadata;
/// Submit extrinsic for inclusion in block.
#[rpc(name = "author_submitRichExtrinsic")]
fn submit_rich_extrinsic(&self, Extrinsic) -> Result<Hash>;
/// Submit hex-encoded extrinsic for inclusion in block.
#[rpc(name = "author_submitExtrinsic")]
fn submit_extrinsic(&self, Bytes) -> Result<Hash>;
/// Returns all pending extrinsics, potentially grouped by sender.
#[rpc(name = "author_pendingExtrinsics")]
fn pending_extrinsics(&self) -> Result<PendingExtrinsics>;
fn pending_extrinsics(&self) -> Result<Vec<Bytes>>;
#[pubsub(name = "author_extrinsicUpdate")] {
/// Submit an extrinsic to watch.
@@ -75,23 +71,19 @@ build_rpc_trait! {
}
/// Authoring API
pub struct Author<B, E, P> where
P: PoolChainApi + Sync + Send + 'static,
{
pub struct Author<B, E, P, RA> where P: PoolChainApi + Sync + Send + 'static {
/// Substrate client
client: Arc<Client<B, E, <P as PoolChainApi>::Block>>,
client: Arc<Client<B, E, <P as PoolChainApi>::Block, RA>>,
/// Extrinsic pool
pool: Arc<Pool<P>>,
/// Subscriptions manager
subscriptions: Subscriptions,
}
impl<B, E, P> Author<B, E, P> where
P: PoolChainApi + Sync + Send + 'static,
{
impl<B, E, P, RA> Author<B, E, P, RA> where P: PoolChainApi + Sync + Send + 'static {
/// Create new instance of Authoring API.
pub fn new(
client: Arc<Client<B, E, <P as PoolChainApi>::Block>>,
client: Arc<Client<B, E, <P as PoolChainApi>::Block, RA>>,
pool: Arc<Pool<P>>,
subscriptions: Subscriptions,
) -> Self {
@@ -103,21 +95,18 @@ impl<B, E, P> Author<B, E, P> where
}
}
impl<B, E, P> AuthorApi<ExHash<P>, BlockHash<P>, ExtrinsicFor<P>, Vec<ExtrinsicFor<P>>> for Author<B, E, P> where
impl<B, E, P, RA> AuthorApi<ExHash<P>, BlockHash<P>> for Author<B, E, P, RA> where
B: client::backend::Backend<<P as PoolChainApi>::Block, Blake2Hasher> + Send + Sync + 'static,
E: client::CallExecutor<<P as PoolChainApi>::Block, Blake2Hasher> + Send + Sync + 'static,
P: PoolChainApi + Sync + Send + 'static,
P::Block: traits::Block<Hash=H256>,
P::Error: 'static,
RA: Send + Sync + 'static
{
type Metadata = ::metadata::Metadata;
fn submit_extrinsic(&self, xt: Bytes) -> Result<ExHash<P>> {
let dxt = Decode::decode(&mut &xt[..]).ok_or(error::Error::from(error::ErrorKind::BadFormat))?;
self.submit_rich_extrinsic(dxt)
}
fn submit_rich_extrinsic(&self, xt: <<P as PoolChainApi>::Block as traits::Block>::Extrinsic) -> Result<ExHash<P>> {
fn submit_extrinsic(&self, ext: Bytes) -> Result<ExHash<P>> {
let xt = Decode::decode(&mut &ext[..]).ok_or(error::Error::from(error::ErrorKind::BadFormat))?;
let best_block_hash = self.client.info()?.chain.best_hash;
self.pool
.submit_one(&generic::BlockId::hash(best_block_hash), xt)
@@ -127,8 +116,8 @@ impl<B, E, P> AuthorApi<ExHash<P>, BlockHash<P>, ExtrinsicFor<P>, Vec<ExtrinsicF
)
}
fn pending_extrinsics(&self) -> Result<Vec<ExtrinsicFor<P>>> {
Ok(self.pool.ready().map(|tx| tx.data.clone()).collect())
fn pending_extrinsics(&self) -> Result<Vec<Bytes>> {
Ok(self.pool.ready().map(|tx| tx.data.encode().into()).collect())
}
fn watch_extrinsic(&self, _metadata: Self::Metadata, subscriber: pubsub::Subscriber<Status<ExHash<P>, BlockHash<P>>>, xt: Bytes) {
+7 -7
View File
@@ -35,7 +35,7 @@ fn uxt(sender: Keyring, nonce: u64) -> Extrinsic {
from: sender.to_raw_public().into(),
to: Default::default(),
};
let signature = Keyring::from_raw_public(tx.from.0).unwrap().sign(&tx.encode()).into();
let signature = Keyring::from_raw_public(tx.from.to_fixed_bytes()).unwrap().sign(&tx.encode()).into();
Extrinsic { transfer: tx, signature }
}
@@ -71,11 +71,11 @@ fn submit_rich_transaction_should_not_cause_error() {
let h: H256 = hex!("fccc48291473c53746cd267cf848449edd7711ee6511fba96919d5f9f4859e4f").into();
assert_matches!(
AuthorApi::submit_rich_extrinsic(&p, uxt(Keyring::Alice, 0)),
AuthorApi::submit_extrinsic(&p, uxt(Keyring::Alice, 0).encode().into()),
Ok(h2) if h == h2
);
assert!(
AuthorApi::submit_rich_extrinsic(&p, uxt(Keyring::Alice, 0)).is_err()
AuthorApi::submit_extrinsic(&p, uxt(Keyring::Alice, 0).encode().into()).is_err()
);
}
@@ -105,10 +105,10 @@ fn should_watch_extrinsic() {
from: Keyring::Alice.to_raw_public().into(),
to: Default::default(),
};
let signature = Keyring::from_raw_public(tx.from.0).unwrap().sign(&tx.encode()).into();
let signature = Keyring::from_raw_public(tx.from.to_fixed_bytes()).unwrap().sign(&tx.encode()).into();
Extrinsic { transfer: tx, signature }
};
AuthorApi::submit_rich_extrinsic(&p, replacement).unwrap();
AuthorApi::submit_extrinsic(&p, replacement.encode().into()).unwrap();
let (res, data) = runtime.block_on(data.into_future()).unwrap();
assert_eq!(
res,
@@ -131,9 +131,9 @@ fn should_return_pending_extrinsics() {
subscriptions: Subscriptions::new(runtime.executor()),
};
let ex = uxt(Keyring::Alice, 0);
AuthorApi::submit_rich_extrinsic(&p, ex.clone()).unwrap();
AuthorApi::submit_extrinsic(&p, ex.encode().into()).unwrap();
assert_matches!(
p.pending_extrinsics(),
Ok(ref expected) if expected == &vec![ex]
Ok(ref expected) if *expected == vec![Bytes(ex.encode())]
);
}
+94 -43
View File
@@ -39,7 +39,7 @@ use self::error::Result;
build_rpc_trait! {
/// Substrate blockchain API
pub trait ChainApi<Hash, Header, Number, Extrinsic> {
pub trait ChainApi<Hash, Header, Number, SignedBlock> {
type Metadata;
/// Get header of a relay chain block.
@@ -48,7 +48,7 @@ build_rpc_trait! {
/// Get header and body of a relay chain block.
#[rpc(name = "chain_getBlock")]
fn block(&self, Trailing<Hash>) -> Result<Option<SignedBlock<Header, Extrinsic>>>;
fn block(&self, Trailing<Hash>) -> Result<Option<SignedBlock>>;
/// Get hash of the n-th block in the canon chain.
///
@@ -56,6 +56,10 @@ build_rpc_trait! {
#[rpc(name = "chain_getBlockHash", alias = ["chain_getHead", ])]
fn block_hash(&self, Trailing<Number>) -> Result<Option<Hash>>;
/// Get hash of the last finalised block in the canon chain.
#[rpc(name = "chain_getFinalisedHead")]
fn finalised_head(&self) -> Result<Hash>;
/// Get the runtime version.
#[rpc(name = "chain_getRuntimeVersion")]
fn runtime_version(&self, Trailing<Hash>) -> Result<RuntimeVersion>;
@@ -70,6 +74,16 @@ build_rpc_trait! {
fn unsubscribe_new_head(&self, SubscriptionId) -> RpcResult<bool>;
}
#[pubsub(name = "chain_finalisedHead")] {
/// New head subscription
#[rpc(name = "chain_subscribeFinalisedHeads")]
fn subscribe_finalised_heads(&self, Self::Metadata, pubsub::Subscriber<Header>);
/// Unsubscribe from new head subscription.
#[rpc(name = "chain_unsubscribeFinalisedHeads")]
fn unsubscribe_finalised_heads(&self, SubscriptionId) -> RpcResult<bool>;
}
#[pubsub(name = "chain_runtimeVersion")] {
/// New runtime version subscription
#[rpc(name = "chain_subscribeRuntimeVersion")]
@@ -83,16 +97,16 @@ build_rpc_trait! {
}
/// Chain API with subscriptions support.
pub struct Chain<B, E, Block: BlockT> {
pub struct Chain<B, E, Block: BlockT, RA> {
/// Substrate client.
client: Arc<Client<B, E, Block>>,
client: Arc<Client<B, E, Block, RA>>,
/// Current subscriptions.
subscriptions: Subscriptions,
}
impl<B, E, Block: BlockT> Chain<B, E, Block> {
impl<B, E, Block: BlockT, RA> Chain<B, E, Block, RA> {
/// Create new Chain API RPC handler.
pub fn new(client: Arc<Client<B, E, Block>>, subscriptions: Subscriptions) -> Self {
pub fn new(client: Arc<Client<B, E, Block, RA>>, subscriptions: Subscriptions) -> Self {
Self {
client,
subscriptions,
@@ -100,10 +114,11 @@ impl<B, E, Block: BlockT> Chain<B, E, Block> {
}
}
impl<B, E, Block> Chain<B, E, Block> where
impl<B, E, Block, RA> Chain<B, E, Block, RA> where
Block: BlockT<Hash=H256> + 'static,
B: client::backend::Backend<Block, Blake2Hasher> + Send + Sync + 'static,
E: client::CallExecutor<Block, Blake2Hasher> + Send + Sync + 'static,
RA: Send + Sync + 'static
{
fn unwrap_or_best(&self, hash: Trailing<Block::Hash>) -> Result<Block::Hash> {
Ok(match hash.into() {
@@ -111,43 +126,21 @@ impl<B, E, Block> Chain<B, E, Block> where
Some(hash) => hash,
})
}
}
impl<B, E, Block> ChainApi<Block::Hash, Block::Header, NumberFor<Block>, Block::Extrinsic> for Chain<B, E, Block> where
Block: BlockT<Hash=H256> + 'static,
B: client::backend::Backend<Block, Blake2Hasher> + Send + Sync + 'static,
E: client::CallExecutor<Block, Blake2Hasher> + Send + Sync + 'static,
{
type Metadata = ::metadata::Metadata;
fn header(&self, hash: Trailing<Block::Hash>) -> Result<Option<Block::Header>> {
let hash = self.unwrap_or_best(hash)?;
Ok(self.client.header(&BlockId::Hash(hash))?)
}
fn block(&self, hash: Trailing<Block::Hash>)
-> Result<Option<SignedBlock<Block::Header, Block::Extrinsic>>>
fn subscribe_headers<F, G, S, ERR>(
&self,
subscriber: pubsub::Subscriber<Block::Header>,
best_block_hash: G,
stream: F,
) where
F: FnOnce() -> S,
G: FnOnce() -> Result<Option<Block::Hash>>,
ERR: ::std::fmt::Debug,
S: Stream<Item=Block::Header, Error=ERR> + Send + 'static,
{
let hash = self.unwrap_or_best(hash)?;
Ok(self.client.block(&BlockId::Hash(hash))?)
}
fn block_hash(&self, number: Trailing<NumberFor<Block>>) -> Result<Option<Block::Hash>> {
Ok(match number.into() {
None => Some(self.client.info()?.chain.best_hash),
Some(number) => self.client.header(&BlockId::number(number))?.map(|h| h.hash()),
})
}
fn runtime_version(&self, at: Trailing<Block::Hash>) -> Result<RuntimeVersion> {
let at = self.unwrap_or_best(at)?;
Ok(self.client.runtime_version_at(&BlockId::Hash(at))?)
}
fn subscribe_new_head(&self, _metadata: Self::Metadata, subscriber: pubsub::Subscriber<Block::Header>) {
self.subscriptions.add(subscriber, |sink| {
// send current head right at the start.
let header = self.block_hash(None.into())
let header = best_block_hash()
.and_then(|hash| self.header(hash.into()))
.and_then(|header| {
header.ok_or_else(|| self::error::ErrorKind::Unimplemented.into())
@@ -155,9 +148,8 @@ impl<B, E, Block> ChainApi<Block::Hash, Block::Header, NumberFor<Block>, Block::
.map_err(Into::into);
// send further subscriptions
let stream = self.client.import_notification_stream()
.filter(|notification| notification.is_new_best)
.map(|notification| Ok(notification.header))
let stream = stream()
.map(|res| Ok(res))
.map_err(|e| warn!("Block notification stream error: {:?}", e));
sink
@@ -170,11 +162,70 @@ impl<B, E, Block> ChainApi<Block::Hash, Block::Header, NumberFor<Block>, Block::
.map(|_| ())
});
}
}
impl<B, E, Block, RA> ChainApi<Block::Hash, Block::Header, NumberFor<Block>, SignedBlock<Block>> for Chain<B, E, Block, RA> where
Block: BlockT<Hash=H256> + 'static,
B: client::backend::Backend<Block, Blake2Hasher> + Send + Sync + 'static,
E: client::CallExecutor<Block, Blake2Hasher> + Send + Sync + 'static,
RA: Send + Sync + 'static
{
type Metadata = ::metadata::Metadata;
fn header(&self, hash: Trailing<Block::Hash>) -> Result<Option<Block::Header>> {
let hash = self.unwrap_or_best(hash)?;
Ok(self.client.header(&BlockId::Hash(hash))?)
}
fn block(&self, hash: Trailing<Block::Hash>)
-> Result<Option<SignedBlock<Block>>>
{
let hash = self.unwrap_or_best(hash)?;
Ok(self.client.block(&BlockId::Hash(hash))?)
}
fn block_hash(&self, number: Trailing<NumberFor<Block>>) -> Result<Option<Block::Hash>> {
Ok(match number.into() {
None => Some(self.client.info()?.chain.best_hash),
Some(number) => self.client.header(&BlockId::number(number))?.map(|h| h.hash()),
})
}
fn finalised_head(&self) -> Result<Block::Hash> {
Ok(self.client.info()?.chain.finalized_hash)
}
fn runtime_version(&self, at: Trailing<Block::Hash>) -> Result<RuntimeVersion> {
let at = self.unwrap_or_best(at)?;
Ok(self.client.runtime_version_at(&BlockId::Hash(at))?)
}
fn subscribe_new_head(&self, _metadata: Self::Metadata, subscriber: pubsub::Subscriber<Block::Header>) {
self.subscribe_headers(
subscriber,
|| self.block_hash(None.into()),
|| self.client.import_notification_stream()
.filter(|notification| notification.is_new_best)
.map(|notification| notification.header),
)
}
fn unsubscribe_new_head(&self, id: SubscriptionId) -> RpcResult<bool> {
Ok(self.subscriptions.cancel(id))
}
fn subscribe_finalised_heads(&self, _meta: Self::Metadata, subscriber: pubsub::Subscriber<Block::Header>) {
self.subscribe_headers(
subscriber,
|| Ok(Some(self.client.info()?.chain.finalized_hash)),
|| self.client.finality_notification_stream()
.map(|notification| notification.header),
)
}
fn unsubscribe_finalised_heads(&self, id: SubscriptionId) -> RpcResult<bool> {
Ok(self.subscriptions.cancel(id))
}
fn subscribe_runtime_version(&self, _meta: Self::Metadata, subscriber: pubsub::Subscriber<RuntimeVersion>) {
let stream = match self.client.storage_changes_notification_stream(Some(&[storage::StorageKey(storage::well_known_keys::CODE.to_vec())])) {
+68 -4
View File
@@ -36,7 +36,7 @@ fn should_return_header() {
parent_hash: 0.into(),
number: 0,
state_root: x.state_root.clone(),
extrinsics_root: "03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314".into(),
extrinsics_root: "03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314".parse().unwrap(),
digest: Default::default(),
}
);
@@ -47,7 +47,7 @@ fn should_return_header() {
parent_hash: 0.into(),
number: 0,
state_root: x.state_root.clone(),
extrinsics_root: "03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314".into(),
extrinsics_root: "03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314".parse().unwrap(),
digest: Default::default(),
}
);
@@ -86,7 +86,7 @@ fn should_return_a_block() {
parent_hash: api.client.genesis_hash(),
number: 1,
state_root: x.block.header.state_root.clone(),
extrinsics_root: "03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314".into(),
extrinsics_root: "03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314".parse().unwrap(),
digest: Default::default(),
},
extrinsics: vec![],
@@ -100,7 +100,7 @@ fn should_return_a_block() {
parent_hash: api.client.genesis_hash(),
number: 1,
state_root: x.block.header.state_root.clone(),
extrinsics_root: "03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314".into(),
extrinsics_root: "03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314".parse().unwrap(),
digest: Default::default(),
},
extrinsics: vec![],
@@ -150,7 +150,39 @@ fn should_return_block_hash() {
client.block_hash(Some(1u64).into()),
Ok(Some(ref x)) if x == &block.hash()
);
}
#[test]
fn should_return_finalised_hash() {
let core = ::tokio::runtime::Runtime::new().unwrap();
let remote = core.executor();
let client = Chain {
client: Arc::new(test_client::new()),
subscriptions: Subscriptions::new(remote),
};
assert_matches!(
client.finalised_head(),
Ok(ref x) if x == &client.client.genesis_hash()
);
// import new block
let builder = client.client.new_block().unwrap();
client.client.justify_and_import(BlockOrigin::Own, builder.bake().unwrap()).unwrap();
// no finalisation yet
assert_matches!(
client.finalised_head(),
Ok(ref x) if x == &client.client.genesis_hash()
);
// finalise
client.client.finalize_block(BlockId::number(1), true).unwrap();
assert_matches!(
client.finalised_head(),
Ok(ref x) if x == &client.client.block_hash(1).unwrap().unwrap()
);
}
#[test]
@@ -184,6 +216,38 @@ fn should_notify_about_latest_block() {
assert_eq!(core.block_on(next.into_future()).unwrap().0, None);
}
#[test]
fn should_notify_about_finalised_block() {
let mut core = ::tokio::runtime::Runtime::new().unwrap();
let remote = core.executor();
let (subscriber, id, transport) = pubsub::Subscriber::new_test("test");
{
let api = Chain {
client: Arc::new(test_client::new()),
subscriptions: Subscriptions::new(remote),
};
api.subscribe_finalised_heads(Default::default(), subscriber);
// assert id assigned
assert_eq!(core.block_on(id), Ok(Ok(SubscriptionId::Number(1))));
let builder = api.client.new_block().unwrap();
api.client.justify_and_import(BlockOrigin::Own, builder.bake().unwrap()).unwrap();
api.client.finalize_block(BlockId::number(1), true).unwrap();
}
// assert initial head sent.
let (notification, next) = core.block_on(transport.into_future()).unwrap();
assert!(notification.is_some());
// assert notification sent to transport
let (notification, next) = core.block_on(next.into_future()).unwrap();
assert!(notification.is_some());
// no more notifications on this channel
assert_eq!(core.block_on(next.into_future()).unwrap().0, None);
}
#[test]
fn should_return_runtime_version() {
let core = ::tokio::runtime::Runtime::new().unwrap();
-2
View File
@@ -14,9 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
// tag::description[]
//! Substrate RPC interfaces.
// end::description[]
#![warn(missing_docs)]
+10 -9
View File
@@ -32,7 +32,7 @@ use primitives::{Blake2Hasher, Bytes};
use rpc::Result as RpcResult;
use rpc::futures::{stream, Future, Sink, Stream};
use runtime_primitives::generic::BlockId;
use runtime_primitives::traits::{Block as BlockT, Header};
use runtime_primitives::traits::{Block as BlockT, Header, ProvideRuntimeApi};
use subscriptions::Subscriptions;
@@ -87,16 +87,16 @@ build_rpc_trait! {
}
/// State API with subscriptions support.
pub struct State<B, E, Block: BlockT> {
pub struct State<B, E, Block: BlockT, RA> {
/// Substrate client.
client: Arc<Client<B, E, Block>>,
client: Arc<Client<B, E, Block, RA>>,
/// Current subscriptions.
subscriptions: Subscriptions,
}
impl<B, E, Block: BlockT> State<B, E, Block> {
impl<B, E, Block: BlockT, RA> State<B, E, Block, RA> {
/// Create new State API RPC handler.
pub fn new(client: Arc<Client<B, E, Block>>, subscriptions: Subscriptions) -> Self {
pub fn new(client: Arc<Client<B, E, Block, RA>>, subscriptions: Subscriptions) -> Self {
Self {
client,
subscriptions,
@@ -104,7 +104,7 @@ impl<B, E, Block: BlockT> State<B, E, Block> {
}
}
impl<B, E, Block> State<B, E, Block> where
impl<B, E, Block, RA> State<B, E, Block, RA> where
Block: BlockT<Hash=H256>,
B: client::backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher>,
@@ -114,10 +114,11 @@ impl<B, E, Block> State<B, E, Block> where
}
}
impl<B, E, Block> StateApi<Block::Hash> for State<B, E, Block> where
impl<B, E, Block, RA> StateApi<Block::Hash> for State<B, E, Block, RA> where
Block: BlockT<Hash=H256> + 'static,
B: client::backend::Backend<Block, Blake2Hasher> + Send + Sync + 'static,
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + 'static,
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + 'static + Clone,
RA: Metadata<Block>
{
type Metadata = ::metadata::Metadata;
@@ -151,7 +152,7 @@ impl<B, E, Block> StateApi<Block::Hash> for State<B, E, Block> where
fn metadata(&self, block: Trailing<Block::Hash>) -> Result<Bytes> {
let block = self.unwrap_or_best(block)?;
self.client.metadata(&BlockId::Hash(block)).map(Bytes).map_err(Into::into)
self.client.runtime_api().metadata(&BlockId::Hash(block)).map(Into::into).map_err(Into::into)
}
fn query_storage(&self, keys: Vec<StorageKey>, from: Block::Hash, to: Trailing<Block::Hash>) -> Result<Vec<StorageChangeSet<Block::Hash>>> {
+4
View File
@@ -37,5 +37,9 @@ build_rpc_trait! {
/// Get the chain's type. Given as a string identifier.
#[rpc(name = "system_chain")]
fn system_chain(&self) -> Result<String>;
/// Get a custom set of properties as a JSON object, defined in the chain spec.
#[rpc(name = "system_properties")]
fn system_properties(&self) -> Result<serde_json::map::Map<String, serde_json::Value>>;
}
}
+11
View File
@@ -27,6 +27,9 @@ impl SystemApi for () {
fn system_chain(&self) -> Result<String> {
Ok("testchain".into())
}
fn system_properties(&self) -> Result<serde_json::map::Map<String, serde_json::Value>> {
Ok(serde_json::map::Map::new())
}
}
#[test]
@@ -52,3 +55,11 @@ fn system_chain_works() {
"testchain".to_owned()
);
}
#[test]
fn system_properties_works() {
assert_eq!(
SystemApi::system_properties(&()).unwrap(),
serde_json::map::Map::new()
);
}
-14
View File
@@ -1,14 +0,0 @@
= Serializer
.Summary
[source, toml]
----
include::Cargo.toml[lines=2..5]
----
.Description
----
include::src/lib.rs[tag=description]
----
-2
View File
@@ -14,12 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
// tag::description[]
//! Substrate customizable serde serializer.
//!
//! The idea is that we can later change the implementation
//! to something more compact, but for now we're using JSON.
// end::description[]
#![warn(missing_docs)]
-14
View File
@@ -1,14 +0,0 @@
= Service
.Summary
[source, toml]
----
include::Cargo.toml[lines=2..5]
----
.Description
----
include::src/lib.rs[tag=description]
----
+7 -5
View File
@@ -18,7 +18,6 @@
use std::{self, io::{Read, Write}};
use futures::Future;
use serde_json;
use runtime_primitives::generic::{SignedBlock, BlockId};
use runtime_primitives::traits::{As, Block, Header};
@@ -34,7 +33,10 @@ use chain_spec::ChainSpec;
/// Export a range of blocks to a binary stream.
pub fn export_blocks<F, E, W>(config: FactoryFullConfiguration<F>, exit: E, mut output: W, from: FactoryBlockNumber<F>, to: Option<FactoryBlockNumber<F>>, json: bool) -> error::Result<()>
where F: ServiceFactory, E: Future<Item=(),Error=()> + Send + 'static, W: Write,
where
F: ServiceFactory,
E: Future<Item=(),Error=()> + Send + 'static,
W: Write,
{
let client = new_client::<F>(&config)?;
let mut block = from;
@@ -108,14 +110,14 @@ pub fn import_blocks<F, E, R>(config: FactoryFullConfiguration<F>, exit: E, mut
if exit_recv.try_recv().is_ok() {
break;
}
if let Some(signed) = SignedBlock::<<F::Block as Block>::Header, <F::Block as Block>::Extrinsic>::decode(&mut input) {
let header = signed.block.header;
if let Some(signed) = SignedBlock::<F::Block>::decode(&mut input) {
let (header, extrinsics) = signed.block.deconstruct();
let hash = header.hash();
let block = message::BlockData::<F::Block> {
hash: hash,
justification: Some(signed.justification),
header: Some(header),
body: Some(signed.block.extrinsics),
body: Some(extrinsics),
receipt: None,
message_queue: None
};
+11
View File
@@ -88,8 +88,12 @@ struct ChainSpecFile {
pub telemetry_url: Option<String>,
pub protocol_id: Option<String>,
pub consensus_engine: Option<String>,
pub properties: Option<Properties>,
}
/// Arbitrary properties defined in chain spec as a JSON object
pub type Properties = json::map::Map<String, json::Value>;
/// A configuration of a chain. Can be used to build a genesis block.
pub struct ChainSpec<G: RuntimeGenesis> {
spec: ChainSpecFile,
@@ -130,6 +134,11 @@ impl<G: RuntimeGenesis> ChainSpec<G> {
self.spec.consensus_engine.as_ref().map(String::as_str)
}
pub fn properties(&self) -> Properties {
// Return an empty JSON object if 'properties' not defined in config
self.spec.properties.as_ref().unwrap_or(&json::map::Map::new()).clone()
}
/// Parse json content into a `ChainSpec`
pub fn from_embedded(json: &'static [u8]) -> Result<Self, String> {
let spec = json::from_slice(json).map_err(|e| format!("Error parsing spec file: {}", e))?;
@@ -158,6 +167,7 @@ impl<G: RuntimeGenesis> ChainSpec<G> {
telemetry_url: Option<&str>,
protocol_id: Option<&str>,
consensus_engine: Option<&str>,
properties: Option<Properties>,
) -> Self
{
let spec = ChainSpecFile {
@@ -167,6 +177,7 @@ impl<G: RuntimeGenesis> ChainSpec<G> {
telemetry_url: telemetry_url.map(str::to_owned),
protocol_id: protocol_id.map(str::to_owned),
consensus_engine: consensus_engine.map(str::to_owned),
properties,
};
ChainSpec {
spec,
+187 -23
View File
@@ -16,21 +16,20 @@
//! Substrate service components.
use std::sync::Arc;
use std::marker::PhantomData;
use std::ops::Deref;
use std::{sync::Arc, net::SocketAddr, marker::PhantomData, ops::Deref};
use serde::{Serialize, de::DeserializeOwned};
use tokio::runtime::TaskExecutor;
use chain_spec::ChainSpec;
use chain_spec::{ChainSpec, Properties};
use client_db;
use client::{self, Client};
use {error, Service};
use client::{self, Client, runtime_api::{TaggedTransactionQueue, Metadata}};
use {error, Service, RpcConfig, maybe_start_server, TransactionPoolAdapter};
use network::{self, OnDemand, import_queue::ImportQueue};
use substrate_executor::{NativeExecutor, NativeExecutionDispatch};
use transaction_pool::txpool::{self, Options as TransactionPoolOptions, Pool as TransactionPool};
use runtime_primitives::{traits::Block as BlockT, traits::Header as HeaderT, BuildStorage};
use runtime_primitives::{traits::Block as BlockT, traits::Header as HeaderT, BuildStorage, generic::SignedBlock};
use config::Configuration;
use primitives::{H256, Blake2Hasher};
use primitives::{Blake2Hasher, H256};
use rpc;
// Type aliases.
// These exist mainly to avoid typing `<F as Factory>::Foo` all over the code.
@@ -70,10 +69,10 @@ pub type LightExecutor<F> = client::light::call_executor::RemoteCallExecutor<
>;
/// Full client type for a factory.
pub type FullClient<F> = Client<FullBackend<F>, FullExecutor<F>, <F as ServiceFactory>::Block>;
pub type FullClient<F> = Client<FullBackend<F>, FullExecutor<F>, <F as ServiceFactory>::Block, <F as ServiceFactory>::RuntimeApi>;
/// Light client type for a factory.
pub type LightClient<F> = Client<LightBackend<F>, LightExecutor<F>, <F as ServiceFactory>::Block>;
pub type LightClient<F> = Client<LightBackend<F>, LightExecutor<F>, <F as ServiceFactory>::Block, <F as ServiceFactory>::RuntimeApi>;
/// `ChainSpec` specialization for a factory.
pub type FactoryChainSpec<F> = ChainSpec<<F as ServiceFactory>::Genesis>;
@@ -97,7 +96,8 @@ pub type FactoryFullConfiguration<F> = Configuration<<F as ServiceFactory>::Conf
pub type ComponentClient<C> = Client<
<C as Components>::Backend,
<C as Components>::Executor,
FactoryBlock<<C as Components>::Factory>
FactoryBlock<<C as Components>::Factory>,
<C as Components>::RuntimeApi,
>;
/// Block type for `Components`
@@ -116,12 +116,116 @@ pub type PoolApi<C> = <C as Components>::TransactionPoolApi;
pub trait RuntimeGenesis: Serialize + DeserializeOwned + BuildStorage {}
impl<T: Serialize + DeserializeOwned + BuildStorage> RuntimeGenesis for T {}
/// Something that can start the RPC service.
pub trait StartRPC<C: Components> {
fn start_rpc(
client: Arc<Client<C::Backend, C::Executor, ComponentBlock<C>, C::RuntimeApi>>,
chain_name: String,
impl_name: &'static str,
impl_version: &'static str,
rpc_http: Option<SocketAddr>,
rpc_ws: Option<SocketAddr>,
properties: Properties,
task_executor: TaskExecutor,
transaction_pool: Arc<TransactionPool<C::TransactionPoolApi>>,
) -> Result<(Option<rpc::HttpServer>, Option<rpc::WsServer>), error::Error>;
}
impl<T: Components> StartRPC<Self> for T where
T::RuntimeApi: Metadata<ComponentBlock<T>>,
for<'de> SignedBlock<ComponentBlock<T>>: ::serde::Deserialize<'de>,
{
fn start_rpc(
client: Arc<Client<T::Backend, T::Executor, ComponentBlock<T>, T::RuntimeApi>>,
chain_name: String,
impl_name: &'static str,
impl_version: &'static str,
rpc_http: Option<SocketAddr>,
rpc_ws: Option<SocketAddr>,
properties: Properties,
task_executor: TaskExecutor,
transaction_pool: Arc<TransactionPool<T::TransactionPoolApi>>,
) -> Result<(Option<rpc::HttpServer>, Option<rpc::WsServer>), error::Error> {
let rpc_config = RpcConfig { properties, chain_name, impl_name, impl_version };
let handler = || {
let client = client.clone();
let subscriptions = rpc::apis::Subscriptions::new(task_executor.clone());
let chain = rpc::apis::chain::Chain::new(client.clone(), subscriptions.clone());
let state = rpc::apis::state::State::new(client.clone(), subscriptions.clone());
let author = rpc::apis::author::Author::new(
client.clone(), transaction_pool.clone(), subscriptions
);
rpc::rpc_handler::<ComponentBlock<T>, ComponentExHash<T>, _, _, _, _>(
state,
chain,
author,
rpc_config.clone(),
)
};
Ok((
maybe_start_server(rpc_http, |address| rpc::start_http(address, handler()))?,
maybe_start_server(rpc_ws, |address| rpc::start_ws(address, handler()))?,
))
}
}
/// Something that can create an instance of `network::Params`.
pub trait CreateNetworkParams<C: Components> {
fn create_network_params<S>(
client: Arc<Client<C::Backend, C::Executor, ComponentBlock<C>, C::RuntimeApi>>,
roles: network::config::Roles,
network_config: network::config::NetworkConfiguration,
on_demand: Option<Arc<OnDemand<FactoryBlock<C::Factory>, NetworkService<C::Factory>>>>,
transaction_pool_adapter: TransactionPoolAdapter<C>,
specialization: S,
) -> network::config::Params<ComponentBlock<C>, S, ComponentExHash<C>>;
}
impl<T: Components> CreateNetworkParams<Self> for T where
T::RuntimeApi: TaggedTransactionQueue<ComponentBlock<T>>
{
fn create_network_params<S>(
client: Arc<Client<T::Backend, T::Executor, ComponentBlock<T>, T::RuntimeApi>>,
roles: network::config::Roles,
network_config: network::config::NetworkConfiguration,
on_demand: Option<Arc<OnDemand<FactoryBlock<T::Factory>, NetworkService<T::Factory>>>>,
transaction_pool_adapter: TransactionPoolAdapter<T>,
specialization: S,
) -> network::config::Params<ComponentBlock<T>, S, ComponentExHash<T>> {
network::config::Params {
config: network::config::ProtocolConfig { roles },
network_config,
chain: client,
on_demand: on_demand.map(|d| d as Arc<network::OnDemandService<ComponentBlock<T>>>),
transaction_pool: Arc::new(transaction_pool_adapter),
specialization,
}
}
}
/// The super trait that combines all required traits a `Service` needs to implement.
pub trait ServiceTrait<C: Components>:
Deref<Target = Service<C>>
+ Send
+ Sync
+ 'static
+ StartRPC<C>
+ CreateNetworkParams<C>
{}
impl<C: Components, T> ServiceTrait<C> for T where
T: Deref<Target = Service<C>> + Send + Sync + 'static + StartRPC<C> + CreateNetworkParams<C>
{}
/// A collection of types and methods to build a service on top of the substrate service.
pub trait ServiceFactory: 'static + Sized {
/// Block type.
type Block: BlockT<Hash=H256>;
/// The type that implements the runtime API.
type RuntimeApi: Send + Sync;
/// Network protocol extensions.
type NetworkProtocol: network::specialization::Specialization<Self::Block>;
type NetworkProtocol: network::specialization::NetworkSpecialization<Self::Block>;
/// Chain runtime.
type RuntimeDispatch: NativeExecutionDispatch + Send + Sync + 'static;
/// Extrinsic pool backend type for the full client.
@@ -133,9 +237,9 @@ pub trait ServiceFactory: 'static + Sized {
/// Other configuration for service members.
type Configuration: Default;
/// Extended full service type.
type FullService: Deref<Target = Service<FullComponents<Self>>> + Send + Sync + 'static;
type FullService: ServiceTrait<FullComponents<Self>>;
/// Extended light service type.
type LightService: Deref<Target = Service<LightComponents<Self>>> + Send + Sync + 'static;
type LightService: ServiceTrait<LightComponents<Self>>;
/// ImportQueue for full client
type FullImportQueue: network::import_queue::ImportQueue<Self::Block> + 'static;
/// ImportQueue for light clients
@@ -192,7 +296,7 @@ pub trait ServiceFactory: 'static + Sized {
}
/// A collection of types and function to generalise over full / light client type.
pub trait Components: 'static {
pub trait Components: Sized + 'static {
/// Associated service factory.
type Factory: ServiceFactory;
/// Client backend.
@@ -204,6 +308,12 @@ pub trait Components: 'static {
Hash = <<Self::Factory as ServiceFactory>::Block as BlockT>::Hash,
Block = FactoryBlock<Self::Factory>
>;
/// The type that implements the runtime API.
type RuntimeApi: Send + Sync;
/// A type that can start the RPC.
type RPC: StartRPC<Self>;
/// A type that can create the network params.
type CreateNetworkParams: CreateNetworkParams<Self>;
/// Our Import Queue
type ImportQueue: ImportQueue<FactoryBlock<Self::Factory>> + 'static;
@@ -212,11 +322,13 @@ pub trait Components: 'static {
fn build_client(
config: &FactoryFullConfiguration<Self::Factory>,
executor: CodeExecutor<Self::Factory>,
)
-> Result<(
) -> Result<
(
Arc<ComponentClient<Self>>,
Option<Arc<OnDemand<FactoryBlock<Self::Factory>, NetworkService<Self::Factory>>>>
), error::Error>;
),
error::Error
>;
/// Create extrinsic pool.
fn build_transaction_pool(config: TransactionPoolOptions, client: Arc<ComponentClient<Self>>)
@@ -232,6 +344,29 @@ pub trait Components: 'static {
/// A struct that implement `Components` for the full client.
pub struct FullComponents<Factory: ServiceFactory> {
_factory: PhantomData<Factory>,
service: Service<FullComponents<Factory>>,
}
impl<Factory: ServiceFactory> FullComponents<Factory> {
pub fn new(
config: FactoryFullConfiguration<Factory>,
task_executor: TaskExecutor
) -> Result<Self, error::Error> {
Ok(
Self {
_factory: Default::default(),
service: Service::new(config, task_executor)?,
}
)
}
}
impl<Factory: ServiceFactory> Deref for FullComponents<Factory> {
type Target = Service<Self>;
fn deref(&self) -> &Self::Target {
&self.service
}
}
impl<Factory: ServiceFactory> Components for FullComponents<Factory> {
@@ -240,6 +375,9 @@ impl<Factory: ServiceFactory> Components for FullComponents<Factory> {
type Backend = FullBackend<Factory>;
type TransactionPoolApi = <Factory as ServiceFactory>::FullTransactionPoolApi;
type ImportQueue = Factory::FullImportQueue;
type RuntimeApi = Factory::RuntimeApi;
type RPC = Factory::FullService;
type CreateNetworkParams = Factory::FullService;
fn build_client(
config: &FactoryFullConfiguration<Factory>,
@@ -281,6 +419,29 @@ impl<Factory: ServiceFactory> Components for FullComponents<Factory> {
/// A struct that implement `Components` for the light client.
pub struct LightComponents<Factory: ServiceFactory> {
_factory: PhantomData<Factory>,
service: Service<LightComponents<Factory>>,
}
impl<Factory: ServiceFactory> LightComponents<Factory> {
pub fn new(
config: FactoryFullConfiguration<Factory>,
task_executor: TaskExecutor
) -> Result<Self, error::Error> {
Ok(
Self {
_factory: Default::default(),
service: Service::new(config, task_executor)?,
}
)
}
}
impl<Factory: ServiceFactory> Deref for LightComponents<Factory> {
type Target = Service<Self>;
fn deref(&self) -> &Self::Target {
&self.service
}
}
impl<Factory: ServiceFactory> Components for LightComponents<Factory> {
@@ -289,16 +450,19 @@ impl<Factory: ServiceFactory> Components for LightComponents<Factory> {
type Backend = LightBackend<Factory>;
type TransactionPoolApi = <Factory as ServiceFactory>::LightTransactionPoolApi;
type ImportQueue = <Factory as ServiceFactory>::LightImportQueue;
type RuntimeApi = Factory::RuntimeApi;
type RPC = Factory::LightService;
type CreateNetworkParams = Factory::LightService;
fn build_client(
config: &FactoryFullConfiguration<Factory>,
executor: CodeExecutor<Self::Factory>,
)
-> Result<(
Arc<ComponentClient<Self>>,
Option<Arc<OnDemand<FactoryBlock<Self::Factory>,
NetworkService<Self::Factory>>>>
), error::Error>
-> Result<
(
Arc<ComponentClient<Self>>,
Option<Arc<OnDemand<FactoryBlock<Self::Factory>, NetworkService<Self::Factory>>>>
), error::Error>
{
let db_settings = client_db::DatabaseSettings {
cache_size: None,
+1 -2
View File
@@ -20,9 +20,8 @@ use std::net::SocketAddr;
use transaction_pool;
use chain_spec::ChainSpec;
pub use client::ExecutionStrategy;
pub use network::Roles;
pub use network::NetworkConfiguration;
pub use client_db::PruningMode;
pub use network::config::{NetworkConfiguration, Roles};
use runtime_primitives::BuildStorage;
use serde::{Serialize, de::DeserializeOwned};
use target_info::Target;
+23 -24
View File
@@ -23,11 +23,11 @@ use std::time::{self, Duration, Instant};
use std;
use client::{self, error, Client as SubstrateClient, CallExecutor};
use client::runtime_api::{Core, BlockBuilder as BlockBuilderAPI, id::BLOCK_BUILDER};
use client::{block_builder::api::BlockBuilder as BlockBuilderApi, runtime_api::{id::BLOCK_BUILDER, Core}};
use codec::{Decode, Encode};
use consensus_common::{self, InherentData, evaluation, offline_tracker::OfflineTracker};
use primitives::{H256, AuthorityId, ed25519, Blake2Hasher};
use runtime_primitives::traits::{Block as BlockT, Hash as HashT, Header as HeaderT};
use runtime_primitives::traits::{Block as BlockT, Hash as HashT, Header as HeaderT, ProvideRuntimeApi};
use runtime_primitives::generic::BlockId;
use transaction_pool::txpool::{self, Pool as TransactionPool};
@@ -47,11 +47,8 @@ pub trait BlockBuilder<Block: BlockT> {
}
/// Local client abstraction for the consensus.
pub trait AuthoringApi:
Send
+ Sync
+ BlockBuilderAPI<<Self as AuthoringApi>::Block, Error=<Self as AuthoringApi>::Error>
+ Core<<Self as AuthoringApi>::Block, AuthorityId, Error=<Self as AuthoringApi>::Error>
pub trait AuthoringApi: Send + Sync + ProvideRuntimeApi where
<Self as ProvideRuntimeApi>::Api: Core<Self::Block>
{
/// The block used for this API type.
type Block: BlockT;
@@ -67,20 +64,22 @@ pub trait AuthoringApi:
) -> Result<Self::Block, error::Error>;
}
impl<'a, B, E, Block> BlockBuilder<Block> for client::block_builder::BlockBuilder<'a, B, E, Block, Blake2Hasher> where
B: client::backend::Backend<Block, Blake2Hasher> + Send + Sync + 'static,
impl<'a, B, E, Block, RA> BlockBuilder<Block> for client::block_builder::BlockBuilder<'a, Block, SubstrateClient<B, E, Block, RA>> where
B: client::backend::Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + Clone + 'static,
Block: BlockT<Hash=H256>,
RA: BlockBuilderApi<Block>,
{
fn push_extrinsic(&mut self, extrinsic: <Block as BlockT>::Extrinsic) -> Result<(), error::Error> {
client::block_builder::BlockBuilder::push(self, extrinsic).map_err(Into::into)
}
}
impl<'a, B, E, Block> AuthoringApi for SubstrateClient<B, E, Block> where
impl<B, E, Block, RA> AuthoringApi for SubstrateClient<B, E, Block, RA> where
B: client::backend::Backend<Block, Blake2Hasher> + Send + Sync + 'static,
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + Clone + 'static,
Block: BlockT<Hash=H256>,
RA: BlockBuilderApi<Block>,
{
type Block = Block;
type Error = client::error::Error;
@@ -95,7 +94,7 @@ impl<'a, B, E, Block> AuthoringApi for SubstrateClient<B, E, Block> where
let mut block_builder = self.new_block_at(at)?;
if runtime_version.has_api(BLOCK_BUILDER, 1) {
self.inherent_extrinsics(at, &inherent_data)?
self.runtime_api().inherent_extrinsics(at, &inherent_data)?
.into_iter().try_for_each(|i| block_builder.push(i))?;
}
@@ -106,10 +105,7 @@ impl<'a, B, E, Block> AuthoringApi for SubstrateClient<B, E, Block> where
}
/// Proposer factory.
pub struct ProposerFactory<C, A> where
C: AuthoringApi,
A: txpool::ChainApi,
{
pub struct ProposerFactory<C, A> where A: txpool::ChainApi {
/// The client instance.
pub client: Arc<C>,
/// The transaction pool.
@@ -122,10 +118,11 @@ pub struct ProposerFactory<C, A> where
impl<C, A> consensus_common::Environment<<C as AuthoringApi>::Block> for ProposerFactory<C, A> where
C: AuthoringApi,
<C as ProvideRuntimeApi>::Api: BlockBuilderApi<<C as AuthoringApi>::Block>,
A: txpool::ChainApi<Block=<C as AuthoringApi>::Block>,
client::error::Error: From<<C as AuthoringApi>::Error>
{
type Proposer = Proposer<C, A>;
type Proposer = Proposer<<C as AuthoringApi>::Block, C, A>;
type Error = error::Error;
fn init(
@@ -138,7 +135,7 @@ impl<C, A> consensus_common::Environment<<C as AuthoringApi>::Block> for Propose
let id = BlockId::hash(parent_hash);
let authorities: Vec<AuthorityId> = self.client.authorities(&id)?;
let authorities: Vec<AuthorityId> = self.client.runtime_api().authorities(&id)?;
self.offline.write().note_new_block(&authorities[..]);
info!("Starting consensus session on top of parent {:?}", parent_hash);
@@ -161,21 +158,23 @@ impl<C, A> consensus_common::Environment<<C as AuthoringApi>::Block> for Propose
}
/// The proposer logic.
pub struct Proposer<C: AuthoringApi, A: txpool::ChainApi> {
pub struct Proposer<Block: BlockT, C, A: txpool::ChainApi> {
client: Arc<C>,
start: Instant,
parent_hash: <<C as AuthoringApi>::Block as BlockT>::Hash,
parent_id: BlockId<<C as AuthoringApi>::Block>,
parent_number: <<<C as AuthoringApi>::Block as BlockT>::Header as HeaderT>::Number,
parent_hash: <Block as BlockT>::Hash,
parent_id: BlockId<Block>,
parent_number: <<Block as BlockT>::Header as HeaderT>::Number,
transaction_pool: Arc<TransactionPool<A>>,
offline: SharedOfflineTracker,
authorities: Vec<AuthorityId>,
minimum_timestamp: u64,
}
impl<C, A> consensus_common::Proposer<<C as AuthoringApi>::Block> for Proposer<C, A> where
C: AuthoringApi,
A: txpool::ChainApi<Block=<C as AuthoringApi>::Block>,
impl<Block, C, A> consensus_common::Proposer<<C as AuthoringApi>::Block> for Proposer<Block, C, A> where
Block: BlockT,
C: AuthoringApi<Block=Block>,
<C as ProvideRuntimeApi>::Api: BlockBuilderApi<Block>,
A: txpool::ChainApi<Block=Block>,
client::error::Error: From<<C as AuthoringApi>::Error>
{
type Create = Result<<C as AuthoringApi>::Block, error::Error>;
+28 -82
View File
@@ -14,10 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
// tag::description[]
//! Substrate service. Starts a thread that spins up the network, client, and extrinsic pool.
//! Manages communication between them.
// end::description[]
#![warn(unused_extern_crates)]
@@ -78,7 +76,7 @@ use codec::{Encode, Decode};
pub use self::error::{ErrorKind, Error};
pub use config::{Configuration, Roles, PruningMode};
pub use chain_spec::ChainSpec;
pub use chain_spec::{ChainSpec, Properties};
pub use transaction_pool::txpool::{self, Pool as TransactionPool, Options as TransactionPoolOptions, ChainApi, IntoPoolError};
pub use client::ExecutionStrategy;
@@ -89,8 +87,11 @@ pub use components::{ServiceFactory, FullBackend, FullExecutor, LightBackend,
ComponentBlock, FullClient, LightClient, FullComponents, LightComponents,
CodeExecutor, NetworkService, FactoryChainSpec, FactoryBlock,
FactoryFullConfiguration, RuntimeGenesis, FactoryGenesis,
ComponentExHash, ComponentExtrinsic, FactoryExtrinsic,
ComponentExHash, ComponentExtrinsic, FactoryExtrinsic
};
use components::{StartRPC, CreateNetworkParams};
#[doc(hidden)]
pub use network::OnDemand;
const DEFAULT_PROTOCOL_ID: &'static str = "sup";
@@ -124,8 +125,7 @@ impl<Components> Service<Components>
where
Components: components::Components,
<Components as components::Components>::Executor: std::clone::Clone,
txpool::ExHash<Components::TransactionPoolApi>: serde::de::DeserializeOwned + serde::Serialize,
txpool::ExtrinsicFor<Components::TransactionPoolApi>: serde::de::DeserializeOwned + serde::Serialize,
// <Components as components::Components>::RuntimeApi: client::runtime_api::BlockBuilder<<<Components as components::Components>::Factory as ServiceFactory>::Block, Error=client::error::Error, OverlayedChanges=client::runtime_api::OverlayedChanges> + Sync + Send + client::runtime_api::Core<<<Components as components::Components>::Factory as components::ServiceFactory>::Block, primitives::AuthorityId, Error=client::error::Error, OverlayedChanges=client::runtime_api::OverlayedChanges> + client::runtime_api::ConstructRuntimeApi<Block=<<Components as components::Components>::Factory as ServiceFactory>::Block> + client::runtime_api::Metadata<<<Components as components::Components>::Factory as components::ServiceFactory>::Block, Vec<u8>, Error=client::error::Error> + client::runtime_api::TaggedTransactionQueue<<<Components as components::Components>::Factory as components::ServiceFactory>::Block, Error=client::error::Error>,
{
/// Creates a new service.
pub fn new(
@@ -140,10 +140,12 @@ impl<Components> Service<Components>
let executor = NativeExecutor::new();
let mut keystore = Keystore::open(config.keystore_path.as_str().into())?;
// This is meant to be for testing only
// FIXME: remove this - https://github.com/paritytech/substrate/issues/1063
for seed in &config.keys {
keystore.generate_from_seed(seed)?;
}
// Keep the public key for telemetry
let public_key = match keystore.contents()?.get(0) {
Some(public_key) => public_key.clone(),
@@ -174,17 +176,10 @@ impl<Components> Service<Components>
client: client.clone(),
};
let network_params = network::Params {
config: network::ProtocolConfig {
roles: config.roles,
},
network_config: config.network,
chain: client.clone(),
on_demand: on_demand.clone()
.map(|d| d as Arc<network::OnDemandService<ComponentBlock<Components>>>),
transaction_pool: Arc::new(transaction_pool_adapter),
specialization: network_protocol,
};
let network_params = Components::CreateNetworkParams::create_network_params(
client.clone(), config.roles, config.network, on_demand.clone(),
transaction_pool_adapter, network_protocol
);
let mut protocol_id = network::ProtocolId::default();
let protocol_id_full = config.chain_spec.protocol_id().unwrap_or(DEFAULT_PROTOCOL_ID).as_bytes();
@@ -233,32 +228,13 @@ impl<Components> Service<Components>
task_executor.spawn(events);
}
// RPC
let rpc_config = RpcConfig {
chain_name: config.chain_spec.name().to_string(),
impl_name: config.impl_name,
impl_version: config.impl_version,
};
let (rpc_http, rpc_ws) = {
let handler = || {
let client = client.clone();
let subscriptions = rpc::apis::Subscriptions::new(task_executor.clone());
let chain = rpc::apis::chain::Chain::new(client.clone(), subscriptions.clone());
let state = rpc::apis::state::State::new(client.clone(), subscriptions.clone());
let author = rpc::apis::author::Author::new(client.clone(), transaction_pool.clone(), subscriptions.clone());
rpc::rpc_handler::<ComponentBlock<Components>, ComponentExHash<Components>, _, _, _, _, _>(
state,
chain,
author,
rpc_config.clone(),
)
};
(
maybe_start_server(config.rpc_http, |address| rpc::start_http(address, handler()))?,
maybe_start_server(config.rpc_ws, |address| rpc::start_ws(address, handler()))?,
)
};
// RPC
let (rpc_http, rpc_ws) = Components::RPC::start_rpc(
client.clone(), config.chain_spec.name().to_string(), config.impl_name,
config.impl_version, config.rpc_http, config.rpc_ws, config.chain_spec.properties(),
task_executor.clone(), transaction_pool.clone()
)?;
let proposer = Arc::new(ProposerFactory {
client: client.clone(),
@@ -309,9 +285,7 @@ impl<Components> Service<Components>
}
}
impl<Components> Service<Components> where
Components: components::Components,
{
impl<Components> Service<Components> where Components: components::Components {
/// Get shared client instance.
pub fn client(&self) -> Arc<ComponentClient<Components>> {
self.client.clone()
@@ -379,6 +353,7 @@ fn maybe_start_server<T, F>(address: Option<SocketAddr>, start: F) -> Result<Opt
#[derive(Clone)]
struct RpcConfig {
chain_name: String,
properties: Properties,
impl_name: &'static str,
impl_version: &'static str,
}
@@ -395,6 +370,10 @@ impl substrate_rpc::system::SystemApi for RpcConfig {
fn system_chain(&self) -> substrate_rpc::system::error::Result<String> {
Ok(self.chain_name.clone())
}
fn system_properties(&self) -> substrate_rpc::system::error::Result<Properties> {
Ok(self.properties.clone())
}
}
/// Transaction pool adapter.
@@ -415,7 +394,7 @@ impl<C: Components> TransactionPoolAdapter<C> {
}
}
impl<C: Components> network::TransactionPool<ComponentExHash<C>, ComponentBlock<C>> for TransactionPoolAdapter<C> {
impl<C: Components> network::TransactionPool<ComponentExHash<C>, ComponentBlock<C>> for TransactionPoolAdapter<C> where <C as components::Components>::RuntimeApi: Send + Sync{
fn transactions(&self) -> Vec<(ComponentExHash<C>, ComponentExtrinsic<C>)> {
self.pool.ready()
.map(|t| {
@@ -463,41 +442,6 @@ impl<C: Components> network::TransactionPool<ComponentExHash<C>, ComponentBlock<
}
}
/// 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::Arc<$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::Arc::new($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.
@@ -539,6 +483,7 @@ macro_rules! construct_service_factory {
$(#[$attr:meta])*
struct $name:ident {
Block = $block:ty,
RuntimeApi = $runtime_api:ty,
NetworkProtocol = $protocol:ty { $( $protocol_init:tt )* },
RuntimeDispatch = $dispatch:ty,
FullTransactionPoolApi = $full_transaction:ty { $( $full_transaction_init:tt )* },
@@ -559,6 +504,7 @@ macro_rules! construct_service_factory {
#[allow(unused_variables)]
impl $crate::ServiceFactory for $name {
type Block = $block;
type RuntimeApi = $runtime_api;
type NetworkProtocol = $protocol;
type RuntimeDispatch = $dispatch;
type FullTransactionPoolApi = $full_transaction;
+11 -2
View File
@@ -47,7 +47,8 @@ use service::{
Roles,
FactoryExtrinsic,
};
use network::{NetworkConfiguration, NonReservedPeerMode, Protocol, SyncProvider, ManageNetwork};
use network::{Protocol, SyncProvider, ManageNetwork};
use network::config::{NetworkConfiguration, NonReservedPeerMode};
use sr_primitives::traits::As;
use sr_primitives::generic::BlockId;
use consensus::{ImportBlock, BlockImport};
@@ -179,7 +180,10 @@ impl<F: ServiceFactory> TestNet<F> {
}
}
pub fn connectivity<F: ServiceFactory>(spec: FactoryChainSpec<F>) {
pub fn connectivity<F: ServiceFactory>(spec: FactoryChainSpec<F>) where
<F as ServiceFactory>::RuntimeApi:
client::block_builder::api::BlockBuilder<<F as service::ServiceFactory>::Block>
{
const NUM_NODES: u32 = 10;
{
let temp = TempDir::new("substrate-connectivity-test").expect("Error creating test dir");
@@ -219,6 +223,9 @@ where
F: ServiceFactory,
B: Fn(&F::FullService) -> ImportBlock<F::Block>,
E: Fn(&F::FullService) -> FactoryExtrinsic<F>,
<F as ServiceFactory>::RuntimeApi:
client::block_builder::api::BlockBuilder<<F as service::ServiceFactory>::Block> +
client::runtime_api::TaggedTransactionQueue<<F as service::ServiceFactory>::Block>
{
const NUM_NODES: u32 = 10;
const NUM_BLOCKS: usize = 512;
@@ -255,6 +262,8 @@ where
pub fn consensus<F>(spec: FactoryChainSpec<F>, authorities: Vec<String>)
where
F: ServiceFactory,
<F as ServiceFactory>::RuntimeApi:
client::block_builder::api::BlockBuilder<<F as service::ServiceFactory>::Block>
{
const NUM_NODES: u32 = 20;
const NUM_BLOCKS: u64 = 200;

Some files were not shown because too many files have changed in this diff Show More