Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions script/config/VerifyChain.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,8 @@ contract VerifyChain is Script {
if (isEvm) {
_checkApi(name, json);
bool rpcOk = _checkRpc(json);
if (rpcOk) _checkOnChainCode(name, json);
// Called unconditionally: the rung self-gates on the fork so a failed RPC still gets a line.
_checkOnChainCode(name, json);
_checkRegistryAndExtras(name, json);
_checkMesh(name, json, projectJson);
_checkLanesOnChain(name, json, projectJson);
Expand Down Expand Up @@ -869,7 +870,12 @@ contract VerifyChain is Script {

// ---------------------------------------------------------------- 5. ON-CHAIN
function _checkOnChainCode(string memory name, string memory json) private {
if (!s_forked) return;
if (!s_forked) {
_skipUnverified(
"on-chain: code checks need an RPC (no fork) - router/rmnProxy/tokenAdminRegistry/registryModuleOwnerCustom/link not read"
);
return;
}
string[5] memory keys = ["router", "rmnProxy", "tokenAdminRegistry", "registryModuleOwnerCustom", "link"];
uint256 bad = 0;
for (uint256 i = 0; i < keys.length; i++) {
Expand Down Expand Up @@ -1456,11 +1462,12 @@ contract VerifyChain is Script {
return s_skips;
}

/// @notice Test hook: drives the three RPC-gated rungs in their no-fork state and returns the
/// @notice Test hook: drives the four RPC-gated rungs in their no-fork state and returns the
/// unverified-gap count, so each counted site is pinned individually (a regression demoting one
/// back to a designed skip fails this). `rolesJson` must declare `.roles.token` so the roles rung
/// reaches its RPC gate rather than the designed no-roles skip. Not used by any production path.
function rpcGatedSkipsForTest(string memory rolesJson) public returns (uint256 skipsOut) {
_checkOnChainCode("zz-tt-rpcgaps", "{}");
_reconcilePoolWithTar(address(1), address(1), address(1));
_checkRoles("zz-tt-rpcgaps", rolesJson, false);
_checkLanesOnChain("zz-tt-rpcgaps", "", "{}");
Expand All @@ -1475,6 +1482,13 @@ contract VerifyChain is Script {
return s_skips;
}

/// @notice Test hook: runs the on-chain rung with no fork and then the verdict, so a test can
/// prove that rung reports its gap instead of vanishing. Not used by any production path.
function onChainNoForkVerdictForTest(string memory name) public {
_checkOnChainCode(name, "{}");
_verdict(name);
}

/// @notice Test hook: seeds the outcome counters and runs `_verdict`, so the three-outcome
/// contract (VERIFIED / INCOMPLETE / FAILED, and that designed skips never taint the verdict) is
/// pinnable without the ffi/API/RPC doctor run. Not used by any production path.
Expand Down
6 changes: 6 additions & 0 deletions script/config/test-tooling.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1443,6 +1443,12 @@ run_case "doctor lanes rung prints its SKIP without an RPC (run INCOMPLETE)" non
"lanes: on-chain reconciliation needs an RPC" -- \
env FOUNDRY_PROFILE=sync forge script script/config/VerifyChain.s.sol --tc VerifyChain --sig "run(string)" "$TMP_CHAIN"

# 28c. same for the ON-CHAIN rung (code at the ccip{} addresses). It used to return silently without a
# fork, so the verdict counted a rung that printed nothing at all.
run_case "doctor on-chain rung prints its SKIP without an RPC (run INCOMPLETE)" nonzero \
"\[SKIP\] UNVERIFIED on-chain: code checks need an RPC" -- \
env FOUNDRY_PROFILE=sync forge script script/config/VerifyChain.s.sol --tc VerifyChain --sig "run(string)" "$TMP_CHAIN"

# 29. BOTH=1 writes the reciprocal entry on the remote's file in the same invocation
rm_fixture_config "$TMP_FILE" "$TMP_FILE_B"
python3 -c "
Expand Down
12 changes: 11 additions & 1 deletion test/config/VerifyChainVerdict.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,17 @@ contract VerifyChainVerdictTest is Test {
function test_RpcGatedRungs_EachCountsOneUnverifiedGap() public {
uint256 skips = new VerifyChain()
.rpcGatedSkipsForTest('{"roles":{"token":{"address":"0x0000000000000000000000000000000000000001"}}}');
assertEq(skips, 3, "registry TAR, roles, and lanes each count one unverified gap without an RPC");
assertEq(skips, 4, "on-chain, registry TAR, roles, and lanes each count one gap without an RPC");
}

/// @dev The on-chain rung used to return silently with no fork, so a run that read nothing on-chain
/// still exited clean. It must report the gap and take the verdict to INCOMPLETE.
function test_OnChainRung_WithoutFork_IsIncompleteNotSilent() public {
try new VerifyChain().onChainNoForkVerdictForTest(NAME) {
assertTrue(false, "an unforked on-chain rung must not exit clean");
} catch Error(string memory reason) {
_assertContains(reason, string.concat("check-chain INCOMPLETE for ", NAME));
}
}

function _assertContains(string memory haystack, string memory needle) internal pure {
Expand Down
Loading