Skip to content
Snippets Groups Projects
Commit 2bc7c573 authored by asynchronous rob's avatar asynchronous rob
Browse files

reintroduce slicable to primitives

parent e4e755a8
No related merge requests found
......@@ -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" }
......
......@@ -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]
......
......@@ -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)
}
}
}
}
......
......@@ -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()
......
......@@ -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::*;
......
......@@ -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())
}
}
......@@ -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())
}
}
......@@ -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
......
......@@ -22,7 +22,6 @@
mod endiansensitive;
mod slicable;
mod streamreader;
mod joiner;
mod keyedvec;
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment