synapsecns/sanguine

View on GitHub
packages/contracts-core/test/suite/inbox/Inbox.t.sol

Summary

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

import {
    AgentNotGuard,
    CallerNotDestination,
    GuardInDispute,
    NotaryInDispute,
    MustBeSynapseDomain,
    NotaryInDispute
} from "../../../contracts/libs/Errors.sol";
import {InterfaceSummit} from "../../../contracts/interfaces/InterfaceSummit.sol";

import {StatementInboxTest} from "./StatementInbox.t.sol";

import {BaseMock} from "../../mocks/base/BaseMock.t.sol";
import {Random} from "../../utils/libs/Random.t.sol";
import {
    RawExecReceipt,
    RawTips,
    RawTipsProof,
    RawReceiptTips,
    RawState,
    RawStateIndex
} from "../../utils/libs/SynapseStructs.t.sol";

import {Inbox, SynapseTest} from "../../utils/SynapseTest.t.sol";

// solhint-disable func-name-mixedcase
// solhint-disable no-empty-blocks
// solhint-disable ordering
contract InboxTest is StatementInboxTest {
    // Deploy mocks for everything except BondingManager and Inbox
    constructor() SynapseTest(0) {}

    // ═══════════════════════════════════════════════ TESTS: SETUP ════════════════════════════════════════════════════

    function test_cleanSetup(Random memory random) public override {
        uint32 domain = DOMAIN_SYNAPSE;
        vm.chainId(domain);
        address caller = random.nextAddress();
        address agentManager = random.nextAddress();
        address origin_ = random.nextAddress();
        address destination_ = random.nextAddress();
        address summit_ = random.nextAddress();
        Inbox inbox_ = new Inbox(domain);
        vm.prank(caller);
        inbox_.initialize(agentManager, origin_, destination_, summit_);
        assertEq(inbox_.owner(), caller);
        assertEq(inbox_.localDomain(), domain);
        assertEq(inbox_.origin(), origin_);
        assertEq(inbox_.destination(), destination_);
        assertEq(inbox_.summit(), summit_);
        assertEq(inbox_.agentManager(), agentManager);
    }

    function test_setup() public override {
        super.test_setup();
        assertEq(inbox.summit(), summit);
        assertEq(inbox.version(), LATEST_VERSION);
    }

    function test_constructor_revert_notOnSynapseChain(uint32 domain) public {
        vm.assume(domain != DOMAIN_SYNAPSE);
        vm.chainId(domain);
        vm.expectRevert(MustBeSynapseDomain.selector);
        new Inbox(DOMAIN_SYNAPSE);
    }

    function test_constructor_revert_chainIdOverflow() public {
        vm.chainId(2 ** 32);
        vm.expectRevert("SafeCast: value doesn't fit in 32 bits");
        new Inbox({synapseDomain_: DOMAIN_SYNAPSE});
    }

    function initializeLocalContract() public override {
        Inbox(localContract()).initialize(address(0), address(0), address(0), address(0));
    }

    // ══════════════════════════════════════════ TEST: SUBMIT STATEMENTS ══════════════════════════════════════════════

    function test_submitSnapshot_guard(uint256 agentId, RawState memory rs, RawStateIndex memory rsi)
        public
        boundIndex(rsi)
    {
        address guard = getGuard(agentId);
        (bytes memory snapPayload, bytes memory snapSig) = createSignedSnapshot(guard, rs, rsi);
        vm.expectCall(
            summit,
            abi.encodeWithSelector(
                InterfaceSummit.acceptGuardSnapshot.selector, agentIndex[guard], nextSignatureIndex(), snapPayload
            )
        );
        inbox.submitSnapshot(snapPayload, snapSig);
    }

    function test_submitSnapshot_notary(uint256 domainId, uint256 agentId, RawState memory rs, RawStateIndex memory rsi)
        public
        boundIndex(rsi)
    {
        address notary = getNotary(domainId, agentId);
        (bytes memory snapPayload, bytes memory snapSig) = createSignedSnapshot(notary, rs, rsi);
        vm.expectCall(
            summit,
            abi.encodeWithSelector(
                InterfaceSummit.acceptNotarySnapshot.selector,
                agentIndex[notary],
                nextSignatureIndex(),
                getAgentRoot(),
                snapPayload
            )
        );
        inbox.submitSnapshot(snapPayload, snapSig);
    }

    function test_submitReceipt(
        uint256 domainId,
        uint256 agentId,
        uint256 attNotaryId,
        RawReceiptTips memory receipt,
        uint256 attNonce
    ) public {
        address rcptNotary = getNotary(domainId, agentId);
        receipt.re.destination = DOMAIN_REMOTE;
        receipt.re.attNotary = domains[DOMAIN_REMOTE].agents[attNotaryId % DOMAIN_AGENTS];
        receipt.re.messageHash = receipt.tips.getMessageHash(receipt.rtp);
        (bytes memory receiptPayload, bytes memory receiptSig) = signReceipt(rcptNotary, receipt.re);
        // Set value for getAttestationNonce call
        attNonce = bound(attNonce, 1, type(uint32).max);
        BaseMock(localDestination()).setMockReturnValue(attNonce);
        vm.expectCall(
            summit,
            abi.encodeWithSelector(
                InterfaceSummit.acceptReceipt.selector,
                agentIndex[rcptNotary],
                agentIndex[receipt.re.attNotary],
                nextSignatureIndex(),
                attNonce,
                receipt.tips.encodeTips(),
                receipt.re.formatReceipt()
            )
        );
        inbox.submitReceipt(
            receiptPayload, receiptSig, receipt.tips.encodeTips(), receipt.rtp.headerHash, receipt.rtp.bodyHash
        );
    }

    function test_passReceipt_revert_notDestination(address caller) public {
        vm.assume(caller != localDestination());
        vm.expectRevert(CallerNotDestination.selector);
        vm.prank(caller);
        inbox.passReceipt(0, 0, 0, "");
    }

    // ════════════════════════════════════════════ TEST: OPEN DISPUTES ════════════════════════════════════════════════

    function test_submitReceiptReport(Random memory random) public {
        address prover = makeAddr("Prover");
        RawExecReceipt memory re = random.nextReceipt(random.nextUint32());
        // Create Notary signature for the attestation
        address notary = domains[DOMAIN_LOCAL].agent;
        (, bytes memory rcptSignature) = signReceipt(notary, re);
        // Create Guard signature for the report
        address guard = domains[0].agent;
        (bytes memory rcptPayload, bytes memory rrSignature) = signReceiptReport(guard, re);
        expectDisputeOpened(0, guard, notary);
        vm.prank(prover);
        inbox.submitReceiptReport(rcptPayload, rcptSignature, rrSignature);
        assertEq(inbox.getReportsAmount(), 1, "!reportsAmount");
        (bytes memory reportPayload, bytes memory reportSignature) = inbox.getGuardReport(0);
        assertEq(reportPayload, rcptPayload, "!reportPayload");
        assertEq(reportSignature, rrSignature, "!reportSig");
    }

    function test_submitReceiptReport_revert_signedByNotary(Random memory random) public {
        RawExecReceipt memory re = random.nextReceipt(random.nextUint32());
        // Create Notary signature for the attestation
        address notary = domains[DOMAIN_LOCAL].agent;
        (, bytes memory rcptSignature) = signReceipt(notary, re);
        // Force a random Notary to sign the report
        address reportSigner = getNotary(random.nextUint256(), random.nextUint256());
        (bytes memory rcptPayload, bytes memory rrSignature) = signReceiptReport(reportSigner, re);
        vm.expectRevert(AgentNotGuard.selector);
        inbox.submitReceiptReport(rcptPayload, rcptSignature, rrSignature);
    }

    function test_submitReceiptReport_revert_guardInDispute(Random memory random) public {
        RawExecReceipt memory re = random.nextReceipt(random.nextUint32());
        // Create Notary signature for the attestation
        address notary = domains[DOMAIN_LOCAL].agent;
        (, bytes memory rcptSignature) = signReceipt(notary, re);
        // Create Guard signature for the report
        address guard = domains[0].agent;
        (bytes memory rcptPayload, bytes memory rrSignature) = signReceiptReport(guard, re);
        // Put the Guard in Dispute with another Notary
        openDispute({guard: guard, notary: domains[DOMAIN_LOCAL].agents[1]});
        vm.expectRevert(GuardInDispute.selector);
        inbox.submitReceiptReport(rcptPayload, rcptSignature, rrSignature);
    }

    function test_submitReceiptReport_revert_notaryInDispute(Random memory random) public {
        RawExecReceipt memory re = random.nextReceipt(random.nextUint32());
        // Create Notary signature for the attestation
        address notary = domains[DOMAIN_LOCAL].agent;
        (, bytes memory rcptSignature) = signReceipt(notary, re);
        // Create Guard signature for the report
        address guard = domains[0].agent;
        (bytes memory rcptPayload, bytes memory rrSignature) = signReceiptReport(guard, re);
        // Put the Notary in Dispute with another Guard
        openDispute({guard: domains[0].agents[1], notary: notary});
        vm.expectRevert(NotaryInDispute.selector);
        inbox.submitReceiptReport(rcptPayload, rcptSignature, rrSignature);
    }

    // ══════════════════════════════════════════════════ HELPERS ══════════════════════════════════════════════════════

    /// @notice Returns local domain for the tested contract
    function localDomain() public pure override returns (uint32) {
        return DOMAIN_SYNAPSE;
    }
}