diff --git a/Cargo.lock b/Cargo.lock index 2ad3750..33965f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1195,7 +1195,7 @@ dependencies = [ [[package]] name = "casper-runtime" -version = "4.0.17" +version = "4.0.18" dependencies = [ "casper-runtime-constants", "frame-benchmarking", @@ -3762,7 +3762,7 @@ dependencies = [ [[package]] name = "ghost-helpers" -version = "0.0.15" +version = "0.0.16" dependencies = [ "frame-support", "ghost-traits", diff --git a/pallets/helpers/Cargo.toml b/pallets/helpers/Cargo.toml index 07c4e9c..014faae 100644 --- a/pallets/helpers/Cargo.toml +++ b/pallets/helpers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ghost-helpers" -version = "0.0.15" +version = "0.0.16" description = "Cryptographic utility suite for custom runtimes: optimized Bitmaps, UTXO parsing, Merkle Tree proofs, and Hash Chain components." license.workspace = true authors.workspace = true diff --git a/pallets/helpers/src/lib.rs b/pallets/helpers/src/lib.rs index 3b9f72a..5ad4402 100644 --- a/pallets/helpers/src/lib.rs +++ b/pallets/helpers/src/lib.rs @@ -3,6 +3,7 @@ use ghost_traits::hashing::GhostHasher; use sp_core::H256; use sp_io::hashing::{blake2_256, keccak_256, sha2_256}; +use sp_runtime::{Perbill, traits::{Saturating, UniqueSaturatedInto}}; pub mod bounded_bitmap; pub mod hash_chain; @@ -49,6 +50,34 @@ where value - f } +pub fn get_submission_probability( + participants_count: usize, + values_count: usize, + blocks_left: B +) -> Perbill +where + B: UniqueSaturatedInto, +{ + // y = min(1, (1/6) + (A-C)/2x) + let a: u32 = participants_count.unique_saturated_into(); + let c: u32 = values_count.unique_saturated_into(); + 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) +} + macro_rules! impl_ghost_hasher { ( struct $name:ident; @@ -155,4 +184,38 @@ mod tests { assert_eq!(get_byzantium_quorum_threshold(4u64), 3u64); 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()); + } } diff --git a/pallets/weaver/src/lib.rs b/pallets/weaver/src/lib.rs index 7379a29..b8dadcf 100644 --- a/pallets/weaver/src/lib.rs +++ b/pallets/weaver/src/lib.rs @@ -22,13 +22,16 @@ use sp_runtime::{ storage::StorageValueRef, storage_lock::{StorageLock, Time}, }, - traits::{BlockNumberProvider, Saturating, UniqueSaturatedInto}, - RuntimeAppPublic, + traits::{ + TrailingZeroInput, BlockNumberProvider, Saturating, + UniqueSaturatedInto, + }, + RuntimeAppPublic, Perbill, PerThing, }; use ghost_helpers::{ bounded_bitmap::{validate_bitmap_sizes, BoundedBitmap}, - get_byzantium_quorum_threshold, + get_byzantium_quorum_threshold, get_submission_probability, networks::{NetworkData, NetworkType}, }; use ghost_traits::{ @@ -39,6 +42,7 @@ use ghost_traits::{ }, }; +pub mod migrations; mod errors; mod impls; mod types; @@ -99,7 +103,7 @@ type WeaversBitmap = BoundedBitmap::MaxAuthorities pub mod pallet { use super::*; - const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); + const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] @@ -183,9 +187,10 @@ pub mod pallet { } #[pallet::storage] - #[pallet::getter(fn attested_blocks)] - pub(super) type AttestedBlocks = StorageMap< + #[pallet::getter(fn network_attested_blocks)] + pub(super) type NetworkAttestedBlocks = StorageDoubleMap< _, + Twox64Concat, NetworkIdOf, Twox64Concat, AuthIndex, BlockNumberFor, ValueQuery, @@ -356,7 +361,10 @@ pub mod pallet { })?; let current_block = T::BlockNumberProvider::current_block_number(); - AttestedBlocks::::insert(&authority_index, current_block); + NetworkAttestedBlocks::::insert( + &network_id, &authority_index, + current_block + ); Self::deposit_event(Event::::BlockAttestated { network_id, @@ -410,12 +418,19 @@ pub mod pallet { WeavingStates::::mutate(&network_id, |weaving_state| { 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(); - AttestedBlocks::::insert(&authority_index, current_block); + NetworkAttestedBlocks::::insert( + &network_id, &authority_index, + current_block + ); Self::deposit_event(Event::::HashAttestated { authority_index, @@ -688,6 +703,41 @@ impl Pallet { A: Encode, { let authority_index = attestation.authority_index(); + let network_id = attestation.network_id(); + + if !T::NetworkDataHandler::contains_key(&network_id) { + return Err(InvalidTransaction::BadProof); + } + + let current_attestation_len = + NetworkAttestations::::decode_len(&network_id).unwrap_or(0); + + if authority_index as usize >= current_attestation_len { + return Err(InvalidTransaction::BadSigner.into()); + } + + if DisabledAuthorities::::get().contains(authority_index) { + return Err(InvalidTransaction::BadSigner); + } + + let attestation_block = attestation.block(); + let current_block = T::BlockNumberProvider::current_block_number(); + let lock: BlockNumberFor = 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::::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::::get() .get(authority_index as usize) @@ -699,35 +749,6 @@ impl Pallet { .ok_or(InvalidTransaction::BadSigner) })?; - let attestation_block = attestation.block(); - let current_block = T::BlockNumberProvider::current_block_number(); - let lock: BlockNumberFor = LOCK_BLOCK_EXPIRATION.unique_saturated_into(); - let block_expiration = attestation_block.saturating_add(lock); - - let block_expired = current_block >= block_expiration; - let block_attested = AttestedBlocks::::get(authority_index) - .ge(&attestation_block); - - if block_expired || block_attested { - return Err(InvalidTransaction::BadProof); - } - - let network_id = attestation.network_id(); - let current_attestation_len = NetworkAttestations::::decode_len(&network_id) - .unwrap_or(0); - - if authority_index as usize >= current_attestation_len { - return Err(InvalidTransaction::BadSigner.into()); - } - - if DisabledAuthorities::::get().contains(authority_index) { - return Err(InvalidTransaction::BadSigner); - } - - if !T::NetworkDataHandler::contains_key(&network_id) { - return Err(InvalidTransaction::BadProof); - } - Ok((authority_index, network_id)) } @@ -780,14 +801,31 @@ impl Pallet { return Err(WeavingError::NoEndpointsAvailable(network_id)); } + let check_probability = |values_count: usize, blocks_left: BlockNumberFor| -> bool { + let total_authorities = Authorities::::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 = ::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::::get(&network_id) { Some(tapestry_draft) => Self::try_request_and_submit_hash( network_id, current_block, - tapestry_draft.median, + tapestry_draft, deadline, &network_data, &rpc_endpoints, + check_probability, ), None => Self::try_request_and_submit_block( network_id, @@ -795,6 +833,7 @@ impl Pallet { deadline, &network_data, &rpc_endpoints, + check_probability, ), } } @@ -802,13 +841,22 @@ impl Pallet { fn try_request_and_submit_hash( network_id: NetworkIdOf, current_block: BlockNumberFor, - target_block: ExternalBlockNumber, + tapestry_draft: TapestryDraft>, deadline: rt_offchain::Timestamp, network_data: &NetworkData, rpc_endpoints: &Vec>, + check_probability: impl Fn(usize, BlockNumberFor) -> bool, ) -> WeavingResult>> { let weaving_session = CurrentWeavingSession::::get(&network_id); + let block_until = tapestry_draft.until.saturating_sub(current_block); + let weaving_count = WeavingStates::::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_body = network_data.r#type.get_hash_request_body( @@ -902,7 +950,20 @@ impl Pallet { deadline: rt_offchain::Timestamp, network_data: &NetworkData, rpc_endpoints: &Vec>, + check_probability: impl Fn(usize, BlockNumberFor) -> bool, ) -> WeavingResult>> { + let next_attempt_block = NextConsensusAttemptBlocks::::get(&network_id); + let block_until = next_attempt_block.saturating_sub(current_block); + + let attestations_count = NetworkAttestations::::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 distance_key = Self::create_storage_key(b"distance-", &network_id_encoded); let block_deviation = diff --git a/pallets/weaver/src/migrations/mod.rs b/pallets/weaver/src/migrations/mod.rs new file mode 100644 index 0000000..5cd70fb --- /dev/null +++ b/pallets/weaver/src/migrations/mod.rs @@ -0,0 +1,9 @@ +pub mod v1; + +pub type MigrateV0ToV1 = frame_support::migrations::VersionedMigration< + 0, + 1, + v1::RemoveAttestedBlocksStorage, + crate::Pallet::, + ::DbWeight, +>; diff --git a/pallets/weaver/src/migrations/v1.rs b/pallets/weaver/src/migrations/v1.rs new file mode 100644 index 0000000..dfee020 --- /dev/null +++ b/pallets/weaver/src/migrations/v1.rs @@ -0,0 +1,33 @@ +use frame_support::{ + traits::{Get, UncheckedOnRuntimeUpgrade}, + weights::Weight, +}; + +use core::marker::PhantomData; +use crate::{Config, LOG_TARGET}; + +pub struct RemoveAttestedBlocksStorage(PhantomData); +impl UncheckedOnRuntimeUpgrade for RemoveAttestedBlocksStorage { + 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) + } +} diff --git a/pallets/weaver/src/tests.rs b/pallets/weaver/src/tests.rs index 412f507..57b7386 100644 --- a/pallets/weaver/src/tests.rs +++ b/pallets/weaver/src/tests.rs @@ -1,6 +1,6 @@ #![cfg(test)] -use crate::sr25519::AuthorityPair; +use crate::{LOCK_BLOCK_EXPIRATION, sr25519::AuthorityPair}; use crate::mock::*; use crate::pallet::*; @@ -76,11 +76,11 @@ fn should_successfully_compute_median_and_start_weaving_round() { let networks = vec![(network_id, network_data, Default::default())]; new_test_ext(authorities, networks).execute_with(|| { - GhostWeaver::on_initialize(1); - System::set_block_number(2); + GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 1); + System::set_block_number(LOCK_BLOCK_EXPIRATION + 2); let context = AttestationContext::default() - .with_current_block(1) + .with_current_block(LOCK_BLOCK_EXPIRATION + 1) .with_network_id(network_id); let att1 = context.with_authority_index(0).build_block_attestation(100); @@ -97,12 +97,12 @@ fn should_successfully_compute_median_and_start_weaving_round() { assert!(GhostWeaver::tapestry_drafts(network_id).is_none()); - System::set_block_number(3); - GhostWeaver::on_initialize(3); + System::set_block_number(LOCK_BLOCK_EXPIRATION + 3); + GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 3); let round = GhostWeaver::tapestry_drafts(network_id); assert!(round.is_some()); - assert_eq!(round.unwrap().until, 3 + WeavingDelay::get()); + assert_eq!(round.unwrap().until, LOCK_BLOCK_EXPIRATION + 3 + WeavingDelay::get()); }); } @@ -121,11 +121,11 @@ fn should_apply_cooldown_delay_on_consensus_failure() { let networks = vec![(network_id, network_data, Default::default())]; new_test_ext(authorities, networks).execute_with(|| { - GhostWeaver::on_initialize(9); - System::set_block_number(10); + GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 9); + System::set_block_number(LOCK_BLOCK_EXPIRATION + 10); let context = AttestationContext::default() - .with_current_block(9) + .with_current_block(LOCK_BLOCK_EXPIRATION + 9) .with_network_id(network_id); let att1 = context.with_authority_index(0).build_block_attestation(100); @@ -140,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(), att3, sig3).is_ok()); - System::set_block_number(11); - GhostWeaver::on_initialize(11); + System::set_block_number(LOCK_BLOCK_EXPIRATION + 11); + GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 11); let cooldown_block = GhostWeaver::next_consensus_attempt_blocks(network_id); - assert_eq!(cooldown_block, 14); + assert_eq!(cooldown_block, LOCK_BLOCK_EXPIRATION + 14); TapestryDrafts::::remove(network_id); - System::set_block_number(12); - GhostWeaver::on_initialize(12); + System::set_block_number(LOCK_BLOCK_EXPIRATION + 12); + GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION+ 12); assert!(TapestryDrafts::::get(network_id).is_none()); - System::set_block_number(14); - GhostWeaver::on_initialize(14); + System::set_block_number(LOCK_BLOCK_EXPIRATION + 14); + GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 14); assert!(TapestryDrafts::::get(network_id).is_some()); }); } @@ -170,11 +170,11 @@ fn validate_unsigned_should_prevent_mempool_dos_via_tags() { let networks = vec![(network_id, network_data, Default::default())]; new_test_ext(authorities, networks).execute_with(|| { - GhostWeaver::on_initialize(1); - System::set_block_number(2); + GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 1); + System::set_block_number(LOCK_BLOCK_EXPIRATION + 2); let att = AttestationContext::default() - .with_current_block(1) + .with_current_block(LOCK_BLOCK_EXPIRATION + 1) .with_network_id(network_id) .with_authority_index(0) .build_block_attestation(100); @@ -187,7 +187,9 @@ fn validate_unsigned_should_prevent_mempool_dos_via_tags() { signature: valid_sig, }; - let validity = GhostWeaver::validate_unsigned(TransactionSource::External, &call); + let validity = + GhostWeaver::validate_unsigned(TransactionSource::External, &call); + assert!(validity.is_ok()); let valid_info = validity.unwrap(); @@ -202,7 +204,9 @@ fn validate_unsigned_should_prevent_mempool_dos_via_tags() { 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!( spam_validity, Err(TransactionValidityError::Invalid(InvalidTransaction::BadSigner).into()) @@ -243,8 +247,8 @@ fn offchain_worker_should_fetch_block_and_submit_unsigned_tx() { ext.register_extension(TransactionPoolExt::new(pool)); ext.execute_with(|| { - GhostWeaver::on_initialize(1); - GhostWeaver::on_initialize(2); + GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 1); + GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 2); let network_id_encoded = evm_network_id.encode(); @@ -258,6 +262,7 @@ fn offchain_worker_should_fetch_block_and_submit_unsigned_tx() { let target_endpoint = b"http://localhost:8545".to_vec(); let rpc_endpoints: Vec> = vec![target_endpoint.clone()]; + sp_io::offchain::local_storage_set( sp_core::offchain::StorageKind::PERSISTENT, &endpoint_key, @@ -294,8 +299,8 @@ fn offchain_worker_should_fetch_block_and_submit_unsigned_tx() { ("Accept".into(), "application/json".into()), ("Content-Type".into(), "application/json".into()), ], - body: b"{\"id\":1,\"jsonrpc\":\"2.0\",\"method\":\"getblockcount\"}".to_vec(), - response: Some(b"{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":960127}".to_vec()), + body: b"{\"id\":21,\"jsonrpc\":\"2.0\",\"method\":\"getblockcount\"}".to_vec(), + response: Some(b"{\"jsonrpc\":\"2.0\",\"id\":21,\"result\":960127}".to_vec()), sent: true, ..Default::default() }); @@ -309,16 +314,16 @@ fn offchain_worker_should_fetch_block_and_submit_unsigned_tx() { ("X-Custom-Provider".into(), "GhostWeaverNode".into()), ("Authorization".into(), "Bearer new-api-token-123".into()), ], - body: b"{\"id\":2,\"jsonrpc\":\"2.0\",\"method\":\"eth_blockNumber\"}".to_vec(), - response: Some(b"{\"jsonrpc\":\"2.0\",\"id\":2,\"result\":\"0x1873a84\"}".to_vec()), + body: b"{\"id\":22,\"jsonrpc\":\"2.0\",\"method\":\"eth_blockNumber\"}".to_vec(), + response: Some(b"{\"jsonrpc\":\"2.0\",\"id\":22,\"result\":\"0x1873a84\"}".to_vec()), sent: true, ..Default::default() }); } ext.execute_with(|| { - System::set_block_number(1u64); - GhostWeaver::offchain_worker(1u64); + System::set_block_number(LOCK_BLOCK_EXPIRATION + 1u64); + GhostWeaver::offchain_worker(LOCK_BLOCK_EXPIRATION + 1u64); let state = pool_state.read(); assert_eq!(state.transactions.len(), 1); @@ -339,13 +344,13 @@ fn offchain_worker_should_fetch_block_and_submit_unsigned_tx() { assert_eq!(curr_utxo_block, 960127); assert!(TapestryDrafts::::get(&utxo_network_id).is_none()); - GhostWeaver::on_initialize(3u64); + GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 3u64); assert!(TapestryDrafts::::get(&utxo_network_id).is_some()); }); ext.execute_with(|| { - System::set_block_number(2u64); - GhostWeaver::offchain_worker(2u64); + System::set_block_number(LOCK_BLOCK_EXPIRATION + 2u64); + GhostWeaver::offchain_worker(LOCK_BLOCK_EXPIRATION + 2u64); let state = pool_state.read(); assert_eq!(state.transactions.len(), 2); @@ -366,8 +371,8 @@ fn offchain_worker_should_fetch_block_and_submit_unsigned_tx() { assert_eq!(curr_evm_block, 25639556); assert!(TapestryDrafts::::get(&evm_network_id).is_none()); - GhostWeaver::on_initialize(3u64); - GhostWeaver::on_initialize(4u64); + GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 3u64); + GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 4u64); assert!(TapestryDrafts::::get(&evm_network_id).is_some()); }); } @@ -414,8 +419,8 @@ fn offchain_worker_should_fetch_hash_and_submit_unsigned_tx() { ext.register_extension(TransactionPoolExt::new(pool)); ext.execute_with(|| { - GhostWeaver::on_initialize(1); - GhostWeaver::on_initialize(2); + GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 1); + GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 2); let tapestry_draft = TapestryDraftBuilder::::default(); let evm_tapestry_draft = tapestry_draft.with_median_external(median_evm_block).build(); @@ -475,8 +480,8 @@ fn offchain_worker_should_fetch_hash_and_submit_unsigned_tx() { ("Accept".into(), "application/json".into()), ("Content-Type".into(), "application/json".into()), ], - body: b"{\"id\":1,\"jsonrpc\":\"2.0\",\"method\":\"getblockhash\",\"params\":[960129]}".to_vec(), - response: Some(b"{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":\"0000000000000000000091c72779c83d76dfa8149e950f3a5287377a9708d5c1\"}".to_vec()), + body: b"{\"id\":21,\"jsonrpc\":\"2.0\",\"method\":\"getblockhash\",\"params\":[960129]}".to_vec(), + response: Some(b"{\"jsonrpc\":\"2.0\",\"id\":21,\"result\":\"0000000000000000000091c72779c83d76dfa8149e950f3a5287377a9708d5c1\"}".to_vec()), sent: true, ..Default::default() }); @@ -491,16 +496,16 @@ fn offchain_worker_should_fetch_hash_and_submit_unsigned_tx() { ("Authorization".into(), "Bearer new-api-token-123".into()), ], // 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(), - response: Some(b"{\"jsonrpc\":\"2.0\",\"id\":2,\"result\":\"0xf72b9ef0b66c8cc3952ffd11dc0b85f3b02e6edfa9236da400138f2fcba4668d0000000000000000000000000000000000000000000000000000000000000046\"}".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\":22,\"result\":\"0xf72b9ef0b66c8cc3952ffd11dc0b85f3b02e6edfa9236da400138f2fcba4668d0000000000000000000000000000000000000000000000000000000000000046\"}".to_vec()), sent: true, ..Default::default() }); } ext.execute_with(|| { - System::set_block_number(1u64); - GhostWeaver::offchain_worker(1u64); + System::set_block_number(LOCK_BLOCK_EXPIRATION + 1u64); + GhostWeaver::offchain_worker(LOCK_BLOCK_EXPIRATION + 1u64); let state = pool_state.read(); assert_eq!(state.transactions.len(), 1); @@ -533,8 +538,8 @@ fn offchain_worker_should_fetch_hash_and_submit_unsigned_tx() { }); ext.execute_with(|| { - System::set_block_number(2u64); - GhostWeaver::offchain_worker(2u64); + System::set_block_number(LOCK_BLOCK_EXPIRATION + 2u64); + GhostWeaver::offchain_worker(LOCK_BLOCK_EXPIRATION + 2u64); let state = pool_state.read(); assert_eq!(state.transactions.len(), 2); @@ -557,8 +562,8 @@ fn offchain_worker_should_fetch_hash_and_submit_unsigned_tx() { assert_eq!(curr_weaving_state.hash, block_hash); let prev_state = LoomStates::::get(&evm_network_id, &evm_session); - GhostWeaver::on_initialize(3u64); - GhostWeaver::on_initialize(4u64); + GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 3u64); + GhostWeaver::on_initialize(LOCK_BLOCK_EXPIRATION + 4u64); let curr_state = LoomStates::::get(&evm_network_id, &evm_session); assert_eq!(prev_state.root_hash, H256::default()); @@ -945,7 +950,8 @@ fn on_initialize_should_finalize_session_when_timeout_is_reached() { let networks = vec![(network_id, new_network_data, 0u64)]; 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); @@ -1018,7 +1024,7 @@ fn on_initialize_should_update_at_block_when_threshold_reached_with_same_hash() let networks = vec![(network_id, new_network_data, 0u64)]; new_test_ext(authorities, networks).execute_with(|| { - let current_substrate_block = 10; + let current_substrate_block = LOCK_BLOCK_EXPIRATION + 10; let current_session = CurrentWeavingSession::::get(&network_id); let target_hash = H256::repeat_byte(0xAA); diff --git a/pallets/weaver/src/types.rs b/pallets/weaver/src/types.rs index 29cde6c..ed560b8 100644 --- a/pallets/weaver/src/types.rs +++ b/pallets/weaver/src/types.rs @@ -36,6 +36,7 @@ const PROTOCOL_PREFIX: [u8; 7] = [0x69, 0x64, 0x2b, 0x5b, 0xb6, 0x66, 0xac]; const PROTOCOL_PREFIX: [u8; 7] = [0x47, 0x48, 0x53, 0x54, 0x74, 0x6F, 0x3A]; pub enum WeavingOk { + Skipped(NetworkId), Block(ExternalBlockNumber, u32, NetworkId), Hash(ExternalBlockNumber, H256, WeavingSession, NetworkId), } @@ -46,6 +47,7 @@ where { fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { match self { + Self::Skipped(network_id) => write!(fmt, "Submission step skipped for network: {:?} based on probability distribution.", 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), } diff --git a/runtime/casper/Cargo.toml b/runtime/casper/Cargo.toml index b436f1f..f59e2d6 100644 --- a/runtime/casper/Cargo.toml +++ b/runtime/casper/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "casper-runtime" -version = "4.0.17" +version = "4.0.18" build = "build.rs" description = "Runtime of the Casper Network" edition.workspace = true diff --git a/runtime/casper/src/lib.rs b/runtime/casper/src/lib.rs index d62ad05..3291b00 100644 --- a/runtime/casper/src/lib.rs +++ b/runtime/casper/src/lib.rs @@ -124,7 +124,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("casper"), impl_name: create_runtime_str!("casper-svengali"), authoring_version: 0, - spec_version: 10, + spec_version: 12, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -1035,7 +1035,9 @@ pub type SignedExtra = ( /// All migrations that will run on the next runtime upgrade. /// Should be cleared after release. -pub type Migrations = (); +pub type Migrations = ( + ghost_weaver::migrations::MigrateV0ToV1, +); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic =