-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
351 additions
and
1 deletion.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
//! Test environment for EconomicSecurity pallet. | ||
use super::*; | ||
|
||
use core::marker::PhantomData; | ||
use std::collections::HashMap; | ||
|
||
use frame_support::{ | ||
construct_runtime, | ||
traits::{ConstU16, ConstU32, ConstU64}, | ||
}; | ||
|
||
use sp_core::{ | ||
H256, Pair as PairTrait, | ||
sr25519::{Public, Pair}, | ||
}; | ||
use sp_runtime::{ | ||
traits::{BlakeTwo256, IdentityLookup}, | ||
BuildStorage, | ||
}; | ||
|
||
use serai_primitives::*; | ||
use validator_sets::{primitives::MAX_KEY_SHARES_PER_SET, MembershipProof}; | ||
|
||
pub use crate as economic_security; | ||
pub use coins_pallet as coins; | ||
pub use dex_pallet as dex; | ||
pub use pallet_babe as babe; | ||
pub use pallet_grandpa as grandpa; | ||
pub use pallet_timestamp as timestamp; | ||
pub use validator_sets_pallet as validator_sets; | ||
|
||
type Block = frame_system::mocking::MockBlock<Test>; | ||
// Maximum number of authorities per session. | ||
pub type MaxAuthorities = ConstU32<{ MAX_KEY_SHARES_PER_SET }>; | ||
|
||
pub const PRIMARY_PROBABILITY: (u64, u64) = (1, 4); | ||
pub const BABE_GENESIS_EPOCH_CONFIG: sp_consensus_babe::BabeEpochConfiguration = | ||
sp_consensus_babe::BabeEpochConfiguration { | ||
c: PRIMARY_PROBABILITY, | ||
allowed_slots: sp_consensus_babe::AllowedSlots::PrimaryAndSecondaryPlainSlots, | ||
}; | ||
|
||
pub const MEDIAN_PRICE_WINDOW_LENGTH: u16 = 10; | ||
|
||
construct_runtime!( | ||
pub enum Test | ||
{ | ||
System: frame_system, | ||
Timestamp: timestamp, | ||
Coins: coins, | ||
LiquidityTokens: coins::<Instance1>::{Pallet, Call, Storage, Event<T>}, | ||
ValidatorSets: validator_sets, | ||
EconomicSecurity: economic_security, | ||
Dex: dex, | ||
Babe: babe, | ||
Grandpa: grandpa, | ||
} | ||
); | ||
|
||
impl frame_system::Config for Test { | ||
type BaseCallFilter = frame_support::traits::Everything; | ||
type BlockWeights = (); | ||
type BlockLength = (); | ||
type RuntimeOrigin = RuntimeOrigin; | ||
type RuntimeCall = RuntimeCall; | ||
type Nonce = u64; | ||
type Hash = H256; | ||
type Hashing = BlakeTwo256; | ||
type AccountId = Public; | ||
type Lookup = IdentityLookup<Self::AccountId>; | ||
type Block = Block; | ||
type RuntimeEvent = RuntimeEvent; | ||
type BlockHashCount = ConstU64<250>; | ||
type DbWeight = (); | ||
type Version = (); | ||
type PalletInfo = PalletInfo; | ||
type AccountData = (); | ||
type OnNewAccount = (); | ||
type OnKilledAccount = (); | ||
type SystemWeightInfo = (); | ||
type SS58Prefix = (); | ||
type OnSetCode = (); | ||
type MaxConsumers = ConstU32<16>; | ||
} | ||
|
||
impl timestamp::Config for Test { | ||
type Moment = u64; | ||
type OnTimestampSet = Babe; | ||
type MinimumPeriod = ConstU64<{ (TARGET_BLOCK_TIME * 1000) / 2 }>; | ||
type WeightInfo = (); | ||
} | ||
|
||
impl babe::Config for Test { | ||
type EpochDuration = ConstU64<{ FAST_EPOCH_DURATION }>; | ||
|
||
type ExpectedBlockTime = ConstU64<{ TARGET_BLOCK_TIME * 1000 }>; | ||
type EpochChangeTrigger = babe::ExternalTrigger; | ||
type DisabledValidators = ValidatorSets; | ||
|
||
type WeightInfo = (); | ||
type MaxAuthorities = MaxAuthorities; | ||
|
||
type KeyOwnerProof = MembershipProof<Self>; | ||
type EquivocationReportSystem = (); | ||
} | ||
|
||
impl grandpa::Config for Test { | ||
type RuntimeEvent = RuntimeEvent; | ||
|
||
type WeightInfo = (); | ||
type MaxAuthorities = MaxAuthorities; | ||
|
||
type MaxSetIdSessionEntries = ConstU64<0>; | ||
type KeyOwnerProof = MembershipProof<Self>; | ||
type EquivocationReportSystem = (); | ||
} | ||
|
||
impl coins::Config for Test { | ||
type RuntimeEvent = RuntimeEvent; | ||
type AllowMint = ValidatorSets; | ||
} | ||
|
||
impl coins::Config<coins::Instance1> for Test { | ||
type RuntimeEvent = RuntimeEvent; | ||
type AllowMint = (); | ||
} | ||
|
||
impl dex::Config for Test { | ||
type RuntimeEvent = RuntimeEvent; | ||
|
||
type LPFee = ConstU32<3>; // 0.3% | ||
type MintMinLiquidity = ConstU64<10000>; | ||
|
||
type MaxSwapPathLength = ConstU32<3>; // coin1 -> SRI -> coin2 | ||
|
||
type MedianPriceWindowLength = ConstU16<{ MEDIAN_PRICE_WINDOW_LENGTH }>; | ||
|
||
type WeightInfo = dex::weights::SubstrateWeight<Test>; | ||
} | ||
|
||
impl validator_sets::Config for Test { | ||
type RuntimeEvent = RuntimeEvent; | ||
type ShouldEndSession = Babe; | ||
} | ||
|
||
impl Config for Test { | ||
type RuntimeEvent = RuntimeEvent; | ||
} | ||
|
||
// For a const we can't define | ||
pub fn genesis_participants() -> Vec<Pair> { | ||
vec![ | ||
insecure_pair_from_name("Alice"), | ||
insecure_pair_from_name("Bob"), | ||
insecure_pair_from_name("Charlie"), | ||
insecure_pair_from_name("Dave"), | ||
] | ||
} | ||
|
||
// Amounts for single key share per network | ||
pub fn key_shares() -> HashMap<NetworkId, Amount> { | ||
HashMap::from([ | ||
(NetworkId::Serai, Amount(50_000 * 10_u64.pow(8))), | ||
(NetworkId::External(ExternalNetworkId::Bitcoin), Amount(1_000_000 * 10_u64.pow(8))), | ||
(NetworkId::External(ExternalNetworkId::Ethereum), Amount(1_000_000 * 10_u64.pow(8))), | ||
(NetworkId::External(ExternalNetworkId::Monero), Amount(100_000 * 10_u64.pow(8))), | ||
]) | ||
} | ||
|
||
pub(crate) fn new_test_ext() -> sp_io::TestExternalities { | ||
let mut t = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap(); | ||
let networks: Vec<(NetworkId, Amount)> = key_shares().into_iter().collect::<Vec<_>>(); | ||
|
||
coins::GenesisConfig::<Test> { | ||
accounts: genesis_participants() | ||
.clone() | ||
.into_iter() | ||
.map(|a| (a.public(), Balance { coin: Coin::Serai, amount: Amount(1 << 60) })) | ||
.collect(), | ||
_ignore: Default::default(), | ||
} | ||
.assimilate_storage(&mut t) | ||
.unwrap(); | ||
|
||
validator_sets::GenesisConfig::<Test> { | ||
networks, | ||
participants: genesis_participants().into_iter().map(|p| p.public()).collect(), | ||
} | ||
.assimilate_storage(&mut t) | ||
.unwrap(); | ||
|
||
babe::GenesisConfig::<Test> { | ||
authorities: genesis_participants() | ||
.into_iter() | ||
.map(|validator| (validator.public().into(), 1)) | ||
.collect(), | ||
epoch_config: Some(BABE_GENESIS_EPOCH_CONFIG), | ||
_config: PhantomData, | ||
} | ||
.assimilate_storage(&mut t) | ||
.unwrap(); | ||
|
||
grandpa::GenesisConfig::<Test> { | ||
authorities: genesis_participants() | ||
.into_iter() | ||
.map(|validator| (validator.public().into(), 1)) | ||
.collect(), | ||
_config: PhantomData, | ||
} | ||
.assimilate_storage(&mut t) | ||
.unwrap(); | ||
|
||
let mut ext = sp_io::TestExternalities::new(t); | ||
ext.execute_with(|| System::set_block_number(0)); | ||
ext | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
use crate::mock::*; | ||
|
||
use frame_support::traits::Hooks; | ||
use frame_system::RawOrigin; | ||
|
||
use sp_core::{sr25519::Signature, Pair as PairTrait}; | ||
use sp_runtime::BoundedVec; | ||
|
||
use validator_sets::primitives::KeyPair; | ||
use serai_primitives::{ | ||
insecure_pair_from_name, Balance, Coin, ExternalBalance, ExternalCoin, ExternalNetworkId, | ||
EXTERNAL_COINS, EXTERNAL_NETWORKS, | ||
}; | ||
|
||
fn set_keys_for_session(network: ExternalNetworkId) { | ||
ValidatorSets::set_keys( | ||
RawOrigin::None.into(), | ||
network, | ||
BoundedVec::new(), | ||
KeyPair(insecure_pair_from_name("Alice").public(), vec![].try_into().unwrap()), | ||
Signature([0u8; 64]), | ||
) | ||
.unwrap(); | ||
} | ||
|
||
fn make_pool_with_liquidity(coin: &ExternalCoin) { | ||
// make a pool so that we have security oracle value for the coin | ||
let liq_acc = insecure_pair_from_name("liq-acc").public(); | ||
let balance = ExternalBalance { coin: *coin, amount: key_shares()[&coin.network().into()] }; | ||
Coins::mint(liq_acc, balance.into()).unwrap(); | ||
Coins::mint(liq_acc, Balance { coin: Coin::Serai, amount: balance.amount }).unwrap(); | ||
|
||
Dex::add_liquidity( | ||
RawOrigin::Signed(liq_acc).into(), | ||
*coin, | ||
balance.amount.0 / 2, | ||
balance.amount.0 / 2, | ||
1, | ||
1, | ||
liq_acc, | ||
) | ||
.unwrap(); | ||
Dex::on_finalize(1); | ||
assert!(Dex::security_oracle_value(coin).unwrap().0 > 0) | ||
} | ||
|
||
#[test] | ||
fn economic_security() { | ||
new_test_ext().execute_with(|| { | ||
// update the state | ||
EconomicSecurity::on_initialize(1); | ||
|
||
// make sure it is right at the beginning | ||
// this is none at this point since no set has set their keys so TAS isn't up-to-date | ||
for network in EXTERNAL_NETWORKS { | ||
assert_eq!(EconomicSecurity::economic_security_block(network), None); | ||
} | ||
|
||
// set the keys for TAS and have pools for oracle value | ||
for coin in EXTERNAL_COINS { | ||
set_keys_for_session(coin.network()); | ||
make_pool_with_liquidity(&coin); | ||
} | ||
|
||
// update the state | ||
EconomicSecurity::on_initialize(1); | ||
|
||
// check again. The reason we have economic security now is because we stake a key share | ||
// per participant per network(total of 4 key share) in genesis for all networks. | ||
for network in EXTERNAL_NETWORKS { | ||
assert_eq!(EconomicSecurity::economic_security_block(network), Some(1)); | ||
} | ||
|
||
// TODO: Not sure how much sense this test makes since we start from an economically secure | ||
// state. Ideally we should start from not economically secure state and stake the necessary | ||
// amount and then check whether the pallet set the value right since that will be the mainnet | ||
// path. But we cant do that at the moment since vs-pallet genesis build auto stake per network | ||
// to construct the set. This also makes a missing piece of logic explicit. We need genesis | ||
// validators to be in-set but without their stake, or at least its affect on TAS. So this test | ||
// should be updated once that logic is coded. | ||
}); | ||
} |
Oops, something went wrong.