From 2e99e3e34264eda8c16f51956fd9ce24b79f269b Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 4 Jun 2020 19:54:23 +0200 Subject: [PATCH] Admin facility to move claims with council permission. --- runtime/common/src/claims.rs | 65 +++++++++++++++++++++++++++++++-- runtime/kusama/src/lib.rs | 1 + runtime/polkadot/src/lib.rs | 2 + runtime/test-runtime/src/lib.rs | 1 + 4 files changed, 66 insertions(+), 3 deletions(-) diff --git a/runtime/common/src/claims.rs b/runtime/common/src/claims.rs index 4207f56b63b9..7271ae195193 100644 --- a/runtime/common/src/claims.rs +++ b/runtime/common/src/claims.rs @@ -19,8 +19,8 @@ use sp_std::{prelude::*, fmt::Debug}; use sp_io::{hashing::keccak_256, crypto::secp256k1_ecdsa_recover}; use frame_support::{ - decl_event, decl_storage, decl_module, decl_error, ensure, - traits::{Currency, Get, VestingSchedule}, weights::{Pays, DispatchClass}, dispatch::IsSubType + decl_event, decl_storage, decl_module, decl_error, ensure, dispatch::IsSubType, + traits::{Currency, Get, VestingSchedule, EnsureOrigin}, weights::{Pays, DispatchClass} }; use system::{ensure_signed, ensure_root, ensure_none}; use codec::{Encode, Decode}; @@ -46,6 +46,7 @@ pub trait Trait: system::Trait { type Event: From> + Into<::Event>; type VestingSchedule: VestingSchedule; type Prefix: Get<&'static [u8]>; + type MoveClaimOrigin: EnsureOrigin; } /// The kind of a statement an account needs to make for a claim to be valid. @@ -391,6 +392,26 @@ decl_module! { Self::process_claim(signer, who.clone())?; Preclaims::::remove(&who); } + + #[weight = ( + T::DbWeight::get().reads_writes(4, 4) + 100_000_000_000, + DispatchClass::Normal, + Pays::No + )] + fn move_claim(origin, + old: EthereumAddress, + new: EthereumAddress, + maybe_preclaim: Option, + ) { + T::MoveClaimOrigin::try_origin(origin).map(|_| ()).or_else(ensure_root)?; + + Claims::::take(&old).map(|c| Claims::::insert(&new, c)); + Vesting::::take(&old).map(|c| Vesting::::insert(&new, c)); + Signing::take(&old).map(|c| Signing::insert(&new, c)); + maybe_preclaim.map(|preclaim| Preclaims::::mutate(&preclaim, |maybe_o| + if maybe_o.as_ref().map_or(false, |o| o == &old) { *maybe_o = Some(new) } + )); + } } } @@ -618,7 +639,8 @@ mod tests { use sp_runtime::{Perbill, traits::{BlakeTwo256, IdentityLookup, Identity}, testing::Header}; use frame_support::{ impl_outer_origin, impl_outer_dispatch, assert_ok, assert_err, assert_noop, parameter_types, - weights::{Pays, GetDispatchInfo}, traits::ExistenceRequirement, + ord_parameter_types, weights::{Pays, GetDispatchInfo}, traits::ExistenceRequirement, + dispatch::DispatchError::BadOrigin, }; use balances; use super::Call as ClaimsCall; @@ -693,11 +715,15 @@ mod tests { parameter_types!{ pub Prefix: &'static [u8] = b"Pay RUSTs to the TEST account:"; } + ord_parameter_types! { + pub const Six: u64 = 6; + } impl Trait for Test { type Event = (); type VestingSchedule = Vesting; type Prefix = Prefix; + type MoveClaimOrigin = system::EnsureSignedBy; } type System = system::Module; type Balances = balances::Module; @@ -775,6 +801,39 @@ mod tests { }); } + #[test] + fn basic_claim_moving_works() { + new_test_ext().execute_with(|| { + assert_eq!(Balances::free_balance(42), 0); + assert_noop!(Claims::move_claim(Origin::signed(1), eth(&alice()), eth(&bob()), None), BadOrigin); + assert_ok!(Claims::move_claim(Origin::signed(6), eth(&alice()), eth(&bob()), None)); + assert_noop!(Claims::claim(Origin::NONE, 42, sig::(&alice(), &42u64.encode(), &[][..])), Error::::SignerHasNoClaim); + assert_ok!(Claims::claim(Origin::NONE, 42, sig::(&bob(), &42u64.encode(), &[][..]))); + assert_eq!(Balances::free_balance(&42), 100); + assert_eq!(Vesting::vesting_balance(&42), Some(50)); + assert_eq!(Claims::total(), total_claims() - 100); + }); + } + + #[test] + fn claim_attest_moving_works() { + new_test_ext().execute_with(|| { + assert_ok!(Claims::move_claim(Origin::signed(6), eth(&dave()), eth(&bob()), None)); + let s = sig::(&bob(), &42u64.encode(), StatementKind::Regular.to_text()); + assert_ok!(Claims::claim_attest(Origin::NONE, 42, s, StatementKind::Regular.to_text().to_vec())); + assert_eq!(Balances::free_balance(&42), 200); + }); + } + + #[test] + fn attest_moving_works() { + new_test_ext().execute_with(|| { + assert_ok!(Claims::move_claim(Origin::signed(6), eth(&eve()), eth(&bob()), Some(42))); + assert_ok!(Claims::attest(Origin::signed(42), StatementKind::Saft.to_text().to_vec())); + assert_eq!(Balances::free_balance(&42), 300); + }); + } + #[test] fn claiming_does_not_bypass_signing() { new_test_ext().execute_with(|| { diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index 2b3804047bdc..eb28085b82d0 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -662,6 +662,7 @@ impl claims::Trait for Runtime { type Event = Event; type VestingSchedule = Vesting; type Prefix = Prefix; + type MoveClaimOrigin = collective::EnsureProportionMoreThan<_1, _2, AccountId, CouncilCollective>; } parameter_types! { diff --git a/runtime/polkadot/src/lib.rs b/runtime/polkadot/src/lib.rs index bc2c9f3c212d..3db5bb4a576b 100644 --- a/runtime/polkadot/src/lib.rs +++ b/runtime/polkadot/src/lib.rs @@ -710,6 +710,8 @@ impl claims::Trait for Runtime { type Event = Event; type VestingSchedule = Vesting; type Prefix = Prefix; + /// At least 3/4 of the council must agree to a claim move before it can happen. + type MoveClaimOrigin = collective::EnsureProportionAtLeast<_3, _4, AccountId, CouncilCollective>; } parameter_types! { diff --git a/runtime/test-runtime/src/lib.rs b/runtime/test-runtime/src/lib.rs index 15cfbe96b9b5..d7e6884fc4b2 100644 --- a/runtime/test-runtime/src/lib.rs +++ b/runtime/test-runtime/src/lib.rs @@ -473,6 +473,7 @@ impl claims::Trait for Runtime { type Event = Event; type VestingSchedule = Vesting; type Prefix = Prefix; + type MoveClaimOrigin = system::EnsureRoot; } parameter_types! {