Skip to content

Commit

Permalink
Merge pull request ethereum-optimism#6913 from ethereum-optimism/fpvm…
Browse files Browse the repository at this point in the history
…-status

cannon,op-challenger,dispute-game: VM Status prefix byte in claim hash
  • Loading branch information
OptimismBot authored Sep 7, 2023
2 parents 302500e + 7feb2e1 commit db94109
Show file tree
Hide file tree
Showing 32 changed files with 454 additions and 167 deletions.
11 changes: 8 additions & 3 deletions cannon/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli/v2"

Expand Down Expand Up @@ -331,12 +330,18 @@ func Run(ctx *cli.Context) error {
}

if proofAt(state) {
preStateHash := crypto.Keccak256Hash(state.EncodeWitness())
preStateHash, err := state.EncodeWitness().StateHash()
if err != nil {
return fmt.Errorf("failed to hash prestate witness: %w", err)
}
witness, err := stepFn(true)
if err != nil {
return fmt.Errorf("failed at proof-gen step %d (PC: %08x): %w", step, state.PC, err)
}
postStateHash := crypto.Keccak256Hash(state.EncodeWitness())
postStateHash, err := state.EncodeWitness().StateHash()
if err != nil {
return fmt.Errorf("failed to hash poststate witness: %w", err)
}
proof := &Proof{
Step: step,
Pre: preStateHash,
Expand Down
6 changes: 4 additions & 2 deletions cannon/cmd/witness.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"os"

"github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/urfave/cli/v2"
)

Expand All @@ -31,7 +30,10 @@ func Witness(ctx *cli.Context) error {
return fmt.Errorf("invalid input state (%v): %w", input, err)
}
witness := state.EncodeWitness()
h := crypto.Keccak256Hash(witness)
h, err := witness.StateHash()
if err != nil {
return fmt.Errorf("failed to compute witness hash: %w", err)
}
if output != "" {
if err := os.WriteFile(output, witness, 0755); err != nil {
return fmt.Errorf("writing output to %v: %w", output, err)
Expand Down
6 changes: 4 additions & 2 deletions cannon/mipsevm/evm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/stretchr/testify/require"

Expand Down Expand Up @@ -92,7 +91,10 @@ func (m *MIPSEVM) Step(t *testing.T, stepWitness *StepWitness) []byte {
logs := m.evmState.Logs()
require.Equal(t, 1, len(logs), "expecting a log with post-state")
evmPost := logs[0].Data
require.Equal(t, crypto.Keccak256Hash(evmPost), postHash, "logged state must be accurate")

stateHash, err := StateWitness(evmPost).StateHash()
require.NoError(t, err, "state hash could not be computed")
require.Equal(t, stateHash, postHash, "logged state must be accurate")

m.env.StateDB.RevertToSnapshot(snap)
t.Logf("EVM step took %d gas, and returned stateHash %s", startingGas-leftOverGas, postHash)
Expand Down
49 changes: 48 additions & 1 deletion cannon/mipsevm/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@ package mipsevm

import (
"encoding/binary"
"fmt"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
)

// StateWitnessSize is the size of the state witness encoding in bytes.
var StateWitnessSize = 226

type State struct {
Memory *Memory `json:"memory"`

Expand Down Expand Up @@ -37,7 +42,11 @@ type State struct {
LastHint hexutil.Bytes `json:"lastHint,omitempty"`
}

func (s *State) EncodeWitness() []byte {
func (s *State) VMStatus() uint8 {
return vmStatus(s.Exited, s.ExitCode)
}

func (s *State) EncodeWitness() StateWitness {
out := make([]byte, 0)
memRoot := s.Memory.MerkleRoot()
out = append(out, memRoot[:]...)
Expand All @@ -60,3 +69,41 @@ func (s *State) EncodeWitness() []byte {
}
return out
}

type StateWitness []byte

const (
VMStatusValid = 0
VMStatusInvalid = 1
VMStatusPanic = 2
VMStatusUnfinished = 3
)

func (sw StateWitness) StateHash() (common.Hash, error) {
if len(sw) != 226 {
return common.Hash{}, fmt.Errorf("Invalid witness length. Got %d, expected at least 88", len(sw))
}

hash := crypto.Keccak256Hash(sw)
offset := 32*2 + 4*6
exitCode := sw[offset]
exited := sw[offset+1]
status := vmStatus(exited == 1, exitCode)
hash[0] = status
return hash, nil
}

func vmStatus(exited bool, exitCode uint8) uint8 {
if !exited {
return VMStatusUnfinished
}

switch exitCode {
case 0:
return VMStatusValid
case 1:
return VMStatusInvalid
default:
return VMStatusPanic
}
}
47 changes: 47 additions & 0 deletions cannon/mipsevm/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,53 @@ func TestState(t *testing.T) {
}
}

// Run through all permutations of `exited` / `exitCode` and ensure that the
// correct witness, state hash, and VM Status is produced.
func TestStateHash(t *testing.T) {
cases := []struct {
exited bool
exitCode uint8
}{
{exited: false, exitCode: 0},
{exited: false, exitCode: 1},
{exited: false, exitCode: 2},
{exited: false, exitCode: 3},
{exited: true, exitCode: 0},
{exited: true, exitCode: 1},
{exited: true, exitCode: 2},
{exited: true, exitCode: 3},
}

exitedOffset := 32*2 + 4*6
for _, c := range cases {
state := &State{
Memory: NewMemory(),
Exited: c.exited,
ExitCode: c.exitCode,
}

actualWitness := state.EncodeWitness()
actualStateHash, err := StateWitness(actualWitness).StateHash()
require.NoError(t, err, "Error hashing witness")
require.Equal(t, len(actualWitness), StateWitnessSize, "Incorrect witness size")

expectedWitness := make(StateWitness, 226)
memRoot := state.Memory.MerkleRoot()
copy(expectedWitness[:32], memRoot[:])
expectedWitness[exitedOffset] = c.exitCode
var exited uint8
if c.exited {
exited = 1
}
expectedWitness[exitedOffset+1] = uint8(exited)
require.Equal(t, expectedWitness[:], actualWitness[:], "Incorrect witness")

expectedStateHash := crypto.Keccak256Hash(actualWitness)
expectedStateHash[0] = vmStatus(c.exited, c.exitCode)
require.Equal(t, expectedStateHash, actualStateHash, "Incorrect state hash")
}
}

func TestHello(t *testing.T) {
elfProgram, err := elf.Open("../example/bin/hello.elf")
require.NoError(t, err, "open ELF file")
Expand Down
2 changes: 1 addition & 1 deletion op-bindings/bindings/alphabetvm.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit db94109

Please sign in to comment.