From 2bc7c57359c3b75d5649f0a9b984dbcd7a4556fb Mon Sep 17 00:00:00 2001 From: Robert Habermeier <rphmeier@gmail.com> Date: Mon, 5 Feb 2018 16:15:45 +0100 Subject: [PATCH] reintroduce slicable to primitives --- substrate/primitives/Cargo.toml | 2 +- substrate/primitives/src/block.rs | 109 +++++++++++++++--- substrate/primitives/src/hash.rs | 10 ++ substrate/primitives/src/lib.rs | 17 +++ substrate/primitives/src/parachain.rs | 20 ++++ substrate/primitives/src/proposal.rs | 112 ++++++++++++++----- substrate/primitives/src/runtime_function.rs | 103 +++++++++++++++-- substrate/primitives/src/transaction.rs | 33 ++++++ substrate/runtime-codec/src/lib.rs | 1 - 9 files changed, 351 insertions(+), 56 deletions(-) diff --git a/substrate/primitives/Cargo.toml b/substrate/primitives/Cargo.toml index 9421c8f6249..01aa0315aea 100644 --- a/substrate/primitives/Cargo.toml +++ b/substrate/primitives/Cargo.toml @@ -15,7 +15,7 @@ untrusted = "0.5" twox-hash = "1.1.0" byteorder = "1.1" blake2-rfc = "0.2.18" -polkadot-runtime-codec = { path = "../runtime-codec", version = "0.1" } +polkadot-runtime-codec = { path = "../runtime-codec", version = "0.1", default_features = false } [dev-dependencies] polkadot-serializer = { path = "../serializer", version = "0.1" } diff --git a/substrate/primitives/src/block.rs b/substrate/primitives/src/block.rs index 26639d7d2e3..3fd73b09cc9 100644 --- a/substrate/primitives/src/block.rs +++ b/substrate/primitives/src/block.rs @@ -17,6 +17,7 @@ //! Block and header type definitions. use bytes::{self, Vec}; +use codec::Slicable; use hash::H256; use parachain; use transaction::UncheckedTransaction; @@ -34,6 +35,35 @@ pub type TransactionHash = H256; #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub struct Log(#[serde(with="bytes")] pub Vec<u8>); +impl Slicable for Log { + fn from_slice(value: &mut &[u8]) -> Option<Self> { + Vec::<u8>::from_slice(value).map(Log) + } + + fn as_slice_then<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R { + self.0.as_slice_then(f) + } +} + +impl ::codec::NonTrivialSlicable for Log { } + +/// The digest of a block, useful for light-clients. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct Digest { + /// All logs that have happened in the block. + pub logs: Vec<Log>, +} + +impl Slicable for Digest { + fn from_slice(value: &mut &[u8]) -> Option<Self> { + Vec::<Log>::from_slice(value).map(|logs| Digest { logs }) + } + + fn as_slice_then<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R { + self.logs.as_slice_then(f) + } +} + /// A Polkadot relay chain block. #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub struct Block { @@ -43,11 +73,26 @@ pub struct Block { pub transactions: Vec<UncheckedTransaction>, } -/// The digest of a block, useful for light-clients. -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct Digest { - /// All logs that have happened in the block. - pub logs: Vec<Log>, +impl Slicable for Block { + fn from_slice(value: &mut &[u8]) -> Option<Self> { + Some(Block { + header: try_opt!(Slicable::from_slice(value)), + transactions: try_opt!(Slicable::from_slice(value)), + }) + } + + fn to_vec(&self) -> Vec<u8> { + let mut v = Vec::new(); + + v.extend(self.header.to_vec()); + v.extend(self.transactions.to_vec()); + + v + } + + fn as_slice_then<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R { + f(self.to_vec().as_slice()) + } } /// A relay chain block header. @@ -65,12 +110,38 @@ pub struct Header { pub state_root: H256, /// The root of the trie that represents this block's transactions, indexed by a 32-byte integer. pub transaction_root: H256, - /// Parachain activity bitfield - pub parachain_activity: parachain::Activity, /// The digest of activity on the block. pub digest: Digest, } +impl Slicable for Header { + fn from_slice(value: &mut &[u8]) -> Option<Self> { + Some(Header { + parent_hash: try_opt!(Slicable::from_slice(value)), + number: try_opt!(Slicable::from_slice(value)), + state_root: try_opt!(Slicable::from_slice(value)), + transaction_root: try_opt!(Slicable::from_slice(value)), + digest: try_opt!(Slicable::from_slice(value)), + }) + } + + fn to_vec(&self) -> Vec<u8> { + let mut v = Vec::new(); + + self.parent_hash.as_slice_then(|s| v.extend(s)); + self.number.as_slice_then(|s| v.extend(s)); + self.state_root.as_slice_then(|s| v.extend(s)); + self.transaction_root.as_slice_then(|s| v.extend(s)); + self.digest.as_slice_then(|s| v.extend(s)); + + v + } + + fn as_slice_then<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R { + f(self.to_vec().as_slice()) + } +} + /// A relay chain block body. /// /// Included candidates should be sorted by parachain ID, and without duplicate @@ -86,25 +157,33 @@ pub struct Body { #[cfg(test)] mod tests { use super::*; + use codec::Slicable; use polkadot_serializer as ser; #[test] fn test_header_serialization() { - assert_eq!(ser::to_string_pretty(&Header { + let header = Header { parent_hash: 5.into(), number: 67, state_root: 3.into(), - parachain_activity: parachain::Activity(vec![0]), - logs: vec![Log(vec![1])], - }), r#"{ + transaction_root: 6.into(), + digest: Digest { logs: vec![Log(vec![1])] }, + }; + + assert_eq!(ser::to_string_pretty(&header), r#"{ "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000005", "number": 67, "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000003", - "parachainActivity": "0x00", - "logs": [ - "0x01" - ] + "transactionRoot": "0x0000000000000000000000000000000000000000000000000000000000000006", + "digest": { + "logs": [ + "0x01" + ] + } }"#); + + let v = header.to_vec(); + assert_eq!(Header::from_slice(&mut &v[..]).unwrap(), header); } #[test] diff --git a/substrate/primitives/src/hash.rs b/substrate/primitives/src/hash.rs index b6711cdfcb6..8e7c6f2b8f3 100644 --- a/substrate/primitives/src/hash.rs +++ b/substrate/primitives/src/hash.rs @@ -34,6 +34,16 @@ macro_rules! impl_serde { .map(|x| (&*x).into()) } } + + impl ::codec::Slicable for $name { + fn from_slice(value: &mut &[u8]) -> Option<Self> { + <[u8; $len] as ::codec::Slicable>::from_slice(value).map($name) + } + + fn as_slice_then<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R { + self.0.as_slice_then(f) + } + } } } diff --git a/substrate/primitives/src/lib.rs b/substrate/primitives/src/lib.rs index 00ceee2d548..fb6a056dd5a 100644 --- a/substrate/primitives/src/lib.rs +++ b/substrate/primitives/src/lib.rs @@ -40,6 +40,7 @@ extern crate uint as uint_crate; #[cfg(feature = "std")] extern crate core; +extern crate polkadot_runtime_codec as codec; #[cfg(test)] extern crate polkadot_serializer; #[cfg(test)] @@ -50,6 +51,16 @@ extern crate pretty_assertions; #[macro_use] extern crate alloc; +// TODO: factor out to separate crate. +macro_rules! try_opt { + ($e: expr) => { + match $e { + Some(x) => x, + None => return None, + } + } +} + mod bytes; pub mod block; pub mod contract; @@ -95,6 +106,12 @@ pub type Hash = [u8; 32]; /// Alias to 520-bit hash when used in the context of a signature. pub type Signature = hash::H512; +/// A balance in the staking subsystem. +pub type Balance = u64; + +/// A timestamp. +pub type Timestamp = u64; + /// A hash function. pub fn hash(data: &[u8]) -> hash::H256 { blake2_256(data).into() diff --git a/substrate/primitives/src/parachain.rs b/substrate/primitives/src/parachain.rs index 360b7bacdda..5c5429e0199 100644 --- a/substrate/primitives/src/parachain.rs +++ b/substrate/primitives/src/parachain.rs @@ -30,6 +30,16 @@ impl From<u64> for Id { fn from(x: u64) -> Self { Id(x) } } +impl ::codec::Slicable for Id { + fn from_slice(value: &mut &[u8]) -> Option<Self> { + u64::from_slice(value).map(Id) + } + + fn as_slice_then<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R { + self.0.as_slice_then(f) + } +} + /// Candidate parachain block. /// /// https://github.com/w3f/polkadot-spec/blob/master/spec.md#candidate-para-chain-block @@ -101,6 +111,16 @@ pub struct ValidationCode(#[serde(with="bytes")] pub Vec<u8>); #[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)] pub struct Activity(#[serde(with="bytes")] pub Vec<u8>); +impl ::codec::Slicable for Activity { + fn from_slice(value: &mut &[u8]) -> Option<Self> { + Vec::<u8>::from_slice(value).map(Activity) + } + + fn as_slice_then<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R { + self.0.as_slice_then(f) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/substrate/primitives/src/proposal.rs b/substrate/primitives/src/proposal.rs index f8f39c815bc..833f08a94ad 100644 --- a/substrate/primitives/src/proposal.rs +++ b/substrate/primitives/src/proposal.rs @@ -19,12 +19,12 @@ //! This describes a combination of a function ID and data that can be used to call into //! an internal function. -use bytes; +use block::Number as BlockNumber; +use codec::Slicable; -/// Internal functions that can be dispatched to. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[repr(u8)] -pub enum InternalFunction { +enum InternalFunctionId { /// Set the system's code. SystemSetCode = 0, /// Set the number of sessions per era. @@ -39,17 +39,17 @@ pub enum InternalFunction { SessionSetLength = 5, } -impl InternalFunction { +impl InternalFunctionId { /// Derive `Some` value from a `u8`, or `None` if it's invalid. - pub fn from_u8(value: u8) -> Option<InternalFunction> { + fn from_u8(value: u8) -> Option<InternalFunctionId> { use self::*; let functions = [ - InternalFunction::SystemSetCode, - InternalFunction::StakingSetSessionsPerEra, - InternalFunction::StakingSetBondingDuration, - InternalFunction::StakingSetValidatorCount, - InternalFunction::GovernanceSetApprovalPpmRequired, - InternalFunction::SessionSetLength + InternalFunctionId::SystemSetCode, + InternalFunctionId::StakingSetSessionsPerEra, + InternalFunctionId::StakingSetBondingDuration, + InternalFunctionId::StakingSetValidatorCount, + InternalFunctionId::GovernanceSetApprovalPpmRequired, + InternalFunctionId::SessionSetLength ]; if (value as usize) < functions.len() { Some(functions[value as usize]) @@ -59,31 +59,85 @@ impl InternalFunction { } } +/// Internal functions that can be dispatched to. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[repr(u8)] +pub enum InternalFunction { + /// Set the system's code. + SystemSetCode(Vec<u8>), + /// Set the number of sessions per era. + StakingSetSessionsPerEra(BlockNumber), + /// Set the minimum bonding duration for staking. + StakingSetBondingDuration(BlockNumber), + /// Set the validator count for staking. + StakingSetValidatorCount(u32), + /// Set the per-mille of validator approval required for governance changes. + GovernanceSetApprovalPpmRequired(u32), + /// Set the session length. + SessionSetLength(BlockNumber), +} + /// An internal function. -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Proposal { /// The privileged function to call. pub function: InternalFunction, - /// The serialised data to call it with. - #[serde(with = "bytes")] - pub input_data: Vec<u8>, } -#[cfg(test)] -mod test { - use super::*; - use support::StaticHexInto; - - #[test] - fn slicing_should_work() { - let p = Proposal { - function: InternalFunction::SystemSetCode, - input_data: b"Hello world".to_vec(), +impl Slicable for Proposal { + fn from_slice(value: &mut &[u8]) -> Option<Self> { + let id = try_opt!(u8::from_slice(value).and_then(InternalFunctionId::from_u8)); + let function = match id { + InternalFunctionId::SystemSetCode => + InternalFunction::SystemSetCode(try_opt!(Slicable::from_slice(value))), + InternalFunctionId::StakingSetSessionsPerEra => + InternalFunction::StakingSetSessionsPerEra(try_opt!(Slicable::from_slice(value))), + InternalFunctionId::StakingSetBondingDuration => + InternalFunction::StakingSetBondingDuration(try_opt!(Slicable::from_slice(value))), + InternalFunctionId::StakingSetValidatorCount => + InternalFunction::StakingSetValidatorCount(try_opt!(Slicable::from_slice(value))), + InternalFunctionId::GovernanceSetApprovalPpmRequired => + InternalFunction::GovernanceSetApprovalPpmRequired(try_opt!(Slicable::from_slice(value))), + InternalFunctionId::SessionSetLength => + InternalFunction::SessionSetLength(try_opt!(Slicable::from_slice(value))), }; - let v = p.to_vec(); - assert_eq!(v, "000b00000048656c6c6f20776f726c64".convert::<Vec<u8>>()); - let o = Proposal::from_slice(&v).unwrap(); - assert_eq!(p, o); + Some(Proposal { function }) + } + + fn to_vec(&self) -> Vec<u8> { + let mut v = Vec::new(); + match self.function { + InternalFunction::SystemSetCode(ref data) => { + (InternalFunctionId::SystemSetCode as u8).as_slice_then(|s| v.extend(s)); + data.as_slice_then(|s| v.extend(s)); + } + InternalFunction::StakingSetSessionsPerEra(ref data) => { + (InternalFunctionId::StakingSetSessionsPerEra as u8).as_slice_then(|s| v.extend(s)); + data.as_slice_then(|s| v.extend(s)); + } + InternalFunction::StakingSetBondingDuration(ref data) => { + (InternalFunctionId::StakingSetBondingDuration as u8).as_slice_then(|s| v.extend(s)); + data.as_slice_then(|s| v.extend(s)); + } + InternalFunction::StakingSetValidatorCount(ref data) => { + (InternalFunctionId::StakingSetValidatorCount as u8).as_slice_then(|s| v.extend(s)); + data.as_slice_then(|s| v.extend(s)); + } + InternalFunction::GovernanceSetApprovalPpmRequired(ref data) => { + (InternalFunctionId::GovernanceSetApprovalPpmRequired as u8).as_slice_then(|s| v.extend(s)); + data.as_slice_then(|s| v.extend(s)); + } + InternalFunction::SessionSetLength(ref data) => { + (InternalFunctionId::SessionSetLength as u8).as_slice_then(|s| v.extend(s)); + data.as_slice_then(|s| v.extend(s)); + } + } + + v + } + + fn as_slice_then<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R { + f(self.to_vec().as_slice()) } } diff --git a/substrate/primitives/src/runtime_function.rs b/substrate/primitives/src/runtime_function.rs index 0104447f022..c4f6298f747 100644 --- a/substrate/primitives/src/runtime_function.rs +++ b/substrate/primitives/src/runtime_function.rs @@ -17,10 +17,12 @@ //! Polkadot runtime functions. //! This describes a function that can be called from an external transaction. +use codec::Slicable; + /// Public functions that can be dispatched to. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[repr(u8)] -pub enum Function { +enum FunctionId { /// Staking subsystem: begin staking. StakingStake = 0, /// Staking subsystem: stop staking. @@ -37,18 +39,99 @@ pub enum Function { GovernanceApprove = 6, } -impl Function { +impl FunctionId { /// Derive `Some` value from a `u8`, or `None` if it's invalid. - pub fn from_u8(value: u8) -> Option<Function> { + fn from_u8(value: u8) -> Option<FunctionId> { match value { - 0 => Some(Function::StakingStake), - 1 => Some(Function::StakingUnstake), - 2 => Some(Function::StakingTransfer), - 3 => Some(Function::SessionSetKey), - 4 => Some(Function::TimestampSet), - 5 => Some(Function::GovernancePropose), - 6 => Some(Function::GovernanceApprove), + 0 => Some(FunctionId::StakingStake), + 1 => Some(FunctionId::StakingUnstake), + 2 => Some(FunctionId::StakingTransfer), + 3 => Some(FunctionId::SessionSetKey), + 4 => Some(FunctionId::TimestampSet), + 5 => Some(FunctionId::GovernancePropose), + 6 => Some(FunctionId::GovernanceApprove), _ => None, } } } + +/// Functions on the runtime. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[repr(u8)] +pub enum Function { + /// Staking subsystem: begin staking. + StakingStake, + /// Staking subsystem: stop staking. + StakingUnstake, + /// Staking subsystem: transfer stake. + StakingTransfer(::AccountId, ::Balance), + /// Set temporary session key as a validator. + SessionSetKey(::SessionKey), + /// Set the timestamp. + TimestampSet(::Timestamp), + /// Make a proposal for the governance system. + GovernancePropose(::proposal::Proposal), + /// Approve a proposal for the governance system. + GovernanceApprove(::block::Number), +} + +impl Slicable for Function { + fn from_slice(value: &mut &[u8]) -> Option<Self> { + let id = try_opt!(u8::from_slice(value).and_then(FunctionId::from_u8)); + Some(match id { + FunctionId::StakingStake => Function::StakingStake, + FunctionId::StakingUnstake => Function::StakingUnstake, + FunctionId::StakingTransfer => Function::StakingTransfer( + try_opt!(Slicable::from_slice(value)), + try_opt!(Slicable::from_slice(value)), + ), + FunctionId::SessionSetKey => + Function::SessionSetKey(try_opt!(Slicable::from_slice(value))), + FunctionId::TimestampSet => + Function::TimestampSet(try_opt!(Slicable::from_slice(value))), + FunctionId::GovernancePropose => + Function::GovernancePropose(try_opt!(Slicable::from_slice(value))), + FunctionId::GovernanceApprove => + Function::GovernanceApprove(try_opt!(Slicable::from_slice(value))), + }) + } + + fn to_vec(&self) -> Vec<u8> { + let mut v = Vec::new(); + match *self { + Function::StakingStake => { + (FunctionId::StakingStake as u8).as_slice_then(|s| v.extend(s)); + } + Function::StakingUnstake => { + (FunctionId::StakingUnstake as u8).as_slice_then(|s| v.extend(s)); + } + Function::StakingTransfer(ref to, ref amount) => { + (FunctionId::StakingTransfer as u8).as_slice_then(|s| v.extend(s)); + to.as_slice_then(|s| v.extend(s)); + amount.as_slice_then(|s| v.extend(s)); + } + Function::SessionSetKey(ref data) => { + (FunctionId::SessionSetKey as u8).as_slice_then(|s| v.extend(s)); + data.as_slice_then(|s| v.extend(s)); + } + Function::TimestampSet(ref data) => { + (FunctionId::TimestampSet as u8).as_slice_then(|s| v.extend(s)); + data.as_slice_then(|s| v.extend(s)); + } + Function::GovernancePropose(ref data) => { + (FunctionId::GovernancePropose as u8).as_slice_then(|s| v.extend(s)); + data.as_slice_then(|s| v.extend(s)); + } + Function::GovernanceApprove(ref data) => { + (FunctionId::GovernanceApprove as u8).as_slice_then(|s| v.extend(s)); + data.as_slice_then(|s| v.extend(s)); + } + } + + v + } + + fn as_slice_then<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R { + f(self.to_vec().as_slice()) + } +} diff --git a/substrate/primitives/src/transaction.rs b/substrate/primitives/src/transaction.rs index eb3e823c582..57b62c878b3 100644 --- a/substrate/primitives/src/transaction.rs +++ b/substrate/primitives/src/transaction.rs @@ -17,6 +17,7 @@ //! Transaction type. use bytes::{self, Vec}; +use codec::Slicable; use runtime_function::Function; #[cfg(feature = "std")] @@ -48,6 +49,38 @@ pub struct UncheckedTransaction { pub signature: ::Signature, } +impl Slicable for UncheckedTransaction { + fn from_slice(value: &mut &[u8]) -> Option<Self> { + Some(UncheckedTransaction { + transaction: Transaction { + signed: try_opt!(Slicable::from_slice(value)), + nonce: try_opt!(Slicable::from_slice(value)), + function: try_opt!(Slicable::from_slice(value)), + input_data: try_opt!(Slicable::from_slice(value)), + }, + signature: try_opt!(Slicable::from_slice(value)), + }) + } + + fn to_vec(&self) -> Vec<u8> { + let mut v = Vec::new(); + + self.transaction.signed.as_slice_then(|s| v.extend(s)); + self.transaction.nonce.as_slice_then(|s| v.extend(s)); + self.transaction.function.as_slice_then(|s| v.extend(s)); + self.transaction.input_data.as_slice_then(|s| v.extend(s)); + self.signature.as_slice_then(|s| v.extend(s)); + + v + } + + fn as_slice_then<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R { + f(self.to_vec().as_slice()) + } +} + +impl ::codec::NonTrivialSlicable for UncheckedTransaction {} + impl PartialEq for UncheckedTransaction { fn eq(&self, other: &Self) -> bool { self.signature.iter().eq(other.signature.iter()) && self.transaction == other.transaction diff --git a/substrate/runtime-codec/src/lib.rs b/substrate/runtime-codec/src/lib.rs index 05999531568..3e076858beb 100644 --- a/substrate/runtime-codec/src/lib.rs +++ b/substrate/runtime-codec/src/lib.rs @@ -22,7 +22,6 @@ mod endiansensitive; mod slicable; -mod streamreader; mod joiner; mod keyedvec; -- GitLab