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