Skip to content

Commit

Permalink
feat: large incoming messages (#571)
Browse files Browse the repository at this point in the history
* chore(wip): remove IncomingMessageWrapper type

* chore: fix some tests

* feat: new instructions and their build functions

* feat: initialize message payload

* chore: refactor initialize message payload test

* chore: minor updates

* feat: write to message payload pda

* feat: close message payload account

* feat: commit message payload and MessagePayload struct

* refactor: message payload assertions

* doc: rewrite "buffer account" -> "message payload account"

* chore: post merge fixes

* chore: more post merge fixes

* chore: even more post change fixes

* chore: project wide check and fmt

* chore: don't touch IncomingMessage PDA within MessagePayload ixs

* chore: don't pass bump seed as an argument

* chore: remove unused todo

* refactor: use GatewayError + ValidPDA for MessagePayload

* docs: update required accounts for MessagePayload instructions

* chore: add initialization checks for the MessagePayload PDA

* fix: relayer error test
  • Loading branch information
tilacog authored Dec 13, 2024
1 parent f065eda commit 93bd4ac
Show file tree
Hide file tree
Showing 25 changed files with 1,169 additions and 108 deletions.
6 changes: 3 additions & 3 deletions solana/crates/axelar-executable/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use core::str::FromStr;

use axelar_solana_encoding::types::messages::Message;
use axelar_solana_gateway::get_validate_message_signing_pda;
use axelar_solana_gateway::state::incoming_message::{command_id, IncomingMessageWrapper};
use axelar_solana_gateway::state::incoming_message::{command_id, IncomingMessage};
use axelar_solana_gateway::state::BytemuckedPda;
use num_traits::{FromPrimitive, ToPrimitive};
use rkyv::bytecheck::{self, CheckBytes};
Expand Down Expand Up @@ -56,7 +56,7 @@ pub fn validate_message(
let accounts_iter = &mut relayer_prepended_accs.iter();
let incoming_message_pda = next_account_info(accounts_iter)?;
let incoming_message_data = incoming_message_pda.try_borrow_data()?;
let incoming_message = IncomingMessageWrapper::read(&incoming_message_data)?;
let incoming_message = IncomingMessage::read(&incoming_message_data)?;
incoming_message.signing_pda_bump
};

Expand Down Expand Up @@ -106,7 +106,7 @@ pub fn validate_with_gmp_metadata(
let accounts_iter = &mut accounts.iter();
let incoming_message_pda = next_account_info(accounts_iter)?;
let incoming_message_data = incoming_message_pda.try_borrow_data()?;
let incoming_message = IncomingMessageWrapper::read(&incoming_message_data)?;
let incoming_message = IncomingMessage::read(&incoming_message_data)?;
incoming_message.signing_pda_bump
};

Expand Down
38 changes: 24 additions & 14 deletions solana/crates/axelar-solana-gateway-test-fixtures/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use std::path::PathBuf;
use solana_program::hash::Hash;
use solana_program::pubkey::Pubkey;
use solana_program_test::{
BanksClient, BanksTransactionResultWithMetadata, ProgramTest, ProgramTestBanksClientExt as _,
ProgramTestContext,
BanksClient, BanksClientError, BanksTransactionResultWithMetadata, ProgramTest,
ProgramTestBanksClientExt as _, ProgramTestContext,
};
use solana_sdk::account::{Account, AccountSharedData, WritableAccount as _};
use solana_sdk::account_utils::StateMut as _;
Expand Down Expand Up @@ -244,19 +244,29 @@ impl TestFixture {
/// Get the account data
///
/// # Panics
/// if the account does not exist or the expected owner does not match
/// if the account does not exist or the expected owner does not match.
#[allow(clippy::panic)]
pub async fn get_account(&mut self, account: &Pubkey, expected_owner: &Pubkey) -> Account {
let account = self
.banks_client
.get_account(*account)
.await
.expect("get_account")
.expect("account not none");
assert_eq!(
account.owner, *expected_owner,
"expected owners don't match"
);
account
match self.try_get_account(account, expected_owner).await {
Ok(Some(account)) => account,
Ok(None) => panic!("account not found"),
Err(error) => panic!("error while getting account: {error}"),
}
}

/// Tries to get an account.
///
/// Non-panicking version of `Self::get_account`
pub async fn try_get_account(
&mut self,
account: &Pubkey,
expected_owner: &Pubkey,
) -> Result<Option<Account>, BanksClientError> {
match self.banks_client.get_account(*account).await? {
None => Ok(None),
Some(account) if account.owner == *expected_owner => Ok(Some(account)),
Some(_) => Err(BanksClientError::ClientError("unexpected account owner")),
}
}
}

Expand Down
11 changes: 3 additions & 8 deletions solana/crates/axelar-solana-gateway-test-fixtures/src/gateway.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use axelar_solana_encoding::{borsh, hash_payload};
use axelar_solana_gateway::error::GatewayError;
use axelar_solana_gateway::num_traits::FromPrimitive;
use axelar_solana_gateway::processor::GatewayEvent;
use axelar_solana_gateway::state::incoming_message::{command_id, IncomingMessageWrapper};
use axelar_solana_gateway::state::incoming_message::{command_id, IncomingMessage};
use axelar_solana_gateway::state::signature_verification_pda::SignatureVerificationSessionData;
use axelar_solana_gateway::state::verifier_set_tracker::VerifierSetTracker;
use axelar_solana_gateway::state::{BytemuckedPda, GatewayConfig};
Expand Down Expand Up @@ -404,10 +404,7 @@ impl SolanaAxelarIntegrationMetadata {
}

/// Get the verifier set tracker data
pub async fn incoming_message(
&mut self,
incoming_message_pda: Pubkey,
) -> IncomingMessageWrapper {
pub async fn incoming_message(&mut self, incoming_message_pda: Pubkey) -> IncomingMessage {
let gateway_root_pda_account = self
.banks_client
.get_account(incoming_message_pda)
Expand All @@ -421,9 +418,7 @@ impl SolanaAxelarIntegrationMetadata {
axelar_solana_gateway::ID,
"must be owned by the gateway"
);
let res = *IncomingMessageWrapper::read(gateway_root_pda_account.data()).unwrap();

res
*IncomingMessage::read(gateway_root_pda_account.data()).unwrap()
}
}

Expand Down
6 changes: 5 additions & 1 deletion solana/programs/axelar-solana-gateway/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ pub enum GatewayError {
#[error("Verifier set tracker PDA already initialized")]
VerifierSetTrackerAlreadyInitialised,

/// Message Payload PDA was already initialized.
#[error("Message Payload PDA was already initialized")]
MessagePayloadAlreadyInitialized,

/// Error indicating an underflow occurred during epoch calculation.
#[error("Epoch calculation resulted in an underflow")]
// --- NOTICE ---
Expand Down Expand Up @@ -168,7 +172,7 @@ mod tests {
.collect_vec();

// confidence check that we derived the errors correctly
assert_eq!(errors_to_proceed.len(), 5);
assert_eq!(errors_to_proceed.len(), 6);
assert_eq!(errors_to_not_proceed.len(), 23);

// Errors that should cause the relayer to proceed (error numbers < 500)
Expand Down
169 changes: 169 additions & 0 deletions solana/programs/axelar-solana-gateway/src/instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,75 @@ pub enum GatewayInstruction {
verifier_info: SigningVerifierSetInfo,
},

/// Initializes a Message Payload PDA account.
///
/// This instruction will revert if the account already exists.
///
/// Accounts expected by this instruction:
/// 0. [WRITE, SIGNER] Funding account, which becomes the authority for the Message Payload account.
/// 1. [] Gateway Root PDA account
/// 2. [WRITE] Message Payload PDA account
/// 3. [] System Program account
InitializeMessagePayload {
/// The number of bytes to allocate for the new message payload account
buffer_size: u64,
/// Message's command id
command_id: [u8; 32],
},

/// Write message payload parts into the Message Payload PDA account.
///
/// This instruction will revert on the following cases
/// 1. Message payload account is already committed.
/// 2. offset + bytes.len() is greater than the account size.
/// 3. SIGNER is not the authority for the Message Payload account.
///
/// Accounts expected by this instruction:
/// 0. [SIGNER] Funding account and authority for the Message Payload account.
/// 1. [] Gateway Root PDA account
/// 2. [WRITE] Message Payload PDA account
WriteMessagePayload {
/// Offset at which to write the given bytes.
offset: usize,
/// Serialized `execute_data` data.
bytes: Vec<u8>,
/// Message's command id
command_id: [u8; 32],
},

/// Finalizes the writing phase for a Message Payload PDA buffer
/// account and writes the calculated hash into its metadata
/// section.
///
/// This instruction will revert on the following circumstances:
/// 1. The message payload account is already finalized.
/// 2. The message payload account already had the payload hash calculated
/// and persisted.
/// 3. SIGNER is not the authority for the Message Payload account.
///
/// Accounts expected by this instruction:
/// 0. [SIGNER] Funding account and authority for the Message Payload account.
/// 1. [] Gateway Root PDA account
/// 2. [WRITE] Message Payload PDA account
CommitMessagePayload {
/// Message's command id
command_id: [u8; 32],
},

/// Closes the message payload account and reclaim its lamports.
///
/// This instruction will revert on the following circumstances:
/// 1. SIGNER is not the authority for the Message Payload account.
///
/// Accounts expected by this instruction:
/// 0. [SIGNER] Funding account and authority for the Message Payload account.
/// 1. [] Gateway Root PDA account
/// 2. [WRITE] Message Payload PDA account
CloseMessagePayload {
/// Message's command id
command_id: [u8; 32],
},

/// Validates message.
/// It is the responsibility of the destination program (contract) that
/// receives a message from Axelar to validate that the message has been
Expand Down Expand Up @@ -380,3 +449,103 @@ pub fn validate_message(
data,
})
}

/// Creates a [`GatewayInstruction::InitializeMessagePayload`] instruction.
pub fn initialize_message_payload(
gateway_root_pda: Pubkey,
payer: Pubkey,
command_id: [u8; 32],
buffer_size: u64,
) -> Result<Instruction, ProgramError> {
let (message_payload_pda, _) =
crate::find_message_payload_pda(gateway_root_pda, command_id, payer);

let accounts = vec![
AccountMeta::new(payer, true),
AccountMeta::new_readonly(gateway_root_pda, false),
AccountMeta::new(message_payload_pda, false),
AccountMeta::new_readonly(solana_program::system_program::id(), false),
];

let instruction = GatewayInstruction::InitializeMessagePayload {
buffer_size,
command_id,
};

Ok(Instruction {
program_id: crate::id(),
accounts,
data: borsh::to_vec(&instruction)?,
})
}

/// Creates a [`GatewayInstruction::WriteMessagePayload`] instruction.
pub fn write_message_payload(
gateway_root_pda: Pubkey,
authority: Pubkey,
command_id: [u8; 32],
bytes: &[u8],
offset: usize,
) -> Result<Instruction, ProgramError> {
let (message_payload_pda, _) =
crate::find_message_payload_pda(gateway_root_pda, command_id, authority);
let accounts = vec![
AccountMeta::new(authority, true),
AccountMeta::new_readonly(gateway_root_pda, false),
AccountMeta::new(message_payload_pda, false),
];
let instruction = GatewayInstruction::WriteMessagePayload {
offset,
bytes: bytes.to_vec(),
command_id,
};
Ok(Instruction {
program_id: crate::id(),
accounts,
data: borsh::to_vec(&instruction)?,
})
}

/// Creates a [`GatewayInstruction::CommitMessagePayload`] instruction.
pub fn commit_message_payload(
gateway_root_pda: Pubkey,
authority: Pubkey,
command_id: [u8; 32],
) -> Result<Instruction, ProgramError> {
let (message_payload_pda, _) =
crate::find_message_payload_pda(gateway_root_pda, command_id, authority);

let accounts = vec![
AccountMeta::new(authority, true),
AccountMeta::new_readonly(gateway_root_pda, false),
AccountMeta::new(message_payload_pda, false),
];

let instruction = GatewayInstruction::CommitMessagePayload { command_id };
Ok(Instruction {
program_id: crate::id(),
accounts,
data: borsh::to_vec(&instruction)?,
})
}

/// Creates a [`GatewayInstrucon::CloseMessagePayload`] instruction.
pub fn close_message_payload(
gateway_root_pda: Pubkey,
authority: Pubkey,
command_id: [u8; 32],
) -> Result<Instruction, ProgramError> {
let (message_payload_pda, _) =
crate::find_message_payload_pda(gateway_root_pda, command_id, authority);
let accounts = vec![
AccountMeta::new(authority, false),
AccountMeta::new_readonly(gateway_root_pda, false),
AccountMeta::new(message_payload_pda, false),
];
let instruction = GatewayInstruction::CloseMessagePayload { command_id };
Ok(Instruction {
program_id: crate::id(),
accounts,
data: borsh::to_vec(&instruction)?,
})
}
Loading

0 comments on commit 93bd4ac

Please sign in to comment.