Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IBC Hooks: add hooks to call bridge escrow program #403

Merged
merged 11 commits into from
Nov 1, 2024
Merged
9 changes: 7 additions & 2 deletions solana/solana-ibc/programs/solana-ibc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ pub const WSOL_ADDRESS: &str = "So11111111111111111111111111111111111111112";
pub const MINIMUM_FEE_ACCOUNT_BALANCE: u64 =
solana_program::native_token::LAMPORTS_PER_SOL;

pub const BRIDGE_ESCROW_PROGRAM_ID: &str =
"AhfoGVmS19tvkEG2hBuZJ1D6qYEjyFmXZ1qPoFD6H4Mj";
pub const HOOK_TOKEN_ADDRESS: &str =
"0x36dd1bfe89d409f869fabbe72c3cf72ea8b460f6";
mina86 marked this conversation as resolved.
Show resolved Hide resolved

declare_id!("2HLLVco5HvwWriNbUhmVwA2pCetRkpgrqwnjcsZdyTKT");

#[cfg(not(feature = "mocks"))]
Expand Down Expand Up @@ -472,8 +477,8 @@ pub mod solana_ibc {
/// doesnt exists.
///
/// Would panic if it doesnt match the one that is in the packet
pub fn send_transfer(
ctx: Context<SendTransfer>,
pub fn send_transfer<'a, 'info>(
ctx: Context<'a, 'a, 'a, 'info, SendTransfer<'info>>,
hashed_full_denom: CryptoHash,
msg: ibc::MsgTransfer,
) -> Result<()> {
Expand Down
5 changes: 5 additions & 0 deletions solana/solana-ibc/programs/solana-ibc/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,9 @@ pub struct TransferAccounts<'a> {
pub mint_authority: Option<AccountInfo<'a>>,
pub token_program: Option<AccountInfo<'a>>,
pub fee_collector: Option<AccountInfo<'a>>,
/// Contains the list of accounts required for the hooks
/// if present
pub remaining_accounts: Vec<AccountInfo<'a>>,
}

#[derive(Debug)]
Expand Down Expand Up @@ -544,6 +547,7 @@ macro_rules! from_ctx {
};
($ctx:expr, with accounts) => {{
let accounts = &$ctx.accounts;
let remaining_accounts = &$ctx.remaining_accounts;
let accounts = TransferAccounts {
sender: Some(accounts.sender.as_ref().to_account_info()),
receiver: accounts
Expand Down Expand Up @@ -574,6 +578,7 @@ macro_rules! from_ctx {
.fee_collector
.as_deref()
.map(ToAccountInfo::to_account_info),
remaining_accounts: remaining_accounts.to_vec()
};
$crate::storage::from_ctx!($ctx, accounts = accounts)
}};
Expand Down
119 changes: 112 additions & 7 deletions solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use std::result::Result;
use std::str;
use std::str::{self, FromStr};

use anchor_lang::prelude::*;
use serde::{Deserialize, Serialize};
use spl_token::solana_program::instruction::Instruction;
use spl_token::solana_program::program::invoke;

use crate::ibc;
use crate::ibc::apps::transfer::types::packet::PacketData;
use crate::ibc::apps::transfer::types::proto::transfer::v2::FungibleTokenPacketData;
use crate::storage::IbcStorage;
use crate::{ibc, BRIDGE_ESCROW_PROGRAM_ID, HOOK_TOKEN_ADDRESS};

pub(crate) mod impls;

Expand Down Expand Up @@ -142,12 +144,115 @@
.into_bytes(),
..packet.clone()
};
let (extras, ack) = ibc::apps::transfer::module::on_recv_packet_execute(
self,
&maybe_ft_packet,
);
let ack_status = str::from_utf8(ack.as_bytes())
let (extras, mut ack) =
ibc::apps::transfer::module::on_recv_packet_execute(
self,
&maybe_ft_packet,
);
let cloned_ack = ack.clone();
let ack_status = str::from_utf8(cloned_ack.as_bytes())
mina86 marked this conversation as resolved.
Show resolved Hide resolved
.expect("Invalid acknowledgement string");
let status = serde_json::from_slice::<ibc::AcknowledgementStatus>(
ack.as_bytes(),
);
mina86 marked this conversation as resolved.
Show resolved Hide resolved
let success = if let Ok(status) = status {
status.is_successful()
} else {
ack = ibc::AcknowledgementStatus::error(
ibc::TokenTransferError::AckDeserialization.into(),
)
.into();
false
};
fn call_bridge_escrow(
accounts: &[AccountInfo],
data: Vec<u8>,
) -> Result<(), ibc::AcknowledgementStatus> {
// Perform hooks
let data = match serde_json::from_slice::<PacketData>(&data) {
Ok(data) => data,
Err(_) => {
return Err(ibc::AcknowledgementStatus::error(
ibc::TokenTransferError::PacketDataDeserialization
.into(),
)
.into());

Check failure on line 179 in solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

useless conversion to the same type: `ibc::core::channel::types::acknowledgement::AcknowledgementStatus`

error: useless conversion to the same type: `ibc::core::channel::types::acknowledgement::AcknowledgementStatus` --> solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs:175:32 | 175 | return Err(ibc::AcknowledgementStatus::error( | ________________________________^ 176 | | ibc::TokenTransferError::PacketDataDeserialization 177 | | .into(), 178 | | ) 179 | | .into()); | |___________________________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion = note: `-D clippy::useless-conversion` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::useless_conversion)]` help: consider removing `.into()` | 175 ~ return Err(ibc::AcknowledgementStatus::error( 176 + ibc::TokenTransferError::PacketDataDeserialization 177 + .into(), 178 ~ )); |
}
};
// The hook would only be called if the transferred token is the one we are interested in
if data.token.denom.base_denom.as_str() == HOOK_TOKEN_ADDRESS {
mina86 marked this conversation as resolved.
Show resolved Hide resolved
// The memo is a string and the structure is as follow:
// "<number of accounts>,<AccountKey1> ..... <AccountKeyN>,<intent_id>,<memo>"
//
// The relayer would parse the memo and pass the relevant accounts
// The intent_id and memo needs to be stripped
let memo = data.memo.as_ref();
let (accounts_size, rest) = memo.split_once(",").ok_or(

Check failure on line 190 in solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

single-character string constant used as pattern

error: single-character string constant used as pattern --> solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs:190:61 | 190 | let (accounts_size, rest) = memo.split_once(",").ok_or( | ^^^ help: consider using a `char`: `','` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern = note: `-D clippy::single-char-pattern` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::single_char_pattern)]`
ibc::AcknowledgementStatus::error(
ibc::TokenTransferError::Other(
"Invalid memo".to_string(),
)
.into(),
)
.into(),

Check failure on line 197 in solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

useless conversion to the same type: `ibc::core::channel::types::acknowledgement::AcknowledgementStatus`

error: useless conversion to the same type: `ibc::core::channel::types::acknowledgement::AcknowledgementStatus` --> solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs:191:21 | 191 | / ibc::AcknowledgementStatus::error( 192 | | ibc::TokenTransferError::Other( 193 | | "Invalid memo".to_string(), 194 | | ) 195 | | .into(), 196 | | ) 197 | | .into(), | |___________________________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion help: consider removing `.into()` | 191 ~ ibc::AcknowledgementStatus::error( 192 + ibc::TokenTransferError::Other( 193 + "Invalid memo".to_string(), 194 + ) 195 + .into(), 196 ~ ), |
)?;
// This is the 8 byte discriminant since the program is written in
// anchor. it is hash of "<namespace>:<function_name>" which is
// "global:on_receive_transfer" respectively.
let instruction_discriminant: Vec<u8> =
vec![149, 112, 68, 208, 4, 206, 248, 125];
mina86 marked this conversation as resolved.
Show resolved Hide resolved
let values = rest.split(",").collect::<Vec<&str>>();

Check failure on line 204 in solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

single-character string constant used as pattern

error: single-character string constant used as pattern --> solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs:204:41 | 204 | let values = rest.split(",").collect::<Vec<&str>>(); | ^^^ help: consider using a `char`: `','` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
let (_passed_accounts, ix_data) =
values.split_at(accounts_size.parse::<usize>().unwrap());
let intent_id = ix_data.get(0).ok_or(

Check failure on line 207 in solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

accessing first element with `ix_data.get(0)`

error: accessing first element with `ix_data.get(0)` --> solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs:207:33 | 207 | let intent_id = ix_data.get(0).ok_or( | ^^^^^^^^^^^^^^ help: try: `ix_data.first()` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#get_first = note: `-D clippy::get-first` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::get_first)]`
ibc::AcknowledgementStatus::error(
ibc::TokenTransferError::Other(
"Invalid memo".to_string(),
)
.into(),
)
.into(),

Check failure on line 214 in solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

useless conversion to the same type: `ibc::core::channel::types::acknowledgement::AcknowledgementStatus`

error: useless conversion to the same type: `ibc::core::channel::types::acknowledgement::AcknowledgementStatus` --> solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs:208:21 | 208 | / ibc::AcknowledgementStatus::error( 209 | | ibc::TokenTransferError::Other( 210 | | "Invalid memo".to_string(), 211 | | ) 212 | | .into(), 213 | | ) 214 | | .into(), | |___________________________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion help: consider removing `.into()` | 208 ~ ibc::AcknowledgementStatus::error( 209 + ibc::TokenTransferError::Other( 210 + "Invalid memo".to_string(), 211 + ) 212 + .into(), 213 ~ ), |
)?;
let memo = ix_data[1..].join(",");
let mut instruction_data = instruction_discriminant;
instruction_data.extend_from_slice(&intent_id.as_bytes());

Check failure on line 218 in solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

this expression creates a reference which is immediately dereferenced by the compiler

error: this expression creates a reference which is immediately dereferenced by the compiler --> solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs:218:52 | 218 | instruction_data.extend_from_slice(&intent_id.as_bytes()); | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `intent_id.as_bytes()` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow = note: `-D clippy::needless-borrow` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_borrow)]`
instruction_data.extend_from_slice(memo.as_bytes());

let bridge_escrow_program_id =
Pubkey::from_str(BRIDGE_ESCROW_PROGRAM_ID).unwrap();

let account_metas = accounts
.iter()
.map(|account| AccountMeta {
pubkey: *account.key,
is_signer: account.is_signer,
is_writable: account.is_writable,
})
.collect::<Vec<AccountMeta>>();
let instruction = Instruction::new_with_bytes(
bridge_escrow_program_id,
&instruction_data,
account_metas,
);

invoke(&instruction, accounts).map_err(|err| {
return ibc::AcknowledgementStatus::error(
ibc::TokenTransferError::Other(err.to_string()).into(),
);

Check failure on line 241 in solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

unneeded `return` statement

error: unneeded `return` statement --> solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs:239:21 | 239 | / return ibc::AcknowledgementStatus::error( 240 | | ibc::TokenTransferError::Other(err.to_string()).into(), 241 | | ); | |_____________________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return = note: `-D clippy::needless-return` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_return)]` help: remove `return` | 239 ~ ibc::AcknowledgementStatus::error( 240 + ibc::TokenTransferError::Other(err.to_string()).into(), 241 ~ ) |
})?;
msg!("Hook: Bridge escrow call successful");
}
Ok(())
}

if success {
let store = self.borrow();
let accounts = &store.accounts.remaining_accounts;
let result = call_bridge_escrow(accounts, maybe_ft_packet.data);
if let Err(status) = result {
ack = status.into();
}
}
msg!("ibc::Packet acknowledgement: {}", ack_status);
(extras, ack)
}
Expand Down
Loading