secure unsigned validation with help of sliding window

Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
This commit is contained in:
Uncle Stinky 2026-10-21 18:46:47 +03:00
parent f72387c9cb
commit ba575d0ab8
Signed by: st1nky
GPG Key ID: 016064BD97603B40
4 changed files with 46 additions and 23 deletions

View File

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

@ -56,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
@ -104,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

View File

@ -22,7 +22,7 @@ use sp_runtime::{
storage::StorageValueRef, storage::StorageValueRef,
storage_lock::{StorageLock, Time}, storage_lock::{StorageLock, Time},
}, },
traits::{BlockNumberProvider, UniqueSaturatedInto}, traits::{BlockNumberProvider, Saturating, UniqueSaturatedInto},
RuntimeAppPublic, RuntimeAppPublic,
}; };
@ -182,6 +182,15 @@ pub mod pallet {
CouldNotRegisterIncoming, CouldNotRegisterIncoming,
} }
#[pallet::storage]
#[pallet::getter(fn attested_blocks)]
pub(super) type AttestedBlocks<T: Config> = StorageMap<
_,
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> =
@ -341,6 +350,9 @@ pub mod pallet {
Ok(()) Ok(())
})?; })?;
let current_block = T::BlockNumberProvider::current_block_number();
AttestedBlocks::<T>::insert(&authority_index, current_block);
Self::deposit_event(Event::<T>::BlockAttestated { Self::deposit_event(Event::<T>::BlockAttestated {
network_id, network_id,
authority_index, authority_index,
@ -397,6 +409,9 @@ pub mod pallet {
} }
}); });
let current_block = T::BlockNumberProvider::current_block_number();
AttestedBlocks::<T>::insert(&authority_index, current_block);
Self::deposit_event(Event::<T>::HashAttestated { Self::deposit_event(Event::<T>::HashAttestated {
authority_index, authority_index,
next_session, next_session,
@ -666,23 +681,6 @@ 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 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) {
return Err(InvalidTransaction::BadSigner);
}
if T::BlockNumberProvider::current_block_number() <= attestation_block {
return Err(InvalidTransaction::BadProof);
}
Authorities::<T>::get() Authorities::<T>::get()
.get(authority_index as usize) .get(authority_index as usize)
@ -694,6 +692,31 @@ impl<T: Config> Pallet<T> {
.ok_or(InvalidTransaction::BadSigner) .ok_or(InvalidTransaction::BadSigner)
})?; })?;
let attestation_block = attestation.block();
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);
let block_expired = current_block >= block_expiration;
let block_attested = AttestedBlocks::<T>::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::<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) {
return Err(InvalidTransaction::BadSigner);
}
if !T::NetworkDataHandler::contains_key(&network_id) { if !T::NetworkDataHandler::contains_key(&network_id) {
return Err(InvalidTransaction::BadProof); return Err(InvalidTransaction::BadProof);
} }

View File

@ -319,7 +319,6 @@ fn offchain_worker_should_fetch_block_and_submit_unsigned_tx() {
ext.execute_with(|| { ext.execute_with(|| {
System::set_block_number(1u64); System::set_block_number(1u64);
GhostWeaver::offchain_worker(1u64); GhostWeaver::offchain_worker(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);
@ -347,7 +346,6 @@ fn offchain_worker_should_fetch_block_and_submit_unsigned_tx() {
ext.execute_with(|| { ext.execute_with(|| {
System::set_block_number(2u64); System::set_block_number(2u64);
GhostWeaver::offchain_worker(2u64); GhostWeaver::offchain_worker(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);
@ -368,6 +366,7 @@ 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(3u64);
GhostWeaver::on_initialize(4u64); GhostWeaver::on_initialize(4u64);
assert!(TapestryDrafts::<TestRuntime>::get(&evm_network_id).is_some()); assert!(TapestryDrafts::<TestRuntime>::get(&evm_network_id).is_some());
}); });
@ -499,7 +498,6 @@ fn offchain_worker_should_fetch_hash_and_submit_unsigned_tx() {
ext.execute_with(|| { ext.execute_with(|| {
System::set_block_number(1u64); System::set_block_number(1u64);
GhostWeaver::offchain_worker(1u64); GhostWeaver::offchain_worker(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);
@ -532,7 +530,6 @@ fn offchain_worker_should_fetch_hash_and_submit_unsigned_tx() {
ext.execute_with(|| { ext.execute_with(|| {
System::set_block_number(2u64); System::set_block_number(2u64);
GhostWeaver::offchain_worker(2u64); GhostWeaver::offchain_worker(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);
@ -555,6 +552,7 @@ fn offchain_worker_should_fetch_hash_and_submit_unsigned_tx() {
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(3u64);
GhostWeaver::on_initialize(4u64); GhostWeaver::on_initialize(4u64);
let curr_state = LoomStates::<TestRuntime>::get(&evm_network_id, &evm_session); let curr_state = LoomStates::<TestRuntime>::get(&evm_network_id, &evm_session);