diff --git a/.changeset/twelve-jokes-confess.md b/.changeset/twelve-jokes-confess.md new file mode 100644 index 00000000..041c6fcb --- /dev/null +++ b/.changeset/twelve-jokes-confess.md @@ -0,0 +1,9 @@ +--- +'@axelar-network/axelar-cgp-sui': minor +--- + +added previous signer retention in gateway setup + +added move source copy util to avoid modifying sources in place + +added move package build util diff --git a/.github/actions/install/action.yaml b/.github/actions/install/action.yaml index 8e623ae1..89e2e9fb 100644 --- a/.github/actions/install/action.yaml +++ b/.github/actions/install/action.yaml @@ -59,6 +59,7 @@ runs: with: node-version: 18 cache: 'npm' + registry-url: 'https://registry.npmjs.org' - name: Install dependencies shell: bash diff --git a/.gitignore b/.gitignore index f01e2714..9661985b 100644 --- a/.gitignore +++ b/.gitignore @@ -68,6 +68,5 @@ target/ # MSVC Windows builds of rustc generate these, which store debugging information *.pdb -# dist and move_compile -dist -move_comile +# move_compile +move_compile/ diff --git a/README.md b/README.md index 1dde9c60..48eedcc6 100644 --- a/README.md +++ b/README.md @@ -150,3 +150,4 @@ This module is responsible for managing all of the storage needs of the ITS This is the module that anyone would directly interract with. It needs to be able to do the following - `register_coin`: This function takes the `ITS` object and mutates it by adding a coin with the specified `CoinManagement` and `CoinInfo`. +- \ No newline at end of file diff --git a/move/axelar_gateway/sources/auth.move b/move/axelar_gateway/sources/auth.move index a3e0f07f..681ccef7 100644 --- a/move/axelar_gateway/sources/auth.move +++ b/move/axelar_gateway/sources/auth.move @@ -22,12 +22,6 @@ module axelar_gateway::auth { const EMalformedSigners: u64 = 5; const EInvalidEpoch: u64 = 6; - // --------- - // Constants - // --------- - /// Used for a check in `validate_proof_old` function. - const PREVIOUS_KEY_RETENTION: u64 = 16; - // ----- // Types // ----- @@ -40,8 +34,10 @@ module axelar_gateway::auth { domain_separator: Bytes32, /// Minimum rotation delay. minimum_rotation_delay: u64, - /// Timestamp of the last rotation + /// Timestamp of the last rotation. last_rotation_timestamp: u64, + /// Number of previous signers retained (latest signer isn't included in the count). + previous_signers_retention: u64, } public struct MessageToSign has copy, drop, store { @@ -70,12 +66,14 @@ module axelar_gateway::auth { domain_separator: bytes32::default(), minimum_rotation_delay: 0, last_rotation_timestamp: 0, + previous_signers_retention: 0, } } public(package) fun setup( domain_separator: Bytes32, minimum_rotation_delay: u64, + previous_signers_retention: u64, initial_signers: WeightedSigners, clock: &Clock, ctx: &mut TxContext, @@ -86,6 +84,7 @@ module axelar_gateway::auth { domain_separator, minimum_rotation_delay, last_rotation_timestamp: 0, + previous_signers_retention, }; signers.rotate_signers(clock, initial_signers, false); @@ -104,7 +103,7 @@ module axelar_gateway::auth { let current_epoch = self.epoch; let is_latest_signers = current_epoch == signers_epoch; - assert!(signers_epoch != 0 && (current_epoch - signers_epoch) <= PREVIOUS_KEY_RETENTION, EInvalidEpoch); + assert!(signers_epoch != 0 && (current_epoch - signers_epoch) <= self.previous_signers_retention, EInvalidEpoch); let message = MessageToSign { domain_separator: self.domain_separator, diff --git a/move/axelar_gateway/sources/gateway.move b/move/axelar_gateway/sources/gateway.move index 392119b4..a4fc3a7a 100644 --- a/move/axelar_gateway/sources/gateway.move +++ b/move/axelar_gateway/sources/gateway.move @@ -128,6 +128,7 @@ module axelar_gateway::gateway { operator: address, domain_separator: Bytes32, minimum_rotation_delay: u64, + previous_signers_retention: u64, initial_signers: vector, clock: &Clock, ctx: &mut TxContext @@ -139,7 +140,7 @@ module axelar_gateway::gateway { id: object::new(ctx), operator, messages: table::new(ctx), - signers: auth::setup(domain_separator, minimum_rotation_delay, peel_weighted_signers(initial_signers), clock, ctx), + signers: auth::setup(domain_separator, minimum_rotation_delay, previous_signers_retention, peel_weighted_signers(initial_signers), clock, ctx), }; // Share the gateway object for anyone to use. diff --git a/package.json b/package.json index 1cc4216c..09110d71 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,9 @@ "files": [ "dist", "move", - "!move/**/build", - "!move/**/test" + "src", + "tsconfig.json", + "!move/**/build" ], "main": "dist/index.js", "scripts": { @@ -22,7 +23,8 @@ "coverage": "./scripts/coverage.sh", "cs": "changeset", "lint": "eslint --fix './src/*.ts' './test/*.js'", - "prettier": "prettier --write './src/*.ts' './test/*.js'" + "prettier": "prettier --write './src/*.ts' './test/*.js'", + "postinstall": "npm run build-ts" }, "keywords": [ "axelar", @@ -40,14 +42,14 @@ "ethers": "^5.0.0", "fs": "^0.0.1-security", "secp256k1": "^5.0.0", - "tmp": "^0.2.1" + "tmp": "^0.2.1", + "@types/tmp": "^0.2.6" }, "devDependencies": { "@changesets/cli": "^2.27.6", "@ianvs/prettier-plugin-sort-imports": "^4.2.1", "@typescript-eslint/eslint-plugin": "^7.13.1", "@typescript-eslint/parser": "^7.13.1", - "@types/tmp": "^0.2.6", "chai": "^4.3.7", "dotenv": "^16.3.1", "eslint": "^8.57.0", diff --git a/src/tx-builder.ts b/src/tx-builder.ts index 7f46d609..1fbf0097 100644 --- a/src/tx-builder.ts +++ b/src/tx-builder.ts @@ -11,7 +11,7 @@ import { } from '@mysten/sui.js/client'; import { Keypair } from '@mysten/sui.js/dist/cjs/cryptography'; import { TransactionBlock, TransactionObjectInput, TransactionResult } from '@mysten/sui.js/transactions'; -import { utils as ethersUtils } from 'ethers'; +import { Bytes, utils as ethersUtils } from 'ethers'; import tmp from 'tmp'; import { updateMoveToml } from './utils'; @@ -243,20 +243,29 @@ export class TxBuilder { }); } - async publishPackage(packageName: string, moveDir: string = `${__dirname}/../move`): Promise { + async getContractBuild( + packageName: string, + moveDir: string = `${__dirname}/../move`, + ): Promise<{ modules: string[]; dependencies: string[]; digest: Bytes }> { updateMoveToml(packageName, '0x0', moveDir); tmp.setGracefulCleanup(); const tmpobj = tmp.dirSync({ unsafeCleanup: true }); - const { modules, dependencies } = JSON.parse( + const { modules, dependencies, digest } = JSON.parse( execSync(`sui move build --dump-bytecode-as-base64 --path ${path.join(moveDir, packageName)} --install-dir ${tmpobj.name}`, { encoding: 'utf-8', stdio: 'pipe', // silent the output }), ); + return { modules, dependencies, digest }; + } + + async publishPackage(packageName: string, moveDir: string = `${__dirname}/../move`): Promise { + const { modules, dependencies } = await this.getContractBuild(packageName, moveDir); + return this.tx.publish({ modules, dependencies, diff --git a/src/utils.ts b/src/utils.ts index 69bec017..17e0a920 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -76,6 +76,14 @@ export function updateMoveToml(packageName: string, packageId: string, moveDir: fs.writeFileSync(path, toml); } +export function copyMovePackage(packageName: string, fromDir: null | string, toDir: string) { + if (fromDir == null) { + fromDir = `${__dirname}/../move`; + } + + fs.cpSync(`${fromDir}/${packageName}`, `${toDir}/${packageName}`, { recursive: true }); +} + export function parseEnv(arg: string) { switch (arg?.toLowerCase()) { case 'localnet': diff --git a/test/axelar-gateway.js b/test/axelar-gateway.js index 8d682530..7e7257d1 100644 --- a/test/axelar-gateway.js +++ b/test/axelar-gateway.js @@ -20,6 +20,7 @@ describe('Axelar Gateway', () => { const deployer = Ed25519Keypair.fromSecretKey(arrayify(getRandomBytes32())); const keypair = Ed25519Keypair.fromSecretKey(arrayify(getRandomBytes32())); const domainSeparator = getRandomBytes32(); + const network = process.env.NETWORK || 'localnet'; let operatorKeys; let signers; let nonce = 0; @@ -74,14 +75,15 @@ describe('Axelar Gateway', () => { } const minimumRotationDelay = 1000; + const previousSignersRetention = 15; before(async () => { - client = new SuiClient({ url: getFullnodeUrl('localnet') }); + client = new SuiClient({ url: getFullnodeUrl(network) }); await Promise.all( [operator, deployer, keypair].map((keypair) => requestSuiFromFaucetV0({ - host: getFaucetHost('localnet'), + host: getFaucetHost(network), recipient: keypair.toSuiAddress(), }), ), @@ -105,7 +107,15 @@ describe('Axelar Gateway', () => { await builder.moveCall({ target: `${packageId}::gateway::setup`, - arguments: [creatorCap, operator.toSuiAddress(), separator, minimumRotationDelay, encodedSigners, '0x6'], + arguments: [ + creatorCap, + operator.toSuiAddress(), + separator, + minimumRotationDelay, + previousSignersRetention, + encodedSigners, + '0x6', + ], }); result = await builder.signAndExecute(deployer); diff --git a/test/utils.js b/test/utils.js index a7721d3e..f479b78e 100644 --- a/test/utils.js +++ b/test/utils.js @@ -1,17 +1,19 @@ const { keccak256, defaultAbiCoder } = require('ethers/lib/utils'); const { TxBuilder } = require('../dist/tx-builder'); -const { updateMoveToml } = require('../dist/utils'); +const { updateMoveToml, copyMovePackage } = require('../dist/utils'); const chai = require('chai'); const { expect } = chai; async function publishPackage(client, keypair, packageName) { + const compileDir = `${__dirname}/../move_compile`; + copyMovePackage(packageName, null, compileDir); const builder = new TxBuilder(client); - await builder.publishPackageAndTransferCap(packageName, keypair.toSuiAddress()); + await builder.publishPackageAndTransferCap(packageName, keypair.toSuiAddress(), compileDir); const publishTxn = await builder.signAndExecute(keypair); const packageId = (publishTxn.objectChanges?.find((a) => a.type === 'published') ?? []).packageId; - updateMoveToml(packageName, packageId); + updateMoveToml(packageName, packageId, compileDir); return { packageId, publishTxn }; }