6 Commits

Author SHA1 Message Date
Luke Parker
ea66cd0d1a Update build-dependencies CI action 2025-09-21 15:40:15 -04:00
Luke Parker
8b32fba458 Minor cargo update 2025-09-21 15:40:05 -04:00
Luke Parker
e63acf3f67 Restore a runtime which compiles
Adds BABE, GRANDPA, to the runtime definition and a few stubs for not yet
implemented interfaces.
2025-09-21 13:16:43 -04:00
Luke Parker
d373d2a4c9 Restore AllowMint to serai-validator-sets-pallet and reorganize TODOs 2025-09-20 04:45:28 -04:00
Luke Parker
cbf998ff30 Restore report_slashes
This does not yet handle the `SlashReport`. It solely handles the routing for
it.
2025-09-20 04:16:01 -04:00
Luke Parker
ef07253a27 Restore the event to serai-validator-sets-pallet 2025-09-20 03:42:24 -04:00
27 changed files with 778 additions and 1399 deletions

View File

@@ -7,6 +7,10 @@ runs:
- name: Remove unused packages
shell: bash
run: |
# Ensure the repositories are synced
sudo apt update -y
# Actually perform the removals
sudo apt remove -y "*powershell*" "*nuget*" "*bazel*" "*ansible*" "*terraform*" "*heroku*" "*aws*" azure-cli
sudo apt remove -y "*nodejs*" "*npm*" "*yarn*" "*java*" "*kotlin*" "*golang*" "*swift*" "*julia*" "*fortran*" "*android*"
sudo apt remove -y "*apache2*" "*nginx*" "*firefox*" "*chromium*" "*chrome*" "*edge*"
@@ -14,8 +18,9 @@ runs:
sudo apt remove -y --allow-remove-essential -f shim-signed *python3*
# This removal command requires the prior removals due to unmet dependencies otherwise
sudo apt remove -y "*qemu*" "*sql*" "*texinfo*" "*imagemagick*"
# Reinstall python3 as a general dependency of a functional operating system
sudo apt install python3
sudo apt install -y python3 --fix-missing
if: runner.os == 'Linux'
- name: Remove unused packages

447
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -22,12 +22,12 @@ workspace = true
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"] }
bitvec = { version = "1", default-features = false, features = ["alloc"] }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
serde = { version = "1", default-features = false, features = ["derive"], optional = true }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"], optional = true }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false, features = ["serde"], optional = true }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false, optional = true }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false, features = ["serde"], optional = true }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false, optional = true }
serai-primitives = { path = "../primitives", version = "0.1", default-features = false }

View File

