More tests for council voting.

Also allow AsRef to be used for Public keys to simplify test code.
This commit is contained in:
Gav
2018-03-05 12:11:56 +01:00
parent 8d84ca8b48
commit 7d378d3de3
11 changed files with 273 additions and 66 deletions
+1
View File
@@ -1598,6 +1598,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"ed25519 0.1.0", "ed25519 0.1.0",
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
+16 -6
View File
@@ -44,6 +44,8 @@ enum InternalFunctionId {
StakingSetValidatorCount = 0x22, StakingSetValidatorCount = 0x22,
/// Force a new staking era. /// Force a new staking era.
StakingForceNewEra = 0x23, StakingForceNewEra = 0x23,
/// See below.
DemocracyCancelReferendum = 0x30,
} }
impl InternalFunctionId { impl InternalFunctionId {
@@ -57,6 +59,7 @@ impl InternalFunctionId {
InternalFunctionId::StakingSetBondingDuration, InternalFunctionId::StakingSetBondingDuration,
InternalFunctionId::StakingSetValidatorCount, InternalFunctionId::StakingSetValidatorCount,
InternalFunctionId::StakingForceNewEra, InternalFunctionId::StakingForceNewEra,
InternalFunctionId::DemocracyCancelReferendum,
]; ];
functions.iter().map(|&f| f).find(|&f| value == f as u8) functions.iter().map(|&f| f).find(|&f| value == f as u8)
} }
@@ -80,24 +83,27 @@ pub enum Proposal {
StakingSetValidatorCount(u32), StakingSetValidatorCount(u32),
/// Force a new staking era. /// Force a new staking era.
StakingForceNewEra, StakingForceNewEra,
/// Cancel a referendum.
DemocracyCancelReferendum(u32),
} }
impl Slicable for Proposal { impl Slicable for Proposal {
fn decode<I: Input>(input: &mut I) -> Option<Self> { fn decode<I: Input>(input: &mut I) -> Option<Self> {
let id = try_opt!(u8::decode(input).and_then(InternalFunctionId::from_u8)); let id = u8::decode(input).and_then(InternalFunctionId::from_u8)?;
let function = match id { let function = match id {
InternalFunctionId::SystemSetCode => InternalFunctionId::SystemSetCode =>
Proposal::SystemSetCode(try_opt!(Slicable::decode(input))), Proposal::SystemSetCode(Slicable::decode(input)?),
InternalFunctionId::SessionSetLength => InternalFunctionId::SessionSetLength =>
Proposal::SessionSetLength(try_opt!(Slicable::decode(input))), Proposal::SessionSetLength(Slicable::decode(input)?),
InternalFunctionId::SessionForceNewSession => Proposal::SessionForceNewSession, InternalFunctionId::SessionForceNewSession => Proposal::SessionForceNewSession,
InternalFunctionId::StakingSetSessionsPerEra => InternalFunctionId::StakingSetSessionsPerEra =>
Proposal::StakingSetSessionsPerEra(try_opt!(Slicable::decode(input))), Proposal::StakingSetSessionsPerEra(Slicable::decode(input)?),
InternalFunctionId::StakingSetBondingDuration => InternalFunctionId::StakingSetBondingDuration =>
Proposal::StakingSetBondingDuration(try_opt!(Slicable::decode(input))), Proposal::StakingSetBondingDuration(Slicable::decode(input)?),
InternalFunctionId::StakingSetValidatorCount => InternalFunctionId::StakingSetValidatorCount =>
Proposal::StakingSetValidatorCount(try_opt!(Slicable::decode(input))), Proposal::StakingSetValidatorCount(Slicable::decode(input)?),
InternalFunctionId::StakingForceNewEra => Proposal::StakingForceNewEra, InternalFunctionId::StakingForceNewEra => Proposal::StakingForceNewEra,
InternalFunctionId::DemocracyCancelReferendum => Proposal::DemocracyCancelReferendum(Slicable::decode(input)?),
}; };
Some(function) Some(function)
@@ -132,6 +138,10 @@ impl Slicable for Proposal {
Proposal::StakingForceNewEra => { Proposal::StakingForceNewEra => {
(InternalFunctionId::StakingForceNewEra as u8).using_encoded(|s| v.extend(s)); (InternalFunctionId::StakingForceNewEra as u8).using_encoded(|s| v.extend(s));
} }
Proposal::DemocracyCancelReferendum(ref data) => {
(InternalFunctionId::DemocracyCancelReferendum as u8).using_encoded(|s| v.extend(s));
data.using_encoded(|s| v.extend(s));
}
} }
v v
+4 -1
View File
@@ -17,7 +17,7 @@
//! Democratic system: Handles administration of general stakeholder voting. //! Democratic system: Handles administration of general stakeholder voting.
use demo_primitives::Proposal; use demo_primitives::Proposal;
use runtime::{staking, system, session}; use runtime::{staking, system, session, democracy};
pub fn enact_proposal(proposal: Proposal) { pub fn enact_proposal(proposal: Proposal) {
match proposal { match proposal {
@@ -42,5 +42,8 @@ pub fn enact_proposal(proposal: Proposal) {
Proposal::StakingForceNewEra => { Proposal::StakingForceNewEra => {
staking::privileged::force_new_era() staking::privileged::force_new_era()
} }
Proposal::DemocracyCancelReferendum(ref_index) => {
democracy::privileged::clear_referendum(ref_index)
}
} }
} }
@@ -91,8 +91,8 @@ pub const TERM_DURATION: &[u8] = b"cou:trm";
pub const DESIRED_SEATS: &[u8] = b"cou:sts"; pub const DESIRED_SEATS: &[u8] = b"cou:sts";
// permanent state (always relevant, changes only at the finalisation of voting) // permanent state (always relevant, changes only at the finalisation of voting)
pub const ACTIVE_COUNCIL: &[u8] = b"cou:act"; pub const ACTIVE_COUNCIL: &[u8] = b"cou:act"; // Vec<(AccountId, expiry: BlockNumber)>
pub const VOTE_COUNT: &[u8] = b"cou:vco"; pub const VOTE_COUNT: &[u8] = b"cou:vco"; // VoteIndex
// persistent state (always relevant, changes constantly) // persistent state (always relevant, changes constantly)
pub const APPROVALS_OF: &[u8] = b"cou:apr:"; // Vec<bool> pub const APPROVALS_OF: &[u8] = b"cou:apr:"; // Vec<bool>
@@ -51,27 +51,27 @@ pub fn was_vetoed(proposal: &ProposalHash) -> bool {
storage::exists(&proposal.to_keyed_vec(VETOED_PROPOSAL)) storage::exists(&proposal.to_keyed_vec(VETOED_PROPOSAL))
} }
pub fn will_still_be_councillor_at(who: &AccountId, n: BlockNumber) -> bool { pub fn will_still_be_councillor_at<P: AsRef<AccountId>>(who: P, n: BlockNumber) -> bool {
council::active_council().iter() council::active_council().iter()
.find(|&&(ref a, _)| a == who) .find(|&&(ref a, _)| a == who.as_ref())
.map(|&(_, expires)| expires > n) .map(|&(_, expires)| expires > n)
.unwrap_or(false) .unwrap_or(false)
} }
pub fn vote_of(who: &AccountId, proposal: &ProposalHash) -> Option<bool> { pub fn vote_of<P: AsRef<AccountId>>(who: P, proposal: &ProposalHash) -> Option<bool> {
storage::get(&(*who, *proposal).to_keyed_vec(COUNCIL_VOTE_OF)) storage::get(&(*proposal, *who.as_ref()).to_keyed_vec(COUNCIL_VOTE_OF))
} }
pub fn take_vote_of(who: &AccountId, proposal: &ProposalHash) -> Option<bool> { pub fn proposal_voters(proposal: &ProposalHash) -> Vec<AccountId> {
storage::get(&(*who, *proposal).to_keyed_vec(COUNCIL_VOTE_OF)) storage::get_or_default(&proposal.to_keyed_vec(PROPOSAL_VOTERS))
} }
pub fn tally(proposal_hash: &ProposalHash) -> (u32, u32, u32) { pub fn tally(proposal_hash: &ProposalHash) -> (u32, u32, u32) {
generic_tally(proposal_hash, vote_of) generic_tally(proposal_hash, |w: &AccountId, p: &ProposalHash| storage::get(&(*p, *w).to_keyed_vec(COUNCIL_VOTE_OF)))
} }
fn take_tally(proposal_hash: &ProposalHash) -> (u32, u32, u32) { fn take_tally(proposal_hash: &ProposalHash) -> (u32, u32, u32) {
generic_tally(proposal_hash, take_vote_of) generic_tally(proposal_hash, |w: &AccountId, p: &ProposalHash| storage::get(&(*p, *w).to_keyed_vec(COUNCIL_VOTE_OF)))
} }
fn generic_tally<F: Fn(&AccountId, &ProposalHash) -> Option<bool>>(proposal_hash: &ProposalHash, vote_of: F) -> (u32, u32, u32) { fn generic_tally<F: Fn(&AccountId, &ProposalHash) -> Option<bool>>(proposal_hash: &ProposalHash, vote_of: F) -> (u32, u32, u32) {
@@ -103,7 +103,7 @@ fn take_proposal_if_expiring_at(n: BlockNumber) -> Option<(Proposal, ProposalHas
pub mod public { pub mod public {
use super::*; use super::*;
pub fn propose(signed: &AccountId, proposal: &Proposal) { pub fn propose<P: AsRef<AccountId> + Copy>(signed: P, proposal: &Proposal) {
let expiry = system::block_number() + voting_period(); let expiry = system::block_number() + voting_period();
assert!(will_still_be_councillor_at(signed, expiry)); assert!(will_still_be_councillor_at(signed, expiry));
@@ -116,19 +116,24 @@ pub mod public {
set_proposals(&proposals); set_proposals(&proposals);
storage::put(&proposal_hash.to_keyed_vec(PROPOSAL_OF), proposal); storage::put(&proposal_hash.to_keyed_vec(PROPOSAL_OF), proposal);
storage::put(&proposal_hash.to_keyed_vec(PROPOSAL_VOTERS), &vec![*signed]); storage::put(&proposal_hash.to_keyed_vec(PROPOSAL_VOTERS), &vec![*signed.as_ref()]);
storage::put(&(proposal_hash, *signed).to_keyed_vec(COUNCIL_VOTE_OF), &true); storage::put(&(proposal_hash, *(signed.as_ref())).to_keyed_vec(COUNCIL_VOTE_OF), &true);
} }
pub fn vote(signed: AccountId, proposal: &ProposalHash, approve: bool) { pub fn vote<P: AsRef<AccountId> + Copy>(signed: P, proposal: &ProposalHash, approve: bool) {
if vote_of(signed, proposal).is_none() {
let mut voters = proposal_voters(proposal);
voters.push(*signed.as_ref());
storage::put(&proposal.to_keyed_vec(PROPOSAL_VOTERS), &voters);
}
storage::put(&(*proposal, *(signed.as_ref())).to_keyed_vec(COUNCIL_VOTE_OF), &approve);
}
pub fn veto<P: AsRef<AccountId> + Copy>(signed: P, proposal: &ProposalHash) {
} }
pub fn veto(signed: AccountId, proposal: &ProposalHash) { pub fn repropose<P: AsRef<AccountId> + Copy>(signed: P, proposal: &Proposal) {
}
pub fn repropose(signed: AccountId, proposal: &Proposal) {
} }
} }
@@ -153,17 +158,147 @@ pub mod internal {
pub fn end_block(now: BlockNumber) { pub fn end_block(now: BlockNumber) {
while let Some((proposal, proposal_hash)) = take_proposal_if_expiring_at(now) { while let Some((proposal, proposal_hash)) = take_proposal_if_expiring_at(now) {
let tally = take_tally(&proposal_hash); let tally = take_tally(&proposal_hash);
let vote_threshold = match tally.0 { if let Proposal::DemocracyCancelReferendum(ref_index) = proposal {
x if x == tally.2 => VoteThreshold::SuperMajorityAgainst, if tally.0 == tally.2 {
x if x > tally.2 / 2 => VoteThreshold::SimpleMajority, democracy::privileged::clear_referendum(ref_index);
_ => VoteThreshold::SuperMajorityApprove, }
} else {
match tally {
(_, 0, 0) => start_referendum(proposal, VoteThreshold::SuperMajorityAgainst),
(y, n, x) if y > n + x => start_referendum(proposal, VoteThreshold::SimpleMajority),
_ => {},
}; };
start_referendum(proposal, vote_threshold); }
} }
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { pub mod testing {
use super::*;
use runtime_io::{twox_128, TestExternalities};
use keyring::Keyring::{Alice, Bob, Charlie};
use codec::Joiner;
use runtime::council;
pub fn externalities() -> TestExternalities {
let expiry: BlockNumber = 10;
let extras: TestExternalities = map![
twox_128(council::ACTIVE_COUNCIL).to_vec() => vec![].and(&vec![
(Alice.to_raw_public(), expiry),
(Bob.into(), expiry),
(Charlie.into(), expiry)
]),
twox_128(COOLOFF_PERIOD).to_vec() => vec![].and(&2u64),
twox_128(VOTING_PERIOD).to_vec() => vec![].and(&1u64)
];
council::testing::externalities()
.into_iter().chain(extras.into_iter()).collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
use runtime_io::{with_externalities, twox_128, TestExternalities};
use codec::{KeyedVec, Joiner};
use keyring::Keyring::{Alice, Bob, Charlie, Dave};
use environment::with_env;
use demo_primitives::{AccountId, Proposal};
use runtime::{staking, council, democracy};
use runtime::democracy::VoteThreshold;
fn new_test_ext() -> TestExternalities {
testing::externalities()
}
#[test]
fn basic_environment_works() {
with_externalities(&mut new_test_ext(), || {
with_env(|e| e.block_number = 1);
assert_eq!(staking::bonding_duration(), 0);
assert_eq!(cooloff_period(), 2);
assert_eq!(voting_period(), 1);
assert_eq!(will_still_be_councillor_at(Alice, 1), true);
assert_eq!(will_still_be_councillor_at(Alice, 10), false);
assert_eq!(will_still_be_councillor_at(Dave, 10), false);
assert_eq!(proposals(), Vec::<(BlockNumber, ProposalHash)>::new());
assert_eq!(proposal_voters(&ProposalHash::default()), Vec::<AccountId>::new());
assert_eq!(was_vetoed(&ProposalHash::default()), false);
assert_eq!(vote_of(Alice, &ProposalHash::default()), None);
assert_eq!(tally(&ProposalHash::default()), (0, 0, 3));
});
}
#[test]
fn simple_propose_should_work() {
with_externalities(&mut new_test_ext(), || {
with_env(|e| e.block_number = 1);
let proposal = Proposal::StakingSetBondingDuration(42);
let hash = proposal.blake2_256();
public::propose(Alice, &proposal);
assert_eq!(proposals().len(), 1);
assert_eq!(proposal_voters(&hash), vec![Alice.to_raw_public()]);
assert_eq!(vote_of(Alice, &hash), Some(true));
assert_eq!(tally(&hash), (1, 0, 2));
});
}
#[test]
fn unvoted_proposal_should_expire_without_action() {
with_externalities(&mut new_test_ext(), || {
with_env(|e| e.block_number = 1);
public::propose(Alice, &Proposal::StakingSetBondingDuration(42));
assert_eq!(tally(&Proposal::StakingSetBondingDuration(42).blake2_256()), (1, 0, 2));
internal::end_block(1);
with_env(|e| e.block_number = 2);
internal::end_block(2);
assert_eq!(proposals().len(), 0);
assert_eq!(democracy::active_referendums().len(), 0);
});
}
#[test]
fn unanimous_proposal_should_expire_with_biased_referendum() {
with_externalities(&mut new_test_ext(), || {
with_env(|e| e.block_number = 1);
public::propose(Alice, &Proposal::StakingSetBondingDuration(42));
public::vote(Bob, &Proposal::StakingSetBondingDuration(42).blake2_256(), true);
public::vote(Charlie, &Proposal::StakingSetBondingDuration(42).blake2_256(), true);
assert_eq!(tally(&Proposal::StakingSetBondingDuration(42).blake2_256()), (3, 0, 0));
internal::end_block(1);
with_env(|e| e.block_number = 2);
internal::end_block(2);
assert_eq!(proposals().len(), 0);
assert_eq!(democracy::active_referendums(), vec![(0, 3, Proposal::StakingSetBondingDuration(42), VoteThreshold::SuperMajorityAgainst)]);
});
}
#[test]
fn majority_proposal_should_expire_with_unbiased_referendum() {
with_externalities(&mut new_test_ext(), || {
with_env(|e| e.block_number = 1);
public::propose(Alice, &Proposal::StakingSetBondingDuration(42));
public::vote(Bob, &Proposal::StakingSetBondingDuration(42).blake2_256(), true);
public::vote(Charlie, &Proposal::StakingSetBondingDuration(42).blake2_256(), false);
assert_eq!(tally(&Proposal::StakingSetBondingDuration(42).blake2_256()), (2, 1, 0));
internal::end_block(1);
with_env(|e| e.block_number = 2);
internal::end_block(2);
assert_eq!(proposals().len(), 0);
assert_eq!(democracy::active_referendums(), vec![(0, 3, Proposal::StakingSetBondingDuration(42), VoteThreshold::SimpleMajority)]);
});
}
#[test]
#[should_panic]
fn propose_by_public_should_panic() {
with_externalities(&mut new_test_ext(), || {
with_env(|e| e.block_number = 1);
public::propose(Dave, &Proposal::StakingSetBondingDuration(42));
});
}
} }
@@ -27,6 +27,8 @@ use runtime::staking::Balance;
pub type PropIndex = u32; pub type PropIndex = u32;
pub type ReferendumIndex = u32; pub type ReferendumIndex = u32;
#[cfg_attr(test, derive(Debug))]
#[derive(Clone, Copy, PartialEq)]
pub enum VoteThreshold { pub enum VoteThreshold {
SuperMajorityApprove, SuperMajorityApprove,
SuperMajorityAgainst, SuperMajorityAgainst,
@@ -138,6 +140,15 @@ pub fn referendum_info(ref_index: ReferendumIndex) -> Option<(BlockNumber, Propo
storage::get(&ref_index.to_keyed_vec(REFERENDUM_INFO_OF)) storage::get(&ref_index.to_keyed_vec(REFERENDUM_INFO_OF))
} }
/// Get all referendums currently active.
pub fn active_referendums() -> Vec<(ReferendumIndex, BlockNumber, Proposal, VoteThreshold)> {
let next: ReferendumIndex = storage::get_or_default(NEXT_TALLY);
let last: ReferendumIndex = storage::get_or_default(REFERENDUM_COUNT);
(next..last).into_iter()
.filter_map(|i| referendum_info(i).map(|(n, p, t)| (i, n, p, t)))
.collect()
}
/// Get all referendums ready for tally at block `n`. /// Get all referendums ready for tally at block `n`.
pub fn maturing_referendums_at(n: BlockNumber) -> Vec<(ReferendumIndex, BlockNumber, Proposal, VoteThreshold)> { pub fn maturing_referendums_at(n: BlockNumber) -> Vec<(ReferendumIndex, BlockNumber, Proposal, VoteThreshold)> {
let next: ReferendumIndex = storage::get_or_default(NEXT_TALLY); let next: ReferendumIndex = storage::get_or_default(NEXT_TALLY);
+20 -8
View File
@@ -37,8 +37,8 @@ pub struct LocalizedSignature {
} }
/// Verify a message without type checking the parameters' types for the right size. /// Verify a message without type checking the parameters' types for the right size.
pub fn verify(sig: &[u8], message: &[u8], public: &[u8]) -> bool { pub fn verify<P: AsRef<[u8]>>(sig: &[u8], message: &[u8], public: P) -> bool {
let public_key = untrusted::Input::from(public); let public_key = untrusted::Input::from(public.as_ref());
let msg = untrusted::Input::from(message); let msg = untrusted::Input::from(message);
let sig = untrusted::Input::from(sig); let sig = untrusted::Input::from(sig);
@@ -104,6 +104,18 @@ impl Into<[u8; 32]> for Public {
} }
} }
impl AsRef<Public> for Public {
fn as_ref(&self) -> &Public {
&self
}
}
impl AsRef<Pair> for Pair {
fn as_ref(&self) -> &Pair {
&self
}
}
impl Pair { impl Pair {
/// Generate new secure (random) key pair. /// Generate new secure (random) key pair.
pub fn new() -> Pair { pub fn new() -> Pair {
@@ -144,8 +156,8 @@ impl Pair {
} }
/// Verify a signature on a message. /// Verify a signature on a message.
pub fn verify_strong(sig: &Signature, message: &[u8], pubkey: &Public) -> bool { pub fn verify_strong<P: AsRef<Public>>(sig: &Signature, message: &[u8], pubkey: P) -> bool {
let public_key = untrusted::Input::from(&pubkey.0[..]); let public_key = untrusted::Input::from(&pubkey.as_ref().0[..]);
let msg = untrusted::Input::from(message); let msg = untrusted::Input::from(message);
let sig = untrusted::Input::from(&sig.0[..]); let sig = untrusted::Input::from(&sig.0[..]);
@@ -157,19 +169,19 @@ pub fn verify_strong(sig: &Signature, message: &[u8], pubkey: &Public) -> bool {
pub trait Verifiable { pub trait Verifiable {
/// Verify something that acts like a signature. /// Verify something that acts like a signature.
fn verify(&self, message: &[u8], pubkey: &Public) -> bool; fn verify<P: AsRef<Public>>(&self, message: &[u8], pubkey: P) -> bool;
} }
impl Verifiable for Signature { impl Verifiable for Signature {
/// Verify something that acts like a signature. /// Verify something that acts like a signature.
fn verify(&self, message: &[u8], pubkey: &Public) -> bool { fn verify<P: AsRef<Public>>(&self, message: &[u8], pubkey: P) -> bool {
verify_strong(&self, message, pubkey) verify_strong(&self, message, pubkey)
} }
} }
impl Verifiable for LocalizedSignature { impl Verifiable for LocalizedSignature {
fn verify(&self, message: &[u8], pubkey: &Public) -> bool { fn verify<P: AsRef<Public>>(&self, message: &[u8], pubkey: P) -> bool {
pubkey == &self.signer && self.signature.verify(message, pubkey) pubkey.as_ref() == &self.signer && self.signature.verify(message, pubkey)
} }
} }
+1
View File
@@ -6,3 +6,4 @@ authors = ["Parity Technologies <admin@parity.io>"]
[dependencies] [dependencies]
ed25519 = { path = "../ed25519" } ed25519 = { path = "../ed25519" }
hex-literal = { version = "0.1.0" } hex-literal = { version = "0.1.0" }
lazy_static = { version = "1.0" }
+56 -22
View File
@@ -17,12 +17,14 @@
//! Support code for the runtime. //! Support code for the runtime.
#[macro_use] extern crate hex_literal; #[macro_use] extern crate hex_literal;
#[macro_use] extern crate lazy_static;
extern crate ed25519; extern crate ed25519;
use std::collections::HashMap;
use ed25519::{Pair, Public, Signature}; use ed25519::{Pair, Public, Signature};
/// Set of test accounts. /// Set of test accounts.
#[derive(Clone, Copy, PartialEq)] #[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum Keyring { pub enum Keyring {
Alice, Alice,
Bob, Bob,
@@ -63,7 +65,7 @@ impl Keyring {
} }
pub fn sign(self, msg: &[u8]) -> Signature { pub fn sign(self, msg: &[u8]) -> Signature {
Pair::from(self).sign(msg) AsRef::<Pair>::as_ref(&self).sign(msg)
} }
} }
@@ -82,32 +84,64 @@ impl From<Keyring> for &'static str {
} }
} }
impl From<Keyring> for Pair { lazy_static! {
fn from(k: Keyring) -> Self { static ref PRIVATE_KEYS: HashMap<Keyring, Pair> = {
match k { let mut m = HashMap::new();
Keyring::Alice => Pair::from_seed(b"Alice "), m.insert(Keyring::Alice, Pair::from_seed(b"Alice "));
Keyring::Bob => Pair::from_seed(b"Bob "), m.insert(Keyring::Bob, Pair::from_seed(b"Bob "));
Keyring::Charlie => Pair::from_seed(b"Charlie "), m.insert(Keyring::Charlie, Pair::from_seed(b"Charlie "));
Keyring::Dave => Pair::from_seed(b"Dave "), m.insert(Keyring::Dave, Pair::from_seed(b"Dave "));
Keyring::Eve => Pair::from_seed(b"Eve "), m.insert(Keyring::Eve, Pair::from_seed(b"Eve "));
Keyring::Ferdie => Pair::from_seed(b"Ferdie "), m.insert(Keyring::Ferdie, Pair::from_seed(b"Ferdie "));
Keyring::One => Pair::from_seed(b"12345678901234567890123456789012"), m.insert(Keyring::One, Pair::from_seed(b"12345678901234567890123456789012"));
Keyring::Two => Pair::from_seed(&hex!("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60")), m.insert(Keyring::Two, Pair::from_seed(&hex!("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60")));
} m
} };
static ref PUBLIC_KEYS: HashMap<Keyring, Public> = {
PRIVATE_KEYS.iter().map(|(&name, pair)| (name, pair.public())).collect()
};
} }
impl From<Keyring> for Public { impl From<Keyring> for Public {
fn from(k: Keyring) -> Self { fn from(k: Keyring) -> Self {
let pair: Pair = k.into(); (*PUBLIC_KEYS).get(&k).unwrap().clone()
pair.public()
} }
} }
impl From<Keyring> for [u8; 32] { impl From<Keyring> for [u8; 32] {
fn from(k: Keyring) -> Self { fn from(k: Keyring) -> Self {
let pair: Pair = k.into(); *(*PUBLIC_KEYS).get(&k).unwrap().as_array_ref()
*pair.public().as_array_ref() }
}
impl From<Keyring> for &'static [u8; 32] {
fn from(k: Keyring) -> Self {
(*PUBLIC_KEYS).get(&k).unwrap().as_array_ref()
}
}
impl AsRef<[u8; 32]> for Keyring {
fn as_ref(&self) -> &[u8; 32] {
(*PUBLIC_KEYS).get(self).unwrap().as_array_ref()
}
}
impl AsRef<[u8]> for Keyring {
fn as_ref(&self) -> &[u8] {
(*PUBLIC_KEYS).get(self).unwrap().as_array_ref()
}
}
impl AsRef<Public> for Keyring {
fn as_ref(&self) -> &Public {
(*PUBLIC_KEYS).get(self).unwrap()
}
}
impl AsRef<Pair> for Keyring {
fn as_ref(&self) -> &Pair {
(*PRIVATE_KEYS).get(self).unwrap()
} }
} }
@@ -118,8 +152,8 @@ mod tests {
#[test] #[test]
fn should_work() { fn should_work() {
assert!(Keyring::Alice.sign(b"I am Alice!").verify(b"I am Alice!", &Keyring::Alice.into())); assert!(Keyring::Alice.sign(b"I am Alice!").verify(b"I am Alice!", Keyring::Alice));
assert!(!Keyring::Alice.sign(b"I am Alice!").verify(b"I am Bob!", &Keyring::Alice.into())); assert!(!Keyring::Alice.sign(b"I am Alice!").verify(b"I am Bob!", Keyring::Alice));
assert!(!Keyring::Alice.sign(b"I am Alice!").verify(b"I am Alice!", &Keyring::Bob.into())); assert!(!Keyring::Alice.sign(b"I am Alice!").verify(b"I am Alice!", Keyring::Bob));
} }
} }
+1 -1
View File
@@ -86,7 +86,7 @@ pub fn enumerated_trie_root(serialised_values: &[&[u8]]) -> [u8; 32] {
} }
/// Verify a ed25519 signature. /// Verify a ed25519 signature.
pub fn ed25519_verify(sig: &[u8; 64], msg: &[u8], pubkey: &[u8; 32]) -> bool { pub fn ed25519_verify<P: AsRef<[u8]>>(sig: &[u8; 64], msg: &[u8], pubkey: P) -> bool {
ed25519::verify(sig, msg, pubkey) ed25519::verify(sig, msg, pubkey)
} }
@@ -161,9 +161,9 @@ pub fn twox_128(data: &[u8]) -> [u8; 16] {
} }
/// Verify a ed25519 signature. /// Verify a ed25519 signature.
pub fn ed25519_verify(sig: &[u8; 64], msg: &[u8], pubkey: &[u8; 32]) -> bool { pub fn ed25519_verify<P: AsRef<[u8]>>(sig: &[u8; 64], msg: &[u8], pubkey: P) -> bool {
unsafe { unsafe {
ext_ed25519_verify(msg.as_ptr(), msg.len() as u32, sig.as_ptr(), pubkey.as_ptr()) == 0 ext_ed25519_verify(msg.as_ptr(), msg.len() as u32, sig.as_ptr(), pubkey.as_ref().as_ptr()) == 0
} }
} }