Skip to content

Commit

Permalink
Merge pull request #173 from OffchainLabs/cond-osp
Browse files Browse the repository at this point in the history
  • Loading branch information
gzeoneth authored Apr 30, 2024
2 parents ecb46c1 + 09ff1db commit 2537361
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 17 deletions.
30 changes: 24 additions & 6 deletions src/challenge/ChallengeManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager {
ISequencerInbox public sequencerInbox;
IBridge public bridge;
IOneStepProofEntry public osp;
mapping(bytes32 => IOneStepProofEntry) public ospCond;

function challengeInfo(uint64 challengeIndex)
external
Expand Down Expand Up @@ -110,12 +111,28 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager {
osp = osp_;
}

function postUpgradeInit(IOneStepProofEntry osp_) external onlyDelegated onlyProxyOwner {
// when updating to 4844 we need to create new osp contracts and set them here
// on the challenge manager
/// @dev A osp breaking change is introduced as part of Stylus upgrade, where the new osp would not support
/// pre-Stylus legacy wasmModuleRoot. To ensure that the new osp is not used for legacy wasmModuleRoot,
/// we introduce a conditional OSP where condRoot should be set to the pre-Stylus root and condOsp should
/// be set to the pre-Stylus osp. The correct value should be handled by the upgrade action contract.
function postUpgradeInit(
IOneStepProofEntry osp_,
bytes32 condRoot,
IOneStepProofEntry condOsp
) external onlyDelegated onlyProxyOwner {
ospCond[condRoot] = condOsp;
osp = osp_;
}

function getOsp(bytes32 wasmModuleRoot) public view returns (IOneStepProofEntry) {
IOneStepProofEntry t = ospCond[wasmModuleRoot];
if (address(t) == address(0)) {
return osp;
} else {
return t;
}
}

function createChallenge(
bytes32 wasmModuleRoot_,
MachineStatus[2] calldata startAndEndMachineStatuses_,
Expand Down Expand Up @@ -233,8 +250,9 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager {
}

bytes32[] memory segments = new bytes32[](2);
segments[0] = osp.getStartMachineHash(globalStateHashes[0], challenge.wasmModuleRoot);
segments[1] = osp.getEndMachineHash(machineStatuses[1], globalStateHashes[1]);
IOneStepProofEntry _osp = getOsp(challenge.wasmModuleRoot);
segments[0] = _osp.getStartMachineHash(globalStateHashes[0], challenge.wasmModuleRoot);
segments[1] = _osp.getEndMachineHash(machineStatuses[1], globalStateHashes[1]);

challenge.mode = ChallengeLib.ChallengeMode.EXECUTION;

Expand All @@ -256,7 +274,7 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager {
require(challengeLength == 1, "TOO_LONG");
}

bytes32 afterHash = osp.proveOneStep(
bytes32 afterHash = getOsp(challenge.wasmModuleRoot).proveOneStep(
ExecutionContext({maxInboxMessagesRead: challenge.maxInboxMessages, bridge: bridge}),
challengeStart,
selection.oldSegments[selection.challengePosition],
Expand Down
13 changes: 13 additions & 0 deletions src/challenge/IChallengeManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,19 @@ interface IChallengeManager {
IOneStepProofEntry osp_
) external;

function postUpgradeInit(
IOneStepProofEntry osp_,
bytes32 condRoot,
IOneStepProofEntry condOsp
) external;

/// @notice Get the default osp, which is used for all wasm module roots that don't have a conditional OSP set
/// Use getOsp(wasmModuleRoot) to get the OSP for a specific wasm module root
function osp() external view returns (IOneStepProofEntry);

/// @notice Get the OSP for a given wasm module root
function getOsp(bytes32 wasmModuleRoot) external view returns (IOneStepProofEntry);

function createChallenge(
bytes32 wasmModuleRoot_,
MachineStatus[2] calldata startAndEndMachineStatuses_,
Expand Down
117 changes: 107 additions & 10 deletions test/foundry/ChallengeManager.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,59 +4,156 @@ pragma solidity ^0.8.4;
import "forge-std/Test.sol";
import "./util/TestUtil.sol";
import "../../src/challenge/ChallengeManager.sol";
import "../../src/osp/OneStepProofEntry.sol";

contract ChallengeManagerTest is Test {
IChallengeResultReceiver resultReceiver = IChallengeResultReceiver(address(137));
ISequencerInbox sequencerInbox = ISequencerInbox(address(138));
IBridge bridge = IBridge(address(139));
IOneStepProofEntry osp = IOneStepProofEntry(address(140));
IOneStepProofEntry newOsp = IOneStepProofEntry(address(141));
IOneStepProofEntry condOsp = IOneStepProofEntry(address(142));
address proxyAdmin = address(141);
ChallengeManager chalmanImpl = new ChallengeManager();

bytes32 randomRoot = keccak256(abi.encodePacked("randomRoot"));

function deploy() public returns (ChallengeManager) {
ChallengeManager chalman = ChallengeManager(
address(new TransparentUpgradeableProxy(address(chalmanImpl), proxyAdmin, ""))
);
chalman.initialize(resultReceiver, sequencerInbox, bridge, osp);
assertEq(
address(chalman.resultReceiver()),
address(resultReceiver),
"Result receiver not set"
address(chalman.resultReceiver()), address(resultReceiver), "Result receiver not set"
);
assertEq(
address(chalman.sequencerInbox()),
address(sequencerInbox),
"Sequencer inbox not set"
address(chalman.sequencerInbox()), address(sequencerInbox), "Sequencer inbox not set"
);
assertEq(address(chalman.bridge()), address(bridge), "Bridge not set");
assertEq(address(chalman.osp()), address(osp), "OSP not set");
return chalman;
}

function testCondOsp() public {
ChallengeManager chalman = deploy();

/// legacy root and OSP that will be used as conditional
IOneStepProofEntry legacyOSP = IOneStepProofEntry(
address(
new OneStepProofEntry(
IOneStepProver(makeAddr("0")),
IOneStepProver(makeAddr("mem")),
IOneStepProver(makeAddr("math")),
IOneStepProver(makeAddr("hostio"))
)
)
);
bytes32 legacyRoot = keccak256(abi.encodePacked("legacyRoot"));

// legacy hashes
bytes32 legacySegment0 = legacyOSP.getStartMachineHash(
keccak256(abi.encodePacked("globalStateHash[0]")), legacyRoot
);
bytes32 legacySegment1 = legacyOSP.getEndMachineHash(
MachineStatus.FINISHED, keccak256(abi.encodePacked("globalStateHashes[1]"))
);

/// new OSP
IOneStepProofEntry _newOSP = IOneStepProofEntry(
address(
new OneStepProofEntry(
IOneStepProver(makeAddr("0")),
IOneStepProver(makeAddr("mem")),
IOneStepProver(makeAddr("math")),
IOneStepProver(makeAddr("hostio"))
)
)
);

// new hashes
bytes32 newSegment0 = _newOSP.getStartMachineHash(
keccak256(abi.encodePacked("globalStateHash[0]")), randomRoot
);
bytes32 newSegment1 = _newOSP.getEndMachineHash(
MachineStatus.FINISHED, keccak256(abi.encodePacked("new_globalStateHashes[1]"))
);

/// do upgrade
vm.prank(proxyAdmin);
TransparentUpgradeableProxy(payable(address(chalman))).upgradeToAndCall(
address(chalmanImpl),
abi.encodeWithSelector(
ChallengeManager.postUpgradeInit.selector, _newOSP, legacyRoot, legacyOSP
)
);

/// check cond osp
IOneStepProofEntry _condOsp = chalman.getOsp(legacyRoot);
assertEq(address(_condOsp), address(legacyOSP), "Legacy osp not set");
assertEq(
_condOsp.getStartMachineHash(
keccak256(abi.encodePacked("globalStateHash[0]")), legacyRoot
),
legacySegment0,
"Unexpected start machine hash"
);
assertEq(
_condOsp.getEndMachineHash(
MachineStatus.FINISHED, keccak256(abi.encodePacked("globalStateHashes[1]"))
),
legacySegment1,
"Unexpected end machine hash"
);

/// check new osp
IOneStepProofEntry _newOsp = chalman.getOsp(randomRoot);
assertEq(address(_newOsp), address(_newOSP), "New osp not set");
assertEq(
_newOsp.getStartMachineHash(
keccak256(abi.encodePacked("globalStateHash[0]")), randomRoot
),
newSegment0,
"Unexpected start machine hash"
);
assertEq(
_newOsp.getEndMachineHash(
MachineStatus.FINISHED, keccak256(abi.encodePacked("new_globalStateHashes[1]"))
),
newSegment1,
"Unexpected end machine hash"
);

/// check hashes are different
assertNotEq(legacySegment0, newSegment0, "Start machine hash should be different");
assertNotEq(legacySegment1, newSegment1, "End machine hash should be different");
}

function testPostUpgradeInit() public {
ChallengeManager chalman = deploy();

vm.prank(proxyAdmin);
TransparentUpgradeableProxy(payable(address(chalman))).upgradeToAndCall(
address(chalmanImpl),
abi.encodeWithSelector(ChallengeManager.postUpgradeInit.selector, newOsp)
abi.encodeWithSelector(
ChallengeManager.postUpgradeInit.selector, newOsp, randomRoot, condOsp
)
);

assertEq(address(chalman.osp()), address(newOsp), "New osp not set");
assertEq(address(chalman.getOsp(bytes32(0))), address(newOsp), "New osp not set");
assertEq(address(chalman.getOsp(randomRoot)), address(condOsp), "Cond osp not set");
}

function testPostUpgradeInitFailsNotAdmin() public {
ChallengeManager chalman = deploy();

vm.expectRevert(abi.encodeWithSelector(NotOwner.selector, address(151), proxyAdmin));
vm.prank(address(151));
chalman.postUpgradeInit(osp);
chalman.postUpgradeInit(newOsp, randomRoot, condOsp);
}

function testPostUpgradeInitFailsNotDelCall() public {
vm.expectRevert(bytes("Function must be called through delegatecall"));
vm.prank(proxyAdmin);
chalmanImpl.postUpgradeInit(osp);
chalmanImpl.postUpgradeInit(newOsp, randomRoot, condOsp);
}
}
4 changes: 3 additions & 1 deletion test/signatures/ChallengeManager
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
"clearChallenge(uint64)": "56e9df97",
"createChallenge(bytes32,uint8[2],(bytes32[2],uint64[2])[2],uint64,address,address,uint256,uint256)": "14eab5e7",
"currentResponder(uint64)": "23a9ef23",
"getOsp(bytes32)": "3690b011",
"initialize(address,address,address,address)": "f8c8765e",
"isTimedOut(uint64)": "9ede42b9",
"oneStepProveExecution(uint64,(uint256,uint256,bytes32[],uint256),bytes)": "d248d124",
"osp()": "f26a62c6",
"postUpgradeInit(address)": "c474d2c5",
"ospCond(bytes32)": "dc74bf8b",
"postUpgradeInit(address,bytes32,address)": "5038934d",
"resultReceiver()": "3504f1d7",
"sequencerInbox()": "ee35f327",
"timeout(uint64)": "1b45c86a",
Expand Down
1 change: 1 addition & 0 deletions test/storage/ChallengeManager
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
| sequencerInbox | contract ISequencerInbox | 3 | 0 | 20 | src/challenge/ChallengeManager.sol:ChallengeManager |
| bridge | contract IBridge | 4 | 0 | 20 | src/challenge/ChallengeManager.sol:ChallengeManager |
| osp | contract IOneStepProofEntry | 5 | 0 | 20 | src/challenge/ChallengeManager.sol:ChallengeManager |
| ospCond | mapping(bytes32 => contract IOneStepProofEntry) | 6 | 0 | 32 | src/challenge/ChallengeManager.sol:ChallengeManager |

0 comments on commit 2537361

Please sign in to comment.