diff --git a/Cargo.lock b/Cargo.lock index 94a95168d2..30a208c092 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -159,6 +159,12 @@ version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dabe5a181f83789739c194cbe5a897dde195078fac08568d09221fd6137a7ba8" +[[package]] +name = "array-bytes" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43759f68cc29a37659a6b7a48a49674e03a888b6d41f36f4204c36d0d09a51de" + [[package]] name = "array-bytes" version = "1.2.3" @@ -1236,7 +1242,7 @@ dependencies = [ name = "darwinia-claims" version = "1.2.3" dependencies = [ - "array-bytes", + "array-bytes 1.2.3", "darwinia-balances", "darwinia-support", "frame-support", @@ -1332,7 +1338,7 @@ dependencies = [ name = "darwinia-ethereum-backing" version = "1.2.3" dependencies = [ - "array-bytes", + "array-bytes 1.2.3", "darwinia-balances", "darwinia-ethereum-linear-relay", "darwinia-ethereum-relay", @@ -1361,7 +1367,7 @@ dependencies = [ name = "darwinia-ethereum-linear-relay" version = "1.2.3" dependencies = [ - "array-bytes", + "array-bytes 1.2.3", "darwinia-balances", "darwinia-support", "ethereum-primitives", @@ -1382,7 +1388,7 @@ dependencies = [ name = "darwinia-ethereum-relay" version = "1.2.3" dependencies = [ - "array-bytes", + "array-bytes 1.2.3", "blake2-rfc", "ckb-merkle-mountain-range", "darwinia-balances", @@ -1444,7 +1450,7 @@ dependencies = [ name = "darwinia-header-mmr" version = "1.2.3" dependencies = [ - "array-bytes", + "array-bytes 1.2.3", "ckb-merkle-mountain-range", "darwinia-header-mmr-rpc-runtime-api", "darwinia-relay-primitives", @@ -1490,6 +1496,7 @@ dependencies = [ name = "darwinia-relay-authorities" version = "1.2.3" dependencies = [ + "array-bytes 0.3.0", "darwinia-balances", "darwinia-relay-primitives", "darwinia-support", @@ -1715,7 +1722,7 @@ dependencies = [ name = "drml" version = "1.0.10" dependencies = [ - "array-bytes", + "array-bytes 1.2.3", "darwinia-balances-rpc", "darwinia-balances-rpc-runtime-api", "darwinia-claims", @@ -2156,7 +2163,7 @@ dependencies = [ name = "ethereum-primitives" version = "1.2.3" dependencies = [ - "array-bytes", + "array-bytes 1.2.3", "ethash", "ethbloom 0.8.1", "ethereum-types 0.8.0", diff --git a/bin/node/runtime/pangolin/polkadot-compatible-types.json b/bin/node/runtime/pangolin/polkadot-compatible-types.json index 73d7acf169..4c55629eea 100644 --- a/bin/node/runtime/pangolin/polkadot-compatible-types.json +++ b/bin/node/runtime/pangolin/polkadot-compatible-types.json @@ -208,6 +208,7 @@ "parent_mmr_root": "Hash" }, "__[primitives.relay]__": {}, + "OpCode": "[u8; 4; OpCode]", "Term": "u32", "RelayHeaderId": "EthereumBlockNumber", "RelayHeaderParcel": "EthereumRelayHeaderParcel", diff --git a/bin/node/runtime/pangolin/src/lib.rs b/bin/node/runtime/pangolin/src/lib.rs index 76c5d813ed..f9992e946a 100644 --- a/bin/node/runtime/pangolin/src/lib.rs +++ b/bin/node/runtime/pangolin/src/lib.rs @@ -265,6 +265,7 @@ use constants::*; use darwinia_balances_rpc_runtime_api::RuntimeDispatchInfo as BalancesRuntimeDispatchInfo; use darwinia_evm::Runner; use darwinia_header_mmr_rpc_runtime_api::RuntimeDispatchInfo as HeaderMMRRuntimeDispatchInfo; +use darwinia_relay_primitives::relay_authorities::OpCode; use darwinia_staking::EraIndex; use darwinia_staking_rpc_runtime_api::RuntimeDispatchInfo as StakingRuntimeDispatchInfo; use drml_primitives::*; @@ -978,6 +979,8 @@ impl darwinia_crab_backing::Trait for Runtime { parameter_types! { pub const EthereumBackingModuleId: ModuleId = ModuleId(*b"da/ethbk"); pub const EthereumBackingFeeModuleId: ModuleId = ModuleId(*b"da/ethfe"); + pub const RingLockLimit: Balance = 10_000_000 * COIN; + pub const KtonLockLimit: Balance = 1000 * COIN; pub const AdvancedFee: Balance = 50 * COIN; pub const SyncReward: Balance = 1000 * COIN; } @@ -990,6 +993,8 @@ impl darwinia_ethereum_backing::Trait for Runtime { type OnDepositRedeem = Staking; type RingCurrency = Ring; type KtonCurrency = Kton; + type RingLockLimit = RingLockLimit; + type KtonLockLimit = KtonLockLimit; type AdvancedFee = AdvancedFee; type SyncReward = SyncReward; type EcdsaAuthorities = EthereumRelayAuthorities; @@ -1085,6 +1090,10 @@ parameter_types! { pub const EthereumRelayAuthoritiesLockId: LockIdentifier = *b"ethrauth"; pub const EthereumRelayAuthoritiesTermDuration: BlockNumber = 30 * DAYS; pub const MaxCandidates: usize = 7; + pub const OpCodes: (OpCode, OpCode) = ( + [71, 159, 189, 249], + [180, 188, 244, 151] + ); pub const SignThreshold: Perbill = Perbill::from_percent(60); pub const SubmitDuration: BlockNumber = 100; } @@ -1099,6 +1108,7 @@ impl darwinia_relay_authorities::Trait for Run type ResetOrigin = ApproveOrigin; type DarwiniaMMR = HeaderMMR; type Sign = EthereumBacking; + type OpCodes = OpCodes; type SignThreshold = SignThreshold; type SubmitDuration = SubmitDuration; type WeightInfo = (); diff --git a/bin/node/runtime/pangolin/types.json b/bin/node/runtime/pangolin/types.json index 9997cdc263..4c9524b5b2 100644 --- a/bin/node/runtime/pangolin/types.json +++ b/bin/node/runtime/pangolin/types.json @@ -200,6 +200,7 @@ "parent_mmr_root": "Hash" }, "__[primitives.relay]__": {}, + "OpCode": "[u8; 4; OpCode]", "Term": "u32", "RelayHeaderId": "EthereumBlockNumber", "RelayHeaderParcel": "EthereumRelayHeaderParcel", @@ -226,6 +227,10 @@ "stake": "Balance", "term": "BlockNumber" }, + "ScheduledAuthoritiesChange": { + "next_authorities": "Vec", + "deadline": "BlockNumber" + }, "MMRRoot": "Hash", "__[pangolin.runtime]__": { "ProxyType": { diff --git a/frame/bridge/ethereum/backing/src/lib.rs b/frame/bridge/ethereum/backing/src/lib.rs index 68e6590f06..c81f8cf7ac 100644 --- a/frame/bridge/ethereum/backing/src/lib.rs +++ b/frame/bridge/ethereum/backing/src/lib.rs @@ -85,25 +85,21 @@ use types::*; pub trait Trait: frame_system::Trait { /// The ethereum backing module id, used for deriving its sovereign account ID. type ModuleId: Get; - type FeeModuleId: Get; type Event: From> + Into<::Event>; type RedeemAccountId: From<[u8; 32]> + Into; - type EthereumRelay: EthereumReceipt>; - type OnDepositRedeem: OnDepositRedeem>; type RingCurrency: LockableCurrency; - type KtonCurrency: LockableCurrency; + type RingLockLimit: Get>; + type KtonLockLimit: Get>; type AdvancedFee: Get>; - type SyncReward: Get>; - type EcdsaAuthorities: RelayAuthorityProtocol; /// Weight information for the extrinsics in this pallet. @@ -150,8 +146,8 @@ decl_error! { AddressCF, /// Asset - ALREADY REDEEMED AssetAR, - /// Authorities Set - ALREADY SYNCED - AuthoritiesSetAR, + /// Authorities Change - ALREADY SYNCED + AuthoritiesChangeAR, /// EthereumReceipt Proof - INVALID ReceiptProofInv, /// Eth Log - PARSING FAILED @@ -167,6 +163,10 @@ decl_error! { // FeeIns, /// Redeem - DISABLED RedeemDis, + /// Ring Lock - LIMITED + RingLockLim, + /// Kton Lock - LIMITED + KtonLockLim, } } @@ -274,6 +274,8 @@ decl_module! { if !ring_value.is_zero() { let ring_to_lock = ring_value.min(T::RingCurrency::usable_balance(&user)); + ensure!(ring_to_lock < T::RingLockLimit::get(), >::RingLockLim); + T::RingCurrency::transfer(&user, &Self::account_id(), ring_to_lock, KeepAlive)?; let raw_event = RawEvent::LockRing( @@ -293,6 +295,8 @@ decl_module! { if !kton_value.is_zero() { let kton_to_lock = kton_value.min(T::KtonCurrency::usable_balance(&user)); + ensure!(kton_to_lock < T::KtonLockLimit::get(), >::KtonLockLim); + T::KtonCurrency::transfer(&user, &Self::account_id(), kton_to_lock, KeepAlive)?; let raw_event = RawEvent::LockKton( @@ -311,7 +315,7 @@ decl_module! { } if locked { - T::EcdsaAuthorities::new_mmr_to_sign(( + T::EcdsaAuthorities::schedule_mmr_root(( >::block_number().saturated_into() / 10 * 10 + 10 ).saturated_into()); @@ -319,15 +323,15 @@ decl_module! { } #[weight = 10_000_000] - fn sync_authorities_set(origin, proof: EthereumReceiptProofThing) { + fn sync_authorities_change(origin, proof: EthereumReceiptProofThing) { let bridger = ensure_signed(origin)?; let tx_index = T::EthereumRelay::gen_receipt_index(&proof); - ensure!(!VerifiedProof::contains_key(tx_index), >::AuthoritiesSetAR); + ensure!(!VerifiedProof::contains_key(tx_index), >::AuthoritiesChangeAR); let (term, authorities, beneficiary) = Self::parse_authorities_set_proof(&proof)?; - T::EcdsaAuthorities::check_sync_result(term, authorities)?; + T::EcdsaAuthorities::check_authorities_change_to_sync(term, authorities)?; let fee_account = Self::fee_account_id(); let sync_reward = T::SyncReward::get().min( @@ -344,7 +348,7 @@ decl_module! { )?; } - T::EcdsaAuthorities::finish_authorities_change(); + T::EcdsaAuthorities::sync_authorities_change(); VerifiedProof::insert(tx_index, true); } diff --git a/frame/bridge/ethereum/backing/src/mock.rs b/frame/bridge/ethereum/backing/src/mock.rs index b539e538a6..89948673ba 100644 --- a/frame/bridge/ethereum/backing/src/mock.rs +++ b/frame/bridge/ethereum/backing/src/mock.rs @@ -62,17 +62,19 @@ macro_rules! decl_tests { impl RelayAuthorityProtocol for EcdsaAuthorities { type Signer = EthereumAddress; - fn new_mmr_to_sign(_: BlockNumber) {} + fn schedule_mmr_root(_: BlockNumber) {} - fn check_sync_result(_: Term, _: Vec) -> DispatchResult { + fn check_authorities_change_to_sync(_: Term, _: Vec) -> DispatchResult { Ok(()) } - fn finish_authorities_change() {} + fn sync_authorities_change() {} } parameter_types! { pub const EthereumBackingModuleId: ModuleId = ModuleId(*b"da/backi"); pub const EthereumBackingFeeModuleId: ModuleId = ModuleId(*b"da/ethfe"); + pub const RingLockLimit: Balance = 1000; + pub const KtonLockLimit: Balance = 1000; pub const AdvancedFee: Balance = 1; } impl Trait for Test { @@ -84,6 +86,8 @@ macro_rules! decl_tests { type OnDepositRedeem = Staking; type RingCurrency = Ring; type KtonCurrency = Kton; + type RingLockLimit = RingLockLimit; + type KtonLockLimit = KtonLockLimit; type AdvancedFee = AdvancedFee; type SyncReward = (); type EcdsaAuthorities = EcdsaAuthorities; diff --git a/frame/bridge/ethereum/backing/src/test_with_relay.rs b/frame/bridge/ethereum/backing/src/test_with_relay.rs index 5169c98855..e42388d8e1 100644 --- a/frame/bridge/ethereum/backing/src/test_with_relay.rs +++ b/frame/bridge/ethereum/backing/src/test_with_relay.rs @@ -432,6 +432,35 @@ fn lock_should_work() { }); } +#[test] +fn lock_limit_should_work() { + ExtBuilder::default().build().execute_with(|| { + let account = Default::default(); + let lock_balance = 10_000; + let _ = Ring::deposit_creating(&account, lock_balance); + let _ = Kton::deposit_creating(&account, lock_balance); + + assert_err!( + EthereumBacking::lock( + Origin::signed(account.clone()), + lock_balance, + 0, + Default::default() + ), + >::RingLockLim + ); + assert_err!( + EthereumBacking::lock( + Origin::signed(account.clone()), + 0, + lock_balance, + Default::default() + ), + >::KtonLockLim + ); + }); +} + #[test] fn verify_signature_should_work() { assert!(EthereumBacking::verify_signature( @@ -456,17 +485,17 @@ fn verify_new_authorities() { EthereumRelay::confirm_relay_header_parcel_with_reason(relay_header_parcel, vec![]); assert_ok!( - EthereumBacking::sync_authorities_set( + EthereumBacking::sync_authorities_change( Origin::signed(Default::default()), test_receipt_proof_thing.clone(), ) ); assert_err!( - EthereumBacking::sync_authorities_set( + EthereumBacking::sync_authorities_change( Origin::signed(Default::default()), test_receipt_proof_thing, ), - >::AuthoritiesSetAR + >::AuthoritiesChangeAR ); }); } diff --git a/frame/bridge/relay-authorities/Cargo.toml b/frame/bridge/relay-authorities/Cargo.toml index 605f39596b..5381aeb558 100644 --- a/frame/bridge/relay-authorities/Cargo.toml +++ b/frame/bridge/relay-authorities/Cargo.toml @@ -23,6 +23,8 @@ sp-runtime = { default-features = false, git = "https://github.com/darwinia-n sp-std = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", branch = "common-library" } [dev-dependencies] +# crates +array-bytes = { version = "0.3.0" } # darwinia darwinia-balances = { path = "../../balances" } darwinia-support = { path = "../../support" } diff --git a/frame/bridge/relay-authorities/src/lib.rs b/frame/bridge/relay-authorities/src/lib.rs index 15310c8c9e..332496f617 100644 --- a/frame/bridge/relay-authorities/src/lib.rs +++ b/frame/bridge/relay-authorities/src/lib.rs @@ -41,6 +41,12 @@ mod types { <>::Sign as Sign>>::Signature; pub type RelayAuthorityT = RelayAuthority, RelayAuthoritySigner, RingBalance, BlockNumber>; + pub type ScheduledAuthoritiesChangeT = ScheduledAuthoritiesChange< + AccountId, + RelayAuthoritySigner, + RingBalance, + BlockNumber, + >; } // --- crates --- @@ -56,7 +62,7 @@ use frame_system::ensure_signed; use sp_runtime::{DispatchError, DispatchResult, Perbill, SaturatedConversion}; #[cfg(not(feature = "std"))] use sp_std::borrow::ToOwned; -use sp_std::prelude::*; +use sp_std::{mem, prelude::*}; // --- darwinia --- use darwinia_relay_primitives::relay_authorities::*; use darwinia_support::balance::lock::*; @@ -73,6 +79,7 @@ pub trait Trait: frame_system::Trait { type ResetOrigin: EnsureOrigin; type DarwiniaMMR: MMR; type Sign: Sign; + type OpCodes: Get<(OpCode, OpCode)>; type SignThreshold: Get; type SubmitDuration: Get; type WeightInfo: WeightInfo; @@ -91,14 +98,14 @@ decl_event! { RelayAuthorityMessage = RelayAuthorityMessage, RelayAuthoritySignature = RelayAuthoritySignature, { - /// A New MMR Root Request to be Signed. [block number of the mmr root to sign] - NewMMRRoot(BlockNumber), + /// A New MMR Root Scheduled Request to be Signed. [block number of the mmr root to sign] + ScheduleMMRRoot(BlockNumber), /// MMR Root Signed. [block number of the mmr root, mmr root, signatures] MMRRootSigned(BlockNumber, MMRRoot, Vec<(AccountId, RelayAuthoritySignature)>), - /// A New Authorities Request to be Signed. [message to sign] - NewAuthorities(RelayAuthorityMessage), - /// Authorities Signed. [term, new authorities, signatures] - AuthoritiesSetSigned(Term, Vec, Vec<(AccountId, RelayAuthoritySignature)>), + /// A New Authority Set Change Scheduled Request to be Signed. [message to sign] + ScheduleAuthoritiesChange(RelayAuthorityMessage), + /// The Next Authorities Signed. [term, next authorities, signatures] + AuthoritiesChangeSigned(Term, Vec, Vec<(AccountId, RelayAuthoritySignature)>), } } @@ -114,6 +121,8 @@ decl_error! { AuthorityNE, /// Authority - IN TERM AuthorityIT, + /// Authorities Count - TOO LOW + AuthoritiesCountTL, /// Stake - INSUFFICIENT StakeIns, /// On Authorities Change - DISABLED @@ -128,6 +137,8 @@ decl_error! { TermMis, /// Authorities - MISMATCHED AuthoritiesMis, + /// Next Authorities - NOT EXISTED + NextAuthoritiesNE, } } @@ -145,22 +156,16 @@ decl_storage! { /// /// Only council or root can be the voter of the election /// - /// Once you become an authority, you must serve for a specific term. Before that, you can't renounce + /// Once you become an authority, you must serve for a specific term. + /// Before that, you can't renounce pub Authorities get(fn authorities): Vec>; - /// A snapshot for the old authorities while authorities changed - pub OldAuthorities get(fn old_authorities): Vec>; + /// The scheduled change of authority set + pub NextAuthorities get(fn next_authorities): Option>; /// A term index counter, play the same role as nonce in extrinsic pub AuthorityTerm get(fn authority_term): Term; - /// The state of current authorities set - /// - /// Tuple Params - /// 1. is on authority change - /// 1. the authorities change signature submit deadline, this will be delay indefinitely if can't collect enough signatures - pub AuthoritiesState get(fn authorities_state): (bool, BlockNumber) = (false, 0.into()); - /// The authorities change requirements /// /// Once the signatures count reaches the sign threshold storage will be killed then raise a signed event @@ -190,11 +195,6 @@ decl_storage! { : map hasher(identity) BlockNumber => Option, RelayAuthoritySignature)>>; - /// A cache for the old authorities who was renounce or kicked from authorities - /// - /// Remove their lock while the submit authorities change signatures finished - pub OldAuthoritiesLockToRemove get(fn old_authorities_lock_to_remove): Vec>; - /// The mmr root signature submit duration, will be delayed if on authorities change pub SubmitDuration get(fn submit_duration): BlockNumber = T::SubmitDuration::get(); } @@ -265,6 +265,16 @@ decl_module! { ) { let account_id = ensure_signed(origin)?; + if let Some(scheduled_authorities_change) = >::get() { + ensure!( + find_authority_position::( + &scheduled_authorities_change.next_authorities, + &account_id + ).is_none(), + >::AuthorityAE + ); + } + ensure!( find_authority_position::(&>::get(), &account_id).is_none(), >::AuthorityAE @@ -330,85 +340,93 @@ decl_module! { ); } - // TODO: not allow to renounce, if there's only one authority - /// Renounce the authority for you - /// - /// This call is disallowed during the authorities change - /// - /// No-op if can't find the authority + /// Require reset origin /// - /// Will fail if you still in the term + /// Clear the candidates #[weight = 10_000_000] - pub fn renounce_authority(origin) { - let account_id = ensure_signed(origin)?; + pub fn kill_candidates(origin) { + T::ResetOrigin::ensure_origin(origin)?; - ensure!(!Self::on_authorities_change(), >::OnAuthoritiesChangeDis); + let lock_id = T::LockId::get(); - Self::remove_authority_by_id_with( - &account_id, - |authority| if authority.term >= >::block_number() { - Some(>::AuthorityIT) - } else { - None - } - )?; + for RelayAuthority { account_id, .. } in >::take() { + >::remove_lock(lock_id, &account_id); + } } - // TODO: add several authorities once /// Require add origin /// /// Add an authority from the candidates /// /// This call is disallowed during the authorities change #[weight = 10_000_000] - pub fn add_authority(origin, account_id: AccountId) { + pub fn add_authority(origin, account_ids: Vec>) { T::AddOrigin::ensure_origin(origin)?; ensure!(!Self::on_authorities_change(), >::OnAuthoritiesChangeDis); - - let mut authority = Self::remove_candidate_by_id_with(&account_id, || ())?; - - authority.term = >::block_number() + T::TermDuration::get(); - // Won't check duplicated here, MUST make this authority sure is unique // As we already make a check in `request_authority` - >::mutate(|authorities| { - let old_authorities = authorities.clone(); + let next_authorities = { + let mut authorities = >::get(); - authorities.push(authority); + for account_id in account_ids { + let mut authority = Self::remove_candidate_by_id_with(&account_id, || ())?; + authority.term = >::block_number() + T::TermDuration::get(); - Self::start_authorities_change(&old_authorities, &authorities); - }); + authorities.push(authority); + } + + authorities + }; + + Self::schedule_authorities_change(next_authorities); } - // TODO: remove several authorities once - // TODO: not allow to renounce, if there's only one authority - /// Require remove origin + /// Renounce the authority for you /// /// This call is disallowed during the authorities change /// /// No-op if can't find the authority + /// + /// Will fail if you still in the term #[weight = 10_000_000] - pub fn remove_authority(origin, account_id: AccountId) { - T::RemoveOrigin::ensure_origin(origin)?; + pub fn renounce_authority(origin) { + let account_id = ensure_signed(origin)?; ensure!(!Self::on_authorities_change(), >::OnAuthoritiesChangeDis); - let _ = Self::remove_authority_by_id_with(&account_id, |_| None); + let next_authorities = Self::remove_authority_by_ids_with( + vec![account_id], + |authority| if authority.term >= >::block_number() { + Some(>::AuthorityIT) + } else { + None + } + )?; + + if next_authorities.is_empty() { + Err(>::AuthoritiesCountTL)?; + } + + Self::schedule_authorities_change(next_authorities); } - /// Require reset origin + /// Require remove origin /// - /// Clear the candidates. Also, remember to release the stake + /// This call is disallowed during the authorities change #[weight = 10_000_000] - pub fn kill_candidates(origin) { - T::ResetOrigin::ensure_origin(origin)?; + pub fn remove_authority(origin, account_ids: Vec>) { + T::RemoveOrigin::ensure_origin(origin)?; - let lock_id = T::LockId::get(); + ensure!(!Self::on_authorities_change(), >::OnAuthoritiesChangeDis); - for RelayAuthority { account_id, .. } in >::take() { - >::remove_lock(lock_id, &account_id); + let next_authorities = Self::remove_authority_by_ids_with(account_ids, |_| None)?; + + if next_authorities.is_empty() { + Err(>::AuthoritiesCountTL)?; } + + Self::schedule_authorities_change(next_authorities); } /// Require authority origin @@ -428,7 +446,7 @@ decl_module! { ) { let authority = ensure_signed(origin)?; - // Not allow to submit during the authorities set change + // Not allow to submit during the authority set change ensure!(!Self::on_authorities_change(), >::OnAuthoritiesChangeDis); let mut signatures = @@ -449,12 +467,20 @@ decl_module! { // The message is composed of: // - // hash(codec(spec_name: String, block number: Compact, mmr_root: Hash)) + // hash( + // codec( + // spec_name: String, + // op_code: OpCode, + // block number: Compact, + // mmr_root: Hash + // ) + // ) let message = T::Sign::hash( &_S { _1: T::Version::get().spec_name, - _2: block_number, - _3: mmr_root + _2: T::OpCodes::get().0, + _3: block_number, + _4: mmr_root } .encode() ); @@ -471,7 +497,7 @@ decl_module! { { // TODO: clean the mmr root which was contains in this mmr root? - Self::finish_collect_mmr_root_sign(block_number); + Self::mmr_root_signed(block_number); Self::deposit_event(RawEvent::MMRRootSigned(block_number, mmr_root, signatures)); } else { >::insert(block_number, signatures); @@ -489,7 +515,7 @@ decl_module! { /// - the signature is signed by the submitter #[weight = 10_000_000] pub fn submit_signed_authorities(origin, signature: RelayAuthoritySignature) { - let old_authority = ensure_signed(origin)?; + let authority = ensure_signed(origin)?; ensure!(Self::on_authorities_change(), >::OnAuthoritiesChangeDis); @@ -501,16 +527,16 @@ decl_module! { if signatures .iter() - .position(|(old_authority_, _)| old_authority_ == &old_authority) + .position(|(authority_, _)| authority_ == &authority) .is_some() { return Ok(()); } - let old_authorities = >::get(); + let authorities = >::get(); let signer = find_signer::( - &old_authorities, - &old_authority + &authorities, + &authority ).ok_or(>::AuthorityNE)?; ensure!( @@ -518,13 +544,13 @@ decl_module! { >::SignatureInv ); - signatures.push((old_authority, signature)); + signatures.push((authority, signature)); - if Perbill::from_rational_approximation(signatures.len() as u32, old_authorities.len() as _) + if Perbill::from_rational_approximation(signatures.len() as u32, authorities.len() as _) >= T::SignThreshold::get() { - Self::wait_target_chain_authorities_change(); - Self::deposit_event(RawEvent::AuthoritiesSetSigned( + Self::apply_authorities_change()?; + Self::deposit_event(RawEvent::AuthoritiesChangeSigned( >::get(), >::get() .into_iter() @@ -547,8 +573,7 @@ decl_module! { >::remove_lock(lock_id, &account_id); } - >::kill(); - >::kill(); + >::kill(); >::kill(); { >::remove_all(); @@ -556,10 +581,19 @@ decl_module! { >::block_number().saturated_into() / 10 * 10 + 10 ).saturated_into(); >::mutate(|schedules| *schedules = vec![schedule]); - Self::new_mmr_to_sign(schedule); + Self::schedule_mmr_root(schedule); } >::kill(); } + + #[weight = 10_000_000] + pub fn force_new_term(origin) { + T::ResetOrigin::ensure_origin(origin)?; + + Self::apply_authorities_change()?; + + >::kill(); + } } } @@ -568,88 +602,95 @@ where T: Trait, I: Instance, { - pub fn remove_authority_by_id_with( + pub fn remove_candidate_by_id_with( account_id: &AccountId, - is_able_to_remove: F, + f: F, ) -> Result, DispatchError> where - F: Fn(&RelayAuthorityT) -> Option>, + F: Fn(), { - Ok(>::try_mutate(|authorities| { - if let Some(position) = find_authority_position::(&authorities, account_id) { - if let Some(e) = is_able_to_remove(&authorities[position]) { - return Err(e); - } - - let old_authorities = authorities.clone(); - let removed_authority = authorities.remove(position); + Ok(>::try_mutate(|candidates| { + if let Some(position) = find_authority_position::(&candidates, account_id) { + f(); - Self::start_authorities_change(&old_authorities, &authorities); + Ok(candidates.remove(position)) + } else { + Err(>::CandidateNE) + } + })?) + } - >::remove_lock(T::LockId::get(), account_id); + pub fn remove_authority_by_ids_with( + account_ids: Vec>, + f: F, + ) -> Result>, DispatchError> + where + F: Fn(&RelayAuthorityT) -> Option>, + { + let mut authorities = >::get(); + let mut remove_authorities = vec![]; - // TODO: optimize DB R/W, but it's ok in real case, since the set won't grow so large - for key in >::get() { - if let Some(mut signatures) = >::get(key) { - if let Some(position) = signatures - .iter() - .position(|(authority, _)| authority == account_id) - { - signatures.remove(position); - } + for account_id in account_ids.iter() { + let position = find_authority_position::(&authorities, account_id) + .ok_or(>::AuthorityNE)?; - >::insert(key, signatures); - } else { - // Should never enter this condition - // TODO: error log - } - } + if let Some(e) = f(&authorities[position]) { + Err(e)?; + } - >::append(account_id); + authorities.remove(position); + >::remove_lock(T::LockId::get(), account_id); - return Ok(removed_authority); - } + remove_authorities.push(account_id); + } - Err(>::AuthorityNE) - })?) - } + if remove_authorities.is_empty() { + Err(>::AuthorityNE)? + } - pub fn remove_candidate_by_id_with( - account_id: &AccountId, - maybe_remove_lock: F, - ) -> Result, DispatchError> - where - F: Fn(), - { - Ok(>::try_mutate(|candidates| { - if let Some(position) = find_authority_position::(&candidates, account_id) { - maybe_remove_lock(); + // TODO: optimize DB R/W, but it's ok in real case, since the set won't grow so large + for key in >::get() { + if let Some(mut signatures) = >::get(key) { + for account_id in &remove_authorities { + if let Some(position) = signatures + .iter() + .position(|(authority, _)| &authority == account_id) + { + signatures.remove(position); + } - Ok(candidates.remove(position)) + >::insert(key, &signatures); + } } else { - Err(>::CandidateNE) + // Should never enter this condition + // TODO: error log } - })?) + } + + Ok(authorities) } pub fn on_authorities_change() -> bool { - >::get().0 + >::exists() } - pub fn start_authorities_change( - old_authorities: &[RelayAuthorityT], - new_authorities: &[RelayAuthorityT], - ) { - >::put(old_authorities); - + pub fn schedule_authorities_change(next_authorities: Vec>) { // The message is composed of: // - // hash(codec(spec_name: String, term: Compact, new authorities: Vec)) + // hash( + // codec( + // spec_name: String, + // op_code: OpCode, + // term: Compact, + // next authorities: Vec + // ) + // ) let message = T::Sign::hash( &_S { _1: T::Version::get().spec_name, - _2: >::get(), - _3: new_authorities + _2: T::OpCodes::get().1, + _3: >::get(), + _4: next_authorities .iter() .map(|authority| authority.signer.clone()) .collect::>(), @@ -662,29 +703,42 @@ where , RelayAuthoritySignature)>>::new(), )); - Self::deposit_event(RawEvent::NewAuthorities(message)); - let submit_duration = T::SubmitDuration::get(); - >::put(( - true, - >::block_number() + submit_duration, - )); + >::put(ScheduledAuthoritiesChange { + next_authorities, + deadline: >::block_number() + submit_duration, + }); >::mutate(|submit_duration_| *submit_duration_ += submit_duration); + + Self::deposit_event(RawEvent::ScheduleAuthoritiesChange(message)); } - pub fn wait_target_chain_authorities_change() { + pub fn apply_authorities_change() -> DispatchResult { >::kill(); - >::mutate(|authorities_state| authorities_state.1 = 0.into()); + >::try_mutate(|maybe_scheduled_authorities_change| { + let scheduled_authorities_change = maybe_scheduled_authorities_change + .as_mut() + .ok_or(>::NextAuthoritiesNE)?; - for account_id in >::take() { - >::remove_lock(T::LockId::get(), &account_id); - } + >::mutate(|authorities| { + let next_authorities = + mem::take(&mut scheduled_authorities_change.next_authorities); + let previous_authorities = mem::replace(authorities, next_authorities); + + for RelayAuthority { account_id, .. } in previous_authorities { + >::remove_lock(T::LockId::get(), &account_id); + } + }); + DispatchResult::Ok(()) + })?; >::kill(); + + Ok(()) } - pub fn finish_collect_mmr_root_sign(block_number: BlockNumber) { + pub fn mmr_root_signed(block_number: BlockNumber) { >::remove(block_number); >::mutate(|mmr_roots_to_sign_keys| { if let Some(position) = mmr_roots_to_sign_keys @@ -696,7 +750,7 @@ where }); } - pub fn check_misbehavior(at: BlockNumber) { + pub fn check_misbehavior(now: BlockNumber) { let find_and_slash_misbehavior = |signatures: Vec<(AccountId, RelayAuthoritySignature)>| { for RelayAuthority { @@ -713,10 +767,9 @@ where } } }; - let (on_authorities_change, deadline) = >::get(); - if on_authorities_change { - if deadline == at { + if let Some(mut scheduled_authorities_change) = >::get() { + if scheduled_authorities_change.deadline == now { if let Some((_, signatures)) = >::get() { find_and_slash_misbehavior(signatures); } else { @@ -726,18 +779,17 @@ where let submit_duration = T::SubmitDuration::get(); - >::put(( - true, - >::block_number() + submit_duration, - )); + scheduled_authorities_change.deadline += submit_duration; + + >::put(scheduled_authorities_change); >::mutate(|submit_duration_| { *submit_duration_ += submit_duration }); } } else { - if let Some(signatures) = >::take( - >::block_number() - >::get(), - ) { + if let Some(signatures) = + >::take(now - >::get()) + { find_and_slash_misbehavior(signatures); // TODO: delay or discard? @@ -753,7 +805,7 @@ where { type Signer = RelayAuthoritySigner; - fn new_mmr_to_sign(block_number: BlockNumber) { + fn schedule_mmr_root(block_number: BlockNumber) { let _ = >::try_mutate(block_number, |signed_mmr_root| { // No-op if the sign was already scheduled if signed_mmr_root.is_some() { @@ -764,13 +816,16 @@ where *signed_mmr_root = Some(, RelayAuthoritySignature)>>::new()); - Self::deposit_event(RawEvent::NewMMRRoot(block_number)); + Self::deposit_event(RawEvent::ScheduleMMRRoot(block_number)); Ok(()) }); } - fn check_sync_result(term: Term, mut authorities: Vec) -> DispatchResult { + fn check_authorities_change_to_sync( + term: Term, + mut authorities: Vec, + ) -> DispatchResult { ensure!(term == >::get(), >::TermMis); let mut chain_authorities = >::get() @@ -788,8 +843,8 @@ where } } - fn finish_authorities_change() { - >::kill(); + fn sync_authorities_change() { + >::kill(); >::mutate(|authority_term| *authority_term += 1); } } @@ -824,16 +879,3 @@ where None } } - -#[derive(Encode)] -struct _S<_1, _2, _3> -where - _1: Encode, - _2: Encode, - _3: Encode, -{ - _1: _1, - #[codec(compact)] - _2: _2, - _3: _3, -} diff --git a/frame/bridge/relay-authorities/src/mock.rs b/frame/bridge/relay-authorities/src/mock.rs index 1b91bbf4df..fab6acd23d 100644 --- a/frame/bridge/relay-authorities/src/mock.rs +++ b/frame/bridge/relay-authorities/src/mock.rs @@ -104,6 +104,7 @@ impl Trait for Test { type ResetOrigin = EnsureRoot; type DarwiniaMMR = DarwiniaMMR; type Sign = Sign; + type OpCodes = (); type SignThreshold = SignThreshold; type SubmitDuration = SubmitDuration; type WeightInfo = (); diff --git a/frame/bridge/relay-authorities/src/test.rs b/frame/bridge/relay-authorities/src/test.rs index ad27031456..aeb8d21488 100644 --- a/frame/bridge/relay-authorities/src/test.rs +++ b/frame/bridge/relay-authorities/src/test.rs @@ -2,7 +2,7 @@ use frame_support::{assert_err, assert_ok}; // --- darwinia --- use crate::{ - mock::{AccountId, BlockNumber, Event, *}, + mock::{AccountId, BlockNumber, Event, SubmitDuration, *}, *, }; @@ -13,10 +13,13 @@ fn duplicate_request_should_fail() { // Already in candidates assert_err!(request_authority(1), RelayAuthoritiesError::CandidateAE); - assert_ok!(RelayAuthorities::add_authority(Origin::root(), 1)); + assert_ok!(RelayAuthorities::add_authority(Origin::root(), vec![1])); - // Already is authority + // Already in next authorities assert_err!(request_authority(1), RelayAuthoritiesError::AuthorityAE); + + // Already in authorities + assert_err!(request_authority(9), RelayAuthoritiesError::AuthorityAE); }); } @@ -78,7 +81,7 @@ fn cancel_request_should_work() { fn renounce_authority_should_work() { new_test_ext().execute_with(|| { assert_ok!(request_authority(1)); - assert_ok!(RelayAuthorities::add_authority(Origin::root(), 1)); + assert_ok!(RelayAuthorities::add_authority(Origin::root(), vec![1])); assert!(!Ring::locks(1).is_empty()); assert_err!( @@ -86,7 +89,8 @@ fn renounce_authority_should_work() { RelayAuthoritiesError::OnAuthoritiesChangeDis ); - RelayAuthorities::finish_authorities_change(); + RelayAuthorities::apply_authorities_change().unwrap(); + RelayAuthorities::sync_authorities_change(); let term_duration = >::get(); @@ -110,14 +114,58 @@ fn renounce_authority_should_work() { fn add_authority_should_work() { new_test_ext().execute_with(|| { assert_err!( - RelayAuthorities::add_authority(Origin::root(), 1), + RelayAuthorities::add_authority(Origin::root(), vec![1]), RelayAuthoritiesError::CandidateNE ); assert!(Ring::locks(1).is_empty()); + assert!(Ring::locks(2).is_empty()); + assert!(Ring::locks(3).is_empty()); + assert!(RelayAuthorities::next_authorities().is_none()); + assert_ok!(request_authority(1)); - assert_ok!(RelayAuthorities::add_authority(Origin::root(), 1)); + assert_ok!(request_authority(2)); + assert_ok!(request_authority(3)); + + assert_ok!(RelayAuthorities::add_authority( + Origin::root(), + vec![1, 2, 3] + )); + assert!(!Ring::locks(1).is_empty()); + assert!(!Ring::locks(2).is_empty()); + assert!(!Ring::locks(3).is_empty()); + assert_eq!( + RelayAuthorities::next_authorities() + .unwrap() + .next_authorities, + vec![ + RelayAuthority { + account_id: 9, + signer: [0; 20], + stake: 1, + term: 10 + }, + RelayAuthority { + account_id: 1, + signer: [0; 20], + stake: 1, + term: 10 + }, + RelayAuthority { + account_id: 2, + signer: [0; 20], + stake: 1, + term: 10 + }, + RelayAuthority { + account_id: 3, + signer: [0; 20], + stake: 1, + term: 10 + } + ] + ); }); } @@ -125,17 +173,89 @@ fn add_authority_should_work() { fn remove_authority_should_work() { new_test_ext().execute_with(|| { assert_ok!(request_authority(1)); - assert_ok!(RelayAuthorities::add_authority(Origin::root(), 1)); + assert_ok!(RelayAuthorities::add_authority(Origin::root(), vec![1])); assert!(!Ring::locks(1).is_empty()); assert_err!( - RelayAuthorities::remove_authority(Origin::root(), 1), + RelayAuthorities::remove_authority(Origin::root(), vec![1]), RelayAuthoritiesError::OnAuthoritiesChangeDis ); - RelayAuthorities::finish_authorities_change(); + RelayAuthorities::apply_authorities_change().unwrap(); + RelayAuthorities::sync_authorities_change(); - assert_ok!(RelayAuthorities::remove_authority(Origin::root(), 1)); + assert_err!( + RelayAuthorities::remove_authority(Origin::root(), vec![10]), + RelayAuthoritiesError::AuthorityNE + ); + assert_ok!(RelayAuthorities::remove_authority(Origin::root(), vec![1])); assert!(Ring::locks(1).is_empty()); + + RelayAuthorities::apply_authorities_change().unwrap(); + RelayAuthorities::sync_authorities_change(); + + assert_err!( + RelayAuthorities::remove_authority(Origin::root(), vec![9]), + RelayAuthoritiesError::AuthoritiesCountTL + ); + + assert_ok!(request_authority(3)); + assert_ok!(request_authority(4)); + assert_ok!(request_authority(5)); + assert_ok!(RelayAuthorities::add_authority( + Origin::root(), + vec![3, 4, 5] + )); + + RelayAuthorities::apply_authorities_change().unwrap(); + RelayAuthorities::sync_authorities_change(); + + assert_eq!( + RelayAuthorities::authorities(), + vec![ + RelayAuthority { + account_id: 9, + signer: [0; 20], + stake: 1, + term: 10 + }, + RelayAuthority { + account_id: 3, + signer: [0; 20], + stake: 1, + term: 10 + }, + RelayAuthority { + account_id: 4, + signer: [0; 20], + stake: 1, + term: 10 + }, + RelayAuthority { + account_id: 5, + signer: [0; 20], + stake: 1, + term: 10 + } + ] + ); + + assert_ok!(RelayAuthorities::remove_authority( + Origin::root(), + vec![9, 4, 5] + )); + + RelayAuthorities::apply_authorities_change().unwrap(); + RelayAuthorities::sync_authorities_change(); + + assert_eq!( + RelayAuthorities::authorities(), + vec![RelayAuthority { + account_id: 3, + signer: [0; 20], + stake: 1, + term: 10 + }] + ); }); } @@ -167,9 +287,12 @@ fn authority_term_should_work() { for i in 1..=max_candidates { assert_eq!(RelayAuthorities::authority_term(), i as Term - 1); assert_ok!(request_authority(i as _)); - assert_ok!(RelayAuthorities::add_authority(Origin::root(), i as _)); + assert_ok!(RelayAuthorities::add_authority( + Origin::root(), + vec![i as _] + )); - RelayAuthorities::finish_authorities_change(); + RelayAuthorities::sync_authorities_change(); assert_eq!(RelayAuthorities::authority_term(), i as Term); } }); @@ -182,29 +305,47 @@ fn encode_message_should_work() { // The message is composed of: // - // codec(spec_name: RuntimeString, block number: Compact, mmr_root: Hash) + // hash( + // codec( + // spec_name: String, + // op_code: OpCode, + // block number: Compact, + // mmr_root: Hash + // ) + // ) let message = { _S { _1: RuntimeString::from("DRML"), - _2: 789u32, - _3: [0u8; 32], + _2: array_bytes::hex_str_array_unchecked!("0x479fbdf9", 4), + _3: 789u32, + _4: [0u8; 32], } .encode() }; println!("{:?}", message); + println!("{}", array_bytes::hex_str("0x", message)); // The message is composed of: // - // codec(spec_name: RuntimeString, term: Compact, new authorities: Vec) + // hash( + // codec( + // spec_name: String, + // op_code: OpCode, + // term: Compact, + // next authorities: Vec + // ) + // ) let message = { _S { _1: RuntimeString::from("DRML"), - _2: 789u32, - _3: vec![[7u8; 20], [8u8; 20], [9u8; 20]], + _2: array_bytes::hex_str_array_unchecked!("0xb4bcf497", 4), + _3: 789u32, + _4: vec![[7u8; 20], [8u8; 20], [9u8; 20]], } .encode() }; println!("{:?}", message); + println!("{}", array_bytes::hex_str("0x", message)); } #[test] @@ -213,16 +354,16 @@ fn mmr_root_signed_event_should_work() { System::set_block_number(1); assert_ok!(request_authority(1)); - assert_ok!(RelayAuthorities::add_authority(Origin::root(), 1)); + assert_ok!(RelayAuthorities::add_authority(Origin::root(), vec![1])); assert_ok!(RelayAuthorities::submit_signed_authorities( Origin::signed(9), [0; 65] )); - RelayAuthorities::finish_authorities_change(); + RelayAuthorities::sync_authorities_change(); System::reset_events(); - RelayAuthorities::new_mmr_to_sign(10); + RelayAuthorities::schedule_mmr_root(10); System::reset_events(); assert_ok!(RelayAuthorities::submit_signed_mmr_root( @@ -248,12 +389,12 @@ fn mmr_root_signed_event_should_work() { } #[test] -fn authorities_set_signed_event_should_work() { +fn authorities_change_signed_event_should_work() { new_test_ext().execute_with(|| { System::set_block_number(1); assert_ok!(request_authority(1)); - assert_ok!(RelayAuthorities::add_authority(Origin::root(), 1)); + assert_ok!(RelayAuthorities::add_authority(Origin::root(), vec![1])); System::reset_events(); @@ -264,17 +405,17 @@ fn authorities_set_signed_event_should_work() { assert_eq!( relay_authorities_events(), - vec![Event::relay_authorities(RawEvent::AuthoritiesSetSigned( + vec![Event::relay_authorities(RawEvent::AuthoritiesChangeSigned( 0, vec![Default::default(), Default::default()], vec![(9, [0; 65])] ))] ); - RelayAuthorities::finish_authorities_change(); + RelayAuthorities::sync_authorities_change(); assert_ok!(request_authority(2)); - assert_ok!(RelayAuthorities::add_authority(Origin::root(), 2)); + assert_ok!(RelayAuthorities::add_authority(Origin::root(), vec![2])); System::reset_events(); @@ -292,7 +433,7 @@ fn authorities_set_signed_event_should_work() { // Enough signatures, `2 / 2 > 60%` assert_eq!( relay_authorities_events(), - vec![Event::relay_authorities(RawEvent::AuthoritiesSetSigned( + vec![Event::relay_authorities(RawEvent::AuthoritiesChangeSigned( 1, vec![Default::default(), Default::default(), Default::default()], vec![(9, [0; 65]), (1, [0; 65])] @@ -300,3 +441,175 @@ fn authorities_set_signed_event_should_work() { ); }); } + +#[test] +fn schedule_authorities_change_should_work() { + new_test_ext().execute_with(|| { + assert!(RelayAuthorities::next_authorities().is_none()); + + assert_ok!(request_authority(1)); + assert_ok!(RelayAuthorities::add_authority(Origin::root(), vec![1])); + + assert_eq!( + RelayAuthorities::authorities(), + vec![RelayAuthority { + account_id: 9, + signer: [0; 20], + stake: 1, + term: 10 + }] + ); + assert_eq!( + RelayAuthorities::next_authorities(), + Some(ScheduledAuthoritiesChange { + next_authorities: vec![ + RelayAuthority { + account_id: 9, + signer: [0; 20], + stake: 1, + term: 10 + }, + RelayAuthority { + account_id: 1, + signer: [0; 20], + stake: 1, + term: 10 + } + ], + deadline: 3 + }) + ); + + RelayAuthorities::apply_authorities_change().unwrap(); + + assert_eq!( + RelayAuthorities::authorities(), + vec![ + RelayAuthority { + account_id: 9, + signer: [0; 20], + stake: 1, + term: 10 + }, + RelayAuthority { + account_id: 1, + signer: [0; 20], + stake: 1, + term: 10 + } + ] + ); + assert_eq!( + RelayAuthorities::next_authorities(), + Some(ScheduledAuthoritiesChange { + next_authorities: vec![], + deadline: 3 + }) + ); + + RelayAuthorities::sync_authorities_change(); + + assert_eq!( + RelayAuthorities::authorities(), + vec![ + RelayAuthority { + account_id: 9, + signer: [0; 20], + stake: 1, + term: 10 + }, + RelayAuthority { + account_id: 1, + signer: [0; 20], + stake: 1, + term: 10 + } + ] + ); + assert!(RelayAuthorities::next_authorities().is_none()); + }); +} + +#[test] +fn kill_authorities_and_force_new_term_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(request_authority(1)); + assert_ok!(RelayAuthorities::add_authority(Origin::root(), vec![1])); + + RelayAuthorities::apply_authorities_change().unwrap(); + RelayAuthorities::sync_authorities_change(); + + assert_eq!( + RelayAuthorities::authorities(), + vec![ + RelayAuthority { + account_id: 9, + signer: [0; 20], + stake: 1, + term: 10 + }, + RelayAuthority { + account_id: 1, + signer: [0; 20], + stake: 1, + term: 10 + } + ] + ); + assert!(RelayAuthorities::next_authorities().is_none()); + assert_eq!(RelayAuthorities::submit_duration(), SubmitDuration::get()); + + assert_err!( + RelayAuthorities::force_new_term(Origin::root()), + RelayAuthoritiesError::NextAuthoritiesNE + ); + + assert_ok!(request_authority(2)); + assert_ok!(RelayAuthorities::add_authority(Origin::root(), vec![2])); + + assert_ok!(RelayAuthorities::force_new_term(Origin::root())); + + assert_eq!( + RelayAuthorities::authorities(), + vec![ + RelayAuthority { + account_id: 9, + signer: [0; 20], + stake: 1, + term: 10 + }, + RelayAuthority { + account_id: 1, + signer: [0; 20], + stake: 1, + term: 10 + }, + RelayAuthority { + account_id: 2, + signer: [0; 20], + stake: 1, + term: 10 + } + ] + ); + assert!(RelayAuthorities::next_authorities().is_none()); + assert_eq!(RelayAuthorities::submit_duration(), SubmitDuration::get()); + + assert_ok!(RelayAuthorities::kill_authorities(Origin::root())); + assert_ok!(request_authority(3)); + assert_ok!(RelayAuthorities::add_authority(Origin::root(), vec![3])); + assert_ok!(RelayAuthorities::force_new_term(Origin::root())); + + assert_eq!( + RelayAuthorities::authorities(), + vec![RelayAuthority { + account_id: 3, + signer: [0; 20], + stake: 1, + term: 10 + },] + ); + assert!(RelayAuthorities::next_authorities().is_none()); + assert_eq!(RelayAuthorities::submit_duration(), SubmitDuration::get()); + }); +} diff --git a/primitives/relay/src/relay_authorities.rs b/primitives/relay/src/relay_authorities.rs index cc81810ce9..0b1a03773e 100644 --- a/primitives/relay/src/relay_authorities.rs +++ b/primitives/relay/src/relay_authorities.rs @@ -26,12 +26,13 @@ use codec::{Decode, Encode, FullCodec}; use sp_runtime::{DispatchResult, RuntimeDebug}; use sp_std::prelude::*; +pub type OpCode = [u8; 4]; pub type Term = u32; pub trait Sign { type Signature: Clone + Debug + PartialEq + FullCodec; type Message: Clone + Debug + Default + PartialEq + FullCodec; - type Signer: Clone + Debug + Ord + PartialEq + FullCodec; + type Signer: Clone + Debug + Default + Ord + PartialEq + FullCodec; fn hash(raw_message: impl AsRef<[u8]>) -> Self::Message; @@ -45,11 +46,11 @@ pub trait Sign { pub trait RelayAuthorityProtocol { type Signer; - fn new_mmr_to_sign(block_number: BlockNumber); + fn schedule_mmr_root(block_number: BlockNumber); - fn check_sync_result(term: Term, authorities: Vec) -> DispatchResult; + fn check_authorities_change_to_sync(term: Term, authorities: Vec) -> DispatchResult; - fn finish_authorities_change(); + fn sync_authorities_change(); } pub trait MMR { @@ -89,3 +90,28 @@ where &self.account_id == account_id } } + +#[derive(Encode)] +pub struct _S<_1, _2, _3, _4> +where + _1: Encode, + _2: Encode, + _3: Encode, + _4: Encode, +{ + pub _1: _1, + pub _2: _2, + #[codec(compact)] + pub _3: _3, + pub _4: _4, +} + + +/// The scheduled change of authority set +#[derive(Clone, Default, PartialEq, Encode, Decode, RuntimeDebug)] +pub struct ScheduledAuthoritiesChange { + /// The new authorities after the change + pub next_authorities: Vec>, + /// The deadline of the previous authorities to sign for the next authorities + pub deadline: BlockNumber, +}