diff --git a/solana/solana-ibc/programs/solana-ibc/src/client_state.rs b/solana/solana-ibc/programs/solana-ibc/src/client_state.rs index fd43cfca..07f42495 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/client_state.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/client_state.rs @@ -25,8 +25,8 @@ use ibc_proto::ibc::mock::ClientState as RawMockClientState; use ibc_proto::protobuf::Protobuf; use serde::{Deserialize, Serialize}; -use super::consensus_state::AnyConsensusState; -use crate::IbcStorage; +use crate::consensus_state::AnyConsensusState; +use crate::storage::IbcStorage; const TENDERMINT_CLIENT_STATE_TYPE_URL: &str = "/ibc.lightclients.tendermint.v1.ClientState"; @@ -395,8 +395,7 @@ impl ibc::clients::ics07_tendermint::CommonContext for IbcStorage<'_, '_> { ) -> Result, ContextError> { // TODO(mina86): use BTreeMap::range here so that we don’t iterate over // the entire map. - self.0 - .borrow() + self.borrow() .private .consensus_states .keys() @@ -445,8 +444,7 @@ impl ibc::clients::ics07_tendermint::ValidationContext for IbcStorage<'_, '_> { use core::ops::Bound; let height = (height.revision_number(), height.revision_height()); let min = (client_id.to_string(), height); - self.0 - .borrow() + self.borrow() .private .consensus_states .range((Bound::Excluded(min), Bound::Unbounded)) @@ -466,8 +464,7 @@ impl ibc::clients::ics07_tendermint::ValidationContext for IbcStorage<'_, '_> { height: &Height, ) -> Result, ContextError> { let height = (height.revision_number(), height.revision_height()); - self.0 - .borrow() + self.borrow() .private .consensus_states .range(..(client_id.to_string(), height)) 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 89698f3f..12302c0e 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/execution_context.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/execution_context.rs @@ -25,11 +25,9 @@ use ibc::Height; use crate::client_state::AnyClientState; use crate::consensus_state::AnyConsensusState; +use crate::storage::{IbcStorage, InnerChannelId, InnerPortId, InnerSequence}; use crate::trie_key::TrieKey; -use crate::{ - EmitIBCEvent, IbcStorage, IbcStorageInner, InnerChannelId, InnerPortId, - InnerSequence, -}; +use crate::EmitIBCEvent; type Result = core::result::Result; @@ -51,7 +49,7 @@ impl ClientExecutionContext for IbcStorage<'_, '_> { let client_state_key = client_state_path.0.to_string(); let serialized_client_state = serde_json::to_string(&client_state).unwrap(); - let mut store = self.0.borrow_mut(); + let mut store = self.borrow_mut(); let client_state_trie_key = TrieKey::from(&client_state_path); let trie = &mut store.provable; msg!( @@ -82,7 +80,7 @@ impl ClientExecutionContext for IbcStorage<'_, '_> { consensus_state_path.client_id.to_string(), (consensus_state_path.epoch, consensus_state_path.height), ); - let mut store = self.0.borrow_mut(); + let mut store = self.borrow_mut(); let serialized_consensus_state = serde_json::to_string(&consensus_state).unwrap(); @@ -111,7 +109,7 @@ impl ClientExecutionContext for IbcStorage<'_, '_> { ) -> Result<(), ContextError> { msg!("delete_consensus_state({})", path); let key = (path.client_id.to_string(), (path.epoch, path.height)); - let mut store = self.0.borrow_mut(); + let mut store = self.borrow_mut(); store.private.consensus_states.remove(&key); store.provable.del(&TrieKey::from(&path)).unwrap(); Ok(()) @@ -123,8 +121,7 @@ impl ClientExecutionContext for IbcStorage<'_, '_> { client_id: ClientId, height: Height, ) -> Result<(), ContextError> { - self.0 - .borrow_mut() + self.borrow_mut() .private .client_processed_heights .get_mut(client_id.as_str()) @@ -142,8 +139,7 @@ impl ClientExecutionContext for IbcStorage<'_, '_> { client_id: ClientId, height: Height, ) -> Result<(), ContextError> { - self.0 - .borrow_mut() + self.borrow_mut() .private .client_processed_times .get_mut(client_id.as_str()) @@ -163,8 +159,7 @@ impl ClientExecutionContext for IbcStorage<'_, '_> { timestamp: Timestamp, ) -> Result<(), ContextError> { msg!("store_update_time({}, {}, {})", client_id, height, timestamp); - self.0 - .borrow_mut() + self.borrow_mut() .private .client_processed_times .entry(client_id.to_string()) @@ -183,8 +178,7 @@ impl ClientExecutionContext for IbcStorage<'_, '_> { host_height: Height, ) -> Result<(), ContextError> { msg!("store_update_height({}, {}, {})", client_id, height, host_height); - self.0 - .borrow_mut() + self.borrow_mut() .private .client_processed_heights .entry(client_id.to_string()) @@ -199,7 +193,7 @@ impl ClientExecutionContext for IbcStorage<'_, '_> { impl ExecutionContext for IbcStorage<'_, '_> { fn increase_client_counter(&mut self) -> Result { - let mut store = self.0.borrow_mut(); + let mut store = self.borrow_mut(); store.private.client_counter = store.private.client_counter.checked_add(1).unwrap(); msg!( @@ -220,7 +214,7 @@ impl ExecutionContext for IbcStorage<'_, '_> { connection_end ); - let mut store = self.0.borrow_mut(); + let mut store = self.borrow_mut(); let serialized_connection_end = serde_json::to_string(&connection_end).unwrap(); let connection_trie_key = TrieKey::from(connection_path); @@ -250,7 +244,7 @@ impl ExecutionContext for IbcStorage<'_, '_> { client_connection_path, conn_id ); - let mut store = self.0.borrow_mut(); + let mut store = self.borrow_mut(); store .private .client_to_connection @@ -259,7 +253,7 @@ impl ExecutionContext for IbcStorage<'_, '_> { } fn increase_connection_counter(&mut self) -> Result { - let mut store = self.0.borrow_mut(); + let mut store = self.borrow_mut(); store.private.connection_counter = store.private.connection_counter.checked_add(1).unwrap(); msg!( @@ -279,7 +273,7 @@ impl ExecutionContext for IbcStorage<'_, '_> { commitment_path, commitment ); - let mut store = self.0.borrow_mut(); + let mut store = self.borrow_mut(); let commitment_trie_key = TrieKey::from(commitment_path); let trie = &mut store.provable; trie.set( @@ -302,7 +296,7 @@ impl ExecutionContext for IbcStorage<'_, '_> { commitment_path: &CommitmentPath, ) -> Result { msg!("delete_packet_commitment: path: {}", commitment_path); - let mut store = self.0.borrow_mut(); + let mut store = self.borrow_mut(); let sequences = store.private.packet_commitment_sequence_sets.get_mut(&( commitment_path.port_id.clone().to_string(), @@ -328,7 +322,7 @@ impl ExecutionContext for IbcStorage<'_, '_> { receipt_path, receipt ); - let mut store = self.0.borrow_mut(); + let mut store = self.borrow_mut(); let receipt_trie_key = TrieKey::from(receipt_path); let trie = &mut store.provable; trie.set(&receipt_trie_key, &lib::hash::CryptoHash::DEFAULT).unwrap(); @@ -352,7 +346,7 @@ impl ExecutionContext for IbcStorage<'_, '_> { ack_path, ack_commitment ); - let mut store = self.0.borrow_mut(); + let mut store = self.borrow_mut(); let ack_commitment_trie_key = TrieKey::from(ack_path); let trie = &mut store.provable; trie.set( @@ -371,7 +365,7 @@ impl ExecutionContext for IbcStorage<'_, '_> { fn delete_packet_acknowledgement(&mut self, ack_path: &AckPath) -> Result { msg!("delete_packet_acknowledgement: path: {}", ack_path,); - let mut store = self.0.borrow_mut(); + let mut store = self.borrow_mut(); let sequences = store.private.packet_acknowledgement_sequence_sets.get_mut(&( ack_path.port_id.clone().to_string(), @@ -394,7 +388,7 @@ impl ExecutionContext for IbcStorage<'_, '_> { channel_end_path, channel_end ); - let mut store = self.0.borrow_mut(); + let mut store = self.borrow_mut(); store.private.port_channel_id_set.push(( channel_end_path.0.clone().to_string(), channel_end_path.1.clone().to_string(), @@ -422,10 +416,9 @@ impl ExecutionContext for IbcStorage<'_, '_> { seq: Sequence, ) -> Result { msg!("store_next_sequence_send: path: {path}, seq: {seq}"); - let store: &mut IbcStorageInner<'_, '_> = &mut self.0.borrow_mut(); - store.store_next_sequence( + self.borrow_mut().store_next_sequence( path.into(), - super::SequenceTripleIdx::Send, + crate::storage::SequenceTripleIdx::Send, seq, ) } @@ -436,10 +429,9 @@ impl ExecutionContext for IbcStorage<'_, '_> { seq: Sequence, ) -> Result { msg!("store_next_sequence_recv: path: {path}, seq: {seq}"); - let store: &mut IbcStorageInner<'_, '_> = &mut self.0.borrow_mut(); - store.store_next_sequence( + self.borrow_mut().store_next_sequence( path.into(), - super::SequenceTripleIdx::Recv, + crate::storage::SequenceTripleIdx::Recv, seq, ) } @@ -450,16 +442,15 @@ impl ExecutionContext for IbcStorage<'_, '_> { seq: Sequence, ) -> Result { msg!("store_next_sequence_ack: path: {path}, seq: {seq}"); - let store: &mut IbcStorageInner<'_, '_> = &mut self.0.borrow_mut(); - store.store_next_sequence( + self.borrow_mut().store_next_sequence( path.into(), - super::SequenceTripleIdx::Ack, + crate::storage::SequenceTripleIdx::Ack, seq, ) } fn increase_channel_counter(&mut self) -> Result { - let mut store = self.0.borrow_mut(); + let mut store = self.borrow_mut(); store.private.channel_counter += 1; msg!( "channel_counter has increased to: {}", @@ -469,7 +460,7 @@ impl ExecutionContext for IbcStorage<'_, '_> { } fn emit_ibc_event(&mut self, event: IbcEvent) -> Result { - let mut store = self.0.borrow_mut(); + let mut store = self.borrow_mut(); let host_height = ibc::Height::new(store.private.height.0, store.private.height.1) .map_err(ContextError::ClientError) @@ -495,11 +486,11 @@ impl ExecutionContext for IbcStorage<'_, '_> { fn get_client_execution_context(&mut self) -> &mut Self::E { self } } -impl IbcStorageInner<'_, '_> { +impl crate::storage::IbcStorageInner<'_, '_> { fn store_next_sequence( &mut self, path: crate::trie_key::SequencePath<'_>, - index: super::SequenceTripleIdx, + index: crate::storage::SequenceTripleIdx, seq: Sequence, ) -> Result { let trie = &mut self.provable; diff --git a/solana/solana-ibc/programs/solana-ibc/src/lib.rs b/solana/solana-ibc/programs/solana-ibc/src/lib.rs index 1080352f..1e256699 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/lib.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/lib.rs @@ -3,13 +3,8 @@ #![allow(clippy::result_large_err)] extern crate alloc; -use alloc::collections::BTreeMap; -use alloc::rc::Rc; -use core::cell::RefCell; - use anchor_lang::prelude::*; use borsh::{BorshDeserialize, BorshSerialize}; -use ibc::core::ics04_channel::packet::Sequence; use ibc::core::ics24_host::identifier::PortId; use ibc::core::router::{Module, ModuleId, Router}; @@ -23,6 +18,7 @@ declare_id!("EnfDJsAK7BGgetnmKzBx86CsgC5kfSPcsktFCQ4YLC81"); mod client_state; mod consensus_state; mod execution_context; +mod storage; #[cfg(test)] mod tests; mod transfer; @@ -42,7 +38,7 @@ pub mod solana_ibc { msg!("Called deliver method: {message}"); let _sender = ctx.accounts.sender.to_account_info(); - let private: &mut PrivateStorage = &mut ctx.accounts.storage; + let private: &mut storage::PrivateStorage = &mut ctx.accounts.storage; msg!("This is private_store {:?}", private); let account = &ctx.accounts.trie; @@ -50,21 +46,23 @@ pub mod solana_ibc { solana_trie::AccountTrie::new(account.try_borrow_mut_data()?) .ok_or(ProgramError::InvalidAccountData)?; - let inner = IbcStorageInner { private, provable }; - let mut store = IbcStorage(Rc::new(RefCell::new(inner))); - let mut router = store.clone(); - - if let Err(e) = ibc::core::dispatch(&mut store, &mut router, message) { - return err!(Error::RouterError(&e)); + let mut store = storage::IbcStorage::new(storage::IbcStorageInner { + private, + provable, + }); + + { + let mut router = store.clone(); + if let Err(e) = + ibc::core::dispatch(&mut store, &mut router, message) + { + return err!(Error::RouterError(&e)); + } } - // Drop refcount on store so we can unwrap the Rc object below. - core::mem::drop(router); - - // TODO(dhruvja): Since Solana-program uses rustc version 1.69 - // and Rc::into_inner() is supported only from 1.70 and above - // so using the inner function instead. - let inner = Rc::try_unwrap(store.0).unwrap().into_inner(); + // `store` is the only reference to inner storage making refcount == 1 + // which means try_into_inner will succeed. + let inner = store.try_into_inner().unwrap(); msg!("This is final structure {:?}", inner.private); @@ -77,13 +75,13 @@ pub mod solana_ibc { #[derive(Accounts)] pub struct Deliver<'info> { #[account(mut)] - pub sender: Signer<'info>, + sender: Signer<'info>, #[account(init_if_needed, payer = sender, seeds = [SOLANA_IBC_STORAGE_SEED],bump, space = 10000)] - pub storage: Account<'info, PrivateStorage>, + storage: Account<'info, storage::PrivateStorage>, #[account(init_if_needed, payer = sender, seeds = [TRIE_SEED], bump, space = 1000)] /// CHECK: - pub trie: AccountInfo<'info>, - pub system_program: Program<'info, System>, + trie: AccountInfo<'info>, + system_program: Program<'info, System>, } /// Error returned when handling a request. @@ -117,136 +115,7 @@ pub struct EmitIBCEvent { pub ibc_event: Vec, } -pub type InnerHeight = (u64, u64); -pub type HostHeight = InnerHeight; -pub type SolanaTimestamp = u64; -pub type InnerClientId = String; -pub type InnerConnectionId = String; -pub type InnerPortId = String; -pub type InnerChannelId = String; -pub type InnerSequence = u64; -pub type InnerIbcEvent = Vec; -pub type InnerClient = String; // Serialized -pub type InnerConnectionEnd = String; // Serialized -pub type InnerChannelEnd = String; // Serialized -pub type InnerConsensusState = String; // Serialized - -/// A triple of send, receive and acknowledge sequences. -#[derive( - Clone, - Debug, - Default, - PartialEq, - Eq, - borsh::BorshSerialize, - borsh::BorshDeserialize, -)] -pub struct InnerSequenceTriple { - sequences: [u64; 3], - mask: u8, -} - -#[derive(Clone, Copy)] -pub enum SequenceTripleIdx { - Send = 0, - Recv = 1, - Ack = 2, -} - -impl InnerSequenceTriple { - /// Returns sequence at given index or `None` if it wasn’t set yet. - pub fn get(&self, idx: SequenceTripleIdx) -> Option { - if self.mask & (1 << (idx as u32)) == 1 { - Some(Sequence::from(self.sequences[idx as usize])) - } else { - None - } - } - - /// Sets sequence at given index. - pub fn set(&mut self, idx: SequenceTripleIdx, seq: Sequence) { - self.sequences[idx as usize] = u64::from(seq); - self.mask |= 1 << (idx as u32) - } - - /// Encodes the object as a `CryptoHash` so it can be stored in the trie - /// directly. - pub fn to_hash(&self) -> lib::hash::CryptoHash { - let mut hash = lib::hash::CryptoHash::default(); - let (first, tail) = stdx::split_array_mut::<8, 24, 32>(&mut hash.0); - let (second, tail) = stdx::split_array_mut::<8, 16, 24>(tail); - let (third, tail) = stdx::split_array_mut::<8, 8, 16>(tail); - *first = self.sequences[0].to_be_bytes(); - *second = self.sequences[1].to_be_bytes(); - *third = self.sequences[2].to_be_bytes(); - tail[0] = self.mask; - hash - } -} - -#[account] -#[derive(Debug)] -/// All the structs from IBC are stored as String since they dont implement AnchorSerialize and AnchorDeserialize -pub struct PrivateStorage { - pub height: InnerHeight, - pub clients: BTreeMap, - /// The client ids of the clients. - pub client_id_set: Vec, - pub client_counter: u64, - pub client_processed_times: - BTreeMap>, - pub client_processed_heights: - BTreeMap>, - pub consensus_states: - BTreeMap<(InnerClientId, InnerHeight), InnerConsensusState>, - /// This collection contains the heights corresponding to all consensus states of - /// all clients stored in the contract. - pub client_consensus_state_height_sets: - BTreeMap>, - /// The connection ids of the connections. - pub connection_id_set: Vec, - pub connection_counter: u64, - pub connections: BTreeMap, - pub channel_ends: BTreeMap<(InnerPortId, InnerChannelId), InnerChannelEnd>, - // Contains the client id corresponding to the connectionId - pub client_to_connection: BTreeMap, - /// The port and channel id tuples of the channels. - pub port_channel_id_set: Vec<(InnerPortId, InnerChannelId)>, - pub channel_counter: u64, - - /// Next send, receive and ack sequence for given (port, channel). - /// - /// We’re storing all three sequences in a single object to reduce amount of - /// different maps we need to maintain. This saves us on the amount of - /// trie nodes we need to maintain. - pub next_sequence: - BTreeMap<(InnerPortId, InnerChannelId), InnerSequenceTriple>, - - /// The sequence numbers of the packet commitments. - pub packet_commitment_sequence_sets: - BTreeMap<(InnerPortId, InnerChannelId), Vec>, - /// The sequence numbers of the packet receipts. - pub packet_receipt_sequence_sets: - BTreeMap<(InnerPortId, InnerChannelId), Vec>, - /// The sequence numbers of the packet acknowledgements. - pub packet_acknowledgement_sequence_sets: - BTreeMap<(InnerPortId, InnerChannelId), Vec>, - /// The history of IBC events. - pub ibc_events_history: BTreeMap>, -} - -/// All the structs from IBC are stored as String since they dont implement AnchorSerialize and AnchorDeserialize -#[derive(Debug)] -pub struct IbcStorageInner<'a, 'b> { - pub private: &'a mut PrivateStorage, - pub provable: - solana_trie::AccountTrie>, -} - -#[derive(Debug, Clone)] -struct IbcStorage<'a, 'b>(Rc>>); - -impl Router for IbcStorage<'_, '_> { +impl Router for storage::IbcStorage<'_, '_> { // fn get_route(&self, module_id: &ModuleId) -> Option<&dyn Module> { let module_id = core::borrow::Borrow::borrow(module_id); diff --git a/solana/solana-ibc/programs/solana-ibc/src/storage.rs b/solana/solana-ibc/programs/solana-ibc/src/storage.rs new file mode 100644 index 00000000..71fd1ef6 --- /dev/null +++ b/solana/solana-ibc/programs/solana-ibc/src/storage.rs @@ -0,0 +1,180 @@ +use alloc::collections::BTreeMap; +use alloc::rc::Rc; +use core::cell::RefCell; + +use anchor_lang::prelude::*; +use ibc::core::ics04_channel::packet::Sequence; + +pub(crate) type InnerHeight = (u64, u64); +pub(crate) type HostHeight = InnerHeight; +pub(crate) type SolanaTimestamp = u64; +pub(crate) type InnerClientId = String; +pub(crate) type InnerConnectionId = String; +pub(crate) type InnerPortId = String; +pub(crate) type InnerChannelId = String; +pub(crate) type InnerSequence = u64; +pub(crate) type InnerIbcEvent = Vec; +pub(crate) type InnerClient = String; // Serialized +pub(crate) type InnerConnectionEnd = String; // Serialized +pub(crate) type InnerChannelEnd = String; // Serialized +pub(crate) type InnerConsensusState = String; // Serialized + +/// A triple of send, receive and acknowledge sequences. +#[derive( + Clone, + Debug, + Default, + PartialEq, + Eq, + borsh::BorshSerialize, + borsh::BorshDeserialize, +)] +pub(crate) struct InnerSequenceTriple { + sequences: [u64; 3], + mask: u8, +} + +#[derive(Clone, Copy)] +pub(crate) enum SequenceTripleIdx { + Send = 0, + Recv = 1, + Ack = 2, +} + +impl InnerSequenceTriple { + /// Returns sequence at given index or `None` if it wasn’t set yet. + pub(crate) fn get(&self, idx: SequenceTripleIdx) -> Option { + if self.mask & (1 << (idx as u32)) == 1 { + Some(Sequence::from(self.sequences[idx as usize])) + } else { + None + } + } + + /// Sets sequence at given index. + pub(crate) fn set(&mut self, idx: SequenceTripleIdx, seq: Sequence) { + self.sequences[idx as usize] = u64::from(seq); + self.mask |= 1 << (idx as u32) + } + + /// Encodes the object as a `CryptoHash` so it can be stored in the trie + /// directly. + pub(crate) fn to_hash(&self) -> lib::hash::CryptoHash { + let mut hash = lib::hash::CryptoHash::default(); + let (first, tail) = stdx::split_array_mut::<8, 24, 32>(&mut hash.0); + let (second, tail) = stdx::split_array_mut::<8, 16, 24>(tail); + let (third, tail) = stdx::split_array_mut::<8, 8, 16>(tail); + *first = self.sequences[0].to_be_bytes(); + *second = self.sequences[1].to_be_bytes(); + *third = self.sequences[2].to_be_bytes(); + tail[0] = self.mask; + hash + } +} + +#[account] +#[derive(Debug)] +/// All the structs from IBC are stored as String since they dont implement +/// AnchorSerialize and AnchorDeserialize +pub(crate) struct PrivateStorage { + pub height: InnerHeight, + pub clients: BTreeMap, + /// The client ids of the clients. + pub client_id_set: Vec, + pub client_counter: u64, + pub client_processed_times: + BTreeMap>, + pub client_processed_heights: + BTreeMap>, + pub consensus_states: + BTreeMap<(InnerClientId, InnerHeight), InnerConsensusState>, + /// This collection contains the heights corresponding to all consensus states of + /// all clients stored in the contract. + pub client_consensus_state_height_sets: + BTreeMap>, + /// The connection ids of the connections. + pub connection_id_set: Vec, + pub connection_counter: u64, + pub connections: BTreeMap, + pub channel_ends: BTreeMap<(InnerPortId, InnerChannelId), InnerChannelEnd>, + // Contains the client id corresponding to the connectionId + pub client_to_connection: BTreeMap, + /// The port and channel id tuples of the channels. + pub port_channel_id_set: Vec<(InnerPortId, InnerChannelId)>, + pub channel_counter: u64, + + /// Next send, receive and ack sequence for given (port, channel). + /// + /// We’re storing all three sequences in a single object to reduce amount of + /// different maps we need to maintain. This saves us on the amount of + /// trie nodes we need to maintain. + pub next_sequence: + BTreeMap<(InnerPortId, InnerChannelId), InnerSequenceTriple>, + + /// The sequence numbers of the packet commitments. + pub packet_commitment_sequence_sets: + BTreeMap<(InnerPortId, InnerChannelId), Vec>, + /// The sequence numbers of the packet receipts. + pub packet_receipt_sequence_sets: + BTreeMap<(InnerPortId, InnerChannelId), Vec>, + /// The sequence numbers of the packet acknowledgements. + pub packet_acknowledgement_sequence_sets: + BTreeMap<(InnerPortId, InnerChannelId), Vec>, + /// The history of IBC events. + pub ibc_events_history: BTreeMap>, +} + +/// All the structs from IBC are stored as String since they dont implement +/// AnchorSerialize and AnchorDeserialize +#[derive(Debug)] +pub(crate) struct IbcStorageInner<'a, 'b> { + pub private: &'a mut PrivateStorage, + pub provable: + solana_trie::AccountTrie>, +} + +/// A reference-counted reference to the IBC storage. +/// +/// Uses inner-mutability via [`RefCell`] to allow modifications to the storage. +/// Accessing the data must follow aliasing rules as enforced by `RefCell`. +/// Violations will cause a panic. +#[derive(Debug, Clone)] +pub(crate) struct IbcStorage<'a, 'b>(Rc>>); + +impl<'a, 'b> IbcStorage<'a, 'b> { + /// Constructs a new object with given inner storage. + pub fn new(inner: IbcStorageInner<'a, 'b>) -> Self { + Self(Rc::new(RefCell::new(inner))) + } + + /// Consumes the object returning the inner storage if it was the last + /// reference to it. + /// + /// This is mostly a wrapper around [`Rc::try_unwrap`]. Returns `None` if + /// there are other references to the inner storage object. + pub fn try_into_inner(self) -> Option> { + Rc::try_unwrap(self.0).ok().map(RefCell::into_inner) + } + + /// Immutably borrows the storage. + /// + /// # Panics + /// + /// Panics if the value is currently mutably borrowed. + pub fn borrow<'c>( + &'c self, + ) -> core::cell::Ref<'c, IbcStorageInner<'a, 'b>> { + self.0.borrow() + } + + /// Mutably borrows the storage. + /// + /// # Panics + /// + /// Panics if the value is currently mutably borrowed. + pub fn borrow_mut<'c>( + &'c self, + ) -> core::cell::RefMut<'c, IbcStorageInner<'a, 'b>> { + self.0.borrow_mut() + } +} diff --git a/solana/solana-ibc/programs/solana-ibc/src/tests.rs b/solana/solana-ibc/programs/solana-ibc/src/tests.rs index b674c48d..227e40d3 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/tests.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/tests.rs @@ -22,10 +22,8 @@ use ibc::mock::consensus_state::MockConsensusState; use ibc::mock::header::MockHeader; use ibc_proto::google::protobuf::Any; -use crate::{ - accounts, instruction, PrivateStorage, ID, SOLANA_IBC_STORAGE_SEED, - TRIE_SEED, -}; +use crate::storage::PrivateStorage; +use crate::{accounts, instruction, ID, SOLANA_IBC_STORAGE_SEED, TRIE_SEED}; const IBC_TRIE_PREFIX: &[u8] = b"ibc/"; diff --git a/solana/solana-ibc/programs/solana-ibc/src/transfer/impls.rs b/solana/solana-ibc/programs/solana-ibc/src/transfer/impls.rs index 4c4e13ab..7b11a453 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/transfer/impls.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/transfer/impls.rs @@ -8,7 +8,7 @@ use ibc::core::ics24_host::identifier::{ChannelId, PortId}; use ibc::Signer; // use crate::module_holder::IbcStorage<'_,'_>; -use crate::IbcStorage; +use crate::storage::IbcStorage; impl TokenTransferExecutionContext for IbcStorage<'_, '_> { fn send_coins_execute( diff --git a/solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs b/solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs index 0306825e..f26f5651 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs @@ -13,7 +13,7 @@ use ibc::Signer; use ibc_proto::ibc::apps::transfer::v2::FungibleTokenPacketData; use serde::{Deserialize, Serialize}; -use crate::IbcStorage; +use crate::storage::IbcStorage; mod impls; diff --git a/solana/solana-ibc/programs/solana-ibc/src/validation_context.rs b/solana/solana-ibc/programs/solana-ibc/src/validation_context.rs index 6e2425c6..62e0df26 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/validation_context.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/validation_context.rs @@ -23,7 +23,7 @@ use ibc::Height; use crate::client_state::AnyClientState; use crate::consensus_state::AnyConsensusState; -use crate::IbcStorage; +use crate::storage::IbcStorage; impl ValidationContext for IbcStorage<'_, '_> { type V = Self; // ClientValidationContext @@ -35,7 +35,7 @@ impl ValidationContext for IbcStorage<'_, '_> { &self, client_id: &ClientId, ) -> std::result::Result { - let store = self.0.borrow(); + let store = self.borrow(); match store.private.clients.get(client_id.as_str()) { Some(data) => { let client: AnyClientState = @@ -65,7 +65,7 @@ impl ValidationContext for IbcStorage<'_, '_> { client_cons_state_path.client_id.to_string(), (client_cons_state_path.epoch, client_cons_state_path.height), ); - let store = self.0.borrow(); + let store = self.borrow(); match store.private.consensus_states.get(consensus_state_key) { Some(data) => { let result: Self::AnyConsensusState = @@ -85,7 +85,7 @@ impl ValidationContext for IbcStorage<'_, '_> { } fn host_height(&self) -> std::result::Result { - let store = self.0.borrow(); + let store = self.borrow(); ibc::Height::new(store.private.height.0, store.private.height.1) .map_err(ContextError::ClientError) } @@ -108,7 +108,7 @@ impl ValidationContext for IbcStorage<'_, '_> { } fn client_counter(&self) -> std::result::Result { - let store = self.0.borrow(); + let store = self.borrow(); Ok(store.private.client_counter) } @@ -116,7 +116,7 @@ impl ValidationContext for IbcStorage<'_, '_> { &self, conn_id: &ConnectionId, ) -> std::result::Result { - let store = self.0.borrow(); + let store = self.borrow(); match store.private.connections.get(conn_id.as_str()) { Some(data) => { let connection: ConnectionEnd = @@ -149,7 +149,7 @@ impl ValidationContext for IbcStorage<'_, '_> { } fn connection_counter(&self) -> std::result::Result { - let store = self.0.borrow(); + let store = self.borrow(); Ok(store.private.connection_counter) } @@ -159,7 +159,7 @@ impl ValidationContext for IbcStorage<'_, '_> { ) -> std::result::Result { let channel_end_key = &(channel_end_path.0.to_string(), channel_end_path.1.to_string()); - let store = self.0.borrow(); + let store = self.borrow(); match store.private.channel_ends.get(channel_end_key) { Some(data) => { let channel_end: ChannelEnd = @@ -179,39 +179,48 @@ impl ValidationContext for IbcStorage<'_, '_> { &self, path: &SeqSendPath, ) -> std::result::Result { - self.get_next_sequence(path.into(), super::SequenceTripleIdx::Send) - .map_err(|(port_id, channel_id)| { - ContextError::PacketError(PacketError::MissingNextSendSeq { - port_id, - channel_id, - }) + self.get_next_sequence( + path.into(), + crate::storage::SequenceTripleIdx::Send, + ) + .map_err(|(port_id, channel_id)| { + ContextError::PacketError(PacketError::MissingNextSendSeq { + port_id, + channel_id, }) + }) } fn get_next_sequence_recv( &self, path: &SeqRecvPath, ) -> std::result::Result { - self.get_next_sequence(path.into(), super::SequenceTripleIdx::Recv) - .map_err(|(port_id, channel_id)| { - ContextError::PacketError(PacketError::MissingNextRecvSeq { - port_id, - channel_id, - }) + self.get_next_sequence( + path.into(), + crate::storage::SequenceTripleIdx::Recv, + ) + .map_err(|(port_id, channel_id)| { + ContextError::PacketError(PacketError::MissingNextRecvSeq { + port_id, + channel_id, }) + }) } fn get_next_sequence_ack( &self, path: &SeqAckPath, ) -> std::result::Result { - self.get_next_sequence(path.into(), super::SequenceTripleIdx::Ack) - .map_err(|(port_id, channel_id)| { - ContextError::PacketError(PacketError::MissingNextAckSeq { - port_id, - channel_id, - }) + self.get_next_sequence( + path.into(), + crate::storage::SequenceTripleIdx::Ack, + ) + .map_err(|(port_id, channel_id)| { + ContextError::PacketError(PacketError::MissingNextAckSeq { + port_id, + channel_id, }) + }) } fn get_packet_commitment( @@ -222,7 +231,7 @@ impl ValidationContext for IbcStorage<'_, '_> { commitment_path.port_id.to_string(), commitment_path.channel_id.to_string(), ); - let store = self.0.borrow(); + let store = self.borrow(); match store .private .packet_acknowledgement_sequence_sets @@ -249,7 +258,7 @@ impl ValidationContext for IbcStorage<'_, '_> { receipt_path.port_id.to_string(), receipt_path.channel_id.to_string(), ); - let store = self.0.borrow(); + let store = self.borrow(); match store .private .packet_acknowledgement_sequence_sets @@ -279,7 +288,7 @@ impl ValidationContext for IbcStorage<'_, '_> { ) -> std::result::Result { let ack_key = (ack_path.port_id.to_string(), ack_path.channel_id.to_string()); - let store = self.0.borrow(); + let store = self.borrow(); match store.private.packet_acknowledgement_sequence_sets.get(&ack_key) { Some(data) => { let data_in_u8: Vec = @@ -295,7 +304,7 @@ impl ValidationContext for IbcStorage<'_, '_> { } fn channel_counter(&self) -> std::result::Result { - let store = self.0.borrow(); + let store = self.borrow(); Ok(store.private.channel_counter) } @@ -352,7 +361,7 @@ impl ibc::core::ics02_client::ClientValidationContext for IbcStorage<'_, '_> { client_id: &ClientId, height: &Height, ) -> std::result::Result { - let store = self.0.borrow(); + let store = self.borrow(); store .private .client_processed_times @@ -378,7 +387,7 @@ impl ibc::core::ics02_client::ClientValidationContext for IbcStorage<'_, '_> { client_id: &ClientId, height: &Height, ) -> std::result::Result { - let store = self.0.borrow(); + let store = self.borrow(); store .private .client_processed_heights @@ -406,7 +415,7 @@ impl IbcStorage<'_, '_> { fn get_next_sequence( &self, path: crate::trie_key::SequencePath<'_>, - index: super::SequenceTripleIdx, + index: crate::storage::SequenceTripleIdx, ) -> core::result::Result< Sequence, ( @@ -414,7 +423,7 @@ impl IbcStorage<'_, '_> { ibc::core::ics24_host::identifier::ChannelId, ), > { - let store = self.0.borrow(); + let store = self.borrow(); store .private .next_sequence