Compare commits
2 Commits
bdbf7bd20f
...
ab7f978ccc
| Author | SHA1 | Date | |
|---|---|---|---|
| ab7f978ccc | |||
| 66ca7545ce |
18
Cargo.lock
generated
18
Cargo.lock
generated
@ -1195,7 +1195,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "casper-runtime"
|
||||
version = "4.0.14"
|
||||
version = "4.0.15"
|
||||
dependencies = [
|
||||
"casper-runtime-constants",
|
||||
"frame-benchmarking",
|
||||
@ -1209,6 +1209,7 @@ dependencies = [
|
||||
"frame-try-runtime",
|
||||
"ghost-core-primitives",
|
||||
"ghost-exodus",
|
||||
"ghost-exodus-runtime-api",
|
||||
"ghost-governance",
|
||||
"ghost-helpers",
|
||||
"ghost-networks",
|
||||
@ -3684,7 +3685,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ghost-exodus"
|
||||
version = "0.0.17"
|
||||
version = "0.1.1"
|
||||
dependencies = [
|
||||
"chacha20poly1305",
|
||||
"frame-benchmarking",
|
||||
@ -3694,6 +3695,7 @@ dependencies = [
|
||||
"frost-core",
|
||||
"frost-ed25519",
|
||||
"frost-secp256k1-tr",
|
||||
"ghost-exodus-runtime-api",
|
||||
"ghost-helpers",
|
||||
"ghost-networks",
|
||||
"ghost-traits",
|
||||
@ -3725,10 +3727,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ghost-exodus-runtime-api"
|
||||
version = "0.0.0"
|
||||
version = "0.0.2"
|
||||
dependencies = [
|
||||
"ghost-helpers",
|
||||
"parity-scale-codec",
|
||||
"sp-api",
|
||||
"sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.12.0)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3758,7 +3762,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ghost-helpers"
|
||||
version = "0.0.11"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"frame-support",
|
||||
"ghost-traits",
|
||||
@ -3840,7 +3844,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ghost-networks"
|
||||
version = "0.2.10"
|
||||
version = "0.2.12"
|
||||
dependencies = [
|
||||
"frame-benchmarking",
|
||||
"frame-support",
|
||||
@ -4086,7 +4090,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ghost-traits"
|
||||
version = "0.4.6"
|
||||
version = "0.4.9"
|
||||
dependencies = [
|
||||
"frame-support",
|
||||
"frost-core",
|
||||
@ -4110,7 +4114,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ghost-weaver"
|
||||
version = "0.0.14"
|
||||
version = "0.0.16"
|
||||
dependencies = [
|
||||
"frame-benchmarking",
|
||||
"frame-support",
|
||||
|
||||
@ -239,6 +239,7 @@ ghost-networks = { path = "pallets/networks", default-features = false }
|
||||
ghost-governance = { path = "pallets/governance", default-features = false }
|
||||
ghost-weaver = { path = "pallets/weaver", default-features = false }
|
||||
ghost-exodus = { path = "pallets/exodus", default-features = false }
|
||||
ghost-exodus-runtime-api = { path = "pallets/exodus/runtime-api", default-features = false }
|
||||
ghost-sudo = { path = "pallets/sudo", default-features = false }
|
||||
ghost-nomination-pools = { path = "pallets/pools", default-features = false }
|
||||
ghost-nomination-pools-runtime-api = { path = "pallets/pools/runtime-api", default-features = false }
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ghost-exodus"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
description = "Threshold signature generation with DKG included"
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
@ -40,6 +40,7 @@ parking_lot = { workspace = true }
|
||||
|
||||
ghost-traits = { workspace = true }
|
||||
ghost-helpers = { workspace = true }
|
||||
ghost-exodus-runtime-api = { workspace = true }
|
||||
|
||||
pallet-balances = { workspace = true }
|
||||
pallet-session = { workspace = true }
|
||||
@ -80,6 +81,7 @@ std = [
|
||||
"frost-core/std",
|
||||
"ghost-traits/std",
|
||||
"ghost-helpers/std",
|
||||
"ghost-exodus-runtime-api/std",
|
||||
"pallet-balances/std",
|
||||
"pallet-session/std",
|
||||
"pallet-staking/std",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ghost-exodus-runtime-api"
|
||||
version = "0.0.1"
|
||||
version = "0.0.2"
|
||||
description = "RPC runtime API for ghost EXODUS pallet"
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
@ -14,7 +14,14 @@ targets = ["x86_64-unknown-linux-gnu"]
|
||||
[dependencies]
|
||||
codec = { workspace = true, features = ["derive"] }
|
||||
sp-api = { workspace = true }
|
||||
sp-std = { workspace = true }
|
||||
ghost-helpers = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = ["codec/std", "sp-api/std"]
|
||||
std = [
|
||||
"codec/std",
|
||||
"sp-api/std",
|
||||
"sp-std/std",
|
||||
"ghost-helpers/std",
|
||||
]
|
||||
|
||||
@ -1,28 +1,17 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use sp_std::vec::Vec;
|
||||
use ghost_helpers::networks::{NetworkType, NetworkCurve};
|
||||
|
||||
pub type PreparedEvmMessage = Option<(Vec<u8>, Vec<u8>, Vec<u8>)>;
|
||||
|
||||
sp_api::decl_runtime_apis! {
|
||||
pub trait ExodusApi<NetworkId> {
|
||||
fn get_signed_rotation_message(
|
||||
session: ExodusSession,
|
||||
network_id: NetworkId,
|
||||
dkg_index: DkgIndex
|
||||
) -> Vec<u8>;
|
||||
|
||||
fn get_signed_bridge_message(
|
||||
session: ExodusSession,
|
||||
network_id: NetworkId,
|
||||
dkg_index: DkgIndex
|
||||
) -> Vec<u8>;
|
||||
|
||||
fn get_signed_governance_message(
|
||||
session: ExodusSession,
|
||||
network_id: NetworkId,
|
||||
dkg_index: DkgIndex
|
||||
) -> Vec<u8>;
|
||||
|
||||
fn active_verifying_key(network_curve: NetworkCurve) -> Vec<u8>;
|
||||
fn pending_exodus_request_count(network_curve: NetworkCurve) -> u32;
|
||||
fn is_qualification_onging(network_curve: NetworkCurve) -> bool;
|
||||
pub trait ExodusApi {
|
||||
fn get_evm_rotation_metadata(dkg_index: u64, network_type: NetworkType) -> PreparedEvmMessage;
|
||||
fn get_evm_bridge_metadata(dkg_index: u64, network_id: u64, session: u64) -> PreparedEvmMessage;
|
||||
fn get_evm_governance_metadata(dkg_index: u64, network_id: u64, session: u64) -> PreparedEvmMessage;
|
||||
|
||||
fn pending_exodus_request_count() -> u32;
|
||||
fn is_qualification_ongoing(curve: NetworkCurve) -> bool;
|
||||
}
|
||||
}
|
||||
|
||||
@ -539,7 +539,7 @@ mod benchmarks {
|
||||
intervals: [34, 35],
|
||||
booleans: [true, true],
|
||||
};
|
||||
let request = ExodusRequest::<BalanceOf<T>>::evm_governance(
|
||||
let request = ExodusRequest::<T>::evm_governance(
|
||||
dummy_address,
|
||||
create_bond,
|
||||
);
|
||||
@ -625,7 +625,7 @@ mod benchmarks {
|
||||
intervals: [34, 35],
|
||||
booleans: [true, true],
|
||||
};
|
||||
let request = ExodusRequest::<BalanceOf<T>>::evm_governance(
|
||||
let request = ExodusRequest::<T>::evm_governance(
|
||||
dummy_address,
|
||||
create_bond,
|
||||
);
|
||||
@ -732,7 +732,7 @@ mod benchmarks {
|
||||
intervals: [34, 35],
|
||||
booleans: [true, true],
|
||||
};
|
||||
let request = ExodusRequest::<BalanceOf<T>>::evm_governance(
|
||||
let request = ExodusRequest::<T>::evm_governance(
|
||||
dummy_address,
|
||||
create_bond,
|
||||
);
|
||||
|
||||
@ -31,6 +31,7 @@ use sp_io::hashing::blake2_256;
|
||||
|
||||
use ghost_helpers::{
|
||||
get_byzantium_quorum_threshold, SubstrateBlake2Hasher,
|
||||
lcg::PerfectSlotMapperU128,
|
||||
hash_chain::cumulative_hash,
|
||||
networks::{NetworkData, NetworkCurve, NetworkType},
|
||||
bounded_bitmap::{validate_bitmap_sizes, BoundedBitmap},
|
||||
@ -227,9 +228,16 @@ type EvmBytes32 = sp_core::H256;
|
||||
type ParticipantsBitmap<T> = BoundedBitmap<BitmapChunk, <T as Config>::MaxAuthoritiesChunks>;
|
||||
type BitmapByAuthority<T> = BoundedBTreeMap<AuthIndex, ParticipantsBitmap<T>, <T as Config>::MaxAuthorities>;
|
||||
|
||||
pub type EvmSessionsBundle<T> = BoundedVec::<u8, MaxEvmRotationSessionsSize<T>>;
|
||||
pub type BalanceOf<T> = <<T as Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::Balance;
|
||||
pub type NetworkIdOf<T> = <<T as Config>::NetworkDataHandler as NetworkDataBasicHandler>::NetworkId;
|
||||
|
||||
pub type ExodusSessionsMap<T> = BoundedBTreeMap<
|
||||
NetworkIdOf<T>,
|
||||
ExodusSession,
|
||||
<<T as Config>::NetworkDataHandler as NetworkDataBasicHandler>::MaxNetworks,
|
||||
>;
|
||||
|
||||
pub type ValidatorId<T> = <<T as Config>::ValidatorSet as ValidatorSet<
|
||||
<T as frame_system::Config>::AccountId,
|
||||
>>::ValidatorId;
|
||||
@ -243,6 +251,16 @@ pub type IdentificationTuple<T> = (
|
||||
|
||||
type ExodusResult<T> = Result<T, ExodusError>;
|
||||
|
||||
pub struct MaxEvmRotationSessionsSize<T>(sp_std::marker::PhantomData<T>);
|
||||
impl<T: Config> Get<u32> for MaxEvmRotationSessionsSize<T> {
|
||||
fn get() -> u32 {
|
||||
let exodus_session_size = core::mem::size_of::<ExodusSession>() as u32;
|
||||
let max_networks = <T::NetworkDataHandler as NetworkDataBasicHandler>::MaxNetworks::get();
|
||||
|
||||
exodus_session_size.saturating_mul(max_networks)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MaxAuthoritiesBitmaskSize<T>(sp_std::marker::PhantomData<T>);
|
||||
impl<T: Config> Get<u32> for MaxAuthoritiesBitmaskSize<T> {
|
||||
fn get() -> u32 {
|
||||
@ -415,7 +433,7 @@ pub mod pallet {
|
||||
type MaxAuthorities: Get<u32>;
|
||||
|
||||
#[pallet::constant]
|
||||
type MaxAuthoritiesChunks: Get<u32> + core::fmt::Debug;
|
||||
type MaxAuthoritiesChunks: Get<u32>;
|
||||
|
||||
#[pallet::constant]
|
||||
type DkgRoundPeriod: Get<u64>;
|
||||
@ -554,15 +572,8 @@ pub mod pallet {
|
||||
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn current_exodus_sessions)]
|
||||
pub(super) type CurrentExodusSessions<T: Config> = StorageValue<
|
||||
_,
|
||||
BoundedBTreeMap<
|
||||
NetworkIdOf<T>,
|
||||
ExodusSession,
|
||||
<T::NetworkDataHandler as NetworkDataBasicHandler>::MaxNetworks,
|
||||
>,
|
||||
ValueQuery
|
||||
>;
|
||||
pub(super) type CurrentExodusSessions<T: Config> =
|
||||
StorageValue<_, ExodusSessionsMap<T>, ValueQuery>;
|
||||
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn exodus_requests)]
|
||||
@ -572,7 +583,7 @@ pub mod pallet {
|
||||
NMapKey<Twox64Concat, NetworkCurve>,
|
||||
NMapKey<Twox64Concat, ExodusContext<NetworkIdOf<T>>>,
|
||||
),
|
||||
ExodusRequest<BalanceOf<T>>,
|
||||
ExodusRequest<T>,
|
||||
OptionQuery,
|
||||
>;
|
||||
|
||||
@ -646,8 +657,8 @@ pub mod pallet {
|
||||
pub(super) type ExodusSignedRotations<T: Config> = StorageDoubleMap<
|
||||
_,
|
||||
Twox64Concat, DkgIndex,
|
||||
Twox64Concat, ExodusContext<NetworkIdOf<T>>,
|
||||
ExodusSignedMessage<BalanceOf<T>>,
|
||||
Twox64Concat, NetworkType,
|
||||
(ExodusContext<NetworkIdOf<T>>, ExodusSignedMessage<T>),
|
||||
OptionQuery,
|
||||
>;
|
||||
|
||||
@ -657,7 +668,7 @@ pub mod pallet {
|
||||
_,
|
||||
Twox64Concat, DkgIndex,
|
||||
Twox64Concat, ExodusContext<NetworkIdOf<T>>,
|
||||
ExodusSignedMessage<BalanceOf<T>>,
|
||||
ExodusSignedMessage<T>,
|
||||
OptionQuery,
|
||||
>;
|
||||
|
||||
@ -667,7 +678,7 @@ pub mod pallet {
|
||||
_,
|
||||
Twox64Concat, DkgIndex,
|
||||
Twox64Concat, ExodusContext<NetworkIdOf<T>>,
|
||||
ExodusSignedMessage<BalanceOf<T>>,
|
||||
ExodusSignedMessage<T>,
|
||||
OptionQuery,
|
||||
>;
|
||||
|
||||
@ -865,18 +876,34 @@ pub mod pallet {
|
||||
SCALAR_MAX_BYTES,
|
||||
);
|
||||
|
||||
for (network_id, network_type) in T::NetworkDataHandler::iter_types_by_curve(&network_curve) {
|
||||
for (network_type, network_ids) in T::NetworkDataHandler::iter_types_by_curve(&network_curve) {
|
||||
let rotation_message_size = match network_type {
|
||||
NetworkType::Evm => {
|
||||
let dummy_evm_xor_key = u128::MAX;
|
||||
let dummy_evm_bitmask = u128::MIN;
|
||||
let dummy_public_address = EvmBytes32::repeat_byte(69u8);
|
||||
let mut dummy_packed_sessions =
|
||||
EvmSessionsBundle::<T>::with_max_capacity();
|
||||
|
||||
let max_size = EvmSessionsBundle::<T>::bound();
|
||||
dummy_packed_sessions.bounded_resize(max_size, 69u8);
|
||||
|
||||
let network_id = network_ids
|
||||
.as_ref()
|
||||
.first()
|
||||
.copied()
|
||||
.expect("At this point we should have at least one `NetworkId` registered; qed");
|
||||
|
||||
let dummy_exodus_request =
|
||||
ExodusRequest::<BalanceOf<T>>::evm_rotation(
|
||||
ExodusRequest::<T>::evm_rotation(
|
||||
dummy_public_address,
|
||||
0u8, // parity
|
||||
dummy_evm_xor_key,
|
||||
dummy_evm_bitmask,
|
||||
dummy_packed_sessions,
|
||||
);
|
||||
|
||||
let dummy_exodus_context = ExodusContext::new(network_id, 0);
|
||||
let dummy_exodus_context =
|
||||
ExodusContext::<NetworkIdOf<T>>::new(network_id, 0);
|
||||
|
||||
let mut message_buffer = [0u8; MAX_MESSAGE_SIZE as usize];
|
||||
dummy_exodus_request
|
||||
@ -1637,9 +1664,14 @@ pub mod pallet {
|
||||
|
||||
ensure!(
|
||||
match exodus_request.r#type {
|
||||
ExodusRequestType::EvmRotation { .. } | ExodusRequestType::UtxoRotation => {
|
||||
ExodusRequestType::EvmRotation { .. } => {
|
||||
!ExodusSignedRotations::<T>::contains_key(
|
||||
active_dkg_index, exodus_context,
|
||||
active_dkg_index, NetworkType::Evm,
|
||||
)
|
||||
},
|
||||
ExodusRequestType::UtxoRotation => {
|
||||
!ExodusSignedRotations::<T>::contains_key(
|
||||
active_dkg_index, NetworkType::Utxo,
|
||||
)
|
||||
},
|
||||
ExodusRequestType::EvmBridgeOut { .. } | ExodusRequestType::UtxoBridgeOut { .. } => {
|
||||
@ -1789,25 +1821,26 @@ pub mod pallet {
|
||||
|
||||
ExodusRequests::<T>::remove((&network_curve, exodus_context));
|
||||
|
||||
match exodus_request.r#type {
|
||||
ExodusRequestType::EvmRotation { .. } | ExodusRequestType::UtxoRotation => {
|
||||
ExodusSignedRotations::<T>::insert(
|
||||
active_dkg_index, exodus_context,
|
||||
exodus_signed_message,
|
||||
);
|
||||
},
|
||||
ExodusRequestType::EvmBridgeOut { .. } | ExodusRequestType::UtxoBridgeOut { .. } => {
|
||||
ExodusSignedBridges::<T>::insert(
|
||||
active_dkg_index, exodus_context,
|
||||
exodus_signed_message,
|
||||
);
|
||||
}
|
||||
ExodusRequestType::EvmGovernance { .. } => {
|
||||
ExodusSignedGovernance::<T>::insert(
|
||||
active_dkg_index, exodus_context,
|
||||
exodus_signed_message,
|
||||
);
|
||||
}
|
||||
if exodus_signed_message.is_evm_rotation() {
|
||||
ExodusSignedRotations::<T>::insert(
|
||||
active_dkg_index, NetworkType::Evm,
|
||||
(exodus_context, exodus_signed_message),
|
||||
);
|
||||
} else if exodus_signed_message.is_utxo_rotation() {
|
||||
ExodusSignedRotations::<T>::insert(
|
||||
active_dkg_index, NetworkType::Utxo,
|
||||
(exodus_context, exodus_signed_message),
|
||||
);
|
||||
} else if exodus_signed_message.is_bridge_out() {
|
||||
ExodusSignedBridges::<T>::insert(
|
||||
active_dkg_index, exodus_context,
|
||||
exodus_signed_message,
|
||||
);
|
||||
} else if exodus_signed_message.is_evm_governance() {
|
||||
ExodusSignedGovernance::<T>::insert(
|
||||
active_dkg_index, exodus_context,
|
||||
exodus_signed_message,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -2133,7 +2166,7 @@ impl<T: Config> Pallet<T> {
|
||||
|
||||
fn do_register_evm_exodus(
|
||||
network_id: NetworkIdOf<T>,
|
||||
exodus_request: ExodusRequest<BalanceOf<T>>,
|
||||
exodus_request: ExodusRequest<T>,
|
||||
) -> DispatchResult {
|
||||
let network = T::NetworkDataHandler::get(&network_id)
|
||||
.ok_or(Error::<T>::NetworkDoesNotExist)?;
|
||||
@ -2588,68 +2621,55 @@ impl<T: Config> Pallet<T> {
|
||||
return;
|
||||
}
|
||||
|
||||
weight.saturating_accrue(T::DbWeight::get().reads(2));
|
||||
weight.saturating_accrue(T::DbWeight::get().reads(1));
|
||||
let mut all_exodus_sessions = CurrentExodusSessions::<T>::get();
|
||||
let mut rotation_requests = BoundedVec::<
|
||||
(ExodusRequest<T>, ExodusContext<NetworkIdOf<T>>),
|
||||
<T::NetworkDataHandler as NetworkDataBasicHandler>::MaxNetworks,
|
||||
>::with_max_capacity();
|
||||
|
||||
let max_networks = <T::NetworkDataHandler as NetworkDataBasicHandler>::MaxNetworks::get();
|
||||
let mut exodus_requests = Vec::with_capacity(max_networks as usize);
|
||||
let mut too_much_entries = false;
|
||||
let mut rotation_failed = false;
|
||||
|
||||
for (network_id, network_type) in T::NetworkDataHandler::iter_types_by_curve(&network_curve) {
|
||||
if !matches!(network_type, NetworkType::Evm) {
|
||||
continue;
|
||||
weight.saturating_accrue(T::DbWeight::get().reads(1));
|
||||
for (network_type, network_ids) in T::NetworkDataHandler::iter_types_by_curve(&network_curve) {
|
||||
let processed_network_type = matches!(network_type, NetworkType::Evm);
|
||||
if !processed_network_type { continue; }
|
||||
|
||||
let mut process_iteration = || -> Option<()> {
|
||||
let element_len = network_curve.element_bytes_len();
|
||||
let evm_public_key = EvmBytes32::from_slice(
|
||||
&verifying_key_slice[1..element_len]
|
||||
);
|
||||
|
||||
let (request, context) = Self::prepare_evm_rotation(
|
||||
evm_public_key,
|
||||
network_ids.as_ref(),
|
||||
&mut all_exodus_sessions
|
||||
)?;
|
||||
|
||||
if !request.r#type.is_evm_rotation() { return None; }
|
||||
|
||||
dkg_qualification.try_push_rotation(network_type, context)?;
|
||||
rotation_requests.try_push((request, context)).ok()?;
|
||||
|
||||
Some(())
|
||||
};
|
||||
|
||||
if process_iteration().is_none() {
|
||||
rotation_failed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
let exodus_session = match all_exodus_sessions.get_mut(&network_id) {
|
||||
Some(session_ref) => {
|
||||
let current = *session_ref;
|
||||
*session_ref = current.saturating_add(1);
|
||||
current
|
||||
},
|
||||
None => {
|
||||
if let Err(_) = all_exodus_sessions.try_insert(network_id, 1) {
|
||||
too_much_entries = true;
|
||||
}
|
||||
0
|
||||
}
|
||||
};
|
||||
|
||||
if too_much_entries { break; }
|
||||
|
||||
let exodus_request = match network_type {
|
||||
NetworkType::Evm => {
|
||||
let parity_byte = verifying_key_slice[0];
|
||||
let parity = if parity_byte % 2 == 0 { 0u8 } else { 1u8 };
|
||||
|
||||
let public_key = EvmBytes32::from_slice(&verifying_key_slice[1..33]);
|
||||
|
||||
ExodusRequest::evm_rotation(public_key, parity)
|
||||
}
|
||||
_ => {
|
||||
unreachable!();
|
||||
}
|
||||
};
|
||||
|
||||
let exodus_context = ExodusContext::new(network_id, exodus_session);
|
||||
|
||||
if let None = dkg_qualification
|
||||
.try_push_rotation_context(exodus_context) {
|
||||
too_much_entries = true;
|
||||
break;
|
||||
}
|
||||
|
||||
let key = (network_curve, exodus_context);
|
||||
exodus_requests.push((key, exodus_request));
|
||||
}
|
||||
|
||||
if too_much_entries {
|
||||
if rotation_failed {
|
||||
dkg_qualification.nullify_indexes();
|
||||
} else {
|
||||
let requests_count = exodus_requests.len() as u64;
|
||||
weight.saturating_accrue(T::DbWeight::get().writes(requests_count));
|
||||
|
||||
for exodus in exodus_requests {
|
||||
ExodusRequests::<T>::insert(exodus.0, exodus.1);
|
||||
for (exodus_request, exodus_context) in rotation_requests {
|
||||
weight.saturating_accrue(T::DbWeight::get().writes(1));
|
||||
ExodusRequests::<T>::insert(
|
||||
(network_curve, exodus_context),
|
||||
exodus_request
|
||||
);
|
||||
}
|
||||
|
||||
weight.saturating_accrue(T::DbWeight::get().writes(1));
|
||||
@ -2678,15 +2698,15 @@ impl<T: Config> Pallet<T> {
|
||||
!ActiveVerifyingKey::<T>::contains_key(network_curve);
|
||||
|
||||
let all_rotations_have_signatures = dkg_qualification
|
||||
.iter_rotation_contexts()
|
||||
.fold(true, |accumulator, exodus_context| {
|
||||
.iter_rotations()
|
||||
.fold(true, |accumulator, (network_type, exodus_context)| {
|
||||
weight.saturating_accrue(T::DbWeight::get().writes(1));
|
||||
ExodusRequests::<T>::remove((&network_curve, &exodus_context));
|
||||
|
||||
weight.saturating_accrue(T::DbWeight::get().reads(1));
|
||||
let has_signature = ExodusSignedRotations::<T>::contains_key(
|
||||
dkg_index,
|
||||
exodus_context,
|
||||
network_type,
|
||||
);
|
||||
|
||||
accumulator && has_signature
|
||||
@ -2709,6 +2729,93 @@ impl<T: Config> Pallet<T> {
|
||||
})
|
||||
}
|
||||
|
||||
fn prepare_evm_rotation(
|
||||
evm_public_key: EvmBytes32,
|
||||
network_ids: &[NetworkIdOf<T>],
|
||||
evm_exodus_sessions: &mut ExodusSessionsMap<T>,
|
||||
) -> Option<(ExodusRequest<T>, ExodusContext<NetworkIdOf<T>>)> {
|
||||
if network_ids.len() >= 128 { return None; }
|
||||
|
||||
let exodus_session_size = core::mem::size_of::<ExodusSession>();
|
||||
let packed_sessions_size = exodus_session_size * network_ids.len();
|
||||
|
||||
let evm_xor_key = PerfectSlotMapperU128::get_lcg_xor_mask(network_ids)?;
|
||||
let (bit_shift, bit_mask) = PerfectSlotMapperU128::get_bits_metadata()?;
|
||||
|
||||
let mut evm_sessions_bitmask: u128 = 0;
|
||||
let mut maybe_best_exodus_context: Option<ExodusContext<NetworkIdOf<T>>> = None;
|
||||
let mut evm_packed_sessions = EvmSessionsBundle::with_max_capacity();
|
||||
evm_packed_sessions.bounded_resize(packed_sessions_size, 0u8);
|
||||
|
||||
let mut evm_slots = [0usize; 128];
|
||||
for (i, network_id) in network_ids.iter().enumerate() {
|
||||
let slot_index =
|
||||
PerfectSlotMapperU128::get_lcg_based_slot_index(
|
||||
bit_mask,
|
||||
bit_shift,
|
||||
evm_xor_key,
|
||||
network_id
|
||||
);
|
||||
|
||||
if slot_index > 127 { return None; }
|
||||
|
||||
evm_sessions_bitmask |= 1u128 << slot_index;
|
||||
evm_slots[i] = slot_index;
|
||||
}
|
||||
|
||||
for (i, &network_id) in network_ids.into_iter().enumerate() {
|
||||
let exodus_session = match evm_exodus_sessions.get_mut(&network_id) {
|
||||
Some(session_ref) => {
|
||||
let current = *session_ref;
|
||||
*session_ref = current.saturating_add(1);
|
||||
current
|
||||
},
|
||||
None => {
|
||||
evm_exodus_sessions.try_insert(network_id, 1).ok()?;
|
||||
0
|
||||
}
|
||||
};
|
||||
|
||||
if maybe_best_exodus_context
|
||||
.as_ref()
|
||||
.map_or(true, |c| exodus_session > c.session) {
|
||||
maybe_best_exodus_context = Some(
|
||||
ExodusContext::<NetworkIdOf<T>>::new(
|
||||
network_id,
|
||||
exodus_session
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
let lcg_slot_index = evm_slots[i];
|
||||
|
||||
let lower_bits = (1u128 << lcg_slot_index) - 1;
|
||||
let slot_index = (evm_sessions_bitmask & lower_bits).count_ones() as usize;
|
||||
|
||||
let offset_start = slot_index * exodus_session_size;
|
||||
let offset_end = offset_start + exodus_session_size;
|
||||
|
||||
if offset_end > evm_packed_sessions.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
evm_packed_sessions[offset_start..offset_end]
|
||||
.copy_from_slice(&exodus_session.to_be_bytes());
|
||||
}
|
||||
|
||||
let best_exodus_context = maybe_best_exodus_context?;
|
||||
|
||||
let rotation_request =
|
||||
ExodusRequest::<T>::evm_rotation(
|
||||
evm_public_key,
|
||||
evm_xor_key,
|
||||
evm_sessions_bitmask,
|
||||
evm_packed_sessions
|
||||
);
|
||||
|
||||
Some((rotation_request, best_exodus_context))
|
||||
}
|
||||
|
||||
fn validate_round0_package_size(
|
||||
dkg_package: & DkgPackage<NetworkCurve, BlockNumberFor<T>, T>,
|
||||
signature: &<T::AuthorityId as RuntimeAppPublic>::Signature,
|
||||
@ -3231,7 +3338,7 @@ impl<T: Config> Pallet<T> {
|
||||
fn validate_exodus_nonce_commitment_size_and_get_metadata<'a>(
|
||||
exodus_package: &'a ExodusPackage<NetworkCurve, NetworkIdOf<T>, BlockNumberFor<T>, T>,
|
||||
signature: &<T::AuthorityId as RuntimeAppPublic>::Signature,
|
||||
) -> Result<(ExodusRequest<BalanceOf<T>>, RoastSession), InvalidTransaction> {
|
||||
) -> Result<(ExodusRequest<T>, RoastSession), InvalidTransaction> {
|
||||
let ExodusPackage::NonceCommitment(package) = exodus_package else {
|
||||
return Err(InvalidTransaction::BadProof);
|
||||
};
|
||||
@ -3526,11 +3633,7 @@ impl<T: Config> Pallet<T> {
|
||||
|
||||
let mut exodus_requests_by_network = BoundedBTreeMap::<
|
||||
NetworkIdOf<T>,
|
||||
BoundedBTreeMap<
|
||||
ExodusSession,
|
||||
ExodusRequest<BalanceOf<T>>,
|
||||
T::MaxExodusRequestCount
|
||||
>,
|
||||
BoundedBTreeMap<ExodusSession, ExodusRequest<T>, T::MaxExodusRequestCount>,
|
||||
<T::NetworkDataHandler as NetworkDataBasicHandler>::MaxNetworks,
|
||||
>::new();
|
||||
|
||||
@ -5273,6 +5376,75 @@ impl<T: Config> Pallet<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Pallet<T> {
|
||||
pub fn api_get_evm_rotation(
|
||||
dkg_index_u64: u64,
|
||||
network_type: NetworkType,
|
||||
) -> ghost_exodus_runtime_api::PreparedEvmMessage {
|
||||
let dkg_index: DkgIndex = dkg_index_u64.unique_saturated_into();
|
||||
let (context, rotation) =
|
||||
ExodusSignedRotations::<T>::get(dkg_index, network_type)?;
|
||||
|
||||
let mut message_buffer = [0u8; MAX_MESSAGE_SIZE as usize];
|
||||
let message = rotation.get_message(&mut message_buffer, context)?;
|
||||
|
||||
let nonce_bytes = rotation.nonce[1..].to_vec();
|
||||
let signature_bytes = rotation.signature.to_vec();
|
||||
|
||||
Some((message.to_vec(), nonce_bytes, signature_bytes))
|
||||
}
|
||||
|
||||
pub fn api_get_evm_bridge(
|
||||
dkg_index_u64: u64,
|
||||
network_id_u64: u64,
|
||||
session_u64: u64,
|
||||
) -> ghost_exodus_runtime_api::PreparedEvmMessage {
|
||||
let dkg_index: DkgIndex = dkg_index_u64.unique_saturated_into();
|
||||
let session: ExodusSession = session_u64.unique_saturated_into();
|
||||
let network_id: NetworkIdOf<T> = network_id_u64.unique_saturated_into();
|
||||
|
||||
let context = ExodusContext::<NetworkIdOf<T>>::new(network_id, session);
|
||||
let bridge = ExodusSignedBridges::<T>::get(dkg_index, context)?;
|
||||
|
||||
let mut message_buffer = [0u8; MAX_MESSAGE_SIZE as usize];
|
||||
let message = bridge.get_message(&mut message_buffer, context)?;
|
||||
|
||||
let nonce_bytes = bridge.nonce[1..].to_vec();
|
||||
let signature_bytes = bridge.signature.to_vec();
|
||||
|
||||
Some((message.to_vec(), nonce_bytes, signature_bytes))
|
||||
}
|
||||
|
||||
pub fn api_get_evm_governance(
|
||||
dkg_index_u64: u64,
|
||||
network_id_u64: u64,
|
||||
session_u64: u64,
|
||||
) -> ghost_exodus_runtime_api::PreparedEvmMessage {
|
||||
let dkg_index: DkgIndex = dkg_index_u64.unique_saturated_into();
|
||||
let session: ExodusSession = session_u64.unique_saturated_into();
|
||||
let network_id: NetworkIdOf<T> = network_id_u64.unique_saturated_into();
|
||||
|
||||
let context = ExodusContext::<NetworkIdOf<T>>::new(network_id, session);
|
||||
let governance = ExodusSignedGovernance::<T>::get(dkg_index, context)?;
|
||||
|
||||
let mut message_buffer = [0u8; MAX_MESSAGE_SIZE as usize];
|
||||
let message = governance.get_message(&mut message_buffer, context)?;
|
||||
|
||||
let nonce_bytes = governance.nonce[1..].to_vec();
|
||||
let signature_bytes = governance.signature.to_vec();
|
||||
|
||||
Some((message.to_vec(), nonce_bytes, signature_bytes))
|
||||
}
|
||||
|
||||
pub fn api_pending_exodus_request_count() -> u32 {
|
||||
ExodusRequests::<T>::count()
|
||||
}
|
||||
|
||||
pub fn api_is_qualification_ongoing(network_curve: NetworkCurve) -> bool {
|
||||
QualificationDkgState::<T>::get(network_curve).is_dkg_pending()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> sp_runtime::BoundToRuntimeAppPublic for Pallet<T> {
|
||||
type Public = T::AuthorityId;
|
||||
}
|
||||
|
||||
@ -926,6 +926,16 @@ fn test_all_validators_can_execute_dkg() {
|
||||
|
||||
let (mut ext, tx_pool_state) = run_initial_dkg_session(&authorities_1, curve);
|
||||
|
||||
ext.execute_with(|| {
|
||||
let network_data = NetworkDataBuilder::default()
|
||||
.with_network_curve(curve)
|
||||
.with_network_type(NetworkType::Evm)
|
||||
.with_outgoing_share(500_000_000)
|
||||
.build();
|
||||
|
||||
let _ = <Runtime as Config>::NetworkDataHandler::register(network_id, network_data).unwrap();
|
||||
});
|
||||
|
||||
let mut authorities_2 = authorities_1.clone();
|
||||
let new_authority = authorities_2.iter().max().unwrap() + 1;
|
||||
authorities_2.push(new_authority);
|
||||
@ -935,13 +945,6 @@ fn test_all_validators_can_execute_dkg() {
|
||||
run_dkg_session(&mut ext, &tx_pool_state, authorities_1.clone(), authorities_2.clone(), curve);
|
||||
|
||||
ext.execute_with(|| {
|
||||
let network_data = NetworkDataBuilder::default()
|
||||
.with_network_curve(curve)
|
||||
.with_network_type(NetworkType::Evm)
|
||||
.with_outgoing_share(500_000_000)
|
||||
.build();
|
||||
|
||||
let _ = <Runtime as Config>::NetworkDataHandler::register(network_id, network_data).unwrap();
|
||||
let _ = <Runtime as Config>::NetworkDataHandler::register_incoming(&network_id, 1337).unwrap();
|
||||
|
||||
Exodus::do_register_evm_bridge_out_exodus(
|
||||
@ -1007,6 +1010,14 @@ fn test_all_validators_can_execute_dkg() {
|
||||
Exodus::do_register_evm_governance(network_id, dummy_address, audit).unwrap();
|
||||
});
|
||||
|
||||
ext.execute_with(|| {
|
||||
CurrentExodusSessions::<Runtime>::mutate(|sessions| {
|
||||
if let Some(session) = sessions.get_mut(&420) {
|
||||
*session = 1337;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
run_dkg_session(&mut ext, &tx_pool_state, authorities_1, authorities_2.clone(), curve);
|
||||
|
||||
let rotations_count = ext.execute_with(|| {
|
||||
@ -1025,7 +1036,7 @@ fn test_all_validators_can_execute_dkg() {
|
||||
ExodusRequests::<Runtime>::count()
|
||||
});
|
||||
|
||||
assert_eq!(rotations_count, 5);
|
||||
assert_eq!(rotations_count, 3);
|
||||
assert_eq!(bridges_count, 2);
|
||||
assert_eq!(governances_count, 15);
|
||||
assert_eq!(pending_exodus_requests, 0);
|
||||
|
||||
@ -12,16 +12,17 @@ use ghost_traits::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
ELEMENT_MAX_BYTES, Config, ExodusContext, NetworkIdOf,
|
||||
ELEMENT_MAX_BYTES, Config, NetworkType, NetworkIdOf, ExodusContext,
|
||||
ParticipantsBitmap, BlockNumberFor, DkgIndex,
|
||||
};
|
||||
|
||||
type RotationSessions<T> = BoundedVec<
|
||||
ExodusContext<NetworkIdOf<T>>,
|
||||
<<T as Config>::NetworkDataHandler as NetworkDataBasicHandler>::MaxNetworks,
|
||||
type RotationItem<T> = (NetworkType, ExodusContext<NetworkIdOf<T>>);
|
||||
type Rotations<T> = BoundedVec<
|
||||
RotationItem<T>,
|
||||
<<T as Config>::NetworkDataHandler as NetworkDataBasicHandler>::MaxNetworks,
|
||||
>;
|
||||
|
||||
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
|
||||
#[derive(Encode, Decode, Clone, PartialEq, Eq, TypeInfo)]
|
||||
#[scale_info(skip_type_params(T))]
|
||||
pub struct ReadyDkgAuthorities<T: Config> {
|
||||
authorities_indexes: ParticipantsBitmap<T>,
|
||||
@ -72,12 +73,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
|
||||
#[derive(Encode, Decode, Clone, PartialEq, Eq, TypeInfo)]
|
||||
#[scale_info(skip_type_params(T))]
|
||||
pub struct PendingDkgAuthorities<T: Config> {
|
||||
verifying_key: BoundedVec<u8, ConstU32<ELEMENT_MAX_BYTES>>,
|
||||
authorities_indexes: ParticipantsBitmap<T>,
|
||||
rotation_sessions: RotationSessions<T>,
|
||||
rotations: Rotations<T>,
|
||||
dkg_state: DkgState<BlockNumberFor<T>>,
|
||||
}
|
||||
|
||||
@ -86,7 +87,7 @@ impl<T: Config> Default for PendingDkgAuthorities<T> {
|
||||
Self {
|
||||
verifying_key: Default::default(),
|
||||
authorities_indexes: Default::default(),
|
||||
rotation_sessions: Default::default(),
|
||||
rotations: Default::default(),
|
||||
dkg_state: Default::default(),
|
||||
}
|
||||
}
|
||||
@ -101,7 +102,7 @@ where
|
||||
pub fn new_dkg(&mut self, authorities_len: usize) {
|
||||
self.authorities_indexes = ParticipantsBitmap::<T>::empty(authorities_len);
|
||||
self.dkg_state.restart();
|
||||
self.rotation_sessions = Default::default();
|
||||
self.rotations = Default::default();
|
||||
}
|
||||
|
||||
pub fn set_release_block(&mut self, block: BlockNumberFor<T>) {
|
||||
@ -124,7 +125,7 @@ where
|
||||
self.verifying_key = Default::default();
|
||||
self.authorities_indexes.nullify();
|
||||
self.dkg_state.restart();
|
||||
self.rotation_sessions = Default::default();
|
||||
self.rotations = Default::default();
|
||||
}
|
||||
|
||||
pub fn next_phase(&mut self) {
|
||||
@ -206,11 +207,13 @@ where
|
||||
self.authorities_indexes.insert(i)
|
||||
}
|
||||
|
||||
pub fn try_push_rotation_context(
|
||||
pub fn try_push_rotation(
|
||||
&mut self,
|
||||
context: ExodusContext<NetworkIdOf<T>>,
|
||||
network_type: NetworkType,
|
||||
exodus_context: ExodusContext<NetworkIdOf<T>>,
|
||||
) -> Option<()> {
|
||||
self.rotation_sessions.try_push(context).ok()
|
||||
let item = (network_type, exodus_context);
|
||||
self.rotations.try_push(item).ok()
|
||||
}
|
||||
|
||||
pub fn count_ones<N>(&self) -> N
|
||||
@ -229,10 +232,10 @@ where
|
||||
&self.authorities_indexes
|
||||
}
|
||||
|
||||
pub fn iter_rotation_contexts(
|
||||
pub fn iter_rotations(
|
||||
&self
|
||||
) -> impl Iterator<Item = &ExodusContext<NetworkIdOf<T>>> {
|
||||
self.rotation_sessions.iter()
|
||||
) -> impl Iterator<Item = &RotationItem<T>> {
|
||||
self.rotations.iter()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -6,8 +6,8 @@ use sp_runtime::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
EvmAddress, EvmBytes32, ExodusContext, RoastSession,
|
||||
MAX_MESSAGE_SIZE,
|
||||
EvmAddress, EvmBytes32, ExodusContext, RoastSession, EvmSessionsBundle,
|
||||
Config, BalanceOf, MAX_MESSAGE_SIZE,
|
||||
};
|
||||
|
||||
pub trait EvmAbiEncode {
|
||||
@ -308,24 +308,31 @@ impl GovernanceAction {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, Clone, Copy, PartialEq, TypeInfo, RuntimeDebug)]
|
||||
pub struct ExodusRequest<Balance> {
|
||||
pub r#type: ExodusRequestType<Balance>,
|
||||
#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, RuntimeDebug)]
|
||||
#[scale_info(skip_type_params(T))]
|
||||
pub struct ExodusRequest<T: Config> {
|
||||
pub r#type: ExodusRequestType<T>,
|
||||
pub next_roast_session: RoastSession,
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, Clone, Copy, PartialEq, TypeInfo, RuntimeDebug)]
|
||||
pub enum ExodusRequestType<Balance> {
|
||||
EvmRotation { public_key: EvmBytes32, parity: u8 },
|
||||
#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, RuntimeDebug)]
|
||||
#[scale_info(skip_type_params(T))]
|
||||
pub enum ExodusRequestType<T: Config> {
|
||||
EvmRotation {
|
||||
public_key: EvmBytes32,
|
||||
xor_key: u128,
|
||||
bitmask: u128,
|
||||
sessions: EvmSessionsBundle<T>,
|
||||
},
|
||||
UtxoRotation,
|
||||
EvmBridgeOut { amount: Balance, bounty: Perbill, receiver: EvmAddress },
|
||||
EvmBridgeOut { amount: BalanceOf<T>, bounty: Perbill, receiver: EvmAddress },
|
||||
EvmGovernance { target: EvmAddress, action: GovernanceAction },
|
||||
UtxoBridgeOut,
|
||||
}
|
||||
|
||||
impl<Balance> ExodusRequestType<Balance>
|
||||
impl<T: Config> ExodusRequestType<T>
|
||||
where
|
||||
Balance: UniqueSaturatedInto<u128> + Copy,
|
||||
BalanceOf<T>: UniqueSaturatedInto<u128> + Copy,
|
||||
{
|
||||
pub fn get_message<'a, NetworkId>(
|
||||
&self,
|
||||
@ -336,21 +343,19 @@ where
|
||||
NetworkId: UniqueSaturatedInto<u64> + Copy + Default,
|
||||
{
|
||||
match &self {
|
||||
ExodusRequestType::EvmRotation { public_key, parity } => {
|
||||
ExodusRequestType::EvmRotation { public_key, xor_key, bitmask, sessions } => {
|
||||
let mut buffer = [0u8; 32];
|
||||
buffer[0] = *parity;
|
||||
|
||||
let chain_id_u64: u64 = exodus_context.network_id
|
||||
.unique_saturated_into();
|
||||
buffer[..16].copy_from_slice(&xor_key.to_be_bytes());
|
||||
buffer[16..].copy_from_slice(&bitmask.to_be_bytes());
|
||||
let packed_bits = EvmBytes32::from(&buffer);
|
||||
|
||||
buffer[1..9].copy_from_slice(&chain_id_u64.to_be_bytes());
|
||||
|
||||
let packed_metadata = EvmBytes32::from(buffer);
|
||||
let payload = sessions.as_slice();
|
||||
|
||||
abi_encode_evm_message!(
|
||||
message_buffer,
|
||||
"rotate(uint256,bytes32,uint256)",
|
||||
[&exodus_context.session, public_key, &packed_metadata]
|
||||
"rotate(uint256,bytes32,uint256,bytes)",
|
||||
[&exodus_context.session, public_key, &packed_bits, payload]
|
||||
)
|
||||
},
|
||||
ExodusRequestType::EvmGovernance { target, action } => {
|
||||
@ -407,11 +412,39 @@ where
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_evm_rotation(&self) -> bool {
|
||||
match &self {
|
||||
ExodusRequestType::EvmRotation { .. } => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_utxo_rotation(&self) -> bool {
|
||||
match &self {
|
||||
ExodusRequestType::UtxoRotation { .. } => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_bridge_out(&self) -> bool {
|
||||
match &self {
|
||||
ExodusRequestType::EvmBridgeOut { .. } | ExodusRequestType::UtxoBridgeOut { .. } => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_evm_governance(&self) -> bool {
|
||||
match &self {
|
||||
ExodusRequestType::EvmGovernance { .. } => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Balance> ExodusRequest<Balance>
|
||||
impl<T: Config> ExodusRequest<T>
|
||||
where
|
||||
Balance: UniqueSaturatedInto<u128> + Copy,
|
||||
BalanceOf<T>: UniqueSaturatedInto<u128> + Copy,
|
||||
{
|
||||
pub fn get_message<'a, NetworkId>(
|
||||
&self,
|
||||
@ -424,15 +457,26 @@ where
|
||||
self.r#type.get_message(message_buffer, exodus_context)
|
||||
}
|
||||
|
||||
pub fn evm_rotation(public_key: EvmBytes32, parity: u8) -> Self {
|
||||
pub fn evm_rotation(
|
||||
public_key: EvmBytes32,
|
||||
xor_key: u128,
|
||||
bitmask: u128,
|
||||
sessions: EvmSessionsBundle<T>,
|
||||
) -> Self {
|
||||
let rotation = ExodusRequestType::EvmRotation {
|
||||
public_key,
|
||||
parity,
|
||||
xor_key,
|
||||
bitmask,
|
||||
sessions,
|
||||
};
|
||||
Self::default_request_for(rotation)
|
||||
}
|
||||
|
||||
pub fn evm_bridge_out(amount: Balance, bounty: Perbill, receiver: EvmAddress) -> Self {
|
||||
pub fn evm_bridge_out(
|
||||
amount: BalanceOf<T>,
|
||||
bounty: Perbill,
|
||||
receiver: EvmAddress
|
||||
) -> Self {
|
||||
let evm_bridge_out = ExodusRequestType::EvmBridgeOut {
|
||||
amount,
|
||||
bounty,
|
||||
@ -449,14 +493,7 @@ where
|
||||
Self::default_request_for(evm_governance)
|
||||
}
|
||||
|
||||
pub fn is_rotation(&self) -> bool {
|
||||
match &self.r#type {
|
||||
ExodusRequestType::EvmRotation { .. } | ExodusRequestType::UtxoRotation { .. } => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn default_request_for(request_type: ExodusRequestType<Balance>) -> Self {
|
||||
fn default_request_for(request_type: ExodusRequestType<T>) -> Self {
|
||||
Self {
|
||||
r#type: request_type,
|
||||
next_roast_session: Default::default()
|
||||
@ -467,6 +504,7 @@ where
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::mock::*;
|
||||
use sp_io::hashing::keccak_256;
|
||||
|
||||
#[test]
|
||||
@ -477,27 +515,45 @@ mod tests {
|
||||
let network_id = 420;
|
||||
|
||||
let context = ExodusContext::new(network_id, session);
|
||||
let dummy_xor_key = u128::MAX;
|
||||
let dummy_evm_bitmask = 1337;
|
||||
|
||||
let rotation_request = ExodusRequest::<u128>::evm_rotation(
|
||||
let mut dummy_packed_sessions =
|
||||
EvmSessionsBundle::<Runtime>::with_max_capacity();
|
||||
|
||||
let max_size = EvmSessionsBundle::<Runtime>::bound();
|
||||
dummy_packed_sessions.bounded_resize(max_size, 69);
|
||||
|
||||
let rotation_request = ExodusRequest::<Runtime>::evm_rotation(
|
||||
public_key.clone(),
|
||||
1,
|
||||
dummy_xor_key,
|
||||
dummy_evm_bitmask,
|
||||
dummy_packed_sessions,
|
||||
);
|
||||
|
||||
let calldata = rotation_request.get_message(&mut buffer, context).unwrap();
|
||||
assert_eq!(calldata.len(), 100);
|
||||
// prefix + public_key + packed_bits + pointer + length + bytes array + padding
|
||||
// 4 + 32 + 32 + 32 + 32 + 32 + (8 * 5) + (32 - 40 % 32)
|
||||
assert_eq!(calldata.len(), 228);
|
||||
|
||||
let expected_hash = keccak_256("rotate(uint256,bytes32,uint256)".as_bytes());
|
||||
let expected_hash = keccak_256("rotate(uint256,bytes32,uint256,bytes)".as_bytes());
|
||||
|
||||
assert_eq!(calldata[0..4], expected_hash[0..4]);
|
||||
assert_eq!(calldata[35], 0x45);
|
||||
assert_eq!(&calldata[4..35], &[0u8; 31]);
|
||||
assert_eq!(&calldata[36..68], public_key.as_ref());
|
||||
|
||||
assert_eq!(calldata[68], 1);
|
||||
assert_eq!(&calldata[69..75], &[0u8; 6]);
|
||||
assert_eq!(calldata[75], 0x01);
|
||||
assert_eq!(calldata[76], 0xA4);
|
||||
assert_eq!(&calldata[77..100], &[0u8; 23]);
|
||||
assert_eq!(&calldata[68..84], &dummy_xor_key.to_be_bytes());
|
||||
assert_eq!(&calldata[84..100], &dummy_evm_bitmask.to_be_bytes());
|
||||
|
||||
assert_eq!(&calldata[100..131], &[0u8; 31]);
|
||||
assert_eq!(calldata[131], 128);
|
||||
|
||||
assert_eq!(&calldata[132..163], &[0u8; 31]);
|
||||
assert_eq!(calldata[163], 40);
|
||||
|
||||
assert_eq!(calldata[164], 69);
|
||||
assert_eq!(&calldata[204..228], &[0u8; 24]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -509,8 +565,8 @@ mod tests {
|
||||
|
||||
let context = ExodusContext::new(network_id, session);
|
||||
|
||||
let bridge_request = ExodusRequest::<u128>::evm_bridge_out(
|
||||
1_000_000u128,
|
||||
let bridge_request = ExodusRequest::<Runtime>::evm_bridge_out(
|
||||
1_000_000u64,
|
||||
sp_runtime::Perbill::from_percent(1),
|
||||
receiver.clone()
|
||||
);
|
||||
@ -586,7 +642,7 @@ mod tests {
|
||||
let current_session = session + (index as u64);
|
||||
let context = ExodusContext::new(network_id, current_session);
|
||||
|
||||
let governance_request = ExodusRequest::<u128>::evm_governance(
|
||||
let governance_request = ExodusRequest::<Runtime>::evm_governance(
|
||||
target,
|
||||
action,
|
||||
);
|
||||
|
||||
@ -6,27 +6,28 @@ use sp_runtime::{
|
||||
traits::{AtLeast32BitUnsigned, UniqueSaturatedInto},
|
||||
};
|
||||
|
||||
use crate::ExodusContext;
|
||||
use crate::{BalanceOf, Config, ExodusContext};
|
||||
use super::ExodusRequestType;
|
||||
|
||||
type ExodusNonceT = BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>;
|
||||
type ExodusSignatureT = BoundedVec<u8, ConstU32<{ crate::SCALAR_MAX_BYTES }>>;
|
||||
|
||||
#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, RuntimeDebug)]
|
||||
pub struct ExodusSignedMessage<Balance: Default> {
|
||||
#[scale_info(skip_type_params(T))]
|
||||
pub struct ExodusSignedMessage<T: Config> {
|
||||
pub nonce: ExodusNonceT,
|
||||
pub signature: ExodusSignatureT,
|
||||
pub r#type: ExodusRequestType<Balance>,
|
||||
pub r#type: ExodusRequestType<T>,
|
||||
}
|
||||
|
||||
impl<Balance> ExodusSignedMessage<Balance>
|
||||
impl<T: Config> ExodusSignedMessage<T>
|
||||
where
|
||||
Balance: UniqueSaturatedInto<u128> + Copy + Default,
|
||||
BalanceOf<T>: UniqueSaturatedInto<u128> + Copy + Default,
|
||||
{
|
||||
pub fn new<E, S>(
|
||||
nonce_iter: E,
|
||||
signature_iter: S,
|
||||
r#type: ExodusRequestType<Balance>,
|
||||
r#type: ExodusRequestType<T>,
|
||||
) -> Option<Self>
|
||||
where
|
||||
E: Iterator<Item = u8> + ExactSizeIterator,
|
||||
@ -48,4 +49,17 @@ where
|
||||
) -> Option<&'a [u8]> {
|
||||
self.r#type.get_message(message_buffer, exodus_context)
|
||||
}
|
||||
|
||||
pub fn is_evm_rotation(&self) -> bool {
|
||||
self.r#type.is_evm_rotation()
|
||||
}
|
||||
pub fn is_utxo_rotation(&self) -> bool {
|
||||
self.r#type.is_utxo_rotation()
|
||||
}
|
||||
pub fn is_bridge_out(&self) -> bool {
|
||||
self.r#type.is_bridge_out()
|
||||
}
|
||||
pub fn is_evm_governance(&self) -> bool {
|
||||
self.r#type.is_evm_governance()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ghost-helpers"
|
||||
version = "0.0.13"
|
||||
version = "0.0.14"
|
||||
description = "Cryptographic utility suite for custom runtimes: optimized Bitmaps, UTXO parsing, Merkle Tree proofs, and Hash Chain components."
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
|
||||
452
pallets/helpers/src/lcg.rs
Normal file
452
pallets/helpers/src/lcg.rs
Normal file
@ -0,0 +1,452 @@
|
||||
use sp_runtime::traits::UniqueSaturatedInto;
|
||||
|
||||
macro_rules! generate_perfect_slot_mapper {
|
||||
(
|
||||
struct_name: $struct_name:ident,
|
||||
inner_type: $inner_type:ty,
|
||||
init_rng: $init_rng:expr,
|
||||
golden_ratio: $golden_ratio:expr,
|
||||
lcg_multiplier: $lcg_multiplier:expr,
|
||||
lcg_increment: $lcg_increment:expr,
|
||||
) => {
|
||||
pub struct $struct_name;
|
||||
|
||||
impl $struct_name {
|
||||
const TOTAL_BITS: usize = core::mem::size_of::<$inner_type>() * 8;
|
||||
|
||||
fn get_exact_iterations(items_count: usize) -> Option<usize> {
|
||||
let table_size = Self::TOTAL_BITS;
|
||||
|
||||
if items_count <= 1 { return Some(1); }
|
||||
if items_count > table_size { return None; }
|
||||
|
||||
let density_pct = (items_count * 100) / table_size;
|
||||
|
||||
match table_size {
|
||||
16 => match density_pct {
|
||||
0 => Some(1),
|
||||
1..=5 => Some(1), // max 0 items
|
||||
6..=10 => Some(1), // max 1 items
|
||||
11..=15 => Some(2), // max 2 items
|
||||
16..=20 => Some(2), // max 3 items
|
||||
21..=25 => Some(3), // max 4 items
|
||||
26..=30 => Some(3), // max 4 items
|
||||
31..=35 => Some(5), // max 5 items
|
||||
36..=40 => Some(8), // max 6 items
|
||||
41..=45 => Some(13), // max 7 items
|
||||
46..=50 => Some(24), // max 8 items
|
||||
51..=55 => Some(24), // max 8 items
|
||||
56..=60 => Some(49), // max 9 items
|
||||
61..=65 => Some(112), // max 10 items
|
||||
66..=70 => Some(301), // max 11 items
|
||||
71..=75 => Some(966), // max 12 items
|
||||
76..=80 => Some(966), // max 12 items
|
||||
81..=85 => Some(3868), // max 13 items
|
||||
86..=90 => Some(20633), // max 14 items
|
||||
91..=95 => Some(165075), // max 15 items
|
||||
_ => None,
|
||||
},
|
||||
32 => match density_pct {
|
||||
0 => Some(1),
|
||||
1..=5 => Some(1), // max 1 items
|
||||
6..=10 => Some(2), // max 3 items
|
||||
11..=15 => Some(2), // max 4 items
|
||||
16..=20 => Some(4), // max 6 items
|
||||
21..=25 => Some(7), // max 8 items
|
||||
26..=30 => Some(9), // max 9 items
|
||||
31..=35 => Some(20), // max 11 items
|
||||
36..=40 => Some(31), // max 12 items
|
||||
41..=45 => Some(85), // max 14 items
|
||||
46..=50 => Some(287), // max 16 items
|
||||
51..=55 => Some(575), // max 17 items
|
||||
56..=60 => Some(2807), // max 19 items
|
||||
61..=65 => Some(6912), // max 20 items
|
||||
66..=70 => Some(53627), // max 22 items
|
||||
71..=75 => Some(610170), // max 24 items
|
||||
_ => None,
|
||||
},
|
||||
64 => match density_pct {
|
||||
0 => Some(1),
|
||||
1..=5 => Some(1), // max 3 items
|
||||
6..=10 => Some(2), // max 6 items
|
||||
11..=15 => Some(4), // max 9 items
|
||||
16..=20 => Some(8), // max 12 items
|
||||
21..=25 => Some(22), // max 16 items
|
||||
26..=30 => Some(58), // max 19 items
|
||||
31..=35 => Some(180), // max 22 items
|
||||
36..=40 => Some(686), // max 25 items
|
||||
41..=45 => Some(3285), // max 28 items
|
||||
46..=50 => Some(38995), // max 32 items
|
||||
51..=55 => Some(343497), // max 35 items
|
||||
_ => None,
|
||||
},
|
||||
128 => match density_pct {
|
||||
0 => Some(1),
|
||||
1..=5 => Some(2), // max 6 items
|
||||
6..=10 => Some(4), // max 12 items
|
||||
11..=15 => Some(11), // max 19 items
|
||||
16..=20 => Some(36), // max 25 items
|
||||
21..=25 => Some(207), // max 32 items
|
||||
26..=30 => Some(1368), // max 38 items
|
||||
31..=35 => Some(13426), // max 44 items
|
||||
36..=40 => Some(331105), // max 51 items
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_bits_metadata() -> Option<($inner_type, $inner_type)> {
|
||||
let table_size = Self::TOTAL_BITS;
|
||||
let bits_needed = table_size.trailing_zeros() as usize;
|
||||
|
||||
let bit_shift_usize = table_size.checked_sub(bits_needed)?;
|
||||
let bit_shift = bit_shift_usize as $inner_type;
|
||||
let bit_mask = (table_size - 1) as $inner_type;
|
||||
|
||||
Some((bit_shift, bit_mask))
|
||||
}
|
||||
|
||||
pub fn get_lcg_xor_mask<V>(values: &[V]) -> Option<$inner_type>
|
||||
where
|
||||
V: UniqueSaturatedInto<$inner_type> + Copy,
|
||||
{
|
||||
let table_size = Self::TOTAL_BITS;
|
||||
|
||||
if values.len() > table_size { return None; }
|
||||
|
||||
let max_iterations = Self::get_exact_iterations(values.len())?;
|
||||
let (bit_shift, bit_mask) = Self::get_bits_metadata()?;
|
||||
|
||||
let mut lcg_rng_state: $inner_type = $init_rng;
|
||||
let mut xor_key: $inner_type = lcg_rng_state;
|
||||
let mut slots_taken = [false; Self::TOTAL_BITS];
|
||||
|
||||
for _ in 0..max_iterations {
|
||||
slots_taken.fill(false);
|
||||
let mut collision_found = false;
|
||||
|
||||
for value in values {
|
||||
let slot_index = Self::get_lcg_based_slot_index(
|
||||
bit_mask,
|
||||
bit_shift,
|
||||
xor_key,
|
||||
value
|
||||
);
|
||||
|
||||
if slot_index >= table_size || slots_taken[slot_index] {
|
||||
collision_found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
slots_taken[slot_index] = true;
|
||||
}
|
||||
|
||||
if !collision_found { return Some(xor_key); }
|
||||
|
||||
lcg_rng_state = lcg_rng_state
|
||||
.wrapping_mul($lcg_multiplier)
|
||||
.wrapping_add($lcg_increment);
|
||||
|
||||
xor_key = lcg_rng_state;
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get_lcg_based_slot_index<V>(
|
||||
bit_mask: $inner_type,
|
||||
bit_shift: $inner_type,
|
||||
xor_key: $inner_type,
|
||||
value: &V
|
||||
) -> usize
|
||||
where
|
||||
V: UniqueSaturatedInto<$inner_type> + Copy,
|
||||
{
|
||||
let converted_value: $inner_type = (*value).unique_saturated_into();
|
||||
let knuth_hash = (converted_value ^ xor_key).wrapping_mul($golden_ratio);
|
||||
|
||||
let shifted_hash = knuth_hash.checked_shr(bit_shift as u32).unwrap_or(knuth_hash);
|
||||
let slot_index_converted = shifted_hash & bit_mask;
|
||||
|
||||
slot_index_converted as usize
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
generate_perfect_slot_mapper!(
|
||||
struct_name: PerfectSlotMapperU16,
|
||||
inner_type: u16,
|
||||
|
||||
init_rng: 0x1234,
|
||||
golden_ratio: 0x9E37, // Computed with: 2^16 * ( (sqrt(5) - 1) / 2)
|
||||
lcg_multiplier: 25173, // Reference: Peter Grogono, Programming in Pascal, Section 4.5.
|
||||
lcg_increment: 13849, // http://engr.case.edu/merat_francis/eeap282f97/labs/Random_Lab.pdf
|
||||
);
|
||||
|
||||
generate_perfect_slot_mapper!(
|
||||
struct_name: PerfectSlotMapperU32,
|
||||
inner_type: u32,
|
||||
|
||||
init_rng: 0x12345678,
|
||||
golden_ratio: 0x9E3779B9, // Computd with: 2^32 * ( (sqrt(5) - 1) / 2)
|
||||
lcg_multiplier: 1664525, // Numerical Recipes `ranqd1`, Chapter 7.1,
|
||||
lcg_increment: 1013904223, // An Even Quicker Generator, Eq. 7.1.6
|
||||
);
|
||||
|
||||
generate_perfect_slot_mapper!(
|
||||
struct_name: PerfectSlotMapperU64,
|
||||
inner_type: u64,
|
||||
|
||||
init_rng: 0x123456789ABCDEF,
|
||||
golden_ratio: 0x9E3779B97F4A7C15, // Taken from `splitmix64`, this generator is specifically used in PostgreSQL
|
||||
lcg_multiplier: 6364136223846793005, // Newlib / Musl libc 64-bit LCG multiplier
|
||||
lcg_increment: 1, // https://sourceware.org/git/?p=newlib-cygwin.git;a=blob;f=newlib/libc/stdlib/rand.c
|
||||
);
|
||||
|
||||
generate_perfect_slot_mapper!(
|
||||
struct_name: PerfectSlotMapperU128,
|
||||
inner_type: u128,
|
||||
|
||||
init_rng: 0x123456789ABCDEF0123456789ABCDEF0,
|
||||
golden_ratio: 0x9E3779B97F4A7C15F39CC0605CEDC834, // Computd with: 2^128 * ( (sqrt(5) - 1) / 2) with python
|
||||
lcg_multiplier: 0x2360ED051FC65DA44385DF649FCCF645, // 2549297995355413924ULL,4865540595714422341ULL
|
||||
lcg_increment: 0x5851F42D4C957F2D14057B7EF767814F, // 6364136223846793005ULL,1442695040888963407ULL
|
||||
// https://github.com/imneme/pcg-cpp/blob/master/include/pcg_random.hpp
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod perfect_mapper_tests {
|
||||
use super::*;
|
||||
|
||||
macro_rules! generate_mapper_test {
|
||||
($mod_name:ident, $struct_name:ident, $init_rng:expr, $inner_type:ty) => {
|
||||
mod $mod_name {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_standard_pack_20_percentages() {
|
||||
const TABLE_SIZE: usize = $struct_name::TOTAL_BITS;
|
||||
const ITEMS_COUNT: usize = (TABLE_SIZE * 20) / 100;
|
||||
|
||||
let mut values = [0 as $inner_type; TABLE_SIZE];
|
||||
for i in 0..ITEMS_COUNT {
|
||||
values[i] = ((i as $inner_type).wrapping_mul(999)) ^ 0xABC;
|
||||
}
|
||||
|
||||
let filled_values = &values[0..ITEMS_COUNT];
|
||||
let mask = $struct_name::get_lcg_xor_mask(filled_values);
|
||||
|
||||
assert!(mask.is_some());
|
||||
let xor_key = mask.unwrap();
|
||||
|
||||
let bits_needed = TABLE_SIZE.trailing_zeros() as $inner_type;
|
||||
let bit_mask = (TABLE_SIZE - 1) as $inner_type;
|
||||
let bit_shift = (TABLE_SIZE as $inner_type) - bits_needed;
|
||||
|
||||
let mut slots = vec![false; TABLE_SIZE];
|
||||
|
||||
for val in filled_values {
|
||||
let idx = $struct_name::get_lcg_based_slot_index(
|
||||
bit_mask,
|
||||
bit_shift,
|
||||
xor_key,
|
||||
val
|
||||
);
|
||||
assert!(idx < TABLE_SIZE);
|
||||
assert!(!slots[idx]);
|
||||
slots[idx] = true;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_heavy_math() {
|
||||
const TABLE_SIZE: usize = $struct_name::TOTAL_BITS;
|
||||
|
||||
let max_val = <$inner_type>::MAX;
|
||||
let one_val = 1 as $inner_type;
|
||||
|
||||
let mut block_pattern = 0 as $inner_type;
|
||||
let mut noise_pattern = 0 as $inner_type;
|
||||
|
||||
let total_bytes = core::mem::size_of::<$inner_type>();
|
||||
let mut i = 0;
|
||||
while i < total_bytes {
|
||||
let byte_val = if i % 2 == 0 { 0x55 } else { 0xAA };
|
||||
noise_pattern = (noise_pattern << 8) | (byte_val as $inner_type);
|
||||
|
||||
let block_val = if i < total_bytes / 3 {
|
||||
0xEE
|
||||
} else if i < (total_bytes * 2) / 3 {
|
||||
0xFF
|
||||
} else {
|
||||
0xAA
|
||||
};
|
||||
block_pattern = (block_pattern << 8) | (block_val as $inner_type);
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
let center_bit_shift = (TABLE_SIZE / 2) as u32;
|
||||
let center_bit_pattern = (1 as $inner_type)
|
||||
.checked_shl(center_bit_shift)
|
||||
.unwrap_or(1);
|
||||
|
||||
let values: [$inner_type; 6] = [
|
||||
$init_rng,
|
||||
noise_pattern,
|
||||
max_val,
|
||||
one_val,
|
||||
center_bit_pattern,
|
||||
block_pattern
|
||||
];
|
||||
|
||||
let mask = $struct_name::get_lcg_xor_mask(&values);
|
||||
assert!(mask.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_single_element() {
|
||||
const TABLE_SIZE: usize = $struct_name::TOTAL_BITS;
|
||||
|
||||
let single_value = [420 as $inner_type];
|
||||
let mask = $struct_name::get_lcg_xor_mask(&single_value);
|
||||
assert!(mask.is_some());
|
||||
|
||||
let xor_key = mask.unwrap();
|
||||
|
||||
let bits_needed = TABLE_SIZE.trailing_zeros() as $inner_type;
|
||||
let bit_mask = (TABLE_SIZE - 1) as $inner_type;
|
||||
let bit_shift = (TABLE_SIZE as $inner_type) - bits_needed;
|
||||
|
||||
let idx = $struct_name::get_lcg_based_slot_index(
|
||||
bit_mask,
|
||||
bit_shift,
|
||||
xor_key,
|
||||
&single_value[0]
|
||||
);
|
||||
|
||||
assert!(idx < TABLE_SIZE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_bounds_returns_none() {
|
||||
const TABLE_SIZE: usize = $struct_name::TOTAL_BITS;
|
||||
let oversized_values = [1 as $inner_type; TABLE_SIZE];
|
||||
let mask = $struct_name::get_lcg_xor_mask(&oversized_values);
|
||||
assert!(mask.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_top_25_evm_chains_by_tvl() {
|
||||
const TABLE_SIZE: usize = $struct_name::TOTAL_BITS;
|
||||
|
||||
let evm_network_ids: [u64; 25] = [
|
||||
1, // Ethereum Mainnet
|
||||
8453, // Base
|
||||
56, // BNB Smart Chain
|
||||
42161, // Arbitrum One
|
||||
10143, // Monad
|
||||
137, // Polygon PoS
|
||||
43114, // Avalanche C-Chain
|
||||
10, // OP Mainnet
|
||||
57073, // Ink
|
||||
14, // Flare Network
|
||||
196, // X Layer
|
||||
25, // Cronos
|
||||
5000, // Mantle
|
||||
100, // Gnosis Chain
|
||||
59144, // Linea
|
||||
534352, // Scroll
|
||||
324, // zkSync Era
|
||||
146, // Sonic (Fantom)
|
||||
1116, // Core
|
||||
42220, // Celo
|
||||
1088, // Metis
|
||||
592, // Astar
|
||||
1284, // Moonbeam
|
||||
98865, // Plume Network
|
||||
60808, // BOB (Build on Bitcoin)
|
||||
];
|
||||
|
||||
let mask = $struct_name::get_lcg_xor_mask(&evm_network_ids);
|
||||
|
||||
if let Some(xor_key) = mask {
|
||||
let mut slots_check = [false; TABLE_SIZE];
|
||||
|
||||
let bits_needed = TABLE_SIZE.trailing_zeros() as $inner_type;
|
||||
let bit_mask = (TABLE_SIZE - 1) as $inner_type;
|
||||
let bit_shift = (TABLE_SIZE as $inner_type) - bits_needed;
|
||||
|
||||
for chain_id in &evm_network_ids {
|
||||
let slot_idx = $struct_name::get_lcg_based_slot_index(
|
||||
bit_mask,
|
||||
bit_shift,
|
||||
xor_key,
|
||||
chain_id
|
||||
);
|
||||
|
||||
assert!(slot_idx < TABLE_SIZE);
|
||||
assert!(!slots_check[slot_idx]);
|
||||
|
||||
slots_check[slot_idx] = true;
|
||||
}
|
||||
} else {
|
||||
assert!(TABLE_SIZE <= 32);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_top_25_evm_testnet_chains() {
|
||||
const TABLE_SIZE: usize = $struct_name::TOTAL_BITS;
|
||||
|
||||
let evm_network_ids: [u64; 10] = [
|
||||
63, // Mordor (Ethereum Classic Testnet)
|
||||
11155111, // Ethereum Sepolia
|
||||
80002, // Polygon Amoy
|
||||
421614, // Arbitrum Sepolia
|
||||
84532, // Base Sepolia
|
||||
11155420, // Optimism Sepolia
|
||||
59141, // Linea Sepolia
|
||||
534351, // Scroll Sepolia
|
||||
80084, // Berachain Bartio
|
||||
98864, // Plume Testnet
|
||||
];
|
||||
|
||||
let mask = $struct_name::get_lcg_xor_mask(&evm_network_ids);
|
||||
|
||||
if let Some(xor_key) = mask {
|
||||
let mut slots_check = [false; TABLE_SIZE];
|
||||
|
||||
let bits_needed = TABLE_SIZE.trailing_zeros() as $inner_type;
|
||||
let bit_mask = (TABLE_SIZE - 1) as $inner_type;
|
||||
let bit_shift = (TABLE_SIZE as $inner_type) - bits_needed;
|
||||
|
||||
for chain_id in &evm_network_ids {
|
||||
let slot_idx = $struct_name::get_lcg_based_slot_index(
|
||||
bit_mask,
|
||||
bit_shift,
|
||||
xor_key,
|
||||
chain_id
|
||||
);
|
||||
|
||||
assert!(slot_idx < TABLE_SIZE);
|
||||
assert!(!slots_check[slot_idx]);
|
||||
|
||||
slots_check[slot_idx] = true;
|
||||
}
|
||||
} else {
|
||||
assert!(TABLE_SIZE == 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
generate_mapper_test!(u16_mapper, PerfectSlotMapperU16, 0x1234, u16);
|
||||
generate_mapper_test!(u32_mapper, PerfectSlotMapperU32, 0x12345678, u32);
|
||||
generate_mapper_test!(u64_mapper, PerfectSlotMapperU64, 0x123456789ABCDEF, u64);
|
||||
generate_mapper_test!(u128_mapper, PerfectSlotMapperU128, 0x123456789ABCDEF0123456789ABCDEF0, u128);
|
||||
}
|
||||
@ -9,6 +9,7 @@ pub mod hash_chain;
|
||||
pub mod merkle_tree;
|
||||
pub mod networks;
|
||||
pub mod utxo;
|
||||
pub mod lcg;
|
||||
|
||||
pub type AuthIndexU8 = u8;
|
||||
pub type AuthIndexU16 = u16;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ghost-networks"
|
||||
version = "0.2.11"
|
||||
version = "0.2.12"
|
||||
description = "Registry and lifecycle management for external network metadata and cryptographic profiles."
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
|
||||
@ -805,6 +805,8 @@ impl<T: Config> NetworkDataBasicHandler for Pallet<T> {
|
||||
}
|
||||
|
||||
impl<T: Config> NetworkDataInspectHandler<NetworkData> for Pallet<T> {
|
||||
type TypesItem = BoundedVec<T::NetworkId, T::MaxNetworks>;
|
||||
|
||||
fn count() -> u32 {
|
||||
NetworkIndexes::<T>::decode_len()
|
||||
.map(|len| len as u32)
|
||||
@ -850,11 +852,11 @@ impl<T: Config> NetworkDataInspectHandler<NetworkData> for Pallet<T> {
|
||||
|
||||
fn iter_types_by_curve(
|
||||
curve: &Self::NetworkCurve
|
||||
) -> impl Iterator<Item = (T::NetworkId, Self::NetworkType)> {
|
||||
) -> impl Iterator<Item = (Self::NetworkType, Self::TypesItem)> {
|
||||
let network_types = NetworkTypes::<T>::get(curve);
|
||||
|
||||
network_types.into_iter().flat_map(|(network_type, network_ids)| {
|
||||
network_ids.into_iter().map(move |network_id| (network_id, network_type))
|
||||
network_types.into_iter().map(|(network_type, network_ids)| {
|
||||
(network_type, network_ids)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ghost-traits"
|
||||
version = "0.4.8"
|
||||
version = "0.4.9"
|
||||
description = "Shared traits including `GhostHasher`, `NetworkDataBasicHandler`, `BoundedBTreeMap`, `MerkleTree` and more."
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
|
||||
@ -36,6 +36,8 @@ pub trait NetworkDataBasicHandler {
|
||||
}
|
||||
|
||||
pub trait NetworkDataInspectHandler<Network>: NetworkDataBasicHandler {
|
||||
type TypesItem: AsRef<[Self::NetworkId]>;
|
||||
|
||||
fn count() -> u32;
|
||||
fn contains_key(n: &Self::NetworkId) -> bool;
|
||||
fn curve_exists(c: &Self::NetworkCurve) -> bool;
|
||||
@ -46,7 +48,7 @@ pub trait NetworkDataInspectHandler<Network>: NetworkDataBasicHandler {
|
||||
fn iter_curves() -> impl Iterator<Item = Self::NetworkCurve>;
|
||||
fn iter_types_by_curve(
|
||||
c: &Self::NetworkCurve
|
||||
) -> impl Iterator<Item = (Self::NetworkId, Self::NetworkType)>;
|
||||
) -> impl Iterator<Item = (Self::NetworkType, Self::TypesItem)>;
|
||||
}
|
||||
|
||||
pub trait NetworkDataMutateHandler<Network, Balance>: NetworkDataInspectHandler<Network> {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "casper-runtime"
|
||||
version = "4.0.14"
|
||||
version = "4.0.15"
|
||||
build = "build.rs"
|
||||
description = "Runtime of the Casper Network"
|
||||
edition.workspace = true
|
||||
@ -75,6 +75,7 @@ ghost-networks = { workspace = true }
|
||||
ghost-governance = { workspace = true }
|
||||
ghost-weaver = { workspace = true }
|
||||
ghost-exodus = { workspace = true }
|
||||
ghost-exodus-runtime-api = { workspace = true }
|
||||
ghost-sudo = { workspace = true }
|
||||
ghost-traits = { workspace = true }
|
||||
ghost-helpers = { workspace = true }
|
||||
@ -188,6 +189,7 @@ std = [
|
||||
"ghost-governance/std",
|
||||
"ghost-weaver/std",
|
||||
"ghost-exodus/std",
|
||||
"ghost-exodus-runtime-api/std",
|
||||
"ghost-sudo/std",
|
||||
"casper-runtime-constants/std",
|
||||
"runtime-common/std",
|
||||
|
||||
@ -69,6 +69,7 @@ pub use frame_system::Call as SystemCall;
|
||||
pub use pallet_balances::Call as BalancesCall;
|
||||
pub use pallet_election_provider_multi_phase::{Call as EPMCall, GeometricDepositBase};
|
||||
|
||||
use ghost_helpers::networks::{NetworkCurve, NetworkType};
|
||||
use ghost_weaver::sr25519::AuthorityId as GhostWeaverId;
|
||||
use ghost_exodus::sr25519::AuthorityId as GhostExodusId;
|
||||
|
||||
@ -123,7 +124,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||
spec_name: create_runtime_str!("casper"),
|
||||
impl_name: create_runtime_str!("casper-svengali"),
|
||||
authoring_version: 0,
|
||||
spec_version: 7,
|
||||
spec_version: 8,
|
||||
impl_version: 0,
|
||||
apis: RUNTIME_API_VERSIONS,
|
||||
transaction_version: 1,
|
||||
@ -906,6 +907,7 @@ parameter_types! {
|
||||
pub ExodusUnsignedPriority: TransactionPriority =
|
||||
Perbill::from_percent(90) * TransactionPriority::MAX;
|
||||
|
||||
pub MaxExodusRequestCount: u32 = 32;
|
||||
pub MaxAuthoritiesChunks: u32 = 32;
|
||||
pub WeavingDelay: u64 = 50;
|
||||
pub AttestationDelay: u64 = 25;
|
||||
@ -949,6 +951,7 @@ impl ghost_exodus::Config for Runtime {
|
||||
type UnsignedPriority = ExodusUnsignedPriority;
|
||||
type MaxAuthorities = MaxActiveValidators;
|
||||
type MaxAuthoritiesChunks = MaxAuthoritiesChunks;
|
||||
type MaxExodusRequestCount = MaxExodusRequestCount;
|
||||
|
||||
type DkgRoundPeriod = ExodusDkgRoundPeriod;
|
||||
type RemovalLimit = ExodusRemovalLimit;
|
||||
@ -1156,6 +1159,39 @@ sp_api::impl_runtime_apis! {
|
||||
}
|
||||
}
|
||||
|
||||
impl ghost_exodus_runtime_api::ExodusApi<Block> for Runtime {
|
||||
fn get_evm_rotation_metadata(
|
||||
dkg_index: u64,
|
||||
network_type: NetworkType,
|
||||
) -> ghost_exodus_runtime_api::PreparedEvmMessage {
|
||||
GhostExodus::api_get_evm_rotation(dkg_index, network_type)
|
||||
}
|
||||
|
||||
fn get_evm_bridge_metadata(
|
||||
dkg_index: u64,
|
||||
network_id: u64,
|
||||
session: u64,
|
||||
) -> ghost_exodus_runtime_api::PreparedEvmMessage {
|
||||
GhostExodus::api_get_evm_bridge(dkg_index, network_id, session)
|
||||
}
|
||||
|
||||
fn get_evm_governance_metadata(
|
||||
dkg_index: u64,
|
||||
network_id: u64,
|
||||
session: u64,
|
||||
) -> ghost_exodus_runtime_api::PreparedEvmMessage {
|
||||
GhostExodus::api_get_evm_governance(dkg_index, network_id, session)
|
||||
}
|
||||
|
||||
fn pending_exodus_request_count() -> u32 {
|
||||
GhostExodus::api_pending_exodus_request_count()
|
||||
}
|
||||
|
||||
fn is_qualification_ongoing(network_curve: NetworkCurve) -> bool {
|
||||
GhostExodus::api_is_qualification_ongoing(network_curve)
|
||||
}
|
||||
}
|
||||
|
||||
impl pallet_staking_runtime_api::StakingApi<Block, Balance, AccountId> for Runtime {
|
||||
fn nominations_quota(balance: Balance) -> u32 {
|
||||
Staking::api_nominations_quota(balance)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user