diff --git a/README.md b/README.md index ffb32976125f0..35e32bec0ba63 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,104 @@ # Polkadot -Implementation of a https://polkadot.io node in Rust. \ No newline at end of file +Implementation of a https://polkadot.io node in Rust. + +## To play + +If you'd like to play with Polkadot, you'll need to install a client like this +one. First, get Rust and the support software if you don't already have it: + +``` +curl https://sh.rustup.rs -sSf | sh +sudo apt install make clang +``` + +Then, install Polkadot PoC-1: + +``` +cargo install --git https://github.com/paritytech/polkadot.git --branch v0.1.0 +``` + +You'll now have a `polkadot` binary installed to your `PATH`. You can drop the +`--branch v0.1.0` to get the very latest version of Polkadot, but these +instructions might not work in that case. + +### Development + +You can run a simple single-node development "network" on your machine by +running in a terminal: + +``` +polkadot --chain=dev --validator --key Alice +``` + +You can muck around by cloning and building the http://github.com/paritytech/polka-ui and http://github.com/paritytech/polkadot-ui or just heading to https://polkadot.js.org/apps. + +### PoC-1 Testnet + +You can also connect to the global PoC-1 testnet. To do this, just use: + +``` +polkadot --chain=poc-1 +``` + +If you want to do anything on it (not that there's much to do), then you'll need +to get some PoC-1 testnet DOTs. Ask in the Polkadot watercooler. + +## Local Two-node Testnet + +If you want to see the multi-node consensus algorithm in action locally, then +you can create a local testnet. You'll need two terminals open. In one, run: + +``` +polkadot --chain=dev --validator --key Alice -d /tmp/alice +``` + +and in the other, run: + +``` +polkadot --chain=dev --validator --key Bob -d /tmp/bob --port 30334 --bootnodes 'enode://ALICE_BOOTNODE_ID_HERE@127.0.0.1:30333' +``` + +Ensure you replace `ALICE_BOOTNODE_ID_HERE` with the node ID from the output of +the first terminal. + +## Hacking on Polkadot + +If you'd actually like hack on Polkadot, you can just grab the source code and +build it. Ensure you have Rust and the support software installed: + +``` +curl https://sh.rustup.rs -sSf | sh +rustup update nightly +rustup target add wasm32-unknown-unknown --toolchain nightly +rustup update stable +cargo install --git https://github.com/alexcrichton/wasm-gc +cargo install --git https://github.com/pepyakin/wasm-export-table.git +sudo apt install make clang +``` + +Then, grab the Polkadot source code: + +``` +git clone https://github.com/paritytech/polkadot.git +cd polkadot +``` + +Then build the code: + +``` +./build.sh # Builds the WebAssembly binaries +cargo build # Builds all native code +``` + +You can run the tests if you like: + +``` +cargo test --all +``` + +You can start a development chain with: + +``` +cargo run -- --chain=dev --validator --key Alice +``` diff --git a/demo/executor/src/lib.rs b/demo/executor/src/lib.rs index 17351de138ab5..3d0aa193e40eb 100644 --- a/demo/executor/src/lib.rs +++ b/demo/executor/src/lib.rs @@ -197,7 +197,7 @@ mod tests { construct_block( 1, [69u8; 32].into(), - hex!("57ba67304318efaee95c4f9ab95ed5704eafe030bc8db2df00acb08c2f4979c8").into(), + hex!("a63d59c6a7347cd7a1dc1ec139723b531f0ac450e39b1c532d5ca69ff74ad811").into(), vec![Extrinsic { signed: Alice.into(), index: 0, @@ -210,7 +210,7 @@ mod tests { construct_block( 2, block1().1, - hex!("ead4c60c0cad06b7ee73e64efeec2d4eb82c651469fb2ec748cfe5026bea5c49").into(), + hex!("1c3623b2e3f7e43752debb9015bace4f6931593579b5af34457b931315f5e2ab").into(), vec![ Extrinsic { signed: Bob.into(), diff --git a/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm b/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm index ca7192b60ea6f..2823d62ae5865 100644 Binary files a/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm and b/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm differ diff --git a/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm b/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm index 8b6b21ce23360..d95558e2ed6ac 100755 Binary files a/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm and b/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm differ diff --git a/polkadot/api/src/lib.rs b/polkadot/api/src/lib.rs index e178f1d547c24..bf747624c3945 100644 --- a/polkadot/api/src/lib.rs +++ b/polkadot/api/src/lib.rs @@ -31,9 +31,6 @@ extern crate substrate_state_machine as state_machine; #[macro_use] extern crate error_chain; -#[macro_use] -extern crate log; - #[cfg(test)] extern crate substrate_keyring as keyring; @@ -42,7 +39,7 @@ use client::Client; use polkadot_executor::Executor as LocalDispatch; use substrate_executor::{NativeExecutionDispatch, NativeExecutor}; use state_machine::OverlayedChanges; -use primitives::{AccountId, BlockId, Index, SessionKey, Timestamp}; +use primitives::{AccountId, BlockId, Hash, Index, SessionKey, Timestamp}; use primitives::parachain::DutyRoster; use runtime::{Block, Header, UncheckedExtrinsic, Extrinsic, Call, TimestampCall}; @@ -126,6 +123,9 @@ pub trait PolkadotApi { /// Get validators at a given block. fn validators(&self, at: &Self::CheckedBlockId) -> Result>; + /// Get the value of the randomness beacon at a given block. + fn random_seed(&self, at: &Self::CheckedBlockId) -> Result; + /// Get the authority duty roster at a block. fn duty_roster(&self, at: &Self::CheckedBlockId) -> Result; @@ -191,6 +191,10 @@ impl PolkadotApi for Client> with_runtime!(self, at, ::runtime::Session::validators) } + fn random_seed(&self, at: &CheckedId) -> Result { + with_runtime!(self, at, ::runtime::System::random_seed) + } + fn duty_roster(&self, at: &CheckedId) -> Result { // duty roster can only be queried at the start of a block, // so we create a dummy. diff --git a/polkadot/cli/src/cli.yml b/polkadot/cli/src/cli.yml index 07d3c0ec66d13..191d5bb4b21a0 100644 --- a/polkadot/cli/src/cli.yml +++ b/polkadot/cli/src/cli.yml @@ -56,6 +56,6 @@ args: - chain: long: chain value_name: CHAIN_SPEC - help: Specify the chain specification (one of dev or poc-1) + help: Specify the chain specification (one of dev, local or poc-1) takes_value: true subcommands: diff --git a/polkadot/cli/src/lib.rs b/polkadot/cli/src/lib.rs index d10fdfd32b849..f09324f6964d4 100644 --- a/polkadot/cli/src/lib.rs +++ b/polkadot/cli/src/lib.rs @@ -126,13 +126,15 @@ pub fn run(args: I) -> error::Result<()> where } match matches.value_of("chain") { - Some("poc-1") => config.chain_spec = ChainSpec::PoC1Testnet, Some("dev") => config.chain_spec = ChainSpec::Development, + Some("local") => config.chain_spec = ChainSpec::LocalTestnet, + Some("poc-1") => config.chain_spec = ChainSpec::PoC1Testnet, None => (), Some(unknown) => panic!("Invalid chain name: {}", unknown), } info!("Chain specification: {}", match config.chain_spec { - ChainSpec::Development => "Local Development", + ChainSpec::Development => "Development", + ChainSpec::LocalTestnet => "Local Testnet", ChainSpec::PoC1Testnet => "PoC-1 Testnet", }); diff --git a/polkadot/consensus/src/lib.rs b/polkadot/consensus/src/lib.rs index 2fef41c2d468e..2630bea720b3e 100644 --- a/polkadot/consensus/src/lib.rs +++ b/polkadot/consensus/src/lib.rs @@ -495,6 +495,7 @@ impl bft::ProposerFactory for ProposerFactory let checked_id = self.client.check_id(BlockId::Hash(parent_hash))?; let duty_roster = self.client.duty_roster(&checked_id)?; + let random_seed = self.client.random_seed(&checked_id)?; let group_info = make_group_info(duty_roster, authorities)?; let table = Arc::new(SharedTable::new(group_info, sign_with.clone(), parent_hash)); @@ -510,6 +511,7 @@ impl bft::ProposerFactory for ProposerFactory parent_hash, parent_number: parent_header.number, parent_id: checked_id, + random_seed, local_key: sign_with, client: self.client.clone(), transaction_pool: self.transaction_pool.clone(), @@ -533,6 +535,7 @@ pub struct Proposer { parent_hash: HeaderHash, parent_number: BlockNumber, parent_id: C::CheckedBlockId, + random_seed: Hash, client: Arc, local_key: Arc, transaction_pool: Arc>, @@ -561,6 +564,7 @@ impl bft::Proposer for Proposer { let mut pool = self.transaction_pool.lock(); let mut unqueue_invalid = Vec::new(); let mut pending_size = 0; + pool.cull(None, readiness_evaluator.clone()); for pending in pool.pending(readiness_evaluator.clone()) { // skip and cull transactions which are too large. if pending.encoded_size() > MAX_TRANSACTIONS_SIZE { @@ -624,6 +628,16 @@ impl bft::Proposer for Proposer { Box::new(self.delay.clone().map_err(Error::from).and_then(move |_| evaluated)) } + fn round_proposer(&self, round_number: usize, authorities: &[AuthorityId]) -> AuthorityId { + use primitives::uint::U256; + + let len: U256 = authorities.len().into(); + let offset = U256::from_big_endian(&self.random_seed.0) % len; + let offset = offset.low_u64() as usize + round_number; + + authorities[offset % authorities.len()].clone() + } + fn import_misbehavior(&self, misbehavior: Vec<(AuthorityId, bft::Misbehavior)>) { use bft::generic::Misbehavior as GenericMisbehavior; use primitives::bft::{MisbehaviorKind, MisbehaviorReport}; @@ -634,6 +648,7 @@ impl bft::Proposer for Proposer { let mut next_index = { let readiness_evaluator = Ready::create(self.parent_id.clone(), &*self.client); + pool.cull(None, readiness_evaluator.clone()); let cur_index = pool.pending(readiness_evaluator) .filter(|tx| tx.as_ref().as_ref().signed == local_id) .last() diff --git a/polkadot/runtime/src/lib.rs b/polkadot/runtime/src/lib.rs index a75fadd4109d2..f933085d51052 100644 --- a/polkadot/runtime/src/lib.rs +++ b/polkadot/runtime/src/lib.rs @@ -27,6 +27,10 @@ extern crate substrate_runtime_support as runtime_support; #[macro_use] extern crate substrate_runtime_primitives as runtime_primitives; +#[cfg(feature = "std")] +#[macro_use] +extern crate hex_literal; + #[cfg(test)] extern crate substrate_serializer; @@ -298,4 +302,24 @@ mod tests { println!("{}", HexDisplay::from(&v)); assert_eq!(UncheckedExtrinsic::decode(&mut &v[..]).unwrap(), tx); } + + #[test] + fn serialize_checked() { + let xt = Extrinsic { + signed: hex!["0d71d1a9cad6f2ab773435a7dec1bac019994d05d1dd5eb3108211dcf25c9d1e"], + index: 0u64, + function: Call::CouncilVoting(council::voting::Call::propose(Box::new( + PrivCall::Consensus(consensus::PrivCall::set_code( + vec![] + )) + ))), + }; + let v = Slicable::encode(&xt); + + let data = hex!["e00000000d71d1a9cad6f2ab773435a7dec1bac019994d05d1dd5eb3108211dcf25c9d1e000000000000000007000000000000006369D39D892B7B87A6769F90E14C618C2B84EBB293E2CC46640136E112C078C75619AC2E0815F2511568736623C055156C8FC427CE2AEE4AE2838F86EFE80208"]; + let uxt: UncheckedExtrinsic = Slicable::decode(&mut &data[..]).unwrap(); + assert_eq!(uxt.extrinsic, xt); + + assert_eq!(Extrinsic::decode(&mut &v[..]).unwrap(), xt); + } } diff --git a/polkadot/runtime/wasm/genesis.wasm b/polkadot/runtime/wasm/genesis.wasm index bc0861d31f35d..05ff2f4505ecc 100644 Binary files a/polkadot/runtime/wasm/genesis.wasm and b/polkadot/runtime/wasm/genesis.wasm differ diff --git a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm index e81d2066e6258..05ff2f4505ecc 100644 Binary files a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm and b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm differ diff --git a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm index 7e31a2dea60aa..4bb8fff698606 100755 Binary files a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm and b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm differ diff --git a/polkadot/service/src/config.rs b/polkadot/service/src/config.rs index d4cacf37b93e9..177675ea704a0 100644 --- a/polkadot/service/src/config.rs +++ b/polkadot/service/src/config.rs @@ -23,8 +23,10 @@ pub use network::NetworkConfiguration; /// The chain specification (this should eventually be replaced by a more general JSON-based chain /// specification). pub enum ChainSpec { - /// Whatever the current runtime is, with simple Alice/Bob auths. + /// Whatever the current runtime is, with just Alice as an auth. Development, + /// Whatever the current runtime is, with simple Alice/Bob auths. + LocalTestnet, /// The PoC-1 testnet. PoC1Testnet, } diff --git a/polkadot/service/src/lib.rs b/polkadot/service/src/lib.rs index 7d770b67d3031..36651dcac1621 100644 --- a/polkadot/service/src/lib.rs +++ b/polkadot/service/src/lib.rs @@ -56,7 +56,7 @@ use parking_lot::Mutex; use tokio_core::reactor::Core; use codec::Slicable; use primitives::block::{Id as BlockId, Extrinsic, ExtrinsicHash, HeaderHash}; -use primitives::hashing; +use primitives::{AuthorityId, hashing}; use transaction_pool::TransactionPool; use substrate_executor::NativeExecutor; use polkadot_executor::Executor as LocalDispatch; @@ -98,8 +98,9 @@ impl network::TransactionPool for TransactionPoolAdapter { } }; let id = self.client.check_id(BlockId::Hash(best_block)).expect("Best block is always valid; qed."); - let ready = transaction_pool::Ready::create(id, &*self.client); - self.pool.lock().pending(ready).map(|t| { + let mut pool = self.pool.lock(); + pool.cull(None, transaction_pool::Ready::create(id, &*self.client)); + pool.pending(transaction_pool::Ready::create(id, &*self.client)).map(|t| { let hash = ::primitives::Hash::from(&t.hash()[..]); let tx = codec::Slicable::encode(t.as_transaction()); (hash, tx) @@ -135,9 +136,10 @@ fn poc_1_testnet_config() -> ChainConfig { hex!["82c39b31a2b79a90f8e66e7a77fdb85a4ed5517f2ae39f6a80565e8ecae85cf5"].into(), hex!["4de37a07567ebcbf8c64568428a835269a566723687058e017b6d69db00a77e7"].into(), hex!["063d7787ebca768b7445dfebe7d62cbb1625ff4dba288ea34488da266dd6dca5"].into(), + hex!["8101764f45778d4980dadaceee6e8af2517d3ab91ac9bec9cd1714fa5994081c"].into(), ]; let endowed_accounts = vec![ - hex!["24d132eb1a4cbf8e46de22652019f1e07fadd5037a6a057c75dbbfd4641ba85d"].into(), + hex!["f295940fa750df68a686fcf4abd4111c8a9c5a5a5a83c4c8639c451a94a7adfd"].into(), ]; let genesis_config = GenesisConfig { consensus: Some(ConsensusConfig { @@ -151,7 +153,7 @@ fn poc_1_testnet_config() -> ChainConfig { }), staking: Some(StakingConfig { current_era: 0, - intentions: vec![], + intentions: initial_authorities.clone(), transaction_fee: 100, balances: endowed_accounts.iter().map(|&k|(k, 1u64 << 60)).collect(), validator_count: 12, @@ -180,15 +182,15 @@ fn poc_1_testnet_config() -> ChainConfig { }), parachains: Some(Default::default()), }; - let boot_nodes = Vec::new(); + let boot_nodes = vec![ + "enode://ce29df27adace5c2a08efc2fd58ce1a2587f2157061e7788861dfef3c0cbf275af8476f93b5f10ecbcd7d6c2fdac109b581502dd7a67a361f9efa7593308bedd@104.211.54.233:30333".into(), + "enode://db86cdf0d653c774cb9f357ba99ee035b2dc3ae4313e93a79a38d9e0089dc5eacdf01a5cab7d41b6a44c83bc78599b76318bc59501f9d62cc6b08cfb74777032@104.211.48.51:30333".into(), + "enode://a9458a01ccc278eab98ee329f529ca3bcb88e13e4e0cda7318a63c6ae704b74eca7c5a05cff106d531cdc41facfbe63540de5f733108fbbbb7d0235131ca39a0@104.211.48.247:30333".into(), + ]; ChainConfig { genesis_config, boot_nodes } } -fn local_testnet_config() -> ChainConfig { - let initial_authorities = vec![ - ed25519::Pair::from_seed(b"Alice ").public().into(), - ed25519::Pair::from_seed(b"Bob ").public().into(), - ]; +fn testnet_config(initial_authorities: Vec) -> ChainConfig { let endowed_accounts = vec![ ed25519::Pair::from_seed(b"Alice ").public().into(), ed25519::Pair::from_seed(b"Bob ").public().into(), @@ -222,15 +224,15 @@ fn local_testnet_config() -> ChainConfig { minimum_deposit: 10, }), council: Some(CouncilConfig { - active_council: vec![], + active_council: endowed_accounts.iter().filter(|a| initial_authorities.iter().find(|b| a == b).is_none()).map(|a| (a.clone(), 1000000)).collect(), candidacy_bond: 10, voter_bond: 2, present_slash_per_voter: 1, carry_count: 4, presentation_duration: 10, approval_voting_period: 20, - term_duration: 40, - desired_seats: 0, + term_duration: 1000000, + desired_seats: (endowed_accounts.len() - initial_authorities.len()) as u32, inactive_grace_period: 1, cooloff_period: 75, @@ -242,6 +244,19 @@ fn local_testnet_config() -> ChainConfig { ChainConfig { genesis_config, boot_nodes } } +fn development_config() -> ChainConfig { + testnet_config(vec![ + ed25519::Pair::from_seed(b"Alice ").public().into(), + ]) +} + +fn local_testnet_config() -> ChainConfig { + testnet_config(vec![ + ed25519::Pair::from_seed(b"Alice ").public().into(), + ed25519::Pair::from_seed(b"Bob ").public().into(), + ]) +} + impl Service { /// Creates and register protocol with the network service pub fn new(mut config: Configuration) -> Result { @@ -264,7 +279,8 @@ impl Service { } let ChainConfig { genesis_config, boot_nodes } = match config.chain_spec { - ChainSpec::Development => local_testnet_config(), + ChainSpec::Development => development_config(), + ChainSpec::LocalTestnet => local_testnet_config(), ChainSpec::PoC1Testnet => poc_1_testnet_config(), }; config.network.boot_nodes.extend(boot_nodes); diff --git a/substrate/bft/src/lib.rs b/substrate/bft/src/lib.rs index e21051b1aef36..a0ffaf3968ad1 100644 --- a/substrate/bft/src/lib.rs +++ b/substrate/bft/src/lib.rs @@ -133,11 +133,16 @@ pub trait Proposer { /// Create a proposal. fn propose(&self) -> Self::Create; + /// Evaluate proposal. True means valid. - // TODO: change this to a future. fn evaluate(&self, proposal: &Block) -> Self::Evaluate; + /// Import witnessed misbehavior. fn import_misbehavior(&self, misbehavior: Vec<(AuthorityId, Misbehavior)>); + + /// Determine the proposer for a given round. This should be a deterministic function + /// with consistent results across all authorities. + fn round_proposer(&self, round_number: usize, authorities: &[AuthorityId]) -> AuthorityId; } /// Block import trait. @@ -189,20 +194,7 @@ impl generic::Context for BftInstance

