Commit b0c8bbf6 authored by clabby's avatar clabby Committed by GitHub

Merge pull request #7629 from ethereum-optimism/cl/ctb/mips-local-context

feat(ctb): Deeper local key contexts
parents b0114bef 2d1dd18e
......@@ -94,9 +94,6 @@ type Proof struct {
OracleKey hexutil.Bytes `json:"oracle-key,omitempty"`
OracleValue hexutil.Bytes `json:"oracle-value,omitempty"`
OracleOffset uint32 `json:"oracle-offset,omitempty"`
StepInput hexutil.Bytes `json:"step-input"`
OracleInput hexutil.Bytes `json:"oracle-input"`
}
type rawHint string
......@@ -348,14 +345,8 @@ func Run(ctx *cli.Context) error {
Post: postStateHash,
StateData: witness.State,
ProofData: witness.MemProof,
StepInput: witness.EncodeStepInput(),
}
if witness.HasPreimage() {
inp, err := witness.EncodePreimageOracleInput()
if err != nil {
return fmt.Errorf("failed to encode pre-image oracle input: %w", err)
}
proof.OracleInput = inp
proof.OracleKey = witness.PreimageKey[:]
proof.OracleValue = witness.PreimageValue
proof.OracleOffset = witness.PreimageOffset
......
......@@ -16,7 +16,6 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
......@@ -24,11 +23,26 @@ import (
)
var (
StepBytes4 = crypto.Keccak256([]byte("step(bytes,bytes)"))[:4]
LoadKeccak256PreimagePartBytes4 = crypto.Keccak256([]byte("loadKeccak256PreimagePart(uint256,bytes)"))[:4]
LoadLocalDataBytes4 = crypto.Keccak256([]byte("loadLocalData(uint256,bytes32,uint256,uint256)"))[:4]
StepBytes4 []byte
LoadKeccak256PreimagePartBytes4 []byte
LoadLocalDataBytes4 []byte
)
func init() {
mipsAbi, err := bindings.MIPSMetaData.GetAbi()
if err != nil {
panic(fmt.Errorf("failed to load MIPS ABI: %w", err))
}
StepBytes4 = mipsAbi.Methods["step"].ID[:4]
preimageAbi, err := bindings.PreimageOracleMetaData.GetAbi()
if err != nil {
panic(fmt.Errorf("failed to load pre-image oracle ABI: %w", err))
}
LoadKeccak256PreimagePartBytes4 = preimageAbi.Methods["loadKeccak256PreimagePart"].ID[:4]
LoadLocalDataBytes4 = preimageAbi.Methods["loadLocalData"].ID[:4]
}
// LoadContracts loads the Cannon contracts, from op-bindings package
func LoadContracts() (*Contracts, error) {
var mips, oracle Contract
......
......@@ -76,13 +76,13 @@ func (m *MIPSEVM) Step(t *testing.T, stepWitness *StepWitness) []byte {
if stepWitness.HasPreimage() {
t.Logf("reading preimage key %x at offset %d", stepWitness.PreimageKey, stepWitness.PreimageOffset)
poInput, err := stepWitness.EncodePreimageOracleInput()
poInput, err := stepWitness.EncodePreimageOracleInput(0)
require.NoError(t, err, "encode preimage oracle input")
_, leftOverGas, err := m.env.Call(vm.AccountRef(sender), m.addrs.Oracle, poInput, startingGas, big.NewInt(0))
require.NoErrorf(t, err, "evm should not fail, took %d gas", startingGas-leftOverGas)
}
input := stepWitness.EncodeStepInput()
input := stepWitness.EncodeStepInput(0)
ret, leftOverGas, err := m.env.Call(vm.AccountRef(sender), m.addrs.MIPS, input, startingGas, big.NewInt(0))
require.NoError(t, err, "evm should not fail")
require.Len(t, ret, 32, "expecting 32-byte state hash")
......@@ -241,7 +241,7 @@ func TestEVMFault(t *testing.T) {
State: initialState.EncodeWitness(),
MemProof: insnProof[:],
}
input := stepWitness.EncodeStepInput()
input := stepWitness.EncodeStepInput(0)
startingGas := uint64(30_000_000)
_, _, err := env.Call(vm.AccountRef(sender), addrs.MIPS, input, startingGas, big.NewInt(0))
......
......@@ -8,6 +8,8 @@ import (
preimage "github.com/ethereum-optimism/optimism/op-preimage"
)
type LocalContext uint64
type StepWitness struct {
// encoded state witness
State []byte
......@@ -25,7 +27,13 @@ func uint32ToBytes32(v uint32) []byte {
return out[:]
}
func (wit *StepWitness) EncodeStepInput() []byte {
func uint64ToBytes32(v uint64) []byte {
var out [32]byte
binary.BigEndian.PutUint64(out[32-8:], v)
return out[:]
}
func (wit *StepWitness) EncodeStepInput(localContext LocalContext) []byte {
abiStateLen := len(wit.State)
if abiStateLen%32 != 0 {
abiStateLen += 32 - (abiStateLen % 32)
......@@ -36,8 +44,9 @@ func (wit *StepWitness) EncodeStepInput() []byte {
var input []byte
input = append(input, StepBytes4...)
input = append(input, uint32ToBytes32(32*2)...) // state data offset in bytes
input = append(input, uint32ToBytes32(32*2+32+uint32(len(abiState)))...) // proof data offset in bytes
input = append(input, uint32ToBytes32(32*3)...) // state data offset in bytes
input = append(input, uint32ToBytes32(32*3+32+uint32(len(abiState)))...) // proof data offset in bytes
input = append(input, uint64ToBytes32(uint64(localContext))...) // local context in bytes
input = append(input, uint32ToBytes32(uint32(len(wit.State)))...) // state data length in bytes
input = append(input, abiState[:]...)
......@@ -50,7 +59,7 @@ func (wit *StepWitness) HasPreimage() bool {
return wit.PreimageKey != ([32]byte{})
}
func (wit *StepWitness) EncodePreimageOracleInput() ([]byte, error) {
func (wit *StepWitness) EncodePreimageOracleInput(localContext LocalContext) ([]byte, error) {
if wit.PreimageKey == ([32]byte{}) {
return nil, errors.New("cannot encode pre-image oracle input, witness has no pre-image to proof")
}
......@@ -63,6 +72,7 @@ func (wit *StepWitness) EncodePreimageOracleInput() ([]byte, error) {
var input []byte
input = append(input, LoadLocalDataBytes4...)
input = append(input, wit.PreimageKey[:]...)
input = append(input, uint64ToBytes32(uint64(localContext))...) // local context in bytes
preimagePart := wit.PreimageValue[8:]
var tmp [32]byte
......
This diff is collapsed.
......@@ -13,7 +13,7 @@ const AlphabetVMStorageLayoutJSON = "{\"storage\":[{\"astId\":1000,\"contract\":
var AlphabetVMStorageLayout = new(solc.StorageLayout)
var AlphabetVMDeployedBin = "0x608060405234801561001057600080fd5b50600436106100365760003560e01c80637dc0d1d01461003b578063f8e0cb9614610085575b600080fd5b60005461005b9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b610098610093366004610212565b6100a6565b60405190815260200161007c565b600080600060087f0000000000000000000000000000000000000000000000000000000000000000901b600888886040516100e292919061027e565b6040518091039020901b0361010857600091506101018688018861028e565b9050610127565b610114868801886102a7565b909250905081610123816102f8565b9250505b81610133826001610330565b604080516020810193909352820152606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101207effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f010000000000000000000000000000000000000000000000000000000000000017979650505050505050565b60008083601f8401126101db57600080fd5b50813567ffffffffffffffff8111156101f357600080fd5b60208301915083602082850101111561020b57600080fd5b9250929050565b6000806000806040858703121561022857600080fd5b843567ffffffffffffffff8082111561024057600080fd5b61024c888389016101c9565b9096509450602087013591508082111561026557600080fd5b50610272878288016101c9565b95989497509550505050565b8183823760009101908152919050565b6000602082840312156102a057600080fd5b5035919050565b600080604083850312156102ba57600080fd5b50508035926020909101359150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610329576103296102c9565b5060010190565b60008219821115610343576103436102c9565b50019056fea164736f6c634300080f000a"
var AlphabetVMDeployedBin = "0x608060405234801561001057600080fd5b50600436106100365760003560e01c80637dc0d1d01461003b578063836e7b3214610085575b600080fd5b60005461005b9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b610098610093366004610213565b6100a6565b60405190815260200161007c565b600080600060087f0000000000000000000000000000000000000000000000000000000000000000901b600889896040516100e2929190610287565b6040518091039020901b03610108576000915061010187890189610297565b9050610127565b610114878901896102b0565b90925090508161012381610301565b9250505b81610133826001610339565b604080516020810193909352820152606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101207effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01000000000000000000000000000000000000000000000000000000000000001798975050505050505050565b60008083601f8401126101dc57600080fd5b50813567ffffffffffffffff8111156101f457600080fd5b60208301915083602082850101111561020c57600080fd5b9250929050565b60008060008060006060868803121561022b57600080fd5b853567ffffffffffffffff8082111561024357600080fd5b61024f89838a016101ca565b9097509550602088013591508082111561026857600080fd5b50610275888289016101ca565b96999598509660400135949350505050565b8183823760009101908152919050565b6000602082840312156102a957600080fd5b5035919050565b600080604083850312156102c357600080fd5b50508035926020909101359150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610332576103326102d2565b5060010190565b6000821982111561034c5761034c6102d2565b50019056fea164736f6c634300080f000a"
func init() {
if err := json.Unmarshal([]byte(AlphabetVMStorageLayoutJSON), AlphabetVMStorageLayout); err != nil {
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -118,6 +118,7 @@ func (u *cannonUpdater) BuildLocalOracleData(data *types.PreimageOracleData) ([]
return u.fdgAbi.Pack(
"addLocalData",
data.GetIdent(),
big.NewInt(int64(data.LocalContext)),
big.NewInt(int64(data.OracleOffset)),
)
}
......
......@@ -6,6 +6,7 @@ import (
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-service/testlog"
......@@ -104,12 +105,15 @@ func TestCannonUpdater_BuildLocalOracleData(t *testing.T) {
txData, err := updater.BuildLocalOracleData(oracleData)
require.NoError(t, err)
var addLocalDataBytes4 = crypto.Keccak256([]byte("addLocalData(uint256,uint256)"))[:4]
fdgAbi, err := bindings.FaultDisputeGameMetaData.GetAbi()
require.NoError(t, err)
addLocalDataBytes4 := fdgAbi.Methods["addLocalData"].ID[:4]
// Pack the tx data manually.
var expected []byte
expected = append(expected, addLocalDataBytes4...)
expected = append(expected, common.Hex2Bytes("00aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")...)
expected = append(expected, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")...)
expected = append(expected, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000007")...)
require.Equal(t, expected, txData)
......
......@@ -16,6 +16,7 @@ var (
// to load into the onchain oracle.
type PreimageOracleData struct {
IsLocal bool
LocalContext uint64
OracleKey []byte
OracleData []byte
OracleOffset uint32
......
This diff is collapsed.
......@@ -20,7 +20,7 @@
"src/L2/SequencerFeeVault.sol": "0xc2f733c1128d06ad60bf1e1d98c8f684a4825b11875ccdf2376ede33f5aad4e6",
"src/dispute/BlockOracle.sol": "0x7e724b1ee0116dfd744f556e6237af449c2f40c6426d6f1462ae2a47589283bb",
"src/dispute/DisputeGameFactory.sol": "0xfdfa141408d7f8de7e230ff4bef088e30d0e4d569ca743d60d292abdd21ff270",
"src/dispute/FaultDisputeGame.sol": "0xfdf4be4d6ed4bcbf6492c43fdbfd04d0c62ebee11b8fe9ee2e7757bde8f7383d",
"src/dispute/FaultDisputeGame.sol": "0x0766707ab32338a6586c2340ddfbfd4e9023eeb9dfa3ef87e4b404fb0260479f",
"src/legacy/DeployerWhitelist.sol": "0x0a6840074734c9d167321d3299be18ef911a415e4c471fa92af7d6cfaa8336d4",
"src/legacy/L1BlockNumber.sol": "0x20d83a636c5e2067fca8c0ed505b295174e6eddb25960d8705e6b6fea8e77fa6",
"src/legacy/LegacyMessagePasser.sol": "0x80f355c9710af586f58cf6a86d1925e0073d1e504d0b3d814284af1bafe4dece",
......
......@@ -141,8 +141,9 @@ contract MIPS {
}
/// @notice Handles a syscall.
/// @param _localContext The local key context for the preimage oracle.
/// @return out_ The hashed MIPS state.
function handleSyscall() internal returns (bytes32 out_) {
function handleSyscall(uint256 _localContext) internal returns (bytes32 out_) {
unchecked {
// Load state from memory
State memory state;
......@@ -202,7 +203,7 @@ contract MIPS {
bytes32 preimageKey = state.preimageKey;
// If the preimage key is a local key, localize it in the context of the caller.
if (uint8(preimageKey[0]) == 1) {
preimageKey = PreimageKeyLib.localize(preimageKey);
preimageKey = PreimageKeyLib.localize(preimageKey, _localContext);
}
(bytes32 dat, uint256 datLen) = ORACLE.readPreimage(preimageKey, state.preimageOffset);
......@@ -508,8 +509,8 @@ contract MIPS {
function proofOffset(uint8 _proofIndex) internal pure returns (uint256 offset_) {
unchecked {
// 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
offset_ = 388 + (uint256(_proofIndex) * (28 * 32));
// And the leaf value itself needs to be encoded as well. And proof.offset == 420
offset_ = 420 + (uint256(_proofIndex) * (28 * 32));
uint256 s = 0;
assembly {
s := calldatasize()
......@@ -621,7 +622,11 @@ contract MIPS {
/// @notice Executes a single step of the vm.
/// 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 {
State memory state;
......@@ -631,16 +636,16 @@ contract MIPS {
// expected state mem offset check
revert(0, 0)
}
if iszero(eq(mload(0x40), mul(32, 48))) {
if iszero(eq(mload(0x40), shl(5, 48))) {
// expected memory check
revert(0, 0)
}
if iszero(eq(stateData.offset, 100)) {
// 32*3+4=100 expected state data offset
if iszero(eq(_stateData.offset, 132)) {
// 32*4+4=132 expected state data offset
revert(0, 0)
}
if iszero(eq(proof.offset, 388)) {
// 100+32+256=388 expected proof offset
if iszero(eq(_proof.offset, 420)) {
// 132+32+256=420 expected proof offset
revert(0, 0)
}
......@@ -653,7 +658,7 @@ contract MIPS {
}
// Unpack state from calldata into memory
let c := stateData.offset // calldata offset
let c := _stateData.offset // calldata offset
let m := 0x80 // mem offset
c, m := putField(c, m, 32) // memRoot
c, m := putField(c, m, 32) // preimageKey
......@@ -764,7 +769,7 @@ contract MIPS {
// syscall (can read and write)
if (func == 0xC) {
return handleSyscall();
return handleSyscall(_localContext);
}
// lo and hi registers
......
......@@ -7,29 +7,37 @@ library PreimageKeyLib {
/// @notice Generates a context-specific local key for the given local data identifier.
/// @dev See `localize` for a description of the localization operation.
/// @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.
function localizeIdent(uint256 _ident) internal view returns (bytes32 key_) {
function localizeIdent(uint256 _ident, uint256 _localContext) internal view returns (bytes32 key_) {
assembly {
// Set the type byte in the given identifier to `1` (Local). We only care about
// the [1, 32) bytes in this value.
key_ := or(shl(248, 1), and(_ident, not(shl(248, 0xFF))))
}
key_ = localize(key_);
// Localize the key with the given local context.
key_ = localize(key_, _localContext);
}
/// @notice Localizes a given local data key for the caller's context.
/// @dev The localization operation is defined as:
/// localize(k) = H(k .. sender) & ~(0xFF << 248) | (0x01 << 248)
/// localize(k) = H(k .. sender .. local_context) & ~(0xFF << 248) | (0x01 << 248)
/// where H is the Keccak-256 hash function.
/// @param _key The local data key to localize.
/// @param _localContext The local context for the 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 {
// 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.
mstore(0, _key)
mstore(0x20, caller())
mstore(0x40, _localContext)
// 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 {
/// @inheritdoc IPreimageOracle
function loadLocalData(
uint256 _ident,
uint256 _localContext,
bytes32 _word,
uint256 _size,
uint256 _partOffset
......@@ -42,7 +43,7 @@ contract PreimageOracle is IPreimageOracle {
returns (bytes32 key_)
{
// 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.
if (_partOffset > _size + 8 || _size > 32) {
......
......@@ -13,6 +13,8 @@ interface IPreimageOracle {
/// @notice Loads of local data part into the preimage oracle.
/// @param _ident The identifier of the local data.
/// @param _localContext The local key context for the preimage oracle. Optionally, can be set as a constant
/// if the caller only requires one set of local keys.
/// @param _word The local data word.
/// @param _size The number of bytes in `_word` to load.
/// @param _partOffset The offset of the local data part to write to the oracle.
......@@ -32,6 +34,7 @@ interface IPreimageOracle {
/// └────────────┴────────────────────────┘
function loadLocalData(
uint256 _ident,
uint256 _localContext,
bytes32 _word,
uint256 _size,
uint256 _partOffset
......
......@@ -82,8 +82,8 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
bool internal subgameAtRootResolved;
/// @notice Semantic version.
/// @custom:semver 0.0.10
string public constant version = "0.0.10";
/// @custom:semver 0.0.11
string public constant version = "0.0.11";
/// @param _gameType The type ID of the game.
/// @param _absolutePrestate The absolute prestate of the instruction trace.
......@@ -174,7 +174,10 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
// SAFETY: While the `attack` path does not need an extra check for the post
// state's depth in relation to the parent, we don't need another
// branch because (n - n) % 2 == 0.
bool validStep = VM.step(_stateData, _proof) == Claim.unwrap(postState.claim);
// TODO(client-pod#94): Once output bisection is implemented, the local context will no longer
// be constant. We will need to pass it in here based off of the ancestor
// disputed output root's L2 block number.
bool validStep = VM.step(_stateData, _proof, 0) == Claim.unwrap(postState.claim);
bool parentPostAgree = (parentPos.depth() - postState.position.depth()) % 2 == 0;
if (parentPostAgree == validStep) revert ValidStep();
......@@ -278,7 +281,7 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
}
/// @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.
if (status != GameStatus.IN_PROGRESS) revert GameNotInProgress();
......@@ -289,6 +292,8 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
mstore(0x1C, loadLocalDataSelector)
// Store the `_ident` argument
mstore(0x20, _ident)
// Store the `_localContext` argument
mstore(0x40, _l2BlockNumber)
// Store the data to load
let data
switch _ident
......@@ -319,16 +324,16 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
// Revert with `InvalidLocalIdent()`
revert(0x1C, 0x04)
}
mstore(0x40, data)
mstore(0x60, data)
// Store the size of the data to load
// _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
mstore(0x80, _partOffset)
mstore(0xA0, _partOffset)
// Attempt to add the local data to the preimage oracle and bubble up the revert
// 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())
revert(0x00, returndatasize())
}
......
......@@ -29,8 +29,16 @@ interface IBigStepper {
/// hash.
/// @param _stateData The preimage of the prestate hash.
/// @param _proof A proof for the inclusion of the prestate's memory in the merkle tree.
/// @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.
/// @return postState_ The poststate hash after the instruction step.
function step(bytes calldata _stateData, bytes calldata _proof) external returns (bytes32 postState_);
function step(
bytes calldata _stateData,
bytes calldata _proof,
uint256 _localContext
)
external
returns (bytes32 postState_);
/// @notice Returns the preimage oracle used by the stepper.
function oracle() external view returns (IPreimageOracle oracle_);
......
......@@ -70,8 +70,10 @@ interface IFaultDisputeGame is IDisputeGame {
/// @notice Posts the requested local data to the VM's `PreimageOralce`.
/// @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.
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.
/// @dev This function must be called bottom-up in the DAG
......
......@@ -483,12 +483,12 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init {
}
/// @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.
if (_ident <= 5) _ident = 0;
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.
......@@ -508,8 +508,8 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init {
for (uint256 i = 1; i <= 5; i++) {
uint256 expectedLen = i > 3 ? 8 : 32;
gameProxy.addLocalData(i, 0);
bytes32 key = _getKey(i);
gameProxy.addLocalData(i, 0, 0);
bytes32 key = _getKey(i, 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
......@@ -519,8 +519,8 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init {
// total.)
assertEq(datLen, expectedLen + (i > 3 ? 8 : 0));
gameProxy.addLocalData(i, 8);
key = _getKey(i);
gameProxy.addLocalData(i, 0, 8);
key = _getKey(i, 0);
(dat, datLen) = oracle.readPreimage(key, 8);
assertEq(dat, data[i - 1]);
assertEq(datLen, expectedLen);
......@@ -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.
function _getKey(uint256 _ident) internal view returns (bytes32) {
bytes32 h = keccak256(abi.encode(_ident | (1 << 248), address(gameProxy)));
function _getKey(uint256 _ident, uint256 _localContext) internal view returns (bytes32) {
bytes32 h = keccak256(abi.encode(_ident | (1 << 248), address(gameProxy), _localContext));
return bytes32((uint256(h) & ~uint256(0xFF << 248)) | (1 << 248));
}
......@@ -1099,7 +1099,7 @@ contract AlphabetVM is IBigStepper {
}
/// @inheritdoc IBigStepper
function step(bytes calldata _stateData, bytes calldata) external view returns (bytes32 postState_) {
function step(bytes calldata _stateData, bytes calldata, uint256) external view returns (bytes32 postState_) {
uint256 traceIndex;
uint256 claim;
if ((keccak256(_stateData) << 8) == (Claim.unwrap(ABSOLUTE_PRESTATE) << 8)) {
......
This diff is collapsed.
......@@ -32,7 +32,7 @@ contract PreimageOracle_Test is Test {
uint8 partOffset = 0;
// 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
bool ok = oracle.preimagePartOk(contextKey, partOffset);
......@@ -47,9 +47,39 @@ contract PreimageOracle_Test is Test {
assertEq(length, size);
}
/// @notice Tests that multiple local key contexts can be used by the same address for the
/// same local data identifier.
function test_loadLocalData_multipleContexts_succeeds() public {
uint256 ident = 1;
uint8 size = 4;
uint8 partOffset = 0;
// Form the words we'll be storing
bytes32[2] memory words = [bytes32(uint256(0xdeadbeef) << 224), bytes32(uint256(0xbeefbabe) << 224)];
for (uint256 i; i < words.length; i++) {
// Load the local data into the preimage oracle under the test contract's context
// and the given local context.
bytes32 contextKey = oracle.loadLocalData(ident, i, words[i], size, partOffset);
// Validate that the pre-image part is set
bool ok = oracle.preimagePartOk(contextKey, partOffset);
assertTrue(ok);
// Validate the local data part
bytes32 expectedPart = bytes32(uint256(words[i] >> 64) | uint256(size) << 192);
assertEq(oracle.preimageParts(contextKey, partOffset), expectedPart);
// Validate the local data length
uint256 length = oracle.preimageLengths(contextKey);
assertEq(length, size);
}
}
/// @notice Tests that context-specific data [0, 32] bytes in length can be loaded correctly.
function testFuzz_loadLocalData_varyingLength_succeeds(
uint256 ident,
uint256 localContext,
bytes32 word,
uint256 size,
uint256 partOffset
......@@ -62,7 +92,7 @@ contract PreimageOracle_Test is Test {
partOffset = bound(partOffset, 0, size + 8);
// 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
bool ok = oracle.preimagePartOk(contextKey, partOffset);
......@@ -112,7 +142,7 @@ contract PreimageOracle_Test is Test {
uint256 offset = preimage.length + 9;
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.
......
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