10 Commits

Author SHA1 Message Date
Luke Parker
ffae6753ec Restore the set_keys call 2025-09-20 03:04:26 -04:00
Luke Parker
a04215bc13 Remove commented-out slashing code from serai-validator-sets-pallet
Deferred to https://github.com/serai-dex/serai/issues/657.
2025-09-20 03:04:19 -04:00
Luke Parker
28aea8a442 Incorporate check a validator won't prevent ever not having a single point of failure 2025-09-20 01:58:39 -04:00
Luke Parker
7b46477ca0 Add explicit hook for deciding whether to include the genesis validators 2025-09-20 01:57:55 -04:00
Luke Parker
e62b62ddfb Restore usage of pallet-grandpa to serai-validator-sets-pallet 2025-09-20 01:36:11 -04:00
Luke Parker
a2d8d0fd13 Restore integration with pallet-babe to serai-validator-sets-pallet 2025-09-20 01:23:02 -04:00
Luke Parker
b2b36b17c4 Restore GenesisConfig to the validator sets pallet 2025-09-20 00:06:19 -04:00
Luke Parker
9de8394efa Emit events within the signals pallet 2025-09-19 22:44:29 -04:00
Luke Parker
3cb9432daa Have the coins pallet emit events via serai_core_pallet
`serai_core_pallet` solely defines an accumulator for the events. We use the
traditional `frame_system::Events` to store them for now and enable retrieval.
2025-09-19 22:18:55 -04:00
Luke Parker
3f5150b3fa Properly define the core pallet instead of placing it within the runtime 2025-09-19 19:05:47 -04:00
41 changed files with 1195 additions and 1255 deletions

View File

@@ -83,21 +83,16 @@ jobs:
run: |
GITHUB_CI=true RUST_BACKTRACE=1 cargo test --all-features \
-p serai-primitives \
-p serai-coins-primitives \
-p serai-coins-pallet \
-p serai-dex-pallet \
-p serai-validator-sets-primitives \
-p serai-validator-sets-pallet \
-p serai-genesis-liquidity-primitives \
-p serai-genesis-liquidity-pallet \
-p serai-emissions-primitives \
-p serai-emissions-pallet \
-p serai-economic-security-pallet \
-p serai-in-instructions-primitives \
-p serai-in-instructions-pallet \
-p serai-signals-primitives \
-p serai-signals-pallet \
-p serai-abi \
-p serai-core-pallet \
-p serai-coins-pallet \
-p serai-validator-sets-pallet \
-p serai-signals-pallet \
-p serai-dex-pallet \
-p serai-genesis-liquidity-pallet \
-p serai-economic-security-pallet \
-p serai-emissions-pallet \
-p serai-in-instructions-pallet \
-p serai-runtime \
-p serai-node

289
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -84,6 +84,7 @@ members = [
"substrate/primitives",
"substrate/abi",
"substrate/core",
"substrate/coins",
"substrate/validator-sets",
"substrate/signals",

View File

@@ -79,6 +79,7 @@ exceptions = [
{ allow = ["AGPL-3.0-only"], name = "serai-coordinator-libp2p-p2p" },
{ allow = ["AGPL-3.0-only"], name = "serai-coordinator" },
{ allow = ["AGPL-3.0-only"], name = "serai-core-pallet" },
{ allow = ["AGPL-3.0-only"], name = "serai-coins-pallet" },
{ allow = ["AGPL-3.0-only"], name = "serai-dex-pallet" },

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 = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", 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 = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false, features = ["serde"], optional = true }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false, 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 }
serai-primitives = { path = "../primitives", version = "0.1", default-features = false }

View File

@@ -44,6 +44,15 @@ pub enum Event {
/// The coins minted.
coins: Balance,
},
/// The specified coins were transferred.
Transfer {
/// The address transferred from.
from: SeraiAddress,
/// The address transferred to.
to: SeraiAddress,
/// The coins transferred.
coins: Balance,
},
/// The specified coins were burnt.
Burn {
/// The address burnt from.
@@ -58,13 +67,4 @@ pub enum Event {
/// The `OutInstruction` specified, and the coins burnt.
instruction: OutInstructionWithBalance,
},
/// The specified coins were transferred.
Transfer {
/// The address transferred from.
from: SeraiAddress,
/// The address transferred to.
to: SeraiAddress,
/// The coins transferred.
coins: Balance,
},
}

View File

@@ -11,3 +11,10 @@ pub enum Event {
network: ExternalNetworkId,
},
}
/// A trait representing access to the information on economic security.
pub trait EconomicSecurity {
/// If am external network has _ever_ achieved economic security.
#[must_use]
fn achieved_economic_security(network: ExternalNetworkId) -> bool;
}

View File

@@ -96,3 +96,44 @@ pub enum Event {
/// The event for `InInstruction`s.
InInstructions(in_instructions::Event) = 7,
}
impl From<system::Event> for Event {
fn from(event: system::Event) -> Self {
Self::System(event)
}
}
impl From<coins::Event> for Event {
fn from(event: coins::Event) -> Self {
Self::Coins(event)
}
}
impl From<validator_sets::Event> for Event {
fn from(event: validator_sets::Event) -> Self {
Self::ValidatorSets(event)
}
}
impl From<signals::Event> for Event {
fn from(event: signals::Event) -> Self {
Self::Signals(event)
}
}
impl From<dex::Event> for Event {
fn from(event: dex::Event) -> Self {
Self::Dex(event)
}
}
impl From<genesis_liquidity::Event> for Event {
fn from(event: genesis_liquidity::Event) -> Self {
Self::GenesisLiquidity(event)
}
}
impl From<economic_security::Event> for Event {
fn from(event: economic_security::Event) -> Self {
Self::EconomicSecurity(event)
}
}
impl From<in_instructions::Event> for Event {
fn from(event: in_instructions::Event) -> Self {
Self::InInstructions(event)
}
}

View File

@@ -1,7 +1,9 @@
use borsh::{BorshSerialize, BorshDeserialize};
use serai_primitives::{
address::SeraiAddress, network_id::NetworkId, validator_sets::ValidatorSet, signals::Signal,
address::SeraiAddress,
network_id::{ExternalNetworkId, NetworkId},
signals::Signal,
};
/// A call to signals.
@@ -95,38 +97,40 @@ pub enum Event {
/// The network with which favor for the signal was revoked.
with_network: NetworkId,
},
/// A supermajority of a validator set now favor a signal.
SetInFavor {
/// The signal which now has a supermajority of a validator set favoring it.
/// A supermajority of a network's validator set now favor a signal.
NetworkInFavor {
/// The signal which now has a supermajority of a network's validator set favoring it.
signal: Signal,
/// The validator set which is now considered to favor the signal.
set: ValidatorSet,
/// The network which is now considered to favor the signal.
network: NetworkId,
},
/// A validator set is no longer considered to favor a signal.
SetNoLongerInFavor {
/// The signal which no longer has the validator set considered in favor of it.
/// A network's validator set is no longer considered to favor a signal.
NetworkNoLongerInFavor {
/// The signal which no longer has the network considered in favor of it.
signal: Signal,
/// The validator set which is no longer considered to be in favor of the signal.
set: ValidatorSet,
/// The network which is no longer considered to be in favor of the signal.
network: NetworkId,
},
/// A retirement signal has been locked in.
RetirementSignalLockedIn {
/// The signal which has been locked in.
signal: [u8; 32],
},
/// A validator set's ability to publish batches was halted.
/// A network's ability to publish batches was halted.
///
/// This also halts set rotation in effect, as handovers are via new sets starting to publish
/// batches.
SetHalted {
/// The signal which has been locked in.
signal: [u8; 32],
NetworkHalted {
/// The network which has been halted.
network: ExternalNetworkId,
},
/// An account has stood against a signal.
AgainstSignal {
/// The signal stood against.
signal: Signal,
/// The account which stood against the signal.
who: SeraiAddress,
account: SeraiAddress,
/// The network with which this was expressed.
with_network: NetworkId,
},
}

View File

@@ -297,6 +297,8 @@ mod substrate {
/// Begin execution of a transaction.
fn start_transaction(&self);
/// Consume the next nonce for an account.
///
/// This MUST NOT be called if the next nonce is `u32::MAX`. The caller MAY panic in that case.
fn consume_next_nonce(&self, signer: &SeraiAddress);
/// Have the transaction pay its SRI fee.
fn pay_fee(&self, signer: &SeraiAddress, fee: Amount) -> Result<(), TransactionValidityError>;
@@ -425,13 +427,20 @@ mod substrate {
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale))?;
}
}
match self.1.next_nonce(signer).cmp(nonce) {
core::cmp::Ordering::Less => {
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale))?
{
let next_nonce = self.1.next_nonce(signer);
if next_nonce == u32::MAX {
Err(TransactionValidityError::Invalid(InvalidTransaction::BadSigner))?;
}
core::cmp::Ordering::Equal => {}
core::cmp::Ordering::Greater => {
Err(TransactionValidityError::Invalid(InvalidTransaction::Future))?
match next_nonce.cmp(nonce) {
core::cmp::Ordering::Less => {
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale))?
}
core::cmp::Ordering::Equal => {}
core::cmp::Ordering::Greater => {
Err(TransactionValidityError::Invalid(InvalidTransaction::Future))?
}
}
}

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 = "d4624c561765c13b38eb566e435131a8c329a543", optional = true }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", optional = true }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", 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 }
async-lock = "3"

