From 74bcd90d7b0218f949e95776d030ef0d92c723d9 Mon Sep 17 00:00:00 2001 From: Aton Date: Tue, 18 Dec 2018 11:46:54 +0800 Subject: [PATCH] Feature/xsystem xsupport (#158) * add xsystem module * add xsupport --- Cargo.lock | 33 + Cargo.toml | 4 + cxrml/system/src/lib.rs | 68 -- runtime/src/lib.rs | 2 + xrml/xsupport/.gitkeep | 0 xrml/xsupport/Cargo.toml | 31 + xrml/xsupport/src/lib.rs | 16 + xrml/xsupport/src/storage/btree_map.rs | 35 + xrml/xsupport/src/storage/double_map.rs | 104 +++ xrml/xsupport/src/storage/linked_node.rs | 776 ++++++++++++++++++++ xrml/xsupport/src/storage/mod.rs | 5 + xrml/xsupport/src/tests.rs | 120 +++ xrml/xsystem/.gitkeep | 0 {cxrml/system => xrml/xsystem}/Cargo.toml | 16 +- xrml/xsystem/src/lib.rs | 84 +++ {cxrml/system => xrml/xsystem}/src/tests.rs | 0 16 files changed, 1218 insertions(+), 76 deletions(-) delete mode 100644 cxrml/system/src/lib.rs delete mode 100644 xrml/xsupport/.gitkeep create mode 100644 xrml/xsupport/Cargo.toml create mode 100644 xrml/xsupport/src/lib.rs create mode 100644 xrml/xsupport/src/storage/btree_map.rs create mode 100644 xrml/xsupport/src/storage/double_map.rs create mode 100644 xrml/xsupport/src/storage/linked_node.rs create mode 100644 xrml/xsupport/src/storage/mod.rs create mode 100644 xrml/xsupport/src/tests.rs delete mode 100644 xrml/xsystem/.gitkeep rename {cxrml/system => xrml/xsystem}/Cargo.toml (56%) create mode 100644 xrml/xsystem/src/lib.rs rename {cxrml/system => xrml/xsystem}/src/tests.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 6042a34825d51..a217128cd5b46 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4319,6 +4319,39 @@ dependencies = [ "substrate-primitives 0.1.0 (git+https://github.com/chainpool/substrate)", ] +[[package]] +name = "xrml-xsupport" +version = "0.3.0" +dependencies = [ + "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-io 0.1.0 (git+https://github.com/chainpool/substrate)", + "sr-primitives 0.1.0 (git+https://github.com/chainpool/substrate)", + "sr-std 0.1.0 (git+https://github.com/chainpool/substrate)", + "srml-support 0.1.0 (git+https://github.com/chainpool/substrate)", + "substrate-primitives 0.1.0 (git+https://github.com/chainpool/substrate)", +] + +[[package]] +name = "xrml-xsystem" +version = "0.4.0" +dependencies = [ + "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-io 0.1.0 (git+https://github.com/chainpool/substrate)", + "sr-primitives 0.1.0 (git+https://github.com/chainpool/substrate)", + "sr-std 0.1.0 (git+https://github.com/chainpool/substrate)", + "srml-support 0.1.0 (git+https://github.com/chainpool/substrate)", + "srml-system 0.1.0 (git+https://github.com/chainpool/substrate)", + "substrate-primitives 0.1.0 (git+https://github.com/chainpool/substrate)", +] + [[package]] name = "yamux" version = "0.1.3" diff --git a/Cargo.toml b/Cargo.toml index fc823225feeaa..1d7401b51e3b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,5 +20,9 @@ members = [ "executor", "runtime", "cli", + + # xrml + "xrml/xsystem", + "xrml/xsupport", "xrml/xexecutive", ] diff --git a/cxrml/system/src/lib.rs b/cxrml/system/src/lib.rs deleted file mode 100644 index fd9ad97b0a61b..0000000000000 --- a/cxrml/system/src/lib.rs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2018 Chainpool. - -//! this module is for chainx system - -#![cfg_attr(not(feature = "std"), no_std)] -// for encode/decode -// Needed for deriving `Serialize` and `Deserialize` for various types. -// We only implement the serde traits for std builds - they're unneeded -// in the wasm runtime. - -// Needed for deriving `Encode` and `Decode` for `RawEvent`. -//#[macro_use] -//extern crate parity_codec_derive; -extern crate parity_codec as codec; - -// for substrate -// Needed for the set of mock primitives used in our tests. -#[cfg(feature = "std")] -extern crate substrate_primitives; - -// for substrate runtime -// map!, vec! marco. -extern crate sr_std as rstd; - -#[cfg(feature = "std")] -extern crate sr_io as runtime_io; -extern crate sr_primitives as runtime_primitives; -// for substrate runtime module lib -// Needed for type-safe access to storage DB. -#[macro_use] -extern crate srml_support as runtime_support; -extern crate srml_system as system; - -#[cfg(test)] -mod tests; - -use rstd::prelude::*; -use runtime_support::dispatch::Result; -use runtime_support::StorageValue; - -use system::ensure_inherent; - -pub trait Trait: system::Trait {} - -decl_module! { - pub struct Module for enum Call where origin: T::Origin { - } -} - -decl_storage! { - trait Store for Module as CXSystem { - pub BlockProdocer get(block_producer): Option; - pub DeathAccount get(death_account) config(): T::AccountId; - pub FeeBuyAccount get(fee_buy_account) config(): T::AccountId; - } -} - -impl Module { - fn set_block_producer(origin: T::Origin, producer: T::AccountId) -> Result { - ensure_inherent(origin)?; - BlockProdocer::::put(producer); - Ok(()) - } - - fn on_finalise(_: T::BlockNumber) { - BlockProdocer::::kill(); - } -} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 9aa4014ff2a20..f3c011970e372 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -366,6 +366,8 @@ impl_runtime_apis! { .map(|v| (v.0, UncheckedExtrinsic::new_unsigned(Call::Consensus(v.1)))) ); + // TODO add blockproducer + inherent.as_mut_slice().sort_unstable_by_key(|v| v.0); inherent.into_iter().map(|v| v.1).collect() } diff --git a/xrml/xsupport/.gitkeep b/xrml/xsupport/.gitkeep deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/xrml/xsupport/Cargo.toml b/xrml/xsupport/Cargo.toml new file mode 100644 index 0000000000000..0a03cf827a214 --- /dev/null +++ b/xrml/xsupport/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "xrml-xsupport" +version = "0.3.0" +authors = ["Chainpool "] + +[dependencies] +hex-literal = "0.1.0" +serde = { version = "1.0", default_features = false } +serde_derive = { version = "1.0", optional = true } +parity-codec = { version = "2.0", default-features = false } +parity-codec-derive = { version = "2.0", default-features = false } +sr-std = { git = "https://github.com/chainpool/substrate", default_features = false } +sr-io = { git = "https://github.com/chainpool/substrate", default_features = false } +sr-primitives = { git = "https://github.com/chainpool/substrate", default_features = false } +srml-support = { git = "https://github.com/chainpool/substrate", default_features = false } +substrate-primitives = { git = "https://github.com/chainpool/substrate", default_features = false } + + +[features] +default = ["std"] +std=[ + "serde/std", + "serde_derive", + "parity-codec/std", + "parity-codec-derive/std", + "substrate-primitives/std", + "sr-std/std", + "sr-io/std", + "sr-primitives/std", + "srml-support/std", +] diff --git a/xrml/xsupport/src/lib.rs b/xrml/xsupport/src/lib.rs new file mode 100644 index 0000000000000..d441adb8531f8 --- /dev/null +++ b/xrml/xsupport/src/lib.rs @@ -0,0 +1,16 @@ +// Copyright 2018 Chainpool. + +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate parity_codec as codec; +#[macro_use] +extern crate parity_codec_derive; +extern crate sr_io as runtime_io; +extern crate sr_primitives as primitives; +extern crate sr_std as rstd; + +extern crate srml_support as runtime_support; + +pub use storage::double_map::StorageDoubleMap; + +pub mod storage; diff --git a/xrml/xsupport/src/storage/btree_map.rs b/xrml/xsupport/src/storage/btree_map.rs new file mode 100644 index 0000000000000..6e3ebdc3c72c5 --- /dev/null +++ b/xrml/xsupport/src/storage/btree_map.rs @@ -0,0 +1,35 @@ +// Copyright 2018 Chainpool. + +use codec::{Decode, Encode, Input, Output}; +use rstd::collections::btree_map::BTreeMap; + +#[derive(Default)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct CodecBTreeMap(pub BTreeMap); + +impl Encode for CodecBTreeMap { + fn encode_to(&self, dest: &mut W) { + let len = self.0.len(); + assert!( + len <= u32::max_value() as usize, + "Attempted to serialize a collection with too many elements." + ); + (len as u32).encode_to(dest); + for i in self.0.iter() { + i.encode_to(dest); + } + } +} + +impl Decode for CodecBTreeMap { + fn decode(input: &mut I) -> Option { + u32::decode(input).and_then(move |len| { + let mut r: BTreeMap = BTreeMap::new(); // with_capacity(len as usize); + for _ in 0..len { + let (key, v) = <(K, V)>::decode(input)?; + r.insert(key, v); + } + Some(CodecBTreeMap::(r)) + }) + } +} diff --git a/xrml/xsupport/src/storage/double_map.rs b/xrml/xsupport/src/storage/double_map.rs new file mode 100644 index 0000000000000..ecda527d841bc --- /dev/null +++ b/xrml/xsupport/src/storage/double_map.rs @@ -0,0 +1,104 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! An implementation of double map backed by storage. +//! +//! This implementation is somewhat specialized to the tracking of the storage of accounts. + +use codec::{Codec, Encode}; +use rstd::prelude::*; +use runtime_io::{blake2_256, twox_128}; +use runtime_support::storage::unhashed; + +/// Returns only a first part of the storage key. +/// +/// Hashed by XX. +#[allow(dead_code)] +fn first_part_of_key(k1: M::Key1) -> [u8; 16] { + let mut raw_prefix = Vec::new(); + raw_prefix.extend(M::PREFIX); + raw_prefix.extend(Encode::encode(&k1)); + twox_128(&raw_prefix) +} + +/// Returns a compound key that consist of the two parts: (prefix, `k1`) and `k2`. +/// +/// The first part is hased by XX and then concatenated with a blake2 hash of `k2`. +#[allow(dead_code)] +fn full_key(k1: M::Key1, k2: M::Key2) -> Vec { + let first_part = first_part_of_key::(k1); + let second_part = blake2_256(&Encode::encode(&k2)); + + let mut k = Vec::new(); + k.extend(&first_part); + k.extend(&second_part); + k +} + +/// An implementation of a map with a two keys. +/// +/// It provides an important ability to efficiently remove all entries +/// that have a common first key. +/// +/// # Mapping of keys to a storage path +/// +/// The storage key (i.e. the key under which the `Value` will be stored) is created from two parts. +/// The first part is a XX hash of a concatenation of the `PREFIX` and `Key1`. And the second part +/// is a blake2 hash of a `Key2`. +/// +/// Blake2 is used for `Key2` is because it will be used as a key for contract's storage and +/// thus will be susceptible for a untrusted input. +pub trait StorageDoubleMap { + type Key1: Codec; + type Key2: Codec; + type Value: Codec + Default; + + const PREFIX: &'static [u8]; + + /// Insert an entry into this map. + fn insert(k1: Self::Key1, k2: Self::Key2, val: Self::Value) { + unhashed::put(&full_key::(k1, k2)[..], &val); + } + + /// Remove an entry from this map. + fn remove(k1: Self::Key1, k2: Self::Key2) { + unhashed::kill(&full_key::(k1, k2)[..]); + } + + /// Get an entry from this map. + /// + /// If there is entry stored under the given keys, returns `None`. + fn get(k1: Self::Key1, k2: Self::Key2) -> Option { + unhashed::get(&full_key::(k1, k2)[..]) + } + + /// Get an entry from this map. + /// + /// If there is entry stored under the given keys, returns default value. + fn get_or_default(k1: Self::Key1, k2: Self::Key2) -> Self::Value { + unhashed::get_or_default(&full_key::(k1, k2)[..]) + } + + /// Removes all entries that shares the `k1` as the first key. + fn remove_prefix(k1: Self::Key1) { + unhashed::kill_prefix(&first_part_of_key::(k1)) + } + + /// Remove and return the value, None for the value not exist. + fn take(k1: Self::Key1, k2: Self::Key2) -> Option { + unhashed::take(&full_key::(k1, k2)[..]) + } +} diff --git a/xrml/xsupport/src/storage/linked_node.rs b/xrml/xsupport/src/storage/linked_node.rs new file mode 100644 index 0000000000000..8a61597412b4f --- /dev/null +++ b/xrml/xsupport/src/storage/linked_node.rs @@ -0,0 +1,776 @@ +// Copyright 2018 Chainpool. + +use codec::Codec; +use runtime_support::dispatch::Result; +use runtime_support::{StorageMap, StorageValue}; + +pub trait NodeT { + type Index: Codec + Clone + Eq + PartialEq + Default; + fn index(&self) -> Self::Index; +} + +#[derive(Decode, Encode, Clone, Default)] +pub struct Node { + prev: Option, + next: Option, + pub data: T, +} + +impl Node { + pub fn prev(&self) -> Option { + match &self.prev { + Some(i) => Some(i.clone()), + None => None, + } + } + pub fn next(&self) -> Option { + match &self.next { + Some(i) => Some(i.clone()), + + None => None, + } + } + pub fn index(&self) -> T::Index { + self.data.index() + } + pub fn is_none(&self) -> bool { + self.prev.is_none() && self.next.is_none() + } +} + +pub trait NormalNodeT { + type NodeType; + fn data(&self) -> &Self::NodeType; + fn mut_data(&mut self) -> &mut Self::NodeType; +} + +impl NormalNodeT for Node { + type NodeType = Node; + fn data(&self) -> &Node { + self + } + fn mut_data(&mut self) -> &mut Node { + self + } +} + +#[derive(Decode, Encode, Eq, PartialEq, Clone, Default)] +pub struct NodeIndex { + index: T::Index, +} + +impl NodeIndex { + pub fn index(&self) -> T::Index { + self.index.clone() + } +} + +pub trait NodeIndexT { + type IndexType; + fn data(&self) -> &Self::IndexType; +} + +impl NodeIndexT for NodeIndex { + type IndexType = NodeIndex; + fn data(&self) -> &NodeIndex { + self + } +} + +pub trait MultiNodeIndexT { + type KeyType; + type IndexType; + fn data(&self) -> &Self::IndexType; + fn key(&self) -> Self::KeyType; +} + +#[derive(Decode, Encode, Eq, PartialEq, Clone, Default)] +pub struct MultiNodeIndex +where + K: Codec + Clone + Eq + PartialEq + Default, +{ + index: T::Index, + multi_key: K, +} + +impl MultiNodeIndex +where + K: Codec + Clone + Eq + PartialEq + Default, +{ + pub fn index(&self) -> T::Index { + self.index.clone() + } +} + +impl MultiNodeIndexT for MultiNodeIndex +where + K: Codec + Clone + Eq + PartialEq + Default, +{ + type KeyType = K; + type IndexType = MultiNodeIndex; + fn data(&self) -> &MultiNodeIndex { + self + } + fn key(&self) -> K { + self.multi_key.clone() + } +} + +pub trait OptionT { + type OptionType; + fn data(&self) -> Option<&Self::OptionType>; + fn mut_data(&mut self) -> Option<&mut Self::OptionType>; +} + +impl OptionT for Option> { + type OptionType = Node; + fn data(&self) -> Option<&Node> { + match self { + None => None, + Some(ref i) => Some(i), + } + } + + fn mut_data(&mut self) -> Option<&mut Node> { + match self { + None => None, + Some(ref mut i) => Some(i), + } + } +} + +impl OptionT for Option> { + type OptionType = NodeIndex; + fn data(&self) -> Option<&NodeIndex> { + match self { + None => None, + Some(ref i) => Some(i), + } + } + + fn mut_data(&mut self) -> Option<&mut NodeIndex> { + match self { + None => None, + Some(ref mut i) => Some(i), + } + } +} + +impl OptionT for Option> +where + K: Codec + Clone + Eq + PartialEq + Default, +{ + type OptionType = MultiNodeIndex; + fn data(&self) -> Option<&MultiNodeIndex> { + match self { + None => None, + Some(ref i) => Some(i), + } + } + + fn mut_data(&mut self) -> Option<&mut MultiNodeIndex> { + match self { + None => None, + Some(ref mut i) => Some(i), + } + } +} + +pub trait LinkedNodeCollection { + type Header; + type NodeMap; + type Tail; +} + +impl Node { + pub fn new(data: T) -> Node { + Node:: { + prev: None, + next: None, + data, + } + } + + pub fn add_option_node_before(&mut self, mut node: Node) -> Result + where + C::NodeMap: StorageMap>, + C::Header: StorageValue>, + ::Index, Node>>::Query: + OptionT>, + { + let i = self.index(); + if i == node.index() { + return Ok(()); + } + match &self.prev { + Some(p) => { + C::NodeMap::mutate(p, |prev| { + // TODO add Result when substrate update + if let Some(ref mut prev_node) = prev.mut_data() { + node.prev = Some(prev_node.index()); + node.next = Some(i); + prev_node.next = Some(node.index()); + } + }); + } + None => { + node.prev = None; + node.next = Some(i); + C::Header::put(NodeIndex:: { + index: node.index(), + }); + } + } + if node.is_none() { + // something err + return Err("do add for a invalid node"); + } + self.prev = Some(node.index()); + C::NodeMap::insert(self.index(), self); + + let i = node.index(); + C::NodeMap::insert(i, node); + Ok(()) + } + + pub fn add_option_node_after(&mut self, mut node: Node) -> Result + where + C::NodeMap: StorageMap>, + C::Tail: StorageValue>, + ::Index, Node>>::Query: + OptionT>, + { + let i = self.index(); + if i == node.index() { + return Ok(()); + } + match &self.next { + Some(n) => { + C::NodeMap::mutate(n, |next| { + // TODO add Result when substrate update + if let Some(ref mut next_node) = next.mut_data() { + node.prev = Some(i); + node.next = Some(next_node.index()); + next_node.prev = Some(node.index()); + } + }) + } + None => { + node.prev = Some(i); + node.next = None; + C::Tail::put(NodeIndex:: { + index: node.index(), + }); + } + } + if node.is_none() { + return Err("do add for a invalid node"); + } + self.next = Some(node.index()); + C::NodeMap::insert(self.index(), self); + let i = node.index(); + C::NodeMap::insert(i, node); + Ok(()) + } + + pub fn remove_option_node(&mut self) -> Result + where + C::NodeMap: StorageMap>, + C::Header: StorageValue>, + C::Tail: StorageValue>, + ::Index, Node>>::Query: + OptionT>, + >>::Query: OptionT>, + >>::Query: OptionT>, + { + if self.is_none() { + let self_index = self.index(); + C::NodeMap::remove(&self_index); + if let Some(header) = C::Header::get().data() { + if self_index == header.index { + C::Header::kill(); + } + } + + if let Some(tail) = C::Tail::get().data() { + if self_index == tail.index { + C::Tail::kill(); + } + } + return Ok(()); + } + + if self.prev.is_none() { + match &self.next { + Some(next) => { + C::NodeMap::mutate(next, |next| { + // TODO add Result when substrate update + if let Some(next_node) = next.mut_data() { + next_node.prev = None; + C::Header::put(NodeIndex:: { + index: next_node.index(), + }); + C::NodeMap::remove(self.index()); + } + }) + } + None => { + // something err + return Err("prev is none, next should't be none"); + } + } + } else if self.next.is_none() { + match &self.prev { + Some(prev) => { + C::NodeMap::mutate(prev, |prev| { + // TODO add Result when substrate update + if let Some(prev_node) = prev.mut_data() { + prev_node.next = None; + C::Tail::put(NodeIndex:: { + index: prev_node.index(), + }); + C::NodeMap::remove(self.index()); + } + }) + } + None => { + // something err + return Err("next is none, prev should't be none"); + } + } + } else { + let prev = self.prev.clone().unwrap_or(Default::default()); + let next = self.next.clone().unwrap_or(Default::default()); + + C::NodeMap::mutate(&prev, |prev| { + // TODO add Result when substrate update + if let Some(prev_node) = prev.mut_data() { + prev_node.next = Some(next.clone()); + self.prev = None; + } + }); + C::NodeMap::mutate(&next, |next| { + // TODO add Result when substrate update + if let Some(next_node) = next.mut_data() { + next_node.prev = Some(prev.clone()); + self.next = None; + } + }); + C::NodeMap::remove(self.index()); + } + Ok(()) + } + + pub fn add_node_before(&mut self, mut node: Node) -> Result + where + C::NodeMap: StorageMap>, + C::Header: StorageValue>, + ::Index, Node>>::Query: + NormalNodeT>, + { + let i = self.index(); + if i == node.index() { + return Ok(()); + } + match &self.prev { + Some(p) => C::NodeMap::mutate(p, |prev_node| { + if prev_node.data().is_none() == false { + node.prev = Some(prev_node.data().index()); + node.next = Some(i); + prev_node.mut_data().next = Some(node.index()); + } + }), + None => { + node.prev = None; + node.next = Some(i); + C::Header::put(NodeIndex:: { + index: node.index(), + }); + } + } + if node.is_none() { + // something err + return Err("do add for a invalid node"); + } + + self.prev = Some(node.index()); + C::NodeMap::insert(self.index(), self); + + let i = node.index(); + C::NodeMap::insert(i, node); + Ok(()) + } + + pub fn add_node_after(&mut self, mut node: Node) -> Result + where + C::NodeMap: StorageMap>, + C::Tail: StorageValue>, + ::Index, Node>>::Query: + NormalNodeT>, + { + let i = self.index(); + if i == node.index() { + return Ok(()); + } + match &self.next { + Some(n) => C::NodeMap::mutate(n, |next_node| { + if next_node.data().is_none() == false { + node.prev = Some(i); + node.next = Some(next_node.data().index()); + next_node.mut_data().prev = Some(node.index()); + } + }), + None => { + node.prev = Some(i); + node.next = None; + C::Tail::put(NodeIndex:: { + index: node.index(), + }); + } + } + if node.is_none() { + return Err("do add for a invalid node"); + } + self.next = Some(node.index()); + C::NodeMap::insert(self.index(), self); + let i = node.index(); + C::NodeMap::insert(i, node); + Ok(()) + } + + pub fn remove_node(&mut self) -> Result + where + C::NodeMap: StorageMap>, + C::Header: StorageValue>, + C::Tail: StorageValue>, + ::Index, Node>>::Query: + NormalNodeT>, + >>::Query: NodeIndexT>, + >>::Query: NodeIndexT>, + { + if self.is_none() { + let self_index = self.index(); + let head_index = C::Header::get(); + let tail_index = C::Tail::get(); + C::NodeMap::remove(&self_index); + if self_index == head_index.data().index && self_index == tail_index.data().index { + C::Header::kill(); + C::Tail::kill(); + } else { + // something err + return Err("remove the node normally but meet err for header and tail"); + } + return Ok(()); + } + + if self.prev.is_none() { + match &self.next { + Some(next) => C::NodeMap::mutate(next, |next_node| { + if next_node.data().is_none() == false { + next_node.mut_data().prev = None; + C::Header::put(NodeIndex:: { + index: next_node.data().index(), + }); + C::NodeMap::remove(self.index()); + } + }), + None => { + // something err + return Err("prev is none, next should't be none"); + } + } + } else if self.next.is_none() { + match &self.prev { + Some(prev) => C::NodeMap::mutate(prev, |prev_node| { + if prev_node.data().is_none() == false { + prev_node.mut_data().next = None; + C::Tail::put(NodeIndex:: { + index: prev_node.data().index(), + }); + C::NodeMap::remove(self.index()); + } + }), + None => { + // something err + return Err("next is none, prev should't be none"); + } + } + } else { + let prev = self.prev.clone().unwrap_or(Default::default()); + let next = self.next.clone().unwrap_or(Default::default()); + + C::NodeMap::mutate(&prev, |prev_node| { + if prev_node.data().is_none() == false { + prev_node.mut_data().next = Some(next.clone()); + self.prev = None; + } + }); + C::NodeMap::mutate(&next, |next_node| { + if next_node.data().is_none() == false { + next_node.mut_data().prev = Some(prev.clone()); + self.next = None; + } + }); + if self.is_none() { + C::NodeMap::remove(self.index()); + } else { + // something err + return Err("prev or next not exist in the storage yet, do not remove this node, but link maybe has been changed"); + } + } + Ok(()) + } + + pub fn init_storage(&self) + where + C::NodeMap: StorageMap>, + C::Header: StorageValue>, + C::Tail: StorageValue>, + { + if C::Header::exists() == false { + C::Header::put(NodeIndex:: { + index: self.index(), + }); + } + if C::Tail::exists() == false { + C::Tail::put(NodeIndex:: { + index: self.index(), + }); + } + C::NodeMap::insert(self.index(), self); + } +} + +// for multi index +impl Node { + pub fn init_storage_withkey(&self, key: K) + where + K: Codec + Clone + Eq + PartialEq + Default, + C::NodeMap: StorageMap>, + C::Header: + StorageMap< as MultiNodeIndexT>::KeyType, MultiNodeIndex>, + C::Tail: + StorageMap< as MultiNodeIndexT>::KeyType, MultiNodeIndex>, + { + if C::Header::exists(&key) == false { + C::Header::insert( + key.clone(), + MultiNodeIndex:: { + index: self.index(), + multi_key: key.clone(), + }, + ); + } + if C::Tail::exists(&key) == false { + C::Tail::insert( + key.clone(), + MultiNodeIndex:: { + index: self.index(), + multi_key: key.clone(), + }, + ); + } + C::NodeMap::insert(self.index(), self); + } + + pub fn add_option_node_before_withkey( + &mut self, + mut node: Node, + key: K, + ) -> Result + where + K: Codec + Clone + Eq + PartialEq + Default, + C::NodeMap: StorageMap>, + C::Header: + StorageMap< as MultiNodeIndexT>::KeyType, MultiNodeIndex>, + ::Index, Node>>::Query: + OptionT>, + { + let i = self.index(); + if i == node.index() { + return Ok(()); + } + match &self.prev { + Some(p) => { + C::NodeMap::mutate(p, |prev| { + // TODO add Result when substrate update + if let Some(ref mut prev_node) = prev.mut_data() { + node.prev = Some(prev_node.index()); + node.next = Some(i); + prev_node.next = Some(node.index()); + } + }); + } + None => { + node.prev = None; + node.next = Some(i); + C::Header::insert( + key.clone(), + MultiNodeIndex:: { + index: node.index(), + multi_key: key, + }, + ); + } + } + if node.is_none() { + // something err + return Err("do add for a invalid node"); + } + self.prev = Some(node.index()); + C::NodeMap::insert(self.index(), self); + + let i = node.index(); + C::NodeMap::insert(i, node); + Ok(()) + } + + pub fn add_option_node_after_withkey( + &mut self, + mut node: Node, + key: K, + ) -> Result + where + K: Codec + Clone + Eq + PartialEq + Default, + C::NodeMap: StorageMap>, + C::Tail: + StorageMap< as MultiNodeIndexT>::KeyType, MultiNodeIndex>, + ::Index, Node>>::Query: + OptionT>, + { + let i = self.index(); + if i == node.index() { + return Ok(()); + } + match &self.next { + Some(n) => { + C::NodeMap::mutate(n, |next| { + // TODO add Result when substrate update + if let Some(ref mut next_node) = next.mut_data() { + node.prev = Some(i); + node.next = Some(next_node.index()); + next_node.prev = Some(node.index()); + } + }) + } + None => { + node.prev = Some(i); + node.next = None; + C::Tail::insert( + key.clone(), + MultiNodeIndex:: { + index: node.index(), + multi_key: key, + }, + ); + } + } + if node.is_none() { + return Err("do add for a invalid node"); + } + self.next = Some(node.index()); + C::NodeMap::insert(self.index(), self); + let i = node.index(); + C::NodeMap::insert(i, node); + Ok(()) + } + + pub fn remove_option_node_withkey(&mut self, key: K) -> Result + where + K: Codec + Clone + Eq + PartialEq + Default, + C::NodeMap: StorageMap>, + C::Header: StorageMap>, + C::Tail: StorageMap>, + ::Index, Node>>::Query: + OptionT>, + >>::Query: + OptionT>, + >>::Query: + OptionT>, + { + if self.is_none() { + let self_index = self.index(); + C::NodeMap::remove(&self_index); + if let Some(header) = C::Header::get(&key).data() { + if self_index == header.index { + C::Header::remove(&key); + } + } + + if let Some(tail) = C::Tail::get(&key).data() { + if self_index == tail.index { + C::Tail::remove(&key); + } + } + return Ok(()); + } + + if self.prev.is_none() { + match &self.next { + Some(next) => { + C::NodeMap::mutate(next, |next| { + // TODO add Result when substrate update + if let Some(next_node) = next.mut_data() { + next_node.prev = None; + C::Header::insert( + key.clone(), + MultiNodeIndex:: { + index: next_node.index(), + multi_key: key, + }, + ); + C::NodeMap::remove(self.index()); + } + }) + } + None => { + // something err + return Err("prev is none, next should't be none"); + } + } + } else if self.next.is_none() { + match &self.prev { + Some(prev) => { + C::NodeMap::mutate(prev, |prev| { + // TODO add Result when substrate update + if let Some(prev_node) = prev.mut_data() { + prev_node.next = None; + C::Tail::insert( + key.clone(), + MultiNodeIndex:: { + index: prev_node.index(), + multi_key: key, + }, + ); + C::NodeMap::remove(self.index()); + } + }) + } + None => { + // something err + return Err("next is none, prev should't be none"); + } + } + } else { + let prev = self.prev.clone().unwrap_or(Default::default()); + let next = self.next.clone().unwrap_or(Default::default()); + + C::NodeMap::mutate(&prev, |prev| { + // TODO add Result when substrate update + if let Some(prev_node) = prev.mut_data() { + prev_node.next = Some(next.clone()); + self.prev = None; + } + }); + C::NodeMap::mutate(&next, |next| { + // TODO add Result when substrate update + if let Some(next_node) = next.mut_data() { + next_node.prev = Some(prev.clone()); + self.next = None; + } + }); + C::NodeMap::remove(self.index()); + } + Ok(()) + } +} diff --git a/xrml/xsupport/src/storage/mod.rs b/xrml/xsupport/src/storage/mod.rs new file mode 100644 index 0000000000000..2434adab417b1 --- /dev/null +++ b/xrml/xsupport/src/storage/mod.rs @@ -0,0 +1,5 @@ +// Copyright 2018 Chainpool. + +pub mod btree_map; +pub mod double_map; +pub mod linked_node; diff --git a/xrml/xsupport/src/tests.rs b/xrml/xsupport/src/tests.rs new file mode 100644 index 0000000000000..c977ce382ab8d --- /dev/null +++ b/xrml/xsupport/src/tests.rs @@ -0,0 +1,120 @@ +// Copyright 2018 Chainpool. + +use substrate_primitives::{Blake2Hasher, H256}; + +use primitives::testing::{Digest, DigestItem, Header}; +use primitives::traits::BlakeTwo256; +use primitives::BuildStorage; +use runtime_io; +use runtime_io::with_externalities; + +use super::*; + +impl_outer_origin! { + pub enum Origin for Test {} +} + +#[derive(Clone, Eq, PartialEq)] +pub struct Test; + +impl system::Trait for Test { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type Digest = Digest; + type AccountId = u64; + type Header = Header; + type Event = (); + type Log = DigestItem; +} + +impl balances::Trait for Test { + type Balance = u64; + type AccountIndex = u64; + type OnFreeBalanceZero = (); + type EnsureAccountLiquid = (); + type Event = (); +} + +impl cxsystem::Trait for Test {} + +impl associations::Trait for Test { + type OnCalcFee = CXSupport; + type Event = (); +} + +impl Trait for Test {} + +type Balances = balances::Module; +type CXSystem = cxsystem::Module; +type CXSupport = Module; + +pub fn new_test_ext() -> runtime_io::TestExternalities { + let mut r = system::GenesisConfig::::default() + .build_storage() + .unwrap(); + // balances + r.extend( + balances::GenesisConfig:: { + balances: vec![(1, 1000), (2, 510), (3, 1000)], + transaction_base_fee: 0, + transaction_byte_fee: 0, + existential_deposit: 0, + transfer_fee: 0, + creation_fee: 0, + reclaim_rebate: 0, + } + .build_storage() + .unwrap(), + ); + // cxsystem + r.extend( + cxsystem::GenesisConfig:: { death_account: 100 } + .build_storage() + .unwrap(), + ); + + r.into() +} + +#[test] +fn test_no_relation_no_producer() { + with_externalities(&mut new_test_ext(), || { + assert_ok!(CXSupport::handle_fee_before(&1, 100, true, || Ok(()))); + + assert_eq!(Balances::free_balance(CXSystem::death_account()), 100); + }) +} + +#[test] +fn test_no_relation_with_producer() { + with_externalities(&mut new_test_ext(), || { + let origin = system::RawOrigin::Inherent.into(); + CXSystem::set_block_producer(origin, 5).unwrap(); + + assert_ok!(CXSupport::handle_fee_before(&1, 99, true, || Ok(()))); + assert_eq!(Balances::free_balance(5), 99); + }) +} + +#[test] +fn test_with_relation_with_producer() { + with_externalities(&mut new_test_ext(), || { + use runtime_support::StorageMap; + + let origin = system::RawOrigin::Inherent.into(); + CXSystem::set_block_producer(origin, 5).unwrap(); + + let origin = system::RawOrigin::Signed(1).into(); + assert_ok!(associations::Module::::init_account(origin, 10, 100)); + assert_eq!(balances::FreeBalance::::exists(10), true); + + assert_ok!(CXSupport::handle_fee_before(&10, 99, true, || Ok(()))); + + assert_eq!(Balances::free_balance(1), 1000 - 100 + 49); + assert_eq!(Balances::free_balance(5), 50); // block producer + assert_eq!(Balances::free_balance(10), 100 - 99); + }) +} diff --git a/xrml/xsystem/.gitkeep b/xrml/xsystem/.gitkeep deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/cxrml/system/Cargo.toml b/xrml/xsystem/Cargo.toml similarity index 56% rename from cxrml/system/Cargo.toml rename to xrml/xsystem/Cargo.toml index f4f165aec1e17..e419ecf70719c 100644 --- a/cxrml/system/Cargo.toml +++ b/xrml/xsystem/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "cxrml-system" -version = "0.3.0" +name = "xrml-xsystem" +version = "0.4.0" authors = ["Chainpool "] @@ -10,12 +10,12 @@ serde = { version = "1.0", default_features = false } serde_derive = { version = "1.0", optional = true } parity-codec = { version = "2.0", default-features = false } parity-codec-derive = { version = "2.0", default-features = false } -substrate-primitives = { git = "https://github.com/paritytech/substrate", default_features = false } -sr-std = { git = "https://github.com/paritytech/substrate", default_features = false } -sr-io = { git = "https://github.com/paritytech/substrate", default_features = false } -sr-primitives = { git = "https://github.com/paritytech/substrate", default_features = false } -srml-support = { git = "https://github.com/paritytech/substrate", default_features = false } -srml-system = { git = "https://github.com/paritytech/substrate", default_features = false } +substrate-primitives = { git = "https://github.com/chainpool/substrate", default_features = false } +sr-std = { git = "https://github.com/chainpool/substrate", default_features = false } +sr-io = { git = "https://github.com/chainpool/substrate", default_features = false } +sr-primitives = { git = "https://github.com/chainpool/substrate", default_features = false } +srml-support = { git = "https://github.com/chainpool/substrate", default_features = false } +srml-system = { git = "https://github.com/chainpool/substrate", default_features = false } [features] diff --git a/xrml/xsystem/src/lib.rs b/xrml/xsystem/src/lib.rs new file mode 100644 index 0000000000000..a89ac9a1a6ec5 --- /dev/null +++ b/xrml/xsystem/src/lib.rs @@ -0,0 +1,84 @@ +// Copyright 2018 Chainpool. + +//! this module is for chainx system + +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate parity_codec as codec; + +// for substrate +#[cfg(feature = "std")] +extern crate substrate_primitives; + +// for substrate runtime +// map!, vec! marco. +extern crate sr_std as rstd; + +#[cfg(feature = "std")] +extern crate sr_io as runtime_io; +extern crate sr_primitives as runtime_primitives; +// for substrate runtime module lib +// Needed for type-safe access to storage DB. +#[macro_use] +extern crate srml_support as runtime_support; +extern crate srml_system as system; + +#[cfg(test)] +mod tests; + +use rstd::prelude::*; +use rstd::result; +use runtime_support::dispatch::Result; +use runtime_support::StorageValue; +use runtime_primitives::CheckInherentError; +use runtime_primitives::traits::{ProvideInherent, Block as BlockT}; + +use system::ensure_inherent; + +pub trait Trait: system::Trait { + const XSYSTEM_SET_POSITION: u32; +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + fn set_block_producer(origin, producer: T::AccountId) -> Result { + ensure_inherent(origin)?; + + assert!( + >::extrinsic_index() == Some(T::XSYSTEM_SET_POSITION), + "BlockProducer extrinsic must be at position {} in the block", + T::XSYSTEM_SET_POSITION + ); + + BlockProdocer::::put(producer); + Ok(()) + } + fn on_finalise(_n: T::BlockNumber) { + BlockProdocer::::kill(); + } + } +} + +decl_storage! { + trait Store for Module as XSystem { + pub BlockProdocer get(block_producer): Option; + pub DeathAccount get(death_account) config(): T::AccountId; + // TODO remove this to other module + pub FeeBuyAccount get(fee_buy_account) config(): T::AccountId; + } +} + +impl ProvideInherent for Module { + type Inherent = T::AccountId; + type Call = Call; + + fn create_inherent_extrinsics(data: Self::Inherent) -> Vec<(u32, Self::Call)> { + vec![(T::XSYSTEM_SET_POSITION, Call::set_block_producer(data))] + } + + fn check_inherent Option<&Self::Call>>( + _block: &Block, _data: Self::Inherent, _extract_function: &F + ) -> result::Result<(), CheckInherentError> { + Ok(()) + } +} diff --git a/cxrml/system/src/tests.rs b/xrml/xsystem/src/tests.rs similarity index 100% rename from cxrml/system/src/tests.rs rename to xrml/xsystem/src/tests.rs