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 { ...@@ -94,9 +94,6 @@ type Proof struct {
OracleKey hexutil.Bytes `json:"oracle-key,omitempty"` OracleKey hexutil.Bytes `json:"oracle-key,omitempty"`
OracleValue hexutil.Bytes `json:"oracle-value,omitempty"` OracleValue hexutil.Bytes `json:"oracle-value,omitempty"`
OracleOffset uint32 `json:"oracle-offset,omitempty"` OracleOffset uint32 `json:"oracle-offset,omitempty"`
StepInput hexutil.Bytes `json:"step-input"`
OracleInput hexutil.Bytes `json:"oracle-input"`
} }
type rawHint string type rawHint string
...@@ -348,14 +345,8 @@ func Run(ctx *cli.Context) error { ...@@ -348,14 +345,8 @@ func Run(ctx *cli.Context) error {
Post: postStateHash, Post: postStateHash,
StateData: witness.State, StateData: witness.State,
ProofData: witness.MemProof, ProofData: witness.MemProof,
StepInput: witness.EncodeStepInput(),
} }
if witness.HasPreimage() { 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.OracleKey = witness.PreimageKey[:]
proof.OracleValue = witness.PreimageValue proof.OracleValue = witness.PreimageValue
proof.OracleOffset = witness.PreimageOffset proof.OracleOffset = witness.PreimageOffset
......
...@@ -16,7 +16,6 @@ import ( ...@@ -16,7 +16,6 @@ import (
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
...@@ -24,11 +23,26 @@ import ( ...@@ -24,11 +23,26 @@ import (
) )
var ( var (
StepBytes4 = crypto.Keccak256([]byte("step(bytes,bytes)"))[:4] StepBytes4 []byte
LoadKeccak256PreimagePartBytes4 = crypto.Keccak256([]byte("loadKeccak256PreimagePart(uint256,bytes)"))[:4] LoadKeccak256PreimagePartBytes4 []byte
LoadLocalDataBytes4 = crypto.Keccak256([]byte("loadLocalData(uint256,bytes32,uint256,uint256)"))[:4] 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 // LoadContracts loads the Cannon contracts, from op-bindings package
func LoadContracts() (*Contracts, error) { func LoadContracts() (*Contracts, error) {
var mips, oracle Contract var mips, oracle Contract
......
...@@ -76,13 +76,13 @@ func (m *MIPSEVM) Step(t *testing.T, stepWitness *StepWitness) []byte { ...@@ -76,13 +76,13 @@ func (m *MIPSEVM) Step(t *testing.T, stepWitness *StepWitness) []byte {
if stepWitness.HasPreimage() { if stepWitness.HasPreimage() {
t.Logf("reading preimage key %x at offset %d", stepWitness.PreimageKey, stepWitness.PreimageOffset) 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") require.NoError(t, err, "encode preimage oracle input")
_, leftOverGas, err := m.env.Call(vm.AccountRef(sender), m.addrs.Oracle, poInput, startingGas, big.NewInt(0)) _, 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) 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)) 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.NoError(t, err, "evm should not fail")
require.Len(t, ret, 32, "expecting 32-byte state hash") require.Len(t, ret, 32, "expecting 32-byte state hash")
...@@ -241,7 +241,7 @@ func TestEVMFault(t *testing.T) { ...@@ -241,7 +241,7 @@ func TestEVMFault(t *testing.T) {
State: initialState.EncodeWitness(), State: initialState.EncodeWitness(),
MemProof: insnProof[:], MemProof: insnProof[:],
} }
input := stepWitness.EncodeStepInput() input := stepWitness.EncodeStepInput(0)
startingGas := uint64(30_000_000) startingGas := uint64(30_000_000)
_, _, err := env.Call(vm.AccountRef(sender), addrs.MIPS, input, startingGas, big.NewInt(0)) _, _, err := env.Call(vm.AccountRef(sender), addrs.MIPS, input, startingGas, big.NewInt(0))
......
...@@ -8,6 +8,8 @@ import ( ...@@ -8,6 +8,8 @@ import (
preimage "github.com/ethereum-optimism/optimism/op-preimage" preimage "github.com/ethereum-optimism/optimism/op-preimage"
) )
type LocalContext uint64
type StepWitness struct { type StepWitness struct {
// encoded state witness // encoded state witness
State []byte State []byte
...@@ -25,7 +27,13 @@ func uint32ToBytes32(v uint32) []byte { ...@@ -25,7 +27,13 @@ func uint32ToBytes32(v uint32) []byte {
return out[:] 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) abiStateLen := len(wit.State)
if abiStateLen%32 != 0 { if abiStateLen%32 != 0 {
abiStateLen += 32 - (abiStateLen % 32) abiStateLen += 32 - (abiStateLen % 32)
...@@ -36,8 +44,9 @@ func (wit *StepWitness) EncodeStepInput() []byte { ...@@ -36,8 +44,9 @@ func (wit *StepWitness) EncodeStepInput() []byte {
var input []byte var input []byte
input = append(input, StepBytes4...) input = append(input, StepBytes4...)
input = append(input, uint32ToBytes32(32*2)...) // state data offset in bytes input = append(input, uint32ToBytes32(32*3)...) // 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+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, uint32ToBytes32(uint32(len(wit.State)))...) // state data length in bytes
input = append(input, abiState[:]...) input = append(input, abiState[:]...)
...@@ -50,7 +59,7 @@ func (wit *StepWitness) HasPreimage() bool { ...@@ -50,7 +59,7 @@ func (wit *StepWitness) HasPreimage() bool {
return wit.PreimageKey != ([32]byte{}) return wit.PreimageKey != ([32]byte{})
} }
func (wit *StepWitness) EncodePreimageOracleInput() ([]byte, error) { func (wit *StepWitness) EncodePreimageOracleInput(localContext LocalContext) ([]byte, error) {
if wit.PreimageKey == ([32]byte{}) { if wit.PreimageKey == ([32]byte{}) {
return nil, errors.New("cannot encode pre-image oracle input, witness has no pre-image to proof") 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) { ...@@ -63,6 +72,7 @@ func (wit *StepWitness) EncodePreimageOracleInput() ([]byte, error) {
var input []byte var input []byte
input = append(input, LoadLocalDataBytes4...) input = append(input, LoadLocalDataBytes4...)
input = append(input, wit.PreimageKey[:]...) input = append(input, wit.PreimageKey[:]...)
input = append(input, uint64ToBytes32(uint64(localContext))...) // local context in bytes
preimagePart := wit.PreimageValue[8:] preimagePart := wit.PreimageValue[8:]
var tmp [32]byte var tmp [32]byte
......
This diff is collapsed.
...@@ -13,7 +13,7 @@ const AlphabetVMStorageLayoutJSON = "{\"storage\":[{\"astId\":1000,\"contract\": ...@@ -13,7 +13,7 @@ const AlphabetVMStorageLayoutJSON = "{\"storage\":[{\"astId\":1000,\"contract\":
var AlphabetVMStorageLayout = new(solc.StorageLayout) var AlphabetVMStorageLayout = new(solc.StorageLayout)
var AlphabetVMDeployedBin = "0x608060405234801561001057600080fd5b50600436106100365760003560e01c80637dc0d1d01461003b578063f8e0cb9614610085575b600080fd5b60005461005b9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b610098610093366004610212565b6100a6565b60405190815260200161007c565b600080600060087f0000000000000000000000000000000000000000000000000000000000000000901b600888886040516100e292919061027e565b6040518091039020901b0361010857600091506101018688018861028e565b9050610127565b610114868801886102a7565b909250905081610123816102f8565b9250505b81610133826001610330565b604080516020810193909352820152606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101207effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f010000000000000000000000000000000000000000000000000000000000000017979650505050505050565b60008083601f8401126101db57600080fd5b50813567ffffffffffffffff8111156101f357600080fd5b60208301915083602082850101111561020b57600080fd5b9250929050565b6000806000806040858703121561022857600080fd5b843567ffffffffffffffff8082111561024057600080fd5b61024c888389016101c9565b9096509450602087013591508082111561026557600080fd5b50610272878288016101c9565b95989497509550505050565b8183823760009101908152919050565b6000602082840312156102a057600080fd5b5035919050565b600080604083850312156102ba57600080fd5b50508035926020909101359150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610329576103296102c9565b5060010190565b60008219821115610343576103436102c9565b50019056fea164736f6c634300080f000a" var AlphabetVMDeployedBin = "0x608060405234801561001057600080fd5b50600436106100365760003560e01c80637dc0d1d01461003b578063836e7b3214610085575b600080fd5b60005461005b9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b610098610093366004610213565b6100a6565b60405190815260200161007c565b600080600060087f0000000000000000000000000000000000000000000000000000000000000000901b600889896040516100e2929190610287565b6040518091039020901b03610108576000915061010187890189610297565b9050610127565b610114878901896102b0565b90925090508161012381610301565b9250505b81610133826001610339565b604080516020810193909352820152606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101207effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01000000000000000000000000000000000000000000000000000000000000001798975050505050505050565b60008083601f8401126101dc57600080fd5b50813567ffffffffffffffff8111156101f457600080fd5b60208301915083602082850101111561020c57600080fd5b9250929050565b60008060008060006060868803121561022b57600080fd5b853567ffffffffffffffff8082111561024357600080fd5b61024f89838a016101ca565b9097509550602088013591508082111561026857600080fd5b50610275888289016101ca565b96999598509660400135949350505050565b8183823760009101908152919050565b6000602082840312156102a957600080fd5b5035919050565b600080604083850312156102c357600080fd5b50508035926020909101359150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610332576103326102d2565b5060010190565b6000821982111561034c5761034c6102d2565b50019056fea164736f6c634300080f000a"
func init() { func init() {
if err := json.Unmarshal([]byte(AlphabetVMStorageLayoutJSON), AlphabetVMStorageLayout); err != nil { 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) ([] ...@@ -118,6 +118,7 @@ func (u *cannonUpdater) BuildLocalOracleData(data *types.PreimageOracleData) ([]
return u.fdgAbi.Pack( return u.fdgAbi.Pack(
"addLocalData", "addLocalData",
data.GetIdent(), data.GetIdent(),
big.NewInt(int64(data.LocalContext)),
big.NewInt(int64(data.OracleOffset)), big.NewInt(int64(data.OracleOffset)),
) )
} }
......
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"math/big" "math/big"
"testing" "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-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testlog"
...@@ -104,12 +105,15 @@ func TestCannonUpdater_BuildLocalOracleData(t *testing.T) { ...@@ -104,12 +105,15 @@ func TestCannonUpdater_BuildLocalOracleData(t *testing.T) {
txData, err := updater.BuildLocalOracleData(oracleData) txData, err := updater.BuildLocalOracleData(oracleData)
require.NoError(t, err) 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. // Pack the tx data manually.
var expected []byte var expected []byte
expected = append(expected, addLocalDataBytes4...) expected = append(expected, addLocalDataBytes4...)
expected = append(expected, common.Hex2Bytes("00aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")...) expected = append(expected, common.Hex2Bytes("00aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")...)
expected = append(expected, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")...)
expected = append(expected, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000007")...) expected = append(expected, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000007")...)
require.Equal(t, expected, txData) require.Equal(t, expected, txData)
......
...@@ -16,6 +16,7 @@ var ( ...@@ -16,6 +16,7 @@ var (
// to load into the onchain oracle. // to load into the onchain oracle.
type PreimageOracleData struct { type PreimageOracleData struct {
IsLocal bool IsLocal bool
LocalContext uint64
OracleKey []byte OracleKey []byte
OracleData []byte OracleData []byte
OracleOffset uint32 OracleOffset uint32
......
This diff is collapsed.
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
"src/L2/SequencerFeeVault.sol": "0xc2f733c1128d06ad60bf1e1d98c8f684a4825b11875ccdf2376ede33f5aad4e6", "src/L2/SequencerFeeVault.sol": "0xc2f733c1128d06ad60bf1e1d98c8f684a4825b11875ccdf2376ede33f5aad4e6",
"src/dispute/BlockOracle.sol": "0x7e724b1ee0116dfd744f556e6237af449c2f40c6426d6f1462ae2a47589283bb", "src/dispute/BlockOracle.sol": "0x7e724b1ee0116dfd744f556e6237af449c2f40c6426d6f1462ae2a47589283bb",
"src/dispute/DisputeGameFactory.sol": "0xfdfa141408d7f8de7e230ff4bef088e30d0e4d569ca743d60d292abdd21ff270", "src/dispute/DisputeGameFactory.sol": "0xfdfa141408d7f8de7e230ff4bef088e30d0e4d569ca743d60d292abdd21ff270",
"src/dispute/FaultDisputeGame.sol": "0xfdf4be4d6ed4bcbf6492c43fdbfd04d0c62ebee11b8fe9ee2e7757bde8f7383d", "src/dispute/FaultDisputeGame.sol": "0x0766707ab32338a6586c2340ddfbfd4e9023eeb9dfa3ef87e4b404fb0260479f",
"src/legacy/DeployerWhitelist.sol": "0x0a6840074734c9d167321d3299be18ef911a415e4c471fa92af7d6cfaa8336d4", "src/legacy/DeployerWhitelist.sol": "0x0a6840074734c9d167321d3299be18ef911a415e4c471fa92af7d6cfaa8336d4",
"src/legacy/L1BlockNumber.sol": "0x20d83a636c5e2067fca8c0ed505b295174e6eddb25960d8705e6b6fea8e77fa6", "src/legacy/L1BlockNumber.sol": "0x20d83a636c5e2067fca8c0ed505b295174e6eddb25960d8705e6b6fea8e77fa6",
"src/legacy/LegacyMessagePasser.sol": "0x80f355c9710af586f58cf6a86d1925e0073d1e504d0b3d814284af1bafe4dece", "src/legacy/LegacyMessagePasser.sol": "0x80f355c9710af586f58cf6a86d1925e0073d1e504d0b3d814284af1bafe4dece",
......
...@@ -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,29 +7,37 @@ library PreimageKeyLib { ...@@ -7,29 +7,37 @@ 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_); // Localize the key with the given local context.
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.
/// @dev The localization operation is defined as: /// @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. /// 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. Optionally, 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
......
...@@ -82,8 +82,8 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver { ...@@ -82,8 +82,8 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
bool internal subgameAtRootResolved; bool internal subgameAtRootResolved;
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 0.0.10 /// @custom:semver 0.0.11
string public constant version = "0.0.10"; string public constant version = "0.0.11";
/// @param _gameType The type ID of the game. /// @param _gameType The type ID of the game.
/// @param _absolutePrestate The absolute prestate of the instruction trace. /// @param _absolutePrestate The absolute prestate of the instruction trace.
...@@ -174,7 +174,10 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver { ...@@ -174,7 +174,10 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
// SAFETY: While the `attack` path does not need an extra check for the post // 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 // state's depth in relation to the parent, we don't need another
// branch because (n - n) % 2 == 0. // 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; bool parentPostAgree = (parentPos.depth() - postState.position.depth()) % 2 == 0;
if (parentPostAgree == validStep) revert ValidStep(); if (parentPostAgree == validStep) revert ValidStep();
...@@ -278,7 +281,7 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver { ...@@ -278,7 +281,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 +292,8 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver { ...@@ -289,6 +292,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 +324,16 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver { ...@@ -319,16 +324,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())
} }
......
...@@ -29,8 +29,16 @@ interface IBigStepper { ...@@ -29,8 +29,16 @@ interface IBigStepper {
/// hash. /// hash.
/// @param _stateData The preimage of the prestate 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 _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. /// @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. /// @notice Returns the preimage oracle used by the stepper.
function oracle() external view returns (IPreimageOracle oracle_); function oracle() external view returns (IPreimageOracle oracle_);
......
...@@ -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));
} }
...@@ -1099,7 +1099,7 @@ contract AlphabetVM is IBigStepper { ...@@ -1099,7 +1099,7 @@ contract AlphabetVM is IBigStepper {
} }
/// @inheritdoc 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 traceIndex;
uint256 claim; uint256 claim;
if ((keccak256(_stateData) << 8) == (Claim.unwrap(ABSOLUTE_PRESTATE) << 8)) { if ((keccak256(_stateData) << 8) == (Claim.unwrap(ABSOLUTE_PRESTATE) << 8)) {
......
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);
...@@ -47,9 +47,39 @@ contract PreimageOracle_Test is Test { ...@@ -47,9 +47,39 @@ contract PreimageOracle_Test is Test {
assertEq(length, size); 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. /// @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 +92,7 @@ contract PreimageOracle_Test is Test { ...@@ -62,7 +92,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 +142,7 @@ contract PreimageOracle_Test is Test { ...@@ -112,7 +142,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