mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 08:41:02 +00:00
Fixes and improvements for PoC-1 Testnet (#143)
* Fix initialisations and add a test. * Fix test. * Fix overflow bug. * Minor refactoring and fixes. * Fix vote threshold. * Add note. * Fixes for latest rust and the readme. * Better readme. * An extra validator for PoC-1 * Update README. * PoC-1 bootnodes. * don't return async::notready for messages without scheduling wakeup * Fix endowed account * give polkadot control over round proposer based on random seed * address grumbles.
This commit is contained in:
+102
-1
@@ -1,3 +1,104 @@
|
|||||||
# Polkadot
|
# Polkadot
|
||||||
|
|
||||||
Implementation of a https://polkadot.io node in Rust.
|
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
|
||||||
|
```
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ mod tests {
|
|||||||
construct_block(
|
construct_block(
|
||||||
1,
|
1,
|
||||||
[69u8; 32].into(),
|
[69u8; 32].into(),
|
||||||
hex!("57ba67304318efaee95c4f9ab95ed5704eafe030bc8db2df00acb08c2f4979c8").into(),
|
hex!("a63d59c6a7347cd7a1dc1ec139723b531f0ac450e39b1c532d5ca69ff74ad811").into(),
|
||||||
vec![Extrinsic {
|
vec![Extrinsic {
|
||||||
signed: Alice.into(),
|
signed: Alice.into(),
|
||||||
index: 0,
|
index: 0,
|
||||||
@@ -210,7 +210,7 @@ mod tests {
|
|||||||
construct_block(
|
construct_block(
|
||||||
2,
|
2,
|
||||||
block1().1,
|
block1().1,
|
||||||
hex!("ead4c60c0cad06b7ee73e64efeec2d4eb82c651469fb2ec748cfe5026bea5c49").into(),
|
hex!("1c3623b2e3f7e43752debb9015bace4f6931593579b5af34457b931315f5e2ab").into(),
|
||||||
vec![
|
vec![
|
||||||
Extrinsic {
|
Extrinsic {
|
||||||
signed: Bob.into(),
|
signed: Bob.into(),
|
||||||
|
|||||||
BIN
Binary file not shown.
Binary file not shown.
@@ -31,9 +31,6 @@ extern crate substrate_state_machine as state_machine;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate error_chain;
|
extern crate error_chain;
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate log;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
extern crate substrate_keyring as keyring;
|
extern crate substrate_keyring as keyring;
|
||||||
|
|
||||||
@@ -42,7 +39,7 @@ use client::Client;
|
|||||||
use polkadot_executor::Executor as LocalDispatch;
|
use polkadot_executor::Executor as LocalDispatch;
|
||||||
use substrate_executor::{NativeExecutionDispatch, NativeExecutor};
|
use substrate_executor::{NativeExecutionDispatch, NativeExecutor};
|
||||||
use state_machine::OverlayedChanges;
|
use state_machine::OverlayedChanges;
|
||||||
use primitives::{AccountId, BlockId, Index, SessionKey, Timestamp};
|
use primitives::{AccountId, BlockId, Hash, Index, SessionKey, Timestamp};
|
||||||
use primitives::parachain::DutyRoster;
|
use primitives::parachain::DutyRoster;
|
||||||
use runtime::{Block, Header, UncheckedExtrinsic, Extrinsic, Call, TimestampCall};
|
use runtime::{Block, Header, UncheckedExtrinsic, Extrinsic, Call, TimestampCall};
|
||||||
|
|
||||||
@@ -126,6 +123,9 @@ pub trait PolkadotApi {
|
|||||||
/// Get validators at a given block.
|
/// Get validators at a given block.
|
||||||
fn validators(&self, at: &Self::CheckedBlockId) -> Result<Vec<AccountId>>;
|
fn validators(&self, at: &Self::CheckedBlockId) -> Result<Vec<AccountId>>;
|
||||||
|
|
||||||
|
/// Get the value of the randomness beacon at a given block.
|
||||||
|
fn random_seed(&self, at: &Self::CheckedBlockId) -> Result<Hash>;
|
||||||
|
|
||||||
/// Get the authority duty roster at a block.
|
/// Get the authority duty roster at a block.
|
||||||
fn duty_roster(&self, at: &Self::CheckedBlockId) -> Result<DutyRoster>;
|
fn duty_roster(&self, at: &Self::CheckedBlockId) -> Result<DutyRoster>;
|
||||||
|
|
||||||
@@ -191,6 +191,10 @@ impl<B: Backend> PolkadotApi for Client<B, NativeExecutor<LocalDispatch>>
|
|||||||
with_runtime!(self, at, ::runtime::Session::validators)
|
with_runtime!(self, at, ::runtime::Session::validators)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn random_seed(&self, at: &CheckedId) -> Result<Hash> {
|
||||||
|
with_runtime!(self, at, ::runtime::System::random_seed)
|
||||||
|
}
|
||||||
|
|
||||||
fn duty_roster(&self, at: &CheckedId) -> Result<DutyRoster> {
|
fn duty_roster(&self, at: &CheckedId) -> Result<DutyRoster> {
|
||||||
// duty roster can only be queried at the start of a block,
|
// duty roster can only be queried at the start of a block,
|
||||||
// so we create a dummy.
|
// so we create a dummy.
|
||||||
|
|||||||
@@ -56,6 +56,6 @@ args:
|
|||||||
- chain:
|
- chain:
|
||||||
long: chain
|
long: chain
|
||||||
value_name: CHAIN_SPEC
|
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
|
takes_value: true
|
||||||
subcommands:
|
subcommands:
|
||||||
|
|||||||
@@ -126,13 +126,15 @@ pub fn run<I, T>(args: I) -> error::Result<()> where
|
|||||||
}
|
}
|
||||||
|
|
||||||
match matches.value_of("chain") {
|
match matches.value_of("chain") {
|
||||||
Some("poc-1") => config.chain_spec = ChainSpec::PoC1Testnet,
|
|
||||||
Some("dev") => config.chain_spec = ChainSpec::Development,
|
Some("dev") => config.chain_spec = ChainSpec::Development,
|
||||||
|
Some("local") => config.chain_spec = ChainSpec::LocalTestnet,
|
||||||
|
Some("poc-1") => config.chain_spec = ChainSpec::PoC1Testnet,
|
||||||
None => (),
|
None => (),
|
||||||
Some(unknown) => panic!("Invalid chain name: {}", unknown),
|
Some(unknown) => panic!("Invalid chain name: {}", unknown),
|
||||||
}
|
}
|
||||||
info!("Chain specification: {}", match config.chain_spec {
|
info!("Chain specification: {}", match config.chain_spec {
|
||||||
ChainSpec::Development => "Local Development",
|
ChainSpec::Development => "Development",
|
||||||
|
ChainSpec::LocalTestnet => "Local Testnet",
|
||||||
ChainSpec::PoC1Testnet => "PoC-1 Testnet",
|
ChainSpec::PoC1Testnet => "PoC-1 Testnet",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -495,6 +495,7 @@ impl<C: PolkadotApi, N: Network> bft::ProposerFactory for ProposerFactory<C, N>
|
|||||||
|
|
||||||
let checked_id = self.client.check_id(BlockId::Hash(parent_hash))?;
|
let checked_id = self.client.check_id(BlockId::Hash(parent_hash))?;
|
||||||
let duty_roster = self.client.duty_roster(&checked_id)?;
|
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 group_info = make_group_info(duty_roster, authorities)?;
|
||||||
let table = Arc::new(SharedTable::new(group_info, sign_with.clone(), parent_hash));
|
let table = Arc::new(SharedTable::new(group_info, sign_with.clone(), parent_hash));
|
||||||
@@ -510,6 +511,7 @@ impl<C: PolkadotApi, N: Network> bft::ProposerFactory for ProposerFactory<C, N>
|
|||||||
parent_hash,
|
parent_hash,
|
||||||
parent_number: parent_header.number,
|
parent_number: parent_header.number,
|
||||||
parent_id: checked_id,
|
parent_id: checked_id,
|
||||||
|
random_seed,
|
||||||
local_key: sign_with,
|
local_key: sign_with,
|
||||||
client: self.client.clone(),
|
client: self.client.clone(),
|
||||||
transaction_pool: self.transaction_pool.clone(),
|
transaction_pool: self.transaction_pool.clone(),
|
||||||
@@ -533,6 +535,7 @@ pub struct Proposer<C: PolkadotApi, R> {
|
|||||||
parent_hash: HeaderHash,
|
parent_hash: HeaderHash,
|
||||||
parent_number: BlockNumber,
|
parent_number: BlockNumber,
|
||||||
parent_id: C::CheckedBlockId,
|
parent_id: C::CheckedBlockId,
|
||||||
|
random_seed: Hash,
|
||||||
client: Arc<C>,
|
client: Arc<C>,
|
||||||
local_key: Arc<ed25519::Pair>,
|
local_key: Arc<ed25519::Pair>,
|
||||||
transaction_pool: Arc<Mutex<TransactionPool>>,
|
transaction_pool: Arc<Mutex<TransactionPool>>,
|
||||||
@@ -561,6 +564,7 @@ impl<C: PolkadotApi, R: TableRouter> bft::Proposer for Proposer<C, R> {
|
|||||||
let mut pool = self.transaction_pool.lock();
|
let mut pool = self.transaction_pool.lock();
|
||||||
let mut unqueue_invalid = Vec::new();
|
let mut unqueue_invalid = Vec::new();
|
||||||
let mut pending_size = 0;
|
let mut pending_size = 0;
|
||||||
|
pool.cull(None, readiness_evaluator.clone());
|
||||||
for pending in pool.pending(readiness_evaluator.clone()) {
|
for pending in pool.pending(readiness_evaluator.clone()) {
|
||||||
// skip and cull transactions which are too large.
|
// skip and cull transactions which are too large.
|
||||||
if pending.encoded_size() > MAX_TRANSACTIONS_SIZE {
|
if pending.encoded_size() > MAX_TRANSACTIONS_SIZE {
|
||||||
@@ -624,6 +628,16 @@ impl<C: PolkadotApi, R: TableRouter> bft::Proposer for Proposer<C, R> {
|
|||||||
Box::new(self.delay.clone().map_err(Error::from).and_then(move |_| evaluated))
|
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)>) {
|
fn import_misbehavior(&self, misbehavior: Vec<(AuthorityId, bft::Misbehavior)>) {
|
||||||
use bft::generic::Misbehavior as GenericMisbehavior;
|
use bft::generic::Misbehavior as GenericMisbehavior;
|
||||||
use primitives::bft::{MisbehaviorKind, MisbehaviorReport};
|
use primitives::bft::{MisbehaviorKind, MisbehaviorReport};
|
||||||
@@ -634,6 +648,7 @@ impl<C: PolkadotApi, R: TableRouter> bft::Proposer for Proposer<C, R> {
|
|||||||
let mut next_index = {
|
let mut next_index = {
|
||||||
let readiness_evaluator = Ready::create(self.parent_id.clone(), &*self.client);
|
let readiness_evaluator = Ready::create(self.parent_id.clone(), &*self.client);
|
||||||
|
|
||||||
|
pool.cull(None, readiness_evaluator.clone());
|
||||||
let cur_index = pool.pending(readiness_evaluator)
|
let cur_index = pool.pending(readiness_evaluator)
|
||||||
.filter(|tx| tx.as_ref().as_ref().signed == local_id)
|
.filter(|tx| tx.as_ref().as_ref().signed == local_id)
|
||||||
.last()
|
.last()
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ extern crate substrate_runtime_support as runtime_support;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate substrate_runtime_primitives as runtime_primitives;
|
extern crate substrate_runtime_primitives as runtime_primitives;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate hex_literal;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
extern crate substrate_serializer;
|
extern crate substrate_serializer;
|
||||||
|
|
||||||
@@ -298,4 +302,24 @@ mod tests {
|
|||||||
println!("{}", HexDisplay::from(&v));
|
println!("{}", HexDisplay::from(&v));
|
||||||
assert_eq!(UncheckedExtrinsic::decode(&mut &v[..]).unwrap(), tx);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -23,8 +23,10 @@ pub use network::NetworkConfiguration;
|
|||||||
/// The chain specification (this should eventually be replaced by a more general JSON-based chain
|
/// The chain specification (this should eventually be replaced by a more general JSON-based chain
|
||||||
/// specification).
|
/// specification).
|
||||||
pub enum ChainSpec {
|
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,
|
Development,
|
||||||
|
/// Whatever the current runtime is, with simple Alice/Bob auths.
|
||||||
|
LocalTestnet,
|
||||||
/// The PoC-1 testnet.
|
/// The PoC-1 testnet.
|
||||||
PoC1Testnet,
|
PoC1Testnet,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ use parking_lot::Mutex;
|
|||||||
use tokio_core::reactor::Core;
|
use tokio_core::reactor::Core;
|
||||||
use codec::Slicable;
|
use codec::Slicable;
|
||||||
use primitives::block::{Id as BlockId, Extrinsic, ExtrinsicHash, HeaderHash};
|
use primitives::block::{Id as BlockId, Extrinsic, ExtrinsicHash, HeaderHash};
|
||||||
use primitives::hashing;
|
use primitives::{AuthorityId, hashing};
|
||||||
use transaction_pool::TransactionPool;
|
use transaction_pool::TransactionPool;
|
||||||
use substrate_executor::NativeExecutor;
|
use substrate_executor::NativeExecutor;
|
||||||
use polkadot_executor::Executor as LocalDispatch;
|
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 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);
|
let mut pool = self.pool.lock();
|
||||||
self.pool.lock().pending(ready).map(|t| {
|
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 hash = ::primitives::Hash::from(&t.hash()[..]);
|
||||||
let tx = codec::Slicable::encode(t.as_transaction());
|
let tx = codec::Slicable::encode(t.as_transaction());
|
||||||
(hash, tx)
|
(hash, tx)
|
||||||
@@ -135,9 +136,10 @@ fn poc_1_testnet_config() -> ChainConfig {
|
|||||||
hex!["82c39b31a2b79a90f8e66e7a77fdb85a4ed5517f2ae39f6a80565e8ecae85cf5"].into(),
|
hex!["82c39b31a2b79a90f8e66e7a77fdb85a4ed5517f2ae39f6a80565e8ecae85cf5"].into(),
|
||||||
hex!["4de37a07567ebcbf8c64568428a835269a566723687058e017b6d69db00a77e7"].into(),
|
hex!["4de37a07567ebcbf8c64568428a835269a566723687058e017b6d69db00a77e7"].into(),
|
||||||
hex!["063d7787ebca768b7445dfebe7d62cbb1625ff4dba288ea34488da266dd6dca5"].into(),
|
hex!["063d7787ebca768b7445dfebe7d62cbb1625ff4dba288ea34488da266dd6dca5"].into(),
|
||||||
|
hex!["8101764f45778d4980dadaceee6e8af2517d3ab91ac9bec9cd1714fa5994081c"].into(),
|
||||||
];
|
];
|
||||||
let endowed_accounts = vec![
|
let endowed_accounts = vec![
|
||||||
hex!["24d132eb1a4cbf8e46de22652019f1e07fadd5037a6a057c75dbbfd4641ba85d"].into(),
|
hex!["f295940fa750df68a686fcf4abd4111c8a9c5a5a5a83c4c8639c451a94a7adfd"].into(),
|
||||||
];
|
];
|
||||||
let genesis_config = GenesisConfig {
|
let genesis_config = GenesisConfig {
|
||||||
consensus: Some(ConsensusConfig {
|
consensus: Some(ConsensusConfig {
|
||||||
@@ -151,7 +153,7 @@ fn poc_1_testnet_config() -> ChainConfig {
|
|||||||
}),
|
}),
|
||||||
staking: Some(StakingConfig {
|
staking: Some(StakingConfig {
|
||||||
current_era: 0,
|
current_era: 0,
|
||||||
intentions: vec![],
|
intentions: initial_authorities.clone(),
|
||||||
transaction_fee: 100,
|
transaction_fee: 100,
|
||||||
balances: endowed_accounts.iter().map(|&k|(k, 1u64 << 60)).collect(),
|
balances: endowed_accounts.iter().map(|&k|(k, 1u64 << 60)).collect(),
|
||||||
validator_count: 12,
|
validator_count: 12,
|
||||||
@@ -180,15 +182,15 @@ fn poc_1_testnet_config() -> ChainConfig {
|
|||||||
}),
|
}),
|
||||||
parachains: Some(Default::default()),
|
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 }
|
ChainConfig { genesis_config, boot_nodes }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn local_testnet_config() -> ChainConfig {
|
fn testnet_config(initial_authorities: Vec<AuthorityId>) -> ChainConfig {
|
||||||
let initial_authorities = vec![
|
|
||||||
ed25519::Pair::from_seed(b"Alice ").public().into(),
|
|
||||||
ed25519::Pair::from_seed(b"Bob ").public().into(),
|
|
||||||
];
|
|
||||||
let endowed_accounts = vec![
|
let endowed_accounts = vec![
|
||||||
ed25519::Pair::from_seed(b"Alice ").public().into(),
|
ed25519::Pair::from_seed(b"Alice ").public().into(),
|
||||||
ed25519::Pair::from_seed(b"Bob ").public().into(),
|
ed25519::Pair::from_seed(b"Bob ").public().into(),
|
||||||
@@ -222,15 +224,15 @@ fn local_testnet_config() -> ChainConfig {
|
|||||||
minimum_deposit: 10,
|
minimum_deposit: 10,
|
||||||
}),
|
}),
|
||||||
council: Some(CouncilConfig {
|
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,
|
candidacy_bond: 10,
|
||||||
voter_bond: 2,
|
voter_bond: 2,
|
||||||
present_slash_per_voter: 1,
|
present_slash_per_voter: 1,
|
||||||
carry_count: 4,
|
carry_count: 4,
|
||||||
presentation_duration: 10,
|
presentation_duration: 10,
|
||||||
approval_voting_period: 20,
|
approval_voting_period: 20,
|
||||||
term_duration: 40,
|
term_duration: 1000000,
|
||||||
desired_seats: 0,
|
desired_seats: (endowed_accounts.len() - initial_authorities.len()) as u32,
|
||||||
inactive_grace_period: 1,
|
inactive_grace_period: 1,
|
||||||
|
|
||||||
cooloff_period: 75,
|
cooloff_period: 75,
|
||||||
@@ -242,6 +244,19 @@ fn local_testnet_config() -> ChainConfig {
|
|||||||
ChainConfig { genesis_config, boot_nodes }
|
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 {
|
impl Service {
|
||||||
/// Creates and register protocol with the network service
|
/// Creates and register protocol with the network service
|
||||||
pub fn new(mut config: Configuration) -> Result<Service, error::Error> {
|
pub fn new(mut config: Configuration) -> Result<Service, error::Error> {
|
||||||
@@ -264,7 +279,8 @@ impl Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let ChainConfig { genesis_config, boot_nodes } = match config.chain_spec {
|
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(),
|
ChainSpec::PoC1Testnet => poc_1_testnet_config(),
|
||||||
};
|
};
|
||||||
config.network.boot_nodes.extend(boot_nodes);
|
config.network.boot_nodes.extend(boot_nodes);
|
||||||
|
|||||||
@@ -133,11 +133,16 @@ pub trait Proposer {
|
|||||||
|
|
||||||
/// Create a proposal.
|
/// Create a proposal.
|
||||||
fn propose(&self) -> Self::Create;
|
fn propose(&self) -> Self::Create;
|
||||||
|
|
||||||
/// Evaluate proposal. True means valid.
|
/// Evaluate proposal. True means valid.
|
||||||
// TODO: change this to a future.
|
|
||||||
fn evaluate(&self, proposal: &Block) -> Self::Evaluate;
|
fn evaluate(&self, proposal: &Block) -> Self::Evaluate;
|
||||||
|
|
||||||
/// Import witnessed misbehavior.
|
/// Import witnessed misbehavior.
|
||||||
fn import_misbehavior(&self, misbehavior: Vec<(AuthorityId, 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.
|
/// Block import trait.
|
||||||
@@ -189,20 +194,7 @@ impl<P: Proposer> generic::Context for BftInstance<P> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn round_proposer(&self, round: usize) -> AuthorityId {
|
fn round_proposer(&self, round: usize) -> AuthorityId {
|
||||||
use primitives::hashing::blake2_256;
|
self.proposer.round_proposer(round, &self.authorities[..])
|
||||||
|
|
||||||
// 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()]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn proposal_valid(&self, proposal: &Block) -> Self::EvaluateProposal {
|
fn proposal_valid(&self, proposal: &Block) -> Self::EvaluateProposal {
|
||||||
@@ -649,6 +641,10 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn import_misbehavior(&self, _misbehavior: Vec<(AuthorityId, Misbehavior)>) {}
|
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)
|
fn make_service(client: FakeClient)
|
||||||
|
|||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -23,8 +23,9 @@ mod tests;
|
|||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use client::{self, Client};
|
use client::{self, Client};
|
||||||
use primitives::block;
|
use primitives::{block, Hash, blake2_256};
|
||||||
use primitives::storage::{StorageKey, StorageData};
|
use primitives::storage::{StorageKey, StorageData};
|
||||||
|
use primitives::hexdisplay::HexDisplay;
|
||||||
use state_machine;
|
use state_machine;
|
||||||
|
|
||||||
use self::error::Result;
|
use self::error::Result;
|
||||||
@@ -40,6 +41,22 @@ build_rpc_trait! {
|
|||||||
#[rpc(name = "state_callAt")]
|
#[rpc(name = "state_callAt")]
|
||||||
fn call_at(&self, String, Vec<u8>, block::HeaderHash) -> Result<Vec<u8>>;
|
fn call_at(&self, String, Vec<u8>, block::HeaderHash) -> Result<Vec<u8>>;
|
||||||
|
|
||||||
|
/// Returns the hash of a storage entry.
|
||||||
|
#[rpc(name = "state_getStorageHashAt")]
|
||||||
|
fn storage_hash_at(&self, StorageKey, block::HeaderHash) -> Result<Hash>;
|
||||||
|
|
||||||
|
/// Returns the size of a storage entry.
|
||||||
|
#[rpc(name = "state_getStorageSizeAt")]
|
||||||
|
fn storage_size_at(&self, StorageKey, block::HeaderHash) -> Result<u64>;
|
||||||
|
|
||||||
|
/// Returns the hash of a storage entry.
|
||||||
|
#[rpc(name = "state_getStorageHash")]
|
||||||
|
fn storage_hash(&self, StorageKey) -> Result<Hash>;
|
||||||
|
|
||||||
|
/// Returns the size of a storage entry.
|
||||||
|
#[rpc(name = "state_getStorageSize")]
|
||||||
|
fn storage_size(&self, StorageKey) -> Result<u64>;
|
||||||
|
|
||||||
/// Returns a storage entry.
|
/// Returns a storage entry.
|
||||||
#[rpc(name = "state_getStorage")]
|
#[rpc(name = "state_getStorage")]
|
||||||
fn storage(&self, StorageKey) -> Result<StorageData>;
|
fn storage(&self, StorageKey) -> Result<StorageData>;
|
||||||
@@ -56,21 +73,36 @@ impl<B, E> StateApi for Arc<Client<B, E>> where
|
|||||||
client::error::Error: From<<<B as client::backend::Backend>::State as state_machine::backend::Backend>::Error>,
|
client::error::Error: From<<<B as client::backend::Backend>::State as state_machine::backend::Backend>::Error>,
|
||||||
{
|
{
|
||||||
fn storage_at(&self, key: StorageKey, block: block::HeaderHash) -> Result<StorageData> {
|
fn storage_at(&self, key: StorageKey, block: block::HeaderHash) -> Result<StorageData> {
|
||||||
|
trace!(target: "rpc", "Querying storage at {:?} for key {}", block, HexDisplay::from(&key.0));
|
||||||
Ok(self.as_ref().storage(&block::Id::Hash(block), &key)?)
|
Ok(self.as_ref().storage(&block::Id::Hash(block), &key)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_at(&self, method: String, data: Vec<u8>, block: block::HeaderHash) -> Result<Vec<u8>> {
|
fn call_at(&self, method: String, data: Vec<u8>, block: block::HeaderHash) -> Result<Vec<u8>> {
|
||||||
|
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)
|
Ok(self.as_ref().call(&block::Id::Hash(block), &method, &data)?.return_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn storage_hash_at(&self, key: StorageKey, block: block::HeaderHash) -> Result<Hash> {
|
||||||
|
self.storage_at(key, block).map(|x| blake2_256(&x.0).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn storage_size_at(&self, key: StorageKey, block: block::HeaderHash) -> Result<u64> {
|
||||||
|
self.storage_at(key, block).map(|x| x.0.len() as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn storage_hash(&self, key: StorageKey) -> Result<Hash> {
|
||||||
|
self.storage_hash_at(key, self.as_ref().info()?.chain.best_hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn storage_size(&self, key: StorageKey) -> Result<u64> {
|
||||||
|
self.storage_size_at(key, self.as_ref().info()?.chain.best_hash)
|
||||||
|
}
|
||||||
|
|
||||||
fn storage(&self, key: StorageKey) -> Result<StorageData> {
|
fn storage(&self, key: StorageKey) -> Result<StorageData> {
|
||||||
let at = block::Id::Hash(self.as_ref().info()?.chain.best_hash);
|
self.storage_at(key, 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)?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&self, method: String, data: Vec<u8>) -> Result<Vec<u8>> {
|
fn call(&self, method: String, data: Vec<u8>) -> Result<Vec<u8>> {
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
#![cfg_attr(not(feature = "std"), feature(lang_items))]
|
#![cfg_attr(not(feature = "std"), feature(lang_items))]
|
||||||
#![cfg_attr(not(feature = "std"), feature(core_intrinsics))]
|
#![cfg_attr(not(feature = "std"), feature(core_intrinsics))]
|
||||||
#![cfg_attr(not(feature = "std"), feature(alloc))]
|
#![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(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.")]
|
#![cfg_attr(not(feature = "std"), doc = "Polkadot's runtime standard library as compiled without Rust's standard library.")]
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#[cfg(feature = "nightly")]
|
#[cfg(feature = "nightly")]
|
||||||
#[macro_reexport(vec)]
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
#[cfg(feature = "nightly")]
|
#[cfg(feature = "nightly")]
|
||||||
extern crate pwasm_libc;
|
extern crate pwasm_libc;
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ impl<S: codec::Slicable + Default> StorageVec for AuthorityStorageVec<S> {
|
|||||||
|
|
||||||
pub const CODE: &'static [u8] = b":code";
|
pub const CODE: &'static [u8] = b":code";
|
||||||
|
|
||||||
|
pub type KeyValue = (Vec<u8>, Vec<u8>);
|
||||||
|
|
||||||
pub trait Trait: system::Trait {
|
pub trait Trait: system::Trait {
|
||||||
type PublicAux: RefInto<Self::AccountId>;
|
type PublicAux: RefInto<Self::AccountId>;
|
||||||
type SessionKey: Parameter + Default;
|
type SessionKey: Parameter + Default;
|
||||||
@@ -61,6 +63,7 @@ decl_module! {
|
|||||||
}
|
}
|
||||||
pub enum PrivCall {
|
pub enum PrivCall {
|
||||||
fn set_code(new: Vec<u8>) = 0;
|
fn set_code(new: Vec<u8>) = 0;
|
||||||
|
fn set_storage(items: Vec<KeyValue>) = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,6 +78,13 @@ impl<T: Trait> Module<T> {
|
|||||||
storage::unhashed::put_raw(CODE, &new);
|
storage::unhashed::put_raw(CODE, &new);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set some items of storage.
|
||||||
|
fn set_storage(items: Vec<KeyValue>) {
|
||||||
|
for i in &items {
|
||||||
|
storage::unhashed::put_raw(&i.0, &i.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Report some misbehaviour.
|
/// Report some misbehaviour.
|
||||||
fn report_misbehavior(_aux: &T::PublicAux, _report: MisbehaviorReport) {
|
fn report_misbehavior(_aux: &T::PublicAux, _report: MisbehaviorReport) {
|
||||||
// TODO.
|
// TODO.
|
||||||
|
|||||||
@@ -558,7 +558,8 @@ impl<T: Trait> primitives::BuildExternalities for GenesisConfig<T>
|
|||||||
twox_128(<ActiveCouncil<T>>::key()).to_vec() => self.active_council.encode(),
|
twox_128(<ActiveCouncil<T>>::key()).to_vec() => self.active_council.encode(),
|
||||||
|
|
||||||
twox_128(<voting::CooloffPeriod<T>>::key()).to_vec() => self.cooloff_period.encode(),
|
twox_128(<voting::CooloffPeriod<T>>::key()).to_vec() => self.cooloff_period.encode(),
|
||||||
twox_128(<voting::VotingPeriod<T>>::key()).to_vec() => self.voting_period.encode()
|
twox_128(<voting::VotingPeriod<T>>::key()).to_vec() => self.voting_period.encode(),
|
||||||
|
twox_128(<voting::Proposals<T>>::key()).to_vec() => vec![0u8; 0].encode()
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ decl_storage! {
|
|||||||
|
|
||||||
pub CooloffPeriod get(cooloff_period): b"cov:cooloff" => required T::BlockNumber;
|
pub CooloffPeriod get(cooloff_period): b"cov:cooloff" => required T::BlockNumber;
|
||||||
pub VotingPeriod get(voting_period): b"cov:period" => 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 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<T::AccountId> ];
|
pub ProposalVoters get(proposal_voters): b"cov:voters:" => default map [ T::Hash => Vec<T::AccountId> ];
|
||||||
pub CouncilVoteOf get(vote_of): b"cov:vote:" => map [ (T::Hash, T::AccountId) => bool ];
|
pub CouncilVoteOf get(vote_of): b"cov:vote:" => map [ (T::Hash, T::AccountId) => bool ];
|
||||||
@@ -79,6 +79,7 @@ impl<T: Trait> Module<T> {
|
|||||||
|
|
||||||
let proposal_hash = T::Hashing::hash_of(&proposal);
|
let proposal_hash = T::Hashing::hash_of(&proposal);
|
||||||
|
|
||||||
|
assert!(!<ProposalOf<T>>::exists(proposal_hash), "No duplicate proposals allowed");
|
||||||
assert!(!Self::is_vetoed(&proposal_hash));
|
assert!(!Self::is_vetoed(&proposal_hash));
|
||||||
|
|
||||||
let mut proposals = Self::proposals();
|
let mut proposals = Self::proposals();
|
||||||
|
|||||||
@@ -85,9 +85,9 @@ decl_storage! {
|
|||||||
pub VotingPeriod get(voting_period): b"dem:per" => required T::BlockNumber;
|
pub VotingPeriod get(voting_period): b"dem:per" => required T::BlockNumber;
|
||||||
|
|
||||||
// The next free referendum index, aka the number of referendums started so far.
|
// 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.
|
// 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.
|
// Information concerning any given referendum.
|
||||||
pub ReferendumInfoOf get(referendum_info): b"dem:pro:" => map [ ReferendumIndex => (T::BlockNumber, T::Proposal, VoteThreshold) ];
|
pub ReferendumInfoOf get(referendum_info): b"dem:pro:" => map [ ReferendumIndex => (T::BlockNumber, T::Proposal, VoteThreshold) ];
|
||||||
|
|
||||||
@@ -318,7 +318,10 @@ impl<T: Trait> primitives::BuildExternalities for GenesisConfig<T>
|
|||||||
map![
|
map![
|
||||||
twox_128(<LaunchPeriod<T>>::key()).to_vec() => self.launch_period.encode(),
|
twox_128(<LaunchPeriod<T>>::key()).to_vec() => self.launch_period.encode(),
|
||||||
twox_128(<VotingPeriod<T>>::key()).to_vec() => self.voting_period.encode(),
|
twox_128(<VotingPeriod<T>>::key()).to_vec() => self.voting_period.encode(),
|
||||||
twox_128(<MinimumDeposit<T>>::key()).to_vec() => self.minimum_deposit.encode()
|
twox_128(<MinimumDeposit<T>>::key()).to_vec() => self.minimum_deposit.encode(),
|
||||||
|
twox_128(<ReferendumCount<T>>::key()).to_vec() => (0 as ReferendumIndex).encode(),
|
||||||
|
twox_128(<NextTally<T>>::key()).to_vec() => (0 as ReferendumIndex).encode(),
|
||||||
|
twox_128(<PublicPropCount<T>>::key()).to_vec() => (0 as PropIndex).encode()
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,9 @@
|
|||||||
|
|
||||||
//! Voting thresholds.
|
//! Voting thresholds.
|
||||||
|
|
||||||
use primitives::traits::IntegerSquareRoot;
|
use primitives::traits::{Zero, IntegerSquareRoot};
|
||||||
use codec::{Input, Slicable};
|
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.
|
/// A means of determining if a vote is past pass threshold.
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
@@ -58,18 +58,60 @@ pub trait Approved<Balance> {
|
|||||||
fn approved(&self, approve: Balance, against: Balance, electorate: Balance) -> bool;
|
fn approved(&self, approve: Balance, against: Balance, electorate: Balance) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Balance: IntegerSquareRoot + Ord + Add<Balance, Output = Balance> + Mul<Balance, Output = Balance> + Div<Balance, Output = Balance> + Copy> Approved<Balance> for VoteThreshold {
|
/// Return `true` iff `n1 / d1 < n2 / d2`. `d1` and `d2` may not be zero.
|
||||||
|
fn compare_rationals<T: Zero + Mul<T, Output = T> + Div<T, Output = T> + Rem<T, Output = T> + 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<Balance: IntegerSquareRoot + Zero + Ord + Add<Balance, Output = Balance> + Mul<Balance, Output = Balance> + Div<Balance, Output = Balance> + Rem<Balance, Output = Balance> + Copy> Approved<Balance> for VoteThreshold {
|
||||||
/// Given `approve` votes for and `against` votes against from a total electorate size of
|
/// 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
|
/// `electorate` (`electorate - (approve + against)` are abstainers), then returns true if the
|
||||||
/// overall outcome is in favour of approval.
|
/// overall outcome is in favour of approval.
|
||||||
fn approved(&self, approve: Balance, against: Balance, electorate: Balance) -> bool {
|
fn approved(&self, approve: Balance, against: Balance, electorate: Balance) -> bool {
|
||||||
let voters = approve + against;
|
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 {
|
match *self {
|
||||||
VoteThreshold::SuperMajorityApprove =>
|
VoteThreshold::SuperMajorityApprove =>
|
||||||
voters.integer_sqrt() * approve / electorate.integer_sqrt() > against,
|
compare_rationals(against, sqrt_voters, approve, sqrt_electorate),
|
||||||
VoteThreshold::SuperMajorityAgainst =>
|
VoteThreshold::SuperMajorityAgainst =>
|
||||||
approve > voters.integer_sqrt() * against / electorate.integer_sqrt(),
|
compare_rationals(against, sqrt_electorate, approve, sqrt_voters),
|
||||||
VoteThreshold::SimpleMajority => approve > against,
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
Reference in New Issue
Block a user