diff --git a/substrate/primitives/src/block.rs b/substrate/primitives/src/block.rs index fd6eba123a7ee061123f66e26178f2f07603820c..40576352d677d340f505b910a14168527171161c 100644 --- a/substrate/primitives/src/block.rs +++ b/substrate/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<u8>); + /// 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<Log>, } /// 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<parachain::Proposal>, + pub candidates: Vec<parachain::Candidate>, } #[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/substrate/primitives/src/hash.rs b/substrate/primitives/src/hash.rs index 10b32f37411d2b7ddb440af69ddf833fc1f26853..cef573f6f29b11cf7d432c8c6514e15b2089729f 100644 --- a/substrate/primitives/src/hash.rs +++ b/substrate/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/substrate/primitives/src/lib.rs b/substrate/primitives/src/lib.rs index c1e0b6aadf97e46a6e3c34f6bbada2d16af495ec..ca32f302f3e8fb639cbebb941fcae98bd5805d01 100644 --- a/substrate/primitives/src/lib.rs +++ b/substrate/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/substrate/primitives/src/parachain.rs b/substrate/primitives/src/parachain.rs index 5d80ffcf1f7d481bb480e403f7745b944aec1fad..7e931bed8434270290a19541f71ee24abfa45566 100644 --- a/substrate/primitives/src/parachain.rs +++ b/substrate/primitives/src/parachain.rs @@ -30,72 +30,85 @@ impl From<u64> 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<Message>)>, + /// 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<u8>); +/// Parachain ingress queue message. +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct Message(#[serde(with="bytes")] pub Vec<u8>); -/// 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<u8>); -/// Raw proof data. +/// Parachain header raw bytes wrapper type. #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct RawProof(#[serde(with="bytes")] pub Vec<u8>); - -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<u8>); -/// 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<u8>); /// Parachain validation code. #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct ValidationCode(#[serde(with="bytes")] pub Vec<u8>); +/// Activitiy bit field +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Activity(#[serde(with="bytes")] pub Vec<u8>); + #[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/substrate/primitives/src/validator.rs b/substrate/primitives/src/validator.rs index afdff7e20f31e888a0dd159d7770fde48d742015..b6de301cb492e1f674fb467fd697724310af59f0 100644 --- a/substrate/primitives/src/validator.rs +++ b/substrate/primitives/src/validator.rs @@ -17,49 +17,31 @@ //! 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<u8>); +pub struct EgressPost(#[serde(with="bytes")] pub Vec<u8>); -/// Parachain incoming messages delta. +/// Balance upload. #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct IngressPostsDelta(#[serde(with="bytes")] pub Vec<u8>); +pub struct BalanceUpload(#[serde(with="bytes")] pub Vec<u8>); -/// Parachain outgoing messages. +/// Balance download. #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct EgressPosts(#[serde(with="bytes")] pub Vec<u8>); +pub struct BalanceDownload(#[serde(with="bytes")] pub Vec<u8>); -/// Validity result of particular proof and ingress queue. +/// The result of parachain validation. #[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<Option<(IngressPostsDelta, EgressPosts)>> 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<Vec<EgressPost>>, + /// Balance uploads + pub balance_uploads: Vec<BalanceUpload>, } // TODO [ToDr] This shouldn't be here! @@ -73,10 +55,13 @@ pub trait Validator { /// In case of success produces egress posts. fn validate( &self, - messages: &IngressPosts, - proof: &::parachain::Proof, code: &[u8], - ) -> Result<ProofValidity, Self::Error>; + // TODO [ToDr] actually consolidate + consolidated_ingress: &[(u64, Vec<parachain::Message>)], + balance_downloads: &[BalanceDownload], + block_data: ¶chain::BlockData, + previous_head_data: ¶chain::HeadData, + ) -> Result<ValidationResult, Self::Error>; } #[cfg(test)] @@ -85,19 +70,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/substrate/rpc/src/chain/tests.rs b/substrate/rpc/src/chain/tests.rs index 03cee366513ab39abf22ac3c2b9e5e9441c4eec7..00208b95981804c47adcd232a8c6a7fbf146a0c6 100644 --- a/substrate/rpc/src/chain/tests.rs +++ b/substrate/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 <http://www.gnu.org/licenses/>. +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/substrate/rpc/src/test_helpers.rs b/substrate/rpc/src/test_helpers.rs index 3866d657f4656d3a6ff4f333015c355f2f3e8576..6d2e23485b7c35424693bffea6f3fd45db7a5c70 100644 --- a/substrate/rpc/src/test_helpers.rs +++ b/substrate/rpc/src/test_helpers.rs @@ -15,7 +15,7 @@ // along with Polkadot. If not, see <http://www.gnu.org/licenses/>. 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/substrate/validator/src/parachains.rs b/substrate/validator/src/parachains.rs index 7c73ffabae588c123237d36fbec74af4506c5ae9..5adfe704c7f75b68128548e850c6d4932a020800 100644 --- a/substrate/validator/src/parachains.rs +++ b/substrate/validator/src/parachains.rs @@ -19,16 +19,29 @@ use std::fmt; use primitives::validator; 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; + /// Balance download. + type Download: DeserializeOwned; + /// Deserialized block data type. + type BlockData: DeserializeOwned; + /// Parachain head data. + type HeadData: DeserializeOwned; + /// Result + type Result: Into<validator::ValidationResult>; /// 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, + messages: Vec<(u64, Vec<Self::Message>)>, + downloads: Vec<Self::Download>, + block_data: Self::BlockData, + head_data: Self::HeadData, + ) -> Result<Self::Result>; } /// Dummy implementation of the first parachain validation. @@ -36,11 +49,20 @@ pub trait ParachainCode: fmt::Debug { pub struct ParaChain1; impl ParachainCode for ParaChain1 { - type Messages = (); - type Proof = (); + type Message = (); + type Download = (); + type BlockData = (); + type HeadData = (); + type Result = validator::ValidationResult; - fn check(&self, _messages: Self::Messages, _proof: Self::Proof) - -> Option<(validator::IngressPostsDelta, validator::EgressPosts)> { - None + fn check( + &self, + _messages: Vec<(u64, Vec<Self::Message>)>, + _downloads: Vec<Self::Download>, + _block_data: Self::BlockData, + _head_data: Self::HeadData, + ) -> Result<Self::Result> + { + unimplemented!() } } diff --git a/substrate/validator/src/validator.rs b/substrate/validator/src/validator.rs index fcacc23d074793a48797701ffeaca27e21ad0067..40fa94b80d71cd1aa833a2c4f732462f246bdfb5 100644 --- a/substrate/validator/src/validator.rs +++ b/substrate/validator/src/validator.rs @@ -45,14 +45,16 @@ impl validator::Validator for Validator { fn validate( &self, - messages: &validator::IngressPosts, - proof: ¶chain::Proof, code: &[u8], - ) -> Result<validator::ProofValidity> { + consolidated_ingress: &[(u64, Vec<parachain::Message>)], + balance_downloads: &[validator::BalanceDownload], + block_data: ¶chain::BlockData, + previous_head_data: ¶chain::HeadData, + ) -> Result<validator::ValidationResult> { 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(consolidated_ingress, balance_downloads, block_data, previous_head_data), None => bail!(ErrorKind::InvalidCode(format!("Unknown parachain code."))), } } @@ -60,20 +62,43 @@ 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<validator::ProofValidity>; + /// Given parachain candidate block data returns it's validity + /// and possible generated egress posts. + fn check( + &self, + consolidated_ingress: &[(u64, Vec<parachain::Message>)], + balance_downloads: &[validator::BalanceDownload], + block_data: ¶chain::BlockData, + previous_head_data: ¶chain::HeadData, + ) -> Result<validator::ValidationResult>; } -impl<M, P, T> Code for T where +impl<M, B, T, R> Code for T where M: DeserializeOwned, - P: DeserializeOwned, - T: ParachainCode<Messages=M, Proof=P>, + B: DeserializeOwned, + R: Into<validator::ValidationResult>, + T: ParachainCode<Message=M, BlockData=B, Result=R>, { - fn check(&self, messages: &validator::IngressPosts, proof: ¶chain::Proof) -> Result<validator::ProofValidity> { - let messages = serializer::from_slice(&messages.0)?; - let proof = serializer::from_slice(&proof.raw().0)?; + fn check( + &self, + consolidated_ingress: &[(u64, Vec<parachain::Message>)], + balance_downloads: &[validator::BalanceDownload], + block_data: ¶chain::BlockData, + previous_head_data: ¶chain::HeadData, + ) -> Result<validator::ValidationResult> { + 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::<Result<Vec<_>>>()? + ))) + .collect::<Result<Vec<_>>>()?; + let downloads = balance_downloads.iter() + .map(|download| serializer::from_slice(&download.0).map_err(Into::into)) + .collect::<Result<Vec<_>>>()?; + let block_data = serializer::from_slice(&block_data.0)?; + let head_data = serializer::from_slice(&previous_head_data.0)?; - Ok(self.check(messages, proof).into()) + Ok(self.check(messages, downloads, block_data, head_data)?.into()) } }