Skip to content

Commit

Permalink
Merge remote-tracking branch 'up/master' into HEAD
Browse files Browse the repository at this point in the history
  • Loading branch information
mina86 committed Sep 4, 2024
2 parents 8bd7f67 + d798c44 commit 284c6ff
Show file tree
Hide file tree
Showing 10 changed files with 189 additions and 32 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion common/cf-solana/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ serde = { workspace = true, optional = true }
solana-program = { workspace = true, optional = true }

cf-guest.workspace = true
lib = { workspace = true, features = ["bs58", "serde"] }
lib = { workspace = true, features = ["bs58"] }
proto-utils = { workspace = true, features = ["ibc"] }
stdx.workspace = true
trie-ids.workspace = true
Expand All @@ -43,7 +43,12 @@ solana-program-2 = { package = "solana-program", git = "https://github.com/Compo
lib = { workspace = true, features = ["test_utils"] }

[features]
no-blake3-syscall = []
rayon = ["lib/rayon"]
serde = [
"dep:serde",
"lib/serde",
]
solana-program = [
"dep:solana-program",
"lib/solana-program",
Expand Down
39 changes: 18 additions & 21 deletions common/cf-solana/src/blake3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@ pub use ::blake3::Hasher;

use crate::types::Hash;

const CONSIDER_SOL: bool =
!cfg!(feature = "no-blake3-syscall") && cfg!(target_os = "solana-program");
const HAS_SOL: bool =
cfg!(feature = "solana-program") || cfg!(feature = "solana-program-2");
const USE_SOL: bool = CONSIDER_SOL && HAS_SOL;

/// Calculates Blake3 hash of given byte slice.
///
/// When `solana-program` or `solana-program-2` feature is enabled and
/// building a solana program, this is using Solana’s `sol_blake3` syscall.
/// Otherwise, the calculation is done by `blake3` crate.
#[allow(dead_code)]
pub fn hash(bytes: &[u8]) -> Hash {
if cfg!(target_os = "solana-program") &&
(cfg!(feature = "solana-program") ||
cfg!(feature = "solana-program-2"))
{
if USE_SOL {
hashv(&[bytes])
} else {
Hash(::blake3::hash(bytes).into())
Expand All @@ -24,23 +26,18 @@ pub fn hash(bytes: &[u8]) -> Hash {
/// When `solana` or `solana2` feature is enabled and building a Solana
/// program, this is using Solana’s `sol_blake3` syscall. Otherwise, the
/// calculation is done by `blake3` crate.
#[allow(dead_code)]
#[allow(unreachable_code)]
pub fn hashv(slices: &[&[u8]]) -> Hash {
#[cfg(all(target_os = "solana-program", feature = "solana-program-2"))]
return Hash(solana_program_2::blake3::hashv(slices).0);
#[cfg(all(
target_os = "solana-program",
feature = "solana-program",
not(feature = "solana-program-2")
))]
return Hash(solana_program::blake3::hashv(slices).0);
if USE_SOL {
#[cfg(feature = "solana-program-2")]
return Hash(solana_program_2::blake3::hashv(slices).0);
#[cfg(feature = "solana-program")]
return Hash(solana_program::blake3::hashv(slices).0);
}

#[allow(dead_code)]
{
let mut hasher = Hasher::default();
for bytes in slices {
hasher.update(bytes);
}
hasher.finalize().into()
let mut hasher = Hasher::default();
for bytes in slices {
hasher.update(bytes);
}
hasher.finalize().into()
}
18 changes: 14 additions & 4 deletions common/guestchain/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,13 +198,13 @@ impl<PK: crate::PubKey> ChainManager<PK> {
host_height: crate::HostHeight,
host_timestamp: NonZeroU64,
state_root: CryptoHash,
) -> Result<bool, GenerateError> {
) -> Result<(), GenerateError> {
let next_epoch = self.validate_generate_next(
host_height,
host_timestamp,
&state_root,
)?;
let epoch_ends = self.header.next_epoch_commitment.is_some();
let has_next_epoch = next_epoch.is_some();
let next_block = self.header.generate_next(
host_height,
host_timestamp,
Expand All @@ -227,11 +227,21 @@ impl<PK: crate::PubKey> ChainManager<PK> {
signers: Set::new(),
signing_stake: 0,
});
self.candidates.clear_changed_flag();

Ok(epoch_ends)
if has_next_epoch {
self.candidates.clear_changed_flag();
}

Ok(())
}

/// Verifies whether new block can be generated.
///
/// Like [`generate_next`] returns an error if the new block cannot be
/// generated. If it can, returns an `Ok` value.
///
/// If the new block should contain a next epoch commitment, returns `Some`
/// new epoch. Otherwise returns `None`.
pub fn validate_generate_next(
&self,
host_height: crate::HostHeight,
Expand Down
1 change: 1 addition & 0 deletions common/lib/src/par.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub type Chunks<'a, T> = core::slice::Chunks<'a, T>;
/// # Example
///
/// ```
/// #[allow(unused_imports)]
/// use lib::par::prelude::*;
///
/// let chunks = lib::par::chunks(&[0, 1, 2, 3, 4], 3)
Expand Down
1 change: 1 addition & 0 deletions solana/solana-ibc/programs/solana-ibc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ uint.workspace = true

guestchain.workspace = true
cf-guest.workspace = true
cf-solana = { workspace = true, features = ["solana-program", "no-blake3-syscall"] }
lib = { workspace = true, features = ["solana-program"] }
memory.workspace = true
solana-allocator = { workspace = true, optional = true }
Expand Down
114 changes: 114 additions & 0 deletions solana/solana-ibc/programs/solana-ibc/src/client_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type Result<T = (), E = ibc::ClientError> = core::result::Result<T, E>;
pub enum AnyClientState {
Tendermint(ibc::tm::ClientState),
Wasm(ibc::wasm::ClientState),
Rollup(cf_solana::ClientState),
#[cfg(any(test, feature = "mocks"))]
Mock(ibc::mock::MockClientState),
}
Expand All @@ -26,6 +27,7 @@ impl ibc::Protobuf<ibc::Any> for AnyClientState {}
enum AnyClientStateTag {
Tendermint = 0,
Wasm = 1,
Rollup = 2,
#[cfg(any(test, feature = "mocks"))]
Mock = 255,
}
Expand All @@ -37,6 +39,7 @@ impl AnyClientStateTag {
match url {
AnyClientState::TENDERMINT_TYPE => Some(Self::Tendermint),
AnyClientState::WASM_TYPE => Some(Self::Wasm),
AnyClientState::ROLLUP_TYPE => Some(Self::Rollup),
#[cfg(any(test, feature = "mocks"))]
AnyClientState::MOCK_TYPE => Some(Self::Mock),
_ => None,
Expand All @@ -50,6 +53,9 @@ impl AnyClientState {
ibc::tm::TENDERMINT_CLIENT_STATE_TYPE_URL;
/// Protobuf type URL for WASM client state used in Any message.
const WASM_TYPE: &'static str = ibc::wasm::WASM_CLIENT_STATE_TYPE_URL;
/// Protobuf type URL for Rollup client state used in Any message.
const ROLLUP_TYPE: &'static str =
cf_solana::proto::ClientState::IBC_TYPE_URL;
#[cfg(any(test, feature = "mocks"))]
/// Protobuf type URL for Mock client state used in Any message.
const MOCK_TYPE: &'static str = ibc::mock::MOCK_CLIENT_STATE_TYPE_URL;
Expand Down Expand Up @@ -78,6 +84,11 @@ impl AnyClientState {
Self::WASM_TYPE,
Protobuf::<ibc::wasm::ClientStatePB>::encode_vec(state),
),
Self::Rollup(state) => (
AnyClientStateTag::Rollup,
Self::ROLLUP_TYPE,
Protobuf::<cf_solana::proto::ClientState>::encode_vec(state),
),
#[cfg(any(test, feature = "mocks"))]
Self::Mock(state) => (
AnyClientStateTag::Mock,
Expand All @@ -103,6 +114,11 @@ impl AnyClientState {
.map_err(|err| err.to_string())
.map(Self::Wasm)
}
AnyClientStateTag::Rollup => {
Protobuf::<cf_solana::proto::ClientState>::decode_vec(&value)
.map_err(|err| err.to_string())
.map(Self::Rollup)
}
#[cfg(any(test, feature = "mocks"))]
AnyClientStateTag::Mock => {
Protobuf::<ibc::mock::ClientStatePB>::decode_vec(&value)
Expand Down Expand Up @@ -323,6 +339,104 @@ impl guestchain::Verifier<sigverify::ed25519::PubKey> for IbcStorage<'_, '_> {
}
}

impl cf_solana::CommonContext for IbcStorage<'_, '_> {
type ConversionError = &'static str;
type AnyClientState = AnyClientState;
type AnyConsensusState = AnyConsensusState;

fn host_metadata(&self) -> Result<(ibc::Timestamp, ibc::Height)> {
let timestamp = self.borrow().chain.head()?.timestamp_ns.get();
let timestamp =
ibc::Timestamp::from_nanoseconds(timestamp).map_err(|err| {
ibc::ClientError::Other { description: err.to_string() }
})?;

let height = u64::from(self.borrow().chain.head()?.block_height);
let height = ibc::Height::new(1, height)?;

Ok((timestamp, height))
}

fn set_client_state(
&mut self,
client_id: &ibc::ClientId,
state: Self::AnyClientState,
) -> Result<()> {
self.store_client_state_impl(client_id, state)
}

fn consensus_state(
&self,
client_id: &ibc::ClientId,
height: ibc::Height,
) -> Result<Self::AnyConsensusState> {
self.consensus_state_impl(client_id, height)
}

fn consensus_state_neighbourhood(
&self,
client_id: &ibc::ClientId,
height: ibc::Height,
) -> Result<cf_guest::Neighbourhood<Self::AnyConsensusState>> {
use core::cmp::Ordering;

let height = (height.revision_number(), height.revision_height());
let mut prev = ((0, 0), None);
let mut next = ((u64::MAX, u64::MAX), None);

let storage = self.borrow();
let states = &storage.private.client(client_id)?.consensus_states;
for (key, value) in states.iter() {
let key = (key.revision_number(), key.revision_height());
match key.cmp(&height) {
Ordering::Less if key >= prev.0 => prev = (key, Some(value)),
Ordering::Greater if key <= next.0 => next = (key, Some(value)),
Ordering::Equal => {
return value.state().map(cf_guest::Neighbourhood::This)
}
_ => (),
}
}

let prev = prev.1.map(|state| state.state()).transpose()?;
let next = next.1.map(|state| state.state()).transpose()?;
Ok(cf_guest::Neighbourhood::Neighbours(prev, next))
}

fn store_consensus_state_and_metadata(
&mut self,
client_id: &ibc::ClientId,
height: ibc::Height,
consensus: Self::AnyConsensusState,
_host_timestamp: ibc::Timestamp,
_host_height: ibc::Height,
) -> Result {
self.store_consensus_state_impl(client_id, height, consensus)
}

fn delete_consensus_state_and_metadata(
&mut self,
client_id: &ibc::ClientId,
height: ibc::Height,
) -> Result {
self.delete_consensus_state_impl(client_id, height)
}

/// Returns `None`.
///
/// This method is used by the light client to prune old states. However,
/// we are limiting number of consensus states we’re keeping in
/// store_consensus_state_and_metadata method, which makes it unnecessary
/// for the light client to perform the pruning. Because of that, this
/// method returns `None`.
fn earliest_consensus_state(
&self,
_client_id: &ibc::ClientId,
) -> Result<Option<(ibc::Height, Self::AnyConsensusState)>> {
Ok(None)
}
}

#[cfg(any(test, feature = "mocks"))]
impl ibc::mock::MockClientContext for IbcStorage<'_, '_> {
type ConversionError = &'static str;
Expand Down
12 changes: 12 additions & 0 deletions solana/solana-ibc/programs/solana-ibc/src/client_state/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ macro_rules! delegate {
match self {
AnyClientState::Tendermint(cs) => cs.$name($($arg),*),
AnyClientState::Wasm(_) => unimplemented!(),
AnyClientState::Rollup(cs) => cs.$name($($arg),*),
#[cfg(any(test, feature = "mocks"))]
AnyClientState::Mock(cs) => cs.$name($($arg),*),
}
Expand Down Expand Up @@ -53,6 +54,7 @@ impl ibc::ClientStateCommon for AnyClientState {
)
}
AnyClientState::Wasm(_) => unimplemented!(),
AnyClientState::Rollup(_) => unimplemented!(),
#[cfg(any(test, feature = "mocks"))]
AnyClientState::Mock(cs) => cs.verify_upgrade_client(
upgraded_client_state,
Expand Down Expand Up @@ -84,6 +86,9 @@ impl ibc::ClientStateCommon for AnyClientState {
)
}
AnyClientState::Wasm(_) => unimplemented!(),
AnyClientState::Rollup(cs) => {
cs.verify_membership(prefix, proof, root, path, value)
}
#[cfg(any(test, feature = "mocks"))]
AnyClientState::Mock(cs) => {
cs.verify_membership(prefix, proof, root, path, value)
Expand All @@ -107,6 +112,9 @@ impl ibc::ClientStateCommon for AnyClientState {
)
}
AnyClientState::Wasm(_) => unimplemented!(),
AnyClientState::Rollup(cs) => {
cs.verify_non_membership(prefix, proof, root, path)
}
#[cfg(any(test, feature = "mocks"))]
AnyClientState::Mock(cs) => {
cs.verify_non_membership(prefix, proof, root, path)
Expand All @@ -132,6 +140,9 @@ impl<'a, 'b> ibc::ClientStateValidation<IbcStorage<'a, 'b>> for AnyClientState {
)
}
AnyClientState::Wasm(_) => unimplemented!(),
AnyClientState::Rollup(cs) => {
cs.verify_client_message(ctx, client_id, client_message)
}
#[cfg(any(test, feature = "mocks"))]
AnyClientState::Mock(cs) => {
cs.verify_client_message(ctx, client_id, client_message)
Expand All @@ -155,6 +166,7 @@ impl<'a, 'b> ibc::ClientStateValidation<IbcStorage<'a, 'b>> for AnyClientState {
)
}
AnyClientState::Wasm(_) => unimplemented!(),
AnyClientState::Rollup(_) => unimplemented!(),
#[cfg(any(test, feature = "mocks"))]
AnyClientState::Mock(_) => unimplemented!(),
}
Expand Down
Loading

0 comments on commit 284c6ff

Please sign in to comment.