packages/contracts-core/test/suite/inbox/LightInbox.t.sol
// 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;
}
}