packages/contracts-core/test/suite/base/MessagingBase.t.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
import {MessagingBase} from "../../../contracts/base/MessagingBase.sol";
import {AgentFlag, DisputeFlag} from "../../../contracts/libs/Structures.sol";
import {IAgentSecured} from "../../../contracts/interfaces/IAgentSecured.sol";
import {IStatementInbox} from "../../../contracts/interfaces/IStatementInbox.sol";
import {AgentManagerHarness} from "../../harnesses/manager/AgentManagerHarness.t.sol";
import {SynapseTest} from "../../utils/SynapseTest.t.sol";
import {fakeSnapshot} from "../../utils/libs/FakeIt.t.sol";
import {Random} from "../../utils/libs/Random.t.sol";
import {RawAttestation, RawSnapshot, RawState, RawStateIndex} from "../../utils/libs/SynapseStructs.t.sol";
// solhint-disable func-name-mixedcase
abstract contract MessagingBaseTest is SynapseTest {
struct SnapshotMock {
RawState rs;
RawStateIndex rsi;
}
modifier boundIndex(RawStateIndex memory rsi) {
rsi.boundStateIndex();
_;
}
modifier onlySupportedDomain() virtual {
require(localDomain() == DOMAIN_LOCAL || localDomain() == DOMAIN_SYNAPSE, "Unsupported local domain");
_;
}
// To ensure that every MessagingBase contract has this test
function test_cleanSetup(Random memory random) public virtual;
// To ensure that every MessagingBase contract has this test
function test_initializer_revert_alreadyInitialized() public {
expectRevertAlreadyInitialized();
initializeLocalContract();
}
function initializeLocalContract() public virtual;
// ═══════════════════════════════════════════════ EXPECTATIONS ════════════════════════════════════════════════════
function expectStatusUpdated(AgentFlag flag, uint32 domain, address agent) public {
vm.expectEmit();
emit StatusUpdated(flag, domain, agent);
}
function expectDisputeOpened(uint256 disputeIndex, address guard, address notary) public {
vm.expectEmit();
emit DisputeOpened(disputeIndex, agentIndex[guard], agentIndex[notary]);
bytes memory expectedCall =
abi.encodeWithSelector(IAgentSecured.openDispute.selector, agentIndex[guard], agentIndex[notary]);
vm.expectCall(localDestination(), expectedCall);
if (localSummit() != address(0)) {
vm.expectCall(localSummit(), expectedCall);
}
}
function expectDisputeResolved(uint256 disputePtr, address slashed, address honest, address prover) public {
if (disputePtr != 0) {
vm.expectEmit();
emit DisputeResolved(disputePtr - 1, agentIndex[slashed], agentIndex[honest], prover);
}
bytes memory expectedCall =
abi.encodeWithSelector(IAgentSecured.resolveDispute.selector, agentIndex[slashed], agentIndex[honest]);
vm.expectCall(localDestination(), expectedCall);
if (localSummit() != address(0)) {
vm.expectCall(localSummit(), expectedCall);
}
}
// ══════════════════════════════════════════════ DISPUTE CHEATS ═══════════════════════════════════════════════════
function openDispute(address guard, address notary) public {
require(agentIndex[guard] != 0 && agentDomain[guard] == 0, "Invalid Guard");
require(agentIndex[notary] != 0 && agentDomain[notary] != 0, "Invalid Notary");
RawSnapshot memory rs = fakeSnapshot({statesAmount: 1});
(bytes memory snapPayload, bytes memory snapSignature) = signSnapshot(notary, rs);
(, bytes memory srSignature) = signStateReport(guard, rs.states[0]);
IStatementInbox(localInbox()).submitStateReportWithSnapshot(0, srSignature, snapPayload, snapSignature);
}
// ═══════════════════════════════════════════════ AGENT GETTERS ═══════════════════════════════════════════════════
function randomAgent(Random memory random) public view returns (address agent) {
// Pick a random Agent
(, agent) = getAgent(random.nextUint256(), random.nextUint256());
}
function randomGuard(Random memory random) public view returns (address guard) {
// Pick a random Guard
guard = getGuard(random.nextUint256());
}
function randomNotary(Random memory random) public view returns (address notary) {
if (localDomain() != DOMAIN_SYNAPSE) {
// Pick a random Notary from local domain
notary = getDomainAgent(localDomain(), random.nextUint256());
} else {
// Pick a random Notary from any domain
notary = getNotary(random.nextUint256(), random.nextUint256());
}
}
// ═══════════════════════════════════════════════ DATA CREATION ═══════════════════════════════════════════════════
/// @notice Creates attestation for snapshot having given rawState at given index,
/// with some fake data for other states in the snapshots.
function createAttestation(RawState memory rawState, RawAttestation memory ra, RawStateIndex memory rsi)
public
returns (RawAttestation memory)
{
RawSnapshot memory rawSnap = fakeSnapshot(rawState, rsi);
acceptSnapshot(rawSnap);
// Reuse existing metadata in RawAttestation
return rawSnap.castToRawAttestation(ra._agentRoot, ra.nonce, ra.blockNumber, ra.timestamp);
}
function createSnapshotProof(SnapshotMock memory sm)
public
returns (RawAttestation memory ra, bytes32[] memory snapProof)
{
ra = Random(sm.rs.root).nextAttestation(1);
ra = createAttestation(sm.rs, ra, sm.rsi);
snapProof = genSnapshotProof(sm.rsi.stateIndex);
}
function createSignedSnapshot(address notary, RawState memory rs, RawStateIndex memory rsi)
public
view
returns (bytes memory snapPayload, bytes memory snapSig)
{
RawSnapshot memory rawSnap = fakeSnapshot(rs, rsi);
return signSnapshot(notary, rawSnap);
}
// ══════════════════════════════════════════════════ HELPERS ══════════════════════════════════════════════════════
/// @notice Returns local domain for the tested contract
function localDomain() public view virtual returns (uint32);
/// @notice Returns address of the tested contract
function localContract() public view virtual returns (address);
/// @notice Returns address of Agent Manager on the tested domain
function localAgentManager() public view virtual onlySupportedDomain returns (address) {
return localDomain() == DOMAIN_LOCAL ? address(lightManager) : address(bondingManager);
}
/// @notice Returns address of Inbox on the tested domain
function localInbox() public view virtual onlySupportedDomain returns (address) {
return localDomain() == DOMAIN_LOCAL ? address(lightInbox) : address(inbox);
}
/// @notice Returns address of Destination on the tested domain
function localDestination() public view virtual onlySupportedDomain returns (address) {
return localDomain() == DOMAIN_LOCAL ? address(destination) : address(destinationSynapse);
}
/// @notice Returns address of Origin on the tested domain
function localOrigin() public view virtual onlySupportedDomain returns (address) {
return localDomain() == DOMAIN_LOCAL ? address(origin) : address(originSynapse);
}
/// @notice Returns address of Summit on the tested domain
function localSummit() public view virtual onlySupportedDomain returns (address) {
return localDomain() == DOMAIN_SYNAPSE ? address(summit) : address(0);
}
/// @notice Checks if contract is a local SystemContract
function isLocalSystemContract(address addr) public view returns (bool) {
return addr == localAgentManager() || addr == localDestination() || addr == localOrigin();
}
/// @notice Checks if contract is a local AgentSecured contract
function isLocalAgentSecured(address addr) public view returns (bool) {
return addr == localDestination() || addr == localOrigin();
}
}