Commit 9aa079e2 authored by protolambda's avatar protolambda

cannon: run with pre-image oracle server

parent f08f9eb1
......@@ -3,6 +3,8 @@ package cmd
import (
"fmt"
"os"
"os/exec"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
......@@ -11,6 +13,7 @@ import (
"github.com/urfave/cli/v2"
"cannon/mipsevm"
"github.com/ethereum-optimism/cannon/preimage"
)
var (
......@@ -71,6 +74,97 @@ type Proof struct {
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 {
state, err := loadJSON[mipsevm.State](ctx.Path(RunInputFlag.Name))
if err != nil {
......@@ -87,7 +181,24 @@ func Run(ctx *cli.Context) error {
outLog := &mipsevm.LoggingWriter{Name: "program std-out", 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()
proofAt := ctx.Generic(RunProofAtFlag.Name).(*StepMatcherFlag).Matcher()
......@@ -100,9 +211,21 @@ func Run(ctx *cli.Context) error {
proofFmt := ctx.String(RunProofFmtFlag.Name)
snapshotFmt := ctx.String(RunSnapshotFmtFlag.Name)
step := us.Step
if po.cmd != nil {
step = Guard(po.cmd.ProcessState, step)
}
for !state.Exited {
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) {
break
}
......@@ -115,7 +238,10 @@ func Run(ctx *cli.Context) error {
if proofAt(state) {
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())
proof := &Proof{
Step: step,
......@@ -133,7 +259,10 @@ func Run(ctx *cli.Context) error {
return fmt.Errorf("failed to write proof data: %w", err)
}
} 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) {
insn := state.Memory.GetMemory(state.PC)
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()
startingGas := uint64(30_000_000)
......@@ -155,7 +156,8 @@ func TestHelloEVM(t *testing.T) {
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()
startingGas := uint64(30_000_000)
......@@ -230,7 +232,8 @@ func TestClaimEVM(t *testing.T) {
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()
startingGas := uint64(30_000_000)
......
......@@ -299,7 +299,17 @@ func NewUnicornState(mu uc.Unicorn, state *State, po PreimageOracle, stdOut, std
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.lastMemAccess = ^uint32(0)
m.lastPreimageOffset = ^uint32(0)
......@@ -352,12 +362,12 @@ func (m *UnicornState) Step(proof bool) (wit *StepWitness) {
// Execute only a single instruction.
// 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.
Count: 1,
})
if err != nil {
panic("failed to run unicorn")
return nil, err
}
if proof {
......@@ -377,7 +387,7 @@ func (m *UnicornState) Step(proof bool) (wit *StepWitness) {
// 1) match the registers post-state
batch, err := m.mu.RegReadBatch(regBatchKeys)
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++ {
m.state.Registers[i] = uint32(batch[i])
......@@ -393,7 +403,7 @@ func (m *UnicornState) Step(proof bool) (wit *StepWitness) {
m.state.PC = oldNextPC
err = m.mu.RegWrite(uc.MIPS_REG_PC, uint64(oldNextPC))
if err != nil {
panic("failed to write PC register")
return nil, fmt.Errorf("failed to write PC register: %w", err)
}
m.state.NextPC = newNextPC
......
......@@ -67,14 +67,14 @@ const (
PClientWFd = 6
)
func ClientHinterChannel() FileChannel {
func ClientHinterChannel() *ReadWritePair {
r := os.NewFile(HClientRFd, "preimage-hint-read")
w := os.NewFile(HClientWFd, "preimage-hint-write")
return NewReadWritePair(r, w)
}
// 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")
w := os.NewFile(PClientWFd, "preimage-oracle-write")
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