diff --git a/common/lib/src/hash.rs b/common/lib/src/hash.rs index 269ae12c..ebd7c20b 100644 --- a/common/lib/src/hash.rs +++ b/common/lib/src/hash.rs @@ -43,10 +43,31 @@ impl CryptoHash { /// Returns hash of given bytes. #[inline] - pub fn digest(bytes: &[u8]) -> Self { - let mut builder = Self::builder(); - builder.update(bytes); - builder.build() + pub fn digest(bytes: &[u8]) -> Self { Self::digestv(&[bytes]) } + + /// Returns hash of concatenation of given byte slices. + /// + /// This is morally equivalent to feeding all the slices into the builder + /// one-by-one or concatenating them into a single buffer and hashing it in + /// a single step. + /// + /// Depending on platform this call may be more efficient. Most notably, + /// Solana offers a vectorised syscall for calculating a SHA-2 256 digest + /// and this method will pass the request directly to it. + #[inline] + pub fn digestv(slices: &[&[u8]]) -> Self { + #[cfg(target_os = "solana")] + { + Self(solana_program::hash::hashv(slices).to_bytes()) + } + #[cfg(not(target_os = "solana"))] + { + let mut builder = Self::builder(); + for bytes in slices { + builder.update(bytes); + } + builder.build() + } } /// Decodes a base64 string representation of the hash. diff --git a/solana/solana-ibc/programs/solana-ibc/src/execution_context.rs b/solana/solana-ibc/programs/solana-ibc/src/execution_context.rs index 7b6e55db..5f0debc2 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/execution_context.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/execution_context.rs @@ -43,7 +43,12 @@ impl ClientExecutionContext for IbcStorage<'_, '_> { msg!("store_client_state({}, {:?})", path, state); let mut store = self.borrow_mut(); let mut client = store.private.client_mut(&path.0, true)?; - let hash = client.client_state.set(&state)?.digest(); + let serialised = client.client_state.set(&state)?; + let hash = CryptoHash::digestv(&[ + path.0.as_bytes(), + &[0], + serialised.as_bytes(), + ]); let key = TrieKey::for_client_state(client.index); store.provable.set(&key, &hash).map_err(error) } diff --git a/solana/solana-ibc/programs/solana-ibc/src/storage.rs b/solana/solana-ibc/programs/solana-ibc/src/storage.rs index 6315bbbc..b0041814 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/storage.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/storage.rs @@ -350,6 +350,8 @@ pub(crate) struct Serialised(Vec, core::marker::PhantomData); impl Serialised { pub fn empty() -> Self { Self(Vec::new(), core::marker::PhantomData) } + pub fn as_bytes(&self) -> &[u8] { self.0.as_slice() } + pub fn digest(&self) -> CryptoHash { CryptoHash::digest(self.0.as_slice()) } fn make_err(err: io::Error) -> ibc::ClientError { diff --git a/solana/solana-ibc/programs/solana-ibc/src/storage/trie_key.rs b/solana/solana-ibc/programs/solana-ibc/src/storage/trie_key.rs index 42035c84..cd7bbcb2 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/storage/trie_key.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/storage/trie_key.rs @@ -60,12 +60,17 @@ macro_rules! new_key_impl { impl TrieKey { /// Constructs a new key for a client state path for client with given /// counter. + /// + /// The hash stored under the key is `hash(borsh(client_id.as_str()) || + /// borsh(client_state))`. pub fn for_client_state(client: ids::ClientIdx) -> Self { new_key_impl!(Tag::ClientState, client) } /// Constructs a new key for a consensus state path for client with given /// counter and specified height. + /// + /// The hash stored under the key is `hash(borsh(consensus_state))`. pub fn for_consensus_state( client: ids::ClientIdx, height: ibc::Height, @@ -74,6 +79,8 @@ impl TrieKey { } /// Constructs a new key for a connection end path. + /// + /// The hash stored under the key is `hash(borsh(connection_end))`. pub fn for_connection(connection: ids::ConnectionIdx) -> Self { new_key_impl!(Tag::Connection, connection) }