View File

@@ -18,38 +18,45 @@ 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 = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-std = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-core = { 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 = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", 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 }
serai-primitives = { path = "../primitives", default-features = false, features = ["serde", "non_canonical_scale_derivations"] }
serai-abi = { path = "../abi", default-features = false, features = ["substrate"] }
serai-core-pallet = { path = "../core", default-features = false }
[dev-dependencies]
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false, features = ["std"] }
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"] }
[features]
std = [
"scale/std",
"sp-core/std",
"sp-std/std",
"sp-runtime/std",
"frame-system/std",
"frame-support/std",
"serai-primitives/std",
"serai-abi/std",
"serai-core-pallet/std",
]
try-runtime = [
"frame-system/try-runtime",
"frame-support/try-runtime",
"serai-abi/try-runtime",
"serai-core-pallet/try-runtime",
]
runtime-benchmarks = [
"frame-system/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"serai-core-pallet/runtime-benchmarks",
]
default = ["std"]

View File

@@ -11,7 +11,8 @@ mod mock;
#[cfg(test)]
mod tests;
use serai_primitives::balance::ExternalBalance;
use serai_abi::primitives::balance::ExternalBalance;
use serai_core_pallet::Pallet as Core;
/// The decider for if a mint is allowed or not.
pub trait AllowMint {
@@ -27,7 +28,7 @@ impl AllowMint for AlwaysAllowMint {
}
}
#[allow(clippy::cast_possible_truncation)]
#[expect(clippy::cast_possible_truncation)]
#[frame_support::pallet]
mod pallet {
use core::any::TypeId;
@@ -38,7 +39,10 @@ mod pallet {
use frame_system::pallet_prelude::*;
use frame_support::pallet_prelude::*;
use serai_primitives::{coin::*, balance::*, instructions::OutInstructionWithBalance};
use serai_abi::{
primitives::{coin::*, balance::*, instructions::OutInstructionWithBalance},
coins::Event,
};
use super::*;
@@ -53,7 +57,9 @@ mod pallet {
/// The configuration of this pallet.
#[pallet::config]
pub trait Config<I: 'static = ()>: frame_system::Config<AccountId = Public> {
pub trait Config<I: 'static = ()>:
frame_system::Config<AccountId = Public> + serai_core_pallet::Config
{
/// What decides if mints are allowed.
type AllowMint: AllowMint;
}
@@ -90,42 +96,6 @@ mod pallet {
BurnWithInstructionNotAllowed,
}
/// An event emitted.
#[pallet::event]
#[pallet::generate_deposit(fn deposit_event)]
pub enum Event<T: Config<I>, I: 'static = ()> {
/// Coins were minted.
Mint {
/// The account minted to.
to: Public,
/// The balance minted.
balance: Balance,
},
/// Coins were transferred.
Transfer {
/// The account transferred from.
from: Public,
/// The account transferred to.
to: Public,
/// The balance transferred.
balance: Balance,
},
/// Coins were burnt.
Burn {
/// The account burnt from.
from: Public,
/// The balance burnt.
balance: Balance,
},
/// Coins were burnt with an instruction.
BurnWithInstruction {
/// The account burnt from.
from: Public,
/// The instruction, and associated balance.
instruction: OutInstructionWithBalance,
},
}
/// The Pallet struct.
#[pallet::pallet]
pub struct Pallet<T, I = ()>(_);
@@ -151,6 +121,16 @@ mod pallet {
}
impl<T: Config<I>, I: 'static> Pallet<T, I> {
fn emit_event(event: Event) {
if TypeId::of::<I>() == TypeId::of::<CoinsInstance>() {
Core::<T>::emit_event(event)
} else if TypeId::of::<I>() == TypeId::of::<LiquidityTokensInstance>() {
// The DEX pallet is expected to emit this event
} else {
panic!("unrecognized instance type for `coins::Pallet` made it into an execution context")
}
}
/// Returns the balance of `coin` for the specified account.
pub fn balance(
of: impl scale::EncodeLike<Public>,
@@ -195,10 +175,10 @@ mod pallet {
/// Mint `balance` to the given account.
///
/// Errors if any amount overflows.
pub fn mint(to: Public, balance: Balance) -> Result<(), Error<T, I>> {
pub fn mint(to: Public, coins: Balance) -> Result<(), Error<T, I>> {
{
// If this is an external coin, check if we can mint it
let external_balance = ExternalBalance::try_from(balance);
let external_balance = ExternalBalance::try_from(coins);
let can_mint_external = external_balance.as_ref().map(T::AllowMint::is_allowed);
// If it was native to the Serai network, we can always mint it
let can_mint = can_mint_external.unwrap_or(true);
@@ -208,14 +188,14 @@ mod pallet {
}
// update the balance
Self::increase_balance_internal(to, balance)?;
Self::increase_balance_internal(to, coins)?;
// update the supply
let new_supply = (Supply::<T, I>::get(balance.coin) + balance.amount)
.ok_or(Error::<T, I>::AmountOverflowed)?;
Supply::<T, I>::set(balance.coin, new_supply);
let new_supply =
(Supply::<T, I>::get(coins.coin) + coins.amount).ok_or(Error::<T, I>::AmountOverflowed)?;
Supply::<T, I>::set(coins.coin, new_supply);
Self::deposit_event(Event::Mint { to, balance });
Self::emit_event(Event::Mint { to: to.into(), coins });
Ok(())
}
@@ -238,12 +218,12 @@ mod pallet {
Ok(())
}
/// Transfer `balance` from `from` to `to`.
pub fn transfer_fn(from: Public, to: Public, balance: Balance) -> Result<(), Error<T, I>> {
/// Transfer `coins` from `from` to `to`.
pub fn transfer_fn(from: Public, to: Public, coins: Balance) -> Result<(), Error<T, I>> {
// update balances of accounts
Self::decrease_balance_internal(from, balance)?;
Self::increase_balance_internal(to, balance)?;
Self::deposit_event(Event::Transfer { from, to, balance });
Self::decrease_balance_internal(from, coins)?;
Self::increase_balance_internal(to, coins)?;
Self::emit_event(Event::Transfer { from: from.into(), to: to.into(), coins });
Ok(())
}
}
@@ -253,19 +233,19 @@ mod pallet {
/// Transfer `balance` from the signer to `to`.
#[pallet::call_index(0)]
#[pallet::weight((0, DispatchClass::Normal))] // TODO
pub fn transfer(origin: OriginFor<T>, to: Public, balance: Balance) -> DispatchResult {
pub fn transfer(origin: OriginFor<T>, to: Public, coins: Balance) -> DispatchResult {
let from = ensure_signed(origin)?;
Self::transfer_fn(from, to, balance)?;
Self::transfer_fn(from, to, coins)?;
Ok(())
}
/// Burn `balance` from the signer.
/// Burn `coins` from the signer.
#[pallet::call_index(1)]
#[pallet::weight((0, DispatchClass::Normal))] // TODO
pub fn burn(origin: OriginFor<T>, balance: Balance) -> DispatchResult {
pub fn burn(origin: OriginFor<T>, coins: Balance) -> DispatchResult {
let from = ensure_signed(origin)?;
Self::burn_internal(from, balance)?;
Self::deposit_event(Event::Burn { from, balance });
Self::burn_internal(from, coins)?;
Self::emit_event(Event::Burn { from: from.into(), coins });
Ok(())
}
@@ -283,7 +263,7 @@ mod pallet {
let from = ensure_signed(origin)?;
Self::burn_internal(from, instruction.balance.into())?;
Self::deposit_event(Event::BurnWithInstruction { from, instruction });
Self::emit_event(Event::BurnWithInstruction { from: from.into(), instruction });
Ok(())
}
}

View File

@@ -1,8 +1,8 @@
//! Test environment for Coins pallet.
use sp_runtime::BuildStorage;
use borsh::BorshDeserialize;
use frame_support::{derive_impl, construct_runtime};
use frame_support::{sp_runtime::BuildStorage, derive_impl, construct_runtime};
use crate::{self as coins, CoinsInstance};
@@ -10,6 +10,7 @@ construct_runtime!(
pub enum Test
{
System: frame_system,
Core: serai_core_pallet,
Coins: coins::<CoinsInstance>,
}
);
@@ -17,15 +18,28 @@ construct_runtime!(
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for Test {
type AccountId = sp_core::sr25519::Public;
type Lookup = sp_runtime::traits::IdentityLookup<Self::AccountId>;
type Lookup = frame_support::sp_runtime::traits::IdentityLookup<Self::AccountId>;
type Block = frame_system::mocking::MockBlock<Test>;
}
impl serai_core_pallet::Config for Test {}
impl crate::Config<CoinsInstance> for Test {
type RuntimeEvent = RuntimeEvent;
type AllowMint = crate::AlwaysAllowMint;
}
impl TryFrom<RuntimeEvent> for serai_abi::Event {
type Error = ();
fn try_from(event: RuntimeEvent) -> Result<serai_abi::Event, ()> {
match event {
RuntimeEvent::Core(serai_core_pallet::Event::Event(event)) => {
Ok(serai_abi::Event::deserialize_reader(&mut event.as_slice()).unwrap())
}
_ => Err(()),
}
}
}
pub(crate) fn new_test_ext() -> sp_io::TestExternalities {
let mut storage = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();

View File

@@ -1,15 +1,17 @@
use sp_core::{Pair as _, sr25519::Pair};
use frame_system::RawOrigin;
use serai_primitives::{coin::*, balance::*, address::*, instructions::*};
use serai_abi::primitives::{coin::*, balance::*, address::*, instructions::*};
use crate::mock::*;
pub type CoinsEvent = crate::Event<Test, crate::CoinsInstance>;
pub type CoinsEvent = serai_abi::coins::Event;
#[test]
fn mint() {
new_test_ext().execute_with(|| {
Core::start_transaction();
// minting u64::MAX should work
let coin = Coin::Serai;
let to = Pair::generate().0.public();
@@ -25,10 +27,10 @@ fn mint() {
assert_eq!(Coins::supply(coin), balance.amount);
// test events
let mint_events = System::events()
let mint_events = Core::events()
.iter()
.filter_map(|event| {
if let RuntimeEvent::Coins(e) = &event.event {
if let serai_abi::Event::Coins(e) = &event {
if matches!(e, CoinsEvent::Mint { .. }) {
Some(e.clone())
} else {
@@ -40,13 +42,15 @@ fn mint() {
})
.collect::<Vec<_>>();
assert_eq!(mint_events, vec![CoinsEvent::Mint { to, balance }]);
assert_eq!(mint_events, vec![CoinsEvent::Mint { to: to.into(), coins: balance }]);
})
}
#[test]
fn burn_with_instruction() {
new_test_ext().execute_with(|| {
Core::start_transaction();
// mint some coin
let coin = Coin::External(ExternalCoin::Bitcoin);
let to = Pair::generate().0.public();
@@ -76,10 +80,10 @@ fn burn_with_instruction() {
assert_eq!(Coins::balance(to, coin), Amount(0));
assert_eq!(Coins::supply(coin), Amount(0));
let burn_events = System::events()
let burn_events = Core::events()
.iter()
.filter_map(|event| {
if let RuntimeEvent::Coins(e) = &event.event {
if let serai_abi::Event::Coins(e) = &event {
if matches!(e, CoinsEvent::BurnWithInstruction { .. }) {
Some(e.clone())
} else {
@@ -91,13 +95,15 @@ fn burn_with_instruction() {
})
.collect::<Vec<_>>();
assert_eq!(burn_events, vec![CoinsEvent::BurnWithInstruction { from: to, instruction }]);
assert_eq!(burn_events, vec![CoinsEvent::BurnWithInstruction { from: to.into(), instruction }]);
})
}
#[test]
fn transfer() {
new_test_ext().execute_with(|| {
Core::start_transaction();
// mint some coin
let coin = Coin::External(ExternalCoin::Bitcoin);
let from = Pair::generate().0.public();

50
substrate/core/Cargo.toml Normal file
View File

@@ -0,0 +1,50 @@
[package]
name = "serai-core-pallet"
version = "0.1.0"
description = "Core pallet"
license = "AGPL-3.0-only"
repository = "https://github.com/serai-dex/serai/tree/develop/substrate/core"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
edition = "2021"
rust-version = "1.85"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[lints]
workspace = true
[dependencies]
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"] }
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 }
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 }
serai-abi = { path = "../abi", default-features = false, features = ["substrate"] }
[features]
std = [
"borsh/std",
"scale/std",
"sp-core/std",
"frame-system/std",
"frame-support/std",
"serai-abi/std",
]
runtime-benchmarks = [
"frame-system/runtime-benchmarks",
"frame-support/runtime-benchmarks",
]
try-runtime = ["serai-abi/try-runtime"]
default = ["std"]

15
substrate/core/LICENSE Normal file
View File

@@ -0,0 +1,15 @@
AGPL-3.0-only license
Copyright (c) 2023-2025 Luke Parker
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License Version 3 as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

1
substrate/core/README.md Normal file
View File

@@ -0,0 +1 @@
# Serai Core Pallet

View File

@@ -0,0 +1,52 @@
use core::marker::PhantomData;
use borsh::BorshSerialize;
use serai_abi::primitives::merkle::{UnbalancedMerkleTree, IncrementalUnbalancedMerkleTree as Iumt};
/// A wrapper around a `StorageValue` which offers a high-level API as an
/// `IncrementalUnbalancedMerkleTree`.
pub struct IncrementalUnbalancedMerkleTree<
T: frame_support::StorageValue<Iumt, Query = Option<Iumt>>,
const BRANCH_TAG: u8 = 1,
const LEAF_TAG: u8 = 0,
>(PhantomData<T>);
impl<
T: frame_support::StorageValue<Iumt, Query = Option<Iumt>>,
const BRANCH_TAG: u8,
const LEAF_TAG: u8,
> IncrementalUnbalancedMerkleTree<T, BRANCH_TAG, LEAF_TAG>
{
/// Create a new Merkle tree, expecting there to be none already present.
///
/// Panics if a Merkle tree was already present.
pub fn new_expecting_none() {
T::mutate(|value| {
assert!(value.is_none());
*value = Some(Iumt::new());
});
}
/// Append a leaf to the Merkle tree.
///
/// Panics if no Merkle tree was present.
pub fn append<L: BorshSerialize>(leaf: &L) {
let leaf = sp_core::blake2_256(&borsh::to_vec(&(LEAF_TAG, leaf)).unwrap());
T::mutate(|value| {
let tree = value.as_mut().unwrap();
tree.append(BRANCH_TAG, leaf);
})
}
/// Get the unbalanced merkle tree.
///
/// Panics if no Merkle tree was present.
pub fn get() -> UnbalancedMerkleTree {
T::get().unwrap().calculate(BRANCH_TAG)
}
/// Take the Merkle tree.
///
/// Panics if no Merkle tree was present.
pub fn take() -> UnbalancedMerkleTree {
T::mutate(|value| value.take().unwrap().calculate(BRANCH_TAG))
}
}

179
substrate/core/src/lib.rs Normal file
View File

@@ -0,0 +1,179 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc = include_str!("../README.md")]
#![deny(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
use core::marker::PhantomData;
extern crate alloc;
mod iumt;
pub use iumt::*;
#[expect(clippy::cast_possible_truncation)]
#[frame_support::pallet]
pub mod pallet {
use alloc::vec::Vec;
use frame_support::pallet_prelude::*;
use serai_abi::primitives::{prelude::*, merkle::IncrementalUnbalancedMerkleTree as Iumt};
use super::*;
/// The set of all blocks prior added to the blockchain.
#[pallet::storage]
pub(super) type Blocks<T: Config> = StorageMap<_, Identity, T::Hash, (), OptionQuery>;
/// The Merkle tree of all blocks added to the blockchain.
#[pallet::storage]
#[pallet::unbounded]
pub(super) type BlocksCommitment<T: Config> = StorageValue<_, Iumt, OptionQuery>;
pub(super) type BlocksCommitmentMerkle<T> = IncrementalUnbalancedMerkleTree<BlocksCommitment<T>>;
/// The Merkle tree of all transactions within the current block.
#[pallet::storage]
#[pallet::unbounded]
pub(super) type BlockTransactionsCommitment<T: Config> = StorageValue<_, Iumt, OptionQuery>;
pub(super) type BlockTransactionsCommitmentMerkle<T> =
IncrementalUnbalancedMerkleTree<BlockTransactionsCommitment<T>>;
/// The hashes of events caused by the current transaction.
#[pallet::storage]
#[pallet::unbounded]
pub(super) type TransactionEvents<T: Config> = StorageValue<_, Iumt, OptionQuery>;
pub(super) type TransactionEventsMerkle<T> = IncrementalUnbalancedMerkleTree<
TransactionEvents<T>,
{ serai_abi::TRANSACTION_EVENTS_COMMITMENT_BRANCH_TAG },
{ serai_abi::TRANSACTION_EVENTS_COMMITMENT_LEAF_TAG },
>;
/// The roots of the Merkle trees of each transaction's events.
#[pallet::storage]
#[pallet::unbounded]
pub(super) type BlockEventsCommitment<T: Config> = StorageValue<_, Iumt, OptionQuery>;
pub(super) type BlockEventsCommitmentMerkle<T> = IncrementalUnbalancedMerkleTree<
BlockEventsCommitment<T>,
{ serai_abi::EVENTS_COMMITMENT_BRANCH_TAG },
{ serai_abi::EVENTS_COMMITMENT_LEAF_TAG },
>;
/// A mapping from an account to its next nonce.
#[pallet::storage]
type NextNonce<T: Config> = StorageMap<_, Blake2_128Concat, SeraiAddress, T::Nonce, ValueQuery>;
/// Mapping from Serai's events to Substrate's.
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// An event from Serai.
Event(Vec<u8>),
}
#[pallet::config]
pub trait Config: frame_system::Config<Hash: Into<[u8; 32]>> {}
#[pallet::pallet]
pub struct Pallet<T>(_);
impl<T: Config> Pallet<T> {
/// If a block exists on the current blockchain.
#[must_use]
pub fn block_exists(hash: impl scale::EncodeLike<T::Hash>) -> bool {
Blocks::<T>::contains_key(hash)
}
/// The next nonce for an account.
#[must_use]
pub fn next_nonce(account: &SeraiAddress) -> T::Nonce {
NextNonce::<T>::get(account)
}
/// Consume the next nonce for an account.
///
/// Panics if the current nonce is `<_>::MAX`.
pub fn consume_next_nonce(signer: &SeraiAddress) {
NextNonce::<T>::mutate(signer, |value| {
*value = value
.checked_add(&T::Nonce::one())
.expect("`consume_next_nonce` called when current nonce is <_>::MAX")
});
}
/// The code to run when beginning execution of a transaction.
///
/// The caller MUST ensure two transactions aren't simultaneously started.
pub fn start_transaction() {
TransactionEventsMerkle::<T>::new_expecting_none();
}
/// Emit an event.
pub fn emit_event(event: impl Into<serai_abi::Event>) {
let event = event.into();
TransactionEventsMerkle::<T>::append(&event);
Self::deposit_event(Event::Event(borsh::to_vec(&event).unwrap()));
}
/// End execution of a transaction.
pub fn end_transaction(transaction_hash: [u8; 32]) {
BlockTransactionsCommitmentMerkle::<T>::append(&transaction_hash);
let transaction_events_root = TransactionEventsMerkle::<T>::take().root;
// Append the leaf (the transaction's hash and its events' root) to the block's events'
// commitment
BlockEventsCommitmentMerkle::<T>::append(&(&transaction_hash, &transaction_events_root));
}
/// Fetch all of Serai's events.
///
/// This MUST only be used for testing purposes.
pub fn events() -> Vec<serai_abi::Event>
where
serai_abi::Event: TryFrom<T::RuntimeEvent>,
{
frame_system::Pallet::<T>::events()
.into_iter()
.filter_map(|e| serai_abi::Event::try_from(e.event).ok())
.collect()
}
}
}
pub use pallet::*;
/// The code to run at the start of a block for this pallet.
pub struct StartOfBlock<T: Config>(PhantomData<T>);
impl<T: Config> frame_support::traits::PreInherents for StartOfBlock<T> {
fn pre_inherents() {
use frame_support::pallet_prelude::Zero;
if frame_system::Pallet::<T>::block_number().is_zero() {
BlocksCommitmentMerkle::<T>::new_expecting_none();
} else {
let parent_hash = frame_system::Pallet::<T>::parent_hash();
Blocks::<T>::set(parent_hash, Some(()));
let parent_hash: [u8; 32] = parent_hash.into();
BlocksCommitmentMerkle::<T>::append(&parent_hash);
}
BlockTransactionsCommitmentMerkle::<T>::new_expecting_none();
BlockEventsCommitmentMerkle::<T>::new_expecting_none();
}
}
/// The code to run at the end of a block for this pallet.
pub struct EndOfBlock<T: Config>(PhantomData<T>);
impl<T: Config> frame_support::traits::PostTransactions for EndOfBlock<T> {
fn post_transactions() {
use serai_abi::SeraiExecutionDigest;
frame_system::Pallet::<T>::deposit_log(
frame_support::sp_runtime::generic::DigestItem::Consensus(
SeraiExecutionDigest::CONSENSUS_ID,
borsh::to_vec(&SeraiExecutionDigest {
builds_upon: BlocksCommitmentMerkle::<T>::get(),
transactions_commitment: BlockTransactionsCommitmentMerkle::<T>::take(),
events_commitment: BlockEventsCommitmentMerkle::<T>::take(),
})
.unwrap(),
),
);
}
}

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 = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", 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 }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
frame-benchmarking = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false, optional = true }
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 }
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 = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", 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 }
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 = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
pallet-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
pallet-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", 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-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", 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 = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-consensus-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", 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 }
[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 = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", 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 }
sp-std = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", 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 }
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 = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", 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 }
sp-std = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-application-crypto = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", 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 }
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 = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-application-crypto = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
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 }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", 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 }
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 = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
pallet-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
pallet-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", 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-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", 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 = "d4624c561765c13b38eb566e435131a8c329a543" }
sp-keystore = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543" }
sp-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543" }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543" }
sp-blockchain = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543" }
sp-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543" }
sp-block-builder = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543" }
sp-consensus-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543" }
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" }
frame-benchmarking = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543" }
frame-benchmarking = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7" }
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 = "d4624c561765c13b38eb566e435131a8c329a543" }
sc-transaction-pool-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543" }
sc-basic-authorship = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543" }
sc-executor = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543" }
sc-service = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543" }
sc-client-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543" }
sc-network-common = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543" }
sc-network = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
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-consensus = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543" }
sc-consensus-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543" }
sc-consensus-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543" }
sc-authority-discovery = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543" }
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-telemetry = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543" }
sc-cli = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false, features = ["rocksdb"] }
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-rpc-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543" }
sc-rpc-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7" }
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 = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["alloc"] }
schnorr-signatures = { path = "../../crypto/schnorr", default-features = false }

View File

@@ -1,4 +1,4 @@
use alloc::vec::Vec;
use alloc::{vec, vec::Vec};
use zeroize::Zeroize;
use borsh::{BorshSerialize, BorshDeserialize};
@@ -6,8 +6,10 @@ use borsh::{BorshSerialize, BorshDeserialize};
use ciphersuite::{group::GroupEncoding, GroupIo};
use dalek_ff_group::Ristretto;
use sp_core::sr25519::Public;
use crate::{
crypto::{Public, KeyPair},
crypto::KeyPair,
network_id::{ExternalNetworkId, NetworkId},
balance::Amount,
};
@@ -86,15 +88,17 @@ impl ExternalValidatorSet {
/// The MuSig public key for a validator set.
///
/// This function panics on invalid input, per the definition of `dkg::musig::musig_key`.
pub fn musig_key(&self, set_keys: &[Public]) -> Public {
let mut keys = Vec::new();
for key in set_keys {
keys.push(
<Ristretto as GroupIo>::read_G::<&[u8]>(&mut key.0.as_ref()).expect("invalid participant"),
/// This function panics on invalid points as keys and on invalid input, per the definition of
/// `dkg::musig::musig_key`.
pub fn musig_key(&self, keys: &[Public]) -> Public {
let mut decompressed_keys = vec![];
for key in keys {
decompressed_keys.push(
<Ristretto as GroupIo>::read_G::<&[u8]>(&mut key.0.as_slice())
.expect("invalid participant"),
);
}
Public(dkg::musig_key::<Ristretto>(self.musig_context(), &keys).unwrap().to_bytes())
dkg::musig_key::<Ristretto>(self.musig_context(), &decompressed_keys).unwrap().to_bytes().into()
}
/// The message for the `set_keys` signature.
@@ -150,7 +154,7 @@ impl KeyShares {
/// Reduction occurs by reducing each validator in a reverse round-robin. This means the
/// validators with the least key shares are evicted first.
#[must_use]
pub fn amortize_excess(validators: &mut [(sp_core::sr25519::Public, KeyShares)]) -> usize {
pub fn amortize_excess(validators: &mut [(Public, KeyShares)]) -> usize {
let total_key_shares = validators.iter().map(|(_key, shares)| shares.0).sum::<u16>();
let mut actual_len = validators.len();
let mut offset = 1;

View File

@@ -18,27 +18,26 @@ ignored = ["scale"]
workspace = true
[dependencies]
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"] }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-version = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
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 }
serai-abi = { path = "../abi", default-features = false, features = ["substrate"] }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
frame-executive = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", 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-executive = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7", default-features = false }
serai-core-pallet = { path = "../core", default-features = false }
serai-coins-pallet = { path = "../coins", default-features = false }
serai-validator-sets-pallet = { path = "../validator-sets", default-features = false }
serai-signals-pallet = { path = "../signals", default-features = false }
[build-dependencies]
substrate-wasm-builder = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543" }
substrate-wasm-builder = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "16336c737dbe833e9d138a256af99698aba637c7" }
[features]
std = [
@@ -55,6 +54,7 @@ std = [
"frame-support/std",
"frame-executive/std",
"serai-core-pallet/std",
"serai-coins-pallet/std",
"serai-validator-sets-pallet/std",
"serai-signals-pallet/std",
@@ -69,6 +69,7 @@ try-runtime = [
"frame-support/try-runtime",
"frame-executive/try-runtime",
"serai-core-pallet/try-runtime",
"serai-coins-pallet/try-runtime",
"serai-validator-sets-pallet/try-runtime",
"serai-signals-pallet/try-runtime",
@@ -80,6 +81,8 @@ runtime-benchmarks = [
"frame-system/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"serai-core-pallet/runtime-benchmarks",
"serai-coins-pallet/runtime-benchmarks",
"serai-validator-sets-pallet/runtime-benchmarks",
"serai-signals-pallet/runtime-benchmarks",
]

View File

@@ -1,161 +0,0 @@
use core::marker::PhantomData;
use borsh::BorshSerialize;
use frame_support::pallet_prelude::*;
use serai_abi::{
primitives::merkle::{UnbalancedMerkleTree, IncrementalUnbalancedMerkleTree as Iumt},
*,
};
/// A wrapper around a `StorageValue` which offers a high-level API as an IUMT.
struct IncrementalUnbalancedMerkleTree<
T: frame_support::StorageValue<Iumt, Query = Option<Iumt>>,
const BRANCH_TAG: u8 = 1,
const LEAF_TAG: u8 = 0,
>(PhantomData<T>);
impl<
T: frame_support::StorageValue<Iumt, Query = Option<Iumt>>,
const BRANCH_TAG: u8,
const LEAF_TAG: u8,
> IncrementalUnbalancedMerkleTree<T, BRANCH_TAG, LEAF_TAG>
{
/// Create a new Merkle tree, expecting there to be none already present.
///
/// Panics if a Merkle tree was already present.
fn new_expecting_none() {
T::mutate(|value| {
assert!(value.is_none());
*value = Some(Iumt::new());
});
}
/// Append a leaf to the Merkle tree.
///
/// Panics if no Merkle tree was present.
fn append<L: BorshSerialize>(leaf: &L) {
let leaf = sp_core::blake2_256(&borsh::to_vec(&(LEAF_TAG, leaf)).unwrap());
T::mutate(|value| {
let tree = value.as_mut().unwrap();
tree.append(BRANCH_TAG, leaf);
})
}
/// Get the unbalanced merkle tree.
///
/// Panics if no Merkle tree was present.
fn get() -> UnbalancedMerkleTree {
T::get().unwrap().calculate(BRANCH_TAG)
}
/// Take the Merkle tree.
///
/// Panics if no Merkle tree was present.
fn take() -> UnbalancedMerkleTree {
T::mutate(|value| value.take().unwrap().calculate(BRANCH_TAG))
}
}
#[frame_support::pallet]
mod pallet {
use super::*;
/// The set of all blocks prior added to the blockchain.
#[pallet::storage]
pub type Blocks<T: Config> = StorageMap<_, Identity, T::Hash, (), OptionQuery>;
/// The Merkle tree of all blocks added to the blockchain.
#[pallet::storage]
#[pallet::unbounded]
pub(super) type BlocksCommitment<T: Config> = StorageValue<_, Iumt, OptionQuery>;
pub(super) type BlocksCommitmentMerkle<T> = IncrementalUnbalancedMerkleTree<BlocksCommitment<T>>;
/// The Merkle tree of all transactions within the current block.
#[pallet::storage]
#[pallet::unbounded]
pub(super) type BlockTransactionsCommitment<T: Config> = StorageValue<_, Iumt, OptionQuery>;
pub(super) type BlockTransactionsCommitmentMerkle<T> =
IncrementalUnbalancedMerkleTree<BlockTransactionsCommitment<T>>;
/// The hashes of events caused by the current transaction.
#[pallet::storage]
#[pallet::unbounded]
pub(super) type TransactionEvents<T: Config> = StorageValue<_, Iumt, OptionQuery>;
pub(super) type TransactionEventsMerkle<T> = IncrementalUnbalancedMerkleTree<
TransactionEvents<T>,
TRANSACTION_EVENTS_COMMITMENT_BRANCH_TAG,
TRANSACTION_EVENTS_COMMITMENT_LEAF_TAG,
>;
/// The roots of the Merkle trees of each transaction's events.
#[pallet::storage]
#[pallet::unbounded]
pub(super) type BlockEventsCommitment<T: Config> = StorageValue<_, Iumt, OptionQuery>;
pub(super) type BlockEventsCommitmentMerkle<T> = IncrementalUnbalancedMerkleTree<
BlockEventsCommitment<T>,
EVENTS_COMMITMENT_BRANCH_TAG,
EVENTS_COMMITMENT_LEAF_TAG,
>;
/// A mapping from an account to its next nonce.
#[pallet::storage]
pub type NextNonce<T: Config> =
StorageMap<_, Blake2_128Concat, T::AccountId, T::Nonce, ValueQuery>;
#[pallet::config]
pub trait Config: frame_system::Config<Hash: Into<[u8; 32]>> {}
#[pallet::pallet]
pub struct Pallet<T>(_);
impl<T: Config> Pallet<T> {
pub fn start_transaction() {
TransactionEventsMerkle::<T>::new_expecting_none();
}
// TODO: Have this called
pub fn on_event(event: impl TryInto<serai_abi::Event>) {
if let Ok(event) = event.try_into() {
TransactionEventsMerkle::<T>::append(&event);
}
}
pub fn end_transaction(transaction_hash: [u8; 32]) {
BlockTransactionsCommitmentMerkle::<T>::append(&transaction_hash);
let transaction_events_root = TransactionEventsMerkle::<T>::take().root;
// Append the leaf (the transaction's hash and its events' root) to the block's events'
// commitment
BlockEventsCommitmentMerkle::<T>::append(&(&transaction_hash, &transaction_events_root));
}
}
}
pub(super) use pallet::*;
pub struct StartOfBlock<T: Config>(PhantomData<T>);
impl<T: Config> frame_support::traits::PreInherents for StartOfBlock<T> {
fn pre_inherents() {
if frame_system::Pallet::<T>::block_number().is_zero() {
BlocksCommitmentMerkle::<T>::new_expecting_none();
} else {
let parent_hash = frame_system::Pallet::<T>::parent_hash();
Blocks::<T>::set(parent_hash, Some(()));
let parent_hash: [u8; 32] = parent_hash.into();
BlocksCommitmentMerkle::<T>::append(&parent_hash);
}
BlockTransactionsCommitmentMerkle::<T>::new_expecting_none();
BlockEventsCommitmentMerkle::<T>::new_expecting_none();
}
}
pub struct EndOfBlock<T: Config>(PhantomData<T>);
impl<T: Config> frame_support::traits::PostTransactions for EndOfBlock<T> {
fn post_transactions() {
frame_system::Pallet::<T>::deposit_log(sp_runtime::generic::DigestItem::Consensus(
SeraiExecutionDigest::CONSENSUS_ID,
borsh::to_vec(&SeraiExecutionDigest {
builds_upon: BlocksCommitmentMerkle::<T>::get(),
transactions_commitment: BlockTransactionsCommitmentMerkle::<T>::take(),
events_commitment: BlockEventsCommitmentMerkle::<T>::take(),
})
.unwrap(),
));
}
}

View File

@@ -9,7 +9,7 @@ extern crate alloc;
use alloc::borrow::Cow;
use sp_core::sr25519::Public;
use sp_runtime::{Perbill, Weight, traits::Header as _};
use sp_runtime::{Perbill, Weight};
use sp_version::RuntimeVersion;
#[rustfmt::skip]
@@ -19,8 +19,6 @@ use serai_abi::{
use serai_coins_pallet::{CoinsInstance, LiquidityTokensInstance};
mod core_pallet;
type Block = SubstrateBlock;
/// The lookup for a SeraiAddress -> Public.
@@ -75,7 +73,7 @@ mod runtime {
pub type System = frame_system::Pallet<Runtime>;
#[runtime::pallet_index(1)]
pub type Core = core_pallet::Pallet<Runtime>;
pub type Core = serai_core_pallet::Pallet<Runtime>;
#[runtime::pallet_index(2)]
pub type Coins = serai_coins_pallet::Pallet<Runtime, CoinsInstance>;
@@ -125,12 +123,12 @@ impl frame_system::Config for Runtime {
type SingleBlockMigrations = ();
type MultiBlockMigrator = ();
type PreInherents = core_pallet::StartOfBlock<Runtime>;
type PreInherents = serai_core_pallet::StartOfBlock<Runtime>;
type PostInherents = ();
type PostTransactions = core_pallet::EndOfBlock<Runtime>;
type PostTransactions = serai_core_pallet::EndOfBlock<Runtime>;
}
impl core_pallet::Config for Runtime {}
impl serai_core_pallet::Config for Runtime {}
impl serai_coins_pallet::Config<CoinsInstance> for Runtime {
type AllowMint = serai_coins_pallet::AlwaysAllowMint; // TODO
@@ -138,7 +136,9 @@ impl serai_coins_pallet::Config<CoinsInstance> for Runtime {
impl serai_coins_pallet::Config<LiquidityTokensInstance> for Runtime {
type AllowMint = serai_coins_pallet::AlwaysAllowMint;
}
impl serai_validator_sets_pallet::Config for Runtime {}
impl serai_validator_sets_pallet::Config for Runtime {
type ShouldEndSession = Babe;
}
impl serai_signals_pallet::Config for Runtime {
type RetirementValidityDuration = sp_core::ConstU64<0>; // TODO
type RetirementLockInDuration = sp_core::ConstU64<0>; // TODO
@@ -160,11 +160,9 @@ impl From<serai_abi::Call> for RuntimeCall {
use serai_abi::coins::Call;
match call {
Call::transfer { to, coins } => {
RuntimeCall::Coins(serai_coins_pallet::Call::transfer { to: to.into(), balance: coins })
}
Call::burn { coins } => {
RuntimeCall::Coins(serai_coins_pallet::Call::burn { balance: coins })
RuntimeCall::Coins(serai_coins_pallet::Call::transfer { to: to.into(), coins })
}
Call::burn { coins } => RuntimeCall::Coins(serai_coins_pallet::Call::burn { coins }),
Call::burn_with_instruction { instruction } => {
RuntimeCall::Coins(serai_coins_pallet::Call::burn_with_instruction { instruction })
}
@@ -197,7 +195,7 @@ impl From<serai_abi::Call> for RuntimeCall {
Call::transfer_liquidity { to, liquidity_tokens } => {
RuntimeCall::LiquidityTokens(serai_coins_pallet::Call::transfer {
to: to.into(),
balance: liquidity_tokens.into(),
coins: liquidity_tokens.into(),
})
}
Call::add_liquidity { .. } |
@@ -230,7 +228,6 @@ sp_api::impl_runtime_apis! {
VERSION
}
fn initialize_block(header: &Header) -> sp_runtime::ExtrinsicInclusionMode {
core_pallet::Blocks::<Runtime>::set(header.parent_hash(), Some(()));
Executive::initialize_block(header)
}
fn execute_block(block: Block) {
@@ -257,7 +254,7 @@ impl serai_abi::TransactionContext for Context {
/// If a block is present in the blockchain.
fn block_is_present_in_blockchain(&self, hash: &serai_abi::primitives::BlockHash) -> bool {
core_pallet::Blocks::<Runtime>::get(hash).is_some()
serai_core_pallet::Pallet::<Runtime>::block_exists(hash)
}
/// The time embedded into the current block.
fn current_time(&self) -> Option<u64> {
@@ -265,7 +262,7 @@ impl serai_abi::TransactionContext for Context {
}
/// Get the next nonce for an account.
fn next_nonce(&self, signer: &SeraiAddress) -> u32 {
core_pallet::NextNonce::<Runtime>::get(signer)
serai_core_pallet::Pallet::<Runtime>::next_nonce(signer)
}
/// If the signer can pay the SRI fee.
fn can_pay_fee(
@@ -284,11 +281,10 @@ impl serai_abi::TransactionContext for Context {
}
fn start_transaction(&self) {
Core::start_transaction();
Core::start_transaction()
}
/// Consume the next nonce for an account.
fn consume_next_nonce(&self, signer: &SeraiAddress) {
core_pallet::NextNonce::<Runtime>::mutate(signer, |value| *value += 1);
serai_core_pallet::Pallet::<Runtime>::consume_next_nonce(signer)
}
/// Have the transaction pay its SRI fee.
fn pay_fee(
@@ -430,28 +426,11 @@ impl timestamp::Config for Runtime {
type WeightInfo = ();
}
impl transaction_payment::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type OnChargeTransaction = Coins;
type OperationalFeeMultiplier = ConstU8<5>;
type WeightToFee = IdentityFee<SubstrateAmount>;
type LengthToFee = IdentityFee<SubstrateAmount>;
type FeeMultiplierUpdate = ();
}
impl coins::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type AllowMint = ValidatorSets;
}
impl coins::Config<coins::Instance1> for Runtime {
type RuntimeEvent = RuntimeEvent;
type AllowMint = ();
}
impl dex::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type LPFee = ConstU32<3>; // 0.3%
type MintMinLiquidity = ConstU64<10000>;
@@ -462,12 +441,6 @@ impl dex::Config for Runtime {
type WeightInfo = dex::weights::SubstrateWeight<Runtime>;
}
impl validator_sets::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type ShouldEndSession = Babe;
}
pub struct IdentityValidatorIdOf;
impl Convert<PublicKey, Option<PublicKey>> for IdentityValidatorIdOf {
fn convert(key: PublicKey) -> Option<PublicKey> {

View File

@@ -21,37 +21,47 @@ 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 = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-core = { 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 = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", 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 }
serai-abi = { path = "../abi", default-features = false, features = ["substrate"] }
validator-sets-pallet = { package = "serai-validator-sets-pallet", path = "../validator-sets", default-features = false }
serai-core-pallet = { path = "../core", default-features = false }
serai-validator-sets-pallet = { path = "../validator-sets", default-features = false }
[features]
std = [
"scale/std",
"sp-core/std",
"sp-io/std",
"frame-system/std",
"frame-support/std",
"serai-abi/std",
"validator-sets-pallet/std",
"serai-core-pallet/std",
"serai-validator-sets-pallet/std",
]
runtime-benchmarks = [
"frame-system/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"serai-core-pallet/runtime-benchmarks",
"serai-validator-sets-pallet/runtime-benchmarks",
]
# TODO
try-runtime = []
try-runtime = [
"frame-system/try-runtime",
"frame-support/try-runtime",
"serai-abi/try-runtime",
"serai-core-pallet/try-runtime",
"serai-validator-sets-pallet/try-runtime",
]
default = ["std"]

View File

@@ -12,17 +12,19 @@ pub mod pallet {
use serai_abi::{
primitives::{prelude::*, signals::*},
signals::Event,
SubstrateBlock,
};
use frame_system::pallet_prelude::*;
use frame_support::pallet_prelude::*;
use validator_sets_pallet::{Config as VsConfig, Pallet as VsPallet};
use serai_validator_sets_pallet::{Config as VsConfig, Pallet as VsPallet};
use serai_core_pallet::{Config as CoreConfig, Pallet as Core};
#[pallet::config]
pub trait Config:
frame_system::Config<AccountId = Public, Block = SubstrateBlock> + VsConfig
frame_system::Config<AccountId = Public, Block = SubstrateBlock> + VsConfig + CoreConfig
{
/// How long a candidate retirement signal is valid for.
///
@@ -172,12 +174,12 @@ pub mod pallet {
let prior_in_favor = NetworksInFavor::<T>::contains_key((signal, network));
NetworksInFavor::<T>::set((signal, network), Some(()));
if !prior_in_favor {
todo!("Event");
Core::<T>::emit_event(Event::NetworkInFavor { signal, network });
}
} else {
#[allow(clippy::collapsible_else_if)]
if NetworksInFavor::<T>::take((signal, network)).is_some() {
todo!("Event");
Core::<T>::emit_event(Event::NetworkNoLongerInFavor { signal, network });
}
}
@@ -225,7 +227,11 @@ pub mod pallet {
Err::<(), _>(Error::<T>::RevokingNonExistentFavor)?;
}
Favors::<T>::remove((signal, for_network), validator);
// TODO: Event
Core::<T>::emit_event(Event::FavorRevoked {
signal,
by: validator.into(),
with_network: for_network,
});
// Update the tally for this network
Self::tally_for_network(signal, for_network);
@@ -278,9 +284,10 @@ pub mod pallet {
This prevents a malicious actor from frontrunning a proposal, causing them to be the
registrant, just to cancel it later.
*/
let registrant = SeraiAddress::from(validator);
let signal = RegisteredRetirementSignal {
in_favor_of,
registrant: validator.into(),
registrant,
registered_at: frame_system::Pallet::<T>::block_number(),
};
let signal_id = signal.id();
@@ -290,7 +297,12 @@ pub mod pallet {
}
RegisteredRetirementSignals::<T>::set(signal_id, Some(signal));
// TODO: Event
Core::<T>::emit_event(Event::RetirementSignalRegistered {
signal: signal_id,
in_favor_of,
registrant,
});
Ok(())
}
@@ -322,7 +334,8 @@ pub mod pallet {
LockedInRetirement::<T>::kill();
}
// TODO: Event
Core::<T>::emit_event(Event::RetirementSignalRevoked { signal: retirement_signal });
Ok(())
}
@@ -366,7 +379,12 @@ pub mod pallet {
// Set the validator as in favor
Favors::<T>::set((signal, for_network), validator, Some(()));
// TODO: Event
Core::<T>::emit_event(Event::SignalFavored {
signal,
by: validator.into(),
with_network: for_network,
});
// Check if the network is in favor
let network_in_favor = Self::tally_for_network(signal, for_network);
@@ -380,11 +398,11 @@ pub mod pallet {
signal_id,
frame_system::Pallet::<T>::block_number() + T::RetirementLockInDuration::get(),
)));
// TODO: Event
Core::<T>::emit_event(Event::RetirementSignalLockedIn { signal: signal_id });
}
Signal::Halt(network) => {
Halted::<T>::set(network, Some(()));
// TODO: Event
Core::<T>::emit_event(Event::NetworkHalted { network });
}
}
}
@@ -451,53 +469,15 @@ pub mod pallet {
}
}
// Emit the event
// TODO: Event
Core::<T>::emit_event(Event::AgainstSignal {
signal,
account: validator.into(),
with_network: for_network,
});
Ok(())
}
}
/* TODO
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
RetirementSignalRegistered {
signal: [u8; 32],
in_favor_of: [u8; 32],
registrant: T::AccountId,
},
RetirementSignalRevoked {
signal_id: [u8; 32],
},
SignalFavored {
signal_id: Signal,
by: T::AccountId,
for_network: NetworkId,
},
SetInFavor {
signal_id: Signal,
set: ValidatorSet,
},
RetirementSignalLockedIn {
signal_id: [u8; 32],
},
SetNoLongerInFavor {
signal_id: Signal,
set: ValidatorSet,
},
FavorRevoked {
signal_id: Signal,
by: T::AccountId,
for_network: NetworkId,
},
AgainstSignal {
signal_id: Signal,
who: T::AccountId,
for_network: NetworkId,
},
}
*/
}
pub use pallet::*;

View File

@@ -20,32 +20,27 @@ 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 = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
sp-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", 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-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 }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", 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 }
serai-primitives = { path = "../primitives", default-features = false, features = ["non_canonical_scale_derivations"] }
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 }
coins-pallet = { package = "serai-coins-pallet", path = "../coins", default-features = false }
serai-abi = { path = "../abi", default-features = false, features = ["substrate"] }
serai-coins-pallet = { path = "../coins", default-features = false }
[dev-dependencies]
#pallet-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
#sp-consensus-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "d4624c561765c13b38eb566e435131a8c329a543", default-features = false }
#ciphersuite = { path = "../../../crypto/ciphersuite", default-features = false, features = ["std"] }
#dalek-ff-group = { path = "../../../crypto/dalek-ff-group", default-features = false, features = ["std"] }
#dkg-musig = { path = "../../../crypto/dkg/musig", default-features = false, features = ["std"] }
#frost = { package = "modular-frost", path = "../../../crypto/frost", features = ["tests"] }
#schnorrkel = { path = "../../../crypto/schnorrkel", package = "frost-schnorrkel" }
zeroize = "^1.5"
rand_core = "0.6"
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"] }
[features]
@@ -55,28 +50,39 @@ std = [
"scale/std",
"sp-core/std",
"sp-application-crypto/std",
"sp-io/std",
"sp-runtime/std",
"sp-api/std",
"frame-system/std",
"frame-support/std",
"serai-primitives/std",
"pallet-session/std",
"pallet-babe/std",
"pallet-grandpa/std",
"coins-pallet/std",
"serai-abi/std",
"serai-coins-pallet/std",
]
try-runtime = [
"serai-abi/try-runtime",
"frame-system/try-runtime",
"frame-support/try-runtime",
"sp-runtime/try-runtime",
"pallet-session/try-runtime",
"pallet-babe/try-runtime",
"pallet-grandpa/try-runtime",
]
runtime-benchmarks = [
"frame-system/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"pallet-babe/runtime-benchmarks",
"pallet-grandpa/runtime-benchmarks",
]
default = ["std"]

View File

@@ -1,6 +1,6 @@
use sp_core::{Encode, sr25519::Public};
use serai_primitives::{network_id::NetworkId, balance::Amount, validator_sets::KeyShares};
use serai_abi::primitives::{network_id::NetworkId, balance::Amount, validator_sets::KeyShares};
use frame_support::storage::{StorageMap, StoragePrefixedMap};
@@ -14,11 +14,16 @@ pub(crate) type SortedAllocationsKey = (NetworkId, [u8; 8], [u8; 16], Public);
/// This storage is expected to be owned by the `Allocations` interface and not directly read/write
/// to.
pub(crate) trait AllocationsStorage {
/// An opaque map storing allocations.
/// An map storing allocations.
///
/// This is opaque and to be exclusively read/write by `Allocations`.
type Allocations: StorageMap<AllocationsKey, Amount, Query = Option<Amount>>;
/// An opaque map storing allocations in a sorted manner.
/// An map storing allocations in a sorted manner.
///
/// This MUST be instantiated with a map using `Identity` for its hasher.
///
/// This is opaque and to be exclusively read/write by `Allocations`.
/*
This is premised on the underlying trie iterating from keys with low-bytes to keys with
high-bytes.

View File

@@ -1,16 +1,18 @@
use sp_core::sr25519::Public;
use serai_primitives::{crypto::SignedEmbeddedEllipticCurveKeys, network_id::*};
use serai_abi::primitives::{crypto::SignedEmbeddedEllipticCurveKeys, network_id::*};
use frame_support::storage::StorageDoubleMap;
pub(crate) trait EmbeddedEllipticCurveKeysStorage {
/// An opaque map storing keys on an embedded elliptic curve.
/// An map storing keys on an embedded elliptic curve.
///
/// This is opaque and to be exclusively read/write by `EmbeddedEllipticCurveKeys`.
type EmbeddedEllipticCurveKeys: StorageDoubleMap<
ExternalNetworkId,
Public,
serai_primitives::crypto::EmbeddedEllipticCurveKeys,
Query = Option<serai_primitives::crypto::EmbeddedEllipticCurveKeys>,
serai_abi::primitives::crypto::EmbeddedEllipticCurveKeys,
Query = Option<serai_abi::primitives::crypto::EmbeddedEllipticCurveKeys>,
>;
}

View File

@@ -0,0 +1,49 @@
use sp_core::sr25519::Public;
use serai_abi::primitives::{
crypto::{ExternalKey, KeyPair},
validator_sets::ExternalValidatorSet,
};
use frame_support::storage::StorageMap;
pub(crate) trait KeysStorage {
/// An map storing keys validator sets use for oraclization.
///
/// This is opaque and to be exclusively read/write by `Keys`.
type OraclizationKeys: StorageMap<ExternalValidatorSet, Public, Query = Option<Public>>;
/// An map storing keys validator sets use for interacting with external networks.
///
/// This is opaque and to be exclusively read/write by `Keys`.
type ExternalKeys: StorageMap<ExternalValidatorSet, ExternalKey, Query = Option<ExternalKey>>;
}
/// An interface for managing validators' embedded elliptic curve keys.
pub(crate) trait Keys {
/// If a validator set has yet to set keys.
#[must_use]
fn needs_to_set_keys(set: ExternalValidatorSet) -> bool;
/// Set the pair of keys for an external network.
fn set_keys(set: ExternalValidatorSet, key_pair: KeyPair);
/// Clear a historic set of keys.
fn clear_keys(set: ExternalValidatorSet);
}
impl<S: KeysStorage> Keys for S {
fn needs_to_set_keys(set: ExternalValidatorSet) -> bool {
S::OraclizationKeys::contains_key(set)
}
fn set_keys(set: ExternalValidatorSet, key_pair: KeyPair) {
S::OraclizationKeys::insert(set, Public::from(key_pair.0 .0));
S::ExternalKeys::insert(set, key_pair.1);
}
fn clear_keys(set: ExternalValidatorSet) {
S::OraclizationKeys::remove(set);
S::ExternalKeys::remove(set);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -18,11 +18,11 @@ use sp_runtime::{
BuildStorage,
};
use serai_primitives::*;
use serai_abi::primitives::*;
use validator_sets::{primitives::MAX_KEY_SHARES_PER_SET_U32, MembershipProof};
pub use crate as validator_sets;
pub use coins_pallet as coins;
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;

View File

@@ -1,15 +1,17 @@
use alloc::vec::Vec;
use sp_core::{Encode, Decode, ConstU32, sr25519::Public, bounded::BoundedVec};
use serai_primitives::{
use serai_abi::primitives::{
network_id::NetworkId,
balance::Amount,
validator_sets::{KeyShares as KeySharesStruct, Session, ValidatorSet},
validator_sets::{KeyShares as KeySharesStruct, Session, ExternalValidatorSet, ValidatorSet},
};
use frame_support::storage::{StorageValue, StorageMap, StorageDoubleMap, StoragePrefixedMap};
use crate::{embedded_elliptic_curve_keys::EmbeddedEllipticCurveKeys, allocations::Allocations};
use crate::{
embedded_elliptic_curve_keys::EmbeddedEllipticCurveKeys, allocations::Allocations, keys::Keys,
};
/// The list of genesis validators.
pub(crate) type GenesisValidators =
@@ -18,7 +20,7 @@ pub(crate) type GenesisValidators =
/// The key for the SelectedValidators map.
pub(crate) type SelectedValidatorsKey = (ValidatorSet, [u8; 16], Public);
pub(crate) trait SessionsStorage: EmbeddedEllipticCurveKeys + Allocations {
pub(crate) trait SessionsStorage: EmbeddedEllipticCurveKeys + Allocations + Keys {
/// The genesis validators
///
/// The usage of is shared with the rest of the pallet. `Sessions` only reads it.
@@ -148,7 +150,9 @@ pub(crate) trait Sessions {
///
/// Doesn't spawn the next session if the latest decided session has yet to start. This bounds
/// the current session to be the latest decided session or the one prior.
fn attempt_new_session(network: NetworkId, include_genesis_validators: bool);
///
/// Returns `true` if the next session was decided. Returns `false` otherwise.
fn attempt_new_session(network: NetworkId, include_genesis_validators: bool) -> bool;
/// Have the latest-decided session accept the handover from the current set, if one exists.
///
@@ -215,10 +219,23 @@ pub(crate) trait Sessions {
/// The stake for the current validator set.
fn stake_for_current_validator_set(network: NetworkId) -> Option<Amount>;
/// The selected validators for a set.
///
/// This will return an empty iterator if the validators have yet to be decided, or if the
/// selected validators were cleared due to being historic.
fn selected_validators(set: ValidatorSet) -> impl Iterator<Item = (Public, KeySharesStruct)>;
/// The validators for the Serai network, in the form expected by BABE, GRANDPA.
fn serai_validators(session: Session) -> Vec<(Public, Public)> {
Self::selected_validators(ValidatorSet { network: NetworkId::Serai, session })
.map(|(validator, _key_shares)| (validator, validator))
.collect()
}
}
impl<Storage: SessionsStorage> Sessions for Storage {
fn attempt_new_session(network: NetworkId, include_genesis_validators: bool) {
fn attempt_new_session(network: NetworkId, include_genesis_validators: bool) -> bool {
// If we haven't rotated to the latest decided session, return
// This prevents us from deciding session #n+2 when we haven't even started #n+1
let current_session = Storage::CurrentSession::get(network);
@@ -228,12 +245,12 @@ impl<Storage: SessionsStorage> Sessions for Storage {
// If the latest decided session is current, we can decide the next session
} else {
// If we already have a pending session, don't spawn a new one
return;
return false;
}
}
(Some(current), None) => unreachable!("current session but never decided a session"),
(Some(_current), None) => unreachable!("current session but never decided a session"),
// If we decided our first session, but didn't start it, don't decide another session
(None, Some(latest)) => return,
(None, Some(_latest)) => return false,
(None, None) => {
// If we've never started a session, we can decide the first session
}
@@ -273,6 +290,11 @@ impl<Storage: SessionsStorage> Sessions for Storage {
selected_validators.append(&mut genesis_validators);
}
// If we failed to select any validators, return `false` now
if total_key_shares == 0 {
return false;
}
let latest_decided_session = Storage::LatestDecidedSession::mutate(network, |session| {
let next_session = session.map(|session| Session(session.0 + 1)).unwrap_or(Session(0));
*session = Some(next_session);
@@ -290,6 +312,8 @@ impl<Storage: SessionsStorage> Sessions for Storage {
key_shares,
);
}
true
}
fn accept_handover(network: NetworkId) {
@@ -324,6 +348,12 @@ impl<Storage: SessionsStorage> Sessions for Storage {
let historic_set = ValidatorSet { network, session: historic_session };
Storage::KeyShares::remove(historic_set);
clear_selected_validators::<Storage::SelectedValidators>(historic_set);
match historic_set.network {
NetworkId::Serai => {}
NetworkId::External(network) => {
Storage::clear_keys(ExternalValidatorSet { network, session: historic_session })
}
}
}
}
@@ -349,37 +379,45 @@ impl<Storage: SessionsStorage> Sessions for Storage {
Err(AllocationError::AllocationLessThanKeyShare)?
}
/*
If the validator set has a single point of failure, the following does nothing. If the
validator set has decentralized and doesn't have a single point of failure, the following
will ensure this allocation doesn't create a single point of failure.
*/
{
// Check the validator set's current expected key shares
let expected_key_shares = Self::expected_key_shares(network, allocation_per_key_share);
// Check if the top validator in this set may be faulty without causing a halt under this f
let currently_tolerates_single_point_of_failure = if let Some(top_validator) =
Self::iter_allocations(network, allocation_per_key_share).next()
let new_key_shares =
KeySharesStruct::from_allocation(new_allocation, allocation_per_key_share);
// If this would guarantee this validator will be a single point of failure, error
if ((3 * new_key_shares.0) + 1) > KeySharesStruct::MAX_PER_SET {
Err(AllocationError::IntroducesSinglePointOfFailure)?;
}
/*
If the validator set has a single point of failure, the following does nothing. If the
validator set has decentralized and doesn't have a single point of failure, the following
will ensure this allocation doesn't create a single point of failure.
*/
{
let (_key, amount) = top_validator;
let key_shares = KeySharesStruct::from_allocation(amount, allocation_per_key_share);
key_shares.0 <= (expected_key_shares.0 / 3)
} else {
false
};
// If the set currently tolerates the fault of the top validator, don't let that change
if currently_tolerates_single_point_of_failure {
let old_key_shares =
KeySharesStruct::from_allocation(old_allocation, allocation_per_key_share);
let new_key_shares =
KeySharesStruct::from_allocation(new_allocation, allocation_per_key_share);
// Update the amount of expected key shares per the key shares added
let expected_key_shares = KeySharesStruct::saturating_from(
expected_key_shares.0 + (new_key_shares.0 - old_key_shares.0),
);
// If the new key shares exceeds the fault tolerance, don't allow the allocation
if new_key_shares.0 > (expected_key_shares.0 / 3) {
Err(AllocationError::IntroducesSinglePointOfFailure)?
// Check the validator set's current expected key shares
let expected_key_shares = Self::expected_key_shares(network, allocation_per_key_share);
// Check if the top validator in this set may be faulty without causing a halt under this f
let currently_tolerates_single_point_of_failure = if let Some(top_validator) =
Self::iter_allocations(network, allocation_per_key_share).next()
{
let (_key, amount) = top_validator;
let key_shares = KeySharesStruct::from_allocation(amount, allocation_per_key_share);
key_shares.0 <= (expected_key_shares.0 / 3)
} else {
false
};
// If the set currently tolerates the fault of the top validator, don't let that change
if currently_tolerates_single_point_of_failure {
let old_key_shares =
KeySharesStruct::from_allocation(old_allocation, allocation_per_key_share);
// Update the amount of expected key shares per the key shares added
let expected_key_shares = KeySharesStruct::saturating_from(
expected_key_shares.0 + (new_key_shares.0 - old_key_shares.0),
);
// If the new key shares exceeds the fault tolerance, don't allow the allocation
if new_key_shares.0 > (expected_key_shares.0 / 3) {
Err(AllocationError::IntroducesSinglePointOfFailure)?;
}
}
}
}
@@ -524,4 +562,8 @@ impl<Storage: SessionsStorage> Sessions for Storage {
fn stake_for_current_validator_set(network: NetworkId) -> Option<Amount> {
Storage::TotalAllocatedStake::get(network)
}
fn selected_validators(set: ValidatorSet) -> impl Iterator<Item = (Public, KeySharesStruct)> {
selected_validators::<Storage::SelectedValidators>(set)
}
}

View File

@@ -22,7 +22,7 @@ use sp_core::{
};
use sp_runtime::traits::ValidateUnsigned;
use serai_primitives::*;
use serai_abi::primitives::*;
fn active_network_validators(network: NetworkId) -> Vec<(Public, u64)> {
if network == NetworkId::Serai {