From 93ce9390d35e01a071b727f13c7b2110097f676a Mon Sep 17 00:00:00 2001 From: dhruvja Date: Sat, 23 Dec 2023 21:17:29 +0530 Subject: [PATCH 1/6] added method to form message using chunks --- .../solana-ibc/programs/solana-ibc/src/ibc.rs | 4 +- .../solana-ibc/programs/solana-ibc/src/lib.rs | 62 +++++- .../programs/solana-ibc/src/tests.rs | 188 ++++++++++++++++-- 3 files changed, 236 insertions(+), 18 deletions(-) diff --git a/solana/solana-ibc/programs/solana-ibc/src/ibc.rs b/solana/solana-ibc/programs/solana-ibc/src/ibc.rs index 1598e8cc..131d4234 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/ibc.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/ibc.rs @@ -18,7 +18,9 @@ pub use ibc::core::client::context::client_state::{ pub use ibc::core::client::context::consensus_state::ConsensusState; pub use ibc::core::client::context::types::error::ClientError; #[cfg(test)] -pub use ibc::core::client::context::types::msgs::{ClientMsg, MsgCreateClient}; +pub use ibc::core::client::context::types::msgs::{ + ClientMsg, MsgCreateClient, MsgUpdateClient, +}; pub use ibc::core::client::context::{ ClientExecutionContext, ClientValidationContext, }; diff --git a/solana/solana-ibc/programs/solana-ibc/src/lib.rs b/solana/solana-ibc/programs/solana-ibc/src/lib.rs index d422951e..63ac728d 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/lib.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/lib.rs @@ -13,13 +13,16 @@ use borsh::BorshDeserialize; use storage::TransferAccounts; use trie_ids::PortChannelPK; -use crate::ibc::{ClientStateValidation, SendPacketValidationContext}; +use crate::ibc::{ + ClientError, ClientStateValidation, SendPacketValidationContext, +}; pub const CHAIN_SEED: &[u8] = b"chain"; pub const PACKET_SEED: &[u8] = b"packet"; pub const SOLANA_IBC_STORAGE_SEED: &[u8] = b"private"; pub const TRIE_SEED: &[u8] = b"trie"; pub const MINT_ESCROW_SEED: &[u8] = b"mint_escrow"; +pub const MSG_CHUNKS: &[u8] = b"msg_chunks"; declare_id!("EnfDJsAK7BGgetnmKzBx86CsgC5kfSPcsktFCQ4YLC81"); @@ -42,8 +45,6 @@ mod validation_context; #[anchor_lang::program] pub mod solana_ibc { - use ::ibc::core::client::types::error::ClientError; - use super::*; /// Initialises the guest blockchain with given configuration and genesis @@ -263,6 +264,26 @@ pub mod solana_ibc { .map_err(error::Error::ContextError) .map_err(|err| error!((&err))) } + + /// Store messages which are divided into chunk in an account which can be accessed later + /// from the deliver method. + /// + /// Since solana programs have an instruction limit of 1232 bytes, we cannot send arguments + /// with large data. So we divide the data into chunks, call the method below and add it to + /// the account at the specified offset. + pub fn form_msg_chunks( + ctx: Context, + total_len: u32, + offset: u32, + bytes: Vec, + ) -> Result<()> { + let store = &mut ctx.accounts.msg_chunks; + if store.msg.is_empty() { + store.new(total_len as usize); + } + store.copy_into(offset.try_into().unwrap(), &bytes); + Ok(()) + } } /// All the storage accounts are initialized here since it is only called once @@ -505,6 +526,41 @@ pub struct SendPacket<'info> { system_program: Program<'info, System>, } +#[derive(Accounts)] +pub struct FormMessageChunks<'info> { + #[account(mut)] + sender: Signer<'info>, + + #[account(init_if_needed, payer = sender, seeds = [MSG_CHUNKS], bump, space = 10240)] + pub msg_chunks: Account<'info, MsgChunks>, + + pub system_program: Program<'info, System>, +} + +#[account] +#[derive(Debug)] +pub struct MsgChunks { + /// The vector consists of chunks of message data having the first 4 bytes + /// indicating the total size of the message + pub msg: Vec, +} + +impl MsgChunks { + /// Creates a new msg vector of size `total_length + 4` with 0s where the + /// first 4 bytes are allocated for the total size of the message + fn new(&mut self, total_len: usize) { + let msg = vec![0; total_len + 4]; + self.msg = msg; + let total_len_in_bytes = (total_len as u32).to_be_bytes(); + self.copy_into(0, &total_len_in_bytes); + } + + fn copy_into(&mut self, position: usize, data: &[u8]) { + msg!("data size -> {} {}", data.len(), self.msg.len()); + self.msg[position..position + data.len()].copy_from_slice(data); + } +} + impl ibc::Router for storage::IbcStorage<'_, '_> { // fn get_route(&self, module_id: &ibc::ModuleId) -> Option<&dyn ibc::Module> { diff --git a/solana/solana-ibc/programs/solana-ibc/src/tests.rs b/solana/solana-ibc/programs/solana-ibc/src/tests.rs index 8294f26d..f1a8bcd4 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/tests.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/tests.rs @@ -12,10 +12,12 @@ use anchor_client::solana_sdk::compute_budget::ComputeBudgetInstruction; use anchor_client::solana_sdk::pubkey::Pubkey; use anchor_client::solana_sdk::signature::{Keypair, Signature, Signer}; use anchor_client::{Client, Cluster}; +use anchor_lang::prelude::borsh; use anchor_lang::solana_program::instruction::AccountMeta; -use anchor_lang::ToAccountMetas; +use anchor_lang::{AnchorDeserialize, ToAccountMetas}; use anchor_spl::associated_token::get_associated_token_address; use anyhow::Result; +use ibc::{ClientId, MsgEnvelope, MsgUpdateClient}; use crate::ibc::ClientStateCommon; use crate::storage::PrivateStorage; @@ -110,20 +112,14 @@ impl ToAccountMetas for DeliverWithRemainingAccounts { #[test] #[ignore = "Requires local validator to run"] fn anchor_test_deliver() -> Result<()> { - let authority = Rc::new(Keypair::new()); - println!("This is pubkey {}", authority.pubkey().to_string()); - let lamports = 2_000_000_000; - - let client = Client::new_with_options( - Cluster::Localnet, - authority.clone(), - CommitmentConfig::processed(), - ); - let program = client.program(crate::ID).unwrap(); - + let (authority, _client, program, _airdrop_signature) = + setup_client_program( + Keypair::new(), + Cluster::Localnet, + CommitmentConfig::processed(), + true, + ); let sol_rpc_client = program.rpc(); - let _airdrop_signature = - airdrop(&sol_rpc_client, authority.pubkey(), lamports); // Build, sign, and send program instruction let storage = Pubkey::find_program_address( @@ -677,6 +673,170 @@ fn anchor_test_deliver() -> Result<()> { Ok(()) } +#[test] +#[ignore = "Requires local validator to run"] +fn anchor_test_deliver_chunks() -> Result<()> { + let (authority, _client, program, _airdrop_signature) = + setup_client_program( + Keypair::new(), + Cluster::Localnet, + CommitmentConfig::processed(), + true, + ); + + let msg_chunks = + Pubkey::find_program_address(&[crate::MSG_CHUNKS], &crate::ID).0; + + let msg = MsgUpdateClient { + client_id: ClientId::from_str("07-tendermint-1").unwrap(), + client_message: ::ibc::primitives::proto::Any { + type_url: "/ibc.lightclients.tendermint.v1.ClientMessage" + .to_owned(), + value: vec![ + 10, 38, 47, 105, 98, 99, 46, 108, 105, 103, 104, 116, 99, 108, + 105, 101, 110, 116, 115, 46, 116, 101, 110, 100, 101, 114, 109, + 105, 110, 116, 46, 118, 49, 46, 72, 101, 97, 100, 101, 114, 18, + 238, 6, 10, 202, 4, 10, 141, 3, 10, 2, 8, 11, 18, 6, 116, 101, + 115, 116, 45, 49, 24, 228, 1, 34, 12, 8, 166, 239, 150, 172, 6, + 16, 248, 214, 168, 175, 3, 42, 72, 10, 32, 163, 207, 132, 246, + 46, 57, 175, 243, 154, 230, 28, 49, 166, 80, 47, 101, 26, 25, + 167, 48, 251, 79, 183, 120, 220, 249, 104, 20, 75, 18, 121, + 220, 18, 36, 8, 1, 18, 32, 190, 87, 215, 130, 108, 157, 149, + 10, 117, 231, 205, 219, 12, 175, 3, 76, 11, 17, 138, 9, 28, 37, + 199, 131, 252, 206, 185, 173, 193, 143, 227, 33, 50, 32, 132, + 165, 67, 180, 168, 210, 149, 49, 160, 147, 126, 116, 112, 232, + 205, 149, 243, 130, 193, 222, 122, 12, 27, 84, 242, 5, 161, + 200, 150, 96, 209, 60, 58, 32, 227, 176, 196, 66, 152, 252, 28, + 20, 154, 251, 244, 200, 153, 111, 185, 36, 39, 174, 65, 228, + 100, 155, 147, 76, 164, 149, 153, 27, 120, 82, 184, 85, 66, 32, + 119, 230, 213, 242, 99, 59, 194, 128, 185, 41, 83, 174, 149, + 43, 248, 129, 25, 232, 178, 199, 110, 149, 126, 23, 45, 95, 54, + 23, 64, 17, 145, 181, 74, 32, 119, 230, 213, 242, 99, 59, 194, + 128, 185, 41, 83, 174, 149, 43, 248, 129, 25, 232, 178, 199, + 110, 149, 126, 23, 45, 95, 54, 23, 64, 17, 145, 181, 82, 32, 4, + 128, 145, 188, 125, 220, 40, 63, 119, 191, 191, 145, 215, 60, + 68, 218, 88, 195, 223, 138, 156, 188, 134, 116, 5, 216, 183, + 243, 218, 173, 162, 47, 90, 32, 255, 183, 136, 77, 148, 106, + 121, 179, 78, 128, 220, 94, 169, 3, 40, 24, 46, 145, 149, 126, + 249, 194, 220, 159, 9, 22, 55, 92, 227, 111, 193, 135, 98, 32, + 227, 176, 196, 66, 152, 252, 28, 20, 154, 251, 244, 200, 153, + 111, 185, 36, 39, 174, 65, 228, 100, 155, 147, 76, 164, 149, + 153, 27, 120, 82, 184, 85, 106, 32, 227, 176, 196, 66, 152, + 252, 28, 20, 154, 251, 244, 200, 153, 111, 185, 36, 39, 174, + 65, 228, 100, 155, 147, 76, 164, 149, 153, 27, 120, 82, 184, + 85, 114, 20, 197, 236, 6, 68, 250, 32, 151, 158, 18, 66, 74, + 86, 41, 57, 249, 233, 235, 109, 26, 215, 18, 183, 1, 8, 228, 1, + 26, 72, 10, 32, 77, 231, 232, 136, 53, 222, 130, 207, 199, 138, + 166, 59, 173, 215, 106, 153, 129, 106, 241, 53, 113, 77, 188, + 80, 79, 25, 76, 28, 48, 21, 125, 71, 18, 36, 8, 1, 18, 32, 93, + 4, 216, 112, 164, 60, 48, 184, 86, 132, 54, 104, 213, 52, 99, + 155, 105, 155, 7, 110, 132, 153, 225, 219, 245, 33, 115, 154, + 148, 30, 120, 13, 34, 104, 8, 2, 18, 20, 197, 236, 6, 68, 250, + 32, 151, 158, 18, 66, 74, 86, 41, 57, 249, 233, 235, 109, 26, + 215, 26, 12, 8, 171, 239, 150, 172, 6, 16, 176, 140, 197, 204, + 3, 34, 64, 166, 133, 186, 198, 251, 171, 42, 171, 175, 37, 139, + 233, 142, 183, 17, 66, 52, 228, 35, 153, 94, 79, 215, 205, 45, + 8, 192, 196, 246, 8, 156, 34, 160, 115, 245, 111, 188, 42, 99, + 214, 237, 255, 230, 133, 201, 191, 218, 222, 141, 250, 160, + 225, 206, 45, 4, 194, 219, 47, 194, 171, 62, 67, 117, 6, 18, + 138, 1, 10, 64, 10, 20, 197, 236, 6, 68, 250, 32, 151, 158, 18, + 66, 74, 86, 41, 57, 249, 233, 235, 109, 26, 215, 18, 34, 10, + 32, 11, 93, 18, 110, 141, 126, 60, 32, 236, 136, 158, 223, 95, + 73, 175, 130, 55, 184, 247, 241, 143, 50, 115, 96, 210, 46, + 135, 104, 119, 246, 35, 194, 24, 128, 148, 235, 220, 3, 18, 64, + 10, 20, 197, 236, 6, 68, 250, 32, 151, 158, 18, 66, 74, 86, 41, + 57, 249, 233, 235, 109, 26, 215, 18, 34, 10, 32, 11, 93, 18, + 110, 141, 126, 60, 32, 236, 136, 158, 223, 95, 73, 175, 130, + 55, 184, 247, 241, 143, 50, 115, 96, 210, 46, 135, 104, 119, + 246, 35, 194, 24, 128, 148, 235, 220, 3, 24, 128, 148, 235, + 220, 3, 26, 5, 8, 1, 16, 228, 1, 34, 138, 1, 10, 64, 10, 20, + 197, 236, 6, 68, 250, 32, 151, 158, 18, 66, 74, 86, 41, 57, + 249, 233, 235, 109, 26, 215, 18, 34, 10, 32, 11, 93, 18, 110, + 141, 126, 60, 32, 236, 136, 158, 223, 95, 73, 175, 130, 55, + 184, 247, 241, 143, 50, 115, 96, 210, 46, 135, 104, 119, 246, + 35, 194, 24, 128, 148, 235, 220, 3, 18, 64, 10, 20, 197, 236, + 6, 68, 250, 32, 151, 158, 18, 66, 74, 86, 41, 57, 249, 233, + 235, 109, 26, 215, 18, 34, 10, 32, 11, 93, 18, 110, 141, 126, + 60, 32, 236, 136, 158, 223, 95, 73, 175, 130, 55, 184, 247, + 241, 143, 50, 115, 96, 210, 46, 135, 104, 119, 246, 35, 194, + 24, 128, 148, 235, 220, 3, 24, 128, 148, 235, 220, 3, + ], + }, + signer: String::from("oxyzEsUj9CV6HsqPCUZqVwrFJJvpd9iCBrPdzTBWLBb") + .into(), + }; + + let msg_envelope = MsgEnvelope::Client(ibc::ClientMsg::UpdateClient(msg)); + + let serialized_message = borsh::to_vec(&msg_envelope).unwrap(); + + println!("This is serialized message length {}", serialized_message.len()); + + let length = serialized_message.len(); + let chunk_size = 100; + let mut offset = 4; + + for i in serialized_message.chunks(chunk_size) { + let sig = program + .request() + .accounts(accounts::FormMessageChunks { + sender: authority.pubkey(), + msg_chunks, + system_program: system_program::ID, + }) + .args(instruction::FormMsgChunks { + total_len: length as u32, + offset: offset as u32, + bytes: i.to_vec(), + }) + .payer(authority.clone()) + .signer(&*authority) + .send_with_spinner_and_config(RpcSendTransactionConfig { + skip_preflight: true, + ..RpcSendTransactionConfig::default() + })?; + println!(" Signature for message chunks : {sig}"); + offset += chunk_size; + } + + let final_msg: crate::MsgChunks = program.account(msg_chunks).unwrap(); + + let serialized_msg_envelope = &final_msg.msg[4..]; + let unserialized_msg = + MsgEnvelope::try_from_slice(serialized_msg_envelope).unwrap(); + assert_eq!(unserialized_msg, msg_envelope); + Ok(()) +} + +fn setup_client_program( + authority: Keypair, + cluster: Cluster, + commitment_config: CommitmentConfig, + with_airdrop: bool, +) -> ( + Rc, + Client>, + anchor_client::Program>, + Option, +) { + let authority = Rc::new(authority); + println!("This is pubkey {}", authority.pubkey().to_string()); + let lamports = 2_000_000_000; + + let client = + Client::new_with_options(cluster, authority.clone(), commitment_config); + let program = client.program(crate::ID).unwrap(); + + if with_airdrop { + let sol_rpc_client = program.rpc(); + let airdrop_signature = + airdrop(&sol_rpc_client, authority.pubkey(), lamports); + return (authority, client, program, Some(airdrop_signature)); + } + + (authority, client, program, None) +} + fn construct_packet_from_denom( port_id: ibc::PortId, // Channel id used to define if its source chain or destination chain (in From 298cfc39559a743bfca0e7bdfb05cb82223ece03 Mon Sep 17 00:00:00 2001 From: dhruvja Date: Sat, 23 Dec 2023 22:27:15 +0530 Subject: [PATCH 2/6] add deliver with chunks --- .../solana-ibc/programs/solana-ibc/src/lib.rs | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/solana/solana-ibc/programs/solana-ibc/src/lib.rs b/solana/solana-ibc/programs/solana-ibc/src/lib.rs index 63ac728d..99801ebd 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/lib.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/lib.rs @@ -145,6 +145,18 @@ pub mod solana_ibc { .map_err(move |err| error!((&err))) } + pub fn deliver_with_chunks<'a, 'info>( + ctx: Context<'a, 'a, 'a, 'info, DeliverWithChunks<'info>>, + ) -> Result<()> { + let msg_chunks = &ctx.accounts.msg_chunks; + let mut store = storage::from_ctx!(ctx, with accounts); + let mut router = store.clone(); + let message = ibc::MsgEnvelope::try_from_slice(&msg_chunks.msg).unwrap(); + ::ibc::core::entrypoint::dispatch(&mut store, &mut router, message) + .map_err(error::Error::ContextError) + .map_err(move |err| error!((&err))) + } + /// Called to set up escrow and mint accounts for given channel and denom. /// Panics if called without `mocks` feature. pub fn mock_init_escrow<'a, 'info>( @@ -429,6 +441,49 @@ pub struct Deliver<'info> { system_program: Program<'info, System>, } +#[derive(Accounts, Clone)] +pub struct DeliverWithChunks<'info> { + #[account(mut)] + sender: Signer<'info>, + + receiver: Option>, + + /// The account holding private IBC storage. + #[account(mut,seeds = [SOLANA_IBC_STORAGE_SEED], + bump)] + storage: Account<'info, storage::PrivateStorage>, + + #[account(mut, seeds = [MSG_CHUNKS], bump)] + msg_chunks: Account<'info, MsgChunks>, + + /// The account holding provable IBC storage, i.e. the trie. + /// + /// CHECK: Account’s owner is checked by [`storage::get_provable_from`] + /// function. + #[account(mut, seeds = [TRIE_SEED], + bump)] + trie: UncheckedAccount<'info>, + + /// The guest blockchain data. + #[account(mut, seeds = [CHAIN_SEED], bump)] + chain: Box>, + #[account(mut, seeds = [MINT_ESCROW_SEED], bump)] + /// CHECK: + mint_authority: Option>, + #[account(mut, mint::decimals = 6, mint::authority = mint_authority)] + token_mint: Option>>, + #[account(mut, token::mint = token_mint, token::authority = mint_authority)] + escrow_account: Option>>, + #[account(init_if_needed, payer = sender, + associated_token::mint = token_mint, + associated_token::authority = receiver)] + receiver_token_account: Option>>, + + associated_token_program: Option>, + token_program: Option>, + system_program: Program<'info, System>, +} + #[derive(Accounts)] #[instruction(port_id: ibc::PortId, channel_id_on_b: ibc::ChannelId, base_denom: String)] pub struct MockInitEscrow<'info> { From 352c8e15910c6d2dc999f3e7eeb5bfa0c7a2d87e Mon Sep 17 00:00:00 2001 From: dhruvja Date: Sat, 23 Dec 2023 22:30:33 +0530 Subject: [PATCH 3/6] moved msg chunks to storage --- .../solana-ibc/programs/solana-ibc/src/lib.rs | 31 +++---------------- .../programs/solana-ibc/src/storage.rs | 26 ++++++++++++++++ .../programs/solana-ibc/src/tests.rs | 3 +- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/solana/solana-ibc/programs/solana-ibc/src/lib.rs b/solana/solana-ibc/programs/solana-ibc/src/lib.rs index 99801ebd..489dffa5 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/lib.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/lib.rs @@ -151,11 +151,12 @@ pub mod solana_ibc { let msg_chunks = &ctx.accounts.msg_chunks; let mut store = storage::from_ctx!(ctx, with accounts); let mut router = store.clone(); - let message = ibc::MsgEnvelope::try_from_slice(&msg_chunks.msg).unwrap(); + let message = + ibc::MsgEnvelope::try_from_slice(&msg_chunks.msg).unwrap(); ::ibc::core::entrypoint::dispatch(&mut store, &mut router, message) .map_err(error::Error::ContextError) .map_err(move |err| error!((&err))) - } + } /// Called to set up escrow and mint accounts for given channel and denom. /// Panics if called without `mocks` feature. @@ -454,7 +455,7 @@ pub struct DeliverWithChunks<'info> { storage: Account<'info, storage::PrivateStorage>, #[account(mut, seeds = [MSG_CHUNKS], bump)] - msg_chunks: Account<'info, MsgChunks>, + msg_chunks: Account<'info, storage::MsgChunks>, /// The account holding provable IBC storage, i.e. the trie. /// @@ -587,34 +588,12 @@ pub struct FormMessageChunks<'info> { sender: Signer<'info>, #[account(init_if_needed, payer = sender, seeds = [MSG_CHUNKS], bump, space = 10240)] - pub msg_chunks: Account<'info, MsgChunks>, + pub msg_chunks: Account<'info, storage::MsgChunks>, pub system_program: Program<'info, System>, } -#[account] -#[derive(Debug)] -pub struct MsgChunks { - /// The vector consists of chunks of message data having the first 4 bytes - /// indicating the total size of the message - pub msg: Vec, -} - -impl MsgChunks { - /// Creates a new msg vector of size `total_length + 4` with 0s where the - /// first 4 bytes are allocated for the total size of the message - fn new(&mut self, total_len: usize) { - let msg = vec![0; total_len + 4]; - self.msg = msg; - let total_len_in_bytes = (total_len as u32).to_be_bytes(); - self.copy_into(0, &total_len_in_bytes); - } - fn copy_into(&mut self, position: usize, data: &[u8]) { - msg!("data size -> {} {}", data.len(), self.msg.len()); - self.msg[position..position + data.len()].copy_from_slice(data); - } -} impl ibc::Router for storage::IbcStorage<'_, '_> { // diff --git a/solana/solana-ibc/programs/solana-ibc/src/storage.rs b/solana/solana-ibc/programs/solana-ibc/src/storage.rs index dc56126d..ca21f9c1 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/storage.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/storage.rs @@ -379,6 +379,32 @@ pub(crate) struct IbcStorageInner<'a, 'b> { pub chain: &'a mut crate::chain::ChainData, } +/// Struct containing message chunks +/// +/// The struct consists of chunks of message data having the first 4 bytes +/// indicating the total size of the message +#[account] +#[derive(Debug)] +pub struct MsgChunks { + pub msg: Vec, +} + +impl MsgChunks { + /// Creates a new msg vector of size `total_length + 4` with 0s where the + /// first 4 bytes are allocated for the total size of the message + pub fn new(&mut self, total_len: usize) { + let msg = vec![0; total_len + 4]; + self.msg = msg; + let total_len_in_bytes = (total_len as u32).to_be_bytes(); + self.copy_into(0, &total_len_in_bytes); + } + + pub fn copy_into(&mut self, position: usize, data: &[u8]) { + msg!("data size -> {} {}", data.len(), self.msg.len()); + self.msg[position..position + data.len()].copy_from_slice(data); + } +} + /// A reference-counted reference to the IBC storage. /// /// Uses inner-mutability via [`RefCell`] to allow modifications to the storage. diff --git a/solana/solana-ibc/programs/solana-ibc/src/tests.rs b/solana/solana-ibc/programs/solana-ibc/src/tests.rs index f1a8bcd4..ab92ec1a 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/tests.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/tests.rs @@ -799,7 +799,8 @@ fn anchor_test_deliver_chunks() -> Result<()> { offset += chunk_size; } - let final_msg: crate::MsgChunks = program.account(msg_chunks).unwrap(); + let final_msg: crate::storage::MsgChunks = + program.account(msg_chunks).unwrap(); let serialized_msg_envelope = &final_msg.msg[4..]; let unserialized_msg = From 59ba2bd15a882eb1d5108751881d2d8f1563b87e Mon Sep 17 00:00:00 2001 From: dhruvja Date: Sat, 23 Dec 2023 23:01:35 +0530 Subject: [PATCH 4/6] fix clippy --- solana/solana-ibc/programs/solana-ibc/src/lib.rs | 2 +- solana/solana-ibc/programs/solana-ibc/src/storage.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/solana/solana-ibc/programs/solana-ibc/src/lib.rs b/solana/solana-ibc/programs/solana-ibc/src/lib.rs index 489dffa5..816d1e28 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/lib.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/lib.rs @@ -292,7 +292,7 @@ pub mod solana_ibc { ) -> Result<()> { let store = &mut ctx.accounts.msg_chunks; if store.msg.is_empty() { - store.new(total_len as usize); + store.new_alloc(total_len as usize); } store.copy_into(offset.try_into().unwrap(), &bytes); Ok(()) diff --git a/solana/solana-ibc/programs/solana-ibc/src/storage.rs b/solana/solana-ibc/programs/solana-ibc/src/storage.rs index ca21f9c1..cfcea26e 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/storage.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/storage.rs @@ -392,7 +392,7 @@ pub struct MsgChunks { impl MsgChunks { /// Creates a new msg vector of size `total_length + 4` with 0s where the /// first 4 bytes are allocated for the total size of the message - pub fn new(&mut self, total_len: usize) { + pub fn new_alloc(&mut self, total_len: usize) { let msg = vec![0; total_len + 4]; self.msg = msg; let total_len_in_bytes = (total_len as u32).to_be_bytes(); From f3fa8eaa84d285e4230e807deb69d62900b98918 Mon Sep 17 00:00:00 2001 From: dhruvja Date: Wed, 27 Dec 2023 14:03:09 +0530 Subject: [PATCH 5/6] using protobuf instead of borsh --- .../solana-ibc/programs/solana-ibc/src/lib.rs | 16 +++-- .../programs/solana-ibc/src/storage.rs | 18 +++-- .../programs/solana-ibc/src/tests.rs | 70 +++++++++++++++---- 3 files changed, 81 insertions(+), 23 deletions(-) diff --git a/solana/solana-ibc/programs/solana-ibc/src/lib.rs b/solana/solana-ibc/programs/solana-ibc/src/lib.rs index 816d1e28..8f793a6b 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/lib.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/lib.rs @@ -46,6 +46,7 @@ mod validation_context; pub mod solana_ibc { use super::*; + use crate::storage::MsgChunks; /// Initialises the guest blockchain with given configuration and genesis /// epoch. @@ -149,10 +150,14 @@ pub mod solana_ibc { ctx: Context<'a, 'a, 'a, 'info, DeliverWithChunks<'info>>, ) -> Result<()> { let msg_chunks = &ctx.accounts.msg_chunks; + let cloned_msg_chunks: &mut MsgChunks = &mut msg_chunks.clone(); let mut store = storage::from_ctx!(ctx, with accounts); let mut router = store.clone(); - let message = - ibc::MsgEnvelope::try_from_slice(&msg_chunks.msg).unwrap(); + + let message = ibc::MsgEnvelope::try_from(ibc::Any::from( + cloned_msg_chunks.clone(), + )) + .unwrap(); ::ibc::core::entrypoint::dispatch(&mut store, &mut router, message) .map_err(error::Error::ContextError) .map_err(move |err| error!((&err))) @@ -286,13 +291,14 @@ pub mod solana_ibc { /// the account at the specified offset. pub fn form_msg_chunks( ctx: Context, + type_url: String, total_len: u32, offset: u32, bytes: Vec, ) -> Result<()> { let store = &mut ctx.accounts.msg_chunks; - if store.msg.is_empty() { - store.new_alloc(total_len as usize); + if store.value.is_empty() { + store.new_alloc(total_len as usize, type_url); } store.copy_into(offset.try_into().unwrap(), &bytes); Ok(()) @@ -454,7 +460,7 @@ pub struct DeliverWithChunks<'info> { bump)] storage: Account<'info, storage::PrivateStorage>, - #[account(mut, seeds = [MSG_CHUNKS], bump)] + #[account(mut)] msg_chunks: Account<'info, storage::MsgChunks>, /// The account holding provable IBC storage, i.e. the trie. diff --git a/solana/solana-ibc/programs/solana-ibc/src/storage.rs b/solana/solana-ibc/programs/solana-ibc/src/storage.rs index cfcea26e..bdbe8611 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/storage.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/storage.rs @@ -386,22 +386,30 @@ pub(crate) struct IbcStorageInner<'a, 'b> { #[account] #[derive(Debug)] pub struct MsgChunks { - pub msg: Vec, + pub type_url: String, + pub value: Vec, } impl MsgChunks { /// Creates a new msg vector of size `total_length + 4` with 0s where the /// first 4 bytes are allocated for the total size of the message - pub fn new_alloc(&mut self, total_len: usize) { + pub fn new_alloc(&mut self, total_len: usize, type_url: String) { let msg = vec![0; total_len + 4]; - self.msg = msg; + self.value = msg; + self.type_url = type_url; let total_len_in_bytes = (total_len as u32).to_be_bytes(); self.copy_into(0, &total_len_in_bytes); } pub fn copy_into(&mut self, position: usize, data: &[u8]) { - msg!("data size -> {} {}", data.len(), self.msg.len()); - self.msg[position..position + data.len()].copy_from_slice(data); + msg!("data size -> {} {}", data.len(), self.value.len()); + self.value[position..position + data.len()].copy_from_slice(data); + } +} + +impl From for ibc::Any { + fn from(value: MsgChunks) -> Self { + ibc::Any { type_url: value.type_url, value: value.value[4..].to_vec() } } } diff --git a/solana/solana-ibc/programs/solana-ibc/src/tests.rs b/solana/solana-ibc/programs/solana-ibc/src/tests.rs index ab92ec1a..c0e5980a 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/tests.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/tests.rs @@ -4,6 +4,8 @@ use std::str::FromStr; use std::thread::sleep; use std::time::Duration; +use ::ibc::primitives::proto::Protobuf; +use ::ibc::primitives::Msg; use anchor_client::anchor_lang::system_program; use anchor_client::solana_client::rpc_client::RpcClient; use anchor_client::solana_client::rpc_config::RpcSendTransactionConfig; @@ -130,6 +132,8 @@ fn anchor_test_deliver() -> Result<()> { let trie = Pubkey::find_program_address(&[crate::TRIE_SEED], &crate::ID).0; let chain = Pubkey::find_program_address(&[crate::CHAIN_SEED], &crate::ID).0; + let msg_chunks = + Pubkey::find_program_address(&[crate::MSG_CHUNKS], &crate::ID).0; /* * Initialise chain @@ -178,18 +182,55 @@ fn anchor_test_deliver() -> Result<()> { println!("\nCreating Mock Client"); let (mock_client_state, mock_cs_state) = create_mock_client_and_cs_state(); - let message = make_message!( - ibc::MsgCreateClient::new( - ibc::Any::from(mock_client_state), - ibc::Any::from(mock_cs_state), - ibc::Signer::from(authority.pubkey().to_string()), - ), - ibc::ClientMsg::CreateClient, - ibc::MsgEnvelope::Client, + // let message = make_message!( + // ibc::MsgCreateClient::new( + // ibc::Any::from(mock_client_state), + // ibc::Any::from(mock_cs_state.clone()), + // ibc::Signer::from(authority.pubkey().to_string()), + // ), + // ibc::ClientMsg::CreateClient, + // ibc::MsgEnvelope::Client, + // ); + + let test_msg = ibc::MsgCreateClient::new( + ibc::Any::from(mock_client_state), + ibc::Any::from(mock_cs_state), + ibc::Signer::from(authority.pubkey().to_string()), ); + + let serialized_message = test_msg.clone().encode_vec(); + + let length = serialized_message.len(); + let chunk_size = 100; + let mut offset = 4; + + for i in serialized_message.chunks(chunk_size) { + let sig = program + .request() + .accounts(accounts::FormMessageChunks { + sender: authority.pubkey(), + msg_chunks, + system_program: system_program::ID, + }) + .args(instruction::FormMsgChunks { + total_len: length as u32, + offset: offset as u32, + bytes: i.to_vec(), + type_url: test_msg.type_url(), + }) + .payer(authority.clone()) + .signer(&*authority) + .send_with_spinner_and_config(RpcSendTransactionConfig { + skip_preflight: true, + ..RpcSendTransactionConfig::default() + })?; + println!(" Signature for message chunks : {sig}"); + offset += chunk_size; + } + let sig = program .request() - .accounts(accounts::Deliver { + .accounts(accounts::DeliverWithChunks { sender: authority.pubkey(), receiver: None, storage, @@ -202,8 +243,9 @@ fn anchor_test_deliver() -> Result<()> { receiver_token_account: None, associated_token_program: None, token_program: None, + msg_chunks, }) - .args(instruction::Deliver { message }) + .args(instruction::DeliverWithChunks {}) .payer(authority.clone()) .signer(&*authority) .send_with_spinner_and_config(RpcSendTransactionConfig { @@ -675,7 +717,7 @@ fn anchor_test_deliver() -> Result<()> { #[test] #[ignore = "Requires local validator to run"] -fn anchor_test_deliver_chunks() -> Result<()> { +fn test_deliver_chunks() -> Result<()> { let (authority, _client, program, _airdrop_signature) = setup_client_program( Keypair::new(), @@ -766,7 +808,8 @@ fn anchor_test_deliver_chunks() -> Result<()> { .into(), }; - let msg_envelope = MsgEnvelope::Client(ibc::ClientMsg::UpdateClient(msg)); + let msg_envelope = + MsgEnvelope::Client(ibc::ClientMsg::UpdateClient(msg.clone())); let serialized_message = borsh::to_vec(&msg_envelope).unwrap(); @@ -788,6 +831,7 @@ fn anchor_test_deliver_chunks() -> Result<()> { total_len: length as u32, offset: offset as u32, bytes: i.to_vec(), + type_url: msg.type_url(), }) .payer(authority.clone()) .signer(&*authority) @@ -802,7 +846,7 @@ fn anchor_test_deliver_chunks() -> Result<()> { let final_msg: crate::storage::MsgChunks = program.account(msg_chunks).unwrap(); - let serialized_msg_envelope = &final_msg.msg[4..]; + let serialized_msg_envelope = &final_msg.value[4..]; let unserialized_msg = MsgEnvelope::try_from_slice(serialized_msg_envelope).unwrap(); assert_eq!(unserialized_msg, msg_envelope); From a0eacdc016160544273d0fb5d0e9f2cbdb4ddb93 Mon Sep 17 00:00:00 2001 From: Dhruv D Jain Date: Wed, 27 Dec 2023 18:49:08 +0530 Subject: [PATCH 6/6] closing msg_chunks account at deliverWithChunks --- solana/solana-ibc/programs/solana-ibc/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/solana/solana-ibc/programs/solana-ibc/src/lib.rs b/solana/solana-ibc/programs/solana-ibc/src/lib.rs index 8f793a6b..48db2cc4 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/lib.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/lib.rs @@ -460,8 +460,8 @@ pub struct DeliverWithChunks<'info> { bump)] storage: Account<'info, storage::PrivateStorage>, - #[account(mut)] - msg_chunks: Account<'info, storage::MsgChunks>, + #[account(mut, close = sender)] + msg_chunks: Box>, /// The account holding provable IBC storage, i.e. the trie. ///