From 7d0e578b5d19e165262273b82d6d6b268385343f Mon Sep 17 00:00:00 2001 From: gcranju <075bct064.ranju@pcampus.edu.np> Date: Mon, 7 Oct 2024 09:03:12 +0545 Subject: [PATCH 1/7] fix: solana cluster connection init --- contracts/solana/Cargo.lock | 38 +++ .../programs/cluster-connection/Cargo.toml | 23 ++ .../programs/cluster-connection/Xargo.toml | 2 + .../cluster-connection/src/constants.rs | 1 + .../cluster-connection/src/contexts.rs | 289 ++++++++++++++++++ .../programs/cluster-connection/src/error.rs | 16 + .../programs/cluster-connection/src/event.rs | 10 + .../programs/cluster-connection/src/helper.rs | 197 ++++++++++++ .../src/instructions/mod.rs | 3 + .../src/instructions/query_accounts.rs | 194 ++++++++++++ .../programs/cluster-connection/src/lib.rs | 196 ++++++++++++ .../programs/cluster-connection/src/state.rs | 147 +++++++++ 12 files changed, 1116 insertions(+) create mode 100644 contracts/solana/programs/cluster-connection/Cargo.toml create mode 100644 contracts/solana/programs/cluster-connection/Xargo.toml create mode 100644 contracts/solana/programs/cluster-connection/src/constants.rs create mode 100644 contracts/solana/programs/cluster-connection/src/contexts.rs create mode 100644 contracts/solana/programs/cluster-connection/src/error.rs create mode 100644 contracts/solana/programs/cluster-connection/src/event.rs create mode 100644 contracts/solana/programs/cluster-connection/src/helper.rs create mode 100644 contracts/solana/programs/cluster-connection/src/instructions/mod.rs create mode 100644 contracts/solana/programs/cluster-connection/src/instructions/query_accounts.rs create mode 100644 contracts/solana/programs/cluster-connection/src/lib.rs create mode 100644 contracts/solana/programs/cluster-connection/src/state.rs diff --git a/contracts/solana/Cargo.lock b/contracts/solana/Cargo.lock index 8ce7dd91..719242c8 100644 --- a/contracts/solana/Cargo.lock +++ b/contracts/solana/Cargo.lock @@ -645,6 +645,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "cluster-connection" +version = "0.1.0" +dependencies = [ + "anchor-lang", + "ed25519-dalek", + "xcall-lib", +] + [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -776,6 +785,29 @@ dependencies = [ "subtle", ] +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "zeroize", +] + [[package]] name = "either" version = "1.13.0" @@ -1520,6 +1552,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + [[package]] name = "sized-chunks" version = "0.6.5" diff --git a/contracts/solana/programs/cluster-connection/Cargo.toml b/contracts/solana/programs/cluster-connection/Cargo.toml new file mode 100644 index 00000000..868dda7f --- /dev/null +++ b/contracts/solana/programs/cluster-connection/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "cluster-connection" +version = "0.1.0" +description = "Created with Anchor" +edition = "2021" + +[lib] +crate-type = ["cdylib", "lib"] +name = "cluster_connection" + +[features] +default = [] +cpi = ["no-entrypoint"] +no-entrypoint = [] +no-idl = [] +no-log-ix-name = [] +idl-build = ["anchor-lang/idl-build"] + +[dependencies] +anchor-lang = { workspace = true, features = ["init-if-needed"] } + +xcall-lib = { workspace = true } +ed25519-dalek = { version = "1.0.1" } diff --git a/contracts/solana/programs/cluster-connection/Xargo.toml b/contracts/solana/programs/cluster-connection/Xargo.toml new file mode 100644 index 00000000..475fb71e --- /dev/null +++ b/contracts/solana/programs/cluster-connection/Xargo.toml @@ -0,0 +1,2 @@ +[target.bpfel-unknown-unknown.dependencies.std] +features = [] diff --git a/contracts/solana/programs/cluster-connection/src/constants.rs b/contracts/solana/programs/cluster-connection/src/constants.rs new file mode 100644 index 00000000..fe8aad0a --- /dev/null +++ b/contracts/solana/programs/cluster-connection/src/constants.rs @@ -0,0 +1 @@ +pub const ACCOUNT_DISCRIMINATOR_SIZE: usize = 8; diff --git a/contracts/solana/programs/cluster-connection/src/contexts.rs b/contracts/solana/programs/cluster-connection/src/contexts.rs new file mode 100644 index 00000000..20b7115d --- /dev/null +++ b/contracts/solana/programs/cluster-connection/src/contexts.rs @@ -0,0 +1,289 @@ +use anchor_lang::prelude::*; + +use crate::{error::ConnectionError, state::*}; + +#[derive(Accounts)] +pub struct Initialize<'info> { + /// Rent payer + #[account(mut)] + pub signer: Signer<'info>, + + /// System Program: Required for creating the centralized-connection config + pub system_program: Program<'info, System>, + + /// Config + #[account( + init, + payer = signer, + seeds = [Config::SEED_PREFIX.as_bytes()], + bump, + space = Config::LEN + )] + pub config: Account<'info, Config>, + + #[account( + init, + payer = signer, + space = Authority::LEN, + seeds = [Authority::SEED_PREFIX.as_bytes()], + bump + )] + pub authority: Account<'info, Authority>, +} + +#[derive(Accounts)] +#[instruction(to: String)] +pub struct SendMessage<'info> { + #[account(mut)] + pub signer: Signer<'info>, + + pub system_program: Program<'info, System>, + + #[account( + owner = config.xcall @ ConnectionError::OnlyXcall + )] + pub xcall: Signer<'info>, + + #[account( + mut, + seeds = [Config::SEED_PREFIX.as_bytes()], + bump = config.bump, + )] + pub config: Account<'info, Config>, + + #[account( + seeds = [NetworkFee::SEED_PREFIX.as_bytes(), to.as_bytes()], + bump = network_fee.bump + )] + pub network_fee: Account<'info, NetworkFee>, +} + +#[derive(Accounts)] +#[instruction(src_network: String, conn_sn: u128)] +pub struct RecvMessage<'info> { + #[account(mut)] + pub admin: Signer<'info>, + + pub system_program: Program<'info, System>, + + /// Config + #[account( + mut, + seeds = [Config::SEED_PREFIX.as_bytes()], + bump = config.bump, + has_one = admin @ ConnectionError::OnlyAdmin, + )] + pub config: Account<'info, Config>, + + #[account( + init, + payer = admin, + seeds = [Receipt::SEED_PREFIX.as_bytes(), src_network.as_bytes(), &conn_sn.to_be_bytes()], + space = Receipt::LEN, + bump + )] + pub receipt: Account<'info, Receipt>, + + #[account( + seeds = [Authority::SEED_PREFIX.as_bytes()], + bump = authority.bump + )] + pub authority: Account<'info, Authority>, +} + +#[derive(Accounts)] +pub struct RevertMessage<'info> { + #[account(mut)] + pub admin: Signer<'info>, + + pub system_program: Program<'info, System>, + + /// Config + #[account( + mut, + seeds = [Config::SEED_PREFIX.as_bytes()], + bump = config.bump, + has_one = admin @ ConnectionError::OnlyAdmin, + )] + pub config: Account<'info, Config>, + + #[account( + seeds = [Authority::SEED_PREFIX.as_bytes()], + bump = authority.bump + )] + pub authority: Account<'info, Authority>, +} + +#[derive(Accounts)] +pub struct SetAdmin<'info> { + /// Transaction signer + #[account(mut)] + pub admin: Signer<'info>, + + /// Config + #[account( + mut, + seeds = [Config::SEED_PREFIX.as_bytes()], + bump = config.bump, + has_one = admin @ ConnectionError::OnlyAdmin, + )] + pub config: Account<'info, Config>, +} + +#[derive(Accounts)] +#[instruction(network_id: String)] +pub struct SetFee<'info> { + /// Rent payer + #[account(mut)] + pub admin: Signer<'info>, + + /// System Program: Required to create program-derived address + pub system_program: Program<'info, System>, + + /// Fee + #[account( + init_if_needed, + payer = admin, + seeds = [NetworkFee::SEED_PREFIX.as_bytes(), network_id.as_bytes()], + bump, + space = NetworkFee::LEN + )] + pub network_fee: Account<'info, NetworkFee>, + + /// Config + #[account( + mut, + seeds = [Config::SEED_PREFIX.as_bytes()], + bump = config.bump, + has_one = admin @ ConnectionError::OnlyAdmin, + )] + pub config: Account<'info, Config>, +} + +#[derive(Accounts)] +#[instruction(network_id: String)] +pub struct GetFee<'info> { + /// Fee + #[account( + seeds = [NetworkFee::SEED_PREFIX.as_bytes(), network_id.as_bytes()], + bump = network_fee.bump + )] + pub network_fee: Account<'info, NetworkFee>, +} + +#[derive(Accounts)] +pub struct ClaimFees<'info> { + /// Rent payer + #[account(mut)] + pub admin: Signer<'info>, + + /// Config + #[account( + mut, + seeds = [Config::SEED_PREFIX.as_bytes()], + bump = config.bump, + has_one = admin @ ConnectionError::OnlyAdmin, + )] + pub config: Account<'info, Config>, +} + +#[derive(Accounts)] +pub struct SetThreshold<'info> { + /// Transaction signer + #[account(mut)] + pub admin: Signer<'info>, + + /// Config + #[account( + mut, + seeds = [Config::SEED_PREFIX.as_bytes()], + bump = config.bump, + has_one = admin @ ConnectionError::OnlyAdmin, + )] + pub config: Account<'info, Config>, +} + +#[derive(Accounts)] +pub struct GetThreshold<'info> { + /// Config + #[account( + seeds = [Config::SEED_PREFIX.as_bytes()], + bump = config.bump + )] + pub config: Account<'info, Config>, +} + +#[derive(Accounts)] +pub struct AddValidator<'info> { + /// Transaction signer + #[account(mut)] + pub admin: Signer<'info>, + + /// Config + #[account( + mut, + seeds = [Config::SEED_PREFIX.as_bytes()], + bump = config.bump, + has_one = admin @ ConnectionError::OnlyAdmin, + )] + pub config: Account<'info, Config>, +} + +#[derive(Accounts)] +pub struct RemoveValidator<'info> { + /// Transaction signer + #[account(mut)] + pub admin: Signer<'info>, + + /// Config + #[account( + mut, + seeds = [Config::SEED_PREFIX.as_bytes()], + bump = config.bump, + has_one = admin @ ConnectionError::OnlyAdmin, + )] + pub config: Account<'info, Config>, +} + +#[derive(Accounts)] +pub struct GetValidators<'info> { + /// Config + #[account( + seeds = [Config::SEED_PREFIX.as_bytes()], + bump = config.bump + )] + pub config: Account<'info, Config>, +} + +#[derive(Accounts)] +#[instruction(src_network: String, conn_sn: u128)] +pub struct ReceiveMessageWithSignatures<'info> { + #[account(mut)] + pub admin: Signer<'info>, + + pub system_program: Program<'info, System>, + + /// Config + #[account( + mut, + seeds = [Config::SEED_PREFIX.as_bytes()], + bump = config.bump, + has_one = admin @ ConnectionError::OnlyAdmin, + )] + pub config: Account<'info, Config>, + + #[account( + init, + payer = admin, + seeds = [Receipt::SEED_PREFIX.as_bytes(), src_network.as_bytes(), &conn_sn.to_be_bytes()], + space = Receipt::LEN, + bump + )] + pub receipt: Account<'info, Receipt>, + + #[account( + seeds = [Authority::SEED_PREFIX.as_bytes()], + bump = authority.bump + )] + pub authority: Account<'info, Authority>, +} \ No newline at end of file diff --git a/contracts/solana/programs/cluster-connection/src/error.rs b/contracts/solana/programs/cluster-connection/src/error.rs new file mode 100644 index 00000000..eb27da60 --- /dev/null +++ b/contracts/solana/programs/cluster-connection/src/error.rs @@ -0,0 +1,16 @@ +use anchor_lang::prelude::*; + +#[error_code] +pub enum ConnectionError { + #[msg("Only admin")] + OnlyAdmin, + + #[msg("Only xcall")] + OnlyXcall, + + #[msg("Admin Validator Cnnot Be Removed")] + AdminValidatorCnnotBeRemoved, + + #[msg("Validators Must Be Greater Than Threshold")] + ValidatorsMustBeGreaterThanThreshold, +} diff --git a/contracts/solana/programs/cluster-connection/src/event.rs b/contracts/solana/programs/cluster-connection/src/event.rs new file mode 100644 index 00000000..e379a589 --- /dev/null +++ b/contracts/solana/programs/cluster-connection/src/event.rs @@ -0,0 +1,10 @@ +#![allow(non_snake_case)] + +use anchor_lang::prelude::*; + +#[event] +pub struct SendMessage { + pub targetNetwork: String, + pub connSn: u128, + pub msg: Vec, +} diff --git a/contracts/solana/programs/cluster-connection/src/helper.rs b/contracts/solana/programs/cluster-connection/src/helper.rs new file mode 100644 index 00000000..3c8774b3 --- /dev/null +++ b/contracts/solana/programs/cluster-connection/src/helper.rs @@ -0,0 +1,197 @@ +use anchor_lang::{ + prelude::*, + solana_program::{ + hash, + instruction::{AccountMeta,Instruction}, + program::{invoke, invoke_signed}, + sysvar::{recent_blockhashes::ID as SYSVAR_ID}, + ed25519_program, + system_instruction, + }, +}; + +use ed25519_dalek::{PublicKey as Ed25519PublicKey, Signature, Verifier}; +use crate::contexts::*; +use crate::state::*; + +use xcall_lib::xcall_type; + +pub fn transfer_lamports<'info>( + from: &AccountInfo<'info>, + to: &AccountInfo<'info>, + system_program: &AccountInfo<'info>, + amount: u64, +) -> Result<()> { + let ix = system_instruction::transfer(&from.key(), &to.key(), amount); + invoke( + &ix, + &[from.to_owned(), to.to_owned(), system_program.to_owned()], + )?; + + Ok(()) +} + +pub fn get_instruction_data(ix_name: &str, data: Vec) -> Vec { + let preimage = format!("{}:{}", "global", ix_name); + + let mut ix_discriminator = [0u8; 8]; + ix_discriminator.copy_from_slice(&hash::hash(preimage.as_bytes()).to_bytes()[..8]); + + let mut ix_data = Vec::new(); + ix_data.extend_from_slice(&ix_discriminator); + ix_data.extend_from_slice(&data); + + ix_data +} + +pub fn verify_message(signature: [u8; 64], message: Vec, pubkey: Pubkey) -> Result<()> { + // Recover the public key from the signature + + // let pubkey = Pubkey::from([ + // 57, 234, 243, 206, 86, 29, 102, 46, 179, 39, 246, 137, 159, 74, 167, 40, + // 69, 191, 199, 163, 68, 114, 221, 40, 45, 129, 56, 73, 87, 58, 119, 149 + // ]); + + let validator_pubkey = Ed25519PublicKey::from_bytes(&pubkey.to_bytes()).unwrap(); + + let signature = Signature::from_bytes(&signature).unwrap(); + + validator_pubkey + .verify(&message, &signature) + .unwrap(); + + + Ok(()) +} + +pub fn call_xcall_handle_message<'info>( + ctx: Context<'_, '_, '_, 'info, RecvMessage<'info>>, + from_nid: String, + message: Vec, + sequence_no: u128, +) -> Result<()> { + let mut data = vec![]; + let args = xcall_type::HandleMessageArgs { + from_nid, + message, + sequence_no, + }; + args.serialize(&mut data)?; + + let ix_data = get_instruction_data("handle_message", data); + + invoke_instruction( + ix_data, + &ctx.accounts.config, + &ctx.accounts.authority, + &ctx.accounts.admin, + &ctx.accounts.system_program, + ctx.remaining_accounts, + ) +} + +pub fn call_xcall_handle_message_with_signatures<'info>( + ctx: Context<'_, '_, '_, 'info, ReceiveMessageWithSignatures<'info>>, + from_nid: String, + message: Vec, + sequence_no: u128, + signatures: Vec<[u8; 64]>, +) -> Result<()> { + let mut data = vec![]; + let args = xcall_type::HandleMessageArgs { + from_nid, + message, + sequence_no, + }; + args.serialize(&mut data)?; + + let ix_data = get_instruction_data("handle_message", data); + + invoke_instruction( + ix_data, + &ctx.accounts.config, + &ctx.accounts.authority, + &ctx.accounts.admin, + &ctx.accounts.system_program, + ctx.remaining_accounts, + ) +} + +pub fn call_xcall_handle_error<'info>( + ctx: Context<'_, '_, '_, 'info, RevertMessage<'info>>, + sequence_no: u128, +) -> Result<()> { + let mut data = vec![]; + let args = xcall_type::HandleErrorArgs { sequence_no }; + args.serialize(&mut data)?; + + let ix_data = get_instruction_data("handle_error", data); + + invoke_instruction( + ix_data, + &ctx.accounts.config, + &ctx.accounts.authority, + &ctx.accounts.admin, + &ctx.accounts.system_program, + &ctx.remaining_accounts, + ) +} + +pub fn invoke_instruction<'info>( + ix_data: Vec, + config: &Account<'info, Config>, + authority: &Account<'info, Authority>, + admin: &Signer<'info>, + system_program: &Program<'info, System>, + remaining_accounts: &[AccountInfo<'info>], +) -> Result<()> { + let mut account_metas = vec![ + AccountMeta::new(admin.key(), true), + AccountMeta::new_readonly(authority.key(), true), + AccountMeta::new_readonly(system_program.key(), false), + ]; + let mut account_infos = vec![ + admin.to_account_info(), + authority.to_account_info(), + system_program.to_account_info(), + ]; + for i in remaining_accounts { + if i.is_writable { + account_metas.push(AccountMeta::new(i.key(), i.is_signer)); + } else { + account_metas.push(AccountMeta::new_readonly(i.key(), i.is_signer)) + } + account_infos.push(i.to_account_info()); + } + let ix = Instruction { + program_id: config.xcall, + accounts: account_metas, + data: ix_data.clone(), + }; + + invoke_signed( + &ix, + &account_infos, + &[&[Authority::SEED_PREFIX.as_bytes(), &[authority.bump]]], + )?; + + Ok(()) +} + +#[test] +fn test_verify_message() { + let message: Vec = b"Test message".to_vec(); + let result = verify_message([ + 73, 220, 38, 229, 202, 199, 238, 241, 69, 237, 194, + 226, 113, 76, 59, 167, 134, 83, 13, 213, 245, 151, + 26, 220, 196, 162, 247, 240, 95, 245, 78, 205, 26, + 233, 140, 177, 151, 207, 193, 23, 71, 96, 126, 115, + 200, 21, 108, 178, 48, 133, 117, 208, 27, 80, 237, + 93, 180, 97, 235, 40, 109, 36, 31, 11 + ], message.clone(), Pubkey::new(&[57, 234, 243, 206, 86, 29, 102, 46, 179, 39, 246, 137, 159, 74, 167, 40, + 69, 191, 199, 163, 68, 114, 221, 40, 45, 129, 56, 73, 87, 58, 119, 149] + )); + + // Step 6: Assert that the signature verification is successful + assert!(result.is_ok(), "Signature verification failed"); +} diff --git a/contracts/solana/programs/cluster-connection/src/instructions/mod.rs b/contracts/solana/programs/cluster-connection/src/instructions/mod.rs new file mode 100644 index 00000000..912ce532 --- /dev/null +++ b/contracts/solana/programs/cluster-connection/src/instructions/mod.rs @@ -0,0 +1,3 @@ +pub mod query_accounts; + +pub use query_accounts::*; diff --git a/contracts/solana/programs/cluster-connection/src/instructions/query_accounts.rs b/contracts/solana/programs/cluster-connection/src/instructions/query_accounts.rs new file mode 100644 index 00000000..7f149623 --- /dev/null +++ b/contracts/solana/programs/cluster-connection/src/instructions/query_accounts.rs @@ -0,0 +1,194 @@ +use anchor_lang::{ + prelude::*, + solana_program::{ + instruction::Instruction, + program::{get_return_data, invoke}, + system_program, + }, +}; +use xcall_lib::{ + query_account_type::{AccountMetadata, QueryAccountsPaginateResponse, QueryAccountsResponse}, + xcall_connection_type, + xcall_type::{self, QUERY_HANDLE_ERROR_ACCOUNTS_IX, QUERY_HANDLE_MESSAGE_ACCOUNTS_IX}, +}; + +use crate::{helper, id, state::*}; + +pub fn query_send_message_accounts<'info>( + ctx: Context<'_, '_, '_, 'info, QueryAccountsCtx<'info>>, + dst_network: String, +) -> Result { + let config = &ctx.accounts.config; + + let (network_fee, _) = Pubkey::find_program_address( + &[NetworkFee::SEED_PREFIX.as_bytes(), dst_network.as_bytes()], + &id(), + ); + + let account_metas = vec![ + AccountMetadata::new(config.key(), false), + AccountMetadata::new(network_fee, false), + ]; + + Ok(QueryAccountsResponse { + accounts: account_metas, + }) +} + +pub fn query_recv_message_accounts( + ctx: Context, + src_network: String, + conn_sn: u128, + msg: Vec, + sequence_no: u128, + page: u8, + limit: u8, +) -> Result { + let config = &ctx.accounts.config; + let (receipt, _) = Pubkey::find_program_address( + &[ + Receipt::SEED_PREFIX.as_bytes(), + src_network.as_bytes(), + &conn_sn.to_be_bytes(), + ], + &id(), + ); + let (authority, _) = Pubkey::find_program_address( + &[xcall_connection_type::CONNECTION_AUTHORITY_SEED.as_bytes()], + &id(), + ); + + let mut account_metas = vec![ + AccountMetadata::new(system_program::id(), false), + AccountMetadata::new(config.key(), false), + AccountMetadata::new(receipt, false), + AccountMetadata::new(authority, false), + ]; + + let mut xcall_account_metas = vec![]; + let mut xcall_account_infos = vec![]; + + for (_, account) in ctx.remaining_accounts.iter().enumerate() { + if account.is_writable { + xcall_account_metas.push(AccountMeta::new(account.key(), account.is_signer)); + } else { + xcall_account_metas.push(AccountMeta::new_readonly(account.key(), account.is_signer)); + } + + xcall_account_infos.push(account.to_account_info()) + } + + let ix_data = get_handle_message_ix_data(src_network, msg, sequence_no)?; + + let ix = Instruction { + program_id: config.xcall, + accounts: xcall_account_metas, + data: ix_data, + }; + + invoke(&ix, &xcall_account_infos)?; + + let (_, data) = get_return_data().unwrap(); + let mut data_slice: &[u8] = &data; + let res = QueryAccountsResponse::deserialize(&mut data_slice)?; + let mut res_accounts = res.accounts; + + account_metas.append(&mut res_accounts); + + Ok(QueryAccountsPaginateResponse::new( + account_metas, + page, + limit, + )) +} + +pub fn query_revert_message_accounts( + ctx: Context, + sequence_no: u128, + page: u8, + limit: u8, +) -> Result { + let config = &ctx.accounts.config; + let (authority, _) = Pubkey::find_program_address( + &[xcall_connection_type::CONNECTION_AUTHORITY_SEED.as_bytes()], + &id(), + ); + + let mut account_metas = vec![ + AccountMetadata::new(system_program::id(), false), + AccountMetadata::new(config.key(), false), + AccountMetadata::new(authority, false), + ]; + + let mut xcall_account_metas = vec![]; + let mut xcall_account_infos = vec![]; + + for (_, account) in ctx.remaining_accounts.iter().enumerate() { + if account.is_writable { + xcall_account_metas.push(AccountMeta::new(account.key(), account.is_signer)); + } else { + xcall_account_metas.push(AccountMeta::new_readonly(account.key(), account.is_signer)); + } + + xcall_account_infos.push(account.to_account_info()) + } + + let ix_data = get_handle_error_ix_data(sequence_no)?; + + let ix = Instruction { + program_id: config.xcall, + accounts: xcall_account_metas, + data: ix_data, + }; + + invoke(&ix, &xcall_account_infos)?; + + let (_, data) = get_return_data().unwrap(); + let mut data_slice: &[u8] = &data; + let res = QueryAccountsResponse::deserialize(&mut data_slice)?; + let mut res_accounts = res.accounts; + + account_metas.append(&mut res_accounts); + account_metas.push(AccountMetadata::new(config.xcall, false)); + + Ok(QueryAccountsPaginateResponse::new( + account_metas, + page, + limit, + )) +} + +pub fn get_handle_error_ix_data(sequence_no: u128) -> Result> { + let mut ix_args_data = vec![]; + let ix_args = xcall_type::HandleErrorArgs { sequence_no }; + ix_args.serialize(&mut ix_args_data)?; + + let ix_data = helper::get_instruction_data(QUERY_HANDLE_ERROR_ACCOUNTS_IX, ix_args_data); + Ok(ix_data) +} + +pub fn get_handle_message_ix_data( + from_nid: String, + message: Vec, + sequence_no: u128, +) -> Result> { + let mut ix_args_data = vec![]; + let ix_args = xcall_type::HandleMessageArgs { + from_nid, + message, + sequence_no, + }; + ix_args.serialize(&mut ix_args_data)?; + + let ix_data = helper::get_instruction_data(QUERY_HANDLE_MESSAGE_ACCOUNTS_IX, ix_args_data); + Ok(ix_data) +} + +#[derive(Accounts)] +pub struct QueryAccountsCtx<'info> { + #[account( + seeds = [Config::SEED_PREFIX.as_bytes()], + bump = config.bump, + )] + pub config: Account<'info, Config>, +} diff --git a/contracts/solana/programs/cluster-connection/src/lib.rs b/contracts/solana/programs/cluster-connection/src/lib.rs new file mode 100644 index 00000000..e9d737b7 --- /dev/null +++ b/contracts/solana/programs/cluster-connection/src/lib.rs @@ -0,0 +1,196 @@ +use std::ops::DerefMut; + +use anchor_lang::prelude::*; + +pub mod constants; +pub mod contexts; +pub mod error; +pub mod event; +pub mod helper; +pub mod instructions; +pub mod state; + +use contexts::*; +use instructions::*; +use state::*; + +use xcall_lib::query_account_type::{QueryAccountsPaginateResponse, QueryAccountsResponse}; + +declare_id!("8oxnXrSmqWJqkb2spZk2uz1cegzPsLy6nJp9XwFhkMD5"); + +#[program] +pub mod centralized_connection { + use super::*; + + pub fn initialize(ctx: Context, xcall: Pubkey, admin: Pubkey) -> Result<()> { + ctx.accounts + .config + .set_inner(Config::new(xcall, admin, ctx.bumps.config)); + ctx.accounts + .authority + .set_inner(Authority::new(ctx.bumps.authority)); + + Ok(()) + } + + pub fn send_message( + ctx: Context, + to: String, + sn: i64, + msg: Vec, + ) -> Result<()> { + let next_conn_sn = ctx.accounts.config.get_next_conn_sn()?; + + let mut fee = 0; + if sn >= 0 { + fee = ctx.accounts.network_fee.get(sn > 0)?; + } + + if fee > 0 { + helper::transfer_lamports( + &ctx.accounts.signer, + &ctx.accounts.config.to_account_info(), + &ctx.accounts.system_program, + fee, + )? + } + + emit!(event::SendMessage { + targetNetwork: to, + connSn: next_conn_sn, + msg: msg + }); + + Ok(()) + } + + #[allow(unused_variables)] + pub fn recv_message<'info>( + ctx: Context<'_, '_, '_, 'info, RecvMessage<'info>>, + src_network: String, + conn_sn: u128, + msg: Vec, + sequence_no: u128, + ) -> Result<()> { + helper::call_xcall_handle_message(ctx, src_network, msg, sequence_no) + } + + #[allow(unused_variables)] + pub fn receive_message_with_signatures<'info>( + ctx: Context<'_, '_, '_, 'info, ReceiveMessageWithSignatures<'info>>, + src_network: String, + conn_sn: u128, + msg: Vec, + sequence_no: u128, + signatures: Vec<[u8; 64]>, + ) -> Result<()> { + helper::call_xcall_handle_message_with_signatures(ctx, src_network, msg, sequence_no,signatures) + } + + pub fn revert_message<'info>( + ctx: Context<'_, '_, '_, 'info, RevertMessage<'info>>, + sequence_no: u128, + ) -> Result<()> { + helper::call_xcall_handle_error(ctx, sequence_no) + } + + pub fn set_admin(ctx: Context, account: Pubkey) -> Result<()> { + let config = ctx.accounts.config.deref_mut(); + config.admin = account; + + Ok(()) + } + + #[allow(unused_variables)] + pub fn set_fee( + ctx: Context, + network_id: String, + message_fee: u64, + response_fee: u64, + ) -> Result<()> { + ctx.accounts.network_fee.set_inner(NetworkFee::new( + message_fee, + response_fee, + ctx.bumps.network_fee, + )); + + Ok(()) + } + + pub fn set_threshold(ctx: Context, threshold: u8) -> Result<()> { + ctx.accounts.config.set_threshold(threshold); + Ok(()) + } + + pub fn add_validator(ctx: Context, validator: Pubkey) -> Result<()> { + ctx.accounts.config.add_validator(validator); + Ok(()) + } + + pub fn remove_validator(ctx: Context, validator: Pubkey) -> Result<()> { + ctx.accounts.config.remove_validator(validator); + Ok(()) + } + + pub fn get_validators(ctx: Context) -> Result> { + Ok(ctx.accounts.config.get_validators()) + } + + pub fn get_threshold(ctx: Context) -> Result { + Ok(ctx.accounts.config.get_threshold()?) + } + + #[allow(unused_variables)] + pub fn get_fee(ctx: Context, network_id: String, response: bool) -> Result { + ctx.accounts.network_fee.get(response) + } + + pub fn claim_fees(ctx: Context) -> Result<()> { + let config = ctx.accounts.config.to_account_info(); + let fee = ctx.accounts.config.get_claimable_fees(&config)?; + + **config.try_borrow_mut_lamports()? -= fee; + **ctx.accounts.admin.try_borrow_mut_lamports()? += fee; + + Ok(()) + } + + #[allow(unused_variables)] + pub fn query_send_message_accounts<'info>( + ctx: Context<'_, '_, '_, 'info, QueryAccountsCtx<'info>>, + to: String, + sn: i64, + msg: Vec, + ) -> Result { + instructions::query_send_message_accounts(ctx, to) + } + + pub fn query_recv_message_accounts<'info>( + ctx: Context<'_, '_, '_, 'info, QueryAccountsCtx<'info>>, + src_network: String, + conn_sn: u128, + msg: Vec, + sequence_no: u128, + page: u8, + limit: u8, + ) -> Result { + instructions::query_recv_message_accounts( + ctx, + src_network, + conn_sn, + msg, + sequence_no, + page, + limit, + ) + } + + pub fn query_revert_message_accounts<'info>( + ctx: Context<'_, '_, '_, 'info, QueryAccountsCtx<'info>>, + sequence_no: u128, + page: u8, + limit: u8, + ) -> Result { + instructions::query_revert_message_accounts(ctx, sequence_no, page, limit) + } +} diff --git a/contracts/solana/programs/cluster-connection/src/state.rs b/contracts/solana/programs/cluster-connection/src/state.rs new file mode 100644 index 00000000..c44bfb90 --- /dev/null +++ b/contracts/solana/programs/cluster-connection/src/state.rs @@ -0,0 +1,147 @@ +use anchor_lang::prelude::*; +use xcall_lib::xcall_connection_type; + +use crate::{constants, error::*}; + +/// The `Config` state of the centralized connection - the inner data of the +/// program-derived address +#[account] +pub struct Config { + pub admin: Pubkey, + pub xcall: Pubkey, + pub validators: Vec, + pub threshold: u8, + pub sn: u128, + pub bump: u8, +} + +impl Config { + /// The Config seed phrase to derive it's program-derived address + pub const SEED_PREFIX: &'static str = "config"; + + /// Account discriminator + Xcall public key + Admin public key + connection + /// sequence + bump + pub const LEN: usize = constants::ACCOUNT_DISCRIMINATOR_SIZE + 32 + 32 + 16 + 1 + 1; + + /// Creates a new centralized connection `Config` state + pub fn new(xcall: Pubkey, admin: Pubkey, bump: u8) -> Self { + Self { + xcall, + admin, + validators: Vec::new(), + threshold: 0, + sn: 0, + bump, + } + } + + /// It throws error if `signer` is not an admin account + pub fn ensure_admin(&self, signer: Pubkey) -> Result<()> { + if self.admin != signer { + return Err(ConnectionError::OnlyAdmin.into()); + } + Ok(()) + } + + /// It throws error if `address` is not an xcall account + pub fn ensure_xcall(&self, address: Pubkey) -> Result<()> { + if self.xcall != address { + return Err(ConnectionError::OnlyXcall.into()); + } + Ok(()) + } + + pub fn get_next_conn_sn(&mut self) -> Result { + self.sn += 1; + Ok(self.sn) + } + + pub fn get_claimable_fees(&self, account: &AccountInfo) -> Result { + let rent = Rent::default(); + let rent_exempt_balance = rent.minimum_balance(Config::LEN); + + Ok(account.lamports() - rent_exempt_balance) + } + + pub fn get_threshold(&self) -> Result { + Ok(self.threshold) + } + + pub fn set_threshold(&mut self, threshold: u8) { + self.threshold = threshold + } + + pub fn add_validator(&mut self, validator: Pubkey) { + self.validators.push(validator); + } + + pub fn remove_validator(&mut self, validator: Pubkey) { + if self.admin == validator { + return; + } + if self.validators.len() < self.threshold as usize { + return; + } + self.validators.retain(|x| *x != validator); + } + + pub fn get_validators(&self) -> Vec { + self.validators.clone() + } +} + +#[account] +pub struct NetworkFee { + pub message_fee: u64, + pub response_fee: u64, + pub bump: u8, +} + +impl NetworkFee { + /// The Fee seed phrase to derive it's program-derived address + pub const SEED_PREFIX: &'static str = "fee"; + + /// Account discriminator + Message fee + Response fee + bump + pub const LEN: usize = constants::ACCOUNT_DISCRIMINATOR_SIZE + 8 + 8 + 1; + + /// Creates a new `Fee` state for a network_id + pub fn new(message_fee: u64, response_fee: u64, bump: u8) -> Self { + Self { + message_fee, + response_fee, + bump, + } + } + + pub fn get(&self, response: bool) -> Result { + let mut fee = self.message_fee; + if response { + fee += self.response_fee + } + + Ok(fee) + } +} + +#[account] +pub struct Receipt {} + +impl Receipt { + pub const SEED_PREFIX: &'static str = "receipt"; + + pub const LEN: usize = constants::ACCOUNT_DISCRIMINATOR_SIZE; +} + +#[account] +pub struct Authority { + pub bump: u8, +} + +impl Authority { + pub const SEED_PREFIX: &'static str = xcall_connection_type::CONNECTION_AUTHORITY_SEED; + pub const LEN: usize = 8 + 1; + + pub fn new(bump: u8) -> Self { + Self { bump } + } +} From 8da68a8ec2621f1158b4d010a3f1ba6ec3c5ee7a Mon Sep 17 00:00:00 2001 From: Itshyphen <075bct064.ranju@pcampus.edu.np> Date: Fri, 25 Oct 2024 14:21:14 +0545 Subject: [PATCH 2/7] fix: solana cluster connection --- .../cluster-connection/src/contexts.rs | 15 --- .../programs/cluster-connection/src/helper.rs | 99 +++++++------------ .../programs/cluster-connection/src/lib.rs | 37 +++---- .../programs/cluster-connection/src/state.rs | 20 ++-- 4 files changed, 60 insertions(+), 111 deletions(-) diff --git a/contracts/solana/programs/cluster-connection/src/contexts.rs b/contracts/solana/programs/cluster-connection/src/contexts.rs index 20b7115d..fd3bffd9 100644 --- a/contracts/solana/programs/cluster-connection/src/contexts.rs +++ b/contracts/solana/programs/cluster-connection/src/contexts.rs @@ -229,21 +229,6 @@ pub struct AddValidator<'info> { pub config: Account<'info, Config>, } -#[derive(Accounts)] -pub struct RemoveValidator<'info> { - /// Transaction signer - #[account(mut)] - pub admin: Signer<'info>, - - /// Config - #[account( - mut, - seeds = [Config::SEED_PREFIX.as_bytes()], - bump = config.bump, - has_one = admin @ ConnectionError::OnlyAdmin, - )] - pub config: Account<'info, Config>, -} #[derive(Accounts)] pub struct GetValidators<'info> { diff --git a/contracts/solana/programs/cluster-connection/src/helper.rs b/contracts/solana/programs/cluster-connection/src/helper.rs index 3c8774b3..ff55fa59 100644 --- a/contracts/solana/programs/cluster-connection/src/helper.rs +++ b/contracts/solana/programs/cluster-connection/src/helper.rs @@ -1,18 +1,13 @@ use anchor_lang::{ prelude::*, solana_program::{ - hash, - instruction::{AccountMeta,Instruction}, - program::{invoke, invoke_signed}, - sysvar::{recent_blockhashes::ID as SYSVAR_ID}, - ed25519_program, - system_instruction, + hash, instruction::{AccountMeta,Instruction}, keccak::hashv, program::{invoke, invoke_signed}, secp256k1_recover::{secp256k1_recover, Secp256k1Pubkey}, system_instruction, sysvar::recent_blockhashes::ID as SYSVAR_ID }, }; -use ed25519_dalek::{PublicKey as Ed25519PublicKey, Signature, Verifier}; use crate::contexts::*; use crate::state::*; +use crate::error::*; use xcall_lib::xcall_type; @@ -44,60 +39,51 @@ pub fn get_instruction_data(ix_name: &str, data: Vec) -> Vec { ix_data } -pub fn verify_message(signature: [u8; 64], message: Vec, pubkey: Pubkey) -> Result<()> { - // Recover the public key from the signature +pub fn get_message_hash(from_nid: &String, connection_sn: &u128, message: &Vec) -> [u8; 32] { + let mut result = from_nid.as_bytes().to_vec(); + result.extend_from_slice(&connection_sn.to_le_bytes()); + result.extend_from_slice(message); - // let pubkey = Pubkey::from([ - // 57, 234, 243, 206, 86, 29, 102, 46, 179, 39, 246, 137, 159, 74, 167, 40, - // 69, 191, 199, 163, 68, 114, 221, 40, 45, 129, 56, 73, 87, 58, 119, 149 - // ]); - - let validator_pubkey = Ed25519PublicKey::from_bytes(&pubkey.to_bytes()).unwrap(); - - let signature = Signature::from_bytes(&signature).unwrap(); - - validator_pubkey - .verify(&message, &signature) - .unwrap(); - + let hash = hashv(&[&result]); + hash.0 +} - Ok(()) +pub fn recover_pubkey(message: [u8; 32], sig: [u8; 65]) -> Pubkey { + let recovery_key = sig[64]; + let signature = &sig[0..64]; + let recovered_pubkey = secp256k1_recover(&message, recovery_key, signature).unwrap_or( + Secp256k1Pubkey::new(&[0u8; 64]),); + let pubkey_bytes: [u8; 64] = recovered_pubkey.to_bytes(); + let mut solana_pubkey_bytes = [0u8; 32]; + solana_pubkey_bytes.copy_from_slice(&pubkey_bytes[..32]); + Pubkey::new_from_array(solana_pubkey_bytes) } -pub fn call_xcall_handle_message<'info>( - ctx: Context<'_, '_, '_, 'info, RecvMessage<'info>>, - from_nid: String, - message: Vec, - sequence_no: u128, -) -> Result<()> { - let mut data = vec![]; - let args = xcall_type::HandleMessageArgs { - from_nid, - message, - sequence_no, - }; - args.serialize(&mut data)?; - let ix_data = get_instruction_data("handle_message", data); - - invoke_instruction( - ix_data, - &ctx.accounts.config, - &ctx.accounts.authority, - &ctx.accounts.admin, - &ctx.accounts.system_program, - ctx.remaining_accounts, - ) -} pub fn call_xcall_handle_message_with_signatures<'info>( ctx: Context<'_, '_, '_, 'info, ReceiveMessageWithSignatures<'info>>, from_nid: String, message: Vec, + connection_sn: u128, sequence_no: u128, - signatures: Vec<[u8; 64]>, + signatures: Vec<[u8; 65]>, ) -> Result<()> { let mut data = vec![]; + + let message_hash = get_message_hash(&from_nid, &connection_sn, &message); + let mut unique_validators = Vec::new(); + for sig in signatures { + let pubkey = recover_pubkey(message_hash, sig); + if ctx.accounts.config.is_validator(&pubkey) { + unique_validators.push(pubkey); + } + } + + if (unique_validators.len() as u8) < ctx.accounts.config.get_threshold() { + return Err(ConnectionError::ValidatorsMustBeGreaterThanThreshold.into()); + } + let args = xcall_type::HandleMessageArgs { from_nid, message, @@ -178,20 +164,3 @@ pub fn invoke_instruction<'info>( Ok(()) } -#[test] -fn test_verify_message() { - let message: Vec = b"Test message".to_vec(); - let result = verify_message([ - 73, 220, 38, 229, 202, 199, 238, 241, 69, 237, 194, - 226, 113, 76, 59, 167, 134, 83, 13, 213, 245, 151, - 26, 220, 196, 162, 247, 240, 95, 245, 78, 205, 26, - 233, 140, 177, 151, 207, 193, 23, 71, 96, 126, 115, - 200, 21, 108, 178, 48, 133, 117, 208, 27, 80, 237, - 93, 180, 97, 235, 40, 109, 36, 31, 11 - ], message.clone(), Pubkey::new(&[57, 234, 243, 206, 86, 29, 102, 46, 179, 39, 246, 137, 159, 74, 167, 40, - 69, 191, 199, 163, 68, 114, 221, 40, 45, 129, 56, 73, 87, 58, 119, 149] - )); - - // Step 6: Assert that the signature verification is successful - assert!(result.is_ok(), "Signature verification failed"); -} diff --git a/contracts/solana/programs/cluster-connection/src/lib.rs b/contracts/solana/programs/cluster-connection/src/lib.rs index e9d737b7..9607e849 100644 --- a/contracts/solana/programs/cluster-connection/src/lib.rs +++ b/contracts/solana/programs/cluster-connection/src/lib.rs @@ -64,27 +64,17 @@ pub mod centralized_connection { Ok(()) } - #[allow(unused_variables)] - pub fn recv_message<'info>( - ctx: Context<'_, '_, '_, 'info, RecvMessage<'info>>, - src_network: String, - conn_sn: u128, - msg: Vec, - sequence_no: u128, - ) -> Result<()> { - helper::call_xcall_handle_message(ctx, src_network, msg, sequence_no) - } #[allow(unused_variables)] - pub fn receive_message_with_signatures<'info>( + pub fn receive_message<'info>( ctx: Context<'_, '_, '_, 'info, ReceiveMessageWithSignatures<'info>>, src_network: String, conn_sn: u128, msg: Vec, sequence_no: u128, - signatures: Vec<[u8; 64]>, + signatures: Vec<[u8; 65]>, ) -> Result<()> { - helper::call_xcall_handle_message_with_signatures(ctx, src_network, msg, sequence_no,signatures) + helper::call_xcall_handle_message_with_signatures(ctx, src_network, msg, conn_sn, sequence_no,signatures) } pub fn revert_message<'info>( @@ -118,17 +108,22 @@ pub mod centralized_connection { } pub fn set_threshold(ctx: Context, threshold: u8) -> Result<()> { + if ctx.accounts.config.get_validators().len() < threshold as usize { + return Err(error::ConnectionError::ValidatorsMustBeGreaterThanThreshold.into()); + } ctx.accounts.config.set_threshold(threshold); Ok(()) } - pub fn add_validator(ctx: Context, validator: Pubkey) -> Result<()> { - ctx.accounts.config.add_validator(validator); - Ok(()) - } - - pub fn remove_validator(ctx: Context, validator: Pubkey) -> Result<()> { - ctx.accounts.config.remove_validator(validator); + pub fn update_validators(ctx: Context, validators: Vec, threshold: u8) -> Result<()> { + let mut unique_validators = validators.clone(); + unique_validators.sort(); + unique_validators.dedup(); + if unique_validators.len() < threshold as usize { + return Err(error::ConnectionError::ValidatorsMustBeGreaterThanThreshold.into()); + } + ctx.accounts.config.set_threshold(threshold); + ctx.accounts.config.store_validators(unique_validators); Ok(()) } @@ -137,7 +132,7 @@ pub mod centralized_connection { } pub fn get_threshold(ctx: Context) -> Result { - Ok(ctx.accounts.config.get_threshold()?) + Ok(ctx.accounts.config.get_threshold()) } #[allow(unused_variables)] diff --git a/contracts/solana/programs/cluster-connection/src/state.rs b/contracts/solana/programs/cluster-connection/src/state.rs index c44bfb90..f2d333a2 100644 --- a/contracts/solana/programs/cluster-connection/src/state.rs +++ b/contracts/solana/programs/cluster-connection/src/state.rs @@ -63,8 +63,8 @@ impl Config { Ok(account.lamports() - rent_exempt_balance) } - pub fn get_threshold(&self) -> Result { - Ok(self.threshold) + pub fn get_threshold(&self) -> u8 { + self.threshold } pub fn set_threshold(&mut self, threshold: u8) { @@ -75,16 +75,16 @@ impl Config { self.validators.push(validator); } - pub fn remove_validator(&mut self, validator: Pubkey) { - if self.admin == validator { - return; - } - if self.validators.len() < self.threshold as usize { - return; - } - self.validators.retain(|x| *x != validator); + pub fn store_validators(&mut self, validators: Vec) { + self.validators = validators + } + + pub fn is_validator(&self, address: &Pubkey) -> bool { + self.validators.contains(address) } + + pub fn get_validators(&self) -> Vec { self.validators.clone() } From d464ddef8b5bed7868bdd93afe66ba9a81d8bf95 Mon Sep 17 00:00:00 2001 From: gcranju Date: Mon, 9 Dec 2024 11:56:53 +0545 Subject: [PATCH 3/7] fix: solana cluster spec update --- contracts/solana/Cargo.lock | 31 +------ .../programs/cluster-connection/Cargo.toml | 2 +- .../cluster-connection/src/contexts.rs | 84 +----------------- .../programs/cluster-connection/src/error.rs | 3 + .../programs/cluster-connection/src/helper.rs | 85 +++++++++++++++---- .../programs/cluster-connection/src/lib.rs | 37 ++++---- .../programs/cluster-connection/src/state.rs | 35 ++------ 7 files changed, 106 insertions(+), 171 deletions(-) diff --git a/contracts/solana/Cargo.lock b/contracts/solana/Cargo.lock index 719242c8..292f1a07 100644 --- a/contracts/solana/Cargo.lock +++ b/contracts/solana/Cargo.lock @@ -650,7 +650,7 @@ name = "cluster-connection" version = "0.1.0" dependencies = [ "anchor-lang", - "ed25519-dalek", + "rlp", "xcall-lib", ] @@ -785,29 +785,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "ed25519" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" -dependencies = [ - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" -dependencies = [ - "curve25519-dalek", - "ed25519", - "rand 0.7.3", - "serde", - "sha2 0.9.9", - "zeroize", -] - [[package]] name = "either" version = "1.13.0" @@ -1552,12 +1529,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "signature" -version = "1.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" - [[package]] name = "sized-chunks" version = "0.6.5" diff --git a/contracts/solana/programs/cluster-connection/Cargo.toml b/contracts/solana/programs/cluster-connection/Cargo.toml index 868dda7f..d468b7f8 100644 --- a/contracts/solana/programs/cluster-connection/Cargo.toml +++ b/contracts/solana/programs/cluster-connection/Cargo.toml @@ -20,4 +20,4 @@ idl-build = ["anchor-lang/idl-build"] anchor-lang = { workspace = true, features = ["init-if-needed"] } xcall-lib = { workspace = true } -ed25519-dalek = { version = "1.0.1" } +rlp = { workspace = true } diff --git a/contracts/solana/programs/cluster-connection/src/contexts.rs b/contracts/solana/programs/cluster-connection/src/contexts.rs index fd3bffd9..d65a80aa 100644 --- a/contracts/solana/programs/cluster-connection/src/contexts.rs +++ b/contracts/solana/programs/cluster-connection/src/contexts.rs @@ -58,39 +58,6 @@ pub struct SendMessage<'info> { pub network_fee: Account<'info, NetworkFee>, } -#[derive(Accounts)] -#[instruction(src_network: String, conn_sn: u128)] -pub struct RecvMessage<'info> { - #[account(mut)] - pub admin: Signer<'info>, - - pub system_program: Program<'info, System>, - - /// Config - #[account( - mut, - seeds = [Config::SEED_PREFIX.as_bytes()], - bump = config.bump, - has_one = admin @ ConnectionError::OnlyAdmin, - )] - pub config: Account<'info, Config>, - - #[account( - init, - payer = admin, - seeds = [Receipt::SEED_PREFIX.as_bytes(), src_network.as_bytes(), &conn_sn.to_be_bytes()], - space = Receipt::LEN, - bump - )] - pub receipt: Account<'info, Receipt>, - - #[account( - seeds = [Authority::SEED_PREFIX.as_bytes()], - bump = authority.bump - )] - pub authority: Account<'info, Authority>, -} - #[derive(Accounts)] pub struct RevertMessage<'info> { #[account(mut)] @@ -115,7 +82,7 @@ pub struct RevertMessage<'info> { } #[derive(Accounts)] -pub struct SetAdmin<'info> { +pub struct SetConfigItem<'info> { /// Transaction signer #[account(mut)] pub admin: Signer<'info>, @@ -175,63 +142,20 @@ pub struct GetFee<'info> { pub struct ClaimFees<'info> { /// Rent payer #[account(mut)] - pub admin: Signer<'info>, - - /// Config - #[account( - mut, - seeds = [Config::SEED_PREFIX.as_bytes()], - bump = config.bump, - has_one = admin @ ConnectionError::OnlyAdmin, - )] - pub config: Account<'info, Config>, -} - -#[derive(Accounts)] -pub struct SetThreshold<'info> { - /// Transaction signer - #[account(mut)] - pub admin: Signer<'info>, + pub relayer: Signer<'info>, /// Config #[account( mut, seeds = [Config::SEED_PREFIX.as_bytes()], bump = config.bump, - has_one = admin @ ConnectionError::OnlyAdmin, + has_one = relayer @ ConnectionError::OnlyRelayer, )] pub config: Account<'info, Config>, } #[derive(Accounts)] -pub struct GetThreshold<'info> { - /// Config - #[account( - seeds = [Config::SEED_PREFIX.as_bytes()], - bump = config.bump - )] - pub config: Account<'info, Config>, -} - -#[derive(Accounts)] -pub struct AddValidator<'info> { - /// Transaction signer - #[account(mut)] - pub admin: Signer<'info>, - - /// Config - #[account( - mut, - seeds = [Config::SEED_PREFIX.as_bytes()], - bump = config.bump, - has_one = admin @ ConnectionError::OnlyAdmin, - )] - pub config: Account<'info, Config>, -} - - -#[derive(Accounts)] -pub struct GetValidators<'info> { +pub struct GetConfigItem<'info> { /// Config #[account( seeds = [Config::SEED_PREFIX.as_bytes()], diff --git a/contracts/solana/programs/cluster-connection/src/error.rs b/contracts/solana/programs/cluster-connection/src/error.rs index eb27da60..5a150422 100644 --- a/contracts/solana/programs/cluster-connection/src/error.rs +++ b/contracts/solana/programs/cluster-connection/src/error.rs @@ -5,6 +5,9 @@ pub enum ConnectionError { #[msg("Only admin")] OnlyAdmin, + #[msg("Only relayer")] + OnlyRelayer, + #[msg("Only xcall")] OnlyXcall, diff --git a/contracts/solana/programs/cluster-connection/src/helper.rs b/contracts/solana/programs/cluster-connection/src/helper.rs index ff55fa59..a7a1d232 100644 --- a/contracts/solana/programs/cluster-connection/src/helper.rs +++ b/contracts/solana/programs/cluster-connection/src/helper.rs @@ -1,7 +1,7 @@ use anchor_lang::{ prelude::*, solana_program::{ - hash, instruction::{AccountMeta,Instruction}, keccak::hashv, program::{invoke, invoke_signed}, secp256k1_recover::{secp256k1_recover, Secp256k1Pubkey}, system_instruction, sysvar::recent_blockhashes::ID as SYSVAR_ID + hash, instruction::{AccountMeta,Instruction}, keccak::hashv, program::{get_return_data, invoke, invoke_signed}, secp256k1_recover::{secp256k1_recover, Secp256k1Pubkey}, system_instruction }, }; @@ -9,7 +9,26 @@ use crate::contexts::*; use crate::state::*; use crate::error::*; -use xcall_lib::xcall_type; +use xcall_lib::{network_address::{self, NetworkAddress}, xcall_type}; +use rlp::Encodable; + +pub const GET_NETWORK_ADDRESS: &str = "get_network_address"; + +pub struct SignableMsg { + pub src_nid: String, + pub conn_sn: u128, + pub data: Vec, + pub dst_nid: String +} +impl Encodable for SignableMsg { + fn rlp_append(&self, stream: &mut rlp::RlpStream) { + stream.begin_list(4); + stream.append(&self.src_nid); + stream.append(&self.conn_sn); + stream.append(&self.data); + stream.append(&self.dst_nid); + } +} pub fn transfer_lamports<'info>( from: &AccountInfo<'info>, @@ -39,27 +58,44 @@ pub fn get_instruction_data(ix_name: &str, data: Vec) -> Vec { ix_data } -pub fn get_message_hash(from_nid: &String, connection_sn: &u128, message: &Vec) -> [u8; 32] { - let mut result = from_nid.as_bytes().to_vec(); - result.extend_from_slice(&connection_sn.to_le_bytes()); - result.extend_from_slice(message); +pub fn get_message_hash(from_nid: &String, connection_sn: &u128, message: &Vec, dst_nid: &String) -> [u8; 32] { + let msg = SignableMsg { + src_nid: from_nid.to_string(), + conn_sn: *connection_sn, + data: message.to_vec(), + dst_nid: dst_nid.to_string(), + }; + let result = rlp::encode(&msg).to_vec(); let hash = hashv(&[&result]); hash.0 } -pub fn recover_pubkey(message: [u8; 32], sig: [u8; 65]) -> Pubkey { +pub fn recover_pubkey(message: [u8; 32], sig: [u8; 65]) -> [u8; 64] { let recovery_key = sig[64]; let signature = &sig[0..64]; let recovered_pubkey = secp256k1_recover(&message, recovery_key, signature).unwrap_or( Secp256k1Pubkey::new(&[0u8; 64]),); - let pubkey_bytes: [u8; 64] = recovered_pubkey.to_bytes(); - let mut solana_pubkey_bytes = [0u8; 32]; - solana_pubkey_bytes.copy_from_slice(&pubkey_bytes[..32]); - Pubkey::new_from_array(solana_pubkey_bytes) + + recovered_pubkey.to_bytes() } +pub fn get_nid(xcall_config: &AccountInfo, config: &Config) -> String { + let ix_data = get_instruction_data(GET_NETWORK_ADDRESS, vec![]); + let account_metas = vec![AccountMeta::new(xcall_config.key(), false)]; + let account_infos = vec![xcall_config.to_account_info()]; + + let ix = Instruction { + program_id: config.xcall, + accounts: account_metas, + data: ix_data, + }; + + invoke(&ix, &account_infos).unwrap(); + let network_address = String::from_utf8(get_return_data().unwrap().1).unwrap(); + network_address.parse::().unwrap().nid() +} pub fn call_xcall_handle_message_with_signatures<'info>( ctx: Context<'_, '_, '_, 'info, ReceiveMessageWithSignatures<'info>>, @@ -70,17 +106,17 @@ pub fn call_xcall_handle_message_with_signatures<'info>( signatures: Vec<[u8; 65]>, ) -> Result<()> { let mut data = vec![]; - - let message_hash = get_message_hash(&from_nid, &connection_sn, &message); + let dst_nid = get_nid(&ctx.remaining_accounts[1], &ctx.accounts.config); + let message_hash = get_message_hash(&from_nid, &connection_sn, &message, &dst_nid); let mut unique_validators = Vec::new(); for sig in signatures { let pubkey = recover_pubkey(message_hash, sig); - if ctx.accounts.config.is_validator(&pubkey) { + if !unique_validators.contains(&pubkey) && ctx.accounts.config.is_validator(&pubkey) { unique_validators.push(pubkey); } } - if (unique_validators.len() as u8) < ctx.accounts.config.get_threshold() { + if (unique_validators.len() as u8) < ctx.accounts.config.threshold { return Err(ConnectionError::ValidatorsMustBeGreaterThanThreshold.into()); } @@ -164,3 +200,22 @@ pub fn invoke_instruction<'info>( Ok(()) } +#[test] +fn test_get_message_hash() { + let from_nid = "nid"; + let connection_sn = 1; + let message = b"message"; + let dst_nid = "nid"; + + let message_hash = get_message_hash(&from_nid.to_string(), &connection_sn, &message.to_vec(), &dst_nid.to_string()); + + assert_eq!(message_hash, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); +} + +#[test] +fn test_recover_pubkey() { + // let message = b"message"; + let signature = [0u8; 65]; + let pubkey = recover_pubkey([0u8; 32], signature); + assert_eq!(pubkey, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); +} diff --git a/contracts/solana/programs/cluster-connection/src/lib.rs b/contracts/solana/programs/cluster-connection/src/lib.rs index 9607e849..60558cc3 100644 --- a/contracts/solana/programs/cluster-connection/src/lib.rs +++ b/contracts/solana/programs/cluster-connection/src/lib.rs @@ -22,10 +22,10 @@ declare_id!("8oxnXrSmqWJqkb2spZk2uz1cegzPsLy6nJp9XwFhkMD5"); pub mod centralized_connection { use super::*; - pub fn initialize(ctx: Context, xcall: Pubkey, admin: Pubkey) -> Result<()> { + pub fn initialize(ctx: Context, xcall: Pubkey, relayer: Pubkey) -> Result<()> { ctx.accounts .config - .set_inner(Config::new(xcall, admin, ctx.bumps.config)); + .set_inner(Config::new(xcall, ctx.accounts.signer.key(), relayer, ctx.bumps.config)); ctx.accounts .authority .set_inner(Authority::new(ctx.bumps.authority)); @@ -74,7 +74,7 @@ pub mod centralized_connection { sequence_no: u128, signatures: Vec<[u8; 65]>, ) -> Result<()> { - helper::call_xcall_handle_message_with_signatures(ctx, src_network, msg, conn_sn, sequence_no,signatures) + helper::call_xcall_handle_message_with_signatures(ctx, src_network, msg, conn_sn, sequence_no, signatures) } pub fn revert_message<'info>( @@ -84,13 +84,20 @@ pub mod centralized_connection { helper::call_xcall_handle_error(ctx, sequence_no) } - pub fn set_admin(ctx: Context, account: Pubkey) -> Result<()> { + pub fn set_admin(ctx: Context, account: Pubkey) -> Result<()> { let config = ctx.accounts.config.deref_mut(); config.admin = account; Ok(()) } + pub fn set_relayer(ctx: Context, address: Pubkey) -> Result<()> { + let config = ctx.accounts.config.deref_mut(); + config.relayer = address; + + Ok(()) + } + #[allow(unused_variables)] pub fn set_fee( ctx: Context, @@ -107,34 +114,26 @@ pub mod centralized_connection { Ok(()) } - pub fn set_threshold(ctx: Context, threshold: u8) -> Result<()> { - if ctx.accounts.config.get_validators().len() < threshold as usize { + pub fn set_threshold(ctx: Context, threshold: u8) -> Result<()> { + if ctx.accounts.config.validators.len() < threshold as usize { return Err(error::ConnectionError::ValidatorsMustBeGreaterThanThreshold.into()); } - ctx.accounts.config.set_threshold(threshold); + ctx.accounts.config.threshold = threshold; Ok(()) } - pub fn update_validators(ctx: Context, validators: Vec, threshold: u8) -> Result<()> { + pub fn update_validators(ctx: Context, validators: Vec<[u8; 65]>, threshold: u8) -> Result<()> { let mut unique_validators = validators.clone(); unique_validators.sort(); unique_validators.dedup(); if unique_validators.len() < threshold as usize { return Err(error::ConnectionError::ValidatorsMustBeGreaterThanThreshold.into()); } - ctx.accounts.config.set_threshold(threshold); - ctx.accounts.config.store_validators(unique_validators); + ctx.accounts.config.threshold = threshold; + ctx.accounts.config.validators = unique_validators; Ok(()) } - pub fn get_validators(ctx: Context) -> Result> { - Ok(ctx.accounts.config.get_validators()) - } - - pub fn get_threshold(ctx: Context) -> Result { - Ok(ctx.accounts.config.get_threshold()) - } - #[allow(unused_variables)] pub fn get_fee(ctx: Context, network_id: String, response: bool) -> Result { ctx.accounts.network_fee.get(response) @@ -145,7 +144,7 @@ pub mod centralized_connection { let fee = ctx.accounts.config.get_claimable_fees(&config)?; **config.try_borrow_mut_lamports()? -= fee; - **ctx.accounts.admin.try_borrow_mut_lamports()? += fee; + **ctx.accounts.relayer.try_borrow_mut_lamports()? += fee; Ok(()) } diff --git a/contracts/solana/programs/cluster-connection/src/state.rs b/contracts/solana/programs/cluster-connection/src/state.rs index f2d333a2..0888e3ef 100644 --- a/contracts/solana/programs/cluster-connection/src/state.rs +++ b/contracts/solana/programs/cluster-connection/src/state.rs @@ -9,7 +9,8 @@ use crate::{constants, error::*}; pub struct Config { pub admin: Pubkey, pub xcall: Pubkey, - pub validators: Vec, + pub relayer: Pubkey, + pub validators: Vec<[u8; 65]>, pub threshold: u8, pub sn: u128, pub bump: u8, @@ -24,10 +25,11 @@ impl Config { pub const LEN: usize = constants::ACCOUNT_DISCRIMINATOR_SIZE + 32 + 32 + 16 + 1 + 1; /// Creates a new centralized connection `Config` state - pub fn new(xcall: Pubkey, admin: Pubkey, bump: u8) -> Self { + pub fn new(xcall: Pubkey, admin: Pubkey, relayer: Pubkey, bump: u8) -> Self { Self { xcall, admin, + relayer, validators: Vec::new(), threshold: 0, sn: 0, @@ -63,30 +65,11 @@ impl Config { Ok(account.lamports() - rent_exempt_balance) } - pub fn get_threshold(&self) -> u8 { - self.threshold - } - - pub fn set_threshold(&mut self, threshold: u8) { - self.threshold = threshold - } - - pub fn add_validator(&mut self, validator: Pubkey) { - self.validators.push(validator); - } - - pub fn store_validators(&mut self, validators: Vec) { - self.validators = validators - } - - pub fn is_validator(&self, address: &Pubkey) -> bool { - self.validators.contains(address) - } - - - - pub fn get_validators(&self) -> Vec { - self.validators.clone() + pub fn is_validator(&self, pub_key: &[u8; 64]) -> bool { + let mut pub_key_65: [u8; 65] = [0u8; 65]; + pub_key_65[0] = 0x04; + pub_key_65[1..].copy_from_slice(pub_key); + self.validators.contains(&pub_key_65) } } From 27ad3283ccf91e78f15c634576590d008af69305 Mon Sep 17 00:00:00 2001 From: gcranju Date: Wed, 11 Dec 2024 11:20:55 +0545 Subject: [PATCH 4/7] fix: tests --- .../cluster-connection/src/contexts.rs | 16 ++-- .../programs/cluster-connection/src/helper.rs | 75 +++++++------------ 2 files changed, 34 insertions(+), 57 deletions(-) diff --git a/contracts/solana/programs/cluster-connection/src/contexts.rs b/contracts/solana/programs/cluster-connection/src/contexts.rs index d65a80aa..3b51aa89 100644 --- a/contracts/solana/programs/cluster-connection/src/contexts.rs +++ b/contracts/solana/programs/cluster-connection/src/contexts.rs @@ -61,7 +61,7 @@ pub struct SendMessage<'info> { #[derive(Accounts)] pub struct RevertMessage<'info> { #[account(mut)] - pub admin: Signer<'info>, + pub relayer: Signer<'info>, pub system_program: Program<'info, System>, @@ -70,7 +70,7 @@ pub struct RevertMessage<'info> { mut, seeds = [Config::SEED_PREFIX.as_bytes()], bump = config.bump, - has_one = admin @ ConnectionError::OnlyAdmin, + has_one = relayer @ ConnectionError::OnlyRelayer, )] pub config: Account<'info, Config>, @@ -102,7 +102,7 @@ pub struct SetConfigItem<'info> { pub struct SetFee<'info> { /// Rent payer #[account(mut)] - pub admin: Signer<'info>, + pub relayer: Signer<'info>, /// System Program: Required to create program-derived address pub system_program: Program<'info, System>, @@ -110,7 +110,7 @@ pub struct SetFee<'info> { /// Fee #[account( init_if_needed, - payer = admin, + payer = relayer, seeds = [NetworkFee::SEED_PREFIX.as_bytes(), network_id.as_bytes()], bump, space = NetworkFee::LEN @@ -122,7 +122,7 @@ pub struct SetFee<'info> { mut, seeds = [Config::SEED_PREFIX.as_bytes()], bump = config.bump, - has_one = admin @ ConnectionError::OnlyAdmin, + has_one = relayer @ ConnectionError::OnlyRelayer, )] pub config: Account<'info, Config>, } @@ -168,7 +168,7 @@ pub struct GetConfigItem<'info> { #[instruction(src_network: String, conn_sn: u128)] pub struct ReceiveMessageWithSignatures<'info> { #[account(mut)] - pub admin: Signer<'info>, + pub relayer: Signer<'info>, pub system_program: Program<'info, System>, @@ -177,13 +177,13 @@ pub struct ReceiveMessageWithSignatures<'info> { mut, seeds = [Config::SEED_PREFIX.as_bytes()], bump = config.bump, - has_one = admin @ ConnectionError::OnlyAdmin, + has_one = relayer @ ConnectionError::OnlyRelayer, )] pub config: Account<'info, Config>, #[account( init, - payer = admin, + payer = relayer, seeds = [Receipt::SEED_PREFIX.as_bytes(), src_network.as_bytes(), &conn_sn.to_be_bytes()], space = Receipt::LEN, bump diff --git a/contracts/solana/programs/cluster-connection/src/helper.rs b/contracts/solana/programs/cluster-connection/src/helper.rs index a7a1d232..99f1c9ad 100644 --- a/contracts/solana/programs/cluster-connection/src/helper.rs +++ b/contracts/solana/programs/cluster-connection/src/helper.rs @@ -1,7 +1,7 @@ use anchor_lang::{ prelude::*, solana_program::{ - hash, instruction::{AccountMeta,Instruction}, keccak::hashv, program::{get_return_data, invoke, invoke_signed}, secp256k1_recover::{secp256k1_recover, Secp256k1Pubkey}, system_instruction + hash, instruction::{AccountMeta,Instruction}, keccak::hashv, program::{get_return_data, invoke, invoke_signed}, secp256k1_recover::secp256k1_recover, system_instruction }, }; @@ -9,27 +9,10 @@ use crate::contexts::*; use crate::state::*; use crate::error::*; -use xcall_lib::{network_address::{self, NetworkAddress}, xcall_type}; -use rlp::Encodable; +use xcall_lib::{network_address:: NetworkAddress, xcall_type}; pub const GET_NETWORK_ADDRESS: &str = "get_network_address"; -pub struct SignableMsg { - pub src_nid: String, - pub conn_sn: u128, - pub data: Vec, - pub dst_nid: String -} -impl Encodable for SignableMsg { - fn rlp_append(&self, stream: &mut rlp::RlpStream) { - stream.begin_list(4); - stream.append(&self.src_nid); - stream.append(&self.conn_sn); - stream.append(&self.data); - stream.append(&self.dst_nid); - } -} - pub fn transfer_lamports<'info>( from: &AccountInfo<'info>, to: &AccountInfo<'info>, @@ -59,26 +42,23 @@ pub fn get_instruction_data(ix_name: &str, data: Vec) -> Vec { } pub fn get_message_hash(from_nid: &String, connection_sn: &u128, message: &Vec, dst_nid: &String) -> [u8; 32] { - let msg = SignableMsg { - src_nid: from_nid.to_string(), - conn_sn: *connection_sn, - data: message.to_vec(), - dst_nid: dst_nid.to_string(), - }; - let result = rlp::encode(&msg).to_vec(); + let mut encoded_bytes = Vec::new(); + encoded_bytes.extend(from_nid.as_bytes()); + encoded_bytes.extend(connection_sn.to_string().as_bytes()); + encoded_bytes.extend(message); + encoded_bytes.extend(dst_nid.as_bytes()); + + let hash = hashv(&[&encoded_bytes]); - let hash = hashv(&[&result]); - hash.0 + hash.to_bytes() } pub fn recover_pubkey(message: [u8; 32], sig: [u8; 65]) -> [u8; 64] { - let recovery_key = sig[64]; + let recovery_id = sig[64] % 27; let signature = &sig[0..64]; - let recovered_pubkey = secp256k1_recover(&message, recovery_key, signature).unwrap_or( - Secp256k1Pubkey::new(&[0u8; 64]),); - + let recovered_pubkey = secp256k1_recover(&message, recovery_id, signature).unwrap(); recovered_pubkey.to_bytes() -} +} pub fn get_nid(xcall_config: &AccountInfo, config: &Config) -> String { let ix_data = get_instruction_data(GET_NETWORK_ADDRESS, vec![]); @@ -133,7 +113,7 @@ pub fn call_xcall_handle_message_with_signatures<'info>( ix_data, &ctx.accounts.config, &ctx.accounts.authority, - &ctx.accounts.admin, + &ctx.accounts.relayer, &ctx.accounts.system_program, ctx.remaining_accounts, ) @@ -153,7 +133,7 @@ pub fn call_xcall_handle_error<'info>( ix_data, &ctx.accounts.config, &ctx.accounts.authority, - &ctx.accounts.admin, + &ctx.accounts.relayer, &ctx.accounts.system_program, &ctx.remaining_accounts, ) @@ -200,22 +180,19 @@ pub fn invoke_instruction<'info>( Ok(()) } -#[test] -fn test_get_message_hash() { - let from_nid = "nid"; - let connection_sn = 1; - let message = b"message"; - let dst_nid = "nid"; - - let message_hash = get_message_hash(&from_nid.to_string(), &connection_sn, &message.to_vec(), &dst_nid.to_string()); - - assert_eq!(message_hash, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); -} #[test] fn test_recover_pubkey() { // let message = b"message"; - let signature = [0u8; 65]; - let pubkey = recover_pubkey([0u8; 32], signature); - assert_eq!(pubkey, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + let from_nid = "0x2.icon"; + let connection_sn = 128; + let message = b"hello"; + let dst_nid = "archway"; + + let message_hash = get_message_hash(&from_nid.to_string(), &connection_sn, &message.to_vec(), &dst_nid.to_string()); + + let signature = [102,13,84,43,63,109,233,205,8,242,56,253,68,19,62,238,191,234,41,11,33,218,231,50,42,99,181,22,197,123,141,241,44,76,10,52,11,96,237,86,124,141,165,53,120,52,108,33,43,39,183,151,235,66,167,95,180,183,7,108,86,122,111,249,28]; + let pubkey = recover_pubkey(message_hash, signature); + print!("pubkey: {:?}", pubkey); + assert_eq!(pubkey, [222,202,81,45,92,184,118,115,178,58,177,12,62,53,114,227,10,43,93,199,140,213,0,191,132,191,6,98,117,192,187,50,12,182,205,38,106,161,121,180,19,35,181,161,138,180,161,112,36,142,216,155,67,107,85,89,186,179,140,129,108,225,34,9] ); } From 1a1cab644f5eb09aade7502b96f0484636637f62 Mon Sep 17 00:00:00 2001 From: gcranju Date: Wed, 11 Dec 2024 13:11:08 +0545 Subject: [PATCH 5/7] feat: solana changes according to centralized --- .../programs/cluster-connection/src/helper.rs | 7 ++++--- .../src/instructions/query_accounts.rs | 20 ++++++++++++------- .../programs/cluster-connection/src/lib.rs | 2 +- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/contracts/solana/programs/cluster-connection/src/helper.rs b/contracts/solana/programs/cluster-connection/src/helper.rs index 99f1c9ad..4dc9dfce 100644 --- a/contracts/solana/programs/cluster-connection/src/helper.rs +++ b/contracts/solana/programs/cluster-connection/src/helper.rs @@ -81,13 +81,13 @@ pub fn call_xcall_handle_message_with_signatures<'info>( ctx: Context<'_, '_, '_, 'info, ReceiveMessageWithSignatures<'info>>, from_nid: String, message: Vec, - connection_sn: u128, + conn_sn: u128, sequence_no: u128, signatures: Vec<[u8; 65]>, ) -> Result<()> { let mut data = vec![]; let dst_nid = get_nid(&ctx.remaining_accounts[1], &ctx.accounts.config); - let message_hash = get_message_hash(&from_nid, &connection_sn, &message, &dst_nid); + let message_hash = get_message_hash(&from_nid, &conn_sn, &message, &dst_nid); let mut unique_validators = Vec::new(); for sig in signatures { let pubkey = recover_pubkey(message_hash, sig); @@ -104,6 +104,7 @@ pub fn call_xcall_handle_message_with_signatures<'info>( from_nid, message, sequence_no, + conn_sn, }; args.serialize(&mut data)?; @@ -181,9 +182,9 @@ pub fn invoke_instruction<'info>( } + #[test] fn test_recover_pubkey() { - // let message = b"message"; let from_nid = "0x2.icon"; let connection_sn = 128; let message = b"hello"; diff --git a/contracts/solana/programs/cluster-connection/src/instructions/query_accounts.rs b/contracts/solana/programs/cluster-connection/src/instructions/query_accounts.rs index 7f149623..9f21112f 100644 --- a/contracts/solana/programs/cluster-connection/src/instructions/query_accounts.rs +++ b/contracts/solana/programs/cluster-connection/src/instructions/query_accounts.rs @@ -2,7 +2,7 @@ use anchor_lang::{ prelude::*, solana_program::{ instruction::Instruction, - program::{get_return_data, invoke}, + program::{get_return_data, invoke, invoke_signed}, system_program, }, }; @@ -35,8 +35,8 @@ pub fn query_send_message_accounts<'info>( }) } -pub fn query_recv_message_accounts( - ctx: Context, +pub fn query_recv_message_accounts<'info>( + ctx: Context<'_, '_, '_, 'info, QueryAccountsCtx<'info>>, src_network: String, conn_sn: u128, msg: Vec, @@ -65,8 +65,8 @@ pub fn query_recv_message_accounts( AccountMetadata::new(authority, false), ]; - let mut xcall_account_metas = vec![]; - let mut xcall_account_infos = vec![]; + let mut xcall_account_metas = vec![AccountMeta::new_readonly(config.key(), true)]; + let mut xcall_account_infos = vec![config.to_account_info()]; for (_, account) in ctx.remaining_accounts.iter().enumerate() { if account.is_writable { @@ -78,7 +78,7 @@ pub fn query_recv_message_accounts( xcall_account_infos.push(account.to_account_info()) } - let ix_data = get_handle_message_ix_data(src_network, msg, sequence_no)?; + let ix_data = get_handle_message_ix_data(src_network, msg, sequence_no, conn_sn)?; let ix = Instruction { program_id: config.xcall, @@ -86,7 +86,11 @@ pub fn query_recv_message_accounts( data: ix_data, }; - invoke(&ix, &xcall_account_infos)?; + invoke_signed( + &ix, + &xcall_account_infos, + &[&[Config::SEED_PREFIX.as_bytes(), &[config.bump]]], + )?; let (_, data) = get_return_data().unwrap(); let mut data_slice: &[u8] = &data; @@ -171,12 +175,14 @@ pub fn get_handle_message_ix_data( from_nid: String, message: Vec, sequence_no: u128, + conn_sn: u128, ) -> Result> { let mut ix_args_data = vec![]; let ix_args = xcall_type::HandleMessageArgs { from_nid, message, sequence_no, + conn_sn, }; ix_args.serialize(&mut ix_args_data)?; diff --git a/contracts/solana/programs/cluster-connection/src/lib.rs b/contracts/solana/programs/cluster-connection/src/lib.rs index 60558cc3..43b4aa97 100644 --- a/contracts/solana/programs/cluster-connection/src/lib.rs +++ b/contracts/solana/programs/cluster-connection/src/lib.rs @@ -66,7 +66,7 @@ pub mod centralized_connection { #[allow(unused_variables)] - pub fn receive_message<'info>( + pub fn recv_message<'info>( ctx: Context<'_, '_, '_, 'info, ReceiveMessageWithSignatures<'info>>, src_network: String, conn_sn: u128, From 095d1d22106213649811349ac7aa30d190f573be Mon Sep 17 00:00:00 2001 From: gcranju Date: Thu, 12 Dec 2024 11:27:10 +0545 Subject: [PATCH 6/7] fix: increased config size --- contracts/solana/programs/cluster-connection/src/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/solana/programs/cluster-connection/src/state.rs b/contracts/solana/programs/cluster-connection/src/state.rs index 0888e3ef..61b50d09 100644 --- a/contracts/solana/programs/cluster-connection/src/state.rs +++ b/contracts/solana/programs/cluster-connection/src/state.rs @@ -22,7 +22,7 @@ impl Config { /// Account discriminator + Xcall public key + Admin public key + connection /// sequence + bump - pub const LEN: usize = constants::ACCOUNT_DISCRIMINATOR_SIZE + 32 + 32 + 16 + 1 + 1; + pub const LEN: usize = constants::ACCOUNT_DISCRIMINATOR_SIZE + 32 + 32 + 32 + 16 + 1 + 1 + 1 + 4 + 65 * 8; /// Creates a new centralized connection `Config` state pub fn new(xcall: Pubkey, admin: Pubkey, relayer: Pubkey, bump: u8) -> Self { From c2fa85d61f8d908c6e8ac787cb583b11801b099f Mon Sep 17 00:00:00 2001 From: gcranju Date: Fri, 13 Dec 2024 14:21:53 +0545 Subject: [PATCH 7/7] fix: break if thereshold met --- contracts/solana/programs/cluster-connection/src/helper.rs | 3 +++ contracts/solana/programs/cluster-connection/src/state.rs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/contracts/solana/programs/cluster-connection/src/helper.rs b/contracts/solana/programs/cluster-connection/src/helper.rs index 4dc9dfce..51d57ce5 100644 --- a/contracts/solana/programs/cluster-connection/src/helper.rs +++ b/contracts/solana/programs/cluster-connection/src/helper.rs @@ -94,6 +94,9 @@ pub fn call_xcall_handle_message_with_signatures<'info>( if !unique_validators.contains(&pubkey) && ctx.accounts.config.is_validator(&pubkey) { unique_validators.push(pubkey); } + if (unique_validators.len() as u8) >= ctx.accounts.config.threshold { + break; + } } if (unique_validators.len() as u8) < ctx.accounts.config.threshold { diff --git a/contracts/solana/programs/cluster-connection/src/state.rs b/contracts/solana/programs/cluster-connection/src/state.rs index 61b50d09..75cf7105 100644 --- a/contracts/solana/programs/cluster-connection/src/state.rs +++ b/contracts/solana/programs/cluster-connection/src/state.rs @@ -22,7 +22,7 @@ impl Config { /// Account discriminator + Xcall public key + Admin public key + connection /// sequence + bump - pub const LEN: usize = constants::ACCOUNT_DISCRIMINATOR_SIZE + 32 + 32 + 32 + 16 + 1 + 1 + 1 + 4 + 65 * 8; + pub const LEN: usize = constants::ACCOUNT_DISCRIMINATOR_SIZE + 32 + 32 + 32 + 16 + 1 + 1 + 1 + 4 + 65 * 10; /// Creates a new centralized connection `Config` state pub fn new(xcall: Pubkey, admin: Pubkey, relayer: Pubkey, bump: u8) -> Self {