From 27e3a459a5b79f6749fd208696026c54e1f72f5d Mon Sep 17 00:00:00 2001 From: Koji Matsumiya Date: Thu, 18 Jun 2026 12:53:36 +0900 Subject: [PATCH 1/6] fix: validate local signer in initiateTx --- .gas-snapshot | 26 ++++++++++++---------- package-lock.json | 4 +--- pkg/testing/cross_test.go | 2 +- src/core/Initiator.sol | 18 +++++++++++++++- test/Initiator.t.sol | 45 ++++++++++++++++++++++++++++++++++++++- 5 files changed, 78 insertions(+), 17 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 7dfdd05..fafe374 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -49,8 +49,8 @@ CrossModuleTest:test_onRecvPacket_RevertWhen_CallerLacksIbcRole() (gas: 31310) CrossModuleTest:test_onTimeoutPacket_CallsHandlerWhenCallerHasRole() (gas: 55726) CrossModuleTest:test_onTimeoutPacket_RevertWhen_CallerLacksIbcRole() (gas: 29694) CrossModuleTest:test_supportsInterface_ReturnsTrueForIIBCAndIAccessControlAndFalseForUnsupported() (gas: 8391) -CrossSimpleModuleTest:test_constructor_DoesNotGrantIbcRoleWhenDebugModeFalse() (gas: 3240600) -CrossSimpleModuleTest:test_constructor_GrantsIbcRoleWhenDebugModeTrue() (gas: 3262879) +CrossSimpleModuleTest:test_constructor_DoesNotGrantIbcRoleWhenDebugModeFalse() (gas: 3288930) +CrossSimpleModuleTest:test_constructor_GrantsIbcRoleWhenDebugModeTrue() (gas: 3311209) CrossStoreTest:test_SaveAndLoad_CoordinatorState() (gas: 119980) CrossStoreTest:test_SaveLoad_DataLossChannel0() (gas: 102862) CrossStoreTest:test_SaveLoad_DataLossExtraChannels() (gas: 98710) @@ -118,16 +118,20 @@ IBCKeeperTest:test_exposed_initIBCKeeper_AllowsZeroAddress() (gas: 192201) IBCKeeperTest:test_exposed_initIBCKeeper_RevertOn_SecondInitialization() (gas: 12904) IBCKeeperTest:test_getIBCHandler_ReturnsSameAddress() (gas: 9955) InitiatorTest:test_constructor_SetsChainIDHash() (gas: 8310) -InitiatorTest:test_getRequiredAccounts_AggregatesSigners() (gas: 266833) -InitiatorTest:test_getRequiredAccounts_ReturnsEmptyArrayWhenNoSigners() (gas: 69831) -InitiatorTest:test_getRequiredAccounts_ReturnsEmptyArrayWhenNoTxs() (gas: 40964) -InitiatorTest:test_initiateTx_RevertWhen_ChainIDMismatch() (gas: 61018) +InitiatorTest:test_getRequiredAccounts_AggregatesSigners() (gas: 269747) +InitiatorTest:test_getRequiredAccounts_ReturnsEmptyArrayWhenNoSigners() (gas: 69976) +InitiatorTest:test_getRequiredAccounts_ReturnsEmptyArrayWhenNoTxs() (gas: 41000) +InitiatorTest:test_initiateTx_RevertWhen_ChainIDMismatch() (gas: 61029) +InitiatorTest:test_initiateTx_RevertWhen_LocalSignerNotEqualSender() (gas: 196183) +InitiatorTest:test_initiateTx_RevertWhen_SignerAuthModeIsNotLocal() (gas: 237396) +InitiatorTest:test_initiateTx_RevertWhen_SignerLengthMultiple() (gas: 272391) +InitiatorTest:test_initiateTx_RevertWhen_SignerLengthZero() (gas: 98053) InitiatorTest:test_initiateTx_RevertWhen_TimeoutHeightExpired() (gas: 58522) -InitiatorTest:test_initiateTx_RevertWhen_TimeoutTimestampExpired() (gas: 59317) -InitiatorTest:test_initiateTx_RevertWhen_txIDAlreadyExists() (gas: 155517) -InitiatorTest:test_initiateTx_SucceedsAsPendingWhenSignersNotMet() (gas: 272288) -InitiatorTest:test_initiateTx_SucceedsAsVerifiedWhenSignersMet() (gas: 309278) -InitiatorTest:test_selfXCC_ReturnsCorrectChannelInfo() (gas: 17058) +InitiatorTest:test_initiateTx_RevertWhen_TimeoutTimestampExpired() (gas: 59339) +InitiatorTest:test_initiateTx_RevertWhen_txIDAlreadyExists() (gas: 158028) +InitiatorTest:test_initiateTx_SucceedsAsPendingWhenSignersNotMet() (gas: 400388) +InitiatorTest:test_initiateTx_SucceedsAsVerifiedWhenSignersMet() (gas: 437378) +InitiatorTest:test_selfXCC_ReturnsCorrectChannelInfo() (gas: 17102) MockCrossContractTest:test_onAbort_Succeeds() (gas: 9583) MockCrossContractTest:test_onCommit_Succeeds() (gas: 9429) MockCrossContractTest:test_onContractCommitImmediately_ReturnsExpectedBytes() (gas: 15797) diff --git a/package-lock.json b/package-lock.json index 26f28cb..d12fd86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -187,8 +187,7 @@ "version": "5.4.0", "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.4.0.tgz", "integrity": "sha512-eCYgWnLg6WO+X52I16TZt8uEjbtdkgLC0SUX/xnAksjjrQI4Xfn4iBRoI5j55dmlOhDv1Y7BoR3cU7e3WWhC6A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@hyperledger-labs/yui-ibc-solidity/node_modules/@openzeppelin/contracts-upgradeable": { "version": "5.4.0", @@ -289,7 +288,6 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", diff --git a/pkg/testing/cross_test.go b/pkg/testing/cross_test.go index 7e88889..edaee0b 100644 --- a/pkg/testing/cross_test.go +++ b/pkg/testing/cross_test.go @@ -168,7 +168,7 @@ func (suite *CrossTestSuite) TestInitiateTx() { suite.Require().NoError(err) signer1 := crosssimplemodule.AccountData{ - Id: decodeB64("0/syrvWS1CkCswOi9XwXq+gd+dIByQLeH9t/qFrDXqE="), + Id: opts.From.Bytes(), AuthType: crosssimplemodule.AuthTypeData{ Mode: uint8(authtypes.AuthMode_AUTH_MODE_LOCAL), }, diff --git a/src/core/Initiator.sol b/src/core/Initiator.sol index 68034d9..8c7a11a 100644 --- a/src/core/Initiator.sol +++ b/src/core/Initiator.sol @@ -9,7 +9,7 @@ import {ICrossEvent} from "./ICrossEvent.sol"; import {TxIDUtils} from "./TxIDUtils.sol"; import {MsgInitiateTx, MsgInitiateTxResponse, QuerySelfXCCResponse} from "../proto/cross/core/initiator/Initiator.sol"; -import {Account} from "../proto/cross/core/auth/Auth.sol"; +import {Account, AuthType} from "../proto/cross/core/auth/Auth.sol"; import {GoogleProtobufAny} from "@hyperledger-labs/yui-ibc-solidity/contracts/proto/GoogleProtobufAny.sol"; import {ChannelInfo} from "../proto/cross/core/xcc/XCC.sol"; @@ -49,6 +49,8 @@ abstract contract Initiator is IInitiator, TxAuthManagerBase, TxManagerBase, Ree bytes32 txID = msg_.computeTxId(); if (_isTxRecorded(txID)) revert TxIDAlreadyExists(txID); + _validateLocalSigner(msg_.signers, msg.sender); + // persist as PENDING _createTx(txID, msg_); @@ -98,4 +100,18 @@ abstract contract Initiator is IInitiator, TxAuthManagerBase, TxManagerBase, Ree } return out; } + + function _validateLocalSigner(Account.Data[] calldata signers, address sender) internal pure { + if (signers.length != 1) { + revert InvalidSignersLength(); + } + + bytes memory expectedSignerId = abi.encodePacked(sender); + if (signers[0].auth_type.mode != AuthType.AuthMode.AUTH_MODE_LOCAL) { + revert AuthModeMismatch(); + } + if (keccak256(signers[0].id) != keccak256(expectedSignerId)) { + revert SignerMustEqualSender(); + } + } } diff --git a/test/Initiator.t.sol b/test/Initiator.t.sol index eda022f..a7e3178 100644 --- a/test/Initiator.t.sol +++ b/test/Initiator.t.sol @@ -111,7 +111,9 @@ contract InitiatorTest is Test, ICrossEvent { MsgInitiateTx.Data private baseMsg; AuthAccount.Data private signerA; AuthAccount.Data private signerB; + AuthAccount.Data private senderLocalSigner; AuthType.Data private localAuthType; + AuthType.Data private extensionAuthType; string private chainIDStr; function setUp() public { @@ -119,9 +121,14 @@ contract InitiatorTest is Test, ICrossEvent { GoogleProtobufAny.Data memory emptyAny = GoogleProtobufAny.Data({type_url: "", value: ""}); localAuthType = AuthType.Data({mode: AuthType.AuthMode.AUTH_MODE_LOCAL, option: emptyAny}); + extensionAuthType = AuthType.Data({ + mode: AuthType.AuthMode.AUTH_MODE_EXTENSION, + option: GoogleProtobufAny.Data({type_url: "/verifier/test", value: hex"1234"}) + }); signerA = AuthAccount.Data({id: bytes("signerA"), auth_type: localAuthType}); signerB = AuthAccount.Data({id: bytes("signerB"), auth_type: localAuthType}); + senderLocalSigner = AuthAccount.Data({id: abi.encodePacked(address(this)), auth_type: localAuthType}); chainIDStr = Strings.toString(block.chainid); @@ -129,7 +136,7 @@ contract InitiatorTest is Test, ICrossEvent { ContractTransaction.Data[] memory txs = new ContractTransaction.Data[](1); txs[0].signers = new AuthAccount.Data[](1); - txs[0].signers[0] = signerA; + txs[0].signers[0] = senderLocalSigner; baseMsg = MsgInitiateTx.Data({ chain_id: chainIDStr, @@ -158,6 +165,8 @@ contract InitiatorTest is Test, ICrossEvent { } function test_initiateTx_SucceedsAsPendingWhenSignersNotMet() public { + baseMsg.signers = new AuthAccount.Data[](1); + baseMsg.signers[0] = senderLocalSigner; bytes32 txIDHash = sha256(MsgInitiateTx.encode(baseMsg)); harness.setSignReturns(false); @@ -180,6 +189,8 @@ contract InitiatorTest is Test, ICrossEvent { } function test_initiateTx_SucceedsAsVerifiedWhenSignersMet() public { + baseMsg.signers = new AuthAccount.Data[](1); + baseMsg.signers[0] = senderLocalSigner; bytes32 txIDHash = sha256(MsgInitiateTx.encode(baseMsg)); harness.setSignReturns(true); @@ -200,6 +211,38 @@ contract InitiatorTest is Test, ICrossEvent { assertEq(harness.lastInittxID(), txIDHash, "Mock: _initAuthState should be called with txID"); } + function test_initiateTx_RevertWhen_SignerLengthZero() public { + baseMsg.signers = new AuthAccount.Data[](0); + + vm.expectRevert(ICrossError.InvalidSignersLength.selector); + harness.initiateTx(baseMsg); + } + + function test_initiateTx_RevertWhen_SignerLengthMultiple() public { + baseMsg.signers = new AuthAccount.Data[](2); + baseMsg.signers[0] = senderLocalSigner; + baseMsg.signers[1] = signerB; + + vm.expectRevert(ICrossError.InvalidSignersLength.selector); + harness.initiateTx(baseMsg); + } + + function test_initiateTx_RevertWhen_LocalSignerNotEqualSender() public { + baseMsg.signers = new AuthAccount.Data[](1); + baseMsg.signers[0] = signerA; + + vm.expectRevert(ICrossError.SignerMustEqualSender.selector); + harness.initiateTx(baseMsg); + } + + function test_initiateTx_RevertWhen_SignerAuthModeIsNotLocal() public { + baseMsg.signers = new AuthAccount.Data[](1); + baseMsg.signers[0] = AuthAccount.Data({id: abi.encodePacked(address(this)), auth_type: extensionAuthType}); + + vm.expectRevert(ICrossError.AuthModeMismatch.selector); + harness.initiateTx(baseMsg); + } + function test_initiateTx_RevertWhen_ChainIDMismatch() public { baseMsg.chain_id = "wrong-chain"; From c5aa8afb8406740c7ea16d8d15c32477bf14fc73 Mon Sep 17 00:00:00 2001 From: Koji Matsumiya Date: Fri, 19 Jun 2026 13:51:07 +0900 Subject: [PATCH 2/6] feat: support extension mode for initiateTx --- src/core/Initiator.sol | 26 ++++++++++- test/Initiator.t.sol | 98 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 120 insertions(+), 4 deletions(-) diff --git a/src/core/Initiator.sol b/src/core/Initiator.sol index 8c7a11a..3b62ded 100644 --- a/src/core/Initiator.sol +++ b/src/core/Initiator.sol @@ -49,7 +49,7 @@ abstract contract Initiator is IInitiator, TxAuthManagerBase, TxManagerBase, Ree bytes32 txID = msg_.computeTxId(); if (_isTxRecorded(txID)) revert TxIDAlreadyExists(txID); - _validateLocalSigner(msg_.signers, msg.sender); + _validateInitiateSigners(txID, msg_.signers, msg.sender); // persist as PENDING _createTx(txID, msg_); @@ -101,6 +101,30 @@ abstract contract Initiator is IInitiator, TxAuthManagerBase, TxManagerBase, Ree return out; } + function _validateInitiateSigners(bytes32 txID, Account.Data[] calldata signers, address sender) internal { + uint256 len = signers.length; + if (len == 0) { + revert InvalidSignersLength(); + } + + AuthType.AuthMode mode = signers[0].auth_type.mode; + for (uint256 i = 1; i < len; ++i) { + if (signers[i].auth_type.mode != mode) { + revert AuthModeMismatch(); + } + } + + if (mode == AuthType.AuthMode.AUTH_MODE_LOCAL) { + _validateLocalSigner(signers, sender); + return; + } + if (mode == AuthType.AuthMode.AUTH_MODE_EXTENSION) { + _verifySignatures(txID, signers); + return; + } + revert AuthModeMismatch(); + } + function _validateLocalSigner(Account.Data[] calldata signers, address sender) internal pure { if (signers.length != 1) { revert InvalidSignersLength(); diff --git a/test/Initiator.t.sol b/test/Initiator.t.sol index a7e3178..b92076b 100644 --- a/test/Initiator.t.sol +++ b/test/Initiator.t.sol @@ -62,7 +62,10 @@ contract MockTxManager is TxManagerBase { contract MockTxAuthManager is TxAuthManagerBase { mapping(bytes32 => bool) public completed; bytes32 public lastInittxID; + bytes32 public lastVerifytxID; + uint256 public verifyCount; bool private _signReturns = false; + bool private _verifyShouldRevert = false; function _initAuthState(bytes32 txID, AuthAccount.Data[] memory) internal virtual override { lastInittxID = txID; @@ -84,13 +87,21 @@ contract MockTxAuthManager is TxAuthManagerBase { revert("MockTxAuthManager._getAuthState not implemented"); } - function _verifySignatures(bytes32, AuthAccount.Data[] calldata) internal virtual override { - revert("MockTxAuthManager._verifySignatures not implemented"); + function _verifySignatures(bytes32 txID, AuthAccount.Data[] calldata) internal virtual override { + lastVerifytxID = txID; + ++verifyCount; + if (_verifyShouldRevert) { + revert ICrossError.SignatureVerificationFailed(txID); + } } function setSignReturns(bool returnsValue) public { _signReturns = returnsValue; } + + function setVerifyShouldRevert(bool shouldRevert) public { + _verifyShouldRevert = shouldRevert; + } } /** @@ -112,6 +123,8 @@ contract InitiatorTest is Test, ICrossEvent { AuthAccount.Data private signerA; AuthAccount.Data private signerB; AuthAccount.Data private senderLocalSigner; + AuthAccount.Data private extSignerA; + AuthAccount.Data private extSignerB; AuthType.Data private localAuthType; AuthType.Data private extensionAuthType; string private chainIDStr; @@ -129,6 +142,8 @@ contract InitiatorTest is Test, ICrossEvent { signerA = AuthAccount.Data({id: bytes("signerA"), auth_type: localAuthType}); signerB = AuthAccount.Data({id: bytes("signerB"), auth_type: localAuthType}); senderLocalSigner = AuthAccount.Data({id: abi.encodePacked(address(this)), auth_type: localAuthType}); + extSignerA = AuthAccount.Data({id: bytes("extSignerA"), auth_type: extensionAuthType}); + extSignerB = AuthAccount.Data({id: bytes("extSignerB"), auth_type: extensionAuthType}); chainIDStr = Strings.toString(block.chainid); @@ -211,6 +226,49 @@ contract InitiatorTest is Test, ICrossEvent { assertEq(harness.lastInittxID(), txIDHash, "Mock: _initAuthState should be called with txID"); } + function test_initiateTx_SucceedsWithExtensionSignerAsPendingWhenSignersNotMet() public { + baseMsg.signers = new AuthAccount.Data[](1); + baseMsg.signers[0] = extSignerA; + bytes32 txIDHash = sha256(MsgInitiateTx.encode(baseMsg)); + + harness.setSignReturns(false); + + vm.expectEmit(true, false, false, true, address(harness)); + emit TxInitiated(abi.encodePacked(txIDHash), address(this), baseMsg); + + MsgInitiateTxResponse.Data memory resp = harness.initiateTx(baseMsg); + + assertEq( + uint256(resp.status), + uint256(MsgInitiateTxResponse.InitiateTxStatus.INITIATE_TX_STATUS_PENDING), + "Status should be PENDING" + ); + assertEq(harness.verifyCount(), 1, "Mock: _verifySignatures should be called once"); + assertEq(harness.lastVerifytxID(), txIDHash, "Mock: _verifySignatures should receive txID"); + } + + function test_initiateTx_SucceedsWithMultipleExtensionSignersAsVerified() public { + baseMsg.signers = new AuthAccount.Data[](2); + baseMsg.signers[0] = extSignerA; + baseMsg.signers[1] = extSignerB; + bytes32 txIDHash = sha256(MsgInitiateTx.encode(baseMsg)); + + harness.setSignReturns(true); + + vm.expectEmit(true, false, false, true, address(harness)); + emit TxInitiated(abi.encodePacked(txIDHash), address(this), baseMsg); + + MsgInitiateTxResponse.Data memory resp = harness.initiateTx(baseMsg); + + assertEq( + uint256(resp.status), + uint256(MsgInitiateTxResponse.InitiateTxStatus.INITIATE_TX_STATUS_VERIFIED), + "Status should be VERIFIED" + ); + assertEq(harness.verifyCount(), 1, "Mock: _verifySignatures should be called once"); + assertEq(harness.lastVerifytxID(), txIDHash, "Mock: _verifySignatures should receive txID"); + } + function test_initiateTx_RevertWhen_SignerLengthZero() public { baseMsg.signers = new AuthAccount.Data[](0); @@ -237,12 +295,46 @@ contract InitiatorTest is Test, ICrossEvent { function test_initiateTx_RevertWhen_SignerAuthModeIsNotLocal() public { baseMsg.signers = new AuthAccount.Data[](1); - baseMsg.signers[0] = AuthAccount.Data({id: abi.encodePacked(address(this)), auth_type: extensionAuthType}); + baseMsg.signers[0] = AuthAccount.Data({ + id: abi.encodePacked(address(this)), + auth_type: AuthType.Data({ + mode: AuthType.AuthMode.AUTH_MODE_CHANNEL, option: GoogleProtobufAny.Data("", "") + }) + }); vm.expectRevert(ICrossError.AuthModeMismatch.selector); harness.initiateTx(baseMsg); } + function test_initiateTx_RevertWhen_MixedAuthModesLocalThenExtension() public { + baseMsg.signers = new AuthAccount.Data[](2); + baseMsg.signers[0] = senderLocalSigner; + baseMsg.signers[1] = extSignerA; + + vm.expectRevert(ICrossError.AuthModeMismatch.selector); + harness.initiateTx(baseMsg); + } + + function test_initiateTx_RevertWhen_MixedAuthModesExtensionThenLocal() public { + baseMsg.signers = new AuthAccount.Data[](2); + baseMsg.signers[0] = extSignerA; + baseMsg.signers[1] = senderLocalSigner; + + vm.expectRevert(ICrossError.AuthModeMismatch.selector); + harness.initiateTx(baseMsg); + } + + function test_initiateTx_RevertWhen_ExtensionSignatureVerificationFails() public { + baseMsg.signers = new AuthAccount.Data[](1); + baseMsg.signers[0] = extSignerA; + bytes32 txIDHash = sha256(MsgInitiateTx.encode(baseMsg)); + + harness.setVerifyShouldRevert(true); + + vm.expectRevert(abi.encodeWithSelector(ICrossError.SignatureVerificationFailed.selector, txIDHash)); + harness.initiateTx(baseMsg); + } + function test_initiateTx_RevertWhen_ChainIDMismatch() public { baseMsg.chain_id = "wrong-chain"; From e3e2bef5d1415fbc38ace9bc11fe4f4f2313efed Mon Sep 17 00:00:00 2001 From: Koji Matsumiya Date: Fri, 19 Jun 2026 14:00:23 +0900 Subject: [PATCH 3/6] chore: update gas snapshot --- .gas-snapshot | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index fafe374..bd4551d 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -49,8 +49,8 @@ CrossModuleTest:test_onRecvPacket_RevertWhen_CallerLacksIbcRole() (gas: 31310) CrossModuleTest:test_onTimeoutPacket_CallsHandlerWhenCallerHasRole() (gas: 55726) CrossModuleTest:test_onTimeoutPacket_RevertWhen_CallerLacksIbcRole() (gas: 29694) CrossModuleTest:test_supportsInterface_ReturnsTrueForIIBCAndIAccessControlAndFalseForUnsupported() (gas: 8391) -CrossSimpleModuleTest:test_constructor_DoesNotGrantIbcRoleWhenDebugModeFalse() (gas: 3288930) -CrossSimpleModuleTest:test_constructor_GrantsIbcRoleWhenDebugModeTrue() (gas: 3311209) +CrossSimpleModuleTest:test_constructor_DoesNotGrantIbcRoleWhenDebugModeFalse() (gas: 3326619) +CrossSimpleModuleTest:test_constructor_GrantsIbcRoleWhenDebugModeTrue() (gas: 3348898) CrossStoreTest:test_SaveAndLoad_CoordinatorState() (gas: 119980) CrossStoreTest:test_SaveLoad_DataLossChannel0() (gas: 102862) CrossStoreTest:test_SaveLoad_DataLossExtraChannels() (gas: 98710) @@ -117,21 +117,26 @@ ERC20TransferModuleTest:test_onContractPrepare_Success() (gas: 139360) IBCKeeperTest:test_exposed_initIBCKeeper_AllowsZeroAddress() (gas: 192201) IBCKeeperTest:test_exposed_initIBCKeeper_RevertOn_SecondInitialization() (gas: 12904) IBCKeeperTest:test_getIBCHandler_ReturnsSameAddress() (gas: 9955) -InitiatorTest:test_constructor_SetsChainIDHash() (gas: 8310) +InitiatorTest:test_constructor_SetsChainIDHash() (gas: 8332) InitiatorTest:test_getRequiredAccounts_AggregatesSigners() (gas: 269747) -InitiatorTest:test_getRequiredAccounts_ReturnsEmptyArrayWhenNoSigners() (gas: 69976) -InitiatorTest:test_getRequiredAccounts_ReturnsEmptyArrayWhenNoTxs() (gas: 41000) -InitiatorTest:test_initiateTx_RevertWhen_ChainIDMismatch() (gas: 61029) -InitiatorTest:test_initiateTx_RevertWhen_LocalSignerNotEqualSender() (gas: 196183) -InitiatorTest:test_initiateTx_RevertWhen_SignerAuthModeIsNotLocal() (gas: 237396) -InitiatorTest:test_initiateTx_RevertWhen_SignerLengthMultiple() (gas: 272391) -InitiatorTest:test_initiateTx_RevertWhen_SignerLengthZero() (gas: 98053) -InitiatorTest:test_initiateTx_RevertWhen_TimeoutHeightExpired() (gas: 58522) -InitiatorTest:test_initiateTx_RevertWhen_TimeoutTimestampExpired() (gas: 59339) -InitiatorTest:test_initiateTx_RevertWhen_txIDAlreadyExists() (gas: 158028) -InitiatorTest:test_initiateTx_SucceedsAsPendingWhenSignersNotMet() (gas: 400388) -InitiatorTest:test_initiateTx_SucceedsAsVerifiedWhenSignersMet() (gas: 437378) -InitiatorTest:test_selfXCC_ReturnsCorrectChannelInfo() (gas: 17102) +InitiatorTest:test_getRequiredAccounts_ReturnsEmptyArrayWhenNoSigners() (gas: 70064) +InitiatorTest:test_getRequiredAccounts_ReturnsEmptyArrayWhenNoTxs() (gas: 41088) +InitiatorTest:test_initiateTx_RevertWhen_ChainIDMismatch() (gas: 61051) +InitiatorTest:test_initiateTx_RevertWhen_ExtensionSignatureVerificationFails() (gas: 361122) +InitiatorTest:test_initiateTx_RevertWhen_LocalSignerNotEqualSender() (gas: 196736) +InitiatorTest:test_initiateTx_RevertWhen_MixedAuthModesExtensionThenLocal() (gas: 315433) +InitiatorTest:test_initiateTx_RevertWhen_MixedAuthModesLocalThenExtension() (gas: 315939) +InitiatorTest:test_initiateTx_RevertWhen_SignerAuthModeIsNotLocal() (gas: 188750) +InitiatorTest:test_initiateTx_RevertWhen_SignerLengthMultiple() (gas: 273362) +InitiatorTest:test_initiateTx_RevertWhen_SignerLengthZero() (gas: 98136) +InitiatorTest:test_initiateTx_RevertWhen_TimeoutHeightExpired() (gas: 58544) +InitiatorTest:test_initiateTx_RevertWhen_TimeoutTimestampExpired() (gas: 59427) +InitiatorTest:test_initiateTx_RevertWhen_txIDAlreadyExists() (gas: 158138) +InitiatorTest:test_initiateTx_SucceedsAsPendingWhenSignersNotMet() (gas: 401167) +InitiatorTest:test_initiateTx_SucceedsAsVerifiedWhenSignersMet() (gas: 438135) +InitiatorTest:test_initiateTx_SucceedsWithExtensionSignerAsPendingWhenSignersNotMet() (gas: 482608) +InitiatorTest:test_initiateTx_SucceedsWithMultipleExtensionSignersAsVerified() (gas: 670296) +InitiatorTest:test_selfXCC_ReturnsCorrectChannelInfo() (gas: 17256) MockCrossContractTest:test_onAbort_Succeeds() (gas: 9583) MockCrossContractTest:test_onCommit_Succeeds() (gas: 9429) MockCrossContractTest:test_onContractCommitImmediately_ReturnsExpectedBytes() (gas: 15797) From b7f54d28e54c9c96f85a120a7a4bbac66b0e4225 Mon Sep 17 00:00:00 2001 From: Koji Matsumiya Date: Fri, 19 Jun 2026 14:07:52 +0900 Subject: [PATCH 4/6] fix: update initiator test --- .gas-snapshot | 14 +++++++------- test/Initiator.t.sol | 7 ++++++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index bd4551d..b2d4668 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -122,20 +122,20 @@ InitiatorTest:test_getRequiredAccounts_AggregatesSigners() (gas: 269747) InitiatorTest:test_getRequiredAccounts_ReturnsEmptyArrayWhenNoSigners() (gas: 70064) InitiatorTest:test_getRequiredAccounts_ReturnsEmptyArrayWhenNoTxs() (gas: 41088) InitiatorTest:test_initiateTx_RevertWhen_ChainIDMismatch() (gas: 61051) -InitiatorTest:test_initiateTx_RevertWhen_ExtensionSignatureVerificationFails() (gas: 361122) +InitiatorTest:test_initiateTx_RevertWhen_ExtensionSignatureVerificationFails() (gas: 361117) InitiatorTest:test_initiateTx_RevertWhen_LocalSignerNotEqualSender() (gas: 196736) InitiatorTest:test_initiateTx_RevertWhen_MixedAuthModesExtensionThenLocal() (gas: 315433) InitiatorTest:test_initiateTx_RevertWhen_MixedAuthModesLocalThenExtension() (gas: 315939) InitiatorTest:test_initiateTx_RevertWhen_SignerAuthModeIsNotLocal() (gas: 188750) -InitiatorTest:test_initiateTx_RevertWhen_SignerLengthMultiple() (gas: 273362) +InitiatorTest:test_initiateTx_RevertWhen_SignerLengthMultiple() (gas: 273384) InitiatorTest:test_initiateTx_RevertWhen_SignerLengthZero() (gas: 98136) InitiatorTest:test_initiateTx_RevertWhen_TimeoutHeightExpired() (gas: 58544) InitiatorTest:test_initiateTx_RevertWhen_TimeoutTimestampExpired() (gas: 59427) -InitiatorTest:test_initiateTx_RevertWhen_txIDAlreadyExists() (gas: 158138) -InitiatorTest:test_initiateTx_SucceedsAsPendingWhenSignersNotMet() (gas: 401167) -InitiatorTest:test_initiateTx_SucceedsAsVerifiedWhenSignersMet() (gas: 438135) -InitiatorTest:test_initiateTx_SucceedsWithExtensionSignerAsPendingWhenSignersNotMet() (gas: 482608) -InitiatorTest:test_initiateTx_SucceedsWithMultipleExtensionSignersAsVerified() (gas: 670296) +InitiatorTest:test_initiateTx_RevertWhen_txIDAlreadyExists() (gas: 158111) +InitiatorTest:test_initiateTx_SucceedsAsPendingWhenSignersNotMet() (gas: 401162) +InitiatorTest:test_initiateTx_SucceedsAsVerifiedWhenSignersMet() (gas: 438108) +InitiatorTest:test_initiateTx_SucceedsWithExtensionSignerAsPendingWhenAuthNotCompleted() (gas: 539184) +InitiatorTest:test_initiateTx_SucceedsWithMultipleExtensionSignersAsVerified() (gas: 873940) InitiatorTest:test_selfXCC_ReturnsCorrectChannelInfo() (gas: 17256) MockCrossContractTest:test_onAbort_Succeeds() (gas: 9583) MockCrossContractTest:test_onCommit_Succeeds() (gas: 9429) diff --git a/test/Initiator.t.sol b/test/Initiator.t.sol index b92076b..463e0cf 100644 --- a/test/Initiator.t.sol +++ b/test/Initiator.t.sol @@ -226,9 +226,11 @@ contract InitiatorTest is Test, ICrossEvent { assertEq(harness.lastInittxID(), txIDHash, "Mock: _initAuthState should be called with txID"); } - function test_initiateTx_SucceedsWithExtensionSignerAsPendingWhenSignersNotMet() public { + function test_initiateTx_SucceedsWithExtensionSignerAsPendingWhenAuthNotCompleted() public { baseMsg.signers = new AuthAccount.Data[](1); baseMsg.signers[0] = extSignerA; + baseMsg.contract_transactions[0].signers = new AuthAccount.Data[](1); + baseMsg.contract_transactions[0].signers[0] = extSignerA; bytes32 txIDHash = sha256(MsgInitiateTx.encode(baseMsg)); harness.setSignReturns(false); @@ -251,6 +253,9 @@ contract InitiatorTest is Test, ICrossEvent { baseMsg.signers = new AuthAccount.Data[](2); baseMsg.signers[0] = extSignerA; baseMsg.signers[1] = extSignerB; + baseMsg.contract_transactions[0].signers = new AuthAccount.Data[](2); + baseMsg.contract_transactions[0].signers[0] = extSignerA; + baseMsg.contract_transactions[0].signers[1] = extSignerB; bytes32 txIDHash = sha256(MsgInitiateTx.encode(baseMsg)); harness.setSignReturns(true); From fd99c04c7bb7f4f9bb1ff731e26d2dbe627b5286 Mon Sep 17 00:00:00 2001 From: Koji Matsumiya Date: Fri, 19 Jun 2026 16:45:17 +0900 Subject: [PATCH 5/6] feat: update authentication logic to support multiple auth_mode. --- .gas-snapshot | 98 +++++++++++++++--------------- src/core/DelegatedLogicHandler.sol | 2 +- src/core/Initiator.sol | 60 ++++++++++++------ src/core/TxAuthManager.sol | 12 ++-- src/core/TxAuthManagerBase.sol | 2 +- test/Authenticator.t.sol | 2 +- test/Coordinator.t.sol | 2 +- test/Initiator.t.sol | 94 ++++++++++++++++++++++++---- 8 files changed, 180 insertions(+), 92 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index b2d4668..3e0d799 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -4,9 +4,9 @@ AuthenticatorTest:test_decodeTxID_RevertsIf_LengthIsShort() (gas: 9388) AuthenticatorTest:test_decodeTxID_RevertsIf_LengthIsZero() (gas: 9599) AuthenticatorTest:test_decodeTxID_SucceedsWith32Bytes() (gas: 6825) AuthenticatorTest:test_extSignTx_RevertsIf_TxIDLengthInvalid() (gas: 30496) -AuthenticatorTest:test_extSignTx_RevertsIf_VerificationFails() (gas: 49006) -AuthenticatorTest:test_extSignTx_SucceedsAsCompletedAndEmitsEvent() (gas: 84175) -AuthenticatorTest:test_extSignTx_SucceedsAsPending() (gas: 44306) +AuthenticatorTest:test_extSignTx_RevertsIf_VerificationFails() (gas: 51274) +AuthenticatorTest:test_extSignTx_SucceedsAsCompletedAndEmitsEvent() (gas: 86442) +AuthenticatorTest:test_extSignTx_SucceedsAsPending() (gas: 46573) AuthenticatorTest:test_signTx_RevertsIf_SignerNotEqualSender() (gas: 30692) AuthenticatorTest:test_signTx_RevertsIf_SignersLengthMultiple() (gas: 60751) AuthenticatorTest:test_signTx_RevertsIf_SignersLengthZero() (gas: 22045) @@ -49,8 +49,8 @@ CrossModuleTest:test_onRecvPacket_RevertWhen_CallerLacksIbcRole() (gas: 31310) CrossModuleTest:test_onTimeoutPacket_CallsHandlerWhenCallerHasRole() (gas: 55726) CrossModuleTest:test_onTimeoutPacket_RevertWhen_CallerLacksIbcRole() (gas: 29694) CrossModuleTest:test_supportsInterface_ReturnsTrueForIIBCAndIAccessControlAndFalseForUnsupported() (gas: 8391) -CrossSimpleModuleTest:test_constructor_DoesNotGrantIbcRoleWhenDebugModeFalse() (gas: 3326619) -CrossSimpleModuleTest:test_constructor_GrantsIbcRoleWhenDebugModeTrue() (gas: 3348898) +CrossSimpleModuleTest:test_constructor_DoesNotGrantIbcRoleWhenDebugModeFalse() (gas: 3328428) +CrossSimpleModuleTest:test_constructor_GrantsIbcRoleWhenDebugModeTrue() (gas: 3350707) CrossStoreTest:test_SaveAndLoad_CoordinatorState() (gas: 119980) CrossStoreTest:test_SaveLoad_DataLossChannel0() (gas: 102862) CrossStoreTest:test_SaveLoad_DataLossExtraChannels() (gas: 98710) @@ -78,18 +78,18 @@ DelegatedLogicHandlerTest:test_getCoordinatorState_RevertIf_CalledExternally() ( DelegatedLogicHandlerTest:test_handleAcknowledgement_DelegatesToTxManager() (gas: 54817) DelegatedLogicHandlerTest:test_handlePacket_DelegatesToTxManager() (gas: 60530) DelegatedLogicHandlerTest:test_handleTimeout_DelegatesToTxManager() (gas: 53748) -DelegatedLogicHandlerTest:test_initAuthState_DelegatesToAuthManager() (gas: 74424) +DelegatedLogicHandlerTest:test_initAuthState_DelegatesToAuthManager() (gas: 74430) DelegatedLogicHandlerTest:test_isCompletedAuth_DelegatesToAuthManager() (gas: 19325) DelegatedLogicHandlerTest:test_isCompletedAuth_RevertIf_CalledExternally() (gas: 11939) DelegatedLogicHandlerTest:test_isTxRecorded_DelegatesToTxManager() (gas: 19036) DelegatedLogicHandlerTest:test_isTxRecorded_RevertIf_CalledExternally() (gas: 11697) DelegatedLogicHandlerTest:test_runTxIfCompleted_DelegatesToTxManager() (gas: 106839) -DelegatedLogicHandlerTest:test_sign_DelegatesToAuthManager() (gas: 51649) +DelegatedLogicHandlerTest:test_sign_DelegatesToAuthManager() (gas: 51655) DelegatedLogicHandlerTest:test_staticCallSelf_RevertsIf_CallFailsEmpty() (gas: 12600) DelegatedLogicHandlerTest:test_staticCallSelf_RevertsIf_OnStateChange() (gas: 1040431853) DelegatedLogicHandlerTest:test_staticCallSelf_RevertsIf_TargetReverts() (gas: 14237) DelegatedLogicHandlerTest:test_staticCallSelf_Succeeds() (gas: 9181) -DelegatedLogicHandlerTest:test_verifySignatures_DelegatesToAuthManager() (gas: 49084) +DelegatedLogicHandlerTest:test_verifySignatures_DelegatesToAuthManager() (gas: 50944) ERC20TransferModuleTest:test_decodeCallInfo_RevertWhen_InvalidLength() (gas: 11651) ERC20TransferModuleTest:test_decodeCallInfo_Success() (gas: 11807) ERC20TransferModuleTest:test_initialize_RevertWhen_AlreadyInitialized() (gas: 13202) @@ -117,26 +117,28 @@ ERC20TransferModuleTest:test_onContractPrepare_Success() (gas: 139360) IBCKeeperTest:test_exposed_initIBCKeeper_AllowsZeroAddress() (gas: 192201) IBCKeeperTest:test_exposed_initIBCKeeper_RevertOn_SecondInitialization() (gas: 12904) IBCKeeperTest:test_getIBCHandler_ReturnsSameAddress() (gas: 9955) -InitiatorTest:test_constructor_SetsChainIDHash() (gas: 8332) -InitiatorTest:test_getRequiredAccounts_AggregatesSigners() (gas: 269747) -InitiatorTest:test_getRequiredAccounts_ReturnsEmptyArrayWhenNoSigners() (gas: 70064) -InitiatorTest:test_getRequiredAccounts_ReturnsEmptyArrayWhenNoTxs() (gas: 41088) -InitiatorTest:test_initiateTx_RevertWhen_ChainIDMismatch() (gas: 61051) -InitiatorTest:test_initiateTx_RevertWhen_ExtensionSignatureVerificationFails() (gas: 361117) -InitiatorTest:test_initiateTx_RevertWhen_LocalSignerNotEqualSender() (gas: 196736) -InitiatorTest:test_initiateTx_RevertWhen_MixedAuthModesExtensionThenLocal() (gas: 315433) -InitiatorTest:test_initiateTx_RevertWhen_MixedAuthModesLocalThenExtension() (gas: 315939) -InitiatorTest:test_initiateTx_RevertWhen_SignerAuthModeIsNotLocal() (gas: 188750) -InitiatorTest:test_initiateTx_RevertWhen_SignerLengthMultiple() (gas: 273384) -InitiatorTest:test_initiateTx_RevertWhen_SignerLengthZero() (gas: 98136) -InitiatorTest:test_initiateTx_RevertWhen_TimeoutHeightExpired() (gas: 58544) -InitiatorTest:test_initiateTx_RevertWhen_TimeoutTimestampExpired() (gas: 59427) -InitiatorTest:test_initiateTx_RevertWhen_txIDAlreadyExists() (gas: 158111) -InitiatorTest:test_initiateTx_SucceedsAsPendingWhenSignersNotMet() (gas: 401162) -InitiatorTest:test_initiateTx_SucceedsAsVerifiedWhenSignersMet() (gas: 438108) -InitiatorTest:test_initiateTx_SucceedsWithExtensionSignerAsPendingWhenAuthNotCompleted() (gas: 539184) -InitiatorTest:test_initiateTx_SucceedsWithMultipleExtensionSignersAsVerified() (gas: 873940) -InitiatorTest:test_selfXCC_ReturnsCorrectChannelInfo() (gas: 17256) +InitiatorTest:test_constructor_SetsChainIDHash() (gas: 8353) +InitiatorTest:test_getRequiredAccounts_AggregatesSigners() (gas: 270062) +InitiatorTest:test_getRequiredAccounts_ReturnsEmptyArrayWhenNoSigners() (gas: 70159) +InitiatorTest:test_getRequiredAccounts_ReturnsEmptyArrayWhenNoTxs() (gas: 41168) +InitiatorTest:test_initiateTx_RevertWhen_ChainIDMismatch() (gas: 61103) +InitiatorTest:test_initiateTx_RevertWhen_ExtensionSignatureVerificationFails() (gas: 365797) +InitiatorTest:test_initiateTx_RevertWhen_LocalSignerNotEqualSender() (gas: 198641) +InitiatorTest:test_initiateTx_RevertWhen_MixedSignersIncludeUnsupportedAuthMode() (gas: 319234) +InitiatorTest:test_initiateTx_RevertWhen_MultipleLocalSignersProvided() (gas: 276566) +InitiatorTest:test_initiateTx_RevertWhen_SignerAuthModeIsNotLocal() (gas: 189371) +InitiatorTest:test_initiateTx_RevertWhen_SignerLengthZero() (gas: 98265) +InitiatorTest:test_initiateTx_RevertWhen_TimeoutHeightExpired() (gas: 58596) +InitiatorTest:test_initiateTx_RevertWhen_TimeoutTimestampExpired() (gas: 59523) +InitiatorTest:test_initiateTx_RevertWhen_UnsupportedAuthModeMixedWithLocal() (gas: 272094) +InitiatorTest:test_initiateTx_RevertWhen_txIDAlreadyExists() (gas: 158270) +InitiatorTest:test_initiateTx_SucceedsAsPendingWhenSignersNotMet() (gas: 471312) +InitiatorTest:test_initiateTx_SucceedsAsVerifiedWhenSignersMet() (gas: 515207) +InitiatorTest:test_initiateTx_SucceedsWithExtensionSignerAsPendingWhenAuthNotCompleted() (gas: 611996) +InitiatorTest:test_initiateTx_SucceedsWithMixedExtensionAndLocalSignersAsPendingWhenAuthNotCompleted() (gas: 823085) +InitiatorTest:test_initiateTx_SucceedsWithMixedLocalAndExtensionSignersAsVerified() (gas: 857445) +InitiatorTest:test_initiateTx_SucceedsWithMultipleExtensionSignersAsVerified() (gas: 962190) +InitiatorTest:test_selfXCC_ReturnsCorrectChannelInfo() (gas: 17366) MockCrossContractTest:test_onAbort_Succeeds() (gas: 9583) MockCrossContractTest:test_onCommit_Succeeds() (gas: 9429) MockCrossContractTest:test_onContractCommitImmediately_ReturnsExpectedBytes() (gas: 15797) @@ -200,36 +202,36 @@ TxAtomicSimpleTest:test_runTx_RevertWhen_UnknownCommitProtocol() (gas: 23503) TxAtomicSimpleTest:test_runTx_SucceedsAndSendsPacket() (gas: 289827) TxAuthManagerTest:test_accountKey_ExtensionModeIgnoresOptionValue() (gas: 15975) TxAuthManagerTest:test_accountKey_GeneratesUniqueKeys() (gas: 57014) -TxAuthManagerTest:test_getAuthState_ReturnsCorrectRemainingSigners() (gas: 360320) +TxAuthManagerTest:test_getAuthState_ReturnsCorrectRemainingSigners() (gas: 360374) TxAuthManagerTest:test_getAuthState_RevertWhen_NotInitialized() (gas: 16241) -TxAuthManagerTest:test_getRemainingSigners_TracksSigningProgress() (gas: 353717) -TxAuthManagerTest:test_initAuthState_HandlesDuplicateSigners() (gas: 277966) +TxAuthManagerTest:test_getRemainingSigners_TracksSigningProgress() (gas: 353798) +TxAuthManagerTest:test_initAuthState_HandlesDuplicateSigners() (gas: 278020) TxAuthManagerTest:test_initAuthState_RevertWhen_AlreadyInitialized() (gas: 171833) -TxAuthManagerTest:test_initAuthState_Succeeds() (gas: 270899) -TxAuthManagerTest:test_initialize_RevertWhen_AlreadyInitialized() (gas: 1483796) -TxAuthManagerTest:test_initialize_RevertWhen_ArrayLengthMismatch() (gas: 1519901) -TxAuthManagerTest:test_initialize_RevertWhen_EmptyTypeUrl() (gas: 1457667) -TxAuthManagerTest:test_initialize_RevertWhen_ZeroAddressVerifier() (gas: 1395572) -TxAuthManagerTest:test_initialize_SucceedsAndEmitsEvents() (gas: 1565772) -TxAuthManagerTest:test_initialize_SucceedsWithEmptyArrays() (gas: 1392106) +TxAuthManagerTest:test_initAuthState_Succeeds() (gas: 270953) +TxAuthManagerTest:test_initialize_RevertWhen_AlreadyInitialized() (gas: 1427905) +TxAuthManagerTest:test_initialize_RevertWhen_ArrayLengthMismatch() (gas: 1464004) +TxAuthManagerTest:test_initialize_RevertWhen_EmptyTypeUrl() (gas: 1401765) +TxAuthManagerTest:test_initialize_RevertWhen_ZeroAddressVerifier() (gas: 1339669) +TxAuthManagerTest:test_initialize_SucceedsAndEmitsEvents() (gas: 1509831) +TxAuthManagerTest:test_initialize_SucceedsWithEmptyArrays() (gas: 1336241) TxAuthManagerTest:test_isCompletedAuth_ReturnsFalseWhenInitializedEmpty() (gas: 34759) TxAuthManagerTest:test_isCompletedAuth_ReturnsFalseWhenNotCompleted() (gas: 244840) TxAuthManagerTest:test_isCompletedAuth_ReturnsTrueWhenCompleted() (gas: 133928) TxAuthManagerTest:test_isCompletedAuth_RevertWhen_NotInitialized() (gas: 14387) -TxAuthManagerTest:test_setStateFromRemainingList_HandlesDuplicatesAndReturnsRemains() (gas: 261903) +TxAuthManagerTest:test_setStateFromRemainingList_HandlesDuplicatesAndReturnsRemains() (gas: 261957) TxAuthManagerTest:test_sign_FinalSignReturnsTrue() (gas: 214053) -TxAuthManagerTest:test_sign_IgnoresDuplicateSignatures() (gas: 283706) -TxAuthManagerTest:test_sign_IgnoresUnknownSigners() (gas: 211355) +TxAuthManagerTest:test_sign_IgnoresDuplicateSignatures() (gas: 283760) +TxAuthManagerTest:test_sign_IgnoresUnknownSigners() (gas: 211382) TxAuthManagerTest:test_sign_PartialSignReturnsFalse() (gas: 242956) TxAuthManagerTest:test_sign_RevertWhen_AuthAlreadyCompleted() (gas: 140603) TxAuthManagerTest:test_sign_RevertWhen_NotInitialized() (gas: 27957) -TxAuthManagerTest:test_verifySignatures_RevertWhen_AuthModeMismatch() (gas: 23650) -TxAuthManagerTest:test_verifySignatures_RevertWhen_StaticCallFails() (gas: 31433) -TxAuthManagerTest:test_verifySignatures_RevertWhen_TooManySigners() (gas: 146596) -TxAuthManagerTest:test_verifySignatures_RevertWhen_VerifierNotFound() (gas: 27421) -TxAuthManagerTest:test_verifySignatures_RevertWhen_VerifierReturnsFalse() (gas: 32413) -TxAuthManagerTest:test_verifySignatures_SucceedsMultipleSigners() (gas: 42534) -TxAuthManagerTest:test_verifySignatures_SucceedsSingleSigner() (gas: 28188) +TxAuthManagerTest:test_verifySignatures_RevertWhen_AuthModeMismatch() (gas: 25914) +TxAuthManagerTest:test_verifySignatures_RevertWhen_StaticCallFails() (gas: 33239) +TxAuthManagerTest:test_verifySignatures_RevertWhen_TooManySigners() (gas: 212205) +TxAuthManagerTest:test_verifySignatures_RevertWhen_VerifierNotFound() (gas: 29616) +TxAuthManagerTest:test_verifySignatures_RevertWhen_VerifierReturnsFalse() (gas: 34327) +TxAuthManagerTest:test_verifySignatures_SucceedsMultipleSigners() (gas: 45777) +TxAuthManagerTest:test_verifySignatures_SucceedsSingleSigner() (gas: 29980) TxIDUtilsTest:test_computeTxId_ReturnsConsistentId() (gas: 63200) TxIDUtilsTest:test_computeTxId_ReturnsDifferentIdForDifferentCallInfo() (gas: 63666) TxIDUtilsTest:test_computeTxId_ReturnsDifferentIdForDifferentChainId() (gas: 63768) diff --git a/src/core/DelegatedLogicHandler.sol b/src/core/DelegatedLogicHandler.sol index f536744..98234bf 100644 --- a/src/core/DelegatedLogicHandler.sol +++ b/src/core/DelegatedLogicHandler.sol @@ -90,7 +90,7 @@ abstract contract DelegatedLogicHandler is TxAuthManagerBase, TxManagerBase, Pac return abi.decode(ret, (TxAuthState.Data)); } - function _verifySignatures(bytes32 txIDHash, Account.Data[] calldata signers) internal virtual override { + function _verifySignatures(bytes32 txIDHash, Account.Data[] memory signers) internal virtual override { _delegateWithData( TX_AUTH_MANAGER, abi.encodeWithSelector(ITxAuthManager.verifySignatures.selector, txIDHash, signers) ); diff --git a/src/core/Initiator.sol b/src/core/Initiator.sol index 3b62ded..75cb63d 100644 --- a/src/core/Initiator.sol +++ b/src/core/Initiator.sol @@ -107,35 +107,55 @@ abstract contract Initiator is IInitiator, TxAuthManagerBase, TxManagerBase, Ree revert InvalidSignersLength(); } - AuthType.AuthMode mode = signers[0].auth_type.mode; - for (uint256 i = 1; i < len; ++i) { - if (signers[i].auth_type.mode != mode) { - revert AuthModeMismatch(); + uint256 extensionCount = 0; + bool hasLocalSigner = false; + uint256 localSignerIndex = 0; + + for (uint256 i = 0; i < len; ++i) { + AuthType.AuthMode mode = signers[i].auth_type.mode; + if (mode == AuthType.AuthMode.AUTH_MODE_LOCAL) { + if (hasLocalSigner) { + revert InvalidSignersLength(); + } + hasLocalSigner = true; + localSignerIndex = i; + continue; } + if (mode == AuthType.AuthMode.AUTH_MODE_EXTENSION) { + ++extensionCount; + continue; + } + revert AuthModeMismatch(); } - if (mode == AuthType.AuthMode.AUTH_MODE_LOCAL) { - _validateLocalSigner(signers, sender); - return; + if (hasLocalSigner) { + _validateLocalSigner(signers[localSignerIndex], sender); } - if (mode == AuthType.AuthMode.AUTH_MODE_EXTENSION) { - _verifySignatures(txID, signers); - return; - } - revert AuthModeMismatch(); - } - function _validateLocalSigner(Account.Data[] calldata signers, address sender) internal pure { - if (signers.length != 1) { - revert InvalidSignersLength(); + if (extensionCount > 0) { + _verifySignatures(txID, _extractExtensionSigners(signers, extensionCount)); } + } + function _validateLocalSigner(Account.Data calldata signer, address sender) internal pure { bytes memory expectedSignerId = abi.encodePacked(sender); - if (signers[0].auth_type.mode != AuthType.AuthMode.AUTH_MODE_LOCAL) { - revert AuthModeMismatch(); - } - if (keccak256(signers[0].id) != keccak256(expectedSignerId)) { + if (keccak256(signer.id) != keccak256(expectedSignerId)) { revert SignerMustEqualSender(); } } + + function _extractExtensionSigners(Account.Data[] calldata signers, uint256 extensionCount) + internal + pure + returns (Account.Data[] memory extensionSigners) + { + extensionSigners = new Account.Data[](extensionCount); + uint256 index = 0; + for (uint256 i = 0; i < signers.length; ++i) { + if (signers[i].auth_type.mode == AuthType.AuthMode.AUTH_MODE_EXTENSION) { + extensionSigners[index] = signers[i]; + ++index; + } + } + } } diff --git a/src/core/TxAuthManager.sol b/src/core/TxAuthManager.sol index c97f3e1..f2f7377 100644 --- a/src/core/TxAuthManager.sol +++ b/src/core/TxAuthManager.sol @@ -85,7 +85,7 @@ contract TxAuthManager is Initializable, TxAuthManagerBase, CrossStore, ITxAuthM return TxAuthState.Data({remaining_signers: remains}); } - function _verifySignatures(bytes32 txIDHash, Account.Data[] calldata signers) internal virtual override { + function _verifySignatures(bytes32 txIDHash, Account.Data[] memory signers) internal virtual override { uint256 len = signers.length; if (len > MAX_SIGNERS_PER_TX) { @@ -96,13 +96,13 @@ contract TxAuthManager is Initializable, TxAuthManagerBase, CrossStore, ITxAuthM // slither-disable-start calls-loop for (uint256 i = 0; i < len; ++i) { - Account.Data calldata signer = signers[i]; + Account.Data memory signer = signers[i]; if (signer.auth_type.mode != AuthType.AuthMode.AUTH_MODE_EXTENSION) { revert AuthModeMismatch(); } - string calldata typeUrl = signer.auth_type.option.type_url; + string memory typeUrl = signer.auth_type.option.type_url; IAuthExtensionVerifier verifier = s.authVerifiers[typeUrl]; if (address(verifier) == address(0)) { revert VerifierNotFound(typeUrl); @@ -150,11 +150,7 @@ contract TxAuthManager is Initializable, TxAuthManagerBase, CrossStore, ITxAuthM function _accountKey(Account.Data memory a) internal pure returns (bytes32) { if (a.auth_type.mode == AuthType.AuthMode.AUTH_MODE_EXTENSION) { - return keccak256(abi.encode( - a.id, - a.auth_type.mode, - a.auth_type.option.type_url - )); + return keccak256(abi.encode(a.id, a.auth_type.mode, a.auth_type.option.type_url)); } return keccak256(abi.encode(a.id, a.auth_type)); } diff --git a/src/core/TxAuthManagerBase.sol b/src/core/TxAuthManagerBase.sol index dbba8f1..cf87e3b 100644 --- a/src/core/TxAuthManagerBase.sol +++ b/src/core/TxAuthManagerBase.sol @@ -8,5 +8,5 @@ abstract contract TxAuthManagerBase { function _isCompletedAuth(bytes32 txID) internal view virtual returns (bool); function _sign(bytes32 txID, Account.Data[] memory signers) internal virtual returns (bool); function _getAuthState(bytes32 txID) internal view virtual returns (TxAuthState.Data memory); - function _verifySignatures(bytes32 txIDHash, Account.Data[] calldata signers) internal virtual; + function _verifySignatures(bytes32 txIDHash, Account.Data[] memory signers) internal virtual; } diff --git a/test/Authenticator.t.sol b/test/Authenticator.t.sol index 081c353..0674269 100644 --- a/test/Authenticator.t.sol +++ b/test/Authenticator.t.sol @@ -118,7 +118,7 @@ contract MockTxAuthManager is TxAuthManagerBase { function _verifySignatures( bytes32, /*txIDHash*/ - AuthAccount.Data[] calldata + AuthAccount.Data[] memory /*signers*/ ) internal diff --git a/test/Coordinator.t.sol b/test/Coordinator.t.sol index 5e3b20c..4100ea1 100644 --- a/test/Coordinator.t.sol +++ b/test/Coordinator.t.sol @@ -68,7 +68,7 @@ contract MockTxAuthManager is TxAuthManagerBase { TxAuthState.Data memory state; return state; } - function _verifySignatures(bytes32, Account.Data[] calldata) internal virtual override {} + function _verifySignatures(bytes32, Account.Data[] memory) internal virtual override {} function _isCompletedAuth(bytes32 txID) internal view virtual override returns (bool) { return completedAuths[txID]; diff --git a/test/Initiator.t.sol b/test/Initiator.t.sol index 463e0cf..bec0b60 100644 --- a/test/Initiator.t.sol +++ b/test/Initiator.t.sol @@ -62,6 +62,9 @@ contract MockTxManager is TxManagerBase { contract MockTxAuthManager is TxAuthManagerBase { mapping(bytes32 => bool) public completed; bytes32 public lastInittxID; + bytes32 public lastSigntxID; + bytes32 public lastSignersHash; + uint256 public lastSignersCount; bytes32 public lastVerifytxID; uint256 public verifyCount; bool private _signReturns = false; @@ -76,7 +79,10 @@ contract MockTxAuthManager is TxAuthManagerBase { return completed[txID]; } - function _sign(bytes32 txID, AuthAccount.Data[] memory) internal virtual override returns (bool) { + function _sign(bytes32 txID, AuthAccount.Data[] memory signers) internal virtual override returns (bool) { + lastSigntxID = txID; + lastSignersHash = keccak256(abi.encode(signers)); + lastSignersCount = signers.length; if (_signReturns) { completed[txID] = true; } @@ -87,7 +93,7 @@ contract MockTxAuthManager is TxAuthManagerBase { revert("MockTxAuthManager._getAuthState not implemented"); } - function _verifySignatures(bytes32 txID, AuthAccount.Data[] calldata) internal virtual override { + function _verifySignatures(bytes32 txID, AuthAccount.Data[] memory) internal virtual override { lastVerifytxID = txID; ++verifyCount; if (_verifyShouldRevert) { @@ -129,6 +135,10 @@ contract InitiatorTest is Test, ICrossEvent { AuthType.Data private extensionAuthType; string private chainIDStr; + function _signersHash(AuthAccount.Data[] memory signers) internal pure returns (bytes32) { + return keccak256(abi.encode(signers)); + } + function setUp() public { harness = new InitiatorHarness(); @@ -224,6 +234,9 @@ contract InitiatorTest is Test, ICrossEvent { assertTrue(harness.completed(txIDHash), "Mock: auth should be completed"); assertEq(harness.createTxCount(), 1, "Mock: _createTx should be called once"); assertEq(harness.lastInittxID(), txIDHash, "Mock: _initAuthState should be called with txID"); + assertEq(harness.lastSigntxID(), txIDHash, "Mock: _sign should be called with txID"); + assertEq(harness.lastSignersCount(), 1, "Mock: _sign should receive one signer"); + assertEq(harness.lastSignersHash(), _signersHash(baseMsg.signers), "Mock: _sign should receive full signer set"); } function test_initiateTx_SucceedsWithExtensionSignerAsPendingWhenAuthNotCompleted() public { @@ -270,21 +283,65 @@ contract InitiatorTest is Test, ICrossEvent { uint256(MsgInitiateTxResponse.InitiateTxStatus.INITIATE_TX_STATUS_VERIFIED), "Status should be VERIFIED" ); + assertEq(harness.lastSigntxID(), txIDHash, "Mock: _sign should be called with txID"); + assertEq(harness.lastSignersCount(), 2, "Mock: _sign should receive two signers"); + assertEq(harness.lastSignersHash(), _signersHash(baseMsg.signers), "Mock: _sign should receive full signer set"); assertEq(harness.verifyCount(), 1, "Mock: _verifySignatures should be called once"); assertEq(harness.lastVerifytxID(), txIDHash, "Mock: _verifySignatures should receive txID"); } - function test_initiateTx_RevertWhen_SignerLengthZero() public { - baseMsg.signers = new AuthAccount.Data[](0); + function test_initiateTx_SucceedsWithMixedLocalAndExtensionSignersAsVerified() public { + baseMsg.signers = new AuthAccount.Data[](2); + baseMsg.signers[0] = senderLocalSigner; + baseMsg.signers[1] = extSignerA; + baseMsg.contract_transactions[0].signers = new AuthAccount.Data[](2); + baseMsg.contract_transactions[0].signers[0] = senderLocalSigner; + baseMsg.contract_transactions[0].signers[1] = extSignerA; + bytes32 txIDHash = sha256(MsgInitiateTx.encode(baseMsg)); - vm.expectRevert(ICrossError.InvalidSignersLength.selector); - harness.initiateTx(baseMsg); + harness.setSignReturns(true); + + vm.expectEmit(true, false, false, true, address(harness)); + emit TxInitiated(abi.encodePacked(txIDHash), address(this), baseMsg); + + MsgInitiateTxResponse.Data memory resp = harness.initiateTx(baseMsg); + + assertEq( + uint256(resp.status), + uint256(MsgInitiateTxResponse.InitiateTxStatus.INITIATE_TX_STATUS_VERIFIED), + "Status should be VERIFIED" + ); + assertEq(harness.verifyCount(), 1, "Mock: _verifySignatures should be called once"); + assertEq(harness.lastVerifytxID(), txIDHash, "Mock: _verifySignatures should receive txID"); } - function test_initiateTx_RevertWhen_SignerLengthMultiple() public { + function test_initiateTx_SucceedsWithMixedExtensionAndLocalSignersAsPendingWhenAuthNotCompleted() public { baseMsg.signers = new AuthAccount.Data[](2); - baseMsg.signers[0] = senderLocalSigner; - baseMsg.signers[1] = signerB; + baseMsg.signers[0] = extSignerA; + baseMsg.signers[1] = senderLocalSigner; + baseMsg.contract_transactions[0].signers = new AuthAccount.Data[](2); + baseMsg.contract_transactions[0].signers[0] = extSignerA; + baseMsg.contract_transactions[0].signers[1] = senderLocalSigner; + bytes32 txIDHash = sha256(MsgInitiateTx.encode(baseMsg)); + + harness.setSignReturns(false); + + vm.expectEmit(true, false, false, true, address(harness)); + emit TxInitiated(abi.encodePacked(txIDHash), address(this), baseMsg); + + MsgInitiateTxResponse.Data memory resp = harness.initiateTx(baseMsg); + + assertEq( + uint256(resp.status), + uint256(MsgInitiateTxResponse.InitiateTxStatus.INITIATE_TX_STATUS_PENDING), + "Status should be PENDING" + ); + assertEq(harness.verifyCount(), 1, "Mock: _verifySignatures should be called once"); + assertEq(harness.lastVerifytxID(), txIDHash, "Mock: _verifySignatures should receive txID"); + } + + function test_initiateTx_RevertWhen_SignerLengthZero() public { + baseMsg.signers = new AuthAccount.Data[](0); vm.expectRevert(ICrossError.InvalidSignersLength.selector); harness.initiateTx(baseMsg); @@ -311,19 +368,32 @@ contract InitiatorTest is Test, ICrossEvent { harness.initiateTx(baseMsg); } - function test_initiateTx_RevertWhen_MixedAuthModesLocalThenExtension() public { + function test_initiateTx_RevertWhen_MultipleLocalSignersProvided() public { baseMsg.signers = new AuthAccount.Data[](2); baseMsg.signers[0] = senderLocalSigner; - baseMsg.signers[1] = extSignerA; + baseMsg.signers[1] = signerB; + + vm.expectRevert(ICrossError.InvalidSignersLength.selector); + harness.initiateTx(baseMsg); + } + + function test_initiateTx_RevertWhen_UnsupportedAuthModeMixedWithLocal() public { + baseMsg.signers = new AuthAccount.Data[](2); + baseMsg.signers[0] = senderLocalSigner; + baseMsg.signers[1] = senderLocalSigner; + baseMsg.signers[1].auth_type = + AuthType.Data({mode: AuthType.AuthMode.AUTH_MODE_CHANNEL, option: GoogleProtobufAny.Data("", "")}); vm.expectRevert(ICrossError.AuthModeMismatch.selector); harness.initiateTx(baseMsg); } - function test_initiateTx_RevertWhen_MixedAuthModesExtensionThenLocal() public { + function test_initiateTx_RevertWhen_MixedSignersIncludeUnsupportedAuthMode() public { baseMsg.signers = new AuthAccount.Data[](2); baseMsg.signers[0] = extSignerA; baseMsg.signers[1] = senderLocalSigner; + baseMsg.signers[1].auth_type = + AuthType.Data({mode: AuthType.AuthMode.AUTH_MODE_CHANNEL, option: GoogleProtobufAny.Data("", "")}); vm.expectRevert(ICrossError.AuthModeMismatch.selector); harness.initiateTx(baseMsg); From d120aa02f3a9174e8cf6abbb6c89059cdb191bce Mon Sep 17 00:00:00 2001 From: Koji Matsumiya Date: Wed, 1 Jul 2026 09:51:06 +0900 Subject: [PATCH 6/6] feat: canonicalize transaction ID computation by excluding signers --- src/core/TxIDUtils.sol | 16 +++++++++++++++- test/Coordinator.t.sol | 7 ++++++- test/DelegatedLogicHandler.t.sol | 7 ++++++- test/Initiator.t.sol | 21 +++++++++++++-------- test/TxIdUtils.t.sol | 16 ++++++++-------- test/TxManager.t.sol | 7 ++++++- 6 files changed, 54 insertions(+), 20 deletions(-) diff --git a/src/core/TxIDUtils.sol b/src/core/TxIDUtils.sol index 3dc5090..8d566f2 100644 --- a/src/core/TxIDUtils.sol +++ b/src/core/TxIDUtils.sol @@ -2,9 +2,23 @@ pragma solidity ^0.8.20; import {MsgInitiateTx} from "../proto/cross/core/initiator/Initiator.sol"; +import {Account} from "../proto/cross/core/auth/Auth.sol"; library TxIDUtils { function computeTxId(MsgInitiateTx.Data calldata msg_) internal pure returns (bytes32) { - return sha256(MsgInitiateTx.encode(msg_)); + return sha256(MsgInitiateTx.encode(_canonicalizeCalldata(msg_))); + } + + function _canonicalizeCalldata(MsgInitiateTx.Data calldata msg_) private pure returns (MsgInitiateTx.Data memory) { + return MsgInitiateTx.Data({ + chain_id: msg_.chain_id, + nonce: msg_.nonce, + commit_protocol: msg_.commit_protocol, + contract_transactions: msg_.contract_transactions, + // Top-level signers may embed extension signatures, so they are excluded from the txID preimage. + signers: new Account.Data[](0), + timeout_height: msg_.timeout_height, + timeout_timestamp: msg_.timeout_timestamp + }); } } diff --git a/test/Coordinator.t.sol b/test/Coordinator.t.sol index 4100ea1..cb6a3f4 100644 --- a/test/Coordinator.t.sol +++ b/test/Coordinator.t.sol @@ -82,6 +82,11 @@ contract CoordinatorTest is Test, ICrossError, ICrossEvent { MsgInitiateTx.Data private dummyMsg; bytes32 private txID; + function _computeTxId(MsgInitiateTx.Data memory msg_) internal pure returns (bytes32) { + msg_.signers = new AuthAccount.Data[](0); + return sha256(MsgInitiateTx.encode(msg_)); + } + function setUp() public { harness = new CoordinatorHarness(); @@ -95,7 +100,7 @@ contract CoordinatorTest is Test, ICrossError, ICrossEvent { contract_transactions: new ContractTransaction.Data[](0) }); - txID = sha256(MsgInitiateTx.encode(dummyMsg)); + txID = _computeTxId(dummyMsg); } function test_executeTx_Succeeds() public { diff --git a/test/DelegatedLogicHandler.t.sol b/test/DelegatedLogicHandler.t.sol index 07f0fcc..7a3dce9 100644 --- a/test/DelegatedLogicHandler.t.sol +++ b/test/DelegatedLogicHandler.t.sol @@ -276,6 +276,11 @@ contract DelegatedLogicHandlerTest is Test, ICrossError { MsgInitiateTx.Data private txMsg; Packet private dummyPacket; + function _computeTxId(MsgInitiateTx.Data memory msg_) internal pure returns (bytes32) { + msg_.signers = new AuthAccount.Data[](0); + return sha256(MsgInitiateTx.encode(msg_)); + } + function setUp() public { mockAuth = new MockTxAuthManager(); mockTx = new MockTxManager(); @@ -357,7 +362,7 @@ contract DelegatedLogicHandlerTest is Test, ICrossError { } function test_runTxIfCompleted_DelegatesToTxManager() public { - bytes32 expectedTxID = sha256(MsgInitiateTx.encode(txMsg)); + bytes32 expectedTxID = _computeTxId(txMsg); harness.exposed_runTxIfCompleted(expectedTxID, txMsg); diff --git a/test/Initiator.t.sol b/test/Initiator.t.sol index bec0b60..142aa83 100644 --- a/test/Initiator.t.sol +++ b/test/Initiator.t.sol @@ -139,6 +139,11 @@ contract InitiatorTest is Test, ICrossEvent { return keccak256(abi.encode(signers)); } + function _computeTxId(MsgInitiateTx.Data memory msg_) internal pure returns (bytes32) { + msg_.signers = new AuthAccount.Data[](0); + return sha256(MsgInitiateTx.encode(msg_)); + } + function setUp() public { harness = new InitiatorHarness(); @@ -192,7 +197,7 @@ contract InitiatorTest is Test, ICrossEvent { function test_initiateTx_SucceedsAsPendingWhenSignersNotMet() public { baseMsg.signers = new AuthAccount.Data[](1); baseMsg.signers[0] = senderLocalSigner; - bytes32 txIDHash = sha256(MsgInitiateTx.encode(baseMsg)); + bytes32 txIDHash = _computeTxId(baseMsg); harness.setSignReturns(false); @@ -216,7 +221,7 @@ contract InitiatorTest is Test, ICrossEvent { function test_initiateTx_SucceedsAsVerifiedWhenSignersMet() public { baseMsg.signers = new AuthAccount.Data[](1); baseMsg.signers[0] = senderLocalSigner; - bytes32 txIDHash = sha256(MsgInitiateTx.encode(baseMsg)); + bytes32 txIDHash = _computeTxId(baseMsg); harness.setSignReturns(true); @@ -244,7 +249,7 @@ contract InitiatorTest is Test, ICrossEvent { baseMsg.signers[0] = extSignerA; baseMsg.contract_transactions[0].signers = new AuthAccount.Data[](1); baseMsg.contract_transactions[0].signers[0] = extSignerA; - bytes32 txIDHash = sha256(MsgInitiateTx.encode(baseMsg)); + bytes32 txIDHash = _computeTxId(baseMsg); harness.setSignReturns(false); @@ -269,7 +274,7 @@ contract InitiatorTest is Test, ICrossEvent { baseMsg.contract_transactions[0].signers = new AuthAccount.Data[](2); baseMsg.contract_transactions[0].signers[0] = extSignerA; baseMsg.contract_transactions[0].signers[1] = extSignerB; - bytes32 txIDHash = sha256(MsgInitiateTx.encode(baseMsg)); + bytes32 txIDHash = _computeTxId(baseMsg); harness.setSignReturns(true); @@ -297,7 +302,7 @@ contract InitiatorTest is Test, ICrossEvent { baseMsg.contract_transactions[0].signers = new AuthAccount.Data[](2); baseMsg.contract_transactions[0].signers[0] = senderLocalSigner; baseMsg.contract_transactions[0].signers[1] = extSignerA; - bytes32 txIDHash = sha256(MsgInitiateTx.encode(baseMsg)); + bytes32 txIDHash = _computeTxId(baseMsg); harness.setSignReturns(true); @@ -322,7 +327,7 @@ contract InitiatorTest is Test, ICrossEvent { baseMsg.contract_transactions[0].signers = new AuthAccount.Data[](2); baseMsg.contract_transactions[0].signers[0] = extSignerA; baseMsg.contract_transactions[0].signers[1] = senderLocalSigner; - bytes32 txIDHash = sha256(MsgInitiateTx.encode(baseMsg)); + bytes32 txIDHash = _computeTxId(baseMsg); harness.setSignReturns(false); @@ -402,7 +407,7 @@ contract InitiatorTest is Test, ICrossEvent { function test_initiateTx_RevertWhen_ExtensionSignatureVerificationFails() public { baseMsg.signers = new AuthAccount.Data[](1); baseMsg.signers[0] = extSignerA; - bytes32 txIDHash = sha256(MsgInitiateTx.encode(baseMsg)); + bytes32 txIDHash = _computeTxId(baseMsg); harness.setVerifyShouldRevert(true); @@ -443,7 +448,7 @@ contract InitiatorTest is Test, ICrossEvent { } function test_initiateTx_RevertWhen_txIDAlreadyExists() public { - bytes32 txIDHash = sha256(MsgInitiateTx.encode(baseMsg)); + bytes32 txIDHash = _computeTxId(baseMsg); harness.setTxExists(txIDHash, true); diff --git a/test/TxIdUtils.t.sol b/test/TxIdUtils.t.sol index 9026ab4..796de9b 100644 --- a/test/TxIdUtils.t.sol +++ b/test/TxIdUtils.t.sol @@ -94,12 +94,12 @@ contract TxIDUtilsTest is Test { assertNotEq(harness.exposed_computeTxId(m1), harness.exposed_computeTxId(m2)); } - function test_computeTxId_ReturnsDifferentIdForDifferentRootSigners() public view { + function test_computeTxId_ReturnsSameIdForDifferentRootSigners() public view { MsgInitiateTx.Data memory m1 = _createBaseMsg(); MsgInitiateTx.Data memory m2 = _createBaseMsg(); m2.signers = new AuthAccount.Data[](1); m2.signers[0] = _createAccount("signer1"); - assertNotEq(harness.exposed_computeTxId(m1), harness.exposed_computeTxId(m2)); + assertEq(harness.exposed_computeTxId(m1), harness.exposed_computeTxId(m2)); } function test_computeTxId_ReturnsDifferentIdForDifferentLocalSigners() public view { @@ -150,7 +150,7 @@ contract TxIDUtilsTest is Test { assertNotEq(harness.exposed_computeTxId(m1), harness.exposed_computeTxId(m2)); } - function test_computeTxId_ReturnsDifferentIdForDifferentSignerAuthMode() public view { + function test_computeTxId_ReturnsSameIdForDifferentSignerAuthMode() public view { MsgInitiateTx.Data memory m1 = _createBaseMsg(); m1.signers = new AuthAccount.Data[](1); m1.signers[0] = _createAccount("signer1"); @@ -160,10 +160,10 @@ contract TxIDUtilsTest is Test { m2.signers[0] = _createAccount("signer1"); m2.signers[0].auth_type.mode = AuthType.AuthMode.AUTH_MODE_EXTENSION; - assertNotEq(harness.exposed_computeTxId(m1), harness.exposed_computeTxId(m2)); + assertEq(harness.exposed_computeTxId(m1), harness.exposed_computeTxId(m2)); } - function test_computeTxId_ReturnsDifferentIdForDifferentSignerAuthOptionTypeUrl() public view { + function test_computeTxId_ReturnsSameIdForDifferentSignerAuthOptionTypeUrl() public view { MsgInitiateTx.Data memory m1 = _createBaseMsg(); m1.signers = new AuthAccount.Data[](1); m1.signers[0] = _createAccount("signer1"); @@ -173,10 +173,10 @@ contract TxIDUtilsTest is Test { m2.signers[0] = _createAccount("signer1"); m2.signers[0].auth_type.option.type_url = "/new.verifier.v1"; - assertNotEq(harness.exposed_computeTxId(m1), harness.exposed_computeTxId(m2)); + assertEq(harness.exposed_computeTxId(m1), harness.exposed_computeTxId(m2)); } - function test_computeTxId_ReturnsDifferentIdForDifferentSignerAuthOptionValue() public view { + function test_computeTxId_ReturnsSameIdForDifferentSignerAuthOptionValue() public view { MsgInitiateTx.Data memory m1 = _createBaseMsg(); m1.signers = new AuthAccount.Data[](1); m1.signers[0] = _createAccount("signer1"); @@ -186,7 +186,7 @@ contract TxIDUtilsTest is Test { m2.signers[0] = _createAccount("signer1"); m2.signers[0].auth_type.option.value = hex"123456"; - assertNotEq(harness.exposed_computeTxId(m1), harness.exposed_computeTxId(m2)); + assertEq(harness.exposed_computeTxId(m1), harness.exposed_computeTxId(m2)); } // --- Helpers --- diff --git a/test/TxManager.t.sol b/test/TxManager.t.sol index 42581d2..da0cc4d 100644 --- a/test/TxManager.t.sol +++ b/test/TxManager.t.sol @@ -96,6 +96,11 @@ contract TxManagerTest is Test, ICrossError { DummyIBCHandler private dummyHandler; DummyContractModule private dummyModule; + function _computeTxId(MsgInitiateTx.Data memory msg_) internal pure returns (bytes32) { + msg_.signers = new AuthAccount.Data[](0); + return sha256(MsgInitiateTx.encode(msg_)); + } + function setUp() public { harness = new TxManagerHarness(); dummyHandler = new DummyIBCHandler(); @@ -114,7 +119,7 @@ contract TxManagerTest is Test, ICrossError { contract_transactions: txs }); - txID = sha256(MsgInitiateTx.encode(txMsg)); + txID = _computeTxId(txMsg); } // --- initialize ---