Skip to content

Commit

Permalink
feat(axelar-gateway): add pausable to gateway (#205)
Browse files Browse the repository at this point in the history
  • Loading branch information
milapsheth authored Jan 24, 2025
1 parent 0d4af95 commit b97d9d9
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 16 deletions.
32 changes: 18 additions & 14 deletions contracts/stellar-axelar-gateway/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use soroban_sdk::xdr::ToXdr;
use soroban_sdk::{contract, contractimpl, Address, Bytes, BytesN, Env, String, Vec};
use stellar_axelar_std::events::Event;
use stellar_axelar_std::ttl::extend_instance_ttl;
use stellar_axelar_std::{ensure, interfaces, Operatable, Ownable, Upgradable};
use stellar_axelar_std::{ensure, interfaces, Operatable, Ownable, Pausable, Upgradable};

use crate::auth;
use crate::error::ContractError;
Expand All @@ -13,7 +13,7 @@ use crate::storage_types::{DataKey, MessageApprovalKey, MessageApprovalValue};
use crate::types::{CommandType, Message, Proof, WeightedSigners};

#[contract]
#[derive(Ownable, Upgradable, Operatable)]
#[derive(Operatable, Ownable, Pausable, Upgradable)]
pub struct AxelarGateway;

#[contractimpl]
Expand Down Expand Up @@ -135,16 +135,18 @@ impl AxelarGatewayMessagingInterface for AxelarGateway {
#[contractimpl]
impl AxelarGatewayInterface for AxelarGateway {
fn approve_messages(
env: Env,
env: &Env,
messages: Vec<Message>,
proof: Proof,
) -> Result<(), ContractError> {
ensure!(!Self::paused(env), ContractError::ContractPaused);

let data_hash: BytesN<32> = env
.crypto()
.keccak256(&(CommandType::ApproveMessages, messages.clone()).to_xdr(&env))
.keccak256(&(CommandType::ApproveMessages, messages.clone()).to_xdr(env))
.into();

auth::validate_proof(&env, &data_hash, proof)?;
auth::validate_proof(env, &data_hash, proof)?;

ensure!(!messages.is_empty(), ContractError::EmptyMessages);

Expand All @@ -155,43 +157,45 @@ impl AxelarGatewayInterface for AxelarGateway {
};

// Prevent replay if message is already approved/executed
let message_approval = Self::message_approval_by_key(&env, key.clone());
let message_approval = Self::message_approval_by_key(env, key.clone());
if message_approval != MessageApprovalValue::NotApproved {
continue;
}

env.storage().persistent().set(
&DataKey::MessageApproval(key),
&Self::message_approval_hash(&env, message.clone()),
&Self::message_approval_hash(env, message.clone()),
);

MessageApprovedEvent { message }.emit(&env);
MessageApprovedEvent { message }.emit(env);
}

extend_instance_ttl(&env);
extend_instance_ttl(env);

Ok(())
}

fn rotate_signers(
env: Env,
env: &Env,
signers: WeightedSigners,
proof: Proof,
bypass_rotation_delay: bool,
) -> Result<(), ContractError> {
if bypass_rotation_delay {
Self::operator(&env).require_auth();
Self::operator(env).require_auth();
}

let data_hash: BytesN<32> = signers.signers_rotation_hash(&env);
let data_hash: BytesN<32> = signers.signers_rotation_hash(env);

let is_latest_signers = auth::validate_proof(&env, &data_hash, proof)?;
let is_latest_signers = auth::validate_proof(env, &data_hash, proof)?;
ensure!(
bypass_rotation_delay || is_latest_signers,
ContractError::NotLatestSigners
);

auth::rotate_signers(&env, signers, !bypass_rotation_delay)?;
auth::rotate_signers(env, signers, !bypass_rotation_delay)?;

extend_instance_ttl(env);

Ok(())
}
Expand Down
2 changes: 2 additions & 0 deletions contracts/stellar-axelar-gateway/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@ pub enum ContractError {
OutdatedSigners = 14,
/// Messages
EmptyMessages = 15,
/// Pausable
ContractPaused = 16,
}
4 changes: 2 additions & 2 deletions contracts/stellar-axelar-gateway/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ pub trait AxelarGatewayInterface:
{
/// Approves a collection of messages.
fn approve_messages(
env: Env,
env: &Env,
messages: Vec<Message>,
proof: Proof,
) -> Result<(), ContractError>;

// TODO: add docstring about how bypass_rotation_delay supposed to be used.
fn rotate_signers(
env: Env,
env: &Env,
signers: WeightedSigners,
proof: Proof,
bypass_rotation_delay: bool,
Expand Down
17 changes: 17 additions & 0 deletions contracts/stellar-axelar-gateway/src/tests/gateway.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,23 @@ fn approve_messages_fail_empty_messages() {
);
}

#[test]
fn approve_messages_fails_when_contract_is_paused() {
let (env, signers, client) = setup_env(1, randint(1, 10));

assert_auth!(client.owner(), client.pause());

let (message, _) = generate_test_message(&env);
let messages = vec![&env, message];
let data_hash = get_approve_hash(&env, messages.clone());
let proof = generate_proof(&env, data_hash, signers);

assert_contract_err!(
client.try_approve_messages(&messages, &proof),
ContractError::ContractPaused
);
}

#[test]
fn approve_messages_skip_duplicate_message() {
let (env, signers, client) = setup_env(1, randint(1, 10));
Expand Down

0 comments on commit b97d9d9

Please sign in to comment.