axelar-cgp-sui

Module 0xa1::gateway_v0

use 0x1::ascii;
use 0x1::vector;
use 0x2::address;
use 0x2::bcs;
use 0x2::clock;
use 0x2::hash;
use 0x2::table;
use 0x2::tx_context;
use 0xa1::auth;
use 0xa1::bytes32;
use 0xa1::channel;
use 0xa1::events;
use 0xa1::message;
use 0xa1::message_status;
use 0xa1::message_ticket;
use 0xa1::proof;
use 0xa1::weighted_signers;
use 0xb0::version_control;

Struct Gateway_v0

An object holding the state of the Axelar bridge. The central piece in managing call approval creation and signature verification.

struct Gateway_v0 has store
Fields
operator: address
messages: table::Table<bytes32::Bytes32, message_status::MessageStatus>
signers: auth::AxelarSigners
version_control: version_control::VersionControl

Enum CommandType

public enum CommandType
Variants
Variant ApproveMessages
Variant RotateSigners

Constants

const EMessageNotApproved: vector<u8> = [116, 114, 121, 105, 110, 103, 32, 116, 111, 32, 96, 116, 97, 107, 101, 95, 97, 112, 112, 114, 111, 118, 101, 100, 95, 109, 101, 115, 115, 97, 103, 101, 96, 32, 102, 111, 114, 32, 97, 32, 109, 101, 115, 115, 97, 103, 101, 32, 116, 104, 97, 116, 32, 105, 115, 32, 110, 111, 116, 32, 97, 112, 112, 114, 111, 118, 101, 100];

const ENewerMessage: vector<u8> = [109, 101, 115, 115, 97, 103, 101, 32, 116, 105, 99, 107, 101, 116, 32, 99, 114, 101, 97, 116, 101, 100, 32, 102, 114, 111, 109, 32, 110, 101, 119, 101, 114, 32, 118, 101, 114, 115, 105, 111, 110, 115, 32, 99, 97, 110, 110, 111, 116, 32, 98, 101, 32, 115, 101, 110, 116, 32, 104, 101, 114, 101];

const ENotLatestSigners: vector<u8> = [110, 111, 116, 32, 108, 97, 116, 101, 115, 116, 32, 115, 105, 103, 110, 101, 114, 115];

const EZeroMessages: vector<u8> = [110, 111, 32, 109, 101, 115, 97, 103, 101, 115, 32, 102, 111, 117, 110, 100];

Function new

Init the module by giving a CreatorCap to the sender to allow a full setup.

public(friend) fun new(operator: address, messages: table::Table<bytes32::Bytes32, message_status::MessageStatus>, signers: auth::AxelarSigners, version_control: version_control::VersionControl): gateway_v0::Gateway_v0
Implementation
public(package) fun new(
    operator: address,
    messages: Table<Bytes32, MessageStatus>,
    signers: AxelarSigners,
    version_control: VersionControl,
): Gateway_v0 {
    Gateway_v0 {
        operator,
        messages,
        signers,
        version_control,
    }
}

Function version_control

public(friend) fun version_control(self: &gateway_v0::Gateway_v0): &version_control::VersionControl
Implementation
public(package) fun version_control(self: &Gateway_v0): &VersionControl {
    &self.version_control
}

Function approve_messages

public(friend) fun approve_messages(self: &mut gateway_v0::Gateway_v0, message_data: vector<u8>, proof_data: vector<u8>)
Implementation
public(package) fun approve_messages(
    self: &mut Gateway_v0,
    message_data: vector<u8>,
    proof_data: vector<u8>,
) {
    let proof = utils::peel!(proof_data, |bcs| proof::peel(bcs));
    let messages = peel_messages(message_data);

    let _ = self
        .signers
        .validate_proof(
            data_hash(CommandType::ApproveMessages, message_data),
            proof,
        );

    messages.do!(|message| self.approve_message(message));
}

Function rotate_signers

public(friend) fun rotate_signers(self: &mut gateway_v0::Gateway_v0, clock: &clock::Clock, new_signers_data: vector<u8>, proof_data: vector<u8>, ctx: &tx_context::TxContext)
Implementation
public(package) fun rotate_signers(
    self: &mut Gateway_v0,
    clock: &Clock,
    new_signers_data: vector<u8>,
    proof_data: vector<u8>,
    ctx: &TxContext,
) {
    let weighted_signers = utils::peel!(
        new_signers_data,
        |bcs| weighted_signers::peel(bcs),
    );
    let proof = utils::peel!(proof_data, |bcs| proof::peel(bcs));

    let enforce_rotation_delay = ctx.sender() != self.operator;

    let is_latest_signers = self
        .signers
        .validate_proof(
            data_hash(CommandType::RotateSigners, new_signers_data),
            proof,
        );
    assert!(!enforce_rotation_delay || is_latest_signers, ENotLatestSigners);

    // This will fail if signers are duplicated
    self
        .signers
        .rotate_signers(clock, weighted_signers, enforce_rotation_delay);
}

Function is_message_approved

