diff --git a/contracts/src/Functions.sol b/contracts/src/Functions.sol index f372befcaf..7a1d030315 100644 --- a/contracts/src/Functions.sol +++ b/contracts/src/Functions.sol @@ -15,6 +15,7 @@ import {TokenInfo} from "./types/Common.sol"; import {ChannelID, Channel} from "./v1/Types.sol"; import {IGatewayBase} from "./interfaces/IGatewayBase.sol"; import {IGatewayV1} from "./v1/IGateway.sol"; +import {Constants} from "./Constants.sol"; // Common functionality that is shared between V1 and V2 library Functions { @@ -22,10 +23,8 @@ library Functions { using SafeNativeTransfer for address payable; using SafeTokenTransferFrom for IERC20; - error AgentDoesNotExist(); error InvalidToken(); error InvalidAmount(); - error ChannelDoesNotExist(); /// Looks up an agent contract address, failing if no such mapping exists function ensureAgent(bytes32 agentID) internal view returns (address agent) { @@ -35,6 +34,14 @@ library Functions { } } + /// Looks up an agent contract address, failing if no such mapping exists or if the agent is the primary asset hub agent + function ensureNonPrivilegedAgent(bytes32 agentID) internal view returns (address agent) { + if(agentID == Constants.ASSET_HUB_AGENT_ID) { + revert IGatewayBase.UnauthorizedPrivilegedAgent(); + } + agent = ensureAgent(agentID); + } + /// @dev Ensure that the specified parachain has a channel allocated function ensureChannel(ChannelID channelID) internal view returns (Channel storage ch) { ch = CoreStorage.layout().channels[channelID]; diff --git a/contracts/src/interfaces/IGatewayBase.sol b/contracts/src/interfaces/IGatewayBase.sol index f14cb5b932..5a55112f3c 100644 --- a/contracts/src/interfaces/IGatewayBase.sol +++ b/contracts/src/interfaces/IGatewayBase.sol @@ -13,6 +13,7 @@ interface IGatewayBase { error Unsupported(); error InvalidDestinationFee(); error AgentDoesNotExist(); + error UnauthorizedPrivilegedAgent(); error TokenAlreadyRegistered(); error TokenMintFailed(); error TokenTransferFailed(); diff --git a/contracts/src/v2/Handlers.sol b/contracts/src/v2/Handlers.sol index 74b70a56d4..f05e4022d1 100644 --- a/contracts/src/v2/Handlers.sol +++ b/contracts/src/v2/Handlers.sol @@ -65,7 +65,7 @@ library HandlersV2 { function callContract(bytes32 origin, address executor, bytes calldata data) external { CallContractParams memory params = abi.decode(data, (CallContractParams)); - address agent = Functions.ensureAgent(origin); + address agent = Functions.ensureNonPrivilegedAgent(origin); bytes memory call = abi.encodeCall(AgentExecutor.callContract, (params.target, params.data, params.value)); Functions.invokeOnAgent(agent, executor, call); diff --git a/contracts/test/GatewayV2.t.sol b/contracts/test/GatewayV2.t.sol index e037c1a197..533c354102 100644 --- a/contracts/test/GatewayV2.t.sol +++ b/contracts/test/GatewayV2.t.sol @@ -579,6 +579,32 @@ contract GatewayV2Test is Test { vm.expectEmit(true, false, false, true); emit IGatewayV2.InboundMessageDispatched(1, topic, true, relayerRewardAddress); + address bridgeHubAgent = IGatewayV2(address(gateway)).agentOf(Constants.BRIDGE_HUB_AGENT_ID); + vm.deal(bridgeHubAgent, 1 ether); + hoax(relayer, 1 ether); + IGatewayV2(address(gateway)) + .v2_submit( + InboundMessageV2({ + origin: Constants.BRIDGE_HUB_AGENT_ID, + nonce: 1, + topic: topic, + commands: makeCallContractCommand(0.1 ether) + }), + proof, + makeMockProof(), + relayerRewardAddress + ); + } + + function testAgentCallContractFailsForAssetHub() public { + bytes32 topic = keccak256("topic"); + + // The command fails, then the message is dispatched with success: false + vm.expectEmit(true, false, false, false); + emit IGatewayV2.CommandFailed(1, 0); + vm.expectEmit(true, false, false, true); + emit IGatewayV2.InboundMessageDispatched(1, topic, false, relayerRewardAddress); + vm.deal(assetHubAgent, 1 ether); hoax(relayer, 1 ether); IGatewayV2(address(gateway))