From 4c14706be81da424415823ee5d977476da94e22a Mon Sep 17 00:00:00 2001 From: Foivos Date: Mon, 2 Dec 2024 16:27:28 +0200 Subject: [PATCH 01/14] capture all OwnerCaps properly --- sui/deploy-contract.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/sui/deploy-contract.js b/sui/deploy-contract.js index 634597eb7..5a9df54c7 100644 --- a/sui/deploy-contract.js +++ b/sui/deploy-contract.js @@ -96,14 +96,16 @@ const supportedPackages = PACKAGE_DIRS.map((dir) => ({ */ async function postDeployRelayerDiscovery(published, keypair, client, config, chain, options) { - const [relayerDiscoveryObjectId, relayerDiscoveryObjectIdv0] = getObjectIdsByObjectTypes(published.publishTxn, [ + const [relayerDiscoveryObjectId, relayerDiscoveryObjectIdv0, ownerCap] = getObjectIdsByObjectTypes(published.publishTxn, [ `${published.packageId}::discovery::RelayerDiscovery`, `${published.packageId}::relayer_discovery_v0::RelayerDiscovery_v0`, + `${published.packageId}::owner_cap::OwnerCap`, ]); chain.contracts.RelayerDiscovery.objects = { RelayerDiscovery: relayerDiscoveryObjectId, RelayerDiscoveryv0: relayerDiscoveryObjectIdv0, + OwnerCap: ownerCap, }; } @@ -235,15 +237,16 @@ async function postDeployAxelarGateway(published, keypair, client, config, chain async function postDeployIts(published, keypair, client, config, chain, options) { const relayerDiscovery = chain.contracts.RelayerDiscovery?.objects?.RelayerDiscovery; - const [itsObjectId, itsv0ObjectId, ownerCapObjectId] = getObjectIdsByObjectTypes(published.publishTxn, [ + const [itsObjectId, itsv0ObjectId, ownerCapObjectId, upgradeCap] = getObjectIdsByObjectTypes(published.publishTxn, [ `${published.packageId}::its::ITS`, `${published.packageId}::its_v0::ITS_v0`, `${published.packageId}::owner_cap::OwnerCap`, + `${suiPackageAddress}::package::UpgradeCap`, ]); const channelId = await getItsChannelId(client, itsv0ObjectId); - chain.contracts.ITS.objects = { ITS: itsObjectId, ITSv0: itsv0ObjectId, ChannelId: channelId, OwnerCap: ownerCapObjectId }; + chain.contracts.ITS.objects = { ITS: itsObjectId, ITSv0: itsv0ObjectId, ChannelId: channelId, OwnerCap: ownerCapObjectId, UpgradeCap: upgradeCap }; const tx = new Transaction(); tx.moveCall({ @@ -257,9 +260,12 @@ async function postDeployIts(published, keypair, client, config, chain, options) async function postDeploySquid(published, keypair, client, config, chain, options) { const relayerDiscovery = chain.contracts.RelayerDiscovery?.objects?.RelayerDiscovery; - const [squidObjectId] = getObjectIdsByObjectTypes(published.publishTxn, [`${published.packageId}::squid::Squid`]); + const [squidObjectId, ownerCap] = getObjectIdsByObjectTypes(published.publishTxn, [ + `${published.packageId}::squid::Squid`, + `${published.packageId}::owner_cap::OwnerCap`, + ]); const channelId = await getSquidChannelId(client, squidObjectId); - chain.contracts.Squid.objects = { Squid: squidObjectId, ChannelId: channelId }; + chain.contracts.Squid.objects = { Squid: squidObjectId, ChannelId: channelId, OwnerCap: ownerCap }; const tx = new Transaction(); tx.moveCall({ From 5cb0e1a62178bc7ef86630d004369cac103f2d63 Mon Sep 17 00:00:00 2001 From: Foivos Date: Tue, 3 Dec 2024 16:41:06 +0200 Subject: [PATCH 02/14] lint --- sui/deploy-contract.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sui/deploy-contract.js b/sui/deploy-contract.js index 5a9df54c7..51cd00bc5 100644 --- a/sui/deploy-contract.js +++ b/sui/deploy-contract.js @@ -246,7 +246,13 @@ async function postDeployIts(published, keypair, client, config, chain, options) const channelId = await getItsChannelId(client, itsv0ObjectId); - chain.contracts.ITS.objects = { ITS: itsObjectId, ITSv0: itsv0ObjectId, ChannelId: channelId, OwnerCap: ownerCapObjectId, UpgradeCap: upgradeCap }; + chain.contracts.ITS.objects = { + ITS: itsObjectId, + ITSv0: itsv0ObjectId, + ChannelId: channelId, + OwnerCap: ownerCapObjectId, + UpgradeCap: upgradeCap, + }; const tx = new Transaction(); tx.moveCall({ From d85b4ecc9e38ea36e139026fb8bdf0048430fd2e Mon Sep 17 00:00:00 2001 From: Foivos Date: Wed, 11 Dec 2024 15:31:12 +0200 Subject: [PATCH 03/14] rename some vars --- sui/deploy-contract.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sui/deploy-contract.js b/sui/deploy-contract.js index 75ffe8ef4..1e261577d 100644 --- a/sui/deploy-contract.js +++ b/sui/deploy-contract.js @@ -237,7 +237,7 @@ async function postDeployAxelarGateway(published, keypair, client, config, chain async function postDeployIts(published, keypair, client, config, chain, options) { const relayerDiscovery = chain.contracts.RelayerDiscovery?.objects?.RelayerDiscovery; - const [itsObjectId, itsv0ObjectId, ownerCapObjectId, upgradeCap] = getObjectIdsByObjectTypes(published.publishTxn, [ + const [itsObjectId, itsv0ObjectId, ownerCapObjectId, upgradeCapObjectId] = getObjectIdsByObjectTypes(published.publishTxn, [ `${published.packageId}::its::ITS`, `${published.packageId}::its_v0::ITS_v0`, `${published.packageId}::owner_cap::OwnerCap`, @@ -251,7 +251,7 @@ async function postDeployIts(published, keypair, client, config, chain, options) ITSv0: itsv0ObjectId, ChannelId: channelId, OwnerCap: ownerCapObjectId, - UpgradeCap: upgradeCap, + UpgradeCap: upgradeCapObjectId, }; const tx = new Transaction(); @@ -266,12 +266,12 @@ async function postDeployIts(published, keypair, client, config, chain, options) async function postDeploySquid(published, keypair, client, config, chain, options) { const relayerDiscovery = chain.contracts.RelayerDiscovery?.objects?.RelayerDiscovery; - const [squidObjectId, ownerCap] = getObjectIdsByObjectTypes(published.publishTxn, [ + const [squidObjectId, ownerCapObjectId] = getObjectIdsByObjectTypes(published.publishTxn, [ `${published.packageId}::squid::Squid`, `${published.packageId}::owner_cap::OwnerCap`, ]); const channelId = await getSquidChannelId(client, squidObjectId); - chain.contracts.Squid.objects = { Squid: squidObjectId, ChannelId: channelId, OwnerCap: ownerCap }; + chain.contracts.Squid.objects = { Squid: squidObjectId, ChannelId: channelId, OwnerCap: ownerCapObjectId }; const tx = new Transaction(); tx.moveCall({ From bea29b041030e80d9c932d0b97056f91c93a89b4 Mon Sep 17 00:00:00 2001 From: Foivos Date: Fri, 13 Dec 2024 13:54:32 +0200 Subject: [PATCH 04/14] add allow and dissalow function in gateway --- sui/deploy-contract.js | 1 + sui/gateway.js | 76 +++++++++++++++++++++++++++++++++++++++++ sui/utils/sign-utils.js | 1 - sui/utils/utils.js | 22 ++++++++++++ 4 files changed, 99 insertions(+), 1 deletion(-) diff --git a/sui/deploy-contract.js b/sui/deploy-contract.js index 1e261577d..3fe8edcd3 100644 --- a/sui/deploy-contract.js +++ b/sui/deploy-contract.js @@ -227,6 +227,7 @@ async function postDeployAxelarGateway(published, keypair, client, config, chain Gateway: gateway, UpgradeCap: upgradeCap, Gatewayv0: gatewayv0, + OwnerCap: ownerCap, }, domainSeparator, operator, diff --git a/sui/gateway.js b/sui/gateway.js index f76ffe8fe..d70bbf8f4 100644 --- a/sui/gateway.js +++ b/sui/gateway.js @@ -275,6 +275,68 @@ async function rotate(keypair, client, config, chain, contractConfig, args, opti }; } +async function allowFunctions(keypair, client, config, chain, contractConfig, args, options) { + const packageId = contractConfig.address; + + const [versionsArg, functionNamesArg] = args; + + const versions = versionsArg.split(','); + const functionNames = functionNamesArg.split(','); + + if (versions.length !== functionNames.length) throw new Error('Versions and Function Names must have a matching length'); + + const tx = new Transaction(); + console.log(contractConfig.objects); + + for (const i in versions) { + tx.moveCall({ + target: `${packageId}::gateway::allow_function`, + arguments: [ + tx.object(contractConfig.objects.Gateway), + tx.object(contractConfig.objects.OwnerCap), + tx.pure.u64(versions[i]), + tx.pure.string(functionNames[i]), + ], + }); + } + + return { + tx, + message: 'Allow Functions', + }; +} + +async function disallowFunctions(keypair, client, config, chain, contractConfig, args, options) { + const packageId = contractConfig.address; + + const [versionsArg, functionNamesArg] = args; + + const versions = versionsArg.split(','); + const functionNames = functionNamesArg.split(','); + + if (versions.length !== functionNames.length) throw new Error('Versions and Function Names must have a matching length'); + + const tx = new Transaction(); + console.log(contractConfig.objects); + + for (const i in versions) { + tx.moveCall({ + target: `${packageId}::gateway::disallow_function`, + arguments: [ + tx.object(contractConfig.objects.Gateway), + tx.object(contractConfig.objects.OwnerCap), + tx.pure.u64(versions[i]), + tx.pure.string(functionNames[i]), + ], + }); + } + + return { + tx, + message: 'Allow Functions', + }; +} + async function mainProcessor(processor, args, options) { const config = loadConfig(options.env); @@ -346,6 +408,20 @@ if (require.main === module) { mainProcessor(callContract, [destinationChain, destinationAddress, payload], options); }); + program + .command('allow-functions ') + .description('Allow certain funcitons on the gateway') + .action((versions, functionNames, options) => { + mainProcessor(allowFunctions, [versions, functionNames], options); + }); + + program + .command('disallow-functions ') + .description('Allow certain funcitons on the gateway') + .action((versions, functionNames, options) => { + mainProcessor(disallowFunctions, [versions, functionNames], options); + }); + addOptionsToCommands(program, addBaseOptions, { offline: true }); program.parse(); diff --git a/sui/utils/sign-utils.js b/sui/utils/sign-utils.js index ca1768bdf..5d5880277 100644 --- a/sui/utils/sign-utils.js +++ b/sui/utils/sign-utils.js @@ -116,7 +116,6 @@ async function broadcast(client, keypair, tx, actionName) { showContent: true, }, }); - printInfo(actionName || 'Tx', receipt.digest); return receipt; diff --git a/sui/utils/utils.js b/sui/utils/utils.js index e8819b296..e75fab8ad 100644 --- a/sui/utils/utils.js +++ b/sui/utils/utils.js @@ -293,6 +293,27 @@ const saveGeneratedTx = async (tx, message, client, options) => { printInfo(`Unsigned transaction`, txFilePath); }; +const isAllowed = async (builder, sender = '0x0') => { + try { + await builder.devInspect(sender); + } catch (e) { + const errorMessage = e.cause.effects.status.error; + let regexp = /address: (.*?),/; + const packageId = `0x${regexp.exec(errorMessage)[1]}`; + + regexp = /Identifier\("(.*?)"\)/; + const module = regexp.exec(errorMessage)[1]; + + regexp = /Some\("(.*?)"\)/; + const functionName = regexp.exec(errorMessage)[1]; + + regexp = /Some\(".*?"\) \}, (.*?)\)/; + const errorCode = parseInt(regexp.exec(errorMessage)[1]); + console.log(packageId, module, functionName, errorCode); + console.log(errorMessage); + } +}; + module.exports = { suiCoinId, getAmplifierSigners, @@ -319,4 +340,5 @@ module.exports = { parseGatewayInfo, getStructs, saveGeneratedTx, + isAllowed, }; From a8280779b05140e63152c8e9050e2e95dc8af228 Mon Sep 17 00:00:00 2001 From: Foivos Date: Fri, 13 Dec 2024 15:11:48 +0200 Subject: [PATCH 05/14] Add a tool to check which functions are allowed on the gateway --- sui/gateway.js | 151 ++++++++++++++++++++++++++++++++++++++++++++- sui/utils/utils.js | 32 ++++++++-- 2 files changed, 175 insertions(+), 8 deletions(-) diff --git a/sui/gateway.js b/sui/gateway.js index d70bbf8f4..10b2e0587 100644 --- a/sui/gateway.js +++ b/sui/gateway.js @@ -2,7 +2,7 @@ const { Command, Option } = require('commander'); const { Transaction } = require('@mysten/sui/transactions'); const { bcs } = require('@mysten/sui/bcs'); const { ethers } = require('hardhat'); -const { bcsStructs } = require('@axelar-network/axelar-cgp-sui'); +const { bcsStructs, STD_PACKAGE_ID, CLOCK_PACKAGE_ID } = require('@axelar-network/axelar-cgp-sui'); const { utils: { arrayify, keccak256, toUtf8Bytes }, constants: { HashZero }, @@ -19,8 +19,10 @@ const { broadcast, suiClockAddress, saveGeneratedTx, + isAllowed, } = require('./utils'); const secp256k1 = require('secp256k1'); +const chalk = require('chalk'); const COMMAND_TYPE_APPROVE_MESSAGES = 0; const COMMAND_TYPE_ROTATE_SIGNERS = 1; @@ -333,10 +335,146 @@ async function disallowFunctions(keypair, client, config, chain, contractConfig, return { tx, - message: 'Allow Functions', + message: 'Disallow Functions', }; } +async function checkVersionControl(version, options) { + const config = loadConfig(options.env); + + const chain = getChainConfig(config, options.chainName); + const [keypair, client] = getWallet(chain, options); + await printWalletInfo(keypair, client, chain, options); + + if (!chain.contracts?.AxelarGateway) { + throw new Error('Axelar Gateway package not found.'); + } + + const contractConfig = chain.contracts.AxelarGateway; + const packageId = contractConfig.address; + + const functions = {}; + functions.approve_messages = (tx) => tx.moveCall({ + target: `${packageId}::gateway::approve_messages`, + arguments: [ + tx.object(contractConfig.objects.Gateway), + tx.pure.vector('u8', []), + tx.pure.vector('u8', []), + ], + }); + functions.rotate_signers = (tx) => tx.moveCall({ + target: `${packageId}::gateway::rotate_signers`, + arguments: [ + tx.object(contractConfig.objects.Gateway), + tx.object(CLOCK_PACKAGE_ID), + tx.pure.vector('u8', []), + tx.pure.vector('u8', []), + ], + }); + functions.is_message_approved = (tx) => tx.moveCall({ + target: `${packageId}::gateway::is_message_approved`, + arguments: [ + tx.object(contractConfig.objects.Gateway), + tx.pure.string(''), + tx.pure.string(''), + tx.pure.string(''), + tx.pure.address('0x0'), + tx.moveCall({ + target: `${packageId}::bytes32::from_address`, + arguments: [ + tx.pure.address('0x0'), + ], + }) + ], + }); + functions.is_message_executed = (tx) => tx.moveCall({ + target: `${packageId}::gateway::is_message_executed`, + arguments: [ + tx.object(contractConfig.objects.Gateway), + tx.pure.string(''), + tx.pure.string(''), + ], + }); + functions.take_approved_message = (tx) => tx.moveCall({ + target: `${packageId}::gateway::take_approved_message`, + arguments: [ + tx.object(contractConfig.objects.Gateway), + tx.pure.string(''), + tx.pure.string(''), + tx.pure.string(''), + tx.pure.address('0x0'), + tx.pure.vector('u8', []), + ], + }); + functions.send_message = (tx) => { + const channel = tx.moveCall({ + target: `${packageId}::channel::new`, + arguments: [], + }); + + const message = tx.moveCall({ + target: `${packageId}::gateway::prepare_message`, + arguments: [ + channel, + tx.pure.string(''), + tx.pure.string(''), + tx.pure.vector('u8', []), + ], + }); + + tx.moveCall({ + target: `${packageId}::gateway::send_message`, + arguments: [ + tx.object(contractConfig.objects.Gateway), + message, + ], + }); + + tx.moveCall({ + target: `${packageId}::channel::destroy`, + arguments: [ + channel + ], + }); + } + functions.allow_function = (tx) => tx.moveCall({ + target: `${packageId}::gateway::allow_function`, + arguments: [ + tx.object(contractConfig.objects.Gateway), + tx.object(contractConfig.objects.OwnerCap), + tx.pure.u64(0), + tx.pure.string(""), + ], + }); + functions.disallow_function = (tx) => tx.moveCall({ + target: `${packageId}::gateway::disallow_function`, + arguments: [ + tx.object(contractConfig.objects.Gateway), + tx.object(contractConfig.objects.OwnerCap), + tx.pure.u64(0), + tx.pure.string(""), + ], + }); + + if (options.allowedFunctions) { + const allowedFunctions = options.allowedFunctions === 'all' ? Object.keys(functions) : options.allowedFunctions.split(','); + for(const allowedFunction of allowedFunctions) { + const allowed = await isAllowed(client, keypair, chain, functions[allowedFunction]); + const color = allowed ? chalk.green : chalk.red; + console.log(`${allowedFunction} is ${color(allowed ? 'allowed' : 'dissalowed')}`) + } + } + + if (options.disallowedFunctions) { + const disallowedFunctions = options.allowedFunctions === 'all' ? Object.keys(functions) : options.disallowedFunctions.split(','); + for(const disallowedFunction of disallowedFunctions) { + const allowed = await isAllowed(client, keypair, chain, functions[disallowedFunction]); + const color = allowed ? chalk.red : chalk.green; + console.log(`${disallowedFunction} is ${color(allowed ? 'allowed' : 'dissalowed')}`) + } + } +} + async function mainProcessor(processor, args, options) { const config = loadConfig(options.env); @@ -422,6 +560,15 @@ if (require.main === module) { mainProcessor(disallowFunctions, [versions, functionNames], options); }); + program + .command('check-version-control ') + .description('Check if version control works on a certain version') + .addOption(new Option('--allowed-functions ', 'Functions that should be allowed on this version')) + .addOption(new Option('--disallowed-functions ', 'Functions that should be disallowed on this version')) + .action((version, options) => { + checkVersionControl(version, options); + }); + addOptionsToCommands(program, addBaseOptions, { offline: true }); program.parse(); diff --git a/sui/utils/utils.js b/sui/utils/utils.js index e75fab8ad..08a17ff13 100644 --- a/sui/utils/utils.js +++ b/sui/utils/utils.js @@ -18,7 +18,10 @@ const { bcsStructs, getDefinedSuiVersion, getInstalledSuiVersion, + STD_PACKAGE_ID, } = require('@axelar-network/axelar-cgp-sui'); +const { Transaction } = require('@mysten/sui/transactions'); +const { broadcast } = require('./sign-utils'); const suiPackageAddress = '0x2'; const suiClockAddress = '0x6'; @@ -293,9 +296,22 @@ const saveGeneratedTx = async (tx, message, client, options) => { printInfo(`Unsigned transaction`, txFilePath); }; -const isAllowed = async (builder, sender = '0x0') => { +const isAllowed = async (client, keypair, chain, exec) => { + const addError = (tx) => { + tx.moveCall({ + target: `${STD_PACKAGE_ID}::ascii::char`, + arguments: [ + tx.pure.u8(128), + ], + }); + } + + const tx = new Transaction(); + exec(tx); + addError(tx); + try { - await builder.devInspect(sender); + await broadcast(client, keypair, tx); } catch (e) { const errorMessage = e.cause.effects.status.error; let regexp = /address: (.*?),/; @@ -307,11 +323,15 @@ const isAllowed = async (builder, sender = '0x0') => { regexp = /Some\("(.*?)"\)/; const functionName = regexp.exec(errorMessage)[1]; - regexp = /Some\(".*?"\) \}, (.*?)\)/; - const errorCode = parseInt(regexp.exec(errorMessage)[1]); - console.log(packageId, module, functionName, errorCode); - console.log(errorMessage); + if(packageId === chain.contracts.VersionControl.address && module === 'version_control' && functionName === 'check') { + regexp = /Some\(".*?"\) \}, (.*?)\)/; + if(parseInt(regexp.exec(errorMessage)[1]) === 9223372539365950000) { + return false; + } + } } + + return true; }; module.exports = { From 1ec62f1b3f3d9afa0a5ba9b0704f42d7e3081b23 Mon Sep 17 00:00:00 2001 From: Foivos Date: Fri, 13 Dec 2024 15:23:03 +0200 Subject: [PATCH 06/14] made lint happy --- sui/gateway.js | 163 +++++++++++++++++++++------------------------ sui/utils/utils.js | 11 ++- 2 files changed, 82 insertions(+), 92 deletions(-) diff --git a/sui/gateway.js b/sui/gateway.js index 10b2e0587..3fd08d3d7 100644 --- a/sui/gateway.js +++ b/sui/gateway.js @@ -2,7 +2,7 @@ const { Command, Option } = require('commander'); const { Transaction } = require('@mysten/sui/transactions'); const { bcs } = require('@mysten/sui/bcs'); const { ethers } = require('hardhat'); -const { bcsStructs, STD_PACKAGE_ID, CLOCK_PACKAGE_ID } = require('@axelar-network/axelar-cgp-sui'); +const { bcsStructs, CLOCK_PACKAGE_ID } = require('@axelar-network/axelar-cgp-sui'); const { utils: { arrayify, keccak256, toUtf8Bytes }, constants: { HashZero }, @@ -354,58 +354,54 @@ async function checkVersionControl(version, options) { const packageId = contractConfig.address; const functions = {}; - functions.approve_messages = (tx) => tx.moveCall({ - target: `${packageId}::gateway::approve_messages`, - arguments: [ - tx.object(contractConfig.objects.Gateway), - tx.pure.vector('u8', []), - tx.pure.vector('u8', []), - ], - }); - functions.rotate_signers = (tx) => tx.moveCall({ - target: `${packageId}::gateway::rotate_signers`, - arguments: [ - tx.object(contractConfig.objects.Gateway), - tx.object(CLOCK_PACKAGE_ID), - tx.pure.vector('u8', []), - tx.pure.vector('u8', []), - ], - }); - functions.is_message_approved = (tx) => tx.moveCall({ - target: `${packageId}::gateway::is_message_approved`, - arguments: [ - tx.object(contractConfig.objects.Gateway), - tx.pure.string(''), - tx.pure.string(''), - tx.pure.string(''), - tx.pure.address('0x0'), - tx.moveCall({ - target: `${packageId}::bytes32::from_address`, - arguments: [ - tx.pure.address('0x0'), - ], - }) - ], - }); - functions.is_message_executed = (tx) => tx.moveCall({ - target: `${packageId}::gateway::is_message_executed`, - arguments: [ - tx.object(contractConfig.objects.Gateway), - tx.pure.string(''), - tx.pure.string(''), - ], - }); - functions.take_approved_message = (tx) => tx.moveCall({ - target: `${packageId}::gateway::take_approved_message`, - arguments: [ - tx.object(contractConfig.objects.Gateway), - tx.pure.string(''), - tx.pure.string(''), - tx.pure.string(''), - tx.pure.address('0x0'), - tx.pure.vector('u8', []), - ], - }); + functions.approve_messages = (tx) => + tx.moveCall({ + target: `${packageId}::gateway::approve_messages`, + arguments: [tx.object(contractConfig.objects.Gateway), tx.pure.vector('u8', []), tx.pure.vector('u8', [])], + }); + functions.rotate_signers = (tx) => + tx.moveCall({ + target: `${packageId}::gateway::rotate_signers`, + arguments: [ + tx.object(contractConfig.objects.Gateway), + tx.object(CLOCK_PACKAGE_ID), + tx.pure.vector('u8', []), + tx.pure.vector('u8', []), + ], + }); + functions.is_message_approved = (tx) => + tx.moveCall({ + target: `${packageId}::gateway::is_message_approved`, + arguments: [ + tx.object(contractConfig.objects.Gateway), + tx.pure.string(''), + tx.pure.string(''), + tx.pure.string(''), + tx.pure.address('0x0'), + tx.moveCall({ + target: `${packageId}::bytes32::from_address`, + arguments: [tx.pure.address('0x0')], + }), + ], + }); + functions.is_message_executed = (tx) => + tx.moveCall({ + target: `${packageId}::gateway::is_message_executed`, + arguments: [tx.object(contractConfig.objects.Gateway), tx.pure.string(''), tx.pure.string('')], + }); + functions.take_approved_message = (tx) => + tx.moveCall({ + target: `${packageId}::gateway::take_approved_message`, + arguments: [ + tx.object(contractConfig.objects.Gateway), + tx.pure.string(''), + tx.pure.string(''), + tx.pure.string(''), + tx.pure.address('0x0'), + tx.pure.vector('u8', []), + ], + }); + functions.send_message = (tx) => { const channel = tx.moveCall({ target: `${packageId}::channel::new`, @@ -414,63 +410,58 @@ async function checkVersionControl(version, options) { const message = tx.moveCall({ target: `${packageId}::gateway::prepare_message`, - arguments: [ - channel, - tx.pure.string(''), - tx.pure.string(''), - tx.pure.vector('u8', []), - ], + arguments: [channel, tx.pure.string(''), tx.pure.string(''), tx.pure.vector('u8', [])], }); tx.moveCall({ target: `${packageId}::gateway::send_message`, + arguments: [tx.object(contractConfig.objects.Gateway), message], + }); + + tx.moveCall({ + target: `${packageId}::channel::destroy`, + arguments: [channel], + }); + }; + + functions.allow_function = (tx) => + tx.moveCall({ + target: `${packageId}::gateway::allow_function`, arguments: [ tx.object(contractConfig.objects.Gateway), - message, + tx.object(contractConfig.objects.OwnerCap), + tx.pure.u64(0), + tx.pure.string(''), ], }); - + functions.disallow_function = (tx) => tx.moveCall({ - target: `${packageId}::channel::destroy`, + target: `${packageId}::gateway::disallow_function`, arguments: [ - channel + tx.object(contractConfig.objects.Gateway), + tx.object(contractConfig.objects.OwnerCap), + tx.pure.u64(0), + tx.pure.string(''), ], }); - } - functions.allow_function = (tx) => tx.moveCall({ - target: `${packageId}::gateway::allow_function`, - arguments: [ - tx.object(contractConfig.objects.Gateway), - tx.object(contractConfig.objects.OwnerCap), - tx.pure.u64(0), - tx.pure.string(""), - ], - }); - functions.disallow_function = (tx) => tx.moveCall({ - target: `${packageId}::gateway::disallow_function`, - arguments: [ - tx.object(contractConfig.objects.Gateway), - tx.object(contractConfig.objects.OwnerCap), - tx.pure.u64(0), - tx.pure.string(""), - ], - }); if (options.allowedFunctions) { const allowedFunctions = options.allowedFunctions === 'all' ? Object.keys(functions) : options.allowedFunctions.split(','); - for(const allowedFunction of allowedFunctions) { + + for (const allowedFunction of allowedFunctions) { const allowed = await isAllowed(client, keypair, chain, functions[allowedFunction]); const color = allowed ? chalk.green : chalk.red; - console.log(`${allowedFunction} is ${color(allowed ? 'allowed' : 'dissalowed')}`) + console.log(`${allowedFunction} is ${color(allowed ? 'allowed' : 'dissalowed')}`); } } if (options.disallowedFunctions) { const disallowedFunctions = options.allowedFunctions === 'all' ? Object.keys(functions) : options.disallowedFunctions.split(','); - for(const disallowedFunction of disallowedFunctions) { + + for (const disallowedFunction of disallowedFunctions) { const allowed = await isAllowed(client, keypair, chain, functions[disallowedFunction]); const color = allowed ? chalk.red : chalk.green; - console.log(`${disallowedFunction} is ${color(allowed ? 'allowed' : 'dissalowed')}`) + console.log(`${disallowedFunction} is ${color(allowed ? 'allowed' : 'dissalowed')}`); } } } diff --git a/sui/utils/utils.js b/sui/utils/utils.js index 08a17ff13..175247aa1 100644 --- a/sui/utils/utils.js +++ b/sui/utils/utils.js @@ -300,11 +300,9 @@ const isAllowed = async (client, keypair, chain, exec) => { const addError = (tx) => { tx.moveCall({ target: `${STD_PACKAGE_ID}::ascii::char`, - arguments: [ - tx.pure.u8(128), - ], + arguments: [tx.pure.u8(128)], }); - } + }; const tx = new Transaction(); exec(tx); @@ -323,9 +321,10 @@ const isAllowed = async (client, keypair, chain, exec) => { regexp = /Some\("(.*?)"\)/; const functionName = regexp.exec(errorMessage)[1]; - if(packageId === chain.contracts.VersionControl.address && module === 'version_control' && functionName === 'check') { + if (packageId === chain.contracts.VersionControl.address && module === 'version_control' && functionName === 'check') { regexp = /Some\(".*?"\) \}, (.*?)\)/; - if(parseInt(regexp.exec(errorMessage)[1]) === 9223372539365950000) { + + if (parseInt(regexp.exec(errorMessage)[1]) === 9223372539365950000) { return false; } } From fc167668c8388ca8a61706580b85cf5901377963 Mon Sep 17 00:00:00 2001 From: Foivos Date: Fri, 13 Dec 2024 19:05:34 +0200 Subject: [PATCH 07/14] use latest version --- sui/deploy-contract.js | 1 + sui/gateway.js | 2 +- sui/utils/upgrade-utils.js | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/sui/deploy-contract.js b/sui/deploy-contract.js index 3fe8edcd3..31142c5d5 100644 --- a/sui/deploy-contract.js +++ b/sui/deploy-contract.js @@ -307,6 +307,7 @@ async function deploy(keypair, client, supportedContract, config, chain, options // Update chain configuration with deployed contract address chain.contracts[packageName] = { address: published.packageId, + versions: [ published.packageId ], }; chain.contracts[packageName].structs = await getStructs(client, published.packageId); diff --git a/sui/gateway.js b/sui/gateway.js index 3fd08d3d7..8cd10a74d 100644 --- a/sui/gateway.js +++ b/sui/gateway.js @@ -351,7 +351,7 @@ async function checkVersionControl(version, options) { } const contractConfig = chain.contracts.AxelarGateway; - const packageId = contractConfig.address; + const packageId = contractConfig.versions[version]; const functions = {}; functions.approve_messages = (tx) => diff --git a/sui/utils/upgrade-utils.js b/sui/utils/upgrade-utils.js index 5304a128f..147f12066 100644 --- a/sui/utils/upgrade-utils.js +++ b/sui/utils/upgrade-utils.js @@ -71,6 +71,7 @@ async function upgradePackage(client, keypair, packageToUpgrade, contractConfig, const packageId = (result.objectChanges?.filter((a) => a.type === 'published') ?? [])[0].packageId; contractConfig.address = packageId; + contractConfig.versions.push(packageId); const [upgradeCap] = getObjectIdsByObjectTypes(result, [`${suiPackageAddress}::package::UpgradeCap`]); contractConfig.objects.UpgradeCap = upgradeCap; From d1184686ef9413653f39a8358495be4f32eeaf20 Mon Sep 17 00:00:00 2001 From: blockchainguyy Date: Fri, 13 Dec 2024 23:32:22 +0530 Subject: [PATCH 08/14] fixed lint --- sui/deploy-contract.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sui/deploy-contract.js b/sui/deploy-contract.js index 31142c5d5..3eb44b809 100644 --- a/sui/deploy-contract.js +++ b/sui/deploy-contract.js @@ -307,7 +307,7 @@ async function deploy(keypair, client, supportedContract, config, chain, options // Update chain configuration with deployed contract address chain.contracts[packageName] = { address: published.packageId, - versions: [ published.packageId ], + versions: [published.packageId], }; chain.contracts[packageName].structs = await getStructs(client, published.packageId); From 9ac84b614ae5d235ef4e0e80cd03274a96b87a04 Mon Sep 17 00:00:00 2001 From: Foivos Date: Mon, 16 Dec 2024 13:00:37 +0200 Subject: [PATCH 09/14] added check for missing dynamic field --- sui/utils/utils.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sui/utils/utils.js b/sui/utils/utils.js index 175247aa1..2380543f9 100644 --- a/sui/utils/utils.js +++ b/sui/utils/utils.js @@ -19,6 +19,7 @@ const { getDefinedSuiVersion, getInstalledSuiVersion, STD_PACKAGE_ID, + SUI_PACKAGE_ID, } = require('@axelar-network/axelar-cgp-sui'); const { Transaction } = require('@mysten/sui/transactions'); const { broadcast } = require('./sign-utils'); @@ -328,6 +329,17 @@ const isAllowed = async (client, keypair, chain, exec) => { return false; } } + let suiPackageAddress = SUI_PACKAGE_ID; + while (suiPackageAddress.length < 66) { + suiPackageAddress = suiPackageAddress.substring(0, suiPackageAddress.length - 1) + '02'; + } + if( packageId === suiPackageAddress && module === 'dynamic_field' && (functionName === 'borrow_child_object_mut' || functionName === 'borrow_child_object') ) { + regexp = /Some\(".*?"\) \}, (.*?)\)/; + + if (parseInt(regexp.exec(errorMessage)[1]) === 2) { + return false; + } + } } return true; From d52e6fd8718dcf1cd395f4df609947f62350a9ed Mon Sep 17 00:00:00 2001 From: Foivos Date: Mon, 16 Dec 2024 13:25:23 +0200 Subject: [PATCH 10/14] update move toml after upgrade and add ability ot upgrade Utils --- sui/deploy-contract.js | 10 ++++++++++ sui/utils/upgrade-utils.js | 4 +++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/sui/deploy-contract.js b/sui/deploy-contract.js index 3eb44b809..57b6d9448 100644 --- a/sui/deploy-contract.js +++ b/sui/deploy-contract.js @@ -68,6 +68,7 @@ const PACKAGE_CONFIGS = { Operators: postDeployOperators, ITS: postDeployIts, Squid: postDeploySquid, + Utils: postDeployUtils, }, }; @@ -109,6 +110,15 @@ async function postDeployRelayerDiscovery(published, keypair, client, config, ch }; } +async function postDeployUtils(published, keypair, client, config, chain, options) { + const [upgradeCap] = getObjectIdsByObjectTypes(published.publishTxn, [ + `${suiPackageAddress}::package::UpgradeCap`, + ]); + chain.contracts.Utils.objects = { + UpgradeCap: upgradeCap, + }; +} + async function postDeployGasService(published, keypair, client, config, chain, options) { const [gasCollectorCapObjectId, gasServiceObjectId, gasServicev0ObjectId] = getObjectIdsByObjectTypes(published.publishTxn, [ `${published.packageId}::gas_service::GasCollectorCap`, diff --git a/sui/utils/upgrade-utils.js b/sui/utils/upgrade-utils.js index 147f12066..909f23252 100644 --- a/sui/utils/upgrade-utils.js +++ b/sui/utils/upgrade-utils.js @@ -1,7 +1,7 @@ const { bcs } = require('@mysten/bcs'); const { fromB64 } = require('@mysten/bcs'); const { printInfo, validateParameters } = require('../../common/utils'); -const { copyMovePackage } = require('@axelar-network/axelar-cgp-sui'); +const { copyMovePackage, updateMoveToml } = require('@axelar-network/axelar-cgp-sui'); const { getObjectIdsByObjectTypes, suiPackageAddress, moveDir, saveGeneratedTx } = require('./utils'); const UPGRADE_POLICIES = { code_upgrade: 'only_additive_upgrades', @@ -78,6 +78,8 @@ async function upgradePackage(client, keypair, packageToUpgrade, contractConfig, printInfo('Transaction Digest', JSON.stringify(result.digest, null, 2)); printInfo(`${packageName} Upgraded Address`, packageId); + updateMoveToml(packageToUpgrade.packageDir, packageId, moveDir); + return { upgraded: result, packageId }; } } From d78ca75b5dae9d95fd3431fd126b883f7244bcdc Mon Sep 17 00:00:00 2001 From: Foivos Date: Mon, 16 Dec 2024 14:38:59 +0200 Subject: [PATCH 11/14] add new migrate signature --- sui/deploy-contract.js | 4 +--- sui/gateway.js | 9 +++++++-- sui/utils/utils.js | 6 +++++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/sui/deploy-contract.js b/sui/deploy-contract.js index 57b6d9448..53beaa5da 100644 --- a/sui/deploy-contract.js +++ b/sui/deploy-contract.js @@ -111,9 +111,7 @@ async function postDeployRelayerDiscovery(published, keypair, client, config, ch } async function postDeployUtils(published, keypair, client, config, chain, options) { - const [upgradeCap] = getObjectIdsByObjectTypes(published.publishTxn, [ - `${suiPackageAddress}::package::UpgradeCap`, - ]); + const [upgradeCap] = getObjectIdsByObjectTypes(published.publishTxn, [`${suiPackageAddress}::package::UpgradeCap`]); chain.contracts.Utils.objects = { UpgradeCap: upgradeCap, }; diff --git a/sui/gateway.js b/sui/gateway.js index 8cd10a74d..1abbd3bfd 100644 --- a/sui/gateway.js +++ b/sui/gateway.js @@ -187,12 +187,16 @@ async function approve(keypair, client, config, chain, contractConfig, args, opt async function migrate(keypair, client, config, chain, contractConfig, args, options) { const packageId = contractConfig.address; - + const data = new Uint8Array(arrayify(options.migrateData || '0x')); const tx = new Transaction(); tx.moveCall({ target: `${packageId}::gateway::migrate`, - arguments: [tx.object(contractConfig.objects.Gateway)], + arguments: [ + tx.object(contractConfig.objects.Gateway), + tx.object(contractConfig.objects.OwnerCap), + tx.pure(bcs.vector(bcs.u8()).serialize(data).toBytes()), + ], }); return { @@ -518,6 +522,7 @@ if (require.main === module) { program .command('migrate') .description('Migrate the gateway after upgrade') + .addOption(new Option('--migrate-data ', 'bcs encoded data to pass to the migrate function')) .action((options) => { mainProcessor(migrate, null, options); }); diff --git a/sui/utils/utils.js b/sui/utils/utils.js index 2380543f9..c6b61080c 100644 --- a/sui/utils/utils.js +++ b/sui/utils/utils.js @@ -333,7 +333,11 @@ const isAllowed = async (client, keypair, chain, exec) => { while (suiPackageAddress.length < 66) { suiPackageAddress = suiPackageAddress.substring(0, suiPackageAddress.length - 1) + '02'; } - if( packageId === suiPackageAddress && module === 'dynamic_field' && (functionName === 'borrow_child_object_mut' || functionName === 'borrow_child_object') ) { + if ( + packageId === suiPackageAddress && + module === 'dynamic_field' && + (functionName === 'borrow_child_object_mut' || functionName === 'borrow_child_object') + ) { regexp = /Some\(".*?"\) \}, (.*?)\)/; if (parseInt(regexp.exec(errorMessage)[1]) === 2) { From 802d981a4b5a4c9294bc95ac6630cfdb95c7228d Mon Sep 17 00:00:00 2001 From: Foivos Date: Mon, 16 Dec 2024 14:49:31 +0200 Subject: [PATCH 12/14] add a test for the new field added. --- sui/gateway.js | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/sui/gateway.js b/sui/gateway.js index 1abbd3bfd..284fed013 100644 --- a/sui/gateway.js +++ b/sui/gateway.js @@ -470,6 +470,50 @@ async function checkVersionControl(version, options) { } } +async function testNewField(value, options) { + const config = loadConfig(options.env); + + const chain = getChainConfig(config, options.chainName); + const [keypair, client] = getWallet(chain, options); + await printWalletInfo(keypair, client, chain, options); + + if (!chain.contracts?.AxelarGateway) { + throw new Error('Axelar Gateway package not found.'); + } + + const contractConfig = chain.contracts.AxelarGateway; + const packageId = contractConfig.address; + + let tx = new Transaction(); + + tx.moveCall({ + target: `${packageId}::gateway::set_new_field`, + arguments: [ + tx.object(contractConfig.objects.Gateway), + tx.pure.u64(value), + ] + }); + + await broadcast(client, keypair, tx, 'Set new_field'); + + + tx = new Transaction(); + + tx.moveCall({ + target: `${packageId}::gateway::new_field`, + arguments: [ + tx.object(contractConfig.objects.Gateway), + ] + }); + + const response = await client.devInspectTransactionBlock({ + transactionBlock: tx, + sender: keypair.toSuiAddress(), + }); + const returnedValue = bcs.U64.parse(new Uint8Array(response.results[0].returnValues[0][0])); + console.log(`Set the value to ${value} and it was set to ${returnedValue}.`); +} + async function mainProcessor(processor, args, options) { const config = loadConfig(options.env); @@ -565,6 +609,13 @@ if (require.main === module) { checkVersionControl(version, options); }); + program + .command('test-new-field ') + .description('Test the new field added for upgrade-versioned') + .action((value, options) => { + testNewField(value, options); + }); + addOptionsToCommands(program, addBaseOptions, { offline: true }); program.parse(); From d0468a80902ba12b169d1044b5f388b8610e61d2 Mon Sep 17 00:00:00 2001 From: Foivos Date: Mon, 16 Dec 2024 14:54:28 +0200 Subject: [PATCH 13/14] add a delay --- sui/gateway.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sui/gateway.js b/sui/gateway.js index 284fed013..13d95e03d 100644 --- a/sui/gateway.js +++ b/sui/gateway.js @@ -495,7 +495,7 @@ async function testNewField(value, options) { }); await broadcast(client, keypair, tx, 'Set new_field'); - + await new Promise((resolve) => setTimeout(resolve, 1000)); tx = new Transaction(); From 942916167a74d7aaf97d9a7a3e1a77393beb7923 Mon Sep 17 00:00:00 2001 From: Foivos Date: Tue, 17 Dec 2024 15:54:57 +0200 Subject: [PATCH 14/14] prettier --- sui/gateway.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/sui/gateway.js b/sui/gateway.js index 13d95e03d..6a8e4ef26 100644 --- a/sui/gateway.js +++ b/sui/gateway.js @@ -488,10 +488,7 @@ async function testNewField(value, options) { tx.moveCall({ target: `${packageId}::gateway::set_new_field`, - arguments: [ - tx.object(contractConfig.objects.Gateway), - tx.pure.u64(value), - ] + arguments: [tx.object(contractConfig.objects.Gateway), tx.pure.u64(value)], }); await broadcast(client, keypair, tx, 'Set new_field'); @@ -501,9 +498,7 @@ async function testNewField(value, options) { tx.moveCall({ target: `${packageId}::gateway::new_field`, - arguments: [ - tx.object(contractConfig.objects.Gateway), - ] + arguments: [tx.object(contractConfig.objects.Gateway)], }); const response = await client.devInspectTransactionBlock({