Commit 9aa079e2 authored by protolambda's avatar protolambda

cannon: run with pre-image oracle server

parent f08f9eb1
...@@ -3,6 +3,8 @@ package cmd ...@@ -3,6 +3,8 @@ package cmd
import ( import (
"fmt" "fmt"
"os" "os"
"os/exec"
"time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
...@@ -11,6 +13,7 @@ import ( ...@@ -11,6 +13,7 @@ import (
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"cannon/mipsevm" "cannon/mipsevm"
"github.com/ethereum-optimism/cannon/preimage"
) )
var ( var (
...@@ -71,6 +74,97 @@ type Proof struct { ...@@ -71,6 +74,97 @@ type Proof struct {
OracleInput hexutil.Bytes `json:"oracle-input"` OracleInput hexutil.Bytes `json:"oracle-input"`
} }
type rawHint string
func (rh rawHint) Hint() string {
return string(rh)
}
type rawKey [32]byte
func (rk rawKey) PreimageKey() [32]byte {
return rk
}
type ProcessPreimageOracle struct {
pCl *preimage.OracleClient
hCl *preimage.HintWriter
cmd *exec.Cmd
}
func NewProcessPreimageOracle(name string, args []string) *ProcessPreimageOracle {
if name == "" {
return &ProcessPreimageOracle{}
}
pCh := preimage.ClientPreimageChannel()
hCh := preimage.ClientHinterChannel()
cmd := exec.Command(name, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.ExtraFiles = []*os.File{
hCh.Reader(),
hCh.Writer(),
pCh.Reader(),
pCh.Writer(),
}
out := &ProcessPreimageOracle{
pCl: preimage.NewOracleClient(pCh),
hCl: preimage.NewHintWriter(hCh),
cmd: cmd,
}
return out
}
func (p *ProcessPreimageOracle) Hint(v []byte) {
if p.hCl == nil { // no hint processor
return
}
p.hCl.Hint(rawHint(v))
}
func (p *ProcessPreimageOracle) GetPreimage(k [32]byte) []byte {
if p.pCl == nil {
panic("no pre-image retriever available")
}
return p.pCl.Get(rawKey(k))
}
func (p *ProcessPreimageOracle) Start() error {
if p.cmd == nil {
return nil
}
return p.cmd.Start()
}
func (p *ProcessPreimageOracle) Close() error {
if p.cmd == nil {
return nil
}
_ = p.cmd.Process.Signal(os.Interrupt)
p.cmd.WaitDelay = time.Second * 10
return p.cmd.Wait()
}
type StepFn func(proof bool) (*mipsevm.StepWitness, error)
func Guard(proc *os.ProcessState, fn StepFn) StepFn {
return func(proof bool) (*mipsevm.StepWitness, error) {
wit, err := fn(proof)
if err != nil {
if proc.Exited() {
return nil, fmt.Errorf("pre-image server exited with code %d, resulting in err %v", proc.ExitCode(), err)
} else {
return nil, err
}
}
return wit, nil
}
}
var _ mipsevm.PreimageOracle = (*ProcessPreimageOracle)(nil)
func Run(ctx *cli.Context) error { func Run(ctx *cli.Context) error {
state, err := loadJSON[mipsevm.State](ctx.Path(RunInputFlag.Name)) state, err := loadJSON[mipsevm.State](ctx.Path(RunInputFlag.Name))
if err != nil { if err != nil {
...@@ -87,7 +181,24 @@ func Run(ctx *cli.Context) error { ...@@ -87,7 +181,24 @@ func Run(ctx *cli.Context) error {
outLog := &mipsevm.LoggingWriter{Name: "program std-out", Log: l} outLog := &mipsevm.LoggingWriter{Name: "program std-out", Log: l}
errLog := &mipsevm.LoggingWriter{Name: "program std-err", Log: l} errLog := &mipsevm.LoggingWriter{Name: "program std-err", Log: l}
var po mipsevm.PreimageOracle // TODO need to set this up // split CLI args after first '--'
args := ctx.Args().Slice()
for i, arg := range args {
if arg == "--" {
args = args[i:]
break
}
}
po := NewProcessPreimageOracle(args[0], args[1:])
if err := po.Start(); err != nil {
return fmt.Errorf("failed to start pre-image oracle server: %w", err)
}
defer func() {
if err := po.Close(); err != nil {
l.Error("failed to close pre-image server", "err", err)
}
}()
stopAt := ctx.Generic(RunStopAtFlag.Name).(*StepMatcherFlag).Matcher() stopAt := ctx.Generic(RunStopAtFlag.Name).(*StepMatcherFlag).Matcher()
proofAt := ctx.Generic(RunProofAtFlag.Name).(*StepMatcherFlag).Matcher() proofAt := ctx.Generic(RunProofAtFlag.Name).(*StepMatcherFlag).Matcher()
...@@ -100,9 +211,21 @@ func Run(ctx *cli.Context) error { ...@@ -100,9 +211,21 @@ func Run(ctx *cli.Context) error {
proofFmt := ctx.String(RunProofFmtFlag.Name) proofFmt := ctx.String(RunProofFmtFlag.Name)
snapshotFmt := ctx.String(RunSnapshotFmtFlag.Name) snapshotFmt := ctx.String(RunSnapshotFmtFlag.Name)
step := us.Step
if po.cmd != nil {
step = Guard(po.cmd.ProcessState, step)
}
for !state.Exited { for !state.Exited {
step := state.Step step := state.Step
//if infoAt(state) {
// s := lookupSymbol(state.PC)
// var sy elf.Symbol
// l.Info("", "insn", state.Memory.GetMemory(state.PC), "pc", state.PC, "symbol", sy.Name)
// // print name
//}
if stopAt(state) { if stopAt(state) {
break break
} }
...@@ -115,7 +238,10 @@ func Run(ctx *cli.Context) error { ...@@ -115,7 +238,10 @@ func Run(ctx *cli.Context) error {
if proofAt(state) { if proofAt(state) {
preStateHash := crypto.Keccak256Hash(state.EncodeWitness()) preStateHash := crypto.Keccak256Hash(state.EncodeWitness())
witness := us.Step(true) witness, err := us.Step(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 := crypto.Keccak256Hash(state.EncodeWitness())
proof := &Proof{ proof := &Proof{
Step: step, Step: step,
...@@ -133,7 +259,10 @@ func Run(ctx *cli.Context) error { ...@@ -133,7 +259,10 @@ func Run(ctx *cli.Context) error {
return fmt.Errorf("failed to write proof data: %w", err) return fmt.Errorf("failed to write proof data: %w", err)
} }
} else { } else {
_ = us.Step(false) _, err = us.Step(false)
if err != nil {
return fmt.Errorf("failed at step %d (PC: %08x): %w", step, state.PC, err)
}
} }
} }
......
...@@ -84,7 +84,8 @@ func TestEVM(t *testing.T) { ...@@ -84,7 +84,8 @@ func TestEVM(t *testing.T) {
insn := state.Memory.GetMemory(state.PC) insn := state.Memory.GetMemory(state.PC)
t.Logf("step: %4d pc: 0x%08x insn: 0x%08x", state.Step, state.PC, insn) t.Logf("step: %4d pc: 0x%08x insn: 0x%08x", state.Step, state.PC, insn)
stepWitness := us.Step(true) stepWitness, err := us.Step(true)
require.NoError(t, err)
input := stepWitness.EncodeStepInput() input := stepWitness.EncodeStepInput()
startingGas := uint64(30_000_000) startingGas := uint64(30_000_000)
...@@ -155,7 +156,8 @@ func TestHelloEVM(t *testing.T) { ...@@ -155,7 +156,8 @@ func TestHelloEVM(t *testing.T) {
t.Logf("step: %4d pc: 0x%08x insn: 0x%08x", state.Step, state.PC, insn) t.Logf("step: %4d pc: 0x%08x insn: 0x%08x", state.Step, state.PC, insn)
} }
stepWitness := us.Step(true) stepWitness, err := us.Step(true)
require.NoError(t, err)
input := stepWitness.EncodeStepInput() input := stepWitness.EncodeStepInput()
startingGas := uint64(30_000_000) startingGas := uint64(30_000_000)
...@@ -230,7 +232,8 @@ func TestClaimEVM(t *testing.T) { ...@@ -230,7 +232,8 @@ func TestClaimEVM(t *testing.T) {
t.Logf("step: %4d pc: 0x%08x insn: 0x%08x", state.Step, state.PC, insn) t.Logf("step: %4d pc: 0x%08x insn: 0x%08x", state.Step, state.PC, insn)
} }
stepWitness := us.Step(true) stepWitness, err := us.Step(true)
require.NoError(t, err)
input := stepWitness.EncodeStepInput() input := stepWitness.EncodeStepInput()
startingGas := uint64(30_000_000) startingGas := uint64(30_000_000)
......
...@@ -299,7 +299,17 @@ func NewUnicornState(mu uc.Unicorn, state *State, po PreimageOracle, stdOut, std ...@@ -299,7 +299,17 @@ func NewUnicornState(mu uc.Unicorn, state *State, po PreimageOracle, stdOut, std
return m, nil return m, nil
} }
func (m *UnicornState) Step(proof bool) (wit *StepWitness) { func (m *UnicornState) Step(proof bool) (wit *StepWitness, err error) {
defer func() { // pre-image oracle or emulator hooks might panic
if a := recover(); a != nil {
if ae, ok := a.(error); ok {
err = ae
} else {
err = fmt.Errorf("panic: %v", a)
}
}
}()
m.memProofEnabled = proof m.memProofEnabled = proof
m.lastMemAccess = ^uint32(0) m.lastMemAccess = ^uint32(0)
m.lastPreimageOffset = ^uint32(0) m.lastPreimageOffset = ^uint32(0)
...@@ -352,12 +362,12 @@ func (m *UnicornState) Step(proof bool) (wit *StepWitness) { ...@@ -352,12 +362,12 @@ func (m *UnicornState) Step(proof bool) (wit *StepWitness) {
// Execute only a single instruction. // Execute only a single instruction.
// The memory and syscall hooks will update the state with any of the dynamic changes. // The memory and syscall hooks will update the state with any of the dynamic changes.
err := m.mu.StartWithOptions(uint64(m.state.PC), uint64(m.state.NextPC), &uc.UcOptions{ err = m.mu.StartWithOptions(uint64(m.state.PC), uint64(m.state.NextPC), &uc.UcOptions{
Timeout: 0, // 0 to disable, value is in ms. Timeout: 0, // 0 to disable, value is in ms.
Count: 1, Count: 1,
}) })
if err != nil { if err != nil {
panic("failed to run unicorn") return nil, err
} }
if proof { if proof {
...@@ -377,7 +387,7 @@ func (m *UnicornState) Step(proof bool) (wit *StepWitness) { ...@@ -377,7 +387,7 @@ func (m *UnicornState) Step(proof bool) (wit *StepWitness) {
// 1) match the registers post-state // 1) match the registers post-state
batch, err := m.mu.RegReadBatch(regBatchKeys) batch, err := m.mu.RegReadBatch(regBatchKeys)
if err != nil { if err != nil {
panic(fmt.Errorf("failed to read register batch: %w", err)) return nil, fmt.Errorf("failed to read register batch: %w", err)
} }
for i := 0; i < 32; i++ { for i := 0; i < 32; i++ {
m.state.Registers[i] = uint32(batch[i]) m.state.Registers[i] = uint32(batch[i])
...@@ -393,7 +403,7 @@ func (m *UnicornState) Step(proof bool) (wit *StepWitness) { ...@@ -393,7 +403,7 @@ func (m *UnicornState) Step(proof bool) (wit *StepWitness) {
m.state.PC = oldNextPC m.state.PC = oldNextPC
err = m.mu.RegWrite(uc.MIPS_REG_PC, uint64(oldNextPC)) err = m.mu.RegWrite(uc.MIPS_REG_PC, uint64(oldNextPC))
if err != nil { if err != nil {
panic("failed to write PC register") return nil, fmt.Errorf("failed to write PC register: %w", err)
} }
m.state.NextPC = newNextPC m.state.NextPC = newNextPC
......
...@@ -67,14 +67,14 @@ const ( ...@@ -67,14 +67,14 @@ const (
PClientWFd = 6 PClientWFd = 6
) )
func ClientHinterChannel() FileChannel { func ClientHinterChannel() *ReadWritePair {
r := os.NewFile(HClientRFd, "preimage-hint-read") r := os.NewFile(HClientRFd, "preimage-hint-read")
w := os.NewFile(HClientWFd, "preimage-hint-write") w := os.NewFile(HClientWFd, "preimage-hint-write")
return NewReadWritePair(r, w) return NewReadWritePair(r, w)
} }
// ClientPreimageChannel returns a FileChannel for the preimage oracle in a detached context // ClientPreimageChannel returns a FileChannel for the preimage oracle in a detached context
func ClientPreimageChannel() FileChannel { func ClientPreimageChannel() *ReadWritePair {
r := os.NewFile(PClientRFd, "preimage-oracle-read") r := os.NewFile(PClientRFd, "preimage-oracle-read")
w := os.NewFile(PClientWFd, "preimage-oracle-write") w := os.NewFile(PClientWFd, "preimage-oracle-write")
return NewReadWritePair(r, w) return NewReadWritePair(r, w)
......
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