mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 23:31:07 +00:00
Merge branch 'master' into rh-grandpa-dynamic2
This commit is contained in:
@@ -0,0 +1 @@
|
||||
Cargo.lock linguist-generated=true -diff
|
||||
Generated
+614
-428
File diff suppressed because it is too large
Load Diff
@@ -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]
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
|
||||
= Substrate CLI
|
||||
|
||||
.Summary
|
||||
[source, toml]
|
||||
----
|
||||
include::Cargo.toml[lines=2..5]
|
||||
----
|
||||
|
||||
.Description
|
||||
Substrate CLI library
|
||||
|
||||
include::doc/shell-completion.adoc[]
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
|
||||
= Client
|
||||
|
||||
.Summary
|
||||
[source, toml]
|
||||
----
|
||||
include::Cargo.toml[lines=2..5]
|
||||
----
|
||||
|
||||
.Description
|
||||
----
|
||||
include::src/lib.rs[tag=description]
|
||||
----
|
||||
@@ -1,13 +0,0 @@
|
||||
|
||||
= Client DB
|
||||
|
||||
.Summary
|
||||
[source, toml]
|
||||
----
|
||||
include::Cargo.toml[lines=2..5]
|
||||
----
|
||||
|
||||
.Description
|
||||
----
|
||||
include::src/lib.rs[tag=description]
|
||||
----
|
||||
@@ -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());
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
+36
-48
@@ -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;
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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]
|
||||
|
||||
@@ -41,7 +41,7 @@ error_chain! {
|
||||
}
|
||||
|
||||
/// Applying extrinsic error.
|
||||
ApplyExtinsicFailed(e: ApplyError) {
|
||||
ApplyExtrinsicFailed(e: ApplyError) {
|
||||
description("Extrinsic error"),
|
||||
display("Extrinsic error: {:?}", e),
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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, _>(
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
+40
-115
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
= Consensus Rhododendron (RHD)
|
||||
|
||||
.Summary
|
||||
[source, toml]
|
||||
----
|
||||
include::Cargo.toml[lines=2..5]
|
||||
----
|
||||
|
||||
.Description
|
||||
----
|
||||
include::src/lib.rs[tag=description]
|
||||
----
|
||||
@@ -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()
|
||||
};
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
|
||||
= Executor
|
||||
|
||||
.Summary
|
||||
[source, toml]
|
||||
----
|
||||
include::Cargo.toml[lines=2..5]
|
||||
----
|
||||
|
||||
.Description
|
||||
----
|
||||
include::src/lib.rs[tag=description]
|
||||
----
|
||||
@@ -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"]
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Generated
+24
-9
@@ -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)>;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
|
||||
= Keyring
|
||||
|
||||
.Summary
|
||||
[source, toml]
|
||||
----
|
||||
include::Cargo.toml[lines=2..5]
|
||||
----
|
||||
|
||||
.Description
|
||||
----
|
||||
include::src/lib.rs[tag=description]
|
||||
----
|
||||
@@ -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;
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
|
||||
= Keystore
|
||||
|
||||
.Summary
|
||||
[source, toml]
|
||||
----
|
||||
include::Cargo.toml[lines=2..5]
|
||||
----
|
||||
|
||||
.Description
|
||||
----
|
||||
include::src/lib.rs[tag=description]
|
||||
----
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)));
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
|
||||
= Network
|
||||
|
||||
.Summary
|
||||
[source, toml]
|
||||
----
|
||||
include::Cargo.toml[lines=2..5]
|
||||
----
|
||||
|
||||
.Description
|
||||
----
|
||||
include::src/lib.rs[tag=description]
|
||||
----
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>;
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
|
||||
= Primitives
|
||||
|
||||
.Summary
|
||||
[source, toml]
|
||||
----
|
||||
include::Cargo.toml[lines=2..5]
|
||||
----
|
||||
|
||||
.Description
|
||||
----
|
||||
include::src/lib.rs[tag=description]
|
||||
----
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
|
||||
= RPC Server
|
||||
|
||||
.Summary
|
||||
[source, toml]
|
||||
----
|
||||
include::Cargo.toml[lines=2..5]
|
||||
----
|
||||
|
||||
.Description
|
||||
----
|
||||
include::src/lib.rs[tag=description]
|
||||
----
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
|
||||
= RPC
|
||||
|
||||
.Summary
|
||||
[source, toml]
|
||||
----
|
||||
include::Cargo.toml[lines=2..5]
|
||||
----
|
||||
|
||||
.Description
|
||||
----
|
||||
include::src/lib.rs[tag=description]
|
||||
----
|
||||
@@ -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) {
|
||||
|
||||
@@ -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())]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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())])) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)]
|
||||
|
||||
|
||||
@@ -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>>> {
|
||||
|
||||
@@ -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>>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
|
||||
= Serializer
|
||||
|
||||
.Summary
|
||||
[source, toml]
|
||||
----
|
||||
include::Cargo.toml[lines=2..5]
|
||||
----
|
||||
|
||||
.Description
|
||||
----
|
||||
include::src/lib.rs[tag=description]
|
||||
----
|
||||
|
||||
@@ -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)]
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
|
||||
= Service
|
||||
|
||||
.Summary
|
||||
[source, toml]
|
||||
----
|
||||
include::Cargo.toml[lines=2..5]
|
||||
----
|
||||
|
||||
.Description
|
||||
----
|
||||
include::src/lib.rs[tag=description]
|
||||
----
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,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>;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user