Commit eb055622 authored by clabby's avatar clabby

Begin contextualization of local keys

parent 5f2ad00c
This diff is collapsed.
...@@ -141,8 +141,9 @@ contract MIPS { ...@@ -141,8 +141,9 @@ contract MIPS {
} }
/// @notice Handles a syscall. /// @notice Handles a syscall.
/// @param _localContext The local key context for the preimage oracle.
/// @return out_ The hashed MIPS state. /// @return out_ The hashed MIPS state.
function handleSyscall() internal returns (bytes32 out_) { function handleSyscall(uint256 _localContext) internal returns (bytes32 out_) {
unchecked { unchecked {
// Load state from memory // Load state from memory
State memory state; State memory state;
...@@ -202,7 +203,7 @@ contract MIPS { ...@@ -202,7 +203,7 @@ contract MIPS {
bytes32 preimageKey = state.preimageKey; bytes32 preimageKey = state.preimageKey;
// If the preimage key is a local key, localize it in the context of the caller. // If the preimage key is a local key, localize it in the context of the caller.
if (uint8(preimageKey[0]) == 1) { if (uint8(preimageKey[0]) == 1) {
preimageKey = PreimageKeyLib.localize(preimageKey); preimageKey = PreimageKeyLib.localize(preimageKey, _localContext);
} }
(bytes32 dat, uint256 datLen) = ORACLE.readPreimage(preimageKey, state.preimageOffset); (bytes32 dat, uint256 datLen) = ORACLE.readPreimage(preimageKey, state.preimageOffset);
...@@ -508,8 +509,8 @@ contract MIPS { ...@@ -508,8 +509,8 @@ contract MIPS {
function proofOffset(uint8 _proofIndex) internal pure returns (uint256 offset_) { function proofOffset(uint8 _proofIndex) internal pure returns (uint256 offset_) {
unchecked { unchecked {
// A proof of 32 bit memory, with 32-byte leaf values, is (32-5)=27 bytes32 entries. // A proof of 32 bit memory, with 32-byte leaf values, is (32-5)=27 bytes32 entries.
// And the leaf value itself needs to be encoded as well. And proof.offset == 388 // And the leaf value itself needs to be encoded as well. And proof.offset == 420
offset_ = 388 + (uint256(_proofIndex) * (28 * 32)); offset_ = 420 + (uint256(_proofIndex) * (28 * 32));
uint256 s = 0; uint256 s = 0;
assembly { assembly {
s := calldatasize() s := calldatasize()
...@@ -621,7 +622,11 @@ contract MIPS { ...@@ -621,7 +622,11 @@ contract MIPS {
/// @notice Executes a single step of the vm. /// @notice Executes a single step of the vm.
/// Will revert if any required input state is missing. /// Will revert if any required input state is missing.
function step(bytes calldata stateData, bytes calldata proof) public returns (bytes32) { /// @param _stateData The encoded state witness data.
/// @param _proof The encoded proof data for leaves within the MIPS VM's memory.
/// @param _localContext The local key context for the preimage oracle. Optional, can be set as a constant
/// if the caller only requires one set of local keys.
function step(bytes calldata _stateData, bytes calldata _proof, uint256 _localContext) public returns (bytes32) {
unchecked { unchecked {
State memory state; State memory state;
...@@ -631,16 +636,16 @@ contract MIPS { ...@@ -631,16 +636,16 @@ contract MIPS {
// expected state mem offset check // expected state mem offset check
revert(0, 0) revert(0, 0)
} }
if iszero(eq(mload(0x40), mul(32, 48))) { if iszero(eq(mload(0x40), shl(5, 48))) {
// expected memory check // expected memory check
revert(0, 0) revert(0, 0)
} }
if iszero(eq(stateData.offset, 100)) { if iszero(eq(_stateData.offset, 132)) {
// 32*3+4=100 expected state data offset // 32*4+4=132 expected state data offset
revert(0, 0) revert(0, 0)
} }
if iszero(eq(proof.offset, 388)) { if iszero(eq(_proof.offset, 420)) {
// 100+32+256=388 expected proof offset // 132+32+256=420 expected proof offset
revert(0, 0) revert(0, 0)
} }
...@@ -653,7 +658,7 @@ contract MIPS { ...@@ -653,7 +658,7 @@ contract MIPS {
} }
// Unpack state from calldata into memory // Unpack state from calldata into memory
let c := stateData.offset // calldata offset let c := _stateData.offset // calldata offset
let m := 0x80 // mem offset let m := 0x80 // mem offset
c, m := putField(c, m, 32) // memRoot c, m := putField(c, m, 32) // memRoot
c, m := putField(c, m, 32) // preimageKey c, m := putField(c, m, 32) // preimageKey
...@@ -764,7 +769,7 @@ contract MIPS { ...@@ -764,7 +769,7 @@ contract MIPS {
// syscall (can read and write) // syscall (can read and write)
if (func == 0xC) { if (func == 0xC) {
return handleSyscall(); return handleSyscall(_localContext);
} }
// lo and hi registers // lo and hi registers
......
...@@ -7,14 +7,15 @@ library PreimageKeyLib { ...@@ -7,14 +7,15 @@ library PreimageKeyLib {
/// @notice Generates a context-specific local key for the given local data identifier. /// @notice Generates a context-specific local key for the given local data identifier.
/// @dev See `localize` for a description of the localization operation. /// @dev See `localize` for a description of the localization operation.
/// @param _ident The identifier of the local data. [0, 32) bytes in size. /// @param _ident The identifier of the local data. [0, 32) bytes in size.
/// @param _localContext The local context for the key.
/// @return key_ The context-specific local key. /// @return key_ The context-specific local key.
function localizeIdent(uint256 _ident) internal view returns (bytes32 key_) { function localizeIdent(uint256 _ident, uint256 _localContext) internal view returns (bytes32 key_) {
assembly { assembly {
// Set the type byte in the given identifier to `1` (Local). We only care about // Set the type byte in the given identifier to `1` (Local). We only care about
// the [1, 32) bytes in this value. // the [1, 32) bytes in this value.
key_ := or(shl(248, 1), and(_ident, not(shl(248, 0xFF)))) key_ := or(shl(248, 1), and(_ident, not(shl(248, 0xFF))))
} }
key_ = localize(key_); key_ = localize(key_, _localContext);
} }
/// @notice Localizes a given local data key for the caller's context. /// @notice Localizes a given local data key for the caller's context.
...@@ -22,14 +23,20 @@ library PreimageKeyLib { ...@@ -22,14 +23,20 @@ library PreimageKeyLib {
/// localize(k) = H(k .. sender) & ~(0xFF << 248) | (0x01 << 248) /// localize(k) = H(k .. sender) & ~(0xFF << 248) | (0x01 << 248)
/// where H is the Keccak-256 hash function. /// where H is the Keccak-256 hash function.
/// @param _key The local data key to localize. /// @param _key The local data key to localize.
/// @param _localContext The local context for the key.
/// @return localizedKey_ The localized local data key. /// @return localizedKey_ The localized local data key.
function localize(bytes32 _key) internal view returns (bytes32 localizedKey_) { function localize(bytes32 _key, uint256 _localContext) internal view returns (bytes32 localizedKey_) {
assembly { assembly {
// Grab the current free memory pointer to restore later.
let ptr := mload(0x40)
// Store the local data key and caller next to each other in memory for hashing. // Store the local data key and caller next to each other in memory for hashing.
mstore(0, _key) mstore(0, _key)
mstore(0x20, caller()) mstore(0x20, caller())
mstore(0x40, _localContext)
// Localize the key with the above `localize` operation. // Localize the key with the above `localize` operation.
localizedKey_ := or(and(keccak256(0, 0x40), not(shl(248, 0xFF))), shl(248, 1)) localizedKey_ := or(and(keccak256(0, 0x60), not(shl(248, 0xFF))), shl(248, 1))
// Restore the free memory pointer.
mstore(0x40, ptr)
} }
} }
......
...@@ -34,6 +34,7 @@ contract PreimageOracle is IPreimageOracle { ...@@ -34,6 +34,7 @@ contract PreimageOracle is IPreimageOracle {
/// @inheritdoc IPreimageOracle /// @inheritdoc IPreimageOracle
function loadLocalData( function loadLocalData(
uint256 _ident, uint256 _ident,
uint256 _localContext,
bytes32 _word, bytes32 _word,
uint256 _size, uint256 _size,
uint256 _partOffset uint256 _partOffset
...@@ -42,7 +43,7 @@ contract PreimageOracle is IPreimageOracle { ...@@ -42,7 +43,7 @@ contract PreimageOracle is IPreimageOracle {
returns (bytes32 key_) returns (bytes32 key_)
{ {
// Compute the localized key from the given local identifier. // Compute the localized key from the given local identifier.
key_ = PreimageKeyLib.localizeIdent(_ident); key_ = PreimageKeyLib.localizeIdent(_ident, _localContext);
// Revert if the given part offset is not within bounds. // Revert if the given part offset is not within bounds.
if (_partOffset > _size + 8 || _size > 32) { if (_partOffset > _size + 8 || _size > 32) {
......
...@@ -13,6 +13,8 @@ interface IPreimageOracle { ...@@ -13,6 +13,8 @@ interface IPreimageOracle {
/// @notice Loads of local data part into the preimage oracle. /// @notice Loads of local data part into the preimage oracle.
/// @param _ident The identifier of the local data. /// @param _ident The identifier of the local data.
/// @param _localContext The local key context for the preimage oracle. Optional, can be set as a constant
/// if the caller only requires one set of local keys.
/// @param _word The local data word. /// @param _word The local data word.
/// @param _size The number of bytes in `_word` to load. /// @param _size The number of bytes in `_word` to load.
/// @param _partOffset The offset of the local data part to write to the oracle. /// @param _partOffset The offset of the local data part to write to the oracle.
...@@ -32,6 +34,7 @@ interface IPreimageOracle { ...@@ -32,6 +34,7 @@ interface IPreimageOracle {
/// └────────────┴────────────────────────┘ /// └────────────┴────────────────────────┘
function loadLocalData( function loadLocalData(
uint256 _ident, uint256 _ident,
uint256 _localContext,
bytes32 _word, bytes32 _word,
uint256 _size, uint256 _size,
uint256 _partOffset uint256 _partOffset
......
...@@ -278,7 +278,7 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver { ...@@ -278,7 +278,7 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
} }
/// @inheritdoc IFaultDisputeGame /// @inheritdoc IFaultDisputeGame
function addLocalData(uint256 _ident, uint256 _partOffset) external { function addLocalData(uint256 _ident, uint256 _l2BlockNumber, uint256 _partOffset) external {
// INVARIANT: Local data can only be added if the game is currently in progress. // INVARIANT: Local data can only be added if the game is currently in progress.
if (status != GameStatus.IN_PROGRESS) revert GameNotInProgress(); if (status != GameStatus.IN_PROGRESS) revert GameNotInProgress();
...@@ -289,6 +289,8 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver { ...@@ -289,6 +289,8 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
mstore(0x1C, loadLocalDataSelector) mstore(0x1C, loadLocalDataSelector)
// Store the `_ident` argument // Store the `_ident` argument
mstore(0x20, _ident) mstore(0x20, _ident)
// Store the `_localContext` argument
mstore(0x40, _l2BlockNumber)
// Store the data to load // Store the data to load
let data let data
switch _ident switch _ident
...@@ -319,16 +321,16 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver { ...@@ -319,16 +321,16 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
// Revert with `InvalidLocalIdent()` // Revert with `InvalidLocalIdent()`
revert(0x1C, 0x04) revert(0x1C, 0x04)
} }
mstore(0x40, data) mstore(0x60, data)
// Store the size of the data to load // Store the size of the data to load
// _ident > 3 ? 8 : 32 // _ident > 3 ? 8 : 32
mstore(0x60, shl(sub(0x05, shl(0x01, gt(_ident, 0x03))), 0x01)) mstore(0x80, shl(sub(0x05, shl(0x01, gt(_ident, 0x03))), 0x01))
// Store the part offset of the data // Store the part offset of the data
mstore(0x80, _partOffset) mstore(0xA0, _partOffset)
// Attempt to add the local data to the preimage oracle and bubble up the revert // Attempt to add the local data to the preimage oracle and bubble up the revert
// if it fails. // if it fails.
if iszero(call(gas(), oracle, 0x00, 0x1C, 0x84, 0x00, 0x00)) { if iszero(call(gas(), oracle, 0x00, 0x1C, 0xA4, 0x00, 0x00)) {
returndatacopy(0x00, 0x00, returndatasize()) returndatacopy(0x00, 0x00, returndatasize())
revert(0x00, returndatasize()) revert(0x00, returndatasize())
} }
......
...@@ -70,8 +70,10 @@ interface IFaultDisputeGame is IDisputeGame { ...@@ -70,8 +70,10 @@ interface IFaultDisputeGame is IDisputeGame {
/// @notice Posts the requested local data to the VM's `PreimageOralce`. /// @notice Posts the requested local data to the VM's `PreimageOralce`.
/// @param _ident The local identifier of the data to post. /// @param _ident The local identifier of the data to post.
/// @param _l2BlockNumber The L2 block number being disputed. This serves as the local context for the
/// `PreimageOracle` key.
/// @param _partOffset The offset of the data to post. /// @param _partOffset The offset of the data to post.
function addLocalData(uint256 _ident, uint256 _partOffset) external; function addLocalData(uint256 _ident, uint256 _l2BlockNumber, uint256 _partOffset) external;
/// @notice Resolves the subgame rooted at the given claim index. /// @notice Resolves the subgame rooted at the given claim index.
/// @dev This function must be called bottom-up in the DAG /// @dev This function must be called bottom-up in the DAG
......
...@@ -483,12 +483,12 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init { ...@@ -483,12 +483,12 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init {
} }
/// @dev Tests that adding local data with an out of bounds identifier reverts. /// @dev Tests that adding local data with an out of bounds identifier reverts.
function testFuzz_addLocalData_oob_reverts(uint256 _ident) public { function testFuzz_addLocalData_oob_reverts(uint256 _ident, uint256 _localContext) public {
// [1, 5] are valid local data identifiers. // [1, 5] are valid local data identifiers.
if (_ident <= 5) _ident = 0; if (_ident <= 5) _ident = 0;
vm.expectRevert(InvalidLocalIdent.selector); vm.expectRevert(InvalidLocalIdent.selector);
gameProxy.addLocalData(_ident, 0); gameProxy.addLocalData(_ident, _localContext, 0);
} }
/// @dev Tests that local data is loaded into the preimage oracle correctly. /// @dev Tests that local data is loaded into the preimage oracle correctly.
...@@ -508,8 +508,8 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init { ...@@ -508,8 +508,8 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init {
for (uint256 i = 1; i <= 5; i++) { for (uint256 i = 1; i <= 5; i++) {
uint256 expectedLen = i > 3 ? 8 : 32; uint256 expectedLen = i > 3 ? 8 : 32;
gameProxy.addLocalData(i, 0); gameProxy.addLocalData(i, 0, 0);
bytes32 key = _getKey(i); bytes32 key = _getKey(i, 0);
(bytes32 dat, uint256 datLen) = oracle.readPreimage(key, 0); (bytes32 dat, uint256 datLen) = oracle.readPreimage(key, 0);
assertEq(dat >> 0xC0, bytes32(expectedLen)); assertEq(dat >> 0xC0, bytes32(expectedLen));
// Account for the length prefix if i > 3 (the data stored // Account for the length prefix if i > 3 (the data stored
...@@ -519,8 +519,8 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init { ...@@ -519,8 +519,8 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init {
// total.) // total.)
assertEq(datLen, expectedLen + (i > 3 ? 8 : 0)); assertEq(datLen, expectedLen + (i > 3 ? 8 : 0));
gameProxy.addLocalData(i, 8); gameProxy.addLocalData(i, 0, 8);
key = _getKey(i); key = _getKey(i, 0);
(dat, datLen) = oracle.readPreimage(key, 8); (dat, datLen) = oracle.readPreimage(key, 8);
assertEq(dat, data[i - 1]); assertEq(dat, data[i - 1]);
assertEq(datLen, expectedLen); assertEq(datLen, expectedLen);
...@@ -528,8 +528,8 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init { ...@@ -528,8 +528,8 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init {
} }
/// @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) internal view returns (bytes32) { function _getKey(uint256 _ident, uint256 _localContext) internal view returns (bytes32) {
bytes32 h = keccak256(abi.encode(_ident | (1 << 248), address(gameProxy))); bytes32 h = keccak256(abi.encode(_ident | (1 << 248), address(gameProxy), _localContext));
return bytes32((uint256(h) & ~uint256(0xFF << 248)) | (1 << 248)); return bytes32((uint256(h) & ~uint256(0xFF << 248)) | (1 << 248));
} }
......
This diff is collapsed.
...@@ -32,7 +32,7 @@ contract PreimageOracle_Test is Test { ...@@ -32,7 +32,7 @@ contract PreimageOracle_Test is Test {
uint8 partOffset = 0; uint8 partOffset = 0;
// Load the local data into the preimage oracle under the test contract's context. // Load the local data into the preimage oracle under the test contract's context.
bytes32 contextKey = oracle.loadLocalData(ident, word, size, partOffset); bytes32 contextKey = oracle.loadLocalData(ident, 0, word, size, partOffset);
// Validate that the pre-image part is set // Validate that the pre-image part is set
bool ok = oracle.preimagePartOk(contextKey, partOffset); bool ok = oracle.preimagePartOk(contextKey, partOffset);
...@@ -50,6 +50,7 @@ contract PreimageOracle_Test is Test { ...@@ -50,6 +50,7 @@ contract PreimageOracle_Test is Test {
/// @notice Tests that context-specific data [0, 32] bytes in length can be loaded correctly. /// @notice Tests that context-specific data [0, 32] bytes in length can be loaded correctly.
function testFuzz_loadLocalData_varyingLength_succeeds( function testFuzz_loadLocalData_varyingLength_succeeds(
uint256 ident, uint256 ident,
uint256 localContext,
bytes32 word, bytes32 word,
uint256 size, uint256 size,
uint256 partOffset uint256 partOffset
...@@ -62,7 +63,7 @@ contract PreimageOracle_Test is Test { ...@@ -62,7 +63,7 @@ contract PreimageOracle_Test is Test {
partOffset = bound(partOffset, 0, size + 8); partOffset = bound(partOffset, 0, size + 8);
// Load the local data into the preimage oracle under the test contract's context. // Load the local data into the preimage oracle under the test contract's context.
bytes32 contextKey = oracle.loadLocalData(ident, word, uint8(size), uint8(partOffset)); bytes32 contextKey = oracle.loadLocalData(ident, localContext, word, uint8(size), uint8(partOffset));
// Validate that the first local data part is set // Validate that the first local data part is set
bool ok = oracle.preimagePartOk(contextKey, partOffset); bool ok = oracle.preimagePartOk(contextKey, partOffset);
...@@ -112,7 +113,7 @@ contract PreimageOracle_Test is Test { ...@@ -112,7 +113,7 @@ contract PreimageOracle_Test is Test {
uint256 offset = preimage.length + 9; uint256 offset = preimage.length + 9;
vm.expectRevert(PartOffsetOOB.selector); vm.expectRevert(PartOffsetOOB.selector);
oracle.loadLocalData(1, preimage, 32, offset); oracle.loadLocalData(1, 0, preimage, 32, offset);
} }
/// @notice Tests that a pre-image cannot be set with an out-of-bounds offset. /// @notice Tests that a pre-image cannot be set with an out-of-bounds offset.
......
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