use ibc::apps::nft_transfer::types::{PrefixedClassId, TokenId};
use ibc::clients::tendermint::consensus_state::ConsensusState as TmConsensusState;
use ibc::clients::tendermint::types::ConsensusState as TmConsensusStateType;
use ibc::core::channel::types::channel::ChannelEnd;
use ibc::core::channel::types::commitment::{
AcknowledgementCommitment, PacketCommitment,
};
use ibc::core::channel::types::packet::Receipt;
use ibc::core::client::types::Height;
use ibc::core::connection::types::ConnectionEnd;
use ibc::core::host::types::error::HostError;
use ibc::core::host::types::identifiers::{
ChannelId, ClientId, ConnectionId, PortId, Sequence,
};
use ibc::primitives::proto::{Any, Protobuf};
use ibc::primitives::{IntoHostTime, IntoTimestamp, Timestamp};
use namada_core::address::Address;
use namada_core::chain::BlockHeight;
use namada_core::storage::Key;
use namada_core::tendermint::Time as TmTime;
use namada_core::token::Amount;
use namada_state::{Error as StorageError, StorageRead, StorageWrite};
use namada_systems::trans_token;
use prost::Message;
use super::client::{AnyClientState, AnyConsensusState};
use super::storage::IbcStorageContext;
use crate::{NftClass, NftMetadata, storage, trace};
pub type Result<T> = std::result::Result<T, HostError>;
pub trait IbcCommonContext: IbcStorageContext {
fn client_state(&self, client_id: &ClientId) -> Result<AnyClientState> {
let key = storage::client_state_key(client_id);
match self.storage().read_bytes(&key)? {
Some(value) => Any::decode(&value[..])
.map_err(|e| HostError::FailedToRetrieve {
description: format!(
"Decoding client state failed: ID {client_id}, error \
{e}"
),
})?
.try_into(),
None => Err(HostError::FailedToRetrieve {
description: format!("No client state: ID {client_id}"),
}),
}
}
fn store_client_state(
&mut self,
client_id: &ClientId,
client_state: AnyClientState,
) -> Result<()> {
let key = storage::client_state_key(client_id);
let bytes = Any::from(client_state).encode_to_vec();
self.storage_mut()
.write_bytes(&key, bytes)
.map_err(HostError::from)
}
fn consensus_state(
&self,
client_id: &ClientId,
height: Height,
) -> Result<AnyConsensusState> {
let key = storage::consensus_state_key(client_id, height);
match self.storage().read_bytes(&key)? {
Some(value) => Any::decode(&value[..])
.map_err(|e| HostError::FailedToRetrieve {
description: format!(
"Decoding consensus state failed: ID {client_id}, \
height {height} error {e}"
),
})?
.try_into(),
None => Err(HostError::FailedToRetrieve {
description: format!(
"No consensus state: ID {client_id}, height {height}"
),
}),
}
}
fn store_consensus_state(
&mut self,
client_id: &ClientId,
height: Height,
consensus_state: AnyConsensusState,
) -> Result<()> {
let key = storage::consensus_state_key(client_id, height);
let bytes = Any::from(consensus_state).encode_to_vec();
self.storage_mut()
.write_bytes(&key, bytes)
.map_err(HostError::from)
}
fn delete_consensus_state(
&mut self,
client_id: &ClientId,
height: Height,
) -> Result<()> {
let key = storage::consensus_state_key(client_id, height);
self.storage_mut().delete(&key).map_err(HostError::from)
}
fn decode_consensus_state_value(
&self,
consensus_state: Vec<u8>,
) -> Result<AnyConsensusState> {
Any::decode(&consensus_state[..])
.map_err(|e| HostError::Other {
description: e.to_string(),
})?
.try_into()
}
fn consensus_state_heights(
&self,
client_id: &ClientId,
) -> Result<Vec<Height>> {
let prefix = storage::consensus_state_prefix(client_id);
let mut iter = self.storage().iter_prefix(&prefix)?;
let mut heights = Vec::new();
while let Some((key, _)) = self.storage().iter_next(&mut iter)? {
let key = Key::parse(key).expect("the key should be parsable");
let height = storage::consensus_height(&key).map_err(|e| {
HostError::FailedToRetrieve {
description: format!("Invalid key: key {key}, error {e}"),
}
})?;
heights.push(height);
}
Ok(heights)
}
fn next_consensus_state(
&self,
client_id: &ClientId,
height: &Height,
) -> Result<Option<AnyConsensusState>> {
let prefix = storage::consensus_state_prefix(client_id);
let mut iter = self.storage().iter_prefix(&prefix)?;
let mut lowest_height_value = None;
while let Some((key, value)) = self.storage().iter_next(&mut iter)? {
let key = Key::parse(key).expect("the key should be parsable");
let consensus_height = storage::consensus_height(&key)
.expect("the key should have a height");
if consensus_height > *height {
lowest_height_value = match lowest_height_value {
Some((lowest, _)) if consensus_height < lowest => {
Some((consensus_height, value))
}
Some(_) => continue,
None => Some((consensus_height, value)),
};
}
}
lowest_height_value
.map(|(_, value)| value.try_into())
.transpose()
}
fn prev_consensus_state(
&self,
client_id: &ClientId,
height: &Height,
) -> Result<Option<AnyConsensusState>> {
let prefix = storage::consensus_state_prefix(client_id);
let mut iter = self.storage().iter_prefix(&prefix)?;
let mut highest_height_value = None;
while let Some((key, value)) = self.storage().iter_next(&mut iter)? {
let key = Key::parse(key).expect("the key should be parsable");
let consensus_height = storage::consensus_height(&key)
.expect("the key should have the height");
if consensus_height < *height {
highest_height_value = match highest_height_value {
Some((highest, _)) if consensus_height > highest => {
Some((consensus_height, value))
}
Some(_) => continue,
None => Some((consensus_height, value)),
};
}
}
highest_height_value
.map(|(_, value)| value.try_into())
.transpose()
}
fn client_update_meta(
&self,
client_id: &ClientId,
) -> Result<(Timestamp, Height)> {
let key = storage::client_update_timestamp_key(client_id);
let value = self.storage().read_bytes(&key)?.ok_or(
HostError::FailedToRetrieve {
description: format!(
"The client update time doesn't exist: ID {client_id}",
),
},
)?;
let time = TmTime::decode_vec(&value)
.map_err(|_| HostError::Other {
description: format!(
"Decoding the client update time failed: ID {client_id}",
),
})?
.into_timestamp()
.map_err(|_| HostError::Other {
description: format!(
"Conversion of the client update time failed: ID \
{client_id}",
),
})?;
let key = storage::client_update_height_key(client_id);
let value = self.storage().read_bytes(&key)?.ok_or({
HostError::FailedToRetrieve {
description: format!(
"The client update height doesn't exist: ID {client_id}",
),
}
})?;
let height = Height::decode_vec(&value).map_err(|_| {
HostError::FailedToRetrieve {
description: format!(
"Decoding the client update height failed: ID {client_id}",
),
}
})?;
Ok((time, height))
}
fn store_update_meta(
&mut self,
client_id: &ClientId,
host_timestamp: Timestamp,
host_height: Height,
) -> Result<()> {
let key = storage::client_update_timestamp_key(client_id);
let time: TmTime = host_timestamp.into_host_time().map_err(|e| {
HostError::FailedToStore {
description: format!(
"Converting to tendermint Time failed: {e}"
),
}
})?;
self.storage_mut()
.write_bytes(&key, time.encode_vec())
.map_err(HostError::from)?;
let key = storage::client_update_height_key(client_id);
let bytes = host_height.encode_vec();
self.storage_mut()
.write_bytes(&key, bytes)
.map_err(HostError::from)
}
fn delete_update_meta(&mut self, client_id: &ClientId) -> Result<()> {
let key = storage::client_update_timestamp_key(client_id);
self.storage_mut().delete(&key).map_err(HostError::from)?;
let key = storage::client_update_height_key(client_id);
self.storage_mut().delete(&key).map_err(HostError::from)
}
fn host_timestamp(&self) -> Result<Timestamp> {
let height = self.storage().get_block_height()?;
let header = self
.storage()
.get_block_header(height)?
.or({
if height > BlockHeight::first() {
self.storage()
.get_block_header(height.prev_height().unwrap())?
} else {
None
}
})
.ok_or_else(|| HostError::FailedToRetrieve {
description: "No host block header".to_string(),
})?;
let time = TmTime::try_from(header.time)
.map_err(|_| HostError::FailedToRetrieve {
description: "Converting to Tendermint time failed".to_string(),
})?
.into_timestamp()
.map_err(|_| HostError::FailedToRetrieve {
description: "Converting to timestamp failed".to_string(),
})?;
Ok(time)
}
fn host_consensus_state(
&self,
height: &Height,
) -> Result<AnyConsensusState> {
let height = BlockHeight(height.revision_height());
let header =
self.storage().get_block_header(height)?.ok_or_else(|| {
HostError::FailedToRetrieve {
description: "No host header".to_string(),
}
})?;
let commitment_root = header.hash.to_vec().into();
let time = header
.time
.try_into()
.expect("The time should be converted");
let next_validators_hash = header.next_validators_hash.into();
let consensus_state: TmConsensusState = TmConsensusStateType::new(
commitment_root,
time,
next_validators_hash,
)
.into();
Ok(consensus_state.into())
}
fn connection_end(&self, conn_id: &ConnectionId) -> Result<ConnectionEnd> {
let key = storage::connection_key(conn_id);
let value = self.storage().read_bytes(&key)?.ok_or(
HostError::FailedToRetrieve {
description: format!("No connection end: ID {conn_id}"),
},
)?;
ConnectionEnd::decode_vec(&value).map_err(|_| {
HostError::FailedToRetrieve {
description: format!(
"Decoding the connection end failed: ID {conn_id}",
),
}
})
}
fn store_connection(
&mut self,
connection_id: &ConnectionId,
connection_end: ConnectionEnd,
) -> Result<()> {
let key = storage::connection_key(connection_id);
let bytes = connection_end.encode_vec();
self.storage_mut()
.write_bytes(&key, bytes)
.map_err(HostError::from)
}
fn append_connection(
&mut self,
client_id: &ClientId,
conn_id: ConnectionId,
) -> Result<()> {
let key = storage::client_connections_key(client_id);
let list = match self.storage().read::<String>(&key)? {
Some(list) => format!("{list},{conn_id}"),
None => conn_id.to_string(),
};
self.storage_mut()
.write(&key, list)
.map_err(HostError::from)
}
fn channel_end(
&self,
port_id: &PortId,
channel_id: &ChannelId,
) -> Result<ChannelEnd> {
let key = storage::channel_key(port_id, channel_id);
let value = self.storage().read_bytes(&key)?.ok_or(
HostError::FailedToRetrieve {
description: format!(
"No channel end: port {port_id}, channel {channel_id}"
),
},
)?;
ChannelEnd::decode_vec(&value).map_err(|_| {
HostError::FailedToRetrieve {
description: format!(
"Decoding the channel end failed: port {port_id}, channel \
{channel_id}",
),
}
})
}
fn store_channel(
&mut self,
port_id: &PortId,
channel_id: &ChannelId,
channel_end: ChannelEnd,
) -> Result<()> {
let key = storage::channel_key(port_id, channel_id);
let bytes = channel_end.encode_vec();
self.storage_mut()
.write_bytes(&key, bytes)
.map_err(HostError::from)
}
fn get_next_sequence_send(
&self,
port_id: &PortId,
channel_id: &ChannelId,
) -> Result<Sequence> {
let key = storage::next_sequence_send_key(port_id, channel_id);
read_sequence(self.storage(), &key).map_err(HostError::from)
}
fn store_next_sequence_send(
&mut self,
port_id: &PortId,
channel_id: &ChannelId,
seq: Sequence,
) -> Result<()> {
let key = storage::next_sequence_send_key(port_id, channel_id);
self.store_sequence(&key, seq)
}
fn get_next_sequence_recv(
&self,
port_id: &PortId,
channel_id: &ChannelId,
) -> Result<Sequence> {
let key = storage::next_sequence_recv_key(port_id, channel_id);
read_sequence(self.storage(), &key).map_err(HostError::from)
}
fn store_next_sequence_recv(
&mut self,
port_id: &PortId,
channel_id: &ChannelId,
seq: Sequence,
) -> Result<()> {
let key = storage::next_sequence_recv_key(port_id, channel_id);
self.store_sequence(&key, seq)
}
fn get_next_sequence_ack(
&self,
port_id: &PortId,
channel_id: &ChannelId,
) -> Result<Sequence> {
let key = storage::next_sequence_ack_key(port_id, channel_id);
read_sequence(self.storage(), &key).map_err(HostError::from)
}
fn store_next_sequence_ack(
&mut self,
port_id: &PortId,
channel_id: &ChannelId,
seq: Sequence,
) -> Result<()> {
let key = storage::next_sequence_ack_key(port_id, channel_id);
self.store_sequence(&key, seq)
}
fn store_sequence(&mut self, key: &Key, sequence: Sequence) -> Result<()> {
let bytes = u64::from(sequence).to_be_bytes().to_vec();
self.storage_mut()
.write_bytes(key, bytes)
.map_err(HostError::from)
}
fn packet_commitment(
&self,
port_id: &PortId,
channel_id: &ChannelId,
sequence: Sequence,
) -> Result<PacketCommitment> {
let key = storage::commitment_key(port_id, channel_id, sequence);
match self.storage().read_bytes(&key)? {
Some(value) => Ok(value.into()),
None => Err(HostError::FailedToRetrieve {
description: format!(
"No packet commitment: port {port_id}, channel \
{channel_id}, sequence {sequence}"
),
}),
}
}
fn store_packet_commitment(
&mut self,
port_id: &PortId,
channel_id: &ChannelId,
sequence: Sequence,
commitment: PacketCommitment,
) -> Result<()> {
let key = storage::commitment_key(port_id, channel_id, sequence);
let bytes = commitment.into_vec();
self.storage_mut()
.write_bytes(&key, bytes)
.map_err(HostError::from)
}
fn delete_packet_commitment(
&mut self,
port_id: &PortId,
channel_id: &ChannelId,
sequence: Sequence,
) -> Result<()> {
let key = storage::commitment_key(port_id, channel_id, sequence);
self.storage_mut().delete(&key).map_err(HostError::from)
}
fn packet_receipt(
&self,
port_id: &PortId,
channel_id: &ChannelId,
sequence: Sequence,
) -> Result<Receipt> {
let key = storage::receipt_key(port_id, channel_id, sequence);
match self.storage().read_bytes(&key)? {
Some(_) => Ok(Receipt::Ok),
None => Ok(Receipt::None),
}
}
fn store_packet_receipt(
&mut self,
port_id: &PortId,
channel_id: &ChannelId,
sequence: Sequence,
) -> Result<()> {
let key = storage::receipt_key(port_id, channel_id, sequence);
let bytes = [1_u8].to_vec();
self.storage_mut()
.write_bytes(&key, bytes)
.map_err(HostError::from)
}
fn packet_ack(
&self,
port_id: &PortId,
channel_id: &ChannelId,
sequence: Sequence,
) -> Result<Option<AcknowledgementCommitment>> {
let key = storage::ack_key(port_id, channel_id, sequence);
match self.storage().read_bytes(&key)? {
Some(value) => Ok(Some(value.into())),
None => Ok(None),
}
}
fn store_packet_ack(
&mut self,
port_id: &PortId,
channel_id: &ChannelId,
sequence: Sequence,
ack_commitment: AcknowledgementCommitment,
) -> Result<()> {
let key = storage::ack_key(port_id, channel_id, sequence);
let bytes = ack_commitment.into_vec();
self.storage_mut()
.write_bytes(&key, bytes)
.map_err(HostError::from)
}
fn delete_packet_ack(
&mut self,
port_id: &PortId,
channel_id: &ChannelId,
sequence: Sequence,
) -> Result<()> {
let key = storage::ack_key(port_id, channel_id, sequence);
self.storage_mut().delete(&key).map_err(HostError::from)
}
fn read_counter(&self, key: &Key) -> Result<u64> {
match self.storage().read::<u64>(key)? {
Some(counter) => Ok(counter),
None => unreachable!("the counter should be initialized"),
}
}
fn increment_counter(&mut self, key: &Key) -> Result<()> {
let count = self.read_counter(key)?;
let count = u64::checked_add(count, 1).ok_or_else(|| {
HostError::FailedToStore {
description: format!("The counter overflow: Key {key}"),
}
})?;
self.storage_mut()
.write(key, count)
.map_err(HostError::from)
}
fn store_ibc_trace(
&mut self,
addr: impl AsRef<str>,
trace_hash: impl AsRef<str>,
trace: impl AsRef<str>,
) -> Result<()> {
let key = storage::ibc_trace_key(addr, trace_hash.as_ref());
let has_key = self.storage().has_key(&key)?;
if !has_key {
self.storage_mut().write(&key, trace.as_ref())?;
}
Ok(())
}
fn nft_class(
&self,
class_id: &PrefixedClassId,
) -> Result<Option<NftClass>> {
let key = storage::nft_class_key(class_id);
self.storage().read(&key).map_err(HostError::from)
}
fn store_nft_class(&mut self, class: NftClass) -> Result<()> {
let key = storage::nft_class_key(&class.class_id);
self.storage_mut()
.write(&key, class)
.map_err(HostError::from)
}
fn nft_metadata(
&self,
class_id: &PrefixedClassId,
token_id: &TokenId,
) -> Result<Option<NftMetadata>> {
let key = storage::nft_metadata_key(class_id, token_id);
self.storage().read(&key).map_err(HostError::from)
}
fn store_nft_metadata(&mut self, metadata: NftMetadata) -> Result<()> {
let key =
storage::nft_metadata_key(&metadata.class_id, &metadata.token_id);
self.storage_mut()
.write(&key, metadata)
.map_err(HostError::from)
}
fn is_nft_owned<Token>(
&self,
class_id: &PrefixedClassId,
token_id: &TokenId,
owner: &Address,
) -> Result<bool>
where
Token: trans_token::Keys,
{
let ibc_token = trace::ibc_token_for_nft(class_id, token_id);
let balance_key = Token::balance_key(&ibc_token, owner);
let amount = self.storage().read::<Amount>(&balance_key)?;
Ok(amount == Some(Amount::from_u64(1)))
}
fn mint_amount(&self, token: &Address) -> Result<Amount> {
let key = storage::mint_amount_key(token);
Ok(self.storage().read::<Amount>(&key)?.unwrap_or_default())
}
fn store_mint_amount(
&mut self,
token: &Address,
amount: Amount,
) -> Result<()> {
let key = storage::mint_amount_key(token);
self.storage_mut()
.write(&key, amount)
.map_err(HostError::from)
}
fn deposit(&self, token: &Address) -> Result<Amount> {
let key = storage::deposit_key(token);
Ok(self.storage().read::<Amount>(&key)?.unwrap_or_default())
}
fn store_deposit(&mut self, token: &Address, amount: Amount) -> Result<()> {
let key = storage::deposit_key(token);
self.storage_mut()
.write(&key, amount)
.map_err(HostError::from)
}
fn withdraw(&self, token: &Address) -> Result<Amount> {
let key = storage::withdraw_key(token);
Ok(self.storage().read::<Amount>(&key)?.unwrap_or_default())
}
fn store_withdraw(
&mut self,
token: &Address,
amount: Amount,
) -> Result<()> {
let key = storage::withdraw_key(token);
self.storage_mut()
.write(&key, amount)
.map_err(HostError::from)
}
}
pub fn read_sequence<S: StorageRead + ?Sized>(
storage: &S,
key: &Key,
) -> std::result::Result<Sequence, StorageError> {
match storage.read_bytes(key)? {
Some(value) => {
let value: [u8; 8] = value.try_into().map_err(|_| {
StorageError::new_alloc(format!(
"The sequence value wasn't u64: Key {key}",
))
})?;
Ok(u64::from_be_bytes(value).into())
}
None => Ok(1.into()),
}
}