Commit b13db0ad authored by clabby's avatar clabby

Update local key loading + handle attack on genesis

parent ec2667bb
This diff is collapsed.
...@@ -50,6 +50,7 @@ ...@@ -50,6 +50,7 @@
"faultGameMaxDepth": 30, "faultGameMaxDepth": 30,
"faultGameMaxDuration": 1200, "faultGameMaxDuration": 1200,
"outputBisectionGameGenesisBlock": 0, "outputBisectionGameGenesisBlock": 0,
"outputBisectionGameGenesisOutputRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
"outputBisectionGameSplitDepth": 15, "outputBisectionGameSplitDepth": 15,
"systemConfigStartBlock": 0, "systemConfigStartBlock": 0,
"requiredProtocolVersion": "0x0000000000000000000000000000000000000000000000000000000000000000", "requiredProtocolVersion": "0x0000000000000000000000000000000000000000000000000000000000000000",
......
...@@ -1106,6 +1106,7 @@ contract Deploy is Deployer { ...@@ -1106,6 +1106,7 @@ contract Deploy is Deployer {
_gameType: _gameType, _gameType: _gameType,
_absolutePrestate: _absolutePrestate, _absolutePrestate: _absolutePrestate,
_genesisBlockNumber: cfg.outputBisectionGameGenesisBlock(), _genesisBlockNumber: cfg.outputBisectionGameGenesisBlock(),
_genesisOutputRoot: Hash.wrap(cfg.outputBisectionGameGenesisOutputRoot()),
_maxGameDepth: _maxGameDepth, _maxGameDepth: _maxGameDepth,
_splitDepth: cfg.outputBisectionGameSplitDepth(), _splitDepth: cfg.outputBisectionGameSplitDepth(),
_gameDuration: Duration.wrap(uint64(cfg.faultGameMaxDuration())), _gameDuration: Duration.wrap(uint64(cfg.faultGameMaxDuration())),
......
...@@ -51,6 +51,7 @@ contract DeployConfig is Script { ...@@ -51,6 +51,7 @@ contract DeployConfig is Script {
uint256 public faultGameMaxDepth; uint256 public faultGameMaxDepth;
uint256 public faultGameMaxDuration; uint256 public faultGameMaxDuration;
uint256 public outputBisectionGameGenesisBlock; uint256 public outputBisectionGameGenesisBlock;
bytes32 public outputBisectionGameGenesisOutputRoot;
uint256 public outputBisectionGameSplitDepth; uint256 public outputBisectionGameSplitDepth;
uint256 public systemConfigStartBlock; uint256 public systemConfigStartBlock;
uint256 public requiredProtocolVersion; uint256 public requiredProtocolVersion;
...@@ -107,6 +108,7 @@ contract DeployConfig is Script { ...@@ -107,6 +108,7 @@ contract DeployConfig is Script {
faultGameMaxDepth = stdJson.readUint(_json, "$.faultGameMaxDepth"); faultGameMaxDepth = stdJson.readUint(_json, "$.faultGameMaxDepth");
faultGameMaxDuration = stdJson.readUint(_json, "$.faultGameMaxDuration"); faultGameMaxDuration = stdJson.readUint(_json, "$.faultGameMaxDuration");
outputBisectionGameGenesisBlock = stdJson.readUint(_json, "$.outputBisectionGameGenesisBlock"); outputBisectionGameGenesisBlock = stdJson.readUint(_json, "$.outputBisectionGameGenesisBlock");
outputBisectionGameGenesisOutputRoot = stdJson.readBytes32(_json, "$.outputBisectionGameGenesisOutputRoot");
outputBisectionGameSplitDepth = stdJson.readUint(_json, "$.outputBisectionGameSplitDepth"); outputBisectionGameSplitDepth = stdJson.readUint(_json, "$.outputBisectionGameSplitDepth");
} }
} }
......
...@@ -27,6 +27,8 @@ contract OutputBisectionGame_Init is DisputeGameFactory_Init { ...@@ -27,6 +27,8 @@ contract OutputBisectionGame_Init is DisputeGameFactory_Init {
/// @dev The genesis block number configured for the output bisection portion of the game. /// @dev The genesis block number configured for the output bisection portion of the game.
uint256 internal constant GENESIS_BLOCK_NUMBER = 0; uint256 internal constant GENESIS_BLOCK_NUMBER = 0;
/// @dev The genesis output root commitment
Hash internal constant GENESIS_OUTPUT_ROOT = Hash.wrap(bytes32(0));
/// @dev The implementation of the game. /// @dev The implementation of the game.
OutputBisectionGame internal gameImpl; OutputBisectionGame internal gameImpl;
...@@ -45,21 +47,34 @@ contract OutputBisectionGame_Init is DisputeGameFactory_Init { ...@@ -45,21 +47,34 @@ contract OutputBisectionGame_Init is DisputeGameFactory_Init {
// Set the extra data for the game creation // Set the extra data for the game creation
extraData = abi.encode(L2_BLOCK_NUMBER); extraData = abi.encode(L2_BLOCK_NUMBER);
AlphabetVM _vm = new AlphabetVM(absolutePrestate);
// Deploy an implementation of the fault game // Deploy an implementation of the fault game
gameImpl = new OutputBisectionGame({ gameImpl = new OutputBisectionGame({
_gameType: GAME_TYPE, _gameType: GAME_TYPE,
_absolutePrestate: absolutePrestate, _absolutePrestate: absolutePrestate,
_genesisBlockNumber: GENESIS_BLOCK_NUMBER, _genesisBlockNumber: GENESIS_BLOCK_NUMBER,
_genesisOutputRoot: GENESIS_OUTPUT_ROOT,
_maxGameDepth: 2**3, _maxGameDepth: 2**3,
_splitDepth: 2**2, _splitDepth: 2**2,
_gameDuration: Duration.wrap(7 days), _gameDuration: Duration.wrap(7 days),
_vm: new AlphabetVM(absolutePrestate) _vm: _vm
}); });
// Register the game implementation with the factory. // Register the game implementation with the factory.
factory.setImplementation(GAME_TYPE, gameImpl); factory.setImplementation(GAME_TYPE, gameImpl);
// Create a new game. // Create a new game.
gameProxy = OutputBisectionGame(address(factory.create(GAME_TYPE, rootClaim, extraData))); gameProxy = OutputBisectionGame(address(factory.create(GAME_TYPE, rootClaim, extraData)));
// Check immutables
assertEq(GameType.unwrap(gameProxy.gameType()), GameType.unwrap(GAME_TYPE));
assertEq(Claim.unwrap(gameProxy.ABSOLUTE_PRESTATE()), Claim.unwrap(absolutePrestate));
assertEq(gameProxy.GENESIS_BLOCK_NUMBER(), GENESIS_BLOCK_NUMBER);
assertEq(Hash.unwrap(gameProxy.GENESIS_OUTPUT_ROOT()), Hash.unwrap(GENESIS_OUTPUT_ROOT));
assertEq(gameProxy.MAX_GAME_DEPTH(), 2 ** 3);
assertEq(gameProxy.SPLIT_DEPTH(), 2 ** 2);
assertEq(Duration.unwrap(gameProxy.GAME_DURATION()), 7 days);
assertEq(address(gameProxy.VM()), address(_vm));
// Label the proxy // Label the proxy
vm.label(address(gameProxy), "OutputBisectionGame_Clone"); vm.label(address(gameProxy), "OutputBisectionGame_Clone");
} }
...@@ -459,6 +474,99 @@ contract OutputBisectionGame_Test is OutputBisectionGame_Init { ...@@ -459,6 +474,99 @@ contract OutputBisectionGame_Test is OutputBisectionGame_Init {
gameProxy.addLocalData(_ident, 5, 0); gameProxy.addLocalData(_ident, 5, 0);
} }
/// @dev Tests that local data is loaded into the preimage oracle correctly in the subgame
/// that is disputing the transition from `GENESIS -> GENESIS + 1`
function test_addLocalDataGenesisTransition_static_succeeds() public {
IPreimageOracle oracle = IPreimageOracle(address(gameProxy.VM().oracle()));
// Get a claim below the split depth so that we can add local data for an execution trace subgame.
for (uint256 i; i < 4; i++) {
gameProxy.attack(i, Claim.wrap(bytes32(i)));
}
gameProxy.attack(4, ROOT_CLAIM);
// Expected start/disputed claims
bytes32 startingClaim = Hash.unwrap(GENESIS_OUTPUT_ROOT);
Position startingPos = LibPosition.wrap(0, 0);
bytes32 disputedClaim = bytes32(uint256(3));
Position disputedPos = LibPosition.wrap(4, 0);
// Expected local data
bytes32[5] memory data = [
Hash.unwrap(gameProxy.settlementHead()),
startingClaim,
disputedClaim,
bytes32(0),
bytes32(block.chainid << 0xC0)
];
for (uint256 i = 1; i <= 5; i++) {
uint256 expectedLen = i > 3 ? 8 : 32;
bytes32 key = _getKey(i, keccak256(abi.encode(disputedClaim, disputedPos)));
gameProxy.addLocalData(i, 5, 0);
(bytes32 dat, uint256 datLen) = oracle.readPreimage(key, 0);
assertEq(dat >> 0xC0, bytes32(expectedLen));
// Account for the length prefix if i > 3 (the data stored
// at identifiers i <= 3 are 32 bytes long, so the expected
// length is already correct. If i > 3, the data is only 8
// bytes long, so the length prefix + the data is 16 bytes
// total.)
assertEq(datLen, expectedLen + (i > 3 ? 8 : 0));
gameProxy.addLocalData(i, 5, 8);
(dat, datLen) = oracle.readPreimage(key, 8);
assertEq(dat, data[i - 1]);
assertEq(datLen, expectedLen);
}
}
/// @dev Tests that local data is loaded into the preimage oracle correctly.
function test_addLocalDataMiddle_static_succeeds() public {
IPreimageOracle oracle = IPreimageOracle(address(gameProxy.VM().oracle()));
// Get a claim below the split depth so that we can add local data for an execution trace subgame.
for (uint256 i; i < 4; i++) {
gameProxy.attack(i, Claim.wrap(bytes32(i)));
}
gameProxy.defend(4, ROOT_CLAIM);
// Expected start/disputed claims
bytes32 startingClaim = bytes32(uint256(3));
Position startingPos = LibPosition.wrap(4, 0);
bytes32 disputedClaim = bytes32(uint256(2));
Position disputedPos = LibPosition.wrap(3, 0);
// Expected local data
bytes32[5] memory data = [
Hash.unwrap(gameProxy.settlementHead()),
startingClaim,
disputedClaim,
bytes32(uint256(1) << 0xC0),
bytes32(block.chainid << 0xC0)
];
for (uint256 i = 1; i <= 5; i++) {
uint256 expectedLen = i > 3 ? 8 : 32;
bytes32 key = _getKey(i, keccak256(abi.encode(startingClaim, startingPos, disputedClaim, disputedPos)));
gameProxy.addLocalData(i, 5, 0);
(bytes32 dat, uint256 datLen) = oracle.readPreimage(key, 0);
assertEq(dat >> 0xC0, bytes32(expectedLen));
// Account for the length prefix if i > 3 (the data stored
// at identifiers i <= 3 are 32 bytes long, so the expected
// length is already correct. If i > 3, the data is only 8
// bytes long, so the length prefix + the data is 16 bytes
// total.)
assertEq(datLen, expectedLen + (i > 3 ? 8 : 0));
gameProxy.addLocalData(i, 5, 8);
(dat, datLen) = oracle.readPreimage(key, 8);
assertEq(dat, data[i - 1]);
assertEq(datLen, expectedLen);
}
}
/// @dev Helper to get the localized key for an identifier in the context of the game proxy. /// @dev Helper to get the localized key for an identifier in the context of the game proxy.
function _getKey(uint256 _ident, bytes32 _localContext) internal view returns (bytes32) { function _getKey(uint256 _ident, bytes32 _localContext) internal view returns (bytes32) {
bytes32 h = keccak256(abi.encode(_ident | (1 << 248), address(gameProxy), _localContext)); bytes32 h = keccak256(abi.encode(_ident | (1 << 248), address(gameProxy), _localContext));
......
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