synapsecns/sanguine

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

Summary

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

import {
    AgentNotGuard,
    AgentNotNotary,
    IncorrectAgentDomain,
    IncorrectDataHash,
    GuardInDispute,
    NotaryInDispute,
    SynapseDomainForbidden
} from "../../../contracts/libs/Errors.sol";
import {InterfaceDestination} from "../../../contracts/interfaces/InterfaceDestination.sol";

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

import {BaseMock} from "../../mocks/base/BaseMock.t.sol";
import {Random} from "../../utils/libs/Random.t.sol";
import {RawAttestation, RawSnapshot} from "../../utils/libs/SynapseStructs.t.sol";

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

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

    function test_cleanSetup(Random memory random) public override {
        uint32 domain = random.nextUint32();
        vm.assume(domain != DOMAIN_SYNAPSE);
        vm.chainId(domain);

        address caller = random.nextAddress();
        address agentManager = random.nextAddress();
        address origin_ = random.nextAddress();
        address destination_ = random.nextAddress();
        LightInbox lightInbox_ = new LightInbox(DOMAIN_SYNAPSE);
        vm.prank(caller);
        lightInbox_.initialize(agentManager, origin_, destination_);
        assertEq(lightInbox_.owner(), caller);
        assertEq(lightInbox_.localDomain(), domain);
        assertEq(lightInbox_.origin(), origin_);
        assertEq(lightInbox_.destination(), destination_);
        assertEq(lightInbox_.agentManager(), agentManager);
    }

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

    function test_constructor_revert_onSynapseChain() public {
        vm.chainId(DOMAIN_SYNAPSE);
        vm.expectRevert(SynapseDomainForbidden.selector);
        new LightInbox({synapseDomain_: DOMAIN_SYNAPSE});
    }

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

    function initializeLocalContract() public override {
        LightInbox(lightInbox).initialize(address(0), address(0), address(0));
    }

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

    function test_submitAttestation(Random memory random) public {
        RawSnapshot memory rs = random.nextSnapshot();
        RawAttestation memory ra = random.nextAttestation(rs, random.nextUint32());
        uint256[] memory snapGas = rs.snapGas();
        address notary = domains[localDomain()].agent;
        (bytes memory attPayload, bytes memory attSignature) = signAttestation(notary, ra);
        // Should pass to Destination: acceptAttestation(status, sigIndex, attestation, agentRoot, snapGas)
        vm.expectCall(
            destination,
            abi.encodeWithSelector(
                InterfaceDestination.acceptAttestation.selector,
                agentIndex[notary],
                nextSignatureIndex(),
                attPayload,
                ra._agentRoot,
                snapGas
            )
        );
        lightInbox.submitAttestation(attPayload, attSignature, ra._agentRoot, snapGas);
    }

    function test_submitAttestation_revert_agentRootMismatch(Random memory random) public {
        RawSnapshot memory rs = random.nextSnapshot();
        RawAttestation memory ra = random.nextAttestation(rs, random.nextUint32());
        uint256[] memory snapGas = rs.snapGas();
        uint256 malformedBit = random.nextUint8();
        address notary = domains[localDomain()].agent;
        (bytes memory attPayload, bytes memory attSignature) = signAttestation(notary, ra);
        vm.expectRevert(IncorrectDataHash.selector);
        // Try to feed the agent root with a single malformed bit
        lightInbox.submitAttestation(attPayload, attSignature, ra._agentRoot ^ bytes32(1 << malformedBit), snapGas);
    }

    function test_submitAttestation_revert_snapGasMismatch(Random memory random) public {
        RawSnapshot memory rs = random.nextSnapshot();
        RawAttestation memory ra = random.nextAttestation(rs, random.nextUint32());
        uint256[] memory snapGas = rs.snapGas();
        uint256 malformedBit = random.nextUint8();
        uint256 malformedIndex = random.nextUint256() % snapGas.length;
        snapGas[malformedIndex] ^= 1 << malformedBit;
        address notary = domains[localDomain()].agent;
        (bytes memory attPayload, bytes memory attSignature) = signAttestation(notary, ra);
        vm.expectRevert(IncorrectDataHash.selector);
        // Try to feed the gas data with a single malformed bit
        lightInbox.submitAttestation(attPayload, attSignature, ra._agentRoot, snapGas);
    }

    function test_submitAttestation_revert_signedByGuard(Random memory random) public {
        RawSnapshot memory rs = random.nextSnapshot();
        RawAttestation memory ra = random.nextAttestation(rs, random.nextUint32());
        uint256[] memory snapGas = rs.snapGas();
        address guard = domains[0].agent;
        (bytes memory attPayload, bytes memory attSignature) = signAttestation(guard, ra);
        vm.expectRevert(AgentNotNotary.selector);
        lightInbox.submitAttestation(attPayload, attSignature, ra._agentRoot, snapGas);
    }

    function test_submitAttestation_revert_signedByRemoteNotary(Random memory random) public {
        RawSnapshot memory rs = random.nextSnapshot();
        RawAttestation memory ra = random.nextAttestation(rs, random.nextUint32());
        uint256[] memory snapGas = rs.snapGas();
        address notary = domains[DOMAIN_REMOTE].agent;
        (bytes memory attPayload, bytes memory attSignature) = signAttestation(notary, ra);
        vm.expectRevert(IncorrectAgentDomain.selector);
        lightInbox.submitAttestation(attPayload, attSignature, ra._agentRoot, snapGas);
    }

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

    function test_submitAttestationReport(Random memory random) public {
        address prover = makeAddr("Prover");
        RawSnapshot memory rs = random.nextSnapshot();
        RawAttestation memory ra = random.nextAttestation(rs, random.nextUint32());
        // Create Notary signature for the attestation
        address notary = domains[DOMAIN_LOCAL].agent;
        (, bytes memory attSignature) = signAttestation(notary, ra);
        // Create Guard signature for the report
        address guard = domains[0].agent;
        (bytes memory attPayload, bytes memory arSignature) = signAttestationReport(guard, ra);
        expectDisputeOpened(0, guard, notary);
        vm.prank(prover);
        lightInbox.submitAttestationReport(attPayload, arSignature, attSignature);
        assertEq(lightInbox.getReportsAmount(), 1, "!reportsAmount");
        (bytes memory reportPayload, bytes memory reportSignature) = lightInbox.getGuardReport(0);
        assertEq(reportPayload, attPayload, "!reportPayload");
        assertEq(reportSignature, arSignature, "!reportSig");
    }

    function test_submitAttestationReport_revert_signedByNotary(Random memory random) public {
        RawSnapshot memory rs = random.nextSnapshot();
        RawAttestation memory ra = random.nextAttestation(rs, random.nextUint32());
        // Create Notary signature for the attestation
        address notary = domains[DOMAIN_LOCAL].agent;
        (, bytes memory attSignature) = signAttestation(notary, ra);
        // Force a random Notary to sign the report
        address reportSigner = getNotary(random.nextUint256(), random.nextUint256());
        (bytes memory attPayload, bytes memory arSignature) = signAttestationReport(reportSigner, ra);
        vm.expectRevert(AgentNotGuard.selector);
        lightInbox.submitAttestationReport(attPayload, arSignature, attSignature);
    }

    function test_submitAttestationReport_revert_guardInDispute(Random memory random) public {
        RawSnapshot memory rs = random.nextSnapshot();
        RawAttestation memory ra = random.nextAttestation(rs, random.nextUint32());
        // Create Notary signature for the attestation
        address notary = domains[DOMAIN_LOCAL].agents[0];
        (, bytes memory attSignature) = signAttestation(notary, ra);
        // Create Guard signature for the report
        address guard = domains[0].agent;
        (bytes memory attPayload, bytes memory arSignature) = signAttestationReport(guard, ra);
        // Put the Guard in Dispute with another Notary
        openDispute({guard: guard, notary: domains[DOMAIN_LOCAL].agents[1]});
        vm.expectRevert(GuardInDispute.selector);
        lightInbox.submitAttestationReport(attPayload, arSignature, attSignature);
    }

    function test_submitAttestationReport_revert_notaryInDispute(Random memory random) public {
        RawSnapshot memory rs = random.nextSnapshot();
        RawAttestation memory ra = random.nextAttestation(rs, random.nextUint32());
        // Create Notary signature for the attestation
        address notary = domains[DOMAIN_LOCAL].agents[0];
        (, bytes memory attSignature) = signAttestation(notary, ra);
        // Create Guard signature for the report
        address guard = domains[0].agents[0];
        (bytes memory attPayload, bytes memory arSignature) = signAttestationReport(guard, ra);
        // Put the Notary in Dispute with another Guard
        openDispute({guard: domains[0].agents[1], notary: notary});
        vm.expectRevert(NotaryInDispute.selector);
        lightInbox.submitAttestationReport(attPayload, arSignature, attSignature);
    }

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

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