{ } fn round_proposer(&self, round: usize) -> AuthorityId { - use primitives::hashing::blake2_256; - - // repeat blake2_256 on parent hash round + 1 times. - // use as index into authorities vec. - // TODO: parent hash is really insecure as a randomness beacon as - // the prior can easily influence the block hash. - let hashed = (0..round + 1).fold(self.parent_hash.0, |a, _| { - blake2_256(&a[..]) - }); - - let index = u32::decode(&mut &hashed[..]) - .expect("there are more than 4 bytes in a 32 byte hash; qed"); - - self.authorities[(index as usize) % self.authorities.len()] + self.proposer.round_proposer(round, &self.authorities[..]) } fn proposal_valid(&self, proposal: &Block) -> Self::EvaluateProposal { @@ -649,6 +641,10 @@ mod tests { } fn import_misbehavior(&self, _misbehavior: Vec<(AuthorityId, Misbehavior)>) {} + + fn round_proposer(&self, round_number: usize, authorities: &[AuthorityId]) -> AuthorityId { + authorities[round_number % authorities.len()].clone() + } } fn make_service(client: FakeClient) diff --git a/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm b/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm index ed1b074c8500b..c7e6b0e25f79e 100644 Binary files a/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm and b/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm differ diff --git a/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm b/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm index ac450a04208e9..d9d13ededb235 100755 Binary files a/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm and b/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm differ diff --git a/substrate/rpc/src/state/mod.rs b/substrate/rpc/src/state/mod.rs index d8f2c8f00c157..29d372aa8f022 100644 --- a/substrate/rpc/src/state/mod.rs +++ b/substrate/rpc/src/state/mod.rs @@ -23,8 +23,9 @@ mod tests; use std::sync::Arc; use client::{self, Client}; -use primitives::block; +use primitives::{block, Hash, blake2_256}; use primitives::storage::{StorageKey, StorageData}; +use primitives::hexdisplay::HexDisplay; use state_machine; use self::error::Result; @@ -40,6 +41,22 @@ build_rpc_trait! { #[rpc(name = "state_callAt")] fn call_at(&self, String, Vec, block::HeaderHash) -> Result>; + /// Returns the hash of a storage entry. + #[rpc(name = "state_getStorageHashAt")] + fn storage_hash_at(&self, StorageKey, block::HeaderHash) -> Result; + + /// Returns the size of a storage entry. + #[rpc(name = "state_getStorageSizeAt")] + fn storage_size_at(&self, StorageKey, block::HeaderHash) -> Result; + + /// Returns the hash of a storage entry. + #[rpc(name = "state_getStorageHash")] + fn storage_hash(&self, StorageKey) -> Result; + + /// Returns the size of a storage entry. + #[rpc(name = "state_getStorageSize")] + fn storage_size(&self, StorageKey) -> Result; + /// Returns a storage entry. #[rpc(name = "state_getStorage")] fn storage(&self, StorageKey) -> Result; @@ -56,21 +73,36 @@ impl StateApi for Arc> where client::error::Error: From<<::State as state_machine::backend::Backend>::Error>, { fn storage_at(&self, key: StorageKey, block: block::HeaderHash) -> Result { + trace!(target: "rpc", "Querying storage at {:?} for key {}", block, HexDisplay::from(&key.0)); Ok(self.as_ref().storage(&block::Id::Hash(block), &key)?) } fn call_at(&self, method: String, data: Vec, block: block::HeaderHash) -> Result> { + trace!(target: "rpc", "Calling runtime at {:?} for method {} ({})", block, method, HexDisplay::from(&data)); Ok(self.as_ref().call(&block::Id::Hash(block), &method, &data)?.return_data) } + fn storage_hash_at(&self, key: StorageKey, block: block::HeaderHash) -> Result { + self.storage_at(key, block).map(|x| blake2_256(&x.0).into()) + } + + fn storage_size_at(&self, key: StorageKey, block: block::HeaderHash) -> Result { + self.storage_at(key, block).map(|x| x.0.len() as u64) + } + + fn storage_hash(&self, key: StorageKey) -> Result { + self.storage_hash_at(key, self.as_ref().info()?.chain.best_hash) + } + + fn storage_size(&self, key: StorageKey) -> Result { + self.storage_size_at(key, self.as_ref().info()?.chain.best_hash) + } + fn storage(&self, key: StorageKey) -> Result { - let at = block::Id::Hash(self.as_ref().info()?.chain.best_hash); - use primitives::hexdisplay::HexDisplay; - info!("Querying storage at {:?} for key {}", at, HexDisplay::from(&key.0)); - Ok(self.as_ref().storage(&at, &key)?) + self.storage_at(key, self.as_ref().info()?.chain.best_hash) } fn call(&self, method: String, data: Vec) -> Result> { - Ok(self.as_ref().call(&block::Id::Hash(self.as_ref().info()?.chain.best_hash), &method, &data)?.return_data) + self.call_at(method, data, self.as_ref().info()?.chain.best_hash) } } diff --git a/substrate/runtime-std/src/lib.rs b/substrate/runtime-std/src/lib.rs index b98bfb43c3bf8..22bdebe96b366 100644 --- a/substrate/runtime-std/src/lib.rs +++ b/substrate/runtime-std/src/lib.rs @@ -21,7 +21,7 @@ #![cfg_attr(not(feature = "std"), feature(lang_items))] #![cfg_attr(not(feature = "std"), feature(core_intrinsics))] #![cfg_attr(not(feature = "std"), feature(alloc))] -#![cfg_attr(not(feature = "std"), feature(macro_reexport))] +#![cfg_attr(not(feature = "std"), feature(use_extern_macros))] #![cfg_attr(feature = "std", doc = "Polkadot runtime standard library as compiled when linked with Rust's standard library.")] #![cfg_attr(not(feature = "std"), doc = "Polkadot's runtime standard library as compiled without Rust's standard library.")] diff --git a/substrate/runtime-std/without_std.rs b/substrate/runtime-std/without_std.rs index 2d05e93887315..5e2c45bde1704 100644 --- a/substrate/runtime-std/without_std.rs +++ b/substrate/runtime-std/without_std.rs @@ -15,7 +15,6 @@ // along with Substrate. If not, see . #[cfg(feature = "nightly")] -#[macro_reexport(vec)] extern crate alloc; #[cfg(feature = "nightly")] extern crate pwasm_libc; diff --git a/substrate/runtime/consensus/src/lib.rs b/substrate/runtime/consensus/src/lib.rs index 354604154e473..40c879e811a8e 100644 --- a/substrate/runtime/consensus/src/lib.rs +++ b/substrate/runtime/consensus/src/lib.rs @@ -49,6 +49,8 @@ impl StorageVec for AuthorityStorageVec { pub const CODE: &'static [u8] = b":code"; +pub type KeyValue = (Vec, Vec); + pub trait Trait: system::Trait { type PublicAux: RefInto; type SessionKey: Parameter + Default; @@ -61,6 +63,7 @@ decl_module! { } pub enum PrivCall { fn set_code(new: Vec) = 0; + fn set_storage(items: Vec) = 1; } } @@ -75,6 +78,13 @@ impl Module { storage::unhashed::put_raw(CODE, &new); } + /// Set some items of storage. + fn set_storage(items: Vec) { + for i in &items { + storage::unhashed::put_raw(&i.0, &i.1); + } + } + /// Report some misbehaviour. fn report_misbehavior(_aux: &T::PublicAux, _report: MisbehaviorReport) { // TODO. diff --git a/substrate/runtime/council/src/lib.rs b/substrate/runtime/council/src/lib.rs index 95bc7f1885aab..78fc0e39eb54c 100644 --- a/substrate/runtime/council/src/lib.rs +++ b/substrate/runtime/council/src/lib.rs @@ -558,7 +558,8 @@ impl primitives::BuildExternalities for GenesisConfig twox_128(>::key()).to_vec() => self.active_council.encode(), twox_128(>::key()).to_vec() => self.cooloff_period.encode(), - twox_128(>::key()).to_vec() => self.voting_period.encode() + twox_128(>::key()).to_vec() => self.voting_period.encode(), + twox_128(>::key()).to_vec() => vec![0u8; 0].encode() ] } } diff --git a/substrate/runtime/council/src/voting.rs b/substrate/runtime/council/src/voting.rs index 13cce3f9158ca..8c2362ed76353 100644 --- a/substrate/runtime/council/src/voting.rs +++ b/substrate/runtime/council/src/voting.rs @@ -42,7 +42,7 @@ decl_storage! { pub CooloffPeriod get(cooloff_period): b"cov:cooloff" => required T::BlockNumber; pub VotingPeriod get(voting_period): b"cov:period" => required T::BlockNumber; - pub Proposals get(proposals): b"cov:prs" => default Vec<(T::BlockNumber, T::Hash)>; // ordered by expiry. + pub Proposals get(proposals): b"cov:prs" => required Vec<(T::BlockNumber, T::Hash)>; // ordered by expiry. pub ProposalOf get(proposal_of): b"cov:pro" => map [ T::Hash => T::Proposal ]; pub ProposalVoters get(proposal_voters): b"cov:voters:" => default map [ T::Hash => Vec ]; pub CouncilVoteOf get(vote_of): b"cov:vote:" => map [ (T::Hash, T::AccountId) => bool ]; @@ -79,6 +79,7 @@ impl Module { let proposal_hash = T::Hashing::hash_of(&proposal); + assert!(!>::exists(proposal_hash), "No duplicate proposals allowed"); assert!(!Self::is_vetoed(&proposal_hash)); let mut proposals = Self::proposals(); diff --git a/substrate/runtime/democracy/src/lib.rs b/substrate/runtime/democracy/src/lib.rs index 1d62416df5cda..86851d6e7ab4f 100644 --- a/substrate/runtime/democracy/src/lib.rs +++ b/substrate/runtime/democracy/src/lib.rs @@ -85,9 +85,9 @@ decl_storage! { pub VotingPeriod get(voting_period): b"dem:per" => required T::BlockNumber; // The next free referendum index, aka the number of referendums started so far. - pub ReferendumCount get(referendum_count): b"dem:rco" => default ReferendumIndex; + pub ReferendumCount get(referendum_count): b"dem:rco" => required ReferendumIndex; // The next referendum index that should be tallied. - pub NextTally get(next_tally): b"dem:nxt" => default ReferendumIndex; + pub NextTally get(next_tally): b"dem:nxt" => required ReferendumIndex; // Information concerning any given referendum. pub ReferendumInfoOf get(referendum_info): b"dem:pro:" => map [ ReferendumIndex => (T::BlockNumber, T::Proposal, VoteThreshold) ]; @@ -318,7 +318,10 @@ impl primitives::BuildExternalities for GenesisConfig map![ twox_128(>::key()).to_vec() => self.launch_period.encode(), twox_128(>::key()).to_vec() => self.voting_period.encode(), - twox_128(>::key()).to_vec() => self.minimum_deposit.encode() + twox_128(>::key()).to_vec() => self.minimum_deposit.encode(), + twox_128(>::key()).to_vec() => (0 as ReferendumIndex).encode(), + twox_128(>::key()).to_vec() => (0 as ReferendumIndex).encode(), + twox_128(>::key()).to_vec() => (0 as PropIndex).encode() ] } } diff --git a/substrate/runtime/democracy/src/vote_threshold.rs b/substrate/runtime/democracy/src/vote_threshold.rs index c40f611e34373..5797d4176486a 100644 --- a/substrate/runtime/democracy/src/vote_threshold.rs +++ b/substrate/runtime/democracy/src/vote_threshold.rs @@ -16,9 +16,9 @@ //! Voting thresholds. -use primitives::traits::IntegerSquareRoot; +use primitives::traits::{Zero, IntegerSquareRoot}; use codec::{Input, Slicable}; -use rstd::ops::{Add, Mul, Div}; +use rstd::ops::{Add, Mul, Div, Rem}; /// A means of determining if a vote is past pass threshold. #[derive(Clone, Copy, PartialEq, Eq)] @@ -58,18 +58,60 @@ pub trait Approved { fn approved(&self, approve: Balance, against: Balance, electorate: Balance) -> bool; } -impl + Mul + Div + Copy> Approved for VoteThreshold { +/// Return `true` iff `n1 / d1 < n2 / d2`. `d1` and `d2` may not be zero. +fn compare_rationals + Div + Rem + Ord + Copy>(mut n1: T, mut d1: T, mut n2: T, mut d2: T) -> bool { + // Uses a continued fractional representation for a non-overflowing compare. + // Detailed at https://janmr.com/blog/2014/05/comparing-rational-numbers-without-overflow/. + loop { + let q1 = n1 / d1; + let q2 = n2 / d2; + if q1 < q2 { + return true; + } + if q2 < q1 { + return false; + } + let r1 = n1 % d1; + let r2 = n2 % d2; + if r2.is_zero() { + return false; + } + if r1.is_zero() { + return true; + } + n1 = d2; + n2 = d1; + d1 = r2; + d2 = r1; + } +} + +impl + Mul + Div + Rem + Copy> Approved for VoteThreshold { /// Given `approve` votes for and `against` votes against from a total electorate size of /// `electorate` (`electorate - (approve + against)` are abstainers), then returns true if the /// overall outcome is in favour of approval. fn approved(&self, approve: Balance, against: Balance, electorate: Balance) -> bool { let voters = approve + against; + let sqrt_voters = voters.integer_sqrt(); + let sqrt_electorate = electorate.integer_sqrt(); + if sqrt_voters.is_zero() { return false; } match *self { VoteThreshold::SuperMajorityApprove => - voters.integer_sqrt() * approve / electorate.integer_sqrt() > against, + compare_rationals(against, sqrt_voters, approve, sqrt_electorate), VoteThreshold::SuperMajorityAgainst => - approve > voters.integer_sqrt() * against / electorate.integer_sqrt(), + compare_rationals(against, sqrt_electorate, approve, sqrt_voters), VoteThreshold::SimpleMajority => approve > against, } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn should_work() { + assert_eq!(VoteThreshold::SuperMajorityApprove.approved(60, 50, 210), false); + assert_eq!(VoteThreshold::SuperMajorityApprove.approved(100, 50, 210), true); + } +} diff --git a/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm b/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm index b5f369fe14b19..59a333c876829 100644 Binary files a/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm and b/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm differ diff --git a/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm b/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm index c62b44fcc0e06..c80254075723f 100755 Binary files a/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm and b/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm differ