@@ -1,6 +1,9 @@
use borsh::{BorshSerialize, BorshDeserialize};
use serai_primitives::network_id::ExternalNetworkId;
use serai_primitives::{
network_id::ExternalNetworkId,
balance::{Amount, ExternalBalance},
};
/// An event from economic security.
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
@@ -14,7 +17,10 @@ pub enum Event {
/// A trait representing access to the information on economic security.
pub trait EconomicSecurity {
/// If am external network has _ever_ achieved economic security.
/// If an external network has _ever_ achieved economic security.
#[must_use]
fn achieved_economic_security(network: ExternalNetworkId) -> bool;
/// The security oracle's valuation of this balance in SRI.
fn sri_value(balance: ExternalBalance) -> Amount;
}

View File

@@ -47,6 +47,8 @@ pub enum Call {
stand_against {
/// The signal to stand against.
signal: Signal,
/// The network this validator is standing against the signal on behalf of.
with_network: NetworkId,
},
}

View File

@@ -13,8 +13,8 @@ use serai_primitives::{
pub enum Call {
/// Set the keys for a validator set.
set_keys {
/// The validator set which is setting their keys.
validator_set: ExternalValidatorSet,
/// The network whose latest decided validator set is setting their keys..
network: ExternalNetworkId,
/// The keys being set.
key_pair: KeyPair,
/// The participants in the validator set who signed off on these keys.
@@ -29,8 +29,8 @@ pub enum Call {
},
/// Report a validator set's slashes onto Serai.
report_slashes {
/// The validator set which is setting their keys.
validator_set: ExternalValidatorSet,
/// The network whose latest retired validator set is reporting their slashes.
network: ExternalNetworkId,
/// The slashes they're reporting.
slashes: SlashReport,
/// The signature confirming the validity of this slash report.
@@ -77,12 +77,24 @@ impl Call {
}
}
/// The timeline for a deallocation.
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
pub enum DeallocationTimeline {
/// The deallocation is available immediately.
Immediate,
/// The dealocation was delayed.
Delayed {
/// The session the deallocation unlocks at and can be claimed.
unlocks_at: Session,
},
}
/// An event from the validator sets.
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
pub enum Event {
/// A new validator set was declared.
NewSet {
/// The set declared.
/// A new validator set was decided.
SetDecided {
/// The set decided.
set: ValidatorSet,
},
/// A validator set has set their keys.
@@ -97,10 +109,20 @@ pub enum Event {
/// The set which accepted responsibility from the prior set.
set: ValidatorSet,
},
/// A validator set has retired.
SetRetired {
/// The set retired.
set: ValidatorSet,
/// A slash report has been entered for this validator set.
///
/// This may be due to a slash report being published or a default being used due to one not
/// being received within time.
SlashReport {
/// The set whose slash report has been entered.
set: ExternalValidatorSet,
},
/// A validator set their keys on an embedded elliptic curve for a network.
SetEmbeddedEllipticCurveKeys {
/// The validator which set their keys.
validator: SeraiAddress,
/// The network which they set embedded elliptic curve keys for.
network: ExternalNetworkId,
},
/// A validator's allocation to a network has increased.
Allocation {
@@ -119,13 +141,11 @@ pub enum Event {
network: NetworkId,
/// The amount of stake deallocated.
amount: Amount,
/// The session which claiming the deallocation was delayed until.
delayed_until: Option<Session>,
/// The timeline for this deallocation.
timeline: DeallocationTimeline,
},
/// A validator's deallocation from a network has been claimed.
///
/// This is only emited for deallocations which were delayed and has to be explicitly claimed.
DeallocationClaimed {
/// A validator's delayed deallocation from a network has been claimed.
DelayedDeallocationClaimed {
/// The validator who claimed their deallocation.
validator: SeraiAddress,
/// The validator set the deallocation was delayed until.

View File

@@ -31,9 +31,9 @@ serde_json = { version = "1", optional = true }
serai-abi = { path = "../abi", version = "0.1" }
multiaddr = { version = "0.18", optional = true }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", optional = true }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", optional = true }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", optional = true }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", optional = true }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", optional = true }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", optional = true }
async-lock = "3"

View File

@@ -18,10 +18,10 @@ workspace = true
[dependencies]
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
serai-abi = { path = "../abi", default-features = false, features = ["substrate"] }
serai-core-pallet = { path = "../core", default-features = false }
@@ -29,7 +29,7 @@ serai-core-pallet = { path = "../core", default-features = false }
[dev-dependencies]
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false, features = ["std"] }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false, features = ["std"] }
[features]
std = [

View File

@@ -49,10 +49,12 @@ mod pallet {
/// The instance used to represent coins on the Serai network.
///
/// This would either be SRI itself or the sriXYZ coins swappable via pools.
#[derive(Default)]
pub struct CoinsInstance;
/// The instance used to represent liquidity tokens on the Serai network.
///
/// Coin::XYZ would be considered as the liquidity token for the Coin::SRI - Coin::XYZ pool.
#[derive(Default)]
pub struct LiquidityTokensInstance;
/// The configuration of this pallet.
@@ -66,7 +68,7 @@ mod pallet {
/// The genesis state to use for this pallet.
#[pallet::genesis_config]
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
#[derive(Clone, Debug)]
pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
/// The balances to initiate the state with.
///
@@ -76,10 +78,9 @@ mod pallet {
/// PhantomData to bind `I`.
pub _instance: PhantomData<I>,
}
impl<T: Config<I>, I: 'static> Default for GenesisConfig<T, I> {
fn default() -> Self {
GenesisConfig { accounts: Default::default(), _instance: Default::default() }
Self { accounts: Default::default(), _instance: PhantomData }
}
}

View File

@@ -20,10 +20,10 @@ borsh = { version = "1", default-features = false, features = ["derive", "de_str
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
serai-abi = { path = "../abi", default-features = false, features = ["substrate"] }

View File

@@ -21,15 +21,15 @@ workspace = true
[dependencies]
scale = { package = "parity-scale-codec", version = "3.6.1", default-features = false }
sp-std = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-std = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
sp-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
frame-benchmarking = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false, optional = true }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
frame-benchmarking = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false, optional = true }
coins-pallet = { package = "serai-coins-pallet", path = "../coins", default-features = false }

View File

@@ -21,8 +21,8 @@ workspace = true
[dependencies]
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
dex-pallet = { package = "serai-dex-pallet", path = "../dex", default-features = false }
coins-pallet = { package = "serai-coins-pallet", path = "../coins", default-features = false }
@@ -30,16 +30,16 @@ coins-pallet = { package = "serai-coins-pallet", path = "../coins", default-feat
serai-primitives = { path = "../primitives", default-features = false }
[dev-dependencies]
pallet-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
pallet-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
pallet-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
pallet-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
pallet-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
pallet-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
validator-sets-pallet = { package = "serai-validator-sets-pallet", path = "../validator-sets", default-features = false }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-consensus-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
sp-consensus-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
[features]
std = [

View File

@@ -21,11 +21,11 @@ workspace = true
[dependencies]
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
sp-std = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-std = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
coins-pallet = { package = "serai-coins-pallet", path = "../coins", default-features = false }
validator-sets-pallet = { package = "serai-validator-sets-pallet", path = "../validator-sets", default-features = false }

View File

@@ -21,12 +21,12 @@ workspace = true
[dependencies]
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
sp-std = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-application-crypto = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-std = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
sp-application-crypto = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
dex-pallet = { package = "serai-dex-pallet", path = "../dex", default-features = false }
coins-pallet = { package = "serai-coins-pallet", path = "../coins", default-features = false }

View File

@@ -24,14 +24,14 @@ bitvec = { version = "1", default-features = false, features = ["alloc"] }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive", "max-encoded-len"] }
sp-std = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-application-crypto = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-std = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
sp-application-crypto = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
serai-primitives = { path = "../primitives", default-features = false }
@@ -42,9 +42,9 @@ genesis-liquidity-pallet = { package = "serai-genesis-liquidity-pallet", path =
emissions-pallet = { package = "serai-emissions-pallet", path = "../emissions", default-features = false }
[dev-dependencies]
pallet-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
pallet-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
pallet-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
pallet-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
pallet-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
pallet-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
economic-security-pallet = { package = "serai-economic-security-pallet", path = "../economic-security", default-features = false }

View File

@@ -34,16 +34,16 @@ secq256k1 = { path = "../../crypto/secq256k1" }
libp2p = "0.56"
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7" }
sp-keystore = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7" }
sp-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7" }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7" }
sp-blockchain = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7" }
sp-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7" }
sp-block-builder = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7" }
sp-consensus-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7" }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9" }
sp-keystore = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9" }
sp-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9" }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9" }
sp-blockchain = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9" }
sp-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9" }
sp-block-builder = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9" }
sp-consensus-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9" }
frame-benchmarking = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7" }
frame-benchmarking = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9" }
serai-runtime = { path = "../runtime", features = ["std"] }
@@ -55,24 +55,24 @@ jsonrpsee = { version = "0.24", features = ["server"] }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
sc-transaction-pool = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7" }
sc-transaction-pool-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7" }
sc-basic-authorship = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7" }
sc-executor = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7" }
sc-service = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7" }
sc-client-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7" }
sc-network-common = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7" }
sc-network = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sc-transaction-pool = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9" }
sc-transaction-pool-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9" }
sc-basic-authorship = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9" }
sc-executor = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9" }
sc-service = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9" }
sc-client-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9" }
sc-network-common = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9" }
sc-network = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
sc-consensus = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7" }
sc-consensus-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7" }
sc-consensus-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7" }
sc-authority-discovery = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7" }
sc-consensus = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9" }
sc-consensus-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9" }
sc-consensus-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9" }
sc-authority-discovery = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9" }
sc-telemetry = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7" }
sc-cli = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false, features = ["rocksdb"] }
sc-telemetry = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9" }
sc-cli = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false, features = ["rocksdb"] }
sc-rpc-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7" }
sc-rpc-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9" }
serai-env = { path = "../../common/env" }

View File

@@ -21,7 +21,7 @@ borsh = { version = "1", default-features = false, features = ["derive", "de_str
bitvec = { version = "1", default-features = false, features = ["alloc"] }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"], optional = true }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["alloc"] }
schnorr-signatures = { path = "../../crypto/schnorr", default-features = false }

View File

@@ -20,16 +20,22 @@ workspace = true
[dependencies]
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-version = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
sp-session = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
sp-version = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
sp-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
serai-abi = { path = "../abi", default-features = false, features = ["substrate"] }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
frame-executive = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
frame-executive = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
pallet-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
pallet-session = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
pallet-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
pallet-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
serai-core-pallet = { path = "../core", default-features = false }
serai-coins-pallet = { path = "../coins", default-features = false }
@@ -37,13 +43,14 @@ serai-validator-sets-pallet = { path = "../validator-sets", default-features = f
serai-signals-pallet = { path = "../signals", default-features = false }
[build-dependencies]
substrate-wasm-builder = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7" }
substrate-wasm-builder = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9" }
[features]
std = [
"scale/std",
"sp-core/std",
"sp-session/std",
"sp-version/std",
"sp-runtime/std",
"sp-api/std",
@@ -54,6 +61,10 @@ std = [
"frame-support/std",
"frame-executive/std",
"pallet-session/std",
"pallet-babe/std",
"pallet-grandpa/std",
"serai-core-pallet/std",
"serai-coins-pallet/std",
"serai-validator-sets-pallet/std",
@@ -69,6 +80,10 @@ try-runtime = [
"frame-support/try-runtime",
"frame-executive/try-runtime",
"pallet-session/try-runtime",
"pallet-babe/try-runtime",
"pallet-grandpa/try-runtime",
"serai-core-pallet/try-runtime",
"serai-coins-pallet/try-runtime",
"serai-validator-sets-pallet/try-runtime",
@@ -81,6 +96,10 @@ runtime-benchmarks = [
"frame-system/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"pallet-babe/runtime-benchmarks",
"pallet-grandpa/runtime-benchmarks",
"pallet-session/runtime-benchmarks",
"serai-core-pallet/runtime-benchmarks",
"serai-coins-pallet/runtime-benchmarks",
"serai-validator-sets-pallet/runtime-benchmarks",

View File

@@ -8,13 +8,17 @@ extern crate alloc;
use alloc::borrow::Cow;
use sp_core::sr25519::Public;
use sp_core::{ConstU32, ConstU64, sr25519::Public};
use sp_runtime::{Perbill, Weight};
use sp_version::RuntimeVersion;
#[rustfmt::skip]
use serai_abi::{
primitives::address::SeraiAddress, SubstrateHeader as Header, SubstrateBlock,
primitives::{
network_id::{ExternalNetworkId, NetworkId},
balance::{Amount, ExternalBalance},
address::SeraiAddress,
},
SubstrateHeader as Header, SubstrateBlock,
};
use serai_coins_pallet::{CoinsInstance, LiquidityTokensInstance};
@@ -79,13 +83,22 @@ mod runtime {
pub type Coins = serai_coins_pallet::Pallet<Runtime, CoinsInstance>;
#[runtime::pallet_index(3)]
pub type LiquidityTokens = serai_coins_pallet::Pallet<Runtime, LiquidityTokensInstance>;
#[runtime::pallet_index(4)]
pub type ValidatorSets = serai_validator_sets_pallet::Pallet<Runtime>;
#[runtime::pallet_index(5)]
#[runtime::pallet_index(4)]
pub type Signals = serai_signals_pallet::Pallet<Runtime>;
#[runtime::pallet_index(5)]
pub type LiquidityTokens = serai_coins_pallet::Pallet<Runtime, LiquidityTokensInstance>;
#[runtime::pallet_index(0xfd)]
pub type Session = pallet_session::Pallet<Runtime>;
#[runtime::pallet_index(0xfe)]
pub type Babe = pallet_babe::Pallet<Runtime>;
#[runtime::pallet_index(0xff)]
pub type Grandpa = pallet_grandpa::Pallet<Runtime>;
}
impl frame_system::Config for Runtime {
@@ -104,7 +117,7 @@ impl frame_system::Config for Runtime {
type Block = Block;
// Don't track old block hashes within the System pallet
// We use not a number -> hash index, but a hash -> () index, in our own pallet
type BlockHashCount = sp_core::ConstU64<1>;
type BlockHashCount = ConstU64<1>;
type DbWeight = frame_support::weights::constants::RocksDbWeight;
type Version = Version;
type PalletInfo = PalletInfo;
@@ -118,7 +131,7 @@ impl frame_system::Config for Runtime {
// We don't invoke any hooks on-set-code as we don't perform upgrades via the blockchain yet via
// nodes, ensuring everyone who upgrades consents to the rules they upgrade to
type OnSetCode = ();
type MaxConsumers = sp_core::ConstU32<{ u32::MAX }>;
type MaxConsumers = ConstU32<{ u32::MAX }>;
// No migrations set
type SingleBlockMigrations = ();
type MultiBlockMigrator = ();
@@ -133,15 +146,84 @@ impl serai_core_pallet::Config for Runtime {}
impl serai_coins_pallet::Config<CoinsInstance> for Runtime {
type AllowMint = serai_coins_pallet::AlwaysAllowMint; // TODO
}
impl serai_coins_pallet::Config<LiquidityTokensInstance> for Runtime {
type AllowMint = serai_coins_pallet::AlwaysAllowMint;
#[doc(hidden)]
pub struct EconomicSecurity; // TODO
impl serai_abi::economic_security::EconomicSecurity for EconomicSecurity {
fn achieved_economic_security(_network: ExternalNetworkId) -> bool {
false
}
fn sri_value(_balance: ExternalBalance) -> Amount {
Amount(0)
}
}
impl serai_validator_sets_pallet::Config for Runtime {
type ShouldEndSession = Babe;
type EconomicSecurity = EconomicSecurity;
}
impl serai_signals_pallet::Config for Runtime {
type RetirementValidityDuration = sp_core::ConstU64<0>; // TODO
type RetirementLockInDuration = sp_core::ConstU64<0>; // TODO
type RetirementValidityDuration = ConstU64<0>; // TODO
type RetirementLockInDuration = ConstU64<0>; // TODO
}
impl serai_coins_pallet::Config<LiquidityTokensInstance> for Runtime {
type AllowMint = serai_coins_pallet::AlwaysAllowMint;
}
/*
`pallet-babe` requires we implement `pallet-timestamp` for the associated constants. It does not
actually require we offer the timestamp pallet however, and we don't as we follow our methodology
(using the block header for timestamps, not an inherent transaction).
TODO: Set timestamp when executing a block.
*/
impl pallet_timestamp::Config for Runtime {
type Moment = u64;
type OnTimestampSet = Babe;
type MinimumPeriod = ConstU64<0>; // TODO
type WeightInfo = ();
}
#[doc(hidden)]
pub struct GetCurrentSessionForSubstrate;
impl pallet_session::GetCurrentSessionForSubstrate for GetCurrentSessionForSubstrate {
fn get() -> u32 {
serai_validator_sets_pallet::Pallet::<Runtime>::current_session(NetworkId::Serai)
.map(|session| session.0)
.unwrap_or(0)
}
}
impl pallet_session::Config for Runtime {
type Session = GetCurrentSessionForSubstrate;
}
type MaxAuthorities =
ConstU32<{ serai_abi::primitives::validator_sets::KeyShares::MAX_PER_SET_U32 }>;
impl pallet_babe::Config for Runtime {
type EpochDuration = ConstU64<0>; // TODO
type ExpectedBlockTime = ConstU64<0>; // TODO
type EpochChangeTrigger = pallet_babe::ExternalTrigger;
type WeightInfo = ();
type MaxAuthorities = MaxAuthorities;
type MaxNominators = ConstU32<1>;
// TODO: https://github.com/serai-dex/serai/issues/657
type DisabledValidators = ();
type KeyOwnerProof = sp_session::MembershipProof;
type EquivocationReportSystem = ();
}
impl pallet_grandpa::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
type MaxAuthorities = MaxAuthorities;
type MaxNominators = ConstU32<1>;
// TODO: https://github.com/serai-dex/serai/issues/657
type MaxSetIdSessionEntries = ConstU64<0>;
type KeyOwnerProof = sp_session::MembershipProof;
type EquivocationReportSystem = ();
}
impl From<Option<SeraiAddress>> for RuntimeOrigin {
@@ -158,36 +240,54 @@ impl From<serai_abi::Call> for RuntimeCall {
match call {
serai_abi::Call::Coins(call) => {
use serai_abi::coins::Call;
match call {
Call::transfer { to, coins } => {
RuntimeCall::Coins(serai_coins_pallet::Call::transfer { to: to.into(), coins })
}
Call::burn { coins } => RuntimeCall::Coins(serai_coins_pallet::Call::burn { coins }),
use serai_coins_pallet::Call as Scall;
RuntimeCall::Coins(match call {
Call::transfer { to, coins } => Scall::transfer { to: to.into(), coins },
Call::burn { coins } => Scall::burn { coins },
Call::burn_with_instruction { instruction } => {
RuntimeCall::Coins(serai_coins_pallet::Call::burn_with_instruction { instruction })
Scall::burn_with_instruction { instruction }
}
}
})
}
serai_abi::Call::ValidatorSets(call) => {
use serai_abi::validator_sets::Call;
match call {
Call::set_keys { .. } |
Call::report_slashes { .. } |
Call::set_embedded_elliptic_curve_keys { .. } |
Call::allocate { .. } |
Call::deallocate { .. } |
Call::claim_deallocation { .. } => todo!("TODO"),
}
use serai_validator_sets_pallet::Call as Scall;
RuntimeCall::ValidatorSets(match call {
Call::set_keys { network, key_pair, signature_participants, signature } => {
Scall::set_keys { network, key_pair, signature_participants, signature }
}
Call::report_slashes { network, slashes, signature } => {
Scall::report_slashes { network, slashes, signature }
}
Call::set_embedded_elliptic_curve_keys { keys } => {
Scall::set_embedded_elliptic_curve_keys { keys }
}
Call::allocate { network, amount } => Scall::allocate { network, amount },
Call::deallocate { network, amount } => Scall::deallocate { network, amount },
Call::claim_deallocation { deallocation } => Scall::claim_deallocation {
network: deallocation.network,
session: deallocation.session,
},
})
}
serai_abi::Call::Signals(call) => {
use serai_abi::signals::Call;
match call {
Call::register_retirement_signal { .. } |
Call::revoke_retirement_signal { .. } |
Call::favor { .. } |
Call::revoke_favor { .. } |
Call::stand_against { .. } => todo!("TODO"),
}
use serai_signals_pallet::Call as Scall;
RuntimeCall::Signals(match call {
Call::register_retirement_signal { in_favor_of } => {
Scall::register_retirement_signal { in_favor_of }
}
Call::revoke_retirement_signal { was_in_favor_of } => {
Scall::revoke_retirement_signal { retirement_signal: was_in_favor_of }
}
Call::favor { signal, with_network } => Scall::favor { signal, with_network },
Call::revoke_favor { signal, with_network } => {
Scall::revoke_favor { signal, with_network }
}
Call::stand_against { signal, with_network } => {
Scall::stand_against { signal, with_network }
}
})
}
serai_abi::Call::Dex(call) => {
use serai_abi::dex::Call;
@@ -331,9 +431,6 @@ pub use in_instructions_pallet as in_instructions;
pub use signals_pallet as signals;
pub use pallet_babe as babe;
pub use pallet_grandpa as grandpa;
pub use genesis_liquidity_pallet as genesis_liquidity;
pub use emissions_pallet as emissions;
@@ -492,43 +589,9 @@ impl pallet_authorship::Config for Runtime {
type EventHandler = ();
}
// Maximum number of authorities per session.
pub type MaxAuthorities = ConstU32<{ validator_sets::primitives::MAX_KEY_SHARES_PER_SET_U32 }>;
/// Longevity of an offence report.
pub type ReportLongevity = <Runtime as pallet_babe::Config>::EpochDuration;
impl babe::Config for Runtime {
#[cfg(feature = "fast-epoch")]
type EpochDuration = ConstU64<{ FAST_EPOCH_DURATION }>;
#[cfg(not(feature = "fast-epoch"))]
type EpochDuration = ConstU64<{ 4 * 7 * DAYS }>;
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 =
babe::EquivocationReportSystem<Self, ValidatorSets, ValidatorSets, ReportLongevity>;
}
impl grandpa::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
type MaxAuthorities = MaxAuthorities;
type MaxSetIdSessionEntries = ConstU64<0>;
type KeyOwnerProof = MembershipProof<Self>;
type EquivocationReportSystem =
grandpa::EquivocationReportSystem<Self, ValidatorSets, ValidatorSets, ReportLongevity>;
}
construct_runtime!(
pub enum Runtime {
System: system exclude_parts { Call },

View File

@@ -21,10 +21,10 @@ workspace = true
[dependencies]
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
serai-abi = { path = "../abi", default-features = false, features = ["substrate"] }

View File

@@ -36,13 +36,13 @@ pub mod pallet {
}
#[pallet::genesis_config]
#[derive(Debug, Encode, Decode)]
#[derive(Clone, Debug)]
pub struct GenesisConfig<T: Config> {
_config: PhantomData<T>,
}
impl<T: Config> Default for GenesisConfig<T> {
fn default() -> Self {
GenesisConfig { _config: PhantomData }
Self { _config: PhantomData }
}
}
#[pallet::genesis_build]
@@ -221,20 +221,16 @@ pub mod pallet {
fn revoke_favor_internal(
validator: T::AccountId,
signal: Signal,
for_network: NetworkId,
with_network: NetworkId,
) -> DispatchResult {
if !Favors::<T>::contains_key((signal, for_network), validator) {
if !Favors::<T>::contains_key((signal, with_network), validator) {
Err::<(), _>(Error::<T>::RevokingNonExistentFavor)?;
}
Favors::<T>::remove((signal, for_network), validator);
Core::<T>::emit_event(Event::FavorRevoked {
signal,
by: validator.into(),
with_network: for_network,
});
Favors::<T>::remove((signal, with_network), validator);
Core::<T>::emit_event(Event::FavorRevoked { signal, by: validator.into(), with_network });
// Update the tally for this network
Self::tally_for_network(signal, for_network);
Self::tally_for_network(signal, with_network);
Ok(())
}
@@ -342,7 +338,7 @@ pub mod pallet {
/// Favor a signal.
#[pallet::call_index(2)]
#[pallet::weight((0, DispatchClass::Normal))] // TODO
pub fn favor(origin: OriginFor<T>, signal: Signal, for_network: NetworkId) -> DispatchResult {
pub fn favor(origin: OriginFor<T>, signal: Signal, with_network: NetworkId) -> DispatchResult {
let validator = ensure_signed(origin)?;
// Perform the relevant checks for this class of signal
@@ -373,21 +369,17 @@ pub mod pallet {
Signal::Halt { .. } => {}
}
if Favors::<T>::contains_key((signal, for_network), validator) {
if Favors::<T>::contains_key((signal, with_network), validator) {
Err::<(), _>(Error::<T>::AlreadyInFavor)?;
}
// Set the validator as in favor
Favors::<T>::set((signal, for_network), validator, Some(()));
Favors::<T>::set((signal, with_network), validator, Some(()));
Core::<T>::emit_event(Event::SignalFavored {
signal,
by: validator.into(),
with_network: for_network,
});
Core::<T>::emit_event(Event::SignalFavored { signal, by: validator.into(), with_network });
// Check if the network is in favor
let network_in_favor = Self::tally_for_network(signal, for_network);
let network_in_favor = Self::tally_for_network(signal, with_network);
// If this network is in favor, check if enough networks are
if network_in_favor && Self::tally_for_all_networks(signal) {
@@ -416,7 +408,7 @@ pub mod pallet {
pub fn revoke_favor(
origin: OriginFor<T>,
signal: Signal,
for_network: NetworkId,
with_network: NetworkId,
) -> DispatchResult {
match signal {
Signal::Retire { .. } => {
@@ -428,7 +420,7 @@ pub mod pallet {
}
let validator = ensure_signed(origin)?;
Self::revoke_favor_internal(validator, signal, for_network)
Self::revoke_favor_internal(validator, signal, with_network)
}
/// Emit an event standing against the signal.
@@ -442,7 +434,7 @@ pub mod pallet {
pub fn stand_against(
origin: OriginFor<T>,
signal: Signal,
for_network: NetworkId,
with_network: NetworkId,
) -> DispatchResult {
match signal {
Signal::Retire { .. } => {
@@ -455,8 +447,8 @@ pub mod pallet {
let validator = ensure_signed(origin)?;
// If currently in favor, revoke the favor
if Favors::<T>::contains_key((signal, for_network), validator) {
Self::revoke_favor_internal(validator, signal, for_network)?;
if Favors::<T>::contains_key((signal, with_network), validator) {
Self::revoke_favor_internal(validator, signal, with_network)?;
} else {
// Check this Signal exists (which would've been implied by `Favors` for it existing)
match signal {
@@ -472,7 +464,7 @@ pub mod pallet {
Core::<T>::emit_event(Event::AgainstSignal {
signal,
account: validator.into(),
with_network: for_network,
with_network,
});
Ok(())

View File

@@ -20,20 +20,21 @@ bitvec = { version = "1", default-features = false, features = ["alloc", "serde"
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive", "bit-vec"] }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-application-crypto = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
sp-application-crypto = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
sp-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
pallet-session = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
pallet-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
pallet-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
pallet-session = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
pallet-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
pallet-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "ef18bfc7029d4a3d7c27e1d0b84da5091628a7d9", default-features = false }
serai-abi = { path = "../abi", default-features = false, features = ["substrate"] }
serai-core-pallet = { path = "../core", default-features = false }
serai-coins-pallet = { path = "../coins", default-features = false }
[dev-dependencies]
@@ -63,6 +64,7 @@ std = [
"serai-abi/std",
"serai-core-pallet/std",
"serai-coins-pallet/std",
]
@@ -75,6 +77,9 @@ try-runtime = [
"pallet-session/try-runtime",
"pallet-babe/try-runtime",
"pallet-grandpa/try-runtime",
"serai-core-pallet/try-runtime",
"serai-coins-pallet/try-runtime",
]
runtime-benchmarks = [
@@ -83,6 +88,9 @@ runtime-benchmarks = [
"pallet-babe/runtime-benchmarks",
"pallet-grandpa/runtime-benchmarks",
"serai-core-pallet/runtime-benchmarks",
"serai-coins-pallet/runtime-benchmarks",
]
default = ["std"]

View File

@@ -30,6 +30,9 @@ pub(crate) trait Keys {
/// Clear a historic set of keys.
fn clear_keys(set: ExternalValidatorSet);
/// The oraclization key for a validator set.
fn oraclization_key(set: ExternalValidatorSet) -> Option<Public>;
}
impl<S: KeysStorage> Keys for S {
@@ -46,4 +49,8 @@ impl<S: KeysStorage> Keys for S {
S::OraclizationKeys::remove(set);
S::ExternalKeys::remove(set);
}
fn oraclization_key(set: ExternalValidatorSet) -> Option<Public> {
S::OraclizationKeys::get(set)
}
}

View File

@@ -3,7 +3,7 @@
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
use alloc::vec::Vec;
use alloc::{vec, vec::Vec};
mod embedded_elliptic_curve_keys;
use embedded_elliptic_curve_keys::*;
@@ -21,6 +21,7 @@ use keys::{KeysStorage, Keys as _};
#[frame_support::pallet]
mod pallet {
use sp_core::sr25519::Public;
use sp_application_crypto::RuntimePublic;
use frame_system::pallet_prelude::*;
use frame_support::{pallet_prelude::*, traits::OneSessionHandler};
@@ -35,13 +36,18 @@ mod pallet {
network_id::*,
coin::*,
balance::*,
validator_sets::{Session, ExternalValidatorSet, ValidatorSet, KeyShares as KeySharesStruct},
validator_sets::{
Session, ExternalValidatorSet, ValidatorSet, KeyShares as KeySharesStruct, SlashReport,
},
address::SeraiAddress,
},
economic_security::EconomicSecurity,
validator_sets::{DeallocationTimeline, Event},
};
use serai_coins_pallet::Pallet as Coins;
use serai_core_pallet::Pallet as Core;
use serai_coins_pallet::AllowMint;
type Coins<T> = serai_coins_pallet::Pallet<T, serai_coins_pallet::CoinsInstance>;
use super::*;
@@ -51,6 +57,7 @@ mod pallet {
+ pallet_session::Config
+ pallet_babe::Config
+ pallet_grandpa::Config
+ serai_core_pallet::Config
+ serai_coins_pallet::Config<serai_coins_pallet::CoinsInstance>
{
type ShouldEndSession: ShouldEndSession<BlockNumberFor<Self>>;
@@ -58,11 +65,16 @@ mod pallet {
}
#[pallet::genesis_config]
#[derive(Clone, PartialEq, Eq, Debug)]
#[derive(Clone, Debug)]
pub struct GenesisConfig<T: Config> {
/// List of participants to place in the initial validator sets.
pub participants: Vec<(T::AccountId, Vec<SignedEmbeddedEllipticCurveKeys>)>,
}
impl<T: Config> Default for GenesisConfig<T> {
fn default() -> Self {
Self { participants: Default::default() }
}
}
#[pallet::pallet]
pub struct Pallet<T>(PhantomData<T>);
@@ -123,8 +135,12 @@ mod pallet {
#[pallet::storage]
type DelayedDeallocations<T: Config> =
StorageDoubleMap<_, Blake2_128Concat, Public, Identity, Session, Amount, OptionQuery>;
#[pallet::storage]
type PendingSlashReport<T: Config> = StorageMap<_, Identity, ExternalNetworkId, (), OptionQuery>;
impl<T: Config> SessionsStorage for Abstractions<T> {
type Config = T;
type GenesisValidators = GenesisValidators<T>;
type AllocationPerKeyShare = AllocationPerKeyShare<T>;
type CurrentSession = CurrentSession<T>;
@@ -133,6 +149,7 @@ mod pallet {
type SelectedValidators = SelectedValidators<T>;
type TotalAllocatedStake = TotalAllocatedStake<T>;
type DelayedDeallocations = DelayedDeallocations<T>;
type PendingSlashReport = PendingSlashReport<T>;
}
// Satisfy the `Keys` abstractions
@@ -148,51 +165,6 @@ mod pallet {
type ExternalKeys = ExternalKeys<T>;
}
/* TODO
/// The key for validator sets which can (and still need to) publish their slash reports.
#[pallet::storage]
pub type PendingSlashReport<T: Config> =
StorageMap<_, Identity, ExternalNetworkId, Public, OptionQuery>;
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
NewSet {
set: ValidatorSet,
},
ParticipantRemoved {
set: ValidatorSet,
removed: T::AccountId,
},
KeyGen {
set: ExternalValidatorSet,
key_pair: KeyPair,
},
AcceptedHandover {
set: ValidatorSet,
},
SetRetired {
set: ValidatorSet,
},
AllocationIncreased {
validator: T::AccountId,
network: NetworkId,
amount: Amount,
},
AllocationDecreased {
validator: T::AccountId,
network: NetworkId,
amount: Amount,
delayed_until: Option<Session>,
},
DeallocationClaimed {
validator: T::AccountId,
network: NetworkId,
session: Session,
},
}
*/
#[pallet::error]
pub enum Error<T> {
/// The provided embedded elliptic curve keys were invalid.
@@ -308,116 +280,28 @@ mod pallet {
}
}
/* TODO
/// Decreases a validator's allocation to a set.
///
/// Errors if the capacity provided by this allocation is in use.
///
/// Errors if a partial decrease of allocation which puts the remaining allocation below the
/// minimum requirement.
///
/// The capacity prior provided by the allocation is immediately removed, in order to ensure it
/// doesn't become used (preventing deallocation).
///
/// Returns if the amount is immediately eligible for deallocation.
fn decrease_allocation(
network: NetworkId,
account: T::AccountId,
amount: Amount,
) -> Result<bool, DispatchError> {
// Check it's safe to decrease this set's stake by this amount
if let NetworkId::External(n) = network {
let new_total_staked = Self::total_allocated_stake(NetworkId::from(n))
.unwrap()
.0
.checked_sub(amount.0)
.ok_or(Error::<T>::NotEnoughAllocated)?;
let required_stake = Self::required_stake_for_network(n);
if new_total_staked < required_stake {
Err(Error::<T>::DeallocationWouldRemoveEconomicSecurity)?;
}
}
let decreased_key_shares =
(old_allocation / allocation_per_key_share) > (new_allocation / allocation_per_key_share);
// If this decreases the validator's key shares, error if the new set is unable to handle
// byzantine faults
let mut was_bft = None;
if decreased_key_shares {
was_bft = Some(Self::is_bft(network));
}
if let Some(was_bft) = was_bft {
if was_bft && (!Self::is_bft(network)) {
Err(Error::<T>::DeallocationWouldRemoveFaultTolerance)?;
}
}
Sessions::<T>::decrease_allocation(network, account, amount)
/// The required amount of stake for a balance.
fn stake_requirement(balance: ExternalBalance) -> AmountRepr {
let value = T::EconomicSecurity::sri_value(balance).0;
// As 67% can misbehave, 67% of stake must be sufficient to secure this
let requirement = value.saturating_mul(3) / 2;
// We add an additional margin of 20%
let margin = requirement / 5;
requirement.saturating_add(margin)
}
// TODO: This is called retire_set, yet just starts retiring the set
// Update the nomenclature within this function
pub fn retire_set(set: ValidatorSet) {
// Serai doesn't set keys and network slashes are handled by BABE/GRANDPA
if let NetworkId::External(n) = set.network {
// If the prior prior set didn't report, emit they're retired now
if PendingSlashReport::<T>::get(n).is_some() {
Self::deposit_event(Event::SetRetired {
set: ValidatorSet { network: set.network, session: Session(set.session.0 - 1) },
});
}
// This overwrites the prior value as the prior to-report set's stake presumably just
// unlocked, making their report unenforceable
let keys =
Keys::<T>::take(ExternalValidatorSet { network: n, session: set.session }).unwrap();
PendingSlashReport::<T>::set(n, Some(keys.0));
} else {
// emit the event for serai network
Self::deposit_event(Event::SetRetired { set });
}
// We're retiring this set because the set after it accepted the handover
Self::deposit_event(Event::AcceptedHandover {
set: ValidatorSet { network: set.network, session: Session(set.session.0 + 1) },
});
}
/// Returns the required stake in terms SRI for a given `Balance`.
pub fn required_stake(balance: &ExternalBalance) -> SubstrateAmount {
use dex_pallet::HigherPrecisionBalance;
// This is inclusive to an increase in accuracy
let sri_per_coin = Dex::<T>::security_oracle_value(balance.coin).unwrap_or(Amount(0));
// See dex-pallet for the reasoning on these
let coin_decimals = balance.coin.decimals().max(5);
let accuracy_increase = HigherPrecisionBalance::from(SubstrateAmount::pow(10, coin_decimals));
let total_coin_value = u64::try_from(
HigherPrecisionBalance::from(balance.amount.0) *
HigherPrecisionBalance::from(sri_per_coin.0) /
accuracy_increase,
)
.unwrap_or(u64::MAX);
// required stake formula (COIN_VALUE * 1.5) + margin(20%)
let required_stake = total_coin_value.saturating_mul(3).saturating_div(2);
required_stake.saturating_add(total_coin_value.saturating_div(5))
}
/// Returns the current total required stake for a given `network`.
pub fn required_stake_for_network(network: ExternalNetworkId) -> SubstrateAmount {
let mut total_required = 0;
/// The required amount of stake for a network.
fn network_stake_requirement(network: ExternalNetworkId) -> AmountRepr {
let mut requirement = AmountRepr::zero();
for coin in network.coins() {
let supply = Coins::<T>::supply(Coin::from(coin));
total_required += Self::required_stake(&ExternalBalance { coin, amount: Amount(supply) });
requirement = requirement
.saturating_add(Self::stake_requirement(ExternalBalance { coin, amount: supply }));
}
total_required
requirement
}
/* TODO
pub fn distribute_block_rewards(
network: NetworkId,
account: T::AccountId,
@@ -541,7 +425,9 @@ mod pallet {
let session = Self::current_session(NetworkId::from(network))
.expect("validated `set_keys` for a non-existent session");
let set = ExternalValidatorSet { network, session };
Abstractions::<T>::set_keys(set, key_pair);
Abstractions::<T>::set_keys(set, key_pair.clone());
Core::<T>::emit_event(Event::SetKeys { set, key_pair });
// If this is the first session of an external network, mark them current, not solely decided
if session == Session(0) {
@@ -551,7 +437,6 @@ mod pallet {
Ok(())
}
/* TODO
#[pallet::call_index(1)]
#[pallet::weight((0, DispatchClass::Operational))] // TODO
pub fn report_slashes(
@@ -562,24 +447,13 @@ mod pallet {
) -> DispatchResult {
ensure_none(origin)?;
// signature isn't checked as this is an unsigned transaction, and validate_unsigned
// (called by pre_dispatch) checks it
// `signature` is checked within `ValidateUnsigned`
let _ = signature;
// TODO: Handle slashes
let _ = slashes;
// Emit set retireed
Pallet::<T>::deposit_event(Event::SetRetired {
set: ValidatorSet {
network: network.into(),
session: Session(Self::session(NetworkId::from(network)).unwrap().0 - 1),
},
});
Abstractions::<T>::handle_slash_report(network, slashes);
Ok(())
}
*/
#[pallet::call_index(2)]
#[pallet::weight((0, DispatchClass::Normal))] // TODO
@@ -587,11 +461,16 @@ mod pallet {
origin: OriginFor<T>,
keys: SignedEmbeddedEllipticCurveKeys,
) -> DispatchResult {
let signer = ensure_signed(origin)?;
let validator = ensure_signed(origin)?;
let network = keys.network();
<Abstractions<T> as crate::EmbeddedEllipticCurveKeys>::set_embedded_elliptic_curve_keys(
signer, keys,
validator, keys,
)
.map_err(|()| Error::<T>::InvalidEmbeddedEllipticCurveKeys)?;
Core::<T>::emit_event(Event::SetEmbeddedEllipticCurveKeys {
validator: validator.into(),
network,
});
Ok(())
}
@@ -599,11 +478,7 @@ mod pallet {
#[pallet::weight((0, DispatchClass::Normal))] // TODO
pub fn allocate(origin: OriginFor<T>, network: NetworkId, amount: Amount) -> DispatchResult {
let validator = ensure_signed(origin)?;
Coins::<T, serai_coins_pallet::CoinsInstance>::transfer_fn(
validator,
Self::account(),
Balance { coin: Coin::Serai, amount },
)?;
Coins::<T>::transfer_fn(validator, Self::account(), Balance { coin: Coin::Serai, amount })?;
Abstractions::<T>::increase_allocation(network, validator, amount, false)
.map_err(Error::<T>::AllocationError)?;
Ok(())
@@ -612,16 +487,20 @@ mod pallet {
#[pallet::call_index(4)]
#[pallet::weight((0, DispatchClass::Normal))] // TODO
pub fn deallocate(origin: OriginFor<T>, network: NetworkId, amount: Amount) -> DispatchResult {
let account = ensure_signed(origin)?;
let validator = ensure_signed(origin)?;
let deallocation_timeline = Abstractions::<T>::decrease_allocation(network, account, amount)
let timeline = Abstractions::<T>::decrease_allocation(network, validator, amount)
.map_err(Error::<T>::DeallocationError)?;
if matches!(deallocation_timeline, DeallocationTimeline::Immediate) {
Coins::<T, serai_coins_pallet::CoinsInstance>::transfer_fn(
Self::account(),
account,
Balance { coin: Coin::Serai, amount },
)?;
Core::<T>::emit_event(Event::Deallocation {
validator: validator.into(),
network,
amount,
timeline,
});
if matches!(timeline, DeallocationTimeline::Immediate) {
Coins::<T>::transfer_fn(Self::account(), validator, Balance { coin: Coin::Serai, amount })?;
}
Ok(())
@@ -634,14 +513,16 @@ mod pallet {
network: NetworkId,
session: Session,
) -> DispatchResult {
let account = ensure_signed(origin)?;
let amount = Abstractions::<T>::claim_delayed_deallocation(account, network, session)
let validator = ensure_signed(origin)?;
let amount = Abstractions::<T>::claim_delayed_deallocation(validator, network, session)
.map_err(Error::<T>::DeallocationError)?;
Coins::<T, serai_coins_pallet::CoinsInstance>::transfer_fn(
Self::account(),
account,
Balance { coin: Coin::Serai, amount },
)?;
Core::<T>::emit_event(Event::DelayedDeallocationClaimed {
validator: validator.into(),
deallocation: ValidatorSet { network, session },
});
Coins::<T>::transfer_fn(Self::account(), validator, Balance { coin: Coin::Serai, amount })?;
Ok(())
}
}
@@ -705,7 +586,6 @@ mod pallet {
}
// Verify the signature with the MuSig key of the signers
use sp_application_crypto::RuntimePublic;
if !set.musig_key(&signers).verify(&set.set_keys_message(key_pair), &signature.0.into()) {
Err(InvalidTransaction::BadProof)?;
}
@@ -716,30 +596,23 @@ mod pallet {
.propagate(true)
.build()
}
/* TODO
Call::report_slashes { network, ref slashes, ref signature } => {
let network = *network;
let Some(key) = PendingSlashReport::<T>::take(network) else {
// Assumed already published
let Some(key) = Abstractions::<T>::waiting_for_slash_report(network) else {
Err(InvalidTransaction::Stale)?
};
// There must have been a previous session is PendingSlashReport is populated
let set = ExternalValidatorSet {
network,
session: Session(Self::session(NetworkId::from(network)).unwrap().0 - 1),
};
if !key.verify(&slashes.report_slashes_message(), signature) {
if !key.verify(&slashes.report_slashes_message(), &signature.0.into()) {
Err(InvalidTransaction::BadProof)?;
}
ValidTransaction::with_tag_prefix("ValidatorSets")
.and_provides((1, set))
.longevity(MAX_KEY_SHARES_PER_SET_U32.into())
.and_provides((1, key))
.longevity(KeySharesStruct::MAX_PER_SET_U32.into())
.propagate(true)
.build()
}
*/
Call::set_embedded_elliptic_curve_keys { .. } |
Call::allocate { .. } |
Call::deallocate { .. } |
@@ -754,20 +627,20 @@ mod pallet {
}
}
/* TODO
/*
TODO: Add an intent. While we shouldn't allow `Transfer`, `AddLiquidity` when we're within a
certain range of the limit, we should still allow swaps.
*/
impl<T: Config> AllowMint for Pallet<T> {
fn is_allowed(balance: &ExternalBalance) -> bool {
// get the required stake
let current_required = Self::required_stake_for_network(balance.coin.network());
let new_required = current_required + Self::required_stake(balance);
// get the total stake for the network & compare.
let current_requirement = Self::network_stake_requirement(balance.coin.network());
let new_requirement = current_requirement.saturating_add(Self::stake_requirement(*balance));
let staked =
Self::total_allocated_stake(NetworkId::from(balance.coin.network())).unwrap_or(Amount(0));
staked.0 >= new_required
Abstractions::<T>::stake_for_current_validator_set(balance.coin.network().into())
.unwrap_or(Amount(0));
staked.0 >= new_requirement
}
}
*/
}
pub use pallet::*;

View File

@@ -1,217 +0,0 @@
//! Test environment for ValidatorSets pallet.
use super::*;
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_abi::primitives::*;
use validator_sets::{primitives::MAX_KEY_SHARES_PER_SET_U32, MembershipProof};
pub use crate as validator_sets;
pub use serai_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;
type Block = frame_system::mocking::MockBlock<Test>;
// Maximum number of authorities per session.
pub type MaxAuthorities = ConstU32<{ MAX_KEY_SHARES_PER_SET_U32 }>;
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,
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 Config for Test {
type RuntimeEvent = RuntimeEvent;
type ShouldEndSession = Babe;
}
// 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();
#[expect(unused_variables, unreachable_code, clippy::diverging_sub_expression)]
validator_sets::GenesisConfig::<Test> {
networks,
participants: genesis_participants()
.into_iter()
.map(|p| {
let keys: crate::AllEmbeddedEllipticCurveKeysAtGenesis = todo!("TODO");
(p.public(), keys)
})
.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
}

View File

@@ -1,14 +1,21 @@
use alloc::vec::Vec;
use sp_core::{Encode, Decode, ConstU32, sr25519::Public, bounded::BoundedVec};
use serai_abi::primitives::{
network_id::NetworkId,
balance::Amount,
validator_sets::{KeyShares as KeySharesStruct, Session, ExternalValidatorSet, ValidatorSet},
use serai_abi::{
primitives::{
network_id::{ExternalNetworkId, NetworkId},
balance::Amount,
validator_sets::{
KeyShares as KeySharesStruct, Session, ExternalValidatorSet, ValidatorSet, SlashReport,
},
},
validator_sets::{DeallocationTimeline, Event},
};
use frame_support::storage::{StorageValue, StorageMap, StorageDoubleMap, StoragePrefixedMap};
use serai_core_pallet::Pallet as Core;
use crate::{
embedded_elliptic_curve_keys::EmbeddedEllipticCurveKeys, allocations::Allocations, keys::Keys,
};
@@ -21,6 +28,9 @@ pub(crate) type GenesisValidators =
pub(crate) type SelectedValidatorsKey = (ValidatorSet, [u8; 16], Public);
pub(crate) trait SessionsStorage: EmbeddedEllipticCurveKeys + Allocations + Keys {
/// The configuration for the core pallet.
type Config: serai_core_pallet::Config;
/// The genesis validators
///
/// The usage of is shared with the rest of the pallet. `Sessions` only reads it.
@@ -68,6 +78,11 @@ pub(crate) trait SessionsStorage: EmbeddedEllipticCurveKeys + Allocations + Keys
///
/// This is opaque and to be exclusively read/write by `Sessions`.
type DelayedDeallocations: StorageDoubleMap<Public, Session, Amount, Query = Option<Amount>>;
/// Networks for which we're awaiting slash reports.
///
/// This is opaque and to be exclusively read/write by `Sessions`.
type PendingSlashReport: StorageMap<ExternalNetworkId, (), Query = Option<()>>;
}
/// The storage key for the SelectedValidators map.
@@ -119,11 +134,6 @@ pub enum AllocationError {
IntroducesSinglePointOfFailure,
}
pub(crate) enum DeallocationTimeline {
Immediate,
Delayed { unlocks_at: Session },
}
/// An error when deallocating.
#[derive(
scale::Encode, scale::Decode, scale::DecodeWithMemTracking, frame_support::PalletError,
@@ -193,6 +203,11 @@ pub(crate) trait Sessions {
session: Session,
) -> Result<Amount, DeallocationError>;
/// Handle a slash report.
///
/// This will panic if this slash report isn't pending.
fn handle_slash_report(network: ExternalNetworkId, slashes: SlashReport);
/// The currently active session for a network.
fn current_session(network: NetworkId) -> Option<Session>;
@@ -232,6 +247,11 @@ pub(crate) trait Sessions {
.map(|(validator, _key_shares)| (validator, validator))
.collect()
}
/// If this network is awaiting a slash report.
///
/// If so, this returns the key which should publish the slash report.
fn waiting_for_slash_report(network: ExternalNetworkId) -> Option<Public>;
}
impl<Storage: SessionsStorage> Sessions for Storage {
@@ -313,11 +333,13 @@ impl<Storage: SessionsStorage> Sessions for Storage {
);
}
Core::<Storage::Config>::emit_event(Event::SetDecided { set: latest_decided_set });
true
}
fn accept_handover(network: NetworkId) {
let current = {
let (prior, current) = {
let current = Storage::CurrentSession::get(network);
let latest_decided = Storage::LatestDecidedSession::get(network)
.expect("accepting handover but never decided a session");
@@ -328,8 +350,8 @@ impl<Storage: SessionsStorage> Sessions for Storage {
);
// Set the CurrentSession variable
Storage::CurrentSession::set(network, Some(latest_decided));
// Return `latest_decided` as `current` as it is now current
latest_decided
// Return `latest_decided` as `current` as it is now current, and `current` as `prior`
(current, latest_decided)
};
let mut total_allocated_stake = Amount(0);
@@ -343,6 +365,23 @@ impl<Storage: SessionsStorage> Sessions for Storage {
// Update the total allocated stake variable to the current session
Storage::TotalAllocatedStake::set(network, Some(total_allocated_stake));
match network {
NetworkId::Serai => {}
NetworkId::External(network) => {
// If this network never submitted its slash report, treat it as submitting `vec![]`
if Storage::PendingSlashReport::take(network).is_some() {
Core::<Storage::Config>::emit_event(Event::SlashReport {
set: ExternalValidatorSet {
network,
session: prior.expect("pending slash report yet no prior session"),
},
});
}
// Mark this network as pending a slash report
Storage::PendingSlashReport::insert(network, ());
}
}
// Clean-up the historic set's storage, if one exists
if let Some(historic_session) = current.0.checked_sub(2).map(Session) {
let historic_set = ValidatorSet { network, session: historic_session };
@@ -355,6 +394,10 @@ impl<Storage: SessionsStorage> Sessions for Storage {
}
}
}
Core::<Storage::Config>::emit_event(Event::AcceptedHandover {
set: ValidatorSet { network, session: current },
});
}
fn increase_allocation(
@@ -437,6 +480,12 @@ impl<Storage: SessionsStorage> Sessions for Storage {
}
}
Core::<Storage::Config>::emit_event(Event::Allocation {
validator: validator.into(),
network,
amount,
});
Ok(())
}
@@ -445,6 +494,9 @@ impl<Storage: SessionsStorage> Sessions for Storage {
validator: Public,
amount: Amount,
) -> Result<DeallocationTimeline, DeallocationError> {
// TODO: Check if this would introduce a single point of failure
// TODO: Check if this would violate economic security
/*
Decrease the allocation.
@@ -522,6 +574,22 @@ impl<Storage: SessionsStorage> Sessions for Storage {
Ok(DeallocationTimeline::Immediate)
}
fn handle_slash_report(network: ExternalNetworkId, _slashes: SlashReport) {
Storage::PendingSlashReport::take(network)
.expect("handling a slash report which wasn't pending");
let current_session =
Self::current_session(network.into()).expect("handling slash report yet no current session");
let prior_session = Session(
current_session.0.checked_sub(1).expect("handling slash report yet no prior session"),
);
Core::<Storage::Config>::emit_event(Event::SlashReport {
set: ExternalValidatorSet { network, session: prior_session },
});
// TODO: Actually handle `_slashes`
}
fn claim_delayed_deallocation(
validator: Public,
network: NetworkId,
@@ -566,4 +634,19 @@ impl<Storage: SessionsStorage> Sessions for Storage {
fn selected_validators(set: ValidatorSet) -> impl Iterator<Item = (Public, KeySharesStruct)> {
selected_validators::<Storage::SelectedValidators>(set)
}
fn waiting_for_slash_report(network: ExternalNetworkId) -> Option<Public> {
if !Storage::PendingSlashReport::contains_key(network) {
None?;
}
let current_session = Self::current_session(network.into())
.expect("network awaiting slash report yet no current session");
let prior_session = Session(
current_session.0.checked_sub(1).expect("network awaiting slash report yet no prior session"),
);
Some(
Storage::oraclization_key(ExternalValidatorSet { network, session: prior_session })
.expect("no oraclization key for set waiting for a slash report"),
)
}
}

View File

@@ -1,558 +0,0 @@
use crate::{mock::*, primitives::*};
use std::collections::HashMap;
use ciphersuite::{WrappedGroup, GroupIo};
use dkg_musig::musig;
use schnorrkel::{frost::curve::Ristretto, Schnorrkel};
use zeroize::Zeroizing;
use rand_core::OsRng;
use frame_support::{
assert_noop, assert_ok,
pallet_prelude::{InvalidTransaction, TransactionSource},
traits::{OnFinalize, OnInitialize},
};
use frame_system::RawOrigin;
use sp_core::{
sr25519::{Public, Pair, Signature},
Pair as PairTrait,
};
use sp_runtime::traits::ValidateUnsigned;
use serai_abi::primitives::*;
fn active_network_validators(network: NetworkId) -> Vec<(Public, u64)> {
if network == NetworkId::Serai {
Babe::authorities().into_iter().map(|(id, key_share)| (id.into_inner(), key_share)).collect()
} else {
ValidatorSets::participants_for_latest_decided_set(network).unwrap().into_inner()
}
}
fn verify_session_and_active_validators(network: NetworkId, participants: &[Public], session: u32) {
let mut validators: Vec<Public> = active_network_validators(network)
.into_iter()
.map(|(p, ks)| {
assert_eq!(ks, 1);
p
})
.collect();
validators.sort();
assert_eq!(ValidatorSets::session(network).unwrap(), Session(session));
assert_eq!(participants, validators);
// TODO: how to make sure block finalizations work as usual here?
}
fn get_session_at_which_changes_activate(network: NetworkId) -> u32 {
let current_session = ValidatorSets::session(network).unwrap().0;
// changes should be active in the next session
if network == NetworkId::Serai {
// it takes 1 extra session for serai net to make the changes active.
current_session + 2
} else {
current_session + 1
}
}
fn set_keys_for_session(network: ExternalNetworkId) {
ValidatorSets::set_keys(
RawOrigin::None.into(),
network,
KeyPair(insecure_pair_from_name("Alice").public(), vec![].try_into().unwrap()),
vec![].try_into().unwrap(),
Signature([0u8; 64]),
)
.unwrap();
}
fn set_keys_signature(set: &ExternalValidatorSet, key_pair: &KeyPair, pairs: &[Pair]) -> Signature {
let mut pub_keys = vec![];
for pair in pairs {
let public_key =
<Ristretto as GroupIo>::read_G::<&[u8]>(&mut pair.public().0.as_ref()).unwrap();
pub_keys.push(public_key);
}
let mut threshold_keys = vec![];
for i in 0 .. pairs.len() {
let secret_key = <Ristretto as GroupIo>::read_F::<&[u8]>(
&mut pairs[i].as_ref().secret.to_bytes()[.. 32].as_ref(),
)
.unwrap();
assert_eq!(Ristretto::generator() * secret_key, pub_keys[i]);
threshold_keys.push(
musig::<Ristretto>(musig_context((*set).into()), Zeroizing::new(secret_key), &pub_keys)
.unwrap(),
);
}
let mut musig_keys = HashMap::new();
for threshold_keys in threshold_keys {
musig_keys.insert(threshold_keys.params().i(), threshold_keys);
}
let sig = frost::tests::sign_without_caching(
&mut OsRng,
frost::tests::algorithm_machines(&mut OsRng, &Schnorrkel::new(b"substrate"), &musig_keys),
&set_keys_message(set, key_pair),
);
Signature(sig.to_bytes())
}
fn get_ordered_keys(network: NetworkId, participants: &[Pair]) -> Vec<Pair> {
// retrieve the current session validators so that we know the order of the keys
// that is necessary for the correct musig signature.
let validators = ValidatorSets::participants_for_latest_decided_set(network).unwrap();
// collect the pairs of the validators
let mut pairs = vec![];
for (v, _) in validators {
let p = participants.iter().find(|pair| pair.public() == v).unwrap().clone();
pairs.push(p);
}
pairs
}
fn rotate_session_until(network: NetworkId, session: u32) {
let mut current = ValidatorSets::session(network).unwrap().0;
while current < session {
Babe::on_initialize(System::block_number() + 1);
ValidatorSets::rotate_session();
if let NetworkId::External(n) = network {
set_keys_for_session(n);
}
ValidatorSets::retire_set(ValidatorSet { session: Session(current), network });
current += 1;
}
assert_eq!(current, session);
}
#[test]
fn rotate_session() {
new_test_ext().execute_with(|| {
let genesis_participants: Vec<Public> =
genesis_participants().into_iter().map(|p| p.public()).collect();
let key_shares = key_shares();
let mut participants = HashMap::from([
(NetworkId::Serai, genesis_participants.clone()),
(NetworkId::External(ExternalNetworkId::Bitcoin), genesis_participants.clone()),
(NetworkId::External(ExternalNetworkId::Ethereum), genesis_participants.clone()),
(NetworkId::External(ExternalNetworkId::Monero), genesis_participants),
]);
// rotate session
for network in NETWORKS {
let participants = participants.get_mut(&network).unwrap();
// verify for session 0
participants.sort();
if let NetworkId::External(n) = network {
set_keys_for_session(n);
}
verify_session_and_active_validators(network, participants, 0);
// add 1 participant
let new_participant = insecure_pair_from_name("new-guy").public();
Coins::mint(new_participant, Balance { coin: Coin::Serai, amount: key_shares[&network] })
.unwrap();
ValidatorSets::allocate(
RawOrigin::Signed(new_participant).into(),
network,
key_shares[&network],
)
.unwrap();
participants.push(new_participant);
// move network to the activation session
let activation_session = get_session_at_which_changes_activate(network);
rotate_session_until(network, activation_session);
// verify
participants.sort();
verify_session_and_active_validators(network, participants, activation_session);
// remove 1 participant
let participant_to_remove = participants[0];
ValidatorSets::deallocate(
RawOrigin::Signed(participant_to_remove).into(),
network,
key_shares[&network],
)
.unwrap();
participants
.swap_remove(participants.iter().position(|k| *k == participant_to_remove).unwrap());
// check pending deallocations
let pending = ValidatorSets::pending_deallocations(
(network, participant_to_remove),
Session(if network == NetworkId::Serai {
activation_session + 3
} else {
activation_session + 2
}),
);
assert_eq!(pending, Some(key_shares[&network]));
// move network to the activation session
let activation_session = get_session_at_which_changes_activate(network);
rotate_session_until(network, activation_session);
// verify
participants.sort();
verify_session_and_active_validators(network, participants, activation_session);
}
})
}
#[test]
fn allocate() {
new_test_ext().execute_with(|| {
let genesis_participants: Vec<Public> =
genesis_participants().into_iter().map(|p| p.public()).collect();
let key_shares = key_shares();
let participant = insecure_pair_from_name("random1").public();
let network = NetworkId::External(ExternalNetworkId::Ethereum);
// check genesis TAS
set_keys_for_session(network.try_into().unwrap());
assert_eq!(
ValidatorSets::total_allocated_stake(network).unwrap().0,
key_shares[&network].0 * u64::try_from(genesis_participants.len()).unwrap()
);
// we can't allocate less than a key share
let amount = Amount(key_shares[&network].0 * 3);
Coins::mint(participant, Balance { coin: Coin::Serai, amount }).unwrap();
assert_noop!(
ValidatorSets::allocate(
RawOrigin::Signed(participant).into(),
network,
Amount(key_shares[&network].0 - 1)
),
validator_sets::Error::<Test>::InsufficientAllocation
);
// we can't allocate too much that the net exhibits the ability to handle any single node
// becoming byzantine
assert_noop!(
ValidatorSets::allocate(RawOrigin::Signed(participant).into(), network, amount),
validator_sets::Error::<Test>::AllocationWouldRemoveFaultTolerance
);
// we should be allocate a proper amount
assert_ok!(ValidatorSets::allocate(
RawOrigin::Signed(participant).into(),
network,
key_shares[&network]
));
assert_eq!(Coins::balance(participant, Coin::Serai).0, amount.0 - key_shares[&network].0);
// check new amount is reflected on TAS on new session
rotate_session_until(network, 1);
assert_eq!(
ValidatorSets::total_allocated_stake(network).unwrap().0,
key_shares[&network].0 * (u64::try_from(genesis_participants.len()).unwrap() + 1)
);
// check that new participants match
let mut active_participants: Vec<Public> =
active_network_validators(network).into_iter().map(|(p, _)| p).collect();
let mut current_participants = genesis_participants.clone();
current_participants.push(participant);
current_participants.sort();
active_participants.sort();
assert_eq!(current_participants, active_participants);
})
}
#[test]
fn deallocate_pending() {
new_test_ext().execute_with(|| {
let genesis_participants: Vec<Public> =
genesis_participants().into_iter().map(|p| p.public()).collect();
let key_shares = key_shares();
let participant = insecure_pair_from_name("random1").public();
let network = NetworkId::External(ExternalNetworkId::Bitcoin);
// check genesis TAS
set_keys_for_session(network.try_into().unwrap());
assert_eq!(
ValidatorSets::total_allocated_stake(network).unwrap().0,
key_shares[&network].0 * u64::try_from(genesis_participants.len()).unwrap()
);
// allocate some amount
Coins::mint(participant, Balance { coin: Coin::Serai, amount: key_shares[&network] }).unwrap();
assert_ok!(ValidatorSets::allocate(
RawOrigin::Signed(participant).into(),
network,
key_shares[&network]
));
assert_eq!(Coins::balance(participant, Coin::Serai).0, 0);
// move to next session
let mut current_session = ValidatorSets::session(network).unwrap().0;
current_session += 1;
rotate_session_until(network, current_session);
assert_eq!(
ValidatorSets::total_allocated_stake(network).unwrap().0,
key_shares[&network].0 * (u64::try_from(genesis_participants.len()).unwrap() + 1)
);
// we can deallocate all of our allocation
assert_ok!(ValidatorSets::deallocate(
RawOrigin::Signed(participant).into(),
network,
key_shares[&network]
));
// check pending deallocations
let pending_session =
if network == NetworkId::Serai { current_session + 3 } else { current_session + 2 };
assert_eq!(
ValidatorSets::pending_deallocations((network, participant), Session(pending_session)),
Some(key_shares[&network])
);
// we can't claim it immediately
assert_noop!(
ValidatorSets::claim_deallocation(
RawOrigin::Signed(participant).into(),
network,
Session(pending_session),
),
validator_sets::Error::<Test>::NonExistentDeallocation
);
// we should be able to claim it in the pending session
rotate_session_until(network, pending_session);
assert_ok!(ValidatorSets::claim_deallocation(
RawOrigin::Signed(participant).into(),
network,
Session(pending_session),
));
})
}
#[test]
fn deallocate_immediately() {
new_test_ext().execute_with(|| {
let genesis_participants: Vec<Public> =
genesis_participants().into_iter().map(|p| p.public()).collect();
let key_shares = key_shares();
let participant = insecure_pair_from_name("random1").public();
let network = NetworkId::External(ExternalNetworkId::Monero);
// check genesis TAS
set_keys_for_session(network.try_into().unwrap());
assert_eq!(
ValidatorSets::total_allocated_stake(network).unwrap().0,
key_shares[&network].0 * u64::try_from(genesis_participants.len()).unwrap()
);
// we can't deallocate when we don't have an allocation
assert_noop!(
ValidatorSets::deallocate(
RawOrigin::Signed(participant).into(),
network,
key_shares[&network]
),
validator_sets::Error::<Test>::NonExistentValidator
);
// allocate some amount
Coins::mint(participant, Balance { coin: Coin::Serai, amount: key_shares[&network] }).unwrap();
assert_ok!(ValidatorSets::allocate(
RawOrigin::Signed(participant).into(),
network,
key_shares[&network]
));
assert_eq!(Coins::balance(participant, Coin::Serai).0, 0);
// we can't deallocate more than our allocation
assert_noop!(
ValidatorSets::deallocate(
RawOrigin::Signed(participant).into(),
network,
Amount(key_shares[&network].0 + 1)
),
validator_sets::Error::<Test>::NotEnoughAllocated
);
// we can't deallocate an amount that would left us less than a key share as long as it isn't 0
assert_noop!(
ValidatorSets::deallocate(
RawOrigin::Signed(participant).into(),
network,
Amount(key_shares[&network].0 / 2)
),
validator_sets::Error::<Test>::DeallocationWouldRemoveParticipant
);
// we can deallocate all of our allocation
assert_ok!(ValidatorSets::deallocate(
RawOrigin::Signed(participant).into(),
network,
key_shares[&network]
));
// It should be immediately deallocated since we are not yet in an active set
assert_eq!(Coins::balance(participant, Coin::Serai), key_shares[&network]);
assert!(ValidatorSets::pending_deallocations((network, participant), Session(1)).is_none());
// allocate again
assert_ok!(ValidatorSets::allocate(
RawOrigin::Signed(participant).into(),
network,
key_shares[&network]
));
assert_eq!(Coins::balance(participant, Coin::Serai).0, 0);
// make a pool so that we have security oracle value for the coin
let liq_acc = insecure_pair_from_name("liq-acc").public();
let coin = ExternalCoin::Monero;
let balance = ExternalBalance { coin, amount: Amount(2 * key_shares[&network].0) };
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);
// we can't deallocate if it would break economic security
// The reason we don't have economic security for the network now is that we just set
// the value for coin/SRI to 1:1 when making the pool and we minted 2 * key_share amount
// of coin but we only allocated 1 key_share of SRI for the network although we need more than
// 3 for the same amount of coin.
assert_noop!(
ValidatorSets::deallocate(
RawOrigin::Signed(participant).into(),
network,
key_shares[&network]
),
validator_sets::Error::<Test>::DeallocationWouldRemoveEconomicSecurity
);
})
}
#[test]
fn set_keys_keys_exist() {
new_test_ext().execute_with(|| {
let network = ExternalNetworkId::Monero;
// set the keys first
ValidatorSets::set_keys(
RawOrigin::None.into(),
network,
KeyPair(insecure_pair_from_name("name").public(), Vec::new().try_into().unwrap()),
vec![].try_into().unwrap(),
Signature([0u8; 64]),
)
.unwrap();
let call = validator_sets::Call::<Test>::set_keys {
network,
key_pair: KeyPair(insecure_pair_from_name("name").public(), Vec::new().try_into().unwrap()),
signature_participants: vec![].try_into().unwrap(),
signature: Signature([0u8; 64]),
};
assert_eq!(
ValidatorSets::validate_unsigned(TransactionSource::External, &call),
InvalidTransaction::Stale.into()
);
})
}
#[test]
fn set_keys_invalid_signature() {
new_test_ext().execute_with(|| {
let network = ExternalNetworkId::Ethereum;
let mut participants = get_ordered_keys(network.into(), &genesis_participants());
// we can't have invalid set
let mut set = ExternalValidatorSet { network, session: Session(1) };
let key_pair =
KeyPair(insecure_pair_from_name("name").public(), Vec::new().try_into().unwrap());
let signature = set_keys_signature(&set, &key_pair, &participants);
let call = validator_sets::Call::<Test>::set_keys {
network,
key_pair: key_pair.clone(),
signature_participants: vec![].try_into().unwrap(),
signature,
};
assert_eq!(
ValidatorSets::validate_unsigned(TransactionSource::External, &call),
InvalidTransaction::BadProof.into()
);
// fix the set
set.session = Session(0);
// participants should match
participants.push(insecure_pair_from_name("random1"));
let signature = set_keys_signature(&set, &key_pair, &participants);
let call = validator_sets::Call::<Test>::set_keys {
network,
key_pair: key_pair.clone(),
signature_participants: vec![].try_into().unwrap(),
signature,
};
assert_eq!(
ValidatorSets::validate_unsigned(TransactionSource::External, &call),
InvalidTransaction::BadProof.into()
);
// fix the participants
participants.pop();
// msg key pair and the key pair to set should match
let key_pair2 =
KeyPair(insecure_pair_from_name("name2").public(), Vec::new().try_into().unwrap());
let signature = set_keys_signature(&set, &key_pair2, &participants);
let call = validator_sets::Call::<Test>::set_keys {
network,
key_pair: key_pair.clone(),
signature_participants: vec![].try_into().unwrap(),
signature,
};
assert_eq!(
ValidatorSets::validate_unsigned(TransactionSource::External, &call),
InvalidTransaction::BadProof.into()
);
// use the same key pair
let signature = set_keys_signature(&set, &key_pair, &participants);
let call = validator_sets::Call::<Test>::set_keys {
network,
key_pair,
signature_participants: vec![].try_into().unwrap(),
signature,
};
ValidatorSets::validate_unsigned(TransactionSource::External, &call).unwrap();
})
}
// TODO: add report_slashes tests when the feature is complete.