mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-30 13:07:56 +00:00
Merge branch 'master' into gav-optional-storage
This commit is contained in:
Generated
+2
-2
@@ -1400,12 +1400,10 @@ dependencies = [
|
||||
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-client 0.1.0",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-executor 0.1.0",
|
||||
"substrate-keyring 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-state-machine 0.1.0",
|
||||
"tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -1419,6 +1417,7 @@ dependencies = [
|
||||
"hex-literal 0.1.0 (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.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-bft 0.1.0",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-executor 0.1.0",
|
||||
"substrate-keyring 0.1.0",
|
||||
@@ -1482,6 +1481,7 @@ dependencies = [
|
||||
"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_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-bft 0.1.0",
|
||||
"substrate-client 0.1.0",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-executor 0.1.0",
|
||||
|
||||
@@ -28,12 +28,12 @@ extern crate substrate_state_machine as state_machine;
|
||||
extern crate error_chain;
|
||||
|
||||
use client::backend::Backend;
|
||||
use client::blockchain::BlockId;
|
||||
use client::Client;
|
||||
use polkadot_runtime::runtime;
|
||||
use polkadot_executor::Executor as LocalDispatch;
|
||||
use substrate_executor::{NativeExecutionDispatch, NativeExecutor};
|
||||
use primitives::{AccountId, SessionKey};
|
||||
use primitives::block::Id as BlockId;
|
||||
use primitives::parachain::DutyRoster;
|
||||
|
||||
error_chain! {
|
||||
|
||||
@@ -23,6 +23,8 @@ use rstd::vec::Vec;
|
||||
use codec::{Input, Slicable};
|
||||
use transaction::UncheckedTransaction;
|
||||
|
||||
pub use primitives::block::Id;
|
||||
|
||||
/// Used to refer to a block number.
|
||||
pub type Number = u64;
|
||||
|
||||
|
||||
@@ -5,10 +5,8 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
futures = "0.1.17"
|
||||
substrate-client = { path = "../client" }
|
||||
substrate-codec = { path = "../codec" }
|
||||
substrate-primitives = { path = "../primitives" }
|
||||
substrate-state-machine = { path = "../state-machine" }
|
||||
ed25519 = { path = "../ed25519" }
|
||||
tokio-timer = "0.1.2"
|
||||
parking_lot = "0.4"
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
error_chain! {
|
||||
errors {
|
||||
/// Missing state at block with given Id.
|
||||
StateUnavailable(b: ::client::BlockId) {
|
||||
StateUnavailable(b: ::primitives::block::Id) {
|
||||
description("State missing at given block."),
|
||||
display("State unavailable at block {:?}", b),
|
||||
}
|
||||
|
||||
@@ -83,6 +83,13 @@ impl<D, S> UncheckedJustification<D, S> {
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Justification<D,S>(UncheckedJustification<D,S>);
|
||||
|
||||
impl<D, S> Justification<D, S> {
|
||||
/// Convert this justification back to unchecked.
|
||||
pub fn uncheck(self) -> UncheckedJustification<D, S> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, S> ::std::ops::Deref for Justification<D, S> {
|
||||
type Target = UncheckedJustification<D, S>;
|
||||
|
||||
|
||||
@@ -20,9 +20,7 @@ pub mod error;
|
||||
pub mod generic;
|
||||
|
||||
extern crate substrate_codec as codec;
|
||||
extern crate substrate_client as client;
|
||||
extern crate substrate_primitives as primitives;
|
||||
extern crate substrate_state_machine as state_machine;
|
||||
extern crate ed25519;
|
||||
extern crate tokio_timer;
|
||||
extern crate parking_lot;
|
||||
@@ -37,13 +35,11 @@ use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use client::{BlockId, Client};
|
||||
use client::backend::Backend;
|
||||
use codec::Slicable;
|
||||
use ed25519::Signature;
|
||||
use primitives::block::{Block, Header, HeaderHash};
|
||||
use ed25519::LocalizedSignature;
|
||||
use primitives::bft::{Message as PrimitiveMessage, Action as PrimitiveAction, Justification as PrimitiveJustification};
|
||||
use primitives::block::{Block, Id as BlockId, Header, HeaderHash};
|
||||
use primitives::AuthorityId;
|
||||
use state_machine::CodeExecutor;
|
||||
|
||||
use futures::{stream, task, Async, Sink, Future, IntoFuture};
|
||||
use futures::future::Executor;
|
||||
@@ -63,20 +59,46 @@ pub type LocalizedMessage = generic::LocalizedMessage<
|
||||
Block,
|
||||
HeaderHash,
|
||||
AuthorityId,
|
||||
Signature
|
||||
LocalizedSignature
|
||||
>;
|
||||
|
||||
/// Justification of some hash.
|
||||
pub type Justification = generic::Justification<HeaderHash, Signature>;
|
||||
pub type Justification = generic::Justification<HeaderHash, LocalizedSignature>;
|
||||
|
||||
/// Justification of a prepare message.
|
||||
pub type PrepareJustification = generic::PrepareJustification<HeaderHash, Signature>;
|
||||
pub type PrepareJustification = generic::PrepareJustification<HeaderHash, LocalizedSignature>;
|
||||
|
||||
/// Unchecked justification.
|
||||
pub type UncheckedJustification = generic::UncheckedJustification<HeaderHash, LocalizedSignature>;
|
||||
|
||||
impl From<PrimitiveJustification> for UncheckedJustification {
|
||||
fn from(just: PrimitiveJustification) -> Self {
|
||||
UncheckedJustification {
|
||||
round_number: just.round_number as usize,
|
||||
digest: just.hash,
|
||||
signatures: just.signatures.into_iter().map(|(from, sig)| LocalizedSignature {
|
||||
signer: ed25519::Public(from),
|
||||
signature: sig,
|
||||
}).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UncheckedJustification> for PrimitiveJustification {
|
||||
fn from(just: UncheckedJustification) -> Self {
|
||||
PrimitiveJustification {
|
||||
round_number: just.round_number as u32,
|
||||
hash: just.digest,
|
||||
signatures: just.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, Signature>;
|
||||
pub type Committed = generic::Committed<Block, HeaderHash, LocalizedSignature>;
|
||||
|
||||
/// Communication between BFT participants.
|
||||
pub type Communication = generic::Communication<Block, HeaderHash, AuthorityId, Signature>;
|
||||
pub type Communication = generic::Communication<Block, HeaderHash, AuthorityId, LocalizedSignature>;
|
||||
|
||||
/// Logic for a proposer.
|
||||
///
|
||||
@@ -108,30 +130,6 @@ pub trait Authorities {
|
||||
fn authorities(&self, at: &BlockId) -> Result<Vec<AuthorityId>, Error>;
|
||||
}
|
||||
|
||||
impl<B, E> BlockImport for Client<B, E>
|
||||
where
|
||||
B: Backend,
|
||||
E: CodeExecutor,
|
||||
client::error::Error: From<<B::State as state_machine::backend::Backend>::Error>
|
||||
{
|
||||
fn import_block(&self, block: Block, _justification: Justification) {
|
||||
// TODO: use justification.
|
||||
let _ = self.import_block(block.header, Some(block.transactions));
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E> Authorities for Client<B, E>
|
||||
where
|
||||
B: Backend,
|
||||
E: CodeExecutor,
|
||||
client::error::Error: From<<B::State as state_machine::backend::Backend>::Error>
|
||||
{
|
||||
fn authorities(&self, at: &BlockId) -> Result<Vec<AuthorityId>, Error> {
|
||||
self.authorities_at(at).map_err(|_| ErrorKind::StateUnavailable(*at).into())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Instance of BFT agreement.
|
||||
struct BftInstance<P> {
|
||||
key: Arc<ed25519::Pair>,
|
||||
@@ -145,7 +143,7 @@ struct BftInstance<P> {
|
||||
impl<P: Proposer> generic::Context for BftInstance<P> {
|
||||
type AuthorityId = AuthorityId;
|
||||
type Digest = HeaderHash;
|
||||
type Signature = Signature;
|
||||
type Signature = LocalizedSignature;
|
||||
type Candidate = Block;
|
||||
type RoundTimeout = Box<Future<Item=(),Error=Error> + Send>;
|
||||
type CreateProposal = <P::CreateProposal as IntoFuture>::Future;
|
||||
@@ -163,28 +161,7 @@ impl<P: Proposer> generic::Context for BftInstance<P> {
|
||||
}
|
||||
|
||||
fn sign_local(&self, message: Message) -> LocalizedMessage {
|
||||
use primitives::bft::{Message as PrimitiveMessage, Action as PrimitiveAction};
|
||||
|
||||
let action = match message.clone() {
|
||||
::generic::Message::Propose(r, p) => PrimitiveAction::Propose(r as u32, p),
|
||||
::generic::Message::Prepare(r, h) => PrimitiveAction::Prepare(r as u32, h),
|
||||
::generic::Message::Commit(r, h) => PrimitiveAction::Commit(r as u32, h),
|
||||
::generic::Message::AdvanceRound(r) => PrimitiveAction::AdvanceRound(r as u32),
|
||||
};
|
||||
|
||||
let primitive = PrimitiveMessage {
|
||||
parent: self.parent_hash,
|
||||
action,
|
||||
};
|
||||
|
||||
let to_sign = Slicable::encode(&primitive);
|
||||
let signature = self.key.sign(&to_sign);
|
||||
|
||||
LocalizedMessage {
|
||||
message,
|
||||
signature,
|
||||
sender: self.key.public().0
|
||||
}
|
||||
sign_message(message, &*self.key, self.parent_hash.clone())
|
||||
}
|
||||
|
||||
fn round_proposer(&self, round: usize) -> AuthorityId {
|
||||
@@ -327,7 +304,7 @@ impl<P, E, I> BftService<P, E, I>
|
||||
// TODO: check key is one of the authorities.
|
||||
let authorities = self.client.authorities(&BlockId::Hash(hash))?;
|
||||
let n = authorities.len();
|
||||
let max_faulty = n.saturating_sub(1) / 3;
|
||||
let max_faulty = max_faulty_of(n);
|
||||
|
||||
let bft_instance = BftInstance {
|
||||
proposer,
|
||||
@@ -372,6 +349,84 @@ impl<P, E, I> BftService<P, E, I>
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a total number of authorities, yield the maximum faulty that would be allowed.
|
||||
/// This will always be under 1/3.
|
||||
pub fn max_faulty_of(n: usize) -> usize {
|
||||
n.saturating_sub(1) / 3
|
||||
}
|
||||
|
||||
fn check_justification_signed_message(authorities: &[AuthorityId], message: &[u8], just: UncheckedJustification)
|
||||
-> Result<Justification, UncheckedJustification>
|
||||
{
|
||||
just.check(authorities.len() - max_faulty_of(authorities.len()), |_, _, sig| {
|
||||
let auth_id = sig.signer.0;
|
||||
if !authorities.contains(&auth_id) { return None }
|
||||
|
||||
if ed25519::verify_strong(&sig.signature, message, &sig.signer) {
|
||||
Some(sig.signer.0)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Check a full justification for a header hash.
|
||||
/// Provide all valid authorities.
|
||||
///
|
||||
/// On failure, returns the justification back.
|
||||
pub fn check_justification(authorities: &[AuthorityId], parent: HeaderHash, just: UncheckedJustification)
|
||||
-> Result<Justification, UncheckedJustification>
|
||||
{
|
||||
let message = Slicable::encode(&PrimitiveMessage {
|
||||
parent,
|
||||
action: PrimitiveAction::Commit(just.round_number as u32, just.digest),
|
||||
});
|
||||
|
||||
check_justification_signed_message(authorities, &message[..], just)
|
||||
}
|
||||
|
||||
/// Check a prepare justification for a header hash.
|
||||
/// Provide all valid authorities.
|
||||
///
|
||||
/// On failure, returns the justification back.
|
||||
pub fn check_prepare_justification(authorities: &[AuthorityId], parent: HeaderHash, just: UncheckedJustification)
|
||||
-> Result<PrepareJustification, UncheckedJustification>
|
||||
{
|
||||
let message = Slicable::encode(&PrimitiveMessage {
|
||||
parent,
|
||||
action: PrimitiveAction::Prepare(just.round_number as u32, just.digest),
|
||||
});
|
||||
|
||||
check_justification_signed_message(authorities, &message[..], just)
|
||||
}
|
||||
|
||||
/// Sign a BFT message with the given key.
|
||||
pub fn sign_message(message: Message, key: &ed25519::Pair, parent_hash: HeaderHash) -> LocalizedMessage {
|
||||
let action = match message.clone() {
|
||||
::generic::Message::Propose(r, p) => PrimitiveAction::Propose(r as u32, p),
|
||||
::generic::Message::Prepare(r, h) => PrimitiveAction::Prepare(r as u32, h),
|
||||
::generic::Message::Commit(r, h) => PrimitiveAction::Commit(r as u32, h),
|
||||
::generic::Message::AdvanceRound(r) => PrimitiveAction::AdvanceRound(r as u32),
|
||||
};
|
||||
|
||||
let primitive = PrimitiveMessage {
|
||||
parent: parent_hash,
|
||||
action,
|
||||
};
|
||||
|
||||
let to_sign = Slicable::encode(&primitive);
|
||||
let signature = LocalizedSignature {
|
||||
signer: key.public(),
|
||||
signature: key.sign(&to_sign),
|
||||
};
|
||||
|
||||
LocalizedMessage {
|
||||
message,
|
||||
signature,
|
||||
sender: key.public().0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -470,4 +525,76 @@ mod tests {
|
||||
|
||||
core.turn(Some(::std::time::Duration::from_millis(100)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn max_faulty() {
|
||||
assert_eq!(max_faulty_of(3), 0);
|
||||
assert_eq!(max_faulty_of(4), 1);
|
||||
assert_eq!(max_faulty_of(100), 33);
|
||||
assert_eq!(max_faulty_of(0), 0);
|
||||
assert_eq!(max_faulty_of(11), 3);
|
||||
assert_eq!(max_faulty_of(99), 32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn justification_check_works() {
|
||||
let parent_hash = Default::default();
|
||||
let hash = [0xff; 32].into();
|
||||
|
||||
let authorities = vec![
|
||||
Keyring::One.to_raw_public(),
|
||||
Keyring::Two.to_raw_public(),
|
||||
Keyring::Alice.to_raw_public(),
|
||||
Keyring::Eve.to_raw_public(),
|
||||
];
|
||||
|
||||
let authorities_keys = vec![
|
||||
Keyring::One.into(),
|
||||
Keyring::Two.into(),
|
||||
Keyring::Alice.into(),
|
||||
Keyring::Eve.into(),
|
||||
];
|
||||
|
||||
let unchecked = UncheckedJustification {
|
||||
digest: hash,
|
||||
round_number: 1,
|
||||
signatures: authorities_keys.iter().take(3).map(|key| {
|
||||
sign_message(generic::Message::Commit(1, hash), key, parent_hash).signature
|
||||
}).collect(),
|
||||
};
|
||||
|
||||
assert!(check_justification(&authorities, parent_hash, unchecked).is_ok());
|
||||
|
||||
let unchecked = UncheckedJustification {
|
||||
digest: hash,
|
||||
round_number: 0, // wrong round number (vs. the signatures)
|
||||
signatures: authorities_keys.iter().take(3).map(|key| {
|
||||
sign_message(generic::Message::Commit(1, hash), key, parent_hash).signature
|
||||
}).collect(),
|
||||
};
|
||||
|
||||
assert!(check_justification(&authorities, parent_hash, unchecked).is_err());
|
||||
|
||||
// not enough signatures.
|
||||
let unchecked = UncheckedJustification {
|
||||
digest: hash,
|
||||
round_number: 1,
|
||||
signatures: authorities_keys.iter().take(2).map(|key| {
|
||||
sign_message(generic::Message::Commit(1, hash), key, parent_hash).signature
|
||||
}).collect(),
|
||||
};
|
||||
|
||||
assert!(check_justification(&authorities, parent_hash, unchecked).is_err());
|
||||
|
||||
// wrong hash.
|
||||
let unchecked = UncheckedJustification {
|
||||
digest: [0xfe; 32].into(),
|
||||
round_number: 1,
|
||||
signatures: authorities_keys.iter().take(3).map(|key| {
|
||||
sign_message(generic::Message::Commit(1, hash), key, parent_hash).signature
|
||||
}).collect(),
|
||||
};
|
||||
|
||||
assert!(check_justification(&authorities, parent_hash, unchecked).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ parking_lot = "0.4"
|
||||
triehash = "0.1"
|
||||
hex-literal = "0.1"
|
||||
ed25519 = { path = "../ed25519" }
|
||||
substrate-bft = { path = "../bft" }
|
||||
substrate-codec = { path = "../codec" }
|
||||
substrate-executor = { path = "../executor" }
|
||||
substrate-primitives = { path = "../primitives" }
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
|
||||
use state_machine;
|
||||
use error;
|
||||
use primitives::block;
|
||||
use blockchain::{self, BlockId};
|
||||
use primitives::block::{self, Id as BlockId};
|
||||
use primitives;
|
||||
|
||||
/// Block insertion operation. Keeps hold if the inserted block state and data.
|
||||
pub trait BlockImportOperation {
|
||||
@@ -29,7 +29,7 @@ pub trait BlockImportOperation {
|
||||
/// Returns pending state.
|
||||
fn state(&self) -> error::Result<&Self::State>;
|
||||
/// Append block data to the transaction.
|
||||
fn set_block_data(&mut self, header: block::Header, body: Option<block::Body>, is_new_best: bool) -> error::Result<()>;
|
||||
fn set_block_data(&mut self, header: block::Header, body: Option<block::Body>, justification: Option<primitives::bft::Justification>, is_new_best: bool) -> error::Result<()>;
|
||||
/// Inject storage data into the database.
|
||||
fn set_storage<I: Iterator<Item=(Vec<u8>, Option<Vec<u8>>)>>(&mut self, changes: I) -> error::Result<()>;
|
||||
/// Inject storage data into the database.
|
||||
@@ -41,7 +41,7 @@ pub trait Backend {
|
||||
/// Associated block insertion operation type.
|
||||
type BlockImportOperation: BlockImportOperation;
|
||||
/// Associated blockchain backend type.
|
||||
type Blockchain: blockchain::Backend;
|
||||
type Blockchain: ::blockchain::Backend;
|
||||
/// Associated state backend type.
|
||||
type State: state_machine::backend::Backend;
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ use std::vec::Vec;
|
||||
use codec::{Joiner, Slicable};
|
||||
use state_machine::{self, CodeExecutor};
|
||||
use primitives::{Header, Block};
|
||||
use primitives::block::Transaction;
|
||||
use {backend, error, BlockId, Client};
|
||||
use primitives::block::{Id as BlockId, Transaction};
|
||||
use {backend, error, Client};
|
||||
use triehash::ordered_trie_root;
|
||||
|
||||
/// Utility for building new (valid) blocks from a stream of transactions.
|
||||
|
||||
@@ -16,27 +16,10 @@
|
||||
|
||||
//! Polkadot blockchain trait
|
||||
|
||||
use std::fmt::{Display, Formatter, Error as FmtError};
|
||||
use primitives::block;
|
||||
use primitives::block::{self, Id as BlockId};
|
||||
use primitives;
|
||||
use error::Result;
|
||||
|
||||
/// Block indentification.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum BlockId {
|
||||
/// Identify by block header hash.
|
||||
Hash(block::HeaderHash),
|
||||
/// Identify by block number.
|
||||
Number(block::Number),
|
||||
}
|
||||
|
||||
impl Display for BlockId {
|
||||
fn fmt(&self, f: &mut Formatter) -> ::std::result::Result<(), FmtError> {
|
||||
match *self {
|
||||
BlockId::Hash(h) => h.fmt(f),
|
||||
BlockId::Number(n) => n.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Blockchain database backend. Does not perform any validation.
|
||||
pub trait Backend: Send + Sync {
|
||||
@@ -44,6 +27,8 @@ pub trait Backend: Send + Sync {
|
||||
fn header(&self, id: BlockId) -> Result<Option<block::Header>>;
|
||||
/// Get block body. Returns `None` if block is not found.
|
||||
fn body(&self, id: BlockId) -> Result<Option<block::Body>>;
|
||||
/// Get block justification. Returns `None` if justification does not exist.
|
||||
fn justification(&self, id: BlockId) -> Result<Option<primitives::bft::Justification>>;
|
||||
/// Get blockchain info.
|
||||
fn info(&self) -> Result<Info>;
|
||||
/// Get block status.
|
||||
|
||||
@@ -30,7 +30,7 @@ error_chain! {
|
||||
}
|
||||
|
||||
/// Unknown block.
|
||||
UnknownBlock(h: blockchain::BlockId) {
|
||||
UnknownBlock(h: ::primitives::block::Id) {
|
||||
description("unknown block"),
|
||||
display("UnknownBlock: {}", h),
|
||||
}
|
||||
@@ -76,6 +76,12 @@ error_chain! {
|
||||
description("authority value state error"),
|
||||
display("Current state of blockchain has invalid authority value for index {}", i),
|
||||
}
|
||||
|
||||
/// Bad justification for header.
|
||||
BadJustification(h: ::primitives::block::Id) {
|
||||
description("bad justification for header"),
|
||||
display("bad justification for header: {}", h),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,8 +22,9 @@ use state_machine;
|
||||
use error;
|
||||
use backend;
|
||||
use runtime_support::Hashable;
|
||||
use primitives::block::{self, HeaderHash};
|
||||
use blockchain::{self, BlockId, BlockStatus};
|
||||
use primitives;
|
||||
use primitives::block::{self, Id as BlockId, HeaderHash};
|
||||
use blockchain::{self, BlockStatus};
|
||||
use state_machine::backend::Backend as StateBackend;
|
||||
|
||||
fn header_hash(header: &block::Header) -> block::HeaderHash {
|
||||
@@ -38,6 +39,7 @@ struct PendingBlock {
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
struct Block {
|
||||
header: block::Header,
|
||||
justification: Option<primitives::bft::Justification>,
|
||||
body: Option<block::Body>,
|
||||
}
|
||||
|
||||
@@ -90,12 +92,13 @@ impl Blockchain {
|
||||
}
|
||||
}
|
||||
|
||||
fn insert(&self, hash: HeaderHash, header: block::Header, body: Option<block::Body>, is_new_best: bool) {
|
||||
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 {
|
||||
@@ -132,6 +135,10 @@ impl blockchain::Backend for Blockchain {
|
||||
Ok(self.id(id).and_then(|hash| self.storage.read().blocks.get(&hash).and_then(|b| b.body.clone())))
|
||||
}
|
||||
|
||||
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 info(&self) -> error::Result<blockchain::Info> {
|
||||
let storage = self.storage.read();
|
||||
Ok(blockchain::Info {
|
||||
@@ -160,12 +167,13 @@ impl backend::BlockImportOperation for BlockImportOperation {
|
||||
Ok(&self.pending_state)
|
||||
}
|
||||
|
||||
fn set_block_data(&mut self, header: block::Header, body: Option<block::Body>, is_new_best: bool) -> error::Result<()> {
|
||||
fn set_block_data(&mut self, header: block::Header, body: Option<block::Body>, justification: Option<primitives::bft::Justification>, 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,
|
||||
},
|
||||
is_best: is_new_best,
|
||||
});
|
||||
@@ -220,7 +228,7 @@ impl backend::Backend for Backend {
|
||||
if let Some(pending_block) = operation.pending_block {
|
||||
let hash = header_hash(&pending_block.block.header);
|
||||
self.states.write().insert(hash, operation.pending_state);
|
||||
self.blockchain.insert(hash, pending_block.block.header, pending_block.block.body, pending_block.is_best);
|
||||
self.blockchain.insert(hash, pending_block.block.header, pending_block.block.justification, pending_block.block.body, pending_block.is_best);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
extern crate substrate_bft as bft;
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
extern crate substrate_primitives as primitives;
|
||||
|
||||
@@ -24,9 +24,18 @@ extern crate untrusted;
|
||||
use ring::{rand, signature};
|
||||
use primitives::hash::H512;
|
||||
|
||||
/// Alias to 520-bit hash when used in the context of a signature on the relay chain.
|
||||
/// Alias to 512-bit hash when used in the context of a signature on the relay chain.
|
||||
pub type Signature = H512;
|
||||
|
||||
/// A localized signature also contains sender information.
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct LocalizedSignature {
|
||||
/// The signer of the signature.
|
||||
pub signer: Public,
|
||||
/// The signature itself.
|
||||
pub signature: Signature,
|
||||
}
|
||||
|
||||
/// Verify a message without type checking the parameters' types for the right size.
|
||||
pub fn verify(sig: &[u8], message: &[u8], public: &[u8]) -> bool {
|
||||
let public_key = untrusted::Input::from(public);
|
||||
@@ -40,7 +49,7 @@ pub fn verify(sig: &[u8], message: &[u8], public: &[u8]) -> bool {
|
||||
}
|
||||
|
||||
/// A public key.
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct Public(pub [u8; 32]);
|
||||
|
||||
/// A key pair.
|
||||
@@ -89,6 +98,12 @@ impl AsRef<[u8]> for Public {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<[u8; 32]> for Public {
|
||||
fn into(self) -> [u8; 32] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Pair {
|
||||
/// Generate new secure (random) key pair.
|
||||
pub fn new() -> Pair {
|
||||
@@ -152,10 +167,21 @@ impl Verifiable for Signature {
|
||||
}
|
||||
}
|
||||
|
||||
impl Verifiable for LocalizedSignature {
|
||||
fn verify(&self, message: &[u8], pubkey: &Public) -> bool {
|
||||
pubkey == &self.signer && self.signature.verify(message, pubkey)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
fn _test_primitives_signature_and_local_the_same() {
|
||||
fn takes_two<T>(_: T, _: T) { }
|
||||
takes_two(Signature::default(), primitives::Signature::default())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vector_should_work() {
|
||||
let pair: Pair = Pair::from_seed(&hex!("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60"));
|
||||
|
||||
@@ -29,4 +29,5 @@ substrate-test-runtime = { path = "../test-runtime" }
|
||||
substrate-executor = { path = "../../substrate/executor" }
|
||||
substrate-keyring = { path = "../../substrate/keyring" }
|
||||
substrate-codec = { path = "../../substrate/codec" }
|
||||
substrate-bft = { path = "../bft" }
|
||||
env_logger = "0.4"
|
||||
|
||||
@@ -205,6 +205,7 @@ mod test {
|
||||
body: None,
|
||||
message_queue: None,
|
||||
receipt: None,
|
||||
justification: None,
|
||||
}).collect()
|
||||
}
|
||||
|
||||
|
||||
@@ -16,14 +16,15 @@
|
||||
|
||||
//! Blockchain access trait
|
||||
|
||||
use client::{self, Client as PolkadotClient, ImportResult, ClientInfo, BlockStatus, BlockId};
|
||||
use client::{self, Client as PolkadotClient, ImportResult, ClientInfo, BlockStatus};
|
||||
use client::error::Error;
|
||||
use state_machine;
|
||||
use primitives::block;
|
||||
use primitives::block::{self, Id as BlockId};
|
||||
use primitives::bft::Justification;
|
||||
|
||||
pub trait Client : Send + Sync {
|
||||
pub trait Client: Send + Sync {
|
||||
/// Given a hash return a header
|
||||
fn import(&self, header: block::Header, body: Option<block::Body>) -> Result<ImportResult, Error>;
|
||||
fn import(&self, header: block::Header, justification: Justification, body: Option<block::Body>) -> Result<ImportResult, Error>;
|
||||
|
||||
/// Get blockchain info.
|
||||
fn info(&self) -> Result<ClientInfo, Error>;
|
||||
@@ -39,6 +40,9 @@ pub trait Client : Send + Sync {
|
||||
|
||||
/// Get block body.
|
||||
fn body(&self, id: &BlockId) -> Result<Option<block::Body>, Error>;
|
||||
|
||||
/// Get block justification.
|
||||
fn justification(&self, id: &BlockId) -> Result<Option<Justification>, Error>;
|
||||
}
|
||||
|
||||
impl<B, E> Client for PolkadotClient<B, E> where
|
||||
@@ -46,8 +50,10 @@ impl<B, E> Client for PolkadotClient<B, E> where
|
||||
E: state_machine::CodeExecutor + Send + Sync + 'static,
|
||||
Error: From<<<B as client::backend::Backend>::State as state_machine::backend::Backend>::Error>, {
|
||||
|
||||
fn import(&self, header: block::Header, body: Option<block::Body>) -> Result<ImportResult, Error> {
|
||||
(self as &PolkadotClient<B, E>).import_block(header, body)
|
||||
fn import(&self, header: block::Header, justification: Justification, body: Option<block::Body>) -> Result<ImportResult, Error> {
|
||||
// TODO: defer justification check.
|
||||
let justified_header = self.check_justification(header, justification.into())?;
|
||||
(self as &PolkadotClient<B, E>).import_block(justified_header, body)
|
||||
}
|
||||
|
||||
fn info(&self) -> Result<ClientInfo, Error> {
|
||||
@@ -69,4 +75,8 @@ impl<B, E> Client for PolkadotClient<B, E> where
|
||||
fn body(&self, id: &BlockId) -> Result<Option<block::Body>, Error> {
|
||||
(self as &PolkadotClient<B, E>).body(id)
|
||||
}
|
||||
|
||||
fn justification(&self, id: &BlockId) -> Result<Option<Justification>, Error> {
|
||||
(self as &PolkadotClient<B, E>).justification(id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ extern crate serde_json;
|
||||
#[cfg(test)] extern crate substrate_keyring as keyring;
|
||||
#[cfg(test)] #[macro_use] extern crate substrate_executor as executor;
|
||||
#[cfg(test)] extern crate substrate_codec as codec;
|
||||
#[cfg(test)] extern crate substrate_bft as bft;
|
||||
|
||||
mod service;
|
||||
mod sync;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
use std::borrow::Borrow;
|
||||
use primitives::AuthorityId;
|
||||
use primitives::block::{Number as BlockNumber, HeaderHash, Header, Body};
|
||||
use primitives::bft::Justification;
|
||||
use service::Role as RoleFlags;
|
||||
|
||||
pub type RequestId = u64;
|
||||
@@ -85,6 +86,8 @@ pub enum BlockAttribute {
|
||||
Receipt,
|
||||
/// Include block message queue.
|
||||
MessageQueue,
|
||||
/// Include a justification for the block.
|
||||
Justification,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
@@ -100,6 +103,8 @@ pub struct BlockData {
|
||||
pub receipt: Option<Bytes>,
|
||||
/// Block message queue if requested.
|
||||
pub message_queue: Option<Bytes>,
|
||||
/// Justification if requested.
|
||||
pub justification: Option<Justification>,
|
||||
}
|
||||
|
||||
#[serde(untagged)]
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
use std::collections::{HashMap, HashSet, BTreeMap};
|
||||
use std::{mem, cmp};
|
||||
use std::sync::Arc;
|
||||
use std::time;
|
||||
use parking_lot::RwLock;
|
||||
use serde_json;
|
||||
use std::time;
|
||||
use primitives::block::{HeaderHash, TransactionHash, Number as BlockNumber, Header};
|
||||
use primitives::block::{HeaderHash, TransactionHash, Number as BlockNumber, Header, Id as BlockId};
|
||||
use network::{PeerId, NodeId};
|
||||
|
||||
use message::{self, Message};
|
||||
@@ -30,7 +30,6 @@ use config::ProtocolConfig;
|
||||
use chain::Client;
|
||||
use io::SyncIo;
|
||||
use error;
|
||||
use client::BlockId;
|
||||
use super::header_hash;
|
||||
|
||||
const REQUEST_TIMEOUT_SEC: u64 = 15;
|
||||
@@ -223,13 +222,14 @@ impl Protocol {
|
||||
};
|
||||
let max = cmp::min(request.max.unwrap_or(u32::max_value()), MAX_BLOCK_DATA_RESPONSE) as usize;
|
||||
// TODO: receipts, etc.
|
||||
let (mut get_header, mut get_body) = (false, false);
|
||||
let (mut get_header, mut get_body, mut get_justification) = (false, false, false);
|
||||
for a in request.fields {
|
||||
match a {
|
||||
message::BlockAttribute::Header => get_header = true,
|
||||
message::BlockAttribute::Body => get_body = true,
|
||||
message::BlockAttribute::Receipt => unimplemented!(),
|
||||
message::BlockAttribute::MessageQueue => unimplemented!(),
|
||||
message::BlockAttribute::Justification => get_justification = true,
|
||||
}
|
||||
}
|
||||
while let Some(header) = self.chain.header(&id).unwrap_or(None) {
|
||||
@@ -244,6 +244,7 @@ impl Protocol {
|
||||
body: if get_body { self.chain.body(&BlockId::Hash(hash)).unwrap_or(None) } else { None },
|
||||
receipt: None,
|
||||
message_queue: None,
|
||||
justification: if get_justification { self.chain.justification(&BlockId::Hash(hash)).unwrap_or(None) } else { None },
|
||||
};
|
||||
blocks.push(block_data);
|
||||
match request.direction {
|
||||
|
||||
@@ -18,8 +18,8 @@ use std::collections::HashMap;
|
||||
use io::SyncIo;
|
||||
use protocol::Protocol;
|
||||
use network::PeerId;
|
||||
use client::{ImportResult, BlockStatus, ClientInfo, BlockId};
|
||||
use primitives::block::{HeaderHash, Number as BlockNumber, Header};
|
||||
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 super::header_hash;
|
||||
@@ -80,7 +80,7 @@ impl ChainSync {
|
||||
blocks: BlockCollection::new(),
|
||||
best_queued_hash: info.best_queued_hash.unwrap_or(info.chain.best_hash),
|
||||
best_queued_number: info.best_queued_number.unwrap_or(info.chain.best_number),
|
||||
required_block_attributes: vec![message::BlockAttribute::Header, message::BlockAttribute::Body],
|
||||
required_block_attributes: vec![message::BlockAttribute::Header, message::BlockAttribute::Body, message::BlockAttribute::Justification],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,41 +215,57 @@ impl ChainSync {
|
||||
for block in new_blocks {
|
||||
let origin = block.origin;
|
||||
let block = block.block;
|
||||
if let Some(header) = block.header {
|
||||
let number = header.number;
|
||||
let hash = header_hash(&header);
|
||||
let parent = header.parent_hash;
|
||||
let result = protocol.chain().import(header, block.body);
|
||||
match result {
|
||||
Ok(ImportResult::AlreadyInChain) => {
|
||||
trace!(target: "sync", "Block already in chain {}: {:?}", number, hash);
|
||||
self.block_imported(&hash, number);
|
||||
},
|
||||
Ok(ImportResult::AlreadyQueued) => {
|
||||
trace!(target: "sync", "Block already queued {}: {:?}", number, hash);
|
||||
self.block_imported(&hash, number);
|
||||
},
|
||||
Ok(ImportResult::Queued) => {
|
||||
trace!(target: "sync", "Block queued {}: {:?}", number, hash);
|
||||
self.block_imported(&hash, number);
|
||||
imported = imported + 1;
|
||||
},
|
||||
Ok(ImportResult::UnknownParent) => {
|
||||
debug!(target: "sync", "Block with unknown parent {}: {:?}, parent: {:?}", number, hash, parent);
|
||||
self.restart(io, protocol);
|
||||
return;
|
||||
},
|
||||
Ok(ImportResult::KnownBad) => {
|
||||
debug!(target: "sync", "Bad block {}: {:?}", number, hash);
|
||||
io.disable_peer(origin); //TODO: use persistent ID
|
||||
self.restart(io, protocol);
|
||||
return;
|
||||
}
|
||||
Err(e) => {
|
||||
debug!(target: "sync", "Error importing block {}: {:?}: {:?}", number, hash, e);
|
||||
self.restart(io, protocol);
|
||||
return;
|
||||
match (block.header, block.justification) {
|
||||
(Some(header), Some(justification)) => {
|
||||
let number = header.number;
|
||||
let hash = header_hash(&header);
|
||||
let parent = header.parent_hash;
|
||||
let result = protocol.chain().import(
|
||||
header,
|
||||
justification,
|
||||
block.body
|
||||
);
|
||||
match result {
|
||||
Ok(ImportResult::AlreadyInChain) => {
|
||||
trace!(target: "sync", "Block already in chain {}: {:?}", number, hash);
|
||||
self.block_imported(&hash, number);
|
||||
},
|
||||
Ok(ImportResult::AlreadyQueued) => {
|
||||
trace!(target: "sync", "Block already queued {}: {:?}", number, hash);
|
||||
self.block_imported(&hash, number);
|
||||
},
|
||||
Ok(ImportResult::Queued) => {
|
||||
trace!(target: "sync", "Block queued {}: {:?}", number, hash);
|
||||
self.block_imported(&hash, number);
|
||||
imported = imported + 1;
|
||||
},
|
||||
Ok(ImportResult::UnknownParent) => {
|
||||
debug!(target: "sync", "Block with unknown parent {}: {:?}, parent: {:?}", number, hash, parent);
|
||||
self.restart(io, protocol);
|
||||
return;
|
||||
},
|
||||
Ok(ImportResult::KnownBad) => {
|
||||
debug!(target: "sync", "Bad block {}: {:?}", number, hash);
|
||||
io.disable_peer(origin); //TODO: use persistent ID
|
||||
self.restart(io, protocol);
|
||||
return;
|
||||
}
|
||||
Err(e) => {
|
||||
debug!(target: "sync", "Error importing block {}: {:?}: {:?}", number, hash, e);
|
||||
self.restart(io, protocol);
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
(None, _) => {
|
||||
debug!(target: "sync", "Header {} was not provided by {} ", block.hash, origin);
|
||||
io.disable_peer(origin); //TODO: use persistent ID
|
||||
return;
|
||||
},
|
||||
(_, None) => {
|
||||
debug!(target: "sync", "Justification set for block {} was not provided by {} ", block.hash, origin);
|
||||
io.disable_peer(origin); //TODO: use persistent ID
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -398,7 +414,7 @@ impl ChainSync {
|
||||
fn request_ancestry(io: &mut SyncIo, protocol: &Protocol, peer_id: PeerId, block: BlockNumber) {
|
||||
let request = message::BlockRequest {
|
||||
id: 0,
|
||||
fields: vec![message::BlockAttribute::Header],
|
||||
fields: vec![message::BlockAttribute::Header, message::BlockAttribute::Justification],
|
||||
from: message::FromBlock::Number(block),
|
||||
to: None,
|
||||
direction: message::Direction::Ascending,
|
||||
|
||||
@@ -19,8 +19,9 @@ mod sync;
|
||||
use std::collections::{VecDeque, HashSet, HashMap};
|
||||
use std::sync::Arc;
|
||||
use parking_lot::RwLock;
|
||||
use client::{self, BlockId, genesis};
|
||||
use client::{self, genesis};
|
||||
use client::block_builder::BlockBuilder;
|
||||
use primitives::block::Id as BlockId;
|
||||
use primitives;
|
||||
use executor;
|
||||
use io::SyncIo;
|
||||
@@ -32,6 +33,7 @@ use runtime_support::Hashable;
|
||||
use test_runtime;
|
||||
use keyring::Keyring;
|
||||
use codec::Slicable;
|
||||
use bft;
|
||||
|
||||
native_executor_instance!(Executor, test_runtime::api::dispatch, include_bytes!("../../../test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm"));
|
||||
|
||||
@@ -100,7 +102,7 @@ pub struct TestPacket {
|
||||
}
|
||||
|
||||
pub struct Peer {
|
||||
chain: Arc<client::Client<client::in_mem::Backend, executor::NativeExecutor<Executor>>>,
|
||||
client: Arc<client::Client<client::in_mem::Backend, executor::NativeExecutor<Executor>>>,
|
||||
pub sync: Protocol,
|
||||
pub queue: RwLock<VecDeque<TestPacket>>,
|
||||
}
|
||||
@@ -108,9 +110,9 @@ pub struct Peer {
|
||||
impl Peer {
|
||||
/// Called after blockchain has been populated to updated current state.
|
||||
fn start(&self) {
|
||||
// Update the sync state to the lates chain state.
|
||||
let info = self.chain.info().expect("In-mem chain does not fail");
|
||||
let header = self.chain.header(&BlockId::Hash(info.chain.best_hash)).unwrap().unwrap();
|
||||
// Update the sync state to the latest chain state.
|
||||
let info = self.client.info().expect("In-mem client does not fail");
|
||||
let header = self.client.header(&BlockId::Hash(info.chain.best_hash)).unwrap().unwrap();
|
||||
self.sync.on_block_imported(&header);
|
||||
}
|
||||
|
||||
@@ -158,13 +160,32 @@ impl Peer {
|
||||
fn flush(&self) {
|
||||
}
|
||||
|
||||
fn justify(header: &primitives::block::Header) -> bft::UncheckedJustification {
|
||||
let hash = header.hash();
|
||||
let authorities = vec![ Keyring::Alice.into() ];
|
||||
|
||||
bft::UncheckedJustification {
|
||||
digest: hash,
|
||||
signatures: authorities.iter().map(|key| {
|
||||
bft::sign_message(
|
||||
bft::generic::Message::Commit(1, hash),
|
||||
key,
|
||||
header.parent_hash
|
||||
).signature
|
||||
}).collect(),
|
||||
round_number: 1,
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_blocks<F>(&self, count: usize, mut edit_block: F) where F: FnMut(&mut BlockBuilder<client::in_mem::Backend, executor::NativeExecutor<Executor>>) {
|
||||
for _ in 0 .. count {
|
||||
let mut builder = self.chain.new_block().unwrap();
|
||||
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);
|
||||
self.chain.import_block(block.header, Some(block.transactions)).unwrap();
|
||||
let justification = Self::justify(&block.header);
|
||||
let justified = self.client.check_justification(block.header, justification).unwrap();
|
||||
self.client.import_block(justified, Some(block.transactions)).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,11 +242,11 @@ impl TestNet {
|
||||
};
|
||||
|
||||
for _ in 0..n {
|
||||
let chain = Arc::new(client::new_in_mem(Executor::new(), Self::prepare_genesis).unwrap());
|
||||
let sync = Protocol::new(config.clone(), chain.clone()).unwrap();
|
||||
let client = Arc::new(client::new_in_mem(Executor::new(), Self::prepare_genesis).unwrap());
|
||||
let sync = Protocol::new(config.clone(), client.clone()).unwrap();
|
||||
net.peers.push(Arc::new(Peer {
|
||||
sync: sync,
|
||||
chain: chain,
|
||||
client: client,
|
||||
queue: RwLock::new(VecDeque::new()),
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ fn sync_from_two_peers_works() {
|
||||
net.peer(1).push_blocks(100, false);
|
||||
net.peer(2).push_blocks(100, false);
|
||||
net.sync();
|
||||
assert!(net.peer(0).chain.backend().blockchain().equals_to(net.peer(1).chain.backend().blockchain()));
|
||||
assert!(net.peer(0).client.backend().blockchain().equals_to(net.peer(1).client.backend().blockchain()));
|
||||
let status = net.peer(0).sync.status();
|
||||
assert_eq!(status.sync.state, SyncState::Idle);
|
||||
}
|
||||
@@ -39,7 +39,7 @@ fn sync_from_two_peers_with_ancestry_search_works() {
|
||||
net.peer(2).push_blocks(100, false);
|
||||
net.restart_peer(0);
|
||||
net.sync();
|
||||
assert!(net.peer(0).chain.backend().blockchain().canon_equals_to(net.peer(1).chain.backend().blockchain()));
|
||||
assert!(net.peer(0).client.backend().blockchain().canon_equals_to(net.peer(1).client.backend().blockchain()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -49,7 +49,7 @@ fn sync_long_chain_works() {
|
||||
net.sync_steps(3);
|
||||
assert_eq!(net.peer(0).sync.status().sync.state, SyncState::Downloading);
|
||||
net.sync();
|
||||
assert!(net.peer(0).chain.backend().blockchain().equals_to(net.peer(1).chain.backend().blockchain()));
|
||||
assert!(net.peer(0).client.backend().blockchain().equals_to(net.peer(1).client.backend().blockchain()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -59,7 +59,7 @@ fn sync_no_common_longer_chain_fails() {
|
||||
net.peer(0).push_blocks(20, true);
|
||||
net.peer(1).push_blocks(20, false);
|
||||
net.sync();
|
||||
assert!(!net.peer(0).chain.backend().blockchain().canon_equals_to(net.peer(1).chain.backend().blockchain()));
|
||||
assert!(!net.peer(0).client.backend().blockchain().canon_equals_to(net.peer(1).client.backend().blockchain()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -78,10 +78,10 @@ fn sync_after_fork_works() {
|
||||
net.peer(2).push_blocks(1, false);
|
||||
|
||||
// peer 1 has the best chain
|
||||
let peer1_chain = net.peer(1).chain.backend().blockchain().clone();
|
||||
let peer1_chain = net.peer(1).client.backend().blockchain().clone();
|
||||
net.sync();
|
||||
assert!(net.peer(0).chain.backend().blockchain().canon_equals_to(&peer1_chain));
|
||||
assert!(net.peer(1).chain.backend().blockchain().canon_equals_to(&peer1_chain));
|
||||
assert!(net.peer(2).chain.backend().blockchain().canon_equals_to(&peer1_chain));
|
||||
assert!(net.peer(0).client.backend().blockchain().canon_equals_to(&peer1_chain));
|
||||
assert!(net.peer(1).client.backend().blockchain().canon_equals_to(&peer1_chain));
|
||||
assert!(net.peer(2).client.backend().blockchain().canon_equals_to(&peer1_chain));
|
||||
}
|
||||
|
||||
|
||||
@@ -120,3 +120,35 @@ impl Slicable for Message {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Justification of a block.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
pub struct Justification {
|
||||
/// The round consensus was reached in.
|
||||
pub round_number: u32,
|
||||
/// The hash of the header justified.
|
||||
pub hash: HeaderHash,
|
||||
/// The signatures and signers of the hash.
|
||||
pub signatures: Vec<(::AuthorityId, ::Signature)>
|
||||
}
|
||||
|
||||
impl Slicable for Justification {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
|
||||
self.round_number.using_encoded(|s| v.extend(s));
|
||||
self.hash.using_encoded(|s| v.extend(s));
|
||||
self.signatures.using_encoded(|s| v.extend(s));
|
||||
|
||||
v
|
||||
}
|
||||
|
||||
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)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
//! Block and header type definitions.
|
||||
|
||||
use rstd::fmt;
|
||||
use rstd::vec::Vec;
|
||||
#[cfg(feature = "std")]
|
||||
use bytes;
|
||||
@@ -177,6 +178,25 @@ impl Slicable for Header {
|
||||
}
|
||||
}
|
||||
|
||||
/// Block indentification.
|
||||
#[derive(Clone, Copy)]
|
||||
#[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::*;
|
||||
|
||||
@@ -98,3 +98,6 @@ pub type Hash = H256;
|
||||
|
||||
/// An identifier for an authority in the consensus algorithm. The same as ed25519::Public.
|
||||
pub type AuthorityId = [u8; 32];
|
||||
|
||||
/// A 512-bit value interpreted as a signature.
|
||||
pub type Signature = hash::H512;
|
||||
|
||||
@@ -42,6 +42,6 @@ impl<B, E> ChainApi for client::Client<B, E> where
|
||||
client::error::Error: From<<<B as client::backend::Backend>::State as state_machine::backend::Backend>::Error>,
|
||||
{
|
||||
fn header(&self, hash: block::HeaderHash) -> Result<Option<block::Header>> {
|
||||
client::Client::header(self, &client::BlockId::Hash(hash)).chain_err(|| "Blockchain error")
|
||||
client::Client::header(self, &block::Id::Hash(hash)).chain_err(|| "Blockchain error")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ mod error;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use client::{self, Client, BlockId};
|
||||
use client::{self, Client};
|
||||
use primitives::block;
|
||||
use primitives::storage::{StorageKey, StorageData};
|
||||
use state_machine;
|
||||
@@ -47,10 +47,10 @@ impl<B, E> StateApi for Client<B, E> where
|
||||
client::error::Error: From<<<B as client::backend::Backend>::State as state_machine::backend::Backend>::Error>,
|
||||
{
|
||||
fn storage(&self, key: StorageKey, block: block::HeaderHash) -> Result<StorageData> {
|
||||
Ok(self.storage(&BlockId::Hash(block), &key)?)
|
||||
Ok(self.storage(&block::Id::Hash(block), &key)?)
|
||||
}
|
||||
|
||||
fn call(&self, method: String, data: Vec<u8>, block: block::HeaderHash) -> Result<Vec<u8>> {
|
||||
Ok(self.call(&BlockId::Hash(block), &method, &data)?.return_data)
|
||||
Ok(self.call(&block::Id::Hash(block), &method, &data)?.return_data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
pub use std::boxed;
|
||||
pub use std::cell;
|
||||
pub use std::cmp;
|
||||
pub use std::fmt;
|
||||
pub use std::iter;
|
||||
pub use std::mem;
|
||||
pub use std::ops;
|
||||
|
||||
@@ -33,3 +33,4 @@ pub use core::mem;
|
||||
pub use core::ops;
|
||||
pub use core::ptr;
|
||||
pub use core::slice;
|
||||
pub use core::fmt;
|
||||
|
||||
Reference in New Issue
Block a user