Commit 7d090bf9 authored by clabby's avatar clabby

fix: Search for expected status byte

parent c856071b
This diff is collapsed.
......@@ -100,8 +100,8 @@
"sourceCodeHash": "0xa995b54dce03ddf5c9c47451bd7181996b91398ad66b54ab0b8cbf582863a33e"
},
"src/dispute/OutputBisectionGame.sol": {
"initCodeHash": "0x959d79d64f526fe67a476f876370814fb583bd1674b692f1025632e4f67a8c71",
"sourceCodeHash": "0xfdce387743a43e48f6aaa5855de0088d9bbb003d0ce62de465cf151320979a7a"
"initCodeHash": "0xd354d78579c42a9f0f8a8c9b5ef04f570fa3bd088f88102b431aca8d48fddaae",
"sourceCodeHash": "0x98f65f2f2f07a525d360eba87e624f1cb44c52326f3b3f2bf7b6ee41a7ec4a2c"
},
"src/legacy/DeployerWhitelist.sol": {
"initCodeHash": "0x8de80fb23b26dd9d849f6328e56ea7c173cd9e9ce1f05c9beea559d1720deb3d",
......
......@@ -151,7 +151,7 @@ contract OutputBisectionGame is IOutputBisectionGame, Clone, ISemver {
// the remainder of the index at depth divided by 2 ** (MAX_GAME_DEPTH - SPLIT_DEPTH),
// which is the number of leaves in each execution trace subgame. This is so that we can
// determine whether or not the step position is represents the `ABSOLUTE_PRESTATE`.
preStateClaim = (stepPos.indexAtDepth() % (2 ** (MAX_GAME_DEPTH - SPLIT_DEPTH))) == 0
preStateClaim = (stepPos.indexAtDepth() % (1 << (MAX_GAME_DEPTH - SPLIT_DEPTH))) == 0
? ABSOLUTE_PRESTATE
: findTraceAncestor(Position.wrap(Position.unwrap(parentPos) - 1), parent.parentIndex, false).claim;
// For all attacks, the poststate is the parent claim.
......@@ -207,7 +207,8 @@ contract OutputBisectionGame is IOutputBisectionGame, Clone, ISemver {
// Compute the position that the claim commits to. Because the parent's position is already
// known, we can compute the next position by moving left or right depending on whether
// or not the move is an attack or defense.
Position nextPosition = parent.position.move(_isAttack);
Position parentPos = parent.position;
Position nextPosition = parentPos.move(_isAttack);
uint256 nextPositionDepth = nextPosition.depth();
// INVARIANT: A defense can never be made against the root claim of either the output root game or any
......@@ -225,7 +226,9 @@ contract OutputBisectionGame is IOutputBisectionGame, Clone, ISemver {
// When the next position surpasses the split depth (i.e., it is the root claim of an execution
// trace bisection sub-game), we need to perform some extra verification steps.
if (nextPositionDepth == SPLIT_DEPTH + 1) verifyExecBisectionRoot(_claim);
if (nextPositionDepth == SPLIT_DEPTH + 1) {
verifyExecBisectionRoot(_claim, _challengeIndex, parentPos, _isAttack);
}
// Fetch the grandparent clock, if it exists.
// The grandparent clock should always exist unless the parent is the root claim.
......@@ -474,11 +477,37 @@ contract OutputBisectionGame is IOutputBisectionGame, Clone, ISemver {
/// @notice Verifies the integrity of an execution bisection subgame's root claim. Reverts if the claim
/// is invalid.
/// @param _rootClaim The root claim of the execution bisection subgame.
function verifyExecBisectionRoot(Claim _rootClaim) internal pure {
// The VMStatus must indicate 'invalid' (1), to argue that disputed thing is invalid.
// Games that agree with the existing outcome are not allowed.
function verifyExecBisectionRoot(
Claim _rootClaim,
uint256 _parentIdx,
Position _parentPos,
bool _isAttack
)
internal
view
{
// The root claim of an execution trace bisection sub-game must:
// 1. Signal that the VM panicked or resulted in an invalid transition if the disputed output root
// was made by the opposing party.
// 2. Signal that the VM resulted in a valid transition if the disputed output root was made by the same party.
// If the move is a defense, the disputed output could have been made by either party. In this case, we
// need to search for the parent output to determine what the expected status byte should be.
Position disputedLeafPos = Position.wrap(Position.unwrap(_parentPos) + 1);
ClaimData storage disputed = findTraceAncestor({ _pos: disputedLeafPos, _start: _parentIdx, _global: true });
uint8 vmStatus = uint8(Claim.unwrap(_rootClaim)[0]);
if (!(vmStatus == VMStatus.unwrap(VMStatuses.INVALID) || vmStatus == VMStatus.unwrap(VMStatuses.PANIC))) {
if (_isAttack || disputed.position.depth() % 2 == SPLIT_DEPTH % 2) {
// If the move is an attack, the parent output is always deemed to be disputed. In this case, we only need
// to check that the root claim signals that the VM panicked or resulted in an invalid transition.
// If the move is a defense, and the disputed output and creator of the execution trace subgame disagree,
// the root claim should also signal that the VM panicked or resulted in an invalid transition.
if (!(vmStatus == VMStatus.unwrap(VMStatuses.INVALID) || vmStatus == VMStatus.unwrap(VMStatuses.PANIC))) {
revert UnexpectedRootClaim(_rootClaim);
}
} else if (vmStatus != VMStatus.unwrap(VMStatuses.VALID)) {
// The disputed output and the creator of the execution trace subgame agree. The status byte should
// have signaled that the VM succeeded.
revert UnexpectedRootClaim(_rootClaim);
}
}
......
......@@ -593,7 +593,7 @@ contract OutputBisectionGame_Test is OutputBisectionGame_Init {
for (uint256 i; i < 4; i++) {
gameProxy.attack(i, Claim.wrap(bytes32(i)));
}
gameProxy.defend(4, ROOT_CLAIM);
gameProxy.defend(4, _changeClaimStatus(ROOT_CLAIM, VMStatuses.VALID));
// Expected start/disputed claims
bytes32 startingClaim = bytes32(uint256(3));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment