synapsecns/sanguine

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

Summary

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

import {StatementInbox} from "../../../contracts/inbox/StatementInbox.sol";
import {AgentNotGuard, GuardInDispute, NotaryInDispute} from "../../../contracts/libs/Errors.sol";
import {AgentFlag} from "../../../contracts/libs/Structures.sol";
import {IExecutionHub} from "../../../contracts/interfaces/IExecutionHub.sol";

import {MessagingBaseTest} from "../base/MessagingBase.t.sol";

import {fakeSnapshot} from "../../utils/libs/FakeIt.t.sol";
import {Random} from "../../utils/libs/Random.t.sol";
import {
    RawAttestation, RawExecReceipt, RawSnapshot, RawState, RawStateIndex
} from "../../utils/libs/SynapseStructs.t.sol";

// solhint-disable func-name-mixedcase
// solhint-disable ordering
abstract contract StatementInboxTest is MessagingBaseTest {
    uint256 private _signatureIndex;

    function test_setup() public virtual {
        assertEq(testedInbox().owner(), address(this));
        assertEq(testedInbox().localDomain(), localDomain());
        assertEq(testedInbox().origin(), localOrigin());
        assertEq(testedInbox().destination(), localDestination());
        assertEq(testedInbox().agentManager(), localAgentManager());
    }

    // ════════════════════════════════════════════ TESTS: OPEN DISPUTE ════════════════════════════════════════════════

    function test_submitStateReportWithSnapshot(RawState memory rs, RawStateIndex memory rsi) public boundIndex(rsi) {
        address prover = makeAddr("Prover");
        // Create Notary signature for the snapshot
        address notary = domains[DOMAIN_LOCAL].agent;
        (bytes memory snapPayload, bytes memory snapSig) = createSignedSnapshot(notary, rs, rsi);
        // Create Guard signature for the report
        address guard = domains[0].agent;
        (bytes memory statePayload, bytes memory srSig) = signStateReport(guard, rs);
        expectDisputeOpened(0, guard, notary);
        vm.prank(prover);
        testedInbox().submitStateReportWithSnapshot(rsi.stateIndex, srSig, snapPayload, snapSig);
        assertEq(testedInbox().getReportsAmount(), 1, "!reportsAmount");
        (bytes memory reportPayload, bytes memory reportSignature) = testedInbox().getGuardReport(0);
        assertEq(reportPayload, statePayload, "!reportPayload");
        assertEq(reportSignature, srSig, "!reportSignature");
    }

    function test_submitStateReportWithSnapshot_revert_signedByNotary(Random memory random) public {
        RawState memory rs = random.nextState();
        RawStateIndex memory rsi = random.nextStateIndex();
        // Create Notary signature for the snapshot
        address notary = domains[DOMAIN_LOCAL].agent;
        (bytes memory snapPayload, bytes memory snapSig) = createSignedSnapshot(notary, rs, rsi);
        // Force a random Notary to sign the report
        address reportSigner = getNotary(random.nextUint256(), random.nextUint256());
        (, bytes memory srSig) = signStateReport(reportSigner, rs);
        vm.expectRevert(AgentNotGuard.selector);
        testedInbox().submitStateReportWithSnapshot(rsi.stateIndex, srSig, snapPayload, snapSig);
    }

    function test_submitStateReportWithAttestation(
        RawState memory rs,
        RawAttestation memory ra,
        RawStateIndex memory rsi
    ) public boundIndex(rsi) {
        address prover = makeAddr("Prover");
        ra = createAttestation(rs, ra, rsi);
        bytes memory snapPayload = fakeSnapshot(rs, rsi).formatSnapshot();
        // Create Notary signature for the attestation
        address notary = domains[DOMAIN_LOCAL].agent;
        (bytes memory attPayload, bytes memory attSig) = signAttestation(notary, ra);
        // Create Guard signature for the report
        address guard = domains[0].agent;
        (bytes memory statePayload, bytes memory srSig) = signStateReport(guard, rs);
        expectDisputeOpened(0, guard, notary);
        vm.prank(prover);
        testedInbox().submitStateReportWithAttestation(rsi.stateIndex, srSig, snapPayload, attPayload, attSig);
        assertEq(testedInbox().getReportsAmount(), 1, "!reportsAmount");
        (bytes memory reportPayload, bytes memory reportSignature) = testedInbox().getGuardReport(0);
        assertEq(reportPayload, statePayload, "!reportPayload");
        assertEq(reportSignature, srSig, "!reportSignature");
    }

    function test_submitStateReportWithAttestation_revert_signedByNotary(Random memory random) public {
        RawState memory rs = random.nextState();
        RawStateIndex memory rsi = random.nextStateIndex();
        RawSnapshot memory rawSnap = fakeSnapshot(rs, rsi);
        bytes memory snapPayload = rawSnap.formatSnapshot();
        RawAttestation memory ra = random.nextAttestation(rawSnap, random.nextUint32());
        // Create Notary signature for the attestation
        address notary = domains[DOMAIN_LOCAL].agent;
        (bytes memory attPayload, bytes memory attSig) = signAttestation(notary, ra);
        // Force a random Notary to sign the report
        address reportSigner = getNotary(random.nextUint256(), random.nextUint256());
        (, bytes memory srSig) = signStateReport(reportSigner, rs);
        vm.expectRevert(AgentNotGuard.selector);
        testedInbox().submitStateReportWithAttestation(rsi.stateIndex, srSig, snapPayload, attPayload, attSig);
    }

    function test_submitStateReportWithSnapshotProof(
        RawState memory rs,
        RawAttestation memory ra,
        RawStateIndex memory rsi
    ) public boundIndex(rsi) {
        address prover = makeAddr("Prover");
        ra = createAttestation(rs, ra, rsi);
        // Create Notary signature for the attestation
        address notary = domains[DOMAIN_LOCAL].agent;
        (bytes memory attPayload, bytes memory attSig) = signAttestation(notary, ra);
        // Create Guard signature for the report
        address guard = domains[0].agent;
        (bytes memory statePayload, bytes memory srSig) = signStateReport(guard, rs);
        // Generate Snapshot Proof
        bytes32[] memory snapProof = genSnapshotProof(rsi.stateIndex);
        expectDisputeOpened(0, guard, notary);
        vm.prank(prover);
        testedInbox().submitStateReportWithSnapshotProof(
            rsi.stateIndex, statePayload, srSig, snapProof, attPayload, attSig
        );
        assertEq(testedInbox().getReportsAmount(), 1, "!reportsAmount");
        (bytes memory reportPayload, bytes memory reportSignature) = testedInbox().getGuardReport(0);
        assertEq(reportPayload, statePayload, "!reportPayload");
        assertEq(reportSignature, srSig, "!reportSignature");
    }

    function test_submitStateReportWithSnapshotProof_revert_signedByNotary(Random memory random) public {
        RawState memory rs = random.nextState();
        RawStateIndex memory rsi = random.nextStateIndex();
        RawSnapshot memory rawSnap = fakeSnapshot(rs, rsi);
        RawAttestation memory ra = random.nextAttestation(rawSnap, random.nextUint32());
        // Create Notary signature for the attestation
        address notary = domains[DOMAIN_LOCAL].agent;
        (bytes memory attPayload, bytes memory attSig) = signAttestation(notary, ra);
        // Force a random Notary to sign the report
        address reportSigner = getNotary(random.nextUint256(), random.nextUint256());
        (bytes memory statePayload, bytes memory srSig) = signStateReport(reportSigner, rs);
        // Generate Snapshot Proof
        acceptSnapshot(rawSnap);
        bytes32[] memory snapProof = genSnapshotProof(rsi.stateIndex);
        vm.expectRevert(AgentNotGuard.selector);
        testedInbox().submitStateReportWithSnapshotProof(
            rsi.stateIndex, statePayload, srSig, snapProof, attPayload, attSig
        );
    }

    // ═════════════════════════════════════════ TESTS: ALREADY IN DISPUTE ═════════════════════════════════════════════

    function test_submitStateReportWithSnapshot_revert_guardInDispute(RawState memory rs, RawStateIndex memory rsi)
        public
        boundIndex(rsi)
    {
        address prover = makeAddr("Prover");
        // Create Notary signature for the snapshot
        address notary = domains[DOMAIN_LOCAL].agent;
        (bytes memory snapPayload, bytes memory snapSig) = createSignedSnapshot(notary, rs, rsi);
        // Create Guard signature for the report
        address guard = domains[0].agent;
        (, bytes memory srSig) = signStateReport(guard, rs);
        openDispute(guard, domains[DOMAIN_LOCAL].agents[1]);
        vm.expectRevert(GuardInDispute.selector);
        vm.prank(prover);
        testedInbox().submitStateReportWithSnapshot(rsi.stateIndex, srSig, snapPayload, snapSig);
    }

    function test_submitStateReportWithSnapshot_revert_notaryInDispute(RawState memory rs, RawStateIndex memory rsi)
        public
        boundIndex(rsi)
    {
        address prover = makeAddr("Prover");
        // Create Notary signature for the snapshot
        address notary = domains[DOMAIN_LOCAL].agent;
        (bytes memory snapPayload, bytes memory snapSig) = createSignedSnapshot(notary, rs, rsi);
        // Create Guard signature for the report
        address guard = domains[0].agent;
        (, bytes memory srSig) = signStateReport(guard, rs);
        openDispute(domains[0].agents[1], notary);
        vm.expectRevert(NotaryInDispute.selector);
        vm.prank(prover);
        testedInbox().submitStateReportWithSnapshot(rsi.stateIndex, srSig, snapPayload, snapSig);
    }

    function test_submitStateReportWithAttestation_revert_guardInDispute(
        RawState memory rs,
        RawAttestation memory ra,
        RawStateIndex memory rsi
    ) public boundIndex(rsi) {
        address prover = makeAddr("Prover");
        ra = createAttestation(rs, ra, rsi);
        bytes memory snapPayload = fakeSnapshot(rs, rsi).formatSnapshot();
        // Create Notary signature for the attestation
        address notary = domains[DOMAIN_LOCAL].agent;
        (bytes memory attPayload, bytes memory attSig) = signAttestation(notary, ra);
        // Create Guard signature for the report
        address guard = domains[0].agent;
        (, bytes memory srSig) = signStateReport(guard, rs);
        openDispute(guard, domains[DOMAIN_LOCAL].agents[1]);
        vm.expectRevert(GuardInDispute.selector);
        vm.prank(prover);
        testedInbox().submitStateReportWithAttestation(rsi.stateIndex, srSig, snapPayload, attPayload, attSig);
    }

    function test_submitStateReportWithAttestation_revert_notaryInDispute(
        RawState memory rs,
        RawAttestation memory ra,
        RawStateIndex memory rsi
    ) public boundIndex(rsi) {
        address prover = makeAddr("Prover");
        ra = createAttestation(rs, ra, rsi);
        bytes memory snapPayload = fakeSnapshot(rs, rsi).formatSnapshot();
        // Create Notary signature for the attestation
        address notary = domains[DOMAIN_LOCAL].agent;
        (bytes memory attPayload, bytes memory attSig) = signAttestation(notary, ra);
        // Create Guard signature for the report
        address guard = domains[0].agent;
        (, bytes memory srSig) = signStateReport(guard, rs);
        openDispute(domains[0].agents[1], notary);
        vm.expectRevert(NotaryInDispute.selector);
        vm.prank(prover);
        testedInbox().submitStateReportWithAttestation(rsi.stateIndex, srSig, snapPayload, attPayload, attSig);
    }

    function test_submitStateReportWithSnapshotProof_revert_guardInDispute(
        RawState memory rs,
        RawAttestation memory ra,
        RawStateIndex memory rsi
    ) public boundIndex(rsi) {
        address prover = makeAddr("Prover");
        ra = createAttestation(rs, ra, rsi);
        // Create Notary signature for the attestation
        address notary = domains[DOMAIN_LOCAL].agent;
        (bytes memory attPayload, bytes memory attSig) = signAttestation(notary, ra);
        // Create Guard signature for the report
        address guard = domains[0].agent;
        (bytes memory statePayload, bytes memory srSig) = signStateReport(guard, rs);
        // Generate Snapshot Proof
        bytes32[] memory snapProof = genSnapshotProof(rsi.stateIndex);
        openDispute(guard, domains[DOMAIN_LOCAL].agents[1]);
        vm.expectRevert(GuardInDispute.selector);
        vm.prank(prover);
        testedInbox().submitStateReportWithSnapshotProof(
            rsi.stateIndex, statePayload, srSig, snapProof, attPayload, attSig
        );
    }

    function test_submitStateReportWithSnapshotProof_revert_notaryInDispute(
        RawState memory rs,
        RawAttestation memory ra,
        RawStateIndex memory rsi
    ) public boundIndex(rsi) {
        address prover = makeAddr("Prover");
        ra = createAttestation(rs, ra, rsi);
        // Create Notary signature for the attestation
        address notary = domains[DOMAIN_LOCAL].agent;
        (bytes memory attPayload, bytes memory attSig) = signAttestation(notary, ra);
        // Create Guard signature for the report
        address guard = domains[0].agent;
        (bytes memory statePayload, bytes memory srSig) = signStateReport(guard, rs);
        // Generate Snapshot Proof
        bytes32[] memory snapProof = genSnapshotProof(rsi.stateIndex);
        openDispute(domains[0].agents[1], notary);
        vm.expectRevert(NotaryInDispute.selector);
        vm.prank(prover);
        testedInbox().submitStateReportWithSnapshotProof(
            rsi.stateIndex, statePayload, srSig, snapProof, attPayload, attSig
        );
    }

    // ═════════════════════════════════════════ TESTS: VERIFY STATEMENTS ══════════════════════════════════════════════

    function test_verifyReceiptReport_validReceipt(Random memory random) public {
        address guard = randomGuard(random);
        RawExecReceipt memory re = random.nextReceipt(localDomain());
        bytes memory rcptPayload = re.formatReceipt();
        bytes memory rrSignature = signReceiptReport(guard, rcptPayload);
        // Force isValidReceipt(rcptPayload) to return true
        vm.mockCall(
            localDestination(),
            abi.encodeWithSelector(IExecutionHub.isValidReceipt.selector, rcptPayload),
            abi.encode(true)
        );
        vm.expectEmit();
        emit InvalidReceiptReport(rcptPayload, rrSignature);
        expectStatusUpdated(AgentFlag.Fraudulent, 0, guard);
        expectDisputeResolved(0, guard, address(0), address(this));
        // Should return false, as the report is invalid
        assertFalse(testedInbox().verifyReceiptReport(rcptPayload, rrSignature));
    }

    function test_verifyReceiptReport_invalidReceipt(Random memory random) public {
        address guard = randomGuard(random);
        RawExecReceipt memory re = random.nextReceipt(localDomain());
        bytes memory rcptPayload = re.formatReceipt();
        bytes memory rrSignature = signReceiptReport(guard, rcptPayload);
        // Force isValidReceipt(rcptPayload) to return false
        vm.mockCall(
            localDestination(),
            abi.encodeWithSelector(IExecutionHub.isValidReceipt.selector, rcptPayload),
            abi.encode(false)
        );
        vm.recordLogs();
        // Should return true, as the report is valid
        assertTrue(testedInbox().verifyReceiptReport(rcptPayload, rrSignature));
        assertEq(vm.getRecordedLogs().length, 0, "Logs should be empty");
    }

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

    function nextSignatureIndex() public returns (uint256 sigIndex) {
        sigIndex = _signatureIndex++;
    }

    /// @notice Returns address of the tested contract
    function localContract() public view override returns (address) {
        return localInbox();
    }

    /// @notice Returns tested contract as StatementInbox
    function testedInbox() public view returns (StatementInbox) {
        return StatementInbox(localInbox());
    }
}