Compare commits

..

4 Commits

Author SHA1 Message Date
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
0162da4604
update genesis file hash example
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-09-26 11:28:53 +03:00
4 changed files with 51 additions and 15 deletions

View File

@ -346,7 +346,7 @@ sha256sum /etc/ghost/casper.json
You should see: You should see:
``` ```
4645c7d39a64424d823f659d408070f8c90426117e7af147b0360697b2db7f4f /etc/ghost/casper.json 74f5a6df5e33dac94a8852ddfb73e472283bba0b30e260ca581919eb50c734db /etc/ghost/casper.json
``` ```
Create running `ghost-node` service that starts on system boot using `--unit-file` flag. Create running `ghost-node` service that starts on system boot using `--unit-file` flag.

View File

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

@ -616,6 +616,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 +638,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);
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,

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};
@ -350,8 +349,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 +363,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>(