reintroduce slicable to primitives

This commit is contained in:
Robert Habermeier
2018-02-05 16:15:45 +01:00
parent e4e755a87a
commit 2bc7c57359
9 changed files with 351 additions and 56 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ untrusted = "0.5"
twox-hash = "1.1.0" twox-hash = "1.1.0"
byteorder = "1.1" byteorder = "1.1"
blake2-rfc = "0.2.18" 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] [dev-dependencies]
polkadot-serializer = { path = "../serializer", version = "0.1" } polkadot-serializer = { path = "../serializer", version = "0.1" }
+94 -15
View File
@@ -17,6 +17,7 @@
//! Block and header type definitions. //! Block and header type definitions.
use bytes::{self, Vec}; use bytes::{self, Vec};
use codec::Slicable;
use hash::H256; use hash::H256;
use parachain; use parachain;
use transaction::UncheckedTransaction; use transaction::UncheckedTransaction;
@@ -34,6 +35,35 @@ pub type TransactionHash = H256;
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct Log(#[serde(with="bytes")] pub Vec<u8>); 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. /// A Polkadot relay chain block.
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct Block { pub struct Block {
@@ -43,11 +73,26 @@ pub struct Block {
pub transactions: Vec<UncheckedTransaction>, pub transactions: Vec<UncheckedTransaction>,
} }
/// The digest of a block, useful for light-clients. impl Slicable for Block {
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] fn from_slice(value: &mut &[u8]) -> Option<Self> {
pub struct Digest { Some(Block {
/// All logs that have happened in the block. header: try_opt!(Slicable::from_slice(value)),
pub logs: Vec<Log>, 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. /// A relay chain block header.
@@ -65,12 +110,38 @@ pub struct Header {
pub state_root: H256, pub state_root: H256,
/// The root of the trie that represents this block's transactions, indexed by a 32-byte integer. /// The root of the trie that represents this block's transactions, indexed by a 32-byte integer.
pub transaction_root: H256, pub transaction_root: H256,
/// Parachain activity bitfield
pub parachain_activity: parachain::Activity,
/// The digest of activity on the block. /// The digest of activity on the block.
pub digest: Digest, 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. /// A relay chain block body.
/// ///
/// Included candidates should be sorted by parachain ID, and without duplicate /// Included candidates should be sorted by parachain ID, and without duplicate
@@ -86,25 +157,33 @@ pub struct Body {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use codec::Slicable;
use polkadot_serializer as ser; use polkadot_serializer as ser;
#[test] #[test]
fn test_header_serialization() { fn test_header_serialization() {
assert_eq!(ser::to_string_pretty(&Header { let header = Header {
parent_hash: 5.into(), parent_hash: 5.into(),
number: 67, number: 67,
state_root: 3.into(), state_root: 3.into(),
parachain_activity: parachain::Activity(vec![0]), transaction_root: 6.into(),
logs: vec![Log(vec![1])], digest: Digest { logs: vec![Log(vec![1])] },
}), r#"{ };
assert_eq!(ser::to_string_pretty(&header), r#"{
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000005", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000005",
"number": 67, "number": 67,
"stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000003", "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000003",
"parachainActivity": "0x00", "transactionRoot": "0x0000000000000000000000000000000000000000000000000000000000000006",
"logs": [ "digest": {
"0x01" "logs": [
] "0x01"
]
}
}"#); }"#);
let v = header.to_vec();
assert_eq!(Header::from_slice(&mut &v[..]).unwrap(), header);
} }
#[test] #[test]
+10
View File
@@ -34,6 +34,16 @@ macro_rules! impl_serde {
.map(|x| (&*x).into()) .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)
}
}
} }
} }
+17
View File
@@ -40,6 +40,7 @@ extern crate uint as uint_crate;
#[cfg(feature = "std")] #[cfg(feature = "std")]
extern crate core; extern crate core;
extern crate polkadot_runtime_codec as codec;
#[cfg(test)] #[cfg(test)]
extern crate polkadot_serializer; extern crate polkadot_serializer;
#[cfg(test)] #[cfg(test)]
@@ -50,6 +51,16 @@ extern crate pretty_assertions;
#[macro_use] #[macro_use]
extern crate alloc; 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; mod bytes;
pub mod block; pub mod block;
pub mod contract; 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. /// Alias to 520-bit hash when used in the context of a signature.
pub type Signature = hash::H512; pub type Signature = hash::H512;
/// A balance in the staking subsystem.
pub type Balance = u64;
/// A timestamp.
pub type Timestamp = u64;
/// A hash function. /// A hash function.
pub fn hash(data: &[u8]) -> hash::H256 { pub fn hash(data: &[u8]) -> hash::H256 {
blake2_256(data).into() blake2_256(data).into()
+20
View File
@@ -30,6 +30,16 @@ impl From<u64> for Id {
fn from(x: u64) -> Self { Id(x) } 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. /// Candidate parachain block.
/// ///
/// https://github.com/w3f/polkadot-spec/blob/master/spec.md#candidate-para-chain-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)] #[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)]
pub struct Activity(#[serde(with="bytes")] pub Vec<u8>); 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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
+83 -29
View File
@@ -19,12 +19,12 @@
//! This describes a combination of a function ID and data that can be used to call into //! This describes a combination of a function ID and data that can be used to call into
//! an internal function. //! 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)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[repr(u8)] #[repr(u8)]
pub enum InternalFunction { enum InternalFunctionId {
/// Set the system's code. /// Set the system's code.
SystemSetCode = 0, SystemSetCode = 0,
/// Set the number of sessions per era. /// Set the number of sessions per era.
@@ -39,17 +39,17 @@ pub enum InternalFunction {
SessionSetLength = 5, SessionSetLength = 5,
} }
impl InternalFunction { impl InternalFunctionId {
/// Derive `Some` value from a `u8`, or `None` if it's invalid. /// 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::*; use self::*;
let functions = [ let functions = [
InternalFunction::SystemSetCode, InternalFunctionId::SystemSetCode,
InternalFunction::StakingSetSessionsPerEra, InternalFunctionId::StakingSetSessionsPerEra,
InternalFunction::StakingSetBondingDuration, InternalFunctionId::StakingSetBondingDuration,
InternalFunction::StakingSetValidatorCount, InternalFunctionId::StakingSetValidatorCount,
InternalFunction::GovernanceSetApprovalPpmRequired, InternalFunctionId::GovernanceSetApprovalPpmRequired,
InternalFunction::SessionSetLength InternalFunctionId::SessionSetLength
]; ];
if (value as usize) < functions.len() { if (value as usize) < functions.len() {
Some(functions[value as usize]) 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. /// An internal function.
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Proposal { pub struct Proposal {
/// The privileged function to call. /// The privileged function to call.
pub function: InternalFunction, pub function: InternalFunction,
/// The serialised data to call it with.
#[serde(with = "bytes")]
pub input_data: Vec<u8>,
} }
#[cfg(test)] impl Slicable for Proposal {
mod test { fn from_slice(value: &mut &[u8]) -> Option<Self> {
use super::*; let id = try_opt!(u8::from_slice(value).and_then(InternalFunctionId::from_u8));
use support::StaticHexInto; let function = match id {
InternalFunctionId::SystemSetCode =>
#[test] InternalFunction::SystemSetCode(try_opt!(Slicable::from_slice(value))),
fn slicing_should_work() { InternalFunctionId::StakingSetSessionsPerEra =>
let p = Proposal { InternalFunction::StakingSetSessionsPerEra(try_opt!(Slicable::from_slice(value))),
function: InternalFunction::SystemSetCode, InternalFunctionId::StakingSetBondingDuration =>
input_data: b"Hello world".to_vec(), 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(); Some(Proposal { function })
assert_eq!(p, o); }
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())
} }
} }
+93 -10
View File
@@ -17,10 +17,12 @@
//! Polkadot runtime functions. //! Polkadot runtime functions.
//! This describes a function that can be called from an external transaction. //! This describes a function that can be called from an external transaction.
use codec::Slicable;
/// Public functions that can be dispatched to. /// Public functions that can be dispatched to.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[repr(u8)] #[repr(u8)]
pub enum Function { enum FunctionId {
/// Staking subsystem: begin staking. /// Staking subsystem: begin staking.
StakingStake = 0, StakingStake = 0,
/// Staking subsystem: stop staking. /// Staking subsystem: stop staking.
@@ -37,18 +39,99 @@ pub enum Function {
GovernanceApprove = 6, GovernanceApprove = 6,
} }
impl Function { impl FunctionId {
/// Derive `Some` value from a `u8`, or `None` if it's invalid. /// 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 { match value {
0 => Some(Function::StakingStake), 0 => Some(FunctionId::StakingStake),
1 => Some(Function::StakingUnstake), 1 => Some(FunctionId::StakingUnstake),
2 => Some(Function::StakingTransfer), 2 => Some(FunctionId::StakingTransfer),
3 => Some(Function::SessionSetKey), 3 => Some(FunctionId::SessionSetKey),
4 => Some(Function::TimestampSet), 4 => Some(FunctionId::TimestampSet),
5 => Some(Function::GovernancePropose), 5 => Some(FunctionId::GovernancePropose),
6 => Some(Function::GovernanceApprove), 6 => Some(FunctionId::GovernanceApprove),
_ => None, _ => 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())
}
}
+33
View File
@@ -17,6 +17,7 @@
//! Transaction type. //! Transaction type.
use bytes::{self, Vec}; use bytes::{self, Vec};
use codec::Slicable;
use runtime_function::Function; use runtime_function::Function;
#[cfg(feature = "std")] #[cfg(feature = "std")]
@@ -48,6 +49,38 @@ pub struct UncheckedTransaction {
pub signature: ::Signature, 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 { impl PartialEq for UncheckedTransaction {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.signature.iter().eq(other.signature.iter()) && self.transaction == other.transaction self.signature.iter().eq(other.signature.iter()) && self.transaction == other.transaction
-1
View File
@@ -22,7 +22,6 @@
mod endiansensitive; mod endiansensitive;
mod slicable; mod slicable;
mod streamreader;
mod joiner; mod joiner;
mod keyedvec; mod keyedvec;