synapsecns/sanguine

View on GitHub
packages/contracts-core/test/suite/client/BaseClient.t.sol

Summary

Maintainability
Test Coverage
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

import {
    BaseClientOptimisticPeriod,
    CallerNotDestination,
    IncorrectNonce,
    IncorrectSender,
    IncorrectRecipient,
    ZeroProofMaturity
} from "../../../contracts/libs/Errors.sol";
import {MessageRecipient} from "../../../contracts/client/MessageRecipient.sol";

import {BaseClientHarness} from "../../harnesses/client/BaseClientHarness.t.sol";
import {SynapseTest} from "../../utils/SynapseTest.t.sol";
import {InterfaceOrigin} from "../../mocks/OriginMock.t.sol";

import {RawBaseMessage, RawHeader, RawRequest} from "../../utils/libs/SynapseStructs.t.sol";

// solhint-disable func-name-mixedcase
// solhint-disable no-empty-blocks
contract BaseClientTest is SynapseTest {
    BaseClientHarness public client;

    // Deploy mocks instead of the production contracts
    constructor() SynapseTest(0) {}

    function setUp() public override {
        super.setUp();
        client = new BaseClientHarness(origin, destination);
    }

    function test_sendBaseMessage(
        address user,
        uint32 destination_,
        uint256 tipsValue,
        RawRequest memory rr,
        uint32 nonce,
        bytes32 msgHash,
        bytes memory content
    ) public {
        vm.assume(destination_ != 0 && destination_ != DOMAIN_LOCAL);
        vm.label(user, "User");
        // Set some sensible limit for fuzzed tips values
        tipsValue = tipsValue % (2 ** 32);
        uint192 encodedRequest = rr.encodeRequest();
        vm.deal(user, tipsValue);
        // Get expected values for sending a message
        bytes32 recipient = client.trustedSender(destination_);
        uint32 optimisticPeriod = client.optimisticPeriod();
        bytes memory expectedCall = abi.encodeWithSelector(
            InterfaceOrigin.sendBaseMessage.selector, destination_, recipient, optimisticPeriod, encodedRequest, content
        );
        MessageRecipient.MessageRequest memory request =
            MessageRecipient.MessageRequest({gasDrop: rr.gasDrop, gasLimit: rr.gasLimit, version: rr.version});
        // Mock returned values for sendBaseMessage call
        vm.mockCall(
            origin, abi.encodeWithSelector(InterfaceOrigin.sendBaseMessage.selector), abi.encode(nonce, msgHash)
        );
        vm.expectCall(origin, tipsValue, expectedCall);
        vm.prank(user);
        (uint32 nonce_, bytes32 msgHash_) =
            client.sendBaseMessage{value: tipsValue}(destination_, tipsValue, request, content);
        assertEq(nonce_, nonce);
        assertEq(msgHash_, msgHash);
    }

    function test_sendBaseMessage_revert_recipientNotSet(
        address user,
        uint256 tipsValue,
        MessageRecipient.MessageRequest memory request
    ) public {
        // There is no trustedSender for this domain => will revert in BaseClient
        uint32 destination_ = 0;
        vm.label(user, "User");
        // Set some sensible limit for fuzzed tips values
        tipsValue = tipsValue % (2 ** 32);
        vm.deal(user, tipsValue);
        vm.expectRevert(IncorrectRecipient.selector);
        vm.prank(user);
        client.sendBaseMessage{value: tipsValue}(destination_, tipsValue, request, "");
    }

    function test_sendBaseMessage_tipsValueNotMsgValue() public {
        address user = makeAddr("User");
        uint32 destination_ = 1;
        uint256 tipsValue = 1337;
        uint256 msgValue = 6969;
        // Use empty request for this test
        MessageRecipient.MessageRequest memory request;
        vm.deal(user, msgValue);
        bytes memory expectedCall = abi.encodeWithSelector(InterfaceOrigin.sendBaseMessage.selector);
        // Should call origin with provided tipsValue as msg.value
        vm.expectCall(origin, tipsValue, expectedCall);
        vm.prank(user);
        client.sendBaseMessage{value: msgValue}(destination_, tipsValue, request, "");
    }

    function test_getMinimumTipsValue(
        uint32 destination_,
        RawRequest memory rr,
        uint256 contentLength,
        uint256 expectedResult
    ) public {
        uint192 encodedRequest = rr.encodeRequest();
        MessageRecipient.MessageRequest memory request =
            MessageRecipient.MessageRequest({gasDrop: rr.gasDrop, gasLimit: rr.gasLimit, version: rr.version});
        // (destination, paddedRequest, contentLength)
        bytes memory expectedCall = abi.encodeWithSelector(
            InterfaceOrigin.getMinimumTipsValue.selector, destination_, encodedRequest, contentLength
        );
        vm.mockCall(origin, expectedCall, abi.encode(expectedResult));
        vm.expectCall(origin, expectedCall);
        uint256 result = client.getMinimumTipsValue(destination_, request, contentLength);
        assertEq(result, expectedResult);
    }

    function test_receiveBaseMessage(
        RawHeader memory rh,
        uint256 rootSubmittedAt,
        uint256 secondsPassed,
        uint32 version,
        bytes memory content
    ) public {
        vm.assume(rh.origin != 0 && rh.origin != DOMAIN_LOCAL && rh.nonce != 0);
        // Get expected values for receiving a message
        uint32 optimisticPeriod = client.optimisticPeriod();
        bytes32 sender = client.trustedSender(rh.origin);
        // Set some sensible restrictions for timestamps
        rootSubmittedAt = bound(rootSubmittedAt, 1, 1e10);
        secondsPassed = bound(secondsPassed, optimisticPeriod, 2 * optimisticPeriod);
        vm.warp(rootSubmittedAt + secondsPassed);
        vm.expectEmit();
        // msg.value should be zero
        emit BaseMessageReceived(0, rh.origin, rh.nonce, version, content);
        vm.prank(destination);
        client.receiveBaseMessage(rh.origin, rh.nonce, sender, secondsPassed, version, content);
    }

    function test_receiveBaseMessage_revert_notDestination(RawHeader memory rh, uint256 rootSubmittedAt, address caller)
        public
    {
        vm.assume(rh.origin != 0 && rh.origin != DOMAIN_LOCAL && rh.nonce != 0);
        vm.assume(caller != destination);
        // Get expected values for receiving a message
        uint32 optimisticPeriod = client.optimisticPeriod();
        bytes32 sender = client.trustedSender(rh.origin);
        // Set some sensible restrictions for timestamps
        rootSubmittedAt = bound(rootSubmittedAt, 1, 1e10);
        vm.warp(rootSubmittedAt + optimisticPeriod);
        vm.expectRevert(CallerNotDestination.selector);
        vm.prank(caller);
        client.receiveBaseMessage(rh.origin, rh.nonce, sender, optimisticPeriod, 0, "");
    }

    function test_receiveBaseMessage_revert_notTrustedSender(
        RawHeader memory rh,
        uint256 rootSubmittedAt,
        bytes32 sender
    ) public {
        vm.assume(rh.origin != 0 && rh.origin != DOMAIN_LOCAL && rh.nonce != 0);
        vm.assume(sender != client.trustedSender(rh.origin));
        // Get expected values for receiving a message
        uint32 optimisticPeriod = client.optimisticPeriod();
        // Set some sensible restrictions for timestamps
        rootSubmittedAt = bound(rootSubmittedAt, 1, 1e10);
        vm.warp(rootSubmittedAt + optimisticPeriod);
        vm.expectRevert(IncorrectSender.selector);
        vm.prank(destination);
        client.receiveBaseMessage(rh.origin, rh.nonce, sender, optimisticPeriod, 0, "");
    }

    function test_receiveBaseMessage_revert_zeroNonce(RawHeader memory rh, uint256 rootSubmittedAt) public {
        vm.assume(rh.origin != 0 && rh.origin != DOMAIN_LOCAL);
        rh.nonce = 0;
        // Get expected values for receiving a message
        uint32 optimisticPeriod = client.optimisticPeriod();
        bytes32 sender = client.trustedSender(rh.origin);
        // Set some sensible restrictions for timestamps
        rootSubmittedAt = bound(rootSubmittedAt, 1, 1e10);
        vm.warp(rootSubmittedAt + optimisticPeriod);
        vm.expectRevert(IncorrectNonce.selector);
        vm.prank(destination);
        client.receiveBaseMessage(rh.origin, rh.nonce, sender, optimisticPeriod, 0, "");
    }

    function test_receiveBaseMessage_revert_optimisticPeriodNotOver(
        RawHeader memory rh,
        uint256 rootSubmittedAt,
        uint256 secondsPassed
    ) public {
        vm.assume(rh.origin != 0 && rh.origin != DOMAIN_LOCAL && rh.nonce != 0);
        // Get expected values for receiving a message
        uint32 optimisticPeriod = client.optimisticPeriod();
        bytes32 sender = client.trustedSender(rh.origin);
        // Set some sensible restrictions for timestamps
        rootSubmittedAt = bound(rootSubmittedAt, 1, 1e10);
        secondsPassed = bound(secondsPassed, 1, optimisticPeriod - 1);
        vm.warp(rootSubmittedAt + secondsPassed);
        vm.expectRevert(BaseClientOptimisticPeriod.selector);
        vm.prank(destination);
        client.receiveBaseMessage(rh.origin, rh.nonce, sender, secondsPassed, 0, "");
    }

    function test_receiveBaseMessage_revert_optimisticPeriodMinus1Second() public {
        uint32 timePassed = client.optimisticPeriod() - 1;
        bytes32 sender = client.trustedSender(DOMAIN_REMOTE);
        skip(timePassed);
        vm.expectRevert(BaseClientOptimisticPeriod.selector);
        vm.prank(destination);
        client.receiveBaseMessage({
            origin_: DOMAIN_REMOTE,
            nonce: 1,
            sender: sender,
            proofMaturity: timePassed,
            version: 0,
            content: ""
        });
    }

    function test_receiveBaseMessage_revert_zeroProofMaturity(RawHeader memory rh, uint256 rootSubmittedAt) public {
        vm.assume(rh.origin != 0 && rh.origin != DOMAIN_LOCAL && rh.nonce != 0);
        uint32 optimisticPeriod = 0;
        // Get expected values for receiving a message
        bytes32 sender = client.trustedSender(rh.origin);
        // Set some sensible restrictions for timestamps
        rootSubmittedAt = bound(rootSubmittedAt, 1, 1e10);
        vm.warp(rootSubmittedAt);
        vm.expectRevert(ZeroProofMaturity.selector);
        vm.prank(destination);
        client.receiveBaseMessage(rh.origin, rh.nonce, sender, optimisticPeriod, 0, "");
    }
}