From 06abfc750a3efca8499fbff2a42b6f89f762e29e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 16 Nov 2017 15:45:38 +0100 Subject: [PATCH 1/2] Update primitives. --- primitives/src/block.rs | 50 ++++++++--------- primitives/src/hash.rs | 2 + primitives/src/lib.rs | 3 ++ primitives/src/parachain.rs | 105 ++++++++++++++++++++---------------- primitives/src/validator.rs | 80 ++++++++++----------------- rpc/src/chain/tests.rs | 6 ++- rpc/src/test_helpers.rs | 7 +-- validator/src/parachains.rs | 42 ++++++++++----- validator/src/validator.rs | 35 +++++++----- 9 files changed, 173 insertions(+), 157 deletions(-) diff --git a/primitives/src/block.rs b/primitives/src/block.rs index fd6eba123a7ee..40576352d677d 100644 --- a/primitives/src/block.rs +++ b/primitives/src/block.rs @@ -16,25 +16,34 @@ //! Block and header type definitions. +use bytes; use hash::H256; use parachain; /// Hash used to refer to a block hash. pub type HeaderHash = H256; +/// Execution log (event) +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Log(#[serde(with="bytes")] pub Vec); + /// A relay chain block header. +/// +/// https://github.com/w3f/polkadot-spec/blob/master/spec.md#header #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] #[serde(deny_unknown_fields)] pub struct Header { /// Block parent's hash. pub parent_hash: HeaderHash, - /// State root after this transition. - pub state_root: H256, - /// Unix time at which this header was produced. - pub timestamp: u64, /// Block number. pub number: u64, + /// State root after this transition. + pub state_root: H256, + /// Parachain activity bitfield + pub parachain_activity: parachain::Activity, + /// Logs (generated by execution) + pub logs: Vec, } /// A relay chain block body. @@ -46,7 +55,7 @@ pub struct Header { #[serde(deny_unknown_fields)] pub struct Body { /// Parachain proposal blocks. - pub para_blocks: Vec, + pub candidates: Vec, } #[cfg(test)] @@ -58,34 +67,17 @@ mod tests { fn test_header_serialization() { assert_eq!(ser::to_string_pretty(&Header { parent_hash: 5.into(), - state_root: 3.into(), - timestamp: 10, number: 67, + state_root: 3.into(), + parachain_activity: parachain::Activity(vec![0]), + logs: vec![Log(vec![1])], }), r#"{ "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000005", + "number": 67, "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000003", - "timestamp": 10, - "number": 67 -}"#); - } - - #[test] - fn test_body_serialization() { - assert_eq!(ser::to_string_pretty(&Body { - para_blocks: vec![ - parachain::Proposal { - parachain: 5.into(), - header: parachain::Header(vec![1, 2, 3, 4]), - proof_hash: 5.into(), - } - ], - }), r#"{ - "paraBlocks": [ - { - "parachain": 5, - "header": "0x01020304", - "proofHash": "0x0000000000000000000000000000000000000000000000000000000000000005" - } + "parachainActivity": "0x00", + "logs": [ + "0x01" ] }"#); } diff --git a/primitives/src/hash.rs b/primitives/src/hash.rs index 10b32f37411d2..cef573f6f29b1 100644 --- a/primitives/src/hash.rs +++ b/primitives/src/hash.rs @@ -41,6 +41,8 @@ impl_hash!(H160, 20); impl_serde!(H160, 20); impl_hash!(H256, 32); impl_serde!(H256, 32); +impl_hash!(H520, 65); +impl_serde!(H520, 65); #[cfg(test)] mod tests { diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index c1e0b6aadf97e..ca32f302f3e8f 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -49,6 +49,9 @@ pub mod validator; /// Alias to 160-bit hash when used in the context of an account address. pub type Address = hash::H160; +/// Alias to 520-bit hash when used in the context of a signature. +pub type Signature = hash::H520; + pub use self::hash::{H160, H256}; pub use self::uint::{U256, U512}; diff --git a/primitives/src/parachain.rs b/primitives/src/parachain.rs index 5d80ffcf1f7d4..98dd48c8defc8 100644 --- a/primitives/src/parachain.rs +++ b/primitives/src/parachain.rs @@ -30,72 +30,85 @@ impl From for Id { fn from(x: u64) -> Self { Id(x) } } -/// A parachain block proposal. -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +/// Candidate parachain block. +/// +/// https://github.com/w3f/polkadot-spec/blob/master/spec.md#candidate-para-chain-block +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] #[serde(deny_unknown_fields)] -pub struct Proposal { +pub struct Candidate { /// The ID of the parachain this is a proposal for. - pub parachain: Id, - /// Parachain block header bytes. - pub header: Header, - /// Hash of data necessary to prove validity of the header. - pub proof_hash: ProofHash, + pub parachain_index: Id, + /// Collator's signature + pub collator_signature: ::Signature, + /// Unprocessed ingress queue. + /// + /// Ordered by parachain ID and block number. + pub unprocessed_ingress: Vec<(u64, Vec)>, + /// Block data + pub block: BlockData, } -/// Parachain header raw bytes wrapper type. -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct Header(#[serde(with="bytes")] pub Vec); +/// Parachain ingress queue message. +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct Message(#[serde(with="bytes")] pub Vec); -/// Hash used to refer to proof of block header. -pub type ProofHash = ::hash::H256; +/// Parachain block data. +/// +/// contains everything required to validate para-block, may contain block and witness data +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct BlockData(#[serde(with="bytes")] pub Vec); -/// Raw proof data. +/// Parachain header raw bytes wrapper type. #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct RawProof(#[serde(with="bytes")] pub Vec); - -impl RawProof { - /// Compute and store the hash of the proof. - pub fn into_proof(self) -> Proof { - let hash = ::hash(&self.0); - Proof(self, hash) - } -} +pub struct Header(#[serde(with="bytes")] pub Vec); -/// Parachain proof data. +/// Parachain head data included in the chain. #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct Proof(RawProof, ProofHash); - -impl Proof { - /// Get raw proof data. - pub fn raw(&self) -> &RawProof { &self.0 } - - /// Get hash of proof data. - pub fn hash(&self) -> &ProofHash { &self.1 } - - /// Decompose the proof back into raw data and hash. - pub fn into_inner(self) -> (RawProof, ProofHash) { - (self.0, self.1) - } -} +pub struct HeadData(#[serde(with="bytes")] pub Vec); /// Parachain validation code. #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct ValidationCode(#[serde(with="bytes")] pub Vec); +/// Activitiy bit field +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Activity(#[serde(with="bytes")] pub Vec); + #[cfg(test)] mod tests { use super::*; use polkadot_serializer as ser; #[test] - fn test_proof_serialization() { - assert_eq!( - ser::to_string_pretty(&Proof(RawProof(vec![1,2,3]), 5.into())), - r#"[ - "0x010203", - "0x0000000000000000000000000000000000000000000000000000000000000005" -]"# - ) + fn test_candidate() { + assert_eq!(ser::to_string_pretty(&Candidate { + parachain_index: 5.into(), + collator_signature: 10.into(), + unprocessed_ingress: vec![ + (1, vec![Message(vec![2])]), + (2, vec![Message(vec![2]), Message(vec![3])]), + ], + block: BlockData(vec![1, 2, 3]), + }), r#"{ + "parachainIndex": 5, + "collatorSignature": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a", + "unprocessedIngress": [ + [ + 1, + [ + "0x02" + ] + ], + [ + 2, + [ + "0x02", + "0x03" + ] + ] + ] + "block": "0x010203" +}"#); } } diff --git a/primitives/src/validator.rs b/primitives/src/validator.rs index afdff7e20f31e..ebfe137bfcf68 100644 --- a/primitives/src/validator.rs +++ b/primitives/src/validator.rs @@ -17,49 +17,27 @@ //! Validator primitives. use bytes; +use parachain; -/// Parachain incoming messages. +/// Parachain outgoing message. #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct IngressPosts(#[serde(with="bytes")] pub Vec); +pub struct EgressPost(#[serde(with="bytes")] pub Vec); -/// Parachain incoming messages delta. +/// Balance upload. #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct IngressPostsDelta(#[serde(with="bytes")] pub Vec); +pub struct BalanceUpload(#[serde(with="bytes")] pub Vec); -/// Parachain outgoing messages. +/// The result of parachain validation. #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct EgressPosts(#[serde(with="bytes")] pub Vec); - -/// Validity result of particular proof and ingress queue. -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(tag="type", content="data")] #[serde(rename_all = "camelCase")] #[serde(deny_unknown_fields)] -pub enum ProofValidity { - /// The proof is invalid. - Invalid, - /// The proof is processed and new egress queue is created. - /// Also includes current ingress queue delta. - Valid(IngressPostsDelta, EgressPosts), -} - -impl ProofValidity { - /// The proof is valid. - pub fn is_valid(&self) -> bool { - match *self { - ProofValidity::Invalid => false, - ProofValidity::Valid(..) => true, - } - } -} - -impl From> for ProofValidity { - fn from(posts: Option<(IngressPostsDelta, EgressPosts)>) -> Self { - match posts { - Some((delta, posts)) => ProofValidity::Valid(delta, posts), - None => ProofValidity::Invalid, - } - } +pub struct ValidationResult { + /// New head data that should be included in the relay chain state. + pub head_data: parachain::HeadData, + /// Outgoing messages (a vec for each parachain). + pub egress_queues: Vec>, + /// Balance uploads + pub balance_uploads: Vec, } // TODO [ToDr] This shouldn't be here! @@ -73,10 +51,9 @@ pub trait Validator { /// In case of success produces egress posts. fn validate( &self, - messages: &IngressPosts, - proof: &::parachain::Proof, + candidate: ¶chain::Candidate, code: &[u8], - ) -> Result; + ) -> Result; } #[cfg(test)] @@ -85,19 +62,20 @@ mod tests { use polkadot_serializer as ser; #[test] - fn test_proof_validity_serialization() { - assert_eq!( - ser::to_string_pretty(&ProofValidity::Invalid), - r#"{ - "type": "invalid" -}"#); - assert_eq!( - ser::to_string_pretty(&ProofValidity::Valid(IngressPostsDelta(vec![1]), EgressPosts(vec![1, 2, 3]))), - r#"{ - "type": "valid", - "data": [ - "0x01", - "0x010203" + fn test_validation_result() { + assert_eq!(ser::to_string_pretty(&ValidationResult { + head_data: parachain::HeadData(vec![1]), + egress_queues: vec![vec![EgressPost(vec![1])]], + balance_uploads: vec![BalanceUpload(vec![2])], + }), r#"{ + "headData": "0x01", + "egressQueues": [ + [ + "0x01" + ] + ], + "balanceUploads": [ + "0x02" ] }"#); } diff --git a/rpc/src/chain/tests.rs b/rpc/src/chain/tests.rs index 03cee366513ab..00208b9598180 100644 --- a/rpc/src/chain/tests.rs +++ b/rpc/src/chain/tests.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . +use primitives::parachain; use super::*; use test_helpers::Blockchain; @@ -26,9 +27,10 @@ fn should_return_header() { ChainApi::header(&state, 0.into()), Ok(Some(ref x)) if x == &block::Header { parent_hash: 0.into(), - state_root: 0.into(), - timestamp: 0, number: 0, + state_root: 0.into(), + parachain_activity: parachain::Activity(vec![0]), + logs: vec![], } ); diff --git a/rpc/src/test_helpers.rs b/rpc/src/test_helpers.rs index 3866d657f4656..6d2e23485b7c3 100644 --- a/rpc/src/test_helpers.rs +++ b/rpc/src/test_helpers.rs @@ -15,7 +15,7 @@ // along with Polkadot. If not, see . use client; -use primitives::block; +use primitives::{block, parachain}; /// Temporary dummy blockchain implementation for tests. #[derive(Debug, Default)] @@ -33,10 +33,11 @@ impl client::Blockchain for Blockchain { None } else { Some(block::Header { - number: 0, parent_hash: 0.into(), + number: 0, state_root: 0.into(), - timestamp: 0, + parachain_activity: parachain::Activity(vec![0]), + logs: vec![], }) }) } diff --git a/validator/src/parachains.rs b/validator/src/parachains.rs index 7c73ffabae588..7e87e57f815f0 100644 --- a/validator/src/parachains.rs +++ b/validator/src/parachains.rs @@ -16,19 +16,28 @@ use std::fmt; -use primitives::validator; +use primitives::{parachain, validator, Signature}; use serde::de::DeserializeOwned; +use error::Result; + /// Parachain code implementation. pub trait ParachainCode: fmt::Debug { - /// Deserialized messages type. - type Messages: DeserializeOwned; - /// Deserialized proof type. - type Proof: DeserializeOwned; + /// Deserialized message type. + type Message: DeserializeOwned; + /// Deserialized block data type. + type BlockData: DeserializeOwned; + /// Result + type Result: Into; /// Given decoded messages and proof validate it and return egress posts. - fn check(&self, messages: Self::Messages, proof: Self::Proof) -> - Option<(validator::IngressPostsDelta, validator::EgressPosts)>; + fn check( + &self, + id: parachain::Id, + signature: Signature, + messages: Vec<(u64, Vec)>, + block_data: Self::BlockData, + ) -> Result; } /// Dummy implementation of the first parachain validation. @@ -36,11 +45,18 @@ pub trait ParachainCode: fmt::Debug { pub struct ParaChain1; impl ParachainCode for ParaChain1 { - type Messages = (); - type Proof = (); - - fn check(&self, _messages: Self::Messages, _proof: Self::Proof) - -> Option<(validator::IngressPostsDelta, validator::EgressPosts)> { - None + type Message = (); + type BlockData = (); + type Result = validator::ValidationResult; + + fn check( + &self, + _id: parachain::Id, + _signature: Signature, + _messages: Vec<(u64, Vec)>, + _block_data: Self::BlockData, + ) -> Result + { + unimplemented!() } } diff --git a/validator/src/validator.rs b/validator/src/validator.rs index fcacc23d07479..a77323e5f2f5c 100644 --- a/validator/src/validator.rs +++ b/validator/src/validator.rs @@ -45,14 +45,13 @@ impl validator::Validator for Validator { fn validate( &self, - messages: &validator::IngressPosts, - proof: ¶chain::Proof, + candidate: ¶chain::Candidate, code: &[u8], - ) -> Result { + ) -> Result { ensure!(code.len() == 1, ErrorKind::InvalidCode(format!("The code should be a single byte."))); match self.codes.get(code[0] as usize) { - Some(code) => code.check(messages, proof), + Some(code) => code.check(candidate), None => bail!(ErrorKind::InvalidCode(format!("Unknown parachain code."))), } } @@ -60,20 +59,30 @@ impl validator::Validator for Validator { /// Simplified parachain code verification trait Code: fmt::Debug { - /// Given bytes of messages and proof determine if the proof is valid and return egress posts. - fn check(&self, messages: &validator::IngressPosts, proof: ¶chain::Proof) -> Result; + /// Given parachain candidate block data returns it's validity + /// and possible generated egress posts. + fn check(&self, candidate: ¶chain::Candidate) -> Result; } -impl Code for T where +impl Code for T where M: DeserializeOwned, - P: DeserializeOwned, - T: ParachainCode, + B: DeserializeOwned, + R: Into, + T: ParachainCode, { - fn check(&self, messages: &validator::IngressPosts, proof: ¶chain::Proof) -> Result { - let messages = serializer::from_slice(&messages.0)?; - let proof = serializer::from_slice(&proof.raw().0)?; + fn check(&self, candidate: ¶chain::Candidate) -> Result { + let candidate = candidate.clone(); + let index = candidate.parachain_index; + let signature = candidate.collator_signature; + let messages = candidate.unprocessed_ingress.into_iter() + .map(|(block, vec)| Ok((block, vec.into_iter() + .map(|msg| serializer::from_slice(&msg.0).map_err(Into::into)) + .collect::>>()? + ))) + .collect::>>()?; + let block_data = serializer::from_slice(&candidate.block.0)?; - Ok(self.check(messages, proof).into()) + Ok(self.check(index, signature, messages, block_data)?.into()) } } From 1f8acdec79d712fd8e37d1155f6651764df90345 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 16 Nov 2017 16:02:34 +0100 Subject: [PATCH 2/2] Fix validator interface. --- primitives/src/parachain.rs | 2 +- primitives/src/validator.rs | 10 +++++++++- validator/src/parachains.rs | 16 +++++++++++----- validator/src/validator.rs | 38 ++++++++++++++++++++++++++----------- 4 files changed, 48 insertions(+), 18 deletions(-) diff --git a/primitives/src/parachain.rs b/primitives/src/parachain.rs index 98dd48c8defc8..7e931bed84342 100644 --- a/primitives/src/parachain.rs +++ b/primitives/src/parachain.rs @@ -107,7 +107,7 @@ mod tests { "0x03" ] ] - ] + ], "block": "0x010203" }"#); } diff --git a/primitives/src/validator.rs b/primitives/src/validator.rs index ebfe137bfcf68..b6de301cb492e 100644 --- a/primitives/src/validator.rs +++ b/primitives/src/validator.rs @@ -27,6 +27,10 @@ pub struct EgressPost(#[serde(with="bytes")] pub Vec); #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct BalanceUpload(#[serde(with="bytes")] pub Vec); +/// Balance download. +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct BalanceDownload(#[serde(with="bytes")] pub Vec); + /// The result of parachain validation. #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -51,8 +55,12 @@ pub trait Validator { /// In case of success produces egress posts. fn validate( &self, - candidate: ¶chain::Candidate, code: &[u8], + // TODO [ToDr] actually consolidate + consolidated_ingress: &[(u64, Vec)], + balance_downloads: &[BalanceDownload], + block_data: ¶chain::BlockData, + previous_head_data: ¶chain::HeadData, ) -> Result; } diff --git a/validator/src/parachains.rs b/validator/src/parachains.rs index 7e87e57f815f0..5adfe704c7f75 100644 --- a/validator/src/parachains.rs +++ b/validator/src/parachains.rs @@ -16,7 +16,7 @@ use std::fmt; -use primitives::{parachain, validator, Signature}; +use primitives::validator; use serde::de::DeserializeOwned; use error::Result; @@ -25,18 +25,22 @@ use error::Result; pub trait ParachainCode: fmt::Debug { /// Deserialized message type. type Message: DeserializeOwned; + /// Balance download. + type Download: DeserializeOwned; /// Deserialized block data type. type BlockData: DeserializeOwned; + /// Parachain head data. + type HeadData: DeserializeOwned; /// Result type Result: Into; /// Given decoded messages and proof validate it and return egress posts. fn check( &self, - id: parachain::Id, - signature: Signature, messages: Vec<(u64, Vec)>, + downloads: Vec, block_data: Self::BlockData, + head_data: Self::HeadData, ) -> Result; } @@ -46,15 +50,17 @@ pub struct ParaChain1; impl ParachainCode for ParaChain1 { type Message = (); + type Download = (); type BlockData = (); + type HeadData = (); type Result = validator::ValidationResult; fn check( &self, - _id: parachain::Id, - _signature: Signature, _messages: Vec<(u64, Vec)>, + _downloads: Vec, _block_data: Self::BlockData, + _head_data: Self::HeadData, ) -> Result { unimplemented!() diff --git a/validator/src/validator.rs b/validator/src/validator.rs index a77323e5f2f5c..40fa94b80d71c 100644 --- a/validator/src/validator.rs +++ b/validator/src/validator.rs @@ -45,13 +45,16 @@ impl validator::Validator for Validator { fn validate( &self, - candidate: ¶chain::Candidate, code: &[u8], + consolidated_ingress: &[(u64, Vec)], + balance_downloads: &[validator::BalanceDownload], + block_data: ¶chain::BlockData, + previous_head_data: ¶chain::HeadData, ) -> Result { ensure!(code.len() == 1, ErrorKind::InvalidCode(format!("The code should be a single byte."))); match self.codes.get(code[0] as usize) { - Some(code) => code.check(candidate), + Some(code) => code.check(consolidated_ingress, balance_downloads, block_data, previous_head_data), None => bail!(ErrorKind::InvalidCode(format!("Unknown parachain code."))), } } @@ -61,7 +64,13 @@ impl validator::Validator for Validator { trait Code: fmt::Debug { /// Given parachain candidate block data returns it's validity /// and possible generated egress posts. - fn check(&self, candidate: ¶chain::Candidate) -> Result; + fn check( + &self, + consolidated_ingress: &[(u64, Vec)], + balance_downloads: &[validator::BalanceDownload], + block_data: ¶chain::BlockData, + previous_head_data: ¶chain::HeadData, + ) -> Result; } impl Code for T where @@ -70,19 +79,26 @@ impl Code for T where R: Into, T: ParachainCode, { - fn check(&self, candidate: ¶chain::Candidate) -> Result { - let candidate = candidate.clone(); - let index = candidate.parachain_index; - let signature = candidate.collator_signature; - let messages = candidate.unprocessed_ingress.into_iter() - .map(|(block, vec)| Ok((block, vec.into_iter() + fn check( + &self, + consolidated_ingress: &[(u64, Vec)], + balance_downloads: &[validator::BalanceDownload], + block_data: ¶chain::BlockData, + previous_head_data: ¶chain::HeadData, + ) -> Result { + let messages = consolidated_ingress.iter() + .map(|&(ref block, ref vec)| Ok((*block, vec.iter() .map(|msg| serializer::from_slice(&msg.0).map_err(Into::into)) .collect::>>()? ))) .collect::>>()?; - let block_data = serializer::from_slice(&candidate.block.0)?; + let downloads = balance_downloads.iter() + .map(|download| serializer::from_slice(&download.0).map_err(Into::into)) + .collect::>>()?; + let block_data = serializer::from_slice(&block_data.0)?; + let head_data = serializer::from_slice(&previous_head_data.0)?; - Ok(self.check(index, signature, messages, block_data)?.into()) + Ok(self.check(messages, downloads, block_data, head_data)?.into()) } }