From f1998105e01d743e102a2c462d1957942acce828 Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Sun, 26 May 2024 03:28:11 -0400 Subject: [PATCH] refactor(gateway)!: switch to axelar gateway package (#34) * refactor(axelar): remove axelar package * refactor(gateway): switch to new axelar gateway --- README.md | 2 +- move/axelar/Move.toml | 11 - move/axelar/info.json | 6 - move/axelar/sources/channel.move | 324 ----------- move/axelar/sources/discovery.move | 314 ----------- move/axelar/sources/gateway.move | 518 ------------------ move/axelar/sources/utils.move | 140 ----- move/axelar/sources/validators.move | 394 ------------- move/axelar_gateway/sources/discovery.move | 2 +- move/gas_service/Move.toml | 4 +- move/governance/Move.toml | 4 +- .../sources/governance/governance.move | 24 +- move/its/Move.toml | 4 +- move/its/sources/discovery.move | 10 +- move/its/sources/its.move | 10 +- move/its/sources/service.move | 29 +- move/squid/Move.toml | 4 +- move/squid/sources/squid/deepbook_v2.move | 2 +- move/squid/sources/squid/discovery.move | 2 +- move/squid/sources/squid/squid.move | 6 +- move/squid/sources/squid/transfers.move | 2 +- move/test/Move.toml | 4 +- move/test/sources/test/test.move | 15 +- scripts/its/set-trusted-address.js | 28 +- scripts/test-receive-call.js | 28 +- 25 files changed, 90 insertions(+), 1797 deletions(-) delete mode 100644 move/axelar/Move.toml delete mode 100644 move/axelar/info.json delete mode 100644 move/axelar/sources/channel.move delete mode 100644 move/axelar/sources/discovery.move delete mode 100644 move/axelar/sources/gateway.move delete mode 100644 move/axelar/sources/utils.move delete mode 100644 move/axelar/sources/validators.move diff --git a/README.md b/README.md index 1367590a..72d560e8 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ First of all, as we mentioned before, for applications to 'validate' themselves - This calldata has the following 3 fields: - `target`: the target method, in the form of `packag_iId::module_name::function_name`. - `arguments`: an array of arguments that can be: - - `contractCall`: the `ApprovedCall` object (see below). + - `contractCall`: the `ApprovedMessage` object (see below). - `pure:${info}`: a pure argument specified by `$info`. - `obj:${objectId}`: a shared object with the specified `id`. - `typeArguments`: a list of types to be passed to the function called diff --git a/move/axelar/Move.toml b/move/axelar/Move.toml deleted file mode 100644 index d8ee6bb4..00000000 --- a/move/axelar/Move.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "Axelar" -version = "0.0.1" -published-at = "0xc6097c5ea89ff12983f963ff0d6e9b6e6f97026d3f284555c1f3e810809a09bb" -edition = "2024.beta" - -[dependencies] -Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/mainnet" } - -[addresses] -axelar = "0xc6097c5ea89ff12983f963ff0d6e9b6e6f97026d3f284555c1f3e810809a09bb" \ No newline at end of file diff --git a/move/axelar/info.json b/move/axelar/info.json deleted file mode 100644 index 9cb0d05d..00000000 --- a/move/axelar/info.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "singletons": [ - "gateway::Gateway", - "discovery::RelayerDiscovery" - ] -} \ No newline at end of file diff --git a/move/axelar/sources/channel.move b/move/axelar/sources/channel.move deleted file mode 100644 index 97e15dcd..00000000 --- a/move/axelar/sources/channel.move +++ /dev/null @@ -1,324 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -module axelar::channel { - use std::ascii::String; - use sui::table::{Self, Table}; - use sui::event; - - /// Generic target for the messaging system. - /// - /// This struct is required on the Sui side to be the destination for the - /// messages sent from other chains. Even though it has a UID field, it does - /// not have a `key` ability to force wrapping. - /// - /// Notes: - /// - /// - Does not have key to prevent 99% of the mistakes related to access management. - /// Also prevents arbitrary Message destruction if the object is shared. Lastly, - /// when shared, `Channel` cannot be destroyed, and its contents will remain locked - /// forever. - /// - /// - Allows asset or capability-locking inside. Some applications might - /// authorize admin actions through the bridge (eg by locking some `AdminCap` - /// inside and getting a `&mut AdminCap` in the `consume_message`); - /// - /// - Can be destroyed freely as the `UID` is guaranteed to be unique across - /// the system. Destroying a channel would mean the end of the Channel cycle - /// and all further messages will have to target a new Channel if there is one. - /// - /// - Does not contain direct link to the state in Sui, as some functions - /// might not take any specific data (eg allow users to create new objects). - /// If specific object on Sui is targeted by this `Channel`, its reference - /// should be implemented using the `data` field. - /// - /// - The funniest and extremely simple implementation would be a `Channel` - /// since it actually contains the data required to point at the object in Sui. - - /// For when trying to consume the wrong object. - const EWrongDestination: u64 = 0; - /// For when message has already been processed and submitted twice. - const EDuplicateMessage: u64 = 2; - - /// The Channel object. Acts as a destination for the messages sent through - /// the bridge. The `target_id` is compared against the `id` of the `Channel` - /// during the message consumption. - /// - /// The `T` parameter allows wrapping a Capability or a piece of data into - /// the channel to be used when the message is consumed (eg authorize a - /// `mint` call using a stored `AdminCap`). - public struct Channel has key, store { - /// Unique ID of the target object which allows message targeting - /// by comparing against `id_bytes`. - id: UID, - /// Messages processed by this object for the current axelar epoch. To make system less - /// centralized, and spread the storage + io costs across multiple - /// destinations, we can track every `Channel`'s messages. - processed_call_approvals: Table, - } - - /// A HotPotato - call received from the Gateway. Must be delivered to the - /// matching Channel, otherwise the TX fails. - public struct ApprovedCall { - /// ID of the call approval, guaranteed to be unique by Axelar. - cmd_id: address, - /// The target Channel's UID. - target_id: address, - /// Name of the chain where this approval came from. - source_chain: String, - /// Address of the source chain (vector used for compatibility). - /// UTF8 / ASCII encoded string (for 0x0... eth address gonna be 42 bytes with 0x) - source_address: String, - /// Payload of the command. - payload: vector, - } - - // ====== Events ====== - - public struct ChannelCreated has copy, drop { - id: address, - } - - public struct ChannelDestroyed has copy, drop { - id: address, - } - - /// Create new `Channel` object. Anyone can create their own `Channel` to target - /// from the outside and there's no limitation to the data stored inside it. - /// - /// `copy` ability is required to disallow asset locking inside the `Channel`. - public fun new(ctx: &mut TxContext): Channel { - let id = object::new(ctx); - event::emit(ChannelCreated { id: id.uid_to_address() }); - - Channel { - id, - processed_call_approvals: table::new(ctx), - } - } - - /// Destroy a `Channen` releasing the T. Not constrained and can be performed - /// by any party as long as they own a Channel. - public fun destroy(self: Channel) { - let Channel { id, processed_call_approvals } = self; - - processed_call_approvals.drop(); - event::emit(ChannelDestroyed { id: id.uid_to_address() }); - id.delete(); - } - - public fun id(self: &Channel): ID { - object::id(self) - } - - public fun to_address(self: &Channel): address { - object::id_address(self) - } - - /// Create a new `ApprovedCall` object to be sent to another chain. Is called - /// by the gateway when a message is "picked up" by the relayer. - public(package) fun create_approved_call( - cmd_id: address, - source_chain: String, - source_address: String, - target_id: address, - payload: vector, - ): ApprovedCall { - ApprovedCall { - cmd_id, - source_chain, - source_address, - target_id, - payload - } - } - - /// Consume a approved call hot potato object sent to this `Channel` from another chain. - /// For Capability-locking, a mutable reference to the `Channel.data` field is returned. - /// - /// Returns a mutable reference to the locked T, the `source_chain`, the `source_address` - /// and the `payload` to be used by the consuming application. - public fun consume_approved_call( - channel: &mut Channel, - approved_call: ApprovedCall - ): (String, String, vector) { - let ApprovedCall { - cmd_id, - target_id, - source_chain, - source_address, - payload, - } = approved_call; - - // Check if the message has already been processed. - assert!(!channel.processed_call_approvals.contains(cmd_id), EDuplicateMessage); - // Check if the message is sent to the correct destination. - assert!(target_id == object::id_address(channel), EWrongDestination); - - channel.processed_call_approvals.add(cmd_id, true); - - ( - source_chain, - source_address, - payload, - ) - } - - #[test_only] - public fun create_test_approved_call( - cmd_id: address, - source_chain: String, - source_address: String, - target_id: address, - payload: vector, - ): ApprovedCall { - create_approved_call( - cmd_id, - source_chain, - source_address, - target_id, - payload, - ) - } - - #[test] - fun test_new_and_destroy() { - let ctx = &mut sui::tx_context::dummy(); - let channel: Channel = new(ctx); - channel.destroy() - } - - #[test] - fun test_id() { - let ctx = &mut sui::tx_context::dummy(); - let channel: Channel = new(ctx); - assert!(channel.id() == object::id(&channel), 0); - channel.destroy() - } - - - #[test] - fun test_to_address() { - let ctx = &mut sui::tx_context::dummy(); - let channel: Channel = new(ctx); - assert!(channel.to_address() == object::id_address(&channel), 0); - channel.destroy() - } - - #[test] - fun test_create_approved_call() { - let input_cmd_id = @0x1234; - let input_source_chain = std::ascii::string(b"Source Chain"); - let input_source_address = std::ascii::string(b"Source Address"); - let input_target_id = @0x5678; - let input_payload = b"payload"; - let approved_call: ApprovedCall = create_approved_call( - input_cmd_id, - input_source_chain, - input_source_address, - input_target_id, - input_payload - ); - - let ApprovedCall { - cmd_id, - source_chain, - source_address, - target_id, - payload - } = approved_call; - assert!(cmd_id == input_cmd_id, 0); - assert!(source_chain == input_source_chain, 1); - assert!(source_address == input_source_address, 2); - assert!(target_id == input_target_id, 3); - assert!(payload == input_payload, 4); - } - - #[test] - fun test_consume_approved_call() { - let ctx = &mut sui::tx_context::dummy(); - let mut channel: Channel = new(ctx); - - let input_cmd_id = @0x1234; - let input_source_chain = std::ascii::string(b"Source Chain"); - let input_source_address = std::ascii::string(b"Source Address"); - let input_target_id = channel.to_address(); - let input_payload = b"payload"; - let approved_call: ApprovedCall = create_approved_call( - input_cmd_id, - input_source_chain, - input_source_address, - input_target_id, - input_payload, - ); - - let (source_chain, source_address, payload) = consume_approved_call(&mut channel, approved_call); - - - assert!(source_chain == input_source_chain, 1); - assert!(source_address == input_source_address, 2); - assert!(payload == input_payload, 4); - - channel.destroy(); - } - - #[test] - #[expected_failure(abort_code = EDuplicateMessage)] - fun test_consume_approved_call_duplicate_message() { - let ctx = &mut sui::tx_context::dummy(); - let mut channel: Channel = new(ctx); - - let cmd_id = @0x1234; - let source_chain1 = std::ascii::string(b"Source Chain 1"); - let source_address1 = std::ascii::string(b"Source Address 1"); - let target_id1 = channel.to_address(); - let payload1 = b"payload 1"; - let source_chain2 = std::ascii::string(b"Source Chain"); - let source_address2 = std::ascii::string(b"Source Address"); - let target_id2 = channel.to_address(); - let payload2 = b"payload 2"; - - consume_approved_call(&mut channel, create_approved_call( - cmd_id, - source_chain1, - source_address1, - target_id1, - payload1, - )); - - consume_approved_call(&mut channel, create_approved_call( - cmd_id, - source_chain2, - source_address2, - target_id2, - payload2, - )); - - channel.destroy(); - } - - #[test] - #[expected_failure(abort_code = EWrongDestination)] - fun test_consume_approved_call_wrong_destination() { - let ctx = &mut sui::tx_context::dummy(); - let mut channel: Channel = new(ctx); - - let cmd_id = @0x1234; - let source_chain = std::ascii::string(b"Source Chain"); - let source_address = std::ascii::string(b"Source Address"); - let target_id = @0x5678; - let payload = b"payload"; - - let approved_call = create_approved_call( - cmd_id, - source_chain, - source_address, - target_id, - payload, - ); - - consume_approved_call(&mut channel, approved_call); - - channel.destroy(); - } -} diff --git a/move/axelar/sources/discovery.move b/move/axelar/sources/discovery.move deleted file mode 100644 index b5f4c589..00000000 --- a/move/axelar/sources/discovery.move +++ /dev/null @@ -1,314 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -/// This module implements a discovery mechanic for the Relayer to be able to -/// call some (!) transactions automatically. -/// -/// Warning: this solution does allow for any transaction to be executed and -/// should be treated as a reference and a temporary solution until there's a -/// proper discovery / execution mechanism in place. -module axelar::discovery { - use std::ascii::{Self, String}; - use std::type_name; - - use sui::table::{Self, Table}; - use sui::bcs::{Self, BCS}; - use sui::hex; - use sui::address; - - use axelar::channel::Channel; - - /// TypeArgument is not a valid string. - const EInvalidString: u64 = 0; - - /// Channel not found. - const EChannelNotFound: u64 = 1; - - /// A central shared object that stores discovery configuration for the - /// Relayer. The Relayer will use this object to discover and execute the - /// transactions when a message is targeted at specific channel. - public struct RelayerDiscovery has key { - id: UID, - /// A map of channel IDs to the target that needs to be executed by the - /// relayer. There can be only one configuration per channel. - configurations: Table, - } - - public struct Function has store, copy, drop { - package_id: address, - module_name: String, - name: String, - } - - /// Arguments are prefixed with: - /// - 0 for objects followed by exactly 32 bytes that cointain the object id - /// - 1 for pures followed by the bcs encoded form of the pure - /// - 2 for the call contract object, followed by nothing (to be passed into the target function) - /// - 3 for the payload of the contract call (to be passed into the intermediate function) - /// - 4 for an argument returned from a previous move call, followed by a u8 specified which call to get the return of (0 for the first transaction AFTER the one that gets ApprovedCall out), and then another u8 specifying which argument to input. - public struct MoveCall has store, copy, drop { - function: Function, - arguments: vector>, - type_arguments: vector, - } - - public struct Transaction has store, copy, drop { - is_final: bool, - move_calls: vector, - } - - - - fun init(ctx: &mut TxContext) { - transfer::share_object(RelayerDiscovery { - id: object::new(ctx), - configurations: table::new(ctx), - }); - } - - /// During the creation of the object, the UID should be passed here to - /// receive the Channel and emit an event which will be handled by the - /// Relayer. - /// - /// Example: - /// ``` - /// let id = object::new(ctx); - /// let channel = discovery::create_configuration( - /// relayer_discovery, &id, contents, ctx - /// ); - /// let wrapper = ExampleWrapper { id, channel }; - /// transfer::share_object(wrapper); - /// ``` - /// - /// Note: Wrapper must be a shared object so that Relayer can access it. - public fun register_transaction( - self: &mut RelayerDiscovery, - channel: &Channel, - tx: Transaction, - ) { - let channel_id = channel.id(); - if (self.configurations.contains(channel_id)) { - self.configurations.remove(channel_id); - }; - self.configurations.add(channel_id, tx); - } - - /// Get a transaction for a specific channel by the channel `ID`. - public fun get_transaction( - self: &mut RelayerDiscovery, - channel_id: ID, - ): Transaction { - assert!(self.configurations.contains(channel_id), EChannelNotFound); - *self.configurations.borrow(channel_id) - } - - // === Tx Building === - - public fun new_function( - package_id: address, module_name: String, name: String - ): Function { - Function { - package_id, - module_name, - name, - } - } - - public fun new_function_from_bcs(bcs: &mut BCS): Function { - Function { - package_id: bcs::peel_address(bcs), - module_name: ascii::string(bcs::peel_vec_u8(bcs)), - name: ascii::string(bcs::peel_vec_u8(bcs)), - } - } - - public fun new_move_call( - function: Function, - arguments: vector>, - type_arguments: vector - ): MoveCall { - MoveCall { - function, - arguments, - type_arguments, - } - } - - public fun new_move_call_from_bcs(bcs: &mut BCS): MoveCall { - let function = new_function_from_bcs(bcs); - let arguments = bcs.peel_vec_vec_u8(); - let length = bcs.peel_vec_length(); - let mut type_arguments = vector[]; - let mut i = 0; - - while (i < length) { - let mut type_argument = ascii::try_string(bcs.peel_vec_u8()); - assert!(type_argument.is_some(), EInvalidString); - type_arguments.push_back(type_argument.extract()); - i = i + 1; - }; - - MoveCall { - function, - arguments, - type_arguments, - } - } - - public fun new_transaction(is_final: bool, move_calls: vector): Transaction { - Transaction { - is_final, - move_calls, - } - } - - public fun new_transaction_from_bcs(bcs: &mut BCS): Transaction { - let is_final = bcs.peel_bool(); - let length = bcs.peel_vec_length(); - let mut move_calls = vector[]; - let mut i = 0; - - while (i < length) { - move_calls.push_back(new_move_call_from_bcs(bcs)); - i = i + 1; - }; - - Transaction { - is_final, - move_calls, - } - } - - /// Helper function which returns the package id of from a type. - public fun package_id(): address { - address::from_bytes( - hex::decode( - *ascii::as_bytes( - &type_name::get_address(&type_name::get()) - ) - ) - ) - } - - #[test_only] - public fun package_id_from_function(self: &Function): address { - self.package_id - } - - #[test_only] - public fun module_name(self: &Function): ascii::String { - self.module_name - } - - #[test_only] - public fun name(self: &Function): ascii::String { - self.name - } - - #[test_only] - public fun function(self: &MoveCall): Function { - self.function - } - - #[test_only] - public fun arguments(self: &MoveCall): vector> { - self.arguments - } - - #[test_only] - public fun type_arguments(self: &MoveCall): vector { - self.type_arguments - } - - #[test_only] - public fun is_final(self: &Transaction): bool { - self.is_final - } - - #[test_only] - public fun move_calls(self: &Transaction): vector { - self.move_calls - } - - - #[test_only] - public fun new(ctx: &mut TxContext): RelayerDiscovery { - RelayerDiscovery { - id: object::new(ctx), - configurations: table::new(ctx), - } - } - - #[test] - fun tx_builder() { - let function = new_function( - @0x1, - ascii::string(b"ascii"), - ascii::string(b"string"), - ); - - let _tx = function.new_move_call( - vector[ bcs::to_bytes(&b"some_string") ], // arguments - vector[ ], // type_arguments - ); - } - - #[test] - fun test_new_function_from_bcs() { - let package_id = @0x5f7809eb09754577387a816582ece609511d0262b2c52aa15306083ca3c85962; - let module_name = std::ascii::string(b"module"); - let name = std::ascii::string(b"function"); - let input = x"5f7809eb09754577387a816582ece609511d0262b2c52aa15306083ca3c85962066d6f64756c650866756e6374696f6e"; - - let function = new_function_from_bcs(&mut bcs::new(input)); - assert!(function.package_id == package_id, 0); - assert!(function.module_name == module_name, 1); - assert!(function.name == name, 2); - } - - #[test] - fun test_new_transaction_from_bcs() { - let package_id = @0x5f7809eb09754577387a816582ece609511d0262b2c52aa15306083ca3c85962; - let module_name = std::ascii::string(b"module"); - let name = std::ascii::string(b"function"); - let arguments = vector[x"1234", x"5678"]; - let type_arguments = vector[ascii::string(b"type1"), ascii::string(b"type2")]; - let input = x"5f7809eb09754577387a816582ece609511d0262b2c52aa15306083ca3c85962066d6f64756c650866756e6374696f6e0202123402567802057479706531057479706532"; - - let transaction = new_move_call_from_bcs(&mut bcs::new(input)); - assert!(transaction.function.package_id == package_id, 0); - assert!(transaction.function.module_name == module_name, 1); - assert!(transaction.function.name == name, 2); - assert!(transaction.arguments == arguments, 3); - assert!(transaction.type_arguments == type_arguments, 4); - } - - #[test] - fun test_register_and_get() { - let ctx = &mut sui::tx_context::dummy(); - let mut self = new(ctx); - let channel = axelar::channel::new(ctx); - - let move_call = MoveCall { - function: Function { - package_id: @0x1234, - module_name: std::ascii::string(b"module"), - name: std::ascii::string(b"function"), - }, - arguments: vector::empty>(), - type_arguments: vector::empty(), - }; - let input_transaction = Transaction { - is_final: true, - move_calls: vector[move_call], - }; - - self.register_transaction(&channel, input_transaction); - - let transaction = self.get_transaction(channel.id()); - assert!(transaction == input_transaction, 0); - - sui::test_utils::destroy(self); - sui::test_utils::destroy(channel); - } -} diff --git a/move/axelar/sources/gateway.move b/move/axelar/sources/gateway.move deleted file mode 100644 index a26765aa..00000000 --- a/move/axelar/sources/gateway.move +++ /dev/null @@ -1,518 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -#[allow(implicit_const_copy)] -/// Implementation a cross-chain messaging system for Axelar. -/// -/// This code is based on the following: -/// -/// - When call approvals is sent to Sui, it targets an object and not a module; -/// - To support cross-chain messaging, a Channel object has to be created; -/// - Channel can be either owned or shared but not frozen; -/// - Module developer on the Sui side will have to implement a system to support messaging; -/// - Checks for uniqueness of approvals should be done through `Channel`s to avoid big data storage; -/// -/// I. Sending call approvals -/// -/// A approval is sent through the `send` function, a Channel is supplied to determine the source -> ID. -/// Event is then emitted and Axelar network can operate -/// -/// II. Receiving call approvals -/// -/// Approval bytes and signatures are passed into `create` function to generate a CallApproval object. -/// - Signatures are checked against the known set of validators. -/// - CallApproval bytes are parsed to determine: source, destination_chain, payload and target_id -/// - `target_id` points to a `Channel` object -/// -/// Once created, `CallApproval` needs to be consumed. And the only way to do it is by calling -/// `consume_call_approval` function and pass a correct `Channel` instance alongside the `CallApproval`. -/// - CallApproval is checked for uniqueness (for this channel) -/// - CallApproval is checked to match the `Channel`.id -/// -module axelar::gateway { - use std::ascii::{Self, String}; - - use sui::bcs; - use sui::hash; - use sui::table::{Self, Table}; - use sui::address; - - use axelar::utils::{to_sui_signed}; - use axelar::channel::{Self, Channel, ApprovedCall}; - use axelar::validators::{Self, AxelarValidators, validate_proof}; - - /// For when approval signatures failed verification. - // const ESignatureInvalid: u64 = 1; - - /// For when number of commands does not match number of command ids. - const EInvalidCommands: u64 = 4; - - /// For when approval chainId is not SUI. - const EInvalidChain: u64 = 3; - - /// Trying to `take_approved_call` with a wrong payload. - const EPayloadHashMismatch: u64 = 5; - - /// Trying to execute the same operatorhsip transfer command again. - const EAlreadyTransferedOperatorship: u64 = 6; - - /// Trying to set initial validators again - const EAlreadyInitialized: u64 = 6; - - // These are currently supported - const SELECTOR_APPROVE_CONTRACT_CALL: vector = b"approveContractCall"; - const SELECTOR_TRANSFER_OPERATORSHIP: vector = b"transferOperatorship"; - - /// An object holding the state of the Axelar bridge. - /// The central piece in managing call approval creation and signature verification. - public struct Gateway has key { - id: UID, - approvals: Table, - validators: AxelarValidators, - operatorship_transfers: Table, - } - - /// CallApproval struct which can consumed only by a `Channel` object. - /// Does not require additional generic field to operate as linking - /// by `id_bytes` is more than enough. - public struct Approval has store { - /// Hash of the cmd_id, target_id, source_chain, source_address, payload_hash - approval_hash: vector, - } - - /// Emitted when a new message is sent from the SUI network. - public struct ContractCall has copy, drop { - source_id: address, - destination_chain: String, - destination_address: String, - payload: vector, - payload_hash: address, - } - - /// Event: emitted when a new message is approved by the SUI network. - public struct ContractCallApproved has copy, drop { - cmd_id: address, - source_chain: String, - source_address: String, - target_id: address, - payload_hash: address, - } - - - fun init(ctx: &mut TxContext) { - let gateway = Gateway { - id: object::new(ctx), - approvals: table::new(ctx), - validators: validators::new(), - operatorship_transfers: table::new(ctx), - }; - - transfer::share_object(gateway); - } - - public fun set_initial_validators(self: &mut Gateway, payload: vector) { - assert!(self.validators.epoch() == 0, EAlreadyInitialized); - self.validators.transfer_operatorship(payload); - } - - #[allow(implicit_const_copy)] - /// The main entrypoint for the external approval processing. - /// Parses data and attaches call approvals to the Axelar object to be - /// later picked up and consumed by their corresponding Channel. - /// - /// Aborts with multiple error codes, ignores call approval which are not - /// supported by the current implementation of the protocol. - /// - /// Input data must be serialized with BCS (see specification here: https://github.com/diem/bcs). - public entry fun process_commands( - self: &mut Gateway, - input: vector - ) { - let mut bytes = bcs::new(input); - // Split input into: - // data: vector (BCS bytes) - // proof: vector (BCS bytes) - let (data, proof) = ( - bytes.peel_vec_u8(), - bytes.peel_vec_u8() - ); - let mut allow_operatorship_transfer = validate_proof(borrow_validators(self), to_sui_signed(*&data), proof); - - // Treat `data` as BCS bytes. - let mut data_bcs = bcs::new(data); - - // Split data into: - // chain_id: u64, - // command_ids: vector> (vector) - // commands: vector> (vector) - // params: vector> (vector) - let chain_id = data_bcs.peel_u64(); - let command_ids = data_bcs.peel_vec_address(); - let commands = data_bcs.peel_vec_vec_u8(); - let params = data_bcs.peel_vec_vec_u8(); - assert!(chain_id == 1, EInvalidChain); - - let (mut i, commands_len) = (0, commands.length()); - - // make sure number of commands passed matches command IDs - assert!(command_ids.length() == commands_len, EInvalidCommands); - // make sure number of commands passed matches params - assert!(params.length() == commands_len, EInvalidCommands); - - while (i < commands_len) { - // TODO: this does not store executed cmd_ids in the gateway, which make too many assumptions for the axelar network that it shouldn't. - let cmd_id = command_ids[i]; - let cmd_selector = commands[i]; - let payload = params[i]; - i = i + 1; - - // Build a `CallApproval` object from the `params[i]`. BCS serializes data - // in order, so field reads have to be done carefully and in order! - if (cmd_selector == &SELECTOR_APPROVE_CONTRACT_CALL) { - let mut payload = bcs::new(payload); - let (source_chain, source_address, target_id, payload_hash) = ( - ascii::string(payload.peel_vec_u8()), - ascii::string(payload.peel_vec_u8()), - payload.peel_address(), - payload.peel_address() - ); - add_approval(self, - cmd_id, source_chain, source_address, target_id, payload_hash - ); - - sui::event::emit(ContractCallApproved { - cmd_id, source_chain, source_address, target_id, payload_hash - }); - continue - } else if (cmd_selector == &SELECTOR_TRANSFER_OPERATORSHIP) { - if (!allow_operatorship_transfer) { - continue - }; - - assert!(!self.operatorship_transfers.contains(cmd_id), EAlreadyTransferedOperatorship); - self.operatorship_transfers.add(cmd_id, true); - allow_operatorship_transfer = false; - borrow_mut_validators(self).transfer_operatorship(payload); - } else { - continue - }; - }; - } - - /// Most common scenario would be to target a shared object, however this - /// messaging system allows sending private messages which can be consumed - /// by single-owner targets. - /// - /// The hot potato approvel call object is returned. - public fun take_approved_call( - self: &mut Gateway, - cmd_id: address, - source_chain: String, - source_address: String, - target_id: address, - payload: vector - ): ApprovedCall { - let Approval { - approval_hash, - } = table::remove(&mut self.approvals, cmd_id); - - let computed_approval_hash = get_approval_hash( - &cmd_id, - &source_chain, - &source_address, - &target_id, - &address::from_bytes(hash::keccak256(&payload)), - ); - assert!(computed_approval_hash == approval_hash, EPayloadHashMismatch); - - // Friend only. - channel::create_approved_call( - cmd_id, - source_chain, - source_address, - target_id, - payload, - ) - } - - /// Call a contract on the destination chain by sending an event from an - /// authorized Channel. Currently we require Channel to be mutable to prevent - /// frozen object scenario or when someone exposes the Channel to the outer - /// world. However, this restriction may be lifted in the future, and having - /// an immutable reference should be enough. - public fun call_contract( - channel: &Channel, - destination_chain: String, - destination_address: String, - payload: vector - ) { - sui::event::emit(ContractCall { - source_id: object::id_address(channel), - destination_chain, - destination_address, - payload, - payload_hash: address::from_bytes(hash::keccak256(&payload)), - }) - } - - fun get_approval_hash( - cmd_id: &address, - source_chain: &String, - source_address: &String, - target_id: &address, - payload_hash: &address - ): vector { - let mut data = vector[]; - vector::append(&mut data, bcs::to_bytes(cmd_id)); - vector::append(&mut data, bcs::to_bytes(target_id)); - vector::append(&mut data, bcs::to_bytes(source_chain)); - vector::append(&mut data, bcs::to_bytes(source_address)); - vector::append(&mut data, bcs::to_bytes(payload_hash)); - - hash::keccak256(&data) - } - - - fun add_approval( - self: &mut Gateway, - cmd_id: address, - source_chain: String, - source_address: String, - target_id: address, - payload_hash: address - ) { - table::add(&mut self.approvals, cmd_id, Approval { - approval_hash: get_approval_hash( - &cmd_id, - &source_chain, - &source_address, - &target_id, - &payload_hash, - ), - }); - } - - fun borrow_validators(self: &Gateway): &AxelarValidators { - &self.validators - } - - fun borrow_mut_validators(self: &mut Gateway): &mut AxelarValidators { - &mut self.validators - } - - #[test_only] - public fun new(ctx: &mut TxContext): Gateway { - let mut validators = validators::new(); - validators.init_for_testing(); - - Gateway { - id: object::new(ctx), - approvals: table::new(ctx), - validators, - operatorship_transfers: table::new(ctx), - } - } - - #[test_only] - public fun get_approval_params(source_chain: &ascii::String, source_address: &ascii::String, target_id: &address, payload_hash: &address): vector { - let mut bcs = vector::empty(); - vector::append(&mut bcs, bcs::to_bytes(source_chain)); - vector::append(&mut bcs, bcs::to_bytes(source_address)); - vector::append(&mut bcs, bcs::to_bytes(target_id)); - vector::append(&mut bcs, bcs::to_bytes(payload_hash)); - bcs - } - - #[test_only] - public fun get_data(chain_id: &u64, command_ids: &vector
, commands: &vector>, params: &vector>): vector { - let mut bcs = vector::empty(); - vector::append(&mut bcs, bcs::to_bytes(chain_id)); - vector::append(&mut bcs, bcs::to_bytes(command_ids)); - vector::append(&mut bcs, bcs::to_bytes(commands)); - vector::append(&mut bcs, bcs::to_bytes(params)); - bcs - } - - #[test] - fun test_process_commands() { - let ctx = &mut sui::tx_context::dummy(); - let mut gateway = new(ctx); - - let source_chain = ascii::string(b"Source Chain"); - let source_address = ascii::string(b"Source Address"); - let target_id = @0x3; - let payload_hash = @0x4; - let approval_params = get_approval_params(&source_chain, &source_address, &target_id, &payload_hash); - - let new_operators_1 = vector[x"1234", x"5678"]; - let new_weights_1 = vector[1u128, 2u128]; - let new_threshold_1 = 2u128; - let transfer_params_1 = validators::get_transfer_params(&new_operators_1, &new_weights_1, &new_threshold_1); - - let new_operators_2 = vector[x"90ab", x"cdef"]; - let new_weights_2 = vector[3u128, 4u128]; - let new_threshold_2 = 5u128; - let transfer_params_2 = validators::get_transfer_params(&new_operators_2, &new_weights_2, &new_threshold_2); - - let chain_id = 1u64; - let command_ids = vector[@0x1, @0x2, @0x3]; - let commands = vector[SELECTOR_APPROVE_CONTRACT_CALL, SELECTOR_TRANSFER_OPERATORSHIP, SELECTOR_TRANSFER_OPERATORSHIP]; - let params = vector[ - approval_params, - transfer_params_1, - transfer_params_2, - ]; - - let data = get_data(&chain_id, &command_ids, &commands, ¶ms); - let proof = validators::proof_for_testing(); - let mut input = vector[]; - - vector::append(&mut input, bcs::to_bytes(&data)); - vector::append(&mut input, bcs::to_bytes(&proof)); - - assert!(gateway.approvals.contains(@0x1) == false, 0); - assert!(gateway.validators.epoch() == 1, 3); - - process_commands(&mut gateway, input); - - assert!(gateway.approvals.contains(@0x1) == true, 2); - let approval_hash = get_approval_hash( - &@0x1, - &source_chain, - &source_address, - &target_id, - &payload_hash, - ); - - assert!(approval_hash == gateway.approvals.borrow(@0x1).approval_hash, 3); - assert!(gateway.validators.epoch() == 2, 4); - - assert!(gateway.validators.test_contains_operators( - &new_operators_1, - &new_weights_1, - new_threshold_1, - ) == true, 5); - assert!(gateway.validators.test_contains_operators( - &new_operators_2, - &new_weights_2, - new_threshold_2, - ) == false, 6); - assert!(gateway.validators.test_epoch_for_operators( - &new_operators_1, - &new_weights_1, - new_threshold_1, - ) == 2, 7); - - - sui::test_utils::destroy(gateway); - } - - #[test] - #[expected_failure(abort_code = EInvalidChain)] - fun test_process_commands_invalid_chain() { - let ctx = &mut sui::tx_context::dummy(); - let mut gateway = new(ctx); - - let chain_id = 2u64; - let command_ids = vector[]; - let commands = vector[]; - let params = vector[]; - - let data = get_data(&chain_id, &command_ids, &commands, ¶ms); - let proof = validators::proof_for_testing(); - let mut input = vector[]; - - vector::append(&mut input, bcs::to_bytes(&data)); - vector::append(&mut input, bcs::to_bytes(&proof)); - - process_commands(&mut gateway, input); - - sui::test_utils::destroy(gateway); - } - - #[test] - #[expected_failure(abort_code = EInvalidCommands)] - fun test_process_commands_invalid_commands_commands() { - let ctx = &mut sui::tx_context::dummy(); - let mut gateway = new(ctx); - - let chain_id = 1u64; - let command_ids = vector[]; - let commands = vector[SELECTOR_APPROVE_CONTRACT_CALL]; - let params = vector[]; - - let data = get_data(&chain_id, &command_ids, &commands, ¶ms); - let proof = validators::proof_for_testing(); - let mut input = vector[]; - - vector::append(&mut input, bcs::to_bytes(&data)); - vector::append(&mut input, bcs::to_bytes(&proof)); - - process_commands(&mut gateway, input); - - sui::test_utils::destroy(gateway); - } - - #[test] - #[expected_failure(abort_code = EInvalidCommands)] - fun test_process_commands_invalid_commands_params() { - let ctx = &mut sui::tx_context::dummy(); - let mut gateway = new(ctx); - - let chain_id = 1u64; - let command_ids = vector[]; - let commands = vector[]; - let params = vector[x""]; - - let data = get_data(&chain_id, &command_ids, &commands, ¶ms); - let proof = validators::proof_for_testing(); - let mut input = vector[]; - - vector::append(&mut input, bcs::to_bytes(&data)); - vector::append(&mut input, bcs::to_bytes(&proof)); - - process_commands(&mut gateway, input); - - sui::test_utils::destroy(gateway); - } - - #[test] - #[expected_failure(abort_code = sui::dynamic_field::EFieldAlreadyExists)] - fun test_process_same_approval_twice() { - let ctx = &mut sui::tx_context::dummy(); - let mut gateway = new(ctx); - - let source_chain_1 = ascii::string(b"Source Chain 1"); - let source_address_1 = ascii::string(b"Source Address 1"); - let target_id_1 = @0x3; - let payload_hash_1 = @0x4; - let approval_params_1 = get_approval_params(&source_chain_1, &source_address_1, &target_id_1, &payload_hash_1); - - let source_chain_2 = ascii::string(b"Source Chain 2"); - let source_address_2 = ascii::string(b"Source Address 2"); - let target_id_2 = @0x5; - let payload_hash_2 = @0x6; - let approval_params_2 = get_approval_params(&source_chain_2, &source_address_2, &target_id_2, &payload_hash_2); - - let chain_id = 1u64; - let command_ids = vector[@0x1, @0x1]; - let commands = vector[SELECTOR_APPROVE_CONTRACT_CALL, SELECTOR_APPROVE_CONTRACT_CALL]; - let params = vector[ - approval_params_1, - approval_params_2, - ]; - - let data = get_data(&chain_id, &command_ids, &commands, ¶ms); - let proof = validators::proof_for_testing(); - let mut input = vector[]; - - vector::append(&mut input, bcs::to_bytes(&data)); - vector::append(&mut input, bcs::to_bytes(&proof)); - - assert!(gateway.approvals.contains(@0x1) == false, 0); - - process_commands(&mut gateway, input); - - sui::test_utils::destroy(gateway); - } -} diff --git a/move/axelar/sources/utils.move b/move/axelar/sources/utils.move deleted file mode 100644 index 1e5abe53..00000000 --- a/move/axelar/sources/utils.move +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -module axelar::utils { - use sui::bcs; - use sui::hash; - - const EInvalidSignatureLength: u64 = 0; - const EVectorLengthMismatch: u64 = 1; - - /// Prefix for Sui Messages. - const PREFIX: vector = b"\x19Sui Signed Message:\n"; - - /// Normalize last byte of the signature. Have it 1 or 0. - /// See https://tech.mystenlabs.com/cryptography-in-sui-cross-chain-signature-verification/ - public fun normalize_signature(signature: &mut vector) { - // Compute v = 0 or 1. - assert!(vector::length(signature) == 65, EInvalidSignatureLength); - let v = vector::borrow_mut(signature, 64); - if (*v == 27) { - *v = 0; - } else if (*v == 28) { - *v = 1; - } else if (*v > 35) { - *v = (*v - 1) % 2; - }; - } - - /// Add a prefix to the bytes. - public fun to_sui_signed(bytes: vector): vector { - let mut res = vector[]; - vector::append(&mut res, PREFIX); - vector::append(&mut res, bytes); - res - } - - /// Compute operators hash from the list of `operators` (public keys). - /// This hash is used in `Axelar.epoch_for_hash`. - public fun operators_hash(operators: &vector>, weights: &vector, threshold: u128): vector { - let mut data = bcs::to_bytes(operators); - vector::append(&mut data, bcs::to_bytes(weights)); - vector::append(&mut data, bcs::to_bytes(&threshold)); - hash::keccak256(&data) - } - - public fun is_address_vector_zero(v: &vector): bool { - let length = vector::length(v); - let mut i = 0; - while(i < length) { - if(*vector::borrow(v, i) != 0) return false; - i = i + 1; - }; - true - } - - public fun compare_address_vectors(v1: &vector, v2: &vector): bool { - let length = vector::length(v1); - assert!(length == vector::length(v2), EVectorLengthMismatch); - let mut i = 0; - while(i < length) { - let b1 = *vector::borrow(v1, i); - let b2 = *vector::borrow(v2, i); - if(b1 < b2) { - return true - } else if(b1 > b2) { - return false - }; - i = i + 1; - }; - false - } - - #[test] - #[expected_failure(abort_code = EInvalidSignatureLength)] - fun test_normalize_signature() { - let prefix = x"5f7809eb09754577387a816582ece609511d0262b2c52aa15306083ca3c85962066d6f64756c650866756e6374696f6e02021234025678020574797065310574"; - let mut signature = x"5f7809eb09754577387a816582ece609511d0262b2c52aa15306083ca3c85962066d6f64756c650866756e6374696f6e02021234025678020574797065310574"; - let inputs = vector[0, 1, 10, 11, 27, 28, 30, 38, 39]; - let outputs = vector[0, 1, 10, 11, 0, 1, 30, 1, 0]; - - let length = inputs.length(); - let mut i = 0; - while(i < length) { - signature.push_back(inputs[i]); - normalize_signature(&mut signature); - assert!(signature.pop_back() == outputs[i], i); - assert!(signature == prefix, i); - i = i + 1; - }; - - normalize_signature(&mut signature); - } - - #[test] - fun test_to_sui_signed() { - let input = b"012345"; - let output = b"\x19Sui Signed Message:\n012345"; - assert!(to_sui_signed(input) == output, 0); - } - - #[test] - fun test_operators_hash() { - let operators = vector[x"0123", x"4567", x"890a"]; - let weights = vector[1, 3, 6]; - let threshold = 4; - let output = x"dd5d3f9c1017e8356ea1858db7b89800b6cd43775c5c1b7c633f6ef933583cfd"; - - assert!(operators_hash(&operators, &weights, threshold) == output, 0); - } - - #[test] - fun test_is_address_vector_zero() { - assert!(is_address_vector_zero(&x"01") == false, 0); - assert!(is_address_vector_zero(&x"") == true, 0); - assert!(is_address_vector_zero(&x"00") == true, 0); - assert!(is_address_vector_zero(&x"00000000000001") == false, 0); - assert!(is_address_vector_zero(&x"00000000000000") == true, 0); - } - - #[test] - #[expected_failure(abort_code = EVectorLengthMismatch)] - fun test_compare_address_vectors() { - let v1 = &x"000000"; - let v2 = &x"000001"; - let v2copy = &x"000001"; - let v3 = &x"010000"; - - assert!(compare_address_vectors(v1, v2) == true, 0); - assert!(compare_address_vectors(v1, v3) == true, 1); - assert!(compare_address_vectors(v2, v3) == true, 2); - - assert!(compare_address_vectors(v2, v1) == false, 3); - assert!(compare_address_vectors(v3, v2) == false, 4); - assert!(compare_address_vectors(v3, v1) == false, 5); - - assert!(compare_address_vectors(v2, v2copy) == false, 6); - - compare_address_vectors(&x"", &x"01"); - } -} diff --git a/move/axelar/sources/validators.move b/move/axelar/sources/validators.move deleted file mode 100644 index 6a99a4a4..00000000 --- a/move/axelar/sources/validators.move +++ /dev/null @@ -1,394 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -module axelar::validators { - use sui::bcs; - use sui::ecdsa_k1 as ecdsa; - use sui::event; - use sui::vec_map:: {Self, VecMap}; - - use axelar::utils::{normalize_signature, operators_hash, is_address_vector_zero, compare_address_vectors}; - - const EInvalidWeights: u64 = 0; - const EInvalidThreshold: u64 = 1; - /// For when operators have changed, and proof is no longer valid. - const EInvalidOperators: u64 = 2; - // const EDuplicateOperators: u64 = 3; - /// For when number of signatures for the call approvals is below the threshold. - const ELowSignaturesWeight: u64 = 4; - const EMalformedSigners: u64 = 5; - - /// Used for a check in `validate_proof` function. - const OLD_KEY_RETENTION: u64 = 16; - - public struct AxelarValidators has store { - /// Epoch of the validators. - epoch: u64, - /// Epoch for the operators hash. - epoch_for_hash: VecMap, u64>, - } - - /// Emitted when the operatorship changes. - public struct OperatorshipTransferred has copy, drop { - epoch: u64, - payload: vector, - } - - public(package) fun new(): AxelarValidators { - AxelarValidators { - epoch: 0, - epoch_for_hash: vec_map::empty(), - } - } - - /// Implementation of the `AxelarAuthWeighted.validateProof`. - /// Does proof validation, fails when proof is invalid or if weight - /// threshold is not reached. - public(package) fun validate_proof( - validators: &AxelarValidators, - approval_hash: vector, - proof: vector - ): bool { - let epoch = epoch(validators); - - // Turn everything into bcs bytes and split data. - let mut proof = bcs::new(proof); - - let (operators, weights, threshold, signatures) = ( - proof.peel_vec_vec_u8(), - proof.peel_vec_u128(), - proof.peel_u128(), - proof.peel_vec_vec_u8() - ); - - let operators_length = operators.length(); - let operators_epoch = *epoch_for_hash(validators) - .get(&operators_hash(&operators, &weights, threshold)); - - // This error cannot be hit because we remove old operators and no set has an epoch of 0. - assert!(operators_epoch != 0 && epoch - operators_epoch < OLD_KEY_RETENTION, EInvalidOperators); - let (mut i, mut weight, mut operator_index) = (0, 0, 0); - let total_signatures = signatures.length(); - while (i < total_signatures && weight < threshold) { - - let mut signature = signatures[i]; - normalize_signature(&mut signature); - - let signed_by: vector = ecdsa::secp256k1_ecrecover(&signature, &approval_hash, 0); - while (operator_index < operators_length && &signed_by != operators[operator_index]) { - operator_index = operator_index + 1; - }; - - assert!(operator_index < operators_length, EMalformedSigners); - - weight = weight + weights[operator_index]; - - operator_index = operator_index + 1; - - i = i + 1; - }; - - if (weight >= threshold) { return operators_epoch == epoch }; - - abort ELowSignaturesWeight - } - - public(package) fun transfer_operatorship(validators: &mut AxelarValidators, payload: vector) { - let mut bcs = bcs::new(payload); - let new_operators = bcs.peel_vec_vec_u8(); - let new_weights = bcs.peel_vec_u128(); - let new_threshold = bcs.peel_u128(); - - let operators_length = new_operators.length(); - let weight_length = new_weights.length(); - - assert!(operators_length != 0 && is_sorted_asc_and_contains_no_duplicate(&new_operators), EInvalidOperators); - - assert!(weight_length == operators_length, EInvalidWeights); - let (mut total_weight, mut i) = (0, 0); - while (i < weight_length) { - total_weight = total_weight + new_weights[i]; - i = i + 1; - }; - assert!(!(new_threshold == 0 || total_weight < new_threshold), EInvalidThreshold); - - let new_operators_hash = operators_hash(&new_operators, &new_weights, new_threshold); - // Remove old epoch for the operators if it exists - let epoch = validators.epoch() + 1; - let epoch_for_hash = validators.epoch_for_hash_mut(); - // We are likely to change the architecture a bit to conform to our standars better in the future. - if (epoch_for_hash.contains(&new_operators_hash)) { - epoch_for_hash.remove(&new_operators_hash); - }; - - // clean up old epoch - if (epoch >= OLD_KEY_RETENTION && epoch_for_hash.size() > 0) { - let old_epoch = epoch - OLD_KEY_RETENTION; - let (_, epoch) = epoch_for_hash.get_entry_by_idx(0); - if (*epoch <= old_epoch) { - epoch_for_hash.remove_entry_by_idx(0); - }; - }; - epoch_for_hash.insert(new_operators_hash, epoch); - - set_epoch(validators, epoch); - - event::emit(OperatorshipTransferred { - epoch, - payload - }); - } - - // === Getters === - - fun epoch_for_hash(validators: &AxelarValidators): &VecMap, u64> { - &validators.epoch_for_hash - } - - fun epoch_for_hash_mut(validators: &mut AxelarValidators): &mut VecMap, u64> { - &mut validators.epoch_for_hash - } - - fun set_epoch(validators: &mut AxelarValidators, epoch: u64) { - validators.epoch = epoch - } - - public fun epoch(validators: &AxelarValidators): u64 { - validators.epoch - } - - fun is_sorted_asc_and_contains_no_duplicate(accounts: &vector>): bool { - let accountsLength = vector::length(accounts); - let mut prevAccount = vector::borrow(accounts, 0); - - if (is_address_vector_zero(prevAccount)) { - return false - }; - - let mut i = 1; - while (i < accountsLength) { - let currAccount = vector::borrow(accounts, i); - - if (!compare_address_vectors(prevAccount, currAccount)) { - return false - }; - - prevAccount = currAccount; - i = i + 1; - }; - - true - } - - // === Testing === - - #[test_only] - use axelar::utils::to_sui_signed; - - #[test_only] - /// Signer PubKey. - /// Expected to be returned from ecrecover. - const SIGNER: vector = x"037286a4f1177bea06c8e15cf6ec3df0b7747a01ac2329ca2999dfd74eff599028"; - - #[test_only] - public fun proof_for_testing(): vector { - let mut proof: vector = vector[]; - let mut i = 0; - while ( i < 19 ) { - proof.push_back(0); - i = i + 1; - }; - proof - } - - #[test_only] - public fun init_for_testing(self: &mut AxelarValidators) { - let new_operators_hash = operators_hash(&vector[], &vector[], 0); - let epoch = 1; - - self.epoch_for_hash.insert(new_operators_hash, epoch); - - self.set_epoch(epoch); - } - - #[test_only] - public fun get_transfer_params(new_operators: &vector>, new_weights: &vector, new_threshold: &u128): vector { - let mut bcs = vector::empty(); - vector::append(&mut bcs, bcs::to_bytes(new_operators)); - vector::append(&mut bcs, bcs::to_bytes(new_weights)); - vector::append(&mut bcs, bcs::to_bytes(new_threshold)); - bcs - } - - #[test_only] - public fun test_contains_operators(self: &AxelarValidators, operators: &vector>, weights: &vector, threshold: u128): bool { - self.epoch_for_hash.contains( - &operators_hash( - operators, - weights, - threshold, - ) - ) - } - - - #[test_only] - public fun test_epoch_for_operators(self: &AxelarValidators, operators: &vector>, weights: &vector, threshold: u128): u64 { - *self.epoch_for_hash.get( - &operators_hash( - operators, - weights, - threshold, - ) - ) - } - - #[test] - /// Tests `ecrecover`, makes sure external signing process works with Sui ecrecover. - /// Samples for this test are generated with the `presets/` application. - fun test_ecrecover() { - let message = x"68656c6c6f20776f726c64"; // hello world - let mut signature = x"0e88ac153a06d86f28dc0f946654d02302099c0c6558806b569d43f8bd062d5c295beb095e9cc396cd68a6b18daa0f1c0489b778831c4b3bb46f7aa1171c23b101"; - - normalize_signature(&mut signature); - let pubkey = ecdsa::secp256k1_ecrecover(&signature, &to_sui_signed(message), 0); - - assert!(pubkey == SIGNER, 0); - } - - #[test] - /// Tests "Sui Signed Message" prefix addition ecrecover. - /// Checks if the signature generated outside matches the message generated in this module. - /// Samples for this test are generated with the `presets/` application. - fun test_to_signed() { - let message = b"hello world"; - let mut signature = x"0e88ac153a06d86f28dc0f946654d02302099c0c6558806b569d43f8bd062d5c295beb095e9cc396cd68a6b18daa0f1c0489b778831c4b3bb46f7aa1171c23b101"; - normalize_signature(&mut signature); - - let pub_key = ecdsa::secp256k1_ecrecover(&signature, &to_sui_signed(message), 0); - assert!(pub_key == SIGNER, 0); - } - - #[test] - fun test_transfer_operatorship() { - let mut validators = new(); - - let operators = vector[x"0123", x"4567", x"890a"]; - let weights = vector[1, 3, 6]; - let threshold = 4; - let payload = x"0302012302456702890a0301000000000000000000000000000000030000000000000000000000000000000600000000000000000000000000000004000000000000000000000000000000"; - - validators.transfer_operatorship(payload); - - let epoch = validators.epoch_for_hash.get(&operators_hash(&operators, &weights, threshold)); - - assert!(*epoch == 1, 0); - - sui::test_utils::destroy(validators); - } - - #[test] - #[expected_failure(abort_code = EInvalidOperators)] - fun test_transfer_operatorship_zero_operator_length() { - let mut validators = new(); - - let payload = x"000301000000000000000000000000000000030000000000000000000000000000000600000000000000000000000000000004000000000000000000000000000000"; - - validators.transfer_operatorship(payload); - - sui::test_utils::destroy(validators); - } - - #[test] - #[expected_failure(abort_code = EInvalidOperators)] - fun test_transfer_operatorship_unsorted_operatros() { - let mut validators = new(); - - let payload = x"0302456702012302890a0301000000000000000000000000000000030000000000000000000000000000000600000000000000000000000000000004000000000000000000000000000000"; - - validators.transfer_operatorship(payload); - - sui::test_utils::destroy(validators); - } - - #[test] - #[expected_failure(abort_code = EInvalidOperators)] - fun test_transfer_operatorship_duplicate_operatros() { - let mut validators = new(); - - let payload = x"0302012302890a02890a0301000000000000000000000000000000030000000000000000000000000000000600000000000000000000000000000004000000000000000000000000000000"; - - validators.transfer_operatorship(payload); - - sui::test_utils::destroy(validators); - } - - #[test] - #[expected_failure(abort_code = EInvalidWeights)] - fun test_transfer_operatorship_invalid_weights() { - let mut validators = new(); - - let payload = x"0302012302456702890a02010000000000000000000000000000000300000000000000000000000000000004000000000000000000000000000000"; - - validators.transfer_operatorship(payload); - - sui::test_utils::destroy(validators); - } - - #[test] - #[expected_failure(abort_code = EInvalidThreshold)] - fun test_transfer_operatorship_zero_threshold() { - let mut validators = new(); - - let payload = x"0302012302456702890a0301000000000000000000000000000000030000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000"; - - validators.transfer_operatorship(payload); - - sui::test_utils::destroy(validators); - } - - #[test] - #[expected_failure(abort_code = EInvalidThreshold)] - fun test_transfer_operatorship_threshold_too_high() { - let mut validators = new(); - - let payload = x"0302012302456702890a030100000000000000000000000000000003000000000000000000000000000000060000000000000000000000000000000b000000000000000000000000000000"; - - validators.transfer_operatorship(payload); - - sui::test_utils::destroy(validators); - } - - #[test] - fun test_validate_proof() { - let mut validators = new(); - - let message = x"123456"; - let payload = x"032102dd7312374396c51c50f95e0c1f370435292de4809b755aca09b49fcd8d0fe9c02103595d141e66c2c1e8e0c114b71ddc9db53a65743e7679a02a4c8c71af16d4522821039494a3cde8ae663d21a0b8692549c56887901c7e4529b0fdb6ce3d39b382bea10303000000000000000000000000000000030000000000000000000000000000000300000000000000000000000000000006000000000000000000000000000000"; - let proof = x"032102dd7312374396c51c50f95e0c1f370435292de4809b755aca09b49fcd8d0fe9c02103595d141e66c2c1e8e0c114b71ddc9db53a65743e7679a02a4c8c71af16d4522821039494a3cde8ae663d21a0b8692549c56887901c7e4529b0fdb6ce3d39b382bea1030300000000000000000000000000000003000000000000000000000000000000030000000000000000000000000000000600000000000000000000000000000003413de59beca835483688338964eb4c314f387e06aef6c46ca2dc90733e5b7baa9b67b9b8530aacaae4263e369fced014e449166441c21b61fcef5978516d1a740301417b6940537f7fa65d37d0964d5dda49b80b5b7fcde93ba3b3224c3e007ff887ee20a203ac52802c29238353b69636cb71bd1da3bdb0c3ac3d85938531f94dd7570041529af0061fa6321419e0b702dd1ac4e16610efa718ad241e4eda8b65dd92bd2e715cfd58951305f6fc4d75a20d2c19bd4491312cff38b9694b02e2175826a2c800"; - let payload2 = x"032102dd7312374396c51c50f95e0c1f370435292de4809b755aca09b49fcd8d0fe9c02103595d141e66c2c1e8e0c114b71ddc9db53a65743e7679a02a4c8c71af16d4522821039494a3cde8ae663d21a0b8692549c56887901c7e4529b0fdb6ce3d39b382bea10303000000000000000000000000000000030000000000000000000000000000000300000000000000000000000000000007000000000000000000000000000000"; - - validators.transfer_operatorship(payload); - assert!(validators.validate_proof(to_sui_signed(message), proof) == true, 0); - - validators.transfer_operatorship(payload2); - assert!(validators.validate_proof(to_sui_signed(message), proof) == false, 0); - - sui::test_utils::destroy(validators); - } - - #[test] - #[expected_failure(abort_code = EMalformedSigners)] - fun test_validate_proof_malformed_signers() { - let mut validators = new(); - - let message = x"1234"; - let payload = x"032102dd7312374396c51c50f95e0c1f370435292de4809b755aca09b49fcd8d0fe9c02103595d141e66c2c1e8e0c114b71ddc9db53a65743e7679a02a4c8c71af16d4522821039494a3cde8ae663d21a0b8692549c56887901c7e4529b0fdb6ce3d39b382bea10303000000000000000000000000000000030000000000000000000000000000000300000000000000000000000000000006000000000000000000000000000000"; - let proof = x"032102dd7312374396c51c50f95e0c1f370435292de4809b755aca09b49fcd8d0fe9c02103595d141e66c2c1e8e0c114b71ddc9db53a65743e7679a02a4c8c71af16d4522821039494a3cde8ae663d21a0b8692549c56887901c7e4529b0fdb6ce3d39b382bea1030300000000000000000000000000000003000000000000000000000000000000030000000000000000000000000000000600000000000000000000000000000003413de59beca835483688338964eb4c314f387e06aef6c46ca2dc90733e5b7baa9b67b9b8530aacaae4263e369fced014e449166441c21b61fcef5978516d1a740301417b6940537f7fa65d37d0964d5dda49b80b5b7fcde93ba3b3224c3e007ff887ee20a203ac52802c29238353b69636cb71bd1da3bdb0c3ac3d85938531f94dd7570041529af0061fa6321419e0b702dd1ac4e16610efa718ad241e4eda8b65dd92bd2e715cfd58951305f6fc4d75a20d2c19bd4491312cff38b9694b02e2175826a2c800"; - - validators.transfer_operatorship(payload); - validators.validate_proof(to_sui_signed(message), proof); - - sui::test_utils::destroy(validators); - } -} diff --git a/move/axelar_gateway/sources/discovery.move b/move/axelar_gateway/sources/discovery.move index 2e6ed669..cb89e0cf 100644 --- a/move/axelar_gateway/sources/discovery.move +++ b/move/axelar_gateway/sources/discovery.move @@ -42,7 +42,7 @@ module axelar_gateway::discovery { /// - 1 for pures followed by the bcs encoded form of the pure /// - 2 for the call contract object, followed by nothing (to be passed into the target function) /// - 3 for the payload of the contract call (to be passed into the intermediate function) - /// - 4 for an argument returned from a previous move call, followed by a u8 specified which call to get the return of (0 for the first transaction AFTER the one that gets ApprovedCall out), and then another u8 specifying which argument to input. + /// - 4 for an argument returned from a previous move call, followed by a u8 specified which call to get the return of (0 for the first transaction AFTER the one that gets ApprovedMessage out), and then another u8 specifying which argument to input. public struct MoveCall has store, copy, drop { function: Function, arguments: vector>, diff --git a/move/gas_service/Move.toml b/move/gas_service/Move.toml index 86729653..d4c819b5 100644 --- a/move/gas_service/Move.toml +++ b/move/gas_service/Move.toml @@ -6,7 +6,7 @@ edition = "2024.beta" [dependencies] Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/mainnet" } -Axelar = { local = "../axelar" } +AxelarGateway = { local = "../axelar_gateway" } [addresses] -gas_service = "0x83187fc09392a9d547acd16caeca3bf8662452d75d1853d5a454074103161161" \ No newline at end of file +gas_service = "0x83187fc09392a9d547acd16caeca3bf8662452d75d1853d5a454074103161161" diff --git a/move/governance/Move.toml b/move/governance/Move.toml index 00ac310b..1c21d4fe 100644 --- a/move/governance/Move.toml +++ b/move/governance/Move.toml @@ -6,8 +6,8 @@ edition = "2024.beta" [dependencies] Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/mainnet" } -Axelar = { local = "../axelar" } +AxelarGateway = { local = "../axelar_gateway" } Abi = { local = "../abi" } [addresses] -governance = "0x927d8379a35d435db5b95daa6e624eea047f71832fadc0ed5d97640a711e601e" \ No newline at end of file +governance = "0x927d8379a35d435db5b95daa6e624eea047f71832fadc0ed5d97640a711e601e" diff --git a/move/governance/sources/governance/governance.move b/move/governance/sources/governance/governance.move index 9e8f5cf3..c0349985 100644 --- a/move/governance/sources/governance/governance.move +++ b/move/governance/sources/governance/governance.move @@ -8,7 +8,7 @@ module governance::governance { use sui::hex; use abi::abi; - use axelar::channel::{Self, Channel, ApprovedCall}; + use axelar_gateway::channel::{Self, Channel, ApprovedMessage}; const EUntrustedAddress: u64 = 0; const EInvalidMessageType: u64 = 1; @@ -50,11 +50,11 @@ module governance::governance { caps: table::new(ctx), }) } - + public fun is_governance( - self: &Governance, - chain_name: String, + self: &Governance, + chain_name: String, addr: String ): bool{ &chain_name == &self.trusted_source_chain && &addr == &self.trusted_source_address @@ -70,8 +70,8 @@ module governance::governance { ) } - public fun authorize_upgrade(self: &mut Governance, approved_call: ApprovedCall): UpgradeTicket { - let (source_chain, source_address, payload) = self.channel.consume_approved_call(approved_call); + public fun authorize_upgrade(self: &mut Governance, approved_message: ApprovedMessage): UpgradeTicket { + let (source_chain, _, source_address, payload) = self.channel.consume_approved_message(approved_message); assert!(is_governance(self, source_chain, source_address), EUntrustedAddress); @@ -84,8 +84,8 @@ module governance::governance { let digest = abi.read_bytes(3); package::authorize_upgrade( - table::borrow_mut(&mut self.caps, cap_id), - policy, + table::borrow_mut(&mut self.caps, cap_id), + policy, digest, ) } @@ -96,15 +96,15 @@ module governance::governance { ) { package::commit_upgrade( table::borrow_mut( - &mut self.caps, + &mut self.caps, package::receipt_cap(&receipt), - ), + ), receipt ) } - + fun is_cap_new(cap: &UpgradeCap) { assert!(package::version(cap) == 1, ENotNewPackage); } -} \ No newline at end of file +} diff --git a/move/its/Move.toml b/move/its/Move.toml index b731b756..806805d3 100644 --- a/move/its/Move.toml +++ b/move/its/Move.toml @@ -7,8 +7,8 @@ edition = "2024.beta" [dependencies] Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/mainnet" } -Axelar = { local = "../axelar" } +AxelarGateway = { local = "../axelar_gateway" } Governance = { local = "../governance" } [addresses] -its = "0x6897eff98e4db46b53131a73baf9e705647bd9c337df3f23d702616fbadf3093" \ No newline at end of file +its = "0x6897eff98e4db46b53131a73baf9e705647bd9c337df3f23d702616fbadf3093" diff --git a/move/its/sources/discovery.move b/move/its/sources/discovery.move index 43e76d9a..ed31ed9b 100644 --- a/move/its/sources/discovery.move +++ b/move/its/sources/discovery.move @@ -9,7 +9,7 @@ module its::discovery { use abi::abi::{Self, AbiReader}; - use axelar::discovery::{Self, RelayerDiscovery, Transaction, package_id}; + use axelar_gateway::discovery::{Self, RelayerDiscovery, Transaction, package_id}; use its::its::ITS; use its::token_id::{Self, TokenId}; @@ -191,7 +191,7 @@ module its::discovery { fun test_discovery_initial() { let ctx = &mut sui::tx_context::dummy(); let mut its = its::its::new(); - let mut discovery = axelar::discovery::new(ctx); + let mut discovery = axelar_gateway::discovery::new(ctx); register_transaction(&mut its, &mut discovery); @@ -206,7 +206,7 @@ module its::discovery { fun test_discovery_interchain_transfer() { let ctx = &mut sui::tx_context::dummy(); let mut its = its::its::new(); - let mut discovery = axelar::discovery::new(ctx); + let mut discovery = axelar_gateway::discovery::new(ctx); register_transaction(&mut its, &mut discovery); @@ -253,7 +253,7 @@ module its::discovery { fun test_discovery_interchain_transfer_with_data() { let ctx = &mut sui::tx_context::dummy(); let mut its = its::its::new(); - let mut discovery = axelar::discovery::new(ctx); + let mut discovery = axelar_gateway::discovery::new(ctx); register_transaction(&mut its, &mut discovery); @@ -291,7 +291,7 @@ module its::discovery { fun test_discovery_deploy_token() { let ctx = &mut sui::tx_context::dummy(); let mut its = its::its::new(); - let mut discovery = axelar::discovery::new(ctx); + let mut discovery = axelar_gateway::discovery::new(ctx); register_transaction(&mut its, &mut discovery); diff --git a/move/its/sources/its.move b/move/its/sources/its.move index 9b5d042a..0bf03a3e 100644 --- a/move/its/sources/its.move +++ b/move/its/sources/its.move @@ -9,8 +9,8 @@ module its::its { use sui::table::{Self, Table}; use sui::coin::{TreasuryCap, CoinMetadata}; - use axelar::channel::Channel; - use axelar::discovery::RelayerDiscovery; + use axelar_gateway::channel::Channel; + use axelar_gateway::discovery::RelayerDiscovery; use its::token_id::{Self, TokenId, UnregisteredTokenId}; use its::address_tracker::{Self, InterchainAddressTracker}; @@ -35,7 +35,7 @@ module its::its { relayer_discovery_id: ID, } - + public struct CoinData has store { coin_management: CoinManagement, coin_info: CoinInfo, @@ -49,7 +49,7 @@ module its::its { fun init(ctx: &mut TxContext) { transfer::share_object(ITS { id: object::new(ctx), - channel: axelar::channel::new(ctx), + channel: axelar_gateway::channel::new(ctx), address_tracker: address_tracker::new( ctx, @@ -208,7 +208,7 @@ module its::its { let ctx = &mut sui::tx_context::dummy(); ITS { id: object::new(ctx), - channel: axelar::channel::new(ctx), + channel: axelar_gateway::channel::new(ctx), address_tracker: address_tracker::new( ctx, diff --git a/move/its/sources/service.move b/move/its/sources/service.move index 603a5b79..bbea4433 100644 --- a/move/its/sources/service.move +++ b/move/its/sources/service.move @@ -10,7 +10,7 @@ module its::service { use abi::abi; - use axelar::channel::{Self, ApprovedCall}; + use axelar_gateway::channel::{Self, ApprovedMessage}; use governance::governance::{Self, Governance}; @@ -20,8 +20,8 @@ module its::service { use its::coin_management::{Self, CoinManagement}; use its::utils as its_utils; - use axelar::gateway; - use axelar::channel::Channel; + use axelar_gateway::gateway; + use axelar_gateway::channel::Channel; const MESSAGE_TYPE_INTERCHAIN_TRANSFER: u256 = 0; const MESSAGE_TYPE_DEPLOY_INTERCHAIN_TOKEN: u256 = 1; @@ -102,8 +102,8 @@ module its::service { send_payload(self, destination_chain, writer.into_bytes()); } - public fun receive_interchain_transfer(self: &mut ITS, approved_call: ApprovedCall, ctx: &mut TxContext) { - let (_, payload) = decode_approved_call(self, approved_call); + public fun receive_interchain_transfer(self: &mut ITS, approved_message: ApprovedMessage, ctx: &mut TxContext) { + let (_, payload) = decode_approved_message(self, approved_message); let reader = abi::new_reader(payload); assert!(reader.read_u256(0) == MESSAGE_TYPE_INTERCHAIN_TRANSFER, EInvalidMessageType); assert!(reader.read_bytes(5).is_empty(), EInterchainTransferHasData); @@ -121,11 +121,11 @@ module its::service { public fun receive_interchain_transfer_with_data( self: &mut ITS, - approved_call: ApprovedCall, + approved_message: ApprovedMessage, channel: &Channel, ctx: &mut TxContext ): (String, vector, vector, Coin) { - let (source_chain, payload) = decode_approved_call(self, approved_call); + let (source_chain, payload) = decode_approved_message(self, approved_message); let reader = abi::new_reader(payload); assert!(reader.read_u256(0) == MESSAGE_TYPE_INTERCHAIN_TRANSFER, EInvalidMessageType); assert!(address::from_bytes(reader.read_bytes(3)) == channel.to_address(), EWrongDestination); @@ -149,8 +149,8 @@ module its::service { ) } - public fun receive_deploy_interchain_token(self: &mut ITS, approved_call: ApprovedCall) { - let (_, payload) = decode_approved_call(self, approved_call); + public fun receive_deploy_interchain_token(self: &mut ITS, approved_message: ApprovedMessage) { + let (_, payload) = decode_approved_message(self, approved_message); let reader = abi::new_reader(payload); assert!(reader.read_u256(0) == MESSAGE_TYPE_DEPLOY_INTERCHAIN_TOKEN, EInvalidMessageType); @@ -227,9 +227,9 @@ module its::service { } // === Special Call Receiving - public fun set_trusted_addresses(its: &mut ITS, governance: &Governance, approved_call: ApprovedCall) { - let (source_chain, source_address, payload) = channel::consume_approved_call( - its::channel_mut(its), approved_call + public fun set_trusted_addresses(its: &mut ITS, governance: &Governance, approved_message: ApprovedMessage) { + let (source_chain, _, source_address, payload) = channel::consume_approved_message( + its::channel_mut(its), approved_message ); assert!(governance::is_governance(governance, source_chain, source_address), EUntrustedAddress); @@ -260,12 +260,13 @@ module its::service { // === Internal functions === /// Decode an approved call and check that the source chain is trusted. - fun decode_approved_call(self: &mut ITS, approved_call: ApprovedCall): (String, vector) { + fun decode_approved_message(self: &mut ITS, approved_message: ApprovedMessage): (String, vector) { let ( source_chain, + _, source_address, payload - ) = self.channel_mut().consume_approved_call(approved_call); + ) = self.channel_mut().consume_approved_message(approved_message); assert!(self.is_trusted_address(source_chain, source_address), EUntrustedAddress); diff --git a/move/squid/Move.toml b/move/squid/Move.toml index 0ce2dfed..b2bef556 100644 --- a/move/squid/Move.toml +++ b/move/squid/Move.toml @@ -7,8 +7,8 @@ edition = "2024.beta" [dependencies] Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/mainnet" } DeepBook = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/deepbook", rev = "framework/mainnet" } -Axelar = { local = "../axelar" } +AxelarGateway = { local = "../axelar_gateway" } ITS = { local = "../its" } [addresses] -squid = "0xf94eccf537d2dc1a8decfc77c0da01bb83d840c37f768f868d81092ca123456a" \ No newline at end of file +squid = "0xf94eccf537d2dc1a8decfc77c0da01bb83d840c37f768f868d81092ca123456a" diff --git a/move/squid/sources/squid/deepbook_v2.move b/move/squid/sources/squid/deepbook_v2.move index 1f7ef32b..1c39968a 100644 --- a/move/squid/sources/squid/deepbook_v2.move +++ b/move/squid/sources/squid/deepbook_v2.move @@ -10,7 +10,7 @@ module squid::deepbook_v2 { use deepbook::custodian_v2::{Self as custodian}; use deepbook::math as clob_math; - use axelar::discovery::{Self, MoveCall}; + use axelar_gateway::discovery::{Self, MoveCall}; use squid::swap_info::{SwapInfo}; use squid::squid::Squid; diff --git a/move/squid/sources/squid/discovery.move b/move/squid/sources/squid/discovery.move index 18c28d68..39e87ebc 100644 --- a/move/squid/sources/squid/discovery.move +++ b/move/squid/sources/squid/discovery.move @@ -3,7 +3,7 @@ module squid::discovery { use sui::bcs; - use axelar::discovery::{Self, RelayerDiscovery, MoveCall, Transaction}; + use axelar_gateway::discovery::{Self, RelayerDiscovery, MoveCall, Transaction}; use its::its::ITS; diff --git a/move/squid/sources/squid/squid.move b/move/squid/sources/squid/squid.move index c3ce5f6e..3bd524a3 100644 --- a/move/squid/sources/squid/squid.move +++ b/move/squid/sources/squid/squid.move @@ -1,5 +1,5 @@ module squid::squid { - use axelar::channel::{Self, Channel, ApprovedCall}; + use axelar_gateway::channel::{Self, Channel, ApprovedMessage}; use its::service; use its::its::ITS; @@ -25,10 +25,10 @@ module squid::squid { &self.channel } - public fun start_swap(self: &mut Squid, its: &mut ITS, approved_call: ApprovedCall, ctx: &mut TxContext): SwapInfo { + public fun start_swap(self: &mut Squid, its: &mut ITS, approved_message: ApprovedMessage, ctx: &mut TxContext): SwapInfo { let (_, _, data, coin) = service::receive_interchain_transfer_with_data( its, - approved_call, + approved_message, &self.channel, ctx, ); diff --git a/move/squid/sources/squid/transfers.move b/move/squid/sources/squid/transfers.move index 7e9e7951..a362d38b 100644 --- a/move/squid/sources/squid/transfers.move +++ b/move/squid/sources/squid/transfers.move @@ -5,7 +5,7 @@ module squid::transfers { use sui::bcs::{Self, BCS}; use sui::coin; - use axelar::discovery::{Self, MoveCall}; + use axelar_gateway::discovery::{Self, MoveCall}; use its::service; use its::its::ITS; diff --git a/move/test/Move.toml b/move/test/Move.toml index 12661116..2c80639a 100644 --- a/move/test/Move.toml +++ b/move/test/Move.toml @@ -6,7 +6,7 @@ edition = "2024.alpha" [dependencies] Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/mainnet" } -Axelar = { local = "../axelar" } +AxelarGateway = { local = "../axelar_gateway" } [addresses] -test = "0xc47f50df280836b74a20a862c71a189e9819ba7305173ef99858c095bba373d7" \ No newline at end of file +test = "0xc47f50df280836b74a20a862c71a189e9819ba7305173ef99858c095bba373d7" diff --git a/move/test/sources/test/test.move b/move/test/sources/test/test.move index 09728264..690ceed7 100644 --- a/move/test/sources/test/test.move +++ b/move/test/sources/test/test.move @@ -7,10 +7,10 @@ module test::test { use sui::address; use sui::hex; - use axelar::channel::{Self, Channel, ApprovedCall}; - use axelar::discovery::{Self, RelayerDiscovery}; + use axelar_gateway::channel::{Self, Channel, ApprovedMessage}; + use axelar_gateway::discovery::{Self, RelayerDiscovery}; - use axelar::gateway; + use axelar_gateway::gateway; public struct Singleton has key { id: UID, @@ -58,15 +58,14 @@ module test::test { gateway::call_contract(&singleton.channel, destination_chain, destination_address, payload); } - public fun execute(call: ApprovedCall, singleton: &mut Singleton) { + public fun execute(call: ApprovedMessage, singleton: &mut Singleton) { let ( + _, _, _, payload, - ) = channel::consume_approved_call( - &mut singleton.channel, - call, - ); + ) = singleton.channel.consume_approved_message(call); + event::emit(Executed { data: payload }); } } diff --git a/scripts/its/set-trusted-address.js b/scripts/its/set-trusted-address.js index 9025ab27..6772d54f 100644 --- a/scripts/its/set-trusted-address.js +++ b/scripts/its/set-trusted-address.js @@ -18,7 +18,7 @@ async function setTrustedAddresses(client, keypair, envAlias, chainNames, truste const axelarPackageId = axelarInfo.packageId; const governance = getConfig('governance', envAlias)['governance::Governance']; - + const bcs = new BCS(getSuiMoveConfig()); bcs.registerStructType("TrustedAddressInfo", { chainNames: "vector", @@ -34,21 +34,21 @@ async function setTrustedAddresses(client, keypair, envAlias, chainNames, truste const payloadHash = keccak256(payload); const commandId = await approveContractCall( - client, + client, keypair, - axelarInfo, - governance.trusted_source_chain, - governance.trusted_source_address, - itsInfo['its::ITS'].channel, + axelarInfo, + governance.trusted_source_chain, + governance.trusted_source_address, + itsInfo['its::ITS'].channel, payloadHash, ); let tx = new TransactionBlock(); - const approvedCall = tx.moveCall({ - target: `${axelarPackageId}::gateway::take_approved_call`, + const ApprovedMessage = tx.moveCall({ + target: `${axelarPackageId}::gateway::take_approved_message`, arguments: [ - tx.object(axelarInfo['gateway::Gateway'].objectId), + tx.object(axelarInfo['gateway::Gateway'].objectId), tx.pure.address(commandId), tx.pure.string(governance.trusted_source_chain), tx.pure.string(governance.trusted_source_address), @@ -60,9 +60,9 @@ async function setTrustedAddresses(client, keypair, envAlias, chainNames, truste tx.moveCall({ target: `${itsPackageId}::service::set_trusted_addresses`, arguments: [ - tx.object(itsObjectId), + tx.object(itsObjectId), tx.object(governance.objectId), - approvedCall, + ApprovedMessage, ], typeArguments: [], }); @@ -87,9 +87,9 @@ if (require.main === module) { const env = process.argv[2] || 'localnet'; const chainName = process.argv[3] || 'Ethereum'; const trustedAddress = process.argv[4] || '0x1234'; - + (async () => { - const privKey = + const privKey = Buffer.from( process.env.SUI_PRIVATE_KEY, "hex" @@ -112,4 +112,4 @@ if (require.main === module) { await setTrustedAddresses(client, keypair, env, [chainName], [trustedAddress]); })(); -} \ No newline at end of file +} diff --git a/scripts/test-receive-call.js b/scripts/test-receive-call.js index c5eb3f58..309b48ec 100644 --- a/scripts/test-receive-call.js +++ b/scripts/test-receive-call.js @@ -16,7 +16,7 @@ async function receiveCall(client, keypair, axelarInfo, sourceChain, sourceAddre const payloadHash = keccak256(payload); await approveContractCall(client, keypair, axelarInfo, sourceChain, sourceAddress, destinationAddress, payloadHash); - + const eventData = (await client.queryEvents({query: { MoveEventType: `${axelarPackageId}::gateway::ContractCallApproved`, }})); @@ -45,17 +45,17 @@ async function receiveCall(client, keypair, axelarInfo, sourceChain, sourceAddre sender: keypair.getPublicKey().toSuiAddress(), transactionBlock: tx, }); - + const txData = resp.results[0].returnValues[0][0]; const nextTx = getTransactionBcs().de('Transaction', new Uint8Array(txData)); is_final = nextTx.is_final; moveCalls = nextTx.move_calls; } const tx = new TransactionBlock(); - const approvedCall = tx.moveCall({ - target: `${axelarPackageId}::gateway::take_approved_call`, + const ApprovedMessage = tx.moveCall({ + target: `${axelarPackageId}::gateway::take_approved_message`, arguments: [ - tx.object(gateway.objectId), + tx.object(gateway.objectId), tx.pure(event.cmd_id), tx.pure(event.source_chain), tx.pure(event.source_address), @@ -64,7 +64,7 @@ async function receiveCall(client, keypair, axelarInfo, sourceChain, sourceAddre ], typeArguments: [], }); - makeCalls(tx, moveCalls, payload, approvedCall); + makeCalls(tx, moveCalls, payload, ApprovedMessage); return await client.signAndExecuteTransactionBlock({ transactionBlock: tx, signer: keypair, @@ -76,11 +76,11 @@ async function receiveCall(client, keypair, axelarInfo, sourceChain, sourceAddre }); } -function makeCalls(tx, moveCalls, payload, approvedCall) { +function makeCalls(tx, moveCalls, payload, ApprovedMessage) { const returns = []; for(const call of moveCalls) { let result = tx.moveCall( - buildMoveCall(tx, call, payload, approvedCall, returns) + buildMoveCall(tx, call, payload, ApprovedMessage, returns) ); if(!Array.isArray(result)) result = [result]; returns.push(result); @@ -151,10 +151,10 @@ if (require.main === module) { const keypair = Ed25519Keypair.fromSecretKey(privKey); // create a new SuiClient object pointing to the network you want to use const client = new SuiClient({ url: getFullnodeUrl(env) }); - + const testPackageId = testInfo.packageId; const test = testInfo['test::Singleton']; - + const payload = '0x1234'; let tx = new TransactionBlock(); @@ -162,7 +162,7 @@ if (require.main === module) { target: `${testPackageId}::test::register_transaction`, arguments: [tx.object(discovery.objectId), tx.object(test.objectId)], }); - await client.signAndExecuteTransactionBlock({ + await client.signAndExecuteTransactionBlock({ transactionBlock: tx, signer: keypair, options: { @@ -177,8 +177,8 @@ if (require.main === module) { const event = (await client.queryEvents({query: { MoveEventType: `${testPackageId}::test::Executed`, }})).data[0].parsedJson; - + if ( hexlify(event.data) != payload ) throw new Error(`Emmited payload missmatch: ${hexlify(event.data)} != ${payload}`); - + })(); -} \ No newline at end of file +}