Skip to content

Commit

Permalink
fix: solana cluster spec update
Browse files Browse the repository at this point in the history
  • Loading branch information
gcranju committed Dec 9, 2024
1 parent 8da68a8 commit d464dde
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 171 deletions.
31 changes: 1 addition & 30 deletions contracts/solana/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion contracts/solana/programs/cluster-connection/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
84 changes: 4 additions & 80 deletions contracts/solana/programs/cluster-connection/src/contexts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -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>,
Expand Down Expand Up @@ -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()],
Expand Down
3 changes: 3 additions & 0 deletions contracts/solana/programs/cluster-connection/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ pub enum ConnectionError {
#[msg("Only admin")]
OnlyAdmin,

#[msg("Only relayer")]
OnlyRelayer,

#[msg("Only xcall")]
OnlyXcall,

Expand Down
85 changes: 70 additions & 15 deletions contracts/solana/programs/cluster-connection/src/helper.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,34 @@
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
},
};

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<u8>,
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>,
Expand Down Expand Up @@ -39,27 +58,44 @@ pub fn get_instruction_data(ix_name: &str, data: Vec<u8>) -> Vec<u8> {
ix_data
}

pub fn get_message_hash(from_nid: &String, connection_sn: &u128, message: &Vec<u8>) -> [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<u8>, 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::<NetworkAddress>().unwrap().nid()
}

pub fn call_xcall_handle_message_with_signatures<'info>(
ctx: Context<'_, '_, '_, 'info, ReceiveMessageWithSignatures<'info>>,
Expand All @@ -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());
}

Expand Down Expand Up @@ -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]);
}
Loading

0 comments on commit d464dde

Please sign in to comment.