public(friend) fun is_message_approved(self: &gateway_v0::Gateway_v0, source_chain: ascii::String, message_id: ascii::String, source_address: ascii::String, destination_id: address, payload_hash: bytes32::Bytes32): bool
Implementation
public(package) fun is_message_approved(
    self: &Gateway_v0,
    source_chain: String,
    message_id: String,
    source_address: String,
    destination_id: address,
    payload_hash: Bytes32,
): bool {
    let message = message::new(
        source_chain,
        message_id,
        source_address,
        destination_id,
        payload_hash,
    );
    let command_id = message.command_id();

    self[command_id] == message_status::approved(message.hash())
}

Function is_message_executed

public(friend) fun is_message_executed(self: &gateway_v0::Gateway_v0, source_chain: ascii::String, message_id: ascii::String): bool
Implementation
public(package) fun is_message_executed(
    self: &Gateway_v0,
    source_chain: String,
    message_id: String,
): bool {
    let command_id = message::message_to_command_id(
        source_chain,
        message_id,
    );

    self[command_id] == message_status::executed()
}

Function take_approved_message

To execute a message, the relayer will call take_approved_message to get the hot potato ApprovedMessage object, and then trigger the app’s package via discovery.

public(friend) fun take_approved_message(self: &mut gateway_v0::Gateway_v0, source_chain: ascii::String, message_id: ascii::String, source_address: ascii::String, destination_id: address, payload: vector<u8>): channel::ApprovedMessage
Implementation
public(package) fun take_approved_message(
    self: &mut Gateway_v0,
    source_chain: String,
    message_id: String,
    source_address: String,
    destination_id: address,
    payload: vector<u8>,
): ApprovedMessage {
    let command_id = message::message_to_command_id(source_chain, message_id);

    let message = message::new(
        source_chain,
        message_id,
        source_address,
        destination_id,
        bytes32::from_bytes(hash::keccak256(&payload)),
    );

    assert!(
        self[command_id] == message_status::approved(message.hash()),
        EMessageNotApproved,
    );

    let message_status_ref = &mut self[command_id];
    *message_status_ref = message_status::executed();

    events::message_executed(
        message,
    );

    channel::create_approved_message(
        source_chain,
        message_id,
        source_address,
        destination_id,
        payload,
    )
}

Function send_message

public(friend) fun send_message(_self: &gateway_v0::Gateway_v0, message: message_ticket::MessageTicket, current_version: u64)
Implementation
public(package) fun send_message(
    _self: &Gateway_v0,
    message: MessageTicket,
    current_version: u64,
) {
    let (
        source_id,
        destination_chain,
        destination_address,
        payload,
        version,
    ) = message.destroy();

    assert!(version <= current_version, ENewerMessage);

    events::contract_call(
        source_id,
        destination_chain,
        destination_address,
        payload,
        address::from_bytes(hash::keccak256(&payload)),
    );
}

Function borrow

fun borrow(self: &gateway_v0::Gateway_v0, command_id: bytes32::Bytes32): &message_status::MessageStatus
Implementation
fun borrow(self: &Gateway_v0, command_id: Bytes32): &MessageStatus {
    table::borrow(&self.messages, command_id)
}

Function borrow_mut

fun borrow_mut(self: &mut gateway_v0::Gateway_v0, command_id: bytes32::Bytes32): &mut message_status::MessageStatus
Implementation
fun borrow_mut(self: &mut Gateway_v0, command_id: Bytes32): &mut MessageStatus {
    table::borrow_mut(&mut self.messages, command_id)
}

Function peel_messages

fun peel_messages(message_data: vector<u8>): vector<message::Message>
Implementation
fun peel_messages(message_data: vector<u8>): vector<Message> {
    utils::peel!(message_data, |bcs| {
        let messages = vector::tabulate!(
            bcs.peel_vec_length(),
            |_| message::peel(bcs),
        );
        assert!(messages.length() > 0, EZeroMessages);
        messages
    })
}

Function data_hash

fun data_hash(command_type: gateway_v0::CommandType, data: vector<u8>): bytes32::Bytes32
Implementation
fun data_hash(command_type: CommandType, data: vector<u8>): Bytes32 {
    let mut typed_data = vector::singleton(command_type.as_u8());
    typed_data.append(data);

    bytes32::from_bytes(hash::keccak256(&typed_data))
}

Function approve_message

fun approve_message(self: &mut gateway_v0::Gateway_v0, message: message::Message)
Implementation
fun approve_message(self: &mut Gateway_v0, message: message::Message) {
    let command_id = message.command_id();

    // If the message was already approved, ignore it.
    if (self.messages.contains(command_id)) {
        return
    };

    self
        .messages
        .add(
            command_id,
            message_status::approved(message.hash()),
        );

    events::message_approved(
        message,
    );
}

Function as_u8

fun as_u8(self: gateway_v0::CommandType): u8
Implementation
fun as_u8(self: CommandType): u8 {
    match (self) {
        CommandType::ApproveMessages => 0,
        CommandType::RotateSigners => 1,
    }
}