Compare commits

...

43 Commits

Author SHA1 Message Date
07cf35d08c
make flag correct for fix-weaver-keystore
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-11-01 18:05:34 +03:00
8dfc37e97e
fix migration struct in the runtime
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-11-01 15:43:41 +03:00
d973b096d9
purge attestation during on_initialize
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-11-01 15:30:42 +03:00
4842fc5108
update keytype ID prefix to 'wevr' for validator consistency
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-11-01 14:40:39 +03:00
d43c616749
resolve global dos vulnerability by isolating attested blocks per network
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-11-01 14:37:02 +03:00
52a1aa0fab
avoid clone during local keys extraction
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-10-31 16:45:14 +03:00
c25c3edd30
remove dead RPC endpoint from tester and bump runtime version
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-10-30 19:59:32 +03:00
2ade4c9637
pace RPC requests, extend storage lock, and improve logs
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-10-30 19:45:04 +03:00
014ed7d5dc
resolve infinite loop in phase switching and block drift
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-10-30 18:29:38 +03:00
a7d3a3fee8
update RPC scripts to support weaver
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-10-30 14:56:23 +03:00
844fc81680
update default weights and runtime weights
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-10-30 11:24:46 +03:00
8e44a7f5d2
Merge branch 'pallet-weaver' into pallet-combined
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-10-29 14:25:50 +03:00
5380ac0b36
Merge branch 'pallet-networks' into pallet-combined
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-10-29 14:24:57 +03:00
9610423617
Merge branch 'pallet-helpers' into pallet-combined
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-10-29 14:22:36 +03:00
d0db73170d
extend unsigned validation to prevent transaction replay
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-10-27 19:15:22 +03:00
d18b8ba11f
refactor package storage to use classic bitmap and dense data blob
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-10-26 21:02:20 +03:00
c22925d758
new governance functions added into GovernanceAction enum
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-10-25 19:22:21 +03:00
6df2088756
add setBondDepository to EVM governance requests
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-10-21 19:50:38 +03:00
6ae8fd9ed4
extend loom state to track last extracted median block
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-10-21 19:32:57 +03:00
ba575d0ab8
secure unsigned validation with help of sliding window
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-10-21 18:46:47 +03:00
ab7f978ccc
expose exodus-pallet API in casper runtime
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-10-21 17:27:12 +03:00
66ca7545ce
merge EVM chains by type into a single ExodusRequest during Round 7
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-10-21 17:25:35 +03:00
bdbf7bd20f
fix for https://ghostforum.org/t/high-lack-of-key-epoch-invalidation-across-dkg-rotations-grants-indefinite-signing-power-to-retired-validator-coalitions-missing-chainid-binding-in-gatekeeper-rotate-amplifies-cross-chain-impact/125 and some other small fixes
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-10-15 21:02:02 +03:00
f72387c9cb
fix for https://ghostforum.org/t/critical-unlimited-token-minting-via-weaver-session-replay/100
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-10-10 18:17:08 +03:00
d940ede8ac
fix for https://ghostforum.org/t/weaver-networkattestations-vec-never-grows-validator-set-growth-locks-out-new-authorities-and-halts-the-bridge/163
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-10-10 18:01:42 +03:00
653bf08486
fix for https://ghostforum.org/t/h16-dkg-round-1-rejects-all-honest-packages-unless-exactly-threshold-n-1-registered-round-0-deterministic-bridge-halt-for-n-7/123
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-10-10 17:16:31 +03:00
1dc61fb17d
fix GJKR arbitration, avoid invalid kicks, and synchronize round 5 output
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-10-10 15:48:50 +03:00
d488bf9a43
fix for https://ghostforum.org/t/pallets-exodus-round-5-to-6-bitmap-intersection-desyncs-the-proof-gate-for-dense-validator-sets-dkg-restart-loop-on-ordinary-dropouts/168
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-10-09 19:34:10 +03:00
74d74f4037
fix for https://ghostforum.org/t/cross-generation-dkg-package-replay-signed-payload-does-not-bind-dkg-index/105
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-10-09 19:07:35 +03:00
7ad0159517
fix for https://ghostforum.org/t/dkg-finalization-gated-on-old-set-orphan-rotation-requests-accumulate-without-expiry/132
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-10-09 17:55:22 +03:00
c884ec7b77
fix fir https://ghostforum.org/t/malicious-authority-can-permanently-kill-an-exodus-signing-session-via-an-undecodable-nonce-commitment/103
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-10-09 16:41:56 +03:00
e3a81e3902
https://ghostforum.org/t/bounty-of-100-encodes-as-0-in-evm-calldata-as-u32-wrap/133
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-10-09 15:23:20 +03:00
d7ac7885b6
Merge branch 'pallet-exodus' into pallet-weaver
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-10-09 15:07:43 +03:00
f3e7122d43
remove Header struct and implement stateless merkle verification
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-10-09 14:56:43 +03:00
84b6dfad9f
fix for https://ghostforum.org/t/critical-utxo-multi-mint-via-non-canonical-varint-re-encoding/111
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-10-09 13:23:23 +03:00
b6759bac6f
optimize merkle tree prefixed computations
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-10-04 19:47:21 +03:00
5f39a7d9b1
optimize pallet hashing to eliminate dynamic memory allocations
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-10-02 15:35:38 +03:00
8abd114a5b
more secure and optimized Round 5 metadata hashing pipeline
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-10-01 21:41:27 +03:00
6b0033425c
implement more secure entropy hedging for offchain RNG
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-10-01 21:20:17 +03:00
f77ca05d3f
avoid partial writes during register_incoming/register_outgoing
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-09-26 16:20:46 +03:00
339ea52864
avoid memory allocation during and_provides
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-09-26 16:04:57 +03:00
d8bfe2a2ac
correct replay check logic and secure unsigned thread validation
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-09-26 15:13:07 +03:00
77bf4419e0
prevent exploit via compute_arguments_hash bypass
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-09-26 14:30:22 +03:00
57 changed files with 5809 additions and 3622 deletions

20
Cargo.lock generated
View File

@ -1195,7 +1195,7 @@ dependencies = [
[[package]] [[package]]
name = "casper-runtime" name = "casper-runtime"
version = "4.0.14" version = "4.0.18"
dependencies = [ dependencies = [
"casper-runtime-constants", "casper-runtime-constants",
"frame-benchmarking", "frame-benchmarking",
@ -1209,6 +1209,7 @@ dependencies = [
"frame-try-runtime", "frame-try-runtime",
"ghost-core-primitives", "ghost-core-primitives",
"ghost-exodus", "ghost-exodus",
"ghost-exodus-runtime-api",
"ghost-governance", "ghost-governance",
"ghost-helpers", "ghost-helpers",
"ghost-networks", "ghost-networks",
@ -3684,7 +3685,7 @@ dependencies = [
[[package]] [[package]]
name = "ghost-exodus" name = "ghost-exodus"
version = "0.0.17" version = "0.1.7"
dependencies = [ dependencies = [
"chacha20poly1305", "chacha20poly1305",
"frame-benchmarking", "frame-benchmarking",
@ -3694,6 +3695,7 @@ dependencies = [
"frost-core", "frost-core",
"frost-ed25519", "frost-ed25519",
"frost-secp256k1-tr", "frost-secp256k1-tr",
"ghost-exodus-runtime-api",
"ghost-helpers", "ghost-helpers",
"ghost-networks", "ghost-networks",
"ghost-traits", "ghost-traits",
@ -3725,15 +3727,17 @@ dependencies = [
[[package]] [[package]]
name = "ghost-exodus-runtime-api" name = "ghost-exodus-runtime-api"
version = "0.0.0" version = "0.0.2"
dependencies = [ dependencies = [
"ghost-helpers",
"parity-scale-codec", "parity-scale-codec",
"sp-api", "sp-api",
"sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.12.0)",
] ]
[[package]] [[package]]
name = "ghost-governance" name = "ghost-governance"
version = "0.3.10" version = "0.3.11"
dependencies = [ dependencies = [
"frame-benchmarking", "frame-benchmarking",
"frame-support", "frame-support",
@ -3758,7 +3762,7 @@ dependencies = [
[[package]] [[package]]
name = "ghost-helpers" name = "ghost-helpers"
version = "0.0.11" version = "0.0.16"
dependencies = [ dependencies = [
"frame-support", "frame-support",
"ghost-traits", "ghost-traits",
@ -3840,7 +3844,7 @@ dependencies = [
[[package]] [[package]]
name = "ghost-networks" name = "ghost-networks"
version = "0.2.10" version = "0.2.12"
dependencies = [ dependencies = [
"frame-benchmarking", "frame-benchmarking",
"frame-support", "frame-support",
@ -4086,7 +4090,7 @@ dependencies = [
[[package]] [[package]]
name = "ghost-traits" name = "ghost-traits"
version = "0.4.6" version = "0.4.10"
dependencies = [ dependencies = [
"frame-support", "frame-support",
"frost-core", "frost-core",
@ -4110,7 +4114,7 @@ dependencies = [
[[package]] [[package]]
name = "ghost-weaver" name = "ghost-weaver"
version = "0.0.14" version = "0.0.26"
dependencies = [ dependencies = [
"frame-benchmarking", "frame-benchmarking",
"frame-support", "frame-support",

View File

@ -239,6 +239,7 @@ ghost-networks = { path = "pallets/networks", default-features = false }
ghost-governance = { path = "pallets/governance", default-features = false } ghost-governance = { path = "pallets/governance", default-features = false }
ghost-weaver = { path = "pallets/weaver", default-features = false } ghost-weaver = { path = "pallets/weaver", default-features = false }
ghost-exodus = { path = "pallets/exodus", default-features = false } ghost-exodus = { path = "pallets/exodus", default-features = false }
ghost-exodus-runtime-api = { path = "pallets/exodus/runtime-api", default-features = false }
ghost-sudo = { path = "pallets/sudo", default-features = false } ghost-sudo = { path = "pallets/sudo", default-features = false }
ghost-nomination-pools = { path = "pallets/pools", default-features = false } ghost-nomination-pools = { path = "pallets/pools", default-features = false }
ghost-nomination-pools-runtime-api = { path = "pallets/pools/runtime-api", default-features = false } ghost-nomination-pools-runtime-api = { path = "pallets/pools/runtime-api", default-features = false }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ghost-exodus" name = "ghost-exodus"
version = "0.0.17" version = "0.1.7"
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
@ -40,6 +40,7 @@ parking_lot = { workspace = true }
ghost-traits = { workspace = true } ghost-traits = { workspace = true }
ghost-helpers = { workspace = true } ghost-helpers = { workspace = true }
ghost-exodus-runtime-api = { workspace = true }
pallet-balances = { workspace = true } pallet-balances = { workspace = true }
pallet-session = { workspace = true } pallet-session = { workspace = true }
@ -80,6 +81,7 @@ std = [
"frost-core/std", "frost-core/std",
"ghost-traits/std", "ghost-traits/std",
"ghost-helpers/std", "ghost-helpers/std",
"ghost-exodus-runtime-api/std",
"pallet-balances/std", "pallet-balances/std",
"pallet-session/std", "pallet-session/std",
"pallet-staking/std", "pallet-staking/std",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ghost-exodus-runtime-api" name = "ghost-exodus-runtime-api"
version = "0.0.0" version = "0.0.2"
description = "RPC runtime API for ghost EXODUS pallet" description = "RPC runtime API for ghost EXODUS pallet"
license.workspace = true license.workspace = true
authors.workspace = true authors.workspace = true
@ -14,7 +14,14 @@ targets = ["x86_64-unknown-linux-gnu"]
[dependencies] [dependencies]
codec = { workspace = true, features = ["derive"] } codec = { workspace = true, features = ["derive"] }
sp-api = { workspace = true } sp-api = { workspace = true }
sp-std = { workspace = true }
ghost-helpers = { workspace = true }
[features] [features]
default = ["std"] default = ["std"]
std = ["codec/std", "sp-api/std"] std = [
"codec/std",
"sp-api/std",
"sp-std/std",
"ghost-helpers/std",
]

View File

@ -1,7 +1,17 @@
#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), no_std)]
use sp_std::vec::Vec;
use ghost_helpers::networks::{NetworkType, NetworkCurve};
pub type PreparedEvmMessage = Option<(Vec<u8>, Vec<u8>, Vec<u8>)>;
sp_api::decl_runtime_apis! { sp_api::decl_runtime_apis! {
pub trait ExodusApi<NetworkId> { pub trait ExodusApi {
fn verifying_key(network_id: NetworkId) -> Vec<u8>; fn get_evm_rotation_metadata(dkg_index: u64, network_type: NetworkType) -> PreparedEvmMessage;
fn get_evm_bridge_metadata(dkg_index: u64, network_id: u64, session: u64) -> PreparedEvmMessage;
fn get_evm_governance_metadata(dkg_index: u64, network_id: u64, session: u64) -> PreparedEvmMessage;
fn pending_exodus_request_count() -> u32;
fn is_qualification_ongoing(curve: NetworkCurve) -> bool;
} }
} }

View File

@ -7,7 +7,7 @@ use frame_system::RawOrigin;
use ghost_helpers::networks::NetworkDataBuilder; use ghost_helpers::networks::NetworkDataBuilder;
fn prepare_pallet<T: Config>(authorities_len: usize) -> ( fn prepare_pallet<T: Config>(authorities_len: usize) -> (
PendingDkgAuthorities<ParticipantsBitmap<T>, BlockNumberFor<T>>, PendingDkgAuthorities<T>,
NetworkCurve, NetworkCurve,
T::AuthorityId, T::AuthorityId,
AuthIndex AuthIndex
@ -47,11 +47,13 @@ mod benchmarks {
prepare_pallet::<T>(1); prepare_pallet::<T>(1);
dkg_state.next_phase(); dkg_state.next_phase();
let dkg_index = dkg_state.get_dkg_index();
QualificationDkgState::<T>::insert(&network_curve, dkg_state); QualificationDkgState::<T>::insert(&network_curve, dkg_state);
let round0_package = PackageContext::default() let round0_package = PackageContext::default()
.with_network_curve(NetworkCurve::Secp256k1) .with_network_curve(NetworkCurve::Secp256k1)
.with_authority_index(0) .with_authority_index(0)
.with_dkg_index(dkg_index)
.with_dkg_round0(dummy_hash); .with_dkg_round0(dummy_hash);
let dkg_package = DkgPackage::Round0(round0_package); let dkg_package = DkgPackage::Round0(round0_package);
@ -83,10 +85,10 @@ mod benchmarks {
dkg_state.next_phase(); dkg_state.next_phase();
dkg_state.insert_index(authority_index); dkg_state.insert_index(authority_index);
let dkg_index = dkg_state.get_dkg_index();
QualificationDkgState::<T>::insert(&network_curve, dkg_state); QualificationDkgState::<T>::insert(&network_curve, dkg_state);
let mut rng = Pallet::<T>::get_offchain_rng(authority_index); let mut rng = ChaCha20Rng::seed_from_u64(694201337);
let (_, public_package) = network_curve.dkg_part1( let (_, public_package) = network_curve.dkg_part1(
authority_index, authority_index,
max_signers, max_signers,
@ -97,9 +99,12 @@ mod benchmarks {
let package_hash = ExodusHash::from(blake2_256(&public_package)); let package_hash = ExodusHash::from(blake2_256(&public_package));
Round0Packages::<T>::insert(network_curve, authority_index, package_hash); Round0Packages::<T>::insert(network_curve, authority_index, package_hash);
let current_block = <T as Config>::BlockNumberProvider::current_block_number();
let round1_package = PackageContext::default() let round1_package = PackageContext::default()
.with_block_number(current_block)
.with_network_curve(NetworkCurve::Secp256k1) .with_network_curve(NetworkCurve::Secp256k1)
.with_authority_index(0) .with_authority_index(0)
.with_dkg_index(dkg_index)
.with_dkg_round1(public_package) .with_dkg_round1(public_package)
.expect("Round1 package should be valid"); .expect("Round1 package should be valid");
@ -120,9 +125,9 @@ mod benchmarks {
#[benchmark] #[benchmark]
fn register_encrypted_round2_packages( fn register_encrypted_round2_packages(
c: Linear<1, { T::MaxAuthorities::get() / 8 }> c: Linear<1, { T::MaxAuthorities::get() }>
) -> Result<(), BenchmarkError> { ) -> Result<(), BenchmarkError> {
let max_signers = (c * 8) as AuthIndex; let max_signers = c as AuthIndex;
let (mut dkg_state, network_curve, authority, authority_index) = let (mut dkg_state, network_curve, authority, authority_index) =
prepare_pallet::<T>(max_signers as usize); prepare_pallet::<T>(max_signers as usize);
@ -131,6 +136,8 @@ mod benchmarks {
dkg_state.next_phase(); dkg_state.next_phase();
dkg_state.next_phase(); dkg_state.next_phase();
let dkg_index = dkg_state.get_dkg_index();
(0..max_signers).for_each(|i| { (0..max_signers).for_each(|i| {
dkg_state.insert_index(i); dkg_state.insert_index(i);
}); });
@ -142,29 +149,29 @@ mod benchmarks {
QualificationDkgState::<T>::insert(&network_curve, dkg_state); QualificationDkgState::<T>::insert(&network_curve, dkg_state);
let bitmask_len = padded_participants.div_ceil(8);
let blob_len = round2_encrypted_package_size( let blob_len = round2_encrypted_package_size(
padded_participants, padded_participants - 1,
network_curve.scalar_bytes_len(), network_curve.scalar_bytes_len(),
network_curve.header_bytes_len(), network_curve.header_bytes_len(),
); );
let mut bitmask = vec![0u8; bitmask_len]; let mut bitmask = ParticipantsBitmap::<T>::empty(padded_participants);
let blob = vec![0u8; blob_len]; let blob = vec![0u8; blob_len];
(0..max_signers).for_each(|i| { (0..max_signers).for_each(|i| {
if i != authority_index { if i != authority_index {
let byte_index = (i / 8) as usize; bitmask.insert(i);
let bit_index = (i % 8) as u8;
bitmask[byte_index] |= 1 << bit_index;
} }
}); });
let encryption_bundle = BundledPackages { bitmask, blob }; let encryption_bundle = BundledPackages { bitmask, blob };
let current_block = <T as Config>::BlockNumberProvider::current_block_number();
let round2_package = PackageContext::default() let round2_package = PackageContext::default()
.with_block_number(current_block)
.with_network_curve(NetworkCurve::Secp256k1) .with_network_curve(NetworkCurve::Secp256k1)
.with_authority_index(authority_index) .with_authority_index(authority_index)
.with_dkg_index(dkg_index)
.with_dkg_round2(encryption_bundle) .with_dkg_round2(encryption_bundle)
.expect("Round2 package should be valid"); .expect("Round2 package should be valid");
@ -185,9 +192,9 @@ mod benchmarks {
#[benchmark] #[benchmark]
fn register_round3_complaints( fn register_round3_complaints(
c: Linear<1, { T::MaxAuthorities::get() / 8 }> c: Linear<1, { T::MaxAuthorities::get() }>
) -> Result<(), BenchmarkError> { ) -> Result<(), BenchmarkError> {
let max_signers = (c * 8) as AuthIndex; let max_signers = c as AuthIndex;
let (mut dkg_state, network_curve, authority, authority_index) = let (mut dkg_state, network_curve, authority, authority_index) =
prepare_pallet::<T>(max_signers as usize); prepare_pallet::<T>(max_signers as usize);
@ -197,26 +204,28 @@ mod benchmarks {
dkg_state.next_phase(); dkg_state.next_phase();
dkg_state.next_phase(); dkg_state.next_phase();
let dkg_index = dkg_state.get_dkg_index();
(0..max_signers).for_each(|i| { (0..max_signers).for_each(|i| {
dkg_state.insert_index(i); dkg_state.insert_index(i);
}); });
QualificationDkgState::<T>::insert(&network_curve, dkg_state); QualificationDkgState::<T>::insert(&network_curve, dkg_state);
let bitvec_len = (max_signers + 7) / 8; let mut accused_indexes = ParticipantsBitmap::<T>::empty(max_signers);
let mut accused_indexes = vec![0u8; bitvec_len as usize];
(0..max_signers).for_each(|i| { (0..max_signers).for_each(|i| {
if i != authority_index { if i != authority_index {
let byte_index = (i / 8) as usize; accused_indexes.insert(i);
let bit_index = (i % 8) as u8;
accused_indexes[byte_index] |= 1 << bit_index;
} }
}); });
let current_block = <T as Config>::BlockNumberProvider::current_block_number();
let round3_package = PackageContext::default() let round3_package = PackageContext::default()
.with_block_number(current_block)
.with_network_curve(NetworkCurve::Secp256k1) .with_network_curve(NetworkCurve::Secp256k1)
.with_authority_index(authority_index) .with_authority_index(authority_index)
.with_dkg_index(dkg_index)
.with_dkg_round3(accused_indexes) .with_dkg_round3(accused_indexes)
.expect("Round3 package should be valid"); .expect("Round3 package should be valid");
@ -240,9 +249,9 @@ mod benchmarks {
#[benchmark] #[benchmark]
fn register_round4_justifications( fn register_round4_justifications(
c: Linear<1, { T::MaxAuthorities::get() / 8 }> c: Linear<1, { T::MaxAuthorities::get() }>
) -> Result<(), BenchmarkError> { ) -> Result<(), BenchmarkError> {
let max_signers = (c * 8) as AuthIndex; let max_signers = c as AuthIndex;
let (mut dkg_state, network_curve, authority, authority_index) = let (mut dkg_state, network_curve, authority, authority_index) =
prepare_pallet::<T>(max_signers as usize); prepare_pallet::<T>(max_signers as usize);
@ -253,6 +262,8 @@ mod benchmarks {
dkg_state.next_phase(); dkg_state.next_phase();
dkg_state.next_phase(); dkg_state.next_phase();
let dkg_index = dkg_state.get_dkg_index();
(0..max_signers).for_each(|i| { (0..max_signers).for_each(|i| {
dkg_state.insert_index(i); dkg_state.insert_index(i);
}); });
@ -264,29 +275,29 @@ mod benchmarks {
QualificationDkgState::<T>::insert(&network_curve, dkg_state); QualificationDkgState::<T>::insert(&network_curve, dkg_state);
let bitmask_len = padded_participants.div_ceil(8);
let blob_len = round2_package_size( let blob_len = round2_package_size(
padded_participants, padded_participants - 1,
network_curve.scalar_bytes_len(), network_curve.scalar_bytes_len(),
network_curve.header_bytes_len(), network_curve.header_bytes_len(),
); );
let mut bitmask = vec![0u8; bitmask_len]; let mut bitmask = ParticipantsBitmap::<T>::empty(padded_participants);
let blob = vec![0u8; blob_len]; let blob = vec![0u8; blob_len];
(0..max_signers).for_each(|i| { (0..max_signers).for_each(|i| {
if i != authority_index { if i != authority_index {
let byte_index = (i / 8) as usize; bitmask.insert(i);
let bit_index = (i % 8) as u8;
bitmask[byte_index] |= 1 << bit_index;
} }
}); });
let justification_bundle = BundledPackages { bitmask, blob }; let justification_bundle = BundledPackages { bitmask, blob };
let current_block = <T as Config>::BlockNumberProvider::current_block_number();
let round4_package = PackageContext::default() let round4_package = PackageContext::default()
.with_block_number(current_block)
.with_network_curve(NetworkCurve::Secp256k1) .with_network_curve(NetworkCurve::Secp256k1)
.with_authority_index(authority_index) .with_authority_index(authority_index)
.with_dkg_index(dkg_index)
.with_dkg_round4(justification_bundle) .with_dkg_round4(justification_bundle)
.expect("Round4 package should be valid"); .expect("Round4 package should be valid");
@ -332,11 +343,19 @@ mod benchmarks {
let dummy_merkle_root = ExodusHash::repeat_byte(69); let dummy_merkle_root = ExodusHash::repeat_byte(69);
let dummy_verifying_key = vec![69u8; network_curve.element_bytes_len()]; let dummy_verifying_key = vec![69u8; network_curve.element_bytes_len()];
let dummy_key_members = ParticipantsBitmap::<T>::empty(4);
let current_block = <T as Config>::BlockNumberProvider::current_block_number();
let round5_package = PackageContext::default() let round5_package = PackageContext::default()
.with_block_number(current_block)
.with_network_curve(NetworkCurve::Secp256k1) .with_network_curve(NetworkCurve::Secp256k1)
.with_authority_index(authority_index) .with_authority_index(authority_index)
.with_dkg_round5(dummy_verifying_key.clone(), dummy_merkle_root) .with_dkg_index(dkg_index)
.with_dkg_round5(
dummy_verifying_key.clone(),
dummy_key_members.clone(),
dummy_merkle_root,
)
.expect("Round5 package should be valid"); .expect("Round5 package should be valid");
let dkg_package = DkgPackage::Round5(round5_package); let dkg_package = DkgPackage::Round5(round5_package);
@ -354,20 +373,22 @@ mod benchmarks {
).expect("Bounded verifying key should be correct"); ).expect("Bounded verifying key should be correct");
let metadata_hashes = [ let metadata_hashes = [
SubstrateBlake2Hasher::hash(dummy_merkle_root.as_ref()), dummy_merkle_root.as_ref(),
SubstrateBlake2Hasher::hash(bounded_verifying_key.as_ref()), bounded_verifying_key.as_ref(),
dummy_key_members.inner_as_ref(),
]; ];
let verifying_hash = sequential_hash::<SubstrateBlake2Hasher, _>(metadata_hashes); let verifying_hash = cumulative_hash::<SubstrateBlake2Hasher, _>(metadata_hashes);
let verification_key = (network_curve, dkg_index, verifying_hash); let verification_key = (network_curve, dkg_index, verifying_hash);
let participants = Verifications::<T>::get(verification_key); let participants = Verifications::<T>::get(verification_key);
assert!(participants.contains(authority_index)); assert!(participants.contains(authority_index));
let consensus = VerifyingKeyConsensus::<T>::get(&network_curve, dkg_index); let consensus = VerifyingKeyConsensus::<T>::get(&network_curve, dkg_index);
assert!(consensus.participants.contains(authority_index)); assert!(consensus.state.participants.contains(authority_index));
assert_eq!(consensus.participants.count_ones::<AuthIndex>(), 1); assert_eq!(consensus.state.participants.count_ones::<AuthIndex>(), 1);
assert_eq!(consensus.merkle_root, dummy_merkle_root); assert_eq!(consensus.state.merkle_root, dummy_merkle_root);
assert_eq!(consensus.element_bytes, bounded_verifying_key); assert_eq!(consensus.state.element_bytes, bounded_verifying_key);
assert_eq!(consensus.members, dummy_key_members);
Ok(()) Ok(())
} }
@ -415,10 +436,16 @@ mod benchmarks {
dummy_values.insert(identifier, dummy_value.clone()); dummy_values.insert(identifier, dummy_value.clone());
} }
let identifier_size = network_curve.scalar_bytes_len();
let merkle_tree = let merkle_tree =
NetworkCurve::build_merkle_tree( NetworkCurve::build_merkle_tree(
identifier_size + dummy_value.len(),
&dummy_values, &dummy_values,
|value: &Vec<u8>| Ok(value.clone()), |value, buffer| {
buffer.extend_from_slice(value.as_ref());
Ok(())
}
).expect("Merkle tree for verifying shares should be valid"); ).expect("Merkle tree for verifying shares should be valid");
let merkle_root = merkle_tree.last().copied().unwrap(); let merkle_root = merkle_tree.last().copied().unwrap();
@ -434,13 +461,16 @@ mod benchmarks {
}); });
VerifyingKeyConsensus::<T>::mutate(&network_curve, &dkg_index, |consensus| { VerifyingKeyConsensus::<T>::mutate(&network_curve, &dkg_index, |consensus| {
consensus.participants.insert(authority_index); consensus.state.participants.insert(authority_index);
consensus.merkle_root = dummy_merkle_root; consensus.state.merkle_root = dummy_merkle_root;
}); });
let current_block = <T as Config>::BlockNumberProvider::current_block_number();
let round6_package = PackageContext::default() let round6_package = PackageContext::default()
.with_block_number(current_block)
.with_network_curve(NetworkCurve::Secp256k1) .with_network_curve(NetworkCurve::Secp256k1)
.with_authority_index(authority_index) .with_authority_index(authority_index)
.with_dkg_index(dkg_index)
.with_dkg_round6(dummy_verifying_share.clone(), dummy_merkle_proof) .with_dkg_round6(dummy_verifying_share.clone(), dummy_merkle_proof)
.expect("Round6 package should be valid"); .expect("Round6 package should be valid");
@ -482,10 +512,22 @@ mod benchmarks {
} }
dkg_state.insert_index(authority_index); dkg_state.insert_index(authority_index);
let exodus_session = CurrentExodus::<T>::get(); let all_exodus_sessions = CurrentExodusSessions::<T>::get();
let exodus_session = all_exodus_sessions
.get(&network_id)
.copied()
.unwrap_or(0);
let dummy_context = ExodusContext::new(network_id, exodus_session);
let dummy_address = EvmAddress::repeat_byte(69); let dummy_address = EvmAddress::repeat_byte(69);
let dummy_nonce = vec![0u8; network_curve.element_bytes_len()];
// generator point for secp256k1
let dummy_nonce = [
0x02, 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55,
0xa0, 0x62, 0x95, 0xce, 0x87, 0x0b, 0x07, 0x02, 0x9b, 0xfc,
0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b, 0x16,
0xf8, 0x17, 0x98
];
let create_bond = GovernanceAction::Create { let create_bond = GovernanceAction::Create {
market: [69, 420, 1337, 0], market: [69, 420, 1337, 0],
@ -494,27 +536,30 @@ mod benchmarks {
intervals: [34, 35], intervals: [34, 35],
booleans: [true, true], booleans: [true, true],
}; };
let request = ExodusRequest::<NetworkIdOf<T>, BalanceOf<T>>::evm_governance( let request = ExodusRequest::<T>::evm_governance(
exodus_session,
network_id,
dummy_address, dummy_address,
create_bond, create_bond,
); );
let current_block = <T as Config>::BlockNumberProvider::current_block_number();
let authorities = QualificationAuthorities::<T>::get(&network_curve); let authorities = QualificationAuthorities::<T>::get(&network_curve);
let active_dkg: ActivatedState<T> = (&dkg_state).into(); let active_dkg: ReadyDkgAuthorities<T> = (&dkg_state).into();
let active_dkg_index = active_dkg.get_dkg_index();
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, &dummy_context), request);
let nonce_package = PackageContext::default() let nonce_package = PackageContext::default()
.with_block_number(current_block)
.with_network_curve(NetworkCurve::Secp256k1) .with_network_curve(NetworkCurve::Secp256k1)
.with_authority_index(authority_index) .with_authority_index(authority_index)
.with_dkg_index(active_dkg_index)
.with_exodus_nonce_commitment( .with_exodus_nonce_commitment(
exodus_session, dummy_context,
dummy_nonce.clone(), dummy_nonce.to_vec(),
dummy_nonce.clone(), dummy_nonce.to_vec(),
) )
.expect("Nonce commitment package should be valid"); .expect("Nonce commitment package should be valid");
@ -527,21 +572,21 @@ mod benchmarks {
#[extrinsic_call] #[extrinsic_call]
_(RawOrigin::None, exodus_package, signature); _(RawOrigin::None, exodus_package, signature);
let roast_session = RoastSessions::<T>::get(&exodus_session, authority_index) let roast_session = RoastSessions::<T>::get(&dummy_context, authority_index)
.expect("ROAST session should exist after nonce commitment"); .expect("ROAST session should exist after nonce commitment");
let roast_state = RoastSessionStates::<T>::get(&exodus_session, roast_session); let roast_state = RoastSessionStates::<T>::get(&dummy_context, roast_session);
assert!(roast_state.nonce_committers.contains(authority_index)); assert!(roast_state.nonce_committers.contains(authority_index));
assert!(!roast_state.group_committers.contains(authority_index)); assert!(!roast_state.group_committers.contains(authority_index));
assert!(!roast_state.partial_signers.contains(authority_index)); assert!(!roast_state.partial_signers.contains(authority_index));
let registered_nonces = NonceCommitments::<T>::get( let registered_nonces = NonceCommitments::<T>::get(
(exodus_session, roast_session), (dummy_context, roast_session),
authority_index, authority_index,
).expect("Nonces should be registered during nonce commitment"); ).expect("Nonces should be registered during nonce commitment");
assert_eq!(registered_nonces.hiding, dummy_nonce.clone()); assert_eq!(registered_nonces.hiding.as_ref(), dummy_nonce);
assert_eq!(registered_nonces.binding, dummy_nonce); assert_eq!(registered_nonces.binding.as_ref(), dummy_nonce);
Ok(()) Ok(())
} }
@ -558,8 +603,13 @@ mod benchmarks {
} }
dkg_state.insert_index(authority_index); dkg_state.insert_index(authority_index);
let exodus_session = CurrentExodus::<T>::get(); let all_exodus_sessions = CurrentExodusSessions::<T>::get();
let exodus_session = all_exodus_sessions
.get(&network_id)
.copied()
.unwrap_or(0);
let dummy_context = ExodusContext::new(network_id, exodus_session);
let dummy_bytes = EvmBytes32::repeat_byte(69); let dummy_bytes = EvmBytes32::repeat_byte(69);
let dummy_address = EvmAddress::repeat_byte(69); let dummy_address = EvmAddress::repeat_byte(69);
let dummy_merkle_root = ExodusHash::repeat_byte(69); let dummy_merkle_root = ExodusHash::repeat_byte(69);
@ -572,33 +622,36 @@ mod benchmarks {
intervals: [34, 35], intervals: [34, 35],
booleans: [true, true], booleans: [true, true],
}; };
let request = ExodusRequest::<NetworkIdOf<T>, BalanceOf<T>>::evm_governance( let request = ExodusRequest::<T>::evm_governance(
exodus_session,
network_id,
dummy_address, dummy_address,
create_bond, create_bond,
); );
let authorities = QualificationAuthorities::<T>::get(&network_curve); let authorities = QualificationAuthorities::<T>::get(&network_curve);
let active_dkg: ActivatedState<T> = (&dkg_state).into(); let active_dkg: ReadyDkgAuthorities<T> = (&dkg_state).into();
let active_dkg_index = active_dkg.get_dkg_index();
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, dummy_context), 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;
roast_state.nonce_committers.insert(authority_index); roast_state.nonce_committers.insert(authority_index);
let roast_session = 3; let roast_session = 3;
RoastSessions::<T>::insert(&exodus_session, authority_index, roast_session); let current_block = <T as Config>::BlockNumberProvider::current_block_number();
RoastSessionStates::<T>::insert(exodus_session, roast_session, roast_state); RoastSessions::<T>::insert(&dummy_context, authority_index, roast_session);
RoastSessionStates::<T>::insert(dummy_context, roast_session, roast_state);
let group_package = PackageContext::default() let group_package = PackageContext::default()
.with_block_number(current_block)
.with_network_curve(NetworkCurve::Secp256k1) .with_network_curve(NetworkCurve::Secp256k1)
.with_authority_index(authority_index) .with_authority_index(authority_index)
.with_dkg_index(active_dkg_index)
.with_exodus_group_commitment( .with_exodus_group_commitment(
exodus_session, dummy_context,
dummy_group_commitment.clone(), dummy_group_commitment.clone(),
dummy_bytes, dummy_bytes,
) )
@ -618,16 +671,16 @@ mod benchmarks {
dummy_group_commitment, dummy_group_commitment,
).expect("Group commtiment should be valid"); ).expect("Group commtiment should be valid");
let roast_state = RoastSessionStates::<T>::get(&exodus_session, roast_session); let roast_state = RoastSessionStates::<T>::get(&dummy_context, roast_session);
assert!(roast_state.nonce_committers.contains(authority_index)); assert!(roast_state.nonce_committers.contains(authority_index));
assert!(roast_state.group_committers.contains(authority_index)); assert!(roast_state.group_committers.contains(authority_index));
let consensus = GroupCommitmentsConsensus::<T>::get(exodus_session, roast_session); let consensus = GroupCommitmentsConsensus::<T>::get(dummy_context, roast_session);
assert!(consensus.participants.contains(authority_index)); assert!(consensus.participants.contains(authority_index));
assert_eq!(&consensus.element_bytes, &bounded_group_commitment); assert_eq!(&consensus.element_bytes, &bounded_group_commitment);
assert_eq!(consensus.merkle_root, dummy_merkle_root); assert_eq!(consensus.merkle_root, dummy_merkle_root);
let group_commitment_key = (exodus_session, roast_session, dummy_merkle_root, bounded_group_commitment); let group_commitment_key = (dummy_context, roast_session, dummy_merkle_root, bounded_group_commitment);
let participants = GroupCommitters::<T>::get(group_commitment_key); let participants = GroupCommitters::<T>::get(group_commitment_key);
assert!(participants.contains(authority_index)); assert!(participants.contains(authority_index));
@ -650,7 +703,13 @@ mod benchmarks {
dkg_state.insert_index(authority_index); dkg_state.insert_index(authority_index);
let dkg_index = dkg_state.get_dkg_index(); let dkg_index = dkg_state.get_dkg_index();
let exodus_session = CurrentExodus::<T>::get(); let all_exodus_sessions = CurrentExodusSessions::<T>::get();
let exodus_session = all_exodus_sessions
.get(&network_id)
.copied()
.unwrap_or(0);
let exodus_context = ExodusContext::new(network_id, exodus_session);
let mut dummy_element = vec![0u8; network_curve.element_bytes_len()]; let mut dummy_element = vec![0u8; network_curve.element_bytes_len()];
dummy_element[0] = 0x02; dummy_element[0] = 0x02;
@ -670,9 +729,7 @@ mod benchmarks {
intervals: [34, 35], intervals: [34, 35],
booleans: [true, true], booleans: [true, true],
}; };
let request = ExodusRequest::<NetworkIdOf<T>, BalanceOf<T>>::evm_governance( let request = ExodusRequest::<T>::evm_governance(
exodus_session,
network_id,
dummy_address, dummy_address,
create_bond, create_bond,
); );
@ -697,10 +754,16 @@ mod benchmarks {
dummy_values.insert(identifier, dummy_binding_factor.clone()); dummy_values.insert(identifier, dummy_binding_factor.clone());
} }
let identifier_size = network_curve.scalar_bytes_len();
let merkle_tree = let merkle_tree =
NetworkCurve::build_merkle_tree( NetworkCurve::build_merkle_tree(
identifier_size + dummy_scalar.len(),
&dummy_values, &dummy_values,
|value: &Vec<u8>| Ok(value.clone()), |value, buffer| {
buffer.extend_from_slice(value.as_ref());
Ok(())
}
).expect("Merkle tree for binding factor should be valid"); ).expect("Merkle tree for binding factor should be valid");
let merkle_root = merkle_tree.last().copied().unwrap(); let merkle_root = merkle_tree.last().copied().unwrap();
@ -717,13 +780,13 @@ mod benchmarks {
}); });
let authorities = QualificationAuthorities::<T>::get(&network_curve); let authorities = QualificationAuthorities::<T>::get(&network_curve);
let active_dkg: ActivatedState<T> = (&dkg_state).into(); let active_dkg: ReadyDkgAuthorities<T> = (&dkg_state).into();
let active_dkg_index = active_dkg.get_dkg_index(); let active_dkg_index = active_dkg.get_dkg_index();
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_context), 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;
@ -736,8 +799,8 @@ mod benchmarks {
}); });
let roast_session = 3; let roast_session = 3;
RoastSessions::<T>::insert(&exodus_session, authority_index, roast_session); RoastSessions::<T>::insert(&exodus_context, authority_index, roast_session);
RoastSessionStates::<T>::insert(exodus_session, roast_session, roast_state); RoastSessionStates::<T>::insert(exodus_context, roast_session, roast_state);
let bounded_group_commitment = BoundedVec::try_from(dummy_group_commitment).unwrap(); let bounded_group_commitment = BoundedVec::try_from(dummy_group_commitment).unwrap();
let dummy_verifying_key = BoundedVec::try_from(dummy_element.clone()).unwrap(); let dummy_verifying_key = BoundedVec::try_from(dummy_element.clone()).unwrap();
@ -750,9 +813,9 @@ mod benchmarks {
ActiveVerifyingKey::<T>::insert(&network_curve, dummy_verifying_key); ActiveVerifyingKey::<T>::insert(&network_curve, dummy_verifying_key);
VerifyingShares::<T>::insert((&network_curve, active_dkg_index, authority_index), dummy_verifying_share); VerifyingShares::<T>::insert((&network_curve, active_dkg_index, authority_index), dummy_verifying_share);
NonceCommitments::<T>::insert((&exodus_session, &roast_session), authority_index, dummy_nonce); NonceCommitments::<T>::insert((&exodus_context, &roast_session), authority_index, dummy_nonce);
GroupCommitmentsConsensus::<T>::mutate(&exodus_session, &roast_session, |consensus| { GroupCommitmentsConsensus::<T>::mutate(&exodus_context, &roast_session, |consensus| {
consensus.element_bytes = bounded_group_commitment.clone(); consensus.element_bytes = bounded_group_commitment.clone();
consensus.merkle_root = dummy_merkle_root; consensus.merkle_root = dummy_merkle_root;
(0..threshold).for_each(|i| { (0..threshold).for_each(|i| {
@ -760,11 +823,14 @@ mod benchmarks {
}); });
}); });
let current_block = <T as Config>::BlockNumberProvider::current_block_number();
let share_package = PackageContext::default() let share_package = PackageContext::default()
.with_block_number(current_block)
.with_network_curve(NetworkCurve::Secp256k1) .with_network_curve(NetworkCurve::Secp256k1)
.with_authority_index(authority_index) .with_authority_index(authority_index)
.with_dkg_index(active_dkg_index)
.with_exodus_signature_share( .with_exodus_signature_share(
exodus_session, exodus_context,
dummy_signature_share.clone(), dummy_signature_share.clone(),
dummy_merkle_proof, dummy_merkle_proof,
dummy_binding_factor dummy_binding_factor
@ -780,13 +846,13 @@ mod benchmarks {
#[extrinsic_call] #[extrinsic_call]
_(RawOrigin::None, exodus_package, signature); _(RawOrigin::None, exodus_package, signature);
let maybe_roast_session = RoastSessions::<T>::get(&exodus_session, authority_index); let maybe_roast_session = RoastSessions::<T>::get(&exodus_context, authority_index);
assert!(maybe_roast_session.is_none()); assert!(maybe_roast_session.is_none());
let scalar_exists = SignatureScalars::<T>::contains_key(&exodus_session, &roast_session); let scalar_exists = SignatureScalars::<T>::contains_key(&exodus_context, &roast_session);
let consensus_exists = GroupCommitmentsConsensus::<T>::contains_key(&exodus_session, &roast_session); let consensus_exists = GroupCommitmentsConsensus::<T>::contains_key(&exodus_context, &roast_session);
let committers_exists = GroupCommitters::<T>::contains_key(( let committers_exists = GroupCommitters::<T>::contains_key((
&exodus_session, &exodus_context,
&roast_session, &roast_session,
&dummy_merkle_root, &dummy_merkle_root,
&bounded_group_commitment, &bounded_group_commitment,
@ -796,8 +862,8 @@ 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_context)));
assert!(ExodusSignedGovernance::<T>::contains_key((&exodus_session, network_id), dkg_index)); assert!(ExodusSignedGovernance::<T>::contains_key(dkg_index, &exodus_context));
Ok(()) Ok(())
} }
@ -829,7 +895,11 @@ mod benchmarks {
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 total_issuance = T::Currency::total_issuance();
let current_exodus_session = CurrentExodus::<T>::get(); let all_exodus_sessions = CurrentExodusSessions::<T>::get();
let current_exodus_session = all_exodus_sessions
.get(&network_id)
.copied()
.unwrap_or(0);
#[extrinsic_call] #[extrinsic_call]
_( _(
@ -840,8 +910,12 @@ mod benchmarks {
receiver, receiver,
); );
assert!(ExodusRequests::<T>::contains_key((&network_curve, current_exodus_session))); let all_exodus_sessions = CurrentExodusSessions::<T>::get();
assert_eq!(CurrentExodus::<T>::get(), current_exodus_session + 1); let last_exodus_session = all_exodus_sessions.get(&network_id).copied().unwrap();
let context = ExodusContext::new(network_id, current_exodus_session);
assert!(ExodusRequests::<T>::contains_key((&network_curve, &context)));
assert_eq!(last_exodus_session, current_exodus_session + 1);
assert_eq!(T::Currency::balance(&caller), initial_balance - amount); assert_eq!(T::Currency::balance(&caller), initial_balance - amount);
assert_eq!(T::Currency::total_issuance(), total_issuance - amount); assert_eq!(T::Currency::total_issuance(), total_issuance - amount);

View File

@ -43,6 +43,7 @@ pub enum ExodusError {
InvalidMerkleProof, InvalidMerkleProof,
InvalidIdentityElement, InvalidIdentityElement,
NoActiveAuthorities, NoActiveAuthorities,
TreeGenerationFailed,
Unknown, Unknown,
} }
@ -88,6 +89,7 @@ impl core::fmt::Debug for ExodusError {
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::TreeGenerationFailed => write!(fmt, "Tree generation failed because of inconsistent indexation."),
ExodusError::Unknown => write!(fmt, "Unknown error."), ExodusError::Unknown => write!(fmt, "Unknown error."),
} }
} }

View File

@ -3,6 +3,7 @@ use rand_chacha::rand_core::{CryptoRng, RngCore};
use ghost_traits::exodus::{ use ghost_traits::exodus::{
DistributedKeyGeneration, IdentifierConverter, MerkleTreeBuilder, DistributedKeyGeneration, IdentifierConverter, MerkleTreeBuilder,
CiphersuiteSizes,
}; };
use crate::{AuthIndex, ExodusError, NetworkCurve}; use crate::{AuthIndex, ExodusError, NetworkCurve};
@ -241,6 +242,8 @@ impl DistributedKeyGeneration<AuthIndex, ExodusError> for NetworkCurve {
round2_packages_bytes: &Self::RoundPackages, round2_packages_bytes: &Self::RoundPackages,
) -> Self::Part3Result { ) -> Self::Part3Result {
with_ciphersuite!(self, |f| { with_ciphersuite!(self, |f| {
type InnerGroup = <Ciphersuite as frost_core::Ciphersuite>::Group;
let round2_secret_package = f::keys::dkg::round2::SecretPackage::deserialize(round2_secret_package_bytes) let round2_secret_package = f::keys::dkg::round2::SecretPackage::deserialize(round2_secret_package_bytes)
.map_err(|_| ExodusError::DeserializationError)?; .map_err(|_| ExodusError::DeserializationError)?;
@ -291,13 +294,24 @@ impl DistributedKeyGeneration<AuthIndex, ExodusError> for NetworkCurve {
let key_package_bytes = key_package.serialize() let key_package_bytes = key_package.serialize()
.map_err(|_| ExodusError::SerializationError)?; .map_err(|_| ExodusError::SerializationError)?;
let identifier_size = self.scalar_bytes_len();
let verifying_share_size = self.element_bytes_len();
let verifying_shares_merkle_tree = let verifying_shares_merkle_tree =
Self::build_merkle_tree( Self::build_merkle_tree(
identifier_size + verifying_share_size,
pubkey_package.verifying_shares(), pubkey_package.verifying_shares(),
|verifying_share| verifying_share.serialize() |verifying_share, buffer| {
.map_err(|_| ExodusError::SerializationError) let serialized = <InnerGroup as frost_core::Group>::serialize(
&verifying_share.to_element()
).map_err(|_| ExodusError::SerializationError)?;
buffer.extend_from_slice(serialized.as_ref());
Ok(())
}
)?; )?;
// TODO: make it better
let (_, merkle_proof) = let (_, merkle_proof) =
Self::generate_merkle_proof_from_tree( Self::generate_merkle_proof_from_tree(
pubkey_package.verifying_shares(), pubkey_package.verifying_shares(),

View File

@ -1,5 +1,5 @@
use ghost_traits::exodus::{ use ghost_traits::exodus::{
IdentifierConverter, MerkleTreeBuilder, CiphersuiteSizes, IdentifierConverter, MerkleTreeBuilder,
FlexibleRoundOptimizedSchnorrThresholdSignature, FlexibleRoundOptimizedSchnorrThresholdSignature,
}; };
use sp_std::{ use sp_std::{
@ -15,29 +15,6 @@ impl FlexibleRoundOptimizedSchnorrThresholdSignature<AuthIndex, ExodusError> for
type Packages<'a> = BTreeMap<AuthIndex, &'a [u8]>; type Packages<'a> = BTreeMap<AuthIndex, &'a [u8]>;
type NoncePackages<'a> = BTreeMap<AuthIndex, (&'a [u8], &'a [u8])>; type NoncePackages<'a> = BTreeMap<AuthIndex, (&'a [u8], &'a [u8])>;
fn header_bytes_len(&self) -> usize { 5 }
fn signature_bytes_len(&self) -> usize {
match self {
NetworkCurve::Secp256k1 => 64,
NetworkCurve::Ed25519 => 64,
}
}
fn element_bytes_len(&self) -> usize {
match self {
NetworkCurve::Secp256k1 => 33,
NetworkCurve::Ed25519 => 32,
}
}
fn scalar_bytes_len(&self) -> usize {
match self {
NetworkCurve::Secp256k1 => 32,
NetworkCurve::Ed25519 => 32,
}
}
fn verify_signature_share( fn verify_signature_share(
&self, &self,
index: AuthIndex, index: AuthIndex,
@ -186,6 +163,19 @@ impl FlexibleRoundOptimizedSchnorrThresholdSignature<AuthIndex, ExodusError> for
}) })
} }
fn is_nonce_valid_point(&self, nonce_bytes: &[u8]) -> Result<(), ExodusError> {
with_ciphersuite!(self, |f| {
type InnerGroup = <Ciphersuite as frost_core::Ciphersuite>::Group;
let serialized_nonce = <InnerGroup as frost_core::Group>::Serialization::try_from(nonce_bytes)
.map_err(|_| ExodusError::DeserializationError)?;
<InnerGroup as frost_core::Group>::deserialize(&serialized_nonce)
.map_err(|_| ExodusError::DeserializationError)
.map(|_| ())
})
}
fn is_signature_valid( fn is_signature_valid(
&self, &self,
verifying_key_bytes: &[u8], verifying_key_bytes: &[u8],
@ -330,11 +320,16 @@ impl FlexibleRoundOptimizedSchnorrThresholdSignature<AuthIndex, ExodusError> for
return Err(ExodusError::InvalidParticipantId); return Err(ExodusError::InvalidParticipantId);
} }
let identifier_size = self.scalar_bytes_len();
let binding_factor_size = self.scalar_bytes_len();
let binding_factor_tree = Self::build_merkle_tree( let binding_factor_tree = Self::build_merkle_tree(
identifier_size + binding_factor_size,
&binding_factors_list, &binding_factors_list,
|binding_factor| { |binding_factor, buffer| {
let serialized = <InnerField as frost_core::Field>::serialize(binding_factor); let serialized = <InnerField as frost_core::Field>::serialize(binding_factor);
Ok(serialized.as_ref().to_vec()) buffer.extend_from_slice(serialized.as_ref());
Ok(())
} }
)?; )?;

View File

@ -13,15 +13,16 @@ impl MerkleTreeBuilder<AuthIndex, ExodusError> for NetworkCurve {
type Hash = <SubstrateBlake2Hasher as GhostHasher>::Hash; type Hash = <SubstrateBlake2Hasher as GhostHasher>::Hash;
fn build_merkle_tree<C, V, F>( fn build_merkle_tree<C, V, F>(
size_hint: usize,
values: &BTreeMap<Identifier<C>, V>, values: &BTreeMap<Identifier<C>, V>,
serialize_fn: F, update_preimage_buffer: F,
) -> Result<Vec<Self::Hash>, ExodusError> ) -> Result<Vec<Self::Hash>, ExodusError>
where where
C: frost_core::Ciphersuite, C: frost_core::Ciphersuite,
<<C as Ciphersuite>::Group as Group>::Field: frost_core::Field, <<C as Ciphersuite>::Group as Group>::Field: frost_core::Field,
F: Fn(&V) -> Result<Vec<u8>, ExodusError> F: Fn(&V, &mut Vec<u8>) -> Result<(), ExodusError>
{ {
use ghost_helpers::merkle_tree::generate_tree; use ghost_helpers::merkle_tree::{generate_tree, MerkleTreeError};
type CField<C> = <<C as Ciphersuite>::Group as Group>::Field; type CField<C> = <<C as Ciphersuite>::Group as Group>::Field;
@ -34,25 +35,28 @@ impl MerkleTreeBuilder<AuthIndex, ExodusError> for NetworkCurve {
}) })
.ok_or(ExodusError::IncorrectNumberOfIdentifiers)?; .ok_or(ExodusError::IncorrectNumberOfIdentifiers)?;
generate_tree::<SubstrateBlake2Hasher, _, _, _, _>( let maybe_tree = generate_tree::<SubstrateBlake2Hasher, _, _, _, _>(
max_index, max_index,
size_hint,
values.iter(), values.iter(),
|(identifier, value)| { |(identifier, value), buffer| {
let id_scalar = identifier.to_scalar(); let id_scalar = identifier.to_scalar();
let id_bytes = <CField<C> as frost_core::Field>::serialize(&id_scalar); let id_bytes = <CField<C> as frost_core::Field>::serialize(&id_scalar);
let index = Self::convert_identifier_to_index(id_bytes.as_ref())?; let index = Self::convert_identifier_to_index(id_bytes.as_ref())?;
let value_bytes = serialize_fn(value).map_err(|_| ExodusError::SerializationError)?;
// NOTE: revisit buffer.extend_from_slice(id_bytes.as_ref());
// Is it possible to use [u8; CONSTANT] where constant is update_preimage_buffer(value, buffer)?;
// purely dependant on provided generic.
let mut preimage = Vec::<u8>::with_capacity(id_bytes.as_ref().len() + value_bytes.len());
preimage.extend_from_slice(id_bytes.as_ref());
preimage.extend_from_slice(value_bytes.as_ref());
Ok((index as usize, preimage)) Ok(index as usize)
}) });
match maybe_tree {
Ok(tree) => Ok(tree),
Err(MerkleTreeError::Generate(exodus_err)) => Err(exodus_err),
Err(MerkleTreeError::InvalidIndex) => Err(ExodusError::TreeGenerationFailed),
Err(MerkleTreeError::Empty) => Err(ExodusError::IncorrectNumberOfIdentifiers),
}
} }
fn generate_merkle_proof_from_tree<C, V, F>( fn generate_merkle_proof_from_tree<C, V, F>(
@ -66,7 +70,7 @@ impl MerkleTreeBuilder<AuthIndex, ExodusError> for NetworkCurve {
<<C as Ciphersuite>::Group as Group>::Field: frost_core::Field, <<C as Ciphersuite>::Group as Group>::Field: frost_core::Field,
F: Fn(&V) -> Result<Vec<u8>, ExodusError> F: Fn(&V) -> Result<Vec<u8>, ExodusError>
{ {
use ghost_helpers::merkle_tree::generate_proof; use ghost_helpers::merkle_tree::{generate_proof, MerkleTreeError};
type CField<C> = <<C as Ciphersuite>::Group as Group>::Field; type CField<C> = <<C as Ciphersuite>::Group as Group>::Field;
@ -75,7 +79,7 @@ impl MerkleTreeBuilder<AuthIndex, ExodusError> for NetworkCurve {
.map_err(|_| ExodusError::InvalidParticipantId)?; .map_err(|_| ExodusError::InvalidParticipantId)?;
let value = values.get(&identifier).ok_or(ExodusError::InvalidParticipantId)?; let value = values.get(&identifier).ok_or(ExodusError::InvalidParticipantId)?;
let value_bytes = serialize_fn(&value).map_err(|_| ExodusError::SerializationError)?; let value_bytes = serialize_fn(&value)?;
let max_index = values.keys() let max_index = values.keys()
.next_back() .next_back()
@ -86,11 +90,15 @@ impl MerkleTreeBuilder<AuthIndex, ExodusError> for NetworkCurve {
}) })
.ok_or(ExodusError::IncorrectNumberOfIdentifiers)?; .ok_or(ExodusError::IncorrectNumberOfIdentifiers)?;
let proof = generate_proof::<SubstrateBlake2Hasher, _>( let proof = generate_proof::<SubstrateBlake2Hasher, _, _>(
merkle_tree, merkle_tree,
max_index, max_index,
authority_index, authority_index,
); ).map_err(|err| match err {
MerkleTreeError::Generate(exodus_err) => exodus_err,
MerkleTreeError::InvalidIndex => ExodusError::TreeGenerationFailed,
MerkleTreeError::Empty => ExodusError::IncorrectNumberOfIdentifiers,
})?;
Ok((value_bytes, proof)) Ok((value_bytes, proof))
} }

View File

@ -72,3 +72,6 @@ mod ecdh;
#[macro_use] #[macro_use]
mod frost; mod frost;
#[macro_use]
mod sizes;

View File

@ -0,0 +1,29 @@
use ghost_helpers::networks::NetworkCurve;
use ghost_traits::exodus::CiphersuiteSizes;
use crate::{AuthIndex, ExodusError};
impl CiphersuiteSizes<AuthIndex, ExodusError> for NetworkCurve {
fn header_bytes_len(&self) -> usize { 5 }
fn signature_bytes_len(&self) -> usize {
match self {
NetworkCurve::Secp256k1 => 64,
NetworkCurve::Ed25519 => 64,
}
}
fn element_bytes_len(&self) -> usize {
match self {
NetworkCurve::Secp256k1 => 33,
NetworkCurve::Ed25519 => 32,
}
}
fn scalar_bytes_len(&self) -> usize {
match self {
NetworkCurve::Secp256k1 => 32,
NetworkCurve::Ed25519 => 32,
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -41,7 +41,7 @@ impl frame_system::Config for Runtime {
} }
parameter_types! { parameter_types! {
pub static FixedAuthorities: Vec<u64> = vec![0, 1, 2, 3]; pub static FixedAuthorities: Vec<u64> = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
} }
pub struct TestSessionManager; pub struct TestSessionManager;
@ -136,14 +136,15 @@ impl ghost_exodus::Config for Runtime {
type DisabledValidators = Session; type DisabledValidators = Session;
type UnsignedPriority = ConstU64<{ 1 << 20 }>; type UnsignedPriority = ConstU64<{ 1 << 20 }>;
type UnsignedLongevity = ConstU64<1>; type UnsignedLongevity = ConstU64<5>;
type MaxAuthorities = ConstU32<10>; type MaxAuthorities = ConstU32<15>;
type MaxAuthoritiesChunks = ConstU32<1>; type MaxAuthoritiesChunks = ConstU32<2>;
type DkgRoundPeriod = ConstU64<100>; type DkgRoundPeriod = ConstU64<1000>;
type RemovalLimit = ConstU32<20>; type RemovalLimit = ConstU32<20>;
type MaxCursorLen = ConstU32<20>; type MaxCursorLen = ConstU32<256>;
type BaseEvmAmount = ConstU128<5>; type BaseEvmAmount = ConstU128<5>;
type MaxExodusRequestCount = ConstU32<40>;
type WeightInfo = (); type WeightInfo = ();
} }

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,30 @@ use frame_support::{BoundedVec, traits::ConstU32};
use crate::{ExodusHash, ELEMENT_MAX_BYTES}; use crate::{ExodusHash, ELEMENT_MAX_BYTES};
#[derive(Default, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
pub struct QualifiedConsensusState<Bitmap> {
pub state: ConsensusState<Bitmap>,
pub members: Bitmap,
}
impl<Bitmap> QualifiedConsensusState<Bitmap> {
pub fn new(
element_bytes: BoundedVec<u8, ConstU32<ELEMENT_MAX_BYTES>>,
participants: Bitmap,
merkle_root: ExodusHash,
members: Bitmap,
) -> Self {
Self {
members,
state: ConsensusState::new(
element_bytes,
participants,
merkle_root,
)
}
}
}
#[derive(Default, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] #[derive(Default, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
pub struct ConsensusState<Bitmap> { pub struct ConsensusState<Bitmap> {
pub element_bytes: BoundedVec<u8, ConstU32<ELEMENT_MAX_BYTES>>, pub element_bytes: BoundedVec<u8, ConstU32<ELEMENT_MAX_BYTES>>,

View File

@ -1,28 +1,46 @@
use sp_runtime::{RuntimeDebug, traits::UniqueSaturatedInto}; use sp_runtime::{RuntimeDebug, traits::UniqueSaturatedInto};
use codec::{Encode, Decode}; use codec::{Encode, Decode};
use scale_info::TypeInfo; use scale_info::TypeInfo;
use strum::EnumCount;
use frame_support::{BoundedVec, traits::ConstU32}; use frame_support::{BoundedVec, traits::ConstU32};
use ghost_helpers::networks::NetworkType; use ghost_traits::{
use ghost_traits::bounded_bitmap::{ networks::NetworkDataBasicHandler,
bounded_bitmap::{
BoundedBitmapGenerator, BoundedBitmapReader, BoundedBitmapWriter, BoundedBitmapGenerator, BoundedBitmapReader, BoundedBitmapWriter,
},
}; };
use crate::{ELEMENT_MAX_BYTES, ExodusSession, DkgIndex}; use crate::{
ELEMENT_MAX_BYTES, Config, NetworkType, NetworkIdOf, ExodusContext,
ParticipantsBitmap, BlockNumberFor, DkgIndex,
};
type RotationSessions = BoundedVec<RotationSession, ConstU32<{ NetworkType::COUNT as u32 }>>; type RotationItem<T> = (NetworkType, ExodusContext<NetworkIdOf<T>>);
type Rotations<T> = BoundedVec<
RotationItem<T>,
<<T as Config>::NetworkDataHandler as NetworkDataBasicHandler>::MaxNetworks,
>;
#[derive(Default, Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)] #[derive(Encode, Decode, Clone, PartialEq, Eq, TypeInfo)]
pub struct ReadyDkgAuthorities<AuthoritiesBitmap> { #[scale_info(skip_type_params(T))]
authorities_indexes: AuthoritiesBitmap, pub struct ReadyDkgAuthorities<T: Config> {
authorities_indexes: ParticipantsBitmap<T>,
dkg_index: DkgIndex, dkg_index: DkgIndex,
} }
impl<AuthoritiesBitmap> ReadyDkgAuthorities<AuthoritiesBitmap> impl<T: Config> Default for ReadyDkgAuthorities<T> {
fn default() -> Self {
Self {
authorities_indexes: Default::default(),
dkg_index: Default::default(),
}
}
}
impl<T: Config> ReadyDkgAuthorities<T>
where where
AuthoritiesBitmap: BoundedBitmapReader, ParticipantsBitmap<T>: BoundedBitmapReader,
{ {
pub fn contains_index<N>(&self, index: N) -> bool pub fn contains_index<N>(&self, index: N) -> bool
where N: UniqueSaturatedInto<usize>, where N: UniqueSaturatedInto<usize>,
@ -30,7 +48,7 @@ where
self.authorities_indexes.contains(index) self.authorities_indexes.contains(index)
} }
pub fn get_indexes(&self) -> &AuthoritiesBitmap { pub fn get_indexes(&self) -> &ParticipantsBitmap<T> {
&self.authorities_indexes &self.authorities_indexes
} }
@ -39,15 +57,15 @@ where
} }
} }
impl<AuthoritiesBitmap, Block> impl<T: Config>
From<&PendingDkgAuthorities<AuthoritiesBitmap, Block>> From<&PendingDkgAuthorities<T>>
for ReadyDkgAuthorities<AuthoritiesBitmap> for ReadyDkgAuthorities<T>
where where
Block: Copy, BlockNumberFor<T>: Copy,
AuthoritiesBitmap: BoundedBitmapWriter + BoundedBitmapReader + BoundedBitmapGenerator + Clone, ParticipantsBitmap<T>: BoundedBitmapWriter + BoundedBitmapReader + BoundedBitmapGenerator + Clone,
for<'a> AuthoritiesBitmap: core::ops::BitAndAssign<&'a AuthoritiesBitmap>, for<'a> ParticipantsBitmap<T>: core::ops::BitAndAssign<&'a ParticipantsBitmap<T>>,
{ {
fn from(pending: &PendingDkgAuthorities<AuthoritiesBitmap, Block>) -> Self { fn from(pending: &PendingDkgAuthorities<T>) -> Self {
Self { Self {
authorities_indexes: pending.get_indexes().clone(), authorities_indexes: pending.get_indexes().clone(),
dkg_index: pending.get_dkg_index(), dkg_index: pending.get_dkg_index(),
@ -55,34 +73,40 @@ where
} }
} }
#[derive(Default, Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)] #[derive(Encode, Decode, Clone, PartialEq, Eq, TypeInfo)]
pub struct RotationSession { #[scale_info(skip_type_params(T))]
pub exodus_session: ExodusSession, pub struct PendingDkgAuthorities<T: Config> {
pub network_type: NetworkType
}
#[derive(Default, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
pub struct PendingDkgAuthorities<AuthoritiesBitmap, Block> {
verifying_key: BoundedVec<u8, ConstU32<ELEMENT_MAX_BYTES>>, verifying_key: BoundedVec<u8, ConstU32<ELEMENT_MAX_BYTES>>,
authorities_indexes: AuthoritiesBitmap, authorities_indexes: ParticipantsBitmap<T>,
rotation_sessions: RotationSessions, rotations: Rotations<T>,
dkg_state: DkgState<Block>, dkg_state: DkgState<BlockNumberFor<T>>,
} }
impl<AuthoritiesBitmap, Block> PendingDkgAuthorities<AuthoritiesBitmap, Block> impl<T: Config> Default for PendingDkgAuthorities<T> {
fn default() -> Self {
Self {
verifying_key: Default::default(),
authorities_indexes: Default::default(),
rotations: Default::default(),
dkg_state: Default::default(),
}
}
}
impl<T: Config> PendingDkgAuthorities<T>
where where
Block: Copy, BlockNumberFor<T>: Copy,
AuthoritiesBitmap: BoundedBitmapWriter + BoundedBitmapReader + BoundedBitmapGenerator + Clone, ParticipantsBitmap<T>: BoundedBitmapWriter + BoundedBitmapReader + BoundedBitmapGenerator + Clone,
for<'a> AuthoritiesBitmap: core::ops::BitAndAssign<&'a AuthoritiesBitmap>, for<'a> ParticipantsBitmap<T>: core::ops::BitAndAssign<&'a ParticipantsBitmap<T>>,
{ {
pub fn new_dkg(&mut self, authorities_len: usize) { pub fn new_dkg(&mut self, authorities_len: usize) {
self.authorities_indexes = AuthoritiesBitmap::empty(authorities_len); self.authorities_indexes = ParticipantsBitmap::<T>::empty(authorities_len);
self.dkg_state.restart(); self.dkg_state.restart();
self.rotation_sessions = Default::default(); self.rotations = Default::default();
} }
pub fn set_block(&mut self, block: Block) { pub fn set_release_block(&mut self, block: BlockNumberFor<T>) {
self.dkg_state.set_block(block); self.dkg_state.set_release_block(block);
} }
pub fn set_verifying_key( pub fn set_verifying_key(
@ -101,7 +125,7 @@ where
self.verifying_key = Default::default(); self.verifying_key = Default::default();
self.authorities_indexes.nullify(); self.authorities_indexes.nullify();
self.dkg_state.restart(); self.dkg_state.restart();
self.rotation_sessions = Default::default(); self.rotations = Default::default();
} }
pub fn next_phase(&mut self) { pub fn next_phase(&mut self) {
@ -109,7 +133,7 @@ where
} }
#[inline(always)] #[inline(always)]
pub fn intersect_indexes(&mut self, mask: &AuthoritiesBitmap) { pub fn intersect_indexes(&mut self, mask: &ParticipantsBitmap<T>) {
self.authorities_indexes &= mask; self.authorities_indexes &= mask;
} }
@ -125,8 +149,8 @@ where
self.dkg_state.get_dkg_index() self.dkg_state.get_dkg_index()
} }
pub fn get_block(&self) -> Block { pub fn get_release_block(&self) -> BlockNumberFor<T> {
self.dkg_state.get_block() self.dkg_state.get_release_block()
} }
pub fn get_phase(&self) -> DkgPhase { pub fn get_phase(&self) -> DkgPhase {
@ -183,17 +207,13 @@ where
self.authorities_indexes.insert(i) self.authorities_indexes.insert(i)
} }
pub fn try_push_rotation_session( pub fn try_push_rotation(
&mut self, &mut self,
exodus_session: ExodusSession,
network_type: NetworkType, network_type: NetworkType,
exodus_context: ExodusContext<NetworkIdOf<T>>,
) -> Option<()> { ) -> Option<()> {
let rotation_session = RotationSession { let item = (network_type, exodus_context);
exodus_session, self.rotations.try_push(item).ok()
network_type,
};
self.rotation_sessions.try_push(rotation_session).ok()
} }
pub fn count_ones<N>(&self) -> N pub fn count_ones<N>(&self) -> N
@ -208,43 +228,44 @@ where
self.authorities_indexes.highest_bit() self.authorities_indexes.highest_bit()
} }
pub fn get_indexes(&self) -> &AuthoritiesBitmap { pub fn get_indexes(&self) -> &ParticipantsBitmap<T> {
&self.authorities_indexes &self.authorities_indexes
} }
pub fn iter_rotation_sessions(&self) -> impl Iterator<Item = &RotationSession> { pub fn iter_rotations(
self.rotation_sessions.iter() &self
) -> impl Iterator<Item = &RotationItem<T>> {
self.rotations.iter()
} }
} }
#[derive(Default, Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)] #[derive(Default, Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)]
pub struct DkgState<Block> { pub struct DkgState<Block> {
block: Block, release_block: Block,
index: DkgIndex, dkg_index: DkgIndex,
phase: DkgPhase, dkg_phase: DkgPhase,
} }
impl<Block: Copy> DkgState<Block> { impl<Block: Copy> DkgState<Block> {
fn is_dkg_pending(&self) -> bool { self.phase.is_dkg_pending() } fn is_dkg_pending(&self) -> bool { self.dkg_phase.is_dkg_pending() }
fn is_dkg_vacant(&self) -> bool { self.phase.is_dkg_vacant() } fn is_dkg_vacant(&self) -> bool { self.dkg_phase.is_dkg_vacant() }
fn get_dkg_index(&self) -> DkgIndex { self.index } fn get_dkg_index(&self) -> DkgIndex { self.dkg_index }
fn get_phase(&self) -> DkgPhase { self.phase } fn get_phase(&self) -> DkgPhase { self.dkg_phase }
fn get_block(&self) -> Block { self.block } fn get_release_block(&self) -> Block { self.release_block }
fn restart(&mut self) { fn restart(&mut self) {
self.phase = Default::default(); self.dkg_phase = Default::default();
self.index = self.index.checked_add(1) self.dkg_index = self.dkg_index.checked_add(1)
.unwrap_or_default(); .unwrap_or_default();
} }
fn next_phase(&mut self) { fn next_phase(&mut self) {
self.phase.next_phase() self.dkg_phase.next_phase()
} }
fn set_block(&mut self, block: Block) { fn set_release_block(&mut self, release_block: Block) {
self.block = block; self.release_block = release_block;
} }
} }

View File

@ -6,8 +6,8 @@ use sp_runtime::{
}; };
use crate::{ use crate::{
EvmAddress, EvmBytes32, ExodusSession, RoastSession, EvmAddress, EvmBytes32, ExodusContext, RoastSession, EvmSessionsBundle,
MAX_MESSAGE_SIZE, Config, BalanceOf, MAX_MESSAGE_SIZE,
}; };
pub trait EvmAbiEncode { pub trait EvmAbiEncode {
@ -172,8 +172,9 @@ pub enum GovernanceStatus {
RewardManager = 4, RewardManager = 4,
} }
#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, RuntimeDebug)] #[derive(Encode, Decode, Clone, Copy, PartialEq, TypeInfo, RuntimeDebug)]
pub enum GovernanceAction { pub enum GovernanceAction {
SetRouterRegistryIndex { index: u128 },
SetDistributor { distributor: EvmAddress }, SetDistributor { distributor: EvmAddress },
SetWarmupPeriod { warmup_period: u128 }, SetWarmupPeriod { warmup_period: u128 },
UpdateGatekeeperAddress { new_gatekeeper: EvmAddress }, UpdateGatekeeperAddress { new_gatekeeper: EvmAddress },
@ -188,6 +189,7 @@ pub enum GovernanceAction {
ForfeitReserves { router: EvmAddress, liquidity: u128, destroyer_mode: bool }, ForfeitReserves { router: EvmAddress, liquidity: u128, destroyer_mode: bool },
RedeemReserves { router: EvmAddress, amount: u128 }, RedeemReserves { router: EvmAddress, amount: u128 },
Withdraw { token: EvmAddress, amount: u128 }, Withdraw { token: EvmAddress, amount: u128 },
SetBondDepository { address: EvmAddress },
AuditReserves, AuditReserves,
Close { id: u128 }, Close { id: u128 },
@ -198,6 +200,8 @@ pub enum GovernanceAction {
intervals: [u32; 2], intervals: [u32; 2],
booleans: [bool; 2], booleans: [bool; 2],
}, },
Approve { spender: EvmAddress, value: u128, },
} }
impl GovernanceAction { impl GovernanceAction {
@ -206,6 +210,13 @@ impl GovernanceAction {
message_buffer: &'a mut [u8; MAX_MESSAGE_SIZE as usize], message_buffer: &'a mut [u8; MAX_MESSAGE_SIZE as usize],
) -> Option<&'a [u8]> { ) -> Option<&'a [u8]> {
match &self { match &self {
Self::SetRouterRegistryIndex { index } => {
abi_encode_evm_message!(
message_buffer,
"setRouterRegistryIndex(uint256)",
[index]
)
},
Self::SetDistributor { distributor } => { Self::SetDistributor { distributor } => {
abi_encode_evm_message!( abi_encode_evm_message!(
message_buffer, message_buffer,
@ -290,6 +301,13 @@ impl GovernanceAction {
[token, amount] [token, amount]
) )
}, },
Self::SetBondDepository { address } => {
abi_encode_evm_message!(
message_buffer,
"setBondDepository(address)",
[address]
)
},
Self::AuditReserves => { Self::AuditReserves => {
abi_encode_evm_message!(message_buffer, "auditReserves()", []) abi_encode_evm_message!(message_buffer, "auditReserves()", [])
}, },
@ -304,64 +322,75 @@ impl GovernanceAction {
[market, terms, token, intervals, &booleans_u8] [market, terms, token, intervals, &booleans_u8]
) )
}, },
Self::Approve { spender, value } => {
abi_encode_evm_message!(
message_buffer,
"approve(address,uint256)",
[spender, value]
)
},
} }
} }
} }
#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, RuntimeDebug)] #[derive(Encode, Decode, Clone, PartialEq, TypeInfo, RuntimeDebug)]
pub struct ExodusRequest<NetworkId, Balance> { #[scale_info(skip_type_params(T))]
pub r#type: ExodusRequestType<NetworkId, Balance>, pub struct ExodusRequest<T: Config> {
pub r#type: ExodusRequestType<T>,
pub next_roast_session: RoastSession, pub next_roast_session: RoastSession,
} }
#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, RuntimeDebug)] #[derive(Encode, Decode, Clone, PartialEq, TypeInfo, RuntimeDebug)]
pub enum ExodusRequestType<NetworkId, Balance> { #[scale_info(skip_type_params(T))]
pub enum ExodusRequestType<T: Config> {
EvmRotation { EvmRotation {
exodus_session: ExodusSession,
public_key: EvmBytes32, public_key: EvmBytes32,
parity: u8, xor_key: u128,
bitmask: u128,
sessions: EvmSessionsBundle<T>,
}, },
UtxoRotation, UtxoRotation,
EvmBridgeOut { EvmBridgeOut { amount: BalanceOf<T>, bounty: Perbill, receiver: EvmAddress },
exodus_session: ExodusSession, EvmGovernance { target: EvmAddress, action: GovernanceAction },
network_id: NetworkId, UtxoBridgeOut,
amount: Balance,
bounty: Perbill,
receiver: EvmAddress,
},
EvmGovernance {
exodus_session: ExodusSession,
network_id: NetworkId,
target: EvmAddress,
action: GovernanceAction,
},
UtxoBridgeOut { network_id: NetworkId },
} }
impl<NetworkId, Balance> ExodusRequestType<NetworkId, Balance> impl<T: Config> ExodusRequestType<T>
where where
NetworkId: UniqueSaturatedInto<u64> + Copy, BalanceOf<T>: UniqueSaturatedInto<u128> + Copy,
Balance: UniqueSaturatedInto<u128> + Copy,
{ {
pub fn get_message<'a>( pub fn get_message<'a, NetworkId>(
&self, &self,
message_buffer: &'a mut [u8; MAX_MESSAGE_SIZE as usize], message_buffer: &'a mut [u8; MAX_MESSAGE_SIZE as usize],
) -> Option<&'a [u8]> { exodus_context: ExodusContext<NetworkId>,
) -> Option<&'a [u8]>
where
NetworkId: UniqueSaturatedInto<u64> + Copy + Default,
{
match &self { match &self {
ExodusRequestType::EvmRotation { exodus_session, public_key, parity } => { ExodusRequestType::EvmRotation { public_key, xor_key, bitmask, sessions } => {
let mut buffer = [0u8; 32];
buffer[..16].copy_from_slice(&xor_key.to_be_bytes());
buffer[16..].copy_from_slice(&bitmask.to_be_bytes());
let packed_bits = EvmBytes32::from(&buffer);
let payload = sessions.as_slice();
abi_encode_evm_message!( abi_encode_evm_message!(
message_buffer, message_buffer,
"rotate(uint256,bytes32,uint8)", "rotate(uint256,bytes32,uint256,bytes)",
[exodus_session, public_key, parity] [&exodus_context.session, public_key, &packed_bits, payload]
) )
}, },
ExodusRequestType::EvmGovernance { exodus_session, network_id, target, action } => { ExodusRequestType::EvmGovernance { target, action } => {
let mut action_buffer = [0u8; MAX_MESSAGE_SIZE as usize]; let mut action_buffer = [0u8; MAX_MESSAGE_SIZE as usize];
let action_payload = action.get_message(&mut action_buffer)?; let action_payload = action.get_message(&mut action_buffer)?;
let mut metadata_buffer = [0u8; 32]; let mut metadata_buffer = [0u8; 32];
let chain_id_u64: u64 = exodus_context.network_id
.unique_saturated_into();
let chain_id_u64: u64 = (*network_id).unique_saturated_into();
metadata_buffer[0..8].copy_from_slice(&chain_id_u64.to_be_bytes()); metadata_buffer[0..8].copy_from_slice(&chain_id_u64.to_be_bytes());
metadata_buffer[8..28].copy_from_slice(target.as_ref()); metadata_buffer[8..28].copy_from_slice(target.as_ref());
@ -370,14 +399,16 @@ where
abi_encode_evm_message!( abi_encode_evm_message!(
message_buffer, message_buffer,
"govern(uint256,uint256,bytes)", "govern(uint256,uint256,bytes)",
[exodus_session, &packed_metadata, action_payload] [&exodus_context.session, &packed_metadata, action_payload]
) )
}, },
ExodusRequestType::EvmBridgeOut { exodus_session, network_id, amount, bounty, receiver } => { ExodusRequestType::EvmBridgeOut { amount, bounty, receiver } => {
let mut buffer = [0u8; 32]; let mut buffer = [0u8; 32];
buffer[0..20].copy_from_slice(receiver.as_ref()); buffer[0..20].copy_from_slice(receiver.as_ref());
let chain_id_u64: u64 = (*network_id).unique_saturated_into(); let chain_id_u64: u64 = exodus_context.network_id
.unique_saturated_into();
buffer[20..28].copy_from_slice(&chain_id_u64.to_be_bytes()); buffer[20..28].copy_from_slice(&chain_id_u64.to_be_bytes());
let scaled_bounty = multiply_by_rational_with_rounding( let scaled_bounty = multiply_by_rational_with_rounding(
@ -387,7 +418,7 @@ where
sp_runtime::Rounding::Down sp_runtime::Rounding::Down
)?; )?;
let bounty_uint32 = scaled_bounty as u32; let bounty_uint32 = scaled_bounty.min(u32::MAX as u128) as u32;
buffer[28..32].copy_from_slice(&bounty_uint32.to_be_bytes()); buffer[28..32].copy_from_slice(&bounty_uint32.to_be_bytes());
let mut amount_buffer = [0u8; 32]; let mut amount_buffer = [0u8; 32];
@ -400,49 +431,78 @@ where
abi_encode_evm_message!( abi_encode_evm_message!(
message_buffer, message_buffer,
"recall(uint256,uint256,uint256)", "recall(uint256,uint256,uint256)",
[exodus_session, &amount_u256, &packed_metadata] [&exodus_context.session, &amount_u256, &packed_metadata]
) )
} }
_ => None _ => None
} }
} }
pub fn is_evm_rotation(&self) -> bool {
match &self {
ExodusRequestType::EvmRotation { .. } => true,
_ => false,
}
}
pub fn is_utxo_rotation(&self) -> bool {
match &self {
ExodusRequestType::UtxoRotation { .. } => true,
_ => false,
}
}
pub fn is_bridge_out(&self) -> bool {
match &self {
ExodusRequestType::EvmBridgeOut { .. } | ExodusRequestType::UtxoBridgeOut { .. } => true,
_ => false,
}
}
pub fn is_evm_governance(&self) -> bool {
match &self {
ExodusRequestType::EvmGovernance { .. } => true,
_ => false,
}
}
} }
impl<NetworkId, Balance> ExodusRequest<NetworkId, Balance> impl<T: Config> ExodusRequest<T>
where where
NetworkId: UniqueSaturatedInto<u64> + Copy, BalanceOf<T>: UniqueSaturatedInto<u128> + Copy,
Balance: UniqueSaturatedInto<u128> + Copy,
{ {
pub fn get_message<'a>( pub fn get_message<'a, NetworkId>(
&self, &self,
message_buffer: &'a mut [u8; MAX_MESSAGE_SIZE as usize], message_buffer: &'a mut [u8; MAX_MESSAGE_SIZE as usize],
) -> Option<&'a [u8]> { exodus_context: ExodusContext<NetworkId>,
self.r#type.get_message(message_buffer) ) -> Option<&'a [u8]>
where
NetworkId: UniqueSaturatedInto<u64> + Copy + Default,
{
self.r#type.get_message(message_buffer, exodus_context)
} }
pub fn evm_rotation( pub fn evm_rotation(
exodus_session: ExodusSession,
public_key: EvmBytes32, public_key: EvmBytes32,
parity: u8, xor_key: u128,
bitmask: u128,
sessions: EvmSessionsBundle<T>,
) -> Self { ) -> Self {
let rotation = ExodusRequestType::EvmRotation { let rotation = ExodusRequestType::EvmRotation {
exodus_session,
public_key, public_key,
parity, xor_key,
bitmask,
sessions,
}; };
Self::default_request_for(rotation) Self::default_request_for(rotation)
} }
pub fn evm_bridge_out( pub fn evm_bridge_out(
exodus_session: ExodusSession, amount: BalanceOf<T>,
network_id: NetworkId,
amount: Balance,
bounty: Perbill, bounty: Perbill,
receiver: EvmAddress, receiver: EvmAddress
) -> Self { ) -> Self {
let evm_bridge_out = ExodusRequestType::EvmBridgeOut { let evm_bridge_out = ExodusRequestType::EvmBridgeOut {
exodus_session,
network_id,
amount, amount,
bounty, bounty,
receiver, receiver,
@ -450,81 +510,93 @@ where
Self::default_request_for(evm_bridge_out) Self::default_request_for(evm_bridge_out)
} }
pub fn evm_governance( pub fn evm_governance(target: EvmAddress, action: GovernanceAction) -> Self {
exodus_session: ExodusSession,
network_id: NetworkId,
target: EvmAddress,
action: GovernanceAction,
) -> Self {
let evm_governance = ExodusRequestType::EvmGovernance { let evm_governance = ExodusRequestType::EvmGovernance {
exodus_session,
network_id,
target, target,
action, action,
}; };
Self::default_request_for(evm_governance) Self::default_request_for(evm_governance)
} }
pub fn is_rotation(&self) -> bool { fn default_request_for(request_type: ExodusRequestType<T>) -> Self {
match &self.r#type { Self {
ExodusRequestType::EvmRotation { .. } | ExodusRequestType::UtxoRotation { .. } => true, r#type: request_type,
_ => false, next_roast_session: Default::default()
} }
} }
fn default_request_for(
request_type: ExodusRequestType<NetworkId, Balance>,
) -> Self {
Self { r#type: request_type, next_roast_session: Default::default() }
}
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::mock::*;
use sp_io::hashing::keccak_256; use sp_io::hashing::keccak_256;
#[test] #[test]
fn test_rotation_message_valid() { fn test_rotation_message_valid() {
let mut buffer = [0u8; MAX_MESSAGE_SIZE as usize]; let mut buffer = [0u8; MAX_MESSAGE_SIZE as usize];
let public_key = EvmBytes32::repeat_byte(0x11); let public_key = EvmBytes32::repeat_byte(0x11);
let session = 69u64; let session = 69;
let network_id = 420;
let rotation_request = ExodusRequest::<u64, u128>::evm_rotation( let context = ExodusContext::new(network_id, session);
session, let dummy_xor_key = u128::MAX;
let dummy_evm_bitmask = 1337;
let mut dummy_packed_sessions =
EvmSessionsBundle::<Runtime>::with_max_capacity();
let max_size = EvmSessionsBundle::<Runtime>::bound();
dummy_packed_sessions.bounded_resize(max_size, 69);
let rotation_request = ExodusRequest::<Runtime>::evm_rotation(
public_key.clone(), public_key.clone(),
1, dummy_xor_key,
dummy_evm_bitmask,
dummy_packed_sessions,
); );
let calldata = rotation_request.get_message(&mut buffer).unwrap(); let calldata = rotation_request.get_message(&mut buffer, context).unwrap();
assert_eq!(calldata.len(), 100); // prefix + public_key + packed_bits + pointer + length + bytes array + padding
// 4 + 32 + 32 + 32 + 32 + 32 + (8 * 5) + (32 - 40 % 32)
assert_eq!(calldata.len(), 228);
let expected_hash = keccak_256("rotate(uint256,bytes32,uint8)".as_bytes()); let expected_hash = keccak_256("rotate(uint256,bytes32,uint256,bytes)".as_bytes());
assert_eq!(calldata[0..4], expected_hash[0..4]); assert_eq!(calldata[0..4], expected_hash[0..4]);
assert_eq!(calldata[35], 0x45); assert_eq!(calldata[35], 0x45);
assert_eq!(&calldata[4..35], &[0u8; 31]); assert_eq!(&calldata[4..35], &[0u8; 31]);
assert_eq!(&calldata[36..68], public_key.as_ref()); assert_eq!(&calldata[36..68], public_key.as_ref());
assert_eq!(calldata[99], 1);
assert_eq!(&calldata[68..99], &[0u8; 31]); assert_eq!(&calldata[68..84], &dummy_xor_key.to_be_bytes());
assert_eq!(&calldata[84..100], &dummy_evm_bitmask.to_be_bytes());
assert_eq!(&calldata[100..131], &[0u8; 31]);
assert_eq!(calldata[131], 128);
assert_eq!(&calldata[132..163], &[0u8; 31]);
assert_eq!(calldata[163], 40);
assert_eq!(calldata[164], 69);
assert_eq!(&calldata[204..228], &[0u8; 24]);
} }
#[test] #[test]
fn test_bridge_out_message_valid() { fn test_bridge_out_message_valid() {
let mut buffer = [0u8; MAX_MESSAGE_SIZE as usize]; let mut buffer = [0u8; MAX_MESSAGE_SIZE as usize];
let receiver = EvmAddress::repeat_byte(0xAA); let receiver = EvmAddress::repeat_byte(0xAA);
let network_id = 420u64; let network_id = 420;
let session = 69u64; let session = 69;
let bridge_request = ExodusRequest::<u64, u128>::evm_bridge_out( let context = ExodusContext::new(network_id, session);
session,
network_id, let bridge_request = ExodusRequest::<Runtime>::evm_bridge_out(
1_000_000u128, 1_000_000u64,
sp_runtime::Perbill::from_percent(1), sp_runtime::Perbill::from_percent(1),
receiver.clone() receiver.clone()
); );
let calldata = bridge_request.get_message(&mut buffer).unwrap(); let calldata = bridge_request.get_message(&mut buffer, context).unwrap();
assert_eq!(calldata.len(), 100); assert_eq!(calldata.len(), 100);
let expected_hash = keccak_256("recall(uint256,uint256,uint256)".as_bytes()); let expected_hash = keccak_256("recall(uint256,uint256,uint256)".as_bytes());
@ -549,8 +621,8 @@ mod tests {
fn test_governance_message_valid() { fn test_governance_message_valid() {
let mut buffer = [0u8; MAX_MESSAGE_SIZE as usize]; let mut buffer = [0u8; MAX_MESSAGE_SIZE as usize];
let target = EvmAddress::repeat_byte(0xAA); let target = EvmAddress::repeat_byte(0xAA);
let network_id = 420u64; let network_id = 420;
let session = 69u64; let session = 69;
let actions = vec![ let actions = vec![
GovernanceAction::SetDistributor { distributor: EvmAddress::repeat_byte(0x11) }, GovernanceAction::SetDistributor { distributor: EvmAddress::repeat_byte(0x11) },
@ -592,16 +664,16 @@ mod tests {
for (index, action) in actions.into_iter().enumerate() { for (index, action) in actions.into_iter().enumerate() {
let action_clone = action.clone(); let action_clone = action.clone();
let current_session = session + (index as u64);
let context = ExodusContext::new(network_id, current_session);
let governance_request = ExodusRequest::<u64, u128>::evm_governance( let governance_request = ExodusRequest::<Runtime>::evm_governance(
session,
network_id,
target, target,
action, action,
); );
let message = governance_request let message = governance_request
.get_message(&mut buffer) .get_message(&mut buffer, context)
.get_or_insert(&[]) .get_or_insert(&[])
.to_vec(); .to_vec();
@ -610,7 +682,7 @@ mod tests {
let expected_master_hash = keccak_256("govern(uint256,uint256,bytes)".as_bytes()); let expected_master_hash = keccak_256("govern(uint256,uint256,bytes)".as_bytes());
assert_eq!(message[0..4], expected_master_hash[0..4]); assert_eq!(message[0..4], expected_master_hash[0..4]);
assert_eq!(message[35], 69); assert_eq!(message[35], current_session as u8);
assert_eq!(&message[4..35], &[0u8; 31]); assert_eq!(&message[4..35], &[0u8; 31]);
assert_eq!(message[43], 0xA4); assert_eq!(message[43], 0xA4);
@ -626,6 +698,12 @@ mod tests {
assert_eq!((message.len() - 4) % 32, 0); assert_eq!((message.len() - 4) % 32, 0);
match action_clone { match action_clone {
GovernanceAction::SetRouterRegistryIndex { index } => {
let hash = keccak_256("setRouterRegistryIndex(uint256)".as_bytes());
assert_eq!(inner_payload[0..4], hash[0..4]);
let decoded_warmup = u32::from_be_bytes(inner_payload[32..36].try_into().unwrap());
assert_eq!(decoded_warmup, index as u32);
},
GovernanceAction::SetDistributor { distributor } => { GovernanceAction::SetDistributor { distributor } => {
let hash = keccak_256("setDistributor(address)".as_bytes()); let hash = keccak_256("setDistributor(address)".as_bytes());
assert_eq!(inner_payload[0..4], hash[0..4]); assert_eq!(inner_payload[0..4], hash[0..4]);
@ -634,7 +712,8 @@ mod tests {
GovernanceAction::SetWarmupPeriod { warmup_period } => { GovernanceAction::SetWarmupPeriod { warmup_period } => {
let hash = keccak_256("setWarmupPeriod(uint256)".as_bytes()); let hash = keccak_256("setWarmupPeriod(uint256)".as_bytes());
assert_eq!(inner_payload[0..4], hash[0..4]); assert_eq!(inner_payload[0..4], hash[0..4]);
assert_eq!(inner_payload[inner_payload.len() - 1], warmup_period as u8); let decoded_warmup = u32::from_be_bytes(inner_payload[32..36].try_into().unwrap());
assert_eq!(decoded_warmup, warmup_period as u32);
}, },
GovernanceAction::UpdateGatekeeperAddress { new_gatekeeper } => { GovernanceAction::UpdateGatekeeperAddress { new_gatekeeper } => {
let hash = keccak_256("updateGatekeeperAddress(address)".as_bytes()); let hash = keccak_256("updateGatekeeperAddress(address)".as_bytes());
@ -644,7 +723,8 @@ mod tests {
GovernanceAction::SetBounty { bounty } => { GovernanceAction::SetBounty { bounty } => {
let hash = keccak_256("setBounty(uint256)".as_bytes()); let hash = keccak_256("setBounty(uint256)".as_bytes());
assert_eq!(inner_payload[0..4], hash[0..4]); assert_eq!(inner_payload[0..4], hash[0..4]);
assert_eq!(inner_payload[inner_payload.len() - 1], bounty as u8); let decoded_bounty = u32::from_be_bytes(inner_payload[32..36].try_into().unwrap());
assert_eq!(decoded_bounty, bounty as u32);
}, },
GovernanceAction::AddPool { address } => { GovernanceAction::AddPool { address } => {
let hash = keccak_256("addPool(address)".as_bytes()); let hash = keccak_256("addPool(address)".as_bytes());
@ -654,13 +734,16 @@ mod tests {
GovernanceAction::RemovePool { index } => { GovernanceAction::RemovePool { index } => {
let hash = keccak_256("removePool(uint256)".as_bytes()); let hash = keccak_256("removePool(uint256)".as_bytes());
assert_eq!(inner_payload[0..4], hash[0..4]); assert_eq!(inner_payload[0..4], hash[0..4]);
assert_eq!(inner_payload[inner_payload.len() - 1], index as u8); let decoded_index = u32::from_be_bytes(inner_payload[32..36].try_into().unwrap());
assert_eq!(decoded_index, index as u32);
}, },
GovernanceAction::SetAdjustment { rate, target, add } => { GovernanceAction::SetAdjustment { rate, target, add } => {
let hash = keccak_256("setAdjustment(uint256,uint256,bool)".as_bytes()); let hash = keccak_256("setAdjustment(uint256,uint256,bool)".as_bytes());
assert_eq!(inner_payload[0..4], hash[0..4]); assert_eq!(inner_payload[0..4], hash[0..4]);
assert_eq!(inner_payload[35], rate as u8); let decoded_rate = u32::from_be_bytes(inner_payload[32..36].try_into().unwrap());
assert_eq!(inner_payload[67], target as u8); assert_eq!(decoded_rate, rate as u32);
let decoded_target = u32::from_be_bytes(inner_payload[64..68].try_into().unwrap());
assert_eq!(decoded_target, target as u32);
assert_eq!(inner_payload[99], add as u8); assert_eq!(inner_payload[99], add as u8);
}, },
GovernanceAction::Enable { status, address, calculator } => { GovernanceAction::Enable { status, address, calculator } => {
@ -680,7 +763,8 @@ mod tests {
let hash = keccak_256("forfeitReserves(address,uint256,bool)".as_bytes()); let hash = keccak_256("forfeitReserves(address,uint256,bool)".as_bytes());
assert_eq!(inner_payload[0..4], hash[0..4]); assert_eq!(inner_payload[0..4], hash[0..4]);
assert_eq!(&inner_payload[16..36], router.as_ref()); assert_eq!(&inner_payload[16..36], router.as_ref());
assert_eq!(inner_payload[67], liquidity as u8); let decoded_liquidity = u32::from_be_bytes(inner_payload[64..68].try_into().unwrap());
assert_eq!(decoded_liquidity, liquidity as u32);
assert_eq!(inner_payload[99], destroyer_mode as u8); assert_eq!(inner_payload[99], destroyer_mode as u8);
}, },
GovernanceAction::RedeemReserves { router, amount } => { GovernanceAction::RedeemReserves { router, amount } => {
@ -693,7 +777,13 @@ mod tests {
let hash = keccak_256("withdraw(address,uint256)".as_bytes()); let hash = keccak_256("withdraw(address,uint256)".as_bytes());
assert_eq!(inner_payload[0..4], hash[0..4]); assert_eq!(inner_payload[0..4], hash[0..4]);
assert_eq!(&inner_payload[16..36], token.as_ref()); assert_eq!(&inner_payload[16..36], token.as_ref());
assert_eq!(inner_payload[67], amount as u8); let decoded_amount = u32::from_be_bytes(inner_payload[64..68].try_into().unwrap());
assert_eq!(decoded_amount, amount as u32);
},
GovernanceAction::SetBondDepository { address } => {
let hash = keccak_256("setBondDepository(address)".as_bytes());
assert_eq!(inner_payload[0..4], hash[0..4]);
assert_eq!(&inner_payload[16..36], address.as_ref());
}, },
GovernanceAction::AuditReserves => { GovernanceAction::AuditReserves => {
let hash = keccak_256("auditReserves()".as_bytes()); let hash = keccak_256("auditReserves()".as_bytes());
@ -703,7 +793,8 @@ mod tests {
GovernanceAction::Close { id } => { GovernanceAction::Close { id } => {
let hash = keccak_256("close(uint256)".as_bytes()); let hash = keccak_256("close(uint256)".as_bytes());
assert_eq!(inner_payload[0..4], hash[0..4]); assert_eq!(inner_payload[0..4], hash[0..4]);
assert_eq!(inner_payload[inner_payload.len() - 1], id as u8); let decoded_id = u32::from_be_bytes(inner_payload[32..36].try_into().unwrap());
assert_eq!(decoded_id, id as u32);
}, },
GovernanceAction::Create { market, terms, token, intervals, booleans } => { GovernanceAction::Create { market, terms, token, intervals, booleans } => {
let hash = keccak_256("create(uint256[4],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());
@ -726,6 +817,13 @@ mod tests {
assert_eq!(inner_payload[323], booleans[0] as u8); assert_eq!(inner_payload[323], booleans[0] as u8);
assert_eq!(inner_payload[355], booleans[1] as u8); assert_eq!(inner_payload[355], booleans[1] as u8);
},
GovernanceAction::Approve { spender, value } => {
let hash = keccak_256("approve(address,uint256)".as_bytes());
assert_eq!(inner_payload[0..4], hash[0..4]);
assert_eq!(&inner_payload[16..36], spender.as_ref());
let decoded_amount = u32::from_be_bytes(inner_payload[64..68].try_into().unwrap());
assert_eq!(decoded_amount, value as u32);
} }
} }
} }

View File

@ -8,7 +8,7 @@ use sp_runtime::{
}; };
use super::{DkgPackage, ExodusPackage, ExodusOk, PackageContext}; use super::{DkgPackage, ExodusPackage, ExodusOk, PackageContext};
use crate::{AuthIndex, ExodusSession, ExodusHash, ExodusError}; use crate::{AuthIndex, ExodusContext, DkgIndex, ExodusHash, ExodusError};
fn create_storage_key(prefix: &[u8], index: &[u8], network: &[u8]) -> Vec<u8> { fn create_storage_key(prefix: &[u8], index: &[u8], network: &[u8]) -> Vec<u8> {
let mut key = crate::DB_STORAGE_PREFIX.to_vec(); let mut key = crate::DB_STORAGE_PREFIX.to_vec();
@ -59,10 +59,10 @@ pub struct ExodusShare;
#[derive(Default, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] #[derive(Default, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
pub struct ExodusExile; pub struct ExodusExile;
type ExodusNonces<BlockNumber> = BTreeMap<ExodusSession, PersistentData<BlockNumber, ExodusNonce>>; type ExodusNonces<NetworkId, BlockNumber> = BTreeMap<ExodusContext<NetworkId>, PersistentData<BlockNumber, ExodusNonce>>;
type ExodusGroups<BlockNumber> = BTreeMap<ExodusSession, PersistentData<BlockNumber, ExodusGroup>>; type ExodusGroups<NetworkId, BlockNumber> = BTreeMap<ExodusContext<NetworkId>, PersistentData<BlockNumber, ExodusGroup>>;
type ExodusShares<BlockNumber> = BTreeMap<ExodusSession, PersistentData<BlockNumber, ExodusShare>>; type ExodusShares<NetworkId, BlockNumber> = BTreeMap<ExodusContext<NetworkId>, PersistentData<BlockNumber, ExodusShare>>;
type ExodusExiles<BlockNumber> = BTreeMap<ExodusSession, PersistentData<BlockNumber, ExodusExile>>; type ExodusExiles<NetworkId, BlockNumber> = BTreeMap<ExodusContext<NetworkId>, PersistentData<BlockNumber, ExodusExile>>;
pub struct ExodusStorageWrapper<ExodusInnerType> { pub struct ExodusStorageWrapper<ExodusInnerType> {
pub inner: ExodusInnerType, pub inner: ExodusInnerType,
@ -75,7 +75,7 @@ impl<ExodusInnerType> ExodusStorageWrapper<ExodusInnerType> {
} }
} }
#[derive(Default)] #[derive(Default, Encode)]
pub struct ExodusStorage<BlockNumber, NetworkCurve> pub struct ExodusStorage<BlockNumber, NetworkCurve>
where where
BlockNumber: Default, BlockNumber: Default,
@ -113,34 +113,37 @@ where
self self
} }
pub fn read_local_storage(&self) -> ExodusRecords<B> { pub fn read_local_storage<NI>(&self) -> ExodusRecords<NI, B>
where
NI: Default + Encode + Decode + AtLeast32BitUnsigned,
{
let encoded_index = self.authority_index.encode(); let encoded_index = self.authority_index.encode();
let encoded_network = self.network_curve.encode(); let encoded_network = self.network_curve.encode();
let nonces_key = create_storage_key(crate::EXODUS_PREFIX_NONCES, &encoded_index, &encoded_network); let nonces_key = create_storage_key(crate::EXODUS_PREFIX_NONCES, &encoded_index, &encoded_network);
let exodus_nonces = StorageValueRef::persistent(&nonces_key) let exodus_nonces = StorageValueRef::persistent(&nonces_key)
.get::<ExodusNonces<B>>() .get::<ExodusNonces<NI, B>>()
.ok() .ok()
.flatten() .flatten()
.unwrap_or_default(); .unwrap_or_default();
let shares_key = create_storage_key(crate::EXODUS_PREFIX_SHARES, &encoded_index, &encoded_network); let shares_key = create_storage_key(crate::EXODUS_PREFIX_SHARES, &encoded_index, &encoded_network);
let exodus_shares = StorageValueRef::persistent(&shares_key) let exodus_shares = StorageValueRef::persistent(&shares_key)
.get::<ExodusShares<B>>() .get::<ExodusShares<NI, B>>()
.ok() .ok()
.flatten() .flatten()
.unwrap_or_default(); .unwrap_or_default();
let groups_key = create_storage_key(crate::EXODUS_PREFIX_BLENDS, &encoded_index, &encoded_network); let groups_key = create_storage_key(crate::EXODUS_PREFIX_BLENDS, &encoded_index, &encoded_network);
let exodus_groups = StorageValueRef::persistent(&groups_key) let exodus_groups = StorageValueRef::persistent(&groups_key)
.get::<ExodusGroups<B>>() .get::<ExodusGroups<NI, B>>()
.ok() .ok()
.flatten() .flatten()
.unwrap_or_default(); .unwrap_or_default();
let exiles_key = create_storage_key(crate::EXODUS_PREFIX_EXILES, &encoded_index, &encoded_network); let exiles_key = create_storage_key(crate::EXODUS_PREFIX_EXILES, &encoded_index, &encoded_network);
let exodus_exiles = StorageValueRef::persistent(&exiles_key) let exodus_exiles = StorageValueRef::persistent(&exiles_key)
.get::<ExodusExiles<B>>() .get::<ExodusExiles<NI, B>>()
.ok() .ok()
.flatten() .flatten()
.unwrap_or_default(); .unwrap_or_default();
@ -153,10 +156,11 @@ where
} }
} }
pub fn prepare_signed_call_with_context<T, A, F1, F2, F3>( pub fn prepare_signed_call_with_context<T, A, F1, F2, F3, NI>(
&self, &self,
authority_key: &A, authority_key: &A,
exodus_records: &mut ExodusRecords<B>, current_dkg_index: DkgIndex,
exodus_records: &mut ExodusRecords<NI, B>,
make_data: F1, make_data: F1,
submit_call: F2, submit_call: F2,
insert_data: F3, insert_data: F3,
@ -164,13 +168,16 @@ where
where where
T: crate::pallet::Config, T: crate::pallet::Config,
A: RuntimeAppPublic, A: RuntimeAppPublic,
F1: FnOnce(PackageContext<NC>) -> Result<ExodusPackage<NC, T>, ExodusError>, NI: Default + Encode + Decode + AtLeast32BitUnsigned,
F2: FnOnce(ExodusPackage<NC, T>, A::Signature) -> Result<(), ExodusError>, F1: FnOnce(PackageContext<NC, B>) -> Result<ExodusPackage<NC, NI, B, T>, ExodusError>,
F3: FnOnce(&mut ExodusRecords<B>, B), F2: FnOnce(ExodusPackage<NC, NI, B, T>, A::Signature) -> Result<(), ExodusError>,
F3: FnOnce(&mut ExodusRecords<NI, B>, B),
{ {
let context = PackageContext::default() let context = PackageContext::default()
.with_block_number(self.current_block)
.with_authority_index(self.authority_index) .with_authority_index(self.authority_index)
.with_network_curve(self.network_curve); .with_network_curve(self.network_curve)
.with_dkg_index(current_dkg_index);
let package = make_data(context)?; let package = make_data(context)?;
@ -203,7 +210,10 @@ where
StorageValueRef::persistent(&exiles_key).clear(); StorageValueRef::persistent(&exiles_key).clear();
} }
pub fn try_write_local_storage(&self, records: &ExodusRecords<B>) { pub fn try_write_local_storage<NI: Default>(&self, records: &ExodusRecords<NI, B>)
where
NI: Encode + Decode + AtLeast32BitUnsigned,
{
let encoded_index = self.authority_index.encode(); let encoded_index = self.authority_index.encode();
let encoded_network = self.network_curve.encode(); let encoded_network = self.network_curve.encode();
@ -229,99 +239,104 @@ where
} }
} }
pub struct ExodusRecords<BlockNumber> pub struct ExodusRecords<NetworkId, BlockNumber>
where where
NetworkId: Default,
BlockNumber: AtLeast32BitUnsigned, BlockNumber: AtLeast32BitUnsigned,
{ {
exodus_nonces: ExodusStorageWrapper<ExodusNonces<BlockNumber>>, exodus_nonces: ExodusStorageWrapper<ExodusNonces<NetworkId, BlockNumber>>,
exodus_shares: ExodusStorageWrapper<ExodusShares<BlockNumber>>, exodus_shares: ExodusStorageWrapper<ExodusShares<NetworkId, BlockNumber>>,
exodus_groups: ExodusStorageWrapper<ExodusGroups<BlockNumber>>, exodus_groups: ExodusStorageWrapper<ExodusGroups<NetworkId, BlockNumber>>,
exodus_exiles: ExodusStorageWrapper<ExodusExiles<BlockNumber>>, exodus_exiles: ExodusStorageWrapper<ExodusExiles<NetworkId, BlockNumber>>,
} }
impl<BlockNumber> ExodusRecords<BlockNumber> impl<NetworkId, BlockNumber> ExodusRecords<NetworkId, BlockNumber>
where where
BlockNumber: AtLeast32BitUnsigned + Default, BlockNumber: AtLeast32BitUnsigned + Default,
NetworkId: AtLeast32BitUnsigned + Default,
{ {
pub fn get_secret_nonce(&self, exodus_session: &ExodusSession) -> Option<&[u8]> { pub fn get_secret_nonce(
&self,
exodus_context: &ExodusContext<NetworkId>,
) -> Option<&[u8]> {
self.exodus_nonces.inner self.exodus_nonces.inner
.get(exodus_session) .get(exodus_context)
.map(|v| v.data.0.as_slice()) .map(|v| v.data.0.as_slice())
} }
pub fn get_group_record( pub fn get_group_record(
&self, &self,
exodus_session: &ExodusSession, exodus_context: &ExodusContext<NetworkId>,
) -> Option<ExodusGroup> { ) -> Option<ExodusGroup> {
self.exodus_groups.inner self.exodus_groups.inner
.get(exodus_session) .get(exodus_context)
.cloned() .cloned()
.map(|storage| storage.data) .map(|storage| storage.data)
} }
pub fn is_exodus_session_banned( pub fn is_exodus_session_banned(
&self, &self,
exodus_session: &ExodusSession, exodus_context: &ExodusContext<NetworkId>,
) -> bool { ) -> bool {
self.exodus_exiles.inner.contains_key(exodus_session) self.exodus_exiles.inner.contains_key(exodus_context)
} }
pub fn is_group_registered( pub fn is_group_registered(
&self, &self,
exodus_session: &ExodusSession, exodus_context: &ExodusContext<NetworkId>,
) -> bool { ) -> bool {
self.exodus_groups.inner.contains_key(exodus_session) self.exodus_groups.inner.contains_key(exodus_context)
} }
pub fn is_waiting_period_for_nonce( pub fn is_waiting_period_for_nonce(
&self, &self,
exodus_session: &ExodusSession, exodus_context: &ExodusContext<NetworkId>,
current_block: &BlockNumber current_block: &BlockNumber,
) -> bool { ) -> bool {
self.exodus_nonces.inner self.exodus_nonces.inner
.get(exodus_session) .get(exodus_context)
.map(|data| data.release_block.gt(current_block)) .map(|data| data.release_block.gt(current_block))
.unwrap_or(false) .unwrap_or(false)
} }
pub fn is_waiting_period_for_share( pub fn is_waiting_period_for_share(
&self, &self,
exodus_session: &ExodusSession, exodus_context: &ExodusContext<NetworkId>,
current_block: &BlockNumber, current_block: &BlockNumber,
) -> bool { ) -> bool {
self.exodus_shares.inner self.exodus_shares.inner
.get(exodus_session) .get(exodus_context)
.map(|data| data.release_block.gt(&current_block)) .map(|data| data.release_block.gt(&current_block))
.unwrap_or(false) .unwrap_or(false)
} }
pub fn is_waiting_period_for_group( pub fn is_waiting_period_for_group(
&self, &self,
exodus_session: &ExodusSession, exodus_context: &ExodusContext<NetworkId>,
current_block: &BlockNumber, current_block: &BlockNumber,
) -> bool { ) -> bool {
self.exodus_groups.inner self.exodus_groups.inner
.get(exodus_session) .get(exodus_context)
.map(|data| data.release_block.gt(&current_block)) .map(|data| data.release_block.gt(&current_block))
.unwrap_or(false) .unwrap_or(false)
} }
pub fn exclude_exodus_session( pub fn exclude_exodus_session(
&mut self, &mut self,
exodus_session: ExodusSession, exodus_context: ExodusContext<NetworkId>
) { ) {
let exodus_exile = PersistentData { let exodus_exile = PersistentData {
data: ExodusExile, data: ExodusExile,
release_block: Default::default(), release_block: Default::default(),
}; };
self.exodus_exiles.inner.insert(exodus_session, exodus_exile);
self.exodus_exiles.data_changed = true; self.exodus_exiles.data_changed = true;
self.exodus_exiles.inner.insert(exodus_context, exodus_exile);
} }
pub fn insert_exodus_nonce( pub fn insert_exodus_nonce(
&mut self, &mut self,
exodus_session: ExodusSession, exodus_context: ExodusContext<NetworkId>,
next_release_block: BlockNumber, next_release_block: BlockNumber,
nonce_commitment: Vec<u8>, nonce_commitment: Vec<u8>,
) { ) {
@ -330,13 +345,13 @@ where
release_block: next_release_block, release_block: next_release_block,
}; };
self.exodus_nonces.inner.insert(exodus_session, exodus_nonce);
self.exodus_nonces.data_changed = true; self.exodus_nonces.data_changed = true;
self.exodus_nonces.inner.insert(exodus_context, exodus_nonce);
} }
pub fn insert_exodus_share( pub fn insert_exodus_share(
&mut self, &mut self,
exodus_session: ExodusSession, exodus_context: ExodusContext<NetworkId>,
next_release_block: BlockNumber, next_release_block: BlockNumber,
) { ) {
let exodus_share = PersistentData { let exodus_share = PersistentData {
@ -344,13 +359,13 @@ where
release_block: next_release_block, release_block: next_release_block,
}; };
self.exodus_shares.inner.insert(exodus_session, exodus_share);
self.exodus_shares.data_changed = true; self.exodus_shares.data_changed = true;
self.exodus_shares.inner.insert(exodus_context, exodus_share);
} }
pub fn insert_exodus_group( pub fn insert_exodus_group(
&mut self, &mut self,
exodus_session: ExodusSession, exodus_context: ExodusContext<NetworkId>,
next_release_block: BlockNumber, next_release_block: BlockNumber,
self_binding_factor: Vec<u8>, self_binding_factor: Vec<u8>,
binding_factors_proof: Vec<ExodusHash>, binding_factors_proof: Vec<ExodusHash>,
@ -360,12 +375,12 @@ where
release_block: next_release_block, release_block: next_release_block,
}; };
self.exodus_groups.inner.insert(exodus_session, exodus_group);
self.exodus_groups.data_changed = true; self.exodus_groups.data_changed = true;
self.exodus_groups.inner.insert(exodus_context, exodus_group);
} }
} }
#[derive(Default, Clone, Copy)] #[derive(Default, Encode, Clone, Copy)]
pub struct DkgStorage<BlockNumber, NetworkCurve> pub struct DkgStorage<BlockNumber, NetworkCurve>
where where
BlockNumber: Copy + Clone, BlockNumber: Copy + Clone,
@ -406,10 +421,10 @@ where
self.current_block.saturating_add(self.block_longevity) self.current_block.saturating_add(self.block_longevity)
} }
pub fn get_dkg_result( pub fn get_dkg_result<NI>(
&self, &self,
dkg_record: DkgRecord<B>, dkg_record: DkgRecord<B>,
) -> Result<ExodusOk<NC>, ExodusError> { ) -> Result<ExodusOk<NC, NI>, ExodusError> {
match dkg_record.prefix_slice { match dkg_record.prefix_slice {
crate::DKG_ROUND0_PREFIX => return Ok(ExodusOk::DkgRoundRegistered(0u8, self.authority_index, self.network_curve)), crate::DKG_ROUND0_PREFIX => return Ok(ExodusOk::DkgRoundRegistered(0u8, self.authority_index, self.network_curve)),
crate::DKG_ROUND1_PREFIX => return Ok(ExodusOk::DkgRoundRegistered(1u8, self.authority_index, self.network_curve)), crate::DKG_ROUND1_PREFIX => return Ok(ExodusOk::DkgRoundRegistered(1u8, self.authority_index, self.network_curve)),
@ -454,6 +469,7 @@ where
pub fn prepare_signed_call_with_context<T, A, F1, F2>( pub fn prepare_signed_call_with_context<T, A, F1, F2>(
&self, &self,
authority_key: &A, authority_key: &A,
current_dkg_index: DkgIndex,
dkg_record: &mut DkgRecord<B>, dkg_record: &mut DkgRecord<B>,
make_data: F1, make_data: F1,
submit_call: F2, submit_call: F2,
@ -461,12 +477,14 @@ where
where where
T: crate::pallet::Config, T: crate::pallet::Config,
A: RuntimeAppPublic, A: RuntimeAppPublic,
F1: FnOnce(PackageContext<NC>) -> Result<DkgPackage<NC, T>, ExodusError>, F1: FnOnce(PackageContext<NC, B>) -> Result<DkgPackage<NC, B, T>, ExodusError>,
F2: FnOnce(DkgPackage<NC, T>, A::Signature) -> Result<(), ExodusError>, F2: FnOnce(DkgPackage<NC, B, T>, A::Signature) -> Result<(), ExodusError>,
{ {
let context = PackageContext::default() let context = PackageContext::default()
.with_block_number(self.current_block)
.with_authority_index(self.authority_index) .with_authority_index(self.authority_index)
.with_network_curve(self.network_curve); .with_network_curve(self.network_curve)
.with_dkg_index(current_dkg_index);
let package = make_data(context)?; let package = make_data(context)?;

View File

@ -3,37 +3,85 @@ use scale_info::TypeInfo;
use sp_runtime::RuntimeDebug; use sp_runtime::RuntimeDebug;
use codec::{Encode, Decode}; use codec::{Encode, Decode};
use sp_runtime::traits::UniqueSaturatedInto;
use frame_support::{BoundedVec, traits::ConstU32}; use frame_support::{BoundedVec, traits::ConstU32};
use crate::{AuthIndex, ExodusSession, ExodusHash, ExodusError}; use ghost_traits::bounded_bitmap::BoundedBitmapReader;
pub trait PackageMetadata<NetworkCurve> use crate::{
AuthIndex, DkgIndex, ExodusSession, ExodusHash, ExodusError,
ParticipantsBitmap,
};
pub trait IndexedBundle<T>
where
T: crate::pallet::Config,
{
fn blob(&self) -> &[u8];
fn bitmask(&self) -> &ParticipantsBitmap<T>;
fn get_package_by_index<D: Decode>(&self, index: AuthIndex, size: usize) -> Option<D> {
let index_usize = index.unique_saturated_into();
let popcount = self.bitmask().count_ones_before::<usize>(index_usize)?;
popcount.checked_mul(size)
.and_then(|start| start.checked_add(size).map(|end| (start, end)))
.and_then(|(start, end)| self.blob().get(start..end))
.and_then(|raw_slice| D::decode(&mut &raw_slice[..]).ok())
}
}
pub trait PackageMetadata<NetworkCurve, BlockNumber>
where where
NetworkCurve: Copy, NetworkCurve: Copy,
BlockNumber: Copy,
{ {
fn context(&self) -> &PackageContext<NetworkCurve>; fn package_context(&self) -> &PackageContext<NetworkCurve, BlockNumber>;
fn get_authority_index(&self) -> AuthIndex { fn get_authority_index(&self) -> AuthIndex {
self.context().get_authority_index() self.package_context().get_authority_index()
} }
fn get_network_curve(&self) -> NetworkCurve { fn get_network_curve(&self) -> NetworkCurve {
self.context().get_network_curve() self.package_context().get_network_curve()
} }
fn get_dkg_index(&self) -> DkgIndex {
self.package_context().get_dkg_index()
}
fn get_block_number(&self) -> BlockNumber {
self.package_context().get_block_number()
}
fn get_metadata(&self) -> (NetworkCurve, AuthIndex, DkgIndex) {
let network_curve = self.get_network_curve();
let authority_index = self.get_authority_index();
let dkg_index = self.get_dkg_index();
(network_curve, authority_index, dkg_index)
}
}
pub trait ExodusMetadata<NetworkId>
where
NetworkId: Default + Copy,
{
fn get_exodus_context(&self) -> &ExodusContext<NetworkId>;
fn copy_exodus_context(&self) -> ExodusContext<NetworkId>;
} }
#[derive(Encode, Decode, Clone, RuntimeDebug, TypeInfo, PartialEq, Eq)] #[derive(Encode, Decode, Clone, RuntimeDebug, TypeInfo, PartialEq, Eq)]
#[scale_info(skip_type_params(T))] #[scale_info(skip_type_params(T))]
pub enum ExodusPackage<NetworkCurve, T> pub enum ExodusPackage<NetworkCurve, NetworkId, BlockNumber, T>
where where
NetworkId: Default,
T: crate::pallet::Config, T: crate::pallet::Config,
{ {
NonceCommitment(ExodusNonceCommitment<NetworkCurve>), NonceCommitment(ExodusNonceCommitment<NetworkCurve, NetworkId, BlockNumber>),
GroupCommitment(ExodusGroupCommitment<NetworkCurve>), GroupCommitment(ExodusGroupCommitment<NetworkCurve, NetworkId, BlockNumber>),
SignatureShare(ExodusSignatureShare<NetworkCurve, T>), SignatureShare(ExodusSignatureShare<NetworkCurve, NetworkId, BlockNumber, T>),
} }
impl<NetworkCurve, T> ExodusPackage<NetworkCurve, T> impl<NC, NI, B, T> ExodusPackage<NC, NI, B, T>
where where
T: crate::pallet::Config, T: crate::pallet::Config,
NI: Default,
{ {
pub fn bytes_len(&self) -> u32 { pub fn bytes_len(&self) -> u32 {
let length = match self { let length = match self {
@ -51,32 +99,24 @@ where
length as u32 length as u32
} }
pub fn get_exodus_session(&self) -> ExodusSession {
match &self {
Self::SignatureShare(package) => package.session,
Self::NonceCommitment(package) => package.session,
Self::GroupCommitment(package) => package.session,
}
}
} }
#[derive(Encode, Decode, Clone, RuntimeDebug, TypeInfo, PartialEq, Eq)] #[derive(RuntimeDebug, Encode, Decode, Clone, TypeInfo, PartialEq, Eq)]
#[scale_info(skip_type_params(T))] #[scale_info(skip_type_params(T))]
pub enum DkgPackage<NetworkCurve, T> pub enum DkgPackage<NetworkCurve, BlockNumber, T>
where where
T: crate::pallet::Config, T: crate::pallet::Config,
{ {
Round0(Round0Package<NetworkCurve>), Round0(Round0Package<NetworkCurve, BlockNumber>),
Round1(Round1Package<NetworkCurve, T>), Round1(Round1Package<NetworkCurve, BlockNumber, T>),
Round2(Round2Package<NetworkCurve, T>), Round2(Round2Package<NetworkCurve, BlockNumber, T>),
Round3(Round3Package<NetworkCurve, T>), Round3(Round3Package<NetworkCurve, BlockNumber, T>),
Round4(Round4Package<NetworkCurve, T>), Round4(Round4Package<NetworkCurve, BlockNumber, T>),
Round5(Round5Package<NetworkCurve>), Round5(Round5Package<NetworkCurve, BlockNumber, T>),
Round6(Round6Package<NetworkCurve, T>), Round6(Round6Package<NetworkCurve, BlockNumber, T>),
} }
impl<NetworkCurve, T> DkgPackage<NetworkCurve, T> impl<NC, B, T> DkgPackage<NC, B, T>
where where
T: crate::pallet::Config, T: crate::pallet::Config,
{ {
@ -84,12 +124,19 @@ where
let length = match self { let length = match self {
Self::Round0(_) => ExodusHash::len_bytes(), Self::Round0(_) => ExodusHash::len_bytes(),
Self::Round1(round1) => round1.package.len(), Self::Round1(round1) => round1.package.len(),
Self::Round2(round2) => round2.bundle.bitmask.len(), Self::Round2(round2) => round2.bundle.bitmask.count_ones(),
Self::Round3(round3) => round3.indices.len(), Self::Round3(round3) => {
Self::Round4(round4) => round4.bundle.bitmask.len(), round3.indices.highest_bit::<usize>().unwrap_or_default()
},
Self::Round4(round4) => round4.bundle.bitmask.count_ones(),
Self::Round5(round5) => { Self::Round5(round5) => {
let key_members_size = round5.key_members
.highest_bit::<usize>()
.unwrap_or_default();
round5.verifying_key.len() round5.verifying_key.len()
.saturating_add(ExodusHash::len_bytes()) .saturating_add(ExodusHash::len_bytes())
.saturating_add(key_members_size)
}, },
Self::Round6(round6) => round6.merkle_proof.len(), Self::Round6(round6) => round6.merkle_proof.len(),
}; };
@ -98,15 +145,18 @@ where
} }
} }
#[derive(Default, RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] #[derive(Default, RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
pub struct PackageContext<NetworkCurve> { pub struct PackageContext<NetworkCurve, BlockNumber> {
authority_index: AuthIndex, authority_index: AuthIndex,
network_curve: NetworkCurve network_curve: NetworkCurve,
block_number: BlockNumber,
dkg_index: DkgIndex,
} }
impl<NetworkCurve> PackageContext<NetworkCurve> impl<NetworkCurve, BlockNumber> PackageContext<NetworkCurve, BlockNumber>
where where
NetworkCurve: Copy, NetworkCurve: Copy,
BlockNumber: Copy,
{ {
pub fn get_authority_index(&self) -> AuthIndex { pub fn get_authority_index(&self) -> AuthIndex {
self.authority_index self.authority_index
@ -116,6 +166,14 @@ where
self.network_curve self.network_curve
} }
pub fn get_dkg_index(&self) -> DkgIndex {
self.dkg_index
}
pub fn get_block_number(&self) -> BlockNumber {
self.block_number
}
pub fn with_network_curve(mut self, network_curve: NetworkCurve) -> Self { pub fn with_network_curve(mut self, network_curve: NetworkCurve) -> Self {
self.network_curve = network_curve; self.network_curve = network_curve;
self self
@ -125,225 +183,265 @@ where
self.authority_index = authority_index; self.authority_index = authority_index;
self self
} }
pub fn with_dkg_index(mut self, dkg_index: DkgIndex) -> Self {
self.dkg_index = dkg_index;
self
}
pub fn with_block_number(mut self, block_number: BlockNumber) -> Self {
self.block_number = block_number;
self
}
} }
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] #[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
pub struct BundledPackages { #[scale_info(skip_type_params(T))]
pub struct BundledPackages<T>
where
T: crate::pallet::Config
{
pub bitmask: ParticipantsBitmap<T>,
pub blob: Vec<u8>, pub blob: Vec<u8>,
pub bitmask: Vec<u8>,
} }
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] impl<T> IndexedBundle<T> for BundledPackages<T>
where
T: crate::pallet::Config,
{
fn blob(&self) -> &[u8] { &self.blob }
fn bitmask(&self) -> &ParticipantsBitmap<T> { &self.bitmask }
}
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
#[scale_info(skip_type_params(T))] #[scale_info(skip_type_params(T))]
pub struct EncryptionBundledPackages<T> pub struct EncryptionBundledPackages<T>
where where
T: crate::pallet::Config, T: crate::pallet::Config,
{ {
pub blob: BoundedVec<u8, crate::EncryptedRound2BlobMaxBytes<T>>, pub blob: BoundedVec<u8, crate::EncryptedRound2BlobMaxBytes<T>>,
pub bitmask: BoundedVec<u8, crate::MaxAuthoritiesBitmaskSize<T>> pub bitmask: ParticipantsBitmap<T>,
} }
impl<T: crate::pallet::Config> EncryptionBundledPackages<T> { impl<T> IndexedBundle<T> for EncryptionBundledPackages<T>
pub fn len(&self) -> usize { where
let blob_len = self.blob.len(); T: crate::pallet::Config,
let bitmask_len = self.bitmask.len(); {
fn blob(&self) -> &[u8] { &self.blob }
bitmask_len.saturating_add(blob_len) fn bitmask(&self) -> &ParticipantsBitmap<T> { &self.bitmask }
}
} }
impl<T: crate::pallet::Config> TryFrom<BundledPackages> for EncryptionBundledPackages<T> { impl<T: crate::pallet::Config> TryFrom<BundledPackages<T>> for EncryptionBundledPackages<T> {
type Error = ExodusError; type Error = ExodusError;
fn try_from(source: BundledPackages) -> Result<Self, Self::Error> { fn try_from(source: BundledPackages<T>) -> Result<Self, Self::Error> {
let blob = let blob = BoundedVec::try_from(source.blob)
BoundedVec::<u8, crate::EncryptedRound2BlobMaxBytes<T>>::try_from(source.blob)
.map_err(|_| ExodusError::DeserializationError)?; .map_err(|_| ExodusError::DeserializationError)?;
let bitmask = BoundedVec::<u8, crate::MaxAuthoritiesBitmaskSize<T>>::try_from(source.bitmask) Ok(Self { blob, bitmask: source.bitmask })
.map_err(|_| ExodusError::DeserializationError)?;
Ok(Self { blob, bitmask })
} }
} }
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] #[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
#[scale_info(skip_type_params(T))] #[scale_info(skip_type_params(T))]
pub struct JustificationsBundledPackages<T> pub struct JustificationsBundledPackages<T>
where where
T: crate::pallet::Config, T: crate::pallet::Config,
{ {
pub blob: BoundedVec<u8, crate::Round2BlobMaxBytes<T>>, pub blob: BoundedVec<u8, crate::Round2BlobMaxBytes<T>>,
pub bitmask: BoundedVec<u8, crate::MaxAuthoritiesBitmaskSize<T>> pub bitmask: ParticipantsBitmap<T>,
} }
impl<T: crate::pallet::Config> JustificationsBundledPackages<T> { impl<T> IndexedBundle<T> for JustificationsBundledPackages<T>
pub fn len(&self) -> usize {
let blob_len = self.blob.len();
let bitmask_len = self.bitmask.len();
bitmask_len.saturating_add(blob_len)
}
}
impl<T: crate::pallet::Config> TryFrom<BundledPackages> for JustificationsBundledPackages<T> {
type Error = ExodusError;
fn try_from(source: BundledPackages) -> Result<Self, Self::Error> {
let blob =
BoundedVec::<u8, crate::Round2BlobMaxBytes<T>>::try_from(source.blob)
.map_err(|_| ExodusError::DeserializationError)?;
let bitmask = BoundedVec::<u8, crate::MaxAuthoritiesBitmaskSize<T>>::try_from(source.bitmask)
.map_err(|_| ExodusError::DeserializationError)?;
Ok(Self { blob, bitmask })
}
}
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct ExodusNonceCommitment<NetworkCurve> {
context: PackageContext<NetworkCurve>,
pub hiding_commitment: BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>,
pub binding_commitment: BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>,
pub session: ExodusSession,
}
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
pub struct ExodusGroupCommitment<NetworkCurve> {
context: PackageContext<NetworkCurve>,
pub group_commitment: BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>,
pub binding_factors_root: ExodusHash,
pub session: ExodusSession,
}
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct ExodusSignatureShare<NetworkCurve, T>
where where
T: crate::pallet::Config, T: crate::pallet::Config,
{ {
context: PackageContext<NetworkCurve>, fn blob(&self) -> &[u8] { &self.blob }
fn bitmask(&self) -> &ParticipantsBitmap<T> { &self.bitmask }
}
impl<T: crate::pallet::Config> TryFrom<BundledPackages<T>> for JustificationsBundledPackages<T> {
type Error = ExodusError;
fn try_from(source: BundledPackages<T>) -> Result<Self, Self::Error> {
let blob = BoundedVec::try_from(source.blob)
.map_err(|_| ExodusError::DeserializationError)?;
Ok(Self { blob, bitmask: source.bitmask })
}
}
#[derive(RuntimeDebug, Default, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Encode, Decode, TypeInfo)]
pub struct ExodusContext<NetworkId: Default> {
pub session: ExodusSession,
pub network_id: NetworkId,
}
impl<NetworkId: Default + Copy> ExodusContext<NetworkId> {
pub fn new(network_id: NetworkId, session: ExodusSession) -> Self {
Self { session, network_id }
}
pub fn with_network_id(mut self, network_id: NetworkId) -> Self {
self.network_id = network_id;
self
}
pub fn with_exodus_session(mut self, session: ExodusSession) -> Self {
self.session = session;
self
}
}
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct ExodusNonceCommitment<NetworkCurve, NetworkId: Default, BlockNumber> {
package_context: PackageContext<NetworkCurve, BlockNumber>,
exodus_context: ExodusContext<NetworkId>,
pub hiding_commitment: BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>,
pub binding_commitment: BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>,
}
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
pub struct ExodusGroupCommitment<NetworkCurve, NetworkId: Default, BlockNumber> {
package_context: PackageContext<NetworkCurve, BlockNumber>,
exodus_context: ExodusContext<NetworkId>,
pub group_commitment: BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>,
pub binding_factors_root: ExodusHash,
}
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct ExodusSignatureShare<NetworkCurve, NetworkId, BlockNumber, T>
where
NetworkId: Default,
T: crate::pallet::Config,
{
package_context: PackageContext<NetworkCurve, BlockNumber>,
exodus_context: ExodusContext<NetworkId>,
pub binding_factors_proof: BoundedVec<ExodusHash, crate::BindingFactorsProof<T>>, pub binding_factors_proof: BoundedVec<ExodusHash, crate::BindingFactorsProof<T>>,
pub signature_share: BoundedVec<u8, ConstU32<{ crate::SCALAR_MAX_BYTES }>>, pub signature_share: BoundedVec<u8, ConstU32<{ crate::SCALAR_MAX_BYTES }>>,
pub self_binding_factor: BoundedVec<u8, ConstU32<{ crate::SCALAR_MAX_BYTES }>>, pub self_binding_factor: BoundedVec<u8, ConstU32<{ crate::SCALAR_MAX_BYTES }>>,
pub session: ExodusSession,
} }
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] #[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
pub struct Round0Package<NetworkCurve> { pub struct Round0Package<NetworkCurve, BlockNumber> {
context: PackageContext<NetworkCurve>, context: PackageContext<NetworkCurve, BlockNumber>,
pub package_hash: ExodusHash, pub package_hash: ExodusHash,
} }
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] #[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
#[scale_info(skip_type_params(T))] #[scale_info(skip_type_params(T))]
pub struct Round1Package<NetworkCurve, T> pub struct Round1Package<NetworkCurve, BlockNumber, T>
where where
T: crate::pallet::Config, T: crate::pallet::Config,
{ {
context: PackageContext<NetworkCurve>, context: PackageContext<NetworkCurve, BlockNumber>,
pub package: BoundedVec<u8, crate::Round1MaxBytes<T>>, pub package: BoundedVec<u8, crate::Round1MaxBytes<T>>,
} }
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] #[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
#[scale_info(skip_type_params(T))] #[scale_info(skip_type_params(T))]
pub struct Round2Package<NetworkCurve, T> pub struct Round2Package<NetworkCurve, BlockNumber, T>
where where
T: crate::pallet::Config, T: crate::pallet::Config,
{ {
context: PackageContext<NetworkCurve>, context: PackageContext<NetworkCurve, BlockNumber>,
pub bundle: EncryptionBundledPackages<T>, pub bundle: EncryptionBundledPackages<T>,
} }
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] #[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
#[scale_info(skip_type_params(T))] #[scale_info(skip_type_params(T))]
pub struct Round3Package<NetworkCurve, T> pub struct Round3Package<NetworkCurve, BlockNumber, T>
where where
T: crate::pallet::Config, T: crate::pallet::Config,
{ {
context: PackageContext<NetworkCurve>, pub indices: ParticipantsBitmap<T>,
pub indices: BoundedVec<u8, crate::AccusedIndicesMaxBytes<T>>, context: PackageContext<NetworkCurve, BlockNumber>,
} }
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] #[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
#[scale_info(skip_type_params(T))] #[scale_info(skip_type_params(T))]
pub struct Round4Package<NetworkCurve, T> pub struct Round4Package<NetworkCurve, BlockNumber, T>
where where
T: crate::pallet::Config, T: crate::pallet::Config,
{ {
context: PackageContext<NetworkCurve>, context: PackageContext<NetworkCurve, BlockNumber>,
pub bundle: JustificationsBundledPackages<T>, pub bundle: JustificationsBundledPackages<T>,
} }
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] #[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
pub struct Round5Package<NetworkCurve> { #[scale_info(skip_type_params(T))]
context: PackageContext<NetworkCurve>, pub struct Round5Package<NetworkCurve, BlockNumber, T>
where
T: crate::pallet::Config,
{
context: PackageContext<NetworkCurve, BlockNumber>,
pub key_members: ParticipantsBitmap<T>,
pub verifying_key: BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>, pub verifying_key: BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>,
pub merkle_root: ExodusHash, pub merkle_root: ExodusHash,
} }
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] #[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
#[scale_info(skip_type_params(T))] #[scale_info(skip_type_params(T))]
pub struct Round6Package<NetworkCurve, T> pub struct Round6Package<NetworkCurve, BlockNumber, T>
where where
T: crate::pallet::Config, T: crate::pallet::Config,
{ {
context: PackageContext<NetworkCurve>, context: PackageContext<NetworkCurve, BlockNumber>,
pub verifying_share: BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>, pub verifying_share: BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>,
pub merkle_proof: BoundedVec<ExodusHash, crate::MerkleProofMaxSize<T>>, pub merkle_proof: BoundedVec<ExodusHash, crate::MerkleProofMaxSize<T>>,
} }
impl<NetworkCurve> PackageContext<NetworkCurve> { impl<NetworkCurve, BlockNumber> PackageContext<NetworkCurve, BlockNumber> {
pub fn with_exodus_nonce_commitment( pub fn with_exodus_nonce_commitment<NetworkId: Default>(
self, self,
session: ExodusSession, exodus_context: ExodusContext<NetworkId>,
hiding_commitment_vec: Vec<u8>, hiding_commitment_vec: Vec<u8>,
binding_commitment_vec: Vec<u8>, binding_commitment_vec: Vec<u8>,
) -> Result<ExodusNonceCommitment<NetworkCurve>, ExodusError> { ) -> Result<ExodusNonceCommitment<NetworkCurve, NetworkId, BlockNumber>, ExodusError> {
let hiding_commitment = let hiding_commitment = BoundedVec::try_from(hiding_commitment_vec)
BoundedVec::<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>::try_from(hiding_commitment_vec)
.map_err(|_| ExodusError::DeserializationError)?; .map_err(|_| ExodusError::DeserializationError)?;
let binding_commitment = let binding_commitment = BoundedVec::try_from(binding_commitment_vec)
BoundedVec::<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>::try_from(binding_commitment_vec)
.map_err(|_| ExodusError::DeserializationError)?; .map_err(|_| ExodusError::DeserializationError)?;
Ok(ExodusNonceCommitment { context: self, hiding_commitment, binding_commitment, session }) Ok(ExodusNonceCommitment {
} package_context: self,
exodus_context,
pub fn with_exodus_group_commitment( hiding_commitment,
self, binding_commitment,
session: ExodusSession,
commitment_vec: Vec<u8>,
binding_factors_root: ExodusHash,
) -> Result<ExodusGroupCommitment<NetworkCurve>, ExodusError> {
let group_commitment =
BoundedVec::<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>::try_from(commitment_vec)
.map_err(|_| ExodusError::DeserializationError)?;
Ok(ExodusGroupCommitment {
context: self,
group_commitment,
binding_factors_root,
session,
}) })
} }
pub fn with_exodus_signature_share<T: crate::pallet::Config>( pub fn with_exodus_group_commitment<NetworkId: Default>(
self, self,
session: ExodusSession, exodus_context: ExodusContext<NetworkId>,
commitment_vec: Vec<u8>,
binding_factors_root: ExodusHash,
) -> Result<ExodusGroupCommitment<NetworkCurve, NetworkId, BlockNumber>, ExodusError> {
let group_commitment = BoundedVec::try_from(commitment_vec)
.map_err(|_| ExodusError::DeserializationError)?;
Ok(ExodusGroupCommitment {
package_context: self,
exodus_context,
group_commitment,
binding_factors_root,
})
}
pub fn with_exodus_signature_share<NetworkId: Default, T: crate::pallet::Config>(
self,
exodus_context: ExodusContext<NetworkId>,
signature_share_vec: Vec<u8>, signature_share_vec: Vec<u8>,
binding_factors_proof_vec: Vec<ExodusHash>, binding_factors_proof_vec: Vec<ExodusHash>,
self_binding_factor_vec: Vec<u8>, self_binding_factor_vec: Vec<u8>,
) -> Result<ExodusSignatureShare<NetworkCurve, T>, ExodusError> { ) -> Result<ExodusSignatureShare<NetworkCurve, NetworkId, BlockNumber, T>, ExodusError> {
let signature_share = let signature_share = BoundedVec::try_from(signature_share_vec)
BoundedVec::<u8, ConstU32<{ crate::SCALAR_MAX_BYTES }>>::try_from(signature_share_vec)
.map_err(|_| ExodusError::DeserializationError)?; .map_err(|_| ExodusError::DeserializationError)?;
let binding_factors_proof = let binding_factors_proof = BoundedVec::try_from(binding_factors_proof_vec)
BoundedVec::<ExodusHash, crate::BindingFactorsProof<T>>::try_from(binding_factors_proof_vec)
.map_err(|_| ExodusError::DeserializationError)?; .map_err(|_| ExodusError::DeserializationError)?;
let self_binding_factor = let self_binding_factor =
@ -351,27 +449,26 @@ impl<NetworkCurve> PackageContext<NetworkCurve> {
.map_err(|_| ExodusError::DeserializationError)?; .map_err(|_| ExodusError::DeserializationError)?;
Ok(ExodusSignatureShare { Ok(ExodusSignatureShare {
context: self, package_context: self,
exodus_context,
binding_factors_proof, binding_factors_proof,
self_binding_factor, self_binding_factor,
signature_share, signature_share,
session,
}) })
} }
pub fn with_dkg_round0( pub fn with_dkg_round0(
self, self,
package_hash: ExodusHash, package_hash: ExodusHash,
) -> Round0Package<NetworkCurve> { ) -> Round0Package<NetworkCurve, BlockNumber> {
Round0Package { context: self, package_hash } Round0Package { context: self, package_hash }
} }
pub fn with_dkg_round1<T: crate::pallet::Config>( pub fn with_dkg_round1<T: crate::pallet::Config>(
self, self,
package_vec: Vec<u8>, package_vec: Vec<u8>,
) -> Result<Round1Package<NetworkCurve, T>, ExodusError> { ) -> Result<Round1Package<NetworkCurve, BlockNumber, T>, ExodusError> {
let package = let package = BoundedVec::try_from(package_vec)
BoundedVec::<u8, crate::Round1MaxBytes<T>>::try_from(package_vec)
.map_err(|_| ExodusError::DeserializationError)?; .map_err(|_| ExodusError::DeserializationError)?;
Ok(Round1Package { context: self, package }) Ok(Round1Package { context: self, package })
@ -379,80 +476,99 @@ impl<NetworkCurve> PackageContext<NetworkCurve> {
pub fn with_dkg_round2<T: crate::pallet::Config>( pub fn with_dkg_round2<T: crate::pallet::Config>(
self, self,
bundle_raw: BundledPackages, bundle_raw: BundledPackages<T>,
) -> Result<Round2Package<NetworkCurve, T>, ExodusError> { ) -> Result<Round2Package<NetworkCurve, BlockNumber, T>, ExodusError> {
let bundle: EncryptionBundledPackages<T> = bundle_raw.try_into()?; let bundle: EncryptionBundledPackages<T> = bundle_raw.try_into()?;
Ok(Round2Package { context: self, bundle }) Ok(Round2Package { context: self, bundle })
} }
pub fn with_dkg_round3<T: crate::pallet::Config>( pub fn with_dkg_round3<T: crate::pallet::Config>(
self, self,
indices_vec: Vec<u8> indices: ParticipantsBitmap<T>,
) -> Result<Round3Package<NetworkCurve, T>, ExodusError> { ) -> Result<Round3Package<NetworkCurve, BlockNumber, T>, ExodusError> {
let indices =
BoundedVec::<u8, crate::AccusedIndicesMaxBytes<T>>::try_from(indices_vec)
.map_err(|_| ExodusError::DeserializationError)?;
Ok(Round3Package { context: self, indices }) Ok(Round3Package { context: self, indices })
} }
pub fn with_dkg_round4<T: crate::pallet::Config>( pub fn with_dkg_round4<T: crate::pallet::Config>(
self, self,
bundle_raw: BundledPackages, bundle_raw: BundledPackages<T>,
) -> Result<Round4Package<NetworkCurve, T>, ExodusError> { ) -> Result<Round4Package<NetworkCurve, BlockNumber, T>, ExodusError> {
let bundle: JustificationsBundledPackages<T> = bundle_raw.try_into()?; let bundle: JustificationsBundledPackages<T> = bundle_raw.try_into()?;
Ok(Round4Package { context: self, bundle }) Ok(Round4Package { context: self, bundle })
} }
pub fn with_dkg_round5( pub fn with_dkg_round5<T: crate::pallet::Config>(
self, self,
verifying_key_vec: Vec<u8>, verifying_key_vec: Vec<u8>,
key_members: ParticipantsBitmap<T>,
merkle_root: ExodusHash, merkle_root: ExodusHash,
) -> Result<Round5Package<NetworkCurve>, ExodusError> { ) -> Result<Round5Package<NetworkCurve, BlockNumber, T>, ExodusError> {
let verifying_key = let verifying_key = BoundedVec::try_from(verifying_key_vec)
BoundedVec::<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>::try_from(verifying_key_vec)
.map_err(|_| ExodusError::DeserializationError)?; .map_err(|_| ExodusError::DeserializationError)?;
Ok(Round5Package { context: self, verifying_key, merkle_root }) Ok(Round5Package { context: self, key_members, verifying_key, merkle_root })
} }
pub fn with_dkg_round6<T: crate::pallet::Config>( pub fn with_dkg_round6<T: crate::pallet::Config>(
self, self,
verifying_share_vec: Vec<u8>, verifying_share_vec: Vec<u8>,
merkle_proof_vec: Vec<ExodusHash>, merkle_proof_vec: Vec<ExodusHash>,
) -> Result<Round6Package<NetworkCurve, T>, ExodusError> { ) -> Result<Round6Package<NetworkCurve, BlockNumber, T>, ExodusError> {
let verifying_share = let verifying_share = BoundedVec::try_from(verifying_share_vec)
BoundedVec::<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>::try_from(verifying_share_vec)
.map_err(|_| ExodusError::DeserializationError)?; .map_err(|_| ExodusError::DeserializationError)?;
let merkle_proof = let merkle_proof = BoundedVec::try_from(merkle_proof_vec)
BoundedVec::<ExodusHash, crate::MerkleProofMaxSize<T>>::try_from(merkle_proof_vec)
.map_err(|_| ExodusError::DeserializationError)?; .map_err(|_| ExodusError::DeserializationError)?;
Ok(Round6Package { context: self, verifying_share, merkle_proof }) Ok(Round6Package { context: self, verifying_share, merkle_proof })
} }
} }
impl<NC, T> PackageMetadata<NC> for ExodusPackage<NC, T> impl<NC, NI, B, T> ExodusMetadata<NI> for ExodusPackage<NC, NI, B, T>
where where
NC: Copy, NI: Default + Copy,
T: crate::pallet::Config, T: crate::pallet::Config,
{ {
fn context(&self) -> &PackageContext<NC> { fn copy_exodus_context(&self) -> ExodusContext<NI> {
match self { match self {
Self::NonceCommitment(package) => &package.context, Self::NonceCommitment(package) => (*package).exodus_context,
Self::GroupCommitment(package) => &package.context, Self::GroupCommitment(package) => (*package).exodus_context,
Self::SignatureShare(package) => &package.context, Self::SignatureShare(package) => (*package).exodus_context,
}
}
fn get_exodus_context(&self) -> &ExodusContext<NI> {
match self {
Self::NonceCommitment(package) => &package.exodus_context,
Self::GroupCommitment(package) => &package.exodus_context,
Self::SignatureShare(package) => &package.exodus_context,
} }
} }
} }
impl<NC, T> PackageMetadata<NC> for DkgPackage<NC, T> impl<NC, NI, B, T> PackageMetadata<NC, B> for ExodusPackage<NC, NI, B, T>
where where
NI: Default,
NC: Copy, NC: Copy,
B: Copy,
T: crate::pallet::Config, T: crate::pallet::Config,
{ {
fn context(&self) -> &PackageContext<NC> { fn package_context(&self) -> &PackageContext<NC, B> {
match self {
Self::NonceCommitment(package) => &package.package_context,
Self::GroupCommitment(package) => &package.package_context,
Self::SignatureShare(package) => &package.package_context,
}
}
}
impl<NC, B, T> PackageMetadata<NC, B> for DkgPackage<NC, B, T>
where
NC: Copy,
B: Copy,
T: crate::pallet::Config,
{
fn package_context(&self) -> &PackageContext<NC, B> {
match self { match self {
Self::Round0(package) => &package.context, Self::Round0(package) => &package.context,
Self::Round1(package) => &package.context, Self::Round1(package) => &package.context,

View File

@ -1,28 +1,33 @@
use scale_info::TypeInfo; use scale_info::TypeInfo;
use codec::{Encode, Decode}; use codec::{Encode, Decode};
use frame_support::{BoundedVec, traits::ConstU32}; use frame_support::{BoundedVec, traits::ConstU32};
use sp_runtime::{RuntimeDebug, traits::UniqueSaturatedInto}; use sp_runtime::{
RuntimeDebug,
traits::{AtLeast32BitUnsigned, UniqueSaturatedInto},
};
use crate::{BalanceOf, Config, ExodusContext};
use super::ExodusRequestType;
type ExodusNonceT = BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>; type ExodusNonceT = BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>;
type ExodusSignatureT = BoundedVec<u8, ConstU32<{ crate::SCALAR_MAX_BYTES }>>; type ExodusSignatureT = BoundedVec<u8, ConstU32<{ crate::SCALAR_MAX_BYTES }>>;
type Type<NetworkId, Balance> = super::ExodusRequestType<NetworkId, Balance>;
#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, RuntimeDebug)] #[derive(Encode, Decode, Clone, PartialEq, TypeInfo, RuntimeDebug)]
pub struct ExodusSignedMessage<NetworkId: Default, Balance: Default> { #[scale_info(skip_type_params(T))]
pub struct ExodusSignedMessage<T: Config> {
pub nonce: ExodusNonceT, pub nonce: ExodusNonceT,
pub signature: ExodusSignatureT, pub signature: ExodusSignatureT,
pub r#type: Type<NetworkId, Balance>, pub r#type: ExodusRequestType<T>,
} }
impl<NetworkId, Balance> ExodusSignedMessage<NetworkId, Balance> impl<T: Config> ExodusSignedMessage<T>
where where
NetworkId: UniqueSaturatedInto<u64> + Copy + Default, BalanceOf<T>: UniqueSaturatedInto<u128> + Copy + Default,
Balance: UniqueSaturatedInto<u128> + Copy + Default,
{ {
pub fn new<E, S>( pub fn new<E, S>(
nonce_iter: E, nonce_iter: E,
signature_iter: S, signature_iter: S,
r#type: Type<NetworkId, Balance> r#type: ExodusRequestType<T>,
) -> Option<Self> ) -> Option<Self>
where where
E: Iterator<Item = u8> + ExactSizeIterator, E: Iterator<Item = u8> + ExactSizeIterator,
@ -37,10 +42,24 @@ where
Some(Self { nonce, signature, r#type }) Some(Self { nonce, signature, r#type })
} }
pub fn get_message<'a>( pub fn get_message<'a, NetworkId: Default + Copy + AtLeast32BitUnsigned>(
&self, &self,
message_buffer: &'a mut [u8; crate::MAX_MESSAGE_SIZE as usize], message_buffer: &'a mut [u8; crate::MAX_MESSAGE_SIZE as usize],
exodus_context: ExodusContext<NetworkId>,
) -> Option<&'a [u8]> { ) -> Option<&'a [u8]> {
self.r#type.get_message(message_buffer) self.r#type.get_message(message_buffer, exodus_context)
}
pub fn is_evm_rotation(&self) -> bool {
self.r#type.is_evm_rotation()
}
pub fn is_utxo_rotation(&self) -> bool {
self.r#type.is_utxo_rotation()
}
pub fn is_bridge_out(&self) -> bool {
self.r#type.is_bridge_out()
}
pub fn is_evm_governance(&self) -> bool {
self.r#type.is_evm_governance()
} }
} }

View File

@ -1,16 +1,19 @@
use sp_runtime::traits::UniqueSaturatedInto;
use crate::{AuthIndex, ExodusSession, DkgIndex, RoastSession}; use crate::{AuthIndex, ExodusSession, DkgIndex, RoastSession};
pub enum ExodusOk<NetworkCurve> { pub enum ExodusOk<NetworkCurve, NetworkId> {
ExodusRequestsFinished(AuthIndex, NetworkCurve, u32, u32),
ExodusEmptyRequests(AuthIndex, NetworkCurve), ExodusEmptyRequests(AuthIndex, NetworkCurve),
ExodusWaitingState(AuthIndex, NetworkCurve), ExodusWaitingState(AuthIndex, NetworkCurve),
ExodusNonceCommitted(ExodusSession, RoastSession, AuthIndex, NetworkCurve), ExodusNonceCommitted(ExodusSession, NetworkId, RoastSession, AuthIndex, NetworkCurve),
ExodusGroupCommitted(ExodusSession, RoastSession, AuthIndex, NetworkCurve), ExodusGroupCommitted(ExodusSession, NetworkId, RoastSession, AuthIndex, NetworkCurve),
ExodusSignedPartially(ExodusSession, RoastSession, AuthIndex, NetworkCurve), ExodusSignedPartially(ExodusSession, NetworkId, RoastSession, AuthIndex, NetworkCurve),
ExodusVerifyingShareMissed(AuthIndex, DkgIndex, NetworkCurve), ExodusVerifyingShareMissed(AuthIndex, DkgIndex, NetworkCurve),
DkgNotPartOfRound(u8, AuthIndex, NetworkCurve), DkgNotPartOfRound(u8, AuthIndex, NetworkCurve),
DkgRoundRegistered(u8, AuthIndex, NetworkCurve), DkgRoundRegistered(u8, AuthIndex, NetworkCurve),
DkgPreviousPurged(AuthIndex, NetworkCurve), DkgPreviousPurged(AuthIndex, NetworkCurve, u32),
DkgLocalStoragePurged(AuthIndex, NetworkCurve, DkgIndex),
DkgRoundsCleared(AuthIndex, NetworkCurve), DkgRoundsCleared(AuthIndex, NetworkCurve),
DkgRoundAlreadyPassed(u8, AuthIndex, NetworkCurve), DkgRoundAlreadyPassed(u8, AuthIndex, NetworkCurve),
DkgRoundWaitingPeriod(u8, AuthIndex, NetworkCurve), DkgRoundWaitingPeriod(u8, AuthIndex, NetworkCurve),
@ -18,27 +21,41 @@ pub enum ExodusOk<NetworkCurve> {
DkgRoundMaxSignersNarrowed(u8, AuthIndex, AuthIndex, AuthIndex, NetworkCurve), DkgRoundMaxSignersNarrowed(u8, AuthIndex, AuthIndex, AuthIndex, NetworkCurve),
} }
impl<NetworkCurve> core::fmt::Display for ExodusOk<NetworkCurve> impl<NetworkCurve, NetworkId> core::fmt::Debug for ExodusOk<NetworkCurve, NetworkId>
where where
NetworkCurve: core::fmt::Display NetworkCurve: core::fmt::Display,
NetworkId: UniqueSaturatedInto<u64> + Copy,
{ {
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 {
ExodusOk::ExodusEmptyRequests(auth_index, curve) => write!(fmt, "No EXODUS requests found by authority #{auth_index} for curve {curve}."), ExodusOk::ExodusRequestsFinished(auth_index, curve, success_count, failed_count) => {
ExodusOk::ExodusWaitingState(auth_index, curve) => write!(fmt, "Waiting for EXODUS requests to be processed by authority #{auth_index} for curve {curve}."), write!(fmt, "EXODUS requests processing round completed by authority #{auth_index} for curve {curve}: {success_count} tasks succeeded, {failed_count} tasks failed.")
ExodusOk::ExodusNonceCommitted(exodus, roast, auth_index, curve) => write!(fmt, "Nonce committed for EXODUS {exodus:?} on ROAST session {roast:?} by authority #{auth_index} for curve {curve}."), }
ExodusOk::ExodusGroupCommitted(exodus, roast, auth_index, curve) => write!(fmt, "Aggregated nonce and binding factor proof committed for EXODUS {exodus:?} on ROAST session {roast:?} by authority #{auth_index} for curve {curve}."), ExodusOk::ExodusEmptyRequests(auth_index, curve) => write!(fmt, "No EXODUS requests found for authority #{auth_index} on curve {curve}."),
ExodusOk::ExodusSignedPartially(exodus, roast, auth_index, curve) => write!(fmt, "Partial signature created for EXODUS {exodus:?} on ROAST session {roast:?} by authority #{auth_index} for curve {curve}."), ExodusOk::ExodusWaitingState(auth_index, curve) => write!(fmt, "Waiting for EXODUS requests to be processed by authority #{auth_index} on curve {curve}."),
ExodusOk::ExodusVerifyingShareMissed(auth_index, dkg_index, curve) => write!(fmt, "EXODUS Round no verifying share for authority #{auth_index} based on DKG session #{dkg_index} for curve {curve}."), ExodusOk::ExodusNonceCommitted(exodus, network_id, roast, auth_index, curve) => {
let network_id_u64: u64 = (*network_id).unique_saturated_into();
write!(fmt, "Nonce committed for EXODUS {exodus:?} on ROAST session {roast:?} by authority #{auth_index} for curve {curve} on network #{network_id_u64}.")
},
ExodusOk::ExodusGroupCommitted(exodus, network_id, roast, auth_index, curve) => {
let network_id_u64: u64 = (*network_id).unique_saturated_into();
write!(fmt, "Aggregated nonce and binding factor proof committed for EXODUS {exodus:?} on ROAST session {roast:?} by authority #{auth_index} for curve {curve} on network #{network_id_u64}.")
},
ExodusOk::ExodusSignedPartially(exodus, network_id, roast, auth_index, curve) => {
let network_id_u64: u64 = (*network_id).unique_saturated_into();
write!(fmt, "Partial signature created for EXODUS {exodus:?} on ROAST session {roast:?} by authority #{auth_index} for curve {curve} on network #{network_id_u64}.")
},
ExodusOk::ExodusVerifyingShareMissed(auth_index, dkg_index, curve) => write!(fmt, "No verifying share found for authority #{auth_index} in EXODUS round based on DKG session #{dkg_index} for curve {curve}."),
ExodusOk::DkgNotPartOfRound(round, auth_index, curve) => write!(fmt, "DKG authority #{auth_index} for network curve {curve} is not part of Round{round}."), ExodusOk::DkgNotPartOfRound(round, auth_index, curve) => write!(fmt, "DKG authority #{auth_index} is not part of Round {round} for network curve {curve}."),
ExodusOk::DkgRoundRegistered(round, auth_index, curve) => write!(fmt, "DKG Round{round} registered by authority #{auth_index} for network curve {curve}."), ExodusOk::DkgRoundRegistered(round, auth_index, curve) => write!(fmt, "DKG Round {round} registered by authority #{auth_index} for network curve {curve}."),
ExodusOk::DkgPreviousPurged(auth_index, curve) => write!(fmt, "Storage prepared for new DKG session by authority #{auth_index} for network curve {curve}."), ExodusOk::DkgPreviousPurged(auth_index, curve, removed_count) => write!(fmt, "Storage prepared for a new DKG session by authority #{auth_index} for network curve {curve}. {removed_count} entries removed."),
ExodusOk::DkgLocalStoragePurged(auth_index, curve, dkg_index) => write!(fmt, "Storage for DKG session #{dkg_index} cleared by authority #{auth_index} for network curve {curve}."),
ExodusOk::DkgRoundsCleared(auth_index, curve) => write!(fmt, "DKG Rounds cleared from storage by authority #{auth_index} for network curve {curve}."), ExodusOk::DkgRoundsCleared(auth_index, curve) => write!(fmt, "DKG Rounds cleared from storage by authority #{auth_index} for network curve {curve}."),
ExodusOk::DkgRoundAlreadyPassed(round, auth_index, curve) => write!(fmt, "DKG Round{round} already done by authority #{auth_index} for network curve {curve}."), ExodusOk::DkgRoundAlreadyPassed(round, auth_index, curve) => write!(fmt, "DKG Round {round} already completed by authority #{auth_index} for network curve {curve}."),
ExodusOk::DkgRoundWaitingPeriod(round, auth_index, curve) => write!(fmt, "DKG Round{round} waiting to be applied by authority #{auth_index} for network curve {curve}."), ExodusOk::DkgRoundWaitingPeriod(round, auth_index, curve) => write!(fmt, "DKG Round {round} is waiting to be applied by authority #{auth_index} for network curve {curve}."),
ExodusOk::DkgRoundNothingStored(round, auth_index, curve) => write!(fmt, "DKG nothing stored from Round{round} by authority #{auth_index} for network curve {curve}."), ExodusOk::DkgRoundNothingStored(round, auth_index, curve) => write!(fmt, "No data stored for DKG Round {round} by authority #{auth_index} for network curve {curve}."),
ExodusOk::DkgRoundMaxSignersNarrowed(round, auth_index, prev_max, curr_max, curve) => write!(fmt, "DKG Round{round} narrowed max_signers from {prev_max} to {curr_max} by {auth_index} for network curve {curve}."), ExodusOk::DkgRoundMaxSignersNarrowed(round, auth_index, prev_max, curr_max, curve) => write!(fmt, "DKG Round {round} narrowed max_signers from {prev_max} to {curr_max} by authority #{auth_index} for network curve {curve}."),
} }
} }
} }

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-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! DATE: 2026-10-29, 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
@ -63,34 +63,30 @@ pub trait WeightInfo {
/// Weight functions for `ghost_exodus`. /// Weight functions for `ghost_exodus`.
impl WeightInfo for () { impl WeightInfo for () {
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0) /// Storage: `GhostNetworks::NetworkTypes` (r:1 w:0)
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::NetworkTypes` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:1) /// Storage: `GhostExodus::QualificationDkgState` (r:1 w:1)
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0) /// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1)
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::Round0Packages` (r:0 w:1) /// Storage: `GhostExodus::Round0Packages` (r:0 w:1)
/// Proof: `GhostExodus::Round0Packages` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::Round0Packages` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn register_round0_package() -> Weight { fn register_round0_package() -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `351` // Measured: `365`
// Estimated: `3816` // Estimated: `3830`
// Minimum execution time: 240_813_000 picoseconds. // Minimum execution time: 240_843_000 picoseconds.
Weight::from_parts(243_523_000, 0) Weight::from_parts(243_855_000, 0)
.saturating_add(Weight::from_parts(0, 3816)) .saturating_add(Weight::from_parts(0, 3830))
.saturating_add(RocksDbWeight::get().reads(4)) .saturating_add(RocksDbWeight::get().reads(4))
.saturating_add(RocksDbWeight::get().writes(3)) .saturating_add(RocksDbWeight::get().writes(2))
} }
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0) /// Storage: `GhostNetworks::NetworkTypes` (r:1 w:0)
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::NetworkTypes` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0) /// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0) /// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1)
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::Round0Packages` (r:1 w:0) /// Storage: `GhostExodus::Round0Packages` (r:1 w:0)
/// Proof: `GhostExodus::Round0Packages` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::Round0Packages` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::Round1Packages` (r:1 w:1) /// Storage: `GhostExodus::Round1Packages` (r:1 w:1)
@ -98,119 +94,109 @@ impl WeightInfo for () {
/// The range of component `c` is `[4, 300]`. /// The range of component `c` is `[4, 300]`.
fn register_round1_package(c: u32, ) -> Weight { fn register_round1_package(c: u32, ) -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `391 + c * (32 ±0)` // Measured: `405 + c * (32 ±0)`
// Estimated: `3856 + c * (32 ±0)` // Estimated: `3870 + c * (32 ±0)`
// Minimum execution time: 1_356_791_000 picoseconds. // Minimum execution time: 1_349_108_000 picoseconds.
Weight::from_parts(1_221_760_215, 0) Weight::from_parts(1_224_956_354, 0)
.saturating_add(Weight::from_parts(0, 3856)) .saturating_add(Weight::from_parts(0, 3870))
// Standard Error: 16_481 // Standard Error: 14_930
.saturating_add(Weight::from_parts(33_163_393, 0).saturating_mul(c.into())) .saturating_add(Weight::from_parts(33_204_477, 0).saturating_mul(c.into()))
.saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().reads(5))
.saturating_add(RocksDbWeight::get().writes(2)) .saturating_add(RocksDbWeight::get().writes(1))
.saturating_add(Weight::from_parts(0, 32).saturating_mul(c.into())) .saturating_add(Weight::from_parts(0, 32).saturating_mul(c.into()))
} }
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0) /// Storage: `GhostNetworks::NetworkTypes` (r:1 w:0)
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::NetworkTypes` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0) /// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0) /// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1)
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::Round2Packages` (r:1 w:1) /// Storage: `GhostExodus::Round2Packages` (r:1 w:1)
/// Proof: `GhostExodus::Round2Packages` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::Round2Packages` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// The range of component `c` is `[1, 37]`. /// The range of component `c` is `[1, 300]`.
fn register_encrypted_round2_packages(c: u32, ) -> Weight { fn register_encrypted_round2_packages(c: u32, ) -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `317 + c * (257 ±0)` // Measured: `332 + c * (32 ±0)`
// Estimated: `3783 + c * (257 ±0)` // Estimated: `3797 + c * (32 ±0)`
// Minimum execution time: 254_590_000 picoseconds. // Minimum execution time: 245_305_000 picoseconds.
Weight::from_parts(258_091_105, 0) Weight::from_parts(253_863_353, 0)
.saturating_add(Weight::from_parts(0, 3783)) .saturating_add(Weight::from_parts(0, 3797))
// Standard Error: 25_082 // Standard Error: 3_527
.saturating_add(Weight::from_parts(6_595_878, 0).saturating_mul(c.into())) .saturating_add(Weight::from_parts(810_623, 0).saturating_mul(c.into()))
.saturating_add(RocksDbWeight::get().reads(5)) .saturating_add(RocksDbWeight::get().reads(4))
.saturating_add(RocksDbWeight::get().writes(2)) .saturating_add(RocksDbWeight::get().writes(1))
.saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into())) .saturating_add(Weight::from_parts(0, 32).saturating_mul(c.into()))
} }
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0) /// Storage: `GhostNetworks::NetworkTypes` (r:1 w:0)
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::NetworkTypes` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0) /// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0) /// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1)
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::Complaints` (r:1 w:1) /// Storage: `GhostExodus::Complaints` (r:1 w:1)
/// Proof: `GhostExodus::Complaints` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::Complaints` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// The range of component `c` is `[1, 37]`. /// The range of component `c` is `[1, 300]`.
fn register_round3_complaints(c: u32, ) -> Weight { fn register_round3_complaints(c: u32, ) -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `317 + c * (257 ±0)` // Measured: `332 + c * (32 ±0)`
// Estimated: `3783 + c * (257 ±0)` // Estimated: `3797 + c * (32 ±0)`
// Minimum execution time: 240_888_000 picoseconds. // Minimum execution time: 238_391_000 picoseconds.
Weight::from_parts(245_480_975, 0) Weight::from_parts(238_937_380, 0)
.saturating_add(Weight::from_parts(0, 3783)) .saturating_add(Weight::from_parts(0, 3797))
// Standard Error: 13_733 // Standard Error: 1_789
.saturating_add(Weight::from_parts(371_161, 0).saturating_mul(c.into())) .saturating_add(Weight::from_parts(39_316, 0).saturating_mul(c.into()))
.saturating_add(RocksDbWeight::get().reads(5)) .saturating_add(RocksDbWeight::get().reads(4))
.saturating_add(RocksDbWeight::get().writes(2)) .saturating_add(RocksDbWeight::get().writes(1))
.saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into())) .saturating_add(Weight::from_parts(0, 32).saturating_mul(c.into()))
} }
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0) /// Storage: `GhostNetworks::NetworkTypes` (r:1 w:0)
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::NetworkTypes` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0) /// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0) /// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1)
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::Justifications` (r:1 w:1) /// Storage: `GhostExodus::Justifications` (r:1 w:1)
/// Proof: `GhostExodus::Justifications` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::Justifications` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// The range of component `c` is `[1, 37]`. /// The range of component `c` is `[1, 300]`.
fn register_round4_justifications(c: u32, ) -> Weight { fn register_round4_justifications(c: u32, ) -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `317 + c * (257 ±0)` // Measured: `332 + c * (32 ±0)`
// Estimated: `3783 + c * (257 ±0)` // Estimated: `3797 + c * (32 ±0)`
// Minimum execution time: 251_521_000 picoseconds. // Minimum execution time: 245_916_000 picoseconds.
Weight::from_parts(253_053_636, 0) Weight::from_parts(251_070_206, 0)
.saturating_add(Weight::from_parts(0, 3783)) .saturating_add(Weight::from_parts(0, 3797))
// Standard Error: 14_691 // Standard Error: 2_152
.saturating_add(Weight::from_parts(4_101_872, 0).saturating_mul(c.into())) .saturating_add(Weight::from_parts(508_508, 0).saturating_mul(c.into()))
.saturating_add(RocksDbWeight::get().reads(5)) .saturating_add(RocksDbWeight::get().reads(4))
.saturating_add(RocksDbWeight::get().writes(2)) .saturating_add(RocksDbWeight::get().writes(1))
.saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into())) .saturating_add(Weight::from_parts(0, 32).saturating_mul(c.into()))
} }
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0) /// Storage: `GhostNetworks::NetworkTypes` (r:1 w:0)
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::NetworkTypes` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0) /// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0) /// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1)
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::Verifications` (r:1 w:1) /// Storage: `GhostExodus::Verifications` (r:1 w:1)
/// Proof: `GhostExodus::Verifications` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::Verifications` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::VerifyingKeyConsensus` (r:1 w:1) /// Storage: `GhostExodus::VerifyingKeyConsensus` (r:1 w:1)
/// Proof: `GhostExodus::VerifyingKeyConsensus` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::VerifyingKeyConsensus` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn register_round5_verifying_package() -> Weight { fn register_round5_verifying_package() -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `448` // Measured: `462`
// Estimated: `3913` // Estimated: `3927`
// Minimum execution time: 265_187_000 picoseconds. // Minimum execution time: 271_654_000 picoseconds.
Weight::from_parts(269_124_000, 0) Weight::from_parts(286_394_000, 0)
.saturating_add(Weight::from_parts(0, 3913)) .saturating_add(Weight::from_parts(0, 3927))
.saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().reads(5))
.saturating_add(RocksDbWeight::get().writes(3)) .saturating_add(RocksDbWeight::get().writes(2))
} }
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0) /// Storage: `GhostNetworks::NetworkTypes` (r:1 w:0)
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::NetworkTypes` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0) /// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0) /// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1)
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::VerifyingKeyConsensus` (r:1 w:0) /// Storage: `GhostExodus::VerifyingKeyConsensus` (r:1 w:0)
/// Proof: `GhostExodus::VerifyingKeyConsensus` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::VerifyingKeyConsensus` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::VerifyingShares` (r:1 w:1) /// Storage: `GhostExodus::VerifyingShares` (r:1 w:1)
@ -221,18 +207,18 @@ impl WeightInfo for () {
fn register_round6_public_share_package(c: u32, ) -> Weight { fn register_round6_public_share_package(c: u32, ) -> Weight {
// 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: `1698 + c * (1019 ±27)`
// Minimum execution time: 284_558_000 picoseconds. // Minimum execution time: 283_863_000 picoseconds.
Weight::from_parts(284_117_484, 0) Weight::from_parts(286_691_978, 0)
.saturating_add(Weight::from_parts(0, 1551)) .saturating_add(Weight::from_parts(0, 1698))
// Standard Error: 74_361 // Standard Error: 85_685
.saturating_add(Weight::from_parts(3_551_271, 0).saturating_mul(c.into())) .saturating_add(Weight::from_parts(2_854_037, 0).saturating_mul(c.into()))
.saturating_add(RocksDbWeight::get().reads(7)) .saturating_add(RocksDbWeight::get().reads(6))
.saturating_add(RocksDbWeight::get().writes(3)) .saturating_add(RocksDbWeight::get().writes(2))
.saturating_add(Weight::from_parts(0, 1019).saturating_mul(c.into())) .saturating_add(Weight::from_parts(0, 1019).saturating_mul(c.into()))
} }
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0) /// Storage: `GhostNetworks::NetworkTypes` (r:1 w:0)
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::NetworkTypes` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0) /// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ActiveDkgAuthorities` (r:1 w:0) /// Storage: `GhostExodus::ActiveDkgAuthorities` (r:1 w:0)
@ -243,24 +229,22 @@ impl WeightInfo for () {
/// Proof: `GhostExodus::ExodusRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::ExodusRequests` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::RoastSessions` (r:1 w:1) /// Storage: `GhostExodus::RoastSessions` (r:1 w:1)
/// Proof: `GhostExodus::RoastSessions` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::RoastSessions` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1)
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::RoastSessionStates` (r:1 w:1) /// Storage: `GhostExodus::RoastSessionStates` (r:1 w:1)
/// Proof: `GhostExodus::RoastSessionStates` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::RoastSessionStates` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::NonceCommitments` (r:0 w:1) /// Storage: `GhostExodus::NonceCommitments` (r:0 w:1)
/// 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: `627` // Measured: `633`
// Estimated: `4092` // Estimated: `4098`
// Minimum execution time: 315_066_000 picoseconds. // Minimum execution time: 369_666_000 picoseconds.
Weight::from_parts(318_561_000, 0) Weight::from_parts(373_163_000, 0)
.saturating_add(Weight::from_parts(0, 4092)) .saturating_add(Weight::from_parts(0, 4098))
.saturating_add(RocksDbWeight::get().reads(8)) .saturating_add(RocksDbWeight::get().reads(8))
.saturating_add(RocksDbWeight::get().writes(5)) .saturating_add(RocksDbWeight::get().writes(4))
} }
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0) /// Storage: `GhostNetworks::NetworkTypes` (r:1 w:0)
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::NetworkTypes` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0) /// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ActiveDkgAuthorities` (r:1 w:0) /// Storage: `GhostExodus::ActiveDkgAuthorities` (r:1 w:0)
@ -269,8 +253,6 @@ impl WeightInfo for () {
/// Proof: `GhostExodus::ActiveAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::ActiveAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::RoastSessions` (r:1 w:0) /// Storage: `GhostExodus::RoastSessions` (r:1 w:0)
/// Proof: `GhostExodus::RoastSessions` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::RoastSessions` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1)
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusRequests` (r:1 w:0) /// Storage: `GhostExodus::ExodusRequests` (r:1 w:0)
/// Proof: `GhostExodus::ExodusRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::ExodusRequests` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::GroupCommitters` (r:1 w:1) /// Storage: `GhostExodus::GroupCommitters` (r:1 w:1)
@ -281,16 +263,16 @@ 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: `992` // Measured: `1032`
// Estimated: `4457` // Estimated: `4495`
// Minimum execution time: 338_644_000 picoseconds. // Minimum execution time: 339_569_000 picoseconds.
Weight::from_parts(343_954_000, 0) Weight::from_parts(341_788_000, 0)
.saturating_add(Weight::from_parts(0, 4457)) .saturating_add(Weight::from_parts(0, 4495))
.saturating_add(RocksDbWeight::get().reads(10)) .saturating_add(RocksDbWeight::get().reads(9))
.saturating_add(RocksDbWeight::get().writes(4)) .saturating_add(RocksDbWeight::get().writes(3))
} }
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0) /// Storage: `GhostNetworks::NetworkTypes` (r:1 w:0)
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::NetworkTypes` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0) /// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ActiveDkgAuthorities` (r:1 w:0) /// Storage: `GhostExodus::ActiveDkgAuthorities` (r:1 w:0)
@ -299,8 +281,6 @@ impl WeightInfo for () {
/// Proof: `GhostExodus::ActiveAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::ActiveAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::RoastSessions` (r:1 w:1) /// Storage: `GhostExodus::RoastSessions` (r:1 w:1)
/// Proof: `GhostExodus::RoastSessions` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::RoastSessions` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1)
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::GroupCommitmentsConsensus` (r:1 w:1) /// Storage: `GhostExodus::GroupCommitmentsConsensus` (r:1 w:1)
/// Proof: `GhostExodus::GroupCommitmentsConsensus` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::GroupCommitmentsConsensus` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusRequests` (r:1 w:1) /// Storage: `GhostExodus::ExodusRequests` (r:1 w:1)
@ -323,13 +303,13 @@ impl WeightInfo for () {
/// Proof: `GhostExodus::CounterForExodusRequests` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// 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: `11299` // Measured: `11337`
// Estimated: `14764` // Estimated: `14802`
// Minimum execution time: 3_968_938_000 picoseconds. // Minimum execution time: 4_046_065_000 picoseconds.
Weight::from_parts(3_975_088_000, 0) Weight::from_parts(4_059_064_000, 0)
.saturating_add(Weight::from_parts(0, 14764)) .saturating_add(Weight::from_parts(0, 14802))
.saturating_add(RocksDbWeight::get().reads(15)) .saturating_add(RocksDbWeight::get().reads(14))
.saturating_add(RocksDbWeight::get().writes(9)) .saturating_add(RocksDbWeight::get().writes(8))
} }
/// 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,17 +319,17 @@ impl WeightInfo for () {
/// 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: `GhostExodus::QualificationDkgState` (r:1 w:0) /// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
/// 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::CurrentExodusSessions` (r:1 w:1)
/// Proof: `GhostExodus::CurrentExodus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::CurrentExodusSessions` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusRequests` (r:1 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: `457`
// Estimated: `3913` // Estimated: `3922`
// Minimum execution time: 191_374_000 picoseconds. // Minimum execution time: 195_417_000 picoseconds.
Weight::from_parts(192_991_000, 0) Weight::from_parts(196_842_000, 0)
.saturating_add(Weight::from_parts(0, 3913)) .saturating_add(Weight::from_parts(0, 3922))
.saturating_add(RocksDbWeight::get().reads(7)) .saturating_add(RocksDbWeight::get().reads(7))
.saturating_add(RocksDbWeight::get().writes(5)) .saturating_add(RocksDbWeight::get().writes(5))
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ghost-governance" name = "ghost-governance"
version = "0.3.10" version = "0.3.11"
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

View File

@ -79,14 +79,21 @@ fn claim_happy_path() {
let users = vec![user_1.clone(), user_2.clone(), user_3.clone()]; let users = vec![user_1.clone(), user_2.clone(), user_3.clone()];
let max_index = 2u8; let max_index = 2u8;
let tmp_package_probe = ClaimPackage::<Test> {
shares: user_1.allowed_shares,
merkle_proof: Default::default(),
token_id: user_1.token_id,
index: 0u32,
};
let size_hint = tmp_package_probe.get_preimage(&user_1.evm_address, network_id).as_ref().len();
let raw_values_complex = vec![(0u8, &user_1), (1u8, &user_2), (2u8, &user_3)]; let raw_values_complex = vec![(0u8, &user_1), (1u8, &user_2), (2u8, &user_3)];
let merkle_tree = generate_tree::<SubstrateKeccakHasher, u8, _, _, _>( let merkle_tree = generate_tree::<SubstrateKeccakHasher, u8, _, _, ()>(
max_index, max_index,
size_hint,
raw_values_complex, raw_values_complex,
|item: (u8, &NewTestUser)| -> Result<(usize, Vec<u8>), ()> { |(index, u), buffer| {
let (index, u) = item;
let tmp_package = ClaimPackage::<Test> { let tmp_package = ClaimPackage::<Test> {
shares: u.allowed_shares, shares: u.allowed_shares,
merkle_proof: Default::default(), merkle_proof: Default::default(),
@ -94,9 +101,12 @@ fn claim_happy_path() {
index: index as u32 index: index as u32
}; };
Ok((index as usize, tmp_package.get_preimage(&u.evm_address, network_id).to_vec())) let preimage_bytes = tmp_package.get_preimage(&u.evm_address, network_id);
buffer.extend_from_slice(preimage_bytes.as_ref());
Ok(index as usize)
} }
).unwrap(); ).ok().unwrap();
let merkle_root = *merkle_tree.last().unwrap(); let merkle_root = *merkle_tree.last().unwrap();
@ -116,7 +126,9 @@ fn claim_happy_path() {
activated_shares: 0, activated_shares: 0,
}); });
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)
.ok()
.unwrap();
let claim_package = ClaimPackage::<Test> { let claim_package = ClaimPackage::<Test> {
shares: current_user.allowed_shares, shares: current_user.allowed_shares,
@ -205,21 +217,33 @@ fn partial_claims_happy_path() {
let max_index = 0u8; let max_index = 0u8;
let raw_values_complex = vec![(0u8, &current_user)]; let raw_values_complex = vec![(0u8, &current_user)];
let merkle_tree = generate_tree::<SubstrateKeccakHasher, u8, _, _, _>( let tmp_package_probe = ClaimPackage::<Test> {
shares: current_user.allowed_shares,
merkle_proof: Default::default(),
token_id: current_user.token_id,
index: 0u32,
};
let size_hint = tmp_package_probe.get_preimage(&current_user.evm_address, network_id).as_ref().len();
let merkle_tree = generate_tree::<SubstrateKeccakHasher, u8, _, _, ()>(
max_index, max_index,
size_hint,
raw_values_complex, raw_values_complex,
|item: (u8, &NewTestUser)| -> Result<(usize, Vec<u8>), ()> { |(index, u), buffer| {
let (index, u) = item;
let tmp_package = ClaimPackage::<Test> { 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,
index: index as u32 index: index as u32
}; };
Ok((index as usize, tmp_package.get_preimage(&u.evm_address, network_id).to_vec()))
let preimage_bytes = tmp_package.get_preimage(&u.evm_address, network_id);
buffer.extend_from_slice(preimage_bytes.as_ref());
Ok(index as usize)
} }
).unwrap(); ).ok().unwrap();
let total_shares = 1_000_000; let total_shares = 1_000_000;
let merkle_root = *merkle_tree.last().unwrap(); let merkle_root = *merkle_tree.last().unwrap();
@ -240,7 +264,10 @@ fn partial_claims_happy_path() {
setup_governor(&substrate_account_b); setup_governor(&substrate_account_b);
setup_governor(&substrate_account_c); setup_governor(&substrate_account_c);
let proof = generate_proof::<SubstrateKeccakHasher, u8>(&merkle_tree, max_index, 0u8); let proof = generate_proof::<SubstrateKeccakHasher, u8, ()>(&merkle_tree, max_index, 0u8)
.ok()
.unwrap();
let claim_package = ClaimPackage { let claim_package = ClaimPackage {
shares: current_user.allowed_shares, shares: current_user.allowed_shares,
merkle_proof: BoundedVec::try_from(proof).unwrap(), merkle_proof: BoundedVec::try_from(proof).unwrap(),

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-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! DATE: 2026-10-29, 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
@ -67,11 +67,11 @@ impl WeightInfo for () {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `283` // Measured: `283`
// Estimated: `3748` // Estimated: `3748`
// Minimum execution time: 229_254_000 picoseconds. // Minimum execution time: 224_852_000 picoseconds.
Weight::from_parts(229_992_093, 0) Weight::from_parts(225_347_733, 0)
.saturating_add(Weight::from_parts(0, 3748)) .saturating_add(Weight::from_parts(0, 3748))
// Standard Error: 25_680 // Standard Error: 21_234
.saturating_add(Weight::from_parts(1_874_433, 0).saturating_mul(p.into())) .saturating_add(Weight::from_parts(1_581_800, 0).saturating_mul(p.into()))
.saturating_add(RocksDbWeight::get().reads(5)) .saturating_add(RocksDbWeight::get().reads(5))
.saturating_add(RocksDbWeight::get().writes(5)) .saturating_add(RocksDbWeight::get().writes(5))
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ghost-helpers" name = "ghost-helpers"
version = "0.0.11" version = "0.0.16"
description = "Cryptographic utility suite for custom runtimes: optimized Bitmaps, UTXO parsing, Merkle Tree proofs, and Hash Chain components." description = "Cryptographic utility suite for custom runtimes: optimized Bitmaps, UTXO parsing, Merkle Tree proofs, and Hash Chain components."
license.workspace = true license.workspace = true
authors.workspace = true authors.workspace = true

View File

@ -2,8 +2,8 @@ use codec::{Decode, Encode};
use num_traits::PrimInt; use num_traits::PrimInt;
use scale_info::TypeInfo; use scale_info::TypeInfo;
use sp_runtime::traits::UniqueSaturatedInto;
use sp_arithmetic::traits::AtLeast8BitUnsigned; use sp_arithmetic::traits::AtLeast8BitUnsigned;
use sp_runtime::{traits::UniqueSaturatedInto, RuntimeDebug};
use sp_std::{cmp::{min, max}, marker::PhantomData, vec, vec::Vec}; use sp_std::{cmp::{min, max}, marker::PhantomData, vec, vec::Vec};
use ghost_traits::bounded_bitmap as traits; use ghost_traits::bounded_bitmap as traits;
@ -33,7 +33,7 @@ pub fn validate_bitmap_sizes<B: traits::BoundedBitmapMetadata>(authorities: u32)
); );
} }
#[derive(Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] #[derive(Encode, Decode, TypeInfo, MaxEncodedLen)]
#[scale_info(skip_type_params(MaxChunks))] #[scale_info(skip_type_params(MaxChunks))]
pub struct BoundedBitmap<Inner, MaxChunks> pub struct BoundedBitmap<Inner, MaxChunks>
where where
@ -43,6 +43,19 @@ where
active_bits_count: u32, active_bits_count: u32,
} }
impl<Inner: core::fmt::Debug, MaxChunks> core::fmt::Debug for BoundedBitmap<Inner, MaxChunks>
where
MaxChunks: frame_support::traits::Get<u32>,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let slice: &[Inner] = &self.data;
f.debug_struct("BoundedBitmap")
.field("data", &slice)
.field("active_bits_count", &self.active_bits_count)
.finish()
}
}
impl<Inner, MaxChunks> BoundedBitmap<Inner, MaxChunks> impl<Inner, MaxChunks> BoundedBitmap<Inner, MaxChunks>
where where
MaxChunks: Get<u32>, MaxChunks: Get<u32>,
@ -54,6 +67,15 @@ where
Inner: BitAndAssign + BitOrAssign + BitXorAssign, Inner: BitAndAssign + BitOrAssign + BitXorAssign,
Inner: Shl<usize, Output = Inner> + Shr<usize, Output = Inner>, Inner: Shl<usize, Output = Inner> + Shr<usize, Output = Inner>,
{ {
pub fn inner_as_ref(&self) -> &[u8] {
let slice: &[Inner] = self.data.as_ref();
let byte_len = slice.len() * core::mem::size_of::<Inner>();
unsafe {
core::slice::from_raw_parts(slice.as_ptr() as *const u8, byte_len)
}
}
fn combine<F>(&self, rhs: &Self, op: F) -> Self fn combine<F>(&self, rhs: &Self, op: F) -> Self
where where
Inner: Copy, Inner: Copy,
@ -146,41 +168,6 @@ where
new_bitmap new_bitmap
} }
fn from_bitmask<Outer>(values: &Vec<Outer>) -> Self
where
Outer: AtLeast8BitUnsigned + PrimInt,
{
let value_bits = inner_bits::<Outer>();
let n = values.iter().enumerate().rev()
.find(|(_, &val)| val != Outer::zero())
.map(|(idx, &val)| {
let active_bits = value_bits - (val.leading_zeros() as usize);
idx.saturating_mul(value_bits).saturating_add(active_bits)
})
.unwrap_or(0);
let mut new_bitmap = Self::empty(n);
for (value_index, value) in values.iter().enumerate() {
for bit_index in 0..value_bits {
let bit_mask = Outer::one() << bit_index;
if (*value & bit_mask) == Outer::zero() {
continue;
}
let index = value_index
.saturating_mul(value_bits)
.saturating_add(bit_index);
if let None = new_bitmap.insert(index as u32) {
return new_bitmap;
}
}
}
new_bitmap
}
fn all_ones<N>(n: N) -> Self fn all_ones<N>(n: N) -> Self
where where
N: UniqueSaturatedInto<usize>, N: UniqueSaturatedInto<usize>,
@ -307,6 +294,35 @@ where
}) })
} }
fn count_ones_before<N>(&self, index: N) -> Option<N>
where
usize: UniqueSaturatedInto<N>,
N: UniqueSaturatedInto<usize>,
{
let index: usize = index.unique_saturated_into();
if index >= self.active_bits_count as usize { return None; }
let shift = inner_shift::<Inner>();
let element_index = index >> shift;
let bit_index = index & ((1 << shift) - 1);
let current_element = self.data.get(element_index)?;
let lower_bits_mask = (Inner::one() << bit_index) - Inner::one();
let bits_before = *current_element & lower_bits_mask;
self.data.get(0..element_index)?
.iter()
.try_fold(0usize, |acc, &element| {
acc.checked_add(element.count_ones() as usize)
})
.and_then(|popcount| {
popcount.checked_add(bits_before.count_ones() as usize)
})
.map(|total| total.unique_saturated_into())
}
fn count_ones<N>(&self) -> N fn count_ones<N>(&self) -> N
where where
u32: UniqueSaturatedInto<N>, u32: UniqueSaturatedInto<N>,
@ -425,6 +441,14 @@ where
self.highest_bit() self.highest_bit()
} }
fn count_ones_before<N>(&self, index: N) -> Option<N>
where
usize: UniqueSaturatedInto<N>,
N: UniqueSaturatedInto<usize>,
{
self.count_ones_before(index)
}
fn count_ones<N>(&self) -> N fn count_ones<N>(&self) -> N
where where
u32: UniqueSaturatedInto<N>, u32: UniqueSaturatedInto<N>,
@ -476,13 +500,6 @@ where
Self::empty_from(bitmap_ref) Self::empty_from(bitmap_ref)
} }
fn from_bitmask<Outer>(values_ref: &Vec<Outer>) -> Self
where
Outer: AtLeast8BitUnsigned + PrimInt,
{
Self::from_bitmask(values_ref)
}
fn from_vec<Outer>(values_ref: &Vec<Outer>) -> Self fn from_vec<Outer>(values_ref: &Vec<Outer>) -> Self
where where
Outer: AtLeast8BitUnsigned + TryInto<usize> + PartialEq + Copy, Outer: AtLeast8BitUnsigned + TryInto<usize> + PartialEq + Copy,
@ -1099,50 +1116,72 @@ mod tests {
} }
#[test] #[test]
fn test_from_bitmask_where_each_u8_is_a_bit() { fn test_count_ones_before_basic() {
let byte1 = 77u8; let w = <$inner_type>::BITS as usize;
let byte2 = 42u8; let idx = |x: usize| -> $inner_type {
let byte3 = 69u8; x.try_into().ok().expect("bit index fits inner type")
};
let raw_bits: Vec<u8> = vec![byte1, byte2, byte3]; let mut indices: Vec<$inner_type> = vec![
let bitmap = TestBitmap::from_bitmask(&raw_bits); 0u8.into(),
1u8.into(),
3u8.into()
];
let count_ones = raw_bits.iter().map(|b| b.count_ones()).sum::<u32>(); indices.extend([idx(w), idx(w + 1), idx(w + 3)] as [$inner_type; 3]);
let filled_bits = (raw_bits.len() as u32 - 1) * 8; let bitmap = TestBitmap::from_vec(&indices);
let expected_bits = filled_bits + (8 - byte3.leading_zeros());
assert_eq!(bitmap.active_bits_count(), expected_bits); assert_eq!(bitmap.count_ones_before(0), Some(0));
assert_eq!(bitmap.count_ones::<u32>(), count_ones); assert_eq!(bitmap.count_ones_before(1), Some(1));
assert_eq!(bitmap.count_ones_before(2), Some(2));
assert_eq!(bitmap.count_ones_before(3), Some(2));
assert_eq!(bitmap.count_ones_before(4), Some(3));
assert_eq!(bitmap.count_ones_before(w), Some(3));
// 77 - 10110010 assert_eq!(bitmap.count_ones_before(w + 1), Some(4));
assert!(bitmap.contains(0u32)); assert_eq!(bitmap.count_ones_before(w + 4), Some(6));
assert!(!bitmap.contains(1u32)); assert_eq!(bitmap.count_ones_before(2 * w), Some(6));
assert!(bitmap.contains(2u32)); }
assert!(bitmap.contains(3u32));
assert!(!bitmap.contains(4u32));
assert!(!bitmap.contains(5u32));
assert!(bitmap.contains(6u32));
assert!(!bitmap.contains(7u32));
// 42 - 01010100 #[test]
assert!(!bitmap.contains(8u32)); fn test_count_ones_before_crossing_elements() {
assert!(bitmap.contains(9u32)); let w = <$inner_type>::BITS as usize;
assert!(!bitmap.contains(10u32)); let idx = |x: usize| -> $inner_type {
assert!(bitmap.contains(11u32)); x.try_into().ok().expect("bit index fits inner type")
assert!(!bitmap.contains(12u32)); };
assert!(bitmap.contains(13u32));
assert!(!bitmap.contains(14u32));
assert!(!bitmap.contains(15u32));
// 69 - 10100010 let mut bitmap = TestBitmap::empty(2 * w + 1);
assert!(bitmap.contains(16u32)); for i in [0usize, 1, w, w + 1, w + 2] {
assert!(!bitmap.contains(17u32)); bitmap.insert(idx(i));
assert!(bitmap.contains(18u32)); }
assert!(!bitmap.contains(19u32));
assert!(!bitmap.contains(20u32)); let bits_per_element = core::mem::size_of::<$inner_type>() * 8;
assert!(!bitmap.contains(21u32));
assert!(bitmap.contains(22u32)); assert_eq!(bitmap.count_ones_before(bits_per_element), Some(2));
assert!(!bitmap.contains(23u32)); assert_eq!(bitmap.count_ones_before(bits_per_element + 1), Some(3));
assert_eq!(bitmap.count_ones_before(bits_per_element + 3), Some(5));
assert_eq!(bitmap.count_ones_before(2 * bits_per_element), Some(5));
assert_eq!(bitmap.count_ones_before(2 * bits_per_element + 1), None);
}
#[test]
fn test_count_ones_before_bounds_and_overflow() {
let w = <$inner_type>::BITS as usize;
let idx = |x: usize| -> $inner_type {
x.try_into().ok().expect("bit index fits inner type")
};
let mut bitmap = TestBitmap::empty(w);
for i in 0..w {
bitmap.insert(idx(i));
}
let bits_per_element = (core::mem::size_of::<$inner_type>() * 8) as u32;
assert_eq!(bitmap.count_ones_before(bits_per_element - 1), Some(bits_per_element - 1));
assert_eq!(bitmap.count_ones_before(bits_per_element), None);
assert_eq!(bitmap.count_ones_before(10000u32), None);
} }
} }
}; };

View File

@ -1,19 +1,5 @@
use ghost_traits::hashing::GhostHasher; use ghost_traits::hashing::GhostHasher;
fn combine_values<H, V>(left: V, right: V) -> H::Hash
where
H: GhostHasher,
V: AsRef<[u8]>,
{
let hash_len = H::hash_len();
let mut combined = sp_std::vec![0u8; hash_len * 2];
combined[..hash_len].copy_from_slice(left.as_ref());
combined[hash_len..].copy_from_slice(right.as_ref());
H::hash(&combined)
}
pub fn cumulative_hash<H, S>(raw_values: S) -> H::Hash pub fn cumulative_hash<H, S>(raw_values: S) -> H::Hash
where where
H: GhostHasher, H: GhostHasher,
@ -24,7 +10,7 @@ where
.into_iter() .into_iter()
.fold(H::empty(), |acc_hash, raw_value| { .fold(H::empty(), |acc_hash, raw_value| {
let hashed_value = H::hash(raw_value.as_ref()); let hashed_value = H::hash(raw_value.as_ref());
combine_values::<H, _>(acc_hash, hashed_value) H::combine_hashes(acc_hash, hashed_value)
}) })
} }
@ -47,7 +33,7 @@ where
iter.fold(current_hash, |acc_hash, raw_value| { iter.fold(current_hash, |acc_hash, raw_value| {
let next_hash = H::from_slice(raw_value.as_ref()); let next_hash = H::from_slice(raw_value.as_ref());
combine_values::<H, _>(acc_hash, next_hash) H::combine_hashes(acc_hash, next_hash)
}) })
} }
@ -94,45 +80,14 @@ where
mod tests { mod tests {
use super::*; use super::*;
use sp_core::H256; use sp_core::H256;
use sp_io::hashing::{blake2_256, sha2_256};
use sp_std::vec::Vec; use sp_std::vec::Vec;
pub struct TestBlake2Hasher; use crate::{
impl GhostHasher for TestBlake2Hasher { SubstrateBlake2Hasher, SubstrateKeccakHasher, UtxoSha2Hasher,
type Hash = H256; };
type HashBytes = [u8; 32];
fn hash(data: &[u8]) -> Self::Hash {
H256::from(blake2_256(data))
}
fn empty() -> Self::Hash {
H256::zero()
}
fn hash_len() -> usize {
H256::len_bytes()
}
}
pub struct TestSha2Hasher;
impl GhostHasher for TestSha2Hasher {
type Hash = H256;
type HashBytes = [u8; 32];
fn hash(data: &[u8]) -> Self::Hash {
let first_pass: [u8; 32] = sha2_256(data);
let second_pass: [u8; 32] = sha2_256(&first_pass);
H256::from(second_pass)
}
fn hash_len() -> usize {
H256::len_bytes()
}
fn empty() -> Self::Hash {
H256::zero()
}
}
fn mock_sequential_hash(header: &[u8; 80]) -> H256 { fn mock_sequential_hash(header: &[u8; 80]) -> H256 {
sequential_hash::<TestSha2Hasher, _>(core::iter::once(header)) sequential_hash::<UtxoSha2Hasher, _>(core::iter::once(header))
} }
fn create_header(prev_hash: [u8; 32]) -> [u8; 80] { fn create_header(prev_hash: [u8; 32]) -> [u8; 80] {
@ -141,10 +96,15 @@ mod tests {
header header
} }
macro_rules! run_hasher_tests {
($hasher_type:ty, $mod_name:ident) => {
mod $mod_name {
use super::*;
#[test] #[test]
fn test_generate_empty_values() { fn test_generate_empty_values() {
let empty_list: Vec<Vec<u8>> = sp_std::vec![]; let empty_list: Vec<Vec<u8>> = sp_std::vec![];
let result = cumulative_hash::<TestBlake2Hasher, _>(empty_list); let result = cumulative_hash::<$hasher_type, _>(empty_list);
assert_eq!(H256::zero(), result); assert_eq!(H256::zero(), result);
} }
@ -154,9 +114,9 @@ mod tests {
let dataset_2 = sp_std::vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]]; let dataset_2 = sp_std::vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]];
let dataset_3 = sp_std::vec![vec![9, 2, 3], vec![4, 5, 6], vec![7, 8, 9]]; let dataset_3 = sp_std::vec![vec![9, 2, 3], vec![4, 5, 6], vec![7, 8, 9]];
let hash_1 = cumulative_hash::<TestBlake2Hasher, _>(dataset_1); let hash_1 = cumulative_hash::<$hasher_type, _>(dataset_1);
let hash_2 = cumulative_hash::<TestBlake2Hasher, _>(dataset_2); let hash_2 = cumulative_hash::<$hasher_type, _>(dataset_2);
let hash_3 = cumulative_hash::<TestBlake2Hasher, _>(dataset_3); let hash_3 = cumulative_hash::<$hasher_type, _>(dataset_3);
assert_eq!(hash_1, hash_2); assert_eq!(hash_1, hash_2);
assert_ne!(hash_1, hash_3); assert_ne!(hash_1, hash_3);
@ -168,8 +128,8 @@ mod tests {
let dataset_forward = sp_std::vec![sp_std::vec![1, 1, 1], sp_std::vec![2, 2, 2]]; let dataset_forward = sp_std::vec![sp_std::vec![1, 1, 1], sp_std::vec![2, 2, 2]];
let dataset_reversed = sp_std::vec![sp_std::vec![2, 2, 2], sp_std::vec![1, 1, 1]]; let dataset_reversed = sp_std::vec![sp_std::vec![2, 2, 2], sp_std::vec![1, 1, 1]];
let hash_forward = cumulative_hash::<TestBlake2Hasher, _>(dataset_forward); let hash_forward = cumulative_hash::<$hasher_type, _>(dataset_forward);
let hash_reversed = cumulative_hash::<TestBlake2Hasher, _>(dataset_reversed); let hash_reversed = cumulative_hash::<$hasher_type, _>(dataset_reversed);
assert_ne!(hash_forward, hash_reversed); assert_ne!(hash_forward, hash_reversed);
} }
@ -177,22 +137,22 @@ mod tests {
#[test] #[test]
fn test_cumulative_hash_with_slices() { fn test_cumulative_hash_with_slices() {
let slice_data: &[&[u8]] = &[&[1, 2, 3], &[4, 5, 6]]; let slice_data: &[&[u8]] = &[&[1, 2, 3], &[4, 5, 6]];
let hash_from_slice = cumulative_hash::<TestBlake2Hasher, _>(slice_data.iter()); let hash_from_slice = cumulative_hash::<$hasher_type, _>(slice_data.iter());
assert_ne!(hash_from_slice, H256::zero()); assert_ne!(hash_from_slice, H256::zero());
} }
#[test] #[test]
fn test_sequential_hash_empty() { fn test_sequential_hash_empty() {
let empty_list: Vec<Vec<u8>> = sp_std::vec![]; let empty_list: Vec<Vec<u8>> = sp_std::vec![];
let result = sequential_hash::<TestBlake2Hasher, _>(empty_list); let result = sequential_hash::<$hasher_type, _>(empty_list);
assert_eq!(H256::zero(), result); assert_eq!(H256::zero(), result);
} }
#[test] #[test]
fn test_sequential_hash_single_element() { fn test_sequential_hash_single_element() {
let single_item = sp_std::vec![sp_std::vec![1, 2, 3]]; let single_item = sp_std::vec![sp_std::vec![1, 2, 3]];
let result = sequential_hash::<TestBlake2Hasher, _>(single_item); let result = sequential_hash::<$hasher_type, _>(single_item);
let expected = TestBlake2Hasher::hash(&[1, 2, 3]); let expected = <$hasher_type>::hash(&[1, 2, 3]);
assert_eq!(result, expected); assert_eq!(result, expected);
} }
@ -202,9 +162,9 @@ mod tests {
let data_2 = sp_std::vec![sp_std::vec![1], sp_std::vec![2], sp_std::vec![3]]; let data_2 = sp_std::vec![sp_std::vec![1], sp_std::vec![2], sp_std::vec![3]];
let data_reversed = sp_std::vec![sp_std::vec![3], sp_std::vec![2], sp_std::vec![1]]; let data_reversed = sp_std::vec![sp_std::vec![3], sp_std::vec![2], sp_std::vec![1]];
let hash_1 = sequential_hash::<TestBlake2Hasher, _>(data_1); let hash_1 = sequential_hash::<$hasher_type, _>(data_1);
let hash_2 = sequential_hash::<TestBlake2Hasher, _>(data_2); let hash_2 = sequential_hash::<$hasher_type, _>(data_2);
let hash_reversed = sequential_hash::<TestBlake2Hasher, _>(data_reversed); let hash_reversed = sequential_hash::<$hasher_type, _>(data_reversed);
assert_eq!(hash_1, hash_2); assert_eq!(hash_1, hash_2);
assert_ne!(hash_1, hash_reversed); assert_ne!(hash_1, hash_reversed);
@ -213,17 +173,24 @@ mod tests {
#[test] #[test]
fn test_sequential_vs_cumulative_hash() { fn test_sequential_vs_cumulative_hash() {
let dataset = sp_std::vec![sp_std::vec![1, 2, 3], sp_std::vec![4, 5, 6]]; let dataset = sp_std::vec![sp_std::vec![1, 2, 3], sp_std::vec![4, 5, 6]];
let seq_hash = sequential_hash::<TestBlake2Hasher, _>(dataset.clone()); let seq_hash = sequential_hash::<$hasher_type, _>(dataset.clone());
let cum_hash = cumulative_hash::<TestBlake2Hasher, _>(dataset); let cum_hash = cumulative_hash::<$hasher_type, _>(dataset);
assert_ne!(seq_hash, cum_hash); assert_ne!(seq_hash, cum_hash);
} }
#[test] #[test]
fn test_sequential_hash_with_slices() { fn test_sequential_hash_with_slices() {
let slice_data: &[&[u8]] = &[&[10, 20], &[30, 40]]; let slice_data: &[&[u8]] = &[&[10, 20], &[30, 40]];
let result = sequential_hash::<TestBlake2Hasher, _>(slice_data.iter()); let result = sequential_hash::<$hasher_type, _>(slice_data.iter());
assert_ne!(result, H256::zero()); assert_ne!(result, H256::zero());
} }
}
}
}
run_hasher_tests!(SubstrateBlake2Hasher, blake2_hashing_tests);
run_hasher_tests!(SubstrateKeccakHasher, keccak_hashing_tests);
run_hasher_tests!(UtxoSha2Hasher, sha2_hashing_tests);
#[test] #[test]
fn test_verify_in_current_block_with_empty_ancestry() { fn test_verify_in_current_block_with_empty_ancestry() {
@ -234,7 +201,7 @@ mod tests {
trusted_bytes.reverse(); trusted_bytes.reverse();
let trusted_top_block_hash = H256::from(trusted_bytes); let trusted_top_block_hash = H256::from(trusted_bytes);
let result = verify_hash_ancestry::<TestSha2Hasher, &[[u8; 80]]>( let result = verify_hash_ancestry::<UtxoSha2Hasher, &[[u8; 80]]>(
&tx_header, &tx_header,
&[], &[],
trusted_top_block_hash, trusted_top_block_hash,
@ -256,7 +223,7 @@ mod tests {
let trusted_top_block_hash = H256::from(trusted_bytes); let trusted_top_block_hash = H256::from(trusted_bytes);
let ancestry = [header_101]; let ancestry = [header_101];
let result = verify_hash_ancestry::<TestSha2Hasher, &[[u8; 80]]>( let result = verify_hash_ancestry::<UtxoSha2Hasher, &[[u8; 80]]>(
&tx_header_100, &tx_header_100,
&ancestry, &ancestry,
trusted_top_block_hash, trusted_top_block_hash,
@ -284,7 +251,7 @@ mod tests {
let trusted_top_block_hash = H256::from(trusted_bytes); let trusted_top_block_hash = H256::from(trusted_bytes);
let ancestry = [header_101, header_102, header_103]; let ancestry = [header_101, header_102, header_103];
let result = verify_hash_ancestry::<TestSha2Hasher, &[[u8; 80]]>( let result = verify_hash_ancestry::<UtxoSha2Hasher, &[[u8; 80]]>(
&header_100, &header_100,
&ancestry, &ancestry,
trusted_top_block_hash, trusted_top_block_hash,
@ -305,7 +272,7 @@ mod tests {
let trusted_top_block_hash = H256::from(trusted_bytes); let trusted_top_block_hash = H256::from(trusted_bytes);
let ancestry = [broken_header_101]; let ancestry = [broken_header_101];
let result = verify_hash_ancestry::<TestSha2Hasher, &[[u8; 80]]>( let result = verify_hash_ancestry::<UtxoSha2Hasher, &[[u8; 80]]>(
&header_100, &header_100,
&ancestry, &ancestry,
trusted_top_block_hash, trusted_top_block_hash,

452
pallets/helpers/src/lcg.rs Normal file
View File

@ -0,0 +1,452 @@
use sp_runtime::traits::UniqueSaturatedInto;
macro_rules! generate_perfect_slot_mapper {
(
struct_name: $struct_name:ident,
inner_type: $inner_type:ty,
init_rng: $init_rng:expr,
golden_ratio: $golden_ratio:expr,
lcg_multiplier: $lcg_multiplier:expr,
lcg_increment: $lcg_increment:expr,
) => {
pub struct $struct_name;
impl $struct_name {
const TOTAL_BITS: usize = core::mem::size_of::<$inner_type>() * 8;
fn get_exact_iterations(items_count: usize) -> Option<usize> {
let table_size = Self::TOTAL_BITS;
if items_count <= 1 { return Some(1); }
if items_count > table_size { return None; }
let density_pct = (items_count * 100) / table_size;
match table_size {
16 => match density_pct {
0 => Some(1),
1..=5 => Some(1), // max 0 items
6..=10 => Some(1), // max 1 items
11..=15 => Some(2), // max 2 items
16..=20 => Some(2), // max 3 items
21..=25 => Some(3), // max 4 items
26..=30 => Some(3), // max 4 items
31..=35 => Some(5), // max 5 items
36..=40 => Some(8), // max 6 items
41..=45 => Some(13), // max 7 items
46..=50 => Some(24), // max 8 items
51..=55 => Some(24), // max 8 items
56..=60 => Some(49), // max 9 items
61..=65 => Some(112), // max 10 items
66..=70 => Some(301), // max 11 items
71..=75 => Some(966), // max 12 items
76..=80 => Some(966), // max 12 items
81..=85 => Some(3868), // max 13 items
86..=90 => Some(20633), // max 14 items
91..=95 => Some(165075), // max 15 items
_ => None,
},
32 => match density_pct {
0 => Some(1),
1..=5 => Some(1), // max 1 items
6..=10 => Some(2), // max 3 items
11..=15 => Some(2), // max 4 items
16..=20 => Some(4), // max 6 items
21..=25 => Some(7), // max 8 items
26..=30 => Some(9), // max 9 items
31..=35 => Some(20), // max 11 items
36..=40 => Some(31), // max 12 items
41..=45 => Some(85), // max 14 items
46..=50 => Some(287), // max 16 items
51..=55 => Some(575), // max 17 items
56..=60 => Some(2807), // max 19 items
61..=65 => Some(6912), // max 20 items
66..=70 => Some(53627), // max 22 items
71..=75 => Some(610170), // max 24 items
_ => None,
},
64 => match density_pct {
0 => Some(1),
1..=5 => Some(1), // max 3 items
6..=10 => Some(2), // max 6 items
11..=15 => Some(4), // max 9 items
16..=20 => Some(8), // max 12 items
21..=25 => Some(22), // max 16 items
26..=30 => Some(58), // max 19 items
31..=35 => Some(180), // max 22 items
36..=40 => Some(686), // max 25 items
41..=45 => Some(3285), // max 28 items
46..=50 => Some(38995), // max 32 items
51..=55 => Some(343497), // max 35 items
_ => None,
},
128 => match density_pct {
0 => Some(1),
1..=5 => Some(2), // max 6 items
6..=10 => Some(4), // max 12 items
11..=15 => Some(11), // max 19 items
16..=20 => Some(36), // max 25 items
21..=25 => Some(207), // max 32 items
26..=30 => Some(1368), // max 38 items
31..=35 => Some(13426), // max 44 items
36..=40 => Some(331105), // max 51 items
_ => None,
},
_ => None,
}
}
pub fn get_bits_metadata() -> Option<($inner_type, $inner_type)> {
let table_size = Self::TOTAL_BITS;
let bits_needed = table_size.trailing_zeros() as usize;
let bit_shift_usize = table_size.checked_sub(bits_needed)?;
let bit_shift = bit_shift_usize as $inner_type;
let bit_mask = (table_size - 1) as $inner_type;
Some((bit_shift, bit_mask))
}
pub fn get_lcg_xor_mask<V>(values: &[V]) -> Option<$inner_type>
where
V: UniqueSaturatedInto<$inner_type> + Copy,
{
let table_size = Self::TOTAL_BITS;
if values.len() > table_size { return None; }
let max_iterations = Self::get_exact_iterations(values.len())?;
let (bit_shift, bit_mask) = Self::get_bits_metadata()?;
let mut lcg_rng_state: $inner_type = $init_rng;
let mut xor_key: $inner_type = lcg_rng_state;
let mut slots_taken = [false; Self::TOTAL_BITS];
for _ in 0..max_iterations {
slots_taken.fill(false);
let mut collision_found = false;
for value in values {
let slot_index = Self::get_lcg_based_slot_index(
bit_mask,
bit_shift,
xor_key,
value
);
if slot_index >= table_size || slots_taken[slot_index] {
collision_found = true;
break;
}
slots_taken[slot_index] = true;
}
if !collision_found { return Some(xor_key); }
lcg_rng_state = lcg_rng_state
.wrapping_mul($lcg_multiplier)
.wrapping_add($lcg_increment);
xor_key = lcg_rng_state;
}
None
}
#[inline(always)]
pub fn get_lcg_based_slot_index<V>(
bit_mask: $inner_type,
bit_shift: $inner_type,
xor_key: $inner_type,
value: &V
) -> usize
where
V: UniqueSaturatedInto<$inner_type> + Copy,
{
let converted_value: $inner_type = (*value).unique_saturated_into();
let knuth_hash = (converted_value ^ xor_key).wrapping_mul($golden_ratio);
let shifted_hash = knuth_hash.checked_shr(bit_shift as u32).unwrap_or(knuth_hash);
let slot_index_converted = shifted_hash & bit_mask;
slot_index_converted as usize
}
}
}
}
generate_perfect_slot_mapper!(
struct_name: PerfectSlotMapperU16,
inner_type: u16,
init_rng: 0x1234,
golden_ratio: 0x9E37, // Computed with: 2^16 * ( (sqrt(5) - 1) / 2)
lcg_multiplier: 25173, // Reference: Peter Grogono, Programming in Pascal, Section 4.5.
lcg_increment: 13849, // http://engr.case.edu/merat_francis/eeap282f97/labs/Random_Lab.pdf
);
generate_perfect_slot_mapper!(
struct_name: PerfectSlotMapperU32,
inner_type: u32,
init_rng: 0x12345678,
golden_ratio: 0x9E3779B9, // Computd with: 2^32 * ( (sqrt(5) - 1) / 2)
lcg_multiplier: 1664525, // Numerical Recipes `ranqd1`, Chapter 7.1,
lcg_increment: 1013904223, // An Even Quicker Generator, Eq. 7.1.6
);
generate_perfect_slot_mapper!(
struct_name: PerfectSlotMapperU64,
inner_type: u64,
init_rng: 0x123456789ABCDEF,
golden_ratio: 0x9E3779B97F4A7C15, // Taken from `splitmix64`, this generator is specifically used in PostgreSQL
lcg_multiplier: 6364136223846793005, // Newlib / Musl libc 64-bit LCG multiplier
lcg_increment: 1, // https://sourceware.org/git/?p=newlib-cygwin.git;a=blob;f=newlib/libc/stdlib/rand.c
);
generate_perfect_slot_mapper!(
struct_name: PerfectSlotMapperU128,
inner_type: u128,
init_rng: 0x123456789ABCDEF0123456789ABCDEF0,
golden_ratio: 0x9E3779B97F4A7C15F39CC0605CEDC834, // Computd with: 2^128 * ( (sqrt(5) - 1) / 2) with python
lcg_multiplier: 0x2360ED051FC65DA44385DF649FCCF645, // 2549297995355413924ULL,4865540595714422341ULL
lcg_increment: 0x5851F42D4C957F2D14057B7EF767814F, // 6364136223846793005ULL,1442695040888963407ULL
// https://github.com/imneme/pcg-cpp/blob/master/include/pcg_random.hpp
);
#[cfg(test)]
mod perfect_mapper_tests {
use super::*;
macro_rules! generate_mapper_test {
($mod_name:ident, $struct_name:ident, $init_rng:expr, $inner_type:ty) => {
mod $mod_name {
use super::*;
#[test]
fn test_standard_pack_20_percentages() {
const TABLE_SIZE: usize = $struct_name::TOTAL_BITS;
const ITEMS_COUNT: usize = (TABLE_SIZE * 20) / 100;
let mut values = [0 as $inner_type; TABLE_SIZE];
for i in 0..ITEMS_COUNT {
values[i] = ((i as $inner_type).wrapping_mul(999)) ^ 0xABC;
}
let filled_values = &values[0..ITEMS_COUNT];
let mask = $struct_name::get_lcg_xor_mask(filled_values);
assert!(mask.is_some());
let xor_key = mask.unwrap();
let bits_needed = TABLE_SIZE.trailing_zeros() as $inner_type;
let bit_mask = (TABLE_SIZE - 1) as $inner_type;
let bit_shift = (TABLE_SIZE as $inner_type) - bits_needed;
let mut slots = vec![false; TABLE_SIZE];
for val in filled_values {
let idx = $struct_name::get_lcg_based_slot_index(
bit_mask,
bit_shift,
xor_key,
val
);
assert!(idx < TABLE_SIZE);
assert!(!slots[idx]);
slots[idx] = true;
}
}
#[test]
fn test_heavy_math() {
const TABLE_SIZE: usize = $struct_name::TOTAL_BITS;
let max_val = <$inner_type>::MAX;
let one_val = 1 as $inner_type;
let mut block_pattern = 0 as $inner_type;
let mut noise_pattern = 0 as $inner_type;
let total_bytes = core::mem::size_of::<$inner_type>();
let mut i = 0;
while i < total_bytes {
let byte_val = if i % 2 == 0 { 0x55 } else { 0xAA };
noise_pattern = (noise_pattern << 8) | (byte_val as $inner_type);
let block_val = if i < total_bytes / 3 {
0xEE
} else if i < (total_bytes * 2) / 3 {
0xFF
} else {
0xAA
};
block_pattern = (block_pattern << 8) | (block_val as $inner_type);
i += 1;
}
let center_bit_shift = (TABLE_SIZE / 2) as u32;
let center_bit_pattern = (1 as $inner_type)
.checked_shl(center_bit_shift)
.unwrap_or(1);
let values: [$inner_type; 6] = [
$init_rng,
noise_pattern,
max_val,
one_val,
center_bit_pattern,
block_pattern
];
let mask = $struct_name::get_lcg_xor_mask(&values);
assert!(mask.is_some());
}
#[test]
fn test_single_element() {
const TABLE_SIZE: usize = $struct_name::TOTAL_BITS;
let single_value = [420 as $inner_type];
let mask = $struct_name::get_lcg_xor_mask(&single_value);
assert!(mask.is_some());
let xor_key = mask.unwrap();
let bits_needed = TABLE_SIZE.trailing_zeros() as $inner_type;
let bit_mask = (TABLE_SIZE - 1) as $inner_type;
let bit_shift = (TABLE_SIZE as $inner_type) - bits_needed;
let idx = $struct_name::get_lcg_based_slot_index(
bit_mask,
bit_shift,
xor_key,
&single_value[0]
);
assert!(idx < TABLE_SIZE);
}
#[test]
fn test_invalid_bounds_returns_none() {
const TABLE_SIZE: usize = $struct_name::TOTAL_BITS;
let oversized_values = [1 as $inner_type; TABLE_SIZE];
let mask = $struct_name::get_lcg_xor_mask(&oversized_values);
assert!(mask.is_none());
}
#[test]
fn test_top_25_evm_chains_by_tvl() {
const TABLE_SIZE: usize = $struct_name::TOTAL_BITS;
let evm_network_ids: [u64; 25] = [
1, // Ethereum Mainnet
8453, // Base
56, // BNB Smart Chain
42161, // Arbitrum One
10143, // Monad
137, // Polygon PoS
43114, // Avalanche C-Chain
10, // OP Mainnet
57073, // Ink
14, // Flare Network
196, // X Layer
25, // Cronos
5000, // Mantle
100, // Gnosis Chain
59144, // Linea
534352, // Scroll
324, // zkSync Era
146, // Sonic (Fantom)
1116, // Core
42220, // Celo
1088, // Metis
592, // Astar
1284, // Moonbeam
98865, // Plume Network
60808, // BOB (Build on Bitcoin)
];
let mask = $struct_name::get_lcg_xor_mask(&evm_network_ids);
if let Some(xor_key) = mask {
let mut slots_check = [false; TABLE_SIZE];
let bits_needed = TABLE_SIZE.trailing_zeros() as $inner_type;
let bit_mask = (TABLE_SIZE - 1) as $inner_type;
let bit_shift = (TABLE_SIZE as $inner_type) - bits_needed;
for chain_id in &evm_network_ids {
let slot_idx = $struct_name::get_lcg_based_slot_index(
bit_mask,
bit_shift,
xor_key,
chain_id
);
assert!(slot_idx < TABLE_SIZE);
assert!(!slots_check[slot_idx]);
slots_check[slot_idx] = true;
}
} else {
assert!(TABLE_SIZE <= 32);
}
}
#[test]
fn test_top_25_evm_testnet_chains() {
const TABLE_SIZE: usize = $struct_name::TOTAL_BITS;
let evm_network_ids: [u64; 10] = [
63, // Mordor (Ethereum Classic Testnet)
11155111, // Ethereum Sepolia
80002, // Polygon Amoy
421614, // Arbitrum Sepolia
84532, // Base Sepolia
11155420, // Optimism Sepolia
59141, // Linea Sepolia
534351, // Scroll Sepolia
80084, // Berachain Bartio
98864, // Plume Testnet
];
let mask = $struct_name::get_lcg_xor_mask(&evm_network_ids);
if let Some(xor_key) = mask {
let mut slots_check = [false; TABLE_SIZE];
let bits_needed = TABLE_SIZE.trailing_zeros() as $inner_type;
let bit_mask = (TABLE_SIZE - 1) as $inner_type;
let bit_shift = (TABLE_SIZE as $inner_type) - bits_needed;
for chain_id in &evm_network_ids {
let slot_idx = $struct_name::get_lcg_based_slot_index(
bit_mask,
bit_shift,
xor_key,
chain_id
);
assert!(slot_idx < TABLE_SIZE);
assert!(!slots_check[slot_idx]);
slots_check[slot_idx] = true;
}
} else {
assert!(TABLE_SIZE == 16);
}
}
}
}
}
generate_mapper_test!(u16_mapper, PerfectSlotMapperU16, 0x1234, u16);
generate_mapper_test!(u32_mapper, PerfectSlotMapperU32, 0x12345678, u32);
generate_mapper_test!(u64_mapper, PerfectSlotMapperU64, 0x123456789ABCDEF, u64);
generate_mapper_test!(u128_mapper, PerfectSlotMapperU128, 0x123456789ABCDEF0123456789ABCDEF0, u128);
}

View File

@ -3,12 +3,14 @@
use ghost_traits::hashing::GhostHasher; use ghost_traits::hashing::GhostHasher;
use sp_core::H256; use sp_core::H256;
use sp_io::hashing::{blake2_256, keccak_256, sha2_256}; use sp_io::hashing::{blake2_256, keccak_256, sha2_256};
use sp_runtime::{Perbill, traits::{Saturating, UniqueSaturatedInto}};
pub mod bounded_bitmap; pub mod bounded_bitmap;
pub mod hash_chain; pub mod hash_chain;
pub mod merkle_tree; pub mod merkle_tree;
pub mod networks; pub mod networks;
pub mod utxo; pub mod utxo;
pub mod lcg;
pub type AuthIndexU8 = u8; pub type AuthIndexU8 = u8;
pub type AuthIndexU16 = u16; pub type AuthIndexU16 = u16;
@ -48,53 +50,101 @@ where
value - f value - f
} }
pub struct SubstrateBlake2Hasher; pub fn get_submission_probability<B>(
impl GhostHasher for SubstrateBlake2Hasher { participants_count: usize,
type Hash = H256; values_count: usize,
type HashBytes = [u8; 32]; blocks_left: B
fn hash(data: &[u8]) -> Self::Hash { ) -> Perbill
H256::from(blake2_256(data)) where
} B: UniqueSaturatedInto<u32>,
fn hash_len() -> usize { {
H256::len_bytes() // y = min(1, (1/6) + (A-C)/2x)
} let a: u32 = participants_count.unique_saturated_into();
fn empty() -> Self::Hash { let c: u32 = values_count.unique_saturated_into();
H256::zero() let x: u32 = blocks_left.unique_saturated_into();
}
if a == 0 { return Perbill::zero(); }
let part_1 = Perbill::from_percent(6);
let part_2 = if x > 0 {
let numerator = a.saturating_sub(c);
let denominator = x.saturating_mul(2);
Perbill::from_rational(numerator, denominator)
} else {
if c < a { Perbill::one() }
else { Perbill::zero() }
};
part_1.saturating_add(part_2)
} }
pub struct SubstrateKeccakHasher; macro_rules! impl_ghost_hasher {
impl GhostHasher for SubstrateKeccakHasher { (
type Hash = H256; struct $name:ident;
type HashBytes = [u8; 32]; hash_type: $hash_type:ty;
fn hash(data: &[u8]) -> Self::Hash { hash_bytes_len: $hash_bytes_len:expr;
H256::from(keccak_256(data)) fn hash($data:ident) -> $hash_res:expr
) => {
pub struct $name;
impl GhostHasher for $name {
type Hash = $hash_type;
type HashBytes = [u8; $hash_bytes_len];
fn hash($data: &[u8]) -> Self::Hash {
$hash_res
} }
fn hash_len() -> usize { fn hash_len() -> usize {
H256::len_bytes() $hash_bytes_len
} }
fn empty() -> Self::Hash { fn empty() -> Self::Hash {
H256::zero() <$hash_type>::zero()
} }
fn combine_hashes(left: Self::Hash, right: Self::Hash) -> Self::Hash {
let mut combined = [0u8; $hash_bytes_len * 2];
combined[..$hash_bytes_len].copy_from_slice(left.as_ref());
combined[$hash_bytes_len..].copy_from_slice(right.as_ref());
Self::hash(&combined)
}
fn combine_hashes_prefixed(p: u8, l: Self::Hash, r: Self::Hash) -> Self::Hash {
let mut combined = [p; $hash_bytes_len * 2 + 1];
combined[1..1 + $hash_bytes_len].copy_from_slice(l.as_ref());
combined[1 + $hash_bytes_len..].copy_from_slice(r.as_ref());
Self::hash(&combined)
}
}
};
} }
pub struct UtxoSha2Hasher; impl_ghost_hasher!(
impl GhostHasher for UtxoSha2Hasher { struct SubstrateBlake2Hasher;
type Hash = H256; hash_type: H256;
type HashBytes = [u8; 32]; hash_bytes_len: 32;
fn hash(data: &[u8]) -> Self::Hash { fn hash(data) -> H256::from(blake2_256(data))
);
impl_ghost_hasher!(
struct SubstrateKeccakHasher;
hash_type: H256;
hash_bytes_len: 32;
fn hash(data) -> H256::from(keccak_256(data))
);
impl_ghost_hasher!(
struct UtxoSha2Hasher;
hash_type: H256;
hash_bytes_len: 32;
fn hash(data) -> {
let first_pass: [u8; 32] = sha2_256(data); let first_pass: [u8; 32] = sha2_256(data);
let second_pass: [u8; 32] = sha2_256(&first_pass); let second_pass: [u8; 32] = sha2_256(&first_pass);
H256::from(second_pass) H256::from(second_pass)
} }
fn hash_len() -> usize { );
H256::len_bytes()
}
fn empty() -> Self::Hash {
H256::zero()
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -134,4 +184,38 @@ mod tests {
assert_eq!(get_byzantium_quorum_threshold(4u64), 3u64); assert_eq!(get_byzantium_quorum_threshold(4u64), 3u64);
assert_eq!(get_byzantium_quorum_threshold(4usize), 3usize); assert_eq!(get_byzantium_quorum_threshold(4usize), 3usize);
} }
#[test]
fn test_base_probability_when_plenty_of_time() {
let prob = get_submission_probability(100, 0, 10000u32);
assert!(prob > Perbill::from_percent(6));
assert!(prob < Perbill::from_percent(7));
}
#[test]
fn test_urgency_increases_as_blocks_shrink() {
let prob_far = get_submission_probability(20, 10, 10u32);
assert_eq!(prob_far, Perbill::from_percent(56));
let prob_close = get_submission_probability(20, 10, 2u32);
assert_eq!(prob_close, Perbill::one());
}
#[test]
fn test_deadline_reached_but_votes_missing() {
let prob = get_submission_probability(10, 5, 0u32);
assert_eq!(prob, Perbill::one());
}
#[test]
fn test_all_votes_collected() {
let prob = get_submission_probability(10, 10, 5u32);
assert_eq!(prob, Perbill::from_percent(6));
}
#[test]
fn test_zero_participants_edge_case() {
let prob = get_submission_probability(0, 0, 5u32);
assert_eq!(prob, Perbill::zero());
}
} }

View File

@ -6,49 +6,57 @@ use sp_std::vec::Vec;
const LEAF_PREFIX: u8 = 0; const LEAF_PREFIX: u8 = 0;
const NODE_PREFIX: u8 = 1; const NODE_PREFIX: u8 = 1;
pub enum MerkleTreeError<E> {
Generate(E),
InvalidIndex,
Empty,
}
fn leaf_prefixed<H>(preimage: &[u8]) -> H::Hash fn leaf_prefixed<H>(preimage: &[u8]) -> H::Hash
where where
H: GhostHasher, H: GhostHasher,
{ {
let mut prefixed_data = sp_std::vec![LEAF_PREFIX; 1 + preimage.len()]; let mut buffer = Vec::with_capacity(preimage.len() + 1);
prefixed_data[1..].copy_from_slice(preimage); buffer.push(LEAF_PREFIX);
H::hash(&prefixed_data) buffer.extend_from_slice(preimage);
}
fn node_prefixed<H>(preimage1: &[u8], preimage2: &[u8]) -> H::Hash H::hash(&buffer)
where
H: GhostHasher,
{
let total_len = preimage1.len() + preimage2.len();
let mut prefixed_data = sp_std::vec![NODE_PREFIX; 1 + total_len];
prefixed_data[1..1 + preimage1.len()].copy_from_slice(preimage1);
prefixed_data[1 + preimage1.len()..].copy_from_slice(preimage2);
H::hash(&prefixed_data)
} }
pub fn generate_tree<H, I, S, F, E>( pub fn generate_tree<H, I, S, F, E>(
max_index: I, max_index: I,
size_hint: usize,
raw_values: S, raw_values: S,
generate_preimage: F, update_preimage_buffer: F,
) -> Result<Vec<H::Hash>, E> ) -> Result<Vec<H::Hash>, MerkleTreeError<E>>
where where
H: GhostHasher, H: GhostHasher,
S: IntoIterator, S: IntoIterator,
I: AtLeast8BitUnsigned + UniqueSaturatedInto<usize>, I: AtLeast8BitUnsigned + UniqueSaturatedInto<usize>,
F: Fn(S::Item) -> Result<(usize, Vec<u8>), E>, F: Fn(S::Item, &mut Vec<u8>) -> Result<usize, E>,
{ {
let num_of_leaves: usize = (max_index + I::one()).unique_saturated_into(); let num_of_leaves: usize = (max_index + I::one()).unique_saturated_into();
let padded_leaves = num_of_leaves.next_power_of_two(); let padded_leaves = num_of_leaves.next_power_of_two();
let total_capacity = (2 * padded_leaves) - 1; let total_capacity = (2 * padded_leaves) - 1;
if total_capacity == 0 { return Err(MerkleTreeError::Empty); }
let empty_hash = H::empty(); let empty_hash = H::empty();
let empty_leaf_hash = leaf_prefixed::<H>(empty_hash.as_ref()); let empty_leaf_hash = leaf_prefixed::<H>(empty_hash.as_ref());
let mut merkle_tree = sp_std::vec![empty_leaf_hash; total_capacity]; let mut merkle_tree = sp_std::vec![empty_leaf_hash; total_capacity];
let mut buffer = Vec::with_capacity(size_hint + 1);
for item in raw_values.into_iter() { for item in raw_values.into_iter() {
let (index, preimage) = generate_preimage(item)?; buffer.clear();
if index >= padded_leaves { return Ok(sp_std::vec![]); } buffer.push(LEAF_PREFIX);
merkle_tree[index] = leaf_prefixed::<H>(&preimage)
let index = update_preimage_buffer(item, &mut buffer)
.map_err(|err| MerkleTreeError::Generate(err))?;
if index >= padded_leaves { return Err(MerkleTreeError::InvalidIndex); }
merkle_tree[index] = H::hash(&buffer);
} }
let mut layer_start = 0; let mut layer_start = 0;
@ -58,10 +66,15 @@ where
while current_layer_len > 1 { while current_layer_len > 1 {
for i in (0..current_layer_len).step_by(2) { for i in (0..current_layer_len).step_by(2) {
let layer_index = layer_start + i; let layer_index = layer_start + i;
let left_bytes = merkle_tree[layer_index].as_ref(); let left_bytes = merkle_tree[layer_index];
let right_bytes = merkle_tree[layer_index | 1].as_ref(); let right_bytes = merkle_tree[layer_index | 1];
merkle_tree[write_ptr] = node_prefixed::<H>(left_bytes, right_bytes); let combined_hash = H::combine_hashes_prefixed(
NODE_PREFIX,
left_bytes,
right_bytes,
);
merkle_tree[write_ptr] = combined_hash;
write_ptr += 1; write_ptr += 1;
} }
layer_start += current_layer_len; layer_start += current_layer_len;
@ -71,16 +84,16 @@ where
Ok(merkle_tree) Ok(merkle_tree)
} }
pub fn generate_proof<H, I>( pub fn generate_proof<H, I, E>(
merkle_tree: &[H::Hash], merkle_tree: &[H::Hash],
max_index: I, max_index: I,
index: I, index: I,
) -> Vec<H::Hash> ) -> Result<Vec<H::Hash>, MerkleTreeError<E>>
where where
H: GhostHasher, H: GhostHasher,
I: AtLeast8BitUnsigned + UniqueSaturatedInto<usize>, I: AtLeast8BitUnsigned + UniqueSaturatedInto<usize>,
{ {
if index > max_index { return sp_std::vec![]; } if index > max_index { return Err(MerkleTreeError::InvalidIndex); }
let num_of_leaves: usize = (max_index + I::one()).unique_saturated_into(); let num_of_leaves: usize = (max_index + I::one()).unique_saturated_into();
let mut current_index: usize = index.unique_saturated_into(); let mut current_index: usize = index.unique_saturated_into();
@ -101,7 +114,7 @@ where
current_index >>= 1; current_index >>= 1;
} }
proof Ok(proof)
} }
pub fn verify_tree_proof<H, I>( pub fn verify_tree_proof<H, I>(
@ -120,14 +133,11 @@ where
let mut current_hash = leaf_prefixed::<H>(preimage); let mut current_hash = leaf_prefixed::<H>(preimage);
for sibling in merkle_proof.iter() { for sibling in merkle_proof.iter() {
let sibling_bytes = sibling.as_ref(); current_hash = if current_index % 2 == 0 {
let hash_bytes = current_hash.as_ref(); H::combine_hashes_prefixed(NODE_PREFIX, current_hash, *sibling)
if current_index % 2 == 0 {
current_hash = node_prefixed::<H>(hash_bytes, sibling_bytes);
} else { } else {
current_hash = node_prefixed::<H>(sibling_bytes, hash_bytes); H::combine_hashes_prefixed(NODE_PREFIX, *sibling, current_hash)
} };
current_index >>= 1; current_index >>= 1;
} }
@ -138,66 +148,64 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use sp_core::H256;
use sp_io::hashing::blake2_256;
pub struct TestBlake2Hasher; use crate::{
impl GhostHasher for TestBlake2Hasher { SubstrateBlake2Hasher, SubstrateKeccakHasher, UtxoSha2Hasher,
type Hash = H256; };
type HashBytes = [u8; 32];
fn hash(data: &[u8]) -> Self::Hash { macro_rules! run_merkle_tree_tests {
H256::from(blake2_256(data)) ($hasher_type:ty, $mod_name:ident) => {
} mod $mod_name {
fn empty() -> Self::Hash { use super::*;
H256::zero()
}
fn hash_len() -> usize {
H256::len_bytes()
}
}
#[test] #[test]
fn test_generate_tree_perfect_power_of_two() { fn test_generate_tree_perfect_power_of_two() {
let max_index = 3u32; let max_index = 3u32;
let size_hint = 69;
let raw_data = sp_std::vec![ let raw_data = sp_std::vec![
(0, sp_std::vec![1]), (0, sp_std::vec![1; 69]),
(1, sp_std::vec![2]), (1, sp_std::vec![2; 69]),
(2, sp_std::vec![3]), (2, sp_std::vec![3; 69]),
(3, sp_std::vec![4]), (3, sp_std::vec![4; 69]),
]; ];
let tree = generate_tree::<TestBlake2Hasher, u32, _, _, ()>( let tree = generate_tree::<$hasher_type, u32, _, _, ()>(
max_index, max_index,
size_hint,
raw_data, raw_data,
|(index, bytes)| Ok((index, bytes)), |(index, bytes), buffer| {
buffer.extend_from_slice(&bytes);
Ok(index as usize)
}
) )
.ok()
.unwrap(); .unwrap();
assert_eq!(tree.len(), 7); assert_eq!(tree.len(), 7);
let h0 = TestBlake2Hasher::hash(&[ &[0u8], [1].as_slice() ].concat()); let h0 = <$hasher_type>::hash(&[ &[0u8], [1; 69].as_slice() ].concat());
let h1 = TestBlake2Hasher::hash(&[ &[0u8], [2].as_slice() ].concat()); let h1 = <$hasher_type>::hash(&[ &[0u8], [2; 69].as_slice() ].concat());
let h2 = TestBlake2Hasher::hash(&[ &[0u8], [3].as_slice() ].concat()); let h2 = <$hasher_type>::hash(&[ &[0u8], [3; 69].as_slice() ].concat());
let h3 = TestBlake2Hasher::hash(&[ &[0u8], [4].as_slice() ].concat()); let h3 = <$hasher_type>::hash(&[ &[0u8], [4; 69].as_slice() ].concat());
let mut c1 = [0u8; 65]; let mut c1 = [0u8; 65];
c1[0] = NODE_PREFIX; c1[0] = NODE_PREFIX;
c1[1..33].copy_from_slice(h0.as_ref()); c1[1..33].copy_from_slice(h0.as_ref());
c1[33..].copy_from_slice(h1.as_ref()); c1[33..].copy_from_slice(h1.as_ref());
let parent_left = TestBlake2Hasher::hash(&c1); let parent_left = <$hasher_type>::hash(&c1);
let mut c2 = [0u8; 65]; let mut c2 = [0u8; 65];
c2[0] = NODE_PREFIX; c2[0] = NODE_PREFIX;
c2[1..33].copy_from_slice(h2.as_ref()); c2[1..33].copy_from_slice(h2.as_ref());
c2[33..].copy_from_slice(h3.as_ref()); c2[33..].copy_from_slice(h3.as_ref());
let parent_right = TestBlake2Hasher::hash(&c2); let parent_right = <$hasher_type>::hash(&c2);
let mut c_root = [0u8; 65]; let mut c_root = [0u8; 65];
c_root[0] = NODE_PREFIX; c_root[0] = NODE_PREFIX;
c_root[1..33].copy_from_slice(parent_left.as_ref()); c_root[1..33].copy_from_slice(parent_left.as_ref());
c_root[33..].copy_from_slice(parent_right.as_ref()); c_root[33..].copy_from_slice(parent_right.as_ref());
let expected_root = TestBlake2Hasher::hash(&c_root); let expected_root = <$hasher_type>::hash(&c_root);
assert_eq!(tree.last().unwrap(), &expected_root); assert_eq!(tree.last().unwrap(), &expected_root);
} }
@ -205,36 +213,44 @@ mod tests {
#[test] #[test]
fn test_generate_tree_with_padding_hashing() { fn test_generate_tree_with_padding_hashing() {
let max_index = 2u32; let max_index = 2u32;
let size_hint = 420;
let raw_data = sp_std::vec![ let raw_data = sp_std::vec![
(0, sp_std::vec![1]), (0, sp_std::vec![1; size_hint]),
(1, sp_std::vec![2]), (1, sp_std::vec![2; size_hint]),
(2, sp_std::vec![3]), (2, sp_std::vec![3; size_hint]),
]; ];
let tree = generate_tree::<TestBlake2Hasher, u32, _, _, ()>( let tree = generate_tree::<$hasher_type, u32, _, _, ()>(
max_index, max_index,
size_hint,
raw_data, raw_data,
|(index, bytes)| Ok((index, bytes)),
|(index, bytes), buffer| {
buffer.extend_from_slice(&bytes);
Ok(index as usize)
}
) )
.ok()
.unwrap(); .unwrap();
let mut empty_data = sp_std::vec![LEAF_PREFIX]; let mut empty_data = sp_std::vec![LEAF_PREFIX];
empty_data.extend_from_slice(&H256::zero().to_fixed_bytes()); empty_data.extend_from_slice(&<$hasher_type>::empty().to_fixed_bytes());
let empty_hash = TestBlake2Hasher::hash(&empty_data); let empty_hash = <$hasher_type>::hash(&empty_data);
assert_eq!(tree.len(), 7); assert_eq!(tree.len(), 7);
assert_eq!(tree[3], empty_hash); assert_eq!(tree[3], empty_hash);
let mut leaf2_data = sp_std::vec![LEAF_PREFIX]; let mut leaf2_data = sp_std::vec![LEAF_PREFIX];
leaf2_data.extend_from_slice(&[3]); leaf2_data.extend_from_slice(&sp_std::vec![3; size_hint]);
let h2 = TestBlake2Hasher::hash(&leaf2_data); let h2 = <$hasher_type>::hash(&leaf2_data);
let mut combined_right = [0u8; 65]; let mut combined_right = [0u8; 65];
combined_right[0] = NODE_PREFIX; combined_right[0] = NODE_PREFIX;
combined_right[1..33].copy_from_slice(h2.as_ref()); combined_right[1..33].copy_from_slice(h2.as_ref());
combined_right[33..].copy_from_slice(empty_hash.as_ref()); combined_right[33..].copy_from_slice(empty_hash.as_ref());
let expected_parent_right = TestBlake2Hasher::hash(&combined_right); let expected_parent_right = <$hasher_type>::hash(&combined_right);
assert_eq!(tree[5], expected_parent_right); assert_eq!(tree[5], expected_parent_right);
} }
@ -246,20 +262,43 @@ mod tests {
let data_2 = sp_std::vec![(0, sp_std::vec![100]), (1, sp_std::vec![200])]; let data_2 = sp_std::vec![(0, sp_std::vec![100]), (1, sp_std::vec![200])];
let data_3 = sp_std::vec![(0, sp_std::vec![101]), (1, sp_std::vec![200])]; let data_3 = sp_std::vec![(0, sp_std::vec![101]), (1, sp_std::vec![200])];
let tree_1 = let tree_1 = generate_tree::<$hasher_type, u32, _, _, ()>(
generate_tree::<TestBlake2Hasher, u32, _, _, ()>(max_index, data_1, |(i, b)| { max_index,
Ok((i, b)) 1,
}) data_1,
|(index, bytes), buffer| {
buffer.extend_from_slice(&bytes);
Ok(index as usize)
}
)
.ok()
.unwrap(); .unwrap();
let tree_2 =
generate_tree::<TestBlake2Hasher, u32, _, _, ()>(max_index, data_2, |(i, b)| { let tree_2 = generate_tree::<$hasher_type, u32, _, _, ()>(
Ok((i, b)) max_index,
}) 1,
data_2,
|(index, bytes), buffer| {
buffer.extend_from_slice(&bytes);
Ok(index as usize)
}
)
.ok()
.unwrap(); .unwrap();
let tree_3 =
generate_tree::<TestBlake2Hasher, u32, _, _, ()>(max_index, data_3, |(i, b)| { let tree_3 = generate_tree::<$hasher_type, u32, _, _, ()>(
Ok((i, b)) max_index,
}) 1,
data_3,
|(index, bytes), buffer| {
buffer.extend_from_slice(&bytes);
Ok(index as usize)
}
)
.ok()
.unwrap(); .unwrap();
assert_eq!(tree_1.last().unwrap(), tree_2.last().unwrap()); assert_eq!(tree_1.last().unwrap(), tree_2.last().unwrap());
@ -269,96 +308,132 @@ mod tests {
#[test] #[test]
fn test_generate_tree_single_element() { fn test_generate_tree_single_element() {
let max_index = 0u32; let max_index = 0u32;
let raw_data = sp_std::vec![(0, sp_std::vec![42])]; let size_hint = 1337;
let raw_data = sp_std::vec![(0, sp_std::vec![42; size_hint])];
let tree = generate_tree::<TestBlake2Hasher, u32, _, _, ()>( let tree = generate_tree::<$hasher_type, u32, _, _, ()>(
max_index, max_index,
size_hint,
raw_data, raw_data,
|(index, bytes)| Ok((index, bytes)),
|(index, bytes), buffer| {
buffer.extend_from_slice(&bytes);
Ok(index as usize)
}
) )
.ok()
.unwrap(); .unwrap();
let expected_leaf_data = [
&[LEAF_PREFIX][..],
sp_std::vec![42; size_hint].as_slice()
].concat();
let expected_hash = <$hasher_type>::hash(&expected_leaf_data);
assert_eq!(tree.len(), 1); assert_eq!(tree.len(), 1);
assert_eq!(tree[0], TestBlake2Hasher::hash(&vec![LEAF_PREFIX, 42])); assert_eq!(tree[0], expected_hash);
} }
#[test] #[test]
fn test_proof_and_verification_end_to_end() { fn test_proof_and_verification_end_to_end() {
let max_index = 3u32; let max_index = 3u32;
let size_hint = 420;
let raw_data = sp_std::vec![ let raw_data = sp_std::vec![
(0, sp_std::vec![1]), (0, sp_std::vec![1; size_hint]),
(1, sp_std::vec![2]), (1, sp_std::vec![2; size_hint]),
(2, sp_std::vec![3]), (2, sp_std::vec![3; size_hint]),
(3, sp_std::vec![4]), (3, sp_std::vec![4; size_hint]),
]; ];
let tree = generate_tree::<TestBlake2Hasher, u32, _, _, ()>( let tree = generate_tree::<$hasher_type, u32, _, _, ()>(
max_index, max_index,
size_hint,
raw_data, raw_data,
|(index, bytes)| Ok((index, bytes)),
|(index, bytes), buffer| {
buffer.extend_from_slice(&bytes);
Ok(index as usize)
}
) )
.ok()
.unwrap(); .unwrap();
let root = *tree.last().unwrap(); let root = *tree.last().unwrap();
let target_index = 2u32; let target_index = 2u32;
let proof = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, target_index); let proof = generate_proof::<$hasher_type, u32, ()>(&tree, max_index, target_index)
.ok()
.unwrap();
assert_eq!(proof.len(), 2); assert_eq!(proof.len(), 2);
let preimage = sp_std::vec![3]; let preimage = sp_std::vec![3; size_hint];
let is_valid = assert!(verify_tree_proof::<$hasher_type, u32>(&preimage, &proof, root, target_index));
verify_tree_proof::<TestBlake2Hasher, u32>(&preimage, &proof, root, target_index);
assert!(is_valid);
} }
#[test] #[test]
fn test_proof_index_out_of_bounds() { fn test_proof_index_out_of_bounds() {
let max_index = 3u32; let max_index = 3u32;
let size_hint = 420;
let raw_data = sp_std::vec![ let raw_data = sp_std::vec![
(0, sp_std::vec![1]), (0, sp_std::vec![1; size_hint]),
(1, sp_std::vec![2]), (1, sp_std::vec![2; size_hint]),
(2, sp_std::vec![3]), (2, sp_std::vec![3; size_hint]),
(3, sp_std::vec![4]), (3, sp_std::vec![4; size_hint]),
]; ];
let tree = generate_tree::<TestBlake2Hasher, u32, _, _, ()>( let tree = generate_tree::<$hasher_type, u32, _, _, ()>(
max_index, max_index,
size_hint,
raw_data, raw_data,
|(index, bytes)| Ok((index, bytes)),
|(index, bytes), buffer| {
buffer.extend_from_slice(&bytes);
Ok(index as usize)
}
) )
.ok()
.unwrap(); .unwrap();
let root = *tree.last().unwrap(); let root = *tree.last().unwrap();
let target_index = 2u32; let target_index = 2u32;
let proof = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, target_index); let proof = generate_proof::<$hasher_type, u32, ()>(&tree, max_index, target_index)
.ok()
.unwrap();
assert_eq!(proof.len(), 2); assert_eq!(proof.len(), 2);
let preimage = sp_std::vec![3]; let preimage = sp_std::vec![3; size_hint];
let is_valid = assert!(verify_tree_proof::<$hasher_type, u32>(&preimage, &proof, root, target_index));
verify_tree_proof::<TestBlake2Hasher, u32>(&preimage, &proof, root, target_index + 1); assert!(!verify_tree_proof::<$hasher_type, u32>(&preimage, &proof, root, 4));
assert!(!is_valid);
} }
#[test] #[test]
fn test_proof_contains_correct_siblings() { fn test_proof_contains_correct_siblings() {
let max_index = 3u32; let max_index = 3u32;
let size_hint = 1337;
let raw_data = sp_std::vec![ let raw_data = sp_std::vec![
(0, sp_std::vec![10]), (0, sp_std::vec![10; size_hint]),
(1, sp_std::vec![20]), (1, sp_std::vec![20; size_hint]),
(2, sp_std::vec![30]), (2, sp_std::vec![30; size_hint]),
(3, sp_std::vec![40]), (3, sp_std::vec![40; size_hint]),
]; ];
let tree = generate_tree::<TestBlake2Hasher, u32, _, _, ()>( let tree = generate_tree::<$hasher_type, u32, _, _, ()>(
max_index, max_index,
size_hint,
raw_data, raw_data,
|(index, bytes)| Ok((index, bytes)),
|(index, bytes), buffer| {
buffer.extend_from_slice(&bytes);
Ok(index as usize)
}
) )
.ok()
.unwrap(); .unwrap();
let h0 = tree[0]; let h0 = tree[0];
@ -369,19 +444,31 @@ mod tests {
let parent_left = tree[4]; // hash(h0 + h1) let parent_left = tree[4]; // hash(h0 + h1)
let parent_right = tree[5]; // hash(h2 + h3) let parent_right = tree[5]; // hash(h2 + h3)
let proof_for_0 = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, 0); let proof_for_0 = generate_proof::<$hasher_type, u32, ()>(&tree, max_index, 0)
.ok()
.unwrap();
assert_eq!(proof_for_0[0], h1); assert_eq!(proof_for_0[0], h1);
assert_eq!(proof_for_0[1], parent_right); assert_eq!(proof_for_0[1], parent_right);
let proof_for_1 = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, 1); let proof_for_1 = generate_proof::<$hasher_type, u32, ()>(&tree, max_index, 1)
.ok()
.unwrap();
assert_eq!(proof_for_1[0], h0); assert_eq!(proof_for_1[0], h0);
assert_eq!(proof_for_1[1], parent_right); assert_eq!(proof_for_1[1], parent_right);
let proof_for_2 = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, 2); let proof_for_2 = generate_proof::<$hasher_type, u32, ()>(&tree, max_index, 2)
.ok()
.unwrap();
assert_eq!(proof_for_2[0], h3); assert_eq!(proof_for_2[0], h3);
assert_eq!(proof_for_2[1], parent_left); assert_eq!(proof_for_2[1], parent_left);
let proof_for_3 = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, 3); let proof_for_3 = generate_proof::<$hasher_type, u32, ()>(&tree, max_index, 3)
.ok()
.unwrap();
assert_eq!(proof_for_3[0], h2); assert_eq!(proof_for_3[0], h2);
assert_eq!(proof_for_3[1], parent_left); assert_eq!(proof_for_3[1], parent_left);
} }
@ -389,30 +476,38 @@ mod tests {
#[test] #[test]
fn test_proof_for_padded_tree_leaf() { fn test_proof_for_padded_tree_leaf() {
let max_index = 2u32; let max_index = 2u32;
let size_hint = 69;
let raw_data = sp_std::vec![ let raw_data = sp_std::vec![
(0, sp_std::vec![1]), (0, sp_std::vec![1; size_hint]),
(1, sp_std::vec![2]), (1, sp_std::vec![2; size_hint]),
(2, sp_std::vec![3]), (2, sp_std::vec![3; size_hint]),
]; ];
let tree = generate_tree::<TestBlake2Hasher, u32, _, _, ()>( let tree = generate_tree::<$hasher_type, u32, _, _, ()>(
max_index, max_index,
size_hint,
raw_data, raw_data,
|(index, bytes)| Ok((index, bytes)),
|(index, bytes), buffer| {
buffer.extend_from_slice(&bytes);
Ok(index as usize)
}
) )
.ok()
.unwrap(); .unwrap();
let proof = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, 2); let proof = generate_proof::<$hasher_type, u32, ()>(&tree, max_index, 2)
.ok()
.unwrap();
let mut empty_data = sp_std::vec![LEAF_PREFIX]; let mut empty_data = sp_std::vec![LEAF_PREFIX];
empty_data.extend_from_slice(&H256::zero().to_fixed_bytes()); empty_data.extend_from_slice(&<$hasher_type>::empty().to_fixed_bytes());
let empty_hash = TestBlake2Hasher::hash(&empty_data); let empty_hash = <$hasher_type>::hash(&empty_data);
assert_eq!(proof[0], empty_hash); assert_eq!(proof[0], empty_hash);
let root = *tree.last().unwrap(); let root = *tree.last().unwrap();
let is_valid = assert!(verify_tree_proof::<$hasher_type, u32>(&sp_std::vec![3; size_hint], &proof, root, 2));
verify_tree_proof::<TestBlake2Hasher, u32>(&sp_std::vec![3], &proof, root, 2);
assert!(is_valid);
} }
#[test] #[test]
@ -420,24 +515,39 @@ mod tests {
let max_index = 0u32; let max_index = 0u32;
let raw_data = sp_std::vec![(0, sp_std::vec![99])]; let raw_data = sp_std::vec![(0, sp_std::vec![99])];
let tree = generate_tree::<TestBlake2Hasher, u32, _, _, ()>( let tree = generate_tree::<$hasher_type, u32, _, _, ()>(
max_index, max_index,
1,
raw_data, raw_data,
|(index, bytes)| Ok((index, bytes)),
|(index, bytes), buffer| {
buffer.extend_from_slice(&bytes);
Ok(index as usize)
}
) )
.ok()
.unwrap();
let proof = generate_proof::<$hasher_type, u32, ()>(
&tree,
max_index,
0
)
.ok()
.unwrap(); .unwrap();
let proof = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, 0);
assert!(proof.is_empty()); assert!(proof.is_empty());
} }
#[test] #[test]
fn test_verify_tree_proof_with_h0_h3_and_random_values() { fn test_verify_tree_proof_with_h0_h3_and_random_values() {
let max_index = 3u32; let max_index = 3u32;
let leaf_0 = sp_std::vec![10u8]; let size_hint = 69;
let leaf_1 = sp_std::vec![20u8];
let leaf_2 = sp_std::vec![30u8]; let leaf_0 = sp_std::vec![10u8; 69];
let leaf_3 = sp_std::vec![40u8]; let leaf_1 = sp_std::vec![20u8; 69];
let leaf_2 = sp_std::vec![30u8; 69];
let leaf_3 = sp_std::vec![40u8; 69];
let raw_data = sp_std::vec![ let raw_data = sp_std::vec![
(0, leaf_0.clone()), (0, leaf_0.clone()),
@ -446,11 +556,17 @@ mod tests {
(3, leaf_3.clone()) (3, leaf_3.clone())
]; ];
let tree = generate_tree::<TestBlake2Hasher, u32, _, _, ()>( let tree = generate_tree::<$hasher_type, u32, _, _, ()>(
max_index, max_index,
size_hint,
raw_data, raw_data,
|(index, bytes)| Ok((index, bytes)),
|(index, bytes), buffer| {
buffer.extend_from_slice(&bytes);
Ok(index as usize)
}
) )
.ok()
.unwrap(); .unwrap();
let root = *tree.last().unwrap(); let root = *tree.last().unwrap();
@ -460,45 +576,35 @@ mod tests {
let h2 = tree[2]; let h2 = tree[2];
let h3 = tree[3]; let h3 = tree[3];
let proof_for_1 = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, 1); let proof_for_1 = generate_proof::<$hasher_type, u32, ()>(&tree, max_index, 1).ok().unwrap();
assert_eq!(proof_for_1[0], h0); assert_eq!(proof_for_1[0], h0);
let is_valid_1 = verify_tree_proof::<TestBlake2Hasher, u32>(&leaf_1, &proof_for_1, root, 1); let is_valid_1 = verify_tree_proof::<$hasher_type, u32>(&leaf_1, &proof_for_1, root, 1);
assert!(is_valid_1); assert!(is_valid_1);
let proof_for_2 = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, 2); let proof_for_2 = generate_proof::<$hasher_type, u32, ()>(&tree, max_index, 2).ok().unwrap();
assert_eq!(proof_for_2[0], h3); assert_eq!(proof_for_2[0], h3);
let is_valid_2 = verify_tree_proof::<TestBlake2Hasher, u32>(&leaf_2, &proof_for_2, root, 2); let is_valid_2 = verify_tree_proof::<$hasher_type, u32>(&leaf_2, &proof_for_2, root, 2);
assert!(is_valid_2); assert!(is_valid_2);
let proof_for_0 = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, 0); let proof_for_0 = generate_proof::<$hasher_type, u32, ()>(&tree, max_index, 0).ok().unwrap();
assert_eq!(proof_for_0[0], h1); assert_eq!(proof_for_0[0], h1);
assert!(verify_tree_proof::<TestBlake2Hasher, u32>( assert!(verify_tree_proof::<$hasher_type, u32>(&leaf_0, &proof_for_0, root, 0));
&leaf_0,
&proof_for_0,
root,
0
));
let proof_for_3 = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, 3); let proof_for_3 = generate_proof::<$hasher_type, u32, ()>(&tree, max_index, 3).ok().unwrap();
assert_eq!(proof_for_3[0], h2); assert_eq!(proof_for_3[0], h2);
assert!(verify_tree_proof::<TestBlake2Hasher, u32>( assert!(verify_tree_proof::<$hasher_type, u32>(&leaf_3, &proof_for_3, root, 3));
&leaf_3,
&proof_for_3,
root,
3
));
let mut bad_proof = proof_for_1.clone(); let mut bad_proof = proof_for_1.clone();
bad_proof[0] = H256::random(); bad_proof[0] = <$hasher_type>::empty();
let is_valid_bad_proof = let is_valid_bad_proof =
verify_tree_proof::<TestBlake2Hasher, u32>(&leaf_1, &bad_proof, root, 1); verify_tree_proof::<$hasher_type, u32>(&leaf_1, &bad_proof, root, 1);
assert!(!is_valid_bad_proof); assert!(!is_valid_bad_proof);
let bad_preimage = vec![99u8]; let bad_preimage = vec![99u8];
let is_valid_bad_preimage = let is_valid_bad_preimage =
verify_tree_proof::<TestBlake2Hasher, u32>(&bad_preimage, &proof_for_1, root, 1); verify_tree_proof::<$hasher_type, u32>(&bad_preimage, &proof_for_1, root, 1);
assert!(!is_valid_bad_preimage); assert!(!is_valid_bad_preimage);
} }
@ -511,11 +617,17 @@ mod tests {
raw_data.push((i, sp_std::vec![i as u8])); raw_data.push((i, sp_std::vec![i as u8]));
} }
let tree = generate_tree::<TestBlake2Hasher, u32, _, _, ()>( let tree = generate_tree::<$hasher_type, u32, _, _, ()>(
max_index, max_index,
1,
raw_data, raw_data,
|(index, bytes)| Ok((index, bytes)),
|(index, bytes), buffer| {
buffer.extend_from_slice(&bytes);
Ok(index as usize)
}
) )
.ok()
.unwrap(); .unwrap();
let root = *tree.last().unwrap(); let root = *tree.last().unwrap();
@ -525,50 +637,55 @@ mod tests {
let target_index = 11u32; let target_index = 11u32;
let preimage = vec![target_index as u8]; let preimage = vec![target_index as u8];
let proof = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, target_index); let proof = generate_proof::<$hasher_type, u32, ()>(&tree, max_index, target_index)
.ok()
.unwrap();
assert_eq!(proof.len(), 4); assert_eq!(proof.len(), 4);
let h10 = tree[10]; let h10 = tree[10];
assert_eq!(proof[0], h10); assert_eq!(proof[0], h10);
let is_valid = assert!(verify_tree_proof::<$hasher_type, u32>(&preimage, &proof, root, target_index));
verify_tree_proof::<TestBlake2Hasher, u32>(&preimage, &proof, root, target_index); assert!(!verify_tree_proof::<$hasher_type, u32>(&preimage, &proof, root, 4));
assert!(is_valid);
let is_valid_wrong_index =
verify_tree_proof::<TestBlake2Hasher, u32>(&preimage, &proof, root, 4);
assert!(!is_valid_wrong_index);
} }
#[test] #[test]
fn test_verification_index_at_upper_bound() { fn test_verification_index_at_upper_bound() {
let max_index = 3u32; let max_index = 3u32;
let size_hint = 420;
let raw_data = sp_std::vec![ let raw_data = sp_std::vec![
(0, sp_std::vec![1]), (0, sp_std::vec![1; size_hint]),
(1, sp_std::vec![2]), (1, sp_std::vec![2; size_hint]),
(2, sp_std::vec![3]), (2, sp_std::vec![3; size_hint]),
(3, sp_std::vec![4]), (3, sp_std::vec![4; size_hint]),
]; ];
let tree = generate_tree::<TestBlake2Hasher, u32, _, _, ()>( let tree = generate_tree::<$hasher_type, u32, _, _, ()>(
max_index, max_index,
size_hint,
raw_data, raw_data,
|(index, bytes)| Ok((index, bytes)),
|(index, bytes), buffer| {
buffer.extend_from_slice(&bytes);
Ok(index as usize)
}
) )
.ok()
.unwrap(); .unwrap();
let root = *tree.last().unwrap(); let root = *tree.last().unwrap();
let target_index = 3u32; let target_index = 3u32;
let proof = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, target_index); let proof = generate_proof::<$hasher_type, u32, ()>(&tree, max_index, target_index)
.ok()
.unwrap();
assert_eq!(proof.len(), 2); assert_eq!(proof.len(), 2);
let preimage = sp_std::vec![4]; let preimage = sp_std::vec![4; size_hint];
let is_valid = assert!(verify_tree_proof::<$hasher_type, u32>(&preimage, &proof, root, target_index));
verify_tree_proof::<TestBlake2Hasher, u32>(&preimage, &proof, root, target_index);
assert!(is_valid);
} }
#[test] #[test]
@ -581,23 +698,32 @@ mod tests {
(3, sp_std::vec![4]), (3, sp_std::vec![4]),
]; ];
let tree = generate_tree::<TestBlake2Hasher, u32, _, _, ()>( let tree = generate_tree::<$hasher_type, u32, _, _, ()>(
max_index, max_index,
1,
raw_data, raw_data,
|(index, bytes)| Ok((index, bytes)),
|(index, bytes), buffer| {
buffer.extend_from_slice(&bytes);
Ok(index as usize)
}
) )
.ok()
.unwrap(); .unwrap();
let root = *tree.last().unwrap(); let root = *tree.last().unwrap();
let proof = generate_proof::<$hasher_type, u32, ()>(&tree, max_index, 3u32).ok().unwrap();
let proof = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, 3u32);
let target_index = 4u32; let target_index = 4u32;
let preimage = sp_std::vec![4]; let preimage = sp_std::vec![4];
let is_valid = assert!(!verify_tree_proof::<$hasher_type, u32>(&preimage, &proof, root, target_index));
verify_tree_proof::<TestBlake2Hasher, u32>(&preimage, &proof, root, target_index);
assert!(!is_valid);
} }
}
}
}
run_merkle_tree_tests!(SubstrateBlake2Hasher, blake2_merkle_tree_tests);
run_merkle_tree_tests!(SubstrateKeccakHasher, keccak_merkle_tree_tests);
run_merkle_tree_tests!(UtxoSha2Hasher, sha2_merkle_tree_tests);
} }

View File

@ -3,6 +3,8 @@ use sp_std::vec::Vec;
const MAX_INPUTS: usize = 3; const MAX_INPUTS: usize = 3;
const MAX_OUTPUTS: usize = 4; const MAX_OUTPUTS: usize = 4;
const MAX_FLAGS_LEN: usize = 8;
const MAX_HASHES_LEN: usize = 16;
const MAX_SCRIPT_LEN: usize = 512; const MAX_SCRIPT_LEN: usize = 512;
pub const MAX_TX_LEN: usize = 4096; pub const MAX_TX_LEN: usize = 4096;
@ -27,20 +29,17 @@ pub struct Transaction<H: GhostHasher> {
lock_time: u32, lock_time: u32,
} }
pub struct Header<H: GhostHasher> { pub struct Header<H: GhostHasher>(sp_std::marker::PhantomData<H>);
pub merkle_root: H::Hash,
pub tx_count: u32,
pub tree_hashes: Vec<H::Hash>,
pub flags: Vec<u8>,
}
impl<H: GhostHasher> Header<H> { impl<H: GhostHasher> Header<H> {
fn calc_tree_width(&self, h: u32) -> u32 { fn calc_tree_width(tx_count: u32, h: u32) -> u32 {
(self.tx_count + (1 << h) - 1) >> h (tx_count + (1 << h) - 1) >> h
} }
fn traverse( fn traverse(
&self, tx_count: u32,
flags: &[u8],
tree_hashes: &[H::Hash],
height: u32, height: u32,
pos: u32, pos: u32,
bits_used: &mut u32, bits_used: &mut u32,
@ -51,19 +50,19 @@ impl<H: GhostHasher> Header<H> {
let byte_idx = (*bits_used / 8) as usize; let byte_idx = (*bits_used / 8) as usize;
let bit_idx = (*bits_used % 8) as u8; let bit_idx = (*bits_used % 8) as u8;
if byte_idx >= self.flags.len() { if byte_idx >= flags.len() {
return Err(ParseError::InvalidVarint(VarintError::BufferTooShort)); return Err(ParseError::InvalidVarint(VarintError::BufferTooShort));
} }
let parent_of_match = ((self.flags[byte_idx] >> bit_idx) & 1) == 1; let parent_of_match = ((flags[byte_idx] >> bit_idx) & 1) == 1;
*bits_used += 1; *bits_used += 1;
if height == 0 || !parent_of_match { if height == 0 || !parent_of_match {
if *hash_used as usize >= self.tree_hashes.len() { if *hash_used as usize >= tree_hashes.len() {
return Err(ParseError::InvalidLength); return Err(ParseError::InvalidLength);
} }
let curr_hash = self.tree_hashes[*hash_used as usize]; let curr_hash = tree_hashes[*hash_used as usize];
*hash_used += 1; *hash_used += 1;
if height == 0 && parent_of_match && curr_hash == target_txid { if height == 0 && parent_of_match && curr_hash == target_txid {
@ -73,7 +72,10 @@ impl<H: GhostHasher> Header<H> {
return Ok(curr_hash); return Ok(curr_hash);
} }
let left = self.traverse( let left = Self::traverse(
tx_count,
flags,
tree_hashes,
height - 1, height - 1,
pos * 2, pos * 2,
bits_used, bits_used,
@ -82,8 +84,11 @@ impl<H: GhostHasher> Header<H> {
target_txid, target_txid,
)?; )?;
let right = if pos * 2 + 1 < self.calc_tree_width(height - 1) { let right = if pos * 2 + 1 < Self::calc_tree_width(tx_count, height - 1) {
let right = self.traverse( let right = Self::traverse(
tx_count,
flags,
tree_hashes,
height - 1, height - 1,
pos * 2 + 1, pos * 2 + 1,
bits_used, bits_used,
@ -109,17 +114,83 @@ impl<H: GhostHasher> Header<H> {
Ok(current_node_hash) Ok(current_node_hash)
} }
pub fn extract_proof(&self, target_txid: H::Hash) -> Result<H::Hash, ParseError> { pub fn verify_utxo_merkle_proof(
txproof_bytes: &[u8],
target_txid: H::Hash,
) -> Result<(), ParseError> {
if txproof_bytes.len() < 85 {
return Err(ParseError::InvalidLength);
}
let header_bytes = &txproof_bytes[..80];
let mut bytes = &txproof_bytes[80..];
let mut merkle_root = [0u8; 32];
merkle_root.copy_from_slice(&header_bytes[36..68]);
if bytes.len() < 4 {
return Err(ParseError::InvalidLength);
}
let tx_count = u32::from_le_bytes(
bytes[..4]
.try_into()
.map_err(|_| ParseError::TypeConversionFailed)?,
);
bytes = &bytes[4..];
let hash_count = read_varint(&mut bytes)?;
if hash_count > MAX_HASHES_LEN {
return Err(ParseError::InvalidLength);
}
let hash_count_bytes = hash_count
.checked_mul(32)
.ok_or(ParseError::TypeConversionFailed)?;
if bytes.len() < hash_count_bytes {
return Err(ParseError::InvalidLength);
}
let tree_hashes_bytes = &bytes[..hash_count_bytes];
bytes = &bytes[hash_count_bytes..];
let tree_hashes: Vec<H::Hash> = tree_hashes_bytes
.chunks_exact(32)
.map(|chunk| H::from_slice(chunk))
.collect();
let flags_count = read_varint(&mut bytes)?;
if flags_count > MAX_FLAGS_LEN {
return Err(ParseError::InvalidLength);
}
if bytes.len() < flags_count {
return Err(ParseError::InvalidLength);
}
let flags = bytes[..flags_count].to_vec();
bytes = &bytes[flags_count..];
if !bytes.is_empty() {
return Err(ParseError::InvalidLength);
}
let mut bits_used = 0u32; let mut bits_used = 0u32;
let mut hash_used = 0u32; let mut hash_used = 0u32;
let mut matched_index = None; let mut matched_index = None;
let mut max_height = 0; let mut max_height = 0;
while (1 << max_height) < self.tx_count {
while (1 << max_height) < tx_count {
max_height += 1; max_height += 1;
} }
let computed_root = self.traverse( let computed_root = Self::traverse(
tx_count,
&flags,
&tree_hashes,
max_height, max_height,
0, 0,
&mut bits_used, &mut bits_used,
@ -128,19 +199,23 @@ impl<H: GhostHasher> Header<H> {
target_txid, target_txid,
)?; )?;
if hash_used as usize != self.tree_hashes.len() { if hash_used as usize != tree_hashes.len() {
return Err(ParseError::InvalidLength); return Err(ParseError::InvalidLength);
} }
if (bits_used + 7) / 8 != self.flags.len() as u32 { if (bits_used + 7) / 8 != flags.len() as u32 {
return Err(ParseError::InvalidLength); return Err(ParseError::InvalidLength);
} }
if matched_index.is_some() { if matched_index.is_none() {
Ok(computed_root) return Err(ParseError::TransactionNotFoundInProof);
} else {
Err(ParseError::TransactionNotFoundInProof)
} }
if computed_root != H::from_slice(&merkle_root) {
return Err(ParseError::InvalidMerkleRoot);
}
Ok(())
} }
} }
@ -357,6 +432,9 @@ fn read_varint(bytes: &mut &[u8]) -> Result<usize, VarintError> {
.try_into() .try_into()
.map_err(|_| VarintError::TypeConversionFailed)?, .map_err(|_| VarintError::TypeConversionFailed)?,
); );
if val < 0xfd {
return Err(VarintError::TypeConversionFailed);
}
*bytes = &bytes[2..]; *bytes = &bytes[2..];
Ok(val as usize) Ok(val as usize)
} }
@ -369,6 +447,9 @@ fn read_varint(bytes: &mut &[u8]) -> Result<usize, VarintError> {
.try_into() .try_into()
.map_err(|_| VarintError::TypeConversionFailed)?, .map_err(|_| VarintError::TypeConversionFailed)?,
); );
if val < 0x10000 {
return Err(VarintError::TypeConversionFailed);
}
*bytes = &bytes[4..]; *bytes = &bytes[4..];
Ok(val as usize) Ok(val as usize)
} }
@ -381,6 +462,9 @@ fn read_varint(bytes: &mut &[u8]) -> Result<usize, VarintError> {
.try_into() .try_into()
.map_err(|_| VarintError::TypeConversionFailed)?, .map_err(|_| VarintError::TypeConversionFailed)?,
); );
if val < 0x100000000 {
return Err(VarintError::TypeConversionFailed);
}
*bytes = &bytes[8..]; *bytes = &bytes[8..];
Ok(val as usize) Ok(val as usize)
} }
@ -392,6 +476,7 @@ fn read_varint(bytes: &mut &[u8]) -> Result<usize, VarintError> {
pub enum ParseError { pub enum ParseError {
InvalidLength, InvalidLength,
ExceededMaxLimits, ExceededMaxLimits,
InvalidMerkleRoot,
TypeConversionFailed, TypeConversionFailed,
TransactionNotFoundInProof, TransactionNotFoundInProof,
InvalidVarint(VarintError), InvalidVarint(VarintError),
@ -525,60 +610,3 @@ impl<'a, H: GhostHasher> TryFrom<&'a [u8]> for Transaction<H> {
}) })
} }
} }
impl<'a, H: GhostHasher> TryFrom<&'a [u8]> for Header<H> {
type Error = ParseError;
fn try_from(mut bytes: &[u8]) -> Result<Self, Self::Error> {
if bytes.len() < 85 {
return Err(ParseError::InvalidLength);
}
let header_bytes = &bytes[..80];
bytes = &bytes[80..];
let mut merkle_root = [0u8; 32];
merkle_root.copy_from_slice(&header_bytes[36..68]);
if bytes.len() < 4 {
return Err(ParseError::InvalidLength);
}
let tx_count = u32::from_le_bytes(
bytes[..4]
.try_into()
.map_err(|_| ParseError::TypeConversionFailed)?,
);
bytes = &bytes[4..];
let hash_count = read_varint(&mut bytes)?;
if bytes.len() < hash_count * 32 {
return Err(ParseError::InvalidLength);
}
let tree_hashes_bytes = &bytes[..hash_count * 32];
bytes = &bytes[hash_count * 32..];
let tree_hashes: Vec<H::Hash> = tree_hashes_bytes
.chunks_exact(32)
.map(|chunk| H::from_slice(chunk))
.collect();
let flags_count = read_varint(&mut bytes)?;
if bytes.len() < flags_count {
return Err(ParseError::InvalidLength);
}
let flags = bytes[..flags_count].to_vec();
bytes = &bytes[flags_count..];
if !bytes.is_empty() {
return Err(ParseError::InvalidLength);
}
Ok(Self {
merkle_root: H::from_slice(&merkle_root),
tx_count,
tree_hashes,
flags,
})
}
}

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ghost-networks" name = "ghost-networks"
version = "0.2.10" version = "0.2.12"
description = "Registry and lifecycle management for external network metadata and cryptographic profiles." description = "Registry and lifecycle management for external network metadata and cryptographic profiles."
license.workspace = true license.workspace = true
authors.workspace = true authors.workspace = true

View File

@ -197,17 +197,21 @@ pub mod module {
StorageValue<_, BoundedVec<T::NetworkId, T::MaxNetworks>, ValueQuery>; StorageValue<_, BoundedVec<T::NetworkId, T::MaxNetworks>, ValueQuery>;
#[pallet::storage] #[pallet::storage]
#[pallet::getter(fn network_curves)] #[pallet::getter(fn network_types)]
pub type NetworkCurves<T: Config> = StorageMap< pub type NetworkTypes<T: Config> = StorageMap<
_, _,
Twox64Concat, NetworkCurve, Twox64Concat, NetworkCurve,
BoundedBTreeMap<NetworkType, u32, T::MaxNetworks>, BoundedBTreeMap<
NetworkType,
BoundedVec<T::NetworkId, T::MaxNetworks>,
T::MaxNetworks,
>,
ValueQuery, ValueQuery,
>; >;
#[pallet::storage] #[pallet::storage]
#[pallet::getter(fn network_curves_vec)] #[pallet::getter(fn network_curves)]
pub type NetworkCurvesVec<T: Config> = pub type NetworkCurves<T: Config> =
StorageValue<_, BoundedVec<NetworkCurve, T::MaxNetworks>, ValueQuery>; StorageValue<_, BoundedVec<NetworkCurve, T::MaxNetworks>, ValueQuery>;
#[pallet::storage] #[pallet::storage]
@ -429,24 +433,26 @@ impl<T: Config> Pallet<T> {
modified_indexes.try_push(chain_id.clone()) modified_indexes.try_push(chain_id.clone())
.map_err(|_| Error::<T>::TooManyNetworks)?; .map_err(|_| Error::<T>::TooManyNetworks)?;
let mut modified_curves_map = NetworkCurves::<T>::get(network_curve); let mut modified_types = NetworkTypes::<T>::get(network_curve);
if let Some(counter) = modified_curves_map.get_mut(&network_type) { if let Some(chain_ids) = modified_types.get_mut(&network_type) {
*counter = counter.saturating_add(1); chain_ids.try_push(chain_id).map_err(|_| Error::<T>::TooManyNetworks)?;
} else { } else {
modified_curves_map.try_insert(network_type, 1u32) let mut new_ids_vec = BoundedVec::<T::NetworkId, T::MaxNetworks>::default();
new_ids_vec.try_push(chain_id).map_err(|_| Error::<T>::TooManyNetworks)?;
modified_types.try_insert(network_type, new_ids_vec)
.map_err(|_| Error::<T>::TooManyNetworks)?; .map_err(|_| Error::<T>::TooManyNetworks)?;
} }
let mut modified_curves_vec = NetworkCurvesVec::<T>::get(); let mut modified_curves = NetworkCurves::<T>::get();
if !modified_curves_vec.contains(&network_curve) { if !modified_curves.contains(&network_curve) {
modified_curves_vec.try_push(network_curve) modified_curves.try_push(network_curve)
.map_err(|_| Error::<T>::TooManyNetworks)?; .map_err(|_| Error::<T>::TooManyNetworks)?;
} }
Networks::<T>::insert(&chain_id, network); Networks::<T>::insert(&chain_id, network);
NetworkIndexes::<T>::put(modified_indexes); NetworkIndexes::<T>::put(modified_indexes);
NetworkCurves::<T>::insert(network_curve, modified_curves_map); NetworkTypes::<T>::insert(network_curve, modified_types);
NetworkCurvesVec::<T>::put(modified_curves_vec); NetworkCurves::<T>::put(modified_curves);
Self::deposit_event(Event::<T>::NetworkRegistered { chain_id }); Self::deposit_event(Event::<T>::NetworkRegistered { chain_id });
Ok(()) Ok(())
@ -460,38 +466,38 @@ impl<T: Config> Pallet<T> {
let network_curve = network.curve; let network_curve = network.curve;
let network_type = network.r#type; let network_type = network.r#type;
let mut modified_curves_map = NetworkCurves::<T>::get(network_curve); let mut modified_types = NetworkTypes::<T>::get(network_curve);
let counter = modified_curves_map let chain_ids = modified_types
.get_mut(&network_type) .get_mut(&network_type)
.ok_or(Error::<T>::NetworkTypeDoesNotExist)?; .ok_or(Error::<T>::NetworkTypeDoesNotExist)?;
*counter = counter.saturating_sub(1); chain_ids.retain(|id| id != &chain_id);
let mut should_remove_curve_from_vec = false; let mut should_remove_curve = false;
if *counter == 0 { if chain_ids.is_empty() {
modified_curves_map.remove(&network_type); modified_types.remove(&network_type);
if modified_curves_map.is_empty() { if modified_types.is_empty() {
should_remove_curve_from_vec = true; should_remove_curve = true;
} }
} }
let mut modified_indexes = NetworkIndexes::<T>::get(); let mut modified_indexes = NetworkIndexes::<T>::get();
modified_indexes.retain(|id| id != &chain_id); modified_indexes.retain(|id| id != &chain_id);
let mut modified_curves_vec = NetworkCurvesVec::<T>::get(); let mut modified_curves = NetworkCurves::<T>::get();
if should_remove_curve_from_vec { if should_remove_curve {
modified_curves_vec.retain(|curve| curve != &network_curve); modified_curves.retain(|curve| curve != &network_curve);
} }
Networks::<T>::remove(&chain_id); Networks::<T>::remove(&chain_id);
NetworkIndexes::<T>::put(modified_indexes); NetworkIndexes::<T>::put(modified_indexes);
NetworkCurvesVec::<T>::put(modified_curves_vec); NetworkCurves::<T>::put(modified_curves);
if modified_curves_map.is_empty() { if modified_types.is_empty() {
NetworkCurves::<T>::remove(network_curve); NetworkTypes::<T>::remove(network_curve);
} else { } else {
NetworkCurves::<T>::insert(network_curve, modified_curves_map); NetworkTypes::<T>::insert(network_curve, modified_types);
} }
Self::deposit_event(Event::<T>::NetworkRemoved { chain_id }); Self::deposit_event(Event::<T>::NetworkRemoved { chain_id });
@ -625,15 +631,46 @@ impl<T: Config> Pallet<T> {
chain_id: T::NetworkId, chain_id: T::NetworkId,
network_type: NetworkType, network_type: NetworkType,
) -> DispatchResult { ) -> DispatchResult {
Networks::<T>::try_mutate(&chain_id, |maybe_network| -> DispatchResult { let mut network = Networks::<T>::get(&chain_id)
let network = maybe_network.as_mut().ok_or(Error::<T>::NetworkDoesNotExist)?; .ok_or(Error::<T>::NetworkDoesNotExist)?;
let network_curve = network.curve;
let prev_type = network.r#type;
if prev_type == network_type {
return Ok(());
}
let mut modified_types = NetworkTypes::<T>::get(network_curve);
let prev_chain_ids = modified_types
.get_mut(&prev_type)
.ok_or(Error::<T>::NetworkTypeDoesNotExist)?;
prev_chain_ids.retain(|id| id != &chain_id);
if prev_chain_ids.is_empty() {
modified_types.remove(&prev_type);
}
if let Some(chain_ids) = modified_types.get_mut(&network_type) {
chain_ids.try_push(chain_id).map_err(|_| Error::<T>::TooManyNetworks)?;
} else {
let mut new_ids_vec = BoundedVec::<T::NetworkId, T::MaxNetworks>::default();
new_ids_vec.try_push(chain_id).map_err(|_| Error::<T>::TooManyNetworks)?;
modified_types.try_insert(network_type, new_ids_vec)
.map_err(|_| Error::<T>::TooManyNetworks)?;
}
network.r#type = network_type; network.r#type = network_type;
Ok(())
})?; Networks::<T>::insert(&chain_id, network);
NetworkTypes::<T>::insert(network_curve, modified_types);
Self::deposit_event(Event::<T>::NetworkTypeUpdated { Self::deposit_event(Event::<T>::NetworkTypeUpdated {
chain_id, chain_id,
network_type, network_type,
}); });
Ok(()) Ok(())
} }
@ -652,50 +689,53 @@ impl<T: Config> Pallet<T> {
return Ok(()); return Ok(());
} }
let mut modified_prev_map = NetworkCurves::<T>::get(prev_curve); let mut modified_prev_types = NetworkTypes::<T>::get(prev_curve);
let counter = modified_prev_map let prev_chain_ids = modified_prev_types
.get_mut(&network_type) .get_mut(&network_type)
.ok_or(Error::<T>::NetworkTypeDoesNotExist)?; .ok_or(Error::<T>::NetworkTypeDoesNotExist)?;
*counter = counter.saturating_sub(1); prev_chain_ids.retain(|id| id != &chain_id);
let mut should_remove_prev_from_vec = false; let mut should_remove_prev_curve = false;
if *counter == 0 { if prev_chain_ids.is_empty() {
modified_prev_map.remove(&network_type); modified_prev_types.remove(&network_type);
if modified_prev_map.is_empty() { if modified_prev_types.is_empty() {
should_remove_prev_from_vec = true; should_remove_prev_curve = true;
} }
} }
let mut modified_curr_map = NetworkCurves::<T>::get(network_curve); let mut modified_curr_types = NetworkTypes::<T>::get(network_curve);
if let Some(counter) = modified_curr_map.get_mut(&network_type) { if let Some(chain_ids) = modified_curr_types.get_mut(&network_type) {
*counter = counter.saturating_add(1); chain_ids.try_push(chain_id)
.map_err(|_| Error::<T>::TooManyNetworks)?;
} else { } else {
modified_curr_map.try_insert(network_type, 1u32) let mut new_ids_vec = BoundedVec::<T::NetworkId, T::MaxNetworks>::default();
new_ids_vec.try_push(chain_id).map_err(|_| Error::<T>::TooManyNetworks)?;
modified_curr_types.try_insert(network_type, new_ids_vec)
.map_err(|_| Error::<T>::TooManyNetworks)?; .map_err(|_| Error::<T>::TooManyNetworks)?;
} }
let mut modified_curves_vec = NetworkCurvesVec::<T>::get(); let mut modified_curves = NetworkCurves::<T>::get();
if should_remove_prev_from_vec { if should_remove_prev_curve {
modified_curves_vec.retain(|curve| curve != &prev_curve); modified_curves.retain(|curve| curve != &prev_curve);
} }
if !modified_curves_vec.contains(&network_curve) { if !modified_curves.contains(&network_curve) {
modified_curves_vec.try_push(network_curve) modified_curves.try_push(network_curve)
.map_err(|_| Error::<T>::TooManyNetworks)?; .map_err(|_| Error::<T>::TooManyNetworks)?;
} }
network.curve = network_curve; network.curve = network_curve;
Networks::<T>::insert(&chain_id, network); Networks::<T>::insert(&chain_id, network);
NetworkCurves::<T>::insert(network_curve, modified_curr_map); NetworkTypes::<T>::insert(network_curve, modified_curr_types);
NetworkCurvesVec::<T>::put(modified_curves_vec); NetworkCurves::<T>::put(modified_curves);
if modified_prev_map.is_empty() { if modified_prev_types.is_empty() {
NetworkCurves::<T>::remove(prev_curve); NetworkTypes::<T>::remove(prev_curve);
} else { } else {
NetworkCurves::<T>::insert(prev_curve, modified_prev_map); NetworkTypes::<T>::insert(prev_curve, modified_prev_types);
} }
Self::deposit_event(Event::<T>::NetworkCurveUpdated { Self::deposit_event(Event::<T>::NetworkCurveUpdated {
@ -761,9 +801,12 @@ impl<T: Config> NetworkDataBasicHandler for Pallet<T> {
type NetworkId = T::NetworkId; type NetworkId = T::NetworkId;
type NetworkType = NetworkType; type NetworkType = NetworkType;
type NetworkCurve = NetworkCurve; type NetworkCurve = NetworkCurve;
type MaxNetworks = T::MaxNetworks;
} }
impl<T: Config> NetworkDataInspectHandler<NetworkData> for Pallet<T> { impl<T: Config> NetworkDataInspectHandler<NetworkData> for Pallet<T> {
type TypesItem = BoundedVec<T::NetworkId, T::MaxNetworks>;
fn count() -> u32 { fn count() -> u32 {
NetworkIndexes::<T>::decode_len() NetworkIndexes::<T>::decode_len()
.map(|len| len as u32) .map(|len| len as u32)
@ -775,7 +818,7 @@ impl<T: Config> NetworkDataInspectHandler<NetworkData> for Pallet<T> {
} }
fn curve_exists(curve: &Self::NetworkCurve) -> bool { fn curve_exists(curve: &Self::NetworkCurve) -> bool {
NetworkCurves::<T>::contains_key(curve) NetworkTypes::<T>::contains_key(curve)
} }
fn get(n: &Self::NetworkId) -> Option<NetworkData> { fn get(n: &Self::NetworkId) -> Option<NetworkData> {
@ -804,15 +847,17 @@ impl<T: Config> NetworkDataInspectHandler<NetworkData> for Pallet<T> {
} }
fn iter_curves() -> impl Iterator<Item = Self::NetworkCurve> { fn iter_curves() -> impl Iterator<Item = Self::NetworkCurve> {
NetworkCurvesVec::<T>::get().into_iter() NetworkCurves::<T>::get().into_iter()
} }
fn iter_types_by_curve( fn iter_types_by_curve(
curve: &Self::NetworkCurve curve: &Self::NetworkCurve
) -> impl Iterator<Item = Self::NetworkType> { ) -> impl Iterator<Item = (Self::NetworkType, Self::TypesItem)> {
NetworkCurves::<T>::get(curve) let network_types = NetworkTypes::<T>::get(curve);
.into_iter()
.map(|(network_type, _)| network_type) network_types.into_iter().map(|(network_type, network_ids)| {
(network_type, network_ids)
})
} }
} }
@ -833,23 +878,25 @@ impl<T: Config> NetworkDataMutateHandler<NetworkData, BalanceOf<T>> for Pallet<T
.map(|network| network.incoming_share) .map(|network| network.incoming_share)
.ok_or(())?; .ok_or(())?;
GatekeeperAmounts::<T>::try_mutate(network_id, |gatekeeper_amount| -> Result<(), ()> { let increased_gatekeeper_amount = GatekeeperAmounts::<T>::get(network_id)
*gatekeeper_amount = gatekeeper_amount.checked_add(&amount).ok_or(())?; .checked_add(&amount)
Ok(()) .ok_or(())?;
})?;
let curve_share = Perbill::from_parts(incoming_share).mul_ceil(amount); let curve_share = Perbill::from_parts(incoming_share).mul_ceil(amount);
let final_amount = amount.saturating_sub(curve_share); let final_amount = amount.saturating_sub(curve_share);
NetworkImbalance::<T>::try_mutate(|imbalance_state| -> Result<(), ()> { let mut imbalance_state = NetworkImbalance::<T>::get();
imbalance_state.incoming = imbalance_state.incoming imbalance_state.incoming = imbalance_state.incoming
.checked_add(&final_amount) .checked_add(&final_amount)
.ok_or(())?; .ok_or(())?;
imbalance_state.curve_share = imbalance_state.curve_share imbalance_state.curve_share = imbalance_state.curve_share
.checked_add(&curve_share) .checked_add(&curve_share)
.ok_or(())?; .ok_or(())?;
Ok(())
})?; GatekeeperAmounts::<T>::insert(network_id, increased_gatekeeper_amount);
NetworkImbalance::<T>::put(imbalance_state);
Ok(final_amount) Ok(final_amount)
} }
@ -862,23 +909,25 @@ impl<T: Config> NetworkDataMutateHandler<NetworkData, BalanceOf<T>> for Pallet<T
.map(|network| network.outgoing_share) .map(|network| network.outgoing_share)
.ok_or(())?; .ok_or(())?;
GatekeeperAmounts::<T>::try_mutate(network_id, |gatekeeper_amount| -> Result<(), ()> { let decreased_gatekeeper_amount = GatekeeperAmounts::<T>::get(network_id)
*gatekeeper_amount = gatekeeper_amount.checked_sub(&amount).ok_or(())?; .checked_sub(&amount)
Ok(()) .ok_or(())?;
})?;
let curve_share = Perbill::from_parts(outgoing_share).mul_ceil(amount); let curve_share = Perbill::from_parts(outgoing_share).mul_ceil(amount);
let final_amount = amount.saturating_sub(curve_share); let final_amount = amount.saturating_sub(curve_share);
NetworkImbalance::<T>::try_mutate(|imbalance_state| -> Result<(), ()> { let mut imbalance_state = NetworkImbalance::<T>::get();
imbalance_state.outgoing = imbalance_state.outgoing imbalance_state.outgoing = imbalance_state.outgoing
.checked_add(&final_amount) .checked_add(&final_amount)
.ok_or(())?; .ok_or(())?;
imbalance_state.curve_share = imbalance_state.curve_share imbalance_state.curve_share = imbalance_state.curve_share
.checked_add(&curve_share) .checked_add(&curve_share)
.ok_or(())?; .ok_or(())?;
Ok(())
})?; GatekeeperAmounts::<T>::insert(network_id, decreased_gatekeeper_amount);
NetworkImbalance::<T>::put(imbalance_state);
Ok(final_amount) Ok(final_amount)
} }

View File

@ -45,8 +45,8 @@ fn register_and_check_network(chain_id: u32, network: NetworkData) {
)); ));
assert_eq!(Networks::<Test>::get(chain_id), Some(network)); assert_eq!(Networks::<Test>::get(chain_id), Some(network));
assert_eq!(NetworkIndexes::<Test>::get(), vec![chain_id]); assert_eq!(NetworkIndexes::<Test>::get(), vec![chain_id]);
assert!(NetworkCurves::<Test>::contains_key(&network_curve)); assert!(NetworkTypes::<Test>::contains_key(&network_curve));
assert!(NetworkCurvesVec::<Test>::get().contains(&network_curve)); assert!(NetworkCurves::<Test>::get().contains(&network_curve));
} }
#[test] #[test]
@ -55,8 +55,8 @@ fn could_add_network_from_authority() {
let (chain_id, network) = prepare_network_data(None, None); let (chain_id, network) = prepare_network_data(None, None);
assert_eq!(Networks::<Test>::get(chain_id), None); assert_eq!(Networks::<Test>::get(chain_id), None);
assert_eq!(NetworkIndexes::<Test>::get(), vec![]); assert_eq!(NetworkIndexes::<Test>::get(), vec![]);
assert!(!NetworkCurves::<Test>::contains_key(&CURRENT_CURVE)); assert!(!NetworkTypes::<Test>::contains_key(&CURRENT_CURVE));
assert!(!NetworkCurvesVec::<Test>::get().contains(&CURRENT_CURVE)); assert!(!NetworkCurves::<Test>::get().contains(&CURRENT_CURVE));
assert_ok!(GhostNetworks::register_network( assert_ok!(GhostNetworks::register_network(
RuntimeOrigin::signed(RegistererAccount::get()), RuntimeOrigin::signed(RegistererAccount::get()),
@ -68,8 +68,8 @@ fn could_add_network_from_authority() {
)); ));
assert_eq!(Networks::<Test>::get(chain_id), Some(network)); assert_eq!(Networks::<Test>::get(chain_id), Some(network));
assert_eq!(NetworkIndexes::<Test>::get(), vec![chain_id]); assert_eq!(NetworkIndexes::<Test>::get(), vec![chain_id]);
assert!(NetworkCurves::<Test>::contains_key(&CURRENT_CURVE)); assert!(NetworkTypes::<Test>::contains_key(&CURRENT_CURVE));
assert!(NetworkCurvesVec::<Test>::get().contains(&CURRENT_CURVE)); assert!(NetworkCurves::<Test>::get().contains(&CURRENT_CURVE));
}); });
} }
@ -79,8 +79,8 @@ fn could_not_add_network_from_random_account() {
let (chain_id, network) = prepare_network_data(None, None); let (chain_id, network) = prepare_network_data(None, None);
assert_eq!(Networks::<Test>::get(chain_id), None); assert_eq!(Networks::<Test>::get(chain_id), None);
assert_eq!(NetworkIndexes::<Test>::get(), vec![]); assert_eq!(NetworkIndexes::<Test>::get(), vec![]);
assert!(!NetworkCurves::<Test>::contains_key(&CURRENT_CURVE)); assert!(!NetworkTypes::<Test>::contains_key(&CURRENT_CURVE));
assert!(!NetworkCurvesVec::<Test>::get().contains(&CURRENT_CURVE)); assert!(!NetworkCurves::<Test>::get().contains(&CURRENT_CURVE));
assert_err!( assert_err!(
GhostNetworks::register_network( GhostNetworks::register_network(
@ -108,8 +108,8 @@ fn could_not_add_network_from_random_account() {
); );
assert_eq!(Networks::<Test>::get(chain_id), None); assert_eq!(Networks::<Test>::get(chain_id), None);
assert_eq!(NetworkIndexes::<Test>::get(), vec![]); assert_eq!(NetworkIndexes::<Test>::get(), vec![]);
assert!(!NetworkCurves::<Test>::contains_key(&CURRENT_CURVE)); assert!(!NetworkTypes::<Test>::contains_key(&CURRENT_CURVE));
assert!(!NetworkCurvesVec::<Test>::get().contains(&CURRENT_CURVE)); assert!(!NetworkCurves::<Test>::get().contains(&CURRENT_CURVE));
}); });
} }
@ -351,10 +351,10 @@ fn could_update_network_curve_from_authority_account() {
let (chain_id, network) = prepare_network_data(None, None); let (chain_id, network) = prepare_network_data(None, None);
register_and_check_network(chain_id, network.clone()); register_and_check_network(chain_id, network.clone());
assert!(!NetworkCurves::<Test>::contains_key(&new_curve)); assert!(!NetworkTypes::<Test>::contains_key(&new_curve));
assert!(NetworkCurves::<Test>::contains_key(&CURRENT_CURVE)); assert!(NetworkTypes::<Test>::contains_key(&CURRENT_CURVE));
assert!(!NetworkCurvesVec::<Test>::get().contains(&new_curve)); assert!(!NetworkCurves::<Test>::get().contains(&new_curve));
assert!(NetworkCurvesVec::<Test>::get().contains(&CURRENT_CURVE)); assert!(NetworkCurves::<Test>::get().contains(&CURRENT_CURVE));
assert_ok!(GhostNetworks::update_network_curve( assert_ok!(GhostNetworks::update_network_curve(
RuntimeOrigin::signed(UpdaterAccount::get()), RuntimeOrigin::signed(UpdaterAccount::get()),
@ -370,10 +370,10 @@ fn could_update_network_curve_from_authority_account() {
let mut final_network = network.clone(); let mut final_network = network.clone();
final_network.r#curve = new_curve; final_network.r#curve = new_curve;
assert!(NetworkCurves::<Test>::contains_key(&new_curve)); assert!(NetworkTypes::<Test>::contains_key(&new_curve));
assert!(!NetworkCurves::<Test>::contains_key(&CURRENT_CURVE)); assert!(!NetworkTypes::<Test>::contains_key(&CURRENT_CURVE));
assert!(NetworkCurvesVec::<Test>::get().contains(&new_curve)); assert!(NetworkCurves::<Test>::get().contains(&new_curve));
assert!(!NetworkCurvesVec::<Test>::get().contains(&CURRENT_CURVE)); assert!(!NetworkCurves::<Test>::get().contains(&CURRENT_CURVE));
assert_eq!(Networks::<Test>::get(chain_id), Some(final_network.clone())); assert_eq!(Networks::<Test>::get(chain_id), Some(final_network.clone()));
assert_ne!(network, final_network); assert_ne!(network, final_network);
@ -1050,8 +1050,8 @@ fn could_remove_network_from_authority_account() {
)); ));
assert_eq!(Networks::<Test>::get(chain_id), None); assert_eq!(Networks::<Test>::get(chain_id), None);
assert_eq!(NetworkIndexes::<Test>::get(), vec![]); assert_eq!(NetworkIndexes::<Test>::get(), vec![]);
assert!(!NetworkCurves::<Test>::contains_key(&CURRENT_CURVE)); assert!(!NetworkTypes::<Test>::contains_key(&CURRENT_CURVE));
assert!(!NetworkCurvesVec::<Test>::get().contains(&CURRENT_CURVE)); assert!(!NetworkCurves::<Test>::get().contains(&CURRENT_CURVE));
}); });
} }
@ -1077,8 +1077,8 @@ fn could_not_remove_network_from_random_account() {
); );
assert_eq!(Networks::<Test>::get(chain_id), Some(network)); assert_eq!(Networks::<Test>::get(chain_id), Some(network));
assert_eq!(NetworkIndexes::<Test>::get(), vec![chain_id]); assert_eq!(NetworkIndexes::<Test>::get(), vec![chain_id]);
assert!(NetworkCurves::<Test>::contains_key(&CURRENT_CURVE)); assert!(NetworkTypes::<Test>::contains_key(&CURRENT_CURVE));
assert!(NetworkCurvesVec::<Test>::get().contains(&CURRENT_CURVE)); assert!(NetworkCurves::<Test>::get().contains(&CURRENT_CURVE));
}); });
} }
@ -1093,8 +1093,8 @@ fn could_not_remove_non_existent_network() {
); );
assert_eq!(Networks::<Test>::get(chain_id), None); assert_eq!(Networks::<Test>::get(chain_id), None);
assert_eq!(NetworkIndexes::<Test>::get(), vec![]); assert_eq!(NetworkIndexes::<Test>::get(), vec![]);
assert!(!NetworkCurves::<Test>::contains_key(&CURRENT_CURVE)); assert!(!NetworkTypes::<Test>::contains_key(&CURRENT_CURVE));
assert!(!NetworkCurvesVec::<Test>::get().contains(&CURRENT_CURVE)); assert!(!NetworkCurves::<Test>::get().contains(&CURRENT_CURVE));
}); });
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ghost-traits" name = "ghost-traits"
version = "0.4.6" version = "0.4.10"
description = "Shared traits including `GhostHasher`, `NetworkDataBasicHandler`, `BoundedBTreeMap`, `MerkleTree` and more." description = "Shared traits including `GhostHasher`, `NetworkDataBasicHandler`, `BoundedBTreeMap`, `MerkleTree` and more."
license.workspace = true license.workspace = true
authors.workspace = true authors.workspace = true

View File

@ -20,6 +20,10 @@ pub trait BoundedBitmapReader {
fn highest_bit<N>(&self) -> Option<N> fn highest_bit<N>(&self) -> Option<N>
where where
usize: UniqueSaturatedInto<N>; usize: UniqueSaturatedInto<N>;
fn count_ones_before<N>(&self, index: N) -> Option<N>
where
N: UniqueSaturatedInto<usize>,
usize: UniqueSaturatedInto<N>;
fn count_ones<N>(&self) -> N fn count_ones<N>(&self) -> N
where where
u32: UniqueSaturatedInto<N>; u32: UniqueSaturatedInto<N>;
@ -40,9 +44,6 @@ pub trait BoundedBitmapGenerator {
where where
N: UniqueSaturatedInto<usize>; N: UniqueSaturatedInto<usize>;
fn empty_from(bitmap: &Self) -> Self; fn empty_from(bitmap: &Self) -> Self;
fn from_bitmask<Outer>(values: &Vec<Outer>) -> Self
where
Outer: AtLeast8BitUnsigned + num_traits::PrimInt;
fn from_vec<Outer>(values: &Vec<Outer>) -> Self fn from_vec<Outer>(values: &Vec<Outer>) -> Self
where where
Outer: AtLeast8BitUnsigned + TryInto<usize> + PartialEq + Copy; Outer: AtLeast8BitUnsigned + TryInto<usize> + PartialEq + Copy;

View File

@ -6,7 +6,14 @@ use sp_std::{
vec::Vec, vec::Vec,
}; };
pub trait IdentifierConverter<A, E> { pub trait CiphersuiteSizes<A, E> {
fn header_bytes_len(&self) -> usize;
fn element_bytes_len(&self) -> usize;
fn signature_bytes_len(&self) -> usize;
fn scalar_bytes_len(&self) -> usize;
}
pub trait IdentifierConverter<A, E>: CiphersuiteSizes<A, E> {
fn non_zero_index(index: A) -> Result<A, E>; fn non_zero_index(index: A) -> Result<A, E>;
fn convert_identifier_to_index(bytes: &[u8]) -> Result<A, E>; fn convert_identifier_to_index(bytes: &[u8]) -> Result<A, E>;
} }
@ -15,11 +22,6 @@ pub trait FlexibleRoundOptimizedSchnorrThresholdSignature<A, E>: MerkleTreeBuild
type Packages<'a>; type Packages<'a>;
type NoncePackages<'a>; type NoncePackages<'a>;
fn header_bytes_len(&self) -> usize;
fn element_bytes_len(&self) -> usize;
fn signature_bytes_len(&self) -> usize;
fn scalar_bytes_len(&self) -> usize;
fn verify_signature_share( fn verify_signature_share(
&self, &self,
i: A, i: A,
@ -33,6 +35,7 @@ pub trait FlexibleRoundOptimizedSchnorrThresholdSignature<A, E>: MerkleTreeBuild
vk: &[u8], vk: &[u8],
m: &[u8], m: &[u8],
) -> Result<(), E>; ) -> Result<(), E>;
fn is_nonce_valid_point(&self, b: &[u8]) -> Result<(), E>;
fn is_signature_valid(&self, vk: &[u8], r: &[u8], z: &[u8], m: &[u8]) -> Result<(), E>; fn is_signature_valid(&self, vk: &[u8], r: &[u8], z: &[u8], m: &[u8]) -> Result<(), E>;
fn accumulate_signature_scalar(&self, ss: &[u8], sh: &[u8]) -> Result<Vec<u8>, E>; fn accumulate_signature_scalar(&self, ss: &[u8], sh: &[u8]) -> Result<Vec<u8>, E>;
@ -91,13 +94,14 @@ pub trait MerkleTreeBuilder<A, E>: IdentifierConverter<A, E> {
type Hash; type Hash;
fn build_merkle_tree<C, V, F>( fn build_merkle_tree<C, V, F>(
size_hint: usize,
verifying_shares: &BTreeMap<Identifier<C>, V>, verifying_shares: &BTreeMap<Identifier<C>, V>,
serialize_fn: F, serialize_fn: F,
) -> Result<Vec<Self::Hash>, E> ) -> Result<Vec<Self::Hash>, E>
where where
C: frost_core::Ciphersuite, C: frost_core::Ciphersuite,
<<C as Ciphersuite>::Group as Group>::Field: frost_core::Field, <<C as Ciphersuite>::Group as Group>::Field: frost_core::Field,
F: Fn(&V) -> Result<Vec<u8>, E>; F: Fn(&V, &mut Vec<u8>) -> Result<(), E>;
fn generate_merkle_proof_from_tree<C, V, F>( fn generate_merkle_proof_from_tree<C, V, F>(
verifying_shares: &BTreeMap<Identifier<C>, V>, verifying_shares: &BTreeMap<Identifier<C>, V>,

View File

@ -21,6 +21,13 @@ pub trait GhostHasher {
bytes bytes
} }
fn combine_hashes(left: Self::Hash, right: Self::Hash) -> Self::Hash;
fn combine_hashes_prefixed(
prefix: u8,
left: Self::Hash,
right: Self::Hash,
) -> Self::Hash;
fn hash(data: &[u8]) -> Self::Hash; fn hash(data: &[u8]) -> Self::Hash;
fn empty() -> Self::Hash; fn empty() -> Self::Hash;
fn hash_len() -> usize; fn hash_len() -> usize;

View File

@ -1,4 +1,7 @@
use frame_support::{pallet_prelude::*, storage::PrefixIterator}; use frame_support::{
pallet_prelude::*,
storage::PrefixIterator,
};
use sp_std::vec::Vec; use sp_std::vec::Vec;
use sp_runtime::{ use sp_runtime::{
offchain::http::PendingRequest, offchain::http::PendingRequest,
@ -29,9 +32,12 @@ pub trait NetworkDataBasicHandler {
+ TypeInfo + TypeInfo
+ MaybeSerializeDeserialize + MaybeSerializeDeserialize
+ MaxEncodedLen; + MaxEncodedLen;
type MaxNetworks: Get<u32>;
} }
pub trait NetworkDataInspectHandler<Network>: NetworkDataBasicHandler { pub trait NetworkDataInspectHandler<Network>: NetworkDataBasicHandler {
type TypesItem: AsRef<[Self::NetworkId]>;
fn count() -> u32; fn count() -> u32;
fn contains_key(n: &Self::NetworkId) -> bool; fn contains_key(n: &Self::NetworkId) -> bool;
fn curve_exists(c: &Self::NetworkCurve) -> bool; fn curve_exists(c: &Self::NetworkCurve) -> bool;
@ -40,7 +46,9 @@ pub trait NetworkDataInspectHandler<Network>: NetworkDataBasicHandler {
fn iter() -> PrefixIterator<(Self::NetworkId, Network)>; fn iter() -> PrefixIterator<(Self::NetworkId, Network)>;
fn iter_indexes() -> impl Iterator<Item = Self::NetworkId>; fn iter_indexes() -> impl Iterator<Item = Self::NetworkId>;
fn iter_curves() -> impl Iterator<Item = Self::NetworkCurve>; fn iter_curves() -> impl Iterator<Item = Self::NetworkCurve>;
fn iter_types_by_curve(n: &Self::NetworkCurve) -> impl Iterator<Item = Self::NetworkType>; fn iter_types_by_curve(
c: &Self::NetworkCurve
) -> impl Iterator<Item = (Self::NetworkType, Self::TypesItem)>;
} }
pub trait NetworkDataMutateHandler<Network, Balance>: NetworkDataInspectHandler<Network> { pub trait NetworkDataMutateHandler<Network, Balance>: NetworkDataInspectHandler<Network> {

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ghost-weaver" name = "ghost-weaver"
version = "0.0.14" version = "0.0.26"
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

@ -35,6 +35,9 @@ fn prepare_pallet<T: Config>(
T::NetworkDataHandler::register(network_id, network_data) T::NetworkDataHandler::register(network_id, network_data)
.expect("network should be valid for insertion"); .expect("network should be valid for insertion");
let current_block = T::BlockNumberProvider::current_block_number();
Pallet::<T>::on_initialize(current_block);
(network_id, authority, 0) (network_id, authority, 0)
} }
@ -53,6 +56,7 @@ mod benchmarks {
let block_attestation = AttestationContext::default() let block_attestation = AttestationContext::default()
.with_network_id(network_id) .with_network_id(network_id)
.with_authority_index(authority_index) .with_authority_index(authority_index)
.with_current_block(1337u16.into())
.build_block_attestation(420); .build_block_attestation(420);
let signature = authority let signature = authority
@ -101,6 +105,7 @@ mod benchmarks {
.with_network_id(network_id) .with_network_id(network_id)
.with_weaving_session(weaving_session) .with_weaving_session(weaving_session)
.with_authority_index(authority_index) .with_authority_index(authority_index)
.with_current_block(1337u16.into())
.build_hash_attestation(root_hash, next_session); .build_hash_attestation(root_hash, next_session);
let signature = authority let signature = authority
@ -320,9 +325,12 @@ mod benchmarks {
let first_sha = sp_io::hashing::sha2_256(&last_header[..80]); let first_sha = sp_io::hashing::sha2_256(&last_header[..80]);
let mut native_le = sp_io::hashing::sha2_256(&first_sha); let mut native_le = sp_io::hashing::sha2_256(&first_sha);
native_le.reverse(); native_le.reverse();
let dynamic_trusted_root = H256::from(native_le);
LoomStates::<T>::insert(&network_id, &session, dynamic_trusted_root); let at_block = 420;
let dynamic_trusted_root = H256::from(native_le);
let loom_state = LoomState { at_block, root_hash: dynamic_trusted_root };
LoomStates::<T>::insert(&network_id, &session, loom_state);
let utxo_thread_proof = UtxoThreadProof::new( let utxo_thread_proof = UtxoThreadProof::new(
gettxproof_bytes, gettxproof_bytes,
@ -360,8 +368,11 @@ mod benchmarks {
let root_string = let root_string =
hex::decode("07f4a5ba4750d9ddfc080429287cbcbde7464aa2f8ae13feaade0dcb45ca6129") hex::decode("07f4a5ba4750d9ddfc080429287cbcbde7464aa2f8ae13feaade0dcb45ca6129")
.expect("EVM Root hex should be valid"); .expect("EVM Root hex should be valid");
let at_block = 420;
let expected_root = H256::from_slice(&root_string); let expected_root = H256::from_slice(&root_string);
LoomStates::<T>::insert(&network_id, &session, expected_root); let loom_state = LoomState { at_block, root_hash: expected_root };
LoomStates::<T>::insert(&network_id, &session, loom_state);
let receiver_32: [u8; 32] = let receiver_32: [u8; 32] =
hex::decode("0ae3ba32f8555fe3031461e2cb6feeadb67029ef7490b982824fc90751a4cb3e") hex::decode("0ae3ba32f8555fe3031461e2cb6feeadb67029ef7490b982824fc90751a4cb3e")

View File

@ -68,13 +68,14 @@ fn parse_pending_requests<'a, R: Copy + Into<RequestId> + 'static>(
) -> impl Iterator<Item = NetworkResponseType> + 'a { ) -> impl Iterator<Item = NetworkResponseType> + 'a {
PendingRequest::try_wait_all(pending_requests, Some(deadline)) PendingRequest::try_wait_all(pending_requests, Some(deadline))
.into_iter() .into_iter()
.filter_map(move |wait_result| { .enumerate()
.filter_map(move |(idx, wait_result)| {
let http_result = wait_result let http_result = wait_result
.inspect_err(|_| { .inspect_err(|_| {
log::error!( log::error!(
target: LOG_TARGET, target: LOG_TARGET,
"🕸️ Error during request preparation: {:?}", "🕸️ Weaver: RPC request [index {}] timed out (deadline reached).",
WeavingError::<u32>::DeadlineReached, idx
) )
}) })
.ok()?; .ok()?;
@ -83,7 +84,8 @@ fn parse_pending_requests<'a, R: Copy + Into<RequestId> + 'static>(
.inspect_err(|err| { .inspect_err(|err| {
log::error!( log::error!(
target: LOG_TARGET, target: LOG_TARGET,
"🕸️ Error during response parsing: {:?}", "🕸️ Weaver: RPC request [index {}] failed with HTTP error: {:?}",
idx,
err, err,
) )
}) })

View File

@ -22,13 +22,16 @@ use sp_runtime::{
storage::StorageValueRef, storage::StorageValueRef,
storage_lock::{StorageLock, Time}, storage_lock::{StorageLock, Time},
}, },
traits::{BlockNumberProvider, UniqueSaturatedInto}, traits::{
RuntimeAppPublic, TrailingZeroInput, BlockNumberProvider, Saturating,
UniqueSaturatedInto,
},
RuntimeAppPublic, Perbill, PerThing,
}; };
use ghost_helpers::{ use ghost_helpers::{
bounded_bitmap::{validate_bitmap_sizes, BoundedBitmap}, bounded_bitmap::{validate_bitmap_sizes, BoundedBitmap},
get_byzantium_quorum_threshold, get_byzantium_quorum_threshold, get_submission_probability,
networks::{NetworkData, NetworkType}, networks::{NetworkData, NetworkType},
}; };
use ghost_traits::{ use ghost_traits::{
@ -39,6 +42,7 @@ use ghost_traits::{
}, },
}; };
pub mod migrations;
mod errors; mod errors;
mod impls; mod impls;
mod types; mod types;
@ -62,7 +66,7 @@ pub use pallet::*;
pub mod sr25519 { pub mod sr25519 {
mod app_sr25519 { mod app_sr25519 {
use sp_application_crypto::{app_crypto, sr25519, KeyTypeId}; use sp_application_crypto::{app_crypto, sr25519, KeyTypeId};
const WEAVER: KeyTypeId = KeyTypeId(*b"weav"); const WEAVER: KeyTypeId = KeyTypeId(*b"wevr");
app_crypto!(sr25519, WEAVER); app_crypto!(sr25519, WEAVER);
} }
@ -78,8 +82,8 @@ const LOG_TARGET: &str = "runtime::ghost-weaver";
const DB_PREFIX: &[u8] = b"ghost-weaver::"; const DB_PREFIX: &[u8] = b"ghost-weaver::";
const LOCK_BLOCK_EXPIRATION: u64 = 20; const LOCK_BLOCK_EXPIRATION: u64 = 20;
const MIN_LOCK_GUARD_PERIOD: u64 = 15_000; const MIN_LOCK_GUARD_PERIOD: u64 = 60_000;
const FETCH_TIMEOUT_PERIOD: u64 = 3_000; const FETCH_TIMEOUT_PERIOD: u64 = 5_000;
type AuthIndex = ghost_helpers::AuthIndexU16; type AuthIndex = ghost_helpers::AuthIndexU16;
type BitmapChunk = ghost_helpers::BitmapChunkU32; type BitmapChunk = ghost_helpers::BitmapChunkU32;
@ -99,7 +103,7 @@ type WeaversBitmap<T> = BoundedBitmap<BitmapChunk, <T as Config>::MaxAuthorities
pub mod pallet { pub mod pallet {
use super::*; use super::*;
const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
#[pallet::pallet] #[pallet::pallet]
#[pallet::storage_version(STORAGE_VERSION)] #[pallet::storage_version(STORAGE_VERSION)]
@ -182,6 +186,16 @@ pub mod pallet {
CouldNotRegisterIncoming, CouldNotRegisterIncoming,
} }
#[pallet::storage]
#[pallet::getter(fn network_attested_blocks)]
pub(super) type NetworkAttestedBlocks<T: Config> = StorageDoubleMap<
_,
Twox64Concat, NetworkIdOf<T>,
Twox64Concat, AuthIndex,
BlockNumberFor<T>,
ValueQuery,
>;
#[pallet::storage] #[pallet::storage]
#[pallet::getter(fn current_weaving_session)] #[pallet::getter(fn current_weaving_session)]
pub(super) type CurrentWeavingSession<T: Config> = pub(super) type CurrentWeavingSession<T: Config> =
@ -202,7 +216,7 @@ pub mod pallet {
_, _,
Twox64Concat, NetworkIdOf<T>, Twox64Concat, NetworkIdOf<T>,
Twox64Concat, WeavingSession, Twox64Concat, WeavingSession,
H256, LoomState,
ValueQuery, ValueQuery,
>; >;
@ -288,7 +302,12 @@ pub mod pallet {
); );
return; return;
} }
LoomStates::<T>::insert(&data.0, data.1, data.2); let initial_loom_state = LoomState {
at_block: 0u16.into(),
root_hash: data.2,
};
LoomStates::<T>::insert(&data.0, data.1, initial_loom_state);
}); });
if !self.authorities.is_empty() { if !self.authorities.is_empty() {
@ -330,26 +349,23 @@ pub mod pallet {
let external_block = block_attestation.external_block; let external_block = block_attestation.external_block;
let current_authorities_count = Authorities::<T>::decode_len().
unwrap_or_default();
NetworkAttestations::<T>::try_mutate( NetworkAttestations::<T>::try_mutate(
&network_id, &network_id,
|attestations| -> DispatchResult { |attestations| -> DispatchResult {
if attestations.is_empty() { let index = authority_index as usize;
attestations.bounded_resize(current_authorities_count, None);
}
ensure!( ensure!(index < attestations.len(), Error::<T>::TooManyAttestations);
(authority_index as usize) < attestations.len(), attestations[index] = Some(external_block);
Error::<T>::TooManyAttestations,
);
attestations[authority_index as usize] = Some(external_block);
Ok(()) Ok(())
})?; })?;
let current_block = T::BlockNumberProvider::current_block_number();
NetworkAttestedBlocks::<T>::insert(
&network_id, &authority_index,
current_block
);
Self::deposit_event(Event::<T>::BlockAttestated { Self::deposit_event(Event::<T>::BlockAttestated {
network_id, network_id,
authority_index, authority_index,
@ -402,10 +418,20 @@ pub mod pallet {
WeavingStates::<T>::mutate(&network_id, |weaving_state| { WeavingStates::<T>::mutate(&network_id, |weaving_state| {
if weaving_state.count < weavers_count { if weaving_state.count < weavers_count {
*weaving_state = WeavingState::new(root_hash, next_session, weavers_count); *weaving_state = WeavingState::new(
root_hash,
next_session,
weavers_count
);
} }
}); });
let current_block = T::BlockNumberProvider::current_block_number();
NetworkAttestedBlocks::<T>::insert(
&network_id, &authority_index,
current_block
);
Self::deposit_event(Event::<T>::HashAttestated { Self::deposit_event(Event::<T>::HashAttestated {
authority_index, authority_index,
next_session, next_session,
@ -438,7 +464,7 @@ pub mod pallet {
let network_data = T::NetworkDataHandler::get(&network_id) let network_data = T::NetworkDataHandler::get(&network_id)
.ok_or(Error::<T>::NonExistentNetworkId)?; .ok_or(Error::<T>::NonExistentNetworkId)?;
let root_hash = LoomStates::<T>::get(&network_id, &session); let state = LoomStates::<T>::get(&network_id, &session);
let pulled_thread_key = thread_proof.get_unique_key(session, network_id); let pulled_thread_key = thread_proof.get_unique_key(session, network_id);
ensure!( ensure!(
@ -447,7 +473,7 @@ pub mod pallet {
); );
let (receiver_account, amount) = thread_proof let (receiver_account, amount) = thread_proof
.verify_thread_proof(root_hash, network_id, &network_data.gatekeeper) .verify_thread_proof(state.root_hash, network_id, &network_data.gatekeeper)
.ok_or(Error::<T>::InvalidMerkleProof)?; .ok_or(Error::<T>::InvalidMerkleProof)?;
let receiver_bytes: &[u8; 32] = receiver_account.as_ref(); let receiver_bytes: &[u8; 32] = receiver_account.as_ref();
@ -504,11 +530,13 @@ pub mod pallet {
let current_session = CurrentWeavingSession::<T>::get(&network_id); let current_session = CurrentWeavingSession::<T>::get(&network_id);
let loom_state_changed = let loom_state_changed =
LoomStates::<T>::mutate(&network_id, &current_session, |loom_state| { LoomStates::<T>::mutate(&network_id, &current_session, |loom_state| {
if *loom_state != weaving_state.hash && threshold_reached { if !threshold_reached { return false; }
*loom_state = weaving_state.hash;
return true; let hash_changed = loom_state.root_hash != weaving_state.hash;
} loom_state.at_block = tapestry_draft.median;
false if hash_changed { loom_state.root_hash = weaving_state.hash; }
hash_changed
}); });
if loom_state_changed && weaving_state.next_session != current_session { if loom_state_changed && weaving_state.next_session != current_session {
@ -518,8 +546,10 @@ pub mod pallet {
CurrentWeavingSession::<T>::insert(&network_id, next_session); CurrentWeavingSession::<T>::insert(&network_id, next_session);
} }
weight.saturating_accrue(T::DbWeight::get().writes(1)); weight.saturating_accrue(T::DbWeight::get().writes(3));
TapestryDrafts::<T>::remove(&network_id); TapestryDrafts::<T>::remove(&network_id);
WeavingStates::<T>::remove(&network_id);
NetworkAttestations::<T>::remove(&network_id);
} }
} }
None => { None => {
@ -532,9 +562,11 @@ pub mod pallet {
weight.saturating_accrue(T::DbWeight::get().reads(1)); weight.saturating_accrue(T::DbWeight::get().reads(1));
let attestation_len = NetworkAttestations::<T>::decode_len(&network_id); let attestation_len = NetworkAttestations::<T>::decode_len(&network_id);
if attestation_len.unwrap_or(0) > len { if attestation_len.unwrap_or(0) != len {
weight.saturating_accrue(T::DbWeight::get().writes(1)); weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
NetworkAttestations::<T>::remove(&network_id); NetworkAttestations::<T>::mutate(&network_id, |attestations| {
attestations.bounded_resize(len, None);
});
return weight; return weight;
} }
@ -575,7 +607,7 @@ pub mod pallet {
if sp_io::offchain::is_validator() { if sp_io::offchain::is_validator() {
log::warn!(target: LOG_TARGET, "🕸️ Weaver started at block #{:?}", now); log::warn!(target: LOG_TARGET, "🕸️ Weaver started at block #{:?}", now);
match Self::start_weaving(now) { match Self::start_weaving(now) {
Ok(_) => log::warn!(target: LOG_TARGET, "🕸️ Weaver finished gracefully at block #{:?}", now), Ok(msg) => log::warn!(target: LOG_TARGET, "🕸️ Weaver finished gracefully at block #{:?}: {:?}", now, msg),
Err(e) => log::warn!(target: LOG_TARGET, "🕸️ Weaver failed at block #{:?}: {:?}", now, e), Err(e) => log::warn!(target: LOG_TARGET, "🕸️ Weaver failed at block #{:?}: {:?}", now, e),
} }
} }
@ -616,6 +648,21 @@ pub mod pallet {
.propagate(true) .propagate(true)
.build() .build()
} }
Call::pull_thread {
network_id,
session,
thread_proof,
} => {
let thread_context =
Self::validate_thread(network_id, session, thread_proof)?;
ValidTransaction::with_tag_prefix("WeaverThread")
.priority(T::UnsignedPriority::get())
.and_provides(thread_context.to_fixed_bytes())
.longevity(LOCK_BLOCK_EXPIRATION)
.propagate(true)
.build()
}
_ => InvalidTransaction::Call.into(), _ => InvalidTransaction::Call.into(),
} }
} }
@ -623,6 +670,31 @@ pub mod pallet {
} }
impl<T: Config> Pallet<T> { impl<T: Config> Pallet<T> {
fn validate_thread(
network_id: &NetworkIdOf<T>,
session: &WeavingSession,
thread_proof: &ThreadProof<BalanceOf<T>>,
) -> Result<H256, InvalidTransaction> {
let network_data = T::NetworkDataHandler::get(&network_id)
.ok_or(InvalidTransaction::BadProof)?;
let root_hash = LoomStates::<T>::get(&network_id, &session).root_hash;
if root_hash == Default::default() {
return Err(InvalidTransaction::BadProof);
}
thread_proof
.verify_thread_proof(root_hash, *network_id, &network_data.gatekeeper)
.ok_or(InvalidTransaction::BadProof)?;
let pulled_thread_key = thread_proof.get_unique_key(*session, *network_id);
if PulledThreads::<T>::contains_key(&pulled_thread_key) {
return Err(InvalidTransaction::Stale);
}
Ok(pulled_thread_key)
}
fn validate_attestation_signature<A>( fn validate_attestation_signature<A>(
attestation: &A, attestation: &A,
signature: &<T::AuthorityId as RuntimeAppPublic>::Signature, signature: &<T::AuthorityId as RuntimeAppPublic>::Signature,
@ -632,15 +704,40 @@ impl<T: Config> Pallet<T> {
A: Encode, A: Encode,
{ {
let authority_index = attestation.authority_index(); let authority_index = attestation.authority_index();
let attestation_block = attestation.block();
let network_id = attestation.network_id(); let network_id = attestation.network_id();
if !T::NetworkDataHandler::contains_key(&network_id) {
return Err(InvalidTransaction::BadProof);
}
let current_attestation_len =
NetworkAttestations::<T>::decode_len(&network_id).unwrap_or(0);
if authority_index as usize >= current_attestation_len {
return Err(InvalidTransaction::BadSigner.into());
}
if DisabledAuthorities::<T>::get().contains(authority_index) { if DisabledAuthorities::<T>::get().contains(authority_index) {
return Err(InvalidTransaction::BadSigner); return Err(InvalidTransaction::BadSigner);
} }
if T::BlockNumberProvider::current_block_number() <= attestation_block { let attestation_block = attestation.block();
return Err(InvalidTransaction::BadProof); let current_block = T::BlockNumberProvider::current_block_number();
let lock: BlockNumberFor<T> = LOCK_BLOCK_EXPIRATION.unique_saturated_into();
let block_expiration = attestation_block.saturating_add(lock);
if current_block >= block_expiration {
return Err(InvalidTransaction::Stale);
}
let last_attested = NetworkAttestedBlocks::<T>::get(
&network_id,
&authority_index
);
let cooling_off_until = last_attested.saturating_add(lock);
if attestation_block < cooling_off_until {
return Err(InvalidTransaction::Stale);
} }
Authorities::<T>::get() Authorities::<T>::get()
@ -653,10 +750,6 @@ impl<T: Config> Pallet<T> {
.ok_or(InvalidTransaction::BadSigner) .ok_or(InvalidTransaction::BadSigner)
})?; })?;
if !T::NetworkDataHandler::contains_key(&network_id) {
return Err(InvalidTransaction::BadProof);
}
Ok((authority_index, network_id)) Ok((authority_index, network_id))
} }
@ -671,7 +764,7 @@ impl<T: Config> Pallet<T> {
let lock_period_key = Self::create_storage_key(b"network-period", &network_id_encoded); let lock_period_key = Self::create_storage_key(b"network-period", &network_id_encoded);
let lock_period = Self::read_offchain_storage(&lock_period_key, &MIN_LOCK_GUARD_PERIOD); let lock_period = Self::read_offchain_storage(&lock_period_key, &MIN_LOCK_GUARD_PERIOD);
let lock_until = rt_offchain::Duration::from_millis(lock_period); let lock_until = rt_offchain::Duration::from_millis(lock_period.max(MIN_LOCK_GUARD_PERIOD));
let network_lock_key = Self::create_storage_key(b"network-lock-", &network_id_encoded); let network_lock_key = Self::create_storage_key(b"network-lock-", &network_id_encoded);
let mut network_lock = StorageLock::<Time>::with_deadline(&network_lock_key, lock_until); let mut network_lock = StorageLock::<Time>::with_deadline(&network_lock_key, lock_until);
@ -709,14 +802,31 @@ impl<T: Config> Pallet<T> {
return Err(WeavingError::NoEndpointsAvailable(network_id)); return Err(WeavingError::NoEndpointsAvailable(network_id));
} }
let check_probability = |values_count: usize, blocks_left: BlockNumberFor<T>| -> bool {
let total_authorities = Authorities::<T>::decode_len().unwrap_or(1);
let submission_probability = get_submission_probability(
total_authorities,
values_count,
blocks_left,
);
let seed = sp_io::offchain::random_seed();
let randomness = <u32>::decode(&mut TrailingZeroInput::new(seed.as_ref()))
.expect("input is padded with zeroes; qed");
let random_roll = Perbill::from_parts(randomness % Perbill::ACCURACY);
random_roll > submission_probability
};
match TapestryDrafts::<T>::get(&network_id) { match TapestryDrafts::<T>::get(&network_id) {
Some(tapestry_draft) => Self::try_request_and_submit_hash( Some(tapestry_draft) => Self::try_request_and_submit_hash(
network_id, network_id,
current_block, current_block,
tapestry_draft.median, tapestry_draft,
deadline, deadline,
&network_data, &network_data,
&rpc_endpoints, &rpc_endpoints,
check_probability,
), ),
None => Self::try_request_and_submit_block( None => Self::try_request_and_submit_block(
network_id, network_id,
@ -724,6 +834,7 @@ impl<T: Config> Pallet<T> {
deadline, deadline,
&network_data, &network_data,
&rpc_endpoints, &rpc_endpoints,
check_probability,
), ),
} }
} }
@ -731,13 +842,22 @@ impl<T: Config> Pallet<T> {
fn try_request_and_submit_hash( fn try_request_and_submit_hash(
network_id: NetworkIdOf<T>, network_id: NetworkIdOf<T>,
current_block: BlockNumberFor<T>, current_block: BlockNumberFor<T>,
target_block: ExternalBlockNumber, tapestry_draft: TapestryDraft<BlockNumberFor<T>>,
deadline: rt_offchain::Timestamp, deadline: rt_offchain::Timestamp,
network_data: &NetworkData, network_data: &NetworkData,
rpc_endpoints: &Vec<Vec<u8>>, rpc_endpoints: &Vec<Vec<u8>>,
check_probability: impl Fn(usize, BlockNumberFor<T>) -> bool,
) -> WeavingResult<T, WeavingOk<NetworkIdOf<T>>> { ) -> WeavingResult<T, WeavingOk<NetworkIdOf<T>>> {
let weaving_session = CurrentWeavingSession::<T>::get(&network_id); let weaving_session = CurrentWeavingSession::<T>::get(&network_id);
let block_until = tapestry_draft.until.saturating_sub(current_block);
let weaving_count = WeavingStates::<T>::get(&network_id).count as usize;
if check_probability(weaving_count, block_until) {
return Ok(WeavingOk::Skipped(network_id));
}
let target_block = tapestry_draft.median;
let request_id = current_block.unique_saturated_into(); let request_id = current_block.unique_saturated_into();
let request_body = network_data.r#type.get_hash_request_body( let request_body = network_data.r#type.get_hash_request_body(
@ -831,7 +951,20 @@ impl<T: Config> Pallet<T> {
deadline: rt_offchain::Timestamp, deadline: rt_offchain::Timestamp,
network_data: &NetworkData, network_data: &NetworkData,
rpc_endpoints: &Vec<Vec<u8>>, rpc_endpoints: &Vec<Vec<u8>>,
check_probability: impl Fn(usize, BlockNumberFor<T>) -> bool,
) -> WeavingResult<T, WeavingOk<NetworkIdOf<T>>> { ) -> WeavingResult<T, WeavingOk<NetworkIdOf<T>>> {
let next_attempt_block = NextConsensusAttemptBlocks::<T>::get(&network_id);
let block_until = next_attempt_block.saturating_sub(current_block);
let attestations_count = NetworkAttestations::<T>::get(&network_id)
.iter()
.flatten()
.count();
if check_probability(attestations_count, block_until) {
return Ok(WeavingOk::Skipped(network_id));
}
let network_id_encoded = network_id.encode(); let network_id_encoded = network_id.encode();
let distance_key = Self::create_storage_key(b"distance-", &network_id_encoded); let distance_key = Self::create_storage_key(b"distance-", &network_id_encoded);
let block_deviation = let block_deviation =
@ -850,6 +983,7 @@ impl<T: Config> Pallet<T> {
let total_blocks = block_numbers.len(); let total_blocks = block_numbers.len();
let weaving_threshold = get_byzantium_quorum_threshold(total_blocks); let weaving_threshold = get_byzantium_quorum_threshold(total_blocks);
let parsed_blocks_count = total_blocks.unique_saturated_into();
let median_block = Self::validate_block_numbers_consensus( let median_block = Self::validate_block_numbers_consensus(
&mut block_numbers, &mut block_numbers,
@ -898,7 +1032,7 @@ impl<T: Config> Pallet<T> {
} }
}); });
Ok(WeavingOk::Block(finalized_block, network_id)) Ok(WeavingOk::Block(finalized_block, parsed_blocks_count, network_id))
} }
fn prepare_pending_requests( fn prepare_pending_requests(
@ -1042,12 +1176,7 @@ impl<T: Config> Pallet<T> {
local_authorities local_authorities
.binary_search(&authority) .binary_search(&authority)
.ok() .ok()
.map(|location| { .map(|_| (index as AuthIndex, authority))
(
index.unique_saturated_into(),
local_authorities[location].clone(),
)
})
}) })
} }

View File

@ -0,0 +1,9 @@
pub mod v1;
pub type MigrateV0ToV1<T> = frame_support::migrations::VersionedMigration<
0,
1,
v1::RemoveAttestedBlocksStorage<T>,
crate::Pallet::<T>,
<T as frame_system::Config>::DbWeight,
>;

View File

@ -0,0 +1,33 @@
use frame_support::{
traits::{Get, UncheckedOnRuntimeUpgrade},
weights::Weight,
};
use core::marker::PhantomData;
use crate::{Config, LOG_TARGET};
pub struct RemoveAttestedBlocksStorage<T>(PhantomData<T>);
impl<T: Config> UncheckedOnRuntimeUpgrade for RemoveAttestedBlocksStorage<T> {
fn on_runtime_upgrade() -> Weight {
let clear_key = frame_support::storage::storage_prefix(
b"GhostWeaver",
b"AttestedBlocks"
);
let removal_results = frame_support::storage::unhashed::clear_prefix(
&clear_key,
None,
None
);
let killed_keys = removal_results.unique;
log::info!(
target: LOG_TARGET,
"🧹 Success: Purged deprecated AttestedBlocks storage. Wiped {} keys from Trie.",
killed_keys
);
T::DbWeight::get().reads_writes(1, killed_keys as u64)
}
}

View File

@ -1,6 +1,6 @@
#![cfg(test)] #![cfg(test)]
use crate::sr25519::AuthorityPair; use crate::{LOCK_BLOCK_EXPIRATION, sr25519::AuthorityPair};
use crate::mock::*; use crate::mock::*;
use crate::pallet::*; use crate::pallet::*;
@ -76,10 +76,11 @@ fn should_successfully_compute_median_and_start_weaving_round() {
let networks = vec![(network_id, network_data, Default::default())]; let networks = vec![(network_id, network_data, Default::default())];
new_test_ext(authorities, networks).execute_with(|| { new_test_ext(authorities, networks).execute_with(|| {
System::set_block_number(2); GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 1);
System::set_block_number(LOCK_BLOCK_EXPIRATION + 2);
let context = AttestationContext::default() let context = AttestationContext::default()
.with_current_block(1) .with_current_block(LOCK_BLOCK_EXPIRATION + 1)
.with_network_id(network_id); .with_network_id(network_id);
let att1 = context.with_authority_index(0).build_block_attestation(100); let att1 = context.with_authority_index(0).build_block_attestation(100);
@ -96,12 +97,12 @@ fn should_successfully_compute_median_and_start_weaving_round() {
assert!(GhostWeaver::tapestry_drafts(network_id).is_none()); assert!(GhostWeaver::tapestry_drafts(network_id).is_none());
System::set_block_number(3); System::set_block_number(LOCK_BLOCK_EXPIRATION + 3);
GhostWeaver::on_initialize(3); GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 3);
let round = GhostWeaver::tapestry_drafts(network_id); let round = GhostWeaver::tapestry_drafts(network_id);
assert!(round.is_some()); assert!(round.is_some());
assert_eq!(round.unwrap().until, 3 + WeavingDelay::get()); assert_eq!(round.unwrap().until, LOCK_BLOCK_EXPIRATION + 3 + WeavingDelay::get());
}); });
} }
@ -120,10 +121,11 @@ fn should_apply_cooldown_delay_on_consensus_failure() {
let networks = vec![(network_id, network_data, Default::default())]; let networks = vec![(network_id, network_data, Default::default())];
new_test_ext(authorities, networks).execute_with(|| { new_test_ext(authorities, networks).execute_with(|| {
System::set_block_number(10); GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 9);
System::set_block_number(LOCK_BLOCK_EXPIRATION + 10);
let context = AttestationContext::default() let context = AttestationContext::default()
.with_current_block(9) .with_current_block(LOCK_BLOCK_EXPIRATION + 9)
.with_network_id(network_id); .with_network_id(network_id);
let att1 = context.with_authority_index(0).build_block_attestation(100); let att1 = context.with_authority_index(0).build_block_attestation(100);
@ -138,20 +140,20 @@ fn should_apply_cooldown_delay_on_consensus_failure() {
assert!(GhostWeaver::extend_warp(RuntimeOrigin::none(), att2, sig2).is_ok()); assert!(GhostWeaver::extend_warp(RuntimeOrigin::none(), att2, sig2).is_ok());
assert!(GhostWeaver::extend_warp(RuntimeOrigin::none(), att3, sig3).is_ok()); assert!(GhostWeaver::extend_warp(RuntimeOrigin::none(), att3, sig3).is_ok());
System::set_block_number(11); System::set_block_number(LOCK_BLOCK_EXPIRATION + 11);
GhostWeaver::on_initialize(11); GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 11);
let cooldown_block = GhostWeaver::next_consensus_attempt_blocks(network_id); let cooldown_block = GhostWeaver::next_consensus_attempt_blocks(network_id);
assert_eq!(cooldown_block, 14); assert_eq!(cooldown_block, LOCK_BLOCK_EXPIRATION + 14);
TapestryDrafts::<TestRuntime>::remove(network_id); TapestryDrafts::<TestRuntime>::remove(network_id);
System::set_block_number(12); System::set_block_number(LOCK_BLOCK_EXPIRATION + 12);
GhostWeaver::on_initialize(12); GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION+ 12);
assert!(TapestryDrafts::<TestRuntime>::get(network_id).is_none()); assert!(TapestryDrafts::<TestRuntime>::get(network_id).is_none());
System::set_block_number(14); System::set_block_number(LOCK_BLOCK_EXPIRATION + 14);
GhostWeaver::on_initialize(14); GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 14);
assert!(TapestryDrafts::<TestRuntime>::get(network_id).is_some()); assert!(TapestryDrafts::<TestRuntime>::get(network_id).is_some());
}); });
} }
@ -168,10 +170,11 @@ fn validate_unsigned_should_prevent_mempool_dos_via_tags() {
let networks = vec![(network_id, network_data, Default::default())]; let networks = vec![(network_id, network_data, Default::default())];
new_test_ext(authorities, networks).execute_with(|| { new_test_ext(authorities, networks).execute_with(|| {
System::set_block_number(2); GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 1);
System::set_block_number(LOCK_BLOCK_EXPIRATION + 2);
let att = AttestationContext::default() let att = AttestationContext::default()
.with_current_block(1) .with_current_block(LOCK_BLOCK_EXPIRATION + 1)
.with_network_id(network_id) .with_network_id(network_id)
.with_authority_index(0) .with_authority_index(0)
.build_block_attestation(100); .build_block_attestation(100);
@ -184,7 +187,9 @@ fn validate_unsigned_should_prevent_mempool_dos_via_tags() {
signature: valid_sig, signature: valid_sig,
}; };
let validity = GhostWeaver::validate_unsigned(TransactionSource::External, &call); let validity =
GhostWeaver::validate_unsigned(TransactionSource::External, &call);
assert!(validity.is_ok()); assert!(validity.is_ok());
let valid_info = validity.unwrap(); let valid_info = validity.unwrap();
@ -199,7 +204,9 @@ fn validate_unsigned_should_prevent_mempool_dos_via_tags() {
signature: invalid_sig, signature: invalid_sig,
}; };
let spam_validity = GhostWeaver::validate_unsigned(TransactionSource::External, &spam_call); let spam_validity =
GhostWeaver::validate_unsigned(TransactionSource::External, &spam_call);
assert_eq!( assert_eq!(
spam_validity, spam_validity,
Err(TransactionValidityError::Invalid(InvalidTransaction::BadSigner).into()) Err(TransactionValidityError::Invalid(InvalidTransaction::BadSigner).into())
@ -240,6 +247,9 @@ fn offchain_worker_should_fetch_block_and_submit_unsigned_tx() {
ext.register_extension(TransactionPoolExt::new(pool)); ext.register_extension(TransactionPoolExt::new(pool));
ext.execute_with(|| { ext.execute_with(|| {
GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 1);
GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 2);
let network_id_encoded = evm_network_id.encode(); let network_id_encoded = evm_network_id.encode();
let mut endpoint_key = b"ghost-weaver::".to_vec(); let mut endpoint_key = b"ghost-weaver::".to_vec();
@ -252,6 +262,7 @@ fn offchain_worker_should_fetch_block_and_submit_unsigned_tx() {
let target_endpoint = b"http://localhost:8545".to_vec(); let target_endpoint = b"http://localhost:8545".to_vec();
let rpc_endpoints: Vec<Vec<u8>> = vec![target_endpoint.clone()]; let rpc_endpoints: Vec<Vec<u8>> = vec![target_endpoint.clone()];
sp_io::offchain::local_storage_set( sp_io::offchain::local_storage_set(
sp_core::offchain::StorageKind::PERSISTENT, sp_core::offchain::StorageKind::PERSISTENT,
&endpoint_key, &endpoint_key,
@ -273,7 +284,7 @@ fn offchain_worker_should_fetch_block_and_submit_unsigned_tx() {
); );
sp_io::crypto::sr25519_generate( sp_io::crypto::sr25519_generate(
sp_core::crypto::KeyTypeId(*b"weav"), sp_core::crypto::KeyTypeId(*b"wevr"),
Some(b"//Alice".to_vec()), Some(b"//Alice".to_vec()),
); );
}); });
@ -288,8 +299,8 @@ fn offchain_worker_should_fetch_block_and_submit_unsigned_tx() {
("Accept".into(), "application/json".into()), ("Accept".into(), "application/json".into()),
("Content-Type".into(), "application/json".into()), ("Content-Type".into(), "application/json".into()),
], ],
body: b"{\"id\":1,\"jsonrpc\":\"2.0\",\"method\":\"getblockcount\"}".to_vec(), body: b"{\"id\":21,\"jsonrpc\":\"2.0\",\"method\":\"getblockcount\"}".to_vec(),
response: Some(b"{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":960127}".to_vec()), response: Some(b"{\"jsonrpc\":\"2.0\",\"id\":21,\"result\":960127}".to_vec()),
sent: true, sent: true,
..Default::default() ..Default::default()
}); });
@ -303,17 +314,16 @@ fn offchain_worker_should_fetch_block_and_submit_unsigned_tx() {
("X-Custom-Provider".into(), "GhostWeaverNode".into()), ("X-Custom-Provider".into(), "GhostWeaverNode".into()),
("Authorization".into(), "Bearer new-api-token-123".into()), ("Authorization".into(), "Bearer new-api-token-123".into()),
], ],
body: b"{\"id\":2,\"jsonrpc\":\"2.0\",\"method\":\"eth_blockNumber\"}".to_vec(), body: b"{\"id\":22,\"jsonrpc\":\"2.0\",\"method\":\"eth_blockNumber\"}".to_vec(),
response: Some(b"{\"jsonrpc\":\"2.0\",\"id\":2,\"result\":\"0x1873a84\"}".to_vec()), response: Some(b"{\"jsonrpc\":\"2.0\",\"id\":22,\"result\":\"0x1873a84\"}".to_vec()),
sent: true, sent: true,
..Default::default() ..Default::default()
}); });
} }
ext.execute_with(|| { ext.execute_with(|| {
System::set_block_number(1u64); System::set_block_number(LOCK_BLOCK_EXPIRATION + 1u64);
GhostWeaver::offchain_worker(1u64); GhostWeaver::offchain_worker(LOCK_BLOCK_EXPIRATION + 1u64);
System::set_block_number(2u64);
let state = pool_state.read(); let state = pool_state.read();
assert_eq!(state.transactions.len(), 1); assert_eq!(state.transactions.len(), 1);
@ -334,14 +344,13 @@ fn offchain_worker_should_fetch_block_and_submit_unsigned_tx() {
assert_eq!(curr_utxo_block, 960127); assert_eq!(curr_utxo_block, 960127);
assert!(TapestryDrafts::<TestRuntime>::get(&utxo_network_id).is_none()); assert!(TapestryDrafts::<TestRuntime>::get(&utxo_network_id).is_none());
GhostWeaver::on_initialize(3u64); GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 3u64);
assert!(TapestryDrafts::<TestRuntime>::get(&utxo_network_id).is_some()); assert!(TapestryDrafts::<TestRuntime>::get(&utxo_network_id).is_some());
}); });
ext.execute_with(|| { ext.execute_with(|| {
System::set_block_number(2u64); System::set_block_number(LOCK_BLOCK_EXPIRATION + 2u64);
GhostWeaver::offchain_worker(2u64); GhostWeaver::offchain_worker(LOCK_BLOCK_EXPIRATION + 2u64);
System::set_block_number(3u64);
let state = pool_state.read(); let state = pool_state.read();
assert_eq!(state.transactions.len(), 2); assert_eq!(state.transactions.len(), 2);
@ -362,7 +371,8 @@ fn offchain_worker_should_fetch_block_and_submit_unsigned_tx() {
assert_eq!(curr_evm_block, 25639556); assert_eq!(curr_evm_block, 25639556);
assert!(TapestryDrafts::<TestRuntime>::get(&evm_network_id).is_none()); assert!(TapestryDrafts::<TestRuntime>::get(&evm_network_id).is_none());
GhostWeaver::on_initialize(4u64); GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 3u64);
GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 4u64);
assert!(TapestryDrafts::<TestRuntime>::get(&evm_network_id).is_some()); assert!(TapestryDrafts::<TestRuntime>::get(&evm_network_id).is_some());
}); });
} }
@ -372,6 +382,9 @@ fn offchain_worker_should_fetch_hash_and_submit_unsigned_tx() {
let evm_network_id = 1u32; let evm_network_id = 1u32;
let utxo_network_id = 69u32; let utxo_network_id = 69u32;
let median_utxo_block = 960129;
let median_evm_block = 25639556;
let pair = AuthorityPair::from_string("//Alice", None).expect("Should be valid seed"); let pair = AuthorityPair::from_string("//Alice", None).expect("Should be valid seed");
let new_evm_network_data = NetworkDataBuilder::default() let new_evm_network_data = NetworkDataBuilder::default()
@ -406,11 +419,12 @@ fn offchain_worker_should_fetch_hash_and_submit_unsigned_tx() {
ext.register_extension(TransactionPoolExt::new(pool)); ext.register_extension(TransactionPoolExt::new(pool));
ext.execute_with(|| { ext.execute_with(|| {
GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 1);
GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 2);
let tapestry_draft = TapestryDraftBuilder::<crate::ExternalBlockNumber, u32>::default(); let tapestry_draft = TapestryDraftBuilder::<crate::ExternalBlockNumber, u32>::default();
let evm_tapestry_draft = tapestry_draft.with_median_external(median_evm_block).build();
let evm_tapestry_draft = tapestry_draft.with_median_external(25639556).build(); let utxo_tapestry_draft = tapestry_draft.with_median_external(median_utxo_block).build();
let utxo_tapestry_draft = tapestry_draft.with_median_external(960129).build();
CurrentWeavingSession::<TestRuntime>::insert(&evm_network_id, evm_session); CurrentWeavingSession::<TestRuntime>::insert(&evm_network_id, evm_session);
CurrentWeavingSession::<TestRuntime>::insert(&utxo_network_id, utxo_session); CurrentWeavingSession::<TestRuntime>::insert(&utxo_network_id, utxo_session);
@ -451,7 +465,7 @@ fn offchain_worker_should_fetch_hash_and_submit_unsigned_tx() {
); );
sp_io::crypto::sr25519_generate( sp_io::crypto::sr25519_generate(
sp_core::crypto::KeyTypeId(*b"weav"), sp_core::crypto::KeyTypeId(*b"wevr"),
Some(b"//Alice".to_vec()), Some(b"//Alice".to_vec()),
); );
}); });
@ -466,8 +480,8 @@ fn offchain_worker_should_fetch_hash_and_submit_unsigned_tx() {
("Accept".into(), "application/json".into()), ("Accept".into(), "application/json".into()),
("Content-Type".into(), "application/json".into()), ("Content-Type".into(), "application/json".into()),
], ],
body: b"{\"id\":1,\"jsonrpc\":\"2.0\",\"method\":\"getblockhash\",\"params\":[960129]}".to_vec(), body: b"{\"id\":21,\"jsonrpc\":\"2.0\",\"method\":\"getblockhash\",\"params\":[960129]}".to_vec(),
response: Some(b"{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":\"0000000000000000000091c72779c83d76dfa8149e950f3a5287377a9708d5c1\"}".to_vec()), response: Some(b"{\"jsonrpc\":\"2.0\",\"id\":21,\"result\":\"0000000000000000000091c72779c83d76dfa8149e950f3a5287377a9708d5c1\"}".to_vec()),
sent: true, sent: true,
..Default::default() ..Default::default()
}); });
@ -482,17 +496,16 @@ fn offchain_worker_should_fetch_hash_and_submit_unsigned_tx() {
("Authorization".into(), "Bearer new-api-token-123".into()), ("Authorization".into(), "Bearer new-api-token-123".into()),
], ],
// Match the EXACT field order: data first, then to // Match the EXACT field order: data first, then to
body: b"{\"id\":2,\"jsonrpc\":\"2.0\",\"method\":\"eth_call\",\"params\":[{\"data\":\"0x00000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000001873a84\",\"to\":\"0x02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9\"},\"latest\"]}".to_vec(), body: b"{\"id\":22,\"jsonrpc\":\"2.0\",\"method\":\"eth_call\",\"params\":[{\"data\":\"0x00000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000001873a84\",\"to\":\"0x02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9\"},\"latest\"]}".to_vec(),
response: Some(b"{\"jsonrpc\":\"2.0\",\"id\":2,\"result\":\"0xf72b9ef0b66c8cc3952ffd11dc0b85f3b02e6edfa9236da400138f2fcba4668d0000000000000000000000000000000000000000000000000000000000000046\"}".to_vec()), response: Some(b"{\"jsonrpc\":\"2.0\",\"id\":22,\"result\":\"0xf72b9ef0b66c8cc3952ffd11dc0b85f3b02e6edfa9236da400138f2fcba4668d0000000000000000000000000000000000000000000000000000000000000046\"}".to_vec()),
sent: true, sent: true,
..Default::default() ..Default::default()
}); });
} }
ext.execute_with(|| { ext.execute_with(|| {
System::set_block_number(1u64); System::set_block_number(LOCK_BLOCK_EXPIRATION + 1u64);
GhostWeaver::offchain_worker(1u64); GhostWeaver::offchain_worker(LOCK_BLOCK_EXPIRATION + 1u64);
System::set_block_number(2u64);
let state = pool_state.read(); let state = pool_state.read();
assert_eq!(state.transactions.len(), 1); assert_eq!(state.transactions.len(), 1);
@ -518,14 +531,15 @@ fn offchain_worker_should_fetch_hash_and_submit_unsigned_tx() {
GhostWeaver::on_initialize(3u64); GhostWeaver::on_initialize(3u64);
let curr_state = LoomStates::<TestRuntime>::get(&utxo_network_id, &utxo_session); let curr_state = LoomStates::<TestRuntime>::get(&utxo_network_id, &utxo_session);
assert_eq!(prev_state, H256::default()); assert_eq!(prev_state.root_hash, H256::default());
assert_eq!(curr_state, block_hash); assert_eq!(curr_state.root_hash, block_hash);
assert_eq!(prev_state.at_block, 0);
assert_eq!(curr_state.at_block, median_utxo_block);
}); });
ext.execute_with(|| { ext.execute_with(|| {
System::set_block_number(2u64); System::set_block_number(LOCK_BLOCK_EXPIRATION + 2u64);
GhostWeaver::offchain_worker(2u64); GhostWeaver::offchain_worker(LOCK_BLOCK_EXPIRATION + 2u64);
System::set_block_number(3u64);
let state = pool_state.read(); let state = pool_state.read();
assert_eq!(state.transactions.len(), 2); assert_eq!(state.transactions.len(), 2);
@ -542,17 +556,20 @@ fn offchain_worker_should_fetch_hash_and_submit_unsigned_tx() {
assert_eq!(curr_weaving_state.next_session, evm_session + 1); assert_eq!(curr_weaving_state.next_session, evm_session + 1);
let bytes = hex::decode("f72b9ef0b66c8cc3952ffd11dc0b85f3b02e6edfa9236da400138f2fcba4668d") let bytes = hex::decode("f72b9ef0b66c8cc3952ffd11dc0b85f3b02e6edfa9236da400138f2fcba4668d")
.expect("UTXO block hash should be valid"); .expect("EVM block hash should be valid");
let block_hash = H256::from_slice(&bytes); let block_hash = H256::from_slice(&bytes);
assert_eq!(curr_weaving_state.hash, block_hash); assert_eq!(curr_weaving_state.hash, block_hash);
let prev_state = LoomStates::<TestRuntime>::get(&evm_network_id, &evm_session); let prev_state = LoomStates::<TestRuntime>::get(&evm_network_id, &evm_session);
GhostWeaver::on_initialize(4u64); GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 3u64);
GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 4u64);
let curr_state = LoomStates::<TestRuntime>::get(&evm_network_id, &evm_session); let curr_state = LoomStates::<TestRuntime>::get(&evm_network_id, &evm_session);
assert_eq!(prev_state, H256::default()); assert_eq!(prev_state.root_hash, H256::default());
assert_eq!(curr_state, block_hash); assert_eq!(curr_state.root_hash, block_hash);
assert_eq!(prev_state.at_block, 0);
assert_eq!(curr_state.at_block, median_evm_block);
}); });
} }
@ -635,14 +652,18 @@ fn should_successfully_pull_evm_thread_and_mint_currency() {
]; ];
new_test_ext(authorities, networks).execute_with(|| { new_test_ext(authorities, networks).execute_with(|| {
GhostWeaver::on_initialize(1);
// https://sepolia.etherscan.io/address/0x7fec11e652f4d54e8cf11c4b52c360380332f2bd#readContract // https://sepolia.etherscan.io/address/0x7fec11e652f4d54e8cf11c4b52c360380332f2bd#readContract
// getRoot: session 0, atBlock 11716130 // getRoot: session 0, atBlock 11716130
let session = 0; let session = 0;
let root_string = let root_string =
hex::decode("07f4a5ba4750d9ddfc080429287cbcbde7464aa2f8ae13feaade0dcb45ca6129") hex::decode("07f4a5ba4750d9ddfc080429287cbcbde7464aa2f8ae13feaade0dcb45ca6129")
.expect("Root hex should be valid"); .expect("Root hex should be valid");
let expected_root = H256::from_slice(&root_string); let expected_root = H256::from_slice(&root_string);
LoomStates::<TestRuntime>::insert(&network_id, &session, expected_root); let expected_loom_state = LoomState { at_block: 420, root_hash: expected_root };
LoomStates::<TestRuntime>::insert(&network_id, &session, expected_loom_state);
let mut total_issuance = 0; let mut total_issuance = 0;
@ -755,7 +776,8 @@ fn should_successfully_pull_utxo_thread_and_mint_currency() {
.expect("Bitcoin Root hex should be valid"); .expect("Bitcoin Root hex should be valid");
let expected_root = H256::from_slice(&block_hash); let expected_root = H256::from_slice(&block_hash);
LoomStates::<TestRuntime>::insert(&network_id, &session, expected_root); let expected_loom_state = LoomState { at_block: 420, root_hash: expected_root };
LoomStates::<TestRuntime>::insert(&network_id, &session, expected_loom_state);
let expected_receiver_bytes = hex::decode("1657090d02985f0cdb41436fe2d5a83a3acb1995d963a698e7e619e927bdcc5e").unwrap(); let expected_receiver_bytes = hex::decode("1657090d02985f0cdb41436fe2d5a83a3acb1995d963a698e7e619e927bdcc5e").unwrap();
let mut receiver_raw = [0u8; 32]; let mut receiver_raw = [0u8; 32];
@ -801,6 +823,117 @@ fn should_successfully_pull_utxo_thread_and_mint_currency() {
}); });
} }
#[test]
fn should_successfully_pull_utxo_thread_and_reject_nominal_varint_encoding() {
let pair = AuthorityPair::from_string("//Alice", None).expect("Should be valid seed");
let incoming_share = 0;
let network_id = 69;
let network_data = NetworkDataBuilder::default()
.with_network_type(NetworkType::Utxo)
.with_incoming_share(incoming_share)
.with_gatekeeper(
// Transaction: https://blockstream.info/tx/4a3fdd9ebb30b9c6df05d4199f8474b2f0c795d1f40a58d0d73342fc666b4a2e
hex::decode("4635b1794a22bfbe6c5c5eba17b693f4aaf0e348")
.expect("Bytes32 from UTXO should be valid"),
)
.build();
let authorities = vec![pair.clone()];
let networks = vec![(network_id, network_data, Default::default())];
// Real world tx at block #481838
// Block https://blockstream.info/block/0000000000000000011bdccb39e19e942a60edffdccd9779653c5b46b337904a
// Transaction: https://blockstream.info/tx/4a3fdd9ebb30b9c6df05d4199f8474b2f0c795d1f40a58d0d73342fc666b4a2e
let payload = (
// curl --data-binary '{"jsonrpc": "2.0", "id": "test-proof", "method": "gettxoutproof", "params": [["4a3fdd9ebb30b9c6df05d4199f8474b2f0c795d1f40a58d0d73342fc666b4a2e"], "0000000000000000011bdccb39e19e942a60edffdccd9779653c5b46b337904a"]}' -H 'Content-Type: application/json' https://bitcoin-rpc.publicnode.com
hex::decode("00000020375a14cfcf5730daa8ff8d7a5bd2cf59fba797e289c2e0000000000000000000529cd017146361b57e6b7353b16d8d38d32f92ee842039dd779d35bfbfa2f3e53f649e59e93c01187900ea9fb40600000cfbd7b718845d123b1ae1a7fd3bce9c97acba097dc912c4ac67a920395db07ba4eef685960869ba70e0ca38890565c97dc11f1d7ae538e7b509069f81e38eff1a2e4a6b66fc4233d7d0580af4d195c7f0b274849f19d405dfc6b930bb9edd3f4a4c1f1e9fa3a336c50e8ea16039e4db4bef7ff5946e3a019937bdf626c2336d0e3926a83a575218f84e6dc55ffb9d0997a41e4b4d5425a85f22160c9f52362a95021a573ff96bb429bd80a8ec9dcb899971553efe91b6a0fe57c4694c3e8da8dc7f1babd514f2746da1ed06f08664baed853c334e0a3917d0c859b19ee14f448f77e624822070db3b606d5092027ca9ecf7e0243e6c151081021ebb3ddcdd28c1b8f9fdcffebd1962590477675f3d29d52602a1e7e941f772a3bbff42cf2eb6e74a1124bf424f767e294ae6253a21602c7c6b0381743dd6dcd57b025983e0e34375b24f67ac8beb00713d2f70594bbf797d5d94c061cb9965e6458f2f017f983d7ce37361fa13fda372f595dc1f22895ab93339457c72c480fed1c4d1164a242303ff3600").expect("Bytes32 from UTXO should be valid"),
// curl --data-binary '{"jsonrpc": "2.0", "id": "test-proof", "method": "getrawtransaction", "params": ["4a3fdd9ebb30b9c6df05d4199f8474b2f0c795d1f40a58d0d73342fc666b4a2e", false]}' -H 'Content-Type: application/json' https://bitcoin-rpc.publicnode.com
hex::decode("010000000299a43fae581f80baf44e021ddf7b11995f184db282d6e48c22248b9fe79c43bb010000008b483045022100e9db01784dcd879c65d66127c2d52a4f9244321d15796893d6dccadaa1146e3d02203d9bb80f8df8b66692d3d9517f7cbb898d7cc40a30b175432322e1956c9b973a0141048097e4f8bb8ee104d5c2ece11d42f7e4719df8ddba459ecadecaba87d233c81a3c14830371392dd8ed030ef970450c0b7af48d9db4ba20c046896481602eb9dbffffffffb3fd05bc6000a5a0384e9a613d452e9307568df0a964907068e00403a0a0e09e020000006a47304402205bb1cbad4b29421648c1e22cac32a56aa06c933fe655559823fffb71ffebbc0b022021648d970560aa5b2031560b52a0fd06305995a58a457663324c18c1a9b0bd0b8121020d6e50b2660af27933c42bc1395fe93df90ffac5e2a989f6a134919fb8cf8075ffffffff030000000000000000296a2769642b5bb666ac1657090d02985f0cdb41436fe2d5a83a3acb1995d963a698e7e619e927bdcc5e7c150000000000001976a914c1c69d91a5719a23ba2094d89a4df3eadfa4aef788acefea2309000000001976a9144635b1794a22bfbe6c5c5eba17b693f4aaf0e34888ac00000000").expect("Bytes32 from UTXO should be valid"),
vec![
// curl --data-binary '{"jsonrpc": "2.0", "id": "test-proof", "method": "getblockheader", "params": ["000000000000000000e7da64065cc23d5c5a5aa402f088aaaf37c5fe1c3b6b92", false]}' -H 'Content-Type: application/json' https://bitcoin-rpc.publicnode.com
hex::decode("000000204a9037b3465b3c657997cddcffed602a949ee139cbdc1b010000000000000000669c3e2472384bc95993f154aada38d6b2113d8d32069e32e2724ba40915a1802a669e59e93c011801c8c735").expect("Bytes32 from UTXO should be valid"),
// curl --data-binary '{"jsonrpc": "2.0", "id": "test-proof", "method": "getblockheader", "params": ["00000000000000000063da6d9362c0ac3b87877d1c5ad7afd5bd706571d5b393", false]}' -H 'Content-Type: application/json' https://bitcoin-rpc.publicnode.com
hex::decode("02000020926b3b1cfec537afaa88f002a45a5a5c3dc25c0664dae7000000000000000000af0f270bf3f868d47089f395197809c360f1506636c960d96afed68bbd72d0b4496d9e59e93c01183ac94b67").expect("Bytes32 from UTXO should be valid"),
// curl --data-binary '{"jsonrpc": "2.0", "id": "test-proof", "method": "getblockheader", "params": ["000000000000000000ec6063eb743366264368be7ff5021727682cb518963026", false]}' -H 'Content-Type: application/json' https://bitcoin-rpc.publicnode.com
hex::decode("0000002093b3d5716570bdd5afd75a1c7d87873bacc062936dda630000000000000000009ab437e7c66b10a59b67127f290d24f381aba4e03d5e16c062a356eea4d608dea56d9e59e93c0118264f310e").expect("Bytes32 from UTXO should be valid"),
],
);
let encodings = vec![
vec![0x02], // canonical
vec![0xFD, 0x02, 0x00], // non-minimal 3 bytes
vec![0xFE, 0x02, 0x00, 0x00, 0x00], // non-minimal 5 bytes
vec![0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], // non-minimal 9 bytes
];
new_test_ext(authorities, networks).execute_with(|| {
let session = 0;
let block_hash =
hex::decode("000000000000000000ec6063eb743366264368be7ff5021727682cb518963026")
.expect("Bitcoin Root hex should be valid");
let expected_root = H256::from_slice(&block_hash);
let expected_loom_state = LoomState { at_block: 420, root_hash: expected_root };
LoomStates::<TestRuntime>::insert(&network_id, &session, expected_loom_state);
let expected_receiver_bytes = hex::decode("1657090d02985f0cdb41436fe2d5a83a3acb1995d963a698e7e619e927bdcc5e").unwrap();
let mut receiver_raw = [0u8; 32];
receiver_raw.copy_from_slice(&expected_receiver_bytes);
let receiver_account = sp_runtime::AccountId32::from(receiver_raw);
let transfer_amount: u64 = 153348847;
let mut expected_pure_amount: u64 = 0;
for (idx, enc) in encodings.iter().enumerate() {
let mut variant = Vec::with_capacity(payload.1.len() + enc.len() - 1);
variant.extend_from_slice(&payload.1[0..4]);
variant.extend_from_slice(&enc);
variant.extend_from_slice(&payload.1[5..]);
let utxo_thread_proof = UtxoThreadProof::new(payload.0.clone(), variant, payload.2.clone());
let thread_info = ThreadProof::UtxoThreadProof(utxo_thread_proof);
let pulled_thread_key = thread_info.get_unique_key(session, network_id);
assert_eq!(Balances::free_balance(&receiver_account), expected_pure_amount);
assert!(!PulledThreads::<TestRuntime>::contains_key(
&pulled_thread_key
));
let pull_result = GhostWeaver::pull_thread(
RuntimeOrigin::none(),
network_id,
session,
thread_info
);
if idx == 0 {
assert!(pull_result.is_ok());
assert!(PulledThreads::<TestRuntime>::contains_key(&pulled_thread_key));
let curve_share = Perbill::from_parts(incoming_share).mul_ceil(transfer_amount);
expected_pure_amount += transfer_amount.saturating_sub(curve_share);
assert_eq!(Balances::free_balance(&receiver_account), expected_pure_amount);
System::assert_last_event(RuntimeEvent::GhostWeaver(Event::ThreadPulled {
pulled_thread_key,
network_id,
receiver: receiver_account.clone(),
amount: transfer_amount,
}));
} else {
assert!(pull_result.is_err());
assert!(!PulledThreads::<TestRuntime>::contains_key(&pulled_thread_key));
assert_eq!(Balances::free_balance(&receiver_account), expected_pure_amount);
}
}
assert_eq!(Balances::free_balance(&receiver_account), transfer_amount);
});
}
#[test] #[test]
fn on_initialize_should_finalize_session_when_timeout_is_reached() { fn on_initialize_should_finalize_session_when_timeout_is_reached() {
let pair1 = AuthorityPair::from_string("//Alice", None).expect("Should be valid seed"); let pair1 = AuthorityPair::from_string("//Alice", None).expect("Should be valid seed");
@ -817,7 +950,9 @@ fn on_initialize_should_finalize_session_when_timeout_is_reached() {
let networks = vec![(network_id, new_network_data, 0u64)]; let networks = vec![(network_id, new_network_data, 0u64)];
new_test_ext(authorities, networks).execute_with(|| { new_test_ext(authorities, networks).execute_with(|| {
let current_substrate_block = 10; let current_substrate_block = LOCK_BLOCK_EXPIRATION + 10;
GhostWeaver::on_initialize(current_substrate_block);
System::set_block_number(current_substrate_block + 1); System::set_block_number(current_substrate_block + 1);
let tapestry_draft = TapestryDraftBuilder::default() let tapestry_draft = TapestryDraftBuilder::default()
@ -865,8 +1000,96 @@ fn on_initialize_should_finalize_session_when_timeout_is_reached() {
GhostWeaver::on_initialize(current_substrate_block); GhostWeaver::on_initialize(current_substrate_block);
let saved_loom = LoomStates::<TestRuntime>::get(&network_id, current_session); let saved_loom = LoomStates::<TestRuntime>::get(&network_id, current_session);
assert_ne!(saved_loom, target_hash); assert_ne!(saved_loom.root_hash, target_hash);
assert_eq!(saved_loom, saved_loom); assert_eq!(saved_loom.at_block, 0);
let updated_session = CurrentWeavingSession::<TestRuntime>::get(&network_id);
assert_eq!(updated_session, current_session);
});
}
#[test]
fn on_initialize_should_update_at_block_when_threshold_reached_with_same_hash() {
let pair1 = AuthorityPair::from_string("//Alice", None).expect("Should be valid seed");
let pair2 = AuthorityPair::from_string("//Bob", None).expect("Should be valid seed");
let pair3 = AuthorityPair::from_string("//Charlie", None).expect("Should be valid seed");
let network_id = 1;
let new_network_data = NetworkDataBuilder::default()
.with_network_type(NetworkType::Evm)
.build();
let authorities = vec![pair1.clone(), pair2.clone(), pair3.clone()];
let networks = vec![(network_id, new_network_data, 0u64)];
new_test_ext(authorities, networks).execute_with(|| {
let current_substrate_block = LOCK_BLOCK_EXPIRATION + 10;
let current_session = CurrentWeavingSession::<TestRuntime>::get(&network_id);
let target_hash = H256::repeat_byte(0xAA);
LoomStates::<TestRuntime>::insert(&network_id, &current_session, crate::LoomState {
root_hash: target_hash,
at_block: 0,
});
System::set_block_number(current_substrate_block);
GhostWeaver::on_initialize(current_substrate_block);
let tapestry_draft = TapestryDraftBuilder::default()
.with_current_block(current_substrate_block)
.with_block_delay(5u32)
.with_median_external(100)
.build();
TapestryDrafts::<TestRuntime>::insert(&network_id, tapestry_draft);
let attestation = AttestationContext::default()
.with_current_block(current_substrate_block)
.with_network_id(network_id)
.with_weaving_session(current_session);
let hash_att1 = attestation
.with_authority_index(0)
.build_hash_attestation(target_hash, current_session);
let hash_att2 = attestation
.with_authority_index(1)
.build_hash_attestation(target_hash, current_session);
let hash_att3 = attestation
.with_authority_index(2)
.build_hash_attestation(target_hash, current_session);
assert!(GhostWeaver::weave_weft(
RuntimeOrigin::none(),
hash_att1,
pair1.sign(&hash_att1.encode())
)
.is_ok());
assert!(GhostWeaver::weave_weft(
RuntimeOrigin::none(),
hash_att2,
pair2.sign(&hash_att2.encode())
)
.is_ok());
assert!(GhostWeaver::weave_weft(
RuntimeOrigin::none(),
hash_att3,
pair3.sign(&hash_att3.encode())
)
.is_ok());
let state_before = WeavingStates::<TestRuntime>::get(&network_id);
assert_eq!(state_before.count, 3);
assert_eq!(state_before.hash, target_hash);
GhostWeaver::on_initialize(current_substrate_block);
let saved_loom = LoomStates::<TestRuntime>::get(&network_id, current_session);
assert_eq!(saved_loom.root_hash, target_hash);
assert_eq!(saved_loom.at_block, 100);
let updated_session = CurrentWeavingSession::<TestRuntime>::get(&network_id); let updated_session = CurrentWeavingSession::<TestRuntime>::get(&network_id);
assert_eq!(updated_session, current_session); assert_eq!(updated_session, current_session);

View File

@ -14,7 +14,6 @@ use ghost_helpers::{
merkle_tree::verify_tree_proof, merkle_tree::verify_tree_proof,
SubstrateBlake2Hasher, SubstrateKeccakHasher, UtxoSha2Hasher, SubstrateBlake2Hasher, SubstrateKeccakHasher, UtxoSha2Hasher,
}; };
use ghost_traits::hashing::GhostHasher;
use crate::{ExternalBlockNumber, ThreadId, WeavingSession}; use crate::{ExternalBlockNumber, ThreadId, WeavingSession};
@ -37,22 +36,43 @@ const PROTOCOL_PREFIX: [u8; 7] = [0x69, 0x64, 0x2b, 0x5b, 0xb6, 0x66, 0xac];
const PROTOCOL_PREFIX: [u8; 7] = [0x47, 0x48, 0x53, 0x54, 0x74, 0x6F, 0x3A]; const PROTOCOL_PREFIX: [u8; 7] = [0x47, 0x48, 0x53, 0x54, 0x74, 0x6F, 0x3A];
pub enum WeavingOk<NetworkId> { pub enum WeavingOk<NetworkId> {
Block(ExternalBlockNumber, NetworkId), Skipped(NetworkId),
Block(ExternalBlockNumber, u32, NetworkId),
Hash(ExternalBlockNumber, H256, WeavingSession, NetworkId), Hash(ExternalBlockNumber, H256, WeavingSession, NetworkId),
} }
impl<NetworkId> core::fmt::Display for WeavingOk<NetworkId> impl<NetworkId> core::fmt::Debug for WeavingOk<NetworkId>
where where
NetworkId: core::fmt::Debug, NetworkId: core::fmt::Debug,
{ {
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::Block(block, network_id) => write!(fmt, "Verified block height #{:?} found for network: {:?}.", block, network_id), Self::Skipped(network_id) => write!(fmt, "Submission step skipped for network: {:?} based on probability distribution.", network_id),
Self::Hash(block, hash, next_session, network_id) => write!(fmt, "Extracted Merkle Root {:?} where next session is {:?}; from block #{:?} for network: {:?}.", hash, next_session, block, network_id), Self::Block(block, count, network_id) => write!(fmt, "Verified block height #{:?} for network: {:?} based on {} parsed requests.", block, network_id, count),
Self::Hash(block, hash, next_session, network_id) => write!(fmt, "Extracted Merkle Root {:?} (next session is {:?}) from block #{:?} for network: {:?}.", hash, next_session, block, network_id),
} }
} }
} }
#[derive(
Default,
RuntimeDebug,
Clone,
Copy,
Eq,
PartialEq,
Ord,
PartialOrd,
Encode,
Decode,
TypeInfo,
MaxEncodedLen,
)]
pub struct LoomState {
pub at_block: ExternalBlockNumber,
pub root_hash: H256,
}
#[derive( #[derive(
Default, Default,
RuntimeDebug, RuntimeDebug,
@ -350,8 +370,10 @@ where
{ {
if EVM_MERKLE_DEPTH as usize != self.proof.len() { return None; } if EVM_MERKLE_DEPTH as usize != self.proof.len() { return None; }
let preimage_hash = self.get_preimage_hash(network_id)?;
let proof_valid = verify_tree_proof::<SubstrateKeccakHasher, _>( let proof_valid = verify_tree_proof::<SubstrateKeccakHasher, _>(
&self.get_preimage_slice(network_id).to_fixed_bytes(), &preimage_hash.to_fixed_bytes(),
self.proof.as_ref(), self.proof.as_ref(),
root_hash, root_hash,
self.proof_index, self.proof_index,
@ -362,22 +384,17 @@ where
Some((self.receiver.clone(), self.amount)) Some((self.receiver.clone(), self.amount))
} }
fn get_preimage_slice<NetworkId>(&self, network_id: NetworkId) -> H256 fn get_preimage_hash<NetworkId>(&self, network_id: NetworkId) -> Option<H256>
where where
NetworkId: Copy + Clone + Eq + PartialEq + UniqueSaturatedInto<u64>, NetworkId: Copy + Clone + Eq + PartialEq + UniqueSaturatedInto<u64>,
{ {
let slot_index_usize: usize = self.slot_index.unique_saturated_into(); let slot_index_usize: usize = self.slot_index.unique_saturated_into();
let mut slot_values: Vec<H256> = self.slots.to_vec();
match slot_values.get_mut(slot_index_usize) { self.slots.get(slot_index_usize)
Some(slot) => { .filter(|&&provided_hash| {
*slot = self.compute_arguments_hash(network_id); provided_hash == self.compute_arguments_hash(network_id)
cumulative_hash::<SubstrateKeccakHasher, _>(slot_values) })
} .map(|_| cumulative_hash::<SubstrateKeccakHasher, _>(self.slots.iter()))
None => {
SubstrateKeccakHasher::empty()
}
}
} }
pub fn get_unique_key<NetworkId>( pub fn get_unique_key<NetworkId>(
@ -483,7 +500,7 @@ impl UtxoThreadProof {
pub fn verify_thread_proof<Balance>( pub fn verify_thread_proof<Balance>(
&self, &self,
root_hash: H256, block_hash: H256,
gatekeeper_ref: &[u8], gatekeeper_ref: &[u8],
) -> Option<(AccountId32, Balance)> ) -> Option<(AccountId32, Balance)>
where where
@ -498,7 +515,7 @@ impl UtxoThreadProof {
let is_chain_valid = verify_hash_ancestry::<UtxoSha2Hasher, _>( let is_chain_valid = verify_hash_ancestry::<UtxoSha2Hasher, _>(
header_bytes, header_bytes,
self.ancestry_headers.iter(), self.ancestry_headers.iter(),
root_hash, block_hash,
); );
if !is_chain_valid { return None; } if !is_chain_valid { return None; }
@ -507,13 +524,11 @@ impl UtxoThreadProof {
self.getrawtransaction_bytes.as_ref() self.getrawtransaction_bytes.as_ref()
).ok()?; ).ok()?;
let header = Header::<UtxoSha2Hasher>::try_from(
self.gettxproof_bytes.as_ref(),
).ok()?;
let txid_preimage = transaction.compute_txid()?; let txid_preimage = transaction.compute_txid()?;
let root_hash = header.extract_proof(txid_preimage).ok()?; Header::<UtxoSha2Hasher>::verify_utxo_merkle_proof(
if root_hash != header.merkle_root { return None; } self.gettxproof_bytes.as_ref(),
txid_preimage,
).ok()?;
#[cfg(not(any(test, feature = "runtime-benchmarks")))] #[cfg(not(any(test, feature = "runtime-benchmarks")))]
let destination_script = Transaction::<UtxoSha2Hasher>::convert_into_p2tr(gatekeeper_ref)?; let destination_script = Transaction::<UtxoSha2Hasher>::convert_into_p2tr(gatekeeper_ref)?;

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-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! DATE: 2026-10-29, 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
@ -67,16 +67,16 @@ impl WeightInfo for () {
/// The range of component `a` is `[1, 300]`. /// The range of component `a` is `[1, 300]`.
fn extend_warp(a: u32, ) -> Weight { fn extend_warp(a: u32, ) -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `223 + a * (32 ±0)` // Measured: `274 + a * (33 ±0)`
// Estimated: `3687 + a * (32 ±0)` // Estimated: `3736 + a * (33 ±0)`
// Minimum execution time: 212_751_000 picoseconds. // Minimum execution time: 229_711_000 picoseconds.
Weight::from_parts(215_022_916, 0) Weight::from_parts(233_206_680, 0)
.saturating_add(Weight::from_parts(0, 3687)) .saturating_add(Weight::from_parts(0, 3736))
// Standard Error: 1_319 // Standard Error: 1_554
.saturating_add(Weight::from_parts(53_379, 0).saturating_mul(a.into())) .saturating_add(Weight::from_parts(66_095, 0).saturating_mul(a.into()))
.saturating_add(RocksDbWeight::get().reads(5)) .saturating_add(RocksDbWeight::get().reads(6))
.saturating_add(RocksDbWeight::get().writes(1)) .saturating_add(RocksDbWeight::get().writes(2))
.saturating_add(Weight::from_parts(0, 32).saturating_mul(a.into())) .saturating_add(Weight::from_parts(0, 33).saturating_mul(a.into()))
} }
/// Storage: `GhostWeaver::DisabledAuthorities` (r:1 w:0) /// Storage: `GhostWeaver::DisabledAuthorities` (r:1 w:0)
/// Proof: `GhostWeaver::DisabledAuthorities` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Proof: `GhostWeaver::DisabledAuthorities` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
@ -93,16 +93,16 @@ impl WeightInfo for () {
/// The range of component `a` is `[1, 300]`. /// The range of component `a` is `[1, 300]`.
fn weave_weft(a: u32, ) -> Weight { fn weave_weft(a: u32, ) -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `518 + a * (32 ±0)` // Measured: `569 + a * (33 ±0)`
// Estimated: `3982 + a * (32 ±0)` // Estimated: `4031 + a * (33 ±0)`
// Minimum execution time: 212_751_000 picoseconds. // Minimum execution time: 262_809_000 picoseconds.
Weight::from_parts(246_374_350, 0) Weight::from_parts(267_769_707, 0)
.saturating_add(Weight::from_parts(0, 3982)) .saturating_add(Weight::from_parts(0, 4031))
// Standard Error: 1_865 // Standard Error: 1_865
.saturating_add(Weight::from_parts(38_601, 0).saturating_mul(a.into())) .saturating_add(Weight::from_parts(39_109, 0).saturating_mul(a.into()))
.saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().reads(8))
.saturating_add(RocksDbWeight::get().writes(2)) .saturating_add(RocksDbWeight::get().writes(3))
.saturating_add(Weight::from_parts(0, 32).saturating_mul(a.into())) .saturating_add(Weight::from_parts(0, 33).saturating_mul(a.into()))
} }
/// 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`)
@ -119,12 +119,12 @@ impl WeightInfo for () {
/// 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: `449` // Measured: `500`
// Estimated: `3914` // Estimated: `3965`
// Minimum execution time: 217_880_000 picoseconds. // Minimum execution time: 217_481_000 picoseconds.
Weight::from_parts(217_119_201, 0) Weight::from_parts(216_492_234, 0)
// Standard Error: 4_368 // Standard Error: 4_414
.saturating_add(Weight::from_parts(3_445_491, 0).saturating_mul(a.into())) .saturating_add(Weight::from_parts(3_376_569, 0).saturating_mul(a.into()))
.saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().reads(6))
.saturating_add(RocksDbWeight::get().writes(4)) .saturating_add(RocksDbWeight::get().writes(4))
} }
@ -142,11 +142,11 @@ impl WeightInfo for () {
/// 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: `449` // Measured: `500`
// Estimated: `3914` // Estimated: `3965`
// Minimum execution time: 183_413_000 picoseconds. // Minimum execution time: 176_861_000 picoseconds.
Weight::from_parts(186_271_000, 0) Weight::from_parts(177_861_000, 0)
.saturating_add(Weight::from_parts(0, 3914)) .saturating_add(Weight::from_parts(0, 3965))
.saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().reads(6))
.saturating_add(RocksDbWeight::get().writes(4)) .saturating_add(RocksDbWeight::get().writes(4))
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "casper-runtime" name = "casper-runtime"
version = "4.0.14" version = "4.0.18"
build = "build.rs" build = "build.rs"
description = "Runtime of the Casper Network" description = "Runtime of the Casper Network"
edition.workspace = true edition.workspace = true
@ -75,6 +75,7 @@ ghost-networks = { workspace = true }
ghost-governance = { workspace = true } ghost-governance = { workspace = true }
ghost-weaver = { workspace = true } ghost-weaver = { workspace = true }
ghost-exodus = { workspace = true } ghost-exodus = { workspace = true }
ghost-exodus-runtime-api = { workspace = true }
ghost-sudo = { workspace = true } ghost-sudo = { workspace = true }
ghost-traits = { workspace = true } ghost-traits = { workspace = true }
ghost-helpers = { workspace = true } ghost-helpers = { workspace = true }
@ -188,6 +189,7 @@ std = [
"ghost-governance/std", "ghost-governance/std",
"ghost-weaver/std", "ghost-weaver/std",
"ghost-exodus/std", "ghost-exodus/std",
"ghost-exodus-runtime-api/std",
"ghost-sudo/std", "ghost-sudo/std",
"casper-runtime-constants/std", "casper-runtime-constants/std",
"runtime-common/std", "runtime-common/std",

View File

@ -69,6 +69,7 @@ pub use frame_system::Call as SystemCall;
pub use pallet_balances::Call as BalancesCall; pub use pallet_balances::Call as BalancesCall;
pub use pallet_election_provider_multi_phase::{Call as EPMCall, GeometricDepositBase}; pub use pallet_election_provider_multi_phase::{Call as EPMCall, GeometricDepositBase};
use ghost_helpers::networks::{NetworkCurve, NetworkType};
use ghost_weaver::sr25519::AuthorityId as GhostWeaverId; use ghost_weaver::sr25519::AuthorityId as GhostWeaverId;
use ghost_exodus::sr25519::AuthorityId as GhostExodusId; use ghost_exodus::sr25519::AuthorityId as GhostExodusId;
@ -123,7 +124,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("casper"), spec_name: create_runtime_str!("casper"),
impl_name: create_runtime_str!("casper-svengali"), impl_name: create_runtime_str!("casper-svengali"),
authoring_version: 0, authoring_version: 0,
spec_version: 7, spec_version: 12,
impl_version: 0, impl_version: 0,
apis: RUNTIME_API_VERSIONS, apis: RUNTIME_API_VERSIONS,
transaction_version: 1, transaction_version: 1,
@ -906,6 +907,7 @@ parameter_types! {
pub ExodusUnsignedPriority: TransactionPriority = pub ExodusUnsignedPriority: TransactionPriority =
Perbill::from_percent(90) * TransactionPriority::MAX; Perbill::from_percent(90) * TransactionPriority::MAX;
pub MaxExodusRequestCount: u32 = 32;
pub MaxAuthoritiesChunks: u32 = 32; pub MaxAuthoritiesChunks: u32 = 32;
pub WeavingDelay: u64 = 50; pub WeavingDelay: u64 = 50;
pub AttestationDelay: u64 = 25; pub AttestationDelay: u64 = 25;
@ -949,6 +951,7 @@ impl ghost_exodus::Config for Runtime {
type UnsignedPriority = ExodusUnsignedPriority; type UnsignedPriority = ExodusUnsignedPriority;
type MaxAuthorities = MaxActiveValidators; type MaxAuthorities = MaxActiveValidators;
type MaxAuthoritiesChunks = MaxAuthoritiesChunks; type MaxAuthoritiesChunks = MaxAuthoritiesChunks;
type MaxExodusRequestCount = MaxExodusRequestCount;
type DkgRoundPeriod = ExodusDkgRoundPeriod; type DkgRoundPeriod = ExodusDkgRoundPeriod;
type RemovalLimit = ExodusRemovalLimit; type RemovalLimit = ExodusRemovalLimit;
@ -1032,7 +1035,9 @@ pub type SignedExtra = (
/// All migrations that will run on the next runtime upgrade. /// All migrations that will run on the next runtime upgrade.
/// Should be cleared after release. /// Should be cleared after release.
pub type Migrations = (); pub type Migrations = (
ghost_weaver::migrations::MigrateV0ToV1<Runtime>,
);
/// Unchecked extrinsic type as expected by this runtime. /// Unchecked extrinsic type as expected by this runtime.
pub type UncheckedExtrinsic = pub type UncheckedExtrinsic =
@ -1156,6 +1161,39 @@ sp_api::impl_runtime_apis! {
} }
} }
impl ghost_exodus_runtime_api::ExodusApi<Block> for Runtime {
fn get_evm_rotation_metadata(
dkg_index: u64,
network_type: NetworkType,
) -> ghost_exodus_runtime_api::PreparedEvmMessage {
GhostExodus::api_get_evm_rotation(dkg_index, network_type)
}
fn get_evm_bridge_metadata(
dkg_index: u64,
network_id: u64,
session: u64,
) -> ghost_exodus_runtime_api::PreparedEvmMessage {
GhostExodus::api_get_evm_bridge(dkg_index, network_id, session)
}
fn get_evm_governance_metadata(
dkg_index: u64,
network_id: u64,
session: u64,
) -> ghost_exodus_runtime_api::PreparedEvmMessage {
GhostExodus::api_get_evm_governance(dkg_index, network_id, session)
}
fn pending_exodus_request_count() -> u32 {
GhostExodus::api_pending_exodus_request_count()
}
fn is_qualification_ongoing(network_curve: NetworkCurve) -> bool {
GhostExodus::api_is_qualification_ongoing(network_curve)
}
}
impl pallet_staking_runtime_api::StakingApi<Block, Balance, AccountId> for Runtime { impl pallet_staking_runtime_api::StakingApi<Block, Balance, AccountId> for Runtime {
fn nominations_quota(balance: Balance) -> u32 { fn nominations_quota(balance: Balance) -> u32 {
Staking::api_nominations_quota(balance) Staking::api_nominations_quota(balance)

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-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! DATE: 2026-10-29, 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
@ -46,252 +46,232 @@ use core::marker::PhantomData;
/// Weight functions for `ghost_exodus`. /// Weight functions for `ghost_exodus`.
pub struct WeightInfo<T>(PhantomData<T>); pub struct WeightInfo<T>(PhantomData<T>);
impl<T: frame_system::Config> ghost_exodus::WeightInfo for WeightInfo<T> { impl<T: frame_system::Config> ghost_exodus::WeightInfo for WeightInfo<T> {
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0) /// Storage: `GhostNetworks::NetworkTypes` (r:1 w:0)
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::NetworkTypes` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:1) /// Storage: `GhostExodus::QualificationDkgState` (r:1 w:1)
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::Round0Packages` (r:1 w:1)
/// Proof: `GhostExodus::Round0Packages` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0) /// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1)
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::Round0Packages` (r:0 w:1)
/// Proof: `GhostExodus::Round0Packages` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn register_round0_package() -> Weight { fn register_round0_package() -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `351` // Measured: `365`
// Estimated: `3816` // Estimated: `3830`
// Minimum execution time: 240_813_000 picoseconds. // Minimum execution time: 240_843_000 picoseconds.
Weight::from_parts(243_523_000, 0) Weight::from_parts(243_855_000, 0)
.saturating_add(Weight::from_parts(0, 3816)) .saturating_add(Weight::from_parts(0, 3830))
.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(2))
} }
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0) /// Storage: `GhostNetworks::NetworkTypes` (r:1 w:0)
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::NetworkTypes` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0) /// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0) /// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1)
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::Round0Packages` (r:1 w:0)
/// Proof: `GhostExodus::Round0Packages` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::Round1Packages` (r:1 w:1) /// Storage: `GhostExodus::Round1Packages` (r:1 w:1)
/// Proof: `GhostExodus::Round1Packages` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::Round1Packages` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::Round0Packages` (r:1 w:0)
/// Proof: `GhostExodus::Round0Packages` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// The range of component `c` is `[4, 300]`. /// The range of component `c` is `[4, 300]`.
fn register_round1_package(c: u32, ) -> Weight { fn register_round1_package(c: u32, ) -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `391 + c * (32 ±0)` // Measured: `405 + c * (32 ±0)`
// Estimated: `3856 + c * (32 ±0)` // Estimated: `3870 + c * (32 ±0)`
// Minimum execution time: 1_356_791_000 picoseconds. // Minimum execution time: 1_349_108_000 picoseconds.
Weight::from_parts(1_221_760_215, 0) Weight::from_parts(1_224_956_354, 0)
.saturating_add(Weight::from_parts(0, 3856)) .saturating_add(Weight::from_parts(0, 3870))
// Standard Error: 16_481 // Standard Error: 14_930
.saturating_add(Weight::from_parts(33_163_393, 0).saturating_mul(c.into())) .saturating_add(Weight::from_parts(33_204_477, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().writes(2)) .saturating_add(T::DbWeight::get().writes(1))
.saturating_add(Weight::from_parts(0, 32).saturating_mul(c.into())) .saturating_add(Weight::from_parts(0, 32).saturating_mul(c.into()))
} }
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0) /// Storage: `GhostNetworks::NetworkTypes` (r:1 w:0)
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::NetworkTypes` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0) /// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1)
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::Round2Packages` (r:1 w:1) /// Storage: `GhostExodus::Round2Packages` (r:1 w:1)
/// Proof: `GhostExodus::Round2Packages` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::Round2Packages` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// The range of component `c` is `[1, 37]`. /// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// The range of component `c` is `[1, 300]`.
fn register_encrypted_round2_packages(c: u32, ) -> Weight { fn register_encrypted_round2_packages(c: u32, ) -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `317 + c * (257 ±0)` // Measured: `332 + c * (32 ±0)`
// Estimated: `3783 + c * (257 ±0)` // Estimated: `3797 + c * (32 ±0)`
// Minimum execution time: 254_590_000 picoseconds. // Minimum execution time: 245_305_000 picoseconds.
Weight::from_parts(258_091_105, 0) Weight::from_parts(253_863_353, 0)
.saturating_add(Weight::from_parts(0, 3783)) .saturating_add(Weight::from_parts(0, 3797))
// Standard Error: 25_082 // Standard Error: 3_527
.saturating_add(Weight::from_parts(6_595_878, 0).saturating_mul(c.into())) .saturating_add(Weight::from_parts(810_623, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(2)) .saturating_add(T::DbWeight::get().writes(1))
.saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into())) .saturating_add(Weight::from_parts(0, 32).saturating_mul(c.into()))
} }
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0) /// Storage: `GhostNetworks::NetworkTypes` (r:1 w:0)
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::NetworkTypes` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0) /// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1)
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::Complaints` (r:1 w:1) /// Storage: `GhostExodus::Complaints` (r:1 w:1)
/// Proof: `GhostExodus::Complaints` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::Complaints` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// The range of component `c` is `[1, 37]`. /// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// The range of component `c` is `[1, 300]`.
fn register_round3_complaints(c: u32, ) -> Weight { fn register_round3_complaints(c: u32, ) -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `317 + c * (257 ±0)` // Measured: `332 + c * (32 ±0)`
// Estimated: `3783 + c * (257 ±0)` // Estimated: `3797 + c * (32 ±0)`
// Minimum execution time: 240_888_000 picoseconds. // Minimum execution time: 238_391_000 picoseconds.
Weight::from_parts(245_480_975, 0) Weight::from_parts(242_937_380, 0)
.saturating_add(Weight::from_parts(0, 3783)) .saturating_add(Weight::from_parts(0, 3797))
// Standard Error: 13_733 // Standard Error: 1_789
.saturating_add(Weight::from_parts(371_161, 0).saturating_mul(c.into())) .saturating_add(Weight::from_parts(39_316, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(2)) .saturating_add(T::DbWeight::get().writes(1))
.saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into())) .saturating_add(Weight::from_parts(0, 32).saturating_mul(c.into()))
} }
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0) /// Storage: `GhostNetworks::NetworkTypes` (r:1 w:0)
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::NetworkTypes` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0) /// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1)
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::Justifications` (r:1 w:1) /// Storage: `GhostExodus::Justifications` (r:1 w:1)
/// Proof: `GhostExodus::Justifications` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::Justifications` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// The range of component `c` is `[1, 37]`. /// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// The range of component `c` is `[1, 300]`.
fn register_round4_justifications(c: u32, ) -> Weight { fn register_round4_justifications(c: u32, ) -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `317 + c * (257 ±0)` // Measured: `332 + c * (32 ±0)`
// Estimated: `3783 + c * (257 ±0)` // Estimated: `3797 + c * (32 ±0)`
// Minimum execution time: 251_521_000 picoseconds. // Minimum execution time: 245_916_000 picoseconds.
Weight::from_parts(253_053_636, 0) Weight::from_parts(251_070_206, 0)
.saturating_add(Weight::from_parts(0, 3783)) .saturating_add(Weight::from_parts(0, 3797))
// Standard Error: 14_691 // Standard Error: 2_152
.saturating_add(Weight::from_parts(4_101_872, 0).saturating_mul(c.into())) .saturating_add(Weight::from_parts(508_508, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(2)) .saturating_add(T::DbWeight::get().writes(1))
.saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into())) .saturating_add(Weight::from_parts(0, 32).saturating_mul(c.into()))
} }
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0) /// Storage: `GhostNetworks::NetworkTypes` (r:1 w:0)
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::NetworkTypes` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0) /// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1)
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::Verifications` (r:1 w:1)
/// Proof: `GhostExodus::Verifications` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::VerifyingKeyConsensus` (r:1 w:1) /// Storage: `GhostExodus::VerifyingKeyConsensus` (r:1 w:1)
/// Proof: `GhostExodus::VerifyingKeyConsensus` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::VerifyingKeyConsensus` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn register_round5_verifying_package() -> Weight {
// Proof Size summary in bytes:
// Measured: `448`
// Estimated: `3913`
// Minimum execution time: 265_187_000 picoseconds.
Weight::from_parts(269_124_000, 0)
.saturating_add(Weight::from_parts(0, 3913))
.saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(3))
}
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0)
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0) /// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1) /// Storage: `GhostExodus::Verifications` (r:1 w:1)
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::Verifications` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn register_round5_verifying_package() -> Weight {
// Proof Size summary in bytes:
// Measured: `462`
// Estimated: `3927`
// Minimum execution time: 271_654_000 picoseconds.
Weight::from_parts(286_394_000, 0)
.saturating_add(Weight::from_parts(0, 3927))
.saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().writes(2))
}
/// Storage: `GhostNetworks::NetworkTypes` (r:1 w:0)
/// Proof: `GhostNetworks::NetworkTypes` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::VerifyingSharesParticipants` (r:1 w:1)
/// Proof: `GhostExodus::VerifyingSharesParticipants` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::VerifyingKeyConsensus` (r:1 w:0) /// Storage: `GhostExodus::VerifyingKeyConsensus` (r:1 w:0)
/// Proof: `GhostExodus::VerifyingKeyConsensus` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::VerifyingKeyConsensus` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::VerifyingShares` (r:1 w:1) /// Storage: `GhostExodus::VerifyingShares` (r:1 w:1)
/// Proof: `GhostExodus::VerifyingShares` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::VerifyingShares` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::VerifyingSharesParticipants` (r:1 w:1)
/// Proof: `GhostExodus::VerifyingSharesParticipants` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// The range of component `c` is `[1, 9]`. /// The range of component `c` is `[1, 9]`.
fn register_round6_public_share_package(c: u32, ) -> Weight { fn register_round6_public_share_package(c: u32, ) -> Weight {
// 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: `1698 + c * (1019 ±27)`
// Minimum execution time: 284_558_000 picoseconds. // Minimum execution time: 283_863_000 picoseconds.
Weight::from_parts(284_117_484, 0) Weight::from_parts(286_691_978, 0)
.saturating_add(Weight::from_parts(0, 1551)) .saturating_add(Weight::from_parts(0, 1698))
// Standard Error: 74_361 // Standard Error: 85_685
.saturating_add(Weight::from_parts(3_551_271, 0).saturating_mul(c.into())) .saturating_add(Weight::from_parts(2_854_037, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(3)) .saturating_add(T::DbWeight::get().writes(2))
.saturating_add(Weight::from_parts(0, 1019).saturating_mul(c.into())) .saturating_add(Weight::from_parts(0, 1019).saturating_mul(c.into()))
} }
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0) /// Storage: `GhostNetworks::NetworkTypes` (r:1 w:0)
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::NetworkTypes` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0) /// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ActiveDkgAuthorities` (r:1 w:0) /// Storage: `GhostExodus::ActiveDkgAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::ActiveDkgAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::ActiveDkgAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ActiveAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::ActiveAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusRequests` (r:1 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`)
/// Storage: `GhostExodus::RoastSessions` (r:1 w:1) /// Storage: `GhostExodus::RoastSessions` (r:1 w:1)
/// Proof: `GhostExodus::RoastSessions` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::RoastSessions` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1) /// Storage: `GhostExodus::NonceCommitments` (r:1 w:1)
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::NonceCommitments` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ActiveAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::ActiveAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::RoastSessionStates` (r:1 w:1) /// Storage: `GhostExodus::RoastSessionStates` (r:1 w:1)
/// Proof: `GhostExodus::RoastSessionStates` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::RoastSessionStates` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::NonceCommitments` (r:0 w:1)
/// 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: `627` // Measured: `633`
// Estimated: `4092` // Estimated: `4098`
// Minimum execution time: 315_066_000 picoseconds. // Minimum execution time: 369_666_000 picoseconds.
Weight::from_parts(318_561_000, 0) Weight::from_parts(373_163_000, 0)
.saturating_add(Weight::from_parts(0, 4092)) .saturating_add(Weight::from_parts(0, 4098))
.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(4))
} }
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0) /// Storage: `GhostNetworks::NetworkTypes` (r:1 w:0)
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::NetworkTypes` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0) /// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ActiveDkgAuthorities` (r:1 w:0) /// Storage: `GhostExodus::ActiveDkgAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::ActiveDkgAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::ActiveDkgAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ActiveAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::ActiveAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::RoastSessions` (r:1 w:0) /// Storage: `GhostExodus::RoastSessions` (r:1 w:0)
/// Proof: `GhostExodus::RoastSessions` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::RoastSessions` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1) /// Storage: `GhostExodus::RoastSessionStates` (r:1 w:1)
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::RoastSessionStates` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ActiveAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::ActiveAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusRequests` (r:1 w:0) /// Storage: `GhostExodus::ExodusRequests` (r:1 w:0)
/// Proof: `GhostExodus::ExodusRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::ExodusRequests` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::GroupCommitters` (r:1 w:1) /// Storage: `GhostExodus::GroupCommitters` (r:1 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::RoastSessionStates` (r:1 w:1)
/// Proof: `GhostExodus::RoastSessionStates` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::GroupCommitmentsConsensus` (r:1 w:1) /// Storage: `GhostExodus::GroupCommitmentsConsensus` (r:1 w:1)
/// 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: `992` // Measured: `1030`
// Estimated: `4457` // Estimated: `4495`
// Minimum execution time: 338_644_000 picoseconds. // Minimum execution time: 339_569_000 picoseconds.
Weight::from_parts(343_954_000, 0) Weight::from_parts(341_788_000, 0)
.saturating_add(Weight::from_parts(0, 4457)) .saturating_add(Weight::from_parts(0, 4495))
.saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().reads(9))
.saturating_add(T::DbWeight::get().writes(4)) .saturating_add(T::DbWeight::get().writes(3))
} }
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0) /// Storage: `GhostNetworks::NetworkTypes` (r:1 w:0)
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostNetworks::NetworkTypes` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0) /// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ActiveDkgAuthorities` (r:1 w:0) /// Storage: `GhostExodus::ActiveDkgAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::ActiveDkgAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::ActiveDkgAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ActiveAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::ActiveAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::RoastSessions` (r:1 w:1) /// Storage: `GhostExodus::RoastSessions` (r:1 w:1)
/// Proof: `GhostExodus::RoastSessions` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::RoastSessions` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1) /// Storage: `GhostExodus::RoastSessionStates` (r:1 w:1)
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::RoastSessionStates` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ActiveAuthorities` (r:1 w:0)
/// Proof: `GhostExodus::ActiveAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::GroupCommitmentsConsensus` (r:1 w:1) /// Storage: `GhostExodus::GroupCommitmentsConsensus` (r:1 w:1)
/// Proof: `GhostExodus::GroupCommitmentsConsensus` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::GroupCommitmentsConsensus` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusRequests` (r:1 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`)
/// Storage: `GhostExodus::ExodusSignedGovernance` (r:1 w:1) /// Storage: `GhostExodus::ExodusSignedGovernance` (r:1 w:1)
/// Proof: `GhostExodus::ExodusSignedGovernance` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::ExodusSignedGovernance` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::RoastSessionStates` (r:1 w:1)
/// Proof: `GhostExodus::RoastSessionStates` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::NonceCommitments` (r:1 w:0) /// Storage: `GhostExodus::NonceCommitments` (r:1 w:0)
/// Proof: `GhostExodus::NonceCommitments` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::NonceCommitments` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::VerifyingShares` (r:1 w:0) /// Storage: `GhostExodus::VerifyingShares` (r:1 w:0)
@ -306,13 +286,13 @@ impl<T: frame_system::Config> ghost_exodus::WeightInfo for WeightInfo<T> {
/// 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: `11299` // Measured: `11337`
// Estimated: `14764` // Estimated: `14802`
// Minimum execution time: 3_968_938_000 picoseconds. // Minimum execution time: 4_046_065_000 picoseconds.
Weight::from_parts(3_975_088_000, 0) Weight::from_parts(4_059_064_000, 0)
.saturating_add(Weight::from_parts(0, 14764)) .saturating_add(Weight::from_parts(0, 14802))
.saturating_add(T::DbWeight::get().reads(15)) .saturating_add(T::DbWeight::get().reads(14))
.saturating_add(T::DbWeight::get().writes(9)) .saturating_add(T::DbWeight::get().writes(8))
} }
/// Storage: `GhostExodus::CounterForExodusRequests` (r:1 w:1) /// Storage: `GhostExodus::CounterForExodusRequests` (r:1 w:1)
/// Proof: `GhostExodus::CounterForExodusRequests` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::CounterForExodusRequests` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
@ -324,17 +304,17 @@ impl<T: frame_system::Config> ghost_exodus::WeightInfo for WeightInfo<T> {
/// 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: `GhostExodus::QualificationDkgState` (r:1 w:0) /// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
/// 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::CurrentExodusSessions` (r:1 w:1)
/// Proof: `GhostExodus::CurrentExodus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Proof: `GhostExodus::CurrentExodusSessions` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `GhostExodus::ExodusRequests` (r:1 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: `457`
// Estimated: `3913` // Estimated: `3922`
// Minimum execution time: 191_374_000 picoseconds. // Minimum execution time: 195_417_000 picoseconds.
Weight::from_parts(192_991_000, 0) Weight::from_parts(196_842_000, 0)
.saturating_add(Weight::from_parts(0, 3913)) .saturating_add(Weight::from_parts(0, 3922))
.saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().reads(7))
.saturating_add(T::DbWeight::get().writes(5)) .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-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! DATE: 2026-10-29, 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_governance::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `283` // Measured: `283`
// Estimated: `3748` // Estimated: `3748`
// Minimum execution time: 229_254_000 picoseconds. // Minimum execution time: 224_852_000 picoseconds.
Weight::from_parts(229_992_093, 0) Weight::from_parts(225_347_733, 0)
.saturating_add(Weight::from_parts(0, 3748)) .saturating_add(Weight::from_parts(0, 3748))
// Standard Error: 25_680 // Standard Error: 21_234
.saturating_add(Weight::from_parts(1_874_433, 0).saturating_mul(p.into())) .saturating_add(Weight::from_parts(1_581_800, 0).saturating_mul(p.into()))
.saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().writes(5)) .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-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! DATE: 2026-10-29, 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
@ -46,34 +46,40 @@ use core::marker::PhantomData;
/// Weight functions for `ghost_weaver`. /// Weight functions for `ghost_weaver`.
pub struct WeightInfo<T>(PhantomData<T>); pub struct WeightInfo<T>(PhantomData<T>);
impl<T: frame_system::Config> ghost_weaver::WeightInfo for WeightInfo<T> { impl<T: frame_system::Config> ghost_weaver::WeightInfo for WeightInfo<T> {
/// Storage: `GhostWeaver::DisabledAuthorities` (r:1 w:0)
/// Proof: `GhostWeaver::DisabledAuthorities` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `GhostWeaver::Authorities` (r:1 w:0) /// Storage: `GhostWeaver::Authorities` (r:1 w:0)
/// Proof: `GhostWeaver::Authorities` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Proof: `GhostWeaver::Authorities` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `GhostWeaver::AttestedBlocks` (r:1 w:1)
/// Proof: `GhostWeaver::AttestedBlocks` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostWeaver::NetworkAttestations` (r:1 w:1)
/// Proof: `GhostWeaver::NetworkAttestations` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostWeaver::DisabledAuthorities` (r:1 w:0)
/// Proof: `GhostWeaver::DisabledAuthorities` (`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: `GhostWeaver::TapestryDrafts` (r:1 w:0) /// Storage: `GhostWeaver::TapestryDrafts` (r:1 w:0)
/// Proof: `GhostWeaver::TapestryDrafts` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Proof: `GhostWeaver::TapestryDrafts` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostWeaver::NetworkAttestations` (r:1 w:1)
/// Proof: `GhostWeaver::NetworkAttestations` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// The range of component `a` is `[1, 300]`. /// The range of component `a` is `[1, 300]`.
fn extend_warp(a: u32, ) -> Weight { fn extend_warp(a: u32, ) -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `223 + a * (32 ±0)` // Measured: `274 + a * (33 ±0)`
// Estimated: `3687 + a * (32 ±0)` // Estimated: `3736 + a * (33 ±0)`
// Minimum execution time: 212_751_000 picoseconds. // Minimum execution time: 229_711_000 picoseconds.
Weight::from_parts(215_022_916, 0) Weight::from_parts(235_206_680, 0)
.saturating_add(Weight::from_parts(0, 3687)) .saturating_add(Weight::from_parts(0, 3736))
// Standard Error: 1_319 // Standard Error: 1_554
.saturating_add(Weight::from_parts(53_379, 0).saturating_mul(a.into())) .saturating_add(Weight::from_parts(66_905, 0).saturating_mul(a.into()))
.saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(1)) .saturating_add(T::DbWeight::get().writes(2))
.saturating_add(Weight::from_parts(0, 32).saturating_mul(a.into())) .saturating_add(Weight::from_parts(0, 33).saturating_mul(a.into()))
} }
/// Storage: `GhostWeaver::DisabledAuthorities` (r:1 w:0)
/// Proof: `GhostWeaver::DisabledAuthorities` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `GhostWeaver::Authorities` (r:1 w:0) /// Storage: `GhostWeaver::Authorities` (r:1 w:0)
/// Proof: `GhostWeaver::Authorities` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Proof: `GhostWeaver::Authorities` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `GhostWeaver::AttestedBlocks` (r:1 w:1)
/// Proof: `GhostWeaver::AttestedBlocks` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostWeaver::NetworkAttestations` (r:1 w:0)
/// Proof: `GhostWeaver::NetworkAttestations` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `GhostWeaver::DisabledAuthorities` (r:1 w:0)
/// Proof: `GhostWeaver::DisabledAuthorities` (`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: `GhostWeaver::TapestryDrafts` (r:1 w:0) /// Storage: `GhostWeaver::TapestryDrafts` (r:1 w:0)
@ -85,16 +91,16 @@ impl<T: frame_system::Config> ghost_weaver::WeightInfo for WeightInfo<T> {
/// The range of component `a` is `[1, 300]`. /// The range of component `a` is `[1, 300]`.
fn weave_weft(a: u32, ) -> Weight { fn weave_weft(a: u32, ) -> Weight {
// Proof Size summary in bytes: // Proof Size summary in bytes:
// Measured: `518 + a * (32 ±0)` // Measured: `569 + a * (33 ±0)`
// Estimated: `3982 + a * (32 ±0)` // Estimated: `4031 + a * (33 ±0)`
// Minimum execution time: 243_006_000 picoseconds. // Minimum execution time: 262_809_000 picoseconds.
Weight::from_parts(246_374_350, 0) Weight::from_parts(267_769_707, 0)
.saturating_add(Weight::from_parts(0, 3982)) .saturating_add(Weight::from_parts(0, 4031))
// Standard Error: 1_865 // Standard Error: 2_375
.saturating_add(Weight::from_parts(38_601, 0).saturating_mul(a.into())) .saturating_add(Weight::from_parts(39_109, 0).saturating_mul(a.into()))
.saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().reads(8))
.saturating_add(T::DbWeight::get().writes(2)) .saturating_add(T::DbWeight::get().writes(3))
.saturating_add(Weight::from_parts(0, 32).saturating_mul(a.into())) .saturating_add(Weight::from_parts(0, 33).saturating_mul(a.into()))
} }
/// 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`)
@ -111,13 +117,13 @@ impl<T: frame_system::Config> ghost_weaver::WeightInfo for WeightInfo<T> {
/// 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: `449` // Measured: `500`
// Estimated: `3914` // Estimated: `3965`
// Minimum execution time: 217_880_000 picoseconds. // Minimum execution time: 217_481_000 picoseconds.
Weight::from_parts(217_119_201, 0) Weight::from_parts(216_492_234, 0)
.saturating_add(Weight::from_parts(0, 3914)) .saturating_add(Weight::from_parts(0, 3965))
// Standard Error: 5_460 // Standard Error: 4_414
.saturating_add(Weight::from_parts(3_445_491, 0).saturating_mul(a.into())) .saturating_add(Weight::from_parts(3_376_569, 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(4)) .saturating_add(T::DbWeight::get().writes(4))
} }
@ -135,11 +141,11 @@ impl<T: frame_system::Config> ghost_weaver::WeightInfo for WeightInfo<T> {
/// 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: `449` // Measured: `500`
// Estimated: `3914` // Estimated: `3965`
// Minimum execution time: 183_413_000 picoseconds. // Minimum execution time: 176_861_000 picoseconds.
Weight::from_parts(186_271_000, 0) Weight::from_parts(177_918_000, 0)
.saturating_add(Weight::from_parts(0, 3914)) .saturating_add(Weight::from_parts(0, 3965))
.saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(4)) .saturating_add(T::DbWeight::get().writes(4))
} }

View File

@ -20,10 +20,11 @@ MORDOR_RPC=(
HOODI_RPC=( HOODI_RPC=(
"https://rpc.hoodi.ethpandaops.io" "https://rpc.hoodi.ethpandaops.io"
"https://0xrpc.io/hoodi" "https://0xrpc.io/hoodi"
"https://ethereum-hoodi.gateway.tatum.io"
"https://rpc.sentio.xyz/hoodi" "https://rpc.sentio.xyz/hoodi"
) )
HOODI_GATEKEEPER="0xc9D5f50e17eeEAF39E0D0cDbbcF4B04FA4A4Bbd6"
show_help() { show_help() {
cat << EOF cat << EOF
Usage: $(basename "$0") [OPTIONS] Usage: $(basename "$0") [OPTIONS]
@ -58,20 +59,66 @@ spinner() {
tput cnorm tput cnorm
} }
pad32() {
local h="$1"
h="${h#0x}"
printf "%064s" "$h" | tr ' ' '0'
}
build_calldata() {
local selector="$1"
local at_block="$2"
local s_hex
local b_hex
s_hex=$(printf "%x" 0)
b_hex=$(printf "%x" "$at_block")
echo "0x${selector#0x}$(pad32 "$s_hex")$(pad32 "$b_hex")"
}
decode_get_root() {
local res_json="$1"
local hex
hex=$(echo "$res_json" | grep -o '"result":"[^"]*' | cut -d'"' -f4)
if [[ -z "$hex" || "$hex" == "null" ]]; then
echo "ERROR"
return
fi
hex="${hex#0x}"
if (( ${#hex} < 128 )); then
echo "ERROR"
return
fi
local word1="${hex:0:64}"
local word2="${hex:64:64}"
local num
num=$(printf "%d" "0x$word2" 2>/dev/null)
[[ -z "$num" ]] && num="$word2"
echo "0x${word1} ${num}"
}
check_network() { check_network() {
local name=$1 local name=$1
shift local gatekeeper=$2
shift 2
local rpcs=("$@") local rpcs=("$@")
local target_dir="${OUT_DIR:-/tmp}" local target_dir="${OUT_DIR:-/tmp}"
local tmp_file="${target_dir}/$(date +%s)_${name,,}" local tmp_file="${target_dir}/$(date +%s)_${name,,}"
local root_width=66
local sess_width=6
local max_len=0 local max_len=0
for e in "${rpcs[@]}"; do (( ${#e} > max_len )) && max_len=${#e}; done for e in "${rpcs[@]}"; do (( ${#e} > max_len )) && max_len=${#e}; done
[[ $max_len -lt 20 ]] && max_len=20 [[ $max_len -lt 20 ]] && max_len=20
local total_width=$(( max_len + 32 )) local total_width=$(( max_len + 95 ))
local line=$(printf "%.0s-" $(seq 1 $total_width)) local line=$(printf "%.0s-" $(seq 1 $total_width))
local header_fmt="| %-${max_len}s | %-12s | %-10s |\n" local header_fmt="| %-${max_len}s | %-10s | %-${root_width}s | %-${sess_width}s |\n"
( (
for endpoint in "${rpcs[@]}"; do for endpoint in "${rpcs[@]}"; do
@ -80,11 +127,15 @@ check_network() {
hex=$(echo "$res_b" | grep -o '"result":"[^"]*' | cut -d'"' -f4) hex=$(echo "$res_b" | grep -o '"result":"[^"]*' | cut -d'"' -f4)
[[ -n "$hex" && "$hex" != "null" ]] && blk=$(printf "%d" "$hex") || blk="ERROR" [[ -n "$hex" && "$hex" != "null" ]] && blk=$(printf "%d" "$hex") || blk="ERROR"
res_l=$(curl -s -X POST -H "Content-Type: application/json" --max-time 10 \ root="N/A"
--data '{"jsonrpc":"2.0","method":"eth_getLogs","params":[{"fromBlock":"0x1","toBlock":"0x2"}],"id":1}' "$endpoint") if [[ -n "$blk" && "$blk" != "ERROR" ]]; then
echo "$res_l" | grep -q '"result"' && ev="YES" || ev="NO" calldata=$(build_calldata "$SELECTOR" "$blk")
payload=$(printf '{"jsonrpc":"2.0","method":"eth_call","params":[{"to":"%s","data":"%s"},"latest"],"id":1}' "$gatekeeper" "$calldata")
res_c=$(curl -s -X POST -H "Content-Type: application/json" --max-time 10 --data "$payload" "$endpoint")
root=$(decode_get_root "$res_c")
fi
echo "$endpoint|$blk|$ev" echo "$endpoint|$blk|$root"
done done
) > "$tmp_file" & ) > "$tmp_file" &
@ -94,10 +145,17 @@ check_network() {
spinner $worker_pid "$name" spinner $worker_pid "$name"
local table_output="$line\n" local table_output="$line\n"
table_output+=$(printf "$header_fmt" "Endpoint" "Block" "Events")"\n" table_output+=$(printf "$header_fmt" "Endpoint" "Block" "Merkle Root" "Session")"\n"
table_output+="$line\n" table_output+="$line\n"
while IFS="|" read -r url blk ev; do while IFS="|" read -r url blk root; do
table_output+=$(printf "$header_fmt" "$url" "$blk" "$ev")"\n" if [[ "$root" == *" "* ]]; then
hash="${root%% *}"
sess="${root##* }"
else
hash="$root"
sess="-"
fi
table_output+=$(printf "$header_fmt" "$url" "$blk" "$hash" "$sess")"\n"
done < "$tmp_file" done < "$tmp_file"
table_output+="$line" table_output+="$line"
@ -111,6 +169,7 @@ check_network() {
CHOSEN_CHAIN="" CHOSEN_CHAIN=""
BASE_OUT_DIR="" BASE_OUT_DIR=""
CUSTOM_RPCS="" CUSTOM_RPCS=""
SELECTOR="d4be7935"
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case "$1" in case "$1" in
@ -132,14 +191,14 @@ if [[ -n "$CUSTOM_RPCS" ]]; then
check_network "Custom" "${USER_RPC_ARRAY[@]}" check_network "Custom" "${USER_RPC_ARRAY[@]}"
else else
if [ "$CHOSEN_CHAIN" == "sepolia" ]; then if [ "$CHOSEN_CHAIN" == "sepolia" ]; then
check_network "Sepolia" "${SEPOLIA_RPC[@]}" check_network "Sepolia" "$SEPOLIA_GATEKEEPER" "${HOODI_RPC[@]}"
elif [ "$CHOSEN_CHAIN" == "mordor" ]; then elif [ "$CHOSEN_CHAIN" == "mordor" ]; then
check_network "Mordor" "${MORDOR_RPC[@]}" check_network "Mordor" "$MORDOR_GATEKEEPER" "${HOODI_RPC[@]}"
elif [ "$CHOSEN_CHAIN" == "hoodi" ]; then elif [ "$CHOSEN_CHAIN" == "hoodi" ]; then
check_network "Hoodi" "${HOODI_RPC[@]}" check_network "Hoodi" "$HOODI_GATEKEEPER" "${HOODI_RPC[@]}"
else else
check_network "Sepolia" "${SEPOLIA_RPC[@]}" check_network "Sepolia" "$SEPOLIA_GATEKEEPER" "${SEPOLIA_RPC[@]}"
check_network "Mordor" "${MORDOR_RPC[@]}" check_network "Mordor" "$MORDOR_GATEKEEPER" "${MORDOR_RPC[@]}"
check_network "Hoodi" "${HOODI_RPC[@]}" check_network "Hoodi" "$HOODI_GATEKEEPER" "${HOODI_RPC[@]}"
fi fi
fi fi

View File

@ -9,6 +9,7 @@ UNIT_FILE=false
SKIP_BUILD=false SKIP_BUILD=false
SET_ENVIRONMENT=false SET_ENVIRONMENT=false
VALIDATE=false VALIDATE=false
FIX_WEAVER_KEYSTORE=false
EXECUTABLE_PATH="/usr/bin/" EXECUTABLE_PATH="/usr/bin/"
SPECIFICATION_PATH="/etc/ghost/" SPECIFICATION_PATH="/etc/ghost/"
BASE_PATH="/var/lib/ghost" BASE_PATH="/var/lib/ghost"
@ -120,7 +121,8 @@ help() {
echo -e "-s, --base-path\n\tPath to the folder with chain database ('/var/lib/ghost' is default)." echo -e "-s, --base-path\n\tPath to the folder with chain database ('/var/lib/ghost' is default)."
echo -e "-c, --specification-path\n\tPath to specification ('/etc/ghost' is default)." echo -e "-c, --specification-path\n\tPath to specification ('/etc/ghost' is default)."
echo -e "-n, --specification-name\n\tSpecification name to be used ('casper' is default)." echo -e "-n, --specification-name\n\tSpecification name to be used ('casper' is default)."
echo -e "-g, --generate-genesis\n\tGenerate keys to participate in the genesis file." echo -e "--generate-genesis\n\tGenerate keys to participate in the genesis file."
echo -e "--fix-weaver-keystore\n\tFix keystore prefixes (weav -> wevr) to ensure the offchain worker can find the key."
echo -e "--hard-reset-i-know-what-im-doing\n\tWill completely remove ledger along with associated session keys." echo -e "--hard-reset-i-know-what-im-doing\n\tWill completely remove ledger along with associated session keys."
echo -e "-h, --help\n\tPrints help information." echo -e "-h, --help\n\tPrints help information."
} }
@ -192,9 +194,12 @@ while [ $# -gt 0 ]; do
--hard-reset-i-know-what-im-doing) --hard-reset-i-know-what-im-doing)
HARD_RESET=true HARD_RESET=true
;; ;;
--generate-genesis|-g) --generate-genesis)
GENERATE_GENESIS=true GENERATE_GENESIS=true
;; ;;
--fix-weaver-keystore)
FIX_WEAVER_KEYSTORE=true
;;
--help|-h) --help|-h)
help help
exit 0 exit 0
@ -528,6 +533,51 @@ if [ $UNIT_FILE = true ]; then
fi fi
fi fi
if [ $FIX_WEAVER_KEYSTORE = true ]; then
echo "[+] Starting keystore prefix fix (weav -> wevr)..."
KEYSTORE_DIR="${BASE_PATH%/}/chains/${SPECIFICATION_NAME}_staging_testnet/keystore"
echo "[+] Requesting sudo privileges to modify keystore files..."
sudo -v
if ! sudo test -d "$KEYSTORE_DIR"; then
echo "[-] Keystore directory not found at: $KEYSTORE_DIR"
echo "[-] Please check your --base-path and --specification-name settings."
exit 1
fi
WRONG_PREFIX="77656176" # "weav"
CORRECT_PREFIX="77657672" # "wevr"
CHANGES_MADE=0
while IFS= read -r file; do
if sudo test -f "$file"; then
filename=$(basename "$file")
if [[ "$filename" == "$WRONG_PREFIX"* ]]; then
new_filename="${CORRECT_PREFIX}${filename#$WRONG_PREFIX}"
new_file_path="$KEYSTORE_DIR/$new_filename"
if sudo test -f "$new_file_path"; then
echo "[!] WARNING: File with correct prefix already exists: $new_filename. Skipping."
else
sudo mv "$file" "$new_file_path"
echo "[+] Renamed: $filename -> $new_filename"
CHANGES_MADE=1
fi
fi
fi
done < <(sudo find "$KEYSTORE_DIR" -maxdepth 1 -type f)
if [ $CHANGES_MADE -eq 1 ]; then
echo "[+] Keystore fix applied successfully."
echo "[+] IMPORTANT: You must restart the ghost-node service for changes to take effect."
echo "[+] Run: sudo systemctl restart ghost-node"
else
echo "[+] No files with the wrong 'weav' prefix were found. Everything is fine."
fi
fi
if [ "$GENERATE_GENESIS" = true ]; then if [ "$GENERATE_GENESIS" = true ]; then
echo "[+] Generating keys for genesis configuration..." echo "[+] Generating keys for genesis configuration..."