Skip to content

Commit

Permalink
Merge branch 'main' into fix-testutils-feature-import
Browse files Browse the repository at this point in the history
  • Loading branch information
cgorenflo authored Nov 21, 2024
2 parents 720a5c3 + 3bfc691 commit eac676f
Show file tree
Hide file tree
Showing 25 changed files with 2,109 additions and 326 deletions.
1,185 changes: 981 additions & 204 deletions Cargo.lock

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
[workspace]
resolver = "2"
members = [
"contracts/*",
"integration-tests",
"packages/*",
]
members = ["contracts/*", "integration-tests", "packages/*"]

[workspace.package]
edition = "2021"
Expand All @@ -18,8 +14,12 @@ axelar-gas-service = { version = "^0.1.0", path = "contracts/axelar-gas-service"
axelar-gateway = { version = "^0.1.0", path = "contracts/axelar-gateway" }
axelar-operators = { version = "^0.1.0", path = "contracts/axelar-operators" }
interchain-token-service = { version = "^0.1.0", path = "contracts/interchain-token-service" }
interchain-token = { version = "^0.1.0", path = "contracts/interchain-token" }
example = { version = "^0.1.0", path = "contracts/example" }
alloy-primitives = { version = "0.7.6", default-features = false, features = [
"std",
] }
alloy-sol-types = { version = "0.7.6", default-features = false, features = [
"std",
] }

[workspace.lints.clippy]
nursery = "warn"
Expand Down
78 changes: 62 additions & 16 deletions contracts/axelar-gateway/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use crate::error::ContractError;
use crate::interface::AxelarGatewayInterface;
use crate::messaging_interface::AxelarGatewayMessagingInterface;
use crate::storage_types::{DataKey, MessageApprovalKey, MessageApprovalValue};
use crate::types::{CommandType, Message, Proof, WeightedSigners};
use crate::{auth, event};
use axelar_soroban_std::ensure;
pub use axelar_soroban_std::UpgradeableInterface;
use soroban_sdk::xdr::ToXdr;
use soroban_sdk::{contract, contractimpl, Address, Bytes, BytesN, Env, String, Vec};

use crate::interface::AxelarGatewayInterface;
use crate::storage_types::{DataKey, MessageApprovalKey, MessageApprovalValue};
use crate::{auth, event};

const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");

/// Parameters for extending the contract instance and its instance storage.
Expand All @@ -22,6 +23,24 @@ const INSTANCE_TTL_EXTEND_TO: u32 = 60 * LEDGERS_PER_DAY;
#[contract]
pub struct AxelarGateway;

#[contractimpl]
impl UpgradeableInterface for AxelarGateway {
type Error = ContractError;

fn version(env: Env) -> String {
String::from_str(&env, CONTRACT_VERSION)
}

fn upgrade(env: Env, new_wasm_hash: BytesN<32>) -> Result<(), ContractError> {
Self::owner(&env).require_auth();

env.deployer().update_current_contract_wasm(new_wasm_hash);
Self::start_migration(&env);

Ok(())
}
}

#[contractimpl]
impl AxelarGateway {
/// Initialize the gateway
Expand All @@ -47,10 +66,22 @@ impl AxelarGateway {

Ok(())
}

/// Migrate the contract state after upgrading the contract code. the migration_data type can be adjusted as needed.
pub fn migrate(env: Env, migration_data: ()) -> Result<(), ContractError> {
// This function should not get modified.
// Custom migration logic that changes from version to version should be added in the run_migration function
Self::ensure_is_migrating(&env)?;

Self::run_migration(&env, migration_data);
Self::complete_migration(&env);

Ok(())
}
}

#[contractimpl]
impl AxelarGatewayInterface for AxelarGateway {
impl AxelarGatewayMessagingInterface for AxelarGateway {
fn call_contract(
env: Env,
caller: Address,
Expand Down Expand Up @@ -138,7 +169,9 @@ impl AxelarGatewayInterface for AxelarGateway {

false
}

}
#[contractimpl]
impl AxelarGatewayInterface for AxelarGateway {
fn approve_messages(
env: Env,
messages: Vec<Message>,
Expand Down Expand Up @@ -223,16 +256,6 @@ impl AxelarGatewayInterface for AxelarGateway {
auth::epoch(env)
}

fn version(env: &Env) -> String {
String::from_str(env, CONTRACT_VERSION)
}

fn upgrade(env: Env, new_wasm_hash: BytesN<32>) {
Self::owner(&env).require_auth();

env.deployer().update_current_contract_wasm(new_wasm_hash);
}

fn transfer_ownership(env: Env, new_owner: Address) {
let owner: Address = Self::owner(&env);
owner.require_auth();
Expand Down Expand Up @@ -290,4 +313,27 @@ impl AxelarGateway {
.instance()
.extend_ttl(INSTANCE_TTL_THRESHOLD, INSTANCE_TTL_EXTEND_TO);
}

fn ensure_is_migrating(env: &Env) -> Result<(), ContractError> {
let is_migrating = env
.storage()
.instance()
.get::<DataKey, bool>(&DataKey::Migrating)
.unwrap_or(false);

ensure!(is_migrating, ContractError::MigrationNotAllowed);
Ok(())
}

fn start_migration(env: &Env) {
env.storage().instance().set(&DataKey::Migrating, &true);
}

// Modify this function to add migration logic
#[allow(clippy::missing_const_for_fn)] // exclude no-op implementations from this lint
fn run_migration(_env: &Env, _migration_data: ()) {}

fn complete_migration(env: &Env) {
env.storage().instance().set(&DataKey::Migrating, &false);
}
}
3 changes: 1 addition & 2 deletions contracts/axelar-gateway/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,5 @@ pub enum ContractError {
InvalidEpoch = 11,
// Messages
EmptyMessages = 12,
// Executable
NotApproved = 13,
MigrationNotAllowed = 13,
}
24 changes: 16 additions & 8 deletions contracts/axelar-gateway/src/executable.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
use axelar_soroban_std::ensure;
use soroban_sdk::{contractclient, Address, Bytes, Env, String};

use crate::{error::ContractError, AxelarGatewayClient};
use crate::AxelarGatewayMessagingClient;
use soroban_sdk::contracterror;

#[contracterror]
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
#[repr(u32)]
pub enum ExecutableError {
NotApproved = 1,
}

/// Interface for an Axelar Executable app.
#[contractclient(name = "AxelarExecutableClient")]
pub trait AxelarExecutableInterface {
/// Return the trusted gateway contract id.
fn gateway(env: &Env) -> Address;

/// Execute a cross-chain contract call with the given payload. This function must validate that the contract call is received from the trusted gateway.
/// Execute a cross-chain message with the given payload. This function must validate that the message is received from the trusted gateway.
fn execute(
env: Env,
source_chain: String,
Expand All @@ -18,19 +26,19 @@ pub trait AxelarExecutableInterface {
payload: Bytes,
);

/// Validate if a gateway has approved a contract call.
/// Validate if a gateway has approved a message.
/// This should be called from an implementation of `execute` before executing custom app logic.
/// This method doesn't get exposed from the contract, as Soroban SDK's contractimpl macro ignores default trait methods.
fn validate(
fn validate_message(
env: &Env,
source_chain: &String,
message_id: &String,
source_address: &String,
payload: &Bytes,
) -> Result<(), ContractError> {
let gateway = AxelarGatewayClient::new(env, &Self::gateway(env));
) -> Result<(), ExecutableError> {
let gateway = AxelarGatewayMessagingClient::new(env, &Self::gateway(env));

// Validate the contract call was approved by the gateway
// Validate that the message was approved by the gateway
ensure!(
gateway.validate_message(
&env.current_contract_address(),
Expand All @@ -39,7 +47,7 @@ pub trait AxelarExecutableInterface {
source_address,
&env.crypto().keccak256(payload).into(),
),
ContractError::NotApproved
ExecutableError::NotApproved
);

Ok(())
Expand Down
58 changes: 4 additions & 54 deletions contracts/axelar-gateway/src/interface.rs
Original file line number Diff line number Diff line change
@@ -1,57 +1,13 @@
use soroban_sdk::{contractclient, Address, Bytes, BytesN, Env, String, Vec};

use crate::{
error::ContractError,
types::{Message, Proof, WeightedSigners},
AxelarGatewayMessagingInterface,
};
use axelar_soroban_std::UpgradeableInterface;
use soroban_sdk::{contractclient, Address, BytesN, Env, Vec};

#[contractclient(name = "AxelarGatewayClient")]
pub trait AxelarGatewayInterface {
/// Sends a message to the specified destination chain and contarct address with a given payload.
///
/// This function is the entry point for general message passing between chains.
///
/// A registered chain name on Axelar must be used for `destination_chain`.
fn call_contract(
env: Env,
caller: Address,
destination_chain: String,
destination_address: String,
payload: Bytes,
);

/// Checks if a message is approved
///
/// Determines whether a given message, identified by its `source_chain` and `message_id`, is approved.
///
/// Returns true if a message with the given `payload_hash` is approved.
fn is_message_approved(
env: Env,
source_chain: String,
message_id: String,
source_address: String,
contract_address: Address,
payload_hash: BytesN<32>,
) -> bool;

/// Checks if a message is executed.
///
/// Returns true if the message is executed, false otherwise.
fn is_message_executed(env: Env, source_chain: String, message_id: String) -> bool;

/// Validates if a message is approved. If message was in approved status, status is updated to executed to avoid
/// replay.
///
/// `caller` must be the intended `destination_address` of the contract call for validation to succeed.
fn validate_message(
env: Env,
caller: Address,
source_chain: String,
message_id: String,
source_address: String,
payload_hash: BytesN<32>,
) -> bool;

pub trait AxelarGatewayInterface: AxelarGatewayMessagingInterface + UpgradeableInterface {
/// Approves a collection of messages.
fn approve_messages(
env: Env,
Expand All @@ -76,12 +32,6 @@ pub trait AxelarGatewayInterface {
/// Returns the epoch of the gateway.
fn epoch(env: &Env) -> u64;

/// Returns the version of the gateway.
fn version(env: &Env) -> String;

/// Upgrades the gateway to a new wasm hash.
fn upgrade(env: Env, new_wasm_hash: BytesN<32>);

/// Transfers ownership of the gateway to a new address.
fn transfer_ownership(env: Env, new_owner: Address);

Expand Down
4 changes: 3 additions & 1 deletion contracts/axelar-gateway/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
extern crate std;

pub mod error;
pub mod executable;
mod messaging_interface;
pub mod types;
pub use messaging_interface::{AxelarGatewayMessagingClient, AxelarGatewayMessagingInterface};

pub mod executable;
mod interface;

#[cfg(all(target_family = "wasm", feature = "testutils"))]
Expand Down
49 changes: 49 additions & 0 deletions contracts/axelar-gateway/src/messaging_interface.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use soroban_sdk::{contractclient, Address, Bytes, BytesN, Env, String};

#[contractclient(name = "AxelarGatewayMessagingClient")]
pub trait AxelarGatewayMessagingInterface {
/// Sends a message to the specified destination chain and contarct address with a given payload.
///
/// This function is the entry point for general message passing between chains.
///
/// A registered chain name on Axelar must be used for `destination_chain`.
fn call_contract(
env: Env,
caller: Address,
destination_chain: String,
destination_address: String,
payload: Bytes,
);

/// Checks if a message is approved
///
/// Determines whether a given message, identified by its `source_chain` and `message_id`, is approved.
///
/// Returns true if a message with the given `payload_hash` is approved.
fn is_message_approved(
env: Env,
source_chain: String,
message_id: String,
source_address: String,
contract_address: Address,
payload_hash: BytesN<32>,
) -> bool;

/// Checks if a message is executed.
///
/// Returns true if the message is executed, false otherwise.
fn is_message_executed(env: Env, source_chain: String, message_id: String) -> bool;

/// Validates if a message is approved. If message was in approved status, status is updated to executed to avoid
/// replay.
///
/// `caller` must be the intended `destination_address` of the contract call for validation to succeed.
fn validate_message(
env: Env,
caller: Address,
source_chain: String,
message_id: String,
source_address: String,
payload_hash: BytesN<32>,
) -> bool;
}
1 change: 1 addition & 0 deletions contracts/axelar-gateway/src/storage_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub enum DataKey {
Owner,
Operator,
MessageApproval(MessageApprovalKey),
Migrating,
/// Auth Module
PreviousSignerRetention,
DomainSeparator,
Expand Down
6 changes: 3 additions & 3 deletions contracts/example/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::event;
use axelar_gas_service::AxelarGasServiceClient;
use axelar_gateway::AxelarGatewayClient;
use axelar_gateway::AxelarGatewayMessagingClient;
use axelar_soroban_std::types::Token;
use soroban_sdk::{contract, contractimpl, Address, Bytes, Env, String};

Expand All @@ -24,7 +24,7 @@ impl AxelarExecutableInterface for Example {
source_address: String,
payload: Bytes,
) {
let _ = Self::validate(&env, &source_chain, &message_id, &source_address, &payload);
let _ = Self::validate_message(&env, &source_chain, &message_id, &source_address, &payload);

event::executed(&env, source_chain, message_id, source_address, payload);
}
Expand All @@ -51,7 +51,7 @@ impl Example {
message: Bytes,
gas_token: Token,
) {
let gateway = AxelarGatewayClient::new(&env, &Self::gateway(&env));
let gateway = AxelarGatewayMessagingClient::new(&env, &Self::gateway(&env));
let gas_service = AxelarGasServiceClient::new(&env, &Self::gas_service(&env));

caller.require_auth();
Expand Down
Loading

0 comments on commit eac676f

Please sign in to comment.