From aa5528ac996aa62c1fd3a6038691fa069b86bb90 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Mon, 2 Sep 2024 02:50:08 +0200 Subject: [PATCH 1/5] solana-ibc: add support for witness account --- .../restaking/programs/restaking/Cargo.toml | 1 + .../restaking/programs/restaking/src/lib.rs | 46 ++++++--- .../programs/restaking/src/validation.rs | 98 ++++++++++++++----- .../solana-ibc/programs/solana-ibc/Cargo.toml | 1 + .../solana-ibc/programs/solana-ibc/src/lib.rs | 77 +++++++++++++++ .../programs/solana-ibc/src/mocks.rs | 2 + .../programs/solana-ibc/src/storage.rs | 39 +++++--- .../programs/solana-ibc/src/tests.rs | 22 +++++ validator/Cargo.toml | 3 + validator/src/utils.rs | 18 ++++ 10 files changed, 255 insertions(+), 52 deletions(-) diff --git a/solana/restaking/programs/restaking/Cargo.toml b/solana/restaking/programs/restaking/Cargo.toml index ca817d2f..ffa4209d 100644 --- a/solana/restaking/programs/restaking/Cargo.toml +++ b/solana/restaking/programs/restaking/Cargo.toml @@ -15,6 +15,7 @@ no-entrypoint = [] no-idl = [] no-log-ix-name = [] cpi = ["no-entrypoint"] +witness = ["solana-ibc/witness"] default = [] [dependencies] diff --git a/solana/restaking/programs/restaking/src/lib.rs b/solana/restaking/programs/restaking/src/lib.rs index dcbd306e..f0f4ba80 100644 --- a/solana/restaking/programs/restaking/src/lib.rs +++ b/solana/restaking/programs/restaking/src/lib.rs @@ -53,6 +53,7 @@ pub mod restaking { /// sent in the same order as given below /// - Chain Data /// - trie + /// - witness (if compiled with `witness` Cargo feature) /// - Guest blockchain program ID pub fn deposit<'a, 'info>( ctx: Context<'a, 'a, 'a, 'info, Deposit<'info>>, @@ -106,8 +107,12 @@ pub mod restaking { let validator_key = match service { Service::GuestChain { validator } => validator, }; + let remaining_accounts = validation::validate_remaining_accounts( + ctx.remaining_accounts, + &guest_chain_program_id, + )?; let borrowed_chain_data = - ctx.remaining_accounts[0].data.try_borrow().unwrap(); + remaining_accounts.chain.try_borrow_data().unwrap(); let mut chain_data: &[u8] = &borrowed_chain_data; let chain = solana_ibc::chain::ChainData::try_deserialize(&mut chain_data) @@ -118,20 +123,19 @@ pub mod restaking { let amount = validator.map_or(u128::from(amount), |val| { u128::from(val.stake) + u128::from(amount) }); - validation::validate_remaining_accounts( - ctx.remaining_accounts, - &guest_chain_program_id, - )?; core::mem::drop(borrowed_chain_data); + let cpi_accounts = SetStake { sender: ctx.accounts.depositor.to_account_info(), - chain: ctx.remaining_accounts[0].clone(), - trie: ctx.remaining_accounts[1].clone(), + chain: remaining_accounts.chain.clone(), + trie: remaining_accounts.trie.clone(), + #[cfg(feature = "witness")] + witness: remaining_accounts.witness.clone(), system_program: ctx.accounts.system_program.to_account_info(), instruction: ctx.accounts.instruction.to_account_info(), }; - let cpi_program = ctx.remaining_accounts[2].clone(); - let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); + let cpi_ctx = + CpiContext::new(remaining_accounts.program.clone(), cpi_accounts); solana_ibc::cpi::set_stake(cpi_ctx, validator_key, amount) } @@ -359,6 +363,8 @@ pub mod restaking { sender: ctx.accounts.withdrawer.to_account_info(), chain: chain.to_account_info(), trie: ctx.accounts.trie.to_account_info(), + #[cfg(feature = "witness")] + witness: ctx.accounts.witness.to_account_info(), system_program: ctx.accounts.system_program.to_account_info(), instruction: validation::check_instructions_sysvar( &ctx.accounts.instruction, @@ -550,7 +556,6 @@ pub mod restaking { ) -> Result<()> { let vault_params = &mut ctx.accounts.vault_params; let staking_params = &mut ctx.accounts.staking_params; - let guest_chain = &ctx.remaining_accounts[0]; let token_account = &ctx.accounts.receipt_token_account; if token_account.amount < 1 { @@ -561,6 +566,10 @@ pub mod restaking { Some(id) => id, None => return Err(error!(ErrorCodes::OperationNotAllowed)), }; + let remaining_accounts = validation::validate_remaining_accounts( + ctx.remaining_accounts, + &guest_chain_program_id, + )?; if vault_params.service.is_some() { return Err(error!(ErrorCodes::ServiceAlreadySet)); } @@ -577,7 +586,8 @@ pub mod restaking { let validator_key = match service { Service::GuestChain { validator } => validator, }; - let borrowed_chain_data = guest_chain.data.try_borrow().unwrap(); + let borrowed_chain_data = + remaining_accounts.chain.try_borrow_data().unwrap(); let mut chain_data: &[u8] = &borrowed_chain_data; let chain = solana_ibc::chain::ChainData::try_deserialize(&mut chain_data) @@ -593,15 +603,17 @@ pub mod restaking { let cpi_accounts = SetStake { sender: ctx.accounts.depositor.to_account_info(), - chain: guest_chain.to_account_info(), - trie: ctx.remaining_accounts[1].clone(), + chain: remaining_accounts.chain.clone(), + trie: remaining_accounts.trie.clone(), + #[cfg(feature = "witness")] + witness: remaining_accounts.witness.clone(), system_program: ctx.accounts.system_program.to_account_info(), instruction: validation::check_instructions_sysvar( &ctx.accounts.instruction, )?, }; - let cpi_program = ctx.remaining_accounts[2].clone(); - let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); + let cpi_ctx = + CpiContext::new(remaining_accounts.program.clone(), cpi_accounts); solana_ibc::cpi::set_stake(cpi_ctx, validator_key, amount) } @@ -865,6 +877,10 @@ pub struct Withdraw<'info> { #[account(mut, seeds = [TRIE_SEED], bump, seeds::program = guest_chain_program.key())] /// CHECK: pub trie: AccountInfo<'info>, + #[cfg(feature = "witness")] + #[account(mut, seeds = [solana_ibc::WITNESS_SEED, trie.key().as_ref()], bump)] + /// CHECK: + pub witness: AccountInfo<'info>, pub token_mint: Box>, #[account(mut, token::mint = token_mint)] diff --git a/solana/restaking/programs/restaking/src/validation.rs b/solana/restaking/programs/restaking/src/validation.rs index c0ed6d19..1e3a2d7e 100644 --- a/solana/restaking/programs/restaking/src/validation.rs +++ b/solana/restaking/programs/restaking/src/validation.rs @@ -1,8 +1,15 @@ use anchor_lang::prelude::*; -use solana_ibc::{CHAIN_SEED, TRIE_SEED}; use crate::ErrorCodes; +pub(crate) struct RemainingAccounts<'a, 'info> { + pub chain: &'a AccountInfo<'info>, + pub trie: &'a AccountInfo<'info>, + #[cfg(feature = "witness")] + pub witness: &'a AccountInfo<'info>, + pub program: &'a AccountInfo<'info>, +} + /// Validates accounts needed for CPI call to the guest chain. /// /// Right now, this method would only validate accounts for calling `set_stake` @@ -10,42 +17,85 @@ use crate::ErrorCodes; /// extend this method below to do the validation for those accounts as well. /// /// Accounts needed for calling `set_stake` -/// - chain: PDA with seeds ["chain"]. Should be writable -/// - trie: PDA with seeds ["trie"] +/// - chain: PDA with seeds ["chain"]. Must be writable. +/// - trie: PDA with seeds ["trie"]. Must be writable. +/// - witness: Only if compiled with `witness` Cargo feature. PDA with seeds +/// `["witness", trie.key()]`. Must be writable. /// - guest chain program ID: Should match the expected guest chain program ID /// /// Note: The accounts should be sent in above order. -pub(crate) fn validate_remaining_accounts( - accounts: &[AccountInfo<'_>], +pub(crate) fn validate_remaining_accounts<'a, 'info>( + accounts: &'a [AccountInfo<'info>], expected_guest_chain_program_id: &Pubkey, -) -> Result<()> { +) -> Result> { + let accounts = &mut accounts.iter(); + // Chain account - let seeds = [CHAIN_SEED]; - let seeds = seeds.as_ref(); + let chain = next_pda_account( + accounts, + [solana_ibc::CHAIN_SEED].as_ref(), + expected_guest_chain_program_id, + true, + "chain", + )?; - let (storage_account, _bump) = - Pubkey::find_program_address(seeds, expected_guest_chain_program_id); - if &storage_account != accounts[0].key && accounts[0].is_writable { - return Err(error!(ErrorCodes::AccountValidationFailedForCPI)); - } // Trie account - let seeds = [TRIE_SEED]; - let seeds = seeds.as_ref(); + let trie = next_pda_account( + accounts, + [solana_ibc::TRIE_SEED].as_ref(), + expected_guest_chain_program_id, + true, + "trie", + )?; - let (storage_account, _bump) = - Pubkey::find_program_address(seeds, expected_guest_chain_program_id); - if &storage_account != accounts[1].key && accounts[1].is_writable { - return Err(error!(ErrorCodes::AccountValidationFailedForCPI)); - } + // Trie account + #[cfg(feature = "witness")] + let witness = next_pda_account( + accounts, + [solana_ibc::WITNESS_SEED, trie.key().as_ref()].as_ref(), + expected_guest_chain_program_id, + true, + "witness", + )?; // Guest chain program ID - if expected_guest_chain_program_id != accounts[2].key { - return Err(error!(ErrorCodes::AccountValidationFailedForCPI)); - } + let program = next_account_info(accounts) + .ok() + .filter(|info| expected_guest_chain_program_id == info.key) + .ok_or_else(|| error!(ErrorCodes::AccountValidationFailedForCPI))?; - Ok(()) + Ok(RemainingAccounts { + chain, + trie, + program, + #[cfg(feature = "witness")] + witness, + }) } +fn next_pda_account<'a, 'info>( + accounts: &mut impl core::iter::Iterator>, + seeds: &[&[u8]], + program_id: &Pubkey, + must_be_mut: bool, + account_name: &str, +) -> Result<&'a AccountInfo<'info>> { + (|| { + let info = next_account_info(accounts).ok()?; + let addr = Pubkey::try_find_program_address(seeds, program_id)?.0; + if &addr == info.key && (!must_be_mut || info.is_writable) { + Some(info) + } else { + None + } + })() + .ok_or_else(|| { + error!(ErrorCodes::AccountValidationFailedForCPI) + .with_account_name(account_name) + }) +} + + /// Verifies that given account is the Instruction sysvars and returns it if it /// is. pub(crate) fn check_instructions_sysvar<'info>( diff --git a/solana/solana-ibc/programs/solana-ibc/Cargo.toml b/solana/solana-ibc/programs/solana-ibc/Cargo.toml index d9c228c6..d2013aea 100644 --- a/solana/solana-ibc/programs/solana-ibc/Cargo.toml +++ b/solana/solana-ibc/programs/solana-ibc/Cargo.toml @@ -10,6 +10,7 @@ name = "solana_ibc" [features] default = ["custom-entrypoint", "custom-heap"] +witness = [] cpi = ["no-entrypoint"] custom-heap = ["solana-allocator"] custom-entrypoint = ["custom-heap"] diff --git a/solana/solana-ibc/programs/solana-ibc/src/lib.rs b/solana/solana-ibc/programs/solana-ibc/src/lib.rs index 843e02bf..7c4caf91 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/lib.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/lib.rs @@ -21,6 +21,8 @@ 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"; +#[cfg(feature = "witness")] +pub const WITNESS_SEED: &[u8] = b"witness"; pub const MINT_ESCROW_SEED: &[u8] = b"mint_escrow"; pub const MINT: &[u8] = b"mint"; pub const ESCROW: &[u8] = b"escrow"; @@ -116,6 +118,8 @@ pub mod solana_ibc { ) -> Result<()> { let mut provable = storage::get_provable_from( &ctx.accounts.trie, + #[cfg(feature = "witness")] + &ctx.accounts.witness, &ctx.accounts.sender, )?; ctx.accounts.chain.initialise( @@ -137,6 +141,8 @@ pub mod solana_ibc { pub fn generate_block(ctx: Context) -> Result<()> { let provable = storage::get_provable_from( &ctx.accounts.trie, + #[cfg(feature = "witness")] + &ctx.accounts.witness, &ctx.accounts.sender, )?; ctx.accounts.chain.generate_block(&provable) @@ -161,6 +167,8 @@ pub mod solana_ibc { ) -> Result<()> { let provable = storage::get_provable_from( &ctx.accounts.trie, + #[cfg(feature = "witness")] + &ctx.accounts.witness, &ctx.accounts.sender, )?; let mut verifier = sigverify::Verifier::default(); @@ -193,6 +201,8 @@ pub mod solana_ibc { let chain = &mut ctx.accounts.chain; let provable = storage::get_provable_from( &ctx.accounts.trie, + #[cfg(feature = "witness")] + &ctx.accounts.witness, &ctx.accounts.sender, )?; chain.maybe_generate_block(&provable)?; @@ -216,6 +226,8 @@ pub mod solana_ibc { let chain = &mut ctx.accounts.chain; let provable = storage::get_provable_from( &ctx.accounts.trie, + #[cfg(feature = "witness")] + &ctx.accounts.witness, &ctx.accounts.sender, )?; chain.maybe_generate_block(&provable)?; @@ -599,6 +611,15 @@ pub struct Initialise<'info> { #[account(init, payer = sender, seeds = [TRIE_SEED], bump, space = 10240)] trie: UncheckedAccount<'info>, + /// The witness account holding trie’s state root. + /// + /// CHECK: Account’s owner and address is checked by + /// [`storage::get_provable_from`] function. + #[cfg(feature = "witness")] + #[account(init, payer = sender, space = 40, + seeds = [WITNESS_SEED, trie.key().as_ref()], bump)] + witness: UncheckedAccount<'info>, + system_program: Program<'info, System>, } @@ -618,6 +639,14 @@ pub struct Chain<'info> { #[account(mut, seeds = [TRIE_SEED], bump)] trie: UncheckedAccount<'info>, + /// The witness account holding trie’s state root. + /// + /// CHECK: Account’s owner and address is checked by + /// [`storage::get_provable_from`] function. + #[cfg(feature = "witness")] + #[account(mut, seeds = [WITNESS_SEED, trie.key().as_ref()], bump)] + witness: UncheckedAccount<'info>, + system_program: Program<'info, System>, } @@ -637,6 +666,14 @@ pub struct SetStake<'info> { #[account(mut, seeds = [TRIE_SEED], bump)] trie: UncheckedAccount<'info>, + /// The witness account holding trie’s state root. + /// + /// CHECK: Account’s owner and address is checked by + /// [`storage::get_provable_from`] function. + #[cfg(feature = "witness")] + #[account(mut, seeds = [WITNESS_SEED, trie.key().as_ref()], bump)] + witness: UncheckedAccount<'info>, + system_program: Program<'info, System>, #[account(address = solana_program::sysvar::instructions::ID)] @@ -661,6 +698,14 @@ pub struct ChainWithVerifier<'info> { #[account(mut, seeds = [TRIE_SEED], bump)] trie: UncheckedAccount<'info>, + /// The witness account holding trie’s state root. + /// + /// CHECK: Account’s owner and address is checked by + /// [`storage::get_provable_from`] function. + #[cfg(feature = "witness")] + #[account(mut, seeds = [WITNESS_SEED, trie.key().as_ref()], bump)] + witness: UncheckedAccount<'info>, + #[account(address = solana_program::sysvar::instructions::ID)] /// CHECK: ix_sysvar: AccountInfo<'info>, @@ -759,6 +804,14 @@ pub struct Deliver<'info> { #[account(mut, seeds = [TRIE_SEED], bump)] trie: UncheckedAccount<'info>, + /// The witness account holding trie’s state root. + /// + /// CHECK: Account’s owner and address is checked by + /// [`storage::get_provable_from`] function. + #[cfg(feature = "witness")] + #[account(mut, seeds = [WITNESS_SEED, trie.key().as_ref()], bump)] + witness: UncheckedAccount<'info>, + /// The guest blockchain data. #[account(mut, seeds = [CHAIN_SEED], bump)] chain: Box>, @@ -799,6 +852,14 @@ pub struct MockDeliver<'info> { #[account(mut, seeds = [TRIE_SEED], bump)] trie: UncheckedAccount<'info>, + /// The witness account holding trie’s state root. + /// + /// CHECK: Account’s owner and address is checked by + /// [`storage::get_provable_from`] function. + #[cfg(feature = "witness")] + #[account(mut, seeds = [WITNESS_SEED, trie.key().as_ref()], bump)] + witness: UncheckedAccount<'info>, + /// The guest blockchain data. #[account(mut, seeds = [CHAIN_SEED], bump)] chain: Account<'info, chain::ChainData>, @@ -826,6 +887,14 @@ pub struct SendTransfer<'info> { #[account(mut, seeds = [TRIE_SEED], bump)] trie: UncheckedAccount<'info>, + /// The witness account holding trie’s state root. + /// + /// CHECK: Account’s owner and address is checked by + /// [`storage::get_provable_from`] function. + #[cfg(feature = "witness")] + #[account(mut, seeds = [WITNESS_SEED, trie.key().as_ref()], bump)] + witness: UncheckedAccount<'info>, + /// The guest blockchain data. #[account(mut, seeds = [CHAIN_SEED], bump)] chain: Box>, @@ -892,6 +961,14 @@ pub struct UpdateConnectionDelay<'info> { /// function. #[account(mut, seeds = [TRIE_SEED], bump)] trie: UncheckedAccount<'info>, + + /// The witness account holding trie’s state root. + /// + /// CHECK: Account’s owner and address is checked by + /// [`storage::get_provable_from`] function. + #[cfg(feature = "witness")] + #[account(mut, seeds = [WITNESS_SEED, trie.key().as_ref()], bump)] + witness: UncheckedAccount<'info>, } impl ibc::Router for storage::IbcStorage<'_, '_> { diff --git a/solana/solana-ibc/programs/solana-ibc/src/mocks.rs b/solana/solana-ibc/programs/solana-ibc/src/mocks.rs index 53489f09..8021b44c 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/mocks.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/mocks.rs @@ -15,6 +15,8 @@ pub(crate) fn mock_deliver<'a, 'info>( private: &mut ctx.accounts.storage, provable: storage::get_provable_from( &ctx.accounts.trie, + #[cfg(feature = "witness")] + &ctx.accounts.witness, &ctx.accounts.sender, )?, chain: &mut ctx.accounts.chain, diff --git a/solana/solana-ibc/programs/solana-ibc/src/storage.rs b/solana/solana-ibc/programs/solana-ibc/src/storage.rs index 178f810f..712d80ba 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/storage.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/storage.rs @@ -404,20 +404,29 @@ pub type TrieAccount<'a, 'b> = /// account will never be shrunk. pub fn get_provable_from<'a, 'info>( info: &'a UncheckedAccount<'info>, + #[cfg(feature = "witness")] witness: &'a UncheckedAccount<'info>, payer: &'a Signer<'info>, ) -> Result> { - TrieAccount::from_account_with_payer(info, &crate::ID, payer).map_err( - |err| { - let bad_owner = matches!(err, ProgramError::InvalidAccountOwner); - let err = Error::from(err); - let err = if bad_owner { - err.with_pubkeys((*info.owner, crate::ID)) - } else { - err - }; - err.with_account_name("trie") - }, - ) + let make_err = |err, info: &'a AccountInfo<'info>, name| { + let bad_owner = matches!(err, ProgramError::InvalidAccountOwner); + let mut err = Error::from(err); + if bad_owner { + err = err.with_pubkeys((*info.owner, crate::ID)); + } + err.with_account_name(name) + }; + + #[allow(unused_mut)] + let mut trie = + TrieAccount::from_account_with_payer(info, &crate::ID, payer) + .map_err(|err| make_err(err, info, "trie"))?; + #[cfg(feature = "witness")] + { + trie = trie + .with_witness_account(witness, &crate::ID) + .map_err(|err| make_err(err, witness, "witness"))?; + } + Ok(trie) } /// Used for finding the account info from the keys. @@ -542,7 +551,11 @@ macro_rules! from_ctx { }}; ($ctx:expr, accounts = $accounts:expr) => {{ let provable = $crate::storage::get_provable_from( - &$ctx.accounts.trie, &$ctx.accounts.sender)?; + &$ctx.accounts.trie, + #[cfg(feature = "witness")] + &$ctx.accounts.witness, + &$ctx.accounts.sender, + )?; let chain = &mut $ctx.accounts.chain; // Before anything else, try generating a new guest block. However, if diff --git a/solana/solana-ibc/programs/solana-ibc/src/tests.rs b/solana/solana-ibc/programs/solana-ibc/src/tests.rs index 0f7ed436..0a7e7915 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/tests.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/tests.rs @@ -110,6 +110,12 @@ fn anchor_test_deliver() -> Result<()> { ) .0; let trie = Pubkey::find_program_address(&[crate::TRIE_SEED], &crate::ID).0; + #[cfg(feature = "witness")] + let witness = Pubkey::find_program_address( + &[crate::WITNESS_SEED, trie.as_ref()], + &crate::ID, + ) + .0; let chain = Pubkey::find_program_address(&[crate::CHAIN_SEED], &crate::ID).0; let fee_collector_pda = @@ -155,6 +161,8 @@ fn anchor_test_deliver() -> Result<()> { sender: authority.pubkey(), storage, trie, + #[cfg(feature = "witness")] + witness, chain, system_program: system_program::ID, }) @@ -251,6 +259,8 @@ fn anchor_test_deliver() -> Result<()> { receiver: None, storage, trie, + #[cfg(feature = "witness")] + witness, chain, system_program: system_program::ID, mint_authority: None, @@ -306,6 +316,8 @@ fn anchor_test_deliver() -> Result<()> { receiver: None, storage, trie, + #[cfg(feature = "witness")] + witness, chain, system_program: system_program::ID, mint_authority: None, @@ -343,6 +355,8 @@ fn anchor_test_deliver() -> Result<()> { sender: authority.pubkey(), storage, trie, + #[cfg(feature = "witness")] + witness, chain, system_program: system_program::ID, }) @@ -524,6 +538,8 @@ fn anchor_test_deliver() -> Result<()> { receiver: Some(receiver.pubkey()), storage, trie, + #[cfg(feature = "witness")] + witness, chain, system_program: system_program::ID, mint_authority: Some(mint_authority_key), @@ -609,6 +625,8 @@ fn anchor_test_deliver() -> Result<()> { receiver: Some(receiver.pubkey()), storage, trie, + #[cfg(feature = "witness")] + witness, chain, system_program: system_program::ID, mint_authority: Some(mint_authority_key), @@ -679,6 +697,8 @@ fn anchor_test_deliver() -> Result<()> { receiver: Some(authority.pubkey()), storage, trie, + #[cfg(feature = "witness")] + witness, chain, system_program: system_program::ID, mint_authority: Some(mint_authority_key), @@ -768,6 +788,8 @@ fn anchor_test_deliver() -> Result<()> { receiver: Some(receiver.pubkey()), storage, trie, + #[cfg(feature = "witness")] + witness, chain, system_program: system_program::ID, mint_authority: Some(mint_authority_key), diff --git a/validator/Cargo.toml b/validator/Cargo.toml index 6d048d0a..18273703 100644 --- a/validator/Cargo.toml +++ b/validator/Cargo.toml @@ -32,3 +32,6 @@ restaking.workspace = true solana-signature-verifier = { workspace = true, features = ["library"] } solana-ibc.workspace = true solana-trie.workspace = true + +[features] +witness = ["solana-ibc/witness"] diff --git a/validator/src/utils.rs b/validator/src/utils.rs index 280f08f9..6809c488 100644 --- a/validator/src/utils.rs +++ b/validator/src/utils.rs @@ -121,6 +121,13 @@ pub fn submit_call( max_retries: usize, priority_fees: &u64, ) -> Result { + #[cfg(feature = "witness")] + let witness = Pubkey::find_program_address( + [solana_ibc::WITNESS_SEED, trie.as_ref()].as_ref(), + &program.id(), + ) + .0; + let mut tx = Ok(signature); for tries in 0..max_retries { tx = program @@ -140,6 +147,8 @@ pub fn submit_call( sender: validator.pubkey(), chain, trie, + #[cfg(feature = "witness")] + witness, ix_sysvar: anchor_lang::solana_program::sysvar::instructions::ID, system_program: system_program::ID, @@ -168,6 +177,13 @@ pub fn submit_generate_block_call( max_retries: usize, priority_fees: &u64, ) -> Result { + #[cfg(feature = "witness")] + let witness = Pubkey::find_program_address( + [solana_ibc::WITNESS_SEED, trie.as_ref()].as_ref(), + &program.id(), + ) + .0; + let mut tx = Ok(Signature::new_unique()); for tries in 0..max_retries { tx = program @@ -182,6 +198,8 @@ pub fn submit_generate_block_call( sender: validator.pubkey(), chain, trie, + #[cfg(feature = "witness")] + witness, system_program: anchor_lang::system_program::ID, }) .args(instruction::GenerateBlock {}) From 0b51bab01cd73746632971d39c89e4f2b0df8d74 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Wed, 16 Oct 2024 15:36:06 +0200 Subject: [PATCH 2/5] fix clippy --- .../programs/solana-ibc/src/client_state.rs | 41 +++++++------------ .../solana-ibc/programs/solana-ibc/src/lib.rs | 2 +- .../programs/solana-ibc/src/storage.rs | 13 +++++- 3 files changed, 27 insertions(+), 29 deletions(-) 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 9b3970f0..bd02719e 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/client_state.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/client_state.rs @@ -1,5 +1,7 @@ use anchor_lang::prelude::borsh; use anchor_lang::prelude::borsh::maybestd::io; +use anchor_lang::solana_program::sysvar::clock::Clock; +use anchor_lang::solana_program::sysvar::Sysvar; use crate::consensus_state::AnyConsensusState; use crate::ibc; @@ -236,34 +238,21 @@ impl cf_guest::CommonContext type AnyConsensusState = AnyConsensusState; fn host_metadata(&self) -> Result<(ibc::Timestamp, ibc::Height)> { - #[cfg(feature = "witness")] - { - let clock = - anchor_lang::solana_program::sysvar::clock::Clock::get() - .map_err(|e| ibc::ClientError::ClientSpecific { - description: e.to_string(), - })?; - - let slot = clock.slot; - let timestamp_sec = clock.unix_timestamp as u64; - - let timestamp = - ibc::Timestamp::from_nanoseconds(timestamp_sec * 10u64.pow(9)) - .map_err(|e| ibc::ClientError::ClientSpecific { - description: e.to_string(), - })?; - let height = ibc::Height::new(1, slot)?; - return Ok((timestamp, height)); - } - let timestamp = self.borrow().chain.head()?.timestamp_ns.get(); - let timestamp = - ibc::Timestamp::from_nanoseconds(timestamp).map_err(|err| { - ibc::ClientError::Other { description: err.to_string() } + let (timestamp_ns, height) = if cfg!(feature = "witness") { + let clock = Clock::get().map_err(|e| { + ibc::ClientError::ClientSpecific { description: e.to_string() } + })?; + (clock.unix_timestamp as u64 * 10u64.pow(9), clock.slot) + } else { + self.borrow().chain.head().map(|head| { + (head.timestamp_ns.get(), head.block_height.into()) + })? + }; + let timestamp = ibc::Timestamp::from_nanoseconds(timestamp_ns) + .map_err(|e| ibc::ClientError::ClientSpecific { + description: e.to_string(), })?; - - let height = u64::from(self.borrow().chain.head()?.block_height); let height = ibc::Height::new(1, height)?; - Ok((timestamp, height)) } diff --git a/solana/solana-ibc/programs/solana-ibc/src/lib.rs b/solana/solana-ibc/programs/solana-ibc/src/lib.rs index 413cc86d..7790496d 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/lib.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/lib.rs @@ -422,7 +422,7 @@ pub mod solana_ibc { #[cfg(feature = "witness")] { - let root = store.borrow().provable.hash().clone(); + let root = *store.borrow().provable.hash(); if previous_root != root { msg!("Writing local consensus state"); let clock = Clock::get()?; diff --git a/solana/solana-ibc/programs/solana-ibc/src/storage.rs b/solana/solana-ibc/programs/solana-ibc/src/storage.rs index 21d50b61..1180f864 100644 --- a/solana/solana-ibc/programs/solana-ibc/src/storage.rs +++ b/solana/solana-ibc/programs/solana-ibc/src/storage.rs @@ -408,9 +408,18 @@ impl PrivateStorage { } } +#[cfg(not(feature = "witness"))] +type WitnessOptRef<'a> = (); + +#[cfg(feature = "witness")] +type WitnessOptRef<'a> = + Option>; + /// Provable storage, i.e. the trie, held in an account. -pub type TrieAccount<'a, 'b> = - solana_trie::TrieAccount>; +pub type TrieAccount<'a, 'b> = solana_trie::TrieAccount< + solana_trie::ResizableAccount<'a, 'b>, + WitnessOptRef<'a>, +>; /// Checks contents of given unchecked account and returns a trie if it’s valid. /// From f328082145242793c07800d681eb8b48c1deaf94 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Wed, 16 Oct 2024 15:37:29 +0200 Subject: [PATCH 3/5] cargo +nightly clippy --fix --- common/trie-geyser/src/api.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/common/trie-geyser/src/api.rs b/common/trie-geyser/src/api.rs index f722d8d0..376c25ec 100644 --- a/common/trie-geyser/src/api.rs +++ b/common/trie-geyser/src/api.rs @@ -52,18 +52,18 @@ fn test_slot_data_serialisation() { let mut proof = proof::MerkleProof::default(); let level = [ - CryptoHash::test(10).into(), - CryptoHash::test(11).into(), - CryptoHash::test(12).into(), + CryptoHash::test(10), + CryptoHash::test(11), + CryptoHash::test(12), ]; proof.push_level(&level, 1); let data = SlotData { delta_hash_proof: proof::DeltaHashProof { - parent_blockhash: CryptoHash::test(101).into(), - accounts_delta_hash: CryptoHash::test(102).into(), + parent_blockhash: CryptoHash::test(101), + accounts_delta_hash: CryptoHash::test(102), num_sigs: 103, - blockhash: CryptoHash::test(104).into(), + blockhash: CryptoHash::test(104), epoch_accounts_hash: None, }, witness_proof: proof::AccountProof { account_hash_data, proof }, From 07e79b5a94ea17b94d1b28b814b8d966e8423ce5 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Wed, 16 Oct 2024 15:40:07 +0200 Subject: [PATCH 4/5] fix fmt --- common/trie-geyser/src/api.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/common/trie-geyser/src/api.rs b/common/trie-geyser/src/api.rs index 376c25ec..61bd16f3 100644 --- a/common/trie-geyser/src/api.rs +++ b/common/trie-geyser/src/api.rs @@ -51,11 +51,8 @@ fn test_slot_data_serialisation() { ); let mut proof = proof::MerkleProof::default(); - let level = [ - CryptoHash::test(10), - CryptoHash::test(11), - CryptoHash::test(12), - ]; + let level = + [CryptoHash::test(10), CryptoHash::test(11), CryptoHash::test(12)]; proof.push_level(&level, 1); let data = SlotData { From c91047aeaf8b601505ca5dbe6942b74ada62ba64 Mon Sep 17 00:00:00 2001 From: dhruvja Date: Sat, 19 Oct 2024 02:16:17 +0530 Subject: [PATCH 5/5] fix CI by building withdraw ts ix without idl --- solana/restaking/tests/instructions.ts | 78 ++++++++++++++++---------- 1 file changed, 49 insertions(+), 29 deletions(-) diff --git a/solana/restaking/tests/instructions.ts b/solana/restaking/tests/instructions.ts index 788a7740..1f789c12 100644 --- a/solana/restaking/tests/instructions.ts +++ b/solana/restaking/tests/instructions.ts @@ -15,6 +15,7 @@ import { guestChainProgramID, restakingProgramID, } from "./helper"; +import { Transaction, TransactionInstruction } from "@solana/web3.js"; export const depositInstruction = async ( program: anchor.Program, @@ -159,36 +160,55 @@ export const withdrawInstruction = async ( withdrawer ); - const tx = await program.methods - .withdraw() - .preInstructions([ + let instruction = new TransactionInstruction({ + keys: [ + { pubkey: withdrawer, isSigner: true, isWritable: true }, + { pubkey: withdrawer, isSigner: false, isWritable: true }, + { pubkey: vaultParamsPDA, isSigner: false, isWritable: true }, + { pubkey: stakingParamsPDA, isSigner: false, isWritable: true }, + { pubkey: guestChainPDA, isSigner: false, isWritable: true }, + { pubkey: triePDA, isSigner: false, isWritable: true }, + { pubkey: stakedTokenMint, isSigner: false, isWritable: true }, + { + pubkey: withdrawerStakedTokenAccount, + isSigner: false, + isWritable: true, + }, + { pubkey: vaultTokenAccountPDA, isSigner: false, isWritable: true }, + { pubkey: receiptTokenMint, isSigner: false, isWritable: true }, + { pubkey: escrowReceiptTokenPDA, isSigner: false, isWritable: true }, + { pubkey: guestChainProgramID, isSigner: false, isWritable: true }, + { pubkey: spl.TOKEN_PROGRAM_ID, isSigner: false, isWritable: true }, + { + pubkey: anchor.web3.SystemProgram.programId, + isSigner: false, + isWritable: true, + }, + { + pubkey: new anchor.web3.PublicKey(mpl.MPL_TOKEN_METADATA_PROGRAM_ID), + isSigner: false, + isWritable: true, + }, + { pubkey: anchor.web3.SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: true }, + { pubkey: masterEditionPDA, isSigner: false, isWritable: true }, + { pubkey: nftMetadataPDA, isSigner: false, isWritable: true }, + { + pubkey: anchor.web3.SYSVAR_INSTRUCTIONS_PUBKEY, + isSigner: false, + isWritable: true, + }, + ], + programId: restakingProgramID, + data: Buffer.from([183, 18, 70, 156, 148, 109, 161, 34]), + }); + + let tx = new Transaction() + .add( anchor.web3.ComputeBudgetProgram.setComputeUnitLimit({ units: 1000000, - }), - ]) - .accounts({ - signer: withdrawer, - withdrawer, - vaultParams: vaultParamsPDA, - stakingParams: stakingParamsPDA, - guestChain: guestChainPDA, - trie: triePDA, - tokenMint: stakedTokenMint, - withdrawerTokenAccount: withdrawerStakedTokenAccount, - vaultTokenAccount: vaultTokenAccountPDA, - receiptTokenMint, - escrowReceiptTokenAccount: escrowReceiptTokenPDA, - guestChainProgram: guestChainProgramID, - tokenProgram: spl.TOKEN_PROGRAM_ID, - masterEditionAccount: masterEditionPDA, - nftMetadata: nftMetadataPDA, - systemProgram: anchor.web3.SystemProgram.programId, - metadataProgram: new anchor.web3.PublicKey( - mpl.MPL_TOKEN_METADATA_PROGRAM_ID - ), - instruction: anchor.web3.SYSVAR_INSTRUCTIONS_PUBKEY, - }) - .transaction(); + }) + ) + .add(instruction); return tx; }; @@ -315,7 +335,7 @@ export const setServiceInstruction = async ( validator: anchor.web3.PublicKey, receiptTokenMint: anchor.web3.PublicKey, /// Token which is staked - stakeTokenMint: anchor.web3.PublicKey, + stakeTokenMint: anchor.web3.PublicKey ) => { const { vaultParamsPDA } = getVaultParamsPDA(receiptTokenMint); const { stakingParamsPDA } = getStakingParamsPDA();