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