mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-28 12:05:45 +00:00
Make substrate generic (#169)
* Some initial work on RPC and client * Rephrase as params * More work on traitifying substrate. * Traitify in_mem.rs * traitify client.rs * Make new primitives (mainly traits) build again. * Many (superficial) build fixes throughout. * Fix remaining build issues up to bft interface. * Make bft primitives be generic. * Switch out MisBehaviorReport for generic version. * Merge Hashing into Header. * Update runtime for new generics (with Hashing). * Update demo runtime. * Make runtime compile. * Build fixes for runtime * Remove old modules. * port substrate-bft to use generic substrate types * port client * port substrate-test-runtime * mostly port test-runtime to get compiling for std * Ensure `AccountId` has a `Default`. * Fix type deps. * finish porting * initialize test_runtime from genesis correctly * remove commented code * maybe unsigned signatures * runtimes compile * port over most of network * serialization for generic types * fix comment * remove some unnecessary trait bounds * network compiles * tests compile for sync * fix deserialization * temporarily remove deserialize derives * workarounds for serde issues for deriving deserialization * get demo-runtime compiling on std * port extrinsic-pool * primitives reshuffling * get network compiling again * remove debugging file * runtime tests now passing * port client-db * start to port over substrate-rpc * mostly port over PolkadotApi * test_runtime follows normal conventions * substrate runtime tests pass * deal with inherent extrinsics correctly in polkadot-api * port transaction-pool * port polkadot-consensus * port substrate-rpc * everything compiles * tests compile * fix grumbles * test-runtime uses its own transfer type * switch to master branch of jsonrpc * fix network tests and some warnings * all tests pass locally * [ci-skip] add another comment about issue * remove some curlies
This commit is contained in:
committed by
Robert Habermeier
parent
4e844760a3
commit
b94cf078af
@@ -8,6 +8,7 @@ futures = "0.1.17"
|
||||
substrate-codec = { path = "../codec" }
|
||||
substrate-primitives = { path = "../primitives" }
|
||||
substrate-runtime-support = { path = "../runtime-support" }
|
||||
substrate-runtime-primitives = { path = "../runtime/primitives" }
|
||||
ed25519 = { path = "../ed25519" }
|
||||
tokio-timer = "0.1.2"
|
||||
parking_lot = "0.4"
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
error_chain! {
|
||||
errors {
|
||||
/// Missing state at block with given Id.
|
||||
StateUnavailable(b: ::primitives::block::Id) {
|
||||
/// Missing state at block with given descriptor.
|
||||
StateUnavailable(b: String) {
|
||||
description("State missing at given block."),
|
||||
display("State unavailable at block {:?}", b),
|
||||
display("State unavailable at block {}", b),
|
||||
}
|
||||
|
||||
/// I/O terminated unexpectedly
|
||||
|
||||
+169
-142
@@ -22,6 +22,7 @@ pub mod generic;
|
||||
extern crate substrate_codec as codec;
|
||||
extern crate substrate_primitives as primitives;
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
extern crate substrate_runtime_primitives as runtime_primitives;
|
||||
extern crate ed25519;
|
||||
extern crate tokio_timer;
|
||||
extern crate parking_lot;
|
||||
@@ -41,9 +42,9 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use codec::Slicable;
|
||||
use ed25519::LocalizedSignature;
|
||||
use runtime_support::Hashable;
|
||||
use primitives::bft::{Message as PrimitiveMessage, Action as PrimitiveAction, Justification as PrimitiveJustification};
|
||||
use primitives::block::{Block, Id as BlockId, Header, HeaderHash};
|
||||
use runtime_primitives::generic::BlockId;
|
||||
use runtime_primitives::traits::{Block, Header};
|
||||
use runtime_primitives::bft::{Message as PrimitiveMessage, Action as PrimitiveAction, Justification as PrimitiveJustification};
|
||||
use primitives::AuthorityId;
|
||||
|
||||
use futures::{task, Async, Stream, Sink, Future, IntoFuture};
|
||||
@@ -56,27 +57,27 @@ pub use error::{Error, ErrorKind};
|
||||
|
||||
/// Messages over the proposal.
|
||||
/// Each message carries an associated round number.
|
||||
pub type Message = generic::Message<Block, HeaderHash>;
|
||||
pub type Message<B> = generic::Message<B, <B as Block>::Hash>;
|
||||
|
||||
/// Localized message type.
|
||||
pub type LocalizedMessage = generic::LocalizedMessage<
|
||||
Block,
|
||||
HeaderHash,
|
||||
pub type LocalizedMessage<B> = generic::LocalizedMessage<
|
||||
B,
|
||||
<B as Block>::Hash,
|
||||
AuthorityId,
|
||||
LocalizedSignature
|
||||
>;
|
||||
|
||||
/// Justification of some hash.
|
||||
pub type Justification = generic::Justification<HeaderHash, LocalizedSignature>;
|
||||
pub type Justification<H> = generic::Justification<H, LocalizedSignature>;
|
||||
|
||||
/// Justification of a prepare message.
|
||||
pub type PrepareJustification = generic::PrepareJustification<HeaderHash, LocalizedSignature>;
|
||||
pub type PrepareJustification<H> = generic::PrepareJustification<H, LocalizedSignature>;
|
||||
|
||||
/// Unchecked justification.
|
||||
pub type UncheckedJustification = generic::UncheckedJustification<HeaderHash, LocalizedSignature>;
|
||||
pub type UncheckedJustification<H> = generic::UncheckedJustification<H, LocalizedSignature>;
|
||||
|
||||
impl From<PrimitiveJustification> for UncheckedJustification {
|
||||
fn from(just: PrimitiveJustification) -> Self {
|
||||
impl<H> From<PrimitiveJustification<H>> for UncheckedJustification<H> {
|
||||
fn from(just: PrimitiveJustification<H>) -> Self {
|
||||
UncheckedJustification {
|
||||
round_number: just.round_number as usize,
|
||||
digest: just.hash,
|
||||
@@ -88,46 +89,46 @@ impl From<PrimitiveJustification> for UncheckedJustification {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UncheckedJustification> for PrimitiveJustification {
|
||||
fn from(just: UncheckedJustification) -> Self {
|
||||
impl<H> Into<PrimitiveJustification<H>> for UncheckedJustification<H> {
|
||||
fn into(self) -> PrimitiveJustification<H> {
|
||||
PrimitiveJustification {
|
||||
round_number: just.round_number as u32,
|
||||
hash: just.digest,
|
||||
signatures: just.signatures.into_iter().map(|s| (s.signer.into(), s.signature)).collect(),
|
||||
round_number: self.round_number as u32,
|
||||
hash: self.digest,
|
||||
signatures: self.signatures.into_iter().map(|s| (s.signer.into(), s.signature)).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of a committed round of BFT
|
||||
pub type Committed = generic::Committed<Block, HeaderHash, LocalizedSignature>;
|
||||
pub type Committed<B> = generic::Committed<B, <B as Block>::Hash, LocalizedSignature>;
|
||||
|
||||
/// Communication between BFT participants.
|
||||
pub type Communication = generic::Communication<Block, HeaderHash, AuthorityId, LocalizedSignature>;
|
||||
pub type Communication<B> = generic::Communication<B, <B as Block>::Hash, AuthorityId, LocalizedSignature>;
|
||||
|
||||
/// Misbehavior observed from BFT participants.
|
||||
pub type Misbehavior = generic::Misbehavior<HeaderHash, LocalizedSignature>;
|
||||
pub type Misbehavior<H> = generic::Misbehavior<H, LocalizedSignature>;
|
||||
|
||||
/// Proposer factory. Can be used to create a proposer instance.
|
||||
pub trait ProposerFactory {
|
||||
pub trait ProposerFactory<B: Block> {
|
||||
/// The proposer type this creates.
|
||||
type Proposer: Proposer;
|
||||
type Proposer: Proposer<B>;
|
||||
/// Error which can occur upon creation.
|
||||
type Error: From<Error>;
|
||||
|
||||
/// Initialize the proposal logic on top of a specific header.
|
||||
// TODO: provide state context explicitly?
|
||||
fn init(&self, parent_header: &Header, authorities: &[AuthorityId], sign_with: Arc<ed25519::Pair>) -> Result<Self::Proposer, Self::Error>;
|
||||
fn init(&self, parent_header: &B::Header, authorities: &[AuthorityId], sign_with: Arc<ed25519::Pair>) -> Result<Self::Proposer, Self::Error>;
|
||||
}
|
||||
|
||||
/// Logic for a proposer.
|
||||
///
|
||||
/// This will encapsulate creation and evaluation of proposals at a specific
|
||||
/// block.
|
||||
pub trait Proposer {
|
||||
pub trait Proposer<B: Block> {
|
||||
/// Error type which can occur when proposing or evaluating.
|
||||
type Error: From<Error> + From<InputStreamConcluded> + 'static;
|
||||
/// Future that resolves to a created proposal.
|
||||
type Create: IntoFuture<Item=Block,Error=Self::Error>;
|
||||
/// Future that resolves to a committed proposal.
|
||||
type Create: IntoFuture<Item=B,Error=Self::Error>;
|
||||
/// Future that resolves when a proposal is evaluated.
|
||||
type Evaluate: IntoFuture<Item=bool,Error=Self::Error>;
|
||||
|
||||
@@ -135,10 +136,10 @@ pub trait Proposer {
|
||||
fn propose(&self) -> Self::Create;
|
||||
|
||||
/// Evaluate proposal. True means valid.
|
||||
fn evaluate(&self, proposal: &Block) -> Self::Evaluate;
|
||||
fn evaluate(&self, proposal: &B) -> Self::Evaluate;
|
||||
|
||||
/// Import witnessed misbehavior.
|
||||
fn import_misbehavior(&self, misbehavior: Vec<(AuthorityId, Misbehavior)>);
|
||||
fn import_misbehavior(&self, misbehavior: Vec<(AuthorityId, Misbehavior<B::Hash>)>);
|
||||
|
||||
/// Determine the proposer for a given round. This should be a deterministic function
|
||||
/// with consistent results across all authorities.
|
||||
@@ -146,33 +147,37 @@ pub trait Proposer {
|
||||
}
|
||||
|
||||
/// Block import trait.
|
||||
pub trait BlockImport {
|
||||
pub trait BlockImport<B: Block> {
|
||||
/// Import a block alongside its corresponding justification.
|
||||
fn import_block(&self, block: Block, justification: Justification);
|
||||
fn import_block(&self, block: B, justification: Justification<B::Hash>);
|
||||
}
|
||||
|
||||
/// Trait for getting the authorities at a given block.
|
||||
pub trait Authorities {
|
||||
pub trait Authorities<B: Block> {
|
||||
/// Get the authorities at the given block.
|
||||
fn authorities(&self, at: &BlockId) -> Result<Vec<AuthorityId>, Error>;
|
||||
fn authorities(&self, at: &BlockId<B>) -> Result<Vec<AuthorityId>, Error>;
|
||||
}
|
||||
|
||||
/// Instance of BFT agreement.
|
||||
struct BftInstance<P> {
|
||||
struct BftInstance<B: Block, P> {
|
||||
key: Arc<ed25519::Pair>,
|
||||
authorities: Vec<AuthorityId>,
|
||||
parent_hash: HeaderHash,
|
||||
parent_hash: B::Hash,
|
||||
timer: Timer,
|
||||
round_timeout_multiplier: u64,
|
||||
proposer: P,
|
||||
}
|
||||
|
||||
impl<P: Proposer> generic::Context for BftInstance<P> {
|
||||
impl<B: Block, P: Proposer<B>> generic::Context for BftInstance<B, P>
|
||||
where
|
||||
B: Clone + Eq,
|
||||
B::Hash: ::std::hash::Hash,
|
||||
{
|
||||
type Error = P::Error;
|
||||
type AuthorityId = AuthorityId;
|
||||
type Digest = HeaderHash;
|
||||
type Digest = B::Hash;
|
||||
type Signature = LocalizedSignature;
|
||||
type Candidate = Block;
|
||||
type Candidate = B;
|
||||
type RoundTimeout = Box<Future<Item=(),Error=Self::Error> + Send>;
|
||||
type CreateProposal = <P::Create as IntoFuture>::Future;
|
||||
type EvaluateProposal = <P::Evaluate as IntoFuture>::Future;
|
||||
@@ -185,11 +190,11 @@ impl<P: Proposer> generic::Context for BftInstance<P> {
|
||||
self.proposer.propose().into_future()
|
||||
}
|
||||
|
||||
fn candidate_digest(&self, proposal: &Block) -> HeaderHash {
|
||||
proposal.header.blake2_256().into()
|
||||
fn candidate_digest(&self, proposal: &B) -> B::Hash {
|
||||
proposal.hash()
|
||||
}
|
||||
|
||||
fn sign_local(&self, message: Message) -> LocalizedMessage {
|
||||
fn sign_local(&self, message: Message<B>) -> LocalizedMessage<B> {
|
||||
sign_message(message, &*self.key, self.parent_hash.clone())
|
||||
}
|
||||
|
||||
@@ -197,7 +202,7 @@ impl<P: Proposer> generic::Context for BftInstance<P> {
|
||||
self.proposer.round_proposer(round, &self.authorities[..])
|
||||
}
|
||||
|
||||
fn proposal_valid(&self, proposal: &Block) -> Self::EvaluateProposal {
|
||||
fn proposal_valid(&self, proposal: &B) -> Self::EvaluateProposal {
|
||||
self.proposer.evaluate(proposal).into_future()
|
||||
}
|
||||
|
||||
@@ -217,23 +222,27 @@ impl<P: Proposer> generic::Context for BftInstance<P> {
|
||||
|
||||
/// A future that resolves either when canceled (witnessing a block from the network at same height)
|
||||
/// or when agreement completes.
|
||||
pub struct BftFuture<P, I, InStream, OutSink> where
|
||||
P: Proposer,
|
||||
InStream: Stream<Item=Communication, Error=P::Error>,
|
||||
OutSink: Sink<SinkItem=Communication, SinkError=P::Error>,
|
||||
pub struct BftFuture<B, P, I, InStream, OutSink> where
|
||||
B: Block + Clone + Eq,
|
||||
B::Hash: ::std::hash::Hash,
|
||||
P: Proposer<B>,
|
||||
InStream: Stream<Item=Communication<B>, Error=P::Error>,
|
||||
OutSink: Sink<SinkItem=Communication<B>, SinkError=P::Error>,
|
||||
{
|
||||
inner: generic::Agreement<BftInstance<P>, InStream, OutSink>,
|
||||
inner: generic::Agreement<BftInstance<B, P>, InStream, OutSink>,
|
||||
cancel: Arc<AtomicBool>,
|
||||
send_task: Option<oneshot::Sender<task::Task>>,
|
||||
import: Arc<I>,
|
||||
}
|
||||
|
||||
impl<P, I, InStream, OutSink> Future for BftFuture<P, I, InStream, OutSink> where
|
||||
P: Proposer,
|
||||
impl<B, P, I, InStream, OutSink> Future for BftFuture<B, P, I, InStream, OutSink> where
|
||||
B: Block + Clone + Eq,
|
||||
B::Hash: ::std::hash::Hash,
|
||||
P: Proposer<B>,
|
||||
P::Error: ::std::fmt::Display,
|
||||
I: BlockImport,
|
||||
InStream: Stream<Item=Communication, Error=P::Error>,
|
||||
OutSink: Sink<SinkItem=Communication, SinkError=P::Error>,
|
||||
I: BlockImport<B>,
|
||||
InStream: Stream<Item=Communication<B>, Error=P::Error>,
|
||||
OutSink: Sink<SinkItem=Communication<B>, SinkError=P::Error>,
|
||||
{
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
@@ -258,7 +267,7 @@ impl<P, I, InStream, OutSink> Future for BftFuture<P, I, InStream, OutSink> wher
|
||||
// we will get the block from the network later.
|
||||
if let Some(justified_block) = committed.candidate {
|
||||
info!(target: "bft", "Importing block #{} ({}) directly from BFT consensus",
|
||||
justified_block.header.number, HeaderHash::from(justified_block.header.blake2_256()));
|
||||
justified_block.header().number(), justified_block.hash());
|
||||
|
||||
self.import.import_block(justified_block, committed.justification)
|
||||
}
|
||||
@@ -267,10 +276,12 @@ impl<P, I, InStream, OutSink> Future for BftFuture<P, I, InStream, OutSink> wher
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, I, InStream, OutSink> Drop for BftFuture<P, I, InStream, OutSink> where
|
||||
P: Proposer,
|
||||
InStream: Stream<Item=Communication, Error=P::Error>,
|
||||
OutSink: Sink<SinkItem=Communication, SinkError=P::Error>,
|
||||
impl<B, P, I, InStream, OutSink> Drop for BftFuture<B, P, I, InStream, OutSink> where
|
||||
B: Block + Clone + Eq,
|
||||
B::Hash: ::std::hash::Hash,
|
||||
P: Proposer<B>,
|
||||
InStream: Stream<Item=Communication<B>, Error=P::Error>,
|
||||
OutSink: Sink<SinkItem=Communication<B>, SinkError=P::Error>,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
// TODO: have a trait member to pass misbehavior reports into.
|
||||
@@ -301,24 +312,26 @@ impl Drop for AgreementHandle {
|
||||
|
||||
/// The BftService kicks off the agreement process on top of any blocks it
|
||||
/// is notified of.
|
||||
pub struct BftService<P, I> {
|
||||
pub struct BftService<B: Block, P, I> {
|
||||
client: Arc<I>,
|
||||
live_agreement: Mutex<Option<(HeaderHash, AgreementHandle)>>,
|
||||
live_agreement: Mutex<Option<(B::Hash, AgreementHandle)>>,
|
||||
timer: Timer,
|
||||
round_timeout_multiplier: u64,
|
||||
key: Arc<ed25519::Pair>, // TODO: key changing over time.
|
||||
factory: P,
|
||||
}
|
||||
|
||||
impl<P, I> BftService<P, I>
|
||||
impl<B, P, I> BftService<B, P, I>
|
||||
where
|
||||
P: ProposerFactory,
|
||||
<P::Proposer as Proposer>::Error: ::std::fmt::Display,
|
||||
I: BlockImport + Authorities,
|
||||
B: Block + Clone + Eq,
|
||||
B::Hash: ::std::hash::Hash,
|
||||
P: ProposerFactory<B>,
|
||||
<P::Proposer as Proposer<B>>::Error: ::std::fmt::Display,
|
||||
I: BlockImport<B> + Authorities<B>,
|
||||
{
|
||||
|
||||
/// Create a new service instance.
|
||||
pub fn new(client: Arc<I>, key: Arc<ed25519::Pair>, factory: P) -> BftService<P, I> {
|
||||
pub fn new(client: Arc<I>, key: Arc<ed25519::Pair>, factory: P) -> BftService<B, P, I> {
|
||||
BftService {
|
||||
client: client,
|
||||
live_agreement: Mutex::new(None),
|
||||
@@ -340,17 +353,17 @@ impl<P, I> BftService<P, I>
|
||||
/// If the local signing key is an authority, this will begin the consensus process to build a
|
||||
/// block on top of it. If the executor fails to run the future, an error will be returned.
|
||||
/// Returns `None` if the agreement on the block with given parent is already in progress.
|
||||
pub fn build_upon<InStream, OutSink>(&self, header: &Header, input: InStream, output: OutSink)
|
||||
-> Result<Option<BftFuture<<P as ProposerFactory>::Proposer, I, InStream, OutSink>>, P::Error> where
|
||||
InStream: Stream<Item=Communication, Error=<<P as ProposerFactory>::Proposer as Proposer>::Error>,
|
||||
OutSink: Sink<SinkItem=Communication, SinkError=<<P as ProposerFactory>::Proposer as Proposer>::Error>,
|
||||
pub fn build_upon<InStream, OutSink>(&self, header: &B::Header, input: InStream, output: OutSink)
|
||||
-> Result<Option<BftFuture<B, <P as ProposerFactory<B>>::Proposer, I, InStream, OutSink>>, P::Error> where
|
||||
InStream: Stream<Item=Communication<B>, Error=<<P as ProposerFactory<B>>::Proposer as Proposer<B>>::Error>,
|
||||
OutSink: Sink<SinkItem=Communication<B>, SinkError=<<P as ProposerFactory<B>>::Proposer as Proposer<B>>::Error>,
|
||||
{
|
||||
let hash = header.blake2_256().into();
|
||||
if self.live_agreement.lock().as_ref().map_or(false, |&(h, _)| h == hash) {
|
||||
let hash = header.hash();
|
||||
if self.live_agreement.lock().as_ref().map_or(false, |&(ref h, _)| h == &hash) {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let authorities = self.client.authorities(&BlockId::Hash(hash))?;
|
||||
let authorities = self.client.authorities(&BlockId::Hash(hash.clone()))?;
|
||||
|
||||
let n = authorities.len();
|
||||
let max_faulty = max_faulty_of(n);
|
||||
@@ -368,7 +381,7 @@ impl<P, I> BftService<P, I>
|
||||
|
||||
let bft_instance = BftInstance {
|
||||
proposer,
|
||||
parent_hash: hash,
|
||||
parent_hash: hash.clone(),
|
||||
round_timeout_multiplier: self.round_timeout_multiplier,
|
||||
timer: self.timer.clone(),
|
||||
key: self.key.clone(),
|
||||
@@ -409,8 +422,8 @@ impl<P, I> BftService<P, I>
|
||||
}
|
||||
|
||||
/// Get current agreement parent hash if any.
|
||||
pub fn live_agreement(&self) -> Option<HeaderHash> {
|
||||
self.live_agreement.lock().as_ref().map(|&(h, _)| h.clone())
|
||||
pub fn live_agreement(&self) -> Option<B::Hash> {
|
||||
self.live_agreement.lock().as_ref().map(|&(ref h, _)| h.clone())
|
||||
}
|
||||
|
||||
}
|
||||
@@ -427,8 +440,8 @@ pub fn bft_threshold(n: usize) -> usize {
|
||||
n - max_faulty_of(n)
|
||||
}
|
||||
|
||||
fn check_justification_signed_message(authorities: &[AuthorityId], message: &[u8], just: UncheckedJustification)
|
||||
-> Result<Justification, UncheckedJustification>
|
||||
fn check_justification_signed_message<H>(authorities: &[AuthorityId], message: &[u8], just: UncheckedJustification<H>)
|
||||
-> Result<Justification<H>, UncheckedJustification<H>>
|
||||
{
|
||||
// TODO: return additional error information.
|
||||
just.check(authorities.len() - max_faulty_of(authorities.len()), |_, _, sig| {
|
||||
@@ -447,12 +460,12 @@ fn check_justification_signed_message(authorities: &[AuthorityId], message: &[u8
|
||||
/// Provide all valid authorities.
|
||||
///
|
||||
/// On failure, returns the justification back.
|
||||
pub fn check_justification(authorities: &[AuthorityId], parent: HeaderHash, just: UncheckedJustification)
|
||||
-> Result<Justification, UncheckedJustification>
|
||||
pub fn check_justification<B: Block>(authorities: &[AuthorityId], parent: B::Hash, just: UncheckedJustification<B::Hash>)
|
||||
-> Result<Justification<B::Hash>, UncheckedJustification<B::Hash>>
|
||||
{
|
||||
let message = Slicable::encode(&PrimitiveMessage {
|
||||
let message = Slicable::encode(&PrimitiveMessage::<B, _> {
|
||||
parent,
|
||||
action: PrimitiveAction::Commit(just.round_number as u32, just.digest),
|
||||
action: PrimitiveAction::Commit(just.round_number as u32, just.digest.clone()),
|
||||
});
|
||||
|
||||
check_justification_signed_message(authorities, &message[..], just)
|
||||
@@ -462,12 +475,12 @@ pub fn check_justification(authorities: &[AuthorityId], parent: HeaderHash, just
|
||||
/// Provide all valid authorities.
|
||||
///
|
||||
/// On failure, returns the justification back.
|
||||
pub fn check_prepare_justification(authorities: &[AuthorityId], parent: HeaderHash, just: UncheckedJustification)
|
||||
-> Result<PrepareJustification, UncheckedJustification>
|
||||
pub fn check_prepare_justification<B: Block>(authorities: &[AuthorityId], parent: B::Hash, just: UncheckedJustification<B::Hash>)
|
||||
-> Result<PrepareJustification<B::Hash>, UncheckedJustification<B::Hash>>
|
||||
{
|
||||
let message = Slicable::encode(&PrimitiveMessage {
|
||||
let message = Slicable::encode(&PrimitiveMessage::<B, _> {
|
||||
parent,
|
||||
action: PrimitiveAction::Prepare(just.round_number as u32, just.digest),
|
||||
action: PrimitiveAction::Prepare(just.round_number as u32, just.digest.clone()),
|
||||
});
|
||||
|
||||
check_justification_signed_message(authorities, &message[..], just)
|
||||
@@ -475,10 +488,10 @@ pub fn check_prepare_justification(authorities: &[AuthorityId], parent: HeaderHa
|
||||
|
||||
/// Check proposal message signatures and authority.
|
||||
/// Provide all valid authorities.
|
||||
pub fn check_proposal(
|
||||
pub fn check_proposal<B: Block + Clone>(
|
||||
authorities: &[AuthorityId],
|
||||
parent_hash: &HeaderHash,
|
||||
propose: &::generic::LocalizedProposal<Block, HeaderHash, AuthorityId, LocalizedSignature>)
|
||||
parent_hash: &B::Hash,
|
||||
propose: &::generic::LocalizedProposal<B, B::Hash, AuthorityId, LocalizedSignature>)
|
||||
-> Result<(), Error>
|
||||
{
|
||||
if !authorities.contains(&propose.sender) {
|
||||
@@ -487,16 +500,16 @@ pub fn check_proposal(
|
||||
|
||||
let action_header = PrimitiveAction::ProposeHeader(propose.round_number as u32, propose.digest.clone());
|
||||
let action_propose = PrimitiveAction::Propose(propose.round_number as u32, propose.proposal.clone());
|
||||
check_action(action_header, parent_hash, &propose.digest_signature)?;
|
||||
check_action(action_propose, parent_hash, &propose.full_signature)
|
||||
check_action::<B>(action_header, parent_hash, &propose.digest_signature)?;
|
||||
check_action::<B>(action_propose, parent_hash, &propose.full_signature)
|
||||
}
|
||||
|
||||
/// Check vote message signatures and authority.
|
||||
/// Provide all valid authorities.
|
||||
pub fn check_vote(
|
||||
pub fn check_vote<B: Block>(
|
||||
authorities: &[AuthorityId],
|
||||
parent_hash: &HeaderHash,
|
||||
vote: &::generic::LocalizedVote<HeaderHash, AuthorityId, LocalizedSignature>)
|
||||
parent_hash: &B::Hash,
|
||||
vote: &::generic::LocalizedVote<B::Hash, AuthorityId, LocalizedSignature>)
|
||||
-> Result<(), Error>
|
||||
{
|
||||
if !authorities.contains(&vote.sender) {
|
||||
@@ -504,14 +517,14 @@ pub fn check_vote(
|
||||
}
|
||||
|
||||
let action = match vote.vote {
|
||||
::generic::Vote::Prepare(r, h) => PrimitiveAction::Prepare(r as u32, h),
|
||||
::generic::Vote::Commit(r, h) => PrimitiveAction::Commit(r as u32, h),
|
||||
::generic::Vote::Prepare(r, ref h) => PrimitiveAction::Prepare(r as u32, h.clone()),
|
||||
::generic::Vote::Commit(r, ref h) => PrimitiveAction::Commit(r as u32, h.clone()),
|
||||
::generic::Vote::AdvanceRound(r) => PrimitiveAction::AdvanceRound(r as u32),
|
||||
};
|
||||
check_action(action, parent_hash, &vote.signature)
|
||||
check_action::<B>(action, parent_hash, &vote.signature)
|
||||
}
|
||||
|
||||
fn check_action(action: PrimitiveAction, parent_hash: &HeaderHash, sig: &LocalizedSignature) -> Result<(), Error> {
|
||||
fn check_action<B: Block>(action: PrimitiveAction<B, B::Hash>, parent_hash: &B::Hash, sig: &LocalizedSignature) -> Result<(), Error> {
|
||||
let primitive = PrimitiveMessage {
|
||||
parent: parent_hash.clone(),
|
||||
action,
|
||||
@@ -526,12 +539,12 @@ fn check_action(action: PrimitiveAction, parent_hash: &HeaderHash, sig: &Localiz
|
||||
}
|
||||
|
||||
/// Sign a BFT message with the given key.
|
||||
pub fn sign_message(message: Message, key: &ed25519::Pair, parent_hash: HeaderHash) -> LocalizedMessage {
|
||||
pub fn sign_message<B: Block + Clone>(message: Message<B>, key: &ed25519::Pair, parent_hash: B::Hash) -> LocalizedMessage<B> {
|
||||
let signer = key.public();
|
||||
|
||||
let sign_action = |action| {
|
||||
let sign_action = |action: PrimitiveAction<B, B::Hash>| {
|
||||
let primitive = PrimitiveMessage {
|
||||
parent: parent_hash,
|
||||
parent: parent_hash.clone(),
|
||||
action,
|
||||
};
|
||||
|
||||
@@ -544,7 +557,7 @@ pub fn sign_message(message: Message, key: &ed25519::Pair, parent_hash: HeaderHa
|
||||
|
||||
match message {
|
||||
::generic::Message::Propose(r, proposal) => {
|
||||
let header_hash: HeaderHash = proposal.header.blake2_256().into();
|
||||
let header_hash = proposal.hash();
|
||||
let action_header = PrimitiveAction::ProposeHeader(r as u32, header_hash.clone());
|
||||
let action_propose = PrimitiveAction::Propose(r as u32, proposal.clone());
|
||||
|
||||
@@ -559,8 +572,8 @@ pub fn sign_message(message: Message, key: &ed25519::Pair, parent_hash: HeaderHa
|
||||
}
|
||||
::generic::Message::Vote(vote) => {
|
||||
let action = match vote {
|
||||
::generic::Vote::Prepare(r, h) => PrimitiveAction::Prepare(r as u32, h),
|
||||
::generic::Vote::Commit(r, h) => PrimitiveAction::Commit(r as u32, h),
|
||||
::generic::Vote::Prepare(r, ref h) => PrimitiveAction::Prepare(r as u32, h.clone()),
|
||||
::generic::Vote::Commit(r, ref h) => PrimitiveAction::Commit(r as u32, h.clone()),
|
||||
::generic::Vote::AdvanceRound(r) => PrimitiveAction::AdvanceRound(r as u32),
|
||||
};
|
||||
|
||||
@@ -577,7 +590,8 @@ pub fn sign_message(message: Message, key: &ed25519::Pair, parent_hash: HeaderHa
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::collections::HashSet;
|
||||
use primitives::block;
|
||||
use runtime_primitives::testing::{Block as GenericTestBlock, Header as TestHeader};
|
||||
use primitives::H256;
|
||||
use self::tokio_core::reactor::{Core};
|
||||
use self::keyring::Keyring;
|
||||
use futures::stream;
|
||||
@@ -586,19 +600,21 @@ mod tests {
|
||||
extern crate substrate_keyring as keyring;
|
||||
extern crate tokio_core;
|
||||
|
||||
type TestBlock = GenericTestBlock<()>;
|
||||
|
||||
struct FakeClient {
|
||||
authorities: Vec<AuthorityId>,
|
||||
imported_heights: Mutex<HashSet<block::Number>>
|
||||
imported_heights: Mutex<HashSet<u64>>
|
||||
}
|
||||
|
||||
impl BlockImport for FakeClient {
|
||||
fn import_block(&self, block: Block, _justification: Justification) {
|
||||
impl BlockImport<TestBlock> for FakeClient {
|
||||
fn import_block(&self, block: TestBlock, _justification: Justification<H256>) {
|
||||
assert!(self.imported_heights.lock().insert(block.header.number))
|
||||
}
|
||||
}
|
||||
|
||||
impl Authorities for FakeClient {
|
||||
fn authorities(&self, _at: &BlockId) -> Result<Vec<AuthorityId>, Error> {
|
||||
impl Authorities<TestBlock> for FakeClient {
|
||||
fn authorities(&self, _at: &BlockId<TestBlock>) -> Result<Vec<AuthorityId>, Error> {
|
||||
Ok(self.authorities.clone())
|
||||
}
|
||||
}
|
||||
@@ -607,10 +623,10 @@ mod tests {
|
||||
struct Output<E>(::std::marker::PhantomData<E>);
|
||||
|
||||
impl<E> Sink for Output<E> {
|
||||
type SinkItem = Communication;
|
||||
type SinkItem = Communication<TestBlock>;
|
||||
type SinkError = E;
|
||||
|
||||
fn start_send(&mut self, _item: Communication) -> ::futures::StartSend<Communication, E> {
|
||||
fn start_send(&mut self, _item: Communication<TestBlock>) -> ::futures::StartSend<Communication<TestBlock>, E> {
|
||||
Ok(::futures::AsyncSink::Ready)
|
||||
}
|
||||
|
||||
@@ -620,34 +636,35 @@ mod tests {
|
||||
}
|
||||
|
||||
struct DummyFactory;
|
||||
struct DummyProposer(block::Number);
|
||||
struct DummyProposer(u64);
|
||||
|
||||
impl ProposerFactory for DummyFactory {
|
||||
impl ProposerFactory<TestBlock> for DummyFactory {
|
||||
type Proposer = DummyProposer;
|
||||
type Error = Error;
|
||||
|
||||
fn init(&self, parent_header: &Header, _authorities: &[AuthorityId], _sign_with: Arc<ed25519::Pair>) -> Result<DummyProposer, Error> {
|
||||
fn init(&self, parent_header: &TestHeader, _authorities: &[AuthorityId], _sign_with: Arc<ed25519::Pair>) -> Result<DummyProposer, Error> {
|
||||
Ok(DummyProposer(parent_header.number + 1))
|
||||
}
|
||||
}
|
||||
|
||||
impl Proposer for DummyProposer {
|
||||
impl Proposer<TestBlock> for DummyProposer {
|
||||
type Error = Error;
|
||||
type Create = Result<Block, Error>;
|
||||
type Create = Result<TestBlock, Error>;
|
||||
type Evaluate = Result<bool, Error>;
|
||||
|
||||
fn propose(&self) -> Result<Block, Error> {
|
||||
Ok(Block {
|
||||
header: Header::from_block_number(self.0),
|
||||
transactions: Default::default()
|
||||
fn propose(&self) -> Result<TestBlock, Error> {
|
||||
|
||||
Ok(TestBlock {
|
||||
header: from_block_number(self.0),
|
||||
extrinsics: Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
fn evaluate(&self, proposal: &Block) -> Result<bool, Error> {
|
||||
fn evaluate(&self, proposal: &TestBlock) -> Result<bool, Error> {
|
||||
Ok(proposal.header.number == self.0)
|
||||
}
|
||||
|
||||
fn import_misbehavior(&self, _misbehavior: Vec<(AuthorityId, Misbehavior)>) {}
|
||||
fn import_misbehavior(&self, _misbehavior: Vec<(AuthorityId, Misbehavior<H256>)>) {}
|
||||
|
||||
fn round_proposer(&self, round_number: usize, authorities: &[AuthorityId]) -> AuthorityId {
|
||||
authorities[round_number % authorities.len()].clone()
|
||||
@@ -655,7 +672,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn make_service(client: FakeClient)
|
||||
-> BftService<DummyFactory, FakeClient>
|
||||
-> BftService<TestBlock, DummyFactory, FakeClient>
|
||||
{
|
||||
BftService {
|
||||
client: Arc::new(client),
|
||||
@@ -667,13 +684,23 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn sign_vote(vote: ::generic::Vote<HeaderHash>, key: &ed25519::Pair, parent_hash: HeaderHash) -> LocalizedSignature {
|
||||
match sign_message(vote.into(), key, parent_hash) {
|
||||
fn sign_vote(vote: ::generic::Vote<H256>, key: &ed25519::Pair, parent_hash: H256) -> LocalizedSignature {
|
||||
match sign_message::<TestBlock>(vote.into(), key, parent_hash) {
|
||||
::generic::LocalizedMessage::Vote(vote) => vote.signature,
|
||||
_ => panic!("signing vote leads to signed vote"),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_block_number(num: u64) -> TestHeader {
|
||||
TestHeader::new(
|
||||
num,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn future_gets_preempted() {
|
||||
let client = FakeClient {
|
||||
@@ -690,12 +717,12 @@ mod tests {
|
||||
|
||||
let service = make_service(client);
|
||||
|
||||
let first = Header::from_block_number(2);
|
||||
let first_hash = first.blake2_256().into();
|
||||
let first = from_block_number(2);
|
||||
let first_hash = first.hash();
|
||||
|
||||
let mut second = Header::from_block_number(3);
|
||||
let mut second = from_block_number(3);
|
||||
second.parent_hash = first_hash;
|
||||
let second_hash = second.blake2_256().into();
|
||||
let second_hash = second.hash();
|
||||
|
||||
let bft = service.build_upon(&first, stream::empty(), Output(Default::default())).unwrap();
|
||||
assert!(service.live_agreement.lock().as_ref().unwrap().0 == first_hash);
|
||||
@@ -749,7 +776,7 @@ mod tests {
|
||||
}).collect(),
|
||||
};
|
||||
|
||||
assert!(check_justification(&authorities, parent_hash, unchecked).is_ok());
|
||||
assert!(check_justification::<TestBlock>(&authorities, parent_hash, unchecked).is_ok());
|
||||
|
||||
let unchecked = UncheckedJustification {
|
||||
digest: hash,
|
||||
@@ -759,7 +786,7 @@ mod tests {
|
||||
}).collect(),
|
||||
};
|
||||
|
||||
assert!(check_justification(&authorities, parent_hash, unchecked).is_err());
|
||||
assert!(check_justification::<TestBlock>(&authorities, parent_hash, unchecked).is_err());
|
||||
|
||||
// not enough signatures.
|
||||
let unchecked = UncheckedJustification {
|
||||
@@ -770,7 +797,7 @@ mod tests {
|
||||
}).collect(),
|
||||
};
|
||||
|
||||
assert!(check_justification(&authorities, parent_hash, unchecked).is_err());
|
||||
assert!(check_justification::<TestBlock>(&authorities, parent_hash, unchecked).is_err());
|
||||
|
||||
// wrong hash.
|
||||
let unchecked = UncheckedJustification {
|
||||
@@ -781,7 +808,7 @@ mod tests {
|
||||
}).collect(),
|
||||
};
|
||||
|
||||
assert!(check_justification(&authorities, parent_hash, unchecked).is_err());
|
||||
assert!(check_justification::<TestBlock>(&authorities, parent_hash, unchecked).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -793,9 +820,9 @@ mod tests {
|
||||
Keyring::Eve.to_raw_public(),
|
||||
];
|
||||
|
||||
let block = Block {
|
||||
header: Header::from_block_number(1),
|
||||
transactions: Default::default()
|
||||
let block = TestBlock {
|
||||
header: from_block_number(1),
|
||||
extrinsics: Default::default()
|
||||
};
|
||||
|
||||
let proposal = sign_message(::generic::Message::Propose(1, block.clone()), &Keyring::Alice.pair(), parent_hash);;
|
||||
@@ -812,7 +839,7 @@ mod tests {
|
||||
}
|
||||
|
||||
// Not an authority
|
||||
let proposal = sign_message(::generic::Message::Propose(1, block), &Keyring::Bob.pair(), parent_hash);;
|
||||
let proposal = sign_message::<TestBlock>(::generic::Message::Propose(1, block), &Keyring::Bob.pair(), parent_hash);;
|
||||
if let ::generic::LocalizedMessage::Propose(proposal) = proposal {
|
||||
assert!(check_proposal(&authorities, &parent_hash, &proposal).is_err());
|
||||
} else {
|
||||
@@ -822,28 +849,28 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn vote_check_works() {
|
||||
let parent_hash = Default::default();
|
||||
let hash = [0xff; 32].into();
|
||||
let parent_hash: H256 = Default::default();
|
||||
let hash: H256 = [0xff; 32].into();
|
||||
|
||||
let authorities = vec![
|
||||
Keyring::Alice.to_raw_public(),
|
||||
Keyring::Eve.to_raw_public(),
|
||||
];
|
||||
|
||||
let vote = sign_message(::generic::Message::Vote(::generic::Vote::Prepare(1, hash)), &Keyring::Alice.pair(), parent_hash);;
|
||||
let vote = sign_message::<TestBlock>(::generic::Message::Vote(::generic::Vote::Prepare(1, hash)), &Keyring::Alice.pair(), parent_hash);;
|
||||
if let ::generic::LocalizedMessage::Vote(vote) = vote {
|
||||
assert!(check_vote(&authorities, &parent_hash, &vote).is_ok());
|
||||
assert!(check_vote::<TestBlock>(&authorities, &parent_hash, &vote).is_ok());
|
||||
let mut invalid_sender = vote.clone();
|
||||
invalid_sender.signature.signer = Keyring::Eve.into();
|
||||
assert!(check_vote(&authorities, &parent_hash, &invalid_sender).is_err());
|
||||
assert!(check_vote::<TestBlock>(&authorities, &parent_hash, &invalid_sender).is_err());
|
||||
} else {
|
||||
assert!(false);
|
||||
}
|
||||
|
||||
// Not an authority
|
||||
let vote = sign_message(::generic::Message::Vote(::generic::Vote::Prepare(1, hash)), &Keyring::Bob.pair(), parent_hash);;
|
||||
let vote = sign_message::<TestBlock>(::generic::Message::Vote(::generic::Vote::Prepare(1, hash)), &Keyring::Bob.pair(), parent_hash);;
|
||||
if let ::generic::LocalizedMessage::Vote(vote) = vote {
|
||||
assert!(check_vote(&authorities, &parent_hash, &vote).is_err());
|
||||
assert!(check_vote::<TestBlock>(&authorities, &parent_hash, &vote).is_err());
|
||||
} else {
|
||||
assert!(false);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ substrate-executor = { path = "../executor" }
|
||||
substrate-primitives = { path = "../primitives" }
|
||||
substrate-runtime-io = { path = "../runtime-io" }
|
||||
substrate-runtime-support = { path = "../runtime-support" }
|
||||
substrate-runtime-primitives = { path = "../runtime/primitives" }
|
||||
substrate-state-machine = { path = "../state-machine" }
|
||||
substrate-keyring = { path = "../../substrate/keyring" }
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ hashdb = { git = "https://github.com/paritytech/parity.git" }
|
||||
patricia-trie = { git = "https://github.com/paritytech/parity.git" }
|
||||
memorydb = { git = "https://github.com/paritytech/parity.git" }
|
||||
substrate-primitives = { path = "../../../substrate/primitives" }
|
||||
substrate-runtime-primitives = { path = "../../../substrate/runtime/primitives" }
|
||||
substrate-client = { path = "../../../substrate/client" }
|
||||
substrate-state-machine = { path = "../../../substrate/state-machine" }
|
||||
substrate-runtime-support = { path = "../../../substrate/runtime-support" }
|
||||
|
||||
@@ -27,6 +27,7 @@ extern crate patricia_trie;
|
||||
extern crate substrate_state_machine as state_machine;
|
||||
extern crate substrate_primitives as primitives;
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
extern crate substrate_runtime_primitives as runtime_primitives;
|
||||
extern crate substrate_codec as codec;
|
||||
|
||||
#[macro_use]
|
||||
@@ -47,9 +48,9 @@ use kvdb::{KeyValueDB, DBTransaction};
|
||||
use memorydb::MemoryDB;
|
||||
use parking_lot::RwLock;
|
||||
use patricia_trie::{TrieDB, TrieDBMut, TrieError, Trie, TrieMut};
|
||||
use primitives::blake2_256;
|
||||
use primitives::block::{self, Id as BlockId, HeaderHash};
|
||||
use runtime_support::Hashable;
|
||||
use runtime_primitives::generic::BlockId;
|
||||
use runtime_primitives::bft::Justification;
|
||||
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, As, Hashing, HashingFor, Zero};
|
||||
use state_machine::backend::Backend as StateBackend;
|
||||
use state_machine::CodeExecutor;
|
||||
|
||||
@@ -62,14 +63,17 @@ pub struct DatabaseSettings {
|
||||
}
|
||||
|
||||
/// Create an instance of db-backed client.
|
||||
pub fn new_client<E, F>(
|
||||
pub fn new_client<E, F, Block>(
|
||||
settings: DatabaseSettings,
|
||||
executor: E,
|
||||
genesis_builder: F,
|
||||
) -> Result<client::Client<Backend, client::LocalCallExecutor<Backend, E>>, client::error::Error>
|
||||
) -> Result<client::Client<Backend<Block>, client::LocalCallExecutor<Backend<Block>, E>, Block>, client::error::Error>
|
||||
where
|
||||
Block: BlockT,
|
||||
<Block::Header as HeaderT>::Number: As<u32>,
|
||||
Block::Hash: Into<[u8; 32]>, // TODO: remove when patricia_trie generic.
|
||||
E: CodeExecutor,
|
||||
F: client::GenesisBuilder,
|
||||
F: client::GenesisBuilder<Block>,
|
||||
{
|
||||
let backend = Arc::new(Backend::new(&settings)?);
|
||||
let executor = client::LocalCallExecutor::new(backend.clone(), executor);
|
||||
@@ -90,24 +94,26 @@ mod meta {
|
||||
pub const BEST_BLOCK: &[u8; 4] = b"best";
|
||||
}
|
||||
|
||||
struct PendingBlock {
|
||||
header: block::Header,
|
||||
justification: Option<primitives::bft::Justification>,
|
||||
body: Option<block::Body>,
|
||||
struct PendingBlock<Block: BlockT> {
|
||||
header: Block::Header,
|
||||
justification: Option<Justification<Block::Hash>>,
|
||||
body: Option<Vec<Block::Extrinsic>>,
|
||||
is_best: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Meta {
|
||||
best_hash: HeaderHash,
|
||||
best_number: block::Number,
|
||||
genesis_hash: HeaderHash,
|
||||
struct Meta<N, H> {
|
||||
best_hash: H,
|
||||
best_number: N,
|
||||
genesis_hash: H,
|
||||
}
|
||||
|
||||
type BlockKey = [u8; 4];
|
||||
|
||||
// Little endian
|
||||
fn number_to_db_key(n: block::Number) -> BlockKey {
|
||||
fn number_to_db_key<N>(n: N) -> BlockKey where N: As<u32> {
|
||||
let n: u32 = n.as_();
|
||||
|
||||
[
|
||||
(n >> 24) as u8,
|
||||
((n >> 16) & 0xff) as u8,
|
||||
@@ -127,13 +133,13 @@ fn db_err(err: kvdb::Error) -> client::error::Error {
|
||||
}
|
||||
|
||||
/// Block database
|
||||
pub struct BlockchainDb {
|
||||
pub struct BlockchainDb<Block: BlockT> {
|
||||
db: Arc<KeyValueDB>,
|
||||
meta: RwLock<Meta>,
|
||||
meta: RwLock<Meta<<Block::Header as HeaderT>::Number, Block::Hash>>,
|
||||
}
|
||||
|
||||
impl BlockchainDb {
|
||||
fn id(&self, id: BlockId) -> Result<Option<BlockKey>, client::error::Error> {
|
||||
impl<Block: BlockT> BlockchainDb<Block> where <Block::Header as HeaderT>::Number: As<u32> {
|
||||
fn id(&self, id: BlockId<Block>) -> Result<Option<BlockKey>, client::error::Error> {
|
||||
match id {
|
||||
BlockId::Hash(h) => {
|
||||
{
|
||||
@@ -142,7 +148,7 @@ impl BlockchainDb {
|
||||
return Ok(Some(number_to_db_key(meta.best_number)));
|
||||
}
|
||||
}
|
||||
self.db.get(columns::BLOCK_INDEX, &h).map(|v| v.map(|v| {
|
||||
self.db.get(columns::BLOCK_INDEX, h.as_ref()).map(|v| v.map(|v| {
|
||||
let mut key: [u8; 4] = [0; 4];
|
||||
key.copy_from_slice(&v);
|
||||
key
|
||||
@@ -152,21 +158,21 @@ impl BlockchainDb {
|
||||
}
|
||||
}
|
||||
|
||||
fn new(db: Arc<KeyValueDB>) -> Result<BlockchainDb, client::error::Error> {
|
||||
fn new(db: Arc<KeyValueDB>) -> Result<Self, client::error::Error> {
|
||||
let (best_hash, best_number) = if let Some(Some(header)) = db.get(columns::META, meta::BEST_BLOCK).and_then(|id|
|
||||
match id {
|
||||
Some(id) => db.get(columns::HEADER, &id).map(|h| h.map(|b| block::Header::decode(&mut &b[..]))),
|
||||
Some(id) => db.get(columns::HEADER, &id).map(|h| h.map(|b| Block::Header::decode(&mut &b[..]))),
|
||||
None => Ok(None),
|
||||
}).map_err(db_err)?
|
||||
{
|
||||
let hash = header.blake2_256().into();
|
||||
debug!("DB Opened blockchain db, best {:?} ({})", hash, header.number);
|
||||
(hash, header.number)
|
||||
let hash = header.hash();
|
||||
debug!("DB Opened blockchain db, best {:?} ({})", hash, header.number());
|
||||
(hash, header.number().clone())
|
||||
} else {
|
||||
(Default::default(), Default::default())
|
||||
(Default::default(), Zero::zero())
|
||||
};
|
||||
let genesis_hash = db.get(columns::HEADER, &number_to_db_key(0)).map_err(db_err)?
|
||||
.map(|b| blake2_256(&b)).unwrap_or_default().into();
|
||||
let genesis_hash = db.get(columns::HEADER, &number_to_db_key(<Block::Header as HeaderT>::Number::zero())).map_err(db_err)?
|
||||
.map(|b| HashingFor::<Block>::hash(&b)).unwrap_or_default().into();
|
||||
|
||||
Ok(BlockchainDb {
|
||||
db,
|
||||
@@ -178,7 +184,7 @@ impl BlockchainDb {
|
||||
})
|
||||
}
|
||||
|
||||
fn read_db(&self, id: BlockId, column: Option<u32>) -> Result<Option<DBValue>, client::error::Error> {
|
||||
fn read_db(&self, id: BlockId<Block>, column: Option<u32>) -> Result<Option<DBValue>, client::error::Error> {
|
||||
self.id(id).and_then(|key|
|
||||
match key {
|
||||
Some(key) => self.db.get(column, &key).map_err(db_err),
|
||||
@@ -186,10 +192,10 @@ impl BlockchainDb {
|
||||
})
|
||||
}
|
||||
|
||||
fn update_meta(&self, hash: block::HeaderHash, number: block::Number, is_best: bool) {
|
||||
fn update_meta(&self, hash: Block::Hash, number: <Block::Header as HeaderT>::Number, is_best: bool) {
|
||||
if is_best {
|
||||
let mut meta = self.meta.write();
|
||||
if number == 0 {
|
||||
if number == Zero::zero() {
|
||||
meta.genesis_hash = hash;
|
||||
}
|
||||
meta.best_number = number;
|
||||
@@ -198,10 +204,10 @@ impl BlockchainDb {
|
||||
}
|
||||
}
|
||||
|
||||
impl client::blockchain::Backend for BlockchainDb {
|
||||
fn header(&self, id: BlockId) -> Result<Option<block::Header>, client::error::Error> {
|
||||
impl<Block: BlockT> client::blockchain::Backend<Block> for BlockchainDb<Block> where <Block::Header as HeaderT>::Number: As<u32> {
|
||||
fn header(&self, id: BlockId<Block>) -> Result<Option<Block::Header>, client::error::Error> {
|
||||
match self.read_db(id, columns::HEADER)? {
|
||||
Some(header) => match block::Header::decode(&mut &header[..]) {
|
||||
Some(header) => match Block::Header::decode(&mut &header[..]) {
|
||||
Some(header) => Ok(Some(header)),
|
||||
None => return Err(client::error::ErrorKind::Backend("Error decoding header".into()).into()),
|
||||
}
|
||||
@@ -209,9 +215,9 @@ impl client::blockchain::Backend for BlockchainDb {
|
||||
}
|
||||
}
|
||||
|
||||
fn body(&self, id: BlockId) -> Result<Option<block::Body>, client::error::Error> {
|
||||
fn body(&self, id: BlockId<Block>) -> Result<Option<Vec<Block::Extrinsic>>, client::error::Error> {
|
||||
match self.read_db(id, columns::BODY)? {
|
||||
Some(body) => match block::Body::decode(&mut &body[..]) {
|
||||
Some(body) => match Slicable::decode(&mut &body[..]) {
|
||||
Some(body) => Ok(Some(body)),
|
||||
None => return Err(client::error::ErrorKind::Backend("Error decoding body".into()).into()),
|
||||
}
|
||||
@@ -219,9 +225,9 @@ impl client::blockchain::Backend for BlockchainDb {
|
||||
}
|
||||
}
|
||||
|
||||
fn justification(&self, id: BlockId) -> Result<Option<primitives::bft::Justification>, client::error::Error> {
|
||||
fn justification(&self, id: BlockId<Block>) -> Result<Option<Justification<Block::Hash>>, client::error::Error> {
|
||||
match self.read_db(id, columns::JUSTIFICATION)? {
|
||||
Some(justification) => match primitives::bft::Justification::decode(&mut &justification[..]) {
|
||||
Some(justification) => match Slicable::decode(&mut &justification[..]) {
|
||||
Some(justification) => Ok(Some(justification)),
|
||||
None => return Err(client::error::ErrorKind::Backend("Error decoding justification".into()).into()),
|
||||
}
|
||||
@@ -229,7 +235,7 @@ impl client::blockchain::Backend for BlockchainDb {
|
||||
}
|
||||
}
|
||||
|
||||
fn info(&self) -> Result<client::blockchain::Info, client::error::Error> {
|
||||
fn info(&self) -> Result<client::blockchain::Info<Block>, client::error::Error> {
|
||||
let meta = self.meta.read();
|
||||
Ok(client::blockchain::Info {
|
||||
best_hash: meta.best_hash,
|
||||
@@ -238,7 +244,7 @@ impl client::blockchain::Backend for BlockchainDb {
|
||||
})
|
||||
}
|
||||
|
||||
fn status(&self, id: BlockId) -> Result<client::blockchain::BlockStatus, client::error::Error> {
|
||||
fn status(&self, id: BlockId<Block>) -> Result<client::blockchain::BlockStatus, client::error::Error> {
|
||||
let exists = match id {
|
||||
BlockId::Hash(_) => self.id(id)?.is_some(),
|
||||
BlockId::Number(n) => n <= self.meta.read().best_number,
|
||||
@@ -249,28 +255,28 @@ impl client::blockchain::Backend for BlockchainDb {
|
||||
}
|
||||
}
|
||||
|
||||
fn hash(&self, number: block::Number) -> Result<Option<block::HeaderHash>, client::error::Error> {
|
||||
fn hash(&self, number: <Block::Header as HeaderT>::Number) -> Result<Option<Block::Hash>, client::error::Error> {
|
||||
self.read_db(BlockId::Number(number), columns::HEADER).map(|x|
|
||||
x.map(|raw| blake2_256(&raw[..])).map(Into::into)
|
||||
x.map(|raw| HashingFor::<Block>::hash(&raw[..])).map(Into::into)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Database transaction
|
||||
pub struct BlockImportOperation {
|
||||
pub struct BlockImportOperation<Block: BlockT> {
|
||||
old_state: DbState,
|
||||
updates: MemoryDB,
|
||||
pending_block: Option<PendingBlock>,
|
||||
pending_block: Option<PendingBlock<Block>>,
|
||||
}
|
||||
|
||||
impl client::backend::BlockImportOperation for BlockImportOperation {
|
||||
impl<Block: BlockT> client::backend::BlockImportOperation<Block> for BlockImportOperation<Block> {
|
||||
type State = DbState;
|
||||
|
||||
fn state(&self) -> Result<Option<&Self::State>, client::error::Error> {
|
||||
Ok(Some(&self.old_state))
|
||||
}
|
||||
|
||||
fn set_block_data(&mut self, header: block::Header, body: Option<block::Body>, justification: Option<primitives::bft::Justification>, is_best: bool) -> Result<(), client::error::Error> {
|
||||
fn set_block_data(&mut self, header: Block::Header, body: Option<Vec<Block::Extrinsic>>, justification: Option<Justification<Block::Hash>>, is_best: bool) -> Result<(), client::error::Error> {
|
||||
assert!(self.pending_block.is_none(), "Only one block per operation is allowed");
|
||||
self.pending_block = Some(PendingBlock {
|
||||
header,
|
||||
@@ -423,15 +429,15 @@ impl state_machine::Backend for DbState {
|
||||
|
||||
/// Disk backend. Keeps data in a key-value store. In archive mode, trie nodes are kept from all blocks.
|
||||
/// Otherwise, trie nodes are kept only from the most recent block.
|
||||
pub struct Backend {
|
||||
pub struct Backend<Block: BlockT> {
|
||||
db: Arc<KeyValueDB>,
|
||||
blockchain: BlockchainDb,
|
||||
blockchain: BlockchainDb<Block>,
|
||||
archive: bool,
|
||||
}
|
||||
|
||||
impl Backend {
|
||||
impl<Block: BlockT> Backend<Block> where <Block::Header as HeaderT>::Number: As<u32> {
|
||||
/// Create a new instance of database backend.
|
||||
pub fn new(config: &DatabaseSettings) -> Result<Backend, client::error::Error> {
|
||||
pub fn new(config: &DatabaseSettings) -> Result<Self, client::error::Error> {
|
||||
let mut db_config = DatabaseConfig::with_columns(Some(columns::NUM_COLUMNS));
|
||||
db_config.memory_budget = config.cache_size;
|
||||
db_config.wal = true;
|
||||
@@ -442,13 +448,13 @@ impl Backend {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn new_test() -> Backend {
|
||||
fn new_test() -> Self {
|
||||
let db = Arc::new(::kvdb_memorydb::create(columns::NUM_COLUMNS));
|
||||
|
||||
Backend::from_kvdb(db as Arc<_>, false).expect("failed to create test-db")
|
||||
}
|
||||
|
||||
fn from_kvdb(db: Arc<KeyValueDB>, archive: bool) -> Result<Backend, client::error::Error> {
|
||||
fn from_kvdb(db: Arc<KeyValueDB>, archive: bool) -> Result<Self, client::error::Error> {
|
||||
let blockchain = BlockchainDb::new(db.clone())?;
|
||||
|
||||
Ok(Backend {
|
||||
@@ -459,12 +465,15 @@ impl Backend {
|
||||
}
|
||||
}
|
||||
|
||||
impl client::backend::Backend for Backend {
|
||||
type BlockImportOperation = BlockImportOperation;
|
||||
type Blockchain = BlockchainDb;
|
||||
impl<Block: BlockT> client::backend::Backend<Block> for Backend<Block> where
|
||||
<Block::Header as HeaderT>::Number: As<u32>,
|
||||
Block::Hash: Into<[u8; 32]>, // TODO: remove when patricia_trie generic.
|
||||
{
|
||||
type BlockImportOperation = BlockImportOperation<Block>;
|
||||
type Blockchain = BlockchainDb<Block>;
|
||||
type State = DbState;
|
||||
|
||||
fn begin_operation(&self, block: BlockId) -> Result<Self::BlockImportOperation, client::error::Error> {
|
||||
fn begin_operation(&self, block: BlockId<Block>) -> Result<Self::BlockImportOperation, client::error::Error> {
|
||||
let state = self.state_at(block)?;
|
||||
Ok(BlockImportOperation {
|
||||
pending_block: None,
|
||||
@@ -476,9 +485,9 @@ impl client::backend::Backend for Backend {
|
||||
fn commit_operation(&self, mut operation: Self::BlockImportOperation) -> Result<(), client::error::Error> {
|
||||
let mut transaction = DBTransaction::new();
|
||||
if let Some(pending_block) = operation.pending_block {
|
||||
let hash: block::HeaderHash = pending_block.header.blake2_256().into();
|
||||
let number = pending_block.header.number;;
|
||||
let key = number_to_db_key(pending_block.header.number);
|
||||
let hash = pending_block.header.hash();
|
||||
let number = pending_block.header.number().clone();
|
||||
let key = number_to_db_key(pending_block.header.number().clone());
|
||||
transaction.put(columns::HEADER, &key, &pending_block.header.encode());
|
||||
if let Some(body) = pending_block.body {
|
||||
transaction.put(columns::BODY, &key, &body.encode());
|
||||
@@ -486,7 +495,7 @@ impl client::backend::Backend for Backend {
|
||||
if let Some(justification) = pending_block.justification {
|
||||
transaction.put(columns::JUSTIFICATION, &key, &justification.encode());
|
||||
}
|
||||
transaction.put(columns::BLOCK_INDEX, &hash, &key);
|
||||
transaction.put(columns::BLOCK_INDEX, hash.as_ref(), &key);
|
||||
if pending_block.is_best {
|
||||
transaction.put(columns::META, meta::BEST_BLOCK, &key);
|
||||
}
|
||||
@@ -504,11 +513,11 @@ impl client::backend::Backend for Backend {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn blockchain(&self) -> &BlockchainDb {
|
||||
fn blockchain(&self) -> &BlockchainDb<Block> {
|
||||
&self.blockchain
|
||||
}
|
||||
|
||||
fn state_at(&self, block: BlockId) -> Result<Self::State, client::error::Error> {
|
||||
fn state_at(&self, block: BlockId<Block>) -> Result<Self::State, client::error::Error> {
|
||||
use client::blockchain::Backend as BcBackend;
|
||||
|
||||
// special case for genesis initialization
|
||||
@@ -527,15 +536,19 @@ impl client::backend::Backend for Backend {
|
||||
}
|
||||
|
||||
self.blockchain.header(block).and_then(|maybe_hdr| maybe_hdr.map(|hdr| {
|
||||
let root: [u8; 32] = hdr.state_root().clone().into();
|
||||
DbState {
|
||||
db: self.db.clone(),
|
||||
root: hdr.state_root.0.into(),
|
||||
root: root.into(),
|
||||
}
|
||||
}).ok_or_else(|| client::error::ErrorKind::UnknownBlock(block).into()))
|
||||
}).ok_or_else(|| client::error::ErrorKind::UnknownBlock(format!("{:?}", block)).into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl client::backend::LocalBackend for Backend {}
|
||||
impl<Block: BlockT> client::backend::LocalBackend<Block> for Backend<Block> where
|
||||
<Block::Header as HeaderT>::Number: As<u32>,
|
||||
Block::Hash: Into<[u8; 32]>, // TODO: remove when patricia_trie generic.
|
||||
{}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
@@ -543,10 +556,13 @@ mod tests {
|
||||
use client::backend::Backend as BTrait;
|
||||
use client::backend::BlockImportOperation as Op;
|
||||
use client::blockchain::Backend as BCTrait;
|
||||
use runtime_primitives::testing::{Header, Block as RawBlock};
|
||||
|
||||
type Block = RawBlock<u64>;
|
||||
|
||||
#[test]
|
||||
fn block_hash_inserted_correctly() {
|
||||
let db = Backend::new_test();
|
||||
let db = Backend::<Block>::new_test();
|
||||
for i in 0..10 {
|
||||
assert!(db.blockchain().hash(i).unwrap().is_none());
|
||||
|
||||
@@ -558,7 +574,7 @@ mod tests {
|
||||
};
|
||||
|
||||
let mut op = db.begin_operation(id).unwrap();
|
||||
let header = block::Header {
|
||||
let header = Header {
|
||||
number: i,
|
||||
parent_hash: Default::default(),
|
||||
state_root: Default::default(),
|
||||
@@ -581,10 +597,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn set_state_data() {
|
||||
let db = Backend::new_test();
|
||||
let db = Backend::<Block>::new_test();
|
||||
{
|
||||
let mut op = db.begin_operation(BlockId::Hash(Default::default())).unwrap();
|
||||
let mut header = block::Header {
|
||||
let mut header = Header {
|
||||
number: 0,
|
||||
parent_hash: Default::default(),
|
||||
state_root: Default::default(),
|
||||
@@ -623,7 +639,7 @@ mod tests {
|
||||
|
||||
{
|
||||
let mut op = db.begin_operation(BlockId::Number(0)).unwrap();
|
||||
let mut header = block::Header {
|
||||
let mut header = Header {
|
||||
number: 1,
|
||||
parent_hash: Default::default(),
|
||||
state_root: Default::default(),
|
||||
@@ -660,11 +676,11 @@ mod tests {
|
||||
#[test]
|
||||
fn delete_only_when_negative_rc() {
|
||||
let key;
|
||||
let db = Backend::new_test();
|
||||
let db = Backend::<Block>::new_test();
|
||||
|
||||
{
|
||||
let mut op = db.begin_operation(BlockId::Hash(Default::default())).unwrap();
|
||||
let mut header = block::Header {
|
||||
let mut header = Header {
|
||||
number: 0,
|
||||
parent_hash: Default::default(),
|
||||
state_root: Default::default(),
|
||||
@@ -697,7 +713,7 @@ mod tests {
|
||||
|
||||
{
|
||||
let mut op = db.begin_operation(BlockId::Number(0)).unwrap();
|
||||
let mut header = block::Header {
|
||||
let mut header = Header {
|
||||
number: 1,
|
||||
parent_hash: Default::default(),
|
||||
state_root: Default::default(),
|
||||
@@ -729,7 +745,7 @@ mod tests {
|
||||
|
||||
{
|
||||
let mut op = db.begin_operation(BlockId::Number(1)).unwrap();
|
||||
let mut header = block::Header {
|
||||
let mut header = Header {
|
||||
number: 1,
|
||||
parent_hash: Default::default(),
|
||||
state_root: Default::default(),
|
||||
|
||||
@@ -18,18 +18,26 @@
|
||||
|
||||
use state_machine::backend::Backend as StateBackend;
|
||||
use error;
|
||||
use primitives::block::{self, Id as BlockId};
|
||||
use primitives;
|
||||
use runtime_primitives::bft::Justification;
|
||||
use runtime_primitives::traits::Block as BlockT;
|
||||
use runtime_primitives::generic::BlockId;
|
||||
|
||||
/// Block insertion operation. Keeps hold if the inserted block state and data.
|
||||
pub trait BlockImportOperation {
|
||||
pub trait BlockImportOperation<Block: BlockT> {
|
||||
/// Associated state backend type.
|
||||
type State: StateBackend;
|
||||
|
||||
/// Returns pending state. Returns None for backends with locally-unavailable state data.
|
||||
fn state(&self) -> error::Result<Option<&Self::State>>;
|
||||
/// Append block data to the transaction.
|
||||
fn set_block_data(&mut self, header: block::Header, body: Option<block::Body>, justification: Option<primitives::bft::Justification>, is_new_best: bool) -> error::Result<()>;
|
||||
fn set_block_data(
|
||||
&mut self,
|
||||
header: Block::Header,
|
||||
body: Option<Vec<Block::Extrinsic>>,
|
||||
justification: Option<Justification<Block::Hash>>,
|
||||
is_new_best: bool
|
||||
) -> error::Result<()>;
|
||||
|
||||
/// Inject storage data into the database.
|
||||
fn update_storage(&mut self, update: <Self::State as StateBackend>::Transaction) -> error::Result<()>;
|
||||
/// Inject storage data into the database replacing any existing data.
|
||||
@@ -44,27 +52,27 @@ pub trait BlockImportOperation {
|
||||
///
|
||||
/// 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: Send + Sync {
|
||||
pub trait Backend<Block: BlockT>: Send + Sync {
|
||||
/// Associated block insertion operation type.
|
||||
type BlockImportOperation: BlockImportOperation;
|
||||
type BlockImportOperation: BlockImportOperation<Block>;
|
||||
/// Associated blockchain backend type.
|
||||
type Blockchain: ::blockchain::Backend;
|
||||
type Blockchain: ::blockchain::Backend<Block>;
|
||||
/// Associated state backend type.
|
||||
type State: StateBackend;
|
||||
|
||||
/// Begin a new block insertion transaction with given parent block id.
|
||||
/// When constructing the genesis, this is called with all-zero hash.
|
||||
fn begin_operation(&self, block: BlockId) -> error::Result<Self::BlockImportOperation>;
|
||||
fn begin_operation(&self, block: BlockId<Block>) -> error::Result<Self::BlockImportOperation>;
|
||||
/// Commit block insertion.
|
||||
fn commit_operation(&self, transaction: Self::BlockImportOperation) -> error::Result<()>;
|
||||
/// Returns reference to blockchain backend.
|
||||
fn blockchain(&self) -> &Self::Blockchain;
|
||||
/// Returns state backend with post-state of given block.
|
||||
fn state_at(&self, block: BlockId) -> error::Result<Self::State>;
|
||||
fn state_at(&self, block: BlockId<Block>) -> error::Result<Self::State>;
|
||||
}
|
||||
|
||||
/// Mark for all Backend implementations, that are making use of state data, stored locally.
|
||||
pub trait LocalBackend: Backend {}
|
||||
pub trait LocalBackend<Block: BlockT>: Backend<Block> {}
|
||||
|
||||
/// Mark for all Backend implementations, that are fetching required state data from remote nodes.
|
||||
pub trait RemoteBackend: Backend {}
|
||||
pub trait RemoteBackend<Block: BlockT>: Backend<Block> {}
|
||||
|
||||
@@ -17,80 +17,101 @@
|
||||
//! Utility struct to build a block.
|
||||
|
||||
use std::vec::Vec;
|
||||
use codec::{Joiner, Slicable};
|
||||
use codec::Slicable;
|
||||
use state_machine;
|
||||
use primitives::{Header, Block};
|
||||
use primitives::block::{Id as BlockId, Extrinsic};
|
||||
use runtime_primitives::traits::{Header as HeaderT, Hashing as HashingT, Block as BlockT, One, HashingFor};
|
||||
use runtime_primitives::generic::BlockId;
|
||||
use {backend, error, Client, CallExecutor};
|
||||
use triehash::ordered_trie_root;
|
||||
|
||||
/// Utility for building new (valid) blocks from a stream of transactions.
|
||||
pub struct BlockBuilder<B, E> where
|
||||
B: backend::Backend,
|
||||
E: CallExecutor + Clone,
|
||||
error::Error: From<<<B as backend::Backend>::State as state_machine::backend::Backend>::Error>,
|
||||
/// Utility for building new (valid) blocks from a stream of extrinsics.
|
||||
pub struct BlockBuilder<B, E, Block> where
|
||||
B: backend::Backend<Block>,
|
||||
E: CallExecutor<Block> + Clone,
|
||||
Block: BlockT,
|
||||
error::Error: From<<<B as backend::Backend<Block>>::State as state_machine::backend::Backend>::Error>,
|
||||
{
|
||||
header: Header,
|
||||
transactions: Vec<Extrinsic>,
|
||||
header: <Block as BlockT>::Header,
|
||||
extrinsics: Vec<<Block as BlockT>::Extrinsic>,
|
||||
executor: E,
|
||||
state: B::State,
|
||||
changes: state_machine::OverlayedChanges,
|
||||
}
|
||||
|
||||
impl<B, E> BlockBuilder<B, E> where
|
||||
B: backend::Backend,
|
||||
E: CallExecutor + Clone,
|
||||
error::Error: From<<<B as backend::Backend>::State as state_machine::backend::Backend>::Error>,
|
||||
impl<B, E, Block> BlockBuilder<B, E, Block> where
|
||||
B: backend::Backend<Block>,
|
||||
E: CallExecutor<Block> + Clone,
|
||||
Block: BlockT,
|
||||
error::Error: From<<<B as backend::Backend<Block>>::State as state_machine::backend::Backend>::Error>,
|
||||
{
|
||||
/// Create a new instance of builder from the given client, building on the latest block.
|
||||
pub fn new(client: &Client<B, E>) -> error::Result<Self> {
|
||||
pub fn new(client: &Client<B, E, Block>) -> error::Result<Self> {
|
||||
client.info().and_then(|i| Self::at_block(&BlockId::Hash(i.chain.best_hash), client))
|
||||
}
|
||||
|
||||
/// 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, client: &Client<B, E>) -> error::Result<Self> {
|
||||
pub fn at_block(block_id: &BlockId<Block>, client: &Client<B, E, Block>) -> error::Result<Self> {
|
||||
let number = client.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)?
|
||||
.ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{}", block_id)))?;
|
||||
|
||||
let executor = client.executor().clone();
|
||||
let state = client.state_at(block_id)?;
|
||||
let mut changes = Default::default();
|
||||
let header = <<Block as BlockT>::Header as HeaderT>::new(
|
||||
number,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
parent_hash,
|
||||
Default::default()
|
||||
);
|
||||
|
||||
executor.call_at_state(&state, &mut changes, "initialise_block", &header.encode())?;
|
||||
|
||||
Ok(BlockBuilder {
|
||||
header: Header {
|
||||
number: client.block_number_from_id(block_id)?.ok_or(error::ErrorKind::UnknownBlock(*block_id))? + 1,
|
||||
parent_hash: client.block_hash_from_id(block_id)?.ok_or(error::ErrorKind::UnknownBlock(*block_id))?,
|
||||
state_root: Default::default(),
|
||||
extrinsics_root: Default::default(),
|
||||
digest: Default::default(),
|
||||
},
|
||||
transactions: Default::default(),
|
||||
executor: client.executor().clone(),
|
||||
state: client.state_at(block_id)?,
|
||||
changes: Default::default(),
|
||||
header,
|
||||
extrinsics: Vec::new(),
|
||||
executor,
|
||||
state,
|
||||
changes,
|
||||
})
|
||||
}
|
||||
|
||||
/// Push a transaction onto the block's list of transactions. This will ensure the transaction
|
||||
/// Push onto the block's list of extrinsics. This will ensure the extrinsic
|
||||
/// 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, tx: Extrinsic) -> error::Result<()> {
|
||||
let (output, _) = self.executor.call_at_state(
|
||||
&self.state,
|
||||
&mut self.changes,
|
||||
"execute_transaction",
|
||||
&vec![].and(&self.header).and(&tx))?;
|
||||
self.header = Header::decode(&mut &output[..]).expect("Header came straight out of runtime so must be valid");
|
||||
self.transactions.push(tx);
|
||||
Ok(())
|
||||
pub fn push(&mut self, xt: <Block as BlockT>::Extrinsic) -> error::Result<()> {
|
||||
match self.executor.call_at_state(&self.state, &mut self.changes, "apply_extrinsic", &xt.encode()) {
|
||||
Ok(_) => {
|
||||
self.extrinsics.push(xt);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
self.changes.discard_prospective();
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Consume the builder to return a valid `Block` containing all pushed transactions.
|
||||
/// Consume the builder to return a valid `Block` containing all pushed extrinsics.
|
||||
pub fn bake(mut self) -> error::Result<Block> {
|
||||
self.header.extrinsics_root = ordered_trie_root(self.transactions.iter().map(Slicable::encode)).0.into();
|
||||
let (output, _) = self.executor.call_at_state(
|
||||
&self.state,
|
||||
&mut self.changes,
|
||||
"finalise_block",
|
||||
&self.header.encode())?;
|
||||
self.header = Header::decode(&mut &output[..]).expect("Header came straight out of runtime so must be valid");
|
||||
Ok(Block {
|
||||
header: self.header,
|
||||
transactions: self.transactions,
|
||||
})
|
||||
&[],
|
||||
)?;
|
||||
self.header = <<Block as BlockT>::Header as Slicable>::decode(&mut &output[..])
|
||||
.expect("Header came straight out of runtime so must be valid");
|
||||
|
||||
debug_assert_eq!(
|
||||
self.header.extrinsics_root().clone(),
|
||||
HashingFor::<Block>::ordered_trie_root(self.extrinsics.iter().map(Slicable::encode)),
|
||||
);
|
||||
|
||||
Ok(<Block as BlockT>::new(self.header, self.extrinsics))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,24 +16,26 @@
|
||||
|
||||
//! Polkadot blockchain trait
|
||||
|
||||
use primitives::block::{self, Id as BlockId};
|
||||
use primitives;
|
||||
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT};
|
||||
use runtime_primitives::generic::BlockId;
|
||||
use runtime_primitives::bft::Justification;
|
||||
|
||||
use error::Result;
|
||||
|
||||
/// Blockchain database backend. Does not perform any validation.
|
||||
pub trait Backend: Send + Sync {
|
||||
pub trait Backend<Block: BlockT>: Send + Sync {
|
||||
/// Get block header. Returns `None` if block is not found.
|
||||
fn header(&self, id: BlockId) -> Result<Option<block::Header>>;
|
||||
fn header(&self, id: BlockId<Block>) -> Result<Option<<Block as BlockT>::Header>>;
|
||||
/// Get block body. Returns `None` if block is not found.
|
||||
fn body(&self, id: BlockId) -> Result<Option<block::Body>>;
|
||||
fn body(&self, id: BlockId<Block>) -> Result<Option<Vec<<Block as BlockT>::Extrinsic>>>;
|
||||
/// Get block justification. Returns `None` if justification does not exist.
|
||||
fn justification(&self, id: BlockId) -> Result<Option<primitives::bft::Justification>>;
|
||||
fn justification(&self, id: BlockId<Block>) -> Result<Option<Justification<Block::Hash>>>;
|
||||
/// Get blockchain info.
|
||||
fn info(&self) -> Result<Info>;
|
||||
fn info(&self) -> Result<Info<Block>>;
|
||||
/// Get block status.
|
||||
fn status(&self, id: BlockId) -> Result<BlockStatus>;
|
||||
fn status(&self, id: BlockId<Block>) -> Result<BlockStatus>;
|
||||
/// Get block hash by number. Returns `None` if the header is not in the chain.
|
||||
fn hash(&self, number: block::Number) -> Result<Option<block::HeaderHash>>;
|
||||
fn hash(&self, number: <<Block as BlockT>::Header as HeaderT>::Number) -> Result<Option<<<Block as BlockT>::Header as HeaderT>::Hash>>;
|
||||
}
|
||||
|
||||
/// Block import outcome
|
||||
@@ -50,13 +52,13 @@ pub enum ImportResult<E> {
|
||||
|
||||
/// Blockchain info
|
||||
#[derive(Debug)]
|
||||
pub struct Info {
|
||||
pub struct Info<Block: BlockT> {
|
||||
/// Best block hash.
|
||||
pub best_hash: block::HeaderHash,
|
||||
pub best_hash: <<Block as BlockT>::Header as HeaderT>::Hash,
|
||||
/// Best block number.
|
||||
pub best_number: block::Number,
|
||||
pub best_number: <<Block as BlockT>::Header as HeaderT>::Number,
|
||||
/// Genesis block hash.
|
||||
pub genesis_hash: block::HeaderHash,
|
||||
pub genesis_hash: <<Block as BlockT>::Header as HeaderT>::Hash,
|
||||
}
|
||||
|
||||
/// Block status.
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
use futures::{IntoFuture, Future};
|
||||
use primitives::block::Id as BlockId;
|
||||
use runtime_primitives::generic::BlockId;
|
||||
use runtime_primitives::traits::Block as BlockT;
|
||||
use state_machine::{self, OverlayedChanges, Backend as StateBackend, CodeExecutor};
|
||||
use state_machine::backend::InMemory as InMemoryStateBackend;
|
||||
use triehash::trie_root;
|
||||
|
||||
use backend;
|
||||
use blockchain::Backend as ChainBackend;
|
||||
@@ -36,14 +36,14 @@ pub struct CallResult {
|
||||
}
|
||||
|
||||
/// Method call executor.
|
||||
pub trait CallExecutor {
|
||||
pub trait CallExecutor<B: BlockT> {
|
||||
/// Externalities error type.
|
||||
type Error: state_machine::Error;
|
||||
|
||||
/// Execute a call to a contract on top of state in a block of given hash.
|
||||
///
|
||||
/// No changes are made.
|
||||
fn call(&self, id: &BlockId, method: &str, call_data: &[u8]) -> Result<CallResult, error::Error>;
|
||||
fn call(&self, id: &BlockId<B>, method: &str, call_data: &[u8]) -> Result<CallResult, error::Error>;
|
||||
|
||||
/// Execute a call to a contract on top of given state.
|
||||
///
|
||||
@@ -81,15 +81,16 @@ impl<B, E> Clone for LocalCallExecutor<B, E> where E: Clone {
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E> CallExecutor for LocalCallExecutor<B, E>
|
||||
impl<B, E, Block> CallExecutor<Block> for LocalCallExecutor<B, E>
|
||||
where
|
||||
B: backend::LocalBackend,
|
||||
B: backend::LocalBackend<Block>,
|
||||
E: CodeExecutor,
|
||||
error::Error: From<<<B as backend::Backend>::State as StateBackend>::Error>,
|
||||
Block: BlockT,
|
||||
error::Error: From<<<B as backend::Backend<Block>>::State as StateBackend>::Error>,
|
||||
{
|
||||
type Error = E::Error;
|
||||
|
||||
fn call(&self, id: &BlockId, method: &str, call_data: &[u8]) -> error::Result<CallResult> {
|
||||
fn call(&self, id: &BlockId<Block>, method: &str, call_data: &[u8]) -> error::Result<CallResult> {
|
||||
let mut changes = OverlayedChanges::default();
|
||||
let (return_data, _) = self.call_at_state(&self.backend.state_at(*id)?, &mut changes, method, call_data)?;
|
||||
Ok(CallResult{ return_data, changes })
|
||||
@@ -113,19 +114,20 @@ impl<B, F> RemoteCallExecutor<B, F> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, F> CallExecutor for RemoteCallExecutor<B, F>
|
||||
impl<B, F, Block> CallExecutor<Block> for RemoteCallExecutor<B, F>
|
||||
where
|
||||
B: backend::RemoteBackend,
|
||||
F: Fetcher,
|
||||
error::Error: From<<<B as backend::Backend>::State as StateBackend>::Error>,
|
||||
B: backend::RemoteBackend<Block>,
|
||||
F: Fetcher<Block>,
|
||||
Block: BlockT,
|
||||
error::Error: From<<<B as backend::Backend<Block>>::State as StateBackend>::Error>,
|
||||
{
|
||||
type Error = error::Error;
|
||||
|
||||
fn call(&self, id: &BlockId, method: &str, call_data: &[u8]) -> error::Result<CallResult> {
|
||||
fn call(&self, id: &BlockId<Block>, method: &str, call_data: &[u8]) -> error::Result<CallResult> {
|
||||
let block_hash = match *id {
|
||||
BlockId::Hash(hash) => hash,
|
||||
BlockId::Number(number) => self.backend.blockchain().hash(number)?
|
||||
.ok_or_else(|| error::ErrorKind::UnknownBlock(BlockId::Number(number)))?,
|
||||
.ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{}", number)))?,
|
||||
};
|
||||
|
||||
self.fetcher.remote_call(RemoteCallRequest {
|
||||
@@ -141,22 +143,25 @@ impl<B, F> CallExecutor for RemoteCallExecutor<B, F>
|
||||
}
|
||||
|
||||
/// Check remote execution proof.
|
||||
pub fn check_execution_proof<B, E>(backend: &B, executor: &E, request: &RemoteCallRequest, remote_proof: (Vec<u8>, Vec<Vec<u8>>)) -> Result<CallResult, error::Error>
|
||||
pub fn check_execution_proof<B, E, Block>(backend: &B, executor: &E, request: &RemoteCallRequest<Block::Hash>, remote_proof: (Vec<u8>, Vec<Vec<u8>>)) -> Result<CallResult, error::Error>
|
||||
where
|
||||
B: backend::RemoteBackend,
|
||||
B: backend::RemoteBackend<Block>,
|
||||
E: CodeExecutor,
|
||||
error::Error: From<<<B as backend::Backend>::State as StateBackend>::Error>,
|
||||
Block: BlockT,
|
||||
error::Error: From<<<B as backend::Backend<Block>>::State as StateBackend>::Error>,
|
||||
{
|
||||
use runtime_primitives::traits::{Header, Hashing, HashingFor};
|
||||
|
||||
let (remote_result, remote_proof) = remote_proof;
|
||||
|
||||
let remote_state = state_from_execution_proof(remote_proof);
|
||||
let remote_state_root = trie_root(remote_state.pairs().into_iter()).0;
|
||||
let remote_state_root = HashingFor::<Block>::trie_root(remote_state.pairs().into_iter());
|
||||
|
||||
let local_header = backend.blockchain().header(BlockId::Hash(request.block))?;
|
||||
let local_header = local_header.ok_or_else(|| error::ErrorKind::UnknownBlock(BlockId::Hash(request.block)))?;
|
||||
let local_state_root = local_header.state_root;
|
||||
let local_header = local_header.ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{:?}", request.block)))?;
|
||||
let local_state_root = local_header.state_root().clone();
|
||||
|
||||
if remote_state_root != *local_state_root {
|
||||
if remote_state_root != local_state_root {
|
||||
return Err(error::ErrorKind::InvalidExecutionProof.into());
|
||||
}
|
||||
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
use std::sync::Arc;
|
||||
use futures::sync::mpsc;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use primitives::{self, block, AuthorityId};
|
||||
use primitives::block::{Id as BlockId, HeaderHash};
|
||||
use primitives::AuthorityId;
|
||||
use runtime_primitives::{bft::Justification, generic::BlockId};
|
||||
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Zero, One};
|
||||
use primitives::storage::{StorageKey, StorageData};
|
||||
use runtime_support::Hashable;
|
||||
use codec::Slicable;
|
||||
use codec::{Slicable};
|
||||
use state_machine::{self, Ext, OverlayedChanges, Backend as StateBackend, CodeExecutor};
|
||||
|
||||
use backend::{self, BlockImportOperation};
|
||||
@@ -32,45 +32,45 @@ use call_executor::{CallExecutor, LocalCallExecutor};
|
||||
use {error, in_mem, block_builder, runtime_io, bft};
|
||||
|
||||
/// Type that implements `futures::Stream` of block import events.
|
||||
pub type BlockchainEventStream = mpsc::UnboundedReceiver<BlockImportNotification>;
|
||||
pub type BlockchainEventStream<Block> = mpsc::UnboundedReceiver<BlockImportNotification<Block>>;
|
||||
|
||||
/// Polkadot Client genesis block builder.
|
||||
pub trait GenesisBuilder {
|
||||
pub trait GenesisBuilder<B: BlockT> {
|
||||
/// Build genesis block.
|
||||
fn build(self) -> (block::Header, Vec<(Vec<u8>, Vec<u8>)>);
|
||||
fn build(self) -> (B::Header, Vec<(Vec<u8>, Vec<u8>)>);
|
||||
}
|
||||
|
||||
/// Polkadot Client
|
||||
pub struct Client<B, E> {
|
||||
pub struct Client<B, E, Block> where Block: BlockT {
|
||||
backend: Arc<B>,
|
||||
executor: E,
|
||||
import_notification_sinks: Mutex<Vec<mpsc::UnboundedSender<BlockImportNotification>>>,
|
||||
import_notification_sinks: Mutex<Vec<mpsc::UnboundedSender<BlockImportNotification<Block>>>>,
|
||||
import_lock: Mutex<()>,
|
||||
importing_block: RwLock<Option<HeaderHash>>, // holds the block hash currently being imported. TODO: replace this with block queue
|
||||
importing_block: RwLock<Option<Block::Hash>>, // holds the block hash currently being imported. TODO: replace this with block queue
|
||||
}
|
||||
|
||||
/// A source of blockchain evenets.
|
||||
pub trait BlockchainEvents {
|
||||
pub trait BlockchainEvents<Block: BlockT> {
|
||||
/// Get block import event stream.
|
||||
fn import_notification_stream(&self) -> BlockchainEventStream;
|
||||
fn import_notification_stream(&self) -> mpsc::UnboundedReceiver<BlockImportNotification<Block>>;
|
||||
}
|
||||
|
||||
/// Chain head information.
|
||||
pub trait ChainHead {
|
||||
pub trait ChainHead<Block: BlockT> {
|
||||
/// Get best block header.
|
||||
fn best_block_header(&self) -> Result<block::Header, error::Error>;
|
||||
fn best_block_header(&self) -> Result<<Block as BlockT>::Header, error::Error>;
|
||||
}
|
||||
|
||||
/// Client info
|
||||
// TODO: split queue info from chain info and amalgamate into single struct.
|
||||
#[derive(Debug)]
|
||||
pub struct ClientInfo {
|
||||
pub struct ClientInfo<Block: BlockT> {
|
||||
/// Best block hash.
|
||||
pub chain: ChainInfo,
|
||||
pub chain: ChainInfo<Block>,
|
||||
/// Best block number in the queue.
|
||||
pub best_queued_number: Option<block::Number>,
|
||||
pub best_queued_number: Option<<<Block as BlockT>::Header as HeaderT>::Number>,
|
||||
/// Best queued block hash.
|
||||
pub best_queued_hash: Option<block::HeaderHash>,
|
||||
pub best_queued_hash: Option<Block::Hash>,
|
||||
}
|
||||
|
||||
/// Block import result.
|
||||
@@ -120,49 +120,51 @@ pub enum BlockOrigin {
|
||||
|
||||
/// Summary of an imported block
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BlockImportNotification {
|
||||
pub struct BlockImportNotification<Block: BlockT> {
|
||||
/// Imported block header hash.
|
||||
pub hash: block::HeaderHash,
|
||||
pub hash: Block::Hash,
|
||||
/// Imported block origin.
|
||||
pub origin: BlockOrigin,
|
||||
/// Imported block header.
|
||||
pub header: block::Header,
|
||||
pub header: Block::Header,
|
||||
/// Is this the new best block.
|
||||
pub is_new_best: bool,
|
||||
}
|
||||
|
||||
/// A header paired with a justification which has already been checked.
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct JustifiedHeader {
|
||||
header: block::Header,
|
||||
justification: bft::Justification,
|
||||
pub struct JustifiedHeader<Block: BlockT> {
|
||||
header: <Block as BlockT>::Header,
|
||||
justification: ::bft::Justification<Block::Hash>,
|
||||
}
|
||||
|
||||
impl JustifiedHeader {
|
||||
impl<Block: BlockT> JustifiedHeader<Block> {
|
||||
/// Deconstruct the justified header into parts.
|
||||
pub fn into_inner(self) -> (block::Header, bft::Justification) {
|
||||
pub fn into_inner(self) -> (<Block as BlockT>::Header, ::bft::Justification<Block::Hash>) {
|
||||
(self.header, self.justification)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an instance of in-memory client.
|
||||
pub fn new_in_mem<E, F>(
|
||||
pub fn new_in_mem<E, F, Block>(
|
||||
executor: E,
|
||||
genesis_builder: F
|
||||
) -> error::Result<Client<in_mem::Backend, LocalCallExecutor<in_mem::Backend, E>>>
|
||||
) -> error::Result<Client<in_mem::Backend<Block>, LocalCallExecutor<in_mem::Backend<Block>, E>, Block>>
|
||||
where
|
||||
E: CodeExecutor,
|
||||
F: GenesisBuilder,
|
||||
F: GenesisBuilder<Block>,
|
||||
Block: BlockT,
|
||||
{
|
||||
let backend = Arc::new(in_mem::Backend::new());
|
||||
let executor = LocalCallExecutor::new(backend.clone(), executor);
|
||||
Client::new(backend, executor, genesis_builder)
|
||||
}
|
||||
|
||||
impl<B, E> Client<B, E> where
|
||||
B: backend::Backend,
|
||||
E: CallExecutor,
|
||||
error::Error: From<<<B as backend::Backend>::State as StateBackend>::Error>,
|
||||
impl<B, E, Block: BlockT> Client<B, E, Block> where
|
||||
B: backend::Backend<Block>,
|
||||
E: CallExecutor<Block>,
|
||||
Block: BlockT,
|
||||
error::Error: From<<<B as backend::Backend<Block>>::State as StateBackend>::Error>,
|
||||
{
|
||||
/// Creates new Polkadot Client with given blockchain and code executor.
|
||||
pub fn new<F>(
|
||||
@@ -171,12 +173,12 @@ impl<B, E> Client<B, E> where
|
||||
genesis_builder: F,
|
||||
) -> error::Result<Self>
|
||||
where
|
||||
F: GenesisBuilder
|
||||
F: GenesisBuilder<Block>
|
||||
{
|
||||
if backend.blockchain().header(BlockId::Number(0))?.is_none() {
|
||||
if backend.blockchain().header(BlockId::Number(Zero::zero()))?.is_none() {
|
||||
trace!("Empty database, writing genesis block");
|
||||
let (genesis_header, genesis_store) = genesis_builder.build();
|
||||
let mut op = backend.begin_operation(BlockId::Hash(block::HeaderHash::default()))?;
|
||||
let mut op = backend.begin_operation(BlockId::Hash(Default::default()))?;
|
||||
op.reset_storage(genesis_store.into_iter())?;
|
||||
op.set_block_data(genesis_header, Some(vec![]), None, true)?;
|
||||
backend.commit_operation(op)?;
|
||||
@@ -191,7 +193,7 @@ impl<B, E> Client<B, E> where
|
||||
}
|
||||
|
||||
/// Get a reference to the state at a given block.
|
||||
pub fn state_at(&self, block: &BlockId) -> error::Result<B::State> {
|
||||
pub fn state_at(&self, block: &BlockId<Block>) -> error::Result<B::State> {
|
||||
self.backend.state_at(*block)
|
||||
}
|
||||
|
||||
@@ -201,7 +203,7 @@ impl<B, E> Client<B, E> where
|
||||
}
|
||||
|
||||
/// Return single storage entry of contract under given address in state in a block of given hash.
|
||||
pub fn storage(&self, id: &BlockId, key: &StorageKey) -> error::Result<StorageData> {
|
||||
pub fn storage(&self, id: &BlockId<Block>, key: &StorageKey) -> error::Result<StorageData> {
|
||||
Ok(StorageData(self.state_at(id)?
|
||||
.storage(&key.0)?
|
||||
.ok_or_else(|| error::ErrorKind::NoValueForKey(key.0.clone()))?
|
||||
@@ -209,12 +211,12 @@ impl<B, E> Client<B, E> where
|
||||
}
|
||||
|
||||
/// Get the code at a given block.
|
||||
pub fn code_at(&self, id: &BlockId) -> error::Result<Vec<u8>> {
|
||||
pub fn code_at(&self, id: &BlockId<Block>) -> error::Result<Vec<u8>> {
|
||||
self.storage(id, &StorageKey(b":code".to_vec())).map(|data| data.0)
|
||||
}
|
||||
|
||||
/// Get the set of authorities at a given block.
|
||||
pub fn authorities_at(&self, id: &BlockId) -> error::Result<Vec<AuthorityId>> {
|
||||
pub fn authorities_at(&self, id: &BlockId<Block>) -> error::Result<Vec<AuthorityId>> {
|
||||
self.executor.call(id, "authorities",&[])
|
||||
.and_then(|r| Vec::<AuthorityId>::decode(&mut &r.return_data[..])
|
||||
.ok_or(error::ErrorKind::AuthLenInvalid.into()))
|
||||
@@ -229,7 +231,7 @@ impl<B, E> Client<B, E> where
|
||||
/// AND returning execution proof.
|
||||
///
|
||||
/// No changes are made.
|
||||
pub fn execution_proof(&self, id: &BlockId, method: &str, call_data: &[u8]) -> error::Result<(Vec<u8>, Vec<Vec<u8>>)> {
|
||||
pub fn execution_proof(&self, id: &BlockId<Block>, method: &str, call_data: &[u8]) -> error::Result<(Vec<u8>, Vec<Vec<u8>>)> {
|
||||
use call_executor::state_to_execution_proof;
|
||||
|
||||
let result = self.executor.call(id, method, call_data);
|
||||
@@ -248,7 +250,7 @@ impl<B, E> Client<B, E> where
|
||||
/// Set up the native execution environment to call into a native runtime code.
|
||||
pub fn using_environment_at<F: FnOnce() -> T, T>(
|
||||
&self,
|
||||
id: &BlockId,
|
||||
id: &BlockId<Block>,
|
||||
overlay: &mut OverlayedChanges,
|
||||
f: F
|
||||
) -> error::Result<T> {
|
||||
@@ -256,24 +258,29 @@ impl<B, E> Client<B, E> where
|
||||
}
|
||||
|
||||
/// Create a new block, built on the head of the chain.
|
||||
pub fn new_block(&self) -> error::Result<block_builder::BlockBuilder<B, E>> where E: Clone {
|
||||
pub fn new_block(&self) -> error::Result<block_builder::BlockBuilder<B, E, Block>> where E: Clone {
|
||||
block_builder::BlockBuilder::new(self)
|
||||
}
|
||||
|
||||
/// Create a new block, built on top of `parent`.
|
||||
pub fn new_block_at(&self, parent: &BlockId) -> error::Result<block_builder::BlockBuilder<B, E>> where E: Clone {
|
||||
pub fn new_block_at(&self, parent: &BlockId<Block>) -> error::Result<block_builder::BlockBuilder<B, E, Block>> where E: Clone {
|
||||
block_builder::BlockBuilder::at_block(parent, &self)
|
||||
}
|
||||
|
||||
/// Check a header's justification.
|
||||
pub fn check_justification(
|
||||
&self,
|
||||
header: block::Header,
|
||||
justification: bft::UncheckedJustification,
|
||||
) -> error::Result<JustifiedHeader> {
|
||||
let authorities = self.authorities_at(&BlockId::Hash(header.parent_hash))?;
|
||||
let just = bft::check_justification(&authorities[..], header.parent_hash, justification)
|
||||
.map_err(|_| error::ErrorKind::BadJustification(BlockId::Hash(header.blake2_256().into())))?;
|
||||
header: <Block as BlockT>::Header,
|
||||
justification: ::bft::UncheckedJustification<Block::Hash>,
|
||||
) -> error::Result<JustifiedHeader<Block>> {
|
||||
let parent_hash = header.parent_hash().clone();
|
||||
let authorities = self.authorities_at(&BlockId::Hash(parent_hash))?;
|
||||
let just = ::bft::check_justification::<Block>(&authorities[..], parent_hash, justification)
|
||||
.map_err(|_|
|
||||
error::ErrorKind::BadJustification(
|
||||
format!("{}", header.hash())
|
||||
)
|
||||
)?;
|
||||
Ok(JustifiedHeader {
|
||||
header,
|
||||
justification: just,
|
||||
@@ -284,16 +291,16 @@ impl<B, E> Client<B, E> where
|
||||
pub fn import_block(
|
||||
&self,
|
||||
origin: BlockOrigin,
|
||||
header: JustifiedHeader,
|
||||
body: Option<block::Body>,
|
||||
header: JustifiedHeader<Block>,
|
||||
body: Option<Vec<<Block as BlockT>::Extrinsic>>,
|
||||
) -> error::Result<ImportResult> {
|
||||
let (header, justification) = header.into_inner();
|
||||
match self.backend.blockchain().status(BlockId::Hash(header.parent_hash))? {
|
||||
let parent_hash = header.parent_hash().clone();
|
||||
match self.backend.blockchain().status(BlockId::Hash(parent_hash))? {
|
||||
blockchain::BlockStatus::InChain => {},
|
||||
blockchain::BlockStatus::Unknown => return Ok(ImportResult::UnknownParent),
|
||||
}
|
||||
let hash: block::HeaderHash = header.blake2_256().into();
|
||||
|
||||
let hash = header.hash();
|
||||
let _import_lock = self.import_lock.lock();
|
||||
*self.importing_block.write() = Some(hash);
|
||||
let result = self.execute_and_import_block(origin, hash, header, justification, body);
|
||||
@@ -304,17 +311,18 @@ impl<B, E> Client<B, E> where
|
||||
fn execute_and_import_block(
|
||||
&self,
|
||||
origin: BlockOrigin,
|
||||
hash: HeaderHash,
|
||||
header: block::Header,
|
||||
justification: bft::Justification,
|
||||
body: Option<block::Body>,
|
||||
hash: Block::Hash,
|
||||
header: Block::Header,
|
||||
justification: bft::Justification<Block::Hash>,
|
||||
body: Option<Vec<Block::Extrinsic>>,
|
||||
) -> error::Result<ImportResult> {
|
||||
let parent_hash = header.parent_hash().clone();
|
||||
match self.backend.blockchain().status(BlockId::Hash(hash))? {
|
||||
blockchain::BlockStatus::InChain => return Ok(ImportResult::AlreadyInChain),
|
||||
blockchain::BlockStatus::Unknown => {},
|
||||
}
|
||||
|
||||
let mut transaction = self.backend.begin_operation(BlockId::Hash(header.parent_hash))?;
|
||||
let mut transaction = self.backend.begin_operation(BlockId::Hash(parent_hash))?;
|
||||
let storage_update = match transaction.state()? {
|
||||
Some(transaction_state) => {
|
||||
let mut overlay = Default::default();
|
||||
@@ -322,7 +330,7 @@ impl<B, E> Client<B, E> where
|
||||
transaction_state,
|
||||
&mut overlay,
|
||||
"execute_block",
|
||||
&block::Block { header: header.clone(), transactions: body.clone().unwrap_or_default().clone() }.encode(),
|
||||
&<Block as BlockT>::new(header.clone(), body.clone().unwrap_or_default()).encode()
|
||||
)?;
|
||||
|
||||
Some(storage_update)
|
||||
@@ -330,15 +338,15 @@ impl<B, E> Client<B, E> where
|
||||
None => None,
|
||||
};
|
||||
|
||||
let is_new_best = header.number == self.backend.blockchain().info()?.best_number + 1;
|
||||
trace!("Imported {}, (#{}), best={}, origin={:?}", hash, header.number, is_new_best, origin);
|
||||
let is_new_best = header.number() == &(self.backend.blockchain().info()?.best_number + One::one());
|
||||
trace!("Imported {}, (#{}), best={}, origin={:?}", hash, header.number(), is_new_best, origin);
|
||||
transaction.set_block_data(header.clone(), body, Some(justification.uncheck().into()), is_new_best)?;
|
||||
if let Some(storage_update) = storage_update {
|
||||
transaction.update_storage(storage_update)?;
|
||||
}
|
||||
self.backend.commit_operation(transaction)?;
|
||||
if origin == BlockOrigin::NetworkBroadcast || origin == BlockOrigin::Own || origin == BlockOrigin::ConsensusBroadcast {
|
||||
let notification = BlockImportNotification {
|
||||
let notification = BlockImportNotification::<Block> {
|
||||
hash: hash,
|
||||
origin: origin,
|
||||
header: header,
|
||||
@@ -351,7 +359,7 @@ impl<B, E> Client<B, E> where
|
||||
}
|
||||
|
||||
/// Get blockchain info.
|
||||
pub fn info(&self) -> error::Result<ClientInfo> {
|
||||
pub fn info(&self) -> error::Result<ClientInfo<Block>> {
|
||||
let info = self.backend.blockchain().info().map_err(|e| error::Error::from_blockchain(Box::new(e)))?;
|
||||
Ok(ClientInfo {
|
||||
chain: info,
|
||||
@@ -361,7 +369,7 @@ impl<B, E> Client<B, E> where
|
||||
}
|
||||
|
||||
/// Get block status.
|
||||
pub fn block_status(&self, id: &BlockId) -> error::Result<BlockStatus> {
|
||||
pub fn block_status(&self, id: &BlockId<Block>) -> error::Result<BlockStatus> {
|
||||
// TODO: more efficient implementation
|
||||
if let BlockId::Hash(ref h) = id {
|
||||
if self.importing_block.read().as_ref().map_or(false, |importing| h == importing) {
|
||||
@@ -375,12 +383,12 @@ impl<B, E> Client<B, E> where
|
||||
}
|
||||
|
||||
/// Get block hash by number.
|
||||
pub fn block_hash(&self, block_number: block::Number) -> error::Result<Option<block::HeaderHash>> {
|
||||
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) -> error::Result<Option<block::HeaderHash>> {
|
||||
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),
|
||||
@@ -388,83 +396,91 @@ impl<B, E> Client<B, E> where
|
||||
}
|
||||
|
||||
/// Convert an arbitrary block ID into a block hash.
|
||||
pub fn block_number_from_id(&self, id: &BlockId) -> error::Result<Option<block::Number>> {
|
||||
pub fn block_number_from_id(&self, id: &BlockId<Block>) -> error::Result<Option<<<Block as BlockT>::Header as HeaderT>::Number>> {
|
||||
match *id {
|
||||
BlockId::Hash(_) => Ok(self.header(id)?.map(|h| h.number)),
|
||||
BlockId::Hash(_) => Ok(self.header(id)?.map(|h| h.number().clone())),
|
||||
BlockId::Number(n) => Ok(Some(n)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get block header by id.
|
||||
pub fn header(&self, id: &BlockId) -> error::Result<Option<block::Header>> {
|
||||
pub fn header(&self, id: &BlockId<Block>) -> error::Result<Option<<Block as BlockT>::Header>> {
|
||||
self.backend.blockchain().header(*id)
|
||||
}
|
||||
|
||||
/// Get block body by id.
|
||||
pub fn body(&self, id: &BlockId) -> error::Result<Option<block::Body>> {
|
||||
pub fn body(&self, id: &BlockId<Block>) -> error::Result<Option<Vec<<Block as BlockT>::Extrinsic>>> {
|
||||
self.backend.blockchain().body(*id)
|
||||
}
|
||||
|
||||
/// Get block justification set by id.
|
||||
pub fn justification(&self, id: &BlockId) -> error::Result<Option<primitives::bft::Justification>> {
|
||||
pub fn justification(&self, id: &BlockId<Block>) -> error::Result<Option<Justification<Block::Hash>>> {
|
||||
self.backend.blockchain().justification(*id)
|
||||
}
|
||||
|
||||
/// Get best block header.
|
||||
pub fn best_block_header(&self) -> error::Result<block::Header> {
|
||||
pub fn best_block_header(&self) -> error::Result<<Block as BlockT>::Header> {
|
||||
let info = self.backend.blockchain().info().map_err(|e| error::Error::from_blockchain(Box::new(e)))?;
|
||||
Ok(self.header(&BlockId::Hash(info.best_hash))?.expect("Best block header must always exist"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E> bft::BlockImport for Client<B, E>
|
||||
impl<B, E, Block> bft::BlockImport<Block> for Client<B, E, Block>
|
||||
where
|
||||
B: backend::Backend,
|
||||
E: CallExecutor,
|
||||
B: backend::Backend<Block>,
|
||||
E: CallExecutor<Block>,
|
||||
Block: BlockT,
|
||||
error::Error: From<<B::State as state_machine::backend::Backend>::Error>
|
||||
{
|
||||
fn import_block(&self, block: block::Block, justification: bft::Justification) {
|
||||
fn import_block(&self, block: Block, justification: ::bft::Justification<Block::Hash>) {
|
||||
let (header, extrinsics) = block.deconstruct();
|
||||
let justified_header = JustifiedHeader {
|
||||
header: block.header,
|
||||
header: header,
|
||||
justification,
|
||||
};
|
||||
|
||||
let _ = self.import_block(BlockOrigin::ConsensusBroadcast, justified_header, Some(block.transactions));
|
||||
let _ = self.import_block(BlockOrigin::ConsensusBroadcast, justified_header, Some(extrinsics));
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E> bft::Authorities for Client<B, E>
|
||||
impl<B, E, Block> bft::Authorities<Block> for Client<B, E, Block>
|
||||
where
|
||||
B: backend::Backend,
|
||||
E: CallExecutor,
|
||||
error::Error: From<<B::State as state_machine::backend::Backend>::Error>
|
||||
B: backend::Backend<Block>,
|
||||
E: CallExecutor<Block>,
|
||||
Block: BlockT,
|
||||
error::Error: From<<B::State as state_machine::backend::Backend>::Error>,
|
||||
{
|
||||
fn authorities(&self, at: &BlockId) -> Result<Vec<AuthorityId>, bft::Error> {
|
||||
self.authorities_at(at).map_err(|_| bft::ErrorKind::StateUnavailable(*at).into())
|
||||
fn authorities(&self, at: &BlockId<Block>) -> Result<Vec<AuthorityId>, bft::Error> {
|
||||
self.authorities_at(at).map_err(|_| {
|
||||
let descriptor = format!("{:?}", at);
|
||||
bft::ErrorKind::StateUnavailable(descriptor).into()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E> BlockchainEvents for Client<B, E>
|
||||
impl<B, E, Block> BlockchainEvents<Block> for Client<B, E, Block>
|
||||
where
|
||||
B: backend::Backend,
|
||||
E: CallExecutor,
|
||||
B: backend::Backend<Block>,
|
||||
E: CallExecutor<Block>,
|
||||
Block: BlockT,
|
||||
error::Error: From<<B::State as state_machine::backend::Backend>::Error>
|
||||
{
|
||||
/// Get block import event stream.
|
||||
fn import_notification_stream(&self) -> BlockchainEventStream {
|
||||
fn import_notification_stream(&self) -> mpsc::UnboundedReceiver<BlockImportNotification<Block>> {
|
||||
let (sink, stream) = mpsc::unbounded();
|
||||
self.import_notification_sinks.lock().push(sink);
|
||||
stream
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E> ChainHead for Client<B, E>
|
||||
impl<B, E, Block> ChainHead<Block> for Client<B, E, Block>
|
||||
where
|
||||
B: backend::Backend,
|
||||
E: CallExecutor,
|
||||
B: backend::Backend<Block>,
|
||||
E: CallExecutor<Block>,
|
||||
Block: BlockT,
|
||||
error::Error: From<<B::State as state_machine::backend::Backend>::Error>
|
||||
{
|
||||
fn best_block_header(&self) -> error::Result<block::Header> {
|
||||
fn best_block_header(&self) -> error::Result<<Block as BlockT>::Header> {
|
||||
Client::best_block_header(self)
|
||||
}
|
||||
}
|
||||
@@ -474,20 +490,18 @@ mod tests {
|
||||
use super::*;
|
||||
use codec::Slicable;
|
||||
use keyring::Keyring;
|
||||
use primitives::block::Extrinsic as PrimitiveExtrinsic;
|
||||
use test_client::{self, TestClient};
|
||||
use test_client::client::BlockOrigin;
|
||||
use test_client::runtime as test_runtime;
|
||||
use test_client::runtime::{UncheckedTransaction, Transaction};
|
||||
use test_client::runtime::{Transfer, Extrinsic};
|
||||
|
||||
#[test]
|
||||
fn client_initialises_from_genesis_ok() {
|
||||
let client = test_client::new();
|
||||
let genesis_hash = client.block_hash(0).unwrap().unwrap();
|
||||
|
||||
assert_eq!(client.using_environment(|| test_runtime::system::latest_block_hash()).unwrap(), genesis_hash);
|
||||
assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Alice.to_raw_public())).unwrap(), 1000);
|
||||
assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Ferdie.to_raw_public())).unwrap(), 0);
|
||||
assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Alice.to_raw_public().into())).unwrap(), 1000);
|
||||
assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Ferdie.to_raw_public().into())).unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -511,17 +525,11 @@ mod tests {
|
||||
client.justify_and_import(BlockOrigin::Own, builder.bake().unwrap()).unwrap();
|
||||
|
||||
assert_eq!(client.info().unwrap().chain.best_number, 1);
|
||||
assert_eq!(client.using_environment(|| test_runtime::system::latest_block_hash()).unwrap(), client.block_hash(1).unwrap().unwrap());
|
||||
}
|
||||
|
||||
trait Signable {
|
||||
fn signed(self) -> PrimitiveExtrinsic;
|
||||
}
|
||||
impl Signable for Transaction {
|
||||
fn signed(self) -> PrimitiveExtrinsic {
|
||||
let signature = Keyring::from_raw_public(self.from.clone()).unwrap().sign(&self.encode());
|
||||
PrimitiveExtrinsic::decode(&mut UncheckedTransaction { signature, tx: self }.encode().as_ref()).unwrap()
|
||||
}
|
||||
fn sign_tx(tx: Transfer) -> Extrinsic {
|
||||
let signature = Keyring::from_raw_public(tx.from.0.clone()).unwrap().sign(&tx.encode()).into();
|
||||
Extrinsic { transfer: tx, signature }
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -530,18 +538,18 @@ mod tests {
|
||||
|
||||
let mut builder = client.new_block().unwrap();
|
||||
|
||||
builder.push(Transaction {
|
||||
from: Keyring::Alice.to_raw_public(),
|
||||
to: Keyring::Ferdie.to_raw_public(),
|
||||
builder.push(sign_tx(Transfer {
|
||||
from: Keyring::Alice.to_raw_public().into(),
|
||||
to: Keyring::Ferdie.to_raw_public().into(),
|
||||
amount: 42,
|
||||
nonce: 0
|
||||
}.signed()).unwrap();
|
||||
nonce: 0,
|
||||
})).unwrap();
|
||||
|
||||
client.justify_and_import(BlockOrigin::Own, builder.bake().unwrap()).unwrap();
|
||||
|
||||
assert_eq!(client.info().unwrap().chain.best_number, 1);
|
||||
assert!(client.state_at(&BlockId::Number(1)).unwrap() != client.state_at(&BlockId::Number(0)).unwrap());
|
||||
assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Alice.to_raw_public())).unwrap(), 958);
|
||||
assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Ferdie.to_raw_public())).unwrap(), 42);
|
||||
assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Alice.to_raw_public().into())).unwrap(), 958);
|
||||
assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Ferdie.to_raw_public().into())).unwrap(), 42);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,9 +29,9 @@ error_chain! {
|
||||
}
|
||||
|
||||
/// Unknown block.
|
||||
UnknownBlock(h: ::primitives::block::Id) {
|
||||
UnknownBlock(h: String) {
|
||||
description("unknown block"),
|
||||
display("UnknownBlock: {}", h),
|
||||
display("UnknownBlock: {}", &*h),
|
||||
}
|
||||
|
||||
/// Execution error.
|
||||
@@ -77,9 +77,9 @@ error_chain! {
|
||||
}
|
||||
|
||||
/// Bad justification for header.
|
||||
BadJustification(h: ::primitives::block::Id) {
|
||||
BadJustification(h: String) {
|
||||
description("bad justification for header"),
|
||||
display("bad justification for header: {}", h),
|
||||
display("bad justification for header: {}", &*h),
|
||||
}
|
||||
|
||||
/// Not available on light client.
|
||||
@@ -110,8 +110,8 @@ impl From<Box<state_machine::Error>> for Error {
|
||||
}
|
||||
|
||||
impl From<state_machine::backend::Void> for Error {
|
||||
fn from(_e: state_machine::backend::Void) -> Self {
|
||||
unreachable!()
|
||||
fn from(e: state_machine::backend::Void) -> Self {
|
||||
match e {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,54 +17,56 @@
|
||||
//! Tool for creating the genesis block.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use primitives::{Block, Header};
|
||||
use triehash::trie_root;
|
||||
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Hashing as HashingT, Zero};
|
||||
|
||||
/// Create a genesis block, given the initial storage.
|
||||
pub fn construct_genesis_block(storage: &HashMap<Vec<u8>, Vec<u8>>) -> Block {
|
||||
let state_root = trie_root(storage.clone().into_iter()).0.into();
|
||||
let header = Header {
|
||||
parent_hash: Default::default(),
|
||||
number: 0,
|
||||
state_root,
|
||||
extrinsics_root: trie_root(vec![].into_iter()).0.into(),
|
||||
digest: Default::default(),
|
||||
};
|
||||
Block {
|
||||
header,
|
||||
transactions: vec![],
|
||||
}
|
||||
pub fn construct_genesis_block<
|
||||
Block: BlockT
|
||||
> (
|
||||
storage: &HashMap<Vec<u8>, Vec<u8>>
|
||||
) -> Block {
|
||||
let state_root = <<<Block as BlockT>::Header as HeaderT>::Hashing as HashingT>::trie_root(storage.clone().into_iter());
|
||||
let extrinsics_root = <<<Block as BlockT>::Header as HeaderT>::Hashing as HashingT>::trie_root(::std::iter::empty::<(&[u8], &[u8])>());
|
||||
Block::new(
|
||||
<<Block as BlockT>::Header as HeaderT>::new(
|
||||
Zero::zero(),
|
||||
extrinsics_root,
|
||||
state_root,
|
||||
Default::default(),
|
||||
Default::default()
|
||||
),
|
||||
Default::default()
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use codec::{Slicable, Joiner};
|
||||
use runtime_support::Hashable;
|
||||
use keyring::Keyring;
|
||||
use executor::WasmExecutor;
|
||||
use state_machine::{execute, OverlayedChanges};
|
||||
use state_machine::backend::InMemory;
|
||||
use test_client;
|
||||
use test_client::runtime::genesismap::{GenesisConfig, additional_storage_with_genesis};
|
||||
use test_client::runtime::{Hash, Block, BlockNumber, Header, Digest, Transaction,
|
||||
UncheckedTransaction};
|
||||
use test_client::runtime::{Hash, Transfer, Block, BlockNumber, Header, Digest, Extrinsic};
|
||||
use ed25519::{Public, Pair};
|
||||
|
||||
native_executor_instance!(Executor, test_client::runtime::api::dispatch, include_bytes!("../../test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm"));
|
||||
|
||||
fn construct_block(backend: &InMemory, number: BlockNumber, parent_hash: Hash, state_root: Hash, txs: Vec<Transaction>) -> (Vec<u8>, Hash) {
|
||||
fn construct_block(backend: &InMemory, number: BlockNumber, parent_hash: Hash, state_root: Hash, txs: Vec<Transfer>) -> (Vec<u8>, Hash) {
|
||||
use triehash::ordered_trie_root;
|
||||
|
||||
let transactions = txs.into_iter().map(|tx| {
|
||||
let signature = Pair::from(Keyring::from_public(Public::from_raw(tx.from)).unwrap())
|
||||
.sign(&tx.encode());
|
||||
let signature = Pair::from(Keyring::from_public(Public::from_raw(tx.from.0)).unwrap())
|
||||
.sign(&tx.encode()).into();
|
||||
|
||||
UncheckedTransaction { tx, signature }
|
||||
Extrinsic { transfer: tx, signature }
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
let extrinsics_root = ordered_trie_root(transactions.iter().map(Slicable::encode)).0.into();
|
||||
|
||||
println!("root before: {:?}", extrinsics_root);
|
||||
let mut header = Header {
|
||||
parent_hash,
|
||||
number,
|
||||
@@ -72,19 +74,25 @@ mod tests {
|
||||
extrinsics_root,
|
||||
digest: Digest { logs: vec![], },
|
||||
};
|
||||
let hash = header.blake2_256();
|
||||
|
||||
let hash = header.hash();
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
|
||||
execute(
|
||||
backend,
|
||||
&mut overlay,
|
||||
&Executor::new(),
|
||||
"initialise_block",
|
||||
&header.encode(),
|
||||
).unwrap();
|
||||
|
||||
for tx in transactions.iter() {
|
||||
let (ret_data, _) = execute(
|
||||
execute(
|
||||
backend,
|
||||
&mut overlay,
|
||||
&Executor::new(),
|
||||
"execute_transaction",
|
||||
&vec![].and(&header).and(tx)
|
||||
"apply_extrinsic",
|
||||
&tx.encode(),
|
||||
).unwrap();
|
||||
header = Header::decode(&mut &ret_data[..]).unwrap();
|
||||
}
|
||||
|
||||
let (ret_data, _) = execute(
|
||||
@@ -92,11 +100,12 @@ mod tests {
|
||||
&mut overlay,
|
||||
&Executor::new(),
|
||||
"finalise_block",
|
||||
&vec![].and(&header)
|
||||
&[]
|
||||
).unwrap();
|
||||
header = Header::decode(&mut &ret_data[..]).unwrap();
|
||||
println!("root after: {:?}", header.extrinsics_root);
|
||||
|
||||
(vec![].and(&Block { header, transactions }), hash.into())
|
||||
(vec![].and(&Block { header, extrinsics: transactions }), hash)
|
||||
}
|
||||
|
||||
fn block1(genesis_hash: Hash, backend: &InMemory) -> (Vec<u8>, Hash) {
|
||||
@@ -105,9 +114,9 @@ mod tests {
|
||||
1,
|
||||
genesis_hash,
|
||||
hex!("25e5b37074063ab75c889326246640729b40d0c86932edc527bc80db0e04fe5c").into(),
|
||||
vec![Transaction {
|
||||
from: Keyring::One.to_raw_public(),
|
||||
to: Keyring::Two.to_raw_public(),
|
||||
vec![Transfer {
|
||||
from: Keyring::One.to_raw_public().into(),
|
||||
to: Keyring::Two.to_raw_public().into(),
|
||||
amount: 69,
|
||||
nonce: 0,
|
||||
}]
|
||||
@@ -119,8 +128,8 @@ mod tests {
|
||||
let mut storage = GenesisConfig::new_simple(
|
||||
vec![Keyring::One.to_raw_public(), Keyring::Two.to_raw_public()], 1000
|
||||
).genesis_map();
|
||||
let block = construct_genesis_block(&storage);
|
||||
let genesis_hash = block.header.blake2_256().into();
|
||||
let block = construct_genesis_block::<Block>(&storage);
|
||||
let genesis_hash = block.header.hash();
|
||||
storage.extend(additional_storage_with_genesis(&block).into_iter());
|
||||
|
||||
let backend = InMemory::from(storage);
|
||||
@@ -141,8 +150,8 @@ mod tests {
|
||||
let mut storage = GenesisConfig::new_simple(
|
||||
vec![Keyring::One.to_raw_public(), Keyring::Two.to_raw_public()], 1000
|
||||
).genesis_map();
|
||||
let block = construct_genesis_block(&storage);
|
||||
let genesis_hash = block.header.blake2_256().into();
|
||||
let block = construct_genesis_block::<Block>(&storage);
|
||||
let genesis_hash = block.header.hash();
|
||||
storage.extend(additional_storage_with_genesis(&block).into_iter());
|
||||
|
||||
let backend = InMemory::from(storage);
|
||||
@@ -164,8 +173,8 @@ mod tests {
|
||||
let mut storage = GenesisConfig::new_simple(
|
||||
vec![Keyring::One.to_raw_public(), Keyring::Two.to_raw_public()], 68
|
||||
).genesis_map();
|
||||
let block = construct_genesis_block(&storage);
|
||||
let genesis_hash = block.header.blake2_256().into();
|
||||
let block = construct_genesis_block::<Block>(&storage);
|
||||
let genesis_hash = block.header.hash();
|
||||
storage.extend(additional_storage_with_genesis(&block).into_iter());
|
||||
|
||||
let backend = InMemory::from(storage);
|
||||
|
||||
@@ -20,95 +20,136 @@ use std::collections::HashMap;
|
||||
use parking_lot::RwLock;
|
||||
use error;
|
||||
use backend;
|
||||
use runtime_support::Hashable;
|
||||
use primitives;
|
||||
use primitives::block::{self, Id as BlockId, HeaderHash};
|
||||
use runtime_primitives::generic::BlockId;
|
||||
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Zero};
|
||||
use runtime_primitives::bft::Justification;
|
||||
use blockchain::{self, BlockStatus};
|
||||
use state_machine::backend::{Backend as StateBackend, InMemory};
|
||||
|
||||
struct PendingBlock {
|
||||
block: Block,
|
||||
struct PendingBlock<B: BlockT> {
|
||||
block: StoredBlock<B>,
|
||||
is_best: bool,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
struct Block {
|
||||
header: block::Header,
|
||||
justification: Option<primitives::bft::Justification>,
|
||||
body: Option<block::Body>,
|
||||
enum StoredBlock<B: BlockT> {
|
||||
Header(B::Header, Option<Justification<B::Hash>>),
|
||||
Full(B, Option<Justification<B::Hash>>),
|
||||
}
|
||||
|
||||
impl<B: BlockT> StoredBlock<B> {
|
||||
fn new(header: B::Header, body: Option<Vec<B::Extrinsic>>, just: Option<Justification<B::Hash>>) -> Self {
|
||||
match body {
|
||||
Some(body) => StoredBlock::Full(B::new(header, body), just),
|
||||
None => StoredBlock::Header(header, just),
|
||||
}
|
||||
}
|
||||
|
||||
fn header(&self) -> &B::Header {
|
||||
match *self {
|
||||
StoredBlock::Header(ref h, _) => h,
|
||||
StoredBlock::Full(ref b, _) => b.header(),
|
||||
}
|
||||
}
|
||||
|
||||
fn justification(&self) -> Option<&Justification<B::Hash>> {
|
||||
match *self {
|
||||
StoredBlock::Header(_, ref j) | StoredBlock::Full(_, ref j) => j.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
fn extrinsics(&self) -> Option<&[B::Extrinsic]> {
|
||||
match *self {
|
||||
StoredBlock::Header(_, _) => None,
|
||||
StoredBlock::Full(ref b, _) => Some(b.extrinsics())
|
||||
}
|
||||
}
|
||||
|
||||
fn into_inner(self) -> (B::Header, Option<Vec<B::Extrinsic>>, Option<Justification<B::Hash>>) {
|
||||
match self {
|
||||
StoredBlock::Header(header, just) => (header, None, just),
|
||||
StoredBlock::Full(block, just) => {
|
||||
let (header, body) = block.deconstruct();
|
||||
(header, Some(body), just)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct BlockchainStorage {
|
||||
blocks: HashMap<HeaderHash, Block>,
|
||||
hashes: HashMap<block::Number, HeaderHash>,
|
||||
best_hash: HeaderHash,
|
||||
best_number: block::Number,
|
||||
genesis_hash: HeaderHash,
|
||||
struct BlockchainStorage<Block: BlockT> {
|
||||
blocks: HashMap<Block::Hash, StoredBlock<Block>>,
|
||||
hashes: HashMap<<<Block as BlockT>::Header as HeaderT>::Number, Block::Hash>,
|
||||
best_hash: Block::Hash,
|
||||
best_number: <<Block as BlockT>::Header as HeaderT>::Number,
|
||||
genesis_hash: Block::Hash,
|
||||
}
|
||||
|
||||
/// In-memory blockchain. Supports concurrent reads.
|
||||
pub struct Blockchain {
|
||||
storage: RwLock<BlockchainStorage>,
|
||||
pub struct Blockchain<Block: BlockT> {
|
||||
storage: RwLock<BlockchainStorage<Block>>,
|
||||
}
|
||||
|
||||
impl Clone for Blockchain {
|
||||
fn clone(&self) -> Blockchain {
|
||||
impl<Block: BlockT + Clone> Clone for Blockchain<Block> {
|
||||
fn clone(&self) -> Self {
|
||||
Blockchain {
|
||||
storage: RwLock::new(self.storage.read().clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Blockchain {
|
||||
/// Create new in-memory blockchain storage.
|
||||
pub fn new() -> Blockchain {
|
||||
Blockchain {
|
||||
storage: RwLock::new(
|
||||
BlockchainStorage {
|
||||
blocks: HashMap::new(),
|
||||
hashes: HashMap::new(),
|
||||
best_hash: HeaderHash::default(),
|
||||
best_number: 0,
|
||||
genesis_hash: HeaderHash::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: BlockT> Blockchain<Block> {
|
||||
/// Get header hash of given block.
|
||||
pub fn id(&self, id: BlockId) -> Option<HeaderHash> {
|
||||
pub fn id(&self, id: BlockId<Block>) -> Option<Block::Hash> {
|
||||
match id {
|
||||
BlockId::Hash(h) => Some(h),
|
||||
BlockId::Number(n) => self.storage.read().hashes.get(&n).cloned(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert block.
|
||||
pub fn insert(&self, hash: HeaderHash, header: block::Header, justification: Option<primitives::bft::Justification>, body: Option<block::Body>, is_new_best: bool) {
|
||||
let number = header.number;
|
||||
let mut storage = self.storage.write();
|
||||
storage.blocks.insert(hash, Block {
|
||||
header: header,
|
||||
body: body,
|
||||
justification: justification,
|
||||
});
|
||||
storage.hashes.insert(number, hash);
|
||||
if is_new_best {
|
||||
storage.best_hash = hash;
|
||||
storage.best_number = number;
|
||||
/// Create new in-memory blockchain storage.
|
||||
pub fn new() -> Self {
|
||||
Blockchain {
|
||||
storage: RwLock::new(
|
||||
BlockchainStorage {
|
||||
blocks: HashMap::new(),
|
||||
hashes: HashMap::new(),
|
||||
best_hash: Default::default(),
|
||||
best_number: Zero::zero(),
|
||||
genesis_hash: Default::default(),
|
||||
})
|
||||
}
|
||||
if number == 0 {
|
||||
}
|
||||
|
||||
/// Insert a block header and associated data.
|
||||
pub fn insert(
|
||||
&self,
|
||||
hash: Block::Hash,
|
||||
header: <Block as BlockT>::Header,
|
||||
justification: Option<Justification<Block::Hash>>,
|
||||
body: Option<Vec<<Block as BlockT>::Extrinsic>>,
|
||||
is_new_best: bool
|
||||
) {
|
||||
let number = header.number().clone();
|
||||
let mut storage = self.storage.write();
|
||||
storage.blocks.insert(hash.clone(), StoredBlock::new(header, body, justification));
|
||||
storage.hashes.insert(number, hash.clone());
|
||||
if is_new_best {
|
||||
storage.best_hash = hash.clone();
|
||||
storage.best_number = number.clone();
|
||||
}
|
||||
if number == Zero::zero() {
|
||||
storage.genesis_hash = hash;
|
||||
}
|
||||
}
|
||||
|
||||
/// Compare this blockchain with another in-mem blockchain
|
||||
pub fn equals_to(&self, other: &Blockchain) -> bool {
|
||||
pub fn equals_to(&self, other: &Self) -> bool {
|
||||
self.canon_equals_to(other) && self.storage.read().blocks == other.storage.read().blocks
|
||||
}
|
||||
|
||||
/// Compare canonical chain to other canonical chain.
|
||||
pub fn canon_equals_to(&self, other: &Blockchain) -> bool {
|
||||
pub fn canon_equals_to(&self, other: &Self) -> bool {
|
||||
let this = self.storage.read();
|
||||
let other = other.storage.read();
|
||||
this.hashes == other.hashes
|
||||
@@ -118,20 +159,27 @@ impl Blockchain {
|
||||
}
|
||||
}
|
||||
|
||||
impl blockchain::Backend for Blockchain {
|
||||
fn header(&self, id: BlockId) -> error::Result<Option<block::Header>> {
|
||||
Ok(self.id(id).and_then(|hash| self.storage.read().blocks.get(&hash).map(|b| b.header.clone())))
|
||||
impl<Block: BlockT> blockchain::Backend<Block> for Blockchain<Block> {
|
||||
fn header(&self, id: BlockId<Block>) -> error::Result<Option<<Block as BlockT>::Header>> {
|
||||
Ok(self.id(id).and_then(|hash| {
|
||||
self.storage.read().blocks.get(&hash).map(|b| b.header().clone())
|
||||
}))
|
||||
}
|
||||
|
||||
fn body(&self, id: BlockId) -> error::Result<Option<block::Body>> {
|
||||
Ok(self.id(id).and_then(|hash| self.storage.read().blocks.get(&hash).and_then(|b| b.body.clone())))
|
||||
fn body(&self, id: BlockId<Block>) -> error::Result<Option<Vec<<Block as BlockT>::Extrinsic>>> {
|
||||
Ok(self.id(id).and_then(|hash| {
|
||||
self.storage.read().blocks.get(&hash)
|
||||
.and_then(|b| b.extrinsics().map(|x| x.to_vec()))
|
||||
}))
|
||||
}
|
||||
|
||||
fn justification(&self, id: BlockId) -> error::Result<Option<primitives::bft::Justification>> {
|
||||
Ok(self.id(id).and_then(|hash| self.storage.read().blocks.get(&hash).and_then(|b| b.justification.clone())))
|
||||
fn justification(&self, id: BlockId<Block>) -> error::Result<Option<Justification<Block::Hash>>> {
|
||||
Ok(self.id(id).and_then(|hash| self.storage.read().blocks.get(&hash).and_then(|b|
|
||||
b.justification().map(|x| x.clone()))
|
||||
))
|
||||
}
|
||||
|
||||
fn info(&self) -> error::Result<blockchain::Info> {
|
||||
fn info(&self) -> error::Result<blockchain::Info<Block>> {
|
||||
let storage = self.storage.read();
|
||||
Ok(blockchain::Info {
|
||||
best_hash: storage.best_hash,
|
||||
@@ -140,40 +188,42 @@ impl blockchain::Backend for Blockchain {
|
||||
})
|
||||
}
|
||||
|
||||
fn status(&self, id: BlockId) -> error::Result<BlockStatus> {
|
||||
fn status(&self, id: BlockId<Block>) -> error::Result<BlockStatus> {
|
||||
match self.id(id).map_or(false, |hash| self.storage.read().blocks.contains_key(&hash)) {
|
||||
true => Ok(BlockStatus::InChain),
|
||||
false => Ok(BlockStatus::Unknown),
|
||||
}
|
||||
}
|
||||
|
||||
fn hash(&self, number: block::Number) -> error::Result<Option<block::HeaderHash>> {
|
||||
fn hash(&self, number: <<Block as BlockT>::Header as HeaderT>::Number) -> error::Result<Option<Block::Hash>> {
|
||||
Ok(self.id(BlockId::Number(number)))
|
||||
}
|
||||
}
|
||||
|
||||
/// In-memory operation.
|
||||
pub struct BlockImportOperation {
|
||||
pending_block: Option<PendingBlock>,
|
||||
pub struct BlockImportOperation<Block: BlockT> {
|
||||
pending_block: Option<PendingBlock<Block>>,
|
||||
old_state: InMemory,
|
||||
new_state: Option<InMemory>,
|
||||
}
|
||||
|
||||
impl backend::BlockImportOperation for BlockImportOperation {
|
||||
impl<Block: BlockT> backend::BlockImportOperation<Block> for BlockImportOperation<Block> {
|
||||
type State = InMemory;
|
||||
|
||||
fn state(&self) -> error::Result<Option<&Self::State>> {
|
||||
Ok(Some(&self.old_state))
|
||||
}
|
||||
|
||||
fn set_block_data(&mut self, header: block::Header, body: Option<block::Body>, justification: Option<primitives::bft::Justification>, is_new_best: bool) -> error::Result<()> {
|
||||
fn set_block_data(
|
||||
&mut self,
|
||||
header: <Block as BlockT>::Header,
|
||||
body: Option<Vec<<Block as BlockT>::Extrinsic>>,
|
||||
justification: Option<Justification<Block::Hash>>,
|
||||
is_new_best: bool
|
||||
) -> error::Result<()> {
|
||||
assert!(self.pending_block.is_none(), "Only one block per operation is allowed");
|
||||
self.pending_block = Some(PendingBlock {
|
||||
block: Block {
|
||||
header: header,
|
||||
body: body,
|
||||
justification: justification,
|
||||
},
|
||||
block: StoredBlock::new(header, body, justification),
|
||||
is_best: is_new_best,
|
||||
});
|
||||
Ok(())
|
||||
@@ -191,14 +241,18 @@ impl backend::BlockImportOperation for BlockImportOperation {
|
||||
}
|
||||
|
||||
/// In-memory backend. Keeps all states and blocks in memory. Useful for testing.
|
||||
pub struct Backend {
|
||||
states: RwLock<HashMap<block::HeaderHash, InMemory>>,
|
||||
blockchain: Blockchain,
|
||||
pub struct Backend<Block> where
|
||||
Block: BlockT,
|
||||
{
|
||||
states: RwLock<HashMap<Block::Hash, InMemory>>,
|
||||
blockchain: Blockchain<Block>,
|
||||
}
|
||||
|
||||
impl Backend {
|
||||
impl<Block> Backend<Block> where
|
||||
Block: BlockT,
|
||||
{
|
||||
/// Create a new instance of in-mem backend.
|
||||
pub fn new() -> Backend {
|
||||
pub fn new() -> Backend<Block> {
|
||||
Backend {
|
||||
states: RwLock::new(HashMap::new()),
|
||||
blockchain: Blockchain::new(),
|
||||
@@ -206,14 +260,16 @@ impl Backend {
|
||||
}
|
||||
}
|
||||
|
||||
impl backend::Backend for Backend {
|
||||
type BlockImportOperation = BlockImportOperation;
|
||||
type Blockchain = Blockchain;
|
||||
impl<Block> backend::Backend<Block> for Backend<Block> where
|
||||
Block: BlockT,
|
||||
{
|
||||
type BlockImportOperation = BlockImportOperation<Block>;
|
||||
type Blockchain = Blockchain<Block>;
|
||||
type State = InMemory;
|
||||
|
||||
fn begin_operation(&self, block: BlockId) -> error::Result<Self::BlockImportOperation> {
|
||||
fn begin_operation(&self, block: BlockId<Block>) -> error::Result<Self::BlockImportOperation> {
|
||||
let state = match block {
|
||||
BlockId::Hash(h) if h.is_zero() => Self::State::default(),
|
||||
BlockId::Hash(ref h) if h.clone() == Default::default() => Self::State::default(),
|
||||
_ => self.state_at(block)?,
|
||||
};
|
||||
|
||||
@@ -226,24 +282,26 @@ impl backend::Backend for Backend {
|
||||
|
||||
fn commit_operation(&self, operation: Self::BlockImportOperation) -> error::Result<()> {
|
||||
if let Some(pending_block) = operation.pending_block {
|
||||
let hash = pending_block.block.header.blake2_256().into();
|
||||
let old_state = &operation.old_state;
|
||||
let (header, body, justification) = pending_block.block.into_inner();
|
||||
let hash = header.hash();
|
||||
|
||||
self.states.write().insert(hash, operation.new_state.unwrap_or_else(|| old_state.clone()));
|
||||
self.blockchain.insert(hash, pending_block.block.header, pending_block.block.justification, pending_block.block.body, pending_block.is_best);
|
||||
self.blockchain.insert(hash, header, justification, body, pending_block.is_best);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn blockchain(&self) -> &Blockchain {
|
||||
fn blockchain(&self) -> &Self::Blockchain {
|
||||
&self.blockchain
|
||||
}
|
||||
|
||||
fn state_at(&self, block: BlockId) -> error::Result<Self::State> {
|
||||
fn state_at(&self, block: BlockId<Block>) -> error::Result<Self::State> {
|
||||
match self.blockchain.id(block).and_then(|id| self.states.read().get(&id).cloned()) {
|
||||
Some(state) => Ok(state),
|
||||
None => Err(error::ErrorKind::UnknownBlock(block).into()),
|
||||
None => Err(error::ErrorKind::UnknownBlock(format!("{}", block)).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl backend::LocalBackend for Backend {}
|
||||
impl<Block: BlockT> backend::LocalBackend<Block> for Backend<Block> {}
|
||||
|
||||
@@ -24,6 +24,7 @@ extern crate substrate_codec as codec;
|
||||
extern crate substrate_primitives as primitives;
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
extern crate substrate_runtime_primitives as runtime_primitives;
|
||||
extern crate substrate_state_machine as state_machine;
|
||||
#[cfg(test)] extern crate substrate_keyring as keyring;
|
||||
#[cfg(test)] extern crate substrate_test_client as test_client;
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
use futures::future::IntoFuture;
|
||||
use primitives;
|
||||
use primitives::block::{self, Id as BlockId, HeaderHash};
|
||||
use runtime_support::Hashable;
|
||||
use state_machine::CodeExecutor;
|
||||
use state_machine::backend::Backend as StateBackend;
|
||||
use runtime_primitives::generic::BlockId;
|
||||
use runtime_primitives::bft::Justification;
|
||||
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT};
|
||||
use blockchain::{self, BlockStatus};
|
||||
use backend;
|
||||
use call_executor::{CallResult, RemoteCallExecutor, check_execution_proof};
|
||||
@@ -32,9 +32,9 @@ use error;
|
||||
use in_mem::Blockchain as InMemBlockchain;
|
||||
|
||||
/// Remote call request.
|
||||
pub struct RemoteCallRequest {
|
||||
/// Call at state of given block.
|
||||
pub block: HeaderHash,
|
||||
pub struct RemoteCallRequest<H> {
|
||||
/// Call at state of block referenced by given header hash.
|
||||
pub block: H,
|
||||
/// Method to call.
|
||||
pub method: String,
|
||||
/// Call data.
|
||||
@@ -43,62 +43,62 @@ pub struct RemoteCallRequest {
|
||||
|
||||
/// Light client data fetcher. Implementations of this trait must check if remote data
|
||||
/// is correct (see FetchedDataChecker) and return already checked data.
|
||||
pub trait Fetcher: Send + Sync {
|
||||
pub trait Fetcher<B: BlockT>: Send + Sync {
|
||||
/// Remote call result future.
|
||||
type RemoteCallResult: IntoFuture<Item=CallResult, Error=error::Error>;
|
||||
|
||||
/// Fetch remote call result.
|
||||
fn remote_call(&self, request: RemoteCallRequest) -> Self::RemoteCallResult;
|
||||
fn remote_call(&self, request: RemoteCallRequest<B::Hash>) -> Self::RemoteCallResult;
|
||||
}
|
||||
|
||||
/// Light client remote data checker.
|
||||
pub trait FetchChecker: Send + Sync {
|
||||
pub trait FetchChecker<B: BlockT>: Send + Sync {
|
||||
/// Check remote method execution proof.
|
||||
fn check_execution_proof(&self, request: &RemoteCallRequest, remote_proof: (Vec<u8>, Vec<Vec<u8>>)) -> error::Result<CallResult>;
|
||||
fn check_execution_proof(&self, request: &RemoteCallRequest<B::Hash>, remote_proof: (Vec<u8>, Vec<Vec<u8>>)) -> error::Result<CallResult>;
|
||||
}
|
||||
|
||||
/// Light client backend.
|
||||
pub struct Backend {
|
||||
blockchain: Blockchain,
|
||||
pub struct Backend<B: BlockT> {
|
||||
blockchain: Blockchain<B>,
|
||||
}
|
||||
|
||||
/// Light client blockchain.
|
||||
pub struct Blockchain {
|
||||
storage: InMemBlockchain,
|
||||
pub struct Blockchain<B: BlockT> {
|
||||
storage: InMemBlockchain<B>,
|
||||
}
|
||||
|
||||
/// Block (header and justification) import operation.
|
||||
pub struct BlockImportOperation {
|
||||
pending_block: Option<PendingBlock>,
|
||||
pub struct BlockImportOperation<B: BlockT> {
|
||||
pending_block: Option<PendingBlock<B>>,
|
||||
}
|
||||
|
||||
/// On-demand state.
|
||||
#[derive(Clone)]
|
||||
pub struct OnDemandState {
|
||||
pub struct OnDemandState<H> {
|
||||
/// Hash of the block, state is valid for.
|
||||
_block: HeaderHash,
|
||||
_block: H,
|
||||
}
|
||||
|
||||
/// Remote data checker.
|
||||
pub struct LightDataChecker<E> {
|
||||
pub struct LightDataChecker<E, B: BlockT> {
|
||||
/// Backend reference.
|
||||
backend: Arc<Backend>,
|
||||
backend: Arc<Backend<B>>,
|
||||
/// Executor.
|
||||
executor: E,
|
||||
}
|
||||
|
||||
struct PendingBlock {
|
||||
header: block::Header,
|
||||
justification: Option<primitives::bft::Justification>,
|
||||
struct PendingBlock<B: BlockT> {
|
||||
header: B::Header,
|
||||
justification: Option<Justification<B::Hash>>,
|
||||
is_best: bool,
|
||||
}
|
||||
|
||||
impl backend::Backend for Backend {
|
||||
type BlockImportOperation = BlockImportOperation;
|
||||
type Blockchain = Blockchain;
|
||||
type State = OnDemandState;
|
||||
impl<B: BlockT> backend::Backend<B> for Backend<B> {
|
||||
type BlockImportOperation = BlockImportOperation<B>;
|
||||
type Blockchain = Blockchain<B>;
|
||||
type State = OnDemandState<B::Hash>;
|
||||
|
||||
fn begin_operation(&self, _block: BlockId) -> error::Result<Self::BlockImportOperation> {
|
||||
fn begin_operation(&self, _block: BlockId<B>) -> error::Result<Self::BlockImportOperation> {
|
||||
Ok(BlockImportOperation {
|
||||
pending_block: None,
|
||||
})
|
||||
@@ -106,34 +106,34 @@ impl backend::Backend for Backend {
|
||||
|
||||
fn commit_operation(&self, operation: Self::BlockImportOperation) -> error::Result<()> {
|
||||
if let Some(pending_block) = operation.pending_block {
|
||||
let hash = pending_block.header.blake2_256().into();
|
||||
let hash = pending_block.header.hash();
|
||||
self.blockchain.storage.insert(hash, pending_block.header, pending_block.justification, None, pending_block.is_best);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn blockchain(&self) -> &Blockchain {
|
||||
fn blockchain(&self) -> &Blockchain<B> {
|
||||
&self.blockchain
|
||||
}
|
||||
|
||||
fn state_at(&self, block: BlockId) -> error::Result<Self::State> {
|
||||
fn state_at(&self, block: BlockId<B>) -> error::Result<Self::State> {
|
||||
Ok(OnDemandState {
|
||||
_block: self.blockchain.storage.id(block).ok_or(error::ErrorKind::UnknownBlock(block))?,
|
||||
_block: self.blockchain.storage.id(block).ok_or(error::ErrorKind::UnknownBlock(format!("{:?}", block)))?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl backend::RemoteBackend for Backend {}
|
||||
impl<B: BlockT> backend::RemoteBackend<B> for Backend<B> {}
|
||||
|
||||
impl backend::BlockImportOperation for BlockImportOperation {
|
||||
type State = OnDemandState;
|
||||
impl<B: BlockT> backend::BlockImportOperation<B> for BlockImportOperation<B> {
|
||||
type State = OnDemandState<B::Hash>;
|
||||
|
||||
fn state(&self) -> error::Result<Option<&Self::State>> {
|
||||
// None means 'locally-stateless' backend
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn set_block_data(&mut self, header: block::Header, _body: Option<block::Body>, justification: Option<primitives::bft::Justification>, is_new_best: bool) -> error::Result<()> {
|
||||
fn set_block_data(&mut self, header: B::Header, _body: Option<Vec<B::Extrinsic>>, justification: Option<Justification<B::Hash>>, is_new_best: bool) -> error::Result<()> {
|
||||
assert!(self.pending_block.is_none(), "Only one block per operation is allowed");
|
||||
self.pending_block = Some(PendingBlock {
|
||||
header,
|
||||
@@ -154,34 +154,34 @@ impl backend::BlockImportOperation for BlockImportOperation {
|
||||
}
|
||||
}
|
||||
|
||||
impl blockchain::Backend for Blockchain {
|
||||
fn header(&self, id: BlockId) -> error::Result<Option<block::Header>> {
|
||||
impl<B: BlockT> blockchain::Backend<B> for Blockchain<B> {
|
||||
fn header(&self, id: BlockId<B>) -> error::Result<Option<B::Header>> {
|
||||
self.storage.header(id)
|
||||
}
|
||||
|
||||
fn body(&self, _id: BlockId) -> error::Result<Option<block::Body>> {
|
||||
fn body(&self, _id: BlockId<B>) -> error::Result<Option<Vec<B::Extrinsic>>> {
|
||||
// TODO [light]: fetch from remote node
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn justification(&self, id: BlockId) -> error::Result<Option<primitives::bft::Justification>> {
|
||||
fn justification(&self, id: BlockId<B>) -> error::Result<Option<Justification<B::Hash>>> {
|
||||
self.storage.justification(id)
|
||||
}
|
||||
|
||||
fn info(&self) -> error::Result<blockchain::Info> {
|
||||
fn info(&self) -> error::Result<blockchain::Info<B>> {
|
||||
self.storage.info()
|
||||
}
|
||||
|
||||
fn status(&self, id: BlockId) -> error::Result<BlockStatus> {
|
||||
fn status(&self, id: BlockId<B>) -> error::Result<BlockStatus> {
|
||||
self.storage.status(id)
|
||||
}
|
||||
|
||||
fn hash(&self, number: block::Number) -> error::Result<Option<block::HeaderHash>> {
|
||||
fn hash(&self, number: <B::Header as HeaderT>::Number) -> error::Result<Option<B::Hash>> {
|
||||
self.storage.hash(number)
|
||||
}
|
||||
}
|
||||
|
||||
impl StateBackend for OnDemandState {
|
||||
impl<H: Clone> StateBackend for OnDemandState<H> {
|
||||
type Error = error::Error;
|
||||
type Transaction = ();
|
||||
|
||||
@@ -191,7 +191,8 @@ impl StateBackend for OnDemandState {
|
||||
}
|
||||
|
||||
fn storage_root<I>(&self, _delta: I) -> ([u8; 32], Self::Transaction)
|
||||
where I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)> {
|
||||
where I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>
|
||||
{
|
||||
([0; 32], ())
|
||||
}
|
||||
|
||||
@@ -201,43 +202,46 @@ impl StateBackend for OnDemandState {
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> FetchChecker for LightDataChecker<E>
|
||||
impl<E, B> FetchChecker<B> for LightDataChecker<E, B>
|
||||
where
|
||||
E: CodeExecutor,
|
||||
B: BlockT,
|
||||
{
|
||||
fn check_execution_proof(&self, request: &RemoteCallRequest, remote_proof: (Vec<u8>, Vec<Vec<u8>>)) -> error::Result<CallResult> {
|
||||
fn check_execution_proof(&self, request: &RemoteCallRequest<B::Hash>, remote_proof: (Vec<u8>, Vec<Vec<u8>>)) -> error::Result<CallResult> {
|
||||
check_execution_proof(&*self.backend, &self.executor, request, remote_proof)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an instance of light client backend.
|
||||
pub fn new_light_backend() -> Arc<Backend> {
|
||||
pub fn new_light_backend<B: BlockT>() -> Arc<Backend<B>> {
|
||||
let storage = InMemBlockchain::new();
|
||||
let blockchain = Blockchain { storage };
|
||||
Arc::new(Backend { blockchain })
|
||||
}
|
||||
|
||||
/// Create an instance of light client.
|
||||
pub fn new_light<F, B>(
|
||||
backend: Arc<Backend>,
|
||||
pub fn new_light<F, B, Block>(
|
||||
backend: Arc<Backend<Block>>,
|
||||
fetcher: Arc<F>,
|
||||
genesis_builder: B,
|
||||
) -> error::Result<Client<Backend, RemoteCallExecutor<Backend, F>>>
|
||||
) -> error::Result<Client<Backend<Block>, RemoteCallExecutor<Backend<Block>, F>, Block>>
|
||||
where
|
||||
F: Fetcher,
|
||||
B: GenesisBuilder,
|
||||
F: Fetcher<Block>,
|
||||
B: GenesisBuilder<Block>,
|
||||
Block: BlockT,
|
||||
{
|
||||
let executor = RemoteCallExecutor::new(backend.clone(), fetcher);
|
||||
Client::new(backend, executor, genesis_builder)
|
||||
}
|
||||
|
||||
/// Create an instance of fetch data checker.
|
||||
pub fn new_fetch_checker<E>(
|
||||
backend: Arc<Backend>,
|
||||
pub fn new_fetch_checker<E, Block>(
|
||||
backend: Arc<Backend<Block>>,
|
||||
executor: E,
|
||||
) -> LightDataChecker<E>
|
||||
) -> LightDataChecker<E, Block>
|
||||
where
|
||||
E: CodeExecutor,
|
||||
Block: BlockT,
|
||||
{
|
||||
LightDataChecker { backend, executor }
|
||||
}
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -8,5 +8,4 @@ error-chain = "0.11"
|
||||
futures = "0.1"
|
||||
log = "0.3"
|
||||
parking_lot = "0.4"
|
||||
substrate-primitives = { path = "../primitives" }
|
||||
transaction-pool = "1.12"
|
||||
|
||||
@@ -16,12 +16,9 @@
|
||||
|
||||
//! External API for extrinsic pool.
|
||||
|
||||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
use txpool::{self, VerifiedTransaction};
|
||||
use primitives::{
|
||||
Hash,
|
||||
block::{Extrinsic, ExtrinsicHash},
|
||||
};
|
||||
|
||||
/// Extrinsic pool error.
|
||||
pub trait Error: ::std::error::Error + Send + Sized {
|
||||
@@ -38,18 +35,19 @@ impl Error for txpool::Error {
|
||||
}
|
||||
|
||||
/// Extrinsic pool.
|
||||
pub trait ExtrinsicPool: Send + Sync + 'static {
|
||||
pub trait ExtrinsicPool<Ex, Hash>: Send + Sync + 'static {
|
||||
/// Error type
|
||||
type Error: Error;
|
||||
|
||||
/// Submit a collection of extrinsics to the pool.
|
||||
fn submit(&self, xt: Vec<Extrinsic>) -> Result<Vec<ExtrinsicHash>, Self::Error>;
|
||||
fn submit(&self, xt: Vec<Ex>) -> Result<Vec<Hash>, Self::Error>;
|
||||
}
|
||||
|
||||
// Blanket implementation for anything that `Derefs` to the pool.
|
||||
impl<V, S, E, T> ExtrinsicPool for T where
|
||||
T: Deref<Target=super::Pool<V, S, E>> + Send + Sync + 'static,
|
||||
V: txpool::Verifier<Extrinsic>,
|
||||
impl<Ex, Hash, V, S, E, T> ExtrinsicPool<Ex, Hash> for T where
|
||||
Hash: ::std::hash::Hash + Eq + Copy + fmt::Debug + fmt::LowerHex + Default,
|
||||
T: Deref<Target=super::Pool<Ex, Hash, V, S, E>> + Send + Sync + 'static,
|
||||
V: txpool::Verifier<Ex>,
|
||||
S: txpool::Scoring<V::VerifiedTransaction>,
|
||||
V::VerifiedTransaction: txpool::VerifiedTransaction<Hash=Hash>,
|
||||
E: From<V::Error>,
|
||||
@@ -58,7 +56,7 @@ impl<V, S, E, T> ExtrinsicPool for T where
|
||||
{
|
||||
type Error = E;
|
||||
|
||||
fn submit(&self, xt: Vec<Extrinsic>) -> Result<Vec<ExtrinsicHash>, Self::Error> {
|
||||
fn submit(&self, xt: Vec<Ex>) -> Result<Vec<Hash>, Self::Error> {
|
||||
self.deref().submit(xt).map(|result| result.into_iter().map(|xt| *xt.hash()).collect())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
extern crate futures;
|
||||
extern crate parking_lot;
|
||||
extern crate substrate_primitives as primitives;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
@@ -16,29 +16,29 @@
|
||||
|
||||
use std::{
|
||||
sync::Arc,
|
||||
fmt,
|
||||
collections::HashMap,
|
||||
};
|
||||
use primitives::Hash;
|
||||
use txpool;
|
||||
|
||||
use watcher;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Listener {
|
||||
watchers: HashMap<Hash, watcher::Sender>
|
||||
pub struct Listener<H: ::std::hash::Hash + Eq> {
|
||||
watchers: HashMap<H, watcher::Sender<H>>
|
||||
}
|
||||
|
||||
impl Listener {
|
||||
pub fn create_watcher<T: txpool::VerifiedTransaction<Hash=Hash>>(&mut self, xt: Arc<T>) -> watcher::Watcher {
|
||||
impl<H: ::std::hash::Hash + Eq + Copy + fmt::Debug + fmt::LowerHex + Default> Listener<H> {
|
||||
pub fn create_watcher<T: txpool::VerifiedTransaction<Hash=H>>(&mut self, xt: Arc<T>) -> watcher::Watcher<H> {
|
||||
let sender = self.watchers.entry(*xt.hash()).or_insert_with(watcher::Sender::default);
|
||||
sender.new_watcher()
|
||||
}
|
||||
|
||||
pub fn broadcasted(&mut self, hash: &Hash, peers: Vec<String>) {
|
||||
pub fn broadcasted(&mut self, hash: &H, peers: Vec<String>) {
|
||||
self.fire(hash, |watcher| watcher.broadcast(peers));
|
||||
}
|
||||
|
||||
fn fire<F: FnOnce(&mut watcher::Sender)>(&mut self, hash: &Hash, fun: F) {
|
||||
fn fire<F>(&mut self, hash: &H, fun: F) where F: FnOnce(&mut watcher::Sender<H>) {
|
||||
let clean = if let Some(h) = self.watchers.get_mut(hash) {
|
||||
fun(h);
|
||||
h.is_done()
|
||||
@@ -52,7 +52,10 @@ impl Listener {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: txpool::VerifiedTransaction<Hash=Hash>> txpool::Listener<T> for Listener {
|
||||
impl<H, T> txpool::Listener<T> for Listener<H> where
|
||||
H: ::std::hash::Hash + Eq + Copy + fmt::Debug + fmt::LowerHex + Default,
|
||||
T: txpool::VerifiedTransaction<Hash=H>,
|
||||
{
|
||||
fn added(&mut self, tx: &Arc<T>, old: Option<&Arc<T>>) {
|
||||
if let Some(old) = old {
|
||||
let hash = tx.hash();
|
||||
@@ -81,9 +84,7 @@ impl<T: txpool::VerifiedTransaction<Hash=Hash>> txpool::Listener<T> for Listener
|
||||
|
||||
fn mined(&mut self, tx: &Arc<T>) {
|
||||
// TODO [ToDr] latest block number?
|
||||
let header_hash = 1.into();
|
||||
let header_hash = Default::default();
|
||||
self.fire(tx.hash(), |watcher| watcher.finalised(header_hash))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fmt,
|
||||
marker::PhantomData,
|
||||
sync::{Arc, Weak},
|
||||
};
|
||||
@@ -23,28 +24,29 @@ use std::{
|
||||
use futures::sync::mpsc;
|
||||
use parking_lot::{RwLock, Mutex};
|
||||
use txpool;
|
||||
use primitives::{Hash, block::Extrinsic};
|
||||
|
||||
use listener::Listener;
|
||||
use watcher::Watcher;
|
||||
|
||||
/// Extrinsics pool.
|
||||
pub struct Pool<V, S, E> where
|
||||
V: txpool::Verifier<Extrinsic>,
|
||||
pub struct Pool<Ex, Hash, V, S, E> where
|
||||
Hash: ::std::hash::Hash + Eq + Copy + fmt::Debug + fmt::LowerHex,
|
||||
V: txpool::Verifier<Ex>,
|
||||
S: txpool::Scoring<V::VerifiedTransaction>,
|
||||
{
|
||||
_error: Mutex<PhantomData<E>>,
|
||||
pool: RwLock<txpool::Pool<
|
||||
V::VerifiedTransaction,
|
||||
S,
|
||||
Listener,
|
||||
Listener<Hash>,
|
||||
>>,
|
||||
verifier: V,
|
||||
import_notification_sinks: Mutex<Vec<mpsc::UnboundedSender<Weak<V::VerifiedTransaction>>>>,
|
||||
}
|
||||
|
||||
impl<V, S, E> Pool<V, S, E> where
|
||||
V: txpool::Verifier<Extrinsic>,
|
||||
impl<Ex, Hash, V, S, E> Pool<Ex, Hash, V, S, E> where
|
||||
Hash: ::std::hash::Hash + Eq + Copy + fmt::Debug + fmt::LowerHex + Default,
|
||||
V: txpool::Verifier<Ex>,
|
||||
S: txpool::Scoring<V::VerifiedTransaction>,
|
||||
V::VerifiedTransaction: txpool::VerifiedTransaction<Hash=Hash>,
|
||||
E: From<V::Error>,
|
||||
@@ -86,7 +88,7 @@ impl<V, S, E> Pool<V, S, E> where
|
||||
}
|
||||
|
||||
/// Imports a bunch of extrinsics to the pool
|
||||
pub fn submit(&self, xts: Vec<Extrinsic>) -> Result<Vec<Arc<V::VerifiedTransaction>>, E> {
|
||||
pub fn submit(&self, xts: Vec<Ex>) -> Result<Vec<Arc<V::VerifiedTransaction>>, E> {
|
||||
xts
|
||||
.into_iter()
|
||||
.map(|xt| self.verifier.verify_transaction(xt))
|
||||
@@ -97,7 +99,7 @@ impl<V, S, E> Pool<V, S, E> where
|
||||
}
|
||||
|
||||
/// Import a single extrinsic and starts to watch their progress in the pool.
|
||||
pub fn submit_and_watch(&self, xt: Extrinsic) -> Result<Watcher, E> {
|
||||
pub fn submit_and_watch(&self, xt: Ex) -> Result<Watcher<Hash>, E> {
|
||||
let xt = self.submit(vec![xt])?.pop().expect("One extrinsic passed; one result returned; qed");
|
||||
Ok(self.pool.write().listener_mut().create_watcher(xt))
|
||||
}
|
||||
@@ -122,7 +124,7 @@ impl<V, S, E> Pool<V, S, E> where
|
||||
/// Cull transactions from the queue and then compute the pending set.
|
||||
pub fn cull_and_get_pending<R, F, T>(&self, ready: R, f: F) -> T where
|
||||
R: txpool::Ready<V::VerifiedTransaction> + Clone,
|
||||
F: FnOnce(txpool::PendingIterator<V::VerifiedTransaction, R, S, Listener>) -> T,
|
||||
F: FnOnce(txpool::PendingIterator<V::VerifiedTransaction, R, S, Listener<Hash>>) -> T,
|
||||
{
|
||||
let mut pool = self.pool.write();
|
||||
pool.cull(None, ready.clone());
|
||||
|
||||
@@ -15,17 +15,14 @@
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use futures::sync::mpsc;
|
||||
use primitives::{
|
||||
block::{HeaderHash, ExtrinsicHash}
|
||||
};
|
||||
|
||||
/// Possible extrinsic status events
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Status {
|
||||
pub enum Status<H> {
|
||||
/// Extrinsic has been finalised in block with given hash.
|
||||
Finalised(HeaderHash),
|
||||
Finalised(H),
|
||||
/// Some state change (perhaps another extrinsic was included) rendered this extrinsic invalid.
|
||||
Usurped(ExtrinsicHash),
|
||||
Usurped(H),
|
||||
/// The extrinsic has been broadcast to the given peers.
|
||||
Broadcast(Vec<String>),
|
||||
/// Extrinsic has been dropped from the pool because of the limit.
|
||||
@@ -36,19 +33,19 @@ pub enum Status {
|
||||
///
|
||||
/// Represents a stream of status updates for particular extrinsic.
|
||||
#[derive(Debug)]
|
||||
pub struct Watcher {
|
||||
receiver: mpsc::UnboundedReceiver<Status>,
|
||||
pub struct Watcher<H> {
|
||||
receiver: mpsc::UnboundedReceiver<Status<H>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct Sender {
|
||||
receivers: Vec<mpsc::UnboundedSender<Status>>,
|
||||
pub(crate) struct Sender<H> {
|
||||
receivers: Vec<mpsc::UnboundedSender<Status<H>>>,
|
||||
finalised: bool,
|
||||
}
|
||||
|
||||
impl Sender {
|
||||
impl<H: Clone> Sender<H> {
|
||||
/// Add a new watcher to this sender object.
|
||||
pub fn new_watcher(&mut self) -> Watcher {
|
||||
pub fn new_watcher(&mut self) -> Watcher<H> {
|
||||
let (tx, receiver) = mpsc::unbounded();
|
||||
self.receivers.push(tx);
|
||||
Watcher {
|
||||
@@ -57,12 +54,12 @@ impl Sender {
|
||||
}
|
||||
|
||||
/// Some state change (perhaps another extrinsic was included) rendered this extrinsic invalid.
|
||||
pub fn usurped(&mut self, hash: ExtrinsicHash) {
|
||||
pub fn usurped(&mut self, hash: H) {
|
||||
self.send(Status::Usurped(hash))
|
||||
}
|
||||
|
||||
/// Extrinsic has been finalised in block with given hash.
|
||||
pub fn finalised(&mut self, hash: HeaderHash) {
|
||||
pub fn finalised(&mut self, hash: H) {
|
||||
self.send(Status::Finalised(hash));
|
||||
self.finalised = true;
|
||||
}
|
||||
@@ -82,7 +79,7 @@ impl Sender {
|
||||
self.finalised || self.receivers.is_empty()
|
||||
}
|
||||
|
||||
fn send(&mut self, status: Status) {
|
||||
fn send(&mut self, status: Status<H>) {
|
||||
self.receivers.retain(|sender| sender.unbounded_send(status.clone()).is_ok())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
[dependencies]
|
||||
substrate-codec = { path = "../codec", default-features = false }
|
||||
substrate-primitives = { path = "../primitives", default-features = false }
|
||||
substrate-runtime-primitives = { path = "../runtime/primitives", default-features = false }
|
||||
substrate-runtime-io = { path = "../runtime-io", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
@@ -14,4 +15,4 @@ substrate-keyring = { path = "../keyring" }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = ["substrate-codec/std", "substrate-primitives/std", "substrate-runtime-io/std"]
|
||||
std = ["substrate-codec/std", "substrate-primitives/std", "substrate-runtime-primitives/std", "substrate-runtime-io/std"]
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
extern crate substrate_codec as codec;
|
||||
extern crate substrate_primitives as primitives;
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
extern crate substrate_runtime_primitives as runtime_primitives;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate substrate_bft;
|
||||
@@ -29,23 +30,27 @@ extern crate substrate_keyring as keyring;
|
||||
|
||||
use codec::Slicable;
|
||||
use primitives::{AuthorityId, Signature};
|
||||
use primitives::block::HeaderHash;
|
||||
use primitives::bft::{Action, Message, MisbehaviorKind};
|
||||
|
||||
use runtime_primitives::bft::{Action, Message, MisbehaviorKind};
|
||||
|
||||
// check a message signature. returns true if signed by that authority.
|
||||
fn check_message_sig(message: Message, signature: &Signature, from: &AuthorityId) -> bool {
|
||||
let msg = message.encode();
|
||||
fn check_message_sig<B: Slicable, H: Slicable>(
|
||||
message: Message<B, H>,
|
||||
signature: &Signature,
|
||||
from: &AuthorityId
|
||||
) -> bool {
|
||||
let msg: Vec<u8> = message.encode();
|
||||
runtime_io::ed25519_verify(&signature.0, &msg, from)
|
||||
}
|
||||
|
||||
fn prepare(parent: HeaderHash, round_number: u32, hash: HeaderHash) -> Message {
|
||||
fn prepare<B, H>(parent: H, round_number: u32, hash: H) -> Message<B, H> {
|
||||
Message {
|
||||
parent,
|
||||
action: Action::Prepare(round_number, hash),
|
||||
}
|
||||
}
|
||||
|
||||
fn commit(parent: HeaderHash, round_number: u32, hash: HeaderHash) -> Message {
|
||||
fn commit<B, H>(parent: H, round_number: u32, hash: H) -> Message<B, H> {
|
||||
Message {
|
||||
parent,
|
||||
action: Action::Commit(round_number, hash),
|
||||
@@ -57,21 +62,21 @@ fn commit(parent: HeaderHash, round_number: u32, hash: HeaderHash) -> Message {
|
||||
/// Doesn't check that the header hash in question is
|
||||
/// valid or whether the misbehaving authority was part of
|
||||
/// the set at that block.
|
||||
pub fn evaluate_misbehavior(
|
||||
pub fn evaluate_misbehavior<B: Slicable, H: Slicable + Copy>(
|
||||
misbehaved: &AuthorityId,
|
||||
parent_hash: HeaderHash,
|
||||
kind: &MisbehaviorKind,
|
||||
parent_hash: H,
|
||||
kind: &MisbehaviorKind<H>,
|
||||
) -> bool {
|
||||
match *kind {
|
||||
MisbehaviorKind::BftDoublePrepare(round, (h_1, ref s_1), (h_2, ref s_2)) => {
|
||||
s_1 != s_2 &&
|
||||
check_message_sig(prepare(parent_hash, round, h_1), s_1, misbehaved) &&
|
||||
check_message_sig(prepare(parent_hash, round, h_2), s_2, misbehaved)
|
||||
check_message_sig::<B, H>(prepare::<B, H>(parent_hash, round, h_1), s_1, misbehaved) &&
|
||||
check_message_sig::<B, H>(prepare::<B, H>(parent_hash, round, h_2), s_2, misbehaved)
|
||||
}
|
||||
MisbehaviorKind::BftDoubleCommit(round, (h_1, ref s_1), (h_2, ref s_2)) => {
|
||||
s_1 != s_2 &&
|
||||
check_message_sig(commit(parent_hash, round, h_1), s_1, misbehaved) &&
|
||||
check_message_sig(commit(parent_hash, round, h_2), s_2, misbehaved)
|
||||
check_message_sig::<B, H>(commit::<B, H>(parent_hash, round, h_1), s_1, misbehaved) &&
|
||||
check_message_sig::<B, H>(commit::<B, H>(parent_hash, round, h_2), s_2, misbehaved)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,8 +89,12 @@ mod tests {
|
||||
use keyring::ed25519;
|
||||
use keyring::Keyring;
|
||||
|
||||
fn sign_prepare(key: &ed25519::Pair, round: u32, hash: HeaderHash, parent_hash: HeaderHash) -> (HeaderHash, Signature) {
|
||||
let msg = substrate_bft::sign_message(
|
||||
use runtime_primitives::testing::{H256, Block as RawBlock};
|
||||
|
||||
type Block = RawBlock<u64>;
|
||||
|
||||
fn sign_prepare(key: &ed25519::Pair, round: u32, hash: H256, parent_hash: H256) -> (H256, Signature) {
|
||||
let msg = substrate_bft::sign_message::<Block>(
|
||||
generic::Message::Vote(generic::Vote::Prepare(round as _, hash)),
|
||||
key,
|
||||
parent_hash
|
||||
@@ -97,8 +106,8 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn sign_commit(key: &ed25519::Pair, round: u32, hash: HeaderHash, parent_hash: HeaderHash) -> (HeaderHash, Signature) {
|
||||
let msg = substrate_bft::sign_message(
|
||||
fn sign_commit(key: &ed25519::Pair, round: u32, hash: H256, parent_hash: H256) -> (H256, Signature) {
|
||||
let msg = substrate_bft::sign_message::<Block>(
|
||||
generic::Message::Vote(generic::Vote::Commit(round as _, hash)),
|
||||
key,
|
||||
parent_hash
|
||||
@@ -117,7 +126,7 @@ mod tests {
|
||||
let hash_1 = [0; 32].into();
|
||||
let hash_2 = [1; 32].into();
|
||||
|
||||
assert!(evaluate_misbehavior(
|
||||
assert!(evaluate_misbehavior::<Block, H256>(
|
||||
&key.public().0,
|
||||
parent_hash,
|
||||
&MisbehaviorKind::BftDoublePrepare(
|
||||
@@ -129,7 +138,7 @@ mod tests {
|
||||
|
||||
// same signature twice is not misbehavior.
|
||||
let signed = sign_prepare(&key, 1, hash_1, parent_hash);
|
||||
assert!(evaluate_misbehavior(
|
||||
assert!(evaluate_misbehavior::<Block, H256>(
|
||||
&key.public().0,
|
||||
parent_hash,
|
||||
&MisbehaviorKind::BftDoublePrepare(
|
||||
@@ -140,7 +149,7 @@ mod tests {
|
||||
) == false);
|
||||
|
||||
// misbehavior has wrong target.
|
||||
assert!(evaluate_misbehavior(
|
||||
assert!(evaluate_misbehavior::<Block, H256>(
|
||||
&Keyring::Two.to_raw_public(),
|
||||
parent_hash,
|
||||
&MisbehaviorKind::BftDoublePrepare(
|
||||
@@ -158,7 +167,7 @@ mod tests {
|
||||
let hash_1 = [0; 32].into();
|
||||
let hash_2 = [1; 32].into();
|
||||
|
||||
assert!(evaluate_misbehavior(
|
||||
assert!(evaluate_misbehavior::<Block, H256>(
|
||||
&key.public().0,
|
||||
parent_hash,
|
||||
&MisbehaviorKind::BftDoubleCommit(
|
||||
@@ -170,7 +179,7 @@ mod tests {
|
||||
|
||||
// same signature twice is not misbehavior.
|
||||
let signed = sign_commit(&key, 1, hash_1, parent_hash);
|
||||
assert!(evaluate_misbehavior(
|
||||
assert!(evaluate_misbehavior::<Block, H256>(
|
||||
&key.public().0,
|
||||
parent_hash,
|
||||
&MisbehaviorKind::BftDoubleCommit(
|
||||
@@ -181,7 +190,7 @@ mod tests {
|
||||
) == false);
|
||||
|
||||
// misbehavior has wrong target.
|
||||
assert!(evaluate_misbehavior(
|
||||
assert!(evaluate_misbehavior::<Block, H256>(
|
||||
&Keyring::Two.to_raw_public(),
|
||||
parent_hash,
|
||||
&MisbehaviorKind::BftDoubleCommit(
|
||||
|
||||
@@ -27,6 +27,7 @@ substrate-client = { path = "../../substrate/client" }
|
||||
substrate-state-machine = { path = "../../substrate/state-machine" }
|
||||
substrate-serializer = { path = "../../substrate/serializer" }
|
||||
substrate-runtime-support = { path = "../../substrate/runtime-support" }
|
||||
substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" }
|
||||
substrate-bft = { path = "../../substrate/bft" }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -20,47 +20,47 @@ use std::ops::Range;
|
||||
use std::collections::{HashMap, BTreeMap};
|
||||
use std::collections::hash_map::Entry;
|
||||
use network::PeerId;
|
||||
use primitives::block::Number as BlockNumber;
|
||||
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT};
|
||||
use message;
|
||||
|
||||
const MAX_PARALLEL_DOWNLOADS: u32 = 1;
|
||||
|
||||
/// Block data with origin.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct BlockData {
|
||||
pub block: message::BlockData,
|
||||
pub struct BlockData<B: BlockT> {
|
||||
pub block: message::BlockData<B>,
|
||||
pub origin: PeerId,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum BlockRangeState {
|
||||
enum BlockRangeState<B: BlockT> {
|
||||
Downloading {
|
||||
len: BlockNumber,
|
||||
len: u64,
|
||||
downloading: u32,
|
||||
},
|
||||
Complete(Vec<BlockData>),
|
||||
Complete(Vec<BlockData<B>>),
|
||||
}
|
||||
|
||||
impl BlockRangeState {
|
||||
pub fn len(&self) -> BlockNumber {
|
||||
impl<B: BlockT> BlockRangeState<B> where B::Header: HeaderT<Number=u64> {
|
||||
pub fn len(&self) -> u64 {
|
||||
match *self {
|
||||
BlockRangeState::Downloading { len, .. } => len,
|
||||
BlockRangeState::Complete(ref blocks) => blocks.len() as BlockNumber,
|
||||
BlockRangeState::Complete(ref blocks) => blocks.len() as u64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A collection of blocks being downloaded.
|
||||
#[derive(Default)]
|
||||
pub struct BlockCollection {
|
||||
pub struct BlockCollection<B: BlockT> {
|
||||
/// Downloaded blocks.
|
||||
blocks: BTreeMap<BlockNumber, BlockRangeState>,
|
||||
peer_requests: HashMap<PeerId, BlockNumber>,
|
||||
blocks: BTreeMap<u64, BlockRangeState<B>>,
|
||||
peer_requests: HashMap<PeerId, u64>,
|
||||
}
|
||||
|
||||
impl BlockCollection {
|
||||
impl<B: BlockT> BlockCollection<B> where B::Header: HeaderT<Number=u64> {
|
||||
/// Create a new instance.
|
||||
pub fn new() -> BlockCollection {
|
||||
pub fn new() -> Self {
|
||||
BlockCollection {
|
||||
blocks: BTreeMap::new(),
|
||||
peer_requests: HashMap::new(),
|
||||
@@ -74,7 +74,7 @@ impl BlockCollection {
|
||||
}
|
||||
|
||||
/// Insert a set of blocks into collection.
|
||||
pub fn insert(&mut self, start: BlockNumber, blocks: Vec<message::BlockData>, peer_id: PeerId) {
|
||||
pub fn insert(&mut self, start: u64, blocks: Vec<message::BlockData<B>>, peer_id: PeerId) {
|
||||
if blocks.is_empty() {
|
||||
return;
|
||||
}
|
||||
@@ -96,13 +96,13 @@ impl BlockCollection {
|
||||
}
|
||||
|
||||
/// Returns a set of block hashes that require a header download. The returned set is marked as being downloaded.
|
||||
pub fn needed_blocks(&mut self, peer_id: PeerId, count: usize, peer_best: BlockNumber, common: BlockNumber) -> Option<Range<BlockNumber>> {
|
||||
pub fn needed_blocks(&mut self, peer_id: PeerId, count: usize, peer_best: u64, common: u64) -> Option<Range<u64>> {
|
||||
// First block number that we need to download
|
||||
let first_different = common + 1;
|
||||
let count = count as BlockNumber;
|
||||
let count = count as u64;
|
||||
let (mut range, downloading) = {
|
||||
let mut downloading_iter = self.blocks.iter().peekable();
|
||||
let mut prev: Option<(&BlockNumber, &BlockRangeState)> = None;
|
||||
let mut prev: Option<(&u64, &BlockRangeState<B>)> = None;
|
||||
loop {
|
||||
let next = downloading_iter.next();
|
||||
break match &(prev, next) {
|
||||
@@ -137,7 +137,7 @@ impl BlockCollection {
|
||||
}
|
||||
|
||||
/// Get a valid chain of blocks ordered in descending order and ready for importing into blockchain.
|
||||
pub fn drain(&mut self, from: BlockNumber) -> Vec<BlockData> {
|
||||
pub fn drain(&mut self, from: u64) -> Vec<BlockData<B>> {
|
||||
let mut drained = Vec::new();
|
||||
let mut ranges = Vec::new();
|
||||
{
|
||||
@@ -145,7 +145,7 @@ impl BlockCollection {
|
||||
for (start, range_data) in &mut self.blocks {
|
||||
match range_data {
|
||||
&mut BlockRangeState::Complete(ref mut blocks) if *start <= prev => {
|
||||
prev = *start + blocks.len() as BlockNumber;
|
||||
prev = *start + blocks.len() as u64;
|
||||
let mut blocks = mem::replace(blocks, Vec::new());
|
||||
drained.append(&mut blocks);
|
||||
ranges.push(*start);
|
||||
@@ -191,16 +191,19 @@ impl BlockCollection {
|
||||
mod test {
|
||||
use super::{BlockCollection, BlockData};
|
||||
use message;
|
||||
use primitives::block::HeaderHash;
|
||||
use runtime_primitives::testing::Block as RawBlock;
|
||||
use primitives::H256;
|
||||
|
||||
fn is_empty(bc: &BlockCollection) -> bool {
|
||||
type Block = RawBlock<u64>;
|
||||
|
||||
fn is_empty(bc: &BlockCollection<Block>) -> bool {
|
||||
bc.blocks.is_empty() &&
|
||||
bc.peer_requests.is_empty()
|
||||
}
|
||||
|
||||
fn generate_blocks(n: usize) -> Vec<message::BlockData> {
|
||||
(0 .. n).map(|_| message::BlockData {
|
||||
hash: HeaderHash::random(),
|
||||
fn generate_blocks(n: usize) -> Vec<message::BlockData<Block>> {
|
||||
(0 .. n).map(|_| message::generic::BlockData {
|
||||
hash: H256::random(),
|
||||
header: None,
|
||||
body: None,
|
||||
message_queue: None,
|
||||
|
||||
@@ -19,72 +19,74 @@
|
||||
use client::{self, Client as PolkadotClient, ImportResult, ClientInfo, BlockStatus, BlockOrigin, CallExecutor};
|
||||
use client::error::Error;
|
||||
use state_machine;
|
||||
use primitives::block::{self, Id as BlockId};
|
||||
use primitives::bft::Justification;
|
||||
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT};
|
||||
use runtime_primitives::generic::BlockId;
|
||||
use runtime_primitives::bft::Justification;
|
||||
|
||||
pub trait Client: Send + Sync {
|
||||
pub trait Client<Block: BlockT>: Send + Sync {
|
||||
/// Import a new block. Parent is supposed to be existing in the blockchain.
|
||||
fn import(&self, is_best: bool, header: block::Header, justification: Justification, body: Option<block::Body>) -> Result<ImportResult, Error>;
|
||||
fn import(&self, is_best: bool, header: Block::Header, justification: Justification<Block::Hash>, body: Option<Vec<Block::Extrinsic>>) -> Result<ImportResult, Error>;
|
||||
|
||||
/// Get blockchain info.
|
||||
fn info(&self) -> Result<ClientInfo, Error>;
|
||||
fn info(&self) -> Result<ClientInfo<Block>, Error>;
|
||||
|
||||
/// Get block status.
|
||||
fn block_status(&self, id: &BlockId) -> Result<BlockStatus, Error>;
|
||||
fn block_status(&self, id: &BlockId<Block>) -> Result<BlockStatus, Error>;
|
||||
|
||||
/// Get block hash by number.
|
||||
fn block_hash(&self, block_number: block::Number) -> Result<Option<block::HeaderHash>, Error>;
|
||||
fn block_hash(&self, block_number: <Block::Header as HeaderT>::Number) -> Result<Option<Block::Hash>, Error>;
|
||||
|
||||
/// Get block header.
|
||||
fn header(&self, id: &BlockId) -> Result<Option<block::Header>, Error>;
|
||||
fn header(&self, id: &BlockId<Block>) -> Result<Option<Block::Header>, Error>;
|
||||
|
||||
/// Get block body.
|
||||
fn body(&self, id: &BlockId) -> Result<Option<block::Body>, Error>;
|
||||
fn body(&self, id: &BlockId<Block>) -> Result<Option<Vec<Block::Extrinsic>>, Error>;
|
||||
|
||||
/// Get block justification.
|
||||
fn justification(&self, id: &BlockId) -> Result<Option<Justification>, Error>;
|
||||
fn justification(&self, id: &BlockId<Block>) -> Result<Option<Justification<Block::Hash>>, Error>;
|
||||
|
||||
/// Get method execution proof.
|
||||
fn execution_proof(&self, block: &block::HeaderHash, method: &str, data: &[u8]) -> Result<(Vec<u8>, Vec<Vec<u8>>), Error>;
|
||||
fn execution_proof(&self, block: &Block::Hash, method: &str, data: &[u8]) -> Result<(Vec<u8>, Vec<Vec<u8>>), Error>;
|
||||
}
|
||||
|
||||
impl<B, E> Client for PolkadotClient<B, E> where
|
||||
B: client::backend::Backend + Send + Sync + 'static,
|
||||
E: CallExecutor + Send + Sync + 'static,
|
||||
Error: From<<<B as client::backend::Backend>::State as state_machine::backend::Backend>::Error>, {
|
||||
impl<B, E, Block> Client<Block> for PolkadotClient<B, E, Block> where
|
||||
B: client::backend::Backend<Block> + Send + Sync + 'static,
|
||||
E: CallExecutor<Block> + Send + Sync + 'static,
|
||||
Block: BlockT,
|
||||
Error: From<<<B as client::backend::Backend<Block>>::State as state_machine::backend::Backend>::Error>, {
|
||||
|
||||
fn import(&self, is_best: bool, header: block::Header, justification: Justification, body: Option<block::Body>) -> Result<ImportResult, Error> {
|
||||
fn import(&self, is_best: bool, header: Block::Header, justification: Justification<Block::Hash>, body: Option<Vec<Block::Extrinsic>>) -> Result<ImportResult, Error> {
|
||||
// TODO: defer justification check.
|
||||
let justified_header = self.check_justification(header, justification.into())?;
|
||||
let origin = if is_best { BlockOrigin::NetworkBroadcast } else { BlockOrigin::NetworkInitialSync };
|
||||
(self as &PolkadotClient<B, E>).import_block(origin, justified_header, body)
|
||||
(self as &PolkadotClient<B, E, Block>).import_block(origin, justified_header, body)
|
||||
}
|
||||
|
||||
fn info(&self) -> Result<ClientInfo, Error> {
|
||||
(self as &PolkadotClient<B, E>).info()
|
||||
fn info(&self) -> Result<ClientInfo<Block>, Error> {
|
||||
(self as &PolkadotClient<B, E, Block>).info()
|
||||
}
|
||||
|
||||
fn block_status(&self, id: &BlockId) -> Result<BlockStatus, Error> {
|
||||
(self as &PolkadotClient<B, E>).block_status(id)
|
||||
fn block_status(&self, id: &BlockId<Block>) -> Result<BlockStatus, Error> {
|
||||
(self as &PolkadotClient<B, E, Block>).block_status(id)
|
||||
}
|
||||
|
||||
fn block_hash(&self, block_number: block::Number) -> Result<Option<block::HeaderHash>, Error> {
|
||||
(self as &PolkadotClient<B, E>).block_hash(block_number)
|
||||
fn block_hash(&self, block_number: <Block::Header as HeaderT>::Number) -> Result<Option<Block::Hash>, Error> {
|
||||
(self as &PolkadotClient<B, E, Block>).block_hash(block_number)
|
||||
}
|
||||
|
||||
fn header(&self, id: &BlockId) -> Result<Option<block::Header>, Error> {
|
||||
(self as &PolkadotClient<B, E>).header(id)
|
||||
fn header(&self, id: &BlockId<Block>) -> Result<Option<Block::Header>, Error> {
|
||||
(self as &PolkadotClient<B, E, Block>).header(id)
|
||||
}
|
||||
|
||||
fn body(&self, id: &BlockId) -> Result<Option<block::Body>, Error> {
|
||||
(self as &PolkadotClient<B, E>).body(id)
|
||||
fn body(&self, id: &BlockId<Block>) -> Result<Option<Vec<Block::Extrinsic>>, Error> {
|
||||
(self as &PolkadotClient<B, E, Block>).body(id)
|
||||
}
|
||||
|
||||
fn justification(&self, id: &BlockId) -> Result<Option<Justification>, Error> {
|
||||
(self as &PolkadotClient<B, E>).justification(id)
|
||||
fn justification(&self, id: &BlockId<Block>) -> Result<Option<Justification<Block::Hash>>, Error> {
|
||||
(self as &PolkadotClient<B, E, Block>).justification(id)
|
||||
}
|
||||
|
||||
fn execution_proof(&self, block: &block::HeaderHash, method: &str, data: &[u8]) -> Result<(Vec<u8>, Vec<Vec<u8>>), Error> {
|
||||
(self as &PolkadotClient<B, E>).execution_proof(&BlockId::Hash(block.clone()), method, data)
|
||||
fn execution_proof(&self, block: &Block::Hash, method: &str, data: &[u8]) -> Result<(Vec<u8>, Vec<Vec<u8>>), Error> {
|
||||
(self as &PolkadotClient<B, E, Block>).execution_proof(&BlockId::Hash(block.clone()), method, data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,46 +17,35 @@
|
||||
//! Consensus related bits of the network service.
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use futures::sync::{oneshot, mpsc};
|
||||
use futures::sync::mpsc;
|
||||
use std::time::{Instant, Duration};
|
||||
use io::SyncIo;
|
||||
use protocol::Protocol;
|
||||
use network::PeerId;
|
||||
use primitives::{Hash, block::Id as BlockId, block::Header};
|
||||
use message::{self, Message};
|
||||
use runtime_support::Hashable;
|
||||
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT};
|
||||
use runtime_primitives::generic::BlockId;
|
||||
use message::{self, generic::Message as GenericMessage};
|
||||
|
||||
// TODO: Add additional spam/DoS attack protection.
|
||||
const MESSAGE_LIFETIME: Duration = Duration::from_secs(600);
|
||||
|
||||
struct CandidateRequest {
|
||||
id: message::RequestId,
|
||||
completion: oneshot::Sender<Vec<u8>>,
|
||||
}
|
||||
|
||||
struct PeerConsensus {
|
||||
candidate_fetch: Option<CandidateRequest>,
|
||||
candidate_available: Option<Hash>,
|
||||
known_messages: HashSet<Hash>,
|
||||
struct PeerConsensus<H> {
|
||||
known_messages: HashSet<H>,
|
||||
}
|
||||
|
||||
/// Consensus network protocol handler. Manages statements and candidate requests.
|
||||
pub struct Consensus {
|
||||
peers: HashMap<PeerId, PeerConsensus>,
|
||||
our_candidate: Option<(Hash, Vec<u8>)>,
|
||||
statement_sink: Option<mpsc::UnboundedSender<message::Statement>>,
|
||||
bft_message_sink: Option<(mpsc::UnboundedSender<message::LocalizedBftMessage>, Hash)>,
|
||||
messages: Vec<(Hash, Instant, message::Message)>,
|
||||
message_hashes: HashSet<Hash>,
|
||||
pub struct Consensus<B: BlockT> {
|
||||
peers: HashMap<PeerId, PeerConsensus<B::Hash>>,
|
||||
bft_message_sink: Option<(mpsc::UnboundedSender<message::LocalizedBftMessage<B>>, B::Hash)>,
|
||||
messages: Vec<(B::Hash, Instant, message::Message<B>)>,
|
||||
message_hashes: HashSet<B::Hash>,
|
||||
}
|
||||
|
||||
impl Consensus {
|
||||
impl<B: BlockT> Consensus<B> where B::Header: HeaderT<Number=u64> {
|
||||
/// Create a new instance.
|
||||
pub fn new() -> Consensus {
|
||||
pub fn new() -> Self {
|
||||
Consensus {
|
||||
peers: HashMap::new(),
|
||||
our_candidate: None,
|
||||
statement_sink: None,
|
||||
bft_message_sink: None,
|
||||
messages: Default::default(),
|
||||
message_hashes: Default::default(),
|
||||
@@ -65,12 +54,11 @@ impl Consensus {
|
||||
|
||||
/// Closes all notification streams.
|
||||
pub fn restart(&mut self) {
|
||||
self.statement_sink = None;
|
||||
self.bft_message_sink = None;
|
||||
}
|
||||
|
||||
/// Handle new connected peer.
|
||||
pub fn new_peer(&mut self, io: &mut SyncIo, protocol: &Protocol, peer_id: PeerId, roles: &[message::Role]) {
|
||||
pub fn new_peer(&mut self, io: &mut SyncIo, protocol: &Protocol<B>, peer_id: PeerId, roles: &[message::Role]) {
|
||||
if roles.iter().any(|r| *r == message::Role::Validator) {
|
||||
trace!(target:"sync", "Registering validator {}", peer_id);
|
||||
// Send out all known messages.
|
||||
@@ -81,14 +69,12 @@ impl Consensus {
|
||||
protocol.send_message(io, peer_id, message.clone());
|
||||
}
|
||||
self.peers.insert(peer_id, PeerConsensus {
|
||||
candidate_fetch: None,
|
||||
candidate_available: None,
|
||||
known_messages,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn propagate(&mut self, io: &mut SyncIo, protocol: &Protocol, message: message::Message, hash: Hash) {
|
||||
fn propagate(&mut self, io: &mut SyncIo, protocol: &Protocol<B>, message: message::Message<B>, hash: B::Hash) {
|
||||
for (id, ref mut peer) in self.peers.iter_mut() {
|
||||
if peer.known_messages.insert(hash.clone()) {
|
||||
protocol.send_message(io, *id, message.clone());
|
||||
@@ -96,48 +82,13 @@ impl Consensus {
|
||||
}
|
||||
}
|
||||
|
||||
fn register_message(&mut self, hash: Hash, message: message::Message) {
|
||||
fn register_message(&mut self, hash: B::Hash, message: message::Message<B>) {
|
||||
if self.message_hashes.insert(hash) {
|
||||
self.messages.push((hash, Instant::now(), message));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_statement(&mut self, io: &mut SyncIo, protocol: &Protocol, peer_id: PeerId, statement: message::Statement, hash: Hash) {
|
||||
if self.message_hashes.contains(&hash) {
|
||||
trace!(target:"sync", "Ignored already known statement from {}", peer_id);
|
||||
}
|
||||
if let Some(ref mut peer) = self.peers.get_mut(&peer_id) {
|
||||
// TODO: validate signature?
|
||||
match &statement.statement {
|
||||
&message::UnsignedStatement::Candidate(ref receipt) => peer.candidate_available = Some(Hash::from(receipt.blake2_256())),
|
||||
&message::UnsignedStatement::Available(ref hash) => peer.candidate_available = Some(*hash),
|
||||
&message::UnsignedStatement::Valid(_) | &message::UnsignedStatement::Invalid(_) => (),
|
||||
}
|
||||
peer.known_messages.insert(hash);
|
||||
if let Some(sink) = self.statement_sink.take() {
|
||||
if let Err(e) = sink.unbounded_send(statement.clone()) {
|
||||
trace!(target:"sync", "Error broadcasting statement notification: {:?}", e);
|
||||
} else {
|
||||
self.statement_sink = Some(sink);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trace!(target:"sync", "Ignored statement from unregistered peer {}", peer_id);
|
||||
return;
|
||||
}
|
||||
let message = Message::Statement(statement);
|
||||
self.register_message(hash.clone(), message.clone());
|
||||
// Propagate to other peers.
|
||||
self.propagate(io, protocol, message, hash);
|
||||
}
|
||||
|
||||
pub fn statements(&mut self) -> mpsc::UnboundedReceiver<message::Statement>{
|
||||
let (sink, stream) = mpsc::unbounded();
|
||||
self.statement_sink = Some(sink);
|
||||
stream
|
||||
}
|
||||
|
||||
pub fn on_bft_message(&mut self, io: &mut SyncIo, protocol: &Protocol, peer_id: PeerId, message: message::LocalizedBftMessage, hash: Hash) {
|
||||
pub fn on_bft_message(&mut self, io: &mut SyncIo, protocol: &Protocol<B>, peer_id: PeerId, message: message::LocalizedBftMessage<B>, hash: B::Hash) {
|
||||
if self.message_hashes.contains(&hash) {
|
||||
trace!(target:"sync", "Ignored already known BFT message from {}", peer_id);
|
||||
return;
|
||||
@@ -149,7 +100,7 @@ impl Consensus {
|
||||
return;
|
||||
},
|
||||
(Ok(info), Ok(Some(header))) => {
|
||||
if header.number < info.chain.best_number {
|
||||
if header.number() < &info.chain.best_number {
|
||||
trace!(target:"sync", "Ignored ancient BFT message from {}, hash={}", peer_id, message.parent_hash);
|
||||
return;
|
||||
}
|
||||
@@ -174,18 +125,18 @@ impl Consensus {
|
||||
return;
|
||||
}
|
||||
|
||||
let message = Message::BftMessage(message);
|
||||
let message = GenericMessage::BftMessage(message);
|
||||
self.register_message(hash.clone(), message.clone());
|
||||
// Propagate to other peers.
|
||||
self.propagate(io, protocol, message, hash);
|
||||
}
|
||||
|
||||
pub fn bft_messages(&mut self, parent_hash: Hash) -> mpsc::UnboundedReceiver<message::LocalizedBftMessage>{
|
||||
pub fn bft_messages(&mut self, parent_hash: B::Hash) -> mpsc::UnboundedReceiver<message::LocalizedBftMessage<B>> {
|
||||
let (sink, stream) = mpsc::unbounded();
|
||||
|
||||
for &(_, _, ref message) in self.messages.iter() {
|
||||
let bft_message = match *message {
|
||||
Message::BftMessage(ref msg) => msg,
|
||||
GenericMessage::BftMessage(ref msg) => msg,
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
@@ -198,90 +149,20 @@ impl Consensus {
|
||||
stream
|
||||
}
|
||||
|
||||
pub fn fetch_candidate(&mut self, io: &mut SyncIo, protocol: &Protocol, hash: &Hash) -> oneshot::Receiver<Vec<u8>> {
|
||||
// Request from the first peer that has it available.
|
||||
// TODO: random peer selection.
|
||||
trace!(target:"sync", "Trying to fetch candidate {:?}", hash);
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
if let Some((peer_id, ref mut peer)) = self.peers.iter_mut()
|
||||
.find(|&(_, ref peer)| peer.candidate_fetch.is_none() && peer.candidate_available.as_ref().map_or(false, |h| h == hash)) {
|
||||
|
||||
trace!(target:"sync", "Fetching candidate from {}", peer_id);
|
||||
let id = 0; //TODO: generate unique id
|
||||
peer.candidate_fetch = Some(CandidateRequest {
|
||||
id: id,
|
||||
completion: sender,
|
||||
});
|
||||
let request = message::CandidateRequest {
|
||||
id: id,
|
||||
hash: *hash,
|
||||
};
|
||||
protocol.send_message(io, *peer_id, Message::CandidateRequest(request));
|
||||
}
|
||||
// If no peer found `sender` is dropped and `receiver` is canceled immediatelly.
|
||||
return receiver;
|
||||
}
|
||||
|
||||
pub fn send_statement(&mut self, io: &mut SyncIo, protocol: &Protocol, statement: message::Statement) {
|
||||
// Broadcast statement to all validators.
|
||||
trace!(target:"sync", "Broadcasting statement {:?}", statement);
|
||||
let message = Message::Statement(statement);
|
||||
let hash = Protocol::hash_message(&message);
|
||||
self.register_message(hash.clone(), message.clone());
|
||||
self.propagate(io, protocol, message, hash);
|
||||
}
|
||||
|
||||
pub fn send_bft_message(&mut self, io: &mut SyncIo, protocol: &Protocol, message: message::LocalizedBftMessage) {
|
||||
pub fn send_bft_message(&mut self, io: &mut SyncIo, protocol: &Protocol<B>, message: message::LocalizedBftMessage<B>) {
|
||||
// Broadcast message to all validators.
|
||||
trace!(target:"sync", "Broadcasting BFT message {:?}", message);
|
||||
let message = Message::BftMessage(message);
|
||||
let message = GenericMessage::BftMessage(message);
|
||||
let hash = Protocol::hash_message(&message);
|
||||
self.register_message(hash.clone(), message.clone());
|
||||
self.propagate(io, protocol, message, hash);
|
||||
}
|
||||
|
||||
pub fn set_local_candidate(&mut self, candidate: Option<(Hash, Vec<u8>)>) {
|
||||
trace!(target:"sync", "Set local candidate to {:?}", candidate.as_ref().map(|&(h, _)| h));
|
||||
self.our_candidate = candidate;
|
||||
}
|
||||
|
||||
pub fn on_candidate_request(&mut self, io: &mut SyncIo, protocol: &Protocol, peer_id: PeerId, request: message::CandidateRequest) {
|
||||
let response = match self.our_candidate {
|
||||
Some((ref hash, ref data)) if *hash == request.hash => Some(data.clone()),
|
||||
_ => None,
|
||||
};
|
||||
let msg = message::CandidateResponse {
|
||||
id: request.id,
|
||||
data: response,
|
||||
};
|
||||
protocol.send_message(io, peer_id, Message::CandidateResponse(msg));
|
||||
}
|
||||
|
||||
pub fn on_candidate_response(&mut self, io: &mut SyncIo, _protocol: &Protocol, peer_id: PeerId, response: message::CandidateResponse) {
|
||||
if let Some(ref mut peer) = self.peers.get_mut(&peer_id) {
|
||||
if let Some(request) = peer.candidate_fetch.take() {
|
||||
if response.id == request.id {
|
||||
if let Some(data) = response.data {
|
||||
if let Err(e) = request.completion.send(data) {
|
||||
trace!(target:"sync", "Error sending candidate data notification: {:?}", e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trace!(target:"sync", "Unexpected candidate response from {}", peer_id);
|
||||
io.disable_peer(peer_id);
|
||||
}
|
||||
} else {
|
||||
trace!(target:"sync", "Unexpected candidate response from {}", peer_id);
|
||||
io.disable_peer(peer_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn peer_disconnected(&mut self, _io: &mut SyncIo, _protocol: &Protocol, peer_id: PeerId) {
|
||||
pub fn peer_disconnected(&mut self, _io: &mut SyncIo, _protocol: &Protocol<B>, peer_id: PeerId) {
|
||||
self.peers.remove(&peer_id);
|
||||
}
|
||||
|
||||
pub fn collect_garbage(&mut self, best_header: Option<&Header>) {
|
||||
pub fn collect_garbage(&mut self, best_header: Option<&B::Header>) {
|
||||
let hashes = &mut self.message_hashes;
|
||||
let before = self.messages.len();
|
||||
let now = Instant::now();
|
||||
@@ -289,8 +170,7 @@ impl Consensus {
|
||||
if timestamp >= now - MESSAGE_LIFETIME &&
|
||||
best_header.map_or(true, |header|
|
||||
match *message {
|
||||
Message::BftMessage(ref msg) => msg.parent_hash != header.parent_hash,
|
||||
Message::Statement(ref msg) => msg.parent_hash != header.parent_hash,
|
||||
GenericMessage::BftMessage(ref msg) => &msg.parent_hash != header.parent_hash(),
|
||||
_ => true,
|
||||
})
|
||||
{
|
||||
@@ -311,32 +191,33 @@ impl Consensus {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use primitives::Hash;
|
||||
use primitives::bft::Justification;
|
||||
use primitives::block::{HeaderHash, Header};
|
||||
use runtime_primitives::bft::Justification;
|
||||
use runtime_primitives::testing::{H256, Header, Block as RawBlock};
|
||||
use std::time::Instant;
|
||||
use message::{self, Message};
|
||||
use message::{self, generic::Message as GenericMessage};
|
||||
use super::{Consensus, MESSAGE_LIFETIME};
|
||||
|
||||
type Block = RawBlock<u64>;
|
||||
|
||||
#[test]
|
||||
fn collects_garbage() {
|
||||
let prev_hash = HeaderHash::random();
|
||||
let best_hash = HeaderHash::random();
|
||||
let mut consensus = Consensus::new();
|
||||
let prev_hash = H256::random();
|
||||
let best_hash = H256::random();
|
||||
let mut consensus = Consensus::<Block>::new();
|
||||
let now = Instant::now();
|
||||
let m1_hash = Hash::random();
|
||||
let m2_hash = Hash::random();
|
||||
let m1 = Message::BftMessage(message::LocalizedBftMessage {
|
||||
let m1_hash = H256::random();
|
||||
let m2_hash = H256::random();
|
||||
let m1 = GenericMessage::BftMessage(message::LocalizedBftMessage {
|
||||
parent_hash: prev_hash,
|
||||
message: message::BftMessage::Auxiliary(Justification {
|
||||
message: message::generic::BftMessage::Auxiliary(Justification {
|
||||
round_number: 0,
|
||||
hash: Default::default(),
|
||||
signatures: Default::default(),
|
||||
}),
|
||||
});
|
||||
let m2 = Message::BftMessage(message::LocalizedBftMessage {
|
||||
let m2 = GenericMessage::BftMessage(message::LocalizedBftMessage {
|
||||
parent_hash: best_hash,
|
||||
message: message::BftMessage::Auxiliary(Justification {
|
||||
message: message::generic::BftMessage::Auxiliary(Justification {
|
||||
round_number: 0,
|
||||
hash: Default::default(),
|
||||
signatures: Default::default(),
|
||||
@@ -353,7 +234,14 @@ mod tests {
|
||||
assert_eq!(consensus.message_hashes.len(), 2);
|
||||
|
||||
// random header, nothing should be cleared
|
||||
let mut header = Header::from_block_number(0);
|
||||
let mut header = Header {
|
||||
parent_hash: H256::default(),
|
||||
number: 0,
|
||||
state_root: H256::default(),
|
||||
extrinsics_root: H256::default(),
|
||||
digest: Default::default(),
|
||||
};
|
||||
|
||||
consensus.collect_garbage(Some(&header));
|
||||
assert_eq!(consensus.messages.len(), 2);
|
||||
assert_eq!(consensus.message_hashes.len(), 2);
|
||||
|
||||
@@ -30,6 +30,7 @@ extern crate substrate_state_machine as state_machine;
|
||||
extern crate substrate_serializer as ser;
|
||||
extern crate substrate_client as client;
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
extern crate substrate_runtime_primitives as runtime_primitives;
|
||||
extern crate substrate_bft;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
@@ -59,19 +60,12 @@ pub mod error;
|
||||
|
||||
#[cfg(test)] mod test;
|
||||
|
||||
pub use service::{Service, FetchFuture, StatementStream, ConsensusService, BftMessageStream,
|
||||
pub use service::{Service, FetchFuture, ConsensusService, BftMessageStream,
|
||||
TransactionPool, Params, ManageNetwork, SyncProvider};
|
||||
pub use protocol::{ProtocolStatus};
|
||||
pub use sync::{Status as SyncStatus, SyncState};
|
||||
pub use network::{NonReservedPeerMode, NetworkConfiguration};
|
||||
pub use network_devp2p::{ConnectionFilter, ConnectionDirection};
|
||||
pub use message::{Statement, BftMessage, LocalizedBftMessage, ConsensusVote, SignedConsensusVote, SignedConsensusMessage, SignedConsensusProposal};
|
||||
pub use network::{NonReservedPeerMode, NetworkConfiguration, ConnectionFilter, ConnectionDirection};
|
||||
pub use message::{generic as generic_message, BftMessage, LocalizedBftMessage, ConsensusVote, SignedConsensusVote, SignedConsensusMessage, SignedConsensusProposal};
|
||||
pub use error::Error;
|
||||
pub use config::{Role, ProtocolConfig};
|
||||
pub use on_demand::{OnDemand, OnDemandService, Response as OnDemandResponse};
|
||||
|
||||
// TODO: move it elsewhere
|
||||
fn header_hash(header: &primitives::Header) -> primitives::block::HeaderHash {
|
||||
use runtime_support::Hashable;
|
||||
header.blake2_256().into()
|
||||
}
|
||||
|
||||
@@ -16,14 +16,74 @@
|
||||
|
||||
//! Network packet message types. These get serialized and put into the lower level protocol payload.
|
||||
|
||||
use primitives::{AuthorityId, Hash};
|
||||
use primitives::block::{Number as BlockNumber, HeaderHash, Header, Body, Block};
|
||||
use primitives::bft::Justification;
|
||||
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT};
|
||||
use service::Role as RoleFlags;
|
||||
use ed25519;
|
||||
|
||||
pub use self::generic::{BlockAnnounce, RemoteCallRequest, ConsensusVote, SignedConsensusVote, FromBlock};
|
||||
|
||||
pub type RequestId = u64;
|
||||
type Bytes = Vec<u8>;
|
||||
|
||||
/// Type alias for using the message type using block type parameters.
|
||||
pub type Message<B> = generic::Message<
|
||||
B,
|
||||
<B as BlockT>::Header,
|
||||
<B as BlockT>::Hash,
|
||||
<<B as BlockT>::Header as HeaderT>::Number,
|
||||
<B as BlockT>::Extrinsic,
|
||||
>;
|
||||
|
||||
/// Type alias for using the status type using block type parameters.
|
||||
pub type Status<B> = generic::Status<
|
||||
<B as BlockT>::Hash,
|
||||
<<B as BlockT>::Header as HeaderT>::Number,
|
||||
>;
|
||||
|
||||
/// Type alias for using the block request type using block type parameters.
|
||||
pub type BlockRequest<B> = generic::BlockRequest<
|
||||
<B as BlockT>::Hash,
|
||||
<<B as BlockT>::Header as HeaderT>::Number,
|
||||
>;
|
||||
|
||||
/// Type alias for using the localized bft message type using block type parameters.
|
||||
pub type LocalizedBftMessage<B> = generic::LocalizedBftMessage<
|
||||
B,
|
||||
<B as BlockT>::Hash,
|
||||
>;
|
||||
|
||||
/// Type alias for using the BlockData type using block type parameters.
|
||||
pub type BlockData<B> = generic::BlockData<
|
||||
<B as BlockT>::Header,
|
||||
<B as BlockT>::Hash,
|
||||
<B as BlockT>::Extrinsic,
|
||||
>;
|
||||
|
||||
/// Type alias for using the BlockResponse type using block type parameters.
|
||||
pub type BlockResponse<B> = generic::BlockResponse<
|
||||
<B as BlockT>::Header,
|
||||
<B as BlockT>::Hash,
|
||||
<B as BlockT>::Extrinsic,
|
||||
>;
|
||||
|
||||
/// Type alias for using the BftMessage type using block type parameters.
|
||||
pub type BftMessage<B> = generic::BftMessage<
|
||||
B,
|
||||
<B as BlockT>::Hash,
|
||||
>;
|
||||
|
||||
/// Type alias for using the SignedConsensusProposal type using block type parameters.
|
||||
pub type SignedConsensusProposal<B> = generic::SignedConsensusProposal<
|
||||
B,
|
||||
<B as BlockT>::Hash,
|
||||
>;
|
||||
|
||||
/// Type alias for using the SignedConsensusProposal type using block type parameters.
|
||||
pub type SignedConsensusMessage<B> = generic::SignedConsensusProposal<
|
||||
B,
|
||||
<B as BlockT>::Hash,
|
||||
>;
|
||||
|
||||
/// A set of transactions.
|
||||
pub type Transactions<E> = Vec<E>;
|
||||
|
||||
/// Configured node role.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
@@ -73,8 +133,8 @@ impl From<RoleFlags> for Vec<Role> where {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Copy, Clone)]
|
||||
/// Bits of block data and associated artefacts to request.
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Copy, Clone)]
|
||||
pub enum BlockAttribute {
|
||||
/// Include block header.
|
||||
Header,
|
||||
@@ -88,33 +148,6 @@ pub enum BlockAttribute {
|
||||
Justification,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
/// Block data sent in the response.
|
||||
pub struct BlockData {
|
||||
/// Block header hash.
|
||||
pub hash: HeaderHash,
|
||||
/// Block header if requested.
|
||||
pub header: Option<Header>,
|
||||
/// Block body if requested.
|
||||
pub body: Option<Body>,
|
||||
/// Block receipt if requested.
|
||||
pub receipt: Option<Bytes>,
|
||||
/// Block message queue if requested.
|
||||
pub message_queue: Option<Bytes>,
|
||||
/// Justification if requested.
|
||||
pub justification: Option<Justification>,
|
||||
}
|
||||
|
||||
#[serde(untagged)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
/// Identifies starting point of a block sequence.
|
||||
pub enum FromBlock {
|
||||
/// Start with given hash.
|
||||
Hash(HeaderHash),
|
||||
/// Start with given block number.
|
||||
Number(BlockNumber),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
/// Block enumeration direction.
|
||||
pub enum Direction {
|
||||
@@ -124,219 +157,6 @@ pub enum Direction {
|
||||
Descending,
|
||||
}
|
||||
|
||||
/// A set of transactions.
|
||||
pub type Transactions = Vec<Vec<u8>>;
|
||||
|
||||
/// Statements circulated among peers.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub enum UnsignedStatement {
|
||||
/// Broadcast by a authority to indicate that this is his candidate for
|
||||
/// inclusion.
|
||||
///
|
||||
/// Broadcasting two different candidate messages per round is not allowed.
|
||||
Candidate(Vec<u8>),
|
||||
/// Broadcast by a authority to attest that the candidate with given digest
|
||||
/// is valid.
|
||||
Valid(Hash),
|
||||
/// Broadcast by a authority to attest that the auxiliary data for a candidate
|
||||
/// with given digest is available.
|
||||
Available(Hash),
|
||||
/// Broadcast by a authority to attest that the candidate with given digest
|
||||
/// is invalid.
|
||||
Invalid(Hash),
|
||||
}
|
||||
|
||||
/// A signed statement.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub struct Statement {
|
||||
/// Parent relay chain block header hash.
|
||||
pub parent_hash: HeaderHash,
|
||||
/// The statement.
|
||||
pub statement: UnsignedStatement,
|
||||
/// The signature.
|
||||
pub signature: ed25519::Signature,
|
||||
/// The sender.
|
||||
pub sender: AuthorityId,
|
||||
}
|
||||
|
||||
|
||||
/// Communication that can occur between participants in consensus.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub enum BftMessage {
|
||||
/// A consensus message (proposal or vote)
|
||||
Consensus(SignedConsensusMessage),
|
||||
/// Auxiliary communication (just proof-of-lock for now).
|
||||
Auxiliary(Justification),
|
||||
}
|
||||
|
||||
/// BFT Consensus message with parent header hash attached to it.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub struct LocalizedBftMessage {
|
||||
/// Consensus message.
|
||||
pub message: BftMessage,
|
||||
/// Parent header hash.
|
||||
pub parent_hash: HeaderHash,
|
||||
}
|
||||
|
||||
/// A localized proposal message. Contains two signed pieces of data.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub struct SignedConsensusProposal {
|
||||
/// The round number.
|
||||
pub round_number: u32,
|
||||
/// The proposal sent.
|
||||
pub proposal: Block,
|
||||
/// The digest of the proposal.
|
||||
pub digest: Hash,
|
||||
/// The sender of the proposal
|
||||
pub sender: AuthorityId,
|
||||
/// The signature on the message (propose, round number, digest)
|
||||
pub digest_signature: ed25519::Signature,
|
||||
/// The signature on the message (propose, round number, proposal)
|
||||
pub full_signature: ed25519::Signature,
|
||||
}
|
||||
|
||||
/// A localized vote message, including the sender.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub struct SignedConsensusVote {
|
||||
/// The message sent.
|
||||
pub vote: ConsensusVote,
|
||||
/// The sender of the message
|
||||
pub sender: AuthorityId,
|
||||
/// The signature of the message.
|
||||
pub signature: ed25519::Signature,
|
||||
}
|
||||
|
||||
/// Votes during a consensus round.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub enum ConsensusVote {
|
||||
/// Prepare to vote for proposal with digest D.
|
||||
Prepare(u32, Hash),
|
||||
/// Commit to proposal with digest D..
|
||||
Commit(u32, Hash),
|
||||
/// Propose advancement to a new round.
|
||||
AdvanceRound(u32),
|
||||
}
|
||||
|
||||
/// A localized message.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub enum SignedConsensusMessage {
|
||||
/// A proposal.
|
||||
Propose(SignedConsensusProposal),
|
||||
/// A vote.
|
||||
Vote(SignedConsensusVote),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
/// A network message.
|
||||
pub enum Message {
|
||||
/// Status packet.
|
||||
Status(Status),
|
||||
/// Block request.
|
||||
BlockRequest(BlockRequest),
|
||||
/// Block response.
|
||||
BlockResponse(BlockResponse),
|
||||
/// Block announce.
|
||||
BlockAnnounce(BlockAnnounce),
|
||||
/// Transactions.
|
||||
Transactions(Transactions),
|
||||
/// Consensus statement.
|
||||
Statement(Statement),
|
||||
/// Candidate data request.
|
||||
CandidateRequest(CandidateRequest),
|
||||
/// Candidate response.
|
||||
CandidateResponse(CandidateResponse),
|
||||
/// BFT Consensus statement.
|
||||
BftMessage(LocalizedBftMessage),
|
||||
/// Remote method call request.
|
||||
RemoteCallRequest(RemoteCallRequest),
|
||||
/// Remote method call response.
|
||||
RemoteCallResponse(RemoteCallResponse),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub struct Status {
|
||||
/// Protocol version.
|
||||
pub version: u32,
|
||||
/// Supported roles.
|
||||
pub roles: Vec<Role>,
|
||||
/// Best block number.
|
||||
pub best_number: BlockNumber,
|
||||
/// Best block hash.
|
||||
pub best_hash: HeaderHash,
|
||||
/// Genesis block hash.
|
||||
pub genesis_hash: HeaderHash,
|
||||
/// Signatue of `best_hash` made with validator address. Required for the validator role.
|
||||
pub validator_signature: Option<ed25519::Signature>,
|
||||
/// Validator address. Required for the validator role.
|
||||
pub validator_id: Option<AuthorityId>,
|
||||
/// Parachain id. Required for the collator role.
|
||||
pub parachain_id: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
/// Request block data from a peer.
|
||||
pub struct BlockRequest {
|
||||
/// Unique request id.
|
||||
pub id: RequestId,
|
||||
/// Bits of block data to request.
|
||||
pub fields: Vec<BlockAttribute>,
|
||||
/// Start from this block.
|
||||
pub from: FromBlock,
|
||||
/// End at this block. An implementation defined maximum is used when unspecified.
|
||||
pub to: Option<HeaderHash>,
|
||||
/// Sequence direction.
|
||||
pub direction: Direction,
|
||||
/// Maximum number of blocks to return. An implementation defined maximum is used when unspecified.
|
||||
pub max: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
/// Request candidate block data from a peer.
|
||||
pub struct CandidateRequest {
|
||||
/// Unique request id.
|
||||
pub id: RequestId,
|
||||
/// Candidate receipt hash.
|
||||
pub hash: Hash,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
/// Candidate block data response.
|
||||
pub struct CandidateResponse {
|
||||
/// Unique request id.
|
||||
pub id: RequestId,
|
||||
/// Candidate data. Empty if the peer does not have the candidate anymore.
|
||||
pub data: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
/// Response to `BlockRequest`
|
||||
pub struct BlockResponse {
|
||||
/// Id of a request this response was made for.
|
||||
pub id: RequestId,
|
||||
/// Block data for the requested sequence.
|
||||
pub blocks: Vec<BlockData>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
/// Announce a new complete relay chain block on the network.
|
||||
pub struct BlockAnnounce {
|
||||
/// New block header.
|
||||
pub header: Header,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
/// Remote call request.
|
||||
pub struct RemoteCallRequest {
|
||||
/// Unique request id.
|
||||
pub id: RequestId,
|
||||
/// Block at which to perform call.
|
||||
pub block: HeaderHash,
|
||||
/// Method name.
|
||||
pub method: String,
|
||||
/// Call data.
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
/// Remote call response.
|
||||
pub struct RemoteCallResponse {
|
||||
@@ -347,3 +167,192 @@ pub struct RemoteCallResponse {
|
||||
/// Execution proof.
|
||||
pub proof: Vec<Vec<u8>>,
|
||||
}
|
||||
|
||||
/// Generic types.
|
||||
pub mod generic {
|
||||
use primitives::AuthorityId;
|
||||
use runtime_primitives::bft::Justification;
|
||||
use ed25519;
|
||||
|
||||
use super::{Role, BlockAttribute, RemoteCallResponse, RequestId, Transactions, Direction};
|
||||
/// Block data sent in the response.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub struct BlockData<Header, Hash, Extrinsic> {
|
||||
/// Block header hash.
|
||||
pub hash: Hash,
|
||||
/// Block header if requested.
|
||||
pub header: Option<Header>,
|
||||
/// Block body if requested.
|
||||
pub body: Option<Vec<Extrinsic>>,
|
||||
/// Block receipt if requested.
|
||||
pub receipt: Option<Vec<u8>>,
|
||||
/// Block message queue if requested.
|
||||
pub message_queue: Option<Vec<u8>>,
|
||||
/// Justification if requested.
|
||||
pub justification: Option<Justification<Hash>>,
|
||||
}
|
||||
|
||||
/// Identifies starting point of a block sequence.
|
||||
#[serde(untagged)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub enum FromBlock<Hash, Number> {
|
||||
/// Start with given hash.
|
||||
Hash(Hash),
|
||||
/// Start with given block number.
|
||||
Number(Number),
|
||||
}
|
||||
|
||||
/// Communication that can occur between participants in consensus.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub enum BftMessage<Block, Hash> {
|
||||
/// A consensus message (proposal or vote)
|
||||
Consensus(SignedConsensusMessage<Block, Hash>),
|
||||
/// Auxiliary communication (just proof-of-lock for now).
|
||||
Auxiliary(Justification<Hash>),
|
||||
}
|
||||
|
||||
/// BFT Consensus message with parent header hash attached to it.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub struct LocalizedBftMessage<Block, Hash> {
|
||||
/// Consensus message.
|
||||
pub message: BftMessage<Block, Hash>,
|
||||
/// Parent header hash.
|
||||
pub parent_hash: Hash,
|
||||
}
|
||||
|
||||
/// A localized proposal message. Contains two signed pieces of data.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub struct SignedConsensusProposal<Block, Hash> {
|
||||
/// The round number.
|
||||
pub round_number: u32,
|
||||
/// The proposal sent.
|
||||
pub proposal: Block,
|
||||
/// The digest of the proposal.
|
||||
pub digest: Hash,
|
||||
/// The sender of the proposal
|
||||
pub sender: AuthorityId,
|
||||
/// The signature on the message (propose, round number, digest)
|
||||
pub digest_signature: ed25519::Signature,
|
||||
/// The signature on the message (propose, round number, proposal)
|
||||
pub full_signature: ed25519::Signature,
|
||||
}
|
||||
|
||||
/// A localized vote message, including the sender.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub struct SignedConsensusVote<H> {
|
||||
/// The message sent.
|
||||
pub vote: ConsensusVote<H>,
|
||||
/// The sender of the message
|
||||
pub sender: AuthorityId,
|
||||
/// The signature of the message.
|
||||
pub signature: ed25519::Signature,
|
||||
}
|
||||
|
||||
/// Votes during a consensus round.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub enum ConsensusVote<H> {
|
||||
/// Prepare to vote for proposal with digest D.
|
||||
Prepare(u32, H),
|
||||
/// Commit to proposal with digest D..
|
||||
Commit(u32, H),
|
||||
/// Propose advancement to a new round.
|
||||
AdvanceRound(u32),
|
||||
}
|
||||
|
||||
/// A localized message.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub enum SignedConsensusMessage<Block, Hash> {
|
||||
/// A proposal.
|
||||
Propose(SignedConsensusProposal<Block, Hash>),
|
||||
/// A vote.
|
||||
Vote(SignedConsensusVote<Hash>),
|
||||
}
|
||||
|
||||
/// A network message.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub enum Message<Block, Header, Hash, Number, Extrinsic> {
|
||||
/// Status packet.
|
||||
Status(Status<Hash, Number>),
|
||||
/// Block request.
|
||||
BlockRequest(BlockRequest<Hash, Number>),
|
||||
/// Block response.
|
||||
BlockResponse(BlockResponse<Header, Hash, Extrinsic>),
|
||||
/// Block announce.
|
||||
BlockAnnounce(BlockAnnounce<Header>),
|
||||
/// Transactions.
|
||||
Transactions(Transactions<Extrinsic>),
|
||||
/// BFT Consensus statement.
|
||||
BftMessage(LocalizedBftMessage<Block, Hash>),
|
||||
/// Remote method call request.
|
||||
RemoteCallRequest(RemoteCallRequest<Hash>),
|
||||
/// Remote method call response.
|
||||
RemoteCallResponse(RemoteCallResponse),
|
||||
}
|
||||
|
||||
/// Status sent on connection.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub struct Status<Hash, Number> {
|
||||
/// Protocol version.
|
||||
pub version: u32,
|
||||
/// Supported roles.
|
||||
pub roles: Vec<Role>,
|
||||
/// Best block number.
|
||||
pub best_number: Number,
|
||||
/// Best block hash.
|
||||
pub best_hash: Hash,
|
||||
/// Genesis block hash.
|
||||
pub genesis_hash: Hash,
|
||||
/// Signatue of `best_hash` made with validator address. Required for the validator role.
|
||||
pub validator_signature: Option<ed25519::Signature>,
|
||||
/// Validator address. Required for the validator role.
|
||||
pub validator_id: Option<AuthorityId>,
|
||||
/// Parachain id. Required for the collator role.
|
||||
pub parachain_id: Option<u64>,
|
||||
}
|
||||
|
||||
/// Request block data from a peer.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub struct BlockRequest<Hash, Number> {
|
||||
/// Unique request id.
|
||||
pub id: RequestId,
|
||||
/// Bits of block data to request.
|
||||
pub fields: Vec<BlockAttribute>,
|
||||
/// Start from this block.
|
||||
pub from: FromBlock<Hash, Number>,
|
||||
/// End at this block. An implementation defined maximum is used when unspecified.
|
||||
pub to: Option<Hash>,
|
||||
/// Sequence direction.
|
||||
pub direction: Direction,
|
||||
/// Maximum number of blocks to return. An implementation defined maximum is used when unspecified.
|
||||
pub max: Option<u32>,
|
||||
}
|
||||
|
||||
/// Response to `BlockRequest`
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub struct BlockResponse<Header, Hash, Extrinsic> {
|
||||
/// Id of a request this response was made for.
|
||||
pub id: RequestId,
|
||||
/// Block data for the requested sequence.
|
||||
pub blocks: Vec<BlockData<Header, Hash, Extrinsic>>,
|
||||
}
|
||||
|
||||
/// Announce a new complete relay chain block on the network.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub struct BlockAnnounce<H> {
|
||||
/// New block header.
|
||||
pub header: H,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
/// Remote call request.
|
||||
pub struct RemoteCallRequest<H> {
|
||||
/// Unique request id.
|
||||
pub id: RequestId,
|
||||
/// Block at which to perform call.
|
||||
pub block: H,
|
||||
/// Method name.
|
||||
pub method: String,
|
||||
/// Call data.
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ use io::SyncIo;
|
||||
use message;
|
||||
use network::PeerId;
|
||||
use service;
|
||||
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT};
|
||||
|
||||
/// Remote request timeout.
|
||||
const REQUEST_TIMEOUT: Duration = Duration::from_secs(15);
|
||||
@@ -50,9 +51,9 @@ pub trait OnDemandService: Send + Sync {
|
||||
}
|
||||
|
||||
/// On-demand requests service. Dispatches requests to appropriate peers.
|
||||
pub struct OnDemand<E: service::ExecuteInContext> {
|
||||
core: Mutex<OnDemandCore<E>>,
|
||||
checker: Arc<FetchChecker>,
|
||||
pub struct OnDemand<B: BlockT, E: service::ExecuteInContext<B>> {
|
||||
core: Mutex<OnDemandCore<B, E>>,
|
||||
checker: Arc<FetchChecker<B>>,
|
||||
}
|
||||
|
||||
/// On-demand response.
|
||||
@@ -61,19 +62,19 @@ pub struct Response {
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct OnDemandCore<E: service::ExecuteInContext> {
|
||||
struct OnDemandCore<B: BlockT, E: service::ExecuteInContext<B>> {
|
||||
service: Weak<E>,
|
||||
next_request_id: u64,
|
||||
pending_requests: VecDeque<Request>,
|
||||
active_peers: LinkedHashMap<PeerId, Request>,
|
||||
pending_requests: VecDeque<Request<B::Hash>>,
|
||||
active_peers: LinkedHashMap<PeerId, Request<B::Hash>>,
|
||||
idle_peers: VecDeque<PeerId>,
|
||||
}
|
||||
|
||||
struct Request {
|
||||
struct Request<H> {
|
||||
id: u64,
|
||||
timestamp: Instant,
|
||||
sender: Sender<client::CallResult>,
|
||||
request: RemoteCallRequest,
|
||||
request: RemoteCallRequest<H>,
|
||||
}
|
||||
|
||||
impl Future for Response {
|
||||
@@ -86,9 +87,12 @@ impl Future for Response {
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> OnDemand<E> where E: service::ExecuteInContext {
|
||||
impl<B: BlockT, E> OnDemand<B, E> where
|
||||
E: service::ExecuteInContext<B>,
|
||||
B::Header: HeaderT<Number=u64>,
|
||||
{
|
||||
/// Creates new on-demand service.
|
||||
pub fn new(checker: Arc<FetchChecker>) -> Self {
|
||||
pub fn new(checker: Arc<FetchChecker<B>>) -> Self {
|
||||
OnDemand {
|
||||
checker,
|
||||
core: Mutex::new(OnDemandCore {
|
||||
@@ -107,7 +111,7 @@ impl<E> OnDemand<E> where E: service::ExecuteInContext {
|
||||
}
|
||||
|
||||
/// Execute method call on remote node, returning execution result and proof.
|
||||
pub fn remote_call(&self, request: RemoteCallRequest) -> Response {
|
||||
pub fn remote_call(&self, request: RemoteCallRequest<B::Hash>) -> Response {
|
||||
let (sender, receiver) = channel();
|
||||
let result = Response {
|
||||
receiver: receiver,
|
||||
@@ -123,7 +127,11 @@ impl<E> OnDemand<E> where E: service::ExecuteInContext {
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> OnDemandService for OnDemand<E> where E: service::ExecuteInContext {
|
||||
impl<B, E> OnDemandService for OnDemand<B, E> where
|
||||
B: BlockT,
|
||||
E: service::ExecuteInContext<B>,
|
||||
B::Header: HeaderT<Number=u64>,
|
||||
{
|
||||
fn on_connect(&self, peer: PeerId, role: service::Role) {
|
||||
if !role.intersects(service::Role::FULL | service::Role::COLLATOR | service::Role::VALIDATOR) { // TODO: correct?
|
||||
return;
|
||||
@@ -175,15 +183,23 @@ impl<E> OnDemandService for OnDemand<E> where E: service::ExecuteInContext {
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> Fetcher for OnDemand<E> where E: service::ExecuteInContext {
|
||||
impl<B, E> Fetcher<B> for OnDemand<B, E> where
|
||||
B: BlockT,
|
||||
E: service::ExecuteInContext<B>,
|
||||
B::Header: HeaderT<Number=u64>,
|
||||
{
|
||||
type RemoteCallResult = Response;
|
||||
|
||||
fn remote_call(&self, request: RemoteCallRequest) -> Self::RemoteCallResult {
|
||||
self.remote_call(request)
|
||||
fn remote_call(&self, request: RemoteCallRequest<B::Hash>) -> Self::RemoteCallResult {
|
||||
OnDemand::remote_call(self, request)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> OnDemandCore<E> where E: service::ExecuteInContext {
|
||||
impl<B, E> OnDemandCore<B, E> where
|
||||
B: BlockT,
|
||||
E: service::ExecuteInContext<B> ,
|
||||
B::Header: HeaderT<Number=u64>
|
||||
{
|
||||
pub fn add_peer(&mut self, peer: PeerId) {
|
||||
self.idle_peers.push_back(peer);
|
||||
}
|
||||
@@ -214,7 +230,7 @@ impl<E> OnDemandCore<E> where E: service::ExecuteInContext {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, sender: Sender<client::CallResult>, request: RemoteCallRequest) {
|
||||
pub fn insert(&mut self, sender: Sender<client::CallResult>, request: RemoteCallRequest<B::Hash>) {
|
||||
let request_id = self.next_request_id;
|
||||
self.next_request_id += 1;
|
||||
|
||||
@@ -226,7 +242,7 @@ impl<E> OnDemandCore<E> where E: service::ExecuteInContext {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, peer: PeerId, id: u64) -> Option<Request> {
|
||||
pub fn remove(&mut self, peer: PeerId, id: u64) -> Option<Request<B::Hash>> {
|
||||
match self.active_peers.entry(peer) {
|
||||
Entry::Occupied(entry) => match entry.get().id == id {
|
||||
true => {
|
||||
@@ -263,7 +279,7 @@ impl<E> OnDemandCore<E> where E: service::ExecuteInContext {
|
||||
data: request.request.call_data.clone(),
|
||||
};
|
||||
|
||||
protocol.send_message(ctx, peer, message::Message::RemoteCallRequest(message))
|
||||
protocol.send_message(ctx, peer, message::generic::Message::RemoteCallRequest(message))
|
||||
});
|
||||
self.active_peers.insert(peer, request);
|
||||
}
|
||||
@@ -286,16 +302,17 @@ mod tests {
|
||||
use service::{Role, ExecuteInContext};
|
||||
use test::TestIo;
|
||||
use super::{REQUEST_TIMEOUT, OnDemand, OnDemandService};
|
||||
use test_client::runtime::{Block, Hash};
|
||||
|
||||
struct DummyExecutor;
|
||||
struct DummyFetchChecker { ok: bool }
|
||||
|
||||
impl ExecuteInContext for DummyExecutor {
|
||||
fn execute_in_context<F: Fn(&mut NetSyncIo, &Protocol)>(&self, _closure: F) {}
|
||||
impl ExecuteInContext<Block> for DummyExecutor {
|
||||
fn execute_in_context<F: Fn(&mut NetSyncIo, &Protocol<Block>)>(&self, _closure: F) {}
|
||||
}
|
||||
|
||||
impl FetchChecker for DummyFetchChecker {
|
||||
fn check_execution_proof(&self, _request: &RemoteCallRequest, remote_proof: (Vec<u8>, Vec<Vec<u8>>)) -> client::error::Result<client::CallResult> {
|
||||
impl FetchChecker<Block> for DummyFetchChecker {
|
||||
fn check_execution_proof(&self, _request: &RemoteCallRequest<Hash>, remote_proof: (Vec<u8>, Vec<Vec<u8>>)) -> client::error::Result<client::CallResult> {
|
||||
match self.ok {
|
||||
true => Ok(client::CallResult {
|
||||
return_data: remote_proof.0,
|
||||
@@ -306,19 +323,19 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn dummy(ok: bool) -> (Arc<DummyExecutor>, Arc<OnDemand<DummyExecutor>>) {
|
||||
fn dummy(ok: bool) -> (Arc<DummyExecutor>, Arc<OnDemand<Block, DummyExecutor>>) {
|
||||
let executor = Arc::new(DummyExecutor);
|
||||
let service = Arc::new(OnDemand::new(Arc::new(DummyFetchChecker { ok })));
|
||||
service.set_service_link(Arc::downgrade(&executor));
|
||||
(executor, service)
|
||||
}
|
||||
|
||||
fn total_peers(on_demand: &OnDemand<DummyExecutor>) -> usize {
|
||||
fn total_peers(on_demand: &OnDemand<Block, DummyExecutor>) -> usize {
|
||||
let core = on_demand.core.lock();
|
||||
core.idle_peers.len() + core.active_peers.len()
|
||||
}
|
||||
|
||||
fn receive_response(on_demand: &OnDemand<DummyExecutor>, network: &mut TestIo, peer: PeerId, id: message::RequestId) {
|
||||
fn receive_response(on_demand: &OnDemand<Block, DummyExecutor>, network: &mut TestIo, peer: PeerId, id: message::RequestId) {
|
||||
on_demand.on_remote_response(network, peer, message::RemoteCallResponse {
|
||||
id: id,
|
||||
value: vec![1],
|
||||
|
||||
@@ -19,23 +19,21 @@ use std::{mem, cmp};
|
||||
use std::sync::Arc;
|
||||
use std::time;
|
||||
use parking_lot::{RwLock, Mutex};
|
||||
use futures::sync::oneshot;
|
||||
use serde_json;
|
||||
use primitives::block::{HeaderHash, ExtrinsicHash, Number as BlockNumber, Header, Id as BlockId};
|
||||
use primitives::{Hash, blake2_256};
|
||||
use runtime_support::Hashable;
|
||||
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Hashing, HashingFor};
|
||||
use runtime_primitives::generic::BlockId;
|
||||
use network::PeerId;
|
||||
|
||||
use message::{self, Message};
|
||||
use message::generic::Message as GenericMessage;
|
||||
use sync::{ChainSync, Status as SyncStatus, SyncState};
|
||||
use consensus::Consensus;
|
||||
use service::{Role, TransactionPool, StatementStream, BftMessageStream};
|
||||
use service::{Role, TransactionPool, BftMessageStream};
|
||||
use config::ProtocolConfig;
|
||||
use chain::Client;
|
||||
use on_demand::OnDemandService;
|
||||
use io::SyncIo;
|
||||
use error;
|
||||
use super::header_hash;
|
||||
|
||||
const REQUEST_TIMEOUT_SEC: u64 = 40;
|
||||
const PROTOCOL_VERSION: u32 = 0;
|
||||
@@ -44,25 +42,25 @@ const PROTOCOL_VERSION: u32 = 0;
|
||||
const MAX_BLOCK_DATA_RESPONSE: u32 = 128;
|
||||
|
||||
// Lock must always be taken in order declared here.
|
||||
pub struct Protocol {
|
||||
pub struct Protocol<B: BlockT> {
|
||||
config: ProtocolConfig,
|
||||
chain: Arc<Client>,
|
||||
chain: Arc<Client<B>>,
|
||||
on_demand: Option<Arc<OnDemandService>>,
|
||||
genesis_hash: HeaderHash,
|
||||
sync: RwLock<ChainSync>,
|
||||
consensus: Mutex<Consensus>,
|
||||
genesis_hash: B::Hash,
|
||||
sync: RwLock<ChainSync<B>>,
|
||||
consensus: Mutex<Consensus<B>>,
|
||||
// All connected peers
|
||||
peers: RwLock<HashMap<PeerId, Peer>>,
|
||||
peers: RwLock<HashMap<PeerId, Peer<B>>>,
|
||||
// Connected peers pending Status message.
|
||||
handshaking_peers: RwLock<HashMap<PeerId, time::Instant>>,
|
||||
transaction_pool: Arc<TransactionPool>,
|
||||
transaction_pool: Arc<TransactionPool<B>>,
|
||||
}
|
||||
|
||||
/// Syncing status and statistics
|
||||
#[derive(Clone)]
|
||||
pub struct ProtocolStatus {
|
||||
pub struct ProtocolStatus<B: BlockT> {
|
||||
/// Sync status.
|
||||
pub sync: SyncStatus,
|
||||
pub sync: SyncStatus<B>,
|
||||
/// Total number of connected peers
|
||||
pub num_peers: usize,
|
||||
/// Total number of active peers.
|
||||
@@ -70,42 +68,49 @@ pub struct ProtocolStatus {
|
||||
}
|
||||
|
||||
/// Peer information
|
||||
struct Peer {
|
||||
struct Peer<B: BlockT> {
|
||||
/// Protocol version
|
||||
protocol_version: u32,
|
||||
/// Roles
|
||||
roles: Role,
|
||||
/// Peer best block hash
|
||||
best_hash: HeaderHash,
|
||||
best_hash: B::Hash,
|
||||
/// Peer best block number
|
||||
best_number: BlockNumber,
|
||||
best_number: <B::Header as HeaderT>::Number,
|
||||
/// Pending block request if any
|
||||
block_request: Option<message::BlockRequest>,
|
||||
block_request: Option<message::BlockRequest<B>>,
|
||||
/// Request timestamp
|
||||
request_timestamp: Option<time::Instant>,
|
||||
/// Holds a set of transactions known to this peer.
|
||||
known_transactions: HashSet<ExtrinsicHash>,
|
||||
known_transactions: HashSet<B::Hash>,
|
||||
/// Holds a set of blocks known to this peer.
|
||||
known_blocks: HashSet<HeaderHash>,
|
||||
known_blocks: HashSet<B::Hash>,
|
||||
/// Request counter,
|
||||
next_request_id: message::RequestId,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PeerInfo {
|
||||
pub struct PeerInfo<B: BlockT> {
|
||||
/// Roles
|
||||
pub roles: Role,
|
||||
/// Protocol version
|
||||
pub protocol_version: u32,
|
||||
/// Peer best block hash
|
||||
pub best_hash: HeaderHash,
|
||||
pub best_hash: B::Hash,
|
||||
/// Peer best block number
|
||||
pub best_number: BlockNumber,
|
||||
pub best_number: <B::Header as HeaderT>::Number,
|
||||
}
|
||||
|
||||
impl Protocol {
|
||||
impl<B: BlockT> Protocol<B> where
|
||||
B::Header: HeaderT<Number=u64>
|
||||
{
|
||||
/// Create a new instance.
|
||||
pub fn new(config: ProtocolConfig, chain: Arc<Client>, on_demand: Option<Arc<OnDemandService>>, transaction_pool: Arc<TransactionPool>) -> error::Result<Protocol> {
|
||||
pub fn new(
|
||||
config: ProtocolConfig,
|
||||
chain: Arc<Client<B>>,
|
||||
on_demand: Option<Arc<OnDemandService>>,
|
||||
transaction_pool: Arc<TransactionPool<B>>
|
||||
) -> error::Result<Self> {
|
||||
let info = chain.info()?;
|
||||
let sync = ChainSync::new(config.roles, &info);
|
||||
let protocol = Protocol {
|
||||
@@ -123,7 +128,7 @@ impl Protocol {
|
||||
}
|
||||
|
||||
/// Returns protocol status
|
||||
pub fn status(&self) -> ProtocolStatus {
|
||||
pub fn status(&self) -> ProtocolStatus<B> {
|
||||
let sync = self.sync.read();
|
||||
let peers = self.peers.read();
|
||||
ProtocolStatus {
|
||||
@@ -134,7 +139,7 @@ impl Protocol {
|
||||
}
|
||||
|
||||
pub fn handle_packet(&self, io: &mut SyncIo, peer_id: PeerId, data: &[u8]) {
|
||||
let message: Message = match serde_json::from_slice(data) {
|
||||
let message: Message<B> = match serde_json::from_slice(data) {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
debug!("Invalid packet from {}: {}", peer_id, e);
|
||||
@@ -144,9 +149,9 @@ impl Protocol {
|
||||
};
|
||||
|
||||
match message {
|
||||
Message::Status(s) => self.on_status_message(io, peer_id, s),
|
||||
Message::BlockRequest(r) => self.on_block_request(io, peer_id, r),
|
||||
Message::BlockResponse(r) => {
|
||||
GenericMessage::Status(s) => self.on_status_message(io, peer_id, s),
|
||||
GenericMessage::BlockRequest(r) => self.on_block_request(io, peer_id, r),
|
||||
GenericMessage::BlockResponse(r) => {
|
||||
let request = {
|
||||
let mut peers = self.peers.write();
|
||||
if let Some(ref mut peer) = peers.get_mut(&peer_id) {
|
||||
@@ -171,22 +176,19 @@ impl Protocol {
|
||||
}
|
||||
self.on_block_response(io, peer_id, request, r);
|
||||
},
|
||||
Message::BlockAnnounce(announce) => {
|
||||
GenericMessage::BlockAnnounce(announce) => {
|
||||
self.on_block_announce(io, peer_id, announce);
|
||||
},
|
||||
Message::Statement(s) => self.on_statement(io, peer_id, s, blake2_256(data).into()),
|
||||
Message::CandidateRequest(r) => self.on_candidate_request(io, peer_id, r),
|
||||
Message::CandidateResponse(r) => self.on_candidate_response(io, peer_id, r),
|
||||
Message::BftMessage(m) => self.on_bft_message(io, peer_id, m, blake2_256(data).into()),
|
||||
Message::Transactions(m) => self.on_transactions(io, peer_id, m),
|
||||
Message::RemoteCallRequest(request) => self.on_remote_call_request(io, peer_id, request),
|
||||
Message::RemoteCallResponse(response) => self.on_remote_call_response(io, peer_id, response)
|
||||
GenericMessage::BftMessage(m) => self.on_bft_message(io, peer_id, m, HashingFor::<B>::hash(data)),
|
||||
GenericMessage::Transactions(m) => self.on_transactions(io, peer_id, m),
|
||||
GenericMessage::RemoteCallRequest(request) => self.on_remote_call_request(io, peer_id, request),
|
||||
GenericMessage::RemoteCallResponse(response) => self.on_remote_call_response(io, peer_id, response)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_message(&self, io: &mut SyncIo, peer_id: PeerId, mut message: Message) {
|
||||
pub fn send_message(&self, io: &mut SyncIo, peer_id: PeerId, mut message: Message<B>) {
|
||||
match &mut message {
|
||||
&mut Message::BlockRequest(ref mut r) => {
|
||||
&mut GenericMessage::BlockRequest(ref mut r) => {
|
||||
let mut peers = self.peers.write();
|
||||
if let Some(ref mut peer) = peers.get_mut(&peer_id) {
|
||||
r.id = peer.next_request_id;
|
||||
@@ -204,9 +206,9 @@ impl Protocol {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hash_message(message: &Message) -> Hash {
|
||||
pub fn hash_message(message: &Message<B>) -> B::Hash {
|
||||
let data = serde_json::to_vec(&message).expect("Serializer is infallible; qed");
|
||||
blake2_256(&data).into()
|
||||
HashingFor::<B>::hash(&data)
|
||||
}
|
||||
|
||||
/// Called when a new peer is connected
|
||||
@@ -232,7 +234,7 @@ impl Protocol {
|
||||
}
|
||||
}
|
||||
|
||||
fn on_block_request(&self, io: &mut SyncIo, peer: PeerId, request: message::BlockRequest) {
|
||||
fn on_block_request(&self, io: &mut SyncIo, peer: PeerId, request: message::BlockRequest<B>) {
|
||||
trace!(target: "sync", "BlockRequest {} from {}: from {:?} to {:?} max {:?}", request.id, peer, request.from, request.to, request.max);
|
||||
let mut blocks = Vec::new();
|
||||
let mut id = match request.from {
|
||||
@@ -255,9 +257,9 @@ impl Protocol {
|
||||
if blocks.len() >= max{
|
||||
break;
|
||||
}
|
||||
let number = header.number;
|
||||
let hash = header_hash(&header);
|
||||
let block_data = message::BlockData {
|
||||
let number = header.number().clone();
|
||||
let hash = header.hash();
|
||||
let block_data = message::generic::BlockData {
|
||||
hash: hash,
|
||||
header: if get_header { Some(header) } else { None },
|
||||
body: if get_body { self.chain.body(&BlockId::Hash(hash)).unwrap_or(None) } else { None },
|
||||
@@ -276,69 +278,34 @@ impl Protocol {
|
||||
}
|
||||
}
|
||||
}
|
||||
let response = message::BlockResponse {
|
||||
let response = message::generic::BlockResponse {
|
||||
id: request.id,
|
||||
blocks: blocks,
|
||||
};
|
||||
self.send_message(io, peer, Message::BlockResponse(response))
|
||||
self.send_message(io, peer, GenericMessage::BlockResponse(response))
|
||||
}
|
||||
|
||||
fn on_block_response(&self, io: &mut SyncIo, peer: PeerId, request: message::BlockRequest, response: message::BlockResponse) {
|
||||
fn on_block_response(&self, io: &mut SyncIo, peer: PeerId, request: message::BlockRequest<B>, response: message::BlockResponse<B>) {
|
||||
// TODO: validate response
|
||||
trace!(target: "sync", "BlockResponse {} from {} with {} blocks", response.id, peer, response.blocks.len());
|
||||
self.sync.write().on_block_data(io, self, peer, request, response);
|
||||
}
|
||||
|
||||
fn on_candidate_request(&self, io: &mut SyncIo, peer: PeerId, request: message::CandidateRequest) {
|
||||
trace!(target: "sync", "CandidateRequest {} from {} for {}", request.id, peer, request.hash);
|
||||
self.consensus.lock().on_candidate_request(io, self, peer, request);
|
||||
}
|
||||
|
||||
fn on_candidate_response(&self, io: &mut SyncIo, peer: PeerId, response: message::CandidateResponse) {
|
||||
trace!(target: "sync", "CandidateResponse {} from {} with {:?} bytes", response.id, peer, response.data.as_ref().map(|d| d.len()));
|
||||
self.consensus.lock().on_candidate_response(io, self, peer, response);
|
||||
}
|
||||
|
||||
fn on_statement(&self, io: &mut SyncIo, peer: PeerId, statement: message::Statement, hash: Hash) {
|
||||
trace!(target: "sync", "Statement from {}: {:?}", peer, statement);
|
||||
self.consensus.lock().on_statement(io, self, peer, statement, hash);
|
||||
}
|
||||
|
||||
fn on_bft_message(&self, io: &mut SyncIo, peer: PeerId, message: message::LocalizedBftMessage, hash: Hash) {
|
||||
fn on_bft_message(&self, io: &mut SyncIo, peer: PeerId, message: message::LocalizedBftMessage<B>, hash: B::Hash) {
|
||||
trace!(target: "sync", "BFT message from {}: {:?}", peer, message);
|
||||
self.consensus.lock().on_bft_message(io, self, peer, message, hash);
|
||||
}
|
||||
|
||||
/// See `ConsensusService` trait.
|
||||
pub fn send_bft_message(&self, io: &mut SyncIo, message: message::LocalizedBftMessage) {
|
||||
pub fn send_bft_message(&self, io: &mut SyncIo, message: message::LocalizedBftMessage<B>) {
|
||||
self.consensus.lock().send_bft_message(io, self, message)
|
||||
}
|
||||
|
||||
/// See `ConsensusService` trait.
|
||||
pub fn bft_messages(&self, parent_hash: Hash) -> BftMessageStream {
|
||||
pub fn bft_messages(&self, parent_hash: B::Hash) -> BftMessageStream<B> {
|
||||
self.consensus.lock().bft_messages(parent_hash)
|
||||
}
|
||||
|
||||
/// See `ConsensusService` trait.
|
||||
pub fn statements(&self) -> StatementStream {
|
||||
self.consensus.lock().statements()
|
||||
}
|
||||
|
||||
/// See `ConsensusService` trait.
|
||||
pub fn fetch_candidate(&self, io: &mut SyncIo, hash: &Hash) -> oneshot::Receiver<Vec<u8>> {
|
||||
self.consensus.lock().fetch_candidate(io, self, hash)
|
||||
}
|
||||
|
||||
/// See `ConsensusService` trait.
|
||||
pub fn send_statement(&self, io: &mut SyncIo, statement: message::Statement) {
|
||||
self.consensus.lock().send_statement(io, self, statement)
|
||||
}
|
||||
|
||||
/// See `ConsensusService` trait.
|
||||
pub fn set_local_candidate(&self, candidate: Option<(Hash, Vec<u8>)>) {
|
||||
self.consensus.lock().set_local_candidate(candidate)
|
||||
}
|
||||
|
||||
/// Perform time based maintenance.
|
||||
pub fn tick(&self, io: &mut SyncIo) {
|
||||
self.maintain_peers(io);
|
||||
@@ -367,7 +334,7 @@ impl Protocol {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn peer_info(&self, peer: PeerId) -> Option<PeerInfo> {
|
||||
pub fn peer_info(&self, peer: PeerId) -> Option<PeerInfo<B>> {
|
||||
self.peers.read().get(&peer).map(|p| {
|
||||
PeerInfo {
|
||||
roles: p.roles,
|
||||
@@ -379,7 +346,7 @@ impl Protocol {
|
||||
}
|
||||
|
||||
/// Called by peer to report status
|
||||
fn on_status_message(&self, io: &mut SyncIo, peer_id: PeerId, status: message::Status) {
|
||||
fn on_status_message(&self, io: &mut SyncIo, peer_id: PeerId, status: message::Status<B>) {
|
||||
trace!(target: "sync", "New peer {} {:?}", peer_id, status);
|
||||
if io.is_expired() {
|
||||
trace!(target: "sync", "Status packet from expired session {}:{}", peer_id, io.peer_info(peer_id));
|
||||
@@ -426,7 +393,7 @@ impl Protocol {
|
||||
}
|
||||
|
||||
/// Called when peer sends us new transactions
|
||||
fn on_transactions(&self, _io: &mut SyncIo, peer_id: PeerId, transactions: message::Transactions) {
|
||||
fn on_transactions(&self, _io: &mut SyncIo, peer_id: PeerId, transactions: message::Transactions<B::Extrinsic>) {
|
||||
// Accept transactions only when fully synced
|
||||
if self.sync.read().status().state != SyncState::Idle {
|
||||
trace!(target: "sync", "{} Ignoring transactions while syncing", peer_id);
|
||||
@@ -475,7 +442,7 @@ impl Protocol {
|
||||
}
|
||||
}
|
||||
trace!(target: "sync", "Sending {} transactions to {}", to_send.len(), peer_id);
|
||||
self.send_message(io, *peer_id, Message::Transactions(to_send));
|
||||
self.send_message(io, *peer_id, GenericMessage::Transactions(to_send));
|
||||
}
|
||||
}
|
||||
self.transaction_pool.on_broadcasted(propagated_to);
|
||||
@@ -484,7 +451,7 @@ impl Protocol {
|
||||
/// Send Status message
|
||||
fn send_status(&self, io: &mut SyncIo, peer_id: PeerId) {
|
||||
if let Ok(info) = self.chain.info() {
|
||||
let status = message::Status {
|
||||
let status = message::generic::Status {
|
||||
version: PROTOCOL_VERSION,
|
||||
genesis_hash: info.chain.genesis_hash,
|
||||
roles: self.config.roles.into(),
|
||||
@@ -494,7 +461,7 @@ impl Protocol {
|
||||
validator_id: None,
|
||||
parachain_id: None,
|
||||
};
|
||||
self.send_message(io, peer_id, Message::Status(status))
|
||||
self.send_message(io, peer_id, GenericMessage::Status(status))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -508,9 +475,9 @@ impl Protocol {
|
||||
self.consensus.lock().restart();
|
||||
}
|
||||
|
||||
pub fn on_block_announce(&self, io: &mut SyncIo, peer_id: PeerId, announce: message::BlockAnnounce) {
|
||||
pub fn on_block_announce(&self, io: &mut SyncIo, peer_id: PeerId, announce: message::BlockAnnounce<B::Header>) {
|
||||
let header = announce.header;
|
||||
let hash: HeaderHash = header.blake2_256().into();
|
||||
let hash = header.hash();
|
||||
{
|
||||
let mut peers = self.peers.write();
|
||||
if let Some(ref mut peer) = peers.get_mut(&peer_id) {
|
||||
@@ -520,7 +487,7 @@ impl Protocol {
|
||||
self.sync.write().on_block_announce(io, self, peer_id, hash, &header);
|
||||
}
|
||||
|
||||
pub fn on_block_imported(&self, io: &mut SyncIo, hash: HeaderHash, header: &Header) {
|
||||
pub fn on_block_imported(&self, io: &mut SyncIo, hash: B::Hash, header: &B::Header) {
|
||||
self.sync.write().update_chain_info(&header);
|
||||
// send out block announcements
|
||||
let mut peers = self.peers.write();
|
||||
@@ -528,7 +495,7 @@ impl Protocol {
|
||||
for (peer_id, ref mut peer) in peers.iter_mut() {
|
||||
if peer.known_blocks.insert(hash.clone()) {
|
||||
trace!(target: "sync", "Announcing block {:?} to {}", hash, peer_id);
|
||||
self.send_message(io, *peer_id, Message::BlockAnnounce(message::BlockAnnounce {
|
||||
self.send_message(io, *peer_id, GenericMessage::BlockAnnounce(message::BlockAnnounce {
|
||||
header: header.clone()
|
||||
}));
|
||||
}
|
||||
@@ -537,7 +504,7 @@ impl Protocol {
|
||||
self.consensus.lock().collect_garbage(Some(&header));
|
||||
}
|
||||
|
||||
fn on_remote_call_request(&self, io: &mut SyncIo, peer_id: PeerId, request: message::RemoteCallRequest) {
|
||||
fn on_remote_call_request(&self, io: &mut SyncIo, peer_id: PeerId, request: message::RemoteCallRequest<B::Hash>) {
|
||||
trace!(target: "sync", "Remote request {} from {} ({} at {})", request.id, peer_id, request.method, request.block);
|
||||
let (value, proof) = match self.chain.execution_proof(&request.block, &request.method, &request.data) {
|
||||
Ok((value, proof)) => (value, proof),
|
||||
@@ -548,7 +515,7 @@ impl Protocol {
|
||||
},
|
||||
};
|
||||
|
||||
self.send_message(io, peer_id, message::Message::RemoteCallResponse(message::RemoteCallResponse {
|
||||
self.send_message(io, peer_id, GenericMessage::RemoteCallResponse(message::RemoteCallResponse {
|
||||
id: request.id, value, proof,
|
||||
}));
|
||||
}
|
||||
@@ -558,7 +525,7 @@ impl Protocol {
|
||||
self.on_demand.as_ref().map(|s| s.on_remote_response(io, peer_id, response));
|
||||
}
|
||||
|
||||
pub fn chain(&self) -> &Client {
|
||||
pub fn chain(&self) -> &Client<B> {
|
||||
&*self.chain
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,16 +22,15 @@ use futures::sync::{oneshot, mpsc};
|
||||
use network::{NetworkProtocolHandler, NetworkContext, HostInfo, PeerId, ProtocolId,
|
||||
NetworkConfiguration , NonReservedPeerMode, ErrorKind};
|
||||
use network_devp2p::{NetworkService};
|
||||
use primitives::block::{ExtrinsicHash, Header, HeaderHash};
|
||||
use primitives::Hash;
|
||||
use core_io::{TimerToken};
|
||||
use io::NetSyncIo;
|
||||
use protocol::{Protocol, ProtocolStatus, PeerInfo as ProtocolPeerInfo};
|
||||
use config::{ProtocolConfig};
|
||||
use error::Error;
|
||||
use chain::Client;
|
||||
use message::{Statement, LocalizedBftMessage};
|
||||
use message::LocalizedBftMessage;
|
||||
use on_demand::OnDemandService;
|
||||
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT};
|
||||
|
||||
/// Polkadot devp2p protocol id
|
||||
pub const DOT_PROTOCOL_ID: ProtocolId = *b"dot";
|
||||
@@ -40,10 +39,8 @@ const V0_PACKET_COUNT: u8 = 1;
|
||||
|
||||
/// Type that represents fetch completion future.
|
||||
pub type FetchFuture = oneshot::Receiver<Vec<u8>>;
|
||||
/// Type that represents statement stream.
|
||||
pub type StatementStream = mpsc::UnboundedReceiver<Statement>;
|
||||
/// Type that represents bft messages stream.
|
||||
pub type BftMessageStream = mpsc::UnboundedReceiver<LocalizedBftMessage>;
|
||||
pub type BftMessageStream<B> = mpsc::UnboundedReceiver<LocalizedBftMessage<B>>;
|
||||
|
||||
const TICK_TOKEN: TimerToken = 0;
|
||||
const TICK_TIMEOUT: Duration = Duration::from_millis(1000);
|
||||
@@ -68,60 +65,51 @@ bitflags! {
|
||||
}
|
||||
|
||||
/// Sync status
|
||||
pub trait SyncProvider: Send + Sync {
|
||||
pub trait SyncProvider<B: BlockT>: Send + Sync {
|
||||
/// Get sync status
|
||||
fn status(&self) -> ProtocolStatus;
|
||||
fn status(&self) -> ProtocolStatus<B>;
|
||||
/// Get peers information
|
||||
fn peers(&self) -> Vec<PeerInfo>;
|
||||
fn peers(&self) -> Vec<PeerInfo<B>>;
|
||||
/// Get this node id if available.
|
||||
fn node_id(&self) -> Option<String>;
|
||||
}
|
||||
|
||||
/// Transaction pool interface
|
||||
pub trait TransactionPool: Send + Sync {
|
||||
pub trait TransactionPool<B: BlockT>: Send + Sync {
|
||||
/// Get transactions from the pool that are ready to be propagated.
|
||||
fn transactions(&self) -> Vec<(ExtrinsicHash, Vec<u8>)>;
|
||||
fn transactions(&self) -> Vec<(B::Hash, B::Extrinsic)>;
|
||||
/// Import a transction into the pool.
|
||||
fn import(&self, transaction: &[u8]) -> Option<ExtrinsicHash>;
|
||||
fn import(&self, transaction: &B::Extrinsic) -> Option<B::Hash>;
|
||||
/// Notify the pool about transactions broadcast.
|
||||
fn on_broadcasted(&self, propagations: HashMap<ExtrinsicHash, Vec<String>>);
|
||||
fn on_broadcasted(&self, propagations: HashMap<B::Hash, Vec<String>>);
|
||||
}
|
||||
|
||||
/// ConsensusService
|
||||
pub trait ConsensusService: Send + Sync {
|
||||
/// Get statement stream.
|
||||
fn statements(&self) -> StatementStream;
|
||||
/// Send out a statement.
|
||||
fn send_statement(&self, statement: Statement);
|
||||
pub trait ConsensusService<B: BlockT>: Send + Sync {
|
||||
/// Maintain connectivity to given addresses.
|
||||
fn connect_to_authorities(&self, addresses: &[String]);
|
||||
/// Fetch candidate.
|
||||
fn fetch_candidate(&self, hash: &Hash) -> oneshot::Receiver<Vec<u8>>;
|
||||
/// Note local candidate. Accepts candidate receipt hash and candidate data.
|
||||
/// Pass `None` to clear the candidate.
|
||||
fn set_local_candidate(&self, candidate: Option<(Hash, Vec<u8>)>);
|
||||
|
||||
/// Get BFT message stream for messages corresponding to consensus on given
|
||||
/// parent hash.
|
||||
fn bft_messages(&self, parent_hash: Hash) -> BftMessageStream;
|
||||
fn bft_messages(&self, parent_hash: B::Hash) -> BftMessageStream<B>;
|
||||
/// Send out a BFT message.
|
||||
fn send_bft_message(&self, message: LocalizedBftMessage);
|
||||
fn send_bft_message(&self, message: LocalizedBftMessage<B>);
|
||||
}
|
||||
|
||||
/// Service able to execute closure in the network context.
|
||||
pub trait ExecuteInContext: Send + Sync {
|
||||
pub trait ExecuteInContext<B: BlockT>: Send + Sync {
|
||||
/// Execute closure in network context.
|
||||
fn execute_in_context<F: Fn(&mut NetSyncIo, &Protocol)>(&self, closure: F);
|
||||
fn execute_in_context<F: Fn(&mut NetSyncIo, &Protocol<B>)>(&self, closure: F);
|
||||
}
|
||||
|
||||
/// devp2p Protocol handler
|
||||
struct ProtocolHandler {
|
||||
protocol: Protocol,
|
||||
struct ProtocolHandler<B: BlockT> {
|
||||
protocol: Protocol<B>,
|
||||
}
|
||||
|
||||
/// Peer connection information
|
||||
#[derive(Debug)]
|
||||
pub struct PeerInfo {
|
||||
pub struct PeerInfo<B: BlockT> {
|
||||
/// Public node id
|
||||
pub id: Option<String>,
|
||||
/// Node client ID
|
||||
@@ -133,34 +121,34 @@ pub struct PeerInfo {
|
||||
/// Local endpoint address
|
||||
pub local_address: String,
|
||||
/// Dot protocol info.
|
||||
pub dot_info: Option<ProtocolPeerInfo>,
|
||||
pub dot_info: Option<ProtocolPeerInfo<B>>,
|
||||
}
|
||||
|
||||
/// Service initialization parameters.
|
||||
pub struct Params {
|
||||
pub struct Params<B: BlockT> {
|
||||
/// Configuration.
|
||||
pub config: ProtocolConfig,
|
||||
/// Network layer configuration.
|
||||
pub network_config: NetworkConfiguration,
|
||||
/// Polkadot relay chain access point.
|
||||
pub chain: Arc<Client>,
|
||||
pub chain: Arc<Client<B>>,
|
||||
/// On-demand service reference.
|
||||
pub on_demand: Option<Arc<OnDemandService>>,
|
||||
/// Transaction pool.
|
||||
pub transaction_pool: Arc<TransactionPool>,
|
||||
pub transaction_pool: Arc<TransactionPool<B>>,
|
||||
}
|
||||
|
||||
/// Polkadot network service. Handles network IO and manages connectivity.
|
||||
pub struct Service {
|
||||
pub struct Service<B: BlockT + 'static> where B::Header: HeaderT<Number=u64> {
|
||||
/// Network service
|
||||
network: NetworkService,
|
||||
/// Devp2p protocol handler
|
||||
handler: Arc<ProtocolHandler>,
|
||||
handler: Arc<ProtocolHandler<B>>,
|
||||
}
|
||||
|
||||
impl Service {
|
||||
impl<B: BlockT + 'static> Service<B> where B::Header: HeaderT<Number=u64> {
|
||||
/// Creates and register protocol with the network service
|
||||
pub fn new(params: Params) -> Result<Arc<Service>, Error> {
|
||||
pub fn new(params: Params<B>) -> Result<Arc<Service<B>>, Error> {
|
||||
let service = NetworkService::new(params.network_config.clone(), None)?;
|
||||
let sync = Arc::new(Service {
|
||||
network: service,
|
||||
@@ -173,7 +161,7 @@ impl Service {
|
||||
}
|
||||
|
||||
/// Called when a new block is imported by the client.
|
||||
pub fn on_block_imported(&self, hash: HeaderHash, header: &Header) {
|
||||
pub fn on_block_imported(&self, hash: B::Hash, header: &B::Header) {
|
||||
self.network.with_context(DOT_PROTOCOL_ID, |context| {
|
||||
self.handler.protocol.on_block_imported(&mut NetSyncIo::new(context), hash, header)
|
||||
});
|
||||
@@ -199,32 +187,32 @@ impl Service {
|
||||
|
||||
fn stop(&self) {
|
||||
self.handler.protocol.abort();
|
||||
self.network.stop().unwrap_or_else(|e| warn!("Error stopping network: {:?}", e));
|
||||
self.network.stop();
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Service {
|
||||
impl<B: BlockT + 'static> Drop for Service<B> where B::Header: HeaderT<Number=u64> {
|
||||
fn drop(&mut self) {
|
||||
self.stop();
|
||||
}
|
||||
}
|
||||
|
||||
impl ExecuteInContext for Service {
|
||||
fn execute_in_context<F: Fn(&mut NetSyncIo, &Protocol)>(&self, closure: F) {
|
||||
impl<B: BlockT + 'static> ExecuteInContext<B> for Service<B> where B::Header: HeaderT<Number=u64> {
|
||||
fn execute_in_context<F: Fn(&mut NetSyncIo, &Protocol<B>)>(&self, closure: F) {
|
||||
self.network.with_context(DOT_PROTOCOL_ID, |context| {
|
||||
closure(&mut NetSyncIo::new(context), &self.handler.protocol)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl SyncProvider for Service {
|
||||
impl<B: BlockT + 'static> SyncProvider<B> for Service<B> where B::Header: HeaderT<Number=u64> {
|
||||
/// Get sync status
|
||||
fn status(&self) -> ProtocolStatus {
|
||||
fn status(&self) -> ProtocolStatus<B> {
|
||||
self.handler.protocol.status()
|
||||
}
|
||||
|
||||
/// Get sync peers
|
||||
fn peers(&self) -> Vec<PeerInfo> {
|
||||
fn peers(&self) -> Vec<PeerInfo<B>> {
|
||||
self.network.with_context_eval(DOT_PROTOCOL_ID, |ctx| {
|
||||
let peer_ids = self.network.connected_peers();
|
||||
|
||||
@@ -252,43 +240,23 @@ impl SyncProvider for Service {
|
||||
}
|
||||
|
||||
/// ConsensusService
|
||||
impl ConsensusService for Service {
|
||||
fn statements(&self) -> StatementStream {
|
||||
self.handler.protocol.statements()
|
||||
}
|
||||
|
||||
impl<B: BlockT + 'static> ConsensusService<B> for Service<B> where B::Header: HeaderT<Number=u64> {
|
||||
fn connect_to_authorities(&self, _addresses: &[String]) {
|
||||
//TODO: implement me
|
||||
}
|
||||
|
||||
fn fetch_candidate(&self, hash: &Hash) -> oneshot::Receiver<Vec<u8>> {
|
||||
self.network.with_context_eval(DOT_PROTOCOL_ID, |context| {
|
||||
self.handler.protocol.fetch_candidate(&mut NetSyncIo::new(context), hash)
|
||||
}).expect("DOT Service is registered")
|
||||
}
|
||||
|
||||
fn send_statement(&self, statement: Statement) {
|
||||
self.network.with_context(DOT_PROTOCOL_ID, |context| {
|
||||
self.handler.protocol.send_statement(&mut NetSyncIo::new(context), statement);
|
||||
});
|
||||
}
|
||||
|
||||
fn set_local_candidate(&self, candidate: Option<(Hash, Vec<u8>)>) {
|
||||
self.handler.protocol.set_local_candidate(candidate)
|
||||
}
|
||||
|
||||
fn bft_messages(&self, parent_hash: Hash) -> BftMessageStream {
|
||||
fn bft_messages(&self, parent_hash: B::Hash) -> BftMessageStream<B> {
|
||||
self.handler.protocol.bft_messages(parent_hash)
|
||||
}
|
||||
|
||||
fn send_bft_message(&self, message: LocalizedBftMessage) {
|
||||
fn send_bft_message(&self, message: LocalizedBftMessage<B>) {
|
||||
self.network.with_context(DOT_PROTOCOL_ID, |context| {
|
||||
self.handler.protocol.send_bft_message(&mut NetSyncIo::new(context), message);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl NetworkProtocolHandler for ProtocolHandler {
|
||||
impl<B: BlockT + 'static> NetworkProtocolHandler for ProtocolHandler<B> where B::Header: HeaderT<Number=u64> {
|
||||
fn initialize(&self, io: &NetworkContext, _host_info: &HostInfo) {
|
||||
io.register_timer(TICK_TOKEN, TICK_TIMEOUT)
|
||||
.expect("Error registering sync timer");
|
||||
@@ -319,7 +287,7 @@ impl NetworkProtocolHandler for ProtocolHandler {
|
||||
}
|
||||
|
||||
/// Trait for managing network
|
||||
pub trait ManageNetwork : Send + Sync {
|
||||
pub trait ManageNetwork: Send + Sync {
|
||||
/// Set to allow unreserved peers to connect
|
||||
fn accept_unreserved_peers(&self);
|
||||
/// Set to deny unreserved peers to connect
|
||||
@@ -335,7 +303,7 @@ pub trait ManageNetwork : Send + Sync {
|
||||
}
|
||||
|
||||
|
||||
impl ManageNetwork for Service {
|
||||
impl<B: BlockT + 'static> ManageNetwork for Service<B> where B::Header: HeaderT<Number=u64> {
|
||||
fn accept_unreserved_peers(&self) {
|
||||
self.network.set_non_reserved_mode(NonReservedPeerMode::Accept);
|
||||
}
|
||||
|
||||
@@ -19,38 +19,38 @@ use io::SyncIo;
|
||||
use protocol::Protocol;
|
||||
use network::PeerId;
|
||||
use client::{ImportResult, BlockStatus, ClientInfo};
|
||||
use primitives::block::{HeaderHash, Number as BlockNumber, Header, Id as BlockId};
|
||||
use blocks::{self, BlockCollection};
|
||||
use message::{self, Message};
|
||||
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT};
|
||||
use runtime_primitives::generic::BlockId;
|
||||
use message::{self, generic::Message as GenericMessage};
|
||||
use service::Role;
|
||||
use super::header_hash;
|
||||
|
||||
// Maximum blocks to request in a single packet.
|
||||
const MAX_BLOCKS_TO_REQUEST: usize = 128;
|
||||
|
||||
struct PeerSync {
|
||||
pub common_hash: HeaderHash,
|
||||
pub common_number: BlockNumber,
|
||||
pub best_hash: HeaderHash,
|
||||
pub best_number: BlockNumber,
|
||||
pub state: PeerSyncState,
|
||||
struct PeerSync<B: BlockT> {
|
||||
pub common_hash: B::Hash,
|
||||
pub common_number: <B::Header as HeaderT>::Number,
|
||||
pub best_hash: B::Hash,
|
||||
pub best_number: <B::Header as HeaderT>::Number,
|
||||
pub state: PeerSyncState<B>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
enum PeerSyncState {
|
||||
AncestorSearch(BlockNumber),
|
||||
enum PeerSyncState<B: BlockT> {
|
||||
AncestorSearch(<B::Header as HeaderT>::Number),
|
||||
Available,
|
||||
DownloadingNew(BlockNumber),
|
||||
DownloadingStale(HeaderHash),
|
||||
DownloadingNew(<B::Header as HeaderT>::Number),
|
||||
DownloadingStale(B::Hash),
|
||||
}
|
||||
|
||||
/// Relay chain sync strategy.
|
||||
pub struct ChainSync {
|
||||
genesis_hash: HeaderHash,
|
||||
peers: HashMap<PeerId, PeerSync>,
|
||||
blocks: BlockCollection,
|
||||
best_queued_number: BlockNumber,
|
||||
best_queued_hash: HeaderHash,
|
||||
pub struct ChainSync<B: BlockT> {
|
||||
genesis_hash: B::Hash,
|
||||
peers: HashMap<PeerId, PeerSync<B>>,
|
||||
blocks: BlockCollection<B>,
|
||||
best_queued_number: u64,
|
||||
best_queued_hash: B::Hash,
|
||||
required_block_attributes: Vec<message::BlockAttribute>,
|
||||
}
|
||||
|
||||
@@ -65,16 +65,18 @@ pub enum SyncState {
|
||||
|
||||
/// Syncing status and statistics
|
||||
#[derive(Clone)]
|
||||
pub struct Status {
|
||||
pub struct Status<B: BlockT> {
|
||||
/// Current global sync state.
|
||||
pub state: SyncState,
|
||||
/// Target sync block number.
|
||||
pub best_seen_block: Option<BlockNumber>,
|
||||
pub best_seen_block: Option<<B::Header as HeaderT>::Number>,
|
||||
}
|
||||
|
||||
impl ChainSync {
|
||||
impl<B: BlockT> ChainSync<B> where
|
||||
B::Header: HeaderT<Number=u64>,
|
||||
{
|
||||
/// Create a new instance.
|
||||
pub fn new(role: Role, info: &ClientInfo) -> ChainSync {
|
||||
pub fn new(role: Role, info: &ClientInfo<B>) -> Self {
|
||||
let mut required_block_attributes = vec![
|
||||
message::BlockAttribute::Header,
|
||||
message::BlockAttribute::Justification
|
||||
@@ -93,12 +95,12 @@ impl ChainSync {
|
||||
}
|
||||
}
|
||||
|
||||
fn best_seen_block(&self) -> Option<BlockNumber> {
|
||||
fn best_seen_block(&self) -> Option<u64> {
|
||||
self.peers.values().max_by_key(|p| p.best_number).map(|p| p.best_number)
|
||||
}
|
||||
|
||||
/// Returns sync status
|
||||
pub fn status(&self) -> Status {
|
||||
pub fn status(&self) -> Status<B> {
|
||||
let best_seen = self.best_seen_block();
|
||||
let state = match &best_seen {
|
||||
&Some(n) if n > self.best_queued_number && n - self.best_queued_number > 5 => SyncState::Downloading,
|
||||
@@ -111,7 +113,7 @@ impl ChainSync {
|
||||
}
|
||||
|
||||
/// Handle new connected peer.
|
||||
pub fn new_peer(&mut self, io: &mut SyncIo, protocol: &Protocol, peer_id: PeerId) {
|
||||
pub fn new_peer(&mut self, io: &mut SyncIo, protocol: &Protocol<B>, peer_id: PeerId) {
|
||||
if let Some(info) = protocol.peer_info(peer_id) {
|
||||
match (protocol.chain().block_status(&BlockId::Hash(info.best_hash)), info.best_number) {
|
||||
(Err(e), _) => {
|
||||
@@ -165,7 +167,7 @@ impl ChainSync {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_block_data(&mut self, io: &mut SyncIo, protocol: &Protocol, peer_id: PeerId, _request: message::BlockRequest, response: message::BlockResponse) {
|
||||
pub fn on_block_data(&mut self, io: &mut SyncIo, protocol: &Protocol<B>, peer_id: PeerId, _request: message::BlockRequest<B>, response: message::BlockResponse<B>) {
|
||||
let count = response.blocks.len();
|
||||
let mut imported: usize = 0;
|
||||
let new_blocks = if let Some(ref mut peer) = self.peers.get_mut(&peer_id) {
|
||||
@@ -235,9 +237,9 @@ impl ChainSync {
|
||||
let block = block.block;
|
||||
match (block.header, block.justification) {
|
||||
(Some(header), Some(justification)) => {
|
||||
let number = header.number;
|
||||
let hash = header_hash(&header);
|
||||
let parent = header.parent_hash;
|
||||
let number = header.number().clone();
|
||||
let hash = header.hash();
|
||||
let parent = header.parent_hash().clone();
|
||||
let is_best = best_seen.as_ref().map_or(false, |n| number >= *n);
|
||||
|
||||
// check whether the block is known before importing.
|
||||
@@ -305,14 +307,14 @@ impl ChainSync {
|
||||
self.maintain_sync(io, protocol);
|
||||
}
|
||||
|
||||
fn maintain_sync(&mut self, io: &mut SyncIo, protocol: &Protocol) {
|
||||
fn maintain_sync(&mut self, io: &mut SyncIo, protocol: &Protocol<B>) {
|
||||
let peers: Vec<PeerId> = self.peers.keys().map(|p| *p).collect();
|
||||
for peer in peers {
|
||||
self.download_new(io, protocol, peer);
|
||||
}
|
||||
}
|
||||
|
||||
fn block_imported(&mut self, hash: &HeaderHash, number: BlockNumber) {
|
||||
fn block_imported(&mut self, hash: &B::Hash, number: u64) {
|
||||
if number > self.best_queued_number {
|
||||
self.best_queued_number = number;
|
||||
self.best_queued_hash = *hash;
|
||||
@@ -327,28 +329,29 @@ impl ChainSync {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_chain_info(&mut self, best_header: &Header ) {
|
||||
let hash = header_hash(&best_header);
|
||||
self.block_imported(&hash, best_header.number)
|
||||
pub fn update_chain_info(&mut self, best_header: &B::Header) {
|
||||
let hash = best_header.hash();
|
||||
self.block_imported(&hash, best_header.number().clone())
|
||||
}
|
||||
|
||||
pub fn on_block_announce(&mut self, io: &mut SyncIo, protocol: &Protocol, peer_id: PeerId, hash: HeaderHash, header: &Header) {
|
||||
pub fn on_block_announce(&mut self, io: &mut SyncIo, protocol: &Protocol<B>, peer_id: PeerId, hash: B::Hash, header: &B::Header) {
|
||||
let number = *header.number();
|
||||
if let Some(ref mut peer) = self.peers.get_mut(&peer_id) {
|
||||
if header.number > peer.best_number {
|
||||
peer.best_number = header.number;
|
||||
if number > peer.best_number {
|
||||
peer.best_number = number;
|
||||
peer.best_hash = hash;
|
||||
}
|
||||
if header.number <= self.best_queued_number && header.number > peer.common_number {
|
||||
peer.common_number = header.number;
|
||||
if number <= self.best_queued_number && number > peer.common_number {
|
||||
peer.common_number = number
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if !self.is_known_or_already_downloading(protocol, &hash) {
|
||||
let stale = header.number <= self.best_queued_number;
|
||||
let stale = number <= self.best_queued_number;
|
||||
if stale {
|
||||
if !self.is_known_or_already_downloading(protocol, &header.parent_hash) {
|
||||
if !self.is_known_or_already_downloading(protocol, header.parent_hash()) {
|
||||
trace!(target: "sync", "Ignoring unknown stale block announce from {}: {} {:?}", peer_id, hash, header);
|
||||
} else {
|
||||
trace!(target: "sync", "Downloading new stale block announced from {}: {} {:?}", peer_id, hash, header);
|
||||
@@ -363,18 +366,18 @@ impl ChainSync {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_known_or_already_downloading(&self, protocol: &Protocol, hash: &HeaderHash) -> bool {
|
||||
fn is_known_or_already_downloading(&self, protocol: &Protocol<B>, hash: &B::Hash) -> bool {
|
||||
self.peers.iter().any(|(_, p)| p.state == PeerSyncState::DownloadingStale(*hash))
|
||||
|| protocol.chain().block_status(&BlockId::Hash(*hash)).ok().map_or(false, |s| s != BlockStatus::Unknown)
|
||||
}
|
||||
|
||||
pub fn peer_disconnected(&mut self, io: &mut SyncIo, protocol: &Protocol, peer_id: PeerId) {
|
||||
pub fn peer_disconnected(&mut self, io: &mut SyncIo, protocol: &Protocol<B>, peer_id: PeerId) {
|
||||
self.blocks.clear_peer_download(peer_id);
|
||||
self.peers.remove(&peer_id);
|
||||
self.maintain_sync(io, protocol);
|
||||
}
|
||||
|
||||
pub fn restart(&mut self, io: &mut SyncIo, protocol: &Protocol) {
|
||||
pub fn restart(&mut self, io: &mut SyncIo, protocol: &Protocol<B>) {
|
||||
self.blocks.clear();
|
||||
let ids: Vec<PeerId> = self.peers.keys().map(|p| *p).collect();
|
||||
for id in ids {
|
||||
@@ -399,11 +402,11 @@ impl ChainSync {
|
||||
}
|
||||
|
||||
// Download old block.
|
||||
fn download_stale(&mut self, io: &mut SyncIo, protocol: &Protocol, peer_id: PeerId, hash: &HeaderHash) {
|
||||
fn download_stale(&mut self, io: &mut SyncIo, protocol: &Protocol<B>, peer_id: PeerId, hash: &B::Hash) {
|
||||
if let Some(ref mut peer) = self.peers.get_mut(&peer_id) {
|
||||
match peer.state {
|
||||
PeerSyncState::Available => {
|
||||
let request = message::BlockRequest {
|
||||
let request = message::generic::BlockRequest {
|
||||
id: 0,
|
||||
fields: self.required_block_attributes.clone(),
|
||||
from: message::FromBlock::Hash(*hash),
|
||||
@@ -412,7 +415,7 @@ impl ChainSync {
|
||||
max: Some(1),
|
||||
};
|
||||
peer.state = PeerSyncState::DownloadingStale(*hash);
|
||||
protocol.send_message(io, peer_id, Message::BlockRequest(request));
|
||||
protocol.send_message(io, peer_id, GenericMessage::BlockRequest(request));
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
@@ -420,14 +423,14 @@ impl ChainSync {
|
||||
}
|
||||
|
||||
// Issue a request for a peer to download new blocks, if any are available
|
||||
fn download_new(&mut self, io: &mut SyncIo, protocol: &Protocol, peer_id: PeerId) {
|
||||
fn download_new(&mut self, io: &mut SyncIo, protocol: &Protocol<B>, peer_id: PeerId) {
|
||||
if let Some(ref mut peer) = self.peers.get_mut(&peer_id) {
|
||||
trace!(target: "sync", "Considering new block download from {}, common block is {}, best is {:?}", peer_id, peer.common_number, peer.best_number);
|
||||
match peer.state {
|
||||
PeerSyncState::Available => {
|
||||
if let Some(range) = self.blocks.needed_blocks(peer_id, MAX_BLOCKS_TO_REQUEST, peer.best_number, peer.common_number) {
|
||||
trace!(target: "sync", "Requesting blocks from {}, ({} to {})", peer_id, range.start, range.end);
|
||||
let request = message::BlockRequest {
|
||||
let request = message::generic::BlockRequest {
|
||||
id: 0,
|
||||
fields: self.required_block_attributes.clone(),
|
||||
from: message::FromBlock::Number(range.start),
|
||||
@@ -436,7 +439,7 @@ impl ChainSync {
|
||||
max: Some((range.end - range.start) as u32),
|
||||
};
|
||||
peer.state = PeerSyncState::DownloadingNew(range.start);
|
||||
protocol.send_message(io, peer_id, Message::BlockRequest(request));
|
||||
protocol.send_message(io, peer_id, GenericMessage::BlockRequest(request));
|
||||
} else {
|
||||
trace!(target: "sync", "Nothing to request");
|
||||
}
|
||||
@@ -446,9 +449,9 @@ impl ChainSync {
|
||||
}
|
||||
}
|
||||
|
||||
fn request_ancestry(io: &mut SyncIo, protocol: &Protocol, peer_id: PeerId, block: BlockNumber) {
|
||||
fn request_ancestry(io: &mut SyncIo, protocol: &Protocol<B>, peer_id: PeerId, block: u64) {
|
||||
trace!(target: "sync", "Requesting ancestry block #{} from {}", block, peer_id);
|
||||
let request = message::BlockRequest {
|
||||
let request = message::generic::BlockRequest {
|
||||
id: 0,
|
||||
fields: vec![message::BlockAttribute::Header, message::BlockAttribute::Justification],
|
||||
from: message::FromBlock::Number(block),
|
||||
@@ -456,6 +459,6 @@ impl ChainSync {
|
||||
direction: message::Direction::Ascending,
|
||||
max: Some(1),
|
||||
};
|
||||
protocol.send_message(io, peer_id, Message::BlockRequest(request));
|
||||
protocol.send_message(io, peer_id, GenericMessage::BlockRequest(request));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::*;
|
||||
use message::*;
|
||||
use message::{Message, generic};
|
||||
use futures::Stream;
|
||||
use test_client::runtime::Block;
|
||||
|
||||
#[test]
|
||||
fn bft_messages_include_those_sent_before_asking_for_stream() {
|
||||
@@ -28,21 +29,22 @@ fn bft_messages_include_those_sent_before_asking_for_stream() {
|
||||
|
||||
let peer = net.peer(0);
|
||||
let mut io = TestIo::new(&peer.queue, None);
|
||||
let bft_message = BftMessage::Consensus(SignedConsensusMessage::Vote(SignedConsensusVote {
|
||||
vote: ConsensusVote::AdvanceRound(0),
|
||||
let bft_message = generic::BftMessage::Consensus(generic::SignedConsensusMessage::Vote(generic::SignedConsensusVote {
|
||||
vote: generic::ConsensusVote::AdvanceRound(0),
|
||||
sender: [0; 32],
|
||||
signature: Default::default(),
|
||||
}));
|
||||
|
||||
let parent_hash = peer.genesis_hash();
|
||||
|
||||
let localized = LocalizedBftMessage {
|
||||
let localized = ::message::LocalizedBftMessage::<Block> {
|
||||
message: bft_message,
|
||||
parent_hash: parent_hash,
|
||||
};
|
||||
|
||||
let message: Message<Block> = generic::Message::BftMessage(localized.clone());
|
||||
|
||||
let as_bytes = ::serde_json::to_vec(&Message::BftMessage(localized.clone())).unwrap();
|
||||
let as_bytes = ::serde_json::to_vec(&message).unwrap();
|
||||
peer.sync.handle_packet(&mut io, 1, &as_bytes[..]);
|
||||
|
||||
let stream = peer.sync.bft_messages(parent_hash);
|
||||
|
||||
@@ -23,17 +23,17 @@ use std::sync::Arc;
|
||||
use parking_lot::RwLock;
|
||||
use client;
|
||||
use client::block_builder::BlockBuilder;
|
||||
use primitives::block::{Id as BlockId, ExtrinsicHash, HeaderHash};
|
||||
use primitives;
|
||||
use runtime_primitives::traits::Block as BlockT;
|
||||
use runtime_primitives::generic::BlockId;
|
||||
use io::SyncIo;
|
||||
use protocol::Protocol;
|
||||
use config::ProtocolConfig;
|
||||
use service::TransactionPool;
|
||||
use network::{PeerId, SessionInfo, Error as NetworkError};
|
||||
use runtime_support::Hashable;
|
||||
use keyring::Keyring;
|
||||
use codec::Slicable;
|
||||
use test_client::{self, TestClient};
|
||||
use test_client::runtime::{Block, Hash, Transfer, Extrinsic};
|
||||
|
||||
pub struct TestIo<'p> {
|
||||
pub queue: &'p RwLock<VecDeque<TestPacket>>,
|
||||
@@ -100,8 +100,8 @@ pub struct TestPacket {
|
||||
}
|
||||
|
||||
pub struct Peer {
|
||||
client: Arc<client::Client<test_client::Backend, test_client::Executor>>,
|
||||
pub sync: Protocol,
|
||||
client: Arc<client::Client<test_client::Backend, test_client::Executor, Block>>,
|
||||
pub sync: Protocol<Block>,
|
||||
pub queue: RwLock<VecDeque<TestPacket>>,
|
||||
}
|
||||
|
||||
@@ -158,12 +158,12 @@ impl Peer {
|
||||
fn flush(&self) {
|
||||
}
|
||||
|
||||
fn generate_blocks<F>(&self, count: usize, mut edit_block: F) where F: FnMut(&mut BlockBuilder<test_client::Backend, test_client::Executor>) {
|
||||
fn generate_blocks<F>(&self, count: usize, mut edit_block: F) where F: FnMut(&mut BlockBuilder<test_client::Backend, test_client::Executor, Block>) {
|
||||
for _ in 0 .. count {
|
||||
let mut builder = self.client.new_block().unwrap();
|
||||
edit_block(&mut builder);
|
||||
let block = builder.bake().unwrap();
|
||||
trace!("Generating {}, (#{})", primitives::block::HeaderHash::from(block.header.blake2_256()), block.header.number);
|
||||
trace!("Generating {}, (#{})", block.hash(), block.header.number);
|
||||
self.client.justify_and_import(client::BlockOrigin::File, block).unwrap();
|
||||
}
|
||||
}
|
||||
@@ -172,15 +172,14 @@ impl Peer {
|
||||
let mut nonce = 0;
|
||||
if with_tx {
|
||||
self.generate_blocks(count, |builder| {
|
||||
let tx = test_client::runtime::Transaction {
|
||||
from: Keyring::Alice.to_raw_public(),
|
||||
to: Keyring::Alice.to_raw_public(),
|
||||
let transfer = Transfer {
|
||||
from: Keyring::Alice.to_raw_public().into(),
|
||||
to: Keyring::Alice.to_raw_public().into(),
|
||||
amount: 1,
|
||||
nonce: nonce,
|
||||
nonce,
|
||||
};
|
||||
let signature = Keyring::from_raw_public(tx.from.clone()).unwrap().sign(&tx.encode());
|
||||
let tx = primitives::block::Extrinsic::decode(&mut test_client::runtime::UncheckedTransaction { signature, tx: tx }.encode().as_ref()).unwrap();
|
||||
builder.push(tx).unwrap();
|
||||
let signature = Keyring::from_raw_public(transfer.from.0).unwrap().sign(&transfer.encode()).into();
|
||||
builder.push(Extrinsic { transfer, signature }).unwrap();
|
||||
nonce = nonce + 1;
|
||||
});
|
||||
} else {
|
||||
@@ -188,7 +187,7 @@ impl Peer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn genesis_hash(&self) -> HeaderHash {
|
||||
pub fn genesis_hash(&self) -> Hash {
|
||||
let info = self.client.info().expect("In-mem client does not fail");
|
||||
info.chain.genesis_hash
|
||||
}
|
||||
@@ -196,16 +195,16 @@ impl Peer {
|
||||
|
||||
struct EmptyTransactionPool;
|
||||
|
||||
impl TransactionPool for EmptyTransactionPool {
|
||||
fn transactions(&self) -> Vec<(ExtrinsicHash, Vec<u8>)> {
|
||||
impl TransactionPool<Block> for EmptyTransactionPool {
|
||||
fn transactions(&self) -> Vec<(Hash, Extrinsic)> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn import(&self, _transaction: &[u8]) -> Option<ExtrinsicHash> {
|
||||
fn import(&self, _transaction: &Extrinsic) -> Option<Hash> {
|
||||
None
|
||||
}
|
||||
|
||||
fn on_broadcasted(&self, _: HashMap<ExtrinsicHash, Vec<String>>) {}
|
||||
fn on_broadcasted(&self, _: HashMap<Hash, Vec<String>>) {}
|
||||
}
|
||||
|
||||
pub struct TestNet {
|
||||
|
||||
@@ -1,276 +0,0 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot 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.
|
||||
|
||||
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Block and header type definitions.
|
||||
|
||||
use rstd::fmt;
|
||||
use rstd::vec::Vec;
|
||||
#[cfg(feature = "std")]
|
||||
use bytes;
|
||||
use Hash;
|
||||
use codec::{Input, Slicable};
|
||||
|
||||
/// Used to refer to a block number.
|
||||
pub type Number = u64;
|
||||
|
||||
/// Hash used to refer to a block hash.
|
||||
pub type HeaderHash = Hash;
|
||||
|
||||
/// Hash used to refer to an extrinsic.
|
||||
pub type ExtrinsicHash = Hash;
|
||||
|
||||
/// Simple generic extrinsic type.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
pub struct Extrinsic(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec<u8>);
|
||||
|
||||
impl Slicable for Extrinsic {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Vec::<u8>::decode(input).map(Extrinsic)
|
||||
}
|
||||
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
self.0.using_encoded(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Execution log (event)
|
||||
#[derive(PartialEq, Eq, Clone, Default)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
pub struct Log(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec<u8>);
|
||||
|
||||
impl Slicable for Log {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Vec::<u8>::decode(input).map(Log)
|
||||
}
|
||||
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
self.0.using_encoded(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// The digest of a block, useful for light-clients.
|
||||
#[derive(Clone, Default, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
pub struct Digest {
|
||||
/// All logs that have happened in the block.
|
||||
pub logs: Vec<Log>,
|
||||
}
|
||||
|
||||
impl Slicable for Digest {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Vec::<Log>::decode(input).map(|logs| Digest { logs })
|
||||
}
|
||||
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
self.logs.using_encoded(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic types to be specialised later.
|
||||
pub mod generic {
|
||||
use super::{Header, Slicable, Input, Vec};
|
||||
|
||||
/// A Block - this is generic for later specialisation in particular runtimes.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
pub struct Block<Transaction: PartialEq + Eq + Clone> {
|
||||
/// The block header.
|
||||
pub header: Header,
|
||||
/// All relay-chain transactions.
|
||||
pub transactions: Vec<Transaction>,
|
||||
}
|
||||
|
||||
impl<T: PartialEq + Eq + Clone> Slicable for Block<T> where Vec<T>: Slicable {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(Block {
|
||||
header: Slicable::decode(input)?,
|
||||
transactions: Slicable::decode(input)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v: Vec<u8> = Vec::new();
|
||||
v.extend(self.header.encode());
|
||||
v.extend(self.transactions.encode());
|
||||
v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The body of a block is just a bunch of extrinsics.
|
||||
pub type Body = Vec<Extrinsic>;
|
||||
/// The header and body of a concrete, but unspecialised, block. Used by substrate to represent a
|
||||
/// block some fields of which the runtime alone knows how to interpret (e.g. the transactions).
|
||||
pub type Block = generic::Block<Extrinsic>;
|
||||
|
||||
/// A substrate chain block header.
|
||||
// TODO: split out into light-client-specific fields and runtime-specific fields.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
|
||||
#[cfg_attr(feature = "std", serde(deny_unknown_fields))]
|
||||
pub struct Header {
|
||||
/// Block parent's hash.
|
||||
pub parent_hash: HeaderHash,
|
||||
/// Block number.
|
||||
pub number: Number,
|
||||
/// State root after this transition.
|
||||
pub state_root: Hash,
|
||||
/// The root of the trie that represents this block's transactions, indexed by a 32-byte integer.
|
||||
pub extrinsics_root: Hash,
|
||||
// TODO...
|
||||
// /// The root of the trie that represents the receipts from this block's transactions
|
||||
// pub receipts_root: Hash,
|
||||
/// The digest of activity on the block.
|
||||
pub digest: Digest,
|
||||
}
|
||||
|
||||
impl Header {
|
||||
/// Create a new instance with default fields except `number`, which is given as an argument.
|
||||
pub fn from_block_number(number: Number) -> Self {
|
||||
Header {
|
||||
parent_hash: Default::default(),
|
||||
number,
|
||||
state_root: Default::default(),
|
||||
extrinsics_root: Default::default(),
|
||||
digest: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Slicable for Header {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(Header {
|
||||
parent_hash: Slicable::decode(input)?,
|
||||
number: Slicable::decode(input)?,
|
||||
state_root: Slicable::decode(input)?,
|
||||
extrinsics_root: Slicable::decode(input)?,
|
||||
digest: Slicable::decode(input)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
|
||||
self.parent_hash.using_encoded(|s| v.extend(s));
|
||||
self.number.using_encoded(|s| v.extend(s));
|
||||
self.state_root.using_encoded(|s| v.extend(s));
|
||||
self.extrinsics_root.using_encoded(|s| v.extend(s));
|
||||
self.digest.using_encoded(|s| v.extend(s));
|
||||
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
/// Block indentification.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
pub enum Id {
|
||||
/// Identify by block header hash.
|
||||
Hash(HeaderHash),
|
||||
/// Identify by block number.
|
||||
Number(Number),
|
||||
}
|
||||
|
||||
impl fmt::Display for Id {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
Id::Hash(h) => h.fmt(f),
|
||||
Id::Number(n) => n.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use codec::Slicable;
|
||||
use substrate_serializer as ser;
|
||||
|
||||
#[test]
|
||||
fn test_header_encoding() {
|
||||
let header = Header {
|
||||
parent_hash: 5.into(),
|
||||
number: 67,
|
||||
state_root: 3.into(),
|
||||
extrinsics_root: 6.into(),
|
||||
digest: Digest { logs: vec![Log(vec![1]), Log(vec![2])] },
|
||||
};
|
||||
|
||||
assert_eq!(header.encode(), vec![
|
||||
// parent_hash
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5,
|
||||
// number
|
||||
67, 0, 0, 0, 0, 0, 0, 0,
|
||||
// state_root
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
|
||||
// extrinsics_root
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6,
|
||||
// digest (length, log1, log2)
|
||||
2, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 2
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_header_serialization() {
|
||||
let header = Header {
|
||||
parent_hash: 5.into(),
|
||||
number: 67,
|
||||
state_root: 3.into(),
|
||||
extrinsics_root: 6.into(),
|
||||
digest: Digest { logs: vec![Log(vec![1])] },
|
||||
};
|
||||
|
||||
assert_eq!(ser::to_string_pretty(&header), r#"{
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000005",
|
||||
"number": 67,
|
||||
"stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000003",
|
||||
"extrinsicsRoot": "0x0000000000000000000000000000000000000000000000000000000000000006",
|
||||
"digest": {
|
||||
"logs": [
|
||||
"0x01"
|
||||
]
|
||||
}
|
||||
}"#);
|
||||
|
||||
let v = header.encode();
|
||||
assert_eq!(Header::decode(&mut &v[..]).unwrap(), header);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_block_encoding() {
|
||||
let block = Block {
|
||||
header: Header::from_block_number(12),
|
||||
transactions: vec![Extrinsic(vec!(4))],
|
||||
};
|
||||
|
||||
assert_eq!(block.encode(), vec![
|
||||
// parent_hash
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
// number
|
||||
12, 0, 0, 0, 0, 0, 0, 0,
|
||||
// state_root
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
// extrinsics_root
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
// digest
|
||||
0, 0, 0, 0,
|
||||
// transactions (length, tx...)
|
||||
1, 0, 0, 0, 1, 0, 0, 0, 4
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -66,7 +66,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_h160() {
|
||||
let tests = vec![
|
||||
(H160::from(0), "0x0000000000000000000000000000000000000000"),
|
||||
(Default::default(), "0x0000000000000000000000000000000000000000"),
|
||||
(H160::from(2), "0x0000000000000000000000000000000000000002"),
|
||||
(H160::from(15), "0x000000000000000000000000000000000000000f"),
|
||||
(H160::from(16), "0x0000000000000000000000000000000000000010"),
|
||||
@@ -84,7 +84,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_h256() {
|
||||
let tests = vec![
|
||||
(H256::from(0), "0x0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
(Default::default(), "0x0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
(H256::from(2), "0x0000000000000000000000000000000000000000000000000000000000000002"),
|
||||
(H256::from(15), "0x000000000000000000000000000000000000000000000000000000000000000f"),
|
||||
(H256::from(16), "0x0000000000000000000000000000000000000000000000000000000000000010"),
|
||||
|
||||
@@ -54,17 +54,6 @@ extern crate substrate_serializer;
|
||||
#[macro_use]
|
||||
extern crate pretty_assertions;
|
||||
|
||||
// TODO: factor out to separate crate.
|
||||
#[macro_export]
|
||||
macro_rules! try_opt {
|
||||
($e: expr) => {
|
||||
match $e {
|
||||
Some(x) => x,
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! map {
|
||||
($( $name:expr => $value:expr ),*) => (
|
||||
@@ -82,8 +71,6 @@ pub use hashing::{blake2_256, twox_128, twox_256};
|
||||
#[cfg(feature = "std")]
|
||||
pub mod hexdisplay;
|
||||
|
||||
pub mod bft;
|
||||
pub mod block;
|
||||
pub mod hash;
|
||||
pub mod sandbox;
|
||||
pub mod storage;
|
||||
@@ -92,12 +79,8 @@ pub mod uint;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub use self::hash::{H160, H256};
|
||||
pub use self::hash::{H160, H256, H512};
|
||||
pub use self::uint::{U256, U512};
|
||||
pub use block::{Block, Header, Digest};
|
||||
|
||||
/// General hash type.
|
||||
pub type Hash = H256;
|
||||
|
||||
/// An identifier for an authority in the consensus algorithm. The same as ed25519::Public.
|
||||
pub type AuthorityId = [u8; 32];
|
||||
|
||||
@@ -10,3 +10,4 @@ jsonrpc-pubsub = { git = "https://github.com/paritytech/jsonrpc.git" }
|
||||
jsonrpc-ws-server = { git = "https://github.com/paritytech/jsonrpc.git" }
|
||||
log = "0.3"
|
||||
substrate-rpc = { path = "../rpc", version = "0.1" }
|
||||
substrate-runtime-primitives = { path = "../runtime/primitives" }
|
||||
|
||||
@@ -24,25 +24,28 @@ extern crate jsonrpc_core as rpc;
|
||||
extern crate jsonrpc_http_server as http;
|
||||
extern crate jsonrpc_pubsub as pubsub;
|
||||
extern crate jsonrpc_ws_server as ws;
|
||||
extern crate substrate_runtime_primitives;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
use std::io;
|
||||
use substrate_runtime_primitives::traits::Block as BlockT;
|
||||
|
||||
type Metadata = apis::metadata::Metadata;
|
||||
type RpcHandler = pubsub::PubSubHandler<Metadata>;
|
||||
|
||||
/// Construct rpc `IoHandler`
|
||||
pub fn rpc_handler<S, C, A, Y>(
|
||||
pub fn rpc_handler<Block: BlockT, S, C, A, Y>(
|
||||
state: S,
|
||||
chain: C,
|
||||
author: A,
|
||||
system: Y,
|
||||
) -> RpcHandler where
|
||||
S: apis::state::StateApi,
|
||||
C: apis::chain::ChainApi<Metadata=Metadata>,
|
||||
A: apis::author::AuthorApi,
|
||||
Block: 'static,
|
||||
S: apis::state::StateApi<Block::Hash>,
|
||||
C: apis::chain::ChainApi<Block::Hash, Block::Header, Metadata=Metadata>,
|
||||
A: apis::author::AuthorApi<Block::Hash, Block::Extrinsic>,
|
||||
Y: apis::system::SystemApi,
|
||||
{
|
||||
let mut io = pubsub::PubSubHandler::default();
|
||||
|
||||
@@ -14,6 +14,7 @@ substrate-client = { path = "../client" }
|
||||
substrate-executor = { path = "../executor" }
|
||||
substrate-extrinsic-pool = { path = "../extrinsic-pool" }
|
||||
substrate-primitives = { path = "../primitives" }
|
||||
substrate-runtime-primitives = { path = "../runtime/primitives" }
|
||||
substrate-state-machine = { path = "../state-machine" }
|
||||
tokio-core = "0.1.12"
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
//! Substrate block-author/full-node API.
|
||||
|
||||
use std::sync::Arc;
|
||||
use primitives::block::{Extrinsic, ExtrinsicHash};
|
||||
use extrinsic_pool::api::{Error, ExtrinsicPool};
|
||||
|
||||
pub mod error;
|
||||
@@ -29,17 +28,18 @@ use self::error::Result;
|
||||
|
||||
build_rpc_trait! {
|
||||
/// Substrate authoring RPC API
|
||||
pub trait AuthorApi {
|
||||
pub trait AuthorApi<Hash, Extrinsic> {
|
||||
/// Submit extrinsic for inclusion in block.
|
||||
#[rpc(name = "author_submitExtrinsic")]
|
||||
fn submit_extrinsic(&self, Extrinsic) -> Result<ExtrinsicHash>;
|
||||
fn submit_extrinsic(&self, Extrinsic) -> Result<Hash>;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AuthorApi for Arc<T> where
|
||||
T: ExtrinsicPool,
|
||||
impl<Ex, Hash, T> AuthorApi<Hash, Ex> for Arc<T> where
|
||||
T: ExtrinsicPool<Ex, Hash>,
|
||||
T::Error: 'static,
|
||||
{
|
||||
fn submit_extrinsic(&self, xt: Extrinsic) -> Result<ExtrinsicHash> {
|
||||
fn submit_extrinsic(&self, xt: Ex) -> Result<Hash> {
|
||||
self
|
||||
.submit(vec![xt])
|
||||
.map(|mut res| res.pop().expect("One extrinsic passed; one result back; qed"))
|
||||
|
||||
@@ -19,11 +19,13 @@ use super::*;
|
||||
use std::{fmt, sync::Arc};
|
||||
use extrinsic_pool::api;
|
||||
use parking_lot::Mutex;
|
||||
use primitives::block;
|
||||
|
||||
type Extrinsic = u64;
|
||||
type Hash = u64;
|
||||
|
||||
#[derive(Default)]
|
||||
struct DummyTxPool {
|
||||
submitted: Mutex<Vec<block::Extrinsic>>,
|
||||
submitted: Mutex<Vec<Extrinsic>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -38,14 +40,14 @@ impl fmt::Display for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl api::ExtrinsicPool for DummyTxPool {
|
||||
impl api::ExtrinsicPool<Extrinsic, Hash> for DummyTxPool {
|
||||
type Error = Error;
|
||||
|
||||
/// Submit extrinsic for inclusion in block.
|
||||
fn submit(&self, xt: Vec<Extrinsic>) -> ::std::result::Result<Vec<ExtrinsicHash>, Self::Error> {
|
||||
fn submit(&self, xt: Vec<Extrinsic>) -> ::std::result::Result<Vec<Hash>, Self::Error> {
|
||||
let mut submitted = self.submitted.lock();
|
||||
if submitted.len() < 1 {
|
||||
let hashes = xt.iter().map(|_xt| 1.into()).collect();
|
||||
let hashes = xt.iter().map(|_xt| 1).collect();
|
||||
submitted.extend(xt);
|
||||
Ok(hashes)
|
||||
} else {
|
||||
@@ -57,13 +59,12 @@ impl api::ExtrinsicPool for DummyTxPool {
|
||||
#[test]
|
||||
fn submit_transaction_should_not_cause_error() {
|
||||
let p = Arc::new(DummyTxPool::default());
|
||||
let hash: ExtrinsicHash = 1.into();
|
||||
|
||||
assert_matches!(
|
||||
AuthorApi::submit_extrinsic(&p, block::Extrinsic(vec![])),
|
||||
Ok(hash)
|
||||
AuthorApi::submit_extrinsic(&p, 5),
|
||||
Ok(1)
|
||||
);
|
||||
assert!(
|
||||
AuthorApi::submit_extrinsic(&p, block::Extrinsic(vec![])).is_err()
|
||||
AuthorApi::submit_extrinsic(&p, 5).is_err()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use primitives::block;
|
||||
use runtime_primitives::traits::Block as BlockT;
|
||||
use runtime_primitives::generic::BlockId;
|
||||
use client::{self, Client, BlockchainEvents};
|
||||
use state_machine;
|
||||
|
||||
@@ -38,23 +39,23 @@ use self::error::{Result, ResultExt};
|
||||
|
||||
build_rpc_trait! {
|
||||
/// Polkadot blockchain API
|
||||
pub trait ChainApi {
|
||||
pub trait ChainApi<Hash, Header> {
|
||||
type Metadata;
|
||||
|
||||
/// Get header of a relay chain block.
|
||||
#[rpc(name = "chain_getHeader")]
|
||||
fn header(&self, block::HeaderHash) -> Result<Option<block::Header>>;
|
||||
fn header(&self, Hash) -> Result<Option<Header>>;
|
||||
|
||||
/// Get hash of the head.
|
||||
#[rpc(name = "chain_getHead")]
|
||||
fn head(&self) -> Result<block::HeaderHash>;
|
||||
fn head(&self) -> Result<Hash>;
|
||||
|
||||
#[pubsub(name = "chain_newHead")] {
|
||||
/// Hello subscription
|
||||
/// New head subscription
|
||||
#[rpc(name = "subscribe_newHead")]
|
||||
fn subscribe_new_head(&self, Self::Metadata, pubsub::Subscriber<block::Header>);
|
||||
fn subscribe_new_head(&self, Self::Metadata, pubsub::Subscriber<Header>);
|
||||
|
||||
/// Unsubscribe from hello subscription.
|
||||
/// Unsubscribe from new head subscription.
|
||||
#[rpc(name = "unsubscribe_newHead")]
|
||||
fn unsubscribe_new_head(&self, SubscriptionId) -> RpcResult<bool>;
|
||||
}
|
||||
@@ -62,16 +63,16 @@ build_rpc_trait! {
|
||||
}
|
||||
|
||||
/// Chain API with subscriptions support.
|
||||
pub struct Chain<B, E> {
|
||||
pub struct Chain<B, E, Block: BlockT> {
|
||||
/// Substrate client.
|
||||
client: Arc<Client<B, E>>,
|
||||
client: Arc<Client<B, E, Block>>,
|
||||
/// Current subscriptions.
|
||||
subscriptions: Subscriptions,
|
||||
}
|
||||
|
||||
impl<B, E> Chain<B, E> {
|
||||
impl<B, E, Block: BlockT> Chain<B, E, Block> {
|
||||
/// Create new Chain API RPC handler.
|
||||
pub fn new(client: Arc<Client<B, E>>, remote: Remote) -> Self {
|
||||
pub fn new(client: Arc<Client<B, E, Block>>, remote: Remote) -> Self {
|
||||
Chain {
|
||||
client,
|
||||
subscriptions: Subscriptions::new(remote),
|
||||
@@ -79,22 +80,23 @@ impl<B, E> Chain<B, E> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E> ChainApi for Chain<B, E> where
|
||||
B: client::backend::Backend + Send + Sync + 'static,
|
||||
E: client::CallExecutor + Send + Sync + 'static,
|
||||
client::error::Error: From<<<B as client::backend::Backend>::State as state_machine::backend::Backend>::Error>,
|
||||
impl<B, E, Block> ChainApi<Block::Hash, Block::Header> for Chain<B, E, Block> where
|
||||
Block: BlockT + 'static,
|
||||
B: client::backend::Backend<Block> + Send + Sync + 'static,
|
||||
E: client::CallExecutor<Block> + Send + Sync + 'static,
|
||||
client::error::Error: From<<<B as client::backend::Backend<Block>>::State as state_machine::backend::Backend>::Error>,
|
||||
{
|
||||
type Metadata = ::metadata::Metadata;
|
||||
|
||||
fn header(&self, hash: block::HeaderHash) -> Result<Option<block::Header>> {
|
||||
self.client.header(&block::Id::Hash(hash)).chain_err(|| "Blockchain error")
|
||||
fn header(&self, hash: Block::Hash) -> Result<Option<Block::Header>> {
|
||||
self.client.header(&BlockId::Hash(hash)).chain_err(|| "Blockchain error")
|
||||
}
|
||||
|
||||
fn head(&self) -> Result<block::HeaderHash> {
|
||||
fn head(&self) -> Result<Block::Hash> {
|
||||
Ok(self.client.info().chain_err(|| "Blockchain error")?.chain.best_hash)
|
||||
}
|
||||
|
||||
fn subscribe_new_head(&self, _metadata: Self::Metadata, subscriber: pubsub::Subscriber<block::Header>) {
|
||||
fn subscribe_new_head(&self, _metadata: Self::Metadata, subscriber: pubsub::Subscriber<Block::Header>) {
|
||||
self.subscriptions.add(subscriber, |sink| {
|
||||
let stream = self.client.import_notification_stream()
|
||||
.filter(|notification| notification.is_new_best)
|
||||
|
||||
@@ -18,6 +18,7 @@ use super::*;
|
||||
use jsonrpc_macros::pubsub;
|
||||
use client::BlockOrigin;
|
||||
use test_client::{self, TestClient};
|
||||
use test_client::runtime::Header;
|
||||
|
||||
#[test]
|
||||
fn should_return_header() {
|
||||
@@ -30,10 +31,10 @@ fn should_return_header() {
|
||||
};
|
||||
assert_matches!(
|
||||
client.header(client.client.genesis_hash()),
|
||||
Ok(Some(ref x)) if x == &block::Header {
|
||||
Ok(Some(ref x)) if x == &Header {
|
||||
parent_hash: 0.into(),
|
||||
number: 0,
|
||||
state_root: "0c81ab6cfac8c8d7201d78cb699b6b79d714462a4ba00abcacce22444babe315".into(),
|
||||
state_root: "9c70014029b05f780858f654bfcf297ca708b3663d32bc81486193e84a2f2f3d".into(),
|
||||
extrinsics_root: "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".into(),
|
||||
digest: Default::default(),
|
||||
}
|
||||
@@ -69,7 +70,7 @@ fn should_notify_about_latest_block() {
|
||||
// assert notification send to transport
|
||||
let (notification, next) = core.run(transport.into_future()).unwrap();
|
||||
assert_eq!(notification, Some(
|
||||
r#"{"jsonrpc":"2.0","method":"test","params":{"result":{"digest":{"logs":[]},"extrinsicsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","number":1,"parentHash":"0x72ae67388233893fb4594f13df56d4e654aa8721763bcd0bd4e187fee7b2f349","stateRoot":"0x2e1f2f1c53ffb1767fe1abf4fe5953cc87c7650d4af2d4393d1f72324f2cc5d7"},"subscription":0}}"#.to_owned()
|
||||
r#"{"jsonrpc":"2.0","method":"test","params":{"result":{"digest":{"logs":[]},"extrinsicsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","number":1,"parentHash":"0x45a44c50cae6ffcc413b65d6b4444a572fa4be2e5d1cd026cc113f9d8f9bc368","stateRoot":"0x987aa0851a133413b42c6d9aa3c91b1dddc2ad5337508ee8815116b11e44c64d"},"subscription":0}}"#.to_owned()
|
||||
));
|
||||
// no more notifications on this channel
|
||||
assert_eq!(core.run(next.into_future()).unwrap().0, None);
|
||||
|
||||
@@ -24,6 +24,7 @@ extern crate parking_lot;
|
||||
extern crate substrate_client as client;
|
||||
extern crate substrate_extrinsic_pool as extrinsic_pool;
|
||||
extern crate substrate_primitives as primitives;
|
||||
extern crate substrate_runtime_primitives as runtime_primitives;
|
||||
extern crate substrate_state_machine as state_machine;
|
||||
extern crate tokio_core;
|
||||
|
||||
|
||||
@@ -23,7 +23,9 @@ mod tests;
|
||||
|
||||
use std::sync::Arc;
|
||||
use client::{self, Client, CallExecutor};
|
||||
use primitives::{block, Hash, blake2_256};
|
||||
|
||||
use runtime_primitives::generic::BlockId;
|
||||
use runtime_primitives::traits::Block as BlockT;
|
||||
use primitives::storage::{StorageKey, StorageData};
|
||||
use primitives::hexdisplay::HexDisplay;
|
||||
use state_machine;
|
||||
@@ -32,65 +34,67 @@ use self::error::Result;
|
||||
|
||||
build_rpc_trait! {
|
||||
/// Polkadot state API
|
||||
pub trait StateApi {
|
||||
/// Returns a storage entry.
|
||||
pub trait StateApi<Hash> {
|
||||
/// Returns a storage entry at a specific block's state.
|
||||
#[rpc(name = "state_getStorageAt")]
|
||||
fn storage_at(&self, StorageKey, block::HeaderHash) -> Result<StorageData>;
|
||||
fn storage_at(&self, StorageKey, Hash) -> Result<StorageData>;
|
||||
|
||||
/// Call a contract.
|
||||
/// Call a contract at a block's state.
|
||||
#[rpc(name = "state_callAt")]
|
||||
fn call_at(&self, String, Vec<u8>, block::HeaderHash) -> Result<Vec<u8>>;
|
||||
fn call_at(&self, String, Vec<u8>, Hash) -> Result<Vec<u8>>;
|
||||
|
||||
/// Returns the hash of a storage entry.
|
||||
/// Returns the hash of a storage entry at a block's state.
|
||||
#[rpc(name = "state_getStorageHashAt")]
|
||||
fn storage_hash_at(&self, StorageKey, block::HeaderHash) -> Result<Hash>;
|
||||
fn storage_hash_at(&self, StorageKey, Hash) -> Result<Hash>;
|
||||
|
||||
/// Returns the size of a storage entry.
|
||||
/// Returns the size of a storage entry at a block's state.
|
||||
#[rpc(name = "state_getStorageSizeAt")]
|
||||
fn storage_size_at(&self, StorageKey, block::HeaderHash) -> Result<u64>;
|
||||
fn storage_size_at(&self, StorageKey, Hash) -> Result<u64>;
|
||||
|
||||
/// Returns the hash of a storage entry.
|
||||
/// Returns the hash of a storage entry at the best block.
|
||||
#[rpc(name = "state_getStorageHash")]
|
||||
fn storage_hash(&self, StorageKey) -> Result<Hash>;
|
||||
|
||||
/// Returns the size of a storage entry.
|
||||
/// Returns the size of a storage entry at the best block.
|
||||
#[rpc(name = "state_getStorageSize")]
|
||||
fn storage_size(&self, StorageKey) -> Result<u64>;
|
||||
|
||||
/// Returns a storage entry.
|
||||
/// Returns a storage entry at the best block.
|
||||
#[rpc(name = "state_getStorage")]
|
||||
fn storage(&self, StorageKey) -> Result<StorageData>;
|
||||
|
||||
/// Call a contract.
|
||||
/// Call a contract at the best block.
|
||||
#[rpc(name = "state_call")]
|
||||
fn call(&self, String, Vec<u8>) -> Result<Vec<u8>>;
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E> StateApi for Arc<Client<B, E>> where
|
||||
B: client::backend::Backend + Send + Sync + 'static,
|
||||
E: CallExecutor + Send + Sync + 'static,
|
||||
client::error::Error: From<<<B as client::backend::Backend>::State as state_machine::backend::Backend>::Error>,
|
||||
impl<B, E, Block> StateApi<Block::Hash> for Arc<Client<B, E, Block>> where
|
||||
Block: BlockT + 'static,
|
||||
B: client::backend::Backend<Block> + Send + Sync + 'static,
|
||||
E: CallExecutor<Block> + Send + Sync + 'static,
|
||||
client::error::Error: From<<<B as client::backend::Backend<Block>>::State as state_machine::backend::Backend>::Error>,
|
||||
{
|
||||
fn storage_at(&self, key: StorageKey, block: block::HeaderHash) -> Result<StorageData> {
|
||||
fn storage_at(&self, key: StorageKey, block: Block::Hash) -> Result<StorageData> {
|
||||
trace!(target: "rpc", "Querying storage at {:?} for key {}", block, HexDisplay::from(&key.0));
|
||||
Ok(self.as_ref().storage(&block::Id::Hash(block), &key)?)
|
||||
Ok(self.as_ref().storage(&BlockId::Hash(block), &key)?)
|
||||
}
|
||||
|
||||
fn call_at(&self, method: String, data: Vec<u8>, block: block::HeaderHash) -> Result<Vec<u8>> {
|
||||
fn call_at(&self, method: String, data: Vec<u8>, block: Block::Hash) -> Result<Vec<u8>> {
|
||||
trace!(target: "rpc", "Calling runtime at {:?} for method {} ({})", block, method, HexDisplay::from(&data));
|
||||
Ok(self.as_ref().executor().call(&block::Id::Hash(block), &method, &data)?.return_data)
|
||||
Ok(self.as_ref().executor().call(&BlockId::Hash(block), &method, &data)?.return_data)
|
||||
}
|
||||
|
||||
fn storage_hash_at(&self, key: StorageKey, block: block::HeaderHash) -> Result<Hash> {
|
||||
self.storage_at(key, block).map(|x| blake2_256(&x.0).into())
|
||||
fn storage_hash_at(&self, key: StorageKey, block: Block::Hash) -> Result<Block::Hash> {
|
||||
use runtime_primitives::traits::{Hashing, Header as HeaderT};
|
||||
self.storage_at(key, block).map(|x| <Block::Header as HeaderT>::Hashing::hash(&x.0))
|
||||
}
|
||||
|
||||
fn storage_size_at(&self, key: StorageKey, block: block::HeaderHash) -> Result<u64> {
|
||||
fn storage_size_at(&self, key: StorageKey, block: Block::Hash) -> Result<u64> {
|
||||
self.storage_at(key, block).map(|x| x.0.len() as u64)
|
||||
}
|
||||
|
||||
fn storage_hash(&self, key: StorageKey) -> Result<Hash> {
|
||||
fn storage_hash(&self, key: StorageKey) -> Result<Block::Hash> {
|
||||
self.storage_hash_at(key, self.as_ref().info()?.chain.best_hash)
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ environmental = { path = "../environmental", optional = true }
|
||||
substrate-state-machine = { path = "../state-machine", optional = true }
|
||||
substrate-primitives = { path = "../primitives", default_features = false }
|
||||
substrate-codec = { path = "../codec", default_features = false }
|
||||
triehash = { version = "0.1", optional = true }
|
||||
triehash = { version = "0.1.2", optional = true }
|
||||
ed25519 = { path = "../ed25519", optional = true }
|
||||
|
||||
[features]
|
||||
|
||||
@@ -30,37 +30,3 @@ include!("../with_std.rs");
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
include!("../without_std.rs");
|
||||
|
||||
/// Abstraction around hashing
|
||||
pub trait Hashing {
|
||||
/// The hash type produced.
|
||||
type Output;
|
||||
|
||||
/// Produce the hash of some byte-slice.
|
||||
fn hash(s: &[u8]) -> Self::Output;
|
||||
/// Produce the hash of some codec-encodable value.
|
||||
fn hash_of<S: codec::Slicable>(s: &S) -> Self::Output {
|
||||
codec::Slicable::using_encoded(s, Self::hash)
|
||||
}
|
||||
/// Produce the patricia-trie root of a mapping from indices to byte slices.
|
||||
fn enumerated_trie_root(items: &[&[u8]]) -> Self::Output;
|
||||
|
||||
/// Acquire the global storage root.
|
||||
fn storage_root() -> Self::Output;
|
||||
}
|
||||
|
||||
/// Blake2-256 Hashing implementation.
|
||||
pub struct BlakeTwo256;
|
||||
|
||||
impl Hashing for BlakeTwo256 {
|
||||
type Output = primitives::H256;
|
||||
fn hash(s: &[u8]) -> Self::Output {
|
||||
blake2_256(s).into()
|
||||
}
|
||||
fn enumerated_trie_root(items: &[&[u8]]) -> Self::Output {
|
||||
enumerated_trie_root(items).into()
|
||||
}
|
||||
fn storage_root() -> Self::Output {
|
||||
storage_root().into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,11 +82,28 @@ pub fn storage_root() -> [u8; 32] {
|
||||
).unwrap_or([0u8; 32])
|
||||
}
|
||||
|
||||
/// "Commit" all existing operations and get the resultant storage root.
|
||||
/// A trie root formed from the enumerated items.
|
||||
pub fn enumerated_trie_root(serialised_values: &[&[u8]]) -> [u8; 32] {
|
||||
triehash::ordered_trie_root(serialised_values.iter().map(|s| s.to_vec())).0
|
||||
}
|
||||
|
||||
/// A trie root formed from the iterated items.
|
||||
pub fn trie_root<
|
||||
I: IntoIterator<Item = (A, B)>,
|
||||
A: AsRef<[u8]> + Ord,
|
||||
B: AsRef<[u8]>,
|
||||
>(input: I) -> [u8; 32] {
|
||||
triehash::trie_root(input).0
|
||||
}
|
||||
|
||||
/// A trie root formed from the enumerated items.
|
||||
pub fn ordered_trie_root<
|
||||
I: IntoIterator<Item = A>,
|
||||
A: AsRef<[u8]>
|
||||
>(input: I) -> [u8; 32] {
|
||||
triehash::ordered_trie_root(input).0
|
||||
}
|
||||
|
||||
/// Verify a ed25519 signature.
|
||||
pub fn ed25519_verify<P: AsRef<[u8]>>(sig: &[u8; 64], msg: &[u8], pubkey: P) -> bool {
|
||||
ed25519::verify(sig, msg, pubkey)
|
||||
|
||||
@@ -138,6 +138,27 @@ pub fn enumerated_trie_root(values: &[&[u8]]) -> [u8; 32] {
|
||||
result
|
||||
}
|
||||
|
||||
/// A trie root formed from the iterated items.
|
||||
pub fn trie_root<
|
||||
I: IntoIterator<Item = (A, B)>,
|
||||
A: AsRef<[u8]> + Ord,
|
||||
B: AsRef<[u8]>,
|
||||
>(_input: I) -> [u8; 32] {
|
||||
unimplemented!()
|
||||
// TODO Maybe implement (though probably easier/cleaner to have blake2 be the only thing
|
||||
// implemneted natively and compile the trie logic as wasm).
|
||||
}
|
||||
|
||||
/// A trie root formed from the enumerated items.
|
||||
pub fn ordered_trie_root<
|
||||
I: IntoIterator<Item = A>,
|
||||
A: AsRef<[u8]>
|
||||
>(_input: I) -> [u8; 32] {
|
||||
unimplemented!()
|
||||
// TODO Maybe implement (though probably easier/cleaner to have blake2 be the only thing
|
||||
// implemneted natively and compile the trie logic as wasm).
|
||||
}
|
||||
|
||||
/// The current relay chain identifier.
|
||||
pub fn chain_id() -> u64 {
|
||||
unsafe {
|
||||
|
||||
@@ -20,6 +20,7 @@ pub use std::cell;
|
||||
pub use std::clone;
|
||||
pub use std::cmp;
|
||||
pub use std::fmt;
|
||||
pub use std::hash;
|
||||
pub use std::iter;
|
||||
pub use std::marker;
|
||||
pub use std::mem;
|
||||
|
||||
@@ -29,6 +29,7 @@ pub use core::cell;
|
||||
pub use core::clone;
|
||||
pub use core::cmp;
|
||||
pub use core::fmt;
|
||||
pub use core::hash;
|
||||
pub use core::intrinsics;
|
||||
pub use core::iter;
|
||||
pub use core::marker;
|
||||
|
||||
@@ -20,7 +20,6 @@ pub use rstd::prelude::{Vec, Clone, Eq, PartialEq};
|
||||
#[cfg(feature = "std")]
|
||||
pub use std::fmt;
|
||||
pub use rstd::result;
|
||||
pub use rstd::marker::PhantomData;
|
||||
#[cfg(feature = "std")]
|
||||
use serde;
|
||||
pub use codec::{Slicable, Input};
|
||||
@@ -47,6 +46,10 @@ pub trait AuxCallable {
|
||||
type Call: AuxDispatchable + Slicable + Clone + PartialEq + Eq;
|
||||
}
|
||||
|
||||
// dirty hack to work around serde_derive issue
|
||||
// https://github.com/rust-lang/rust/issues/51331
|
||||
pub type AuxCallableCallFor<A> = <A as AuxCallable>::Call;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub trait Callable {
|
||||
type Call: Dispatchable + Slicable + ::serde::Serialize + Clone + PartialEq + Eq;
|
||||
@@ -56,6 +59,10 @@ pub trait Callable {
|
||||
type Call: Dispatchable + Slicable + Clone + PartialEq + Eq;
|
||||
}
|
||||
|
||||
// dirty hack to work around serde_derive issue.
|
||||
// https://github.com/rust-lang/rust/issues/51331
|
||||
pub type CallableCallFor<C> = <C as Callable>::Call;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub trait Parameter: Slicable + serde::Serialize + Clone + Eq + fmt::Debug {}
|
||||
|
||||
@@ -68,26 +75,43 @@ pub trait Parameter: Slicable + Clone + Eq {}
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T> Parameter for T where T: Slicable + Clone + Eq {}
|
||||
|
||||
|
||||
/// Declare a struct for this module, then implement dispatch logic to create a pairing of several
|
||||
/// dispatch traits and enums.
|
||||
#[macro_export]
|
||||
macro_rules! decl_module {
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident>;
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
pub struct $mod_type<$trait_instance: $trait_name>($crate::dispatch::PhantomData<$trait_instance>);
|
||||
// TODO: switching based on std feature is because of an issue in
|
||||
// serde-derive for when we attempt to derive `Deserialize` on these types,
|
||||
// in a situation where we've imported `substrate_runtime_support` as another name.
|
||||
#[cfg(feature = "std")]
|
||||
$(#[$attr])*
|
||||
pub struct $mod_type<$trait_instance: $trait_name>(::std::marker::PhantomData<$trait_instance>);
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
$(#[$attr])*
|
||||
pub struct $mod_type<$trait_instance: $trait_name>(::core::marker::PhantomData<$trait_instance>);
|
||||
|
||||
decl_dispatch! {
|
||||
impl for $mod_type<$trait_instance: $trait_name>;
|
||||
$($rest)*
|
||||
}
|
||||
};
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
struct $mod_type:ident<$trait_instance:ident: $trait_name:ident>;
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
struct $mod_type<$trait_instance: $trait_name>($crate::dispatch::PhantomData<$trait_instance>);
|
||||
#[cfg(feature = "std")]
|
||||
$(#[$attr])*
|
||||
struct $mod_type<$trait_instance: $trait_name>(::std::marker::PhantomData<$trait_instance>);
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
$(#[$attr])*
|
||||
struct $mod_type<$trait_instance: $trait_name>(::core::marker::PhantomData<$trait_instance>);
|
||||
decl_dispatch! {
|
||||
impl for $mod_type<$trait_instance: $trait_name>;
|
||||
$($rest)*
|
||||
@@ -101,6 +125,7 @@ macro_rules! decl_dispatch {
|
||||
// WITHOUT AUX
|
||||
(
|
||||
impl for $mod_type:ident<$trait_instance:ident: $trait_name:ident>;
|
||||
$(#[$attr:meta])*
|
||||
pub enum $call_type:ident {
|
||||
$(
|
||||
fn $fn_name:ident(
|
||||
@@ -115,6 +140,7 @@ macro_rules! decl_dispatch {
|
||||
) => {
|
||||
__decl_dispatch_module_without_aux! {
|
||||
impl for $mod_type<$trait_instance: $trait_name>;
|
||||
$(#[$attr])*
|
||||
pub enum $call_type;
|
||||
$(
|
||||
fn $fn_name( $( $param_name: $param ),* ) -> $result = $id;
|
||||
@@ -128,6 +154,7 @@ macro_rules! decl_dispatch {
|
||||
// WITH AUX
|
||||
(
|
||||
impl for $mod_type:ident<$trait_instance:ident: $trait_name:ident>;
|
||||
$(#[$attr:meta])*
|
||||
pub enum $call_type:ident where aux: $aux_type:ty {
|
||||
$(
|
||||
fn $fn_name:ident(aux
|
||||
@@ -142,6 +169,7 @@ macro_rules! decl_dispatch {
|
||||
) => {
|
||||
__decl_dispatch_module_with_aux! {
|
||||
impl for $mod_type<$trait_instance: $trait_name>;
|
||||
$(#[$attr])*
|
||||
pub enum $call_type where aux: $aux_type;
|
||||
$(
|
||||
fn $fn_name(aux $(, $param_name: $param )*) -> $result = $id;
|
||||
@@ -172,6 +200,7 @@ macro_rules! decl_dispatch {
|
||||
macro_rules! __decl_dispatch_module_without_aux {
|
||||
(
|
||||
impl for $mod_type:ident<$trait_instance:ident: $trait_name:ident>;
|
||||
$(#[$attr:meta])*
|
||||
pub enum $call_type:ident;
|
||||
$(
|
||||
fn $fn_name:ident(
|
||||
@@ -185,6 +214,7 @@ macro_rules! __decl_dispatch_module_without_aux {
|
||||
) => {
|
||||
__decl_dispatch_module_common! {
|
||||
impl for $mod_type<$trait_instance: $trait_name>;
|
||||
$(#[$attr])*
|
||||
pub enum $call_type;
|
||||
$( fn $fn_name( $( $param_name : $param ),* ) -> $result = $id ; )*
|
||||
}
|
||||
@@ -215,6 +245,7 @@ macro_rules! __decl_dispatch_module_without_aux {
|
||||
macro_rules! __decl_dispatch_module_with_aux {
|
||||
(
|
||||
impl for $mod_type:ident<$trait_instance:ident: $trait_name:ident>;
|
||||
$(#[$attr:meta])*
|
||||
pub enum $call_type:ident where aux: $aux_type:ty;
|
||||
$(
|
||||
fn $fn_name:ident(aux
|
||||
@@ -228,6 +259,7 @@ macro_rules! __decl_dispatch_module_with_aux {
|
||||
) => {
|
||||
__decl_dispatch_module_common! {
|
||||
impl for $mod_type<$trait_instance: $trait_name>;
|
||||
$(#[$attr])*
|
||||
pub enum $call_type;
|
||||
$( fn $fn_name( $( $param_name : $param ),* ) -> $result = $id ; )*
|
||||
}
|
||||
@@ -254,11 +286,12 @@ macro_rules! __decl_dispatch_module_with_aux {
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
/// Implement a single dispatch modules to create a pairing of a dispatch trait and enum.
|
||||
#[macro_export]
|
||||
macro_rules! __decl_dispatch_module_common {
|
||||
(
|
||||
impl for $mod_type:ident<$trait_instance:ident: $trait_name:ident>;
|
||||
$(#[$attr:meta])*
|
||||
pub enum $call_type:ident;
|
||||
$(
|
||||
fn $fn_name:ident(
|
||||
@@ -270,10 +303,20 @@ macro_rules! __decl_dispatch_module_common {
|
||||
= $id:expr ;
|
||||
)*
|
||||
) => {
|
||||
#[cfg_attr(feature = "std", derive(Serialize))]
|
||||
#[allow(missing_docs)]
|
||||
#[cfg(feature = "std")]
|
||||
$(#[$attr])*
|
||||
pub enum $call_type<$trait_instance: $trait_name> {
|
||||
__PhantomItem($crate::dispatch::PhantomData<$trait_instance>),
|
||||
__PhantomItem(::std::marker::PhantomData<$trait_instance>),
|
||||
$(
|
||||
#[allow(non_camel_case_types)]
|
||||
$fn_name ( $( $param ),* ),
|
||||
)*
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
$(#[$attr])*
|
||||
pub enum $call_type<$trait_instance: $trait_name> {
|
||||
__PhantomItem(::core::marker::PhantomData<$trait_instance>),
|
||||
$(
|
||||
#[allow(non_camel_case_types)]
|
||||
$fn_name ( $( $param ),* ),
|
||||
@@ -395,6 +438,7 @@ pub trait IsAuxSubType<T: AuxCallable> {
|
||||
macro_rules! impl_outer_dispatch {
|
||||
() => ();
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
pub enum $call_type:ident where aux: $aux:ty {
|
||||
$(
|
||||
$camelcase:ident = $id:expr,
|
||||
@@ -402,12 +446,10 @@ macro_rules! impl_outer_dispatch {
|
||||
}
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Debug))]
|
||||
#[allow(missing_docs)]
|
||||
$(#[$attr])*
|
||||
pub enum $call_type {
|
||||
$(
|
||||
$camelcase ( <$camelcase as $crate::dispatch::AuxCallable>::Call )
|
||||
$camelcase ( $crate::dispatch::AuxCallableCallFor<$camelcase> )
|
||||
,)*
|
||||
}
|
||||
impl_outer_dispatch_common! { $call_type, $($camelcase = $id,)* }
|
||||
@@ -436,6 +478,7 @@ macro_rules! impl_outer_dispatch {
|
||||
impl_outer_dispatch!{ $($rest)* }
|
||||
};
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
pub enum $call_type:ident {
|
||||
$(
|
||||
$camelcase:ident = $id:expr,
|
||||
@@ -443,12 +486,10 @@ macro_rules! impl_outer_dispatch {
|
||||
}
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Debug))]
|
||||
#[allow(missing_docs)]
|
||||
$(#[$attr])*
|
||||
pub enum $call_type {
|
||||
$(
|
||||
$camelcase ( <$camelcase as $crate::dispatch::Callable>::Call )
|
||||
$camelcase ( $crate::dispatch::CallableCallFor<$camelcase> )
|
||||
,)*
|
||||
}
|
||||
impl_outer_dispatch_common! { $call_type, $($camelcase = $id,)* }
|
||||
|
||||
@@ -21,14 +21,6 @@
|
||||
#[cfg(feature = "std")]
|
||||
extern crate serde;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[allow(unused_imports)] // can be removed when fixed: https://github.com/rust-lang/rust/issues/43497
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub use serde_derive::*;
|
||||
|
||||
extern crate substrate_runtime_std as rstd;
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
extern crate substrate_primitives as primitives;
|
||||
|
||||
@@ -6,6 +6,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
[dependencies]
|
||||
hex-literal = "0.1.0"
|
||||
serde = { version = "1.0", default_features = false }
|
||||
serde_derive = { version = "1.0", optional = true }
|
||||
substrate-codec = { path = "../../codec", default_features = false }
|
||||
substrate-primitives = { path = "../../primitives", default_features = false }
|
||||
substrate-runtime-std = { path = "../../runtime-std", default_features = false }
|
||||
@@ -18,6 +19,7 @@ substrate-runtime-system = { path = "../system", default_features = false }
|
||||
default = ["std"]
|
||||
std = [
|
||||
"serde/std",
|
||||
"serde_derive",
|
||||
"substrate-codec/std",
|
||||
"substrate-primitives/std",
|
||||
"substrate-runtime-std/std",
|
||||
|
||||
@@ -25,6 +25,13 @@ extern crate substrate_runtime_std as rstd;
|
||||
#[macro_use]
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
extern crate serde;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
extern crate substrate_runtime_primitives as primitives;
|
||||
extern crate substrate_codec as codec;
|
||||
@@ -36,8 +43,7 @@ use runtime_support::{storage, Parameter};
|
||||
use runtime_support::dispatch::Result;
|
||||
use runtime_support::storage::unhashed::StorageVec;
|
||||
use primitives::traits::RefInto;
|
||||
use substrate_primitives::bft::MisbehaviorReport;
|
||||
|
||||
use primitives::bft::MisbehaviorReport;
|
||||
|
||||
pub const AUTHORITY_AT: &'static [u8] = b":auth:";
|
||||
pub const AUTHORITY_COUNT: &'static [u8] = b":auth:len";
|
||||
@@ -59,9 +65,13 @@ pub trait Trait: system::Trait {
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum Call where aux: T::PublicAux {
|
||||
fn report_misbehavior(aux, report: MisbehaviorReport) -> Result = 0;
|
||||
fn report_misbehavior(aux, report: MisbehaviorReport<T::Hash, T::BlockNumber>) -> Result = 0;
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum PrivCall {
|
||||
fn set_code(new: Vec<u8>) -> Result = 0;
|
||||
fn set_storage(items: Vec<KeyValue>) -> Result = 1;
|
||||
@@ -89,7 +99,7 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
|
||||
/// Report some misbehaviour.
|
||||
fn report_misbehavior(_aux: &T::PublicAux, _report: MisbehaviorReport) -> Result {
|
||||
fn report_misbehavior(_aux: &T::PublicAux, _report: MisbehaviorReport<T::Hash, T::BlockNumber>) -> Result {
|
||||
// TODO.
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
hex-literal = "0.1.0"
|
||||
integer-sqrt = { git = "https://github.com/paritytech/integer-sqrt-rs.git", branch = "master" }
|
||||
serde = { version = "1.0", default_features = false }
|
||||
serde_derive = { version = "1.0", optional = true }
|
||||
safe-mix = { path = "../../../safe-mix", default_features = false}
|
||||
substrate-keyring = { path = "../../keyring", optional = true }
|
||||
substrate-codec = { path = "../../codec", default_features = false }
|
||||
@@ -25,6 +26,7 @@ substrate-runtime-system = { path = "../system", default_features = false }
|
||||
default = ["std"]
|
||||
std = [
|
||||
"serde/std",
|
||||
"serde_derive",
|
||||
"safe-mix/std",
|
||||
"substrate-keyring",
|
||||
"substrate-codec/std",
|
||||
|
||||
@@ -18,7 +18,12 @@
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg(feature = "std")] extern crate serde;
|
||||
#[cfg(feature = "std")]
|
||||
extern crate serde;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
extern crate integer_sqrt;
|
||||
extern crate substrate_codec as codec;
|
||||
@@ -26,7 +31,7 @@ extern crate substrate_primitives;
|
||||
#[cfg(any(feature = "std", test))] extern crate substrate_keyring as keyring;
|
||||
#[macro_use] extern crate substrate_runtime_std as rstd;
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
#[macro_use] extern crate substrate_runtime_support as runtime_support;
|
||||
#[macro_use] extern crate substrate_runtime_support;
|
||||
extern crate substrate_runtime_primitives as primitives;
|
||||
extern crate substrate_runtime_consensus as consensus;
|
||||
extern crate substrate_runtime_democracy as democracy;
|
||||
@@ -36,8 +41,8 @@ extern crate substrate_runtime_system as system;
|
||||
|
||||
use rstd::prelude::*;
|
||||
use primitives::traits::{Zero, One, RefInto, As};
|
||||
use runtime_support::{StorageValue, StorageMap};
|
||||
use runtime_support::dispatch::Result;
|
||||
use substrate_runtime_support::{StorageValue, StorageMap};
|
||||
use substrate_runtime_support::dispatch::Result;
|
||||
|
||||
pub mod voting;
|
||||
|
||||
@@ -101,6 +106,8 @@ pub trait Trait: democracy::Trait {}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum Call where aux: T::PublicAux {
|
||||
fn set_approvals(aux, votes: Vec<bool>, index: VoteIndex) -> Result = 0;
|
||||
fn reap_inactive_voter(aux, signed_index: u32, who: T::AccountId, who_index: u32, assumed_vote_index: VoteIndex) -> Result = 1;
|
||||
@@ -108,6 +115,8 @@ decl_module! {
|
||||
fn submit_candidacy(aux, slot: u32) -> Result = 3;
|
||||
fn present_winner(aux, candidate: T::AccountId, total: T::Balance, index: VoteIndex) -> Result = 4;
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum PrivCall {
|
||||
fn set_desired_seats(count: u32) -> Result = 0;
|
||||
fn remove_member(who: T::AccountId) -> Result = 1;
|
||||
@@ -597,10 +606,11 @@ mod tests {
|
||||
pub use runtime_io::with_externalities;
|
||||
pub use substrate_primitives::H256;
|
||||
use primitives::BuildExternalities;
|
||||
use primitives::traits::{HasPublicAux, Identity};
|
||||
use primitives::traits::{HasPublicAux, Identity, BlakeTwo256};
|
||||
use primitives::testing::{Digest, Header};
|
||||
|
||||
impl_outer_dispatch! {
|
||||
#[derive(Debug, Clone, Eq, Serialize, Deserialize, PartialEq)]
|
||||
pub enum Proposal {
|
||||
Staking = 0,
|
||||
Democracy = 1,
|
||||
@@ -619,7 +629,7 @@ mod tests {
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
type Hashing = runtime_io::BlakeTwo256;
|
||||
type Hashing = BlakeTwo256;
|
||||
type Digest = Digest;
|
||||
type AccountId = u64;
|
||||
type Header = Header;
|
||||
|
||||
@@ -18,20 +18,24 @@
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::borrow::Borrow;
|
||||
use primitives::traits::{Executable, RefInto};
|
||||
use runtime_io::{Hashing, print};
|
||||
use runtime_support::{StorageValue, StorageMap, IsSubType};
|
||||
use runtime_support::dispatch::Result;
|
||||
use primitives::traits::{Executable, RefInto, Hashing};
|
||||
use runtime_io::print;
|
||||
use substrate_runtime_support::dispatch::Result;
|
||||
use substrate_runtime_support::{StorageValue, StorageMap, IsSubType};
|
||||
use {system, democracy};
|
||||
use super::{Trait, Module as Council};
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum Call where aux: T::PublicAux {
|
||||
fn propose(aux, proposal: Box<T::Proposal>) -> Result = 0;
|
||||
fn vote(aux, proposal: T::Hash, approve: bool) -> Result = 1;
|
||||
fn veto(aux, proposal_hash: T::Hash) -> Result = 2;
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum PrivCall {
|
||||
fn set_cooloff_period(blocks: T::BlockNumber) -> Result = 0;
|
||||
fn set_voting_period(blocks: T::BlockNumber) -> Result = 1;
|
||||
@@ -214,7 +218,7 @@ impl<T: Trait> Executable for Council<T> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ::tests::*;
|
||||
use runtime_support::Hashable;
|
||||
use substrate_runtime_support::Hashable;
|
||||
use democracy::VoteThreshold;
|
||||
|
||||
type CouncilVoting = super::Module<Test>;
|
||||
|
||||
@@ -6,6 +6,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
[dependencies]
|
||||
hex-literal = "0.1.0"
|
||||
serde = { version = "1.0", default_features = false }
|
||||
serde_derive = { version = "1.0", optional = true }
|
||||
safe-mix = { path = "../../../safe-mix", default_features = false}
|
||||
substrate-codec = { path = "../../codec", default_features = false }
|
||||
substrate-primitives = { path = "../../primitives", default_features = false }
|
||||
@@ -22,6 +23,7 @@ substrate-runtime-system = { path = "../system", default_features = false }
|
||||
default = ["std"]
|
||||
std = [
|
||||
"serde/std",
|
||||
"serde_derive",
|
||||
"safe-mix/std",
|
||||
"substrate-codec/std",
|
||||
"substrate-primitives/std",
|
||||
|
||||
@@ -21,8 +21,12 @@
|
||||
#[cfg(feature = "std")]
|
||||
extern crate serde;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[macro_use]
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
extern crate serde_derive;
|
||||
|
||||
#[macro_use]
|
||||
extern crate substrate_runtime_support;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
extern crate substrate_primitives;
|
||||
@@ -40,9 +44,9 @@ extern crate substrate_runtime_system as system;
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::result;
|
||||
use primitives::traits::{Zero, Executable, RefInto, As};
|
||||
use runtime_support::{StorageValue, StorageMap, Parameter, Dispatchable, IsSubType};
|
||||
use runtime_support::dispatch::Result;
|
||||
use primitives::traits::{Zero, Executable, RefInto, As, MaybeSerializeDebug};
|
||||
use substrate_runtime_support::{StorageValue, StorageMap, Parameter, Dispatchable, IsSubType};
|
||||
use substrate_runtime_support::dispatch::Result;
|
||||
|
||||
mod vote_threshold;
|
||||
pub use vote_threshold::{Approved, VoteThreshold};
|
||||
@@ -53,16 +57,20 @@ pub type PropIndex = u32;
|
||||
pub type ReferendumIndex = u32;
|
||||
|
||||
pub trait Trait: staking::Trait + Sized {
|
||||
type Proposal: Parameter + Dispatchable + IsSubType<Module<Self>>;
|
||||
type Proposal: Parameter + Dispatchable + IsSubType<Module<Self>> + MaybeSerializeDebug;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum Call where aux: T::PublicAux {
|
||||
fn propose(aux, proposal: Box<T::Proposal>, value: T::Balance) -> Result = 0;
|
||||
fn second(aux, proposal: PropIndex) -> Result = 1;
|
||||
fn vote(aux, ref_index: ReferendumIndex, approve_proposal: bool) -> Result = 2;
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum PrivCall {
|
||||
fn start_referendum(proposal: Box<T::Proposal>, vote_threshold: VoteThreshold) -> Result = 0;
|
||||
fn cancel_referendum(ref_index: ReferendumIndex) -> Result = 1;
|
||||
@@ -346,10 +354,11 @@ mod tests {
|
||||
use runtime_io::with_externalities;
|
||||
use substrate_primitives::H256;
|
||||
use primitives::BuildExternalities;
|
||||
use primitives::traits::{HasPublicAux, Identity};
|
||||
use primitives::traits::{HasPublicAux, Identity, BlakeTwo256};
|
||||
use primitives::testing::{Digest, Header};
|
||||
|
||||
impl_outer_dispatch! {
|
||||
#[derive(Debug, Clone, Eq, Serialize, Deserialize, PartialEq)]
|
||||
pub enum Proposal {
|
||||
Session = 0,
|
||||
Staking = 1,
|
||||
@@ -369,7 +378,7 @@ mod tests {
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
type Hashing = runtime_io::BlakeTwo256;
|
||||
type Hashing = BlakeTwo256;
|
||||
type Digest = Digest;
|
||||
type AccountId = u64;
|
||||
type Header = Header;
|
||||
|
||||
@@ -22,7 +22,7 @@ use rstd::ops::{Add, Mul, Div, Rem};
|
||||
|
||||
/// A means of determining if a vote is past pass threshold.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Debug))]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
pub enum VoteThreshold {
|
||||
/// A supermajority of approvals is needed to pass this vote.
|
||||
SuperMajorityApprove,
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg(feature = "std")] extern crate serde;
|
||||
#[cfg(feature = "std")]
|
||||
extern crate serde;
|
||||
|
||||
extern crate substrate_runtime_std as rstd;
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
@@ -45,9 +46,9 @@ extern crate substrate_runtime_staking as staking;
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::marker::PhantomData;
|
||||
use runtime_io::Hashing;
|
||||
use runtime_support::StorageValue;
|
||||
use primitives::traits::{self, Header, Zero, One, Checkable, Applyable, CheckEqual, Executable, MakePayment};
|
||||
use primitives::traits::{self, Header, Zero, One, Checkable, Applyable, CheckEqual, Executable,
|
||||
MakePayment, Hashing};
|
||||
use codec::Slicable;
|
||||
use system::extrinsics_root;
|
||||
|
||||
@@ -60,7 +61,7 @@ pub struct Executive<
|
||||
|
||||
impl<
|
||||
System: system::Trait,
|
||||
Block: traits::Block<Header = System::Header>,
|
||||
Block: traits::Block<Header = System::Header,Hash = System::Hash>,
|
||||
Payment: MakePayment<System::AccountId>,
|
||||
Finalisation: Executable,
|
||||
> Executive<System, Block, Payment, Finalisation> where
|
||||
@@ -181,7 +182,7 @@ mod tests {
|
||||
use runtime_io::with_externalities;
|
||||
use substrate_primitives::H256;
|
||||
use primitives::BuildExternalities;
|
||||
use primitives::traits::{HasPublicAux, Identity, Header as HeaderT};
|
||||
use primitives::traits::{HasPublicAux, Identity, Header as HeaderT, BlakeTwo256};
|
||||
use primitives::testing::{Digest, Header, Block};
|
||||
|
||||
pub struct Test;
|
||||
@@ -196,7 +197,7 @@ mod tests {
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = substrate_primitives::H256;
|
||||
type Hashing = runtime_io::BlakeTwo256;
|
||||
type Hashing = BlakeTwo256;
|
||||
type Digest = Digest;
|
||||
type AccountId = u64;
|
||||
type Header = Header;
|
||||
|
||||
@@ -14,6 +14,9 @@ substrate-runtime-std = { path = "../../runtime-std", default_features = false }
|
||||
substrate-runtime-io = { path = "../../runtime-io", default_features = false }
|
||||
substrate-runtime-support = { path = "../../runtime-support", default_features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1.0"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
|
||||
+51
-48
@@ -16,10 +16,9 @@
|
||||
|
||||
//! Message formats for the BFT consensus layer.
|
||||
|
||||
use block::{Block, HeaderHash};
|
||||
use rstd::prelude::*;
|
||||
use codec::{Slicable, Input};
|
||||
use rstd::vec::Vec;
|
||||
use ::{AuthorityId, Signature};
|
||||
use substrate_primitives::{AuthorityId, Signature};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
@@ -32,24 +31,27 @@ enum ActionKind {
|
||||
AdvanceRound = 5,
|
||||
}
|
||||
|
||||
/// Type alias for extracting message type from block.
|
||||
pub type ActionFor<B> = Action<B, <B as ::traits::Block>::Hash>;
|
||||
|
||||
/// Actions which can be taken during the BFT process.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
pub enum Action {
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
pub enum Action<Block, H> {
|
||||
/// Proposal of a block candidate.
|
||||
Propose(u32, Block),
|
||||
/// Proposal header of a block candidate. Accompanies any proposal,
|
||||
/// but is used for misbehavior reporting since blocks themselves are big.
|
||||
ProposeHeader(u32, HeaderHash),
|
||||
ProposeHeader(u32, H),
|
||||
/// Preparation to commit for a candidate.
|
||||
Prepare(u32, HeaderHash),
|
||||
Prepare(u32, H),
|
||||
/// Vote to commit to a candidate.
|
||||
Commit(u32, HeaderHash),
|
||||
Commit(u32, H),
|
||||
/// Vote to advance round after inactive primary.
|
||||
AdvanceRound(u32),
|
||||
}
|
||||
|
||||
impl Slicable for Action {
|
||||
impl<Block: Slicable, Hash: Slicable> Slicable for Action<Block, Hash> {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
match *self {
|
||||
@@ -85,22 +87,19 @@ impl Slicable for Action {
|
||||
fn decode<I: Input>(value: &mut I) -> Option<Self> {
|
||||
match i8::decode(value) {
|
||||
Some(x) if x == ActionKind::Propose as i8 => {
|
||||
let (round, block) = try_opt!(Slicable::decode(value));
|
||||
let (round, block) = Slicable::decode(value)?;
|
||||
Some(Action::Propose(round, block))
|
||||
}
|
||||
Some(x) if x == ActionKind::ProposeHeader as i8 => {
|
||||
let (round, hash) = try_opt!(Slicable::decode(value));
|
||||
|
||||
let (round, hash) = Slicable::decode(value)?;
|
||||
Some(Action::ProposeHeader(round, hash))
|
||||
}
|
||||
Some(x) if x == ActionKind::Prepare as i8 => {
|
||||
let (round, hash) = try_opt!(Slicable::decode(value));
|
||||
|
||||
let (round, hash) = Slicable::decode(value)?;
|
||||
Some(Action::Prepare(round, hash))
|
||||
}
|
||||
Some(x) if x == ActionKind::Commit as i8 => {
|
||||
let (round, hash) = try_opt!(Slicable::decode(value));
|
||||
|
||||
let (round, hash) = Slicable::decode(value)?;
|
||||
Some(Action::Commit(round, hash))
|
||||
}
|
||||
Some(x) if x == ActionKind::AdvanceRound as i8 => {
|
||||
@@ -111,17 +110,20 @@ impl Slicable for Action {
|
||||
}
|
||||
}
|
||||
|
||||
/// Type alias for extracting message type from block.
|
||||
pub type MessageFor<B> = Message<B, <B as ::traits::Block>::Hash>;
|
||||
|
||||
/// Messages exchanged between participants in the BFT consensus.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
pub struct Message {
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
pub struct Message<Block, Hash> {
|
||||
/// The parent header hash this action is relative to.
|
||||
pub parent: HeaderHash,
|
||||
pub parent: Hash,
|
||||
/// The action being broadcasted.
|
||||
pub action: Action,
|
||||
pub action: Action<Block, Hash>,
|
||||
}
|
||||
|
||||
impl Slicable for Message {
|
||||
impl<Block: Slicable, Hash: Slicable> Slicable for Message<Block, Hash> {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = self.parent.encode();
|
||||
self.action.using_encoded(|s| v.extend(s));
|
||||
@@ -130,8 +132,8 @@ impl Slicable for Message {
|
||||
|
||||
fn decode<I: Input>(value: &mut I) -> Option<Self> {
|
||||
Some(Message {
|
||||
parent: try_opt!(Slicable::decode(value)),
|
||||
action: try_opt!(Slicable::decode(value)),
|
||||
parent: Slicable::decode(value)?,
|
||||
action: Slicable::decode(value)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -139,16 +141,16 @@ impl Slicable for Message {
|
||||
/// Justification of a block.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
pub struct Justification {
|
||||
pub struct Justification<H> {
|
||||
/// The round consensus was reached in.
|
||||
pub round_number: u32,
|
||||
/// The hash of the header justified.
|
||||
pub hash: HeaderHash,
|
||||
pub hash: H,
|
||||
/// The signatures and signers of the hash.
|
||||
pub signatures: Vec<(::AuthorityId, ::Signature)>
|
||||
pub signatures: Vec<(AuthorityId, Signature)>
|
||||
}
|
||||
|
||||
impl Slicable for Justification {
|
||||
impl<H: Slicable> Slicable for Justification<H> {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
|
||||
@@ -161,9 +163,9 @@ impl Slicable for Justification {
|
||||
|
||||
fn decode<I: Input>(value: &mut I) -> Option<Self> {
|
||||
Some(Justification {
|
||||
round_number: try_opt!(Slicable::decode(value)),
|
||||
hash: try_opt!(Slicable::decode(value)),
|
||||
signatures: try_opt!(Slicable::decode(value)),
|
||||
round_number: Slicable::decode(value)?,
|
||||
hash: Slicable::decode(value)?,
|
||||
signatures: Slicable::decode(value)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -190,28 +192,28 @@ impl MisbehaviorCode {
|
||||
/// Misbehavior kinds.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
pub enum MisbehaviorKind {
|
||||
pub enum MisbehaviorKind<Hash> {
|
||||
/// BFT: double prepare.
|
||||
BftDoublePrepare(u32, (HeaderHash, Signature), (HeaderHash, Signature)),
|
||||
BftDoublePrepare(u32, (Hash, Signature), (Hash, Signature)),
|
||||
/// BFT: double commit.
|
||||
BftDoubleCommit(u32, (HeaderHash, Signature), (HeaderHash, Signature)),
|
||||
BftDoubleCommit(u32, (Hash, Signature), (Hash, Signature)),
|
||||
}
|
||||
|
||||
/// A report of misbehavior by an authority.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
pub struct MisbehaviorReport {
|
||||
pub struct MisbehaviorReport<Hash, Number> {
|
||||
/// The parent hash of the block where the misbehavior occurred.
|
||||
pub parent_hash: HeaderHash,
|
||||
pub parent_hash: Hash,
|
||||
/// The parent number of the block where the misbehavior occurred.
|
||||
pub parent_number: ::block::Number,
|
||||
pub parent_number: Number,
|
||||
/// The authority who misbehavior.
|
||||
pub target: AuthorityId,
|
||||
/// The misbehavior kind.
|
||||
pub misbehavior: MisbehaviorKind,
|
||||
pub misbehavior: MisbehaviorKind<Hash>,
|
||||
}
|
||||
|
||||
impl Slicable for MisbehaviorReport {
|
||||
impl<Hash: Slicable, Number: Slicable> Slicable for MisbehaviorReport<Hash, Number> {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
self.parent_hash.using_encoded(|s| v.extend(s));
|
||||
@@ -241,23 +243,23 @@ impl Slicable for MisbehaviorReport {
|
||||
}
|
||||
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
let parent_hash = HeaderHash::decode(input)?;
|
||||
let parent_number = ::block::Number::decode(input)?;
|
||||
let parent_hash = Hash::decode(input)?;
|
||||
let parent_number = Number::decode(input)?;
|
||||
let target = AuthorityId::decode(input)?;
|
||||
|
||||
let misbehavior = match i8::decode(input).and_then(MisbehaviorCode::from_i8)? {
|
||||
MisbehaviorCode::BftDoublePrepare => {
|
||||
MisbehaviorKind::BftDoublePrepare(
|
||||
u32::decode(input)?,
|
||||
(HeaderHash::decode(input)?, Signature::decode(input)?),
|
||||
(HeaderHash::decode(input)?, Signature::decode(input)?),
|
||||
(Hash::decode(input)?, Signature::decode(input)?),
|
||||
(Hash::decode(input)?, Signature::decode(input)?),
|
||||
)
|
||||
}
|
||||
MisbehaviorCode::BftDoubleCommit => {
|
||||
MisbehaviorKind::BftDoubleCommit(
|
||||
u32::decode(input)?,
|
||||
(HeaderHash::decode(input)?, Signature::decode(input)?),
|
||||
(HeaderHash::decode(input)?, Signature::decode(input)?),
|
||||
(Hash::decode(input)?, Signature::decode(input)?),
|
||||
(Hash::decode(input)?, Signature::decode(input)?),
|
||||
)
|
||||
}
|
||||
};
|
||||
@@ -274,10 +276,11 @@ impl Slicable for MisbehaviorReport {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use substrate_primitives::H256;
|
||||
|
||||
#[test]
|
||||
fn misbehavior_report_roundtrip() {
|
||||
let report = MisbehaviorReport {
|
||||
let report = MisbehaviorReport::<H256, u64> {
|
||||
parent_hash: [0; 32].into(),
|
||||
parent_number: 999,
|
||||
target: [1; 32].into(),
|
||||
@@ -289,9 +292,9 @@ mod test {
|
||||
};
|
||||
|
||||
let encoded = report.encode();
|
||||
assert_eq!(MisbehaviorReport::decode(&mut &encoded[..]).unwrap(), report);
|
||||
assert_eq!(MisbehaviorReport::<H256, u64>::decode(&mut &encoded[..]).unwrap(), report);
|
||||
|
||||
let report = MisbehaviorReport {
|
||||
let report = MisbehaviorReport::<H256, u64> {
|
||||
parent_hash: [0; 32].into(),
|
||||
parent_number: 999,
|
||||
target: [1; 32].into(),
|
||||
@@ -303,6 +306,6 @@ mod test {
|
||||
};
|
||||
|
||||
let encoded = report.encode();
|
||||
assert_eq!(MisbehaviorReport::decode(&mut &encoded[..]).unwrap(), report);
|
||||
assert_eq!(MisbehaviorReport::<H256, u64>::decode(&mut &encoded[..]).unwrap(), report);
|
||||
}
|
||||
}
|
||||
@@ -16,37 +16,23 @@
|
||||
|
||||
//! Generic implementations of Extrinsic/Header/Block.
|
||||
|
||||
#[cfg(feature = "std")] use serde::Serialize;
|
||||
#[cfg(feature = "std")]
|
||||
use std::fmt;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
use rstd::prelude::*;
|
||||
use codec::{Slicable, Input};
|
||||
use runtime_support::AuxDispatchable;
|
||||
use traits;
|
||||
use traits::{self, Member, SimpleArithmetic, SimpleBitOps, MaybeDisplay, Block as BlockT,
|
||||
Header as HeaderT, Hashing as HashingT};
|
||||
use rstd::ops;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::fmt::{self, Debug};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub trait MaybeSerializeDebug: Serialize + Debug {}
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: Serialize + Debug> MaybeSerializeDebug for T {}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub trait MaybeSerializeDebug {}
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T> MaybeSerializeDebug for T {}
|
||||
|
||||
pub trait Member: MaybeSerializeDebug + Eq + PartialEq + Clone {}
|
||||
impl<T: MaybeSerializeDebug + Eq + PartialEq + Clone> Member for T {}
|
||||
|
||||
/// A vetted and verified extrinsic from the external world.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Debug))]
|
||||
pub struct Extrinsic<AccountId, Index, Call> where
|
||||
AccountId: Member,
|
||||
Index: Member,
|
||||
Call: Member,
|
||||
{
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
pub struct Extrinsic<AccountId, Index, Call> {
|
||||
/// Who signed it (note this is not a signature).
|
||||
pub signed: AccountId,
|
||||
/// The number of extrinsics have come before from the same signer.
|
||||
@@ -56,8 +42,8 @@ pub struct Extrinsic<AccountId, Index, Call> where
|
||||
}
|
||||
|
||||
impl<AccountId, Index, Call> Slicable for Extrinsic<AccountId, Index, Call> where
|
||||
AccountId: Member + Slicable,
|
||||
Index: Member + Slicable,
|
||||
AccountId: Member + Slicable + MaybeDisplay,
|
||||
Index: Member + Slicable + MaybeDisplay + SimpleArithmetic,
|
||||
Call: Member + Slicable
|
||||
{
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
@@ -81,37 +67,51 @@ impl<AccountId, Index, Call> Slicable for Extrinsic<AccountId, Index, Call> wher
|
||||
|
||||
/// A extrinsics right from the external world. Unchecked.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize))]
|
||||
pub struct UncheckedExtrinsic<AccountId, Index, Call, Signature> where
|
||||
AccountId: Member,
|
||||
Index: Member,
|
||||
Call: Member,
|
||||
Signature: Member, // TODO: should be Option<Signature>
|
||||
{
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub struct UncheckedExtrinsic<AccountId, Index, Call, Signature> {
|
||||
/// The actual extrinsic information.
|
||||
pub extrinsic: Extrinsic<AccountId, Index, Call>,
|
||||
/// The signature; should be an Ed25519 signature applied to the serialised `extrinsic` field.
|
||||
pub signature: Signature,
|
||||
}
|
||||
|
||||
impl<AccountId, Index, Call, Signature> UncheckedExtrinsic<AccountId, Index, Call, Signature> where
|
||||
AccountId: Member + Default,
|
||||
Index: Member,
|
||||
impl<AccountId, Index, Call, Signature> traits::Checkable for UncheckedExtrinsic<AccountId, Index, Call, Signature> where
|
||||
AccountId: Member + MaybeDisplay,
|
||||
Index: Member + MaybeDisplay + SimpleArithmetic,
|
||||
Call: Member,
|
||||
Signature: Member + Default,
|
||||
Signature: Member + traits::Verify<Signer = AccountId>,
|
||||
Extrinsic<AccountId, Index, Call>: Slicable,
|
||||
{
|
||||
type Checked = CheckedExtrinsic<AccountId, Index, Call, Signature>;
|
||||
|
||||
fn check(self) -> Result<Self::Checked, Self> {
|
||||
if ::verify_encoded_lazy(&self.signature, &self.extrinsic, &self.extrinsic.signed) {
|
||||
Ok(CheckedExtrinsic(self))
|
||||
} else {
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<AccountId, Index, Call, Signature> UncheckedExtrinsic<AccountId, Index, Call, ::MaybeUnsigned<Signature>> where
|
||||
AccountId: Member + Default + MaybeDisplay,
|
||||
Index: Member + MaybeDisplay + SimpleArithmetic,
|
||||
Call: Member,
|
||||
Signature: Member + Default + traits::Verify<Signer = AccountId>,
|
||||
Extrinsic<AccountId, Index, Call>: Slicable,
|
||||
{
|
||||
/// Is this extrinsic signed?
|
||||
pub fn is_signed(&self) -> bool {
|
||||
// TODO: should be Option<Signature> and Option<AccountId>
|
||||
self.signature != Signature::default() || self.extrinsic.signed != AccountId::default()
|
||||
self.signature.is_signed(&self.extrinsic.signed)
|
||||
}
|
||||
}
|
||||
|
||||
impl<AccountId, Index, Call, Signature> Slicable for UncheckedExtrinsic<AccountId, Index, Call, Signature> where
|
||||
AccountId: Member + Slicable,
|
||||
Index: Member + Slicable,
|
||||
Call: Member + Slicable,
|
||||
Signature: Member + Slicable
|
||||
AccountId: Member + MaybeDisplay,
|
||||
Index: Member + MaybeDisplay + SimpleArithmetic,
|
||||
Call: Member,
|
||||
Signature: Member + Slicable,
|
||||
Extrinsic<AccountId, Index, Call>: Slicable,
|
||||
{
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
// This is a little more complicated than usual since the binary format must be compatible
|
||||
@@ -133,9 +133,11 @@ impl<AccountId, Index, Call, Signature> Slicable for UncheckedExtrinsic<AccountI
|
||||
// Vec<u8>. we'll make room for it here, then overwrite once we know the length.
|
||||
v.extend(&[0u8; 4]);
|
||||
|
||||
self.extrinsic.signed.using_encoded(|s| v.extend(s));
|
||||
/* self.extrinsic.signed.using_encoded(|s| v.extend(s));
|
||||
self.extrinsic.index.using_encoded(|s| v.extend(s));
|
||||
self.extrinsic.function.using_encoded(|s| v.extend(s));
|
||||
self.extrinsic.function.using_encoded(|s| v.extend(s));*/
|
||||
self.extrinsic.using_encoded(|s| v.extend(s));
|
||||
|
||||
self.signature.using_encoded(|s| v.extend(s));
|
||||
|
||||
let length = (v.len() - 4) as u32;
|
||||
@@ -147,55 +149,25 @@ impl<AccountId, Index, Call, Signature> Slicable for UncheckedExtrinsic<AccountI
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<AccountId, Index, Call, Signature> fmt::Debug for UncheckedExtrinsic<AccountId, Index, Call, Signature> where
|
||||
AccountId: Member,
|
||||
Index: Member,
|
||||
Call: Member,
|
||||
Signature: Member,
|
||||
AccountId: fmt::Debug,
|
||||
Index: fmt::Debug,
|
||||
Call: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "UncheckedExtrinsic({:?})", self.extrinsic)
|
||||
}
|
||||
}
|
||||
|
||||
impl<AccountId, Index, Call, Signature> traits::Checkable for UncheckedExtrinsic<AccountId, Index, Call, Signature> where
|
||||
AccountId: Member + Default,
|
||||
Index: Member,
|
||||
Call: Member,
|
||||
Signature: Member + Default + traits::Verify<Signer = AccountId>,
|
||||
Extrinsic<AccountId, Index, Call>: Slicable
|
||||
{
|
||||
type Checked = CheckedExtrinsic<AccountId, Index, Call, Signature>;
|
||||
|
||||
fn check(self) -> Result<Self::Checked, Self> {
|
||||
if !self.is_signed() {
|
||||
Ok(CheckedExtrinsic(self))
|
||||
} else {
|
||||
if ::codec::Slicable::using_encoded(&self.extrinsic, |msg|
|
||||
self.signature.verify(msg, &self.extrinsic.signed)
|
||||
) {
|
||||
Ok(CheckedExtrinsic(self))
|
||||
} else {
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A type-safe indicator that a extrinsic has been checked.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
pub struct CheckedExtrinsic<AccountId, Index, Call, Signature>
|
||||
(UncheckedExtrinsic<AccountId, Index, Call, Signature>)
|
||||
where
|
||||
AccountId: Member,
|
||||
Index: Member,
|
||||
Call: Member,
|
||||
Signature: Member;
|
||||
(UncheckedExtrinsic<AccountId, Index, Call, Signature>);
|
||||
|
||||
impl<AccountId, Index, Call, Signature> CheckedExtrinsic<AccountId, Index, Call, Signature>
|
||||
where
|
||||
AccountId: Member,
|
||||
Index: Member,
|
||||
AccountId: Member + MaybeDisplay,
|
||||
Index: Member + MaybeDisplay + SimpleArithmetic,
|
||||
Call: Member,
|
||||
Signature: Member
|
||||
{
|
||||
@@ -218,8 +190,8 @@ where
|
||||
impl<AccountId, Index, Call, Signature> ops::Deref
|
||||
for CheckedExtrinsic<AccountId, Index, Call, Signature>
|
||||
where
|
||||
AccountId: Member,
|
||||
Index: Member,
|
||||
AccountId: Member + MaybeDisplay,
|
||||
Index: Member + MaybeDisplay + SimpleArithmetic,
|
||||
Call: Member,
|
||||
Signature: Member
|
||||
{
|
||||
@@ -233,8 +205,8 @@ where
|
||||
impl<AccountId, Index, Call, Signature> traits::Applyable
|
||||
for CheckedExtrinsic<AccountId, Index, Call, Signature>
|
||||
where
|
||||
AccountId: Member,
|
||||
Index: Member,
|
||||
AccountId: Member + MaybeDisplay,
|
||||
Index: Member + MaybeDisplay + SimpleArithmetic,
|
||||
Call: Member + AuxDispatchable<Aux = AccountId>,
|
||||
Signature: Member
|
||||
{
|
||||
@@ -256,12 +228,13 @@ where
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize))]
|
||||
pub struct Digest<Item: Member> {
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
pub struct Digest<Item> {
|
||||
pub logs: Vec<Item>,
|
||||
}
|
||||
|
||||
impl<Item> Slicable for Digest<Item> where
|
||||
Item: Member + Slicable
|
||||
Item: Member + Default + Slicable
|
||||
{
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(Digest { logs: Slicable::decode(input)? })
|
||||
@@ -271,7 +244,7 @@ impl<Item> Slicable for Digest<Item> where
|
||||
}
|
||||
}
|
||||
impl<Item> traits::Digest for Digest<Item> where
|
||||
Item: Member + Slicable
|
||||
Item: Member + Default + Slicable
|
||||
{
|
||||
type Item = Item;
|
||||
fn push(&mut self, item: Self::Item) {
|
||||
@@ -279,32 +252,69 @@ impl<Item> traits::Digest for Digest<Item> where
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Abstraction over a block header for a substrate chain.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize))]
|
||||
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
|
||||
#[cfg_attr(feature = "std", serde(deny_unknown_fields))]
|
||||
pub struct Header<Number, Hash, DigestItem> where
|
||||
Number: Member,
|
||||
Hash: Member,
|
||||
DigestItem: Member,
|
||||
{
|
||||
pub struct Header<Number, Hashing: HashingT, DigestItem> {
|
||||
/// The parent hash.
|
||||
pub parent_hash: Hash,
|
||||
pub parent_hash: <Hashing as HashingT>::Output,
|
||||
/// The block number.
|
||||
pub number: Number,
|
||||
/// The state trie merkle root
|
||||
pub state_root: Hash,
|
||||
pub state_root: <Hashing as HashingT>::Output,
|
||||
/// The merkle root of the extrinsics.
|
||||
pub extrinsics_root: Hash,
|
||||
pub extrinsics_root: <Hashing as HashingT>::Output,
|
||||
/// A chain-specific digest of data useful for light clients or referencing auxiliary data.
|
||||
pub digest: Digest<DigestItem>,
|
||||
}
|
||||
|
||||
impl<Number, Hash, DigestItem> Slicable for Header<Number, Hash, DigestItem> where
|
||||
Number: Member + Slicable,
|
||||
Hash: Member + Slicable,
|
||||
DigestItem: Member + Slicable,
|
||||
// Hack to work around the fact that deriving deserialize doesn't work nicely with
|
||||
// the `hashing` trait used as a parameter.
|
||||
// dummy struct that uses the hash type directly.
|
||||
// https://github.com/serde-rs/serde/issues/1296
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
|
||||
#[derive(Deserialize)]
|
||||
struct DeserializeHeader<N, H, D> {
|
||||
parent_hash: H,
|
||||
number: N,
|
||||
state_root: H,
|
||||
extrinsics_root: H,
|
||||
digest: Digest<D>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<N, D, Hashing: HashingT> From<DeserializeHeader<N, Hashing::Output, D>> for Header<N, Hashing, D> {
|
||||
fn from(other: DeserializeHeader<N, Hashing::Output, D>) -> Self {
|
||||
Header {
|
||||
parent_hash: other.parent_hash,
|
||||
number: other.number,
|
||||
state_root: other.state_root,
|
||||
extrinsics_root: other.extrinsics_root,
|
||||
digest: other.digest,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<'a, Number: 'a, Hashing: 'a + HashingT, DigestItem: 'a> Deserialize<'a> for Header<Number, Hashing, DigestItem> where
|
||||
Number: Deserialize<'a>,
|
||||
Hashing::Output: Deserialize<'a>,
|
||||
DigestItem: Deserialize<'a>,
|
||||
{
|
||||
fn deserialize<D: Deserializer<'a>>(de: D) -> Result<Self, D::Error> {
|
||||
DeserializeHeader::<Number, Hashing::Output, DigestItem>::deserialize(de).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Number, Hashing, DigestItem> Slicable for Header<Number, Hashing, DigestItem> where
|
||||
Number: Member + Slicable + MaybeDisplay + SimpleArithmetic + Slicable,
|
||||
Hashing: HashingT,
|
||||
DigestItem: Member + Default + Slicable,
|
||||
Hashing::Output: Default + Member + MaybeDisplay + SimpleBitOps + Slicable,
|
||||
{
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(Header {
|
||||
@@ -326,20 +336,33 @@ impl<Number, Hash, DigestItem> Slicable for Header<Number, Hash, DigestItem> whe
|
||||
v
|
||||
}
|
||||
}
|
||||
impl<Number, Hash, DigestItem> traits::Header for Header<Number, Hash, DigestItem> where
|
||||
Number: Member + Slicable,
|
||||
Hash: Member + Slicable,
|
||||
DigestItem: Member + Slicable,
|
||||
|
||||
impl<Number, Hashing, DigestItem> traits::Header for Header<Number, Hashing, DigestItem> where
|
||||
Number: Member + ::rstd::hash::Hash + Copy + Slicable + MaybeDisplay + SimpleArithmetic + Slicable,
|
||||
Hashing: HashingT,
|
||||
DigestItem: Member + Default + Slicable,
|
||||
Hashing::Output: Default + ::rstd::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Slicable,
|
||||
{
|
||||
type Number = Number;
|
||||
type Hash = Hash;
|
||||
type Hash = <Hashing as HashingT>::Output;
|
||||
type Hashing = Hashing;
|
||||
type Digest = Digest<DigestItem>;
|
||||
|
||||
fn number(&self) -> &Self::Number { &self.number }
|
||||
fn set_number(&mut self, num: Self::Number) { self.number = num }
|
||||
|
||||
fn extrinsics_root(&self) -> &Self::Hash { &self.extrinsics_root }
|
||||
fn set_extrinsics_root(&mut self, root: Self::Hash) { self.extrinsics_root = root }
|
||||
|
||||
fn state_root(&self) -> &Self::Hash { &self.state_root }
|
||||
fn set_state_root(&mut self, root: Self::Hash) { self.state_root = root }
|
||||
|
||||
fn parent_hash(&self) -> &Self::Hash { &self.parent_hash }
|
||||
fn set_parent_hash(&mut self, hash: Self::Hash) { self.parent_hash = hash }
|
||||
|
||||
fn digest(&self) -> &Self::Digest { &self.digest }
|
||||
fn set_digest(&mut self, digest: Self::Digest) { self.digest = digest }
|
||||
|
||||
fn new(
|
||||
number: Self::Number,
|
||||
extrinsics_root: Self::Hash,
|
||||
@@ -353,45 +376,72 @@ impl<Number, Hash, DigestItem> traits::Header for Header<Number, Hash, DigestIte
|
||||
}
|
||||
}
|
||||
|
||||
/// Abstraction over a substrate block.
|
||||
impl<Number, Hashing, DigestItem> Header<Number, Hashing, DigestItem> where
|
||||
Number: Member + ::rstd::hash::Hash + Copy + Slicable + MaybeDisplay + SimpleArithmetic + Slicable,
|
||||
Hashing: HashingT,
|
||||
DigestItem: Member + Default + Slicable,
|
||||
Hashing::Output: Default + ::rstd::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Slicable,
|
||||
{
|
||||
/// Convenience helper for computing the hash of the header without having
|
||||
/// to import the trait.
|
||||
pub fn hash(&self) -> Hashing::Output {
|
||||
Hashing::hash_of(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Something to identify a block.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize))]
|
||||
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
|
||||
#[cfg_attr(feature = "std", serde(deny_unknown_fields))]
|
||||
pub struct Block<Number, Hash, DigestItem, AccountId, Index, Call, Signature> where
|
||||
Number: Member,
|
||||
Hash: Member,
|
||||
DigestItem: Member,
|
||||
AccountId: Member,
|
||||
Index: Member,
|
||||
Call: Member,
|
||||
Signature: Member
|
||||
{
|
||||
/// The block header.
|
||||
pub header: Header<Number, Hash, DigestItem>,
|
||||
/// The accompanying extrinsics.
|
||||
pub extrinsics: Vec<UncheckedExtrinsic<AccountId, Index, Call, Signature>>,
|
||||
pub enum BlockId<Block: BlockT> {
|
||||
/// Identify by block header hash.
|
||||
Hash(<<Block as BlockT>::Header as HeaderT>::Hash),
|
||||
/// Identify by block number.
|
||||
Number(<<Block as BlockT>::Header as HeaderT>::Number),
|
||||
}
|
||||
|
||||
impl<Number, Hash, DigestItem, AccountId, Index, Call, Signature> Slicable
|
||||
for Block<Number, Hash, DigestItem, AccountId, Index, Call, Signature>
|
||||
where
|
||||
Number: Member,
|
||||
Hash: Member,
|
||||
DigestItem: Member,
|
||||
AccountId: Member,
|
||||
Index: Member,
|
||||
Call: Member,
|
||||
Signature: Member,
|
||||
Header<Number, Hash, DigestItem>: Slicable,
|
||||
UncheckedExtrinsic<AccountId, Index, Call, Signature>: Slicable,
|
||||
{
|
||||
impl<Block: BlockT> BlockId<Block> {
|
||||
/// Create a block ID from a hash.
|
||||
pub fn hash(hash: Block::Hash) -> Self {
|
||||
BlockId::Hash(hash)
|
||||
}
|
||||
|
||||
/// Create a block ID from a number.
|
||||
pub fn number(number: <Block::Header as HeaderT>::Number) -> Self {
|
||||
BlockId::Number(number)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: BlockT> Copy for BlockId<Block> {}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<Block: BlockT> fmt::Display for BlockId<Block> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Abstraction over a substrate block.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
|
||||
#[cfg_attr(feature = "std", serde(deny_unknown_fields))]
|
||||
pub struct Block<Header, Extrinsic> {
|
||||
/// The block header.
|
||||
pub header: Header,
|
||||
/// The accompanying extrinsics.
|
||||
pub extrinsics: Vec<Extrinsic>,
|
||||
}
|
||||
|
||||
impl<Header: Slicable, Extrinsic: Slicable> Slicable for Block<Header, Extrinsic> {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(Block {
|
||||
header: Slicable::decode(input)?,
|
||||
extrinsics: Slicable::decode(input)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v: Vec<u8> = Vec::new();
|
||||
v.extend(self.header.encode());
|
||||
@@ -400,19 +450,15 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Number, Hash, DigestItem, AccountId, Index, Call, Signature> traits::Block
|
||||
for Block<Number, Hash, DigestItem, AccountId, Index, Call, Signature>
|
||||
impl<Header, Extrinsic> traits::Block for Block<Header, Extrinsic>
|
||||
where
|
||||
Number: Member + Slicable,
|
||||
Hash: Member + Slicable,
|
||||
DigestItem: Member + Slicable,
|
||||
AccountId: Member,
|
||||
Index: Member,
|
||||
Call: Member,
|
||||
Signature: Member
|
||||
Header: HeaderT,
|
||||
Extrinsic: Member + Slicable,
|
||||
{
|
||||
type Extrinsic = UncheckedExtrinsic<AccountId, Index, Call, Signature>;
|
||||
type Header = Header<Number, Hash, DigestItem>;
|
||||
type Extrinsic = Extrinsic;
|
||||
type Header = Header;
|
||||
type Hash = <Self::Header as traits::Header>::Hash;
|
||||
|
||||
fn header(&self) -> &Self::Header {
|
||||
&self.header
|
||||
}
|
||||
@@ -422,4 +468,64 @@ where
|
||||
fn deconstruct(self) -> (Self::Header, Vec<Self::Extrinsic>) {
|
||||
(self.header, self.extrinsics)
|
||||
}
|
||||
fn new(header: Self::Header, extrinsics: Vec<Self::Extrinsic>) -> Self {
|
||||
Block { header, extrinsics }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use codec::Slicable;
|
||||
use substrate_primitives::{H256, H512};
|
||||
use super::{Digest, Header, UncheckedExtrinsic, Extrinsic};
|
||||
|
||||
type Block = super::Block<
|
||||
Header<u64, ::traits::BlakeTwo256, Vec<u8>>,
|
||||
UncheckedExtrinsic<H256, u64, u64, ::Ed25519Signature>,
|
||||
>;
|
||||
|
||||
#[test]
|
||||
fn block_roundtrip_serialization() {
|
||||
let block: Block = Block {
|
||||
header: Header {
|
||||
parent_hash: [0u8; 32].into(),
|
||||
number: 100_000,
|
||||
state_root: [1u8; 32].into(),
|
||||
extrinsics_root: [2u8; 32].into(),
|
||||
digest: Digest { logs: vec![vec![1, 2, 3], vec![4, 5, 6]] },
|
||||
},
|
||||
extrinsics: vec![
|
||||
UncheckedExtrinsic {
|
||||
signature: H512::from([0u8; 64]).into(),
|
||||
extrinsic: Extrinsic {
|
||||
signed: [255u8; 32].into(),
|
||||
index: 0,
|
||||
function: 100,
|
||||
}
|
||||
},
|
||||
UncheckedExtrinsic {
|
||||
signature: H512::from([255u8; 64]).into(),
|
||||
extrinsic: Extrinsic {
|
||||
signed: [128u8; 32].into(),
|
||||
index: 100,
|
||||
function: 99,
|
||||
}
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
{
|
||||
let encoded = ::serde_json::to_vec(&block).unwrap();
|
||||
let decoded: Block = ::serde_json::from_slice(&encoded).unwrap();
|
||||
|
||||
assert_eq!(block, decoded);
|
||||
}
|
||||
|
||||
{
|
||||
let encoded = block.encode();
|
||||
let decoded = Block::decode(&mut &encoded[..]).unwrap();
|
||||
|
||||
assert_eq!(block, decoded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,16 +34,23 @@ extern crate substrate_runtime_support as runtime_support;
|
||||
extern crate substrate_codec as codec;
|
||||
extern crate substrate_primitives;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate serde_json;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::HashMap;
|
||||
|
||||
use substrate_primitives::hash::H512;
|
||||
use rstd::prelude::*;
|
||||
use substrate_primitives::hash::{H256, H512};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub mod testing;
|
||||
|
||||
pub mod traits;
|
||||
pub mod generic;
|
||||
pub mod bft;
|
||||
|
||||
use traits::{Verify, Lazy};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub type BuiltExternalities = HashMap<Vec<u8>, Vec<u8>>;
|
||||
@@ -55,24 +62,90 @@ pub trait BuildExternalities {
|
||||
|
||||
/// Ed25519 signature verify.
|
||||
#[derive(Eq, PartialEq, Clone, Default)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize))]
|
||||
pub struct Ed25519Signature(H512);
|
||||
impl traits::Verify for Ed25519Signature {
|
||||
type Signer = [u8; 32];
|
||||
fn verify(&self, msg: &[u8], signer: &Self::Signer) -> bool {
|
||||
runtime_io::ed25519_verify(&(self.0).0, msg, &signer[..])
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
pub struct Ed25519Signature(pub H512);
|
||||
|
||||
impl Verify for Ed25519Signature {
|
||||
type Signer = H256;
|
||||
fn verify<L: Lazy<[u8]>>(&self, mut msg: L, signer: &Self::Signer) -> bool {
|
||||
runtime_io::ed25519_verify(&(self.0).0, msg.get(), &signer.0[..])
|
||||
}
|
||||
}
|
||||
|
||||
impl codec::Slicable for Ed25519Signature {
|
||||
fn decode<I: codec::Input>(input: &mut I) -> Option<Self> { Some(Ed25519Signature(codec::Slicable::decode(input)?,)) }
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R { self.0.using_encoded(f) }
|
||||
}
|
||||
|
||||
impl From<H512> for Ed25519Signature {
|
||||
fn from(h: H512) -> Ed25519Signature {
|
||||
Ed25519Signature(h)
|
||||
}
|
||||
}
|
||||
|
||||
/// Potentially "unsigned" signature verification.
|
||||
#[derive(Eq, PartialEq, Clone, Default)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
pub struct MaybeUnsigned<T>(pub T);
|
||||
|
||||
impl<T: Verify> MaybeUnsigned<T> where
|
||||
T: Default + Eq,
|
||||
<T as Verify>::Signer: Default + Eq,
|
||||
{
|
||||
fn is_signed(&self, signer: &<Self as Verify>::Signer) -> bool {
|
||||
self.0 != T::default() || signer != &<Self as Verify>::Signer::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Verify> Verify for MaybeUnsigned<T> where
|
||||
T: Default + Eq,
|
||||
<T as Verify>::Signer: Default + Eq,
|
||||
{
|
||||
type Signer = T::Signer;
|
||||
fn verify<L: Lazy<[u8]>>(&self, msg: L, signer: &Self::Signer) -> bool {
|
||||
if !self.is_signed(signer) {
|
||||
true
|
||||
} else {
|
||||
self.0.verify(msg, signer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: codec::Slicable> codec::Slicable for MaybeUnsigned<T> {
|
||||
fn decode<I: codec::Input>(input: &mut I) -> Option<Self> { Some(MaybeUnsigned(codec::Slicable::decode(input)?)) }
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R { self.0.using_encoded(f) }
|
||||
}
|
||||
|
||||
impl<T> From<T> for MaybeUnsigned<T> {
|
||||
fn from(t: T) -> Self {
|
||||
MaybeUnsigned(t)
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify a signature on an encoded value in a lazy manner. This can be
|
||||
/// an optimization if the signature scheme has an "unsigned" escape hash.
|
||||
pub fn verify_encoded_lazy<V: Verify, T: codec::Slicable>(sig: &V, item: &T, signer: &V::Signer) -> bool {
|
||||
// The `Lazy<T>` trait expresses something like `X: FnMut<Output = for<'a> &'a T>`.
|
||||
// unfortunately this is a lifetime relationship that can't
|
||||
// be expressed without generic associated types, better unification of HRTBs in type position,
|
||||
// and some kind of integration into the Fn* traits.
|
||||
struct LazyEncode<F> {
|
||||
inner: F,
|
||||
encoded: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl<F: Fn() -> Vec<u8>> traits::Lazy<[u8]> for LazyEncode<F> {
|
||||
fn get(&mut self) -> &[u8] {
|
||||
self.encoded.get_or_insert_with(&self.inner).as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
sig.verify(
|
||||
LazyEncode { inner: || item.encode(), encoded: None },
|
||||
signer,
|
||||
)
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! __impl_outer_config_types {
|
||||
($concrete:ident $config:ident $snake:ident $($rest:ident)*) => {
|
||||
|
||||
@@ -16,13 +16,15 @@
|
||||
|
||||
//! Testing utilities.
|
||||
|
||||
use serde;
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
use std::fmt::Debug;
|
||||
use codec::{Slicable, Input};
|
||||
use runtime_support::AuxDispatchable;
|
||||
use substrate_primitives::H256;
|
||||
use traits::{self, Checkable, Applyable};
|
||||
use traits::{self, Checkable, Applyable, BlakeTwo256};
|
||||
|
||||
#[derive(Default, PartialEq, Eq, Clone, Serialize, Debug)]
|
||||
pub use substrate_primitives::H256;
|
||||
|
||||
#[derive(Default, PartialEq, Eq, Clone, Serialize, Deserialize, Debug)]
|
||||
pub struct Digest {
|
||||
pub logs: Vec<u64>,
|
||||
}
|
||||
@@ -41,7 +43,7 @@ impl traits::Digest for Digest {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Serialize, Debug)]
|
||||
#[derive(PartialEq, Eq, Clone, Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Header {
|
||||
@@ -74,13 +76,25 @@ impl Slicable for Header {
|
||||
}
|
||||
impl traits::Header for Header {
|
||||
type Number = u64;
|
||||
type Hashing = BlakeTwo256;
|
||||
type Hash = H256;
|
||||
type Digest = Digest;
|
||||
|
||||
fn number(&self) -> &Self::Number { &self.number }
|
||||
fn set_number(&mut self, num: Self::Number) { self.number = num }
|
||||
|
||||
fn extrinsics_root(&self) -> &Self::Hash { &self.extrinsics_root }
|
||||
fn set_extrinsics_root(&mut self, root: Self::Hash) { self.extrinsics_root = root }
|
||||
|
||||
fn state_root(&self) -> &Self::Hash { &self.state_root }
|
||||
fn set_state_root(&mut self, root: Self::Hash) { self.state_root = root }
|
||||
|
||||
fn parent_hash(&self) -> &Self::Hash { &self.parent_hash }
|
||||
fn set_parent_hash(&mut self, hash: Self::Hash) { self.parent_hash = hash }
|
||||
|
||||
fn digest(&self) -> &Self::Digest { &self.digest }
|
||||
fn set_digest(&mut self, digest: Self::Digest) { self.digest = digest }
|
||||
|
||||
fn new(
|
||||
number: Self::Number,
|
||||
extrinsics_root: Self::Hash,
|
||||
@@ -94,12 +108,12 @@ impl traits::Header for Header {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Serialize, Debug)]
|
||||
pub struct Block<Xt: Slicable + Sized + serde::Serialize> {
|
||||
#[derive(PartialEq, Eq, Clone, Serialize, Deserialize, Debug)]
|
||||
pub struct Block<Xt: Slicable + Sized + Send + Sync + Serialize + Clone + Eq + Debug> {
|
||||
pub header: Header,
|
||||
pub extrinsics: Vec<Xt>,
|
||||
}
|
||||
impl<Xt: Slicable + Sized + serde::Serialize> Slicable for Block<Xt> {
|
||||
impl<Xt: Slicable + Sized + Send + Sync + Serialize + DeserializeOwned + Clone + Eq + Debug> Slicable for Block<Xt> {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(Block {
|
||||
header: Slicable::decode(input)?,
|
||||
@@ -113,9 +127,11 @@ impl<Xt: Slicable + Sized + serde::Serialize> Slicable for Block<Xt> {
|
||||
v
|
||||
}
|
||||
}
|
||||
impl<Xt: Slicable + Sized + serde::Serialize> traits::Block for Block<Xt> {
|
||||
impl<Xt: 'static + Slicable + Sized + Send + Sync + Serialize + DeserializeOwned + Clone + Eq + Debug> traits::Block for Block<Xt> {
|
||||
type Extrinsic = Xt;
|
||||
type Header = Header;
|
||||
type Hash = <Header as traits::Header>::Hash;
|
||||
|
||||
fn header(&self) -> &Self::Header {
|
||||
&self.header
|
||||
}
|
||||
@@ -125,11 +141,15 @@ impl<Xt: Slicable + Sized + serde::Serialize> traits::Block for Block<Xt> {
|
||||
fn deconstruct(self) -> (Self::Header, Vec<Self::Extrinsic>) {
|
||||
(self.header, self.extrinsics)
|
||||
}
|
||||
fn new(header: Self::Header, extrinsics: Vec<Self::Extrinsic>) -> Self {
|
||||
Block { header, extrinsics }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Serialize, Debug)]
|
||||
pub struct TestXt<Call: AuxDispatchable + Slicable + Sized + serde::Serialize>(pub (u64, u64, Call));
|
||||
impl<Call: AuxDispatchable + Slicable + Sized + serde::Serialize> Slicable for TestXt<Call> {
|
||||
#[derive(PartialEq, Eq, Clone, Serialize, Deserialize, Debug)]
|
||||
pub struct TestXt<Call: AuxDispatchable + Slicable + Sized + Send + Sync + Serialize>(pub (u64, u64, Call));
|
||||
|
||||
impl<Call: AuxDispatchable + Slicable + Sized + Send + Sync + Serialize + DeserializeOwned + Clone + Eq + Debug> Slicable for TestXt<Call> {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(TestXt(Slicable::decode(input)?))
|
||||
}
|
||||
@@ -137,11 +157,11 @@ impl<Call: AuxDispatchable + Slicable + Sized + serde::Serialize> Slicable for T
|
||||
self.0.encode()
|
||||
}
|
||||
}
|
||||
impl<Call: AuxDispatchable + Slicable + Sized + serde::Serialize> Checkable for TestXt<Call> {
|
||||
impl<Call: 'static + AuxDispatchable + Slicable + Sized + Send + Sync + Serialize + DeserializeOwned + Clone + Eq + Debug> Checkable for TestXt<Call> {
|
||||
type Checked = Self;
|
||||
fn check(self) -> Result<Self, Self> { Ok(self) }
|
||||
}
|
||||
impl<Call: AuxDispatchable<Aux = u64> + Slicable + Sized + serde::Serialize> Applyable for TestXt<Call> {
|
||||
impl<Call: AuxDispatchable<Aux = u64> + Slicable + Sized + Send + Sync + Serialize + DeserializeOwned + Clone + Eq + Debug> Applyable for TestXt<Call> {
|
||||
type AccountId = u64;
|
||||
type Index = u64;
|
||||
fn sender(&self) -> &u64 { &(self.0).0 }
|
||||
|
||||
@@ -18,19 +18,26 @@
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd;
|
||||
#[cfg(not(feature = "std"))] use runtime_io;
|
||||
use runtime_io;
|
||||
#[cfg(feature = "std")] use std::fmt::{Debug, Display};
|
||||
#[cfg(feature = "std")] use serde::{Serialize, de::DeserializeOwned};
|
||||
use substrate_primitives;
|
||||
use codec::Slicable;
|
||||
pub use integer_sqrt::IntegerSquareRoot;
|
||||
pub use num_traits::{Zero, One, Bounded};
|
||||
use rstd::ops::{Add, Sub, Mul, Div, Rem, AddAssign, SubAssign, MulAssign, DivAssign, RemAssign};
|
||||
|
||||
/// A lazy value.
|
||||
pub trait Lazy<T: ?Sized> {
|
||||
fn get(&mut self) -> &T;
|
||||
}
|
||||
|
||||
/// Means of signature verification.
|
||||
pub trait Verify {
|
||||
/// Type of the signer.
|
||||
type Signer;
|
||||
/// Verify a signature.
|
||||
fn verify(&self, msg: &[u8], signer: &Self::Signer) -> bool;
|
||||
fn verify<L: Lazy<[u8]>>(&self, msg: L, signer: &Self::Signer) -> bool;
|
||||
}
|
||||
|
||||
/// Simple payment making trait, operating on a single generic `AccountId` type.
|
||||
@@ -122,13 +129,28 @@ impl<T:
|
||||
PartialOrd<Self> + Ord
|
||||
> SimpleArithmetic for T {}
|
||||
|
||||
/// Trait for things that can be clear (have no bits set). For numeric types, essentially the same
|
||||
/// as `Zero`.
|
||||
pub trait Clear {
|
||||
/// True iff no bits are set.
|
||||
fn is_clear(&self) -> bool;
|
||||
|
||||
/// Return the value of Self that is clear.
|
||||
fn clear() -> Self;
|
||||
}
|
||||
|
||||
impl<T: Default + Eq + PartialEq> Clear for T {
|
||||
fn is_clear(&self) -> bool { *self == Self::clear() }
|
||||
fn clear() -> Self { Default::default() }
|
||||
}
|
||||
|
||||
pub trait SimpleBitOps:
|
||||
Sized +
|
||||
Sized + Clear +
|
||||
rstd::ops::BitOr<Self, Output = Self> +
|
||||
rstd::ops::BitAnd<Self, Output = Self>
|
||||
{}
|
||||
impl<T:
|
||||
Sized +
|
||||
Sized + Clear +
|
||||
rstd::ops::BitOr<Self, Output = Self> +
|
||||
rstd::ops::BitAnd<Self, Output = Self>
|
||||
> SimpleBitOps for T {}
|
||||
@@ -148,114 +170,69 @@ impl<A: Executable, B: Executable> Executable for (A, B) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Something that acts like a `Digest` - it can have `Log`s `push`ed onto it and these `Log`s are
|
||||
/// each `Slicable`.
|
||||
pub trait Digest {
|
||||
type Item: Sized;
|
||||
fn push(&mut self, item: Self::Item);
|
||||
}
|
||||
/// Abstraction around hashing
|
||||
pub trait Hashing: 'static + MaybeSerializeDebug + Clone + Eq + PartialEq { // Stupid bug in the Rust compiler believes derived
|
||||
// traits must be fulfilled by all type parameters.
|
||||
/// The hash type produced.
|
||||
type Output: Member + AsRef<[u8]>;
|
||||
|
||||
impl Digest for substrate_primitives::Digest {
|
||||
type Item = substrate_primitives::block::Log;
|
||||
fn push(&mut self, item: Self::Item) {
|
||||
self.logs.push(item);
|
||||
/// Produce the hash of some byte-slice.
|
||||
fn hash(s: &[u8]) -> Self::Output;
|
||||
|
||||
/// Produce the hash of some codec-encodable value.
|
||||
fn hash_of<S: Slicable>(s: &S) -> Self::Output {
|
||||
Slicable::using_encoded(s, Self::hash)
|
||||
}
|
||||
|
||||
/// Produce the patricia-trie root of a mapping from indices to byte slices.
|
||||
fn enumerated_trie_root(items: &[&[u8]]) -> Self::Output;
|
||||
|
||||
/// Iterator-based version of `enumerated_trie_root`.
|
||||
fn ordered_trie_root<
|
||||
I: IntoIterator<Item = A>,
|
||||
A: AsRef<[u8]>
|
||||
>(input: I) -> Self::Output;
|
||||
|
||||
/// The Patricia tree root of the given mapping as an iterator.
|
||||
fn trie_root<
|
||||
I: IntoIterator<Item = (A, B)>,
|
||||
A: AsRef<[u8]> + Ord,
|
||||
B: AsRef<[u8]>
|
||||
>(input: I) -> Self::Output;
|
||||
|
||||
/// Acquire the global storage root.
|
||||
fn storage_root() -> Self::Output;
|
||||
}
|
||||
|
||||
/// Something which fulfills the abstract idea of a Substrate header. It has types for a `Number`,
|
||||
/// a `Hash` and a `Digest`. It provides access to an `extrinsics_root`, `state_root` and
|
||||
/// `parent_hash`, as well as a `digest` and a block `number`.
|
||||
///
|
||||
/// You can also create a `new` one from those fields.
|
||||
pub trait Header: Sized + Slicable {
|
||||
type Number: Sized;
|
||||
type Hash: Sized;
|
||||
type Digest: Sized;
|
||||
fn number(&self) -> &Self::Number;
|
||||
fn extrinsics_root(&self) -> &Self::Hash;
|
||||
fn state_root(&self) -> &Self::Hash;
|
||||
fn parent_hash(&self) -> &Self::Hash;
|
||||
fn digest(&self) -> &Self::Digest;
|
||||
fn new(
|
||||
number: Self::Number,
|
||||
extrinsics_root: Self::Hash,
|
||||
state_root: Self::Hash,
|
||||
parent_hash: Self::Hash,
|
||||
digest: Self::Digest
|
||||
) -> Self;
|
||||
}
|
||||
/// Blake2-256 Hashing implementation.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
pub struct BlakeTwo256;
|
||||
|
||||
impl Header for substrate_primitives::Header {
|
||||
type Number = substrate_primitives::block::Number;
|
||||
type Hash = substrate_primitives::block::HeaderHash;
|
||||
type Digest = substrate_primitives::block::Digest;
|
||||
fn number(&self) -> &Self::Number { &self.number }
|
||||
fn extrinsics_root(&self) -> &Self::Hash { &self.extrinsics_root }
|
||||
fn state_root(&self) -> &Self::Hash { &self.state_root }
|
||||
fn parent_hash(&self) -> &Self::Hash { &self.parent_hash }
|
||||
fn digest(&self) -> &Self::Digest { &self.digest }
|
||||
fn new(
|
||||
number: Self::Number,
|
||||
extrinsics_root: Self::Hash,
|
||||
state_root: Self::Hash,
|
||||
parent_hash: Self::Hash,
|
||||
digest: Self::Digest
|
||||
) -> Self {
|
||||
substrate_primitives::Header {
|
||||
number: number,
|
||||
extrinsics_root: extrinsics_root,
|
||||
state_root: state_root,
|
||||
parent_hash: parent_hash,
|
||||
digest: digest,
|
||||
}
|
||||
impl Hashing for BlakeTwo256 {
|
||||
type Output = substrate_primitives::H256;
|
||||
fn hash(s: &[u8]) -> Self::Output {
|
||||
runtime_io::blake2_256(s).into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Something which fulfills the abstract idea of a Substrate block. It has types for an
|
||||
/// `Extrinsic` piece of information as well as a `Header`.
|
||||
///
|
||||
/// You can get an iterator over each of the `extrinsics` and retrieve the `header`.
|
||||
pub trait Block {
|
||||
type Extrinsic: Sized;
|
||||
type Header: Header;
|
||||
fn header(&self) -> &Self::Header;
|
||||
fn extrinsics(&self) -> &[Self::Extrinsic];
|
||||
fn deconstruct(self) -> (Self::Header, Vec<Self::Extrinsic>);
|
||||
}
|
||||
|
||||
impl Block for substrate_primitives::Block {
|
||||
type Extrinsic = substrate_primitives::block::Extrinsic;
|
||||
type Header = substrate_primitives::Header;
|
||||
fn header(&self) -> &Self::Header {
|
||||
&self.header
|
||||
fn enumerated_trie_root(items: &[&[u8]]) -> Self::Output {
|
||||
runtime_io::enumerated_trie_root(items).into()
|
||||
}
|
||||
fn extrinsics(&self) -> &[Self::Extrinsic] {
|
||||
&self.transactions[..]
|
||||
fn trie_root<
|
||||
I: IntoIterator<Item = (A, B)>,
|
||||
A: AsRef<[u8]> + Ord,
|
||||
B: AsRef<[u8]>
|
||||
>(input: I) -> Self::Output {
|
||||
runtime_io::trie_root(input).into()
|
||||
}
|
||||
fn deconstruct(self) -> (Self::Header, Vec<Self::Extrinsic>) {
|
||||
(self.header, self.transactions)
|
||||
fn ordered_trie_root<
|
||||
I: IntoIterator<Item = A>,
|
||||
A: AsRef<[u8]>
|
||||
>(input: I) -> Self::Output {
|
||||
runtime_io::ordered_trie_root(input).into()
|
||||
}
|
||||
fn storage_root() -> Self::Output {
|
||||
runtime_io::storage_root().into()
|
||||
}
|
||||
}
|
||||
|
||||
/// A "checkable" piece of information, used by the standard Substrate Executive in order to
|
||||
/// check the validity of a piece of extrinsic information, usually by verifying the signature.
|
||||
pub trait Checkable: Sized {
|
||||
type Checked: Sized;
|
||||
fn check(self) -> Result<Self::Checked, Self>;
|
||||
}
|
||||
|
||||
/// An "executable" piece of information, used by the standard Substrate Executive in order to
|
||||
/// enact a piece of extrinsic information by marshalling and dispatching to a named functioon
|
||||
/// call.
|
||||
///
|
||||
/// Also provides information on to whom this information is attributable and an index that allows
|
||||
/// each piece of attributable information to be disambiguated.
|
||||
pub trait Applyable {
|
||||
type AccountId;
|
||||
type Index;
|
||||
fn index(&self) -> &Self::Index;
|
||||
fn sender(&self) -> &Self::AccountId;
|
||||
fn apply(self);
|
||||
}
|
||||
|
||||
/// Something that can be checked for equality and printed out to a debug channel if bad.
|
||||
@@ -281,3 +258,114 @@ impl CheckEqual for substrate_primitives::H256 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub trait MaybeSerializeDebug: Serialize + DeserializeOwned + Debug {}
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: Serialize + DeserializeOwned + Debug> MaybeSerializeDebug for T {}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub trait MaybeSerializeDebug {}
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T> MaybeSerializeDebug for T {}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub trait MaybeDisplay: Display {}
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: Display> MaybeDisplay for T {}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub trait MaybeDisplay {}
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T> MaybeDisplay for T {}
|
||||
|
||||
pub trait Member: Send + Sync + Sized + MaybeSerializeDebug + Eq + PartialEq + Clone + 'static {}
|
||||
impl<T: Send + Sync + Sized + MaybeSerializeDebug + Eq + PartialEq + Clone + 'static> Member for T {}
|
||||
|
||||
/// Something that acts like a `Digest` - it can have `Log`s `push`ed onto it and these `Log`s are
|
||||
/// each `Slicable`.
|
||||
pub trait Digest {
|
||||
type Item: Member;
|
||||
fn push(&mut self, item: Self::Item);
|
||||
}
|
||||
|
||||
/// Something which fulfills the abstract idea of a Substrate header. It has types for a `Number`,
|
||||
/// a `Hash` and a `Digest`. It provides access to an `extrinsics_root`, `state_root` and
|
||||
/// `parent_hash`, as well as a `digest` and a block `number`.
|
||||
///
|
||||
/// You can also create a `new` one from those fields.
|
||||
pub trait Header: Clone + Send + Sync + Slicable + Eq + MaybeSerializeDebug {
|
||||
type Number: Member + ::rstd::hash::Hash + Copy + MaybeDisplay + SimpleArithmetic + Slicable;
|
||||
type Hash: Member + ::rstd::hash::Hash + Copy + MaybeDisplay + Default + SimpleBitOps + Slicable + AsRef<[u8]>;
|
||||
type Hashing: Hashing<Output = Self::Hash>;
|
||||
type Digest: Member + Default;
|
||||
|
||||
fn new(
|
||||
number: Self::Number,
|
||||
extrinsics_root: Self::Hash,
|
||||
state_root: Self::Hash,
|
||||
parent_hash: Self::Hash,
|
||||
digest: Self::Digest
|
||||
) -> Self;
|
||||
|
||||
fn number(&self) -> &Self::Number;
|
||||
fn set_number(&mut self, Self::Number);
|
||||
|
||||
fn extrinsics_root(&self) -> &Self::Hash;
|
||||
fn set_extrinsics_root(&mut self, Self::Hash);
|
||||
|
||||
fn state_root(&self) -> &Self::Hash;
|
||||
fn set_state_root(&mut self, Self::Hash);
|
||||
|
||||
fn parent_hash(&self) -> &Self::Hash;
|
||||
fn set_parent_hash(&mut self, Self::Hash);
|
||||
|
||||
fn digest(&self) -> &Self::Digest;
|
||||
fn set_digest(&mut self, Self::Digest);
|
||||
|
||||
fn hash(&self) -> Self::Hash {
|
||||
<Self::Hashing as Hashing>::hash_of(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Something which fulfills the abstract idea of a Substrate block. It has types for an
|
||||
/// `Extrinsic` piece of information as well as a `Header`.
|
||||
///
|
||||
/// You can get an iterator over each of the `extrinsics` and retrieve the `header`.
|
||||
pub trait Block: Clone + Send + Sync + Slicable + Eq + MaybeSerializeDebug {
|
||||
type Extrinsic: Member + Slicable;
|
||||
type Header: Header<Hash=Self::Hash>;
|
||||
type Hash: Member + ::rstd::hash::Hash + Copy + MaybeDisplay + Default + SimpleBitOps + Slicable + AsRef<[u8]>;
|
||||
|
||||
fn header(&self) -> &Self::Header;
|
||||
fn extrinsics(&self) -> &[Self::Extrinsic];
|
||||
fn deconstruct(self) -> (Self::Header, Vec<Self::Extrinsic>);
|
||||
fn new(header: Self::Header, extrinsics: Vec<Self::Extrinsic>) -> Self;
|
||||
fn hash(&self) -> Self::Hash {
|
||||
<<Self::Header as Header>::Hashing as Hashing>::hash_of(self.header())
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract the hashing type for a block.
|
||||
pub type HashingFor<B> = <<B as Block>::Header as Header>::Hashing;
|
||||
|
||||
/// A "checkable" piece of information, used by the standard Substrate Executive in order to
|
||||
/// check the validity of a piece of extrinsic information, usually by verifying the signature.
|
||||
pub trait Checkable: Sized + Send + Sync {
|
||||
type Checked: Member;
|
||||
fn check(self) -> Result<Self::Checked, Self>;
|
||||
}
|
||||
|
||||
/// An "executable" piece of information, used by the standard Substrate Executive in order to
|
||||
/// enact a piece of extrinsic information by marshalling and dispatching to a named functioon
|
||||
/// call.
|
||||
///
|
||||
/// Also provides information on to whom this information is attributable and an index that allows
|
||||
/// each piece of attributable information to be disambiguated.
|
||||
pub trait Applyable: Sized + Send + Sync {
|
||||
type AccountId: Member + MaybeDisplay;
|
||||
type Index: Member + MaybeDisplay + SimpleArithmetic;
|
||||
fn index(&self) -> &Self::Index;
|
||||
fn sender(&self) -> &Self::AccountId;
|
||||
fn apply(self);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
[dependencies]
|
||||
hex-literal = "0.1.0"
|
||||
serde = { version = "1.0", default_features = false }
|
||||
serde_derive = { version = "1.0", optional = true }
|
||||
safe-mix = { path = "../../../safe-mix", default_features = false}
|
||||
substrate-keyring = { path = "../../keyring", optional = true }
|
||||
substrate-codec = { path = "../../codec", default_features = false }
|
||||
@@ -21,6 +22,7 @@ substrate-runtime-system = { path = "../system", default_features = false }
|
||||
default = ["std"]
|
||||
std = [
|
||||
"serde/std",
|
||||
"serde_derive",
|
||||
"safe-mix/std",
|
||||
"substrate-keyring",
|
||||
"substrate-codec/std",
|
||||
|
||||
@@ -22,6 +22,10 @@
|
||||
#[cfg(feature = "std")]
|
||||
extern crate serde;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
extern crate substrate_keyring as keyring;
|
||||
|
||||
@@ -51,9 +55,13 @@ pub trait Trait: consensus::Trait {
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum Call where aux: T::PublicAux {
|
||||
fn set_key(aux, key: T::SessionKey) -> Result = 0;
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum PrivCall {
|
||||
fn set_length(new: T::BlockNumber) -> Result = 0;
|
||||
fn force_new_session() -> Result = 1;
|
||||
@@ -197,7 +205,7 @@ mod tests {
|
||||
use runtime_io::with_externalities;
|
||||
use substrate_primitives::H256;
|
||||
use primitives::BuildExternalities;
|
||||
use primitives::traits::{HasPublicAux, Identity};
|
||||
use primitives::traits::{HasPublicAux, Identity, BlakeTwo256};
|
||||
use primitives::testing::{Digest, Header};
|
||||
|
||||
pub struct Test;
|
||||
@@ -212,7 +220,7 @@ mod tests {
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
type Hashing = runtime_io::BlakeTwo256;
|
||||
type Hashing = BlakeTwo256;
|
||||
type Digest = Digest;
|
||||
type AccountId = u64;
|
||||
type Header = Header;
|
||||
|
||||
@@ -6,6 +6,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
[dependencies]
|
||||
hex-literal = "0.1.0"
|
||||
serde = { version = "1.0", default_features = false }
|
||||
serde_derive = { version = "1.0", optional = true }
|
||||
safe-mix = { path = "../../../safe-mix", default_features = false}
|
||||
substrate-keyring = { path = "../../keyring", optional = true }
|
||||
substrate-codec = { path = "../../codec", default_features = false }
|
||||
@@ -27,6 +28,7 @@ wabt = "0.1.7"
|
||||
default = ["std"]
|
||||
std = [
|
||||
"serde/std",
|
||||
"serde_derive",
|
||||
"safe-mix/std",
|
||||
"substrate-keyring",
|
||||
"substrate-codec/std",
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
#[cfg(feature = "std")]
|
||||
extern crate serde;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate wabt;
|
||||
|
||||
@@ -48,7 +52,7 @@ use rstd::collections::btree_map::{BTreeMap, Entry};
|
||||
use codec::Slicable;
|
||||
use runtime_support::{StorageValue, StorageMap, Parameter};
|
||||
use runtime_support::dispatch::Result;
|
||||
use primitives::traits::{Zero, One, Bounded, RefInto, SimpleArithmetic, Executable, MakePayment, As};
|
||||
use primitives::traits::{Zero, One, As, Bounded, RefInto, SimpleArithmetic, Executable, MakePayment, Hashing as HashingT};
|
||||
|
||||
#[cfg(test)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
@@ -71,7 +75,7 @@ pub trait ContractAddressFor<AccountId: Sized> {
|
||||
}
|
||||
|
||||
impl<Hashing, AccountId> ContractAddressFor<AccountId> for Hashing where
|
||||
Hashing: runtime_io::Hashing,
|
||||
Hashing: HashingT,
|
||||
AccountId: Sized + Slicable + From<Hashing::Output>,
|
||||
Hashing::Output: Slicable
|
||||
{
|
||||
@@ -90,11 +94,15 @@ pub trait Trait: system::Trait + session::Trait {
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum Call where aux: T::PublicAux {
|
||||
fn transfer(aux, dest: T::AccountId, value: T::Balance) -> Result = 0;
|
||||
fn stake(aux) -> Result = 1;
|
||||
fn unstake(aux) -> Result = 2;
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum PrivCall {
|
||||
fn set_sessions_per_era(new: T::BlockNumber) -> Result = 0;
|
||||
fn set_bonding_duration(new: T::BlockNumber) -> Result = 1;
|
||||
@@ -701,7 +709,6 @@ pub struct GenesisConfig<T: Trait> {
|
||||
#[cfg(any(feature = "std", test))]
|
||||
impl<T: Trait> GenesisConfig<T> where T::AccountId: From<u64> {
|
||||
pub fn simple() -> Self {
|
||||
use primitives::traits::As;
|
||||
GenesisConfig {
|
||||
sessions_per_era: T::BlockNumber::sa(2),
|
||||
current_era: T::BlockNumber::sa(0),
|
||||
@@ -715,7 +722,6 @@ impl<T: Trait> GenesisConfig<T> where T::AccountId: From<u64> {
|
||||
}
|
||||
|
||||
pub fn extended() -> Self {
|
||||
use primitives::traits::As;
|
||||
GenesisConfig {
|
||||
sessions_per_era: T::BlockNumber::sa(3),
|
||||
current_era: T::BlockNumber::sa(1),
|
||||
@@ -740,7 +746,6 @@ impl<T: Trait> GenesisConfig<T> where T::AccountId: From<u64> {
|
||||
#[cfg(any(feature = "std", test))]
|
||||
impl<T: Trait> Default for GenesisConfig<T> {
|
||||
fn default() -> Self {
|
||||
use primitives::traits::As;
|
||||
GenesisConfig {
|
||||
sessions_per_era: T::BlockNumber::sa(1000),
|
||||
current_era: T::BlockNumber::sa(0),
|
||||
@@ -801,7 +806,7 @@ mod tests {
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
type Hashing = runtime_io::BlakeTwo256;
|
||||
type Hashing = ::primitives::traits::BlakeTwo256;
|
||||
type Digest = Digest;
|
||||
type AccountId = u64;
|
||||
type Header = Header;
|
||||
|
||||
@@ -34,8 +34,8 @@ extern crate substrate_runtime_primitives as primitives;
|
||||
extern crate safe_mix;
|
||||
|
||||
use rstd::prelude::*;
|
||||
use runtime_io::Hashing;
|
||||
use primitives::traits::{self, CheckEqual, SimpleArithmetic, SimpleBitOps, Zero, One, Bounded};
|
||||
use primitives::traits::{self, CheckEqual, SimpleArithmetic, SimpleBitOps, Zero, One, Bounded,
|
||||
Hashing, Member, MaybeDisplay};
|
||||
use runtime_support::{StorageValue, StorageMap, Parameter};
|
||||
use safe_mix::TripletMix;
|
||||
|
||||
@@ -59,13 +59,18 @@ pub fn extrinsics_data_root<H: Hashing>(xts: Vec<Vec<u8>>) -> H::Output {
|
||||
}
|
||||
|
||||
pub trait Trait {
|
||||
type Index: Parameter + Default + SimpleArithmetic + Copy;
|
||||
type BlockNumber: Parameter + SimpleArithmetic + Default + Bounded + Copy;
|
||||
type Hash: Parameter + SimpleBitOps + Default + Copy + CheckEqual;
|
||||
type Index: Parameter + Member + Default + MaybeDisplay + SimpleArithmetic + Copy;
|
||||
type BlockNumber: Parameter + Member + MaybeDisplay + SimpleArithmetic + Default + Bounded + Copy + rstd::hash::Hash;
|
||||
type Hash: Parameter + Member + MaybeDisplay + SimpleBitOps + Default + Copy + CheckEqual + rstd::hash::Hash + AsRef<[u8]>;
|
||||
type Hashing: Hashing<Output = Self::Hash>;
|
||||
type Digest: Parameter + Default + traits::Digest;
|
||||
type AccountId: Parameter + Ord + Default;
|
||||
type Header: traits::Header<Number = Self::BlockNumber, Hash = Self::Hash, Digest = Self::Digest>;
|
||||
type Digest: Parameter + Member + Default + traits::Digest;
|
||||
type AccountId: Parameter + Member + MaybeDisplay + Ord + Default;
|
||||
type Header: Parameter + traits::Header<
|
||||
Number = Self::BlockNumber,
|
||||
Hashing = Self::Hashing,
|
||||
Hash = Self::Hash,
|
||||
Digest = Self::Digest
|
||||
>;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
|
||||
@@ -6,6 +6,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
[dependencies]
|
||||
hex-literal = "0.1.0"
|
||||
serde = { version = "1.0", default_features = false }
|
||||
serde_derive = { version = "1.0", optional = true }
|
||||
substrate-runtime-std = { path = "../../runtime-std", default_features = false }
|
||||
substrate-runtime-io = { path = "../../runtime-io", default_features = false }
|
||||
substrate-runtime-support = { path = "../../runtime-support", default_features = false }
|
||||
@@ -25,6 +26,7 @@ std = [
|
||||
"substrate-runtime-support/std",
|
||||
"substrate-runtime-primitives/std",
|
||||
"serde/std",
|
||||
"serde_derive",
|
||||
"substrate-codec/std",
|
||||
"substrate-primitives/std",
|
||||
"substrate-runtime-system/std",
|
||||
|
||||
@@ -27,6 +27,10 @@ extern crate substrate_runtime_support as runtime_support;
|
||||
#[cfg(any(feature = "std", test))]
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate substrate_primitives;
|
||||
extern crate substrate_runtime_primitives as runtime_primitives;
|
||||
@@ -46,6 +50,8 @@ pub trait Trait: HasPublicAux + system::Trait {
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum Call where aux: T::PublicAux {
|
||||
fn set(aux, now: T::Value) -> Result = 0;
|
||||
}
|
||||
@@ -111,7 +117,7 @@ mod tests {
|
||||
use runtime_support::storage::StorageValue;
|
||||
use substrate_primitives::H256;
|
||||
use runtime_primitives::BuildExternalities;
|
||||
use runtime_primitives::traits::{HasPublicAux};
|
||||
use runtime_primitives::traits::{HasPublicAux, BlakeTwo256};
|
||||
use runtime_primitives::testing::{Digest, Header};
|
||||
|
||||
pub struct Test;
|
||||
@@ -122,7 +128,7 @@ mod tests {
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
type Hashing = runtime_io::BlakeTwo256;
|
||||
type Hashing = BlakeTwo256;
|
||||
type Digest = Digest;
|
||||
type AccountId = u64;
|
||||
type Header = Header;
|
||||
|
||||
@@ -6,6 +6,6 @@ description = "Substrate State Machine"
|
||||
|
||||
[dependencies]
|
||||
substrate-primitives = { path = "../primitives", version = "0.1.0" }
|
||||
triehash = "0.1"
|
||||
triehash = "0.1.2"
|
||||
byteorder = "1.1"
|
||||
hex-literal = "0.1.0"
|
||||
|
||||
@@ -18,13 +18,9 @@
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
extern crate substrate_primitives as primitives;
|
||||
|
||||
#[cfg_attr(test, macro_use)]
|
||||
extern crate hex_literal;
|
||||
|
||||
extern crate triehash;
|
||||
|
||||
extern crate byteorder;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -12,3 +12,4 @@ substrate-keyring = { path = "../../substrate/keyring" }
|
||||
substrate-primitives = { path = "../primitives" }
|
||||
substrate-runtime-support = { path = "../runtime-support" }
|
||||
substrate-test-runtime = { path = "../test-runtime" }
|
||||
substrate-runtime-primitives = { path = "../runtime/primitives" }
|
||||
|
||||
@@ -16,12 +16,10 @@
|
||||
|
||||
//! Client extension for tests.
|
||||
|
||||
use codec::Slicable;
|
||||
use client::{self, Client};
|
||||
use keyring::Keyring;
|
||||
use runtime_support::Hashable;
|
||||
use runtime::genesismap::{GenesisConfig, additional_storage_with_genesis};
|
||||
use primitives::block;
|
||||
use runtime;
|
||||
use bft;
|
||||
use {Backend, Executor, NativeExecutor};
|
||||
|
||||
@@ -31,26 +29,26 @@ pub trait TestClient {
|
||||
fn new_for_tests() -> Self;
|
||||
|
||||
/// Justify and import block to the chain.
|
||||
fn justify_and_import(&self, origin: client::BlockOrigin, block: block::Block) -> client::error::Result<()>;
|
||||
fn justify_and_import(&self, origin: client::BlockOrigin, block: runtime::Block) -> client::error::Result<()>;
|
||||
|
||||
/// Returns hash of the genesis block.
|
||||
fn genesis_hash(&self) -> block::HeaderHash;
|
||||
fn genesis_hash(&self) -> runtime::Hash;
|
||||
}
|
||||
|
||||
impl TestClient for Client<Backend, Executor> {
|
||||
impl TestClient for Client<Backend, Executor, runtime::Block> {
|
||||
fn new_for_tests() -> Self {
|
||||
client::new_in_mem(NativeExecutor::new(), GenesisBuilder).unwrap()
|
||||
}
|
||||
|
||||
fn justify_and_import(&self, origin: client::BlockOrigin, block: block::Block) -> client::error::Result<()> {
|
||||
fn justify_and_import(&self, origin: client::BlockOrigin, block: runtime::Block) -> client::error::Result<()> {
|
||||
let justification = fake_justify(&block.header);
|
||||
let justified = self.check_justification(block.header, justification)?;
|
||||
self.import_block(origin, justified, Some(block.transactions))?;
|
||||
self.import_block(origin, justified, Some(block.extrinsics))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn genesis_hash(&self) -> block::HeaderHash {
|
||||
fn genesis_hash(&self) -> runtime::Hash {
|
||||
self.block_hash(0).unwrap().unwrap()
|
||||
}
|
||||
}
|
||||
@@ -61,8 +59,8 @@ impl TestClient for Client<Backend, Executor> {
|
||||
/// headers.
|
||||
/// TODO: remove this in favor of custom verification pipelines for the
|
||||
/// client
|
||||
fn fake_justify(header: &block::Header) -> bft::UncheckedJustification {
|
||||
let hash = header.blake2_256().into();
|
||||
fn fake_justify(header: &runtime::Header) -> bft::UncheckedJustification<runtime::Hash> {
|
||||
let hash = header.hash();
|
||||
let authorities = vec![
|
||||
Keyring::Alice.into(),
|
||||
Keyring::Bob.into(),
|
||||
@@ -72,7 +70,7 @@ fn fake_justify(header: &block::Header) -> bft::UncheckedJustification {
|
||||
bft::UncheckedJustification {
|
||||
digest: hash,
|
||||
signatures: authorities.iter().map(|key| {
|
||||
let msg = bft::sign_message(
|
||||
let msg = bft::sign_message::<runtime::Block>(
|
||||
bft::generic::Vote::Commit(1, hash).into(),
|
||||
key,
|
||||
header.parent_hash
|
||||
@@ -97,15 +95,14 @@ fn genesis_config() -> GenesisConfig {
|
||||
|
||||
struct GenesisBuilder;
|
||||
|
||||
impl client::GenesisBuilder for GenesisBuilder {
|
||||
fn build(self) -> (block::Header, Vec<(Vec<u8>, Vec<u8>)>) {
|
||||
impl client::GenesisBuilder<runtime::Block> for GenesisBuilder {
|
||||
fn build(self) -> (runtime::Header, Vec<(Vec<u8>, Vec<u8>)>) {
|
||||
let mut storage = genesis_config().genesis_map();
|
||||
let block = client::genesis::construct_genesis_block(&storage);
|
||||
let block: runtime::Block = client::genesis::construct_genesis_block(&storage);
|
||||
storage.extend(additional_storage_with_genesis(&block));
|
||||
|
||||
(
|
||||
block::Header::decode(&mut block.header.encode().as_ref())
|
||||
.expect("to_vec() always gives a valid serialisation; qed"),
|
||||
block.header,
|
||||
storage.into_iter().collect()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ extern crate substrate_codec as codec;
|
||||
extern crate substrate_keyring as keyring;
|
||||
extern crate substrate_primitives as primitives;
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
extern crate substrate_runtime_primitives as runtime_primitives;
|
||||
#[macro_use] extern crate substrate_executor as executor;
|
||||
|
||||
pub extern crate substrate_test_runtime as runtime;
|
||||
@@ -43,12 +44,12 @@ mod native_executor {
|
||||
pub use self::native_executor::NativeExecutor;
|
||||
|
||||
/// Test client database backend.
|
||||
pub type Backend = client::in_mem::Backend;
|
||||
pub type Backend = client::in_mem::Backend<runtime::Block>;
|
||||
|
||||
/// Test client executor.
|
||||
pub type Executor = client::LocalCallExecutor<Backend, executor::NativeExecutor<NativeExecutor>>;
|
||||
|
||||
/// Creates new client instance used for tests.
|
||||
pub fn new() -> client::Client<Backend, Executor> {
|
||||
pub fn new() -> client::Client<Backend, Executor, runtime::Block> {
|
||||
TestClient::new_for_tests()
|
||||
}
|
||||
|
||||
@@ -7,12 +7,15 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
log = { version = "0.3", optional = true }
|
||||
hex-literal = { version = "0.1.0", optional = true }
|
||||
ed25519 = { path = "../ed25519", optional = true }
|
||||
serde = { version = "1.0", optional = true }
|
||||
serde_derive = { version = "1.0", optional = true }
|
||||
substrate-keyring = { path = "../keyring", optional = true }
|
||||
substrate-codec = { path = "../codec", default-features = false }
|
||||
substrate-runtime-std = { path = "../runtime-std", default-features = false }
|
||||
substrate-runtime-io = { path = "../runtime-io", default-features = false }
|
||||
substrate-runtime-support = { path = "../runtime-support", default-features = false }
|
||||
substrate-primitives = { path = "../primitives", default-features = false }
|
||||
substrate-runtime-primitives = { path = "../runtime/primitives", default-features = false }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
@@ -20,10 +23,13 @@ std = [
|
||||
"log",
|
||||
"hex-literal",
|
||||
"ed25519",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"substrate-keyring",
|
||||
"substrate-codec/std",
|
||||
"substrate-runtime-std/std",
|
||||
"substrate-runtime-io/std",
|
||||
"substrate-runtime-support/std",
|
||||
"substrate-primitives/std",
|
||||
"substrate-runtime-primitives/std"
|
||||
]
|
||||
|
||||
@@ -18,10 +18,9 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
use runtime_io::twox_128;
|
||||
use runtime_support::Hashable;
|
||||
use codec::{KeyedVec, Joiner};
|
||||
use primitives::AuthorityId;
|
||||
use primitives::block::Block;
|
||||
use runtime_primitives::traits::Block;
|
||||
|
||||
/// Configuration of a general Substrate test genesis block.
|
||||
pub struct GenesisConfig {
|
||||
@@ -60,12 +59,8 @@ macro_rules! map {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn additional_storage_with_genesis(genesis_block: &Block) -> HashMap<Vec<u8>, Vec<u8>> {
|
||||
use codec::Slicable;
|
||||
use primitives::hexdisplay::HexDisplay;
|
||||
println!("genesis hash {}", HexDisplay::from(&genesis_block.header.blake2_256()));
|
||||
println!("genesis {}", HexDisplay::from(&genesis_block.header.encode()));
|
||||
pub fn additional_storage_with_genesis(genesis_block: &::Block) -> HashMap<Vec<u8>, Vec<u8>> {
|
||||
map![
|
||||
twox_128(&b"latest"[..]).to_vec() => genesis_block.header.blake2_256().to_vec()
|
||||
twox_128(&b"latest"[..]).to_vec() => genesis_block.hash().0.to_vec()
|
||||
]
|
||||
}
|
||||
|
||||
@@ -19,38 +19,114 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
extern crate substrate_runtime_std as rstd;
|
||||
#[macro_use] extern crate substrate_runtime_io as runtime_io;
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
extern crate substrate_codec as codec;
|
||||
#[cfg(test)] #[macro_use] extern crate hex_literal;
|
||||
#[cfg(test)] extern crate ed25519;
|
||||
#[cfg(test)] extern crate substrate_keyring as keyring;
|
||||
#[cfg_attr(test, macro_use)] extern crate substrate_primitives as primitives;
|
||||
extern crate substrate_runtime_primitives as runtime_primitives;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
extern crate serde;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
#[macro_use]
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate hex_literal;
|
||||
#[cfg(test)]
|
||||
extern crate ed25519;
|
||||
#[cfg(test)]
|
||||
extern crate substrate_keyring as keyring;
|
||||
#[cfg_attr(test, macro_use)]
|
||||
extern crate substrate_primitives as primitives;
|
||||
#[macro_use]
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
|
||||
|
||||
#[cfg(feature = "std")] pub mod genesismap;
|
||||
pub mod system;
|
||||
mod transaction;
|
||||
mod unchecked_transaction;
|
||||
|
||||
use rstd::prelude::*;
|
||||
use codec::Slicable;
|
||||
|
||||
use primitives::AuthorityId;
|
||||
use primitives::hash::H512;
|
||||
use primitives::block::generic;
|
||||
use runtime_primitives::traits::{Checkable, BlakeTwo256};
|
||||
use runtime_primitives::Ed25519Signature;
|
||||
pub use primitives::hash::H256;
|
||||
pub use primitives::block::{Header, Number as BlockNumber, Digest};
|
||||
pub use transaction::Transaction;
|
||||
pub use unchecked_transaction::UncheckedTransaction;
|
||||
|
||||
/// A test block.
|
||||
pub type Block = generic::Block<UncheckedTransaction>;
|
||||
/// Calls in transactions.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
pub struct Transfer {
|
||||
pub from: AccountId,
|
||||
pub to: AccountId,
|
||||
pub amount: u64,
|
||||
pub nonce: u64,
|
||||
}
|
||||
|
||||
impl Slicable for Transfer {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
self.from.using_encoded(|s| v.extend(s));
|
||||
self.to.using_encoded(|s| v.extend(s));
|
||||
self.amount.using_encoded(|s| v.extend(s));
|
||||
self.nonce.using_encoded(|s| v.extend(s));
|
||||
v
|
||||
}
|
||||
|
||||
fn decode<I: ::codec::Input>(input: &mut I) -> Option<Self> {
|
||||
Slicable::decode(input).map(|(from, to, amount, nonce)| Transfer { from, to, amount, nonce })
|
||||
}
|
||||
}
|
||||
|
||||
/// Extrinsic for test-runtime.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
pub struct Extrinsic {
|
||||
pub transfer: Transfer,
|
||||
pub signature: Ed25519Signature,
|
||||
}
|
||||
|
||||
impl Slicable for Extrinsic {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
self.transfer.using_encoded(|s| v.extend(s));
|
||||
self.signature.using_encoded(|s| v.extend(s));
|
||||
v
|
||||
}
|
||||
|
||||
fn decode<I: ::codec::Input>(input: &mut I) -> Option<Self> {
|
||||
Slicable::decode(input).map(|(transfer, signature)| Extrinsic { transfer, signature })
|
||||
}
|
||||
}
|
||||
|
||||
impl Checkable for Extrinsic {
|
||||
type Checked = Self;
|
||||
|
||||
fn check(self) -> Result<Self, Self> {
|
||||
if ::runtime_primitives::verify_encoded_lazy(&self.signature, &self.transfer, &self.transfer.from) {
|
||||
Ok(self)
|
||||
} else {
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An identifier for an account on this system.
|
||||
pub type AccountId = AuthorityId;
|
||||
/// Signature for our transactions.
|
||||
pub type Signature = H512;
|
||||
pub type AccountId = H256;
|
||||
/// A simple hash type for all our hashing.
|
||||
pub type Hash = H256;
|
||||
/// The block number type used in this runtime.
|
||||
pub type BlockNumber = u64;
|
||||
/// Index of a transaction.
|
||||
pub type Index = u64;
|
||||
/// The digest of a block.
|
||||
pub type Digest = runtime_primitives::generic::Digest<Vec<u8>>;
|
||||
/// A test block.
|
||||
pub type Block = runtime_primitives::generic::Block<Header, Extrinsic>;
|
||||
/// A test block's header.
|
||||
pub type Header = runtime_primitives::generic::Header<BlockNumber, BlakeTwo256, Vec<u8>>;
|
||||
|
||||
/// Run whatever tests we have.
|
||||
pub fn run_tests(mut input: &[u8]) -> Vec<u8> {
|
||||
@@ -59,7 +135,7 @@ pub fn run_tests(mut input: &[u8]) -> Vec<u8> {
|
||||
print("run_tests...");
|
||||
let block = Block::decode(&mut input).unwrap();
|
||||
print("deserialised block.");
|
||||
let stxs = block.transactions.iter().map(Slicable::encode).collect::<Vec<_>>();
|
||||
let stxs = block.extrinsics.iter().map(Slicable::encode).collect::<Vec<_>>();
|
||||
print("reserialised transactions.");
|
||||
[stxs.len() as u8].encode()
|
||||
}
|
||||
@@ -69,8 +145,9 @@ pub mod api {
|
||||
|
||||
impl_stubs!(
|
||||
authorities => |()| system::authorities(),
|
||||
initialise_block => |header| system::initialise_block(header),
|
||||
execute_block => |block| system::execute_block(block),
|
||||
execute_transaction => |(header, utx)| system::execute_transaction(utx, header),
|
||||
finalise_block => |header| system::finalise_block(header)
|
||||
apply_extrinsic => |utx| system::execute_transaction(utx),
|
||||
finalise_block => |()| system::finalise_block()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,20 +18,23 @@
|
||||
//! and depositing logs.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use primitives::AuthorityId;
|
||||
use runtime_io::{storage_root, enumerated_trie_root, ed25519_verify};
|
||||
use runtime_support::{Hashable, storage};
|
||||
use runtime_io::{storage_root, enumerated_trie_root};
|
||||
use runtime_support::storage::{self, StorageValue, StorageMap};
|
||||
use runtime_primitives::traits::{Hashing, BlakeTwo256};
|
||||
use codec::{KeyedVec, Slicable};
|
||||
use super::{AccountId, UncheckedTransaction, H256 as Hash, Block, Header};
|
||||
use super::{AccountId, BlockNumber, Extrinsic, H256 as Hash, Block, Header};
|
||||
|
||||
const NONCE_OF: &[u8] = b"nonce:";
|
||||
const BALANCE_OF: &[u8] = b"balance:";
|
||||
const LATEST_BLOCK_HASH: &[u8] = b"latest";
|
||||
const AUTHORITY_AT: &'static[u8] = b":auth:";
|
||||
const AUTHORITY_COUNT: &'static[u8] = b":auth:len";
|
||||
|
||||
pub fn latest_block_hash() -> Hash {
|
||||
storage::get(LATEST_BLOCK_HASH).expect("There must always be a latest block")
|
||||
storage_items! {
|
||||
ExtrinsicIndex: b"sys:xti" => required u32;
|
||||
ExtrinsicData: b"sys:xtd" => required map [ u32 => Vec<u8> ];
|
||||
// The current block number being processed. Set by `execute_block`.
|
||||
Number: b"sys:num" => required BlockNumber;
|
||||
ParentHash: b"sys:pha" => required Hash;
|
||||
}
|
||||
|
||||
pub fn balance_of(who: AccountId) -> u64 {
|
||||
@@ -43,62 +46,79 @@ pub fn nonce_of(who: AccountId) -> u64 {
|
||||
}
|
||||
|
||||
/// Get authorities ar given block.
|
||||
pub fn authorities() -> Vec<AuthorityId> {
|
||||
pub fn authorities() -> Vec<::primitives::AuthorityId> {
|
||||
let len: u32 = storage::unhashed::get(AUTHORITY_COUNT).expect("There are always authorities in test-runtime");
|
||||
(0..len)
|
||||
.map(|i| storage::unhashed::get(&i.to_keyed_vec(AUTHORITY_AT)).expect("Authority is properly encoded in test-runtime"))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn initialise_block(header: Header) {
|
||||
// populate environment.
|
||||
<Number>::put(&header.number);
|
||||
<ParentHash>::put(&header.parent_hash);
|
||||
<ExtrinsicIndex>::put(0);
|
||||
}
|
||||
|
||||
/// Actually execute all transitioning for `block`.
|
||||
pub fn execute_block(block: Block) {
|
||||
let ref header = block.header;
|
||||
|
||||
// check parent_hash is correct.
|
||||
assert!(
|
||||
header.number > 0 && latest_block_hash() == header.parent_hash,
|
||||
"Parent hash should be valid."
|
||||
);
|
||||
|
||||
// check transaction trie root represents the transactions.
|
||||
let txs = block.transactions.iter().map(Slicable::encode).collect::<Vec<_>>();
|
||||
let txs = block.extrinsics.iter().map(Slicable::encode).collect::<Vec<_>>();
|
||||
let txs = txs.iter().map(Vec::as_slice).collect::<Vec<_>>();
|
||||
let txs_root = enumerated_trie_root(&txs).into();
|
||||
info_expect_equal_hash(&header.extrinsics_root, &txs_root);
|
||||
assert!(header.extrinsics_root == txs_root, "Transaction trie root must be valid.");
|
||||
|
||||
// execute transactions
|
||||
block.transactions.iter().for_each(execute_transaction_backend);
|
||||
block.extrinsics.iter().for_each(execute_transaction_backend);
|
||||
|
||||
// check storage root.
|
||||
let storage_root = storage_root().into();
|
||||
info_expect_equal_hash(&header.state_root, &storage_root);
|
||||
assert!(header.state_root == storage_root, "Storage root must match that calculated.");
|
||||
|
||||
// put the header hash into storage.
|
||||
storage::put(LATEST_BLOCK_HASH, &header.blake2_256());
|
||||
}
|
||||
|
||||
/// Execute a transaction outside of the block execution function.
|
||||
/// This doesn't attempt to validate anything regarding the block.
|
||||
pub fn execute_transaction(utx: UncheckedTransaction, header: Header) -> Header {
|
||||
pub fn execute_transaction(utx: Extrinsic) {
|
||||
let extrinsic_index = ExtrinsicIndex::get();
|
||||
ExtrinsicData::insert(extrinsic_index, utx.encode());
|
||||
ExtrinsicIndex::put(extrinsic_index + 1);
|
||||
execute_transaction_backend(&utx);
|
||||
header
|
||||
}
|
||||
|
||||
/// Finalise the block - it is up the caller to ensure that all header fields are valid
|
||||
/// except state-root.
|
||||
pub fn finalise_block(mut header: Header) -> Header {
|
||||
header.state_root = storage_root().into();
|
||||
header
|
||||
/// Finalise the block.
|
||||
pub fn finalise_block() -> Header {
|
||||
let extrinsic_index = ExtrinsicIndex::take();
|
||||
let txs: Vec<_> = (0..extrinsic_index).map(ExtrinsicData::take).collect();
|
||||
let txs = txs.iter().map(Vec::as_slice).collect::<Vec<_>>();
|
||||
let extrinsics_root = enumerated_trie_root(&txs).into();
|
||||
|
||||
let number = <Number>::take();
|
||||
let parent_hash = <ParentHash>::take();
|
||||
let storage_root = BlakeTwo256::storage_root();
|
||||
|
||||
Header {
|
||||
number,
|
||||
extrinsics_root,
|
||||
state_root: storage_root,
|
||||
parent_hash,
|
||||
digest: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_transaction_backend(utx: &UncheckedTransaction) {
|
||||
fn execute_transaction_backend(utx: &Extrinsic) {
|
||||
use runtime_primitives::traits::Checkable;
|
||||
|
||||
// check signature
|
||||
let ref tx = utx.tx;
|
||||
let msg = ::codec::Slicable::encode(tx);
|
||||
assert!(ed25519_verify(&utx.signature.0, &msg, &tx.from),
|
||||
"All transactions should be properly signed");
|
||||
let utx = match utx.clone().check() {
|
||||
Ok(tx) => tx,
|
||||
Err(_) => panic!("All transactions should be properly signed"),
|
||||
};
|
||||
|
||||
let tx: ::Transfer = utx.transfer;
|
||||
|
||||
// check nonce
|
||||
let nonce_key = tx.from.to_keyed_vec(NONCE_OF);
|
||||
@@ -111,9 +131,9 @@ fn execute_transaction_backend(utx: &UncheckedTransaction) {
|
||||
// check sender balance
|
||||
let from_balance_key = tx.from.to_keyed_vec(BALANCE_OF);
|
||||
let from_balance: u64 = storage::get_or(&from_balance_key, 0);
|
||||
assert!(tx.amount <= from_balance, "All transactions should transfer at most the sender balance");
|
||||
|
||||
// enact transfer
|
||||
assert!(tx.amount <= from_balance, "All transactions should transfer at most the sender balance");
|
||||
let to_balance_key = tx.to.to_keyed_vec(BALANCE_OF);
|
||||
let to_balance: u64 = storage::get_or(&to_balance_key, 0);
|
||||
storage::put(&from_balance_key, &(from_balance - tx.amount));
|
||||
@@ -144,7 +164,7 @@ mod tests {
|
||||
use runtime_io::{with_externalities, twox_128, TestExternalities};
|
||||
use codec::{Joiner, KeyedVec};
|
||||
use keyring::Keyring;
|
||||
use ::{Header, Digest, Transaction, UncheckedTransaction};
|
||||
use ::{Header, Digest, Extrinsic, Transfer};
|
||||
|
||||
fn new_test_ext() -> TestExternalities {
|
||||
map![
|
||||
@@ -157,9 +177,9 @@ mod tests {
|
||||
]
|
||||
}
|
||||
|
||||
fn construct_signed_tx(tx: Transaction) -> UncheckedTransaction {
|
||||
let signature = Keyring::from_raw_public(tx.from).unwrap().sign(&tx.encode());
|
||||
UncheckedTransaction { tx, signature }
|
||||
fn construct_signed_tx(tx: Transfer) -> Extrinsic {
|
||||
let signature = Keyring::from_raw_public(tx.from.0).unwrap().sign(&tx.encode()).into();
|
||||
Extrinsic { transfer: tx, signature }
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -176,7 +196,7 @@ mod tests {
|
||||
|
||||
let b = Block {
|
||||
header: h,
|
||||
transactions: vec![],
|
||||
extrinsics: vec![],
|
||||
};
|
||||
|
||||
with_externalities(&mut t, || {
|
||||
@@ -189,8 +209,8 @@ mod tests {
|
||||
let mut t = new_test_ext();
|
||||
|
||||
with_externalities(&mut t, || {
|
||||
assert_eq!(balance_of(Keyring::Alice.to_raw_public()), 111);
|
||||
assert_eq!(balance_of(Keyring::Bob.to_raw_public()), 0);
|
||||
assert_eq!(balance_of(Keyring::Alice.to_raw_public().into()), 111);
|
||||
assert_eq!(balance_of(Keyring::Bob.to_raw_public().into()), 0);
|
||||
});
|
||||
|
||||
let b = Block {
|
||||
@@ -198,13 +218,13 @@ mod tests {
|
||||
parent_hash: [69u8; 32].into(),
|
||||
number: 1,
|
||||
state_root: hex!("0dd8210adaf581464cc68555814a787ed491f8c608d0a0dbbf2208a6d44190b1").into(),
|
||||
extrinsics_root: hex!("5e44188712452f900acfa1b4bf4084753122ea1856d58187dd33374a2ca653b1").into(),
|
||||
extrinsics_root: hex!("951508f2cc0071500a74765ab0fb2f280fdcdd329d5f989dda675010adee99d6").into(),
|
||||
digest: Digest { logs: vec![], },
|
||||
},
|
||||
transactions: vec![
|
||||
construct_signed_tx(Transaction {
|
||||
from: Keyring::Alice.to_raw_public(),
|
||||
to: Keyring::Bob.to_raw_public(),
|
||||
extrinsics: vec![
|
||||
construct_signed_tx(Transfer {
|
||||
from: Keyring::Alice.to_raw_public().into(),
|
||||
to: Keyring::Bob.to_raw_public().into(),
|
||||
amount: 69,
|
||||
nonce: 0,
|
||||
})
|
||||
@@ -214,40 +234,40 @@ mod tests {
|
||||
with_externalities(&mut t, || {
|
||||
execute_block(b.clone());
|
||||
|
||||
assert_eq!(balance_of(Keyring::Alice.to_raw_public()), 42);
|
||||
assert_eq!(balance_of(Keyring::Bob.to_raw_public()), 69);
|
||||
assert_eq!(balance_of(Keyring::Alice.to_raw_public().into()), 42);
|
||||
assert_eq!(balance_of(Keyring::Bob.to_raw_public().into()), 69);
|
||||
});
|
||||
|
||||
let b = Block {
|
||||
header: Header {
|
||||
parent_hash: b.header.blake2_256().into(),
|
||||
parent_hash: b.header.hash(),
|
||||
number: 2,
|
||||
state_root: hex!("aea7c370a9fa4075b703742c22cc4fb12759bdd7d5aa5cdd85895447f838b81b").into(),
|
||||
extrinsics_root: hex!("9ac45fbcc93fa6a8b5a3c44f04d936d53569c72a53fbc12eb58bf884f6dbfae5").into(),
|
||||
state_root: hex!("c93f2fd494c386fa32ee76b6198a7ccf5db12c02c3a79755fd2d4646ec2bf8d7").into(),
|
||||
extrinsics_root: hex!("3563642676d7e042c894eedc579ba2d6eeedf9a6c66d9d557599effc9f674372").into(),
|
||||
digest: Digest { logs: vec![], },
|
||||
},
|
||||
transactions: vec![
|
||||
construct_signed_tx(Transaction {
|
||||
from: Keyring::Bob.to_raw_public(),
|
||||
to: Keyring::Alice.to_raw_public(),
|
||||
extrinsics: vec![
|
||||
construct_signed_tx(Transfer {
|
||||
from: Keyring::Bob.to_raw_public().into(),
|
||||
to: Keyring::Alice.to_raw_public().into(),
|
||||
amount: 27,
|
||||
nonce: 0,
|
||||
}),
|
||||
construct_signed_tx(Transaction {
|
||||
from: Keyring::Alice.to_raw_public(),
|
||||
to: Keyring::Charlie.to_raw_public(),
|
||||
construct_signed_tx(Transfer {
|
||||
from: Keyring::Alice.to_raw_public().into(),
|
||||
to: Keyring::Charlie.to_raw_public().into(),
|
||||
amount: 69,
|
||||
nonce: 1,
|
||||
})
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
||||
with_externalities(&mut t, || {
|
||||
execute_block(b);
|
||||
|
||||
assert_eq!(balance_of(Keyring::Alice.to_raw_public()), 0);
|
||||
assert_eq!(balance_of(Keyring::Bob.to_raw_public()), 42);
|
||||
assert_eq!(balance_of(Keyring::Charlie.to_raw_public()), 69);
|
||||
assert_eq!(balance_of(Keyring::Alice.to_raw_public().into()), 0);
|
||||
assert_eq!(balance_of(Keyring::Bob.to_raw_public().into()), 42);
|
||||
assert_eq!(balance_of(Keyring::Charlie.to_raw_public().into()), 69);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! A toy transaction.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use codec::{Input, Slicable, Joiner};
|
||||
use super::AccountId;
|
||||
|
||||
/// An instruction to do something.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
pub struct Transaction {
|
||||
/// Who is sending.
|
||||
pub from: AccountId,
|
||||
/// Who to send to.
|
||||
pub to: AccountId,
|
||||
/// How much to send.
|
||||
pub amount: u64,
|
||||
/// How many transactions `self.from` already sent.
|
||||
pub nonce: u64,
|
||||
}
|
||||
|
||||
impl Slicable for Transaction {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(Transaction {
|
||||
from: Slicable::decode(input)?,
|
||||
to: Slicable::decode(input)?,
|
||||
amount: Slicable::decode(input)?,
|
||||
nonce: Slicable::decode(input)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
Vec::new()
|
||||
.and(&self.from)
|
||||
.and(&self.to)
|
||||
.and(&self.amount)
|
||||
.and(&self.nonce)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use keyring::Keyring;
|
||||
|
||||
#[test]
|
||||
fn test_tx_encoding() {
|
||||
let tx = Transaction {
|
||||
from: Keyring::Alice.to_raw_public(),
|
||||
to: Keyring::Bob.to_raw_public(),
|
||||
amount: 69,
|
||||
nonce: 33,
|
||||
};
|
||||
|
||||
assert_eq!(tx.encode(), vec![
|
||||
// from
|
||||
209, 114, 167, 76, 218, 76, 134, 89, 18, 195, 43, 160, 168, 10, 87, 174, 105, 171, 174, 65, 14, 92, 203, 89, 222, 232, 78, 47, 68, 50, 219, 79,
|
||||
// to
|
||||
215, 86, 142, 95, 10, 126, 218, 103, 168, 38, 145, 255, 55, 154, 196, 187, 164, 249, 201, 184, 89, 254, 119, 155, 93, 70, 54, 59, 97, 173, 45, 185,
|
||||
// amount
|
||||
69, 0, 0, 0, 0, 0, 0, 0,
|
||||
// nonce
|
||||
33, 0, 0, 0, 0, 0, 0, 0
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! A toy unchecked transaction complete with signature.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use codec::{Input, Slicable};
|
||||
use super::{Signature, Transaction};
|
||||
|
||||
/// A transactions right from the external world. Unchecked.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
pub struct UncheckedTransaction {
|
||||
/// The actual transaction information.
|
||||
pub tx: Transaction,
|
||||
/// The signature; should be an Ed25519 signature applied to the serialised `transaction` field.
|
||||
pub signature: Signature,
|
||||
}
|
||||
|
||||
impl Slicable for UncheckedTransaction {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
// This is a little more complicated than usua since the binary format must be compatible
|
||||
// with substrate's generic `Vec<u8>` type. Basically this just means accepting that there
|
||||
// will be a prefix of u32, which has the total number of bytes following (we don't need
|
||||
// to use this).
|
||||
let _length_do_not_remove_me_see_above: u32 = Slicable::decode(input)?;
|
||||
Some(UncheckedTransaction {
|
||||
tx: Slicable::decode(input)?,
|
||||
signature: Slicable::decode(input)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
|
||||
// need to prefix with the total length as u32 to ensure it's binary comptible with
|
||||
// Vec<u8>. we'll make room for it here, then overwrite once we know the length.
|
||||
v.extend(&[0u8; 4]);
|
||||
|
||||
self.tx.using_encoded(|s| v.extend(s));
|
||||
self.signature.using_encoded(|s| v.extend(s));
|
||||
|
||||
let length = (v.len() - 4) as u32;
|
||||
length.using_encoded(|s| v[0..4].copy_from_slice(s));
|
||||
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use keyring::Keyring;
|
||||
use ::Transaction;
|
||||
|
||||
#[test]
|
||||
fn test_unchecked_encoding() {
|
||||
let tx = Transaction {
|
||||
from: Keyring::Alice.to_raw_public(),
|
||||
to: Keyring::Bob.to_raw_public(),
|
||||
amount: 69,
|
||||
nonce: 34,
|
||||
};
|
||||
let signature = Keyring::from_raw_public(tx.from).unwrap().sign(&tx.encode());
|
||||
let signed = UncheckedTransaction { tx, signature };
|
||||
|
||||
assert_eq!(signed.encode(), vec![
|
||||
// length
|
||||
144, 0, 0, 0,
|
||||
// from
|
||||
209, 114, 167, 76, 218, 76, 134, 89, 18, 195, 43, 160, 168, 10, 87, 174, 105, 171, 174, 65, 14, 92, 203, 89, 222, 232, 78, 47, 68, 50, 219, 79,
|
||||
// to
|
||||
215, 86, 142, 95, 10, 126, 218, 103, 168, 38, 145, 255, 55, 154, 196, 187, 164, 249, 201, 184, 89, 254, 119, 155, 93, 70, 54, 59, 97, 173, 45, 185,
|
||||
// amount
|
||||
69, 0, 0, 0, 0, 0, 0, 0,
|
||||
// nonce
|
||||
34, 0, 0, 0, 0, 0, 0, 0,
|
||||
// signature
|
||||
207, 69, 156, 55, 7, 227, 202, 3, 114, 111, 43, 46, 227, 38, 39, 122, 245, 69, 195, 117, 190, 154, 89, 76, 134, 91, 251, 230, 31, 221, 1, 194, 144, 34, 33, 58, 220, 154, 205, 135, 224, 52, 248, 198, 12, 17, 96, 53, 110, 160, 194, 10, 9, 60, 40, 133, 57, 112, 151, 200, 105, 198, 245, 10
|
||||
]);
|
||||
}
|
||||
}
|
||||
+246
-164
@@ -11,19 +11,9 @@ name = "base58"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "bigint"
|
||||
version = "4.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.0.1"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@@ -37,33 +27,55 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.2.1"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.4"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "coco"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.1.6"
|
||||
@@ -75,20 +87,15 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-primitives 0.1.0",
|
||||
"untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "elastic-array"
|
||||
version = "0.9.0"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -99,15 +106,37 @@ name = "environmental"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "ethcore-bigint"
|
||||
name = "ethbloom"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethereum-types-serialize 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fixed-hash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ethereum-types"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethbloom 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethereum-types-serialize 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fixed-hash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"uint 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ethereum-types-serialize"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bigint 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"plain_hasher 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -119,12 +148,23 @@ dependencies = [
|
||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixed-hash"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-zircon"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -148,29 +188,34 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hex-literal"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"hex-literal-impl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex-literal-impl 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex-literal-impl"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keccak-hash"
|
||||
name = "integer-sqrt"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/paritytech/integer-sqrt-rs.git#886e9cb983c46498003878afe965d55caa762025"
|
||||
|
||||
[[package]]
|
||||
name = "keccak-hash"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-bigint 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tiny-keccak 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -178,9 +223,14 @@ name = "lazy_static"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.36"
|
||||
version = "0.2.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@@ -196,9 +246,14 @@ name = "log"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "memory_units"
|
||||
version = "0.3.0"
|
||||
@@ -209,12 +264,17 @@ name = "nodrop"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -230,39 +290,31 @@ name = "parity-wasm"
|
||||
version = "0.27.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.5.4"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot_core 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.2.10"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "plain_hasher"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.4.0"
|
||||
@@ -276,12 +328,20 @@ name = "proc-macro-hack-impl"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pwasm-alloc"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"pwasm-libc 0.1.0",
|
||||
"rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -290,8 +350,11 @@ version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "0.3.15"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
@@ -299,7 +362,7 @@ version = "0.3.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -309,7 +372,7 @@ version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -318,19 +381,19 @@ name = "rayon"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rayon-core 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.3.0"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"coco 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -340,20 +403,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rlp"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"elastic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-bigint 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -369,10 +431,10 @@ source = "git+https://github.com/rphmeier/rustc-hex.git#ee2ec40b9062ac7769ccb9dc
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -382,7 +444,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.6.0"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -395,31 +457,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.27"
|
||||
version = "1.0.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.27"
|
||||
version = "1.0.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive_internals"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "0.6.0"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@@ -436,17 +489,17 @@ name = "substrate-primitives"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.2.3 (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.1.3 (git+https://github.com/rphmeier/primitives.git?branch=compile-for-wasm)",
|
||||
"rustc-hex 2.0.0 (git+https://github.com/rphmeier/rustc-hex.git)",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"uint 0.1.2 (git+https://github.com/rphmeier/primitives.git?branch=compile-for-wasm)",
|
||||
"wasmi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasmi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -455,12 +508,27 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"ed25519 0.1.0",
|
||||
"environmental 0.1.0",
|
||||
"rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-state-machine 0.1.0",
|
||||
"triehash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"triehash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-primitives"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)",
|
||||
"num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -469,7 +537,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"pwasm-alloc 0.1.0",
|
||||
"pwasm-libc 0.1.0",
|
||||
"rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -477,9 +545,9 @@ name = "substrate-runtime-support"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ed25519 0.1.0",
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
@@ -490,10 +558,10 @@ dependencies = [
|
||||
name = "substrate-state-machine"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-primitives 0.1.0",
|
||||
"triehash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"triehash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -501,46 +569,43 @@ name = "substrate-test-runtime"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ed25519 0.1.0",
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.11.11"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synom"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-keccak"
|
||||
version = "1.4.0"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "triehash"
|
||||
version = "0.1.0"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"ethcore-bigint 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"keccak-hash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rlp 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"keccak-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rlp 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -556,14 +621,25 @@ name = "uint"
|
||||
version = "0.1.2"
|
||||
source = "git+https://github.com/rphmeier/primitives.git?branch=compile-for-wasm#8dc457899afdaf968ff7f16140b03d1e37b01d71"
|
||||
dependencies = [
|
||||
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uint"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.0.4"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@@ -573,10 +649,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "wasmi"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-wasm 0.27.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -603,67 +679,73 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
[metadata]
|
||||
"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef"
|
||||
"checksum base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83"
|
||||
"checksum bigint 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5442186ef6560f30f1ee4b9c1e4c87a35a6879d3644550cc248ec2b955eb5fcd"
|
||||
"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
|
||||
"checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789"
|
||||
"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400"
|
||||
"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23"
|
||||
"checksum cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "deaf9ec656256bb25b404c51ef50097207b9cbb29c933d31f92cae5a8a0ffee0"
|
||||
"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
|
||||
"checksum coco 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c06169f5beb7e31c7c67ebf5540b8b472d23e3eade3b2ec7d1f5b504a85f91bd"
|
||||
"checksum byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "74c0b906e9446b0a2e4f760cdb3fa4b2c48cdc6db8766a845c54b6ff063fd2e9"
|
||||
"checksum cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "49ec142f5768efb5b7622aebc3fdbdbb8950a4b9ba996393cb76ef7466e8747d"
|
||||
"checksum cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "405216fd8fe65f718daa7102ea808a946b6ce40c742998fbfd3463645552de18"
|
||||
"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e"
|
||||
"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3"
|
||||
"checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150"
|
||||
"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9"
|
||||
"checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda"
|
||||
"checksum either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "740178ddf48b1a9e878e6d6509a1442a2d42fd2928aae8e7a6f8a36fb01981b3"
|
||||
"checksum elastic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "258ff6a9a94f648d0379dbd79110e057edbb53eb85cc237e33eadf8e5a30df85"
|
||||
"checksum ethcore-bigint 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcb5af77e74a8f70e9c3337e069c37bc82178ef1b459c02091f73c4ad5281eb5"
|
||||
"checksum elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "88d4851b005ef16de812ea9acdb7bece2f0a40dd86c07b85631d7dafa54537bb"
|
||||
"checksum ethbloom 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a93a43ce2e9f09071449da36bfa7a1b20b950ee344b6904ff23de493b03b386"
|
||||
"checksum ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c48729b8aea8aedb12cf4cb2e5cef439fdfe2dda4a89e47eeebd15778ef53b6"
|
||||
"checksum ethereum-types-serialize 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ac59a21a9ce98e188f3dace9eb67a6c4a3c67ec7fbc7218cb827852679dc002"
|
||||
"checksum fixed-hash 0.1.3 (git+https://github.com/rphmeier/primitives.git?branch=compile-for-wasm)" = "<none>"
|
||||
"checksum fixed-hash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18d6fd718fb4396e7a9c93ac59ba7143501467ca7a143c145b5555a571d5576"
|
||||
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
|
||||
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
|
||||
"checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb"
|
||||
"checksum heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1679e6ea370dee694f91f1dc469bf94cf8f52051d147aec3e1f9497c6fc22461"
|
||||
"checksum hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd546ef520ab3745f1aae5f2cdc6de9e6498e94d1ab138b9eb3ddfbf335847fb"
|
||||
"checksum hex-literal-impl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2ea76da4c7f1a54d01d54985566d3fdd960b2bbd7b970da024821c883c2d9631"
|
||||
"checksum keccak-hash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1f300c1f149cd9ca5214eed24f6e713a597517420fb8b15499824aa916259ec1"
|
||||
"checksum hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4da5f0e01bd8a71a224a4eedecaacfcabda388dbb7a80faf04d3514287572d95"
|
||||
"checksum hex-literal-impl 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1d340b6514f232f6db1bd16db65302a5278a04fef9ce867cb932e7e5fa21130a"
|
||||
"checksum integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)" = "<none>"
|
||||
"checksum keccak-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b7f51f30d7986536accaec4a6a288008dfb3dbffe8a2863a65292bc395a3ae7"
|
||||
"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
|
||||
"checksum libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1e5d97d6708edaa407429faa671b942dc0f2727222fb6b6539bf1db936e4b121"
|
||||
"checksum lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e6412c5e2ad9584b0b8e979393122026cdd6d2a80b933f890dcd694ddbe73739"
|
||||
"checksum libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)" = "ac8ebf8343a981e2fa97042b14768f02ed3e1d602eac06cae6166df3c8ced206"
|
||||
"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
|
||||
"checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2"
|
||||
"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3"
|
||||
"checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882"
|
||||
"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2"
|
||||
"checksum num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775393e285254d2f5004596d69bb8bc1149754570dcc08cf30cabeba67955e28"
|
||||
"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30"
|
||||
"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
|
||||
"checksum parity-wasm 0.27.6 (registry+https://github.com/rust-lang/crates.io-index)" = "bd4dc02a80a0315b109e48992c46942c79bcdb8fac416dd575d330ed9ced6cbd"
|
||||
"checksum parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd9d732f2de194336fb02fe11f9eed13d9e76f13f4315b4d88a14ca411750cd"
|
||||
"checksum parking_lot_core 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "9f35048d735bb93dd115a0030498785971aab3234d311fbe273d020084d26bd8"
|
||||
"checksum plain_hasher 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83ae80873992f511142c07d0ec6c44de5636628fdb7e204abd655932ea79d995"
|
||||
"checksum parking_lot 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4d05f1349491390b1730afba60bb20d55761bef489a954546b58b4b34e1e2ac"
|
||||
"checksum parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4db1a8ccf734a7bce794cc19b3df06ed87ab2f3907036b693c68f56b4d4537fa"
|
||||
"checksum proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ba8d4f9257b85eb6cdf13f055cea3190520aab1409ca2ab43493ea4820c25f0"
|
||||
"checksum proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5cb6f960ad471404618e9817c0e5d10b1ae74cfdf01fab89ea0641fe7fb2892"
|
||||
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
|
||||
"checksum proc-macro2 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1fa93823f53cfd0f5ac117b189aed6cfdfb2cfc0a9d82e956dd7927595ed7d46"
|
||||
"checksum quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e44651a0dc4cdd99f71c83b561e221f714912d11af1a4dff0631f923d53af035"
|
||||
"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1"
|
||||
"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5"
|
||||
"checksum rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b614fe08b6665cb9a231d07ac1364b0ef3cb3698f1239ee0c4c3a88a524f54c8"
|
||||
"checksum rayon-core 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e64b609139d83da75902f88fd6c01820046840a18471e4dfcd5ac7c0f46bea53"
|
||||
"checksum rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d24ad214285a7729b174ed6d3bcfcb80177807f959d95fafd5bfc5c4f201ac8"
|
||||
"checksum ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6f7d28b30a72c01b458428e0ae988d4149c20d902346902be881e3edc4bb325c"
|
||||
"checksum rlp 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "babe6fce20c0ca9b1582998734c4569082d0ad08e43772a1c6c40aef4f106ef9"
|
||||
"checksum rlp 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "89db7f8dfdd5eb7ab3ac3ece7a07fd273a680b4b224cb231181280e8996f9f0b"
|
||||
"checksum rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0ceb8ce7a5e520de349e1fa172baeba4a9e8d5ef06c47471863530bc4972ee1e"
|
||||
"checksum rustc-hex 2.0.0 (git+https://github.com/rphmeier/rustc-hex.git)" = "<none>"
|
||||
"checksum rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9743a7670d88d5d52950408ecdb7c71d8986251ab604d4689dd2ca25c9bca69"
|
||||
"checksum rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a54aa04a10c68c1c4eacb4337fd883b435997ede17a9385784b990777686b09a"
|
||||
"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
|
||||
"checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537"
|
||||
"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.27 (registry+https://github.com/rust-lang/crates.io-index)" = "db99f3919e20faa51bb2996057f5031d8685019b5a06139b1ce761da671b8526"
|
||||
"checksum serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "f4ba7591cfe93755e89eeecdbcc668885624829b020050e6aec99c2a03bd3fd0"
|
||||
"checksum serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6e03f1c9530c3fb0a0a5c9b826bdd9246a5921ae995d75f512ac917fc4dd55b5"
|
||||
"checksum smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44db0ecb22921ef790d17ae13a3f6d15784183ff5f2a01aa32098c7498d2b4b9"
|
||||
"checksum serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)" = "fba5be06346c5200249c8c8ca4ccba4a09e8747c71c16e420bd359a0db4d8f91"
|
||||
"checksum serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)" = "79e4620ba6fbe051fc7506fab6f84205823564d55da18d55b695160fb3479cd8"
|
||||
"checksum smallvec 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03dab98ab5ded3a8b43b2c80751194608d0b2aa0f1d46cf95d1c35e192844aa7"
|
||||
"checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b"
|
||||
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
|
||||
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
|
||||
"checksum tiny-keccak 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e9241752647ca572f12c9b520a5d360d9099360c527770647e694001646a1d0"
|
||||
"checksum triehash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9291c7f0fae44858b5e087dd462afb382354120003778f1695b44aab98c7abd7"
|
||||
"checksum syn 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6dfd71b2be5a58ee30a6f8ea355ba8290d397131c00dfa55c3d34e6e13db5101"
|
||||
"checksum tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e9175261fbdb60781fcd388a4d6cc7e14764a2b629a7ad94abb439aed223a44f"
|
||||
"checksum triehash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2033893a813c70e7d8a739ca6c36dc0a7a2c913ec718d7cbf84a3837bbe3c7ce"
|
||||
"checksum twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "475352206e7a290c5fccc27624a163e8d0d115f7bb60ca18a64fc9ce056d7435"
|
||||
"checksum uint 0.1.2 (git+https://github.com/rphmeier/primitives.git?branch=compile-for-wasm)" = "<none>"
|
||||
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
|
||||
"checksum uint 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "38051a96565903d81c9a9210ce11076b2218f3b352926baa1f5f6abbdfce8273"
|
||||
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||
"checksum untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f392d7819dbe58833e26872f5f6f0d68b7bbbe90fc3667e98731c4a15ad9a7ae"
|
||||
"checksum wasmi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "26b20dbeb7caee04597a5d2c93e2b3e64872c6ea2af732d7ad49dbec44067c35"
|
||||
"checksum wasmi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d19da510b59247935ad5f598357b3cc739912666d75d3d28318026478d95bbdb"
|
||||
"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
|
||||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
@@ -12,6 +12,7 @@ substrate-runtime-std = { path = "../../runtime-std", default-features = false }
|
||||
substrate-runtime-io = { path = "../../runtime-io", default-features = false }
|
||||
substrate-runtime-support = { path = "../../runtime-support", default-features = false }
|
||||
substrate-primitives = { path = "../../primitives", default-features = false }
|
||||
substrate-runtime-primitives = { path = "../../runtime/primitives", default-features = false }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
@@ -24,6 +25,7 @@ std = [
|
||||
"substrate-runtime-io/std",
|
||||
"substrate-runtime-support/std",
|
||||
"substrate-primitives/std",
|
||||
"substrate-runtime-primitives/std"
|
||||
]
|
||||
|
||||
[lib]
|
||||
|
||||
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Reference in New Issue
Block a user