Compare commits

..

11 Commits

Author SHA1 Message Date
623a66eb28
update genesis file for new network bootstrap
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-09-25 15:59:54 +03:00
707e0a9c0d
improve error message clarity
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-09-25 15:41:27 +03:00
098ea0e75b
update weights for pallets and runtime
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-09-25 15:10:55 +03:00
ab37f862f5
integrate pallet changes into the runtime
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-09-25 14:23:47 +03:00
9f5a214cd5
receiver amount to existential deposit in benchmarks
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-09-25 14:22:22 +03:00
aa336d9c55
make proof size depend only on runtime constant
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-09-25 14:20:35 +03:00
352e3519a8
apply exponential scale for bridge out amount
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-09-25 12:23:38 +03:00
78bde93b7f
sync bond creation arguments with solidity implementation
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-09-24 13:34:17 +03:00
8ef28234ae
mitigate FROST/OCW DoS via dynamic bridge out threshold
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-09-23 21:17:53 +03:00
d3b926cd47
add support for partial claims of governance shares
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-09-23 19:44:08 +03:00
fde1f68dc0
migrate from legacy Currency trait to fungibles
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-09-18 15:13:06 +03:00
27 changed files with 782 additions and 320 deletions

9
Cargo.lock generated
View File

@ -1195,7 +1195,7 @@ dependencies = [
[[package]] [[package]]
name = "casper-runtime" name = "casper-runtime"
version = "4.0.11" version = "4.0.14"
dependencies = [ dependencies = [
"casper-runtime-constants", "casper-runtime-constants",
"frame-benchmarking", "frame-benchmarking",
@ -3684,7 +3684,7 @@ dependencies = [
[[package]] [[package]]
name = "ghost-exodus" name = "ghost-exodus"
version = "0.0.11" version = "0.0.17"
dependencies = [ dependencies = [
"chacha20poly1305", "chacha20poly1305",
"frame-benchmarking", "frame-benchmarking",
@ -3733,7 +3733,7 @@ dependencies = [
[[package]] [[package]]
name = "ghost-governance" name = "ghost-governance"
version = "0.3.7" version = "0.3.10"
dependencies = [ dependencies = [
"frame-benchmarking", "frame-benchmarking",
"frame-support", "frame-support",
@ -3743,6 +3743,7 @@ dependencies = [
"ghost-traits", "ghost-traits",
"hex-literal", "hex-literal",
"libsecp256k1", "libsecp256k1",
"num-traits",
"pallet-balances", "pallet-balances",
"parity-scale-codec", "parity-scale-codec",
"rustc-hex", "rustc-hex",
@ -4109,7 +4110,7 @@ dependencies = [
[[package]] [[package]]
name = "ghost-weaver" name = "ghost-weaver"
version = "0.0.10" version = "0.0.14"
dependencies = [ dependencies = [
"frame-benchmarking", "frame-benchmarking",
"frame-support", "frame-support",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ghost-exodus" name = "ghost-exodus"
version = "0.0.11" version = "0.0.17"
description = "Threshold signature generation with DKG included" description = "Threshold signature generation with DKG included"
license.workspace = true license.workspace = true
authors.workspace = true authors.workspace = true

View File

@ -488,7 +488,7 @@ mod benchmarks {
let dummy_nonce = vec![0u8; network_curve.element_bytes_len()]; let dummy_nonce = vec![0u8; network_curve.element_bytes_len()];
let create_bond = GovernanceAction::Create { let create_bond = GovernanceAction::Create {
market: [69, 420, 1337], market: [69, 420, 1337, 0],
terms: [420, 1337], terms: [420, 1337],
token: dummy_address, token: dummy_address,
intervals: [34, 35], intervals: [34, 35],
@ -506,7 +506,7 @@ mod benchmarks {
QualificationDkgState::<T>::insert(&network_curve, dkg_state); QualificationDkgState::<T>::insert(&network_curve, dkg_state);
ActiveDkgAuthorities::<T>::insert(&network_curve, active_dkg); ActiveDkgAuthorities::<T>::insert(&network_curve, active_dkg);
ActiveAuthorities::<T>::insert(&network_curve, authorities); ActiveAuthorities::<T>::insert(&network_curve, authorities);
ExodusRequests::<T>::insert(network_curve, exodus_session, request); ExodusRequests::<T>::insert((network_curve, exodus_session), request);
let nonce_package = PackageContext::default() let nonce_package = PackageContext::default()
.with_network_curve(NetworkCurve::Secp256k1) .with_network_curve(NetworkCurve::Secp256k1)
@ -566,7 +566,7 @@ mod benchmarks {
let dummy_group_commitment = vec![0u8; network_curve.element_bytes_len()]; let dummy_group_commitment = vec![0u8; network_curve.element_bytes_len()];
let create_bond = GovernanceAction::Create { let create_bond = GovernanceAction::Create {
market: [69, 420, 1337], market: [69, 420, 1337, 0],
terms: [420, 1337], terms: [420, 1337],
token: dummy_address, token: dummy_address,
intervals: [34, 35], intervals: [34, 35],
@ -584,7 +584,7 @@ mod benchmarks {
QualificationDkgState::<T>::insert(&network_curve, dkg_state); QualificationDkgState::<T>::insert(&network_curve, dkg_state);
ActiveDkgAuthorities::<T>::insert(&network_curve, active_dkg); ActiveDkgAuthorities::<T>::insert(&network_curve, active_dkg);
ActiveAuthorities::<T>::insert(&network_curve, authorities); ActiveAuthorities::<T>::insert(&network_curve, authorities);
ExodusRequests::<T>::insert(network_curve, exodus_session, request); ExodusRequests::<T>::insert((network_curve, exodus_session), request);
let mut roast_state = RoastSessionState::<ParticipantsBitmap::<T>>::default(); let mut roast_state = RoastSessionState::<ParticipantsBitmap::<T>>::default();
roast_state.status = RoastStatus::GroupCommitments; roast_state.status = RoastStatus::GroupCommitments;
@ -664,7 +664,7 @@ mod benchmarks {
let dummy_address = EvmAddress::repeat_byte(69); let dummy_address = EvmAddress::repeat_byte(69);
let create_bond = GovernanceAction::Create { let create_bond = GovernanceAction::Create {
market: [69, 420, 1337], market: [69, 420, 1337, 0],
terms: [420, 1337], terms: [420, 1337],
token: dummy_address, token: dummy_address,
intervals: [34, 35], intervals: [34, 35],
@ -723,7 +723,7 @@ mod benchmarks {
QualificationDkgState::<T>::insert(&network_curve, dkg_state); QualificationDkgState::<T>::insert(&network_curve, dkg_state);
ActiveDkgAuthorities::<T>::insert(&network_curve, active_dkg); ActiveDkgAuthorities::<T>::insert(&network_curve, active_dkg);
ActiveAuthorities::<T>::insert(&network_curve, authorities); ActiveAuthorities::<T>::insert(&network_curve, authorities);
ExodusRequests::<T>::insert(network_curve, exodus_session, request); ExodusRequests::<T>::insert((network_curve, exodus_session), request);
let mut roast_state = RoastSessionState::<ParticipantsBitmap::<T>>::default(); let mut roast_state = RoastSessionState::<ParticipantsBitmap::<T>>::default();
roast_state.status = RoastStatus::PartialSignatures; roast_state.status = RoastStatus::PartialSignatures;
@ -796,7 +796,7 @@ mod benchmarks {
assert!(!consensus_exists); assert!(!consensus_exists);
assert!(!committers_exists); assert!(!committers_exists);
assert!(!ExodusRequests::<T>::contains_key(&network_curve, &exodus_session)); assert!(!ExodusRequests::<T>::contains_key((&network_curve, &exodus_session)));
assert!(ExodusSignedGovernance::<T>::contains_key((&exodus_session, network_id), dkg_index)); assert!(ExodusSignedGovernance::<T>::contains_key((&exodus_session, network_id), dkg_index));
Ok(()) Ok(())
@ -808,7 +808,9 @@ mod benchmarks {
let (_, network_curve, _, _) = prepare_pallet::<T>(1usize); let (_, network_curve, _, _) = prepare_pallet::<T>(1usize);
let network_id = NetworkIdOf::<T>::default(); let network_id = NetworkIdOf::<T>::default();
let amount: BalanceOf<T> = 10_000u32.into(); let base_amount: BalanceOf<T> = T::BaseEvmAmount::get().unique_saturated_into();
let amount = base_amount.saturating_add(T::Currency::minimum_balance());
let bounty = Perbill::from_percent(5); let bounty = Perbill::from_percent(5);
let receiver = EvmAddress::from([69u8; 20]); let receiver = EvmAddress::from([69u8; 20]);
@ -819,14 +821,14 @@ mod benchmarks {
} }
}); });
let total_existential = T::Currency::minimum_balance(); let initial_balance = T::Currency::minimum_balance()
let initial_balance = total_existential
.saturating_mul(5u32.into()) .saturating_mul(5u32.into())
.saturating_add(amount); .saturating_add(amount);
let _ = T::Currency::make_free_balance_be(&caller, initial_balance); T::Currency::mint_into(&caller, initial_balance).unwrap();
let _ = T::NetworkDataHandler::register_incoming(&network_id, amount).unwrap(); let _ = T::NetworkDataHandler::register_incoming(&network_id, amount).unwrap();
let total_issuance = T::Currency::total_issuance();
let current_exodus_session = CurrentExodus::<T>::get(); let current_exodus_session = CurrentExodus::<T>::get();
#[extrinsic_call] #[extrinsic_call]
@ -838,9 +840,10 @@ mod benchmarks {
receiver, receiver,
); );
assert!(ExodusRequests::<T>::contains_key(&network_curve, current_exodus_session)); assert!(ExodusRequests::<T>::contains_key((&network_curve, current_exodus_session)));
assert_eq!(CurrentExodus::<T>::get(), current_exodus_session + 1); assert_eq!(CurrentExodus::<T>::get(), current_exodus_session + 1);
assert_eq!(T::Currency::free_balance(&caller), initial_balance - amount); assert_eq!(T::Currency::balance(&caller), initial_balance - amount);
assert_eq!(T::Currency::total_issuance(), total_issuance - amount);
Ok(()) Ok(())
} }

View File

@ -49,45 +49,45 @@ pub enum ExodusError {
impl core::fmt::Debug for ExodusError { impl core::fmt::Debug for ExodusError {
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
match *self { match *self {
ExodusError::InvalidParticipantId => write!(fmt, "Participant index could not be parsed to identifier."), ExodusError::InvalidParticipantId => write!(fmt, "Participant index could not be parsed as an identifier."),
ExodusError::DeserializationError => write!(fmt, "Error during deserializing value."), ExodusError::DeserializationError => write!(fmt, "Error while deserializing value."),
ExodusError::SerializationError => write!(fmt, "Error serializing value."), ExodusError::SerializationError => write!(fmt, "Error while serializing value."),
ExodusError::InvalidMinSigners(min, max) => write!(fmt, "Number of min_signers ({min}) must be at least 2 and not larger than max_signers ({max})."), ExodusError::InvalidMinSigners(min, max) => write!(fmt, "Number of min_signers ({min}) must be at least 2 and not larger than max_signers ({max})."),
ExodusError::InvalidMaxSigners(max_signers) => write!(fmt, "Number of max_signers must be at least 2, {max_signers} given."), ExodusError::InvalidMaxSigners(max_signers) => write!(fmt, "Number of max_signers must be at least 2, {max_signers} given."),
ExodusError::DKGNotSupported(ref curve) => write!(fmt, "The ciphersuite {curve:?} does not support DKG."), ExodusError::DKGNotSupported(ref curve) => write!(fmt, "The ciphersuite {curve:?} does not support DKG."),
ExodusError::IncorrectNumberOfCoefficients => write!(fmt, "Incorrect number of coefficients; should be equal to min signers."), ExodusError::IncorrectNumberOfCoefficients => write!(fmt, "Incorrect number of coefficients; should be equal to min signers."),
ExodusError::IncorrectNumberOfCommitments => write!(fmt, "Incorrect number of commitments; should be equal to min signers."), ExodusError::IncorrectNumberOfCommitments => write!(fmt, "Incorrect number of commitments; should be equal to min signers."),
ExodusError::InvalidNonceCommitments => write!(fmt, "Hiding nonce commitments indexes should match binding nonce commitments."), ExodusError::InvalidNonceCommitments => write!(fmt, "Hiding nonce commitment indices should match binding nonce commitments."),
ExodusError::InvalidProofOfKnowledge => write!(fmt, "The proof of knowledge is not valid."), ExodusError::InvalidProofOfKnowledge => write!(fmt, "Invalid proof of knowledge."),
ExodusError::IncorrectNumberOfPackages => write!(fmt, "Incorrect number of packages; should be equal to max signers."), ExodusError::IncorrectNumberOfPackages => write!(fmt, "Incorrect number of packages; should be equal to max signers."),
ExodusError::IncorrectPackage => write!(fmt, "The incorrect package was specified."), ExodusError::IncorrectPackage => write!(fmt, "An incorrect package was specified."),
ExodusError::PackageNotFound => write!(fmt, "Round 1 package not found for Round 2 participant."), ExodusError::PackageNotFound => write!(fmt, "Round 1 package not found for Round 2 participant."),
ExodusError::InvalidSecretShare => write!(fmt, "Invalid secret share."), ExodusError::InvalidSecretShare => write!(fmt, "Invalid secret share."),
ExodusError::MissingCommitment => write!(fmt, "The Signing Package must contain the participant's Commitments."), ExodusError::MissingCommitment => write!(fmt, "The signing package must contain the participant's Commitments."),
ExodusError::IncorrectCommitment => write!(fmt, "The participant's commitment is incorrect."), ExodusError::IncorrectCommitment => write!(fmt, "The participant's commitment is incorrect."),
ExodusError::UnknownIdentifier => write!(fmt, "Unknown identifier."), ExodusError::UnknownIdentifier => write!(fmt, "Unknown identifier provided."),
ExodusError::IdentityCommitment => write!(fmt, "Commitment equals the identity."), ExodusError::IdentityCommitment => write!(fmt, "Commitment equals the identity."),
ExodusError::DuplicatedIdentifier => write!(fmt, "Duplicated identifier."), ExodusError::DuplicatedIdentifier => write!(fmt, "Duplicated identifier."),
ExodusError::IncorrectNumberOfIdentifiers => write!(fmt, "Incorrect number of identifiers."), ExodusError::IncorrectNumberOfIdentifiers => write!(fmt, "Incorrect number of identifiers."),
ExodusError::InvalidSignature => write!(fmt, "Invalid signature."), ExodusError::InvalidSignature => write!(fmt, "Invalid signature provided."),
ExodusError::InvalidSignatureShare => write!(fmt, "Invalid signature share."), ExodusError::InvalidSignatureShare => write!(fmt, "Invalid signature share provided."),
ExodusError::EncryptionFailed => write!(fmt, "Encryption failed."), ExodusError::EncryptionFailed => write!(fmt, "Encryption failed."),
ExodusError::DecryptionFailed => write!(fmt, "Decryption failed."), ExodusError::DecryptionFailed => write!(fmt, "Decryption failed."),
ExodusError::HKDFFailed => write!(fmt, "HKDF failed."), ExodusError::HKDFFailed => write!(fmt, "HKDF failed."),
ExodusError::InvalidNonceLength => write!(fmt, "Encryption nonce has invalid length."), ExodusError::InvalidNonceLength => write!(fmt, "Encryption nonce has invalid length."),
ExodusError::MalformedSigningKey => write!(fmt, "Malformed signing key encoding."), ExodusError::MalformedSigningKey => write!(fmt, "Malformed signing key encoding."),
ExodusError::NoStoredNetworks => write!(fmt, "No stored networks to be worked on."), ExodusError::NoStoredNetworks => write!(fmt, "No stored networks to work on."),
ExodusError::OffchainTimeoutPeriod => write!(fmt, "Offchain request should be in-flight."), ExodusError::OffchainTimeoutPeriod => write!(fmt, "Offchain request should be in-flight."),
ExodusError::BadSignature(auth_index) => write!(fmt, "Signature could not be created from {auth_index}."), ExodusError::BadSignature(auth_index) => write!(fmt, "Signature could not be created from {auth_index}."),
ExodusError::TransactionSubmissionFailed(auth_index) => write!(fmt, "Could not submit transaction from {auth_index}."), ExodusError::TransactionSubmissionFailed(auth_index) => write!(fmt, "Could not submit transaction from {auth_index}."),
ExodusError::UnknownReceiverIndex(receiver_index) => write!(fmt, "Unknown receiver index {receiver_index} found."), ExodusError::UnknownReceiverIndex(receiver_index) => write!(fmt, "Unknown receiver index {receiver_index} found."),
ExodusError::CouldNotConstructMessage(auth_index) => write!(fmt, "Could not construct message during OCW from {auth_index}."), ExodusError::CouldNotConstructMessage(auth_index) => write!(fmt, "Could not construct message during OCW from {auth_index}."),
ExodusError::NothingToWrite => write!(fmt, "Nothing to write into local storage."), ExodusError::NothingToWrite => write!(fmt, "Nothing to write to local storage."),
ExodusError::UnknownPrefix => write!(fmt, "Unknown prefix used for local storage."), ExodusError::UnknownPrefix => write!(fmt, "Unknown prefix used for local storage."),
ExodusError::UnexpectedRound => write!(fmt, "Unexpected round provided."), ExodusError::UnexpectedRound => write!(fmt, "Unexpected round provided."),
ExodusError::InvalidMerkleProof => write!(fmt, "Invalid merkle proof provided."), ExodusError::InvalidMerkleProof => write!(fmt, "Invalid Merkle proof provided."),
ExodusError::InvalidIdentityElement => write!(fmt, "Invalid identity element provided."), ExodusError::InvalidIdentityElement => write!(fmt, "Invalid identity element provided."),
ExodusError::NoActiveAuthorities => write!(fmt, "No active authorities for signing exodus transactions."), ExodusError::NoActiveAuthorities => write!(fmt, "No active authorities for signing EXODUS transactions."),
ExodusError::Unknown => write!(fmt, "Unknown error."), ExodusError::Unknown => write!(fmt, "Unknown error."),
} }
} }

View File

@ -4,8 +4,9 @@
use frame_support::{ use frame_support::{
pallet_prelude::*, pallet_prelude::*,
traits::{ traits::{
Currency, DisabledValidators, ValidatorSet, ValidatorSetWithIdentification, tokens::{Preservation, Precision, Fortitude}, fungible::{Inspect, Mutate},
OneSessionHandler, WithdrawReasons, ExistenceRequirement, DisabledValidators, ValidatorSet, ValidatorSetWithIdentification,
OneSessionHandler,
}, },
}; };
use frame_system::{ use frame_system::{
@ -108,7 +109,7 @@ const EXODUS_PREFIX_SHARES: &[u8] = b"exodus-shares";
const EXODUS_PREFIX_BLENDS: &[u8] = b"exodus-blends"; const EXODUS_PREFIX_BLENDS: &[u8] = b"exodus-blends";
const EXODUS_PREFIX_EXILES: &[u8] = b"exodus-exiles"; const EXODUS_PREFIX_EXILES: &[u8] = b"exodus-exiles";
const MAX_MESSAGE_SIZE : u32 = 512; const MAX_MESSAGE_SIZE : u32 = 544;
const HEADER_MAX_BYTES : u32 = 1 + 4; const HEADER_MAX_BYTES : u32 = 1 + 4;
const ELEMENT_MAX_BYTES : u32 = 33; const ELEMENT_MAX_BYTES : u32 = 33;
const SCALAR_MAX_BYTES : u32 = 32; const SCALAR_MAX_BYTES : u32 = 32;
@ -223,11 +224,8 @@ type BitmapByAuthority<T> = BoundedBTreeMap<AuthIndex, ParticipantsBitmap<T>, <T
type QualifyingState<T> = PendingDkgAuthorities<ParticipantsBitmap<T>, BlockNumberFor<T>>; type QualifyingState<T> = PendingDkgAuthorities<ParticipantsBitmap<T>, BlockNumberFor<T>>;
type ActivatedState<T> = ReadyDkgAuthorities<ParticipantsBitmap<T>>; type ActivatedState<T> = ReadyDkgAuthorities<ParticipantsBitmap<T>>;
pub type BalanceOf<T> = pub type BalanceOf<T> = <<T as Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::Balance;
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance; pub type NetworkIdOf<T> = <<T as Config>::NetworkDataHandler as NetworkDataBasicHandler>::NetworkId;
pub type NetworkIdOf<T> =
<<T as Config>::NetworkDataHandler as NetworkDataBasicHandler>::NetworkId;
pub type ValidatorId<T> = <<T as Config>::ValidatorSet as ValidatorSet< pub type ValidatorId<T> = <<T as Config>::ValidatorSet as ValidatorSet<
<T as frame_system::Config>::AccountId, <T as frame_system::Config>::AccountId,
@ -394,7 +392,7 @@ pub mod pallet {
+ MaxEncodedLen; + MaxEncodedLen;
type ValidatorSet: ValidatorSetWithIdentification<Self::AccountId>; type ValidatorSet: ValidatorSetWithIdentification<Self::AccountId>;
type Currency: Currency<Self::AccountId>; type Currency: Inspect<Self::AccountId> + Mutate<Self::AccountId>;
type NetworkDataHandler: NetworkDataInspectHandler<NetworkData> type NetworkDataHandler: NetworkDataInspectHandler<NetworkData>
+ NetworkDataMutateHandler<NetworkData, BalanceOf<Self>> + NetworkDataMutateHandler<NetworkData, BalanceOf<Self>>
@ -425,6 +423,9 @@ pub mod pallet {
#[pallet::constant] #[pallet::constant]
type MaxCursorLen: Get<u32>; type MaxCursorLen: Get<u32>;
#[pallet::constant]
type BaseEvmAmount: Get<u128>;
type WeightInfo: WeightInfo; type WeightInfo: WeightInfo;
} }
@ -504,6 +505,7 @@ pub mod pallet {
NoActiveAuthorities, NoActiveAuthorities,
InvalidSignatureShare, InvalidSignatureShare,
VerifyingShareNotFound, VerifyingShareNotFound,
BridgeQueueMinimumNotMet,
DkgAuthoritiesInProgress, DkgAuthoritiesInProgress,
PackagesAlreadyRegistered, PackagesAlreadyRegistered,
DkgAuthoritiesNotInitialized, DkgAuthoritiesNotInitialized,
@ -552,10 +554,12 @@ pub mod pallet {
#[pallet::storage] #[pallet::storage]
#[pallet::getter(fn exodus_requests)] #[pallet::getter(fn exodus_requests)]
pub(super) type ExodusRequests<T: Config> = StorageDoubleMap< pub(super) type ExodusRequests<T: Config> = CountedStorageNMap<
_, _,
Twox64Concat, NetworkCurve, (
Twox64Concat, ExodusSession, NMapKey<Twox64Concat, NetworkCurve>,
NMapKey<Twox64Concat, ExodusSession>,
),
ExodusRequest<NetworkIdOf<T>, BalanceOf<T>>, ExodusRequest<NetworkIdOf<T>, BalanceOf<T>>,
OptionQuery, OptionQuery,
>; >;
@ -1451,7 +1455,7 @@ pub mod pallet {
exodus_request.next_roast_session = roast_session exodus_request.next_roast_session = roast_session
.saturating_add(1); .saturating_add(1);
ExodusRequests::<T>::insert(&network_curve, &exodus_session, exodus_request); ExodusRequests::<T>::insert((&network_curve, &exodus_session), exodus_request);
} }
RoastSessions::<T>::insert(&exodus_session, &authority_index, roast_session); RoastSessions::<T>::insert(&exodus_session, &authority_index, roast_session);
@ -1509,7 +1513,7 @@ pub mod pallet {
let exodus_session = package.session; let exodus_session = package.session;
ensure!( ensure!(
ExodusRequests::<T>::contains_key(&network_curve, &exodus_session), ExodusRequests::<T>::contains_key((&network_curve, &exodus_session)),
Error::<T>::ExodusRequestNotFound Error::<T>::ExodusRequestNotFound
); );
@ -1641,7 +1645,7 @@ pub mod pallet {
); );
let exodus_request = let exodus_request =
ExodusRequests::<T>::get(&network_curve, &exodus_session) ExodusRequests::<T>::get((&network_curve, &exodus_session))
.ok_or(Error::<T>::ExodusRequestNotFound)?; .ok_or(Error::<T>::ExodusRequestNotFound)?;
ensure!( ensure!(
@ -1796,7 +1800,7 @@ pub mod pallet {
return Ok(()); return Ok(());
} }
ExodusRequests::<T>::remove(&network_curve, &exodus_session); ExodusRequests::<T>::remove((&network_curve, &exodus_session));
match exodus_request.r#type { match exodus_request.r#type {
ExodusRequestType::EvmRotation { .. } => { ExodusRequestType::EvmRotation { .. } => {
@ -1839,16 +1843,31 @@ pub mod pallet {
) -> DispatchResult { ) -> DispatchResult {
let who = ensure_signed(origin)?; let who = ensure_signed(origin)?;
let _imbalance = T::Currency::withdraw( let base_evm_amount: BalanceOf<T> = T::BaseEvmAmount::get()
.unique_saturated_into();
let is_amount_sufficient = 1u32.checked_shl(ExodusRequests::<T>::count())
.map(|scale| {
T::Currency::minimum_balance()
.saturating_mul(scale.unique_saturated_into())
.saturating_add(base_evm_amount)
.le(&amount)
})
.unwrap_or(false);
ensure!(is_amount_sufficient, Error::<T>::BridgeQueueMinimumNotMet);
let actual_amount = T::Currency::burn_from(
&who, &who,
amount, amount,
WithdrawReasons::TRANSFER, Preservation::Expendable,
ExistenceRequirement::AllowDeath, Precision::Exact,
Fortitude::Polite,
)?; )?;
Self::do_register_evm_bridge_out_exodus( Self::do_register_evm_bridge_out_exodus(
network_id, network_id,
amount, actual_amount,
bounty, bounty,
receiver, receiver,
)?; )?;
@ -1856,7 +1875,7 @@ pub mod pallet {
Self::deposit_event(Event::<T>::EvmBridgeOutRegistered { Self::deposit_event(Event::<T>::EvmBridgeOutRegistered {
who, who,
network_id, network_id,
amount, amount: actual_amount,
bounty, bounty,
receiver, receiver,
}); });
@ -1953,7 +1972,7 @@ pub mod pallet {
}, },
Err(err) => { Err(err) => {
log::error!(target: PALLET_LOG_TARGET, "🗺️ Exodus skipped or failed initialization at {:?}: {:?}", current_block, err); log::error!(target: PALLET_LOG_TARGET, "🗺️ Exodus skipped at {:?}: {:?}", current_block, err);
} }
} }
} }
@ -2146,7 +2165,7 @@ impl<T: Config> Pallet<T> {
let exodus_session = CurrentExodus::<T>::get(); let exodus_session = CurrentExodus::<T>::get();
let request = request_builder(exodus_session); let request = request_builder(exodus_session);
ExodusRequests::<T>::insert(&network.curve, &exodus_session, request); ExodusRequests::<T>::insert((&network.curve, &exodus_session), request);
CurrentExodus::<T>::put(exodus_session.saturating_add(1)); CurrentExodus::<T>::put(exodus_session.saturating_add(1));
Ok(()) Ok(())
@ -2561,7 +2580,7 @@ impl<T: Config> Pallet<T> {
}; };
weight.saturating_accrue(T::DbWeight::get().writes(1)); weight.saturating_accrue(T::DbWeight::get().writes(1));
ExodusRequests::<T>::insert(&network_curve, &exodus_session, exodus_request); ExodusRequests::<T>::insert((&network_curve, &exodus_session), exodus_request);
if let None = dkg_qualification if let None = dkg_qualification
.try_push_rotation_session(exodus_session, network_type) { .try_push_rotation_session(exodus_session, network_type) {
@ -2602,7 +2621,7 @@ impl<T: Config> Pallet<T> {
let r#type = rotation_session.network_type; let r#type = rotation_session.network_type;
weight.saturating_accrue(T::DbWeight::get().reads(1)); weight.saturating_accrue(T::DbWeight::get().reads(1));
if ExodusRequests::<T>::contains_key(&network_curve, &session) { if ExodusRequests::<T>::contains_key((&network_curve, &session)) {
return false return false
}; };
@ -3083,7 +3102,7 @@ impl<T: Config> Pallet<T> {
} }
let exodus_request = let exodus_request =
ExodusRequests::<T>::get(&network_curve, &package.session) ExodusRequests::<T>::get((&network_curve, &package.session))
.ok_or(InvalidTransaction::BadProof)?; .ok_or(InvalidTransaction::BadProof)?;
let roast_session = let roast_session =
@ -3281,7 +3300,7 @@ impl<T: Config> Pallet<T> {
let threshold = get_byzantium_quorum_threshold(max_authorities); let threshold = get_byzantium_quorum_threshold(max_authorities);
let mut exodus_requests = ExodusRequests::<T>::iter_prefix(&network_curve) let mut exodus_requests = ExodusRequests::<T>::iter_prefix((&network_curve,))
.collect::<Vec<(ExodusSession, ExodusRequest<_, _>)>>(); .collect::<Vec<(ExodusSession, ExodusRequest<_, _>)>>();
exodus_requests.sort_by_key(|&(session, _)| session); exodus_requests.sort_by_key(|&(session, _)| session);

View File

@ -3,7 +3,7 @@
use frame_system::EnsureRoot; use frame_system::EnsureRoot;
use frame_support::{ use frame_support::{
derive_impl, parameter_types, derive_impl, parameter_types,
traits::{ConstU32, ConstU64}, traits::{ConstU32, ConstU64, ConstU128},
}; };
use crate as ghost_exodus; use crate as ghost_exodus;
@ -143,6 +143,7 @@ impl ghost_exodus::Config for Runtime {
type DkgRoundPeriod = ConstU64<100>; type DkgRoundPeriod = ConstU64<100>;
type RemovalLimit = ConstU32<20>; type RemovalLimit = ConstU32<20>;
type MaxCursorLen = ConstU32<20>; type MaxCursorLen = ConstU32<20>;
type BaseEvmAmount = ConstU128<5>;
type WeightInfo = (); type WeightInfo = ();
} }

View File

@ -810,7 +810,7 @@ fn run_dkg_session(
return Err(TestError::InvalidStorageAfter); return Err(TestError::InvalidStorageAfter);
} }
if ExodusRequests::<Runtime>::contains_key(curve, exodus) { if ExodusRequests::<Runtime>::contains_key((curve, exodus)) {
let roast_state = RoastSessionStates::<Runtime>::get(exodus, roast); let roast_state = RoastSessionStates::<Runtime>::get(exodus, roast);
if !roast_state.partial_signers.contains(idx) if !roast_state.partial_signers.contains(idx)
|| !roast_state.nonce_committers.contains(idx) || !roast_state.nonce_committers.contains(idx)
@ -854,11 +854,7 @@ fn run_dkg_session(
let throttled = prev_authorities.last().copied().unwrap(); let throttled = prev_authorities.last().copied().unwrap();
let prev_len = prev_authorities.len() as AuthIndex; let prev_len = prev_authorities.len() as AuthIndex;
let mut start = 0;
loop { loop {
if start >= 100 { break; }
start += 1;
let mut current_pending_requests: Vec<ExodusSession> = ext.execute_with(|| { let mut current_pending_requests: Vec<ExodusSession> = ext.execute_with(|| {
ExodusRequests::<Runtime>::iter_keys() ExodusRequests::<Runtime>::iter_keys()
.filter_map(|(current_curve, exodus_session)| { .filter_map(|(current_curve, exodus_session)| {
@ -881,7 +877,7 @@ fn run_dkg_session(
for auth in prev_authorities.iter() { for auth in prev_authorities.iter() {
let request_exists = ext.execute_with(|| { let request_exists = ext.execute_with(|| {
ExodusRequests::<Runtime>::contains_key(&curve, &exodus_session) ExodusRequests::<Runtime>::contains_key((&curve, &exodus_session))
}); });
if !request_exists { break; } if !request_exists { break; }
@ -1070,7 +1066,7 @@ fn test_all_validators_can_execute_dkg() {
let remove_pool = GovernanceAction::RemovePool { index: 1337 }; let remove_pool = GovernanceAction::RemovePool { index: 1337 };
let close = GovernanceAction::Close { id: 420 }; let close = GovernanceAction::Close { id: 420 };
let create = GovernanceAction::Create { let create = GovernanceAction::Create {
market: [69, 420, 1337], market: [69, 420, 1337, 0],
terms: [420, 1337], terms: [420, 1337],
token: dummy_address, token: dummy_address,
intervals: [34, 35], intervals: [34, 35],
@ -1136,9 +1132,14 @@ fn test_all_validators_can_execute_dkg() {
ExodusSignedGovernance::<Runtime>::iter().count() ExodusSignedGovernance::<Runtime>::iter().count()
}); });
let pending_exodus_requests = ext.execute_with(|| {
ExodusRequests::<Runtime>::count()
});
assert_eq!(rotations_count, 3); assert_eq!(rotations_count, 3);
assert_eq!(bridges_count, 2); assert_eq!(bridges_count, 2);
assert_eq!(governances_count, 15); assert_eq!(governances_count, 15);
assert_eq!(pending_exodus_requests, 0);
} }
#[test] #[test]
@ -1446,6 +1447,124 @@ fn test_prepare_round5_kick_on_empty_justifications() {
assert_eq!(indexes.active_bits_count(), authorities_len as u32); assert_eq!(indexes.active_bits_count(), authorities_len as u32);
} }
#[test]
fn register_evm_bridge_out_dynamic_threshold_works() {
let (mut ext, _) = new_test_ext();
ext.execute_with(|| {
let network_curve = NetworkCurve::Secp256k1;
let network_id = 69u64;
let user = 420u64;
let receiver = EvmAddress::from([2u8; 20]);
let bounty = Perbill::from_percent(10);
let initial_balance: BalanceOf<Runtime> = 100_000u128.unique_saturated_into();
let _ = <Runtime as Config>::Currency::set_balance(&user, initial_balance);
let base_evm_amount_u128 = <<Runtime as Config>::BaseEvmAmount as Get<u128>>::get();
let base_evm_amount: BalanceOf<Runtime> = base_evm_amount_u128.unique_saturated_into();
let existential_deposit = <Runtime as Config>::Currency::minimum_balance();
let network_data = NetworkDataBuilder::default()
.with_network_curve(network_curve)
.build();
let _ = <Runtime as Config>::NetworkDataHandler::register(network_id, network_data).unwrap();
let _ = <Runtime as Config>::NetworkDataHandler::register_incoming(&network_id, 1337).unwrap();
let mut dkg_state = PendingDkgAuthorities::default();
loop {
if dkg_state.is_dkg_finalized() { break; }
dkg_state.next_phase();
}
QualificationDkgState::<Runtime>::insert(&network_curve, dkg_state);
let amount_1 = base_evm_amount.saturating_add(existential_deposit);
assert_ok!(Exodus::register_evm_bridge_out_exodus(
RuntimeOrigin::signed(user.clone()),
network_id,
amount_1,
bounty,
receiver,
));
assert_eq!(ExodusRequests::<Runtime>::count(), 1);
System::assert_last_event(RuntimeEvent::Exodus(
Event::EvmBridgeOutRegistered {
who: user.clone(),
network_id,
amount: amount_1,
bounty,
receiver,
}
));
assert_err!(
Exodus::register_evm_bridge_out_exodus(
RuntimeOrigin::signed(user.clone()),
network_id,
amount_1,
bounty,
receiver,
),
Error::<Runtime>::BridgeQueueMinimumNotMet
);
let amount_2 = existential_deposit
.saturating_mul(2)
.saturating_add(base_evm_amount);
assert_ok!(Exodus::register_evm_bridge_out_exodus(
RuntimeOrigin::signed(user.clone()),
network_id,
amount_2,
bounty,
receiver,
));
assert_err!(
Exodus::register_evm_bridge_out_exodus(
RuntimeOrigin::signed(user.clone()),
network_id,
amount_2,
bounty,
receiver,
),
Error::<Runtime>::BridgeQueueMinimumNotMet
);
assert_eq!(ExodusRequests::<Runtime>::count(), 2);
let amount_3 = existential_deposit
.saturating_mul(4)
.saturating_add(base_evm_amount);
assert_ok!(Exodus::register_evm_bridge_out_exodus(
RuntimeOrigin::signed(user.clone()),
network_id,
amount_3,
bounty,
receiver,
));
assert_eq!(ExodusRequests::<Runtime>::count(), 3);
assert_err!(
Exodus::register_evm_bridge_out_exodus(
RuntimeOrigin::signed(user.clone()),
network_id,
amount_3,
bounty,
receiver,
),
Error::<Runtime>::BridgeQueueMinimumNotMet
);
});
}
#[test] #[test]
fn test_merkle_tree_success_power_of_two_for_shares() { fn test_merkle_tree_success_power_of_two_for_shares() {
let network_curve = NetworkCurve::Secp256k1; let network_curve = NetworkCurve::Secp256k1;

View File

@ -133,7 +133,7 @@ macro_rules! impl_evm_encodable {
impl_evm_encodable!(numeric: u8, u16, u32, u64, u128); impl_evm_encodable!(numeric: u8, u16, u32, u64, u128);
impl_evm_encodable!(bytes_fixed: EvmAddress, EvmBytes32); impl_evm_encodable!(bytes_fixed: EvmAddress, EvmBytes32);
impl_evm_encodable!(const_array: [u128; 3], [u128; 2], [u32; 2], [u8; 2]); impl_evm_encodable!(const_array: [u128; 4], [u128; 2], [u32; 2], [u8; 2]);
impl_evm_encodable!(bytes_slice:); impl_evm_encodable!(bytes_slice:);
macro_rules! abi_encode_evm_message { macro_rules! abi_encode_evm_message {
@ -192,7 +192,7 @@ pub enum GovernanceAction {
Close { id: u128 }, Close { id: u128 },
Create { Create {
market: [u128; 3], market: [u128; 4],
terms: [u128; 2], terms: [u128; 2],
token: EvmAddress, token: EvmAddress,
intervals: [u32; 2], intervals: [u32; 2],
@ -300,7 +300,7 @@ impl GovernanceAction {
let booleans_u8 = [booleans[0] as u8, booleans[1] as u8]; let booleans_u8 = [booleans[0] as u8, booleans[1] as u8];
abi_encode_evm_message!( abi_encode_evm_message!(
message_buffer, message_buffer,
"create(uint256[3],uint256[2],address,uint32[2],bool[2])", "create(uint256[4],uint256[2],address,uint32[2],bool[2])",
[market, terms, token, intervals, &booleans_u8] [market, terms, token, intervals, &booleans_u8]
) )
}, },
@ -582,7 +582,7 @@ mod tests {
GovernanceAction::Close { id: 99 }, GovernanceAction::Close { id: 99 },
GovernanceAction::Create { GovernanceAction::Create {
market: [69, 420, 1337], market: [69, 420, 1337, 0],
terms: [69, 420], terms: [69, 420],
token: EvmAddress::repeat_byte(0x99), token: EvmAddress::repeat_byte(0x99),
intervals: [69, 1337], intervals: [69, 1337],
@ -706,25 +706,26 @@ mod tests {
assert_eq!(inner_payload[inner_payload.len() - 1], id as u8); assert_eq!(inner_payload[inner_payload.len() - 1], id as u8);
}, },
GovernanceAction::Create { market, terms, token, intervals, booleans } => { GovernanceAction::Create { market, terms, token, intervals, booleans } => {
let hash = keccak_256("create(uint256[3],uint256[2],address,uint32[2],bool[2])".as_bytes()); let hash = keccak_256("create(uint256[4],uint256[2],address,uint32[2],bool[2])".as_bytes());
assert_eq!(inner_payload[0..4], hash[0..4]); assert_eq!(inner_payload[0..4], hash[0..4]);
assert_eq!(inner_payload.len(), 324); assert_eq!(inner_payload.len(), 356);
assert_eq!(inner_payload[35], 69); assert_eq!(inner_payload[35], 69);
assert_eq!(u128::from_be_bytes(inner_payload[20..36].try_into().unwrap()), market[0]); assert_eq!(u128::from_be_bytes(inner_payload[20..36].try_into().unwrap()), market[0]);
assert_eq!(u128::from_be_bytes(inner_payload[52..68].try_into().unwrap()), market[1]); assert_eq!(u128::from_be_bytes(inner_payload[52..68].try_into().unwrap()), market[1]);
assert_eq!(u128::from_be_bytes(inner_payload[84..100].try_into().unwrap()), market[2]); assert_eq!(u128::from_be_bytes(inner_payload[84..100].try_into().unwrap()), market[2]);
assert_eq!(u128::from_be_bytes(inner_payload[116..132].try_into().unwrap()), market[3]);
assert_eq!(u128::from_be_bytes(inner_payload[116..132].try_into().unwrap()), terms[0]); assert_eq!(u128::from_be_bytes(inner_payload[148..164].try_into().unwrap()), terms[0]);
assert_eq!(u128::from_be_bytes(inner_payload[148..164].try_into().unwrap()), terms[1]); assert_eq!(u128::from_be_bytes(inner_payload[180..196].try_into().unwrap()), terms[1]);
assert_eq!(&inner_payload[176..196], token.as_ref()); assert_eq!(&inner_payload[208..228], token.as_ref());
assert_eq!(u32::from_be_bytes(inner_payload[224..228].try_into().unwrap()), intervals[0]); assert_eq!(u32::from_be_bytes(inner_payload[256..260].try_into().unwrap()), intervals[0]);
assert_eq!(u32::from_be_bytes(inner_payload[256..260].try_into().unwrap()), intervals[1]); assert_eq!(u32::from_be_bytes(inner_payload[288..292].try_into().unwrap()), intervals[1]);
assert_eq!(inner_payload[291], booleans[0] as u8); assert_eq!(inner_payload[323], booleans[0] as u8);
assert_eq!(inner_payload[323], booleans[1] as u8); assert_eq!(inner_payload[355], booleans[1] as u8);
} }
} }
} }

View File

@ -16,7 +16,7 @@
//! Autogenerated weights for `ghost_exodus` //! Autogenerated weights for `ghost_exodus`
//! //!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
//! DATE: 2026-09-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! DATE: 2026-09-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000` //! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `ghostown`, CPU: `Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz` //! HOSTNAME: `ghostown`, CPU: `Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz`
//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("casper-dev")`, DB CACHE: 1024 //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("casper-dev")`, DB CACHE: 1024
@ -77,8 +77,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `351` // Measured: `351`
// Estimated: `3816` // Estimated: `3816`
// Minimum execution time: 239_012_000 picoseconds. // Minimum execution time: 240_813_000 picoseconds.
Weight::from_parts(241_388_000, 0) Weight::from_parts(243_523_000, 0)
.saturating_add(Weight::from_parts(0, 3816)) .saturating_add(Weight::from_parts(0, 3816))
.saturating_add(RocksDbWeight::get().reads(4)) .saturating_add(RocksDbWeight::get().reads(4))
.saturating_add(RocksDbWeight::get().writes(3)) .saturating_add(RocksDbWeight::get().writes(3))
@ -100,11 +100,11 @@ impl WeightInfo for () {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `391 + c * (32 ±0)` // Measured: `391 + c * (32 ±0)`
// Estimated: `3856 + c * (32 ±0)` // Estimated: `3856 + c * (32 ±0)`
// Minimum execution time: 1_339_828_000 picoseconds. // Minimum execution time: 1_356_791_000 picoseconds.
Weight::from_parts(1_212_625_790, 0) Weight::from_parts(1_221_760_215, 0)
.saturating_add(Weight::from_parts(0, 3856)) .saturating_add(Weight::from_parts(0, 3856))
// Standard Error: 11_831 // Standard Error: 16_481
.saturating_add(Weight::from_parts(33_120_868, 0).saturating_mul(c.into())) .saturating_add(Weight::from_parts(33_163_393, 0).saturating_mul(c.into()))
.saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().reads(6))
.saturating_add(RocksDbWeight::get().writes(2)) .saturating_add(RocksDbWeight::get().writes(2))
.saturating_add(Weight::from_parts(0, 32).saturating_mul(c.into())) .saturating_add(Weight::from_parts(0, 32).saturating_mul(c.into()))
@ -124,11 +124,11 @@ impl WeightInfo for () {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `317 + c * (257 ±0)` // Measured: `317 + c * (257 ±0)`
// Estimated: `3783 + c * (257 ±0)` // Estimated: `3783 + c * (257 ±0)`
// Minimum execution time: 251_478_000 picoseconds. // Minimum execution time: 254_590_000 picoseconds.
Weight::from_parts(254_144_621, 0) Weight::from_parts(258_091_105, 0)
.saturating_add(Weight::from_parts(0, 3783)) .saturating_add(Weight::from_parts(0, 3783))
// Standard Error: 24_613 // Standard Error: 25_082
.saturating_add(Weight::from_parts(6_595_819, 0).saturating_mul(c.into())) .saturating_add(Weight::from_parts(6_595_878, 0).saturating_mul(c.into()))
.saturating_add(RocksDbWeight::get().reads(5)) .saturating_add(RocksDbWeight::get().reads(5))
.saturating_add(RocksDbWeight::get().writes(2)) .saturating_add(RocksDbWeight::get().writes(2))
.saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into())) .saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into()))
@ -148,11 +148,11 @@ impl WeightInfo for () {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `317 + c * (257 ±0)` // Measured: `317 + c * (257 ±0)`
// Estimated: `3783 + c * (257 ±0)` // Estimated: `3783 + c * (257 ±0)`
// Minimum execution time: 236_155_000 picoseconds. // Minimum execution time: 240_888_000 picoseconds.
Weight::from_parts(242_055_948, 0) Weight::from_parts(245_480_975, 0)
.saturating_add(Weight::from_parts(0, 3783)) .saturating_add(Weight::from_parts(0, 3783))
// Standard Error: 15_623 // Standard Error: 13_733
.saturating_add(Weight::from_parts(360_411, 0).saturating_mul(c.into())) .saturating_add(Weight::from_parts(371_161, 0).saturating_mul(c.into()))
.saturating_add(RocksDbWeight::get().reads(5)) .saturating_add(RocksDbWeight::get().reads(5))
.saturating_add(RocksDbWeight::get().writes(2)) .saturating_add(RocksDbWeight::get().writes(2))
.saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into())) .saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into()))
@ -172,11 +172,11 @@ impl WeightInfo for () {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `317 + c * (257 ±0)` // Measured: `317 + c * (257 ±0)`
// Estimated: `3783 + c * (257 ±0)` // Estimated: `3783 + c * (257 ±0)`
// Minimum execution time: 249_507_000 picoseconds. // Minimum execution time: 251_521_000 picoseconds.
Weight::from_parts(248_505_595, 0) Weight::from_parts(253_053_636, 0)
.saturating_add(Weight::from_parts(0, 3783)) .saturating_add(Weight::from_parts(0, 3783))
// Standard Error: 20_302 // Standard Error: 14_691
.saturating_add(Weight::from_parts(4_191_526, 0).saturating_mul(c.into())) .saturating_add(Weight::from_parts(4_101_872, 0).saturating_mul(c.into()))
.saturating_add(RocksDbWeight::get().reads(5)) .saturating_add(RocksDbWeight::get().reads(5))
.saturating_add(RocksDbWeight::get().writes(2)) .saturating_add(RocksDbWeight::get().writes(2))
.saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into())) .saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into()))
@ -197,8 +197,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `448` // Measured: `448`
// Estimated: `3913` // Estimated: `3913`
// Minimum execution time: 264_214_000 picoseconds. // Minimum execution time: 265_187_000 picoseconds.
Weight::from_parts(266_905_000, 0) Weight::from_parts(269_124_000, 0)
.saturating_add(Weight::from_parts(0, 3913)) .saturating_add(Weight::from_parts(0, 3913))
.saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().reads(6))
.saturating_add(RocksDbWeight::get().writes(3)) .saturating_add(RocksDbWeight::get().writes(3))
@ -222,11 +222,11 @@ impl WeightInfo for () {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `0 + c * (770 ±0)` // Measured: `0 + c * (770 ±0)`
// Estimated: `1551 + c * (1019 ±27)` // Estimated: `1551 + c * (1019 ±27)`
// Minimum execution time: 281_073_000 picoseconds. // Minimum execution time: 284_558_000 picoseconds.
Weight::from_parts(280_778_987, 0) Weight::from_parts(284_117_484, 0)
.saturating_add(Weight::from_parts(0, 1551)) .saturating_add(Weight::from_parts(0, 1551))
// Standard Error: 92_951 // Standard Error: 74_361
.saturating_add(Weight::from_parts(3_489_552, 0).saturating_mul(c.into())) .saturating_add(Weight::from_parts(3_551_271, 0).saturating_mul(c.into()))
.saturating_add(RocksDbWeight::get().reads(7)) .saturating_add(RocksDbWeight::get().reads(7))
.saturating_add(RocksDbWeight::get().writes(3)) .saturating_add(RocksDbWeight::get().writes(3))
.saturating_add(Weight::from_parts(0, 1019).saturating_mul(c.into())) .saturating_add(Weight::from_parts(0, 1019).saturating_mul(c.into()))
@ -251,11 +251,11 @@ impl WeightInfo for () {
/// Proof: `GhostExodus::NonceCommitments` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::NonceCommitments` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn register_nonce_commitment() -> Weight { fn register_nonce_commitment() -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `588` // Measured: `627`
// Estimated: `4053` // Estimated: `4092`
// Minimum execution time: 291_972_000 picoseconds. // Minimum execution time: 315_066_000 picoseconds.
Weight::from_parts(295_122_000, 0) Weight::from_parts(318_561_000, 0)
.saturating_add(Weight::from_parts(0, 4053)) .saturating_add(Weight::from_parts(0, 4092))
.saturating_add(RocksDbWeight::get().reads(8)) .saturating_add(RocksDbWeight::get().reads(8))
.saturating_add(RocksDbWeight::get().writes(5)) .saturating_add(RocksDbWeight::get().writes(5))
} }
@ -281,11 +281,11 @@ impl WeightInfo for () {
/// Proof: `GhostExodus::GroupCommitmentsConsensus` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::GroupCommitmentsConsensus` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn register_group_commitment() -> Weight { fn register_group_commitment() -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `968` // Measured: `992`
// Estimated: `4433` // Estimated: `4457`
// Minimum execution time: 331_528_000 picoseconds. // Minimum execution time: 338_644_000 picoseconds.
Weight::from_parts(333_974_000, 0) Weight::from_parts(343_954_000, 0)
.saturating_add(Weight::from_parts(0, 4433)) .saturating_add(Weight::from_parts(0, 4457))
.saturating_add(RocksDbWeight::get().reads(10)) .saturating_add(RocksDbWeight::get().reads(10))
.saturating_add(RocksDbWeight::get().writes(4)) .saturating_add(RocksDbWeight::get().writes(4))
} }
@ -319,15 +319,17 @@ impl WeightInfo for () {
/// Proof: `GhostExodus::SignatureScalars` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::SignatureScalars` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::GroupCommitters` (r:0 w:1) /// Storage: `GhostExodus::GroupCommitters` (r:0 w:1)
/// Proof: `GhostExodus::GroupCommitters` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::GroupCommitters` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::CounterForExodusRequests` (r:1 w1)
/// Proof: `GhostExodus::CounterForExodusRequests` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
fn register_signature_share() -> Weight { fn register_signature_share() -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `11259` // Measured: `11299`
// Estimated: `14724` // Estimated: `14764`
// Minimum execution time: 3_842_259_000 picoseconds. // Minimum execution time: 3_968_938_000 picoseconds.
Weight::from_parts(3_985_167_000, 0) Weight::from_parts(3_975_088_000, 0)
.saturating_add(Weight::from_parts(0, 14724)) .saturating_add(Weight::from_parts(0, 14764))
.saturating_add(RocksDbWeight::get().reads(14)) .saturating_add(RocksDbWeight::get().reads(15))
.saturating_add(RocksDbWeight::get().writes(8)) .saturating_add(RocksDbWeight::get().writes(9))
} }
/// Storage: `GhostNetworks::Networks` (r:1 w:0) /// Storage: `GhostNetworks::Networks` (r:1 w:0)
/// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`)
@ -339,16 +341,16 @@ impl WeightInfo for () {
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::CurrentExodus` (r:1 w:1) /// Storage: `GhostExodus::CurrentExodus` (r:1 w:1)
/// Proof: `GhostExodus::CurrentExodus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::CurrentExodus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusRequests` (r:0 w:1) /// Storage: `GhostExodus::ExodusRequests` (r:1 w:1)
/// Proof: `GhostExodus::ExodusRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::ExodusRequests` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn register_evm_bridge_out_exodus() -> Weight { fn register_evm_bridge_out_exodus() -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `448` // Measured: `448`
// Estimated: `3913` // Estimated: `3913`
// Minimum execution time: 137_053_000 picoseconds. // Minimum execution time: 191_374_000 picoseconds.
Weight::from_parts(138_693_000, 0) Weight::from_parts(192_991_000, 0)
.saturating_add(Weight::from_parts(0, 3913)) .saturating_add(Weight::from_parts(0, 3913))
.saturating_add(RocksDbWeight::get().reads(5)) .saturating_add(RocksDbWeight::get().reads(7))
.saturating_add(RocksDbWeight::get().writes(4)) .saturating_add(RocksDbWeight::get().writes(5))
} }
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ghost-governance" name = "ghost-governance"
version = "0.3.7" version = "0.3.10"
description = "Full-chain and cross-chain governance pallet with early adopter share claims" description = "Full-chain and cross-chain governance pallet with early adopter share claims"
license.workspace = true license.workspace = true
authors.workspace = true authors.workspace = true
@ -13,6 +13,7 @@ codec = { workspace = true, features = ["derive"] }
scale-info = { workspace = true, features = ["derive"] } scale-info = { workspace = true, features = ["derive"] }
serde = { workspace = true } serde = { workspace = true }
serde_derive = { workspace = true } serde_derive = { workspace = true }
num-traits = { workspace = true }
rustc-hex = { workspace = true } rustc-hex = { workspace = true }
libsecp256k1 = { workspace = true, default-features = false } libsecp256k1 = { workspace = true, default-features = false }
@ -42,6 +43,7 @@ std = [
"serde/std", "serde/std",
"codec/std", "codec/std",
"scale-info/std", "scale-info/std",
"num-traits/std",
"libsecp256k1/std", "libsecp256k1/std",
"frame-support/std", "frame-support/std",
"frame-system/std", "frame-system/std",

View File

@ -32,7 +32,7 @@ mod benchmarks {
let dummy_evm_address = EvmAddress::from(evm_addr_bytes); let dummy_evm_address = EvmAddress::from(evm_addr_bytes);
let target_index = max_proof_depth - 1; let target_index = max_proof_depth - 1;
let claim_package_init = ClaimPackage { let claim_package_init = ClaimPackage::<T> {
shares: dummy_shares, shares: dummy_shares,
merkle_proof: Default::default(), merkle_proof: Default::default(),
token_id: 0, token_id: 0,
@ -99,7 +99,8 @@ mod benchmarks {
let message_string_bytes = Pallet::<T>::ethereum_signable_message( let message_string_bytes = Pallet::<T>::ethereum_signable_message(
&current_account, &current_account,
&network_id, &network_id,
&preimage_hash &preimage_hash,
0u8,
); );
let message_hash = SubstrateKeccakHasher::hash(&message_string_bytes); let message_hash = SubstrateKeccakHasher::hash(&message_string_bytes);
@ -124,6 +125,7 @@ mod benchmarks {
claim_package, claim_package,
dummy_evm_address, dummy_evm_address,
evm_signature, evm_signature,
dummy_shares,
); );
assert_eq!(dummy_shares, GlobalShares::<T>::get().claimed_shares); assert_eq!(dummy_shares, GlobalShares::<T>::get().claimed_shares);

View File

@ -9,7 +9,11 @@ use frame_system::pallet_prelude::*;
use sp_core::U256; use sp_core::U256;
use sp_std::vec::Vec; use sp_std::vec::Vec;
use sp_io::crypto::secp256k1_ecdsa_recover; use sp_io::crypto::secp256k1_ecdsa_recover;
use sp_runtime::traits::{AtLeast32BitUnsigned, CheckedAdd, UniqueSaturatedInto}; use sp_runtime::traits::{
AtLeast32BitUnsigned, CheckedAdd, Saturating, UniqueSaturatedInto,
};
use num_traits::PrimInt;
use ghost_helpers::{ use ghost_helpers::{
SubstrateKeccakHasher, merkle_tree::verify_tree_proof, SubstrateKeccakHasher, merkle_tree::verify_tree_proof,
@ -32,9 +36,8 @@ mod benchmarking;
mod mock; mod mock;
mod tests; mod tests;
const MAX_POSSIBLE_PROOF_DEPTH: u32 = 256;
type TokenId = u128; type TokenId = u128;
type ClaimNonce = u8;
type EvmHash = sp_core::H256; type EvmHash = sp_core::H256;
type EvmAddress = sp_core::H160; type EvmAddress = sp_core::H160;
type EvmSignature = [u8; 65]; type EvmSignature = [u8; 65];
@ -134,25 +137,41 @@ where
} }
pub fn lock_balance(&mut self, extra_balance: Balance) { pub fn lock_balance(&mut self, extra_balance: Balance) {
self.locked_balance.saturating_add(extra_balance); self.locked_balance = self.locked_balance
.saturating_add(extra_balance);
} }
} }
#[derive(Encode, Decode, Clone, RuntimeDebug, TypeInfo, PartialEq, Eq)] #[derive(Encode, Decode, RuntimeDebug, TypeInfo)]
pub struct ClaimPackage<Balance> #[scale_info(skip_type_params(T))]
where pub struct ClaimPackage<T: pallet::Config> {
Balance: Default + Copy + Clone,
{
pub index: u32, pub index: u32,
pub token_id: TokenId, pub token_id: TokenId,
pub shares: Balance, pub shares: BalanceOf<T>,
pub merkle_proof: BoundedVec<EvmHash, ConstU32<MAX_POSSIBLE_PROOF_DEPTH>>, pub merkle_proof: BoundedVec<EvmHash, T::MaxProofDepth>,
} }
impl<Balance> ClaimPackage<Balance> impl<T: pallet::Config> Clone for ClaimPackage<T> {
where fn clone(&self) -> Self {
Balance: Default + Copy + Clone + UniqueSaturatedInto<u128>, Self {
{ index: self.index,
token_id: self.token_id,
shares: self.shares,
merkle_proof: self.merkle_proof.clone(),
}
}
}
impl<T: pallet::Config> PartialEq for ClaimPackage<T> {
fn eq(&self, other: &Self) -> bool {
self.index == other.index &&
self.token_id == other.token_id &&
self.shares == other.shares &&
self.merkle_proof == other.merkle_proof
}
}
impl<T: pallet::Config> ClaimPackage<T> {
pub fn get_preimage<NetworkId>( pub fn get_preimage<NetworkId>(
&self, &self,
evm_address: &EvmAddress, evm_address: &EvmAddress,
@ -214,6 +233,9 @@ pub mod pallet {
#[pallet::constant] #[pallet::constant]
type MaxProofDepth: Get<u32>; type MaxProofDepth: Get<u32>;
#[pallet::constant]
type MaxPartialClaims: Get<ClaimNonce>;
type WeightInfo: WeightInfo; type WeightInfo: WeightInfo;
} }
@ -229,9 +251,10 @@ pub mod pallet {
#[pallet::error] #[pallet::error]
pub enum Error<T> { pub enum Error<T> {
PreimageAlreadyClaimed, PartialClaimsExhausted,
BoundedToOtherNetwork, BoundedToOtherNetwork,
AuthorityNotGovernor, AuthorityNotGovernor,
ExceedsAllowedShares,
NetworkHasNoGenesis, NetworkHasNoGenesis,
InvalidMerkleProof, InvalidMerkleProof,
SharesOverflowed, SharesOverflowed,
@ -269,7 +292,16 @@ pub mod pallet {
pub type ClaimedHashes<T: Config> = StorageMap< pub type ClaimedHashes<T: Config> = StorageMap<
_, _,
Blake2_256, EvmHash, Blake2_256, EvmHash,
bool, ClaimNonce,
ValueQuery,
>;
#[pallet::storage]
#[pallet::getter(fn claimed_shares)]
pub type ClaimedShares<T: Config> = StorageMap<
_,
Twox64Concat, EvmHash,
BalanceOf<T>,
ValueQuery, ValueQuery,
>; >;
@ -314,9 +346,10 @@ pub mod pallet {
pub fn claim( pub fn claim(
origin: OriginFor<T>, origin: OriginFor<T>,
network_id: NetworkIdOf<T>, network_id: NetworkIdOf<T>,
claim_package: ClaimPackage<BalanceOf<T>>, claim_package: ClaimPackage<T>,
evm_address: EvmAddress, evm_address: EvmAddress,
evm_signature: EvmSignature, evm_signature: EvmSignature,
requested_shares: BalanceOf<T>,
) -> DispatchResult { ) -> DispatchResult {
let who = ensure_signed(origin)?; let who = ensure_signed(origin)?;
@ -338,12 +371,21 @@ pub mod pallet {
let preimage = claim_package.get_preimage(&evm_address, network_id); let preimage = claim_package.get_preimage(&evm_address, network_id);
let preimage_hash = SubstrateKeccakHasher::hash(&preimage); let preimage_hash = SubstrateKeccakHasher::hash(&preimage);
ensure!( let max_claim_nonces = T::MaxPartialClaims::get();
!ClaimedHashes::<T>::contains_key(&preimage_hash), let current_nonce = ClaimedHashes::<T>::get(&preimage_hash);
Error::<T>::PreimageAlreadyClaimed
);
Self::verify_signature(&who, &network_id, &preimage_hash, &evm_address, &evm_signature)?; ensure!(current_nonce < max_claim_nonces, Error::<T>::PartialClaimsExhausted);
let already_claimed = ClaimedShares::<T>::get(&preimage_hash);
let claimed_shares = if current_nonce == max_claim_nonces.saturating_sub(1) {
claim_package.shares.saturating_sub(already_claimed)
} else {
let cumulative_shares = already_claimed.saturating_add(requested_shares);
ensure!(cumulative_shares <= claim_package.shares, Error::<T>::ExceedsAllowedShares);
requested_shares
};
Self::verify_signature(&who, &network_id, &preimage_hash, &evm_address, &evm_signature, current_nonce)?;
Self::verify_merkle_proof(&network_id, &preimage, &claim_package)?; Self::verify_merkle_proof(&network_id, &preimage, &claim_package)?;
NetworkShares::<T>::try_mutate(&network_id, |state| -> DispatchResult { NetworkShares::<T>::try_mutate(&network_id, |state| -> DispatchResult {
@ -351,25 +393,29 @@ pub mod pallet {
let proof_size_match = package_proof_size == state.proofs_size; let proof_size_match = package_proof_size == state.proofs_size;
ensure!(proof_size_match, Error::<T>::SharesOverflowed); ensure!(proof_size_match, Error::<T>::SharesOverflowed);
let increased = state.try_increase_claimed(claim_package.shares); let increased = state.try_increase_claimed(claimed_shares);
ensure!(increased.is_some(), Error::<T>::SharesOverflowed); ensure!(increased.is_some(), Error::<T>::SharesOverflowed);
Ok(()) Ok(())
})?; })?;
GlobalShares::<T>::try_mutate(|state| -> DispatchResult { GlobalShares::<T>::try_mutate(|state| -> DispatchResult {
let increased = state.try_increase_claimed(claim_package.shares); let increased = state.try_increase_claimed(claimed_shares);
ensure!(increased.is_some(), Error::<T>::SharesOverflowed); ensure!(increased.is_some(), Error::<T>::SharesOverflowed);
Ok(()) Ok(())
})?; })?;
member_share.increase_activated_shares(claim_package.shares); member_share.increase_activated_shares(claimed_shares);
member_share.initiate_on_network(network_id); member_share.initiate_on_network(network_id);
MemberShares::<T>::insert(&who, member_share); MemberShares::<T>::insert(&who, member_share);
ClaimedHashes::<T>::insert(&preimage_hash, true); ClaimedHashes::<T>::insert(&preimage_hash, current_nonce.saturating_add(1));
ClaimedShares::<T>::insert(
&preimage_hash,
already_claimed.saturating_add(claimed_shares),
);
Self::deposit_event(Event::<T>::SharesClaimed { Self::deposit_event(Event::<T>::SharesClaimed {
shares: claim_package.shares, shares: claimed_shares,
network_id, network_id,
who, who,
}); });
@ -390,45 +436,73 @@ impl<T: Config> Pallet<T> {
r r
} }
fn to_ascii_base10<N: PrimInt + UniqueSaturatedInto<u128>>(num: N) -> Vec<u8> {
let zero = N::zero();
if num == zero { return sp_std::vec![b'0']; }
let ten = N::from(10).unwrap_or_else(|| N::zero());
let num_u128 = num.unique_saturated_into();
let capacity = (num_u128.ilog10() + 1) as usize;
let mut result = Vec::with_capacity(capacity);
let mut n = num;
while n > zero {
result.push(b'0' + (n % ten).to_u8().unwrap_or(0));
n = n / ten;
}
result.reverse();
result
}
fn ethereum_signable_message( fn ethereum_signable_message(
receiver: &T::AccountId, receiver: &T::AccountId,
network_id: &NetworkIdOf<T>, network_id: &NetworkIdOf<T>,
preimage_hash: &EvmHash, preimage_hash: &EvmHash,
nonce: ClaimNonce,
) -> Vec<u8> { ) -> Vec<u8> {
let prefix = b"GMV Claim:"; let prefix = b"GMV Claim:";
let network_id_u64: u64 = (*network_id).unique_saturated_into(); let network_id_u64: u64 = (*network_id).unique_saturated_into();
let receiver_ascii_hex = Self::to_ascii_hex(&receiver.encode()); let receiver_ascii_hex = Self::to_ascii_hex(&receiver.encode());
let preimage_ascii_hex = Self::to_ascii_hex(preimage_hash.as_ref()); let preimage_ascii_hex = Self::to_ascii_hex(preimage_hash.as_ref());
let network_ascii_hex = Self::to_ascii_hex(&network_id_u64.to_be_bytes()); let network_ascii = Self::to_ascii_base10(network_id_u64);
let nonce_ascii = Self::to_ascii_base10(nonce);
let what_length = receiver_ascii_hex.len() let account_label = b"\naccount:0x";
.saturating_add(preimage_ascii_hex.len()) let preimage_label = b"\npreimage:0x";
.saturating_add(network_ascii_hex.len()) let network_label = b"\nnetwork:";
.saturating_add(10 + 11 + 12 + 11); // all 4 prefixes let nonce_label = b"\nnonce:";
let what_length = prefix.len()
.saturating_add(account_label.len()).saturating_add(receiver_ascii_hex.len())
.saturating_add(preimage_label.len()).saturating_add(preimage_ascii_hex.len())
.saturating_add(network_label.len()).saturating_add(network_ascii.len())
.saturating_add(nonce_label.len()).saturating_add(nonce_ascii.len());
let mut what = Vec::with_capacity(what_length); let mut what = Vec::with_capacity(what_length);
what.extend_from_slice(prefix); what.extend_from_slice(prefix);
what.extend_from_slice(b"\naccount:0x"); what.extend_from_slice(account_label);
what.extend_from_slice(&receiver_ascii_hex); what.extend_from_slice(&receiver_ascii_hex);
what.extend_from_slice(b"\npreimage:0x"); what.extend_from_slice(preimage_label);
what.extend_from_slice(&preimage_ascii_hex); what.extend_from_slice(&preimage_ascii_hex);
what.extend_from_slice(b"\nnetwork:0x"); what.extend_from_slice(network_label);
what.extend_from_slice(&network_ascii_hex); what.extend_from_slice(&network_ascii);
let mut l = what.len(); what.extend_from_slice(nonce_label);
let mut rev = Vec::new(); what.extend_from_slice(&nonce_ascii);
while l > 0 {
rev.push(b'0' + (l % 10) as u8);
l /= 10;
}
let mut v = Vec::with_capacity(26 + rev.len() + what.len()); let length_ascii = Self::to_ascii_base10(what.len());
v.extend_from_slice(b"\x19Ethereum Signed Message:\n"); let message_prefix = b"\x19Ethereum Signed Message:\n";
v.extend(rev.into_iter().rev());
let message_capacity = message_prefix.len() + length_ascii.len() + what.len();
let mut v = Vec::with_capacity(message_capacity);
v.extend_from_slice(message_prefix);
v.extend_from_slice(&length_ascii);
v.extend(what); v.extend(what);
v v
@ -440,8 +514,9 @@ impl<T: Config> Pallet<T> {
preimage_hash: &EvmHash, preimage_hash: &EvmHash,
evm_address: &EvmAddress, evm_address: &EvmAddress,
evm_signature: &EvmSignature, evm_signature: &EvmSignature,
nonce: ClaimNonce,
) -> DispatchResult { ) -> DispatchResult {
let message = Self::ethereum_signable_message(who, network_id, preimage_hash); let message = Self::ethereum_signable_message(who, network_id, preimage_hash, nonce);
let message_bytes = SubstrateKeccakHasher::hash(&message).to_fixed_bytes(); let message_bytes = SubstrateKeccakHasher::hash(&message).to_fixed_bytes();
let recovered_pubkey = secp256k1_ecdsa_recover(evm_signature, &message_bytes) let recovered_pubkey = secp256k1_ecdsa_recover(evm_signature, &message_bytes)
@ -457,7 +532,7 @@ impl<T: Config> Pallet<T> {
fn verify_merkle_proof( fn verify_merkle_proof(
network_id: &NetworkIdOf<T>, network_id: &NetworkIdOf<T>,
preimage: &[u8], preimage: &[u8],
claim_package: &ClaimPackage<BalanceOf<T>>, claim_package: &ClaimPackage<T>,
) -> DispatchResult { ) -> DispatchResult {
let network_state = NetworkShares::<T>::get(network_id); let network_state = NetworkShares::<T>::get(network_id);

View File

@ -5,7 +5,7 @@ use super::*;
pub use crate as ghost_governance; pub use crate as ghost_governance;
use frame_support::{ use frame_support::{
derive_impl, parameter_types, derive_impl, parameter_types,
traits::{ConstU32, ConstU128}, traits::{ConstU8, ConstU32, ConstU128},
}; };
use frame_system::EnsureRoot; use frame_system::EnsureRoot;
use sp_runtime::BuildStorage; use sp_runtime::BuildStorage;
@ -52,7 +52,8 @@ impl Config for Test {
type NetworkDataHandler = Networks; type NetworkDataHandler = Networks;
type MinimumDonation = ConstU128<69>; type MinimumDonation = ConstU128<69>;
type MaxProofDepth = ConstU32<5>; type MaxProofDepth = ConstU32<7>;
type MaxPartialClaims = ConstU8<3>;
type WeightInfo = (); type WeightInfo = ();
} }

View File

@ -6,7 +6,7 @@ use hex_literal::hex;
use sp_runtime::AccountId32; use sp_runtime::AccountId32;
use frame_support::traits::Get; use frame_support::traits::Get;
use frame_support::assert_ok; use frame_support::{assert_ok, assert_err};
use libsecp256k1::{Message, SecretKey, PublicKey}; use libsecp256k1::{Message, SecretKey, PublicKey};
use ghost_helpers::merkle_tree::{generate_tree, generate_proof}; use ghost_helpers::merkle_tree::{generate_tree, generate_proof};
@ -87,7 +87,7 @@ fn claim_happy_path() {
|item: (u8, &NewTestUser)| -> Result<(usize, Vec<u8>), ()> { |item: (u8, &NewTestUser)| -> Result<(usize, Vec<u8>), ()> {
let (index, u) = item; let (index, u) = item;
let tmp_package = ClaimPackage { let tmp_package = ClaimPackage::<Test> {
shares: u.allowed_shares, shares: u.allowed_shares,
merkle_proof: Default::default(), merkle_proof: Default::default(),
token_id: u.token_id, token_id: u.token_id,
@ -118,7 +118,7 @@ fn claim_happy_path() {
let proof = generate_proof::<SubstrateKeccakHasher, u8>(&merkle_tree, max_index, slot_index as u8); let proof = generate_proof::<SubstrateKeccakHasher, u8>(&merkle_tree, max_index, slot_index as u8);
let claim_package = ClaimPackage { let claim_package = ClaimPackage::<Test> {
shares: current_user.allowed_shares, shares: current_user.allowed_shares,
merkle_proof: BoundedVec::try_from(proof).unwrap(), merkle_proof: BoundedVec::try_from(proof).unwrap(),
token_id: current_user.token_id, token_id: current_user.token_id,
@ -131,7 +131,8 @@ fn claim_happy_path() {
let message_string_bytes = Governance::ethereum_signable_message( let message_string_bytes = Governance::ethereum_signable_message(
&current_user.substrate_account, &current_user.substrate_account,
&network_id, &network_id,
&preimage_hash &preimage_hash,
0u8,
); );
let message_hash = SubstrateKeccakHasher::hash(&message_string_bytes); let message_hash = SubstrateKeccakHasher::hash(&message_string_bytes);
@ -154,7 +155,8 @@ fn claim_happy_path() {
network_id, network_id,
claim_package, claim_package,
current_user.evm_address.clone(), current_user.evm_address.clone(),
evm_signature evm_signature,
current_user.allowed_shares,
)); ));
let updated_share = MemberShares::<Test>::get(&current_user.substrate_account); let updated_share = MemberShares::<Test>::get(&current_user.substrate_account);
@ -172,6 +174,201 @@ fn claim_happy_path() {
}); });
} }
#[test]
fn partial_claims_happy_path() {
new_test_ext().execute_with(|| {
let network_id = 69u64;
let minimum_donation_u128 = <<Test as crate::Config>::MinimumDonation as Get<u128>>::get();
let minimum_donation: BalanceOf<Test> = minimum_donation_u128.unique_saturated_into();
let secret_key = SecretKey::parse(&[7u8; 32]).unwrap();
let public_key = PublicKey::from_secret_key(&secret_key);
let pubkey_raw = &public_key.serialize()[1..65];
let pubkey_hash = SubstrateKeccakHasher::hash(pubkey_raw);
let mut evm_addr_bytes = [0u8; 20];
evm_addr_bytes.copy_from_slice(&pubkey_hash.as_ref()[12..32]);
let substrate_account_a = AccountId32::new([11u8; 32]);
let substrate_account_b = AccountId32::new([22u8; 32]);
let substrate_account_c = AccountId32::new([33u8; 32]);
let current_user = NewTestUser {
substrate_account: substrate_account_a.clone(),
evm_secret: secret_key,
evm_address: EvmAddress::from(evm_addr_bytes),
allowed_shares: 1000u128,
token_id: 7,
};
let max_index = 0u8;
let raw_values_complex = vec![(0u8, &current_user)];
let merkle_tree = generate_tree::<SubstrateKeccakHasher, u8, _, _, _>(
max_index,
raw_values_complex,
|item: (u8, &NewTestUser)| -> Result<(usize, Vec<u8>), ()> {
let (index, u) = item;
let tmp_package = ClaimPackage::<Test> {
shares: u.allowed_shares,
merkle_proof: Default::default(),
token_id: u.token_id,
index: index as u32
};
Ok((index as usize, tmp_package.get_preimage(&u.evm_address, network_id).to_vec()))
}
).unwrap();
let total_shares = 1_000_000;
let merkle_root = *merkle_tree.last().unwrap();
let network_state = NetworkShare::new(total_shares, merkle_root, 0);
let global_state = ShareState::new(total_shares);
NetworkShares::<Test>::insert(&network_id, network_state);
GlobalShares::<Test>::put(global_state);
let setup_governor = |account: &<Test as frame_system::Config>::AccountId| {
MemberShares::<Test>::insert(account, MemberShare {
locked_balance: minimum_donation,
initiated_network: None,
activated_shares: 0,
});
};
setup_governor(&substrate_account_a);
setup_governor(&substrate_account_b);
setup_governor(&substrate_account_c);
let proof = generate_proof::<SubstrateKeccakHasher, u8>(&merkle_tree, max_index, 0u8);
let claim_package = ClaimPackage {
shares: current_user.allowed_shares,
merkle_proof: BoundedVec::try_from(proof).unwrap(),
token_id: current_user.token_id,
index: 0,
};
let preimage = claim_package.get_preimage(&current_user.evm_address, network_id);
let preimage_hash = SubstrateKeccakHasher::hash(&preimage);
let generate_test_signature = |target_account: &<Test as frame_system::Config>::AccountId| -> EvmSignature {
let active_nonce = ClaimedHashes::<Test>::get(&preimage_hash);
let message_string_bytes = Governance::ethereum_signable_message(
&target_account,
&network_id,
&preimage_hash,
active_nonce,
);
let message_hash = SubstrateKeccakHasher::hash(&message_string_bytes);
let mut message_bytes = [0u8; 32];
message_bytes.copy_from_slice(message_hash.as_ref());
let msg_to_sign = Message::parse(&message_bytes);
let (sig, recovery_id) = libsecp256k1::sign(&msg_to_sign, &current_user.evm_secret);
let mut signature_raw = [0u8; 65];
signature_raw[0..32].copy_from_slice(&sig.r.b32());
signature_raw[32..64].copy_from_slice(&sig.s.b32());
signature_raw[64] = recovery_id.serialize();
EvmSignature::from(signature_raw)
};
let sig_0 = generate_test_signature(&substrate_account_a);
assert_ok!(Governance::claim(
RuntimeOrigin::signed(substrate_account_a.clone()),
network_id,
claim_package.clone(),
current_user.evm_address.clone(),
sig_0,
400,
));
let global_share = GlobalShares::<Test>::get();
assert_eq!(global_share.claimed_shares, 400);
assert_eq!(global_share.total_shares, total_shares);
let network_share = NetworkShares::<Test>::get(&network_id);
assert_eq!(network_share.claimed_shares(), 400);
assert_eq!(network_share.total_shares(), total_shares);
assert_eq!(MemberShares::<Test>::get(&substrate_account_a).activated_shares, 400);
assert_eq!(ClaimedHashes::<Test>::get(&preimage_hash), 1);
assert_eq!(ClaimedShares::<Test>::get(&preimage_hash), 400);
let sig_1_fail = generate_test_signature(&substrate_account_b);
assert_err!(
Governance::claim(
RuntimeOrigin::signed(substrate_account_b.clone()),
network_id,
claim_package.clone(),
current_user.evm_address.clone(),
sig_1_fail,
700,
),
Error::<Test>::ExceedsAllowedShares
);
let sig_1_success = generate_test_signature(&substrate_account_b);
assert_ok!(Governance::claim(
RuntimeOrigin::signed(substrate_account_b.clone()),
network_id,
claim_package.clone(),
current_user.evm_address.clone(),
sig_1_success,
100,
));
let global_share = GlobalShares::<Test>::get();
assert_eq!(global_share.claimed_shares, 500);
assert_eq!(global_share.total_shares, total_shares);
let network_share = NetworkShares::<Test>::get(&network_id);
assert_eq!(network_share.claimed_shares(), 500);
assert_eq!(network_share.total_shares(), total_shares);
assert_eq!(MemberShares::<Test>::get(&substrate_account_b).activated_shares, 100);
assert_eq!(ClaimedHashes::<Test>::get(&preimage_hash), 2);
assert_eq!(ClaimedShares::<Test>::get(&preimage_hash), 500);
let sig_2_final = generate_test_signature(&substrate_account_c);
assert_ok!(Governance::claim(
RuntimeOrigin::signed(substrate_account_c.clone()),
network_id,
claim_package.clone(),
current_user.evm_address.clone(),
sig_2_final,
300,
));
let global_share = GlobalShares::<Test>::get();
assert_eq!(global_share.claimed_shares, 1_000);
assert_eq!(global_share.total_shares, total_shares);
let network_share = NetworkShares::<Test>::get(&network_id);
assert_eq!(network_share.claimed_shares(), 1_000);
assert_eq!(network_share.total_shares(), total_shares);
assert_eq!(MemberShares::<Test>::get(&substrate_account_c).activated_shares, 500);
assert_eq!(ClaimedHashes::<Test>::get(&preimage_hash), 3);
assert_eq!(ClaimedShares::<Test>::get(&preimage_hash), 1_000);
let sig_3_exhausted = generate_test_signature(&substrate_account_a);
assert_err!(
Governance::claim(
RuntimeOrigin::signed(substrate_account_a),
network_id,
claim_package,
current_user.evm_address.clone(),
sig_3_exhausted,
100,
),
Error::<Test>::PartialClaimsExhausted
);
});
}
#[test] #[test]
fn verify_proof_from_python_preclaims_for_sepolia() { fn verify_proof_from_python_preclaims_for_sepolia() {
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {

View File

@ -16,7 +16,7 @@
//! Autogenerated weights for `ghost_governance` //! Autogenerated weights for `ghost_governance`
//! //!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
//! DATE: 2026-09-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! DATE: 2026-09-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000` //! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `ghostown`, CPU: `Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz` //! HOSTNAME: `ghostown`, CPU: `Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz`
//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("casper-dev")`, DB CACHE: 1024 //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("casper-dev")`, DB CACHE: 1024
@ -56,21 +56,23 @@ impl WeightInfo for () {
/// Proof: `GhostGovernance::MemberShares` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostGovernance::MemberShares` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostGovernance::ClaimedHashes` (r:1 w:1) /// Storage: `GhostGovernance::ClaimedHashes` (r:1 w:1)
/// Proof: `GhostGovernance::ClaimedHashes` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostGovernance::ClaimedHashes` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostGovernance::ClaimedShares` (r:1 w:1)
/// Proof: `GhostGovernance::ClaimedShares` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostGovernance::NetworkShares` (r:1 w:1) /// Storage: `GhostGovernance::NetworkShares` (r:1 w:1)
/// Proof: `GhostGovernance::NetworkShares` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostGovernance::NetworkShares` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostGovernance::GlobalShares` (r:1 w:1) /// Storage: `GhostGovernance::GlobalShares` (r:1 w:1)
/// Proof: `GhostGovernance::GlobalShares` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Proof: `GhostGovernance::GlobalShares` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// The range of component `p` is `[1, 20]`. /// The range of component `p` is `[1, 18]`.
fn claim(p: u32, ) -> Weight { fn claim(p: u32, ) -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `283` // Measured: `283`
// Estimated: `3748` // Estimated: `3748`
// Minimum execution time: 214_208_000 picoseconds. // Minimum execution time: 229_254_000 picoseconds.
Weight::from_parts(216_987_870, 0) Weight::from_parts(229_992_093, 0)
.saturating_add(Weight::from_parts(0, 3748)) .saturating_add(Weight::from_parts(0, 3748))
// Standard Error: 30_856 // Standard Error: 25_680
.saturating_add(Weight::from_parts(1_702_104, 0).saturating_mul(p.into())) .saturating_add(Weight::from_parts(1_874_433, 0).saturating_mul(p.into()))
.saturating_add(RocksDbWeight::get().reads(4)) .saturating_add(RocksDbWeight::get().reads(5))
.saturating_add(RocksDbWeight::get().writes(4)) .saturating_add(RocksDbWeight::get().writes(5))
} }
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ghost-weaver" name = "ghost-weaver"
version = "0.0.10" version = "0.0.14"
description = "Weaving a secure cryptographic tapestry across different external chains." description = "Weaving a secure cryptographic tapestry across different external chains."
license.workspace = true license.workspace = true
authors.workspace = true authors.workspace = true

View File

@ -3,6 +3,7 @@
use super::*; use super::*;
use frame_benchmarking::v2::*; use frame_benchmarking::v2::*;
use frame_system::RawOrigin; use frame_system::RawOrigin;
use sp_runtime::traits::Saturating;
use sp_std::vec; use sp_std::vec;
use ghost_helpers::networks::NetworkDataBuilder; use ghost_helpers::networks::NetworkDataBuilder;
@ -144,6 +145,17 @@ mod benchmarks {
let max_history_depth = a as usize; let max_history_depth = a as usize;
let (network_id, _, _) = prepare_pallet::<T>(1, None); let (network_id, _, _) = prepare_pallet::<T>(1, None);
let expected_receiver_bytes = hex::decode("1657090d02985f0cdb41436fe2d5a83a3acb1995d963a698e7e619e927bdcc5e").unwrap();
let mut receiver_raw = [0u8; 32];
receiver_raw.copy_from_slice(&expected_receiver_bytes);
let transfer_amount: u64 = 153348847;
let receiver = T::AccountId::decode(&mut &receiver_raw[..])
.expect("32-byte array must cleanly decode into T::AccountId");
let existential_deposit = <T as Config>::Currency::minimum_balance();
<T as Config>::Currency::set_balance(&receiver, existential_deposit);
// Real world tx at block #481838 // Real world tx at block #481838
// Block https://blockstream.info/block/0000000000000000011bdccb39e19e942a60edffdccd9779653c5b46b337904a // Block https://blockstream.info/block/0000000000000000011bdccb39e19e942a60edffdccd9779653c5b46b337904a
// Transaction: https://blockstream.info/tx/4a3fdd9ebb30b9c6df05d4199f8474b2f0c795d1f40a58d0d73342fc666b4a2e // Transaction: https://blockstream.info/tx/4a3fdd9ebb30b9c6df05d4199f8474b2f0c795d1f40a58d0d73342fc666b4a2e
@ -322,11 +334,16 @@ mod benchmarks {
let pulled_thread_key = thread_proof.get_unique_key(session, network_id); let pulled_thread_key = thread_proof.get_unique_key(session, network_id);
assert!(!PulledThreads::<T>::contains_key(&pulled_thread_key)); assert!(!PulledThreads::<T>::contains_key(&pulled_thread_key));
assert_eq!(T::Currency::balance(&receiver), existential_deposit);
#[extrinsic_call] #[extrinsic_call]
pull_thread(RawOrigin::None, network_id, session, thread_proof); pull_thread(RawOrigin::None, network_id, session, thread_proof);
let final_amount = existential_deposit.saturating_add(
transfer_amount.unique_saturated_into()
);
assert!(PulledThreads::<T>::contains_key(&pulled_thread_key)); assert!(PulledThreads::<T>::contains_key(&pulled_thread_key));
assert_eq!(T::Currency::balance(&receiver), final_amount);
Ok(()) Ok(())
} }
@ -352,8 +369,13 @@ mod benchmarks {
.try_into() .try_into()
.expect("Hex string must be exactly 32 bytes long"); .expect("Hex string must be exactly 32 bytes long");
let receiver_account = sp_runtime::AccountId32::from(receiver_32);
let transfer_amount: BalanceOf<T> = 420u64.unique_saturated_into(); let transfer_amount: BalanceOf<T> = 420u64.unique_saturated_into();
let receiver_account = sp_runtime::AccountId32::from(receiver_32);
let receiver_account_t = T::AccountId::decode(&mut &receiver_32[..])
.expect("32-byte array must cleanly decode into T::AccountId");
let existential_deposit = <T as Config>::Currency::minimum_balance();
<T as Config>::Currency::set_balance(&receiver_account_t, existential_deposit);
let proof = vec![ let proof = vec![
H256::from_slice(&hex::decode("7d831a4be9e37572687117870c3f96669a66bf07971e4fd705a36a0a4d6365b0").expect("Proof should be valid")), H256::from_slice(&hex::decode("7d831a4be9e37572687117870c3f96669a66bf07971e4fd705a36a0a4d6365b0").expect("Proof should be valid")),
@ -370,7 +392,7 @@ mod benchmarks {
.expect("Slot should be valid hex"), .expect("Slot should be valid hex"),
)]; )];
let evm_thread_proof = EvmThreadProof::new(0, 1, receiver_account, transfer_amount, proof, slots); let evm_thread_proof = EvmThreadProof::new(0, 1, receiver_account.into(), transfer_amount, proof, slots);
let thread_proof = ThreadProof::EvmThreadProof(evm_thread_proof); let thread_proof = ThreadProof::EvmThreadProof(evm_thread_proof);
let pulled_thread_key = thread_proof.get_unique_key(session, network_id); let pulled_thread_key = thread_proof.get_unique_key(session, network_id);

View File

@ -14,7 +14,7 @@ pub enum WeavingError<NetworkId> {
impl<NetworkId: core::fmt::Debug> core::fmt::Debug for WeavingError<NetworkId> { impl<NetworkId: core::fmt::Debug> core::fmt::Debug for WeavingError<NetworkId> {
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
match self { match self {
Self::NoStoredNetworks => write!(fmt, "No stored networks to be watch on."), Self::NoStoredNetworks => write!(fmt, "No stored networks to work on."),
Self::UnparsableRequestBody => { Self::UnparsableRequestBody => {
write!(fmt, "HTTP request body contains invalid UTF-8 sequences.") write!(fmt, "HTTP request body contains invalid UTF-8 sequences.")
} }
@ -22,25 +22,25 @@ impl<NetworkId: core::fmt::Debug> core::fmt::Debug for WeavingError<NetworkId> {
write!(fmt, "The request deadline or network timeout was hit.") write!(fmt, "The request deadline or network timeout was hit.")
} }
Self::UnknownWeavingPhase(network_id) => { Self::UnknownWeavingPhase(network_id) => {
write!(fmt, "Unknown weaving phase for network: {:?}.", network_id) write!(fmt, "Unknown weaving phase for network #{:?}.", network_id)
} }
Self::OffchainLockPeriod(network_id) => write!( Self::OffchainLockPeriod(network_id) => write!(
fmt, fmt,
"Storage lock is still held for network: {:?}.", "Storage lock is still held for network #{:?}.",
network_id network_id
), ),
Self::NoEndpointsAvailable(network_id) => write!( Self::NoEndpointsAvailable(network_id) => write!(
fmt, fmt,
"Zero RPC URLs configured for network: {:?}.", "No RPC URLs configured for network #{:?}.",
network_id network_id
), ),
Self::ContradictoryHashes(network_id) => write!( Self::ContradictoryHashes(network_id) => write!(
fmt, fmt,
"Divergence among reported hashes for network: {:?}.", "Divergence among reported hashes for network #{:?}.",
network_id network_id
), ),
Self::ContradictoryBlocks(len, deviation, network_id) => { Self::ContradictoryBlocks(len, deviation, network_id) => {
write!(fmt, "Divergence among {len} reported blocks exceeds the maximum threshold of {deviation} provided for network: {:?}.", network_id) write!(fmt, "Divergence among {len} reported blocks exceeds the maximum allowed deviation of {deviation} for network #{:?}.", network_id)
} }
} }
} }
@ -73,7 +73,7 @@ impl core::fmt::Debug for RpcResolverError {
"The 'result' field in the JSON-RPC response is missing or null." "The 'result' field in the JSON-RPC response is missing or null."
), ),
Self::UnknownNetworkType => { Self::UnknownNetworkType => {
write!(fmt, "he provided network type identifier is not supported.") write!(fmt, "The provided network type identifier is not supported.")
} }
Self::RpcProtocolError => write!( Self::RpcProtocolError => write!(
fmt, fmt,

View File

@ -3,7 +3,10 @@
use frame_support::{ use frame_support::{
pallet_prelude::*, pallet_prelude::*,
traits::{Currency, Get, OneSessionHandler}, traits::{
Get, OneSessionHandler,
tokens::fungible::{Inspect, Mutate},
},
}; };
use frame_system::{ use frame_system::{
pallet_prelude::*, pallet_prelude::*,
@ -86,13 +89,10 @@ type ThreadId = u64;
type WeavingSession = u64; type WeavingSession = u64;
type ExternalBlockNumber = u64; type ExternalBlockNumber = u64;
type BalanceOf<T> = type BalanceOf<T> = <<T as Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::Balance;
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance; type NetworkIdOf<T> = <<T as Config>::NetworkDataHandler as NetworkDataBasicHandler>::NetworkId;
pub type NetworkIdOf<T> = <<T as Config>::NetworkDataHandler as NetworkDataBasicHandler>::NetworkId;
type WeavingResult<T, O> = Result<O, WeavingError<NetworkIdOf<T>>>; type WeavingResult<T, O> = Result<O, WeavingError<NetworkIdOf<T>>>;
type WeaversBitmap<T> = BoundedBitmap<BitmapChunk, <T as Config>::MaxAuthoritiesChunks>; type WeaversBitmap<T> = BoundedBitmap<BitmapChunk, <T as Config>::MaxAuthoritiesChunks>;
#[frame_support::pallet] #[frame_support::pallet]
@ -117,7 +117,7 @@ pub mod pallet {
+ MaybeSerializeDeserialize + MaybeSerializeDeserialize
+ MaxEncodedLen; + MaxEncodedLen;
type Currency: Currency<Self::AccountId>; type Currency: Inspect<Self::AccountId> + Mutate<Self::AccountId>;
type NetworkDataHandler: NetworkDataBasicHandler type NetworkDataHandler: NetworkDataBasicHandler
+ NetworkDataInspectHandler<NetworkData> + NetworkDataInspectHandler<NetworkData>
@ -458,7 +458,7 @@ pub mod pallet {
T::NetworkDataHandler::register_incoming(&network_id, amount) T::NetworkDataHandler::register_incoming(&network_id, amount)
.map_err(|_| Error::<T>::CouldNotRegisterIncoming)?; .map_err(|_| Error::<T>::CouldNotRegisterIncoming)?;
let _ = T::Currency::deposit_creating(&receiver, pure_amount); T::Currency::mint_into(&receiver, pure_amount)?;
PulledThreads::<T>::insert(pulled_thread_key, ()); PulledThreads::<T>::insert(pulled_thread_key, ());
Self::deposit_event(Event::<T>::ThreadPulled { Self::deposit_event(Event::<T>::ThreadPulled {

View File

@ -644,6 +644,8 @@ fn should_successfully_pull_evm_thread_and_mint_currency() {
let expected_root = H256::from_slice(&root_string); let expected_root = H256::from_slice(&root_string);
LoomStates::<TestRuntime>::insert(&network_id, &session, expected_root); LoomStates::<TestRuntime>::insert(&network_id, &session, expected_root);
let mut total_issuance = 0;
for insert in inserts.into_iter() { for insert in inserts.into_iter() {
let index_usize = insert.0 as crate::ThreadId; let index_usize = insert.0 as crate::ThreadId;
let proof_index = index_usize % 256; // tree depth is fixed to 8 let proof_index = index_usize % 256; // tree depth is fixed to 8
@ -671,6 +673,7 @@ fn should_successfully_pull_evm_thread_and_mint_currency() {
let thread_info = ThreadProof::EvmThreadProof(evm_thread_proof); let thread_info = ThreadProof::EvmThreadProof(evm_thread_proof);
let pulled_thread_key = thread_info.get_unique_key(session, network_id); let pulled_thread_key = thread_info.get_unique_key(session, network_id);
assert_eq!(Balances::total_issuance(), total_issuance);
assert_eq!(Balances::free_balance(&receiver_account), 0); assert_eq!(Balances::free_balance(&receiver_account), 0);
assert!(!PulledThreads::<TestRuntime>::contains_key( assert!(!PulledThreads::<TestRuntime>::contains_key(
&pulled_thread_key &pulled_thread_key
@ -688,12 +691,15 @@ fn should_successfully_pull_evm_thread_and_mint_currency() {
let expected_commission = 0u64; let expected_commission = 0u64;
let expected_pure_amount = transfer_amount - expected_commission; let expected_pure_amount = transfer_amount - expected_commission;
total_issuance = total_issuance.saturating_add(expected_pure_amount);
assert_eq!(Balances::total_issuance(), total_issuance);
assert_eq!( assert_eq!(
Balances::free_balance(&receiver_account), Balances::free_balance(&receiver_account),
expected_pure_amount expected_pure_amount
); );
System::assert_last_event(RuntimeEvent::GhostWeaver(Event::ThreadPulled { System::assert_last_event(RuntimeEvent::GhostWeaver(Event::ThreadPulled {
pulled_thread_key, pulled_thread_key,
network_id, network_id,

View File

@ -16,7 +16,7 @@
//! Autogenerated weights for `ghost_weaver` //! Autogenerated weights for `ghost_weaver`
//! //!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
//! DATE: 2026-09-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! DATE: 2026-09-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000` //! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `ghostown`, CPU: `Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz` //! HOSTNAME: `ghostown`, CPU: `Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz`
//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("casper-dev")`, DB CACHE: 1024 //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("casper-dev")`, DB CACHE: 1024
@ -69,11 +69,11 @@ impl WeightInfo for () {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `223 + a * (32 ±0)` // Measured: `223 + a * (32 ±0)`
// Estimated: `3687 + a * (32 ±0)` // Estimated: `3687 + a * (32 ±0)`
// Minimum execution time: 211_189_000 picoseconds. // Minimum execution time: 212_751_000 picoseconds.
Weight::from_parts(215_074_977, 0) Weight::from_parts(215_022_916, 0)
.saturating_add(Weight::from_parts(0, 3687)) .saturating_add(Weight::from_parts(0, 3687))
// Standard Error: 1_505 // Standard Error: 1_319
.saturating_add(Weight::from_parts(46_499, 0).saturating_mul(a.into())) .saturating_add(Weight::from_parts(53_379, 0).saturating_mul(a.into()))
.saturating_add(RocksDbWeight::get().reads(5)) .saturating_add(RocksDbWeight::get().reads(5))
.saturating_add(RocksDbWeight::get().writes(1)) .saturating_add(RocksDbWeight::get().writes(1))
.saturating_add(Weight::from_parts(0, 32).saturating_mul(a.into())) .saturating_add(Weight::from_parts(0, 32).saturating_mul(a.into()))
@ -95,11 +95,11 @@ impl WeightInfo for () {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `518 + a * (32 ±0)` // Measured: `518 + a * (32 ±0)`
// Estimated: `3982 + a * (32 ±0)` // Estimated: `3982 + a * (32 ±0)`
// Minimum execution time: 241_010_000 picoseconds. // Minimum execution time: 212_751_000 picoseconds.
Weight::from_parts(245_378_305, 0) Weight::from_parts(246_374_350, 0)
.saturating_add(Weight::from_parts(0, 3982)) .saturating_add(Weight::from_parts(0, 3982))
// Standard Error: 1_583 // Standard Error: 1_865
.saturating_add(Weight::from_parts(32_330, 0).saturating_mul(a.into())) .saturating_add(Weight::from_parts(38_601, 0).saturating_mul(a.into()))
.saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().reads(6))
.saturating_add(RocksDbWeight::get().writes(2)) .saturating_add(RocksDbWeight::get().writes(2))
.saturating_add(Weight::from_parts(0, 32).saturating_mul(a.into())) .saturating_add(Weight::from_parts(0, 32).saturating_mul(a.into()))
@ -114,19 +114,19 @@ impl WeightInfo for () {
/// Proof: `GhostNetworks::GatekeeperAmounts` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::GatekeeperAmounts` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostNetworks::NetworkImbalance` (r:1 w:1) /// Storage: `GhostNetworks::NetworkImbalance` (r:1 w:1)
/// Proof: `GhostNetworks::NetworkImbalance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::NetworkImbalance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `System::Account` (r:1 w:0) /// Storage: `System::Account` (r:1 w:1)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
/// The range of component `a` is `[1, 144]`. /// The range of component `a` is `[1, 144]`.
fn pull_utxo_thread(a: u32, ) -> Weight { fn pull_utxo_thread(a: u32, ) -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `346` // Measured: `449`
// Estimated: `3811` // Estimated: `3914`
// Minimum execution time: 170_701_000 picoseconds. // Minimum execution time: 217_880_000 picoseconds.
Weight::from_parts(170_150_850, 0) Weight::from_parts(217_119_201, 0)
// Standard Error: 4_368 // Standard Error: 4_368
.saturating_add(Weight::from_parts(3_363_930, 0).saturating_mul(a.into())) .saturating_add(Weight::from_parts(3_445_491, 0).saturating_mul(a.into()))
.saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().reads(6))
.saturating_add(RocksDbWeight::get().writes(3)) .saturating_add(RocksDbWeight::get().writes(4))
} }
/// Storage: `GhostNetworks::Networks` (r:1 w:0) /// Storage: `GhostNetworks::Networks` (r:1 w:0)
/// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`)
@ -138,16 +138,16 @@ impl WeightInfo for () {
/// Proof: `GhostNetworks::GatekeeperAmounts` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::GatekeeperAmounts` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostNetworks::NetworkImbalance` (r:1 w:1) /// Storage: `GhostNetworks::NetworkImbalance` (r:1 w:1)
/// Proof: `GhostNetworks::NetworkImbalance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::NetworkImbalance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `System::Account` (r:1 w:0) /// Storage: `System::Account` (r:1 w:1)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
fn pull_evm_thread() -> Weight { fn pull_evm_thread() -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `3446` // Measured: `449`
// Estimated: `3811` // Estimated: `3914`
// Minimum execution time: 138_215_000 picoseconds. // Minimum execution time: 183_413_000 picoseconds.
Weight::from_parts(140_004_000, 0) Weight::from_parts(186_271_000, 0)
.saturating_add(Weight::from_parts(0, 3811)) .saturating_add(Weight::from_parts(0, 3914))
.saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().reads(6))
.saturating_add(RocksDbWeight::get().writes(3)) .saturating_add(RocksDbWeight::get().writes(4))
} }
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "casper-runtime" name = "casper-runtime"
version = "4.0.11" version = "4.0.14"
build = "build.rs" build = "build.rs"
description = "Runtime of the Casper Network" description = "Runtime of the Casper Network"
edition.workspace = true edition.workspace = true

View File

@ -15,7 +15,7 @@ use frame_support::{
traits::{ traits::{
fungible::HoldConsideration, fungible::HoldConsideration,
tokens::{imbalance::ResolveTo, UnityAssetBalanceConversion}, tokens::{imbalance::ResolveTo, UnityAssetBalanceConversion},
ConstU128, ConstU32, InstanceFilter, KeyOwnerProofSystem, ConstU128, ConstU32, ConstU8, InstanceFilter, KeyOwnerProofSystem,
LinearStoragePrice, LinearStoragePrice,
}, },
}; };
@ -887,7 +887,8 @@ impl ghost_governance::Config for Runtime {
type Currency = Balances; type Currency = Balances;
type NetworkDataHandler = GhostNetworks; type NetworkDataHandler = GhostNetworks;
type MinimumDonation = ConstU128<{ 6_900 * STRH }>; type MinimumDonation = ConstU128<{ 6_900 * STRH }>;
type MaxProofDepth = ConstU32<20>; type MaxProofDepth = ConstU32<18>;
type MaxPartialClaims = ConstU8<20>;
type WeightInfo = weights::ghost_governance::WeightInfo<Runtime>; type WeightInfo = weights::ghost_governance::WeightInfo<Runtime>;
} }
@ -913,6 +914,7 @@ parameter_types! {
pub ExodusDkgRoundPeriod: u64 = 50; pub ExodusDkgRoundPeriod: u64 = 50;
pub ExodusRemovalLimit: u32 = 64; pub ExodusRemovalLimit: u32 = 64;
pub ExodusMaxCursorLen: u32 = 100; pub ExodusMaxCursorLen: u32 = 100;
pub MinBridgeOut: u128 = 500 * STRH;
} }
impl ghost_weaver::Config for Runtime { impl ghost_weaver::Config for Runtime {
@ -951,6 +953,7 @@ impl ghost_exodus::Config for Runtime {
type DkgRoundPeriod = ExodusDkgRoundPeriod; type DkgRoundPeriod = ExodusDkgRoundPeriod;
type RemovalLimit = ExodusRemovalLimit; type RemovalLimit = ExodusRemovalLimit;
type MaxCursorLen = ExodusMaxCursorLen; type MaxCursorLen = ExodusMaxCursorLen;
type BaseEvmAmount = MinBridgeOut;
type WeightInfo = weights::ghost_exodus::WeightInfo<Runtime>; type WeightInfo = weights::ghost_exodus::WeightInfo<Runtime>;
} }

View File

@ -16,7 +16,7 @@
//! Autogenerated weights for `ghost_exodus` //! Autogenerated weights for `ghost_exodus`
//! //!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
//! DATE: 2026-09-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! DATE: 2026-09-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000` //! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `ghostown`, CPU: `Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz` //! HOSTNAME: `ghostown`, CPU: `Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz`
//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("casper-dev")`, DB CACHE: 1024 //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("casper-dev")`, DB CACHE: 1024
@ -60,8 +60,8 @@ impl<T: frame_system::Config> ghost_exodus::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `351` // Measured: `351`
// Estimated: `3816` // Estimated: `3816`
// Minimum execution time: 239_012_000 picoseconds. // Minimum execution time: 240_813_000 picoseconds.
Weight::from_parts(241_388_000, 0) Weight::from_parts(243_523_000, 0)
.saturating_add(Weight::from_parts(0, 3816)) .saturating_add(Weight::from_parts(0, 3816))
.saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(3)) .saturating_add(T::DbWeight::get().writes(3))
@ -83,11 +83,11 @@ impl<T: frame_system::Config> ghost_exodus::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `391 + c * (32 ±0)` // Measured: `391 + c * (32 ±0)`
// Estimated: `3856 + c * (32 ±0)` // Estimated: `3856 + c * (32 ±0)`
// Minimum execution time: 1_339_828_000 picoseconds. // Minimum execution time: 1_356_791_000 picoseconds.
Weight::from_parts(1_212_625_790, 0) Weight::from_parts(1_221_760_215, 0)
.saturating_add(Weight::from_parts(0, 3856)) .saturating_add(Weight::from_parts(0, 3856))
// Standard Error: 11_831 // Standard Error: 16_481
.saturating_add(Weight::from_parts(33_120_868, 0).saturating_mul(c.into())) .saturating_add(Weight::from_parts(33_163_393, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(2)) .saturating_add(T::DbWeight::get().writes(2))
.saturating_add(Weight::from_parts(0, 32).saturating_mul(c.into())) .saturating_add(Weight::from_parts(0, 32).saturating_mul(c.into()))
@ -107,11 +107,11 @@ impl<T: frame_system::Config> ghost_exodus::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `317 + c * (257 ±0)` // Measured: `317 + c * (257 ±0)`
// Estimated: `3783 + c * (257 ±0)` // Estimated: `3783 + c * (257 ±0)`
// Minimum execution time: 251_478_000 picoseconds. // Minimum execution time: 254_590_000 picoseconds.
Weight::from_parts(254_144_621, 0) Weight::from_parts(258_091_105, 0)
.saturating_add(Weight::from_parts(0, 3783)) .saturating_add(Weight::from_parts(0, 3783))
// Standard Error: 24_613 // Standard Error: 25_082
.saturating_add(Weight::from_parts(6_595_819, 0).saturating_mul(c.into())) .saturating_add(Weight::from_parts(6_595_878, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().writes(2)) .saturating_add(T::DbWeight::get().writes(2))
.saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into())) .saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into()))
@ -131,11 +131,11 @@ impl<T: frame_system::Config> ghost_exodus::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `317 + c * (257 ±0)` // Measured: `317 + c * (257 ±0)`
// Estimated: `3783 + c * (257 ±0)` // Estimated: `3783 + c * (257 ±0)`
// Minimum execution time: 236_155_000 picoseconds. // Minimum execution time: 240_888_000 picoseconds.
Weight::from_parts(242_055_898, 0) Weight::from_parts(245_480_975, 0)
.saturating_add(Weight::from_parts(0, 3783)) .saturating_add(Weight::from_parts(0, 3783))
// Standard Error: 15_623 // Standard Error: 13_733
.saturating_add(Weight::from_parts(360_411, 0).saturating_mul(c.into())) .saturating_add(Weight::from_parts(371_161, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().writes(2)) .saturating_add(T::DbWeight::get().writes(2))
.saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into())) .saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into()))
@ -155,11 +155,11 @@ impl<T: frame_system::Config> ghost_exodus::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `317 + c * (257 ±0)` // Measured: `317 + c * (257 ±0)`
// Estimated: `3783 + c * (257 ±0)` // Estimated: `3783 + c * (257 ±0)`
// Minimum execution time: 249_507_000 picoseconds. // Minimum execution time: 251_521_000 picoseconds.
Weight::from_parts(248_505_595, 0) Weight::from_parts(253_053_636, 0)
.saturating_add(Weight::from_parts(0, 3783)) .saturating_add(Weight::from_parts(0, 3783))
// Standard Error: 20_302 // Standard Error: 14_691
.saturating_add(Weight::from_parts(4_191_526, 0).saturating_mul(c.into())) .saturating_add(Weight::from_parts(4_101_872, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().writes(2)) .saturating_add(T::DbWeight::get().writes(2))
.saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into())) .saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into()))
@ -180,8 +180,8 @@ impl<T: frame_system::Config> ghost_exodus::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `448` // Measured: `448`
// Estimated: `3913` // Estimated: `3913`
// Minimum execution time: 264_214_000 picoseconds. // Minimum execution time: 265_187_000 picoseconds.
Weight::from_parts(266_905_000, 0) Weight::from_parts(269_124_000, 0)
.saturating_add(Weight::from_parts(0, 3913)) .saturating_add(Weight::from_parts(0, 3913))
.saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(3)) .saturating_add(T::DbWeight::get().writes(3))
@ -205,11 +205,11 @@ impl<T: frame_system::Config> ghost_exodus::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `0 + c * (770 ±0)` // Measured: `0 + c * (770 ±0)`
// Estimated: `1551 + c * (1019 ±27)` // Estimated: `1551 + c * (1019 ±27)`
// Minimum execution time: 281_073_000 picoseconds. // Minimum execution time: 284_558_000 picoseconds.
Weight::from_parts(280_778_987, 0) Weight::from_parts(284_117_484, 0)
.saturating_add(Weight::from_parts(0, 1551)) .saturating_add(Weight::from_parts(0, 1551))
// Standard Error: 79_426 // Standard Error: 74_361
.saturating_add(Weight::from_parts(3_449_552, 0).saturating_mul(c.into())) .saturating_add(Weight::from_parts(3_551_271, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().reads(7))
.saturating_add(T::DbWeight::get().writes(3)) .saturating_add(T::DbWeight::get().writes(3))
.saturating_add(Weight::from_parts(0, 1019).saturating_mul(c.into())) .saturating_add(Weight::from_parts(0, 1019).saturating_mul(c.into()))
@ -234,11 +234,11 @@ impl<T: frame_system::Config> ghost_exodus::WeightInfo for WeightInfo<T> {
/// Proof: `GhostExodus::NonceCommitments` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::NonceCommitments` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn register_nonce_commitment() -> Weight { fn register_nonce_commitment() -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `588` // Measured: `627`
// Estimated: `4053` // Estimated: `4092`
// Minimum execution time: 291_972_000 picoseconds. // Minimum execution time: 315_066_000 picoseconds.
Weight::from_parts(295_122_000, 0) Weight::from_parts(318_561_000, 0)
.saturating_add(Weight::from_parts(0, 4053)) .saturating_add(Weight::from_parts(0, 4092))
.saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().reads(8))
.saturating_add(T::DbWeight::get().writes(5)) .saturating_add(T::DbWeight::get().writes(5))
} }
@ -264,11 +264,11 @@ impl<T: frame_system::Config> ghost_exodus::WeightInfo for WeightInfo<T> {
/// Proof: `GhostExodus::GroupCommitmentsConsensus` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::GroupCommitmentsConsensus` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn register_group_commitment() -> Weight { fn register_group_commitment() -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `968` // Measured: `992`
// Estimated: `4433` // Estimated: `4457`
// Minimum execution time: 331_528_000 picoseconds. // Minimum execution time: 338_644_000 picoseconds.
Weight::from_parts(333_974_000, 0) Weight::from_parts(343_954_000, 0)
.saturating_add(Weight::from_parts(0, 4433)) .saturating_add(Weight::from_parts(0, 4457))
.saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().reads(10))
.saturating_add(T::DbWeight::get().writes(4)) .saturating_add(T::DbWeight::get().writes(4))
} }
@ -300,18 +300,22 @@ impl<T: frame_system::Config> ghost_exodus::WeightInfo for WeightInfo<T> {
/// Proof: `GhostExodus::ActiveVerifyingKey` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::ActiveVerifyingKey` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::SignatureScalars` (r:1 w:1) /// Storage: `GhostExodus::SignatureScalars` (r:1 w:1)
/// Proof: `GhostExodus::SignatureScalars` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::SignatureScalars` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::CounterForExodusRequests` (r:1 w:1)
/// Proof: `GhostExodus::CounterForExodusRequests` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::GroupCommitters` (r:0 w:1) /// Storage: `GhostExodus::GroupCommitters` (r:0 w:1)
/// Proof: `GhostExodus::GroupCommitters` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::GroupCommitters` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn register_signature_share() -> Weight { fn register_signature_share() -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `11259` // Measured: `11299`
// Estimated: `14724` // Estimated: `14764`
// Minimum execution time: 3_980_585_000 picoseconds. // Minimum execution time: 3_968_938_000 picoseconds.
Weight::from_parts(3_985_167_000, 0) Weight::from_parts(3_975_088_000, 0)
.saturating_add(Weight::from_parts(0, 14724)) .saturating_add(Weight::from_parts(0, 14764))
.saturating_add(T::DbWeight::get().reads(14)) .saturating_add(T::DbWeight::get().reads(15))
.saturating_add(T::DbWeight::get().writes(8)) .saturating_add(T::DbWeight::get().writes(9))
} }
/// Storage: `GhostExodus::CounterForExodusRequests` (r:1 w:1)
/// Proof: `GhostExodus::CounterForExodusRequests` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `GhostNetworks::Networks` (r:1 w:0) /// Storage: `GhostNetworks::Networks` (r:1 w:0)
/// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostNetworks::GatekeeperAmounts` (r:1 w:1) /// Storage: `GhostNetworks::GatekeeperAmounts` (r:1 w:1)
@ -322,16 +326,16 @@ impl<T: frame_system::Config> ghost_exodus::WeightInfo for WeightInfo<T> {
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::CurrentExodus` (r:1 w:1) /// Storage: `GhostExodus::CurrentExodus` (r:1 w:1)
/// Proof: `GhostExodus::CurrentExodus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::CurrentExodus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusRequests` (r:0 w:1) /// Storage: `GhostExodus::ExodusRequests` (r:1 w:1)
/// Proof: `GhostExodus::ExodusRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::ExodusRequests` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn register_evm_bridge_out_exodus() -> Weight { fn register_evm_bridge_out_exodus() -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `448` // Measured: `448`
// Estimated: `3913` // Estimated: `3913`
// Minimum execution time: 137_053_000 picoseconds. // Minimum execution time: 191_374_000 picoseconds.
Weight::from_parts(138_693_000, 0) Weight::from_parts(192_991_000, 0)
.saturating_add(Weight::from_parts(0, 3913)) .saturating_add(Weight::from_parts(0, 3913))
.saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().reads(7))
.saturating_add(T::DbWeight::get().writes(4)) .saturating_add(T::DbWeight::get().writes(5))
} }
} }

View File

@ -16,7 +16,7 @@
//! Autogenerated weights for `ghost_governance` //! Autogenerated weights for `ghost_governance`
//! //!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
//! DATE: 2026-09-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! DATE: 2026-09-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000` //! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `ghostown`, CPU: `Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz` //! HOSTNAME: `ghostown`, CPU: `Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz`
//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("casper-dev")`, DB CACHE: 1024 //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("casper-dev")`, DB CACHE: 1024
@ -50,21 +50,23 @@ impl<T: frame_system::Config> ghost_governance::WeightInfo for WeightInfo<T> {
/// Proof: `GhostGovernance::MemberShares` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostGovernance::MemberShares` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostGovernance::ClaimedHashes` (r:1 w:1) /// Storage: `GhostGovernance::ClaimedHashes` (r:1 w:1)
/// Proof: `GhostGovernance::ClaimedHashes` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostGovernance::ClaimedHashes` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostGovernance::ClaimedShares` (r:1 w:1)
/// Proof: `GhostGovernance::ClaimedShares` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostGovernance::NetworkShares` (r:1 w:1) /// Storage: `GhostGovernance::NetworkShares` (r:1 w:1)
/// Proof: `GhostGovernance::NetworkShares` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostGovernance::NetworkShares` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostGovernance::GlobalShares` (r:1 w:1) /// Storage: `GhostGovernance::GlobalShares` (r:1 w:1)
/// Proof: `GhostGovernance::GlobalShares` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Proof: `GhostGovernance::GlobalShares` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// The range of component `p` is `[1, 20]`. /// The range of component `p` is `[1, 18]`.
fn claim(p: u32, ) -> Weight { fn claim(p: u32, ) -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `283` // Measured: `283`
// Estimated: `3748` // Estimated: `3748`
// Minimum execution time: 214_208_000 picoseconds. // Minimum execution time: 229_254_000 picoseconds.
Weight::from_parts(216_987_870, 0) Weight::from_parts(229_992_093, 0)
.saturating_add(Weight::from_parts(0, 3748)) .saturating_add(Weight::from_parts(0, 3748))
// Standard Error: 30_856 // Standard Error: 25_680
.saturating_add(Weight::from_parts(1_702_104, 0).saturating_mul(p.into())) .saturating_add(Weight::from_parts(1_874_433, 0).saturating_mul(p.into()))
.saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().writes(4)) .saturating_add(T::DbWeight::get().writes(5))
} }
} }

View File

@ -16,7 +16,7 @@
//! Autogenerated weights for `ghost_weaver` //! Autogenerated weights for `ghost_weaver`
//! //!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
//! DATE: 2026-09-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! DATE: 2026-09-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000` //! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `ghostown`, CPU: `Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz` //! HOSTNAME: `ghostown`, CPU: `Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz`
//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("casper-dev")`, DB CACHE: 1024 //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("casper-dev")`, DB CACHE: 1024
@ -61,11 +61,11 @@ impl<T: frame_system::Config> ghost_weaver::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `223 + a * (32 ±0)` // Measured: `223 + a * (32 ±0)`
// Estimated: `3687 + a * (32 ±0)` // Estimated: `3687 + a * (32 ±0)`
// Minimum execution time: 211_189_000 picoseconds. // Minimum execution time: 212_751_000 picoseconds.
Weight::from_parts(215_074_977, 0) Weight::from_parts(215_022_916, 0)
.saturating_add(Weight::from_parts(0, 3687)) .saturating_add(Weight::from_parts(0, 3687))
// Standard Error: 1_505 // Standard Error: 1_319
.saturating_add(Weight::from_parts(46_499, 0).saturating_mul(a.into())) .saturating_add(Weight::from_parts(53_379, 0).saturating_mul(a.into()))
.saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().writes(1)) .saturating_add(T::DbWeight::get().writes(1))
.saturating_add(Weight::from_parts(0, 32).saturating_mul(a.into())) .saturating_add(Weight::from_parts(0, 32).saturating_mul(a.into()))
@ -87,11 +87,11 @@ impl<T: frame_system::Config> ghost_weaver::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `518 + a * (32 ±0)` // Measured: `518 + a * (32 ±0)`
// Estimated: `3982 + a * (32 ±0)` // Estimated: `3982 + a * (32 ±0)`
// Minimum execution time: 241_010_000 picoseconds. // Minimum execution time: 243_006_000 picoseconds.
Weight::from_parts(245_378_305, 0) Weight::from_parts(246_374_350, 0)
.saturating_add(Weight::from_parts(0, 3982)) .saturating_add(Weight::from_parts(0, 3982))
// Standard Error: 1_583 // Standard Error: 1_865
.saturating_add(Weight::from_parts(32_330, 0).saturating_mul(a.into())) .saturating_add(Weight::from_parts(38_601, 0).saturating_mul(a.into()))
.saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(2)) .saturating_add(T::DbWeight::get().writes(2))
.saturating_add(Weight::from_parts(0, 32).saturating_mul(a.into())) .saturating_add(Weight::from_parts(0, 32).saturating_mul(a.into()))
@ -106,20 +106,20 @@ impl<T: frame_system::Config> ghost_weaver::WeightInfo for WeightInfo<T> {
/// Proof: `GhostNetworks::GatekeeperAmounts` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::GatekeeperAmounts` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostNetworks::NetworkImbalance` (r:1 w:1) /// Storage: `GhostNetworks::NetworkImbalance` (r:1 w:1)
/// Proof: `GhostNetworks::NetworkImbalance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::NetworkImbalance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `System::Account` (r:1 w:0) /// Storage: `System::Account` (r:1 w:1)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
/// The range of component `a` is `[1, 144]`. /// The range of component `a` is `[1, 144]`.
fn pull_utxo_thread(a: u32, ) -> Weight { fn pull_utxo_thread(a: u32, ) -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `346` // Measured: `449`
// Estimated: `3811` // Estimated: `3914`
// Minimum execution time: 170_701_000 picoseconds. // Minimum execution time: 217_880_000 picoseconds.
Weight::from_parts(170_150_850, 0) Weight::from_parts(217_119_201, 0)
.saturating_add(Weight::from_parts(0, 3811)) .saturating_add(Weight::from_parts(0, 3914))
// Standard Error: 4_368 // Standard Error: 5_460
.saturating_add(Weight::from_parts(3_363_930, 0).saturating_mul(a.into())) .saturating_add(Weight::from_parts(3_445_491, 0).saturating_mul(a.into()))
.saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(3)) .saturating_add(T::DbWeight::get().writes(4))
} }
/// Storage: `GhostNetworks::Networks` (r:1 w:0) /// Storage: `GhostNetworks::Networks` (r:1 w:0)
/// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`)
@ -131,16 +131,16 @@ impl<T: frame_system::Config> ghost_weaver::WeightInfo for WeightInfo<T> {
/// Proof: `GhostNetworks::GatekeeperAmounts` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::GatekeeperAmounts` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostNetworks::NetworkImbalance` (r:1 w:1) /// Storage: `GhostNetworks::NetworkImbalance` (r:1 w:1)
/// Proof: `GhostNetworks::NetworkImbalance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::NetworkImbalance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `System::Account` (r:1 w:0) /// Storage: `System::Account` (r:1 w:1)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
fn pull_evm_thread() -> Weight { fn pull_evm_thread() -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `346` // Measured: `449`
// Estimated: `3811` // Estimated: `3914`
// Minimum execution time: 138_215_000 picoseconds. // Minimum execution time: 183_413_000 picoseconds.
Weight::from_parts(140_004_000, 0) Weight::from_parts(186_271_000, 0)
.saturating_add(Weight::from_parts(0, 3811)) .saturating_add(Weight::from_parts(0, 3914))
.saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(3)) .saturating_add(T::DbWeight::get().writes(4))
} }
} }

File diff suppressed because one or more lines are too long