Commit a81de910 authored by Matthew Slipper's avatar Matthew Slipper Committed by GitHub

Merge pull request #11503 from ethereum-optimism/inphi/audit-fixes

Fault Proof Fixes
parents 2a995793 3a5e07f1
...@@ -153,7 +153,8 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 { ...@@ -153,7 +153,8 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 {
case 0x06: // srlv case 0x06: // srlv
return rt >> (rs & 0x1F) return rt >> (rs & 0x1F)
case 0x07: // srav case 0x07: // srav
return SignExtend(rt>>rs, 32-rs) shamt := rs & 0x1F
return SignExtend(rt>>shamt, 32-shamt)
// functs in range [0x8, 0x1b] are handled specially by other functions // functs in range [0x8, 0x1b] are handled specially by other functions
case 0x08: // jr case 0x08: // jr
return rs return rs
......
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
) )
// Syscall codes // Syscall codes
...@@ -132,7 +133,6 @@ const ( ...@@ -132,7 +133,6 @@ const (
// Other constants // Other constants
const ( const (
SchedQuantum = 100_000 SchedQuantum = 100_000
BrkStart = 0x40000000
) )
func GetSyscallArgs(registers *[32]uint32) (syscallNum, a0, a1, a2, a3 uint32) { func GetSyscallArgs(registers *[32]uint32) (syscallNum, a0, a1, a2, a3 uint32) {
...@@ -158,6 +158,12 @@ func HandleSysMmap(a0, a1, heap uint32) (v0, v1, newHeap uint32) { ...@@ -158,6 +158,12 @@ func HandleSysMmap(a0, a1, heap uint32) (v0, v1, newHeap uint32) {
v0 = heap v0 = heap
//fmt.Printf("mmap heap 0x%x size 0x%x\n", v0, sz) //fmt.Printf("mmap heap 0x%x size 0x%x\n", v0, sz)
newHeap += sz newHeap += sz
// Fail if new heap exceeds memory limit, newHeap overflows around to low memory, or sz overflows
if newHeap > program.HEAP_END || newHeap < heap || sz < a1 {
v0 = SysErrorSignal
v1 = MipsEINVAL
return v0, v1, heap
}
} else { } else {
v0 = a0 v0 = a0
//fmt.Printf("mmap hint 0x%x size 0x%x\n", v0, sz) //fmt.Printf("mmap hint 0x%x size 0x%x\n", v0, sz)
......
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
) )
func (m *InstrumentedState) handleSyscall() error { func (m *InstrumentedState) handleSyscall() error {
...@@ -26,7 +27,7 @@ func (m *InstrumentedState) handleSyscall() error { ...@@ -26,7 +27,7 @@ func (m *InstrumentedState) handleSyscall() error {
v0, v1, newHeap = exec.HandleSysMmap(a0, a1, m.state.Heap) v0, v1, newHeap = exec.HandleSysMmap(a0, a1, m.state.Heap)
m.state.Heap = newHeap m.state.Heap = newHeap
case exec.SysBrk: case exec.SysBrk:
v0 = exec.BrkStart v0 = program.PROGRAM_BREAK
case exec.SysClone: // clone case exec.SysClone: // clone
// a0 = flag bitmask, a1 = stack pointer // a0 = flag bitmask, a1 = stack pointer
if exec.ValidCloneFlags != a0 { if exec.ValidCloneFlags != a0 {
......
...@@ -9,7 +9,11 @@ import ( ...@@ -9,7 +9,11 @@ import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
) )
const HEAP_START = 0x05000000 const (
HEAP_START = 0x05_00_00_00
HEAP_END = 0x60_00_00_00
PROGRAM_BREAK = 0x40_00_00_00
)
type CreateInitialFPVMState[T mipsevm.FPVMState] func(pc, heapStart uint32) T type CreateInitialFPVMState[T mipsevm.FPVMState] func(pc, heapStart uint32) T
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
) )
func (m *InstrumentedState) handleSyscall() error { func (m *InstrumentedState) handleSyscall() error {
...@@ -20,7 +21,7 @@ func (m *InstrumentedState) handleSyscall() error { ...@@ -20,7 +21,7 @@ func (m *InstrumentedState) handleSyscall() error {
v0, v1, newHeap = exec.HandleSysMmap(a0, a1, m.state.Heap) v0, v1, newHeap = exec.HandleSysMmap(a0, a1, m.state.Heap)
m.state.Heap = newHeap m.state.Heap = newHeap
case exec.SysBrk: case exec.SysBrk:
v0 = exec.BrkStart v0 = program.PROGRAM_BREAK
case exec.SysClone: // clone (not supported) case exec.SysClone: // clone (not supported)
v0 = 1 v0 = 1
case exec.SysExitGroup: case exec.SysExitGroup:
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"io" "io"
"os" "os"
"path" "path"
"strings"
"testing" "testing"
"time" "time"
...@@ -18,6 +19,7 @@ import ( ...@@ -18,6 +19,7 @@ import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
) )
...@@ -48,6 +50,7 @@ func TestEVM(t *testing.T) { ...@@ -48,6 +50,7 @@ func TestEVM(t *testing.T) {
oracle := testutil.SelectOracleFixture(t, f.Name()) oracle := testutil.SelectOracleFixture(t, f.Name())
// Short-circuit early for exit_group.bin // Short-circuit early for exit_group.bin
exitGroup := f.Name() == "exit_group.bin" exitGroup := f.Name() == "exit_group.bin"
expectPanic := strings.HasSuffix(f.Name(), "panic.bin")
evm := testutil.NewMIPSEVM(contracts, addrs) evm := testutil.NewMIPSEVM(contracts, addrs)
evm.SetTracer(tracer) evm.SetTracer(tracer)
...@@ -66,6 +69,17 @@ func TestEVM(t *testing.T) { ...@@ -66,6 +69,17 @@ func TestEVM(t *testing.T) {
goState := singlethreaded.NewInstrumentedState(state, oracle, os.Stdout, os.Stderr, nil) goState := singlethreaded.NewInstrumentedState(state, oracle, os.Stdout, os.Stderr, nil)
// Catch panics and check if they are expected
defer func() {
if r := recover(); r != nil {
if expectPanic {
// Success
} else {
t.Errorf("unexpected panic: %v", r)
}
}
}()
for i := 0; i < 1000; i++ { for i := 0; i < 1000; i++ {
curStep := goState.GetState().GetStep() curStep := goState.GetState().GetStep()
if goState.GetState().GetPC() == testutil.EndAddr { if goState.GetState().GetPC() == testutil.EndAddr {
...@@ -90,6 +104,8 @@ func TestEVM(t *testing.T) { ...@@ -90,6 +104,8 @@ func TestEVM(t *testing.T) {
require.NotEqual(t, uint32(testutil.EndAddr), goState.GetState().GetPC(), "must not reach end") require.NotEqual(t, uint32(testutil.EndAddr), goState.GetState().GetPC(), "must not reach end")
require.True(t, goState.GetState().GetExited(), "must set exited state") require.True(t, goState.GetState().GetExited(), "must set exited state")
require.Equal(t, uint8(1), goState.GetState().GetExitCode(), "must exit with 1") require.Equal(t, uint8(1), goState.GetState().GetExitCode(), "must exit with 1")
} else if expectPanic {
require.NotEqual(t, uint32(testutil.EndAddr), goState.GetState().GetPC(), "must not reach end")
} else { } else {
require.Equal(t, uint32(testutil.EndAddr), state.Cpu.PC, "must reach end") require.Equal(t, uint32(testutil.EndAddr), state.Cpu.PC, "must reach end")
// inspect test result // inspect test result
...@@ -193,6 +209,88 @@ func TestEVMSingleStep(t *testing.T) { ...@@ -193,6 +209,88 @@ func TestEVMSingleStep(t *testing.T) {
} }
} }
func TestEVM_MMap(t *testing.T) {
contracts, addrs := testContractsSetup(t)
var tracer *tracing.Hooks
cases := []struct {
name string
heap uint32
address uint32
size uint32
shouldFail bool
expectedHeap uint32
}{
{name: "Increment heap by max value", heap: program.HEAP_START, address: 0, size: ^uint32(0), shouldFail: true},
{name: "Increment heap to 0", heap: program.HEAP_START, address: 0, size: ^uint32(0) - program.HEAP_START + 1, shouldFail: true},
{name: "Increment heap to previous page", heap: program.HEAP_START, address: 0, size: ^uint32(0) - program.HEAP_START - memory.PageSize + 1, shouldFail: true},
{name: "Increment max page size", heap: program.HEAP_START, address: 0, size: ^uint32(0) & ^uint32(memory.PageAddrMask), shouldFail: true},
{name: "Increment max page size from 0", heap: 0, address: 0, size: ^uint32(0) & ^uint32(memory.PageAddrMask), shouldFail: true},
{name: "Increment heap at limit", heap: program.HEAP_END, address: 0, size: 1, shouldFail: true},
{name: "Increment heap to limit", heap: program.HEAP_END - memory.PageSize, address: 0, size: 1, shouldFail: false, expectedHeap: program.HEAP_END},
{name: "Increment heap within limit", heap: program.HEAP_END - 2*memory.PageSize, address: 0, size: 1, shouldFail: false, expectedHeap: program.HEAP_END - memory.PageSize},
{name: "Request specific address", heap: program.HEAP_START, address: 0x50_00_00_00, size: 0, shouldFail: false, expectedHeap: program.HEAP_START},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
state := singlethreaded.CreateEmptyState()
state.Heap = c.heap
state.Memory.SetMemory(state.GetPC(), syscallInsn)
state.Registers = testutil.RandomRegisters(77)
state.Registers[2] = exec.SysMmap
state.Registers[4] = c.address
state.Registers[5] = c.size
step := state.Step
expectedRegisters := state.Registers
expectedHeap := state.Heap
expectedMemoryRoot := state.Memory.MerkleRoot()
if c.shouldFail {
expectedRegisters[2] = exec.SysErrorSignal
expectedRegisters[7] = exec.MipsEINVAL
} else {
expectedHeap = c.expectedHeap
if c.address == 0 {
expectedRegisters[2] = state.Heap
expectedRegisters[7] = 0
} else {
expectedRegisters[2] = c.address
expectedRegisters[7] = 0
}
}
us := singlethreaded.NewInstrumentedState(state, nil, os.Stdout, os.Stderr, nil)
stepWitness, err := us.Step(true)
require.NoError(t, err)
// Check expectations
require.Equal(t, step+1, state.Step)
require.Equal(t, expectedHeap, state.Heap)
require.Equal(t, expectedRegisters, state.Registers)
require.Equal(t, expectedMemoryRoot, state.Memory.MerkleRoot())
require.Equal(t, common.Hash{}, state.PreimageKey)
require.Equal(t, uint32(0), state.PreimageOffset)
require.Equal(t, uint32(4), state.Cpu.PC)
require.Equal(t, uint32(8), state.Cpu.NextPC)
require.Equal(t, uint32(0), state.Cpu.HI)
require.Equal(t, uint32(0), state.Cpu.LO)
require.Equal(t, false, state.Exited)
require.Equal(t, uint8(0), state.ExitCode)
require.Equal(t, hexutil.Bytes(nil), state.LastHint)
evm := testutil.NewMIPSEVM(contracts, addrs)
evm.SetTracer(tracer)
testutil.LogStepFailureAtCleanup(t, evm)
evmPost := evm.Step(t, stepWitness, step, singlethreaded.GetStateHashFn())
goPost, _ := us.GetState().EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
})
}
}
func TestEVMSysWriteHint(t *testing.T) { func TestEVMSysWriteHint(t *testing.T) {
contracts, addrs := testContractsSetup(t) contracts, addrs := testContractsSetup(t)
var tracer *tracing.Hooks var tracer *tracing.Hooks
......
...@@ -14,6 +14,7 @@ import ( ...@@ -14,6 +14,7 @@ import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
preimage "github.com/ethereum-optimism/optimism/op-preimage" preimage "github.com/ethereum-optimism/optimism/op-preimage"
...@@ -45,7 +46,7 @@ func FuzzStateSyscallBrk(f *testing.F) { ...@@ -45,7 +46,7 @@ func FuzzStateSyscallBrk(f *testing.F) {
state.Memory.SetMemory(pc, syscallInsn) state.Memory.SetMemory(pc, syscallInsn)
preStateRoot := state.Memory.MerkleRoot() preStateRoot := state.Memory.MerkleRoot()
expectedRegisters := state.Registers expectedRegisters := state.Registers
expectedRegisters[2] = 0x4000_0000 expectedRegisters[2] = program.PROGRAM_BREAK
goState := singlethreaded.NewInstrumentedState(state, nil, os.Stdout, os.Stderr, nil) goState := singlethreaded.NewInstrumentedState(state, nil, os.Stdout, os.Stderr, nil)
stepWitness, err := goState.Step(true) stepWitness, err := goState.Step(true)
...@@ -127,7 +128,14 @@ func FuzzStateSyscallClone(f *testing.F) { ...@@ -127,7 +128,14 @@ func FuzzStateSyscallClone(f *testing.F) {
func FuzzStateSyscallMmap(f *testing.F) { func FuzzStateSyscallMmap(f *testing.F) {
contracts, addrs := testContractsSetup(f) contracts, addrs := testContractsSetup(f)
step := uint64(0) step := uint64(0)
f.Fuzz(func(t *testing.T, addr uint32, siz uint32, heap uint32) {
// Add special cases for large memory allocation
f.Add(uint32(0), uint32(0x1000), uint32(program.HEAP_END), int64(1))
f.Add(uint32(0), uint32(1<<31), uint32(program.HEAP_START), int64(2))
// Check edge case - just within bounds
f.Add(uint32(0), uint32(0x1000), uint32(program.HEAP_END-4096), int64(3))
f.Fuzz(func(t *testing.T, addr uint32, siz uint32, heap uint32, seed int64) {
state := &singlethreaded.State{ state := &singlethreaded.State{
Cpu: mipsevm.CpuScalars{ Cpu: mipsevm.CpuScalars{
PC: 0, PC: 0,
...@@ -139,11 +147,14 @@ func FuzzStateSyscallMmap(f *testing.F) { ...@@ -139,11 +147,14 @@ func FuzzStateSyscallMmap(f *testing.F) {
ExitCode: 0, ExitCode: 0,
Exited: false, Exited: false,
Memory: memory.NewMemory(), Memory: memory.NewMemory(),
Registers: [32]uint32{2: exec.SysMmap, 4: addr, 5: siz}, Registers: testutil.RandomRegisters(seed),
Step: step, Step: step,
PreimageOffset: 0, PreimageOffset: 0,
} }
state.Memory.SetMemory(0, syscallInsn) state.Memory.SetMemory(0, syscallInsn)
state.Registers[2] = exec.SysMmap
state.Registers[4] = addr
state.Registers[5] = siz
preStateRoot := state.Memory.MerkleRoot() preStateRoot := state.Memory.MerkleRoot()
preStateRegisters := state.Registers preStateRegisters := state.Registers
...@@ -152,32 +163,42 @@ func FuzzStateSyscallMmap(f *testing.F) { ...@@ -152,32 +163,42 @@ func FuzzStateSyscallMmap(f *testing.F) {
require.NoError(t, err) require.NoError(t, err)
require.False(t, stepWitness.HasPreimage()) require.False(t, stepWitness.HasPreimage())
require.Equal(t, uint32(4), state.Cpu.PC) var expectedHeap uint32
require.Equal(t, uint32(8), state.Cpu.NextPC) expectedRegisters := preStateRegisters
require.Equal(t, uint32(0), state.Cpu.LO)
require.Equal(t, uint32(0), state.Cpu.HI)
require.Equal(t, uint8(0), state.ExitCode)
require.Equal(t, false, state.Exited)
require.Equal(t, preStateRoot, state.Memory.MerkleRoot())
require.Equal(t, uint64(1), state.Step)
require.Equal(t, common.Hash{}, state.PreimageKey)
require.Equal(t, uint32(0), state.PreimageOffset)
if addr == 0 { if addr == 0 {
expectedRegisters := preStateRegisters
expectedRegisters[2] = heap
require.Equal(t, expectedRegisters, state.Registers)
sizAlign := siz sizAlign := siz
if sizAlign&memory.PageAddrMask != 0 { // adjust size to align with page size if sizAlign&memory.PageAddrMask != 0 { // adjust size to align with page size
sizAlign = siz + memory.PageSize - (siz & memory.PageAddrMask) sizAlign = siz + memory.PageSize - (siz & memory.PageAddrMask)
} }
require.Equal(t, uint32(heap+sizAlign), state.Heap) newHeap := heap + sizAlign
if newHeap > program.HEAP_END || newHeap < heap || sizAlign < siz {
expectedHeap = heap
expectedRegisters[2] = exec.SysErrorSignal
expectedRegisters[7] = exec.MipsEINVAL
} else {
expectedRegisters[2] = heap
expectedRegisters[7] = 0 // no error
expectedHeap = heap + sizAlign
}
} else { } else {
expectedRegisters := preStateRegisters
expectedRegisters[2] = addr expectedRegisters[2] = addr
require.Equal(t, expectedRegisters, state.Registers) expectedRegisters[7] = 0 // no error
require.Equal(t, uint32(heap), state.Heap) expectedHeap = heap
} }
require.Equal(t, uint32(4), state.Cpu.PC)
require.Equal(t, uint32(8), state.Cpu.NextPC)
require.Equal(t, uint32(0), state.Cpu.LO)
require.Equal(t, uint32(0), state.Cpu.HI)
require.Equal(t, preStateRoot, state.Memory.MerkleRoot())
require.Equal(t, uint64(1), state.Step)
require.Equal(t, common.Hash{}, state.PreimageKey)
require.Equal(t, uint32(0), state.PreimageOffset)
require.Equal(t, expectedHeap, state.Heap)
require.Equal(t, uint8(0), state.ExitCode)
require.Equal(t, false, state.Exited)
require.Equal(t, expectedRegisters, state.Registers)
evm := testutil.NewMIPSEVM(contracts, addrs) evm := testutil.NewMIPSEVM(contracts, addrs)
evmPost := evm.Step(t, stepWitness, step, singlethreaded.GetStateHashFn()) evmPost := evm.Step(t, stepWitness, step, singlethreaded.GetStateHashFn())
goPost, _ := goState.GetState().EncodeWitness() goPost, _ := goState.GetState().EncodeWitness()
......
...@@ -5,36 +5,37 @@ ...@@ -5,36 +5,37 @@
.ent test .ent test
# load hash at 0x30001000 # load hash at 0x30001000
# point evaluation precompile input - 01e798154708fe7789429634053cbf9f99b619f9f084048927333fce637f549b564c0a11a0f704f4fc3e8acfe0f8245f0ad1347b378fbf96e206da11a5d3630624d25032e67a7e6a4910df5834b8fe70e6bcfeeac0352434196bdf4b2485d5a18f59a8d2a1a625a17f3fea0fe5eb8c896db3764f3185481bc22f91b4aaffcca25f26936857bc3a7c2539ea8ec3a952b7873033e038326e87ed3e1276fd140253fa08e9fc25fb2d9a98527fc22a2c9612fbeafdad446cbc7bcdbdcd780af2c16a # requiredGas is 50_000
# 0x0a44472c cb798bc5 954fc466 e6ee2c31 e1ca8a87 d000966c 629d679a 4a29921f = keccak(address(0xa) ++ precompile_input) # point evaluation precompile input (requiredGas ++ precompileInput) - 000000000000c35001e798154708fe7789429634053cbf9f99b619f9f084048927333fce637f549b564c0a11a0f704f4fc3e8acfe0f8245f0ad1347b378fbf96e206da11a5d3630624d25032e67a7e6a4910df5834b8fe70e6bcfeeac0352434196bdf4b2485d5a18f59a8d2a1a625a17f3fea0fe5eb8c896db3764f3185481bc22f91b4aaffcca25f26936857bc3a7c2539ea8ec3a952b7873033e038326e87ed3e1276fd140253fa08e9fc25fb2d9a98527fc22a2c9612fbeafdad446cbc7bcdbdcd780af2c16a
# 0x0644472c cb798bc5 954fc466 e6ee2c31 e1ca8a87 d000966c 629d679a 4a29921f = keccak(address(0xa) ++ precompile_input).key (precompile) # 0x3efd5c3c 1c555298 0c63aee5 4570c276 cbff7532 796b4d75 3132d51a 6bedf0c6 = keccak(address(0xa) ++ required_gas ++ precompile_input)
# 0x06fd5c3c 1c555298 0c63aee5 4570c276 cbff7532 796b4d75 3132d51a 6bedf0c6 = keccak(address(0xa) ++ required_gas ++ precompile_input).key (precompile)
test: test:
lui $s0, 0x3000 lui $s0, 0x3000
ori $s0, 0x1000 ori $s0, 0x1000
lui $t0, 0x0644 lui $t0, 0x06fd
ori $t0, 0x472c ori $t0, 0x5c3c
sw $t0, 0($s0) sw $t0, 0($s0)
lui $t0, 0xcb79 lui $t0, 0x1c55
ori $t0, 0x8bc5 ori $t0, 0x5298
sw $t0, 4($s0) sw $t0, 4($s0)
lui $t0, 0x954f lui $t0, 0x0c63
ori $t0, 0xc466 ori $t0, 0xaee5
sw $t0, 8($s0) sw $t0, 8($s0)
lui $t0, 0xe6ee lui $t0, 0x4570
ori $t0, 0x2c31 ori $t0, 0xc276
sw $t0, 0xc($s0) sw $t0, 0xc($s0)
lui $t0, 0xe1ca lui $t0, 0xcbff
ori $t0, 0x8a87 ori $t0, 0x7532
sw $t0, 0x10($s0) sw $t0, 0x10($s0)
lui $t0, 0xd000 lui $t0, 0x796b
ori $t0, 0x966c ori $t0, 0x4d75
sw $t0, 0x14($s0) sw $t0, 0x14($s0)
lui $t0, 0x629d lui $t0, 0x3132
ori $t0, 0x679a ori $t0, 0xd51a
sw $t0, 0x18($s0) sw $t0, 0x18($s0)
lui $t0, 0x4a29 lui $t0, 0x6bed
ori $t0, 0x921f ori $t0, 0xf0c6
sw $t0, 0x1c($s0) sw $t0, 0x1c($s0)
# preimage request - write(fdPreimageWrite, preimageData, 32) # preimage request - write(fdPreimageWrite, preimageData, 32)
......
###############################################################################
# Description:
# Tests that the 'srav' instruction properly only utilizes the lower 5 bits
# of the rs register rather than using the entire 32 bits.
#
###############################################################################
.section .test, "x"
.balign 4
.set noreorder
.global test
.ent test
test:
lui $s0, 0xbfff # Load the base address 0xbffffff0
ori $s0, 0xfff0
ori $s1, $0, 1 # Prepare the 'done' status
#### Test code start ####
lui $t0, 0xdeaf # A = 0xdeafbeef
ori $t0, 0xbeef
ori $t1, $0, 0x2c
srav $t2, $t0, $t1 # B = 0xdeafbeef >> (0x2c & 0x1f) = 0xdeadbeef >> 12 = 0xfffdeafb
lui $t3, 0xfffd
ori $t3, 0xeafb
subu $t4, $t2, $t3
sltiu $v0, $t4, 1
#### Test code end ####
sw $v0, 8($s0) # Set the test result
sw $s1, 4($s0) # Set 'done'
$done:
jr $ra
nop
.end test
package testutil package testutil
import ( import (
"encoding/binary"
"errors" "errors"
"fmt" "fmt"
"math" "math"
...@@ -121,11 +122,13 @@ func EncodePreimageOracleInput(t *testing.T, wit *mipsevm.StepWitness, localCont ...@@ -121,11 +122,13 @@ func EncodePreimageOracleInput(t *testing.T, wit *mipsevm.StepWitness, localCont
} }
preimage := localOracle.GetPreimage(preimage.Keccak256Key(wit.PreimageKey).PreimageKey()) preimage := localOracle.GetPreimage(preimage.Keccak256Key(wit.PreimageKey).PreimageKey())
precompile := common.BytesToAddress(preimage[:20]) precompile := common.BytesToAddress(preimage[:20])
callInput := preimage[20:] requiredGas := binary.BigEndian.Uint64(preimage[20:28])
callInput := preimage[28:]
input, err := oracle.ABI.Pack( input, err := oracle.ABI.Pack(
"loadPrecompilePreimagePart", "loadPrecompilePreimagePart",
new(big.Int).SetUint64(uint64(wit.PreimageOffset)), new(big.Int).SetUint64(uint64(wit.PreimageOffset)),
precompile, precompile,
requiredGas,
callInput, callInput,
) )
require.NoError(t, err) require.NoError(t, err)
......
...@@ -42,11 +42,13 @@ func StaticOracle(t *testing.T, preimageData []byte) *TestOracle { ...@@ -42,11 +42,13 @@ func StaticOracle(t *testing.T, preimageData []byte) *TestOracle {
} }
} }
func StaticPrecompileOracle(t *testing.T, precompile common.Address, input []byte, result []byte) *TestOracle { func StaticPrecompileOracle(t *testing.T, precompile common.Address, requiredGas uint64, input []byte, result []byte) *TestOracle {
return &TestOracle{ return &TestOracle{
hint: func(v []byte) {}, hint: func(v []byte) {},
getPreimage: func(k [32]byte) []byte { getPreimage: func(k [32]byte) []byte {
keyData := append(precompile.Bytes(), input...) requiredGasB := binary.BigEndian.AppendUint64(nil, requiredGas)
keyData := append(precompile.Bytes(), requiredGasB...)
keyData = append(keyData, input...)
switch k[0] { switch k[0] {
case byte(preimage.Keccak256KeyType): case byte(preimage.Keccak256KeyType):
if k != preimage.Keccak256Key(crypto.Keccak256Hash(keyData)).PreimageKey() { if k != preimage.Keccak256Key(crypto.Keccak256Hash(keyData)).PreimageKey() {
...@@ -135,7 +137,8 @@ func SelectOracleFixture(t *testing.T, programName string) mipsevm.PreimageOracl ...@@ -135,7 +137,8 @@ func SelectOracleFixture(t *testing.T, programName string) mipsevm.PreimageOracl
precompile := common.BytesToAddress([]byte{0xa}) precompile := common.BytesToAddress([]byte{0xa})
input := common.FromHex("01e798154708fe7789429634053cbf9f99b619f9f084048927333fce637f549b564c0a11a0f704f4fc3e8acfe0f8245f0ad1347b378fbf96e206da11a5d3630624d25032e67a7e6a4910df5834b8fe70e6bcfeeac0352434196bdf4b2485d5a18f59a8d2a1a625a17f3fea0fe5eb8c896db3764f3185481bc22f91b4aaffcca25f26936857bc3a7c2539ea8ec3a952b7873033e038326e87ed3e1276fd140253fa08e9fc25fb2d9a98527fc22a2c9612fbeafdad446cbc7bcdbdcd780af2c16a") input := common.FromHex("01e798154708fe7789429634053cbf9f99b619f9f084048927333fce637f549b564c0a11a0f704f4fc3e8acfe0f8245f0ad1347b378fbf96e206da11a5d3630624d25032e67a7e6a4910df5834b8fe70e6bcfeeac0352434196bdf4b2485d5a18f59a8d2a1a625a17f3fea0fe5eb8c896db3764f3185481bc22f91b4aaffcca25f26936857bc3a7c2539ea8ec3a952b7873033e038326e87ed3e1276fd140253fa08e9fc25fb2d9a98527fc22a2c9612fbeafdad446cbc7bcdbdcd780af2c16a")
blobPrecompileReturnValue := common.FromHex("000000000000000000000000000000000000000000000000000000000000100073eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001") blobPrecompileReturnValue := common.FromHex("000000000000000000000000000000000000000000000000000000000000100073eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001")
return StaticPrecompileOracle(t, precompile, input, append([]byte{0x1}, blobPrecompileReturnValue...)) requiredGas := uint64(50_000)
return StaticPrecompileOracle(t, precompile, requiredGas, input, append([]byte{0x1}, blobPrecompileReturnValue...))
} else if strings.HasPrefix(programName, "oracle") { } else if strings.HasPrefix(programName, "oracle") {
return StaticOracle(t, []byte("hello world")) return StaticOracle(t, []byte("hello world"))
} else { } else {
......
package testutil
import "math/rand"
func RandomRegisters(seed int64) [32]uint32 {
r := rand.New(rand.NewSource(seed))
var registers [32]uint32
for i := 0; i < 32; i++ {
registers[i] = r.Uint32()
}
return registers
}
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"io" "io"
"os" "os"
"path" "path"
"strings"
"testing" "testing"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
...@@ -32,6 +33,7 @@ func RunVMTests_OpenMips[T mipsevm.FPVMState](t *testing.T, stateFactory StateFa ...@@ -32,6 +33,7 @@ func RunVMTests_OpenMips[T mipsevm.FPVMState](t *testing.T, stateFactory StateFa
oracle := SelectOracleFixture(t, f.Name()) oracle := SelectOracleFixture(t, f.Name())
// Short-circuit early for exit_group.bin // Short-circuit early for exit_group.bin
exitGroup := f.Name() == "exit_group.bin" exitGroup := f.Name() == "exit_group.bin"
expectPanic := strings.HasSuffix(f.Name(), "panic.bin")
// TODO: currently tests are compiled as flat binary objects // TODO: currently tests are compiled as flat binary objects
// We can use more standard tooling to compile them to ELF files and get remove maketests.py // We can use more standard tooling to compile them to ELF files and get remove maketests.py
...@@ -51,6 +53,17 @@ func RunVMTests_OpenMips[T mipsevm.FPVMState](t *testing.T, stateFactory StateFa ...@@ -51,6 +53,17 @@ func RunVMTests_OpenMips[T mipsevm.FPVMState](t *testing.T, stateFactory StateFa
us := vmFactory(state, oracle, os.Stdout, os.Stderr, CreateLogger()) us := vmFactory(state, oracle, os.Stdout, os.Stderr, CreateLogger())
// Catch panics and check if they are expected
defer func() {
if r := recover(); r != nil {
if expectPanic {
// Success
} else {
t.Errorf("unexpected panic: %v", r)
}
}
}()
for i := 0; i < 1000; i++ { for i := 0; i < 1000; i++ {
if us.GetState().GetPC() == EndAddr { if us.GetState().GetPC() == EndAddr {
break break
...@@ -66,6 +79,8 @@ func RunVMTests_OpenMips[T mipsevm.FPVMState](t *testing.T, stateFactory StateFa ...@@ -66,6 +79,8 @@ func RunVMTests_OpenMips[T mipsevm.FPVMState](t *testing.T, stateFactory StateFa
require.NotEqual(t, uint32(EndAddr), us.GetState().GetPC(), "must not reach end") require.NotEqual(t, uint32(EndAddr), us.GetState().GetPC(), "must not reach end")
require.True(t, us.GetState().GetExited(), "must set exited state") require.True(t, us.GetState().GetExited(), "must set exited state")
require.Equal(t, uint8(1), us.GetState().GetExitCode(), "must exit with 1") require.Equal(t, uint8(1), us.GetState().GetExitCode(), "must exit with 1")
} else if expectPanic {
require.NotEqual(t, uint32(EndAddr), us.GetState().GetPC(), "must not reach end")
} else { } else {
require.Equal(t, uint32(EndAddr), us.GetState().GetPC(), "must reach end") require.Equal(t, uint32(EndAddr), us.GetState().GetPC(), "must reach end")
done, result := state.GetMemory().GetMemory(BaseAddrEnd+4), state.GetMemory().GetMemory(BaseAddrEnd+8) done, result := state.GetMemory().GetMemory(BaseAddrEnd+4), state.GetMemory().GetMemory(BaseAddrEnd+8)
......
[
{
"inputs": [
{
"internalType": "uint256",
"name": "_minProposalSize",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_challengePeriod",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "KECCAK_TREE_DEPTH",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MAX_LEAF_COUNT",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MIN_BOND_SIZE",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_uuid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_inputStartBlock",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "_input",
"type": "bytes"
},
{
"internalType": "bytes32[]",
"name": "_stateCommitments",
"type": "bytes32[]"
},
{
"internalType": "bool",
"name": "_finalize",
"type": "bool"
}
],
"name": "addLeavesLPP",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_claimant",
"type": "address"
},
{
"internalType": "uint256",
"name": "_uuid",
"type": "uint256"
},
{
"components": [
{
"internalType": "bytes",
"name": "input",
"type": "bytes"
},
{
"internalType": "uint256",
"name": "index",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "stateCommitment",
"type": "bytes32"
}
],
"internalType": "struct PreimageOracle.Leaf",
"name": "_postState",
"type": "tuple"
},
{
"internalType": "bytes32[]",
"name": "_postStateProof",
"type": "bytes32[]"
}
],
"name": "challengeFirstLPP",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_claimant",
"type": "address"
},
{
"internalType": "uint256",
"name": "_uuid",
"type": "uint256"
},
{
"components": [
{
"internalType": "uint64[25]",
"name": "state",
"type": "uint64[25]"
}
],
"internalType": "struct LibKeccak.StateMatrix",
"name": "_stateMatrix",
"type": "tuple"
},
{
"components": [
{
"internalType": "bytes",
"name": "input",
"type": "bytes"
},
{
"internalType": "uint256",
"name": "index",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "stateCommitment",
"type": "bytes32"
}
],
"internalType": "struct PreimageOracle.Leaf",
"name": "_preState",
"type": "tuple"
},
{
"internalType": "bytes32[]",
"name": "_preStateProof",
"type": "bytes32[]"
},
{
"components": [
{
"internalType": "bytes",
"name": "input",
"type": "bytes"
},
{
"internalType": "uint256",
"name": "index",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "stateCommitment",
"type": "bytes32"
}
],
"internalType": "struct PreimageOracle.Leaf",
"name": "_postState",
"type": "tuple"
},
{
"internalType": "bytes32[]",
"name": "_postStateProof",
"type": "bytes32[]"
}
],
"name": "challengeLPP",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "challengePeriod",
"outputs": [
{
"internalType": "uint256",
"name": "challengePeriod_",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_owner",
"type": "address"
},
{
"internalType": "uint256",
"name": "_uuid",
"type": "uint256"
}
],
"name": "getTreeRootLPP",
"outputs": [
{
"internalType": "bytes32",
"name": "treeRoot_",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_uuid",
"type": "uint256"
},
{
"internalType": "uint32",
"name": "_partOffset",
"type": "uint32"
},
{
"internalType": "uint32",
"name": "_claimedSize",
"type": "uint32"
}
],
"name": "initLPP",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_z",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_y",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "_commitment",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "_proof",
"type": "bytes"
},
{
"internalType": "uint256",
"name": "_partOffset",
"type": "uint256"
}
],
"name": "loadBlobPreimagePart",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_partOffset",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "_preimage",
"type": "bytes"
}
],
"name": "loadKeccak256PreimagePart",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_ident",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "_localContext",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "_word",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "_size",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_partOffset",
"type": "uint256"
}
],
"name": "loadLocalData",
"outputs": [
{
"internalType": "bytes32",
"name": "key_",
"type": "bytes32"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_partOffset",
"type": "uint256"
},
{
"internalType": "address",
"name": "_precompile",
"type": "address"
},
{
"internalType": "bytes",
"name": "_input",
"type": "bytes"
}
],
"name": "loadPrecompilePreimagePart",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_partOffset",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "_preimage",
"type": "bytes"
}
],
"name": "loadSha256PreimagePart",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "minProposalSize",
"outputs": [
{
"internalType": "uint256",
"name": "minProposalSize_",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"name": "preimageLengths",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "preimagePartOk",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "preimageParts",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "proposalBlocks",
"outputs": [
{
"internalType": "uint64",
"name": "",
"type": "uint64"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_claimant",
"type": "address"
},
{
"internalType": "uint256",
"name": "_uuid",
"type": "uint256"
}
],
"name": "proposalBlocksLen",
"outputs": [
{
"internalType": "uint256",
"name": "len_",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "proposalBonds",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "proposalBranches",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "proposalCount",
"outputs": [
{
"internalType": "uint256",
"name": "count_",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "proposalMetadata",
"outputs": [
{
"internalType": "LPPMetaData",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "proposalParts",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "proposals",
"outputs": [
{
"internalType": "address",
"name": "claimant",
"type": "address"
},
{
"internalType": "uint256",
"name": "uuid",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "_key",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "_offset",
"type": "uint256"
}
],
"name": "readPreimage",
"outputs": [
{
"internalType": "bytes32",
"name": "dat_",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "datLen_",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_claimant",
"type": "address"
},
{
"internalType": "uint256",
"name": "_uuid",
"type": "uint256"
},
{
"components": [
{
"internalType": "uint64[25]",
"name": "state",
"type": "uint64[25]"
}
],
"internalType": "struct LibKeccak.StateMatrix",
"name": "_stateMatrix",
"type": "tuple"
},
{
"components": [
{
"internalType": "bytes",
"name": "input",
"type": "bytes"
},
{
"internalType": "uint256",
"name": "index",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "stateCommitment",
"type": "bytes32"
}
],
"internalType": "struct PreimageOracle.Leaf",
"name": "_preState",
"type": "tuple"
},
{
"internalType": "bytes32[]",
"name": "_preStateProof",
"type": "bytes32[]"
},
{
"components": [
{
"internalType": "bytes",
"name": "input",
"type": "bytes"
},
{
"internalType": "uint256",
"name": "index",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "stateCommitment",
"type": "bytes32"
}
],
"internalType": "struct PreimageOracle.Leaf",
"name": "_postState",
"type": "tuple"
},
{
"internalType": "bytes32[]",
"name": "_postStateProof",
"type": "bytes32[]"
}
],
"name": "squeezeLPP",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "version",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "zeroHashes",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "ActiveProposal",
"type": "error"
},
{
"inputs": [],
"name": "AlreadyFinalized",
"type": "error"
},
{
"inputs": [],
"name": "BadProposal",
"type": "error"
},
{
"inputs": [],
"name": "BondTransferFailed",
"type": "error"
},
{
"inputs": [],
"name": "InsufficientBond",
"type": "error"
},
{
"inputs": [],
"name": "InvalidInputSize",
"type": "error"
},
{
"inputs": [],
"name": "InvalidPreimage",
"type": "error"
},
{
"inputs": [],
"name": "InvalidProof",
"type": "error"
},
{
"inputs": [],
"name": "NotEOA",
"type": "error"
},
{
"inputs": [],
"name": "NotInitialized",
"type": "error"
},
{
"inputs": [],
"name": "PartOffsetOOB",
"type": "error"
},
{
"inputs": [],
"name": "PostStateMatches",
"type": "error"
},
{
"inputs": [],
"name": "StatesNotContiguous",
"type": "error"
},
{
"inputs": [],
"name": "TreeSizeOverflow",
"type": "error"
},
{
"inputs": [],
"name": "WrongStartingBlock",
"type": "error"
}
]
\ No newline at end of file
...@@ -7,7 +7,6 @@ import ( ...@@ -7,7 +7,6 @@ import (
"fmt" "fmt"
"math" "math"
"math/big" "math/big"
"strings"
"time" "time"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts/metrics" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts/metrics"
...@@ -26,7 +25,6 @@ import ( ...@@ -26,7 +25,6 @@ import (
var maxChildChecks = big.NewInt(512) var maxChildChecks = big.NewInt(512)
var ( var (
methodVersion = "version"
methodMaxClockDuration = "maxClockDuration" methodMaxClockDuration = "maxClockDuration"
methodMaxGameDepth = "maxGameDepth" methodMaxGameDepth = "maxGameDepth"
methodAbsolutePrestate = "absolutePrestate" methodAbsolutePrestate = "absolutePrestate"
...@@ -84,14 +82,8 @@ type outputRootProof struct { ...@@ -84,14 +82,8 @@ type outputRootProof struct {
func NewFaultDisputeGameContract(ctx context.Context, metrics metrics.ContractMetricer, addr common.Address, caller *batching.MultiCaller) (FaultDisputeGameContract, error) { func NewFaultDisputeGameContract(ctx context.Context, metrics metrics.ContractMetricer, addr common.Address, caller *batching.MultiCaller) (FaultDisputeGameContract, error) {
contractAbi := snapshots.LoadFaultDisputeGameABI() contractAbi := snapshots.LoadFaultDisputeGameABI()
result, err := caller.SingleCall(ctx, rpcblock.Latest, batching.NewContractCall(contractAbi, addr, methodVersion)) var builder VersionedBuilder[FaultDisputeGameContract]
if err != nil { builder.AddVersion(0, 8, func() (FaultDisputeGameContract, error) {
return nil, fmt.Errorf("failed to retrieve version of dispute game %v: %w", addr, err)
}
version := result.GetString(0)
if strings.HasPrefix(version, "0.8.") {
// Detected an older version of contracts, use a compatibility shim.
legacyAbi := mustParseAbi(faultDisputeGameAbi020) legacyAbi := mustParseAbi(faultDisputeGameAbi020)
return &FaultDisputeGameContract080{ return &FaultDisputeGameContract080{
FaultDisputeGameContractLatest: FaultDisputeGameContractLatest{ FaultDisputeGameContractLatest: FaultDisputeGameContractLatest{
...@@ -100,8 +92,8 @@ func NewFaultDisputeGameContract(ctx context.Context, metrics metrics.ContractMe ...@@ -100,8 +92,8 @@ func NewFaultDisputeGameContract(ctx context.Context, metrics metrics.ContractMe
contract: batching.NewBoundContract(legacyAbi, addr), contract: batching.NewBoundContract(legacyAbi, addr),
}, },
}, nil }, nil
} else if strings.HasPrefix(version, "0.18.") || strings.HasPrefix(version, "1.0.") { })
// Detected an older version of contracts, use a compatibility shim. builder.AddVersion(0, 18, func() (FaultDisputeGameContract, error) {
legacyAbi := mustParseAbi(faultDisputeGameAbi0180) legacyAbi := mustParseAbi(faultDisputeGameAbi0180)
return &FaultDisputeGameContract0180{ return &FaultDisputeGameContract0180{
FaultDisputeGameContractLatest: FaultDisputeGameContractLatest{ FaultDisputeGameContractLatest: FaultDisputeGameContractLatest{
...@@ -110,8 +102,18 @@ func NewFaultDisputeGameContract(ctx context.Context, metrics metrics.ContractMe ...@@ -110,8 +102,18 @@ func NewFaultDisputeGameContract(ctx context.Context, metrics metrics.ContractMe
contract: batching.NewBoundContract(legacyAbi, addr), contract: batching.NewBoundContract(legacyAbi, addr),
}, },
}, nil }, nil
} else if strings.HasPrefix(version, "1.1.") { })
// Detected an older version of contracts, use a compatibility shim. builder.AddVersion(1, 0, func() (FaultDisputeGameContract, error) {
legacyAbi := mustParseAbi(faultDisputeGameAbi0180)
return &FaultDisputeGameContract0180{
FaultDisputeGameContractLatest: FaultDisputeGameContractLatest{
metrics: metrics,
multiCaller: caller,
contract: batching.NewBoundContract(legacyAbi, addr),
},
}, nil
})
builder.AddVersion(1, 1, func() (FaultDisputeGameContract, error) {
legacyAbi := mustParseAbi(faultDisputeGameAbi111) legacyAbi := mustParseAbi(faultDisputeGameAbi111)
return &FaultDisputeGameContract111{ return &FaultDisputeGameContract111{
FaultDisputeGameContractLatest: FaultDisputeGameContractLatest{ FaultDisputeGameContractLatest: FaultDisputeGameContractLatest{
...@@ -120,13 +122,14 @@ func NewFaultDisputeGameContract(ctx context.Context, metrics metrics.ContractMe ...@@ -120,13 +122,14 @@ func NewFaultDisputeGameContract(ctx context.Context, metrics metrics.ContractMe
contract: batching.NewBoundContract(legacyAbi, addr), contract: batching.NewBoundContract(legacyAbi, addr),
}, },
}, nil }, nil
} else { })
return builder.Build(ctx, caller, contractAbi, addr, func() (FaultDisputeGameContract, error) {
return &FaultDisputeGameContractLatest{ return &FaultDisputeGameContractLatest{
metrics: metrics, metrics: metrics,
multiCaller: caller, multiCaller: caller,
contract: batching.NewBoundContract(contractAbi, addr), contract: batching.NewBoundContract(contractAbi, addr),
}, nil }, nil
} })
} }
func mustParseAbi(json []byte) *abi.ABI { func mustParseAbi(json []byte) *abi.ABI {
...@@ -364,7 +367,7 @@ func (f *FaultDisputeGameContractLatest) getDelayedWETH(ctx context.Context, blo ...@@ -364,7 +367,7 @@ func (f *FaultDisputeGameContractLatest) getDelayedWETH(ctx context.Context, blo
return NewDelayedWETHContract(f.metrics, result.GetAddress(0), f.multiCaller), nil return NewDelayedWETHContract(f.metrics, result.GetAddress(0), f.multiCaller), nil
} }
func (f *FaultDisputeGameContractLatest) GetOracle(ctx context.Context) (*PreimageOracleContract, error) { func (f *FaultDisputeGameContractLatest) GetOracle(ctx context.Context) (PreimageOracleContract, error) {
defer f.metrics.StartContractRequest("GetOracle")() defer f.metrics.StartContractRequest("GetOracle")()
vm, err := f.Vm(ctx) vm, err := f.Vm(ctx)
if err != nil { if err != nil {
...@@ -616,7 +619,7 @@ type FaultDisputeGameContract interface { ...@@ -616,7 +619,7 @@ type FaultDisputeGameContract interface {
GetRequiredBond(ctx context.Context, position types.Position) (*big.Int, error) GetRequiredBond(ctx context.Context, position types.Position) (*big.Int, error)
UpdateOracleTx(ctx context.Context, claimIdx uint64, data *types.PreimageOracleData) (txmgr.TxCandidate, error) UpdateOracleTx(ctx context.Context, claimIdx uint64, data *types.PreimageOracleData) (txmgr.TxCandidate, error)
GetWithdrawals(ctx context.Context, block rpcblock.Block, recipients ...common.Address) ([]*WithdrawalRequest, error) GetWithdrawals(ctx context.Context, block rpcblock.Block, recipients ...common.Address) ([]*WithdrawalRequest, error)
GetOracle(ctx context.Context) (*PreimageOracleContract, error) GetOracle(ctx context.Context) (PreimageOracleContract, error)
GetMaxClockDuration(ctx context.Context) (time.Duration, error) GetMaxClockDuration(ctx context.Context) (time.Duration, error)
GetMaxGameDepth(ctx context.Context) (types.Depth, error) GetMaxGameDepth(ctx context.Context) (types.Depth, error)
GetAbsolutePrestateHash(ctx context.Context) (common.Hash, error) GetAbsolutePrestateHash(ctx context.Context) (common.Hash, error)
......
...@@ -797,6 +797,7 @@ func setupFaultDisputeGameTest(t *testing.T, version contractVersion) (*batching ...@@ -797,6 +797,7 @@ func setupFaultDisputeGameTest(t *testing.T, version contractVersion) (*batching
caller := batching.NewMultiCaller(stubRpc, batching.DefaultBatchSize) caller := batching.NewMultiCaller(stubRpc, batching.DefaultBatchSize)
stubRpc.SetResponse(fdgAddr, methodVersion, rpcblock.Latest, nil, []interface{}{version.version}) stubRpc.SetResponse(fdgAddr, methodVersion, rpcblock.Latest, nil, []interface{}{version.version})
stubRpc.SetResponse(oracleAddr, methodVersion, rpcblock.Latest, nil, []interface{}{oracleLatest})
game, err := NewFaultDisputeGameContract(context.Background(), contractMetrics.NoopContractMetrics, fdgAddr, caller) game, err := NewFaultDisputeGameContract(context.Background(), contractMetrics.NoopContractMetrics, fdgAddr, caller)
require.NoError(t, err) require.NoError(t, err)
return stubRpc, game return stubRpc, game
......
...@@ -60,8 +60,8 @@ type libKeccakStateMatrix struct { ...@@ -60,8 +60,8 @@ type libKeccakStateMatrix struct {
State [25]uint64 State [25]uint64
} }
// PreimageOracleContract is a binding that works with contracts implementing the IPreimageOracle interface // PreimageOracleContractLatest is a binding that works with contracts implementing the IPreimageOracle interface
type PreimageOracleContract struct { type PreimageOracleContractLatest struct {
addr common.Address addr common.Address
multiCaller *batching.MultiCaller multiCaller *batching.MultiCaller
contract *batching.BoundContract contract *batching.BoundContract
...@@ -83,21 +83,36 @@ func toPreimageOracleLeaf(l keccakTypes.Leaf) preimageOracleLeaf { ...@@ -83,21 +83,36 @@ func toPreimageOracleLeaf(l keccakTypes.Leaf) preimageOracleLeaf {
} }
} }
func NewPreimageOracleContract(addr common.Address, caller *batching.MultiCaller) *PreimageOracleContract { func NewPreimageOracleContract(ctx context.Context, addr common.Address, caller *batching.MultiCaller) (PreimageOracleContract, error) {
oracleAbi := snapshots.LoadPreimageOracleABI() oracleAbi := snapshots.LoadPreimageOracleABI()
return &PreimageOracleContract{ var builder VersionedBuilder[PreimageOracleContract]
addr: addr, builder.AddVersion(1, 0, func() (PreimageOracleContract, error) {
multiCaller: caller, legacyAbi := mustParseAbi(preimageOracleAbi100)
contract: batching.NewBoundContract(oracleAbi, addr), return &PreimageOracleContract100{
} PreimageOracleContractLatest{
addr: addr,
multiCaller: caller,
contract: batching.NewBoundContract(legacyAbi, addr),
},
}, nil
})
return builder.Build(ctx, caller, oracleAbi, addr, func() (PreimageOracleContract, error) {
return &PreimageOracleContractLatest{
addr: addr,
multiCaller: caller,
contract: batching.NewBoundContract(oracleAbi, addr),
}, nil
})
} }
func (c *PreimageOracleContract) Addr() common.Address { func (c *PreimageOracleContractLatest) Addr() common.Address {
return c.addr return c.addr
} }
func (c *PreimageOracleContract) AddGlobalDataTx(data *types.PreimageOracleData) (txmgr.TxCandidate, error) { func (c *PreimageOracleContractLatest) AddGlobalDataTx(data *types.PreimageOracleData) (txmgr.TxCandidate, error) {
if len(data.OracleKey) == 0 { if len(data.OracleKey) == 0 {
return txmgr.TxCandidate{}, ErrInvalidPreimageKey return txmgr.TxCandidate{}, ErrInvalidPreimageKey
} }
...@@ -121,6 +136,7 @@ func (c *PreimageOracleContract) AddGlobalDataTx(data *types.PreimageOracleData) ...@@ -121,6 +136,7 @@ func (c *PreimageOracleContract) AddGlobalDataTx(data *types.PreimageOracleData)
call := c.contract.Call(methodLoadPrecompilePreimagePart, call := c.contract.Call(methodLoadPrecompilePreimagePart,
new(big.Int).SetUint64(uint64(data.OracleOffset)), new(big.Int).SetUint64(uint64(data.OracleOffset)),
data.GetPrecompileAddress(), data.GetPrecompileAddress(),
data.GetPrecompileRequiredGas(),
data.GetPrecompileInput()) data.GetPrecompileInput())
return call.ToTxCandidate() return call.ToTxCandidate()
default: default:
...@@ -128,7 +144,7 @@ func (c *PreimageOracleContract) AddGlobalDataTx(data *types.PreimageOracleData) ...@@ -128,7 +144,7 @@ func (c *PreimageOracleContract) AddGlobalDataTx(data *types.PreimageOracleData)
} }
} }
func (c *PreimageOracleContract) InitLargePreimage(uuid *big.Int, partOffset uint32, claimedSize uint32) (txmgr.TxCandidate, error) { func (c *PreimageOracleContractLatest) InitLargePreimage(uuid *big.Int, partOffset uint32, claimedSize uint32) (txmgr.TxCandidate, error) {
bond, err := c.GetMinBondLPP(context.Background()) bond, err := c.GetMinBondLPP(context.Background())
if err != nil { if err != nil {
return txmgr.TxCandidate{}, fmt.Errorf("failed to get min bond for large preimage proposal: %w", err) return txmgr.TxCandidate{}, fmt.Errorf("failed to get min bond for large preimage proposal: %w", err)
...@@ -142,13 +158,13 @@ func (c *PreimageOracleContract) InitLargePreimage(uuid *big.Int, partOffset uin ...@@ -142,13 +158,13 @@ func (c *PreimageOracleContract) InitLargePreimage(uuid *big.Int, partOffset uin
return candidate, nil return candidate, nil
} }
func (c *PreimageOracleContract) AddLeaves(uuid *big.Int, startingBlockIndex *big.Int, input []byte, commitments []common.Hash, finalize bool) (txmgr.TxCandidate, error) { func (c *PreimageOracleContractLatest) AddLeaves(uuid *big.Int, startingBlockIndex *big.Int, input []byte, commitments []common.Hash, finalize bool) (txmgr.TxCandidate, error) {
call := c.contract.Call(methodAddLeavesLPP, uuid, startingBlockIndex, input, commitments, finalize) call := c.contract.Call(methodAddLeavesLPP, uuid, startingBlockIndex, input, commitments, finalize)
return call.ToTxCandidate() return call.ToTxCandidate()
} }
// MinLargePreimageSize returns the minimum size of a large preimage. // MinLargePreimageSize returns the minimum size of a large preimage.
func (c *PreimageOracleContract) MinLargePreimageSize(ctx context.Context) (uint64, error) { func (c *PreimageOracleContractLatest) MinLargePreimageSize(ctx context.Context) (uint64, error) {
result, err := c.multiCaller.SingleCall(ctx, rpcblock.Latest, c.contract.Call(methodMinProposalSize)) result, err := c.multiCaller.SingleCall(ctx, rpcblock.Latest, c.contract.Call(methodMinProposalSize))
if err != nil { if err != nil {
return 0, fmt.Errorf("failed to fetch min lpp size bytes: %w", err) return 0, fmt.Errorf("failed to fetch min lpp size bytes: %w", err)
...@@ -157,7 +173,7 @@ func (c *PreimageOracleContract) MinLargePreimageSize(ctx context.Context) (uint ...@@ -157,7 +173,7 @@ func (c *PreimageOracleContract) MinLargePreimageSize(ctx context.Context) (uint
} }
// ChallengePeriod returns the challenge period for large preimages. // ChallengePeriod returns the challenge period for large preimages.
func (c *PreimageOracleContract) ChallengePeriod(ctx context.Context) (uint64, error) { func (c *PreimageOracleContractLatest) ChallengePeriod(ctx context.Context) (uint64, error) {
if period := c.challengePeriod.Load(); period != 0 { if period := c.challengePeriod.Load(); period != 0 {
return period, nil return period, nil
} }
...@@ -170,7 +186,7 @@ func (c *PreimageOracleContract) ChallengePeriod(ctx context.Context) (uint64, e ...@@ -170,7 +186,7 @@ func (c *PreimageOracleContract) ChallengePeriod(ctx context.Context) (uint64, e
return period, nil return period, nil
} }
func (c *PreimageOracleContract) CallSqueeze( func (c *PreimageOracleContractLatest) CallSqueeze(
ctx context.Context, ctx context.Context,
claimant common.Address, claimant common.Address,
uuid *big.Int, uuid *big.Int,
...@@ -188,7 +204,7 @@ func (c *PreimageOracleContract) CallSqueeze( ...@@ -188,7 +204,7 @@ func (c *PreimageOracleContract) CallSqueeze(
return nil return nil
} }
func (c *PreimageOracleContract) Squeeze( func (c *PreimageOracleContractLatest) Squeeze(
claimant common.Address, claimant common.Address,
uuid *big.Int, uuid *big.Int,
prestateMatrix keccakTypes.StateSnapshot, prestateMatrix keccakTypes.StateSnapshot,
...@@ -214,7 +230,7 @@ func abiEncodeSnapshot(packedState keccakTypes.StateSnapshot) libKeccakStateMatr ...@@ -214,7 +230,7 @@ func abiEncodeSnapshot(packedState keccakTypes.StateSnapshot) libKeccakStateMatr
return libKeccakStateMatrix{State: packedState} return libKeccakStateMatrix{State: packedState}
} }
func (c *PreimageOracleContract) GetActivePreimages(ctx context.Context, blockHash common.Hash) ([]keccakTypes.LargePreimageMetaData, error) { func (c *PreimageOracleContractLatest) GetActivePreimages(ctx context.Context, blockHash common.Hash) ([]keccakTypes.LargePreimageMetaData, error) {
block := rpcblock.ByHash(blockHash) block := rpcblock.ByHash(blockHash)
results, err := batching.ReadArray(ctx, c.multiCaller, block, c.contract.Call(methodProposalCount), func(i *big.Int) *batching.ContractCall { results, err := batching.ReadArray(ctx, c.multiCaller, block, c.contract.Call(methodProposalCount), func(i *big.Int) *batching.ContractCall {
return c.contract.Call(methodProposals, i) return c.contract.Call(methodProposals, i)
...@@ -231,7 +247,7 @@ func (c *PreimageOracleContract) GetActivePreimages(ctx context.Context, blockHa ...@@ -231,7 +247,7 @@ func (c *PreimageOracleContract) GetActivePreimages(ctx context.Context, blockHa
return c.GetProposalMetadata(ctx, block, idents...) return c.GetProposalMetadata(ctx, block, idents...)
} }
func (c *PreimageOracleContract) GetProposalMetadata(ctx context.Context, block rpcblock.Block, idents ...keccakTypes.LargePreimageIdent) ([]keccakTypes.LargePreimageMetaData, error) { func (c *PreimageOracleContractLatest) GetProposalMetadata(ctx context.Context, block rpcblock.Block, idents ...keccakTypes.LargePreimageIdent) ([]keccakTypes.LargePreimageMetaData, error) {
var calls []batching.Call var calls []batching.Call
for _, ident := range idents { for _, ident := range idents {
calls = append(calls, c.contract.Call(methodProposalMetadata, ident.Claimant, ident.UUID)) calls = append(calls, c.contract.Call(methodProposalMetadata, ident.Claimant, ident.UUID))
...@@ -256,7 +272,7 @@ func (c *PreimageOracleContract) GetProposalMetadata(ctx context.Context, block ...@@ -256,7 +272,7 @@ func (c *PreimageOracleContract) GetProposalMetadata(ctx context.Context, block
return proposals, nil return proposals, nil
} }
func (c *PreimageOracleContract) GetProposalTreeRoot(ctx context.Context, block rpcblock.Block, ident keccakTypes.LargePreimageIdent) (common.Hash, error) { func (c *PreimageOracleContractLatest) GetProposalTreeRoot(ctx context.Context, block rpcblock.Block, ident keccakTypes.LargePreimageIdent) (common.Hash, error) {
call := c.contract.Call(methodGetTreeRootLPP, ident.Claimant, ident.UUID) call := c.contract.Call(methodGetTreeRootLPP, ident.Claimant, ident.UUID)
result, err := c.multiCaller.SingleCall(ctx, block, call) result, err := c.multiCaller.SingleCall(ctx, block, call)
if err != nil { if err != nil {
...@@ -265,7 +281,7 @@ func (c *PreimageOracleContract) GetProposalTreeRoot(ctx context.Context, block ...@@ -265,7 +281,7 @@ func (c *PreimageOracleContract) GetProposalTreeRoot(ctx context.Context, block
return result.GetHash(0), nil return result.GetHash(0), nil
} }
func (c *PreimageOracleContract) GetInputDataBlocks(ctx context.Context, block rpcblock.Block, ident keccakTypes.LargePreimageIdent) ([]uint64, error) { func (c *PreimageOracleContractLatest) GetInputDataBlocks(ctx context.Context, block rpcblock.Block, ident keccakTypes.LargePreimageIdent) ([]uint64, error) {
results, err := batching.ReadArray(ctx, c.multiCaller, block, results, err := batching.ReadArray(ctx, c.multiCaller, block,
c.contract.Call(methodProposalBlocksLen, ident.Claimant, ident.UUID), c.contract.Call(methodProposalBlocksLen, ident.Claimant, ident.UUID),
func(i *big.Int) *batching.ContractCall { func(i *big.Int) *batching.ContractCall {
...@@ -285,7 +301,7 @@ func (c *PreimageOracleContract) GetInputDataBlocks(ctx context.Context, block r ...@@ -285,7 +301,7 @@ func (c *PreimageOracleContract) GetInputDataBlocks(ctx context.Context, block r
// An [ErrInvalidAddLeavesCall] error is returned if the call is not a valid call to addLeavesLPP. // An [ErrInvalidAddLeavesCall] error is returned if the call is not a valid call to addLeavesLPP.
// Otherwise, the uuid and input data is returned. The raw data supplied is returned so long as it can be parsed. // Otherwise, the uuid and input data is returned. The raw data supplied is returned so long as it can be parsed.
// Specifically the length of the input data is not validated to ensure it is consistent with the number of commitments. // Specifically the length of the input data is not validated to ensure it is consistent with the number of commitments.
func (c *PreimageOracleContract) DecodeInputData(data []byte) (*big.Int, keccakTypes.InputData, error) { func (c *PreimageOracleContractLatest) DecodeInputData(data []byte) (*big.Int, keccakTypes.InputData, error) {
method, args, err := c.contract.DecodeCall(data) method, args, err := c.contract.DecodeCall(data)
if errors.Is(err, batching.ErrUnknownMethod) { if errors.Is(err, batching.ErrUnknownMethod) {
return nil, keccakTypes.InputData{}, ErrInvalidAddLeavesCall return nil, keccakTypes.InputData{}, ErrInvalidAddLeavesCall
...@@ -312,7 +328,7 @@ func (c *PreimageOracleContract) DecodeInputData(data []byte) (*big.Int, keccakT ...@@ -312,7 +328,7 @@ func (c *PreimageOracleContract) DecodeInputData(data []byte) (*big.Int, keccakT
}, nil }, nil
} }
func (c *PreimageOracleContract) GlobalDataExists(ctx context.Context, data *types.PreimageOracleData) (bool, error) { func (c *PreimageOracleContractLatest) GlobalDataExists(ctx context.Context, data *types.PreimageOracleData) (bool, error) {
call := c.contract.Call(methodPreimagePartOk, common.Hash(data.OracleKey), new(big.Int).SetUint64(uint64(data.OracleOffset))) call := c.contract.Call(methodPreimagePartOk, common.Hash(data.OracleKey), new(big.Int).SetUint64(uint64(data.OracleOffset)))
results, err := c.multiCaller.SingleCall(ctx, rpcblock.Latest, call) results, err := c.multiCaller.SingleCall(ctx, rpcblock.Latest, call)
if err != nil { if err != nil {
...@@ -321,7 +337,7 @@ func (c *PreimageOracleContract) GlobalDataExists(ctx context.Context, data *typ ...@@ -321,7 +337,7 @@ func (c *PreimageOracleContract) GlobalDataExists(ctx context.Context, data *typ
return results.GetBool(0), nil return results.GetBool(0), nil
} }
func (c *PreimageOracleContract) ChallengeTx(ident keccakTypes.LargePreimageIdent, challenge keccakTypes.Challenge) (txmgr.TxCandidate, error) { func (c *PreimageOracleContractLatest) ChallengeTx(ident keccakTypes.LargePreimageIdent, challenge keccakTypes.Challenge) (txmgr.TxCandidate, error) {
var call *batching.ContractCall var call *batching.ContractCall
if challenge.Prestate == (keccakTypes.Leaf{}) { if challenge.Prestate == (keccakTypes.Leaf{}) {
call = c.contract.Call( call = c.contract.Call(
...@@ -344,7 +360,7 @@ func (c *PreimageOracleContract) ChallengeTx(ident keccakTypes.LargePreimageIden ...@@ -344,7 +360,7 @@ func (c *PreimageOracleContract) ChallengeTx(ident keccakTypes.LargePreimageIden
return call.ToTxCandidate() return call.ToTxCandidate()
} }
func (c *PreimageOracleContract) GetMinBondLPP(ctx context.Context) (*big.Int, error) { func (c *PreimageOracleContractLatest) GetMinBondLPP(ctx context.Context) (*big.Int, error) {
if bondSize := c.minBondSizeLPP.Load(); bondSize != 0 { if bondSize := c.minBondSizeLPP.Load(); bondSize != 0 {
return big.NewInt(int64(bondSize)), nil return big.NewInt(int64(bondSize)), nil
} }
...@@ -357,7 +373,7 @@ func (c *PreimageOracleContract) GetMinBondLPP(ctx context.Context) (*big.Int, e ...@@ -357,7 +373,7 @@ func (c *PreimageOracleContract) GetMinBondLPP(ctx context.Context) (*big.Int, e
return period, nil return period, nil
} }
func (c *PreimageOracleContract) decodePreimageIdent(result *batching.CallResult) keccakTypes.LargePreimageIdent { func (c *PreimageOracleContractLatest) decodePreimageIdent(result *batching.CallResult) keccakTypes.LargePreimageIdent {
return keccakTypes.LargePreimageIdent{ return keccakTypes.LargePreimageIdent{
Claimant: result.GetAddress(0), Claimant: result.GetAddress(0),
UUID: result.GetBigInt(1), UUID: result.GetBigInt(1),
...@@ -428,3 +444,39 @@ func (m *metadata) setCountered(value bool) { ...@@ -428,3 +444,39 @@ func (m *metadata) setCountered(value bool) {
func (m *metadata) countered() bool { func (m *metadata) countered() bool {
return binary.BigEndian.Uint64(m[24:32]) != 0 return binary.BigEndian.Uint64(m[24:32]) != 0
} }
type PreimageOracleContract interface {
Addr() common.Address
AddGlobalDataTx(data *types.PreimageOracleData) (txmgr.TxCandidate, error)
InitLargePreimage(uuid *big.Int, partOffset uint32, claimedSize uint32) (txmgr.TxCandidate, error)
AddLeaves(uuid *big.Int, startingBlockIndex *big.Int, input []byte, commitments []common.Hash, finalize bool) (txmgr.TxCandidate, error)
MinLargePreimageSize(ctx context.Context) (uint64, error)
ChallengePeriod(ctx context.Context) (uint64, error)
CallSqueeze(
ctx context.Context,
claimant common.Address,
uuid *big.Int,
prestateMatrix keccakTypes.StateSnapshot,
preState keccakTypes.Leaf,
preStateProof merkle.Proof,
postState keccakTypes.Leaf,
postStateProof merkle.Proof,
) error
Squeeze(
claimant common.Address,
uuid *big.Int,
prestateMatrix keccakTypes.StateSnapshot,
preState keccakTypes.Leaf,
preStateProof merkle.Proof,
postState keccakTypes.Leaf,
postStateProof merkle.Proof,
) (txmgr.TxCandidate, error)
GetActivePreimages(ctx context.Context, blockHash common.Hash) ([]keccakTypes.LargePreimageMetaData, error)
GetProposalMetadata(ctx context.Context, block rpcblock.Block, idents ...keccakTypes.LargePreimageIdent) ([]keccakTypes.LargePreimageMetaData, error)
GetProposalTreeRoot(ctx context.Context, block rpcblock.Block, ident keccakTypes.LargePreimageIdent) (common.Hash, error)
GetInputDataBlocks(ctx context.Context, block rpcblock.Block, ident keccakTypes.LargePreimageIdent) ([]uint64, error)
DecodeInputData(data []byte) (*big.Int, keccakTypes.InputData, error)
GlobalDataExists(ctx context.Context, data *types.PreimageOracleData) (bool, error)
ChallengeTx(ident keccakTypes.LargePreimageIdent, challenge keccakTypes.Challenge) (txmgr.TxCandidate, error)
GetMinBondLPP(ctx context.Context) (*big.Int, error)
}
package contracts
import (
_ "embed"
"math/big"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
preimage "github.com/ethereum-optimism/optimism/op-preimage"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common"
)
//go:embed abis/PreimageOracle-1.0.0.json
var preimageOracleAbi100 []byte
type PreimageOracleContract100 struct {
PreimageOracleContractLatest
}
func (c *PreimageOracleContract100) AddGlobalDataTx(data *types.PreimageOracleData) (txmgr.TxCandidate, error) {
if len(data.OracleKey) == 0 || preimage.KeyType(data.OracleKey[0]) != preimage.PrecompileKeyType {
return c.PreimageOracleContractLatest.AddGlobalDataTx(data)
}
inputs := data.GetPreimageWithoutSize()
call := c.contract.Call(methodLoadPrecompilePreimagePart,
new(big.Int).SetUint64(uint64(data.OracleOffset)),
common.BytesToAddress(inputs[0:20]),
inputs[20:])
return call.ToTxCandidate()
}
...@@ -17,272 +17,367 @@ import ( ...@@ -17,272 +17,367 @@ import (
batchingTest "github.com/ethereum-optimism/optimism/op-service/sources/batching/test" batchingTest "github.com/ethereum-optimism/optimism/op-service/sources/batching/test"
"github.com/ethereum-optimism/optimism/op-service/testutils" "github.com/ethereum-optimism/optimism/op-service/testutils"
"github.com/ethereum-optimism/optimism/packages/contracts-bedrock/snapshots" "github.com/ethereum-optimism/optimism/packages/contracts-bedrock/snapshots"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
const (
oracle100 = "1.0.0"
oracleLatest = "1.1.0"
)
var oracleVersions = []contractVersion{
{
version: oracle100,
loadAbi: func() *abi.ABI {
return mustParseAbi(preimageOracleAbi100)
},
},
{
version: oracleLatest,
loadAbi: snapshots.LoadPreimageOracleABI,
},
}
func TestPreimageOracleContract_AddGlobalDataTx(t *testing.T) { func TestPreimageOracleContract_AddGlobalDataTx(t *testing.T) {
t.Run("UnknownType", func(t *testing.T) { for _, version := range oracleVersions {
_, oracle := setupPreimageOracleTest(t) version := version
data := types.NewPreimageOracleData(common.Hash{0xcc}.Bytes(), make([]byte, 20), uint32(545)) t.Run(version.version, func(t *testing.T) {
_, err := oracle.AddGlobalDataTx(data)
require.ErrorIs(t, err, ErrUnsupportedKeyType) t.Run("UnknownType", func(t *testing.T) {
}) _, oracle := setupPreimageOracleTest(t, version)
data := types.NewPreimageOracleData(common.Hash{0xcc}.Bytes(), make([]byte, 20), uint32(545))
t.Run("Keccak256", func(t *testing.T) { _, err := oracle.AddGlobalDataTx(data)
stubRpc, oracle := setupPreimageOracleTest(t) require.ErrorIs(t, err, ErrUnsupportedKeyType)
data := types.NewPreimageOracleData(common.Hash{byte(preimage.Keccak256KeyType), 0xcc}.Bytes(), make([]byte, 20), uint32(545)) })
stubRpc.SetResponse(oracleAddr, methodLoadKeccak256PreimagePart, rpcblock.Latest, []interface{}{
new(big.Int).SetUint64(uint64(data.OracleOffset)), t.Run("Keccak256", func(t *testing.T) {
data.GetPreimageWithoutSize(), stubRpc, oracle := setupPreimageOracleTest(t, version)
}, nil) data := types.NewPreimageOracleData(common.Hash{byte(preimage.Keccak256KeyType), 0xcc}.Bytes(), make([]byte, 20), uint32(545))
stubRpc.SetResponse(oracleAddr, methodLoadKeccak256PreimagePart, rpcblock.Latest, []interface{}{
tx, err := oracle.AddGlobalDataTx(data) new(big.Int).SetUint64(uint64(data.OracleOffset)),
require.NoError(t, err) data.GetPreimageWithoutSize(),
stubRpc.VerifyTxCandidate(tx) }, nil)
})
tx, err := oracle.AddGlobalDataTx(data)
t.Run("Sha256", func(t *testing.T) { require.NoError(t, err)
stubRpc, oracle := setupPreimageOracleTest(t) stubRpc.VerifyTxCandidate(tx)
data := types.NewPreimageOracleData(common.Hash{byte(preimage.Sha256KeyType), 0xcc}.Bytes(), make([]byte, 20), uint32(545)) })
stubRpc.SetResponse(oracleAddr, methodLoadSha256PreimagePart, rpcblock.Latest, []interface{}{
new(big.Int).SetUint64(uint64(data.OracleOffset)), t.Run("Sha256", func(t *testing.T) {
data.GetPreimageWithoutSize(), stubRpc, oracle := setupPreimageOracleTest(t, version)
}, nil) data := types.NewPreimageOracleData(common.Hash{byte(preimage.Sha256KeyType), 0xcc}.Bytes(), make([]byte, 20), uint32(545))
stubRpc.SetResponse(oracleAddr, methodLoadSha256PreimagePart, rpcblock.Latest, []interface{}{
tx, err := oracle.AddGlobalDataTx(data) new(big.Int).SetUint64(uint64(data.OracleOffset)),
require.NoError(t, err) data.GetPreimageWithoutSize(),
stubRpc.VerifyTxCandidate(tx) }, nil)
})
tx, err := oracle.AddGlobalDataTx(data)
t.Run("Blob", func(t *testing.T) { require.NoError(t, err)
stubRpc, oracle := setupPreimageOracleTest(t) stubRpc.VerifyTxCandidate(tx)
fieldData := testutils.RandomData(rand.New(rand.NewSource(23)), 32) })
data := types.NewPreimageOracleData(common.Hash{byte(preimage.BlobKeyType), 0xcc}.Bytes(), fieldData, uint32(545))
stubRpc.SetResponse(oracleAddr, methodLoadBlobPreimagePart, rpcblock.Latest, []interface{}{ t.Run("Blob", func(t *testing.T) {
new(big.Int).SetUint64(data.BlobFieldIndex), stubRpc, oracle := setupPreimageOracleTest(t, version)
new(big.Int).SetBytes(data.GetPreimageWithoutSize()), fieldData := testutils.RandomData(rand.New(rand.NewSource(23)), 32)
data.BlobCommitment, data := types.NewPreimageOracleData(common.Hash{byte(preimage.BlobKeyType), 0xcc}.Bytes(), fieldData, uint32(545))
data.BlobProof, stubRpc.SetResponse(oracleAddr, methodLoadBlobPreimagePart, rpcblock.Latest, []interface{}{
new(big.Int).SetUint64(uint64(data.OracleOffset)), new(big.Int).SetUint64(data.BlobFieldIndex),
}, nil) new(big.Int).SetBytes(data.GetPreimageWithoutSize()),
data.BlobCommitment,
tx, err := oracle.AddGlobalDataTx(data) data.BlobProof,
require.NoError(t, err) new(big.Int).SetUint64(uint64(data.OracleOffset)),
stubRpc.VerifyTxCandidate(tx) }, nil)
})
tx, err := oracle.AddGlobalDataTx(data)
t.Run("Precompile", func(t *testing.T) { require.NoError(t, err)
stubRpc, oracle := setupPreimageOracleTest(t) stubRpc.VerifyTxCandidate(tx)
input := testutils.RandomData(rand.New(rand.NewSource(23)), 200) })
data := types.NewPreimageOracleData(common.Hash{byte(preimage.PrecompileKeyType), 0xcc}.Bytes(), input, uint32(545))
stubRpc.SetResponse(oracleAddr, methodLoadPrecompilePreimagePart, rpcblock.Latest, []interface{}{ t.Run("Precompile", func(t *testing.T) {
new(big.Int).SetUint64(uint64(data.OracleOffset)), stubRpc, oracle := setupPreimageOracleTest(t, version)
data.GetPrecompileAddress(), input := testutils.RandomData(rand.New(rand.NewSource(23)), 200)
data.GetPrecompileInput(), data := types.NewPreimageOracleData(common.Hash{byte(preimage.PrecompileKeyType), 0xcc}.Bytes(), input, uint32(545))
}, nil) if version.Is(oracle100) {
keyData := data.GetPreimageWithoutSize()
tx, err := oracle.AddGlobalDataTx(data) stubRpc.SetResponse(oracleAddr, methodLoadPrecompilePreimagePart, rpcblock.Latest, []interface{}{
require.NoError(t, err) new(big.Int).SetUint64(uint64(data.OracleOffset)),
stubRpc.VerifyTxCandidate(tx) common.BytesToAddress(keyData[0:20]),
}) keyData[20:],
}, nil)
} else {
stubRpc.SetResponse(oracleAddr, methodLoadPrecompilePreimagePart, rpcblock.Latest, []interface{}{
new(big.Int).SetUint64(uint64(data.OracleOffset)),
data.GetPrecompileAddress(),
data.GetPrecompileRequiredGas(),
data.GetPrecompileInput(),
}, nil)
}
tx, err := oracle.AddGlobalDataTx(data)
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
})
})
}
} }
func TestPreimageOracleContract_ChallengePeriod(t *testing.T) { func TestPreimageOracleContract_ChallengePeriod(t *testing.T) {
stubRpc, oracle := setupPreimageOracleTest(t) for _, version := range oracleVersions {
stubRpc.SetResponse(oracleAddr, methodChallengePeriod, rpcblock.Latest, version := version
[]interface{}{}, t.Run(version.version, func(t *testing.T) {
[]interface{}{big.NewInt(123)},
) stubRpc, oracle := setupPreimageOracleTest(t, version)
challengePeriod, err := oracle.ChallengePeriod(context.Background()) stubRpc.SetResponse(oracleAddr, methodChallengePeriod, rpcblock.Latest,
require.NoError(t, err) []interface{}{},
require.Equal(t, uint64(123), challengePeriod) []interface{}{big.NewInt(123)},
)
// Should cache responses challengePeriod, err := oracle.ChallengePeriod(context.Background())
stubRpc.ClearResponses() require.NoError(t, err)
challengePeriod, err = oracle.ChallengePeriod(context.Background()) require.Equal(t, uint64(123), challengePeriod)
require.NoError(t, err)
require.Equal(t, uint64(123), challengePeriod) // Should cache responses
stubRpc.ClearResponses()
challengePeriod, err = oracle.ChallengePeriod(context.Background())
require.NoError(t, err)
require.Equal(t, uint64(123), challengePeriod)
})
}
} }
func TestPreimageOracleContract_MinLargePreimageSize(t *testing.T) { func TestPreimageOracleContract_MinLargePreimageSize(t *testing.T) {
stubRpc, oracle := setupPreimageOracleTest(t) for _, version := range oracleVersions {
stubRpc.SetResponse(oracleAddr, methodMinProposalSize, rpcblock.Latest, version := version
[]interface{}{}, t.Run(version.version, func(t *testing.T) {
[]interface{}{big.NewInt(123)},
) stubRpc, oracle := setupPreimageOracleTest(t, version)
minProposalSize, err := oracle.MinLargePreimageSize(context.Background()) stubRpc.SetResponse(oracleAddr, methodMinProposalSize, rpcblock.Latest,
require.NoError(t, err) []interface{}{},
require.Equal(t, uint64(123), minProposalSize) []interface{}{big.NewInt(123)},
)
minProposalSize, err := oracle.MinLargePreimageSize(context.Background())
require.NoError(t, err)
require.Equal(t, uint64(123), minProposalSize)
})
}
} }
func TestPreimageOracleContract_MinBondSizeLPP(t *testing.T) { func TestPreimageOracleContract_MinBondSizeLPP(t *testing.T) {
stubRpc, oracle := setupPreimageOracleTest(t) for _, version := range oracleVersions {
stubRpc.SetResponse(oracleAddr, methodMinBondSizeLPP, rpcblock.Latest, version := version
[]interface{}{}, t.Run(version.version, func(t *testing.T) {
[]interface{}{big.NewInt(123)},
) stubRpc, oracle := setupPreimageOracleTest(t, version)
minBond, err := oracle.GetMinBondLPP(context.Background()) stubRpc.SetResponse(oracleAddr, methodMinBondSizeLPP, rpcblock.Latest,
require.NoError(t, err) []interface{}{},
require.Equal(t, big.NewInt(123), minBond) []interface{}{big.NewInt(123)},
)
// Should cache responses minBond, err := oracle.GetMinBondLPP(context.Background())
stubRpc.ClearResponses() require.NoError(t, err)
minBond, err = oracle.GetMinBondLPP(context.Background()) require.Equal(t, big.NewInt(123), minBond)
require.NoError(t, err)
require.Equal(t, big.NewInt(123), minBond) // Should cache responses
stubRpc.ClearResponses()
minBond, err = oracle.GetMinBondLPP(context.Background())
require.NoError(t, err)
require.Equal(t, big.NewInt(123), minBond)
})
}
} }
func TestPreimageOracleContract_PreimageDataExists(t *testing.T) { func TestPreimageOracleContract_PreimageDataExists(t *testing.T) {
t.Run("exists", func(t *testing.T) { for _, version := range oracleVersions {
stubRpc, oracle := setupPreimageOracleTest(t) version := version
data := types.NewPreimageOracleData(common.Hash{0xcc}.Bytes(), make([]byte, 20), 545) t.Run(version.version, func(t *testing.T) {
stubRpc.SetResponse(oracleAddr, methodPreimagePartOk, rpcblock.Latest,
[]interface{}{common.Hash(data.OracleKey), new(big.Int).SetUint64(uint64(data.OracleOffset))}, t.Run("exists", func(t *testing.T) {
[]interface{}{true}, stubRpc, oracle := setupPreimageOracleTest(t, version)
) data := types.NewPreimageOracleData(common.Hash{0xcc}.Bytes(), make([]byte, 20), 545)
exists, err := oracle.GlobalDataExists(context.Background(), data) stubRpc.SetResponse(oracleAddr, methodPreimagePartOk, rpcblock.Latest,
require.NoError(t, err) []interface{}{common.Hash(data.OracleKey), new(big.Int).SetUint64(uint64(data.OracleOffset))},
require.True(t, exists) []interface{}{true},
}) )
t.Run("does not exist", func(t *testing.T) { exists, err := oracle.GlobalDataExists(context.Background(), data)
stubRpc, oracle := setupPreimageOracleTest(t) require.NoError(t, err)
data := types.NewPreimageOracleData(common.Hash{0xcc}.Bytes(), make([]byte, 20), 545) require.True(t, exists)
stubRpc.SetResponse(oracleAddr, methodPreimagePartOk, rpcblock.Latest, })
[]interface{}{common.Hash(data.OracleKey), new(big.Int).SetUint64(uint64(data.OracleOffset))}, t.Run("does not exist", func(t *testing.T) {
[]interface{}{false}, stubRpc, oracle := setupPreimageOracleTest(t, version)
) data := types.NewPreimageOracleData(common.Hash{0xcc}.Bytes(), make([]byte, 20), 545)
exists, err := oracle.GlobalDataExists(context.Background(), data) stubRpc.SetResponse(oracleAddr, methodPreimagePartOk, rpcblock.Latest,
require.NoError(t, err) []interface{}{common.Hash(data.OracleKey), new(big.Int).SetUint64(uint64(data.OracleOffset))},
require.False(t, exists) []interface{}{false},
}) )
exists, err := oracle.GlobalDataExists(context.Background(), data)
require.NoError(t, err)
require.False(t, exists)
})
})
}
} }
func TestPreimageOracleContract_InitLargePreimage(t *testing.T) { func TestPreimageOracleContract_InitLargePreimage(t *testing.T) {
stubRpc, oracle := setupPreimageOracleTest(t) for _, version := range oracleVersions {
version := version
uuid := big.NewInt(123) t.Run(version.version, func(t *testing.T) {
partOffset := uint32(1)
claimedSize := uint32(2) stubRpc, oracle := setupPreimageOracleTest(t, version)
bond := big.NewInt(42984)
stubRpc.SetResponse(oracleAddr, methodMinBondSizeLPP, rpcblock.Latest, nil, []interface{}{bond}) uuid := big.NewInt(123)
stubRpc.SetResponse(oracleAddr, methodInitLPP, rpcblock.Latest, []interface{}{ partOffset := uint32(1)
uuid, claimedSize := uint32(2)
partOffset, bond := big.NewInt(42984)
claimedSize, stubRpc.SetResponse(oracleAddr, methodMinBondSizeLPP, rpcblock.Latest, nil, []interface{}{bond})
}, nil) stubRpc.SetResponse(oracleAddr, methodInitLPP, rpcblock.Latest, []interface{}{
uuid,
tx, err := oracle.InitLargePreimage(uuid, partOffset, claimedSize) partOffset,
require.NoError(t, err) claimedSize,
stubRpc.VerifyTxCandidate(tx) }, nil)
require.Truef(t, bond.Cmp(tx.Value) == 0, "Expected bond %v got %v", bond, tx.Value)
tx, err := oracle.InitLargePreimage(uuid, partOffset, claimedSize)
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
require.Truef(t, bond.Cmp(tx.Value) == 0, "Expected bond %v got %v", bond, tx.Value)
})
}
} }
func TestPreimageOracleContract_AddLeaves(t *testing.T) { func TestPreimageOracleContract_AddLeaves(t *testing.T) {
stubRpc, oracle := setupPreimageOracleTest(t) for _, version := range oracleVersions {
version := version
uuid := big.NewInt(123) t.Run(version.version, func(t *testing.T) {
startingBlockIndex := big.NewInt(0)
input := []byte{0x12} stubRpc, oracle := setupPreimageOracleTest(t, version)
commitments := []common.Hash{{0x34}}
finalize := true uuid := big.NewInt(123)
stubRpc.SetResponse(oracleAddr, methodAddLeavesLPP, rpcblock.Latest, []interface{}{ startingBlockIndex := big.NewInt(0)
uuid, input := []byte{0x12}
startingBlockIndex, commitments := []common.Hash{{0x34}}
input, finalize := true
commitments, stubRpc.SetResponse(oracleAddr, methodAddLeavesLPP, rpcblock.Latest, []interface{}{
finalize, uuid,
}, nil) startingBlockIndex,
input,
tx, err := oracle.AddLeaves(uuid, startingBlockIndex, input, commitments, finalize) commitments,
require.NoError(t, err) finalize,
stubRpc.VerifyTxCandidate(tx) }, nil)
tx, err := oracle.AddLeaves(uuid, startingBlockIndex, input, commitments, finalize)
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
})
}
} }
func TestPreimageOracleContract_Squeeze(t *testing.T) { func TestPreimageOracleContract_Squeeze(t *testing.T) {
stubRpc, oracle := setupPreimageOracleTest(t) for _, version := range oracleVersions {
version := version
claimant := common.Address{0x12} t.Run(version.version, func(t *testing.T) {
uuid := big.NewInt(123)
preStateMatrix := keccakTypes.StateSnapshot{0, 1, 2, 3, 4} stubRpc, oracle := setupPreimageOracleTest(t, version)
preState := keccakTypes.Leaf{
Input: [keccakTypes.BlockSize]byte{0x12}, claimant := common.Address{0x12}
Index: 123, uuid := big.NewInt(123)
StateCommitment: common.Hash{0x34}, preStateMatrix := keccakTypes.StateSnapshot{0, 1, 2, 3, 4}
} preState := keccakTypes.Leaf{
preStateProof := merkle.Proof{{0x34}} Input: [keccakTypes.BlockSize]byte{0x12},
postState := keccakTypes.Leaf{ Index: 123,
Input: [keccakTypes.BlockSize]byte{0x34}, StateCommitment: common.Hash{0x34},
Index: 456, }
StateCommitment: common.Hash{0x56}, preStateProof := merkle.Proof{{0x34}}
postState := keccakTypes.Leaf{
Input: [keccakTypes.BlockSize]byte{0x34},
Index: 456,
StateCommitment: common.Hash{0x56},
}
postStateProof := merkle.Proof{{0x56}}
stubRpc.SetResponse(oracleAddr, methodSqueezeLPP, rpcblock.Latest, []interface{}{
claimant,
uuid,
abiEncodeSnapshot(preStateMatrix),
toPreimageOracleLeaf(preState),
preStateProof,
toPreimageOracleLeaf(postState),
postStateProof,
}, nil)
tx, err := oracle.Squeeze(claimant, uuid, preStateMatrix, preState, preStateProof, postState, postStateProof)
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
})
} }
postStateProof := merkle.Proof{{0x56}}
stubRpc.SetResponse(oracleAddr, methodSqueezeLPP, rpcblock.Latest, []interface{}{
claimant,
uuid,
abiEncodeSnapshot(preStateMatrix),
toPreimageOracleLeaf(preState),
preStateProof,
toPreimageOracleLeaf(postState),
postStateProof,
}, nil)
tx, err := oracle.Squeeze(claimant, uuid, preStateMatrix, preState, preStateProof, postState, postStateProof)
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
} }
func TestGetActivePreimages(t *testing.T) { func TestGetActivePreimages(t *testing.T) {
blockHash := common.Hash{0xaa} for _, version := range oracleVersions {
_, oracle, proposals := setupPreimageOracleTestWithProposals(t, rpcblock.ByHash(blockHash)) version := version
preimages, err := oracle.GetActivePreimages(context.Background(), blockHash) t.Run(version.version, func(t *testing.T) {
require.NoError(t, err)
require.Equal(t, proposals, preimages) blockHash := common.Hash{0xaa}
_, oracle, proposals := setupPreimageOracleTestWithProposals(t, version, rpcblock.ByHash(blockHash))
preimages, err := oracle.GetActivePreimages(context.Background(), blockHash)
require.NoError(t, err)
require.Equal(t, proposals, preimages)
})
}
} }
func TestGetProposalMetadata(t *testing.T) { func TestGetProposalMetadata(t *testing.T) {
blockHash := common.Hash{0xaa} for _, version := range oracleVersions {
block := rpcblock.ByHash(blockHash) version := version
stubRpc, oracle, proposals := setupPreimageOracleTestWithProposals(t, block) t.Run(version.version, func(t *testing.T) {
preimages, err := oracle.GetProposalMetadata(
context.Background(), blockHash := common.Hash{0xaa}
block, block := rpcblock.ByHash(blockHash)
proposals[0].LargePreimageIdent, stubRpc, oracle, proposals := setupPreimageOracleTestWithProposals(t, version, block)
proposals[1].LargePreimageIdent, preimages, err := oracle.GetProposalMetadata(
proposals[2].LargePreimageIdent, context.Background(),
) block,
require.NoError(t, err) proposals[0].LargePreimageIdent,
require.Equal(t, proposals, preimages) proposals[1].LargePreimageIdent,
proposals[2].LargePreimageIdent,
// Fetching a proposal that doesn't exist should return an empty metadata object. )
ident := keccakTypes.LargePreimageIdent{Claimant: common.Address{0x12}, UUID: big.NewInt(123)} require.NoError(t, err)
meta := new(metadata) require.Equal(t, proposals, preimages)
stubRpc.SetResponse(
oracleAddr, // Fetching a proposal that doesn't exist should return an empty metadata object.
methodProposalMetadata, ident := keccakTypes.LargePreimageIdent{Claimant: common.Address{0x12}, UUID: big.NewInt(123)}
block, meta := new(metadata)
[]interface{}{ident.Claimant, ident.UUID}, stubRpc.SetResponse(
[]interface{}{meta}) oracleAddr,
preimages, err = oracle.GetProposalMetadata(context.Background(), rpcblock.ByHash(blockHash), ident) methodProposalMetadata,
require.NoError(t, err) block,
require.Equal(t, []keccakTypes.LargePreimageMetaData{{LargePreimageIdent: ident}}, preimages) []interface{}{ident.Claimant, ident.UUID},
[]interface{}{meta})
preimages, err = oracle.GetProposalMetadata(context.Background(), rpcblock.ByHash(blockHash), ident)
require.NoError(t, err)
require.Equal(t, []keccakTypes.LargePreimageMetaData{{LargePreimageIdent: ident}}, preimages)
})
}
} }
func TestGetProposalTreeRoot(t *testing.T) { func TestGetProposalTreeRoot(t *testing.T) {
blockHash := common.Hash{0xaa} for _, version := range oracleVersions {
expectedRoot := common.Hash{0xbb} version := version
ident := keccakTypes.LargePreimageIdent{Claimant: common.Address{0x12}, UUID: big.NewInt(123)} t.Run(version.version, func(t *testing.T) {
stubRpc, oracle := setupPreimageOracleTest(t)
stubRpc.SetResponse(oracleAddr, methodGetTreeRootLPP, rpcblock.ByHash(blockHash), blockHash := common.Hash{0xaa}
[]interface{}{ident.Claimant, ident.UUID}, expectedRoot := common.Hash{0xbb}
[]interface{}{expectedRoot}) ident := keccakTypes.LargePreimageIdent{Claimant: common.Address{0x12}, UUID: big.NewInt(123)}
actualRoot, err := oracle.GetProposalTreeRoot(context.Background(), rpcblock.ByHash(blockHash), ident) stubRpc, oracle := setupPreimageOracleTest(t, version)
require.NoError(t, err) stubRpc.SetResponse(oracleAddr, methodGetTreeRootLPP, rpcblock.ByHash(blockHash),
require.Equal(t, expectedRoot, actualRoot) []interface{}{ident.Claimant, ident.UUID},
[]interface{}{expectedRoot})
actualRoot, err := oracle.GetProposalTreeRoot(context.Background(), rpcblock.ByHash(blockHash), ident)
require.NoError(t, err)
require.Equal(t, expectedRoot, actualRoot)
})
}
} }
func setupPreimageOracleTestWithProposals(t *testing.T, block rpcblock.Block) (*batchingTest.AbiBasedRpc, *PreimageOracleContract, []keccakTypes.LargePreimageMetaData) { func setupPreimageOracleTestWithProposals(t *testing.T, version contractVersion, block rpcblock.Block) (*batchingTest.AbiBasedRpc, PreimageOracleContract, []keccakTypes.LargePreimageMetaData) {
stubRpc, oracle := setupPreimageOracleTest(t) stubRpc, oracle := setupPreimageOracleTest(t, version)
stubRpc.SetResponse( stubRpc.SetResponse(
oracleAddr, oracleAddr,
methodProposalCount, methodProposalCount,
...@@ -357,11 +452,11 @@ func setupPreimageOracleTestWithProposals(t *testing.T, block rpcblock.Block) (* ...@@ -357,11 +452,11 @@ func setupPreimageOracleTestWithProposals(t *testing.T, block rpcblock.Block) (*
return stubRpc, oracle, proposals return stubRpc, oracle, proposals
} }
func setupPreimageOracleTest(t *testing.T) (*batchingTest.AbiBasedRpc, *PreimageOracleContract) { func setupPreimageOracleTest(t *testing.T, version contractVersion) (*batchingTest.AbiBasedRpc, PreimageOracleContract) {
oracleAbi := snapshots.LoadPreimageOracleABI() stubRpc := batchingTest.NewAbiBasedRpc(t, oracleAddr, version.loadAbi())
stubRpc.SetResponse(oracleAddr, methodVersion, rpcblock.Latest, nil, []interface{}{version.version})
stubRpc := batchingTest.NewAbiBasedRpc(t, oracleAddr, oracleAbi) oracleContract, err := NewPreimageOracleContract(context.Background(), oracleAddr, batching.NewMultiCaller(stubRpc, batching.DefaultBatchSize))
oracleContract := NewPreimageOracleContract(oracleAddr, batching.NewMultiCaller(stubRpc, batching.DefaultBatchSize)) require.NoError(t, err)
return stubRpc, oracleContract return stubRpc, oracleContract
} }
...@@ -396,68 +491,93 @@ func TestMetadata(t *testing.T) { ...@@ -396,68 +491,93 @@ func TestMetadata(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
test := test test := test
for _, value := range uint32Values {
value := value for _, version := range oracleVersions {
t.Run(fmt.Sprintf("%v-%v", test.name, value), func(t *testing.T) { version := version
meta := new(metadata) t.Run(version.version, func(t *testing.T) {
require.Zero(t, test.getter(meta))
test.setter(meta, value) for _, value := range uint32Values {
require.Equal(t, value, test.getter(meta)) value := value
t.Run(fmt.Sprintf("%v-%v", test.name, value), func(t *testing.T) {
meta := new(metadata)
require.Zero(t, test.getter(meta))
test.setter(meta, value)
require.Equal(t, value, test.getter(meta))
})
}
}) })
} }
} }
} }
func TestMetadata_Timestamp(t *testing.T) { func TestMetadata_Timestamp(t *testing.T) {
values := []uint64{0, 1, 2, 3252354, math.MaxUint32, math.MaxUint32 + 1, math.MaxUint64} for _, version := range oracleVersions {
var meta metadata version := version
require.Zero(t, meta.timestamp()) t.Run(version.version, func(t *testing.T) {
for _, value := range values {
meta.setTimestamp(value) values := []uint64{0, 1, 2, 3252354, math.MaxUint32, math.MaxUint32 + 1, math.MaxUint64}
require.Equal(t, value, meta.timestamp()) var meta metadata
require.Zero(t, meta.timestamp())
for _, value := range values {
meta.setTimestamp(value)
require.Equal(t, value, meta.timestamp())
}
})
} }
} }
func TestMetadata_Countered(t *testing.T) { func TestMetadata_Countered(t *testing.T) {
var meta metadata for _, version := range oracleVersions {
require.False(t, meta.countered()) version := version
meta.setCountered(true) t.Run(version.version, func(t *testing.T) {
require.True(t, meta.countered())
meta.setCountered(false) var meta metadata
require.False(t, meta.countered()) require.False(t, meta.countered())
meta.setCountered(true)
require.True(t, meta.countered())
meta.setCountered(false)
require.False(t, meta.countered())
})
}
} }
func TestGetInputDataBlocks(t *testing.T) { func TestGetInputDataBlocks(t *testing.T) {
stubRpc, oracle := setupPreimageOracleTest(t) for _, version := range oracleVersions {
block := rpcblock.ByHash(common.Hash{0xaa}) version := version
t.Run(version.version, func(t *testing.T) {
preimage := keccakTypes.LargePreimageIdent{ stubRpc, oracle := setupPreimageOracleTest(t, version)
Claimant: common.Address{0xbb}, block := rpcblock.ByHash(common.Hash{0xaa})
UUID: big.NewInt(2222),
}
stubRpc.SetResponse( preimage := keccakTypes.LargePreimageIdent{
oracleAddr, Claimant: common.Address{0xbb},
methodProposalBlocksLen, UUID: big.NewInt(2222),
block, }
[]interface{}{preimage.Claimant, preimage.UUID},
[]interface{}{big.NewInt(3)})
blockNums := []uint64{10, 35, 67} stubRpc.SetResponse(
oracleAddr,
methodProposalBlocksLen,
block,
[]interface{}{preimage.Claimant, preimage.UUID},
[]interface{}{big.NewInt(3)})
blockNums := []uint64{10, 35, 67}
for i, blockNum := range blockNums {
stubRpc.SetResponse(
oracleAddr,
methodProposalBlocks,
block,
[]interface{}{preimage.Claimant, preimage.UUID, big.NewInt(int64(i))},
[]interface{}{blockNum})
}
for i, blockNum := range blockNums { actual, err := oracle.GetInputDataBlocks(context.Background(), block, preimage)
stubRpc.SetResponse( require.NoError(t, err)
oracleAddr, require.Len(t, actual, 3)
methodProposalBlocks, require.Equal(t, blockNums, actual)
block, })
[]interface{}{preimage.Claimant, preimage.UUID, big.NewInt(int64(i))},
[]interface{}{blockNum})
} }
actual, err := oracle.GetInputDataBlocks(context.Background(), block, preimage)
require.NoError(t, err)
require.Len(t, actual, 3)
require.Equal(t, blockNums, actual)
} }
func TestDecodeInputData(t *testing.T) { func TestDecodeInputData(t *testing.T) {
...@@ -472,7 +592,6 @@ func TestDecodeInputData(t *testing.T) { ...@@ -472,7 +592,6 @@ func TestDecodeInputData(t *testing.T) {
Claimant: common.Address{0xaa}, Claimant: common.Address{0xaa},
UUID: big.NewInt(1111), UUID: big.NewInt(1111),
} }
_, oracle := setupPreimageOracleTest(t)
tests := []struct { tests := []struct {
name string name string
...@@ -553,106 +672,125 @@ func TestDecodeInputData(t *testing.T) { ...@@ -553,106 +672,125 @@ func TestDecodeInputData(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
test := test test := test
t.Run(test.name, func(t *testing.T) { for _, version := range oracleVersions {
var input []byte version := version
if len(test.input) > 0 { t.Run(version.version, func(t *testing.T) {
input = test.input
} else { t.Run(test.name, func(t *testing.T) {
input = toAddLeavesTxData(t, oracle, ident.UUID, test.inputData) _, oracle := setupPreimageOracleTest(t, version)
} var input []byte
require.Equal(t, test.expectedTxData, common.Bytes2Hex(input), if len(test.input) > 0 {
"ABI has been changed. Add tests to ensure historic transactions can be parsed before updating expectedTxData") input = test.input
uuid, leaves, err := oracle.DecodeInputData(input) } else {
if test.expectedErr != nil { input = toAddLeavesTxData(t, oracle, ident.UUID, test.inputData)
require.ErrorIs(t, err, test.expectedErr) }
} else { require.Equal(t, test.expectedTxData, common.Bytes2Hex(input),
require.NoError(t, err) "ABI has been changed. Add tests to ensure historic transactions can be parsed before updating expectedTxData")
require.Equal(t, ident.UUID, uuid) uuid, leaves, err := oracle.DecodeInputData(input)
require.Equal(t, test.inputData, leaves) if test.expectedErr != nil {
} require.ErrorIs(t, err, test.expectedErr)
}) } else {
require.NoError(t, err)
require.Equal(t, ident.UUID, uuid)
require.Equal(t, test.inputData, leaves)
}
})
})
}
} }
} }
func TestChallenge_First(t *testing.T) { func TestChallenge_First(t *testing.T) {
stubRpc, oracle := setupPreimageOracleTest(t) for _, version := range oracleVersions {
version := version
t.Run(version.version, func(t *testing.T) {
ident := keccakTypes.LargePreimageIdent{ stubRpc, oracle := setupPreimageOracleTest(t, version)
Claimant: common.Address{0xab},
UUID: big.NewInt(4829), ident := keccakTypes.LargePreimageIdent{
} Claimant: common.Address{0xab},
challenge := keccakTypes.Challenge{ UUID: big.NewInt(4829),
StateMatrix: keccakTypes.StateSnapshot{1, 2, 3, 4, 5}, }
Prestate: keccakTypes.Leaf{}, challenge := keccakTypes.Challenge{
Poststate: keccakTypes.Leaf{ StateMatrix: keccakTypes.StateSnapshot{1, 2, 3, 4, 5},
Input: [136]byte{5, 4, 3, 2, 1}, Prestate: keccakTypes.Leaf{},
Index: 0, Poststate: keccakTypes.Leaf{
StateCommitment: common.Hash{0xbb}, Input: [136]byte{5, 4, 3, 2, 1},
}, Index: 0,
PoststateProof: merkle.Proof{common.Hash{0x01}, common.Hash{0x02}}, StateCommitment: common.Hash{0xbb},
},
PoststateProof: merkle.Proof{common.Hash{0x01}, common.Hash{0x02}},
}
stubRpc.SetResponse(oracleAddr, methodChallengeFirstLPP, rpcblock.Latest,
[]interface{}{
ident.Claimant, ident.UUID,
preimageOracleLeaf{
Input: challenge.Poststate.Input[:],
Index: new(big.Int).SetUint64(challenge.Poststate.Index),
StateCommitment: challenge.Poststate.StateCommitment,
},
challenge.PoststateProof,
},
nil)
tx, err := oracle.ChallengeTx(ident, challenge)
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
})
} }
stubRpc.SetResponse(oracleAddr, methodChallengeFirstLPP, rpcblock.Latest,
[]interface{}{
ident.Claimant, ident.UUID,
preimageOracleLeaf{
Input: challenge.Poststate.Input[:],
Index: new(big.Int).SetUint64(challenge.Poststate.Index),
StateCommitment: challenge.Poststate.StateCommitment,
},
challenge.PoststateProof,
},
nil)
tx, err := oracle.ChallengeTx(ident, challenge)
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
} }
func TestChallenge_NotFirst(t *testing.T) { func TestChallenge_NotFirst(t *testing.T) {
stubRpc, oracle := setupPreimageOracleTest(t) for _, version := range oracleVersions {
version := version
t.Run(version.version, func(t *testing.T) {
ident := keccakTypes.LargePreimageIdent{ stubRpc, oracle := setupPreimageOracleTest(t, version)
Claimant: common.Address{0xab},
UUID: big.NewInt(4829), ident := keccakTypes.LargePreimageIdent{
} Claimant: common.Address{0xab},
challenge := keccakTypes.Challenge{ UUID: big.NewInt(4829),
StateMatrix: keccakTypes.StateSnapshot{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25}, }
Prestate: keccakTypes.Leaf{ challenge := keccakTypes.Challenge{
Input: [136]byte{9, 8, 7, 6, 5}, StateMatrix: keccakTypes.StateSnapshot{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25},
Index: 3, Prestate: keccakTypes.Leaf{
StateCommitment: common.Hash{0xcc}, Input: [136]byte{9, 8, 7, 6, 5},
}, Index: 3,
PrestateProof: merkle.Proof{common.Hash{0x01}, common.Hash{0x02}}, StateCommitment: common.Hash{0xcc},
Poststate: keccakTypes.Leaf{ },
Input: [136]byte{5, 4, 3, 2, 1}, PrestateProof: merkle.Proof{common.Hash{0x01}, common.Hash{0x02}},
Index: 4, Poststate: keccakTypes.Leaf{
StateCommitment: common.Hash{0xbb}, Input: [136]byte{5, 4, 3, 2, 1},
}, Index: 4,
PoststateProof: merkle.Proof{common.Hash{0x03}, common.Hash{0x04}}, StateCommitment: common.Hash{0xbb},
},
PoststateProof: merkle.Proof{common.Hash{0x03}, common.Hash{0x04}},
}
stubRpc.SetResponse(oracleAddr, methodChallengeLPP, rpcblock.Latest,
[]interface{}{
ident.Claimant, ident.UUID,
libKeccakStateMatrix{State: challenge.StateMatrix},
preimageOracleLeaf{
Input: challenge.Prestate.Input[:],
Index: new(big.Int).SetUint64(challenge.Prestate.Index),
StateCommitment: challenge.Prestate.StateCommitment,
},
challenge.PrestateProof,
preimageOracleLeaf{
Input: challenge.Poststate.Input[:],
Index: new(big.Int).SetUint64(challenge.Poststate.Index),
StateCommitment: challenge.Poststate.StateCommitment,
},
challenge.PoststateProof,
},
nil)
tx, err := oracle.ChallengeTx(ident, challenge)
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
})
} }
stubRpc.SetResponse(oracleAddr, methodChallengeLPP, rpcblock.Latest,
[]interface{}{
ident.Claimant, ident.UUID,
libKeccakStateMatrix{State: challenge.StateMatrix},
preimageOracleLeaf{
Input: challenge.Prestate.Input[:],
Index: new(big.Int).SetUint64(challenge.Prestate.Index),
StateCommitment: challenge.Prestate.StateCommitment,
},
challenge.PrestateProof,
preimageOracleLeaf{
Input: challenge.Poststate.Input[:],
Index: new(big.Int).SetUint64(challenge.Poststate.Index),
StateCommitment: challenge.Poststate.StateCommitment,
},
challenge.PoststateProof,
},
nil)
tx, err := oracle.ChallengeTx(ident, challenge)
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
} }
func toAddLeavesTxData(t *testing.T, oracle *PreimageOracleContract, uuid *big.Int, inputData keccakTypes.InputData) []byte { func toAddLeavesTxData(t *testing.T, oracle PreimageOracleContract, uuid *big.Int, inputData keccakTypes.InputData) []byte {
tx, err := oracle.AddLeaves(uuid, big.NewInt(1), inputData.Input, inputData.Commitments, inputData.Finalize) tx, err := oracle.AddLeaves(uuid, big.NewInt(1), inputData.Input, inputData.Commitments, inputData.Finalize)
require.NoError(t, err) require.NoError(t, err)
return tx.TxData return tx.TxData
......
package contracts
import (
"context"
"fmt"
"strings"
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
)
var (
methodVersion = "version"
)
type version[C any] struct {
prefixes string
factory func() (C, error)
}
type VersionedBuilder[C any] struct {
versions []version[C]
}
func (v *VersionedBuilder[C]) AddVersion(major int, minor int, factory func() (C, error)) {
v.versions = append(v.versions, version[C]{fmt.Sprintf("%d.%d.", major, minor), factory})
}
func (v *VersionedBuilder[C]) Build(ctx context.Context, caller *batching.MultiCaller, contractAbi *abi.ABI, addr common.Address, defaultVersion func() (C, error)) (C, error) {
var nilC C
result, err := caller.SingleCall(ctx, rpcblock.Latest, batching.NewContractCall(contractAbi, addr, methodVersion))
if err != nil {
return nilC, fmt.Errorf("failed to retrieve version of dispute game %v: %w", addr, err)
}
contractVersion := result.GetString(0)
for _, version := range v.versions {
if strings.HasPrefix(contractVersion, version.prefixes) {
return version.factory()
}
}
return defaultVersion()
}
package contracts
import (
"context"
"testing"
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock"
batchingTest "github.com/ethereum-optimism/optimism/op-service/sources/batching/test"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
)
var versionABI = `[{
"inputs": [],
"name": "version",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
}]`
func TestVersionedBuilder(t *testing.T) {
var builder VersionedBuilder[string]
builder.AddVersion(1, 1, func() (string, error) { return "v1.1", nil })
builder.AddVersion(1, 2, func() (string, error) { return "v1.2", nil })
require.Equal(t, "v1.1", buildWithVersion(t, builder, "1.1.0"))
require.Equal(t, "v1.1", buildWithVersion(t, builder, "1.1.1"))
require.Equal(t, "v1.1", buildWithVersion(t, builder, "1.1.2"))
require.Equal(t, "default", buildWithVersion(t, builder, "1.10.0"))
}
func buildWithVersion(t *testing.T, builder VersionedBuilder[string], version string) string {
addr := common.Address{0xaa}
contractABI := mustParseAbi(([]byte)(versionABI))
stubRpc := batchingTest.NewAbiBasedRpc(t, addr, contractABI)
stubRpc.SetResponse(addr, methodVersion, rpcblock.Latest, nil, []interface{}{version})
actual, err := builder.Build(context.Background(), batching.NewMultiCaller(stubRpc, batching.DefaultBatchSize), contractABI, addr, func() (string, error) {
return "default", nil
})
require.NoError(t, err)
return actual
}
...@@ -33,10 +33,10 @@ func (c *VMContract) Addr() common.Address { ...@@ -33,10 +33,10 @@ func (c *VMContract) Addr() common.Address {
return c.contract.Addr() return c.contract.Addr()
} }
func (c *VMContract) Oracle(ctx context.Context) (*PreimageOracleContract, error) { func (c *VMContract) Oracle(ctx context.Context) (PreimageOracleContract, error) {
results, err := c.multiCaller.SingleCall(ctx, rpcblock.Latest, c.contract.Call(methodOracle)) results, err := c.multiCaller.SingleCall(ctx, rpcblock.Latest, c.contract.Call(methodOracle))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to load oracle address: %w", err) return nil, fmt.Errorf("failed to load oracle address: %w", err)
} }
return NewPreimageOracleContract(results.GetAddress(0), c.multiCaller), nil return NewPreimageOracleContract(ctx, results.GetAddress(0), c.multiCaller)
} }
...@@ -21,6 +21,8 @@ func TestVMContract_Oracle(t *testing.T) { ...@@ -21,6 +21,8 @@ func TestVMContract_Oracle(t *testing.T) {
vmContract := NewVMContract(vmAddr, batching.NewMultiCaller(stubRpc, batching.DefaultBatchSize)) vmContract := NewVMContract(vmAddr, batching.NewMultiCaller(stubRpc, batching.DefaultBatchSize))
stubRpc.SetResponse(vmAddr, methodOracle, rpcblock.Latest, nil, []interface{}{oracleAddr}) stubRpc.SetResponse(vmAddr, methodOracle, rpcblock.Latest, nil, []interface{}{oracleAddr})
stubRpc.AddContract(oracleAddr, snapshots.LoadPreimageOracleABI())
stubRpc.SetResponse(oracleAddr, methodVersion, rpcblock.Latest, nil, []interface{}{oracleLatest})
oracleContract, err := vmContract.Oracle(context.Background()) oracleContract, err := vmContract.Oracle(context.Background())
require.NoError(t, err) require.NoError(t, err)
......
...@@ -60,7 +60,7 @@ type GameContract interface { ...@@ -60,7 +60,7 @@ type GameContract interface {
GetStatus(ctx context.Context) (gameTypes.GameStatus, error) GetStatus(ctx context.Context) (gameTypes.GameStatus, error)
GetMaxGameDepth(ctx context.Context) (types.Depth, error) GetMaxGameDepth(ctx context.Context) (types.Depth, error)
GetMaxClockDuration(ctx context.Context) (time.Duration, error) GetMaxClockDuration(ctx context.Context) (time.Duration, error)
GetOracle(ctx context.Context) (*contracts.PreimageOracleContract, error) GetOracle(ctx context.Context) (contracts.PreimageOracleContract, error)
GetL1Head(ctx context.Context) (common.Hash, error) GetL1Head(ctx context.Context) (common.Hash, error)
} }
......
...@@ -2,6 +2,7 @@ package types ...@@ -2,6 +2,7 @@ package types
import ( import (
"context" "context"
"encoding/binary"
"errors" "errors"
"fmt" "fmt"
"math" "math"
...@@ -149,8 +150,12 @@ func (p *PreimageOracleData) GetPrecompileAddress() common.Address { ...@@ -149,8 +150,12 @@ func (p *PreimageOracleData) GetPrecompileAddress() common.Address {
return common.BytesToAddress(p.oracleData[8:28]) return common.BytesToAddress(p.oracleData[8:28])
} }
func (p *PreimageOracleData) GetPrecompileRequiredGas() uint64 {
return binary.BigEndian.Uint64(p.oracleData[28:36])
}
func (p *PreimageOracleData) GetPrecompileInput() []byte { func (p *PreimageOracleData) GetPrecompileInput() []byte {
return p.oracleData[28:] return p.oracleData[36:]
} }
// NewPreimageOracleData creates a new [PreimageOracleData] instance. // NewPreimageOracleData creates a new [PreimageOracleData] instance.
......
// Code generated - DO NOT EDIT.
// This file is a generated binding and any manual changes will be lost.
package bindings
import (
"errors"
"math/big"
"strings"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
)
// Reference imports to suppress errors if they are not otherwise used.
var (
_ = errors.New
_ = big.NewInt
_ = strings.NewReader
_ = ethereum.NotFound
_ = bind.Bind
_ = common.Big1
_ = types.BloomLookup
_ = event.NewSubscription
)
// LibKeccakStateMatrix is an auto generated low-level Go binding around an user-defined struct.
type LibKeccakStateMatrix struct {
State [25]uint64
}
// PreimageOracleLeaf is an auto generated low-level Go binding around an user-defined struct.
type PreimageOracleLeaf struct {
Input []byte
Index *big.Int
StateCommitment [32]byte
}
// PreimageOracleMetaData contains all meta data concerning the PreimageOracle contract.
var PreimageOracleMetaData = &bind.MetaData{
ABI: "[{\"type\":\"constructor\",\"inputs\":[{\"name\":\"_minProposalSize\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_challengePeriod\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"KECCAK_TREE_DEPTH\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"MAX_LEAF_COUNT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"MIN_BOND_SIZE\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"addLeavesLPP\",\"inputs\":[{\"name\":\"_uuid\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_inputStartBlock\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_input\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"_stateCommitments\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"},{\"name\":\"_finalize\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"challengeFirstLPP\",\"inputs\":[{\"name\":\"_claimant\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_uuid\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_postState\",\"type\":\"tuple\",\"internalType\":\"structPreimageOracle.Leaf\",\"components\":[{\"name\":\"input\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"index\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"stateCommitment\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"name\":\"_postStateProof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"challengeLPP\",\"inputs\":[{\"name\":\"_claimant\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_uuid\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_stateMatrix\",\"type\":\"tuple\",\"internalType\":\"structLibKeccak.StateMatrix\",\"components\":[{\"name\":\"state\",\"type\":\"uint64[25]\",\"internalType\":\"uint64[25]\"}]},{\"name\":\"_preState\",\"type\":\"tuple\",\"internalType\":\"structPreimageOracle.Leaf\",\"components\":[{\"name\":\"input\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"index\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"stateCommitment\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"name\":\"_preStateProof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"},{\"name\":\"_postState\",\"type\":\"tuple\",\"internalType\":\"structPreimageOracle.Leaf\",\"components\":[{\"name\":\"input\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"index\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"stateCommitment\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"name\":\"_postStateProof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"challengePeriod\",\"inputs\":[],\"outputs\":[{\"name\":\"challengePeriod_\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getTreeRootLPP\",\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_uuid\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"treeRoot_\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"initLPP\",\"inputs\":[{\"name\":\"_uuid\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_partOffset\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"_claimedSize\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"loadBlobPreimagePart\",\"inputs\":[{\"name\":\"_z\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_y\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_commitment\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"_proof\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"_partOffset\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"loadKeccak256PreimagePart\",\"inputs\":[{\"name\":\"_partOffset\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_preimage\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"loadLocalData\",\"inputs\":[{\"name\":\"_ident\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_localContext\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"_word\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"_size\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_partOffset\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"key_\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"loadPrecompilePreimagePart\",\"inputs\":[{\"name\":\"_partOffset\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_precompile\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_input\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"loadSha256PreimagePart\",\"inputs\":[{\"name\":\"_partOffset\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_preimage\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"minProposalSize\",\"inputs\":[],\"outputs\":[{\"name\":\"minProposalSize_\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"preimageLengths\",\"inputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"preimagePartOk\",\"inputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"preimageParts\",\"inputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"proposalBlocks\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"proposalBlocksLen\",\"inputs\":[{\"name\":\"_claimant\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_uuid\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"len_\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"proposalBonds\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"proposalBranches\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"proposalCount\",\"inputs\":[],\"outputs\":[{\"name\":\"count_\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"proposalMetadata\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"LPPMetaData\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"proposalParts\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"proposals\",\"inputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"claimant\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"uuid\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"readPreimage\",\"inputs\":[{\"name\":\"_key\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"_offset\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"dat_\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"datLen_\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"squeezeLPP\",\"inputs\":[{\"name\":\"_claimant\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_uuid\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_stateMatrix\",\"type\":\"tuple\",\"internalType\":\"structLibKeccak.StateMatrix\",\"components\":[{\"name\":\"state\",\"type\":\"uint64[25]\",\"internalType\":\"uint64[25]\"}]},{\"name\":\"_preState\",\"type\":\"tuple\",\"internalType\":\"structPreimageOracle.Leaf\",\"components\":[{\"name\":\"input\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"index\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"stateCommitment\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"name\":\"_preStateProof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"},{\"name\":\"_postState\",\"type\":\"tuple\",\"internalType\":\"structPreimageOracle.Leaf\",\"components\":[{\"name\":\"input\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"index\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"stateCommitment\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"name\":\"_postStateProof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"version\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"zeroHashes\",\"inputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"error\",\"name\":\"ActiveProposal\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"AlreadyFinalized\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"BadProposal\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"BondTransferFailed\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InsufficientBond\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidInputSize\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidPreimage\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidProof\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"NotEOA\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"NotInitialized\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"PartOffsetOOB\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"PostStateMatches\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"StatesNotContiguous\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"TreeSizeOverflow\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"WrongStartingBlock\",\"inputs\":[]}]",
Bin: "0x60c06040523480156200001157600080fd5b50604051620039f9380380620039f98339810160408190526200003491620000f3565b60a0829052608081905260005b6200004f600160106200012e565b811015620000ea57600381601081106200006d576200006d62000148565b01546003826010811062000085576200008562000148565b0154604080516020810193909352820152606001604051602081830303815290604052805190602001206003826001620000c091906200015e565b60108110620000d357620000d362000148565b015580620000e18162000179565b91505062000041565b50505062000195565b600080604083850312156200010757600080fd5b505080516020909101519092909150565b634e487b7160e01b600052601160045260246000fd5b60008282101562000143576200014362000118565b500390565b634e487b7160e01b600052603260045260246000fd5b6000821982111562000174576200017462000118565b500190565b6000600182016200018e576200018e62000118565b5060010190565b60805160a051613830620001c9600039600081816105ae0152611d5101526000818161068e01526115b901526138306000f3fe6080604052600436106101cd5760003560e01c80638dc4be11116100f7578063dd24f9bf11610095578063ec5efcbc11610064578063ec5efcbc1461065f578063f3f480d91461067f578063faf37bc7146106b2578063fef2b4ed146106c557600080fd5b8063dd24f9bf1461059f578063ddcd58de146105d2578063e03110e11461060a578063e15926111461063f57600080fd5b8063b2e67ba8116100d1578063b2e67ba814610512578063b4801e611461054a578063d18534b51461056a578063da35c6641461058a57600080fd5b80638dc4be11146104835780639d53a648146104a35780639d7e8769146104f257600080fd5b806354fd4d501161016f5780637917de1d1161013e5780637917de1d146103bf5780637ac54767146103df5780638542cf50146103ff578063882856ef1461044a57600080fd5b806354fd4d50146102dd57806361238bde146103335780636551927b1461036b5780637051472e146103a357600080fd5b80632055b36b116101ab5780632055b36b146102735780633909af5c146102885780634d52b4c9146102a857806352f0f3ad146102bd57600080fd5b8063013cf08b146101d25780630359a5631461022357806304697c7814610251575b600080fd5b3480156101de57600080fd5b506101f26101ed366004612d2f565b6106f2565b6040805173ffffffffffffffffffffffffffffffffffffffff90931683526020830191909152015b60405180910390f35b34801561022f57600080fd5b5061024361023e366004612d71565b610737565b60405190815260200161021a565b34801561025d57600080fd5b5061027161026c366004612de4565b61086f565b005b34801561027f57600080fd5b50610243601081565b34801561029457600080fd5b506102716102a3366004613008565b6109a5565b3480156102b457600080fd5b50610243610bfc565b3480156102c957600080fd5b506102436102d83660046130f4565b610c17565b3480156102e957600080fd5b506103266040518060400160405280600581526020017f302e322e3000000000000000000000000000000000000000000000000000000081525081565b60405161021a919061315b565b34801561033f57600080fd5b5061024361034e3660046131ac565b600160209081526000928352604080842090915290825290205481565b34801561037757600080fd5b50610243610386366004612d71565b601560209081526000928352604080842090915290825290205481565b3480156103af57600080fd5b506102436703782dace9d9000081565b3480156103cb57600080fd5b506102716103da3660046131ce565b610cec565b3480156103eb57600080fd5b506102436103fa366004612d2f565b6111ef565b34801561040b57600080fd5b5061043a61041a3660046131ac565b600260209081526000928352604080842090915290825290205460ff1681565b604051901515815260200161021a565b34801561045657600080fd5b5061046a61046536600461326a565b611206565b60405167ffffffffffffffff909116815260200161021a565b34801561048f57600080fd5b5061027161049e36600461329d565b611260565b3480156104af57600080fd5b506102436104be366004612d71565b73ffffffffffffffffffffffffffffffffffffffff9091166000908152601860209081526040808320938352929052205490565b3480156104fe57600080fd5b5061027161050d3660046132e9565b61135b565b34801561051e57600080fd5b5061024361052d366004612d71565b601760209081526000928352604080842090915290825290205481565b34801561055657600080fd5b5061024361056536600461326a565b611512565b34801561057657600080fd5b50610271610585366004613008565b611544565b34801561059657600080fd5b50601354610243565b3480156105ab57600080fd5b507f0000000000000000000000000000000000000000000000000000000000000000610243565b3480156105de57600080fd5b506102436105ed366004612d71565b601660209081526000928352604080842090915290825290205481565b34801561061657600080fd5b5061062a6106253660046131ac565b611906565b6040805192835260208301919091520161021a565b34801561064b57600080fd5b5061027161065a36600461329d565b6119f7565b34801561066b57600080fd5b5061027161067a366004613375565b611aff565b34801561068b57600080fd5b507f0000000000000000000000000000000000000000000000000000000000000000610243565b6102716106c036600461340e565b611c85565b3480156106d157600080fd5b506102436106e0366004612d2f565b60006020819052908152604090205481565b6013818154811061070257600080fd5b60009182526020909120600290910201805460019091015473ffffffffffffffffffffffffffffffffffffffff909116915082565b73ffffffffffffffffffffffffffffffffffffffff82166000908152601560209081526040808320848452909152812054819061077a9060601c63ffffffff1690565b63ffffffff16905060005b6010811015610867578160011660010361080d5773ffffffffffffffffffffffffffffffffffffffff85166000908152601460209081526040808320878452909152902081601081106107da576107da61344a565b0154604080516020810192909252810184905260600160405160208183030381529060405280519060200120925061084e565b82600382601081106108215761082161344a565b01546040805160208101939093528201526060016040516020818303038152906040528051906020012092505b60019190911c908061085f816134a8565b915050610785565b505092915050565b600080600080608060146030823785878260140137601480870182207effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f06000000000000000000000000000000000000000000000000000000000000001794506000908190889084018b5afa94503d60010191506008820189106108fc5763fe2549876000526004601cfd5b60c082901b81526008018481533d6000600183013e88017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8015160008481526002602090815260408083208c8452825280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600190811790915587845282528083209b83529a81528a82209290925593845283905296909120959095555050505050565b60006109b18a8a610737565b90506109d486868360208b01356109cf6109ca8d6134e0565b611ef0565b611f30565b80156109f257506109f283838360208801356109cf6109ca8a6134e0565b610a28576040517f09bde33900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b866040013588604051602001610a3e91906135af565b6040516020818303038152906040528051906020012014610a8b576040517f1968a90200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b836020013587602001356001610aa191906135ed565b14610ad8576040517f9a3b119900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610b2088610ae68680613605565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611f9192505050565b610b29886120ec565b836040013588604051602001610b3f91906135af565b6040516020818303038152906040528051906020012003610b8c576040517f9843145b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8a1660009081526015602090815260408083208c8452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000166001179055610bf08a8a33612894565b50505050505050505050565b6001610c0a6010600261378c565b610c149190613798565b81565b6000610c23868661294d565b9050610c308360086135ed565b821180610c3d5750602083115b15610c74576040517ffe25498700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000602081815260c085901b82526008959095528251828252600286526040808320858452875280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660019081179091558484528752808320948352938652838220558181529384905292205592915050565b60608115610d0557610cfe86866129fa565b9050610d3f565b85858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293505050505b3360009081526014602090815260408083208b845290915280822081516102008101928390529160109082845b815481526020019060010190808311610d6c57505050505090506000601560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008b81526020019081526020016000205490506000610ded8260601c63ffffffff1690565b63ffffffff169050333214610e2e576040517fba092d1600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e3e8260801c63ffffffff1690565b63ffffffff16600003610e7d576040517f87138d5c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e878260c01c90565b67ffffffffffffffff1615610ec8576040517f475a253500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b898114610f01576040517f60f95d5a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610f0e89898d8886612a73565b83516020850160888204881415608883061715610f33576307b1daf16000526004601cfd5b60405160c8810160405260005b83811015610fe3578083018051835260208101516020840152604081015160408401526060810151606084015260808101516080840152508460888301526088810460051b8b013560a883015260c882206001860195508560005b610200811015610fd8576001821615610fb85782818b0152610fd8565b8981015160009081526020938452604090209260019290921c9101610f9b565b505050608801610f40565b50505050600160106002610ff7919061378c565b6110019190613798565b81111561103a576040517f6229572300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110af61104d8360401c63ffffffff1690565b61105d9063ffffffff168a6135ed565b60401b7fffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffff606084901b167fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff8516171790565b9150841561113c5777ffffffffffffffffffffffffffffffffffffffffffffffff82164260c01b1791506110e98260801c63ffffffff1690565b63ffffffff166110ff8360401c63ffffffff1690565b63ffffffff161461113c576040517f7b1dafd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3360009081526014602090815260408083208e8452909152902061116290846010612ca5565b503360008181526018602090815260408083208f8452825280832080546001810182559084528284206004820401805460039092166008026101000a67ffffffffffffffff818102199093164390931602919091179055838352601582528083208f8452909152812084905560609190911b81523690601437366014016000a05050505050505050505050565b600381601081106111ff57600080fd5b0154905081565b6018602052826000526040600020602052816000526040600020818154811061122e57600080fd5b906000526020600020906004918282040191900660080292509250509054906101000a900467ffffffffffffffff1681565b60443560008060088301861061127e5763fe2549876000526004601cfd5b60c083901b60805260888386823786600882030151915060206000858360025afa9050806112ab57600080fd5b50600080517effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f0400000000000000000000000000000000000000000000000000000000000000178082526002602090815260408084208a8552825280842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660019081179091558385528252808420998452988152888320939093558152908190529490942055505050565b600080603087600037602060006030600060025afa806113835763f91129696000526004601cfd5b6000517effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f010000000000000000000000000000000000000000000000000000000000000017608081815260a08c905260c08b905260308a60e037603088609083013760008060c083600a5afa925082611405576309bde3396000526004601cfd5b6028861061141b5763fe2549876000526004601cfd5b6000602882015278200000000000000000000000000000000000000000000000008152600881018b905285810151935060308a8237603081019b909b52505060509098207effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f0500000000000000000000000000000000000000000000000000000000000000176000818152600260209081526040808320868452825280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600190811790915584845282528083209583529481528482209a909a559081528089529190912096909655505050505050565b6014602052826000526040600020602052816000526040600020816010811061153a57600080fd5b0154925083915050565b73ffffffffffffffffffffffffffffffffffffffff891660009081526015602090815260408083208b845290915290205467ffffffffffffffff8116156115b7576040517fc334f06900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006115e28260c01c90565b6115f69067ffffffffffffffff1642613798565b1161162d576040517f55d4cbf900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006116398b8b610737565b905061165287878360208c01356109cf6109ca8e6134e0565b8015611670575061167084848360208901356109cf6109ca8b6134e0565b6116a6576040517f09bde33900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8760400135896040516020016116bc91906135af565b6040516020818303038152906040528051906020012014611709576040517f1968a90200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84602001358860200135600161171f91906135ed565b141580611751575060016117398360601c63ffffffff1690565b61174391906137af565b63ffffffff16856020013514155b15611788576040517f9a3b119900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61179689610ae68780613605565b61179f896120ec565b60006117aa8a612bc6565b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f020000000000000000000000000000000000000000000000000000000000000017905060006118018460a01c63ffffffff1690565b67ffffffffffffffff169050600160026000848152602001908152602001600020600083815260200190815260200160002060006101000a81548160ff021916908315150217905550601760008e73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008d815260200190815260200160002054600160008481526020019081526020016000206000838152602001908152602001600020819055506118d38460801c63ffffffff1690565b600083815260208190526040902063ffffffff9190911690556118f78d8d81612894565b50505050505050505050505050565b6000828152600260209081526040808320848452909152812054819060ff1661198f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f7072652d696d616765206d757374206578697374000000000000000000000000604482015260640160405180910390fd5b50600083815260208181526040909120546119ab8160086135ed565b6119b68560206135ed565b106119d457836119c78260086135ed565b6119d19190613798565b91505b506000938452600160209081526040808620948652939052919092205492909150565b604435600080600883018610611a155763fe2549876000526004601cfd5b60c083901b6080526088838682378087017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80151908490207effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f02000000000000000000000000000000000000000000000000000000000000001760008181526002602090815260408083208b8452825280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600190811790915584845282528083209a83529981528982209390935590815290819052959095209190915550505050565b6000611b0b8686610737565b9050611b2483838360208801356109cf6109ca8a6134e0565b611b5a576040517f09bde33900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b602084013515611b96576040517f9a3b119900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611b9e612ce3565b611bac81610ae68780613605565b611bb5816120ec565b846040013581604051602001611bcb91906135af565b6040516020818303038152906040528051906020012003611c18576040517f9843145b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff87166000908152601560209081526040808320898452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000166001179055611c7c878733612894565b50505050505050565b6703782dace9d90000341015611cc7576040517fe92c469f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b333214611d00576040517fba092d1600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d0b8160086137d4565b63ffffffff168263ffffffff1610611d4f576040517ffe25498700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000008163ffffffff161015611daf576040517f7b1dafd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000818152601560209081526040808320878452825280832080547fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff1660a09790971b7fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff169690961760809590951b949094179094558251808401845282815280850186815260138054600181018255908452915160029092027f66de8ffda797e3de9c05e8fc57b3bf0ec28a930d40b0d285d93c06501cf6a0908101805473ffffffffffffffffffffffffffffffffffffffff9094167fffffffffffffffffffffffff000000000000000000000000000000000000000090941693909317909255517f66de8ffda797e3de9c05e8fc57b3bf0ec28a930d40b0d285d93c06501cf6a0919091015590815260168352818120938152929091529020349055565b6000816000015182602001518360400151604051602001611f13939291906137fc565b604051602081830303815290604052805190602001209050919050565b60008160005b6010811015611f84578060051b880135600186831c1660018114611f695760008481526020839052604090209350611f7a565b600082815260208590526040902093505b5050600101611f36565b5090931495945050505050565b6088815114611f9f57600080fd5b6020810160208301612020565b8260031b8201518060001a8160011a60081b178160021a60101b8260031a60181b17178160041a60201b8260051a60281b178260061a60301b8360071a60381b171717905061201a81612005868560059190911b015190565b1867ffffffffffffffff16600586901b840152565b50505050565b61202c60008383611fac565b61203860018383611fac565b61204460028383611fac565b61205060038383611fac565b61205c60048383611fac565b61206860058383611fac565b61207460068383611fac565b61208060078383611fac565b61208c60088383611fac565b61209860098383611fac565b6120a4600a8383611fac565b6120b0600b8383611fac565b6120bc600c8383611fac565b6120c8600d8383611fac565b6120d4600e8383611fac565b6120e0600f8383611fac565b61201a60108383611fac565b6040805178010000000000008082800000000000808a8000000080008000602082015279808b00000000800000018000000080008081800000000000800991810191909152788a00000000000000880000000080008009000000008000000a60608201527b8000808b800000000000008b8000000000008089800000000000800360808201527f80000000000080028000000000000080000000000000800a800000008000000a60a08201527f800000008000808180000000000080800000000080000001800000008000800860c082015260009060e00160405160208183030381529060405290506020820160208201612774565b6102808101516101e082015161014083015160a0840151845118189118186102a082015161020083015161016084015160c0850151602086015118189118186102c083015161022084015161018085015160e0860151604087015118189118186102e08401516102408501516101a0860151610100870151606088015118189118186103008501516102608601516101c0870151610120880151608089015118189118188084603f1c61229f8660011b67ffffffffffffffff1690565b18188584603f1c6122ba8660011b67ffffffffffffffff1690565b18188584603f1c6122d58660011b67ffffffffffffffff1690565b181895508483603f1c6122f28560011b67ffffffffffffffff1690565b181894508387603f1c61230f8960011b67ffffffffffffffff1690565b60208b01518b51861867ffffffffffffffff168c5291189190911897508118600181901b603f9190911c18935060c08801518118601481901c602c9190911b1867ffffffffffffffff1660208901526101208801518718602c81901c60149190911b1867ffffffffffffffff1660c08901526102c08801518618600381901c603d9190911b1867ffffffffffffffff166101208901526101c08801518718601981901c60279190911b1867ffffffffffffffff166102c08901526102808801518218602e81901c60129190911b1867ffffffffffffffff166101c089015260408801518618600281901c603e9190911b1867ffffffffffffffff166102808901526101808801518618601581901c602b9190911b1867ffffffffffffffff1660408901526101a08801518518602781901c60199190911b1867ffffffffffffffff166101808901526102608801518718603881901c60089190911b1867ffffffffffffffff166101a08901526102e08801518518600881901c60389190911b1867ffffffffffffffff166102608901526101e08801518218601781901c60299190911b1867ffffffffffffffff166102e089015260808801518718602581901c601b9190911b1867ffffffffffffffff166101e08901526103008801518718603281901c600e9190911b1867ffffffffffffffff1660808901526102a08801518118603e81901c60029190911b1867ffffffffffffffff166103008901526101008801518518600981901c60379190911b1867ffffffffffffffff166102a08901526102008801518118601381901c602d9190911b1867ffffffffffffffff1661010089015260a08801518218601c81901c60249190911b1867ffffffffffffffff1661020089015260608801518518602481901c601c9190911b1867ffffffffffffffff1660a08901526102408801518518602b81901c60159190911b1867ffffffffffffffff1660608901526102208801518618603181901c600f9190911b1867ffffffffffffffff166102408901526101608801518118603681901c600a9190911b1867ffffffffffffffff166102208901525060e08701518518603a81901c60069190911b1867ffffffffffffffff166101608801526101408701518118603d81901c60039190911b1867ffffffffffffffff1660e0880152505067ffffffffffffffff81166101408601525b5050505050565b600582811b8201805160018501831b8401805160028701851b8601805160038901871b8801805160048b0190981b8901805167ffffffffffffffff861985168918811690995283198a16861889169096528819861683188816909352841986168818871690528419831684189095169052919391929190611c7c565b61270e600082612687565b612719600582612687565b612724600a82612687565b61272f600f82612687565b61273a601482612687565b50565b612746816121e2565b61274f81612703565b600383901b820151815160c09190911c9061201a90821867ffffffffffffffff168352565b6127806000828461273d565b61278c6001828461273d565b6127986002828461273d565b6127a46003828461273d565b6127b06004828461273d565b6127bc6005828461273d565b6127c86006828461273d565b6127d46007828461273d565b6127e06008828461273d565b6127ec6009828461273d565b6127f8600a828461273d565b612804600b828461273d565b612810600c828461273d565b61281c600d828461273d565b612828600e828461273d565b612834600f828461273d565b6128406010828461273d565b61284c6011828461273d565b6128586012828461273d565b6128646013828461273d565b6128706014828461273d565b61287c6015828461273d565b6128886016828461273d565b61201a6017828461273d565b73ffffffffffffffffffffffffffffffffffffffff83811660009081526016602090815260408083208684529091528082208054908390559051909284169083908381818185875af1925050503d806000811461290d576040519150601f19603f3d011682016040523d82523d6000602084013e612912565b606091505b5050905080612680576040517f83e6cc6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f01000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8316176129f3818360408051600093845233602052918152606090922091527effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01000000000000000000000000000000000000000000000000000000000000001790565b9392505050565b6060604051905081602082018181018286833760888306808015612a435760888290038501848101848103803687375060806001820353506001845160001a1784538652612a5a565b608836843760018353608060878401536088850186525b5050505050601f19603f82510116810160405292915050565b6000612a858260a01c63ffffffff1690565b67ffffffffffffffff1690506000612aa38360801c63ffffffff1690565b63ffffffff1690506000612abd8460401c63ffffffff1690565b63ffffffff169050600883108015612ad3575080155b15612b075760c082901b6000908152883560085283513382526017602090815260408084208a855290915290912055612bbc565b60088310158015612b25575080612b1f600885613798565b93508310155b8015612b395750612b3687826135ed565b83105b15612bbc576000612b4a8285613798565b905087612b588260206135ed565b10158015612b64575085155b15612b9b576040517ffe25498700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3360009081526017602090815260408083208a845290915290209089013590555b5050505050505050565b6000612c49565b66ff00ff00ff00ff8160081c1667ff00ff00ff00ff00612bf78360081b67ffffffffffffffff1690565b1617905065ffff0000ffff8160101c1667ffff0000ffff0000612c248360101b67ffffffffffffffff1690565b1617905060008160201c612c428360201b67ffffffffffffffff1690565b1792915050565b60808201516020830190612c6190612bcd565b612bcd565b6040820151612c6f90612bcd565b60401b17612c87612c5c60018460059190911b015190565b825160809190911b90612c9990612bcd565b60c01b17179392505050565b8260108101928215612cd3579160200282015b82811115612cd3578251825591602001919060010190612cb8565b50612cdf929150612cfb565b5090565b6040518060200160405280612cf6612d10565b905290565b5b80821115612cdf5760008155600101612cfc565b6040518061032001604052806019906020820280368337509192915050565b600060208284031215612d4157600080fd5b5035919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114612d6c57600080fd5b919050565b60008060408385031215612d8457600080fd5b612d8d83612d48565b946020939093013593505050565b60008083601f840112612dad57600080fd5b50813567ffffffffffffffff811115612dc557600080fd5b602083019150836020828501011115612ddd57600080fd5b9250929050565b60008060008060608587031215612dfa57600080fd5b84359350612e0a60208601612d48565b9250604085013567ffffffffffffffff811115612e2657600080fd5b612e3287828801612d9b565b95989497509550505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610320810167ffffffffffffffff81118282101715612e9157612e91612e3e565b60405290565b6040516060810167ffffffffffffffff81118282101715612e9157612e91612e3e565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715612f0157612f01612e3e565b604052919050565b6000610320808385031215612f1d57600080fd5b604051602080820167ffffffffffffffff8382108183111715612f4257612f42612e3e565b8160405283955087601f880112612f5857600080fd5b612f60612e6d565b9487019491508188861115612f7457600080fd5b875b86811015612f9c5780358381168114612f8f5760008081fd5b8452928401928401612f76565b50909352509295945050505050565b600060608284031215612fbd57600080fd5b50919050565b60008083601f840112612fd557600080fd5b50813567ffffffffffffffff811115612fed57600080fd5b6020830191508360208260051b8501011115612ddd57600080fd5b60008060008060008060008060006103e08a8c03121561302757600080fd5b6130308a612d48565b985060208a013597506130468b60408c01612f09565b96506103608a013567ffffffffffffffff8082111561306457600080fd5b6130708d838e01612fab565b97506103808c013591508082111561308757600080fd5b6130938d838e01612fc3565b90975095506103a08c01359150808211156130ad57600080fd5b6130b98d838e01612fab565b94506103c08c01359150808211156130d057600080fd5b506130dd8c828d01612fc3565b915080935050809150509295985092959850929598565b600080600080600060a0868803121561310c57600080fd5b505083359560208501359550604085013594606081013594506080013592509050565b60005b8381101561314a578181015183820152602001613132565b8381111561201a5750506000910152565b602081526000825180602084015261317a81604085016020870161312f565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b600080604083850312156131bf57600080fd5b50508035926020909101359150565b600080600080600080600060a0888a0312156131e957600080fd5b8735965060208801359550604088013567ffffffffffffffff8082111561320f57600080fd5b61321b8b838c01612d9b565b909750955060608a013591508082111561323457600080fd5b506132418a828b01612fc3565b9094509250506080880135801515811461325a57600080fd5b8091505092959891949750929550565b60008060006060848603121561327f57600080fd5b61328884612d48565b95602085013595506040909401359392505050565b6000806000604084860312156132b257600080fd5b83359250602084013567ffffffffffffffff8111156132d057600080fd5b6132dc86828701612d9b565b9497909650939450505050565b600080600080600080600060a0888a03121561330457600080fd5b8735965060208801359550604088013567ffffffffffffffff8082111561332a57600080fd5b6133368b838c01612d9b565b909750955060608a013591508082111561334f57600080fd5b5061335c8a828b01612d9b565b989b979a50959894979596608090950135949350505050565b60008060008060006080868803121561338d57600080fd5b61339686612d48565b945060208601359350604086013567ffffffffffffffff808211156133ba57600080fd5b6133c689838a01612fab565b945060608801359150808211156133dc57600080fd5b506133e988828901612fc3565b969995985093965092949392505050565b803563ffffffff81168114612d6c57600080fd5b60008060006060848603121561342357600080fd5b83359250613433602085016133fa565b9150613441604085016133fa565b90509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036134d9576134d9613479565b5060010190565b6000606082360312156134f257600080fd5b6134fa612e97565b823567ffffffffffffffff8082111561351257600080fd5b9084019036601f83011261352557600080fd5b813560208282111561353957613539612e3e565b613569817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85011601612eba565b9250818352368183860101111561357f57600080fd5b81818501828501376000918301810191909152908352848101359083015250604092830135928101929092525090565b81516103208201908260005b60198110156135e457825167ffffffffffffffff168252602092830192909101906001016135bb565b50505092915050565b6000821982111561360057613600613479565b500190565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261363a57600080fd5b83018035915067ffffffffffffffff82111561365557600080fd5b602001915036819003821315612ddd57600080fd5b600181815b808511156136c357817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156136a9576136a9613479565b808516156136b657918102915b93841c939080029061366f565b509250929050565b6000826136da57506001613786565b816136e757506000613786565b81600181146136fd576002811461370757613723565b6001915050613786565b60ff84111561371857613718613479565b50506001821b613786565b5060208310610133831016604e8410600b8410161715613746575081810a613786565b613750838361366a565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561378257613782613479565b0290505b92915050565b60006129f383836136cb565b6000828210156137aa576137aa613479565b500390565b600063ffffffff838116908316818110156137cc576137cc613479565b039392505050565b600063ffffffff8083168185168083038211156137f3576137f3613479565b01949350505050565b6000845161380e81846020890161312f565b9190910192835250602082015260400191905056fea164736f6c634300080f000a",
}
// PreimageOracleABI is the input ABI used to generate the binding from.
// Deprecated: Use PreimageOracleMetaData.ABI instead.
var PreimageOracleABI = PreimageOracleMetaData.ABI
// PreimageOracleBin is the compiled bytecode used for deploying new contracts.
// Deprecated: Use PreimageOracleMetaData.Bin instead.
var PreimageOracleBin = PreimageOracleMetaData.Bin
// DeployPreimageOracle deploys a new Ethereum contract, binding an instance of PreimageOracle to it.
func DeployPreimageOracle(auth *bind.TransactOpts, backend bind.ContractBackend, _minProposalSize *big.Int, _challengePeriod *big.Int) (common.Address, *types.Transaction, *PreimageOracle, error) {
parsed, err := PreimageOracleMetaData.GetAbi()
if err != nil {
return common.Address{}, nil, nil, err
}
if parsed == nil {
return common.Address{}, nil, nil, errors.New("GetABI returned nil")
}
address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(PreimageOracleBin), backend, _minProposalSize, _challengePeriod)
if err != nil {
return common.Address{}, nil, nil, err
}
return address, tx, &PreimageOracle{PreimageOracleCaller: PreimageOracleCaller{contract: contract}, PreimageOracleTransactor: PreimageOracleTransactor{contract: contract}, PreimageOracleFilterer: PreimageOracleFilterer{contract: contract}}, nil
}
// PreimageOracle is an auto generated Go binding around an Ethereum contract.
type PreimageOracle struct {
PreimageOracleCaller // Read-only binding to the contract
PreimageOracleTransactor // Write-only binding to the contract
PreimageOracleFilterer // Log filterer for contract events
}
// PreimageOracleCaller is an auto generated read-only Go binding around an Ethereum contract.
type PreimageOracleCaller struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// PreimageOracleTransactor is an auto generated write-only Go binding around an Ethereum contract.
type PreimageOracleTransactor struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// PreimageOracleFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
type PreimageOracleFilterer struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// PreimageOracleSession is an auto generated Go binding around an Ethereum contract,
// with pre-set call and transact options.
type PreimageOracleSession struct {
Contract *PreimageOracle // Generic contract binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
// PreimageOracleCallerSession is an auto generated read-only Go binding around an Ethereum contract,
// with pre-set call options.
type PreimageOracleCallerSession struct {
Contract *PreimageOracleCaller // Generic contract caller binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
}
// PreimageOracleTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
// with pre-set transact options.
type PreimageOracleTransactorSession struct {
Contract *PreimageOracleTransactor // Generic contract transactor binding to set the session for
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
// PreimageOracleRaw is an auto generated low-level Go binding around an Ethereum contract.
type PreimageOracleRaw struct {
Contract *PreimageOracle // Generic contract binding to access the raw methods on
}
// PreimageOracleCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
type PreimageOracleCallerRaw struct {
Contract *PreimageOracleCaller // Generic read-only contract binding to access the raw methods on
}
// PreimageOracleTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
type PreimageOracleTransactorRaw struct {
Contract *PreimageOracleTransactor // Generic write-only contract binding to access the raw methods on
}
// NewPreimageOracle creates a new instance of PreimageOracle, bound to a specific deployed contract.
func NewPreimageOracle(address common.Address, backend bind.ContractBackend) (*PreimageOracle, error) {
contract, err := bindPreimageOracle(address, backend, backend, backend)
if err != nil {
return nil, err
}
return &PreimageOracle{PreimageOracleCaller: PreimageOracleCaller{contract: contract}, PreimageOracleTransactor: PreimageOracleTransactor{contract: contract}, PreimageOracleFilterer: PreimageOracleFilterer{contract: contract}}, nil
}
// NewPreimageOracleCaller creates a new read-only instance of PreimageOracle, bound to a specific deployed contract.
func NewPreimageOracleCaller(address common.Address, caller bind.ContractCaller) (*PreimageOracleCaller, error) {
contract, err := bindPreimageOracle(address, caller, nil, nil)
if err != nil {
return nil, err
}
return &PreimageOracleCaller{contract: contract}, nil
}
// NewPreimageOracleTransactor creates a new write-only instance of PreimageOracle, bound to a specific deployed contract.
func NewPreimageOracleTransactor(address common.Address, transactor bind.ContractTransactor) (*PreimageOracleTransactor, error) {
contract, err := bindPreimageOracle(address, nil, transactor, nil)
if err != nil {
return nil, err
}
return &PreimageOracleTransactor{contract: contract}, nil
}
// NewPreimageOracleFilterer creates a new log filterer instance of PreimageOracle, bound to a specific deployed contract.
func NewPreimageOracleFilterer(address common.Address, filterer bind.ContractFilterer) (*PreimageOracleFilterer, error) {
contract, err := bindPreimageOracle(address, nil, nil, filterer)
if err != nil {
return nil, err
}
return &PreimageOracleFilterer{contract: contract}, nil
}
// bindPreimageOracle binds a generic wrapper to an already deployed contract.
func bindPreimageOracle(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
parsed, err := abi.JSON(strings.NewReader(PreimageOracleABI))
if err != nil {
return nil, err
}
return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_PreimageOracle *PreimageOracleRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
return _PreimageOracle.Contract.PreimageOracleCaller.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_PreimageOracle *PreimageOracleRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
return _PreimageOracle.Contract.PreimageOracleTransactor.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
func (_PreimageOracle *PreimageOracleRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
return _PreimageOracle.Contract.PreimageOracleTransactor.contract.Transact(opts, method, params...)
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_PreimageOracle *PreimageOracleCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
return _PreimageOracle.Contract.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_PreimageOracle *PreimageOracleTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
return _PreimageOracle.Contract.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
func (_PreimageOracle *PreimageOracleTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
return _PreimageOracle.Contract.contract.Transact(opts, method, params...)
}
// KECCAKTREEDEPTH is a free data retrieval call binding the contract method 0x2055b36b.
//
// Solidity: function KECCAK_TREE_DEPTH() view returns(uint256)
func (_PreimageOracle *PreimageOracleCaller) KECCAKTREEDEPTH(opts *bind.CallOpts) (*big.Int, error) {
var out []interface{}
err := _PreimageOracle.contract.Call(opts, &out, "KECCAK_TREE_DEPTH")
if err != nil {
return *new(*big.Int), err
}
out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
return out0, err
}
// KECCAKTREEDEPTH is a free data retrieval call binding the contract method 0x2055b36b.
//
// Solidity: function KECCAK_TREE_DEPTH() view returns(uint256)
func (_PreimageOracle *PreimageOracleSession) KECCAKTREEDEPTH() (*big.Int, error) {
return _PreimageOracle.Contract.KECCAKTREEDEPTH(&_PreimageOracle.CallOpts)
}
// KECCAKTREEDEPTH is a free data retrieval call binding the contract method 0x2055b36b.
//
// Solidity: function KECCAK_TREE_DEPTH() view returns(uint256)
func (_PreimageOracle *PreimageOracleCallerSession) KECCAKTREEDEPTH() (*big.Int, error) {
return _PreimageOracle.Contract.KECCAKTREEDEPTH(&_PreimageOracle.CallOpts)
}
// MAXLEAFCOUNT is a free data retrieval call binding the contract method 0x4d52b4c9.
//
// Solidity: function MAX_LEAF_COUNT() view returns(uint256)
func (_PreimageOracle *PreimageOracleCaller) MAXLEAFCOUNT(opts *bind.CallOpts) (*big.Int, error) {
var out []interface{}
err := _PreimageOracle.contract.Call(opts, &out, "MAX_LEAF_COUNT")
if err != nil {
return *new(*big.Int), err
}
out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
return out0, err
}
// MAXLEAFCOUNT is a free data retrieval call binding the contract method 0x4d52b4c9.
//
// Solidity: function MAX_LEAF_COUNT() view returns(uint256)
func (_PreimageOracle *PreimageOracleSession) MAXLEAFCOUNT() (*big.Int, error) {
return _PreimageOracle.Contract.MAXLEAFCOUNT(&_PreimageOracle.CallOpts)
}
// MAXLEAFCOUNT is a free data retrieval call binding the contract method 0x4d52b4c9.
//
// Solidity: function MAX_LEAF_COUNT() view returns(uint256)
func (_PreimageOracle *PreimageOracleCallerSession) MAXLEAFCOUNT() (*big.Int, error) {
return _PreimageOracle.Contract.MAXLEAFCOUNT(&_PreimageOracle.CallOpts)
}
// MINBONDSIZE is a free data retrieval call binding the contract method 0x7051472e.
//
// Solidity: function MIN_BOND_SIZE() view returns(uint256)
func (_PreimageOracle *PreimageOracleCaller) MINBONDSIZE(opts *bind.CallOpts) (*big.Int, error) {
var out []interface{}
err := _PreimageOracle.contract.Call(opts, &out, "MIN_BOND_SIZE")
if err != nil {
return *new(*big.Int), err
}
out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
return out0, err
}
// MINBONDSIZE is a free data retrieval call binding the contract method 0x7051472e.
//
// Solidity: function MIN_BOND_SIZE() view returns(uint256)
func (_PreimageOracle *PreimageOracleSession) MINBONDSIZE() (*big.Int, error) {
return _PreimageOracle.Contract.MINBONDSIZE(&_PreimageOracle.CallOpts)
}
// MINBONDSIZE is a free data retrieval call binding the contract method 0x7051472e.
//
// Solidity: function MIN_BOND_SIZE() view returns(uint256)
func (_PreimageOracle *PreimageOracleCallerSession) MINBONDSIZE() (*big.Int, error) {
return _PreimageOracle.Contract.MINBONDSIZE(&_PreimageOracle.CallOpts)
}
// ChallengePeriod is a free data retrieval call binding the contract method 0xf3f480d9.
//
// Solidity: function challengePeriod() view returns(uint256 challengePeriod_)
func (_PreimageOracle *PreimageOracleCaller) ChallengePeriod(opts *bind.CallOpts) (*big.Int, error) {
var out []interface{}
err := _PreimageOracle.contract.Call(opts, &out, "challengePeriod")
if err != nil {
return *new(*big.Int), err
}
out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
return out0, err
}
// ChallengePeriod is a free data retrieval call binding the contract method 0xf3f480d9.
//
// Solidity: function challengePeriod() view returns(uint256 challengePeriod_)
func (_PreimageOracle *PreimageOracleSession) ChallengePeriod() (*big.Int, error) {
return _PreimageOracle.Contract.ChallengePeriod(&_PreimageOracle.CallOpts)
}
// ChallengePeriod is a free data retrieval call binding the contract method 0xf3f480d9.
//
// Solidity: function challengePeriod() view returns(uint256 challengePeriod_)
func (_PreimageOracle *PreimageOracleCallerSession) ChallengePeriod() (*big.Int, error) {
return _PreimageOracle.Contract.ChallengePeriod(&_PreimageOracle.CallOpts)
}
// GetTreeRootLPP is a free data retrieval call binding the contract method 0x0359a563.
//
// Solidity: function getTreeRootLPP(address _owner, uint256 _uuid) view returns(bytes32 treeRoot_)
func (_PreimageOracle *PreimageOracleCaller) GetTreeRootLPP(opts *bind.CallOpts, _owner common.Address, _uuid *big.Int) ([32]byte, error) {
var out []interface{}
err := _PreimageOracle.contract.Call(opts, &out, "getTreeRootLPP", _owner, _uuid)
if err != nil {
return *new([32]byte), err
}
out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte)
return out0, err
}
// GetTreeRootLPP is a free data retrieval call binding the contract method 0x0359a563.
//
// Solidity: function getTreeRootLPP(address _owner, uint256 _uuid) view returns(bytes32 treeRoot_)
func (_PreimageOracle *PreimageOracleSession) GetTreeRootLPP(_owner common.Address, _uuid *big.Int) ([32]byte, error) {
return _PreimageOracle.Contract.GetTreeRootLPP(&_PreimageOracle.CallOpts, _owner, _uuid)
}
// GetTreeRootLPP is a free data retrieval call binding the contract method 0x0359a563.
//
// Solidity: function getTreeRootLPP(address _owner, uint256 _uuid) view returns(bytes32 treeRoot_)
func (_PreimageOracle *PreimageOracleCallerSession) GetTreeRootLPP(_owner common.Address, _uuid *big.Int) ([32]byte, error) {
return _PreimageOracle.Contract.GetTreeRootLPP(&_PreimageOracle.CallOpts, _owner, _uuid)
}
// MinProposalSize is a free data retrieval call binding the contract method 0xdd24f9bf.
//
// Solidity: function minProposalSize() view returns(uint256 minProposalSize_)
func (_PreimageOracle *PreimageOracleCaller) MinProposalSize(opts *bind.CallOpts) (*big.Int, error) {
var out []interface{}
err := _PreimageOracle.contract.Call(opts, &out, "minProposalSize")
if err != nil {
return *new(*big.Int), err
}
out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
return out0, err
}
// MinProposalSize is a free data retrieval call binding the contract method 0xdd24f9bf.
//
// Solidity: function minProposalSize() view returns(uint256 minProposalSize_)
func (_PreimageOracle *PreimageOracleSession) MinProposalSize() (*big.Int, error) {
return _PreimageOracle.Contract.MinProposalSize(&_PreimageOracle.CallOpts)
}
// MinProposalSize is a free data retrieval call binding the contract method 0xdd24f9bf.
//
// Solidity: function minProposalSize() view returns(uint256 minProposalSize_)
func (_PreimageOracle *PreimageOracleCallerSession) MinProposalSize() (*big.Int, error) {
return _PreimageOracle.Contract.MinProposalSize(&_PreimageOracle.CallOpts)
}
// PreimageLengths is a free data retrieval call binding the contract method 0xfef2b4ed.
//
// Solidity: function preimageLengths(bytes32 ) view returns(uint256)
func (_PreimageOracle *PreimageOracleCaller) PreimageLengths(opts *bind.CallOpts, arg0 [32]byte) (*big.Int, error) {
var out []interface{}
err := _PreimageOracle.contract.Call(opts, &out, "preimageLengths", arg0)
if err != nil {
return *new(*big.Int), err
}
out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
return out0, err
}
// PreimageLengths is a free data retrieval call binding the contract method 0xfef2b4ed.
//
// Solidity: function preimageLengths(bytes32 ) view returns(uint256)
func (_PreimageOracle *PreimageOracleSession) PreimageLengths(arg0 [32]byte) (*big.Int, error) {
return _PreimageOracle.Contract.PreimageLengths(&_PreimageOracle.CallOpts, arg0)
}
// PreimageLengths is a free data retrieval call binding the contract method 0xfef2b4ed.
//
// Solidity: function preimageLengths(bytes32 ) view returns(uint256)
func (_PreimageOracle *PreimageOracleCallerSession) PreimageLengths(arg0 [32]byte) (*big.Int, error) {
return _PreimageOracle.Contract.PreimageLengths(&_PreimageOracle.CallOpts, arg0)
}
// PreimagePartOk is a free data retrieval call binding the contract method 0x8542cf50.
//
// Solidity: function preimagePartOk(bytes32 , uint256 ) view returns(bool)
func (_PreimageOracle *PreimageOracleCaller) PreimagePartOk(opts *bind.CallOpts, arg0 [32]byte, arg1 *big.Int) (bool, error) {
var out []interface{}
err := _PreimageOracle.contract.Call(opts, &out, "preimagePartOk", arg0, arg1)
if err != nil {
return *new(bool), err
}
out0 := *abi.ConvertType(out[0], new(bool)).(*bool)
return out0, err
}
// PreimagePartOk is a free data retrieval call binding the contract method 0x8542cf50.
//
// Solidity: function preimagePartOk(bytes32 , uint256 ) view returns(bool)
func (_PreimageOracle *PreimageOracleSession) PreimagePartOk(arg0 [32]byte, arg1 *big.Int) (bool, error) {
return _PreimageOracle.Contract.PreimagePartOk(&_PreimageOracle.CallOpts, arg0, arg1)
}
// PreimagePartOk is a free data retrieval call binding the contract method 0x8542cf50.
//
// Solidity: function preimagePartOk(bytes32 , uint256 ) view returns(bool)
func (_PreimageOracle *PreimageOracleCallerSession) PreimagePartOk(arg0 [32]byte, arg1 *big.Int) (bool, error) {
return _PreimageOracle.Contract.PreimagePartOk(&_PreimageOracle.CallOpts, arg0, arg1)
}
// PreimageParts is a free data retrieval call binding the contract method 0x61238bde.
//
// Solidity: function preimageParts(bytes32 , uint256 ) view returns(bytes32)
func (_PreimageOracle *PreimageOracleCaller) PreimageParts(opts *bind.CallOpts, arg0 [32]byte, arg1 *big.Int) ([32]byte, error) {
var out []interface{}
err := _PreimageOracle.contract.Call(opts, &out, "preimageParts", arg0, arg1)
if err != nil {
return *new([32]byte), err
}
out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte)
return out0, err
}
// PreimageParts is a free data retrieval call binding the contract method 0x61238bde.
//
// Solidity: function preimageParts(bytes32 , uint256 ) view returns(bytes32)
func (_PreimageOracle *PreimageOracleSession) PreimageParts(arg0 [32]byte, arg1 *big.Int) ([32]byte, error) {
return _PreimageOracle.Contract.PreimageParts(&_PreimageOracle.CallOpts, arg0, arg1)
}
// PreimageParts is a free data retrieval call binding the contract method 0x61238bde.
//
// Solidity: function preimageParts(bytes32 , uint256 ) view returns(bytes32)
func (_PreimageOracle *PreimageOracleCallerSession) PreimageParts(arg0 [32]byte, arg1 *big.Int) ([32]byte, error) {
return _PreimageOracle.Contract.PreimageParts(&_PreimageOracle.CallOpts, arg0, arg1)
}
// ProposalBlocks is a free data retrieval call binding the contract method 0x882856ef.
//
// Solidity: function proposalBlocks(address , uint256 , uint256 ) view returns(uint64)
func (_PreimageOracle *PreimageOracleCaller) ProposalBlocks(opts *bind.CallOpts, arg0 common.Address, arg1 *big.Int, arg2 *big.Int) (uint64, error) {
var out []interface{}
err := _PreimageOracle.contract.Call(opts, &out, "proposalBlocks", arg0, arg1, arg2)
if err != nil {
return *new(uint64), err
}
out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64)
return out0, err
}
// ProposalBlocks is a free data retrieval call binding the contract method 0x882856ef.
//
// Solidity: function proposalBlocks(address , uint256 , uint256 ) view returns(uint64)
func (_PreimageOracle *PreimageOracleSession) ProposalBlocks(arg0 common.Address, arg1 *big.Int, arg2 *big.Int) (uint64, error) {
return _PreimageOracle.Contract.ProposalBlocks(&_PreimageOracle.CallOpts, arg0, arg1, arg2)
}
// ProposalBlocks is a free data retrieval call binding the contract method 0x882856ef.
//
// Solidity: function proposalBlocks(address , uint256 , uint256 ) view returns(uint64)
func (_PreimageOracle *PreimageOracleCallerSession) ProposalBlocks(arg0 common.Address, arg1 *big.Int, arg2 *big.Int) (uint64, error) {
return _PreimageOracle.Contract.ProposalBlocks(&_PreimageOracle.CallOpts, arg0, arg1, arg2)
}
// ProposalBlocksLen is a free data retrieval call binding the contract method 0x9d53a648.
//
// Solidity: function proposalBlocksLen(address _claimant, uint256 _uuid) view returns(uint256 len_)
func (_PreimageOracle *PreimageOracleCaller) ProposalBlocksLen(opts *bind.CallOpts, _claimant common.Address, _uuid *big.Int) (*big.Int, error) {
var out []interface{}
err := _PreimageOracle.contract.Call(opts, &out, "proposalBlocksLen", _claimant, _uuid)
if err != nil {
return *new(*big.Int), err
}
out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
return out0, err
}
// ProposalBlocksLen is a free data retrieval call binding the contract method 0x9d53a648.
//
// Solidity: function proposalBlocksLen(address _claimant, uint256 _uuid) view returns(uint256 len_)
func (_PreimageOracle *PreimageOracleSession) ProposalBlocksLen(_claimant common.Address, _uuid *big.Int) (*big.Int, error) {
return _PreimageOracle.Contract.ProposalBlocksLen(&_PreimageOracle.CallOpts, _claimant, _uuid)
}
// ProposalBlocksLen is a free data retrieval call binding the contract method 0x9d53a648.
//
// Solidity: function proposalBlocksLen(address _claimant, uint256 _uuid) view returns(uint256 len_)
func (_PreimageOracle *PreimageOracleCallerSession) ProposalBlocksLen(_claimant common.Address, _uuid *big.Int) (*big.Int, error) {
return _PreimageOracle.Contract.ProposalBlocksLen(&_PreimageOracle.CallOpts, _claimant, _uuid)
}
// ProposalBonds is a free data retrieval call binding the contract method 0xddcd58de.
//
// Solidity: function proposalBonds(address , uint256 ) view returns(uint256)
func (_PreimageOracle *PreimageOracleCaller) ProposalBonds(opts *bind.CallOpts, arg0 common.Address, arg1 *big.Int) (*big.Int, error) {
var out []interface{}
err := _PreimageOracle.contract.Call(opts, &out, "proposalBonds", arg0, arg1)
if err != nil {
return *new(*big.Int), err
}
out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
return out0, err
}
// ProposalBonds is a free data retrieval call binding the contract method 0xddcd58de.
//
// Solidity: function proposalBonds(address , uint256 ) view returns(uint256)
func (_PreimageOracle *PreimageOracleSession) ProposalBonds(arg0 common.Address, arg1 *big.Int) (*big.Int, error) {
return _PreimageOracle.Contract.ProposalBonds(&_PreimageOracle.CallOpts, arg0, arg1)
}
// ProposalBonds is a free data retrieval call binding the contract method 0xddcd58de.
//
// Solidity: function proposalBonds(address , uint256 ) view returns(uint256)
func (_PreimageOracle *PreimageOracleCallerSession) ProposalBonds(arg0 common.Address, arg1 *big.Int) (*big.Int, error) {
return _PreimageOracle.Contract.ProposalBonds(&_PreimageOracle.CallOpts, arg0, arg1)
}
// ProposalBranches is a free data retrieval call binding the contract method 0xb4801e61.
//
// Solidity: function proposalBranches(address , uint256 , uint256 ) view returns(bytes32)
func (_PreimageOracle *PreimageOracleCaller) ProposalBranches(opts *bind.CallOpts, arg0 common.Address, arg1 *big.Int, arg2 *big.Int) ([32]byte, error) {
var out []interface{}
err := _PreimageOracle.contract.Call(opts, &out, "proposalBranches", arg0, arg1, arg2)
if err != nil {
return *new([32]byte), err
}
out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte)
return out0, err
}
// ProposalBranches is a free data retrieval call binding the contract method 0xb4801e61.
//
// Solidity: function proposalBranches(address , uint256 , uint256 ) view returns(bytes32)
func (_PreimageOracle *PreimageOracleSession) ProposalBranches(arg0 common.Address, arg1 *big.Int, arg2 *big.Int) ([32]byte, error) {
return _PreimageOracle.Contract.ProposalBranches(&_PreimageOracle.CallOpts, arg0, arg1, arg2)
}
// ProposalBranches is a free data retrieval call binding the contract method 0xb4801e61.
//
// Solidity: function proposalBranches(address , uint256 , uint256 ) view returns(bytes32)
func (_PreimageOracle *PreimageOracleCallerSession) ProposalBranches(arg0 common.Address, arg1 *big.Int, arg2 *big.Int) ([32]byte, error) {
return _PreimageOracle.Contract.ProposalBranches(&_PreimageOracle.CallOpts, arg0, arg1, arg2)
}
// ProposalCount is a free data retrieval call binding the contract method 0xda35c664.
//
// Solidity: function proposalCount() view returns(uint256 count_)
func (_PreimageOracle *PreimageOracleCaller) ProposalCount(opts *bind.CallOpts) (*big.Int, error) {
var out []interface{}
err := _PreimageOracle.contract.Call(opts, &out, "proposalCount")
if err != nil {
return *new(*big.Int), err
}
out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
return out0, err
}
// ProposalCount is a free data retrieval call binding the contract method 0xda35c664.
//
// Solidity: function proposalCount() view returns(uint256 count_)
func (_PreimageOracle *PreimageOracleSession) ProposalCount() (*big.Int, error) {
return _PreimageOracle.Contract.ProposalCount(&_PreimageOracle.CallOpts)
}
// ProposalCount is a free data retrieval call binding the contract method 0xda35c664.
//
// Solidity: function proposalCount() view returns(uint256 count_)
func (_PreimageOracle *PreimageOracleCallerSession) ProposalCount() (*big.Int, error) {
return _PreimageOracle.Contract.ProposalCount(&_PreimageOracle.CallOpts)
}
// ProposalMetadata is a free data retrieval call binding the contract method 0x6551927b.
//
// Solidity: function proposalMetadata(address , uint256 ) view returns(bytes32)
func (_PreimageOracle *PreimageOracleCaller) ProposalMetadata(opts *bind.CallOpts, arg0 common.Address, arg1 *big.Int) ([32]byte, error) {
var out []interface{}
err := _PreimageOracle.contract.Call(opts, &out, "proposalMetadata", arg0, arg1)
if err != nil {
return *new([32]byte), err
}
out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte)
return out0, err
}
// ProposalMetadata is a free data retrieval call binding the contract method 0x6551927b.
//
// Solidity: function proposalMetadata(address , uint256 ) view returns(bytes32)
func (_PreimageOracle *PreimageOracleSession) ProposalMetadata(arg0 common.Address, arg1 *big.Int) ([32]byte, error) {
return _PreimageOracle.Contract.ProposalMetadata(&_PreimageOracle.CallOpts, arg0, arg1)
}
// ProposalMetadata is a free data retrieval call binding the contract method 0x6551927b.
//
// Solidity: function proposalMetadata(address , uint256 ) view returns(bytes32)
func (_PreimageOracle *PreimageOracleCallerSession) ProposalMetadata(arg0 common.Address, arg1 *big.Int) ([32]byte, error) {
return _PreimageOracle.Contract.ProposalMetadata(&_PreimageOracle.CallOpts, arg0, arg1)
}
// ProposalParts is a free data retrieval call binding the contract method 0xb2e67ba8.
//
// Solidity: function proposalParts(address , uint256 ) view returns(bytes32)
func (_PreimageOracle *PreimageOracleCaller) ProposalParts(opts *bind.CallOpts, arg0 common.Address, arg1 *big.Int) ([32]byte, error) {
var out []interface{}
err := _PreimageOracle.contract.Call(opts, &out, "proposalParts", arg0, arg1)
if err != nil {
return *new([32]byte), err
}
out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte)
return out0, err
}
// ProposalParts is a free data retrieval call binding the contract method 0xb2e67ba8.
//
// Solidity: function proposalParts(address , uint256 ) view returns(bytes32)
func (_PreimageOracle *PreimageOracleSession) ProposalParts(arg0 common.Address, arg1 *big.Int) ([32]byte, error) {
return _PreimageOracle.Contract.ProposalParts(&_PreimageOracle.CallOpts, arg0, arg1)
}
// ProposalParts is a free data retrieval call binding the contract method 0xb2e67ba8.
//
// Solidity: function proposalParts(address , uint256 ) view returns(bytes32)
func (_PreimageOracle *PreimageOracleCallerSession) ProposalParts(arg0 common.Address, arg1 *big.Int) ([32]byte, error) {
return _PreimageOracle.Contract.ProposalParts(&_PreimageOracle.CallOpts, arg0, arg1)
}
// Proposals is a free data retrieval call binding the contract method 0x013cf08b.
//
// Solidity: function proposals(uint256 ) view returns(address claimant, uint256 uuid)
func (_PreimageOracle *PreimageOracleCaller) Proposals(opts *bind.CallOpts, arg0 *big.Int) (struct {
Claimant common.Address
Uuid *big.Int
}, error) {
var out []interface{}
err := _PreimageOracle.contract.Call(opts, &out, "proposals", arg0)
outstruct := new(struct {
Claimant common.Address
Uuid *big.Int
})
if err != nil {
return *outstruct, err
}
outstruct.Claimant = *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
outstruct.Uuid = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int)
return *outstruct, err
}
// Proposals is a free data retrieval call binding the contract method 0x013cf08b.
//
// Solidity: function proposals(uint256 ) view returns(address claimant, uint256 uuid)
func (_PreimageOracle *PreimageOracleSession) Proposals(arg0 *big.Int) (struct {
Claimant common.Address
Uuid *big.Int
}, error) {
return _PreimageOracle.Contract.Proposals(&_PreimageOracle.CallOpts, arg0)
}
// Proposals is a free data retrieval call binding the contract method 0x013cf08b.
//
// Solidity: function proposals(uint256 ) view returns(address claimant, uint256 uuid)
func (_PreimageOracle *PreimageOracleCallerSession) Proposals(arg0 *big.Int) (struct {
Claimant common.Address
Uuid *big.Int
}, error) {
return _PreimageOracle.Contract.Proposals(&_PreimageOracle.CallOpts, arg0)
}
// ReadPreimage is a free data retrieval call binding the contract method 0xe03110e1.
//
// Solidity: function readPreimage(bytes32 _key, uint256 _offset) view returns(bytes32 dat_, uint256 datLen_)
func (_PreimageOracle *PreimageOracleCaller) ReadPreimage(opts *bind.CallOpts, _key [32]byte, _offset *big.Int) (struct {
Dat [32]byte
DatLen *big.Int
}, error) {
var out []interface{}
err := _PreimageOracle.contract.Call(opts, &out, "readPreimage", _key, _offset)
outstruct := new(struct {
Dat [32]byte
DatLen *big.Int
})
if err != nil {
return *outstruct, err
}
outstruct.Dat = *abi.ConvertType(out[0], new([32]byte)).(*[32]byte)
outstruct.DatLen = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int)
return *outstruct, err
}
// ReadPreimage is a free data retrieval call binding the contract method 0xe03110e1.
//
// Solidity: function readPreimage(bytes32 _key, uint256 _offset) view returns(bytes32 dat_, uint256 datLen_)
func (_PreimageOracle *PreimageOracleSession) ReadPreimage(_key [32]byte, _offset *big.Int) (struct {
Dat [32]byte
DatLen *big.Int
}, error) {
return _PreimageOracle.Contract.ReadPreimage(&_PreimageOracle.CallOpts, _key, _offset)
}
// ReadPreimage is a free data retrieval call binding the contract method 0xe03110e1.
//
// Solidity: function readPreimage(bytes32 _key, uint256 _offset) view returns(bytes32 dat_, uint256 datLen_)
func (_PreimageOracle *PreimageOracleCallerSession) ReadPreimage(_key [32]byte, _offset *big.Int) (struct {
Dat [32]byte
DatLen *big.Int
}, error) {
return _PreimageOracle.Contract.ReadPreimage(&_PreimageOracle.CallOpts, _key, _offset)
}
// Version is a free data retrieval call binding the contract method 0x54fd4d50.
//
// Solidity: function version() view returns(string)
func (_PreimageOracle *PreimageOracleCaller) Version(opts *bind.CallOpts) (string, error) {
var out []interface{}
err := _PreimageOracle.contract.Call(opts, &out, "version")
if err != nil {
return *new(string), err
}
out0 := *abi.ConvertType(out[0], new(string)).(*string)
return out0, err
}
// Version is a free data retrieval call binding the contract method 0x54fd4d50.
//
// Solidity: function version() view returns(string)
func (_PreimageOracle *PreimageOracleSession) Version() (string, error) {
return _PreimageOracle.Contract.Version(&_PreimageOracle.CallOpts)
}
// Version is a free data retrieval call binding the contract method 0x54fd4d50.
//
// Solidity: function version() view returns(string)
func (_PreimageOracle *PreimageOracleCallerSession) Version() (string, error) {
return _PreimageOracle.Contract.Version(&_PreimageOracle.CallOpts)
}
// ZeroHashes is a free data retrieval call binding the contract method 0x7ac54767.
//
// Solidity: function zeroHashes(uint256 ) view returns(bytes32)
func (_PreimageOracle *PreimageOracleCaller) ZeroHashes(opts *bind.CallOpts, arg0 *big.Int) ([32]byte, error) {
var out []interface{}
err := _PreimageOracle.contract.Call(opts, &out, "zeroHashes", arg0)
if err != nil {
return *new([32]byte), err
}
out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte)
return out0, err
}
// ZeroHashes is a free data retrieval call binding the contract method 0x7ac54767.
//
// Solidity: function zeroHashes(uint256 ) view returns(bytes32)
func (_PreimageOracle *PreimageOracleSession) ZeroHashes(arg0 *big.Int) ([32]byte, error) {
return _PreimageOracle.Contract.ZeroHashes(&_PreimageOracle.CallOpts, arg0)
}
// ZeroHashes is a free data retrieval call binding the contract method 0x7ac54767.
//
// Solidity: function zeroHashes(uint256 ) view returns(bytes32)
func (_PreimageOracle *PreimageOracleCallerSession) ZeroHashes(arg0 *big.Int) ([32]byte, error) {
return _PreimageOracle.Contract.ZeroHashes(&_PreimageOracle.CallOpts, arg0)
}
// AddLeavesLPP is a paid mutator transaction binding the contract method 0x7917de1d.
//
// Solidity: function addLeavesLPP(uint256 _uuid, uint256 _inputStartBlock, bytes _input, bytes32[] _stateCommitments, bool _finalize) returns()
func (_PreimageOracle *PreimageOracleTransactor) AddLeavesLPP(opts *bind.TransactOpts, _uuid *big.Int, _inputStartBlock *big.Int, _input []byte, _stateCommitments [][32]byte, _finalize bool) (*types.Transaction, error) {
return _PreimageOracle.contract.Transact(opts, "addLeavesLPP", _uuid, _inputStartBlock, _input, _stateCommitments, _finalize)
}
// AddLeavesLPP is a paid mutator transaction binding the contract method 0x7917de1d.
//
// Solidity: function addLeavesLPP(uint256 _uuid, uint256 _inputStartBlock, bytes _input, bytes32[] _stateCommitments, bool _finalize) returns()
func (_PreimageOracle *PreimageOracleSession) AddLeavesLPP(_uuid *big.Int, _inputStartBlock *big.Int, _input []byte, _stateCommitments [][32]byte, _finalize bool) (*types.Transaction, error) {
return _PreimageOracle.Contract.AddLeavesLPP(&_PreimageOracle.TransactOpts, _uuid, _inputStartBlock, _input, _stateCommitments, _finalize)
}
// AddLeavesLPP is a paid mutator transaction binding the contract method 0x7917de1d.
//
// Solidity: function addLeavesLPP(uint256 _uuid, uint256 _inputStartBlock, bytes _input, bytes32[] _stateCommitments, bool _finalize) returns()
func (_PreimageOracle *PreimageOracleTransactorSession) AddLeavesLPP(_uuid *big.Int, _inputStartBlock *big.Int, _input []byte, _stateCommitments [][32]byte, _finalize bool) (*types.Transaction, error) {
return _PreimageOracle.Contract.AddLeavesLPP(&_PreimageOracle.TransactOpts, _uuid, _inputStartBlock, _input, _stateCommitments, _finalize)
}
// ChallengeFirstLPP is a paid mutator transaction binding the contract method 0xec5efcbc.
//
// Solidity: function challengeFirstLPP(address _claimant, uint256 _uuid, (bytes,uint256,bytes32) _postState, bytes32[] _postStateProof) returns()
func (_PreimageOracle *PreimageOracleTransactor) ChallengeFirstLPP(opts *bind.TransactOpts, _claimant common.Address, _uuid *big.Int, _postState PreimageOracleLeaf, _postStateProof [][32]byte) (*types.Transaction, error) {
return _PreimageOracle.contract.Transact(opts, "challengeFirstLPP", _claimant, _uuid, _postState, _postStateProof)
}
// ChallengeFirstLPP is a paid mutator transaction binding the contract method 0xec5efcbc.
//
// Solidity: function challengeFirstLPP(address _claimant, uint256 _uuid, (bytes,uint256,bytes32) _postState, bytes32[] _postStateProof) returns()
func (_PreimageOracle *PreimageOracleSession) ChallengeFirstLPP(_claimant common.Address, _uuid *big.Int, _postState PreimageOracleLeaf, _postStateProof [][32]byte) (*types.Transaction, error) {
return _PreimageOracle.Contract.ChallengeFirstLPP(&_PreimageOracle.TransactOpts, _claimant, _uuid, _postState, _postStateProof)
}
// ChallengeFirstLPP is a paid mutator transaction binding the contract method 0xec5efcbc.
//
// Solidity: function challengeFirstLPP(address _claimant, uint256 _uuid, (bytes,uint256,bytes32) _postState, bytes32[] _postStateProof) returns()
func (_PreimageOracle *PreimageOracleTransactorSession) ChallengeFirstLPP(_claimant common.Address, _uuid *big.Int, _postState PreimageOracleLeaf, _postStateProof [][32]byte) (*types.Transaction, error) {
return _PreimageOracle.Contract.ChallengeFirstLPP(&_PreimageOracle.TransactOpts, _claimant, _uuid, _postState, _postStateProof)
}
// ChallengeLPP is a paid mutator transaction binding the contract method 0x3909af5c.
//
// Solidity: function challengeLPP(address _claimant, uint256 _uuid, (uint64[25]) _stateMatrix, (bytes,uint256,bytes32) _preState, bytes32[] _preStateProof, (bytes,uint256,bytes32) _postState, bytes32[] _postStateProof) returns()
func (_PreimageOracle *PreimageOracleTransactor) ChallengeLPP(opts *bind.TransactOpts, _claimant common.Address, _uuid *big.Int, _stateMatrix LibKeccakStateMatrix, _preState PreimageOracleLeaf, _preStateProof [][32]byte, _postState PreimageOracleLeaf, _postStateProof [][32]byte) (*types.Transaction, error) {
return _PreimageOracle.contract.Transact(opts, "challengeLPP", _claimant, _uuid, _stateMatrix, _preState, _preStateProof, _postState, _postStateProof)
}
// ChallengeLPP is a paid mutator transaction binding the contract method 0x3909af5c.
//
// Solidity: function challengeLPP(address _claimant, uint256 _uuid, (uint64[25]) _stateMatrix, (bytes,uint256,bytes32) _preState, bytes32[] _preStateProof, (bytes,uint256,bytes32) _postState, bytes32[] _postStateProof) returns()
func (_PreimageOracle *PreimageOracleSession) ChallengeLPP(_claimant common.Address, _uuid *big.Int, _stateMatrix LibKeccakStateMatrix, _preState PreimageOracleLeaf, _preStateProof [][32]byte, _postState PreimageOracleLeaf, _postStateProof [][32]byte) (*types.Transaction, error) {
return _PreimageOracle.Contract.ChallengeLPP(&_PreimageOracle.TransactOpts, _claimant, _uuid, _stateMatrix, _preState, _preStateProof, _postState, _postStateProof)
}
// ChallengeLPP is a paid mutator transaction binding the contract method 0x3909af5c.
//
// Solidity: function challengeLPP(address _claimant, uint256 _uuid, (uint64[25]) _stateMatrix, (bytes,uint256,bytes32) _preState, bytes32[] _preStateProof, (bytes,uint256,bytes32) _postState, bytes32[] _postStateProof) returns()
func (_PreimageOracle *PreimageOracleTransactorSession) ChallengeLPP(_claimant common.Address, _uuid *big.Int, _stateMatrix LibKeccakStateMatrix, _preState PreimageOracleLeaf, _preStateProof [][32]byte, _postState PreimageOracleLeaf, _postStateProof [][32]byte) (*types.Transaction, error) {
return _PreimageOracle.Contract.ChallengeLPP(&_PreimageOracle.TransactOpts, _claimant, _uuid, _stateMatrix, _preState, _preStateProof, _postState, _postStateProof)
}
// InitLPP is a paid mutator transaction binding the contract method 0xfaf37bc7.
//
// Solidity: function initLPP(uint256 _uuid, uint32 _partOffset, uint32 _claimedSize) payable returns()
func (_PreimageOracle *PreimageOracleTransactor) InitLPP(opts *bind.TransactOpts, _uuid *big.Int, _partOffset uint32, _claimedSize uint32) (*types.Transaction, error) {
return _PreimageOracle.contract.Transact(opts, "initLPP", _uuid, _partOffset, _claimedSize)
}
// InitLPP is a paid mutator transaction binding the contract method 0xfaf37bc7.
//
// Solidity: function initLPP(uint256 _uuid, uint32 _partOffset, uint32 _claimedSize) payable returns()
func (_PreimageOracle *PreimageOracleSession) InitLPP(_uuid *big.Int, _partOffset uint32, _claimedSize uint32) (*types.Transaction, error) {
return _PreimageOracle.Contract.InitLPP(&_PreimageOracle.TransactOpts, _uuid, _partOffset, _claimedSize)
}
// InitLPP is a paid mutator transaction binding the contract method 0xfaf37bc7.
//
// Solidity: function initLPP(uint256 _uuid, uint32 _partOffset, uint32 _claimedSize) payable returns()
func (_PreimageOracle *PreimageOracleTransactorSession) InitLPP(_uuid *big.Int, _partOffset uint32, _claimedSize uint32) (*types.Transaction, error) {
return _PreimageOracle.Contract.InitLPP(&_PreimageOracle.TransactOpts, _uuid, _partOffset, _claimedSize)
}
// LoadBlobPreimagePart is a paid mutator transaction binding the contract method 0x9d7e8769.
//
// Solidity: function loadBlobPreimagePart(uint256 _z, uint256 _y, bytes _commitment, bytes _proof, uint256 _partOffset) returns()
func (_PreimageOracle *PreimageOracleTransactor) LoadBlobPreimagePart(opts *bind.TransactOpts, _z *big.Int, _y *big.Int, _commitment []byte, _proof []byte, _partOffset *big.Int) (*types.Transaction, error) {
return _PreimageOracle.contract.Transact(opts, "loadBlobPreimagePart", _z, _y, _commitment, _proof, _partOffset)
}
// LoadBlobPreimagePart is a paid mutator transaction binding the contract method 0x9d7e8769.
//
// Solidity: function loadBlobPreimagePart(uint256 _z, uint256 _y, bytes _commitment, bytes _proof, uint256 _partOffset) returns()
func (_PreimageOracle *PreimageOracleSession) LoadBlobPreimagePart(_z *big.Int, _y *big.Int, _commitment []byte, _proof []byte, _partOffset *big.Int) (*types.Transaction, error) {
return _PreimageOracle.Contract.LoadBlobPreimagePart(&_PreimageOracle.TransactOpts, _z, _y, _commitment, _proof, _partOffset)
}
// LoadBlobPreimagePart is a paid mutator transaction binding the contract method 0x9d7e8769.
//
// Solidity: function loadBlobPreimagePart(uint256 _z, uint256 _y, bytes _commitment, bytes _proof, uint256 _partOffset) returns()
func (_PreimageOracle *PreimageOracleTransactorSession) LoadBlobPreimagePart(_z *big.Int, _y *big.Int, _commitment []byte, _proof []byte, _partOffset *big.Int) (*types.Transaction, error) {
return _PreimageOracle.Contract.LoadBlobPreimagePart(&_PreimageOracle.TransactOpts, _z, _y, _commitment, _proof, _partOffset)
}
// LoadKeccak256PreimagePart is a paid mutator transaction binding the contract method 0xe1592611.
//
// Solidity: function loadKeccak256PreimagePart(uint256 _partOffset, bytes _preimage) returns()
func (_PreimageOracle *PreimageOracleTransactor) LoadKeccak256PreimagePart(opts *bind.TransactOpts, _partOffset *big.Int, _preimage []byte) (*types.Transaction, error) {
return _PreimageOracle.contract.Transact(opts, "loadKeccak256PreimagePart", _partOffset, _preimage)
}
// LoadKeccak256PreimagePart is a paid mutator transaction binding the contract method 0xe1592611.
//
// Solidity: function loadKeccak256PreimagePart(uint256 _partOffset, bytes _preimage) returns()
func (_PreimageOracle *PreimageOracleSession) LoadKeccak256PreimagePart(_partOffset *big.Int, _preimage []byte) (*types.Transaction, error) {
return _PreimageOracle.Contract.LoadKeccak256PreimagePart(&_PreimageOracle.TransactOpts, _partOffset, _preimage)
}
// LoadKeccak256PreimagePart is a paid mutator transaction binding the contract method 0xe1592611.
//
// Solidity: function loadKeccak256PreimagePart(uint256 _partOffset, bytes _preimage) returns()
func (_PreimageOracle *PreimageOracleTransactorSession) LoadKeccak256PreimagePart(_partOffset *big.Int, _preimage []byte) (*types.Transaction, error) {
return _PreimageOracle.Contract.LoadKeccak256PreimagePart(&_PreimageOracle.TransactOpts, _partOffset, _preimage)
}
// LoadLocalData is a paid mutator transaction binding the contract method 0x52f0f3ad.
//
// Solidity: function loadLocalData(uint256 _ident, bytes32 _localContext, bytes32 _word, uint256 _size, uint256 _partOffset) returns(bytes32 key_)
func (_PreimageOracle *PreimageOracleTransactor) LoadLocalData(opts *bind.TransactOpts, _ident *big.Int, _localContext [32]byte, _word [32]byte, _size *big.Int, _partOffset *big.Int) (*types.Transaction, error) {
return _PreimageOracle.contract.Transact(opts, "loadLocalData", _ident, _localContext, _word, _size, _partOffset)
}
// LoadLocalData is a paid mutator transaction binding the contract method 0x52f0f3ad.
//
// Solidity: function loadLocalData(uint256 _ident, bytes32 _localContext, bytes32 _word, uint256 _size, uint256 _partOffset) returns(bytes32 key_)
func (_PreimageOracle *PreimageOracleSession) LoadLocalData(_ident *big.Int, _localContext [32]byte, _word [32]byte, _size *big.Int, _partOffset *big.Int) (*types.Transaction, error) {
return _PreimageOracle.Contract.LoadLocalData(&_PreimageOracle.TransactOpts, _ident, _localContext, _word, _size, _partOffset)
}
// LoadLocalData is a paid mutator transaction binding the contract method 0x52f0f3ad.
//
// Solidity: function loadLocalData(uint256 _ident, bytes32 _localContext, bytes32 _word, uint256 _size, uint256 _partOffset) returns(bytes32 key_)
func (_PreimageOracle *PreimageOracleTransactorSession) LoadLocalData(_ident *big.Int, _localContext [32]byte, _word [32]byte, _size *big.Int, _partOffset *big.Int) (*types.Transaction, error) {
return _PreimageOracle.Contract.LoadLocalData(&_PreimageOracle.TransactOpts, _ident, _localContext, _word, _size, _partOffset)
}
// LoadPrecompilePreimagePart is a paid mutator transaction binding the contract method 0x04697c78.
//
// Solidity: function loadPrecompilePreimagePart(uint256 _partOffset, address _precompile, bytes _input) returns()
func (_PreimageOracle *PreimageOracleTransactor) LoadPrecompilePreimagePart(opts *bind.TransactOpts, _partOffset *big.Int, _precompile common.Address, _input []byte) (*types.Transaction, error) {
return _PreimageOracle.contract.Transact(opts, "loadPrecompilePreimagePart", _partOffset, _precompile, _input)
}
// LoadPrecompilePreimagePart is a paid mutator transaction binding the contract method 0x04697c78.
//
// Solidity: function loadPrecompilePreimagePart(uint256 _partOffset, address _precompile, bytes _input) returns()
func (_PreimageOracle *PreimageOracleSession) LoadPrecompilePreimagePart(_partOffset *big.Int, _precompile common.Address, _input []byte) (*types.Transaction, error) {
return _PreimageOracle.Contract.LoadPrecompilePreimagePart(&_PreimageOracle.TransactOpts, _partOffset, _precompile, _input)
}
// LoadPrecompilePreimagePart is a paid mutator transaction binding the contract method 0x04697c78.
//
// Solidity: function loadPrecompilePreimagePart(uint256 _partOffset, address _precompile, bytes _input) returns()
func (_PreimageOracle *PreimageOracleTransactorSession) LoadPrecompilePreimagePart(_partOffset *big.Int, _precompile common.Address, _input []byte) (*types.Transaction, error) {
return _PreimageOracle.Contract.LoadPrecompilePreimagePart(&_PreimageOracle.TransactOpts, _partOffset, _precompile, _input)
}
// LoadSha256PreimagePart is a paid mutator transaction binding the contract method 0x8dc4be11.
//
// Solidity: function loadSha256PreimagePart(uint256 _partOffset, bytes _preimage) returns()
func (_PreimageOracle *PreimageOracleTransactor) LoadSha256PreimagePart(opts *bind.TransactOpts, _partOffset *big.Int, _preimage []byte) (*types.Transaction, error) {
return _PreimageOracle.contract.Transact(opts, "loadSha256PreimagePart", _partOffset, _preimage)
}
// LoadSha256PreimagePart is a paid mutator transaction binding the contract method 0x8dc4be11.
//
// Solidity: function loadSha256PreimagePart(uint256 _partOffset, bytes _preimage) returns()
func (_PreimageOracle *PreimageOracleSession) LoadSha256PreimagePart(_partOffset *big.Int, _preimage []byte) (*types.Transaction, error) {
return _PreimageOracle.Contract.LoadSha256PreimagePart(&_PreimageOracle.TransactOpts, _partOffset, _preimage)
}
// LoadSha256PreimagePart is a paid mutator transaction binding the contract method 0x8dc4be11.
//
// Solidity: function loadSha256PreimagePart(uint256 _partOffset, bytes _preimage) returns()
func (_PreimageOracle *PreimageOracleTransactorSession) LoadSha256PreimagePart(_partOffset *big.Int, _preimage []byte) (*types.Transaction, error) {
return _PreimageOracle.Contract.LoadSha256PreimagePart(&_PreimageOracle.TransactOpts, _partOffset, _preimage)
}
// SqueezeLPP is a paid mutator transaction binding the contract method 0xd18534b5.
//
// Solidity: function squeezeLPP(address _claimant, uint256 _uuid, (uint64[25]) _stateMatrix, (bytes,uint256,bytes32) _preState, bytes32[] _preStateProof, (bytes,uint256,bytes32) _postState, bytes32[] _postStateProof) returns()
func (_PreimageOracle *PreimageOracleTransactor) SqueezeLPP(opts *bind.TransactOpts, _claimant common.Address, _uuid *big.Int, _stateMatrix LibKeccakStateMatrix, _preState PreimageOracleLeaf, _preStateProof [][32]byte, _postState PreimageOracleLeaf, _postStateProof [][32]byte) (*types.Transaction, error) {
return _PreimageOracle.contract.Transact(opts, "squeezeLPP", _claimant, _uuid, _stateMatrix, _preState, _preStateProof, _postState, _postStateProof)
}
// SqueezeLPP is a paid mutator transaction binding the contract method 0xd18534b5.
//
// Solidity: function squeezeLPP(address _claimant, uint256 _uuid, (uint64[25]) _stateMatrix, (bytes,uint256,bytes32) _preState, bytes32[] _preStateProof, (bytes,uint256,bytes32) _postState, bytes32[] _postStateProof) returns()
func (_PreimageOracle *PreimageOracleSession) SqueezeLPP(_claimant common.Address, _uuid *big.Int, _stateMatrix LibKeccakStateMatrix, _preState PreimageOracleLeaf, _preStateProof [][32]byte, _postState PreimageOracleLeaf, _postStateProof [][32]byte) (*types.Transaction, error) {
return _PreimageOracle.Contract.SqueezeLPP(&_PreimageOracle.TransactOpts, _claimant, _uuid, _stateMatrix, _preState, _preStateProof, _postState, _postStateProof)
}
// SqueezeLPP is a paid mutator transaction binding the contract method 0xd18534b5.
//
// Solidity: function squeezeLPP(address _claimant, uint256 _uuid, (uint64[25]) _stateMatrix, (bytes,uint256,bytes32) _preState, bytes32[] _preStateProof, (bytes,uint256,bytes32) _postState, bytes32[] _postStateProof) returns()
func (_PreimageOracle *PreimageOracleTransactorSession) SqueezeLPP(_claimant common.Address, _uuid *big.Int, _stateMatrix LibKeccakStateMatrix, _preState PreimageOracleLeaf, _preStateProof [][32]byte, _postState PreimageOracleLeaf, _postStateProof [][32]byte) (*types.Transaction, error) {
return _PreimageOracle.Contract.SqueezeLPP(&_PreimageOracle.TransactOpts, _claimant, _uuid, _stateMatrix, _preState, _preStateProof, _postState, _postStateProof)
}
...@@ -47,23 +47,49 @@ func (g *OutputCannonGameHelper) StartChallenger(ctx context.Context, name strin ...@@ -47,23 +47,49 @@ func (g *OutputCannonGameHelper) StartChallenger(ctx context.Context, name strin
return c return c
} }
func (g *OutputCannonGameHelper) CreateHonestActor(ctx context.Context, l2Node string, options ...challenger.Option) *OutputHonestHelper { type honestActorConfig struct {
opts := g.defaultChallengerOptions() prestateBlock uint64
opts = append(opts, options...) poststateBlock uint64
cfg := challenger.NewChallengerConfig(g.T, g.System, l2Node, opts...) challengerOpts []challenger.Option
}
type HonestActorOpt func(cfg *honestActorConfig)
func WithClaimedL2BlockNumber(num uint64) HonestActorOpt {
return func(cfg *honestActorConfig) {
cfg.poststateBlock = num
}
}
func WithPrivKey(privKey *ecdsa.PrivateKey) HonestActorOpt {
return func(cfg *honestActorConfig) {
cfg.challengerOpts = append(cfg.challengerOpts, challenger.WithPrivKey(privKey))
}
}
func (g *OutputCannonGameHelper) CreateHonestActor(ctx context.Context, l2Node string, options ...HonestActorOpt) *OutputHonestHelper {
logger := testlog.Logger(g.T, log.LevelInfo).New("role", "HonestHelper", "game", g.Addr) logger := testlog.Logger(g.T, log.LevelInfo).New("role", "HonestHelper", "game", g.Addr)
l2Client := g.System.NodeClient(l2Node) l2Client := g.System.NodeClient(l2Node)
prestateBlock, poststateBlock, err := g.Game.GetBlockRange(ctx) realPrestateBlock, realPostStateBlock, err := g.Game.GetBlockRange(ctx)
g.Require.NoError(err, "Failed to load block range") g.Require.NoError(err, "Failed to load block range")
dir := filepath.Join(cfg.Datadir, "honest")
splitDepth := g.SplitDepth(ctx) splitDepth := g.SplitDepth(ctx)
rollupClient := g.System.RollupClient(l2Node) rollupClient := g.System.RollupClient(l2Node)
prestateProvider := outputs.NewPrestateProvider(rollupClient, prestateBlock) actorCfg := &honestActorConfig{
prestateBlock: realPrestateBlock,
poststateBlock: realPostStateBlock,
challengerOpts: g.defaultChallengerOptions(),
}
for _, option := range options {
option(actorCfg)
}
cfg := challenger.NewChallengerConfig(g.T, g.System, l2Node, actorCfg.challengerOpts...)
dir := filepath.Join(cfg.Datadir, "honest")
prestateProvider := outputs.NewPrestateProvider(rollupClient, actorCfg.prestateBlock)
l1Head := g.GetL1Head(ctx) l1Head := g.GetL1Head(ctx)
accessor, err := outputs.NewOutputCannonTraceAccessor( accessor, err := outputs.NewOutputCannonTraceAccessor(
logger, metrics.NoopMetrics, cfg.Cannon, vm.NewOpProgramServerExecutor(), l2Client, prestateProvider, cfg.CannonAbsolutePreState, rollupClient, dir, l1Head, splitDepth, prestateBlock, poststateBlock) logger, metrics.NoopMetrics, cfg.Cannon, vm.NewOpProgramServerExecutor(), l2Client, prestateProvider, cfg.CannonAbsolutePreState, rollupClient, dir, l1Head, splitDepth, actorCfg.prestateBlock, actorCfg.poststateBlock)
g.Require.NoError(err, "Failed to create output cannon trace accessor") g.Require.NoError(err, "Failed to create output cannon trace accessor")
return NewOutputHonestHelper(g.T, g.Require, &g.OutputGameHelper, g.Game, accessor) return NewOutputHonestHelper(g.T, g.Require, &g.OutputGameHelper, g.Game, accessor)
} }
...@@ -128,7 +154,7 @@ func (g *OutputCannonGameHelper) ChallengeToPreimageLoad(ctx context.Context, ou ...@@ -128,7 +154,7 @@ func (g *OutputCannonGameHelper) ChallengeToPreimageLoad(ctx context.Context, ou
if preloadPreimage { if preloadPreimage {
_, _, preimageData, err := provider.GetStepData(ctx, types.NewPosition(execDepth, big.NewInt(int64(targetTraceIndex)))) _, _, preimageData, err := provider.GetStepData(ctx, types.NewPosition(execDepth, big.NewInt(int64(targetTraceIndex))))
g.Require.NoError(err) g.Require.NoError(err)
g.UploadPreimage(ctx, preimageData, challengerKey) g.UploadPreimage(ctx, preimageData)
g.WaitForPreimageInOracle(ctx, preimageData) g.WaitForPreimageInOracle(ctx, preimageData)
} }
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
"fmt" "fmt"
"math/big" "math/big"
"strings"
"testing" "testing"
"time" "time"
...@@ -14,10 +15,8 @@ import ( ...@@ -14,10 +15,8 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
keccakTypes "github.com/ethereum-optimism/optimism/op-challenger/game/keccak/types" keccakTypes "github.com/ethereum-optimism/optimism/op-challenger/game/keccak/types"
gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types" gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-e2e/bindings"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/transactions" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/transactions"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
preimage "github.com/ethereum-optimism/optimism/op-preimage"
"github.com/ethereum-optimism/optimism/op-service/errutil" "github.com/ethereum-optimism/optimism/op-service/errutil"
"github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock" "github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock"
...@@ -586,7 +585,11 @@ func (g *OutputGameHelper) StepFails(ctx context.Context, claimIdx int64, isAtta ...@@ -586,7 +585,11 @@ func (g *OutputGameHelper) StepFails(ctx context.Context, claimIdx int64, isAtta
_, _, err = transactions.SendTx(ctx, g.Client, candidate, g.PrivKey, transactions.WithReceiptFail()) _, _, err = transactions.SendTx(ctx, g.Client, candidate, g.PrivKey, transactions.WithReceiptFail())
err = errutil.TryAddRevertReason(err) err = errutil.TryAddRevertReason(err)
g.Require.Error(err, "Transaction should fail") g.Require.Error(err, "Transaction should fail")
g.Require.Contains(err.Error(), "0xfb4e40dd", "Revert reason should be abi encoded ValidStep()") validStepErr := "0xfb4e40dd"
invalidPrestateErr := "0x696550ff"
if !strings.Contains(err.Error(), validStepErr) && !strings.Contains(err.Error(), invalidPrestateErr) {
g.Require.Failf("Revert reason should be abi encoded ValidStep() or InvalidPrestate() but was: %v", err.Error())
}
} }
// ResolveClaim resolves a single subgame // ResolveClaim resolves a single subgame
...@@ -649,28 +652,14 @@ func (g *OutputGameHelper) WaitForPreimageInOracle(ctx context.Context, data *ty ...@@ -649,28 +652,14 @@ func (g *OutputGameHelper) WaitForPreimageInOracle(ctx context.Context, data *ty
g.Require.NoErrorf(err, "Did not find preimage (%v) in oracle", common.Bytes2Hex(data.OracleKey)) g.Require.NoErrorf(err, "Did not find preimage (%v) in oracle", common.Bytes2Hex(data.OracleKey))
} }
func (g *OutputGameHelper) UploadPreimage(ctx context.Context, data *types.PreimageOracleData, privateKey *ecdsa.PrivateKey) { func (g *OutputGameHelper) UploadPreimage(ctx context.Context, data *types.PreimageOracleData) {
oracle := g.oracle(ctx) oracle := g.oracle(ctx)
boundOracle, err := bindings.NewPreimageOracle(oracle.Addr(), g.Client) tx, err := oracle.AddGlobalDataTx(data)
g.Require.NoError(err) g.Require.NoError(err, "Failed to create preimage upload tx")
var tx *gethtypes.Transaction transactions.RequireSendTx(g.T, ctx, g.Client, tx, g.PrivKey)
switch data.OracleKey[0] {
case byte(preimage.PrecompileKeyType):
tx, err = boundOracle.LoadPrecompilePreimagePart(
g.Opts,
new(big.Int).SetUint64(uint64(data.OracleOffset)),
data.GetPrecompileAddress(),
data.GetPrecompileInput(),
)
default:
tx, err = boundOracle.LoadKeccak256PreimagePart(g.Opts, new(big.Int).SetUint64(uint64(data.OracleOffset)), data.GetPreimageWithoutSize())
}
g.Require.NoError(err, "Failed to load preimage part")
_, err = wait.ForReceiptOK(ctx, g.Client, tx.Hash())
g.Require.NoError(err)
} }
func (g *OutputGameHelper) oracle(ctx context.Context) *contracts.PreimageOracleContract { func (g *OutputGameHelper) oracle(ctx context.Context) contracts.PreimageOracleContract {
oracle, err := g.Game.GetOracle(ctx) oracle, err := g.Game.GetOracle(ctx)
g.Require.NoError(err, "Failed to create oracle contract") g.Require.NoError(err, "Failed to create oracle contract")
return oracle return oracle
......
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts"
"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-e2e/e2eutils/transactions"
"github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock" "github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
...@@ -99,8 +100,13 @@ func (h *OutputHonestHelper) StepFails(ctx context.Context, claimIdx int64, isAt ...@@ -99,8 +100,13 @@ func (h *OutputHonestHelper) StepFails(ctx context.Context, claimIdx int64, isAt
// If we're defending, then the step will be from the trace to the next one // If we're defending, then the step will be from the trace to the next one
pos = pos.MoveRight() pos = pos.MoveRight()
} }
prestate, proofData, _, err := h.correctTrace.GetStepData(ctx, game, claim, pos) prestate, proofData, preimage, err := h.correctTrace.GetStepData(ctx, game, claim, pos)
h.require.NoError(err, "Get step data") h.require.NoError(err, "Get step data")
if preimage != nil {
tx, err := h.game.Game.UpdateOracleTx(ctx, uint64(claimIdx), preimage)
h.require.NoError(err)
transactions.RequireSendTx(h.t, ctx, h.game.Client, tx, h.game.PrivKey)
}
h.game.StepFails(ctx, claimIdx, isAttack, prestate, proofData) h.game.StepFails(ctx, claimIdx, isAttack, prestate, proofData)
} }
......
...@@ -33,11 +33,11 @@ type Helper struct { ...@@ -33,11 +33,11 @@ type Helper struct {
require *require.Assertions require *require.Assertions
client *ethclient.Client client *ethclient.Client
privKey *ecdsa.PrivateKey privKey *ecdsa.PrivateKey
oracle *contracts.PreimageOracleContract oracle contracts.PreimageOracleContract
uuidProvider atomic.Int64 uuidProvider atomic.Int64
} }
func NewHelper(t *testing.T, privKey *ecdsa.PrivateKey, client *ethclient.Client, oracle *contracts.PreimageOracleContract) *Helper { func NewHelper(t *testing.T, privKey *ecdsa.PrivateKey, client *ethclient.Client, oracle contracts.PreimageOracleContract) *Helper {
return &Helper{ return &Helper{
t: t, t: t,
require: require.New(t), require: require.New(t),
......
...@@ -194,7 +194,7 @@ func TestOutputCannonDefendStep(t *testing.T) { ...@@ -194,7 +194,7 @@ func TestOutputCannonDefendStep(t *testing.T) {
game.StartChallenger(ctx, "Challenger", challenger.WithPrivKey(sys.Cfg.Secrets.Alice)) game.StartChallenger(ctx, "Challenger", challenger.WithPrivKey(sys.Cfg.Secrets.Alice))
correctTrace := game.CreateHonestActor(ctx, "sequencer", challenger.WithPrivKey(sys.Cfg.Secrets.Mallory)) correctTrace := game.CreateHonestActor(ctx, "sequencer", disputegame.WithPrivKey(sys.Cfg.Secrets.Mallory))
maxDepth := game.MaxDepth(ctx) maxDepth := game.MaxDepth(ctx)
game.DefendClaim(ctx, outputRootClaim, func(claim *disputegame.ClaimHelper) *disputegame.ClaimHelper { game.DefendClaim(ctx, outputRootClaim, func(claim *disputegame.ClaimHelper) *disputegame.ClaimHelper {
...@@ -410,7 +410,7 @@ func TestOutputCannonProposedOutputRootValid(t *testing.T) { ...@@ -410,7 +410,7 @@ func TestOutputCannonProposedOutputRootValid(t *testing.T) {
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys) disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
game := disputeGameFactory.StartOutputCannonGameWithCorrectRoot(ctx, "sequencer", 1) game := disputeGameFactory.StartOutputCannonGameWithCorrectRoot(ctx, "sequencer", 1)
correctTrace := game.CreateHonestActor(ctx, "sequencer", challenger.WithPrivKey(sys.Cfg.Secrets.Mallory)) correctTrace := game.CreateHonestActor(ctx, "sequencer", disputegame.WithPrivKey(sys.Cfg.Secrets.Mallory))
game.StartChallenger(ctx, "Challenger", challenger.WithPrivKey(sys.Cfg.Secrets.Alice)) game.StartChallenger(ctx, "Challenger", challenger.WithPrivKey(sys.Cfg.Secrets.Alice))
...@@ -445,7 +445,7 @@ func TestOutputCannonPoisonedPostState(t *testing.T) { ...@@ -445,7 +445,7 @@ func TestOutputCannonPoisonedPostState(t *testing.T) {
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys) disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
// Root claim is dishonest // Root claim is dishonest
game := disputeGameFactory.StartOutputCannonGame(ctx, "sequencer", 1, common.Hash{0xaa}) game := disputeGameFactory.StartOutputCannonGame(ctx, "sequencer", 1, common.Hash{0xaa})
correctTrace := game.CreateHonestActor(ctx, "sequencer", challenger.WithPrivKey(sys.Cfg.Secrets.Alice)) correctTrace := game.CreateHonestActor(ctx, "sequencer", disputegame.WithPrivKey(sys.Cfg.Secrets.Alice))
// Honest first attack at "honest" level // Honest first attack at "honest" level
claim := correctTrace.AttackClaim(ctx, game.RootClaim(ctx)) claim := correctTrace.AttackClaim(ctx, game.RootClaim(ctx))
...@@ -509,7 +509,7 @@ func TestDisputeOutputRootBeyondProposedBlock_ValidOutputRoot(t *testing.T) { ...@@ -509,7 +509,7 @@ func TestDisputeOutputRootBeyondProposedBlock_ValidOutputRoot(t *testing.T) {
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys) disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
// Root claim is dishonest // Root claim is dishonest
game := disputeGameFactory.StartOutputCannonGameWithCorrectRoot(ctx, "sequencer", 1) game := disputeGameFactory.StartOutputCannonGameWithCorrectRoot(ctx, "sequencer", 1)
correctTrace := game.CreateHonestActor(ctx, "sequencer", challenger.WithPrivKey(sys.Cfg.Secrets.Alice)) correctTrace := game.CreateHonestActor(ctx, "sequencer", disputegame.WithPrivKey(sys.Cfg.Secrets.Alice))
// Start the honest challenger // Start the honest challenger
game.StartChallenger(ctx, "Honest", challenger.WithPrivKey(sys.Cfg.Secrets.Bob)) game.StartChallenger(ctx, "Honest", challenger.WithPrivKey(sys.Cfg.Secrets.Bob))
...@@ -559,7 +559,7 @@ func TestDisputeOutputRootBeyondProposedBlock_InvalidOutputRoot(t *testing.T) { ...@@ -559,7 +559,7 @@ func TestDisputeOutputRootBeyondProposedBlock_InvalidOutputRoot(t *testing.T) {
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys) disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
// Root claim is dishonest // Root claim is dishonest
game := disputeGameFactory.StartOutputCannonGame(ctx, "sequencer", 1, common.Hash{0xaa}) game := disputeGameFactory.StartOutputCannonGame(ctx, "sequencer", 1, common.Hash{0xaa})
correctTrace := game.CreateHonestActor(ctx, "sequencer", challenger.WithPrivKey(sys.Cfg.Secrets.Alice)) correctTrace := game.CreateHonestActor(ctx, "sequencer", disputegame.WithPrivKey(sys.Cfg.Secrets.Alice))
// Start the honest challenger // Start the honest challenger
game.StartChallenger(ctx, "Honest", challenger.WithPrivKey(sys.Cfg.Secrets.Bob)) game.StartChallenger(ctx, "Honest", challenger.WithPrivKey(sys.Cfg.Secrets.Bob))
...@@ -610,7 +610,7 @@ func TestDisputeOutputRoot_ChangeClaimedOutputRoot(t *testing.T) { ...@@ -610,7 +610,7 @@ func TestDisputeOutputRoot_ChangeClaimedOutputRoot(t *testing.T) {
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys) disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
// Root claim is dishonest // Root claim is dishonest
game := disputeGameFactory.StartOutputCannonGame(ctx, "sequencer", 1, common.Hash{0xaa}) game := disputeGameFactory.StartOutputCannonGame(ctx, "sequencer", 1, common.Hash{0xaa})
correctTrace := game.CreateHonestActor(ctx, "sequencer", challenger.WithPrivKey(sys.Cfg.Secrets.Alice)) correctTrace := game.CreateHonestActor(ctx, "sequencer", disputegame.WithPrivKey(sys.Cfg.Secrets.Alice))
// Start the honest challenger // Start the honest challenger
game.StartChallenger(ctx, "Honest", challenger.WithPrivKey(sys.Cfg.Secrets.Bob)) game.StartChallenger(ctx, "Honest", challenger.WithPrivKey(sys.Cfg.Secrets.Bob))
...@@ -700,7 +700,7 @@ func TestInvalidateUnsafeProposal(t *testing.T) { ...@@ -700,7 +700,7 @@ func TestInvalidateUnsafeProposal(t *testing.T) {
// Root claim is _dishonest_ because the required data is not available on L1 // Root claim is _dishonest_ because the required data is not available on L1
game := disputeGameFactory.StartOutputCannonGameWithCorrectRoot(ctx, "sequencer", blockNum, disputegame.WithUnsafeProposal()) game := disputeGameFactory.StartOutputCannonGameWithCorrectRoot(ctx, "sequencer", blockNum, disputegame.WithUnsafeProposal())
correctTrace := game.CreateHonestActor(ctx, "sequencer", challenger.WithPrivKey(sys.Cfg.Secrets.Alice)) correctTrace := game.CreateHonestActor(ctx, "sequencer", disputegame.WithPrivKey(sys.Cfg.Secrets.Alice))
// Start the honest challenger // Start the honest challenger
game.StartChallenger(ctx, "Challenger", challenger.WithPrivKey(sys.Cfg.Secrets.Bob)) game.StartChallenger(ctx, "Challenger", challenger.WithPrivKey(sys.Cfg.Secrets.Bob))
...@@ -762,7 +762,7 @@ func TestInvalidateProposalForFutureBlock(t *testing.T) { ...@@ -762,7 +762,7 @@ func TestInvalidateProposalForFutureBlock(t *testing.T) {
// Root claim is _dishonest_ because the required data is not available on L1 // Root claim is _dishonest_ because the required data is not available on L1
game := disputeGameFactory.StartOutputCannonGame(ctx, "sequencer", farFutureBlockNum, common.Hash{0xaa}, disputegame.WithFutureProposal()) game := disputeGameFactory.StartOutputCannonGame(ctx, "sequencer", farFutureBlockNum, common.Hash{0xaa}, disputegame.WithFutureProposal())
correctTrace := game.CreateHonestActor(ctx, "sequencer", challenger.WithPrivKey(sys.Cfg.Secrets.Alice)) correctTrace := game.CreateHonestActor(ctx, "sequencer", disputegame.WithPrivKey(sys.Cfg.Secrets.Alice))
// Start the honest challenger // Start the honest challenger
game.StartChallenger(ctx, "Honest", challenger.WithPrivKey(sys.Cfg.Secrets.Bob)) game.StartChallenger(ctx, "Honest", challenger.WithPrivKey(sys.Cfg.Secrets.Bob))
...@@ -815,3 +815,98 @@ func TestInvalidateCorrectProposalFutureBlock(t *testing.T) { ...@@ -815,3 +815,98 @@ func TestInvalidateCorrectProposalFutureBlock(t *testing.T) {
game.WaitForGameStatus(ctx, gameTypes.GameStatusChallengerWon) game.WaitForGameStatus(ctx, gameTypes.GameStatusChallengerWon)
game.LogGameData(ctx) game.LogGameData(ctx)
} }
func TestOutputCannonHonestSafeTraceExtension_ValidRoot(t *testing.T) {
op_e2e.InitParallel(t, op_e2e.UsesCannon)
ctx := context.Background()
sys, l1Client := StartFaultDisputeSystem(t)
t.Cleanup(sys.Close)
// Wait for there to be there are safe L2 blocks past the claimed safe head that have data available on L1 within
// the commitment stored in the dispute game.
safeHeadNum := uint64(3)
require.NoError(t, wait.ForSafeBlock(ctx, sys.RollupClient("sequencer"), safeHeadNum))
// Create a dispute game with an honest claim
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
game := disputeGameFactory.StartOutputCannonGameWithCorrectRoot(ctx, "sequencer", safeHeadNum-1)
require.NotNil(t, game)
// Create a correct trace actor with an honest trace extending to L2 block #4
correctTrace := game.CreateHonestActor(ctx, "sequencer", disputegame.WithPrivKey(sys.Cfg.Secrets.Mallory))
// Create a correct trace actor with an honest trace extending to L2 block #5
// Notably, L2 block #5 is a valid block within the safe chain, and the data required to reproduce it
// will be committed to within the L1 head of the dispute game.
correctTracePlus1 := game.CreateHonestActor(ctx, "sequencer",
disputegame.WithPrivKey(sys.Cfg.Secrets.Mallory),
disputegame.WithClaimedL2BlockNumber(safeHeadNum))
// Start the honest challenger. They will defend the root claim.
game.StartChallenger(ctx, "Challenger", challenger.WithPrivKey(sys.Cfg.Secrets.Alice))
claim := game.RootClaim(ctx)
game.ChallengeClaim(ctx, claim, func(parent *disputegame.ClaimHelper) *disputegame.ClaimHelper {
// Have to disagree with the root claim - we're trying to invalidate a valid output root
if parent.IsRootClaim() {
return parent.Attack(ctx, common.Hash{0xdd})
}
return correctTracePlus1.CounterClaim(ctx, parent)
}, func(parentClaimIdx int64) {
correctTrace.StepFails(ctx, parentClaimIdx, true)
correctTrace.StepFails(ctx, parentClaimIdx, false)
correctTracePlus1.StepFails(ctx, parentClaimIdx, true)
correctTracePlus1.StepFails(ctx, parentClaimIdx, false)
})
game.LogGameData(ctx)
// Time travel past when the game will be resolvable.
sys.TimeTravelClock.AdvanceTime(game.MaxClockDuration(ctx))
require.NoError(t, wait.ForNextBlock(ctx, l1Client))
game.WaitForInactivity(ctx, 10, true)
game.LogGameData(ctx)
require.EqualValues(t, gameTypes.GameStatusDefenderWon, game.Status(ctx))
}
func TestOutputCannonHonestSafeTraceExtension_InvalidRoot(t *testing.T) {
op_e2e.InitParallel(t, op_e2e.UsesCannon)
ctx := context.Background()
sys, l1Client := StartFaultDisputeSystem(t)
t.Cleanup(sys.Close)
// Wait for there to be there are safe L2 blocks past the claimed safe head that have data available on L1 within
// the commitment stored in the dispute game.
safeHeadNum := uint64(2)
require.NoError(t, wait.ForSafeBlock(ctx, sys.RollupClient("sequencer"), safeHeadNum))
// Create a dispute game with a dishonest claim @ L2 block #4
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
game := disputeGameFactory.StartOutputCannonGame(ctx, "sequencer", safeHeadNum-1, common.Hash{0xCA, 0xFE})
require.NotNil(t, game)
// Create a correct trace actor with an honest trace extending to L2 block #5
// Notably, L2 block #5 is a valid block within the safe chain, and the data required to reproduce it
// will be committed to within the L1 head of the dispute game.
correctTracePlus1 := game.CreateHonestActor(ctx, "sequencer",
disputegame.WithPrivKey(sys.Cfg.Secrets.Mallory),
disputegame.WithClaimedL2BlockNumber(safeHeadNum))
// Start the honest challenger. They will challenge the root claim.
game.StartChallenger(ctx, "Challenger", challenger.WithPrivKey(sys.Cfg.Secrets.Alice))
claim := game.RootClaim(ctx)
game.DefendClaim(ctx, claim, func(parent *disputegame.ClaimHelper) *disputegame.ClaimHelper {
return correctTracePlus1.CounterClaim(ctx, parent)
})
// Time travel past when the game will be resolvable.
sys.TimeTravelClock.AdvanceTime(game.MaxClockDuration(ctx))
require.NoError(t, wait.ForNextBlock(ctx, l1Client))
game.WaitForInactivity(ctx, 10, true)
game.LogGameData(ctx)
require.EqualValues(t, gameTypes.GameStatusChallengerWon, game.Status(ctx))
}
...@@ -24,6 +24,7 @@ import ( ...@@ -24,6 +24,7 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/metrics" "github.com/ethereum-optimism/optimism/op-challenger/metrics"
op_e2e "github.com/ethereum-optimism/optimism/op-e2e" op_e2e "github.com/ethereum-optimism/optimism/op-e2e"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/disputegame"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
"github.com/ethereum-optimism/optimism/op-service/client" "github.com/ethereum-optimism/optimism/op-service/client"
"github.com/ethereum-optimism/optimism/op-service/ioutil" "github.com/ethereum-optimism/optimism/op-service/ioutil"
...@@ -35,14 +36,16 @@ func TestPrecompiles(t *testing.T) { ...@@ -35,14 +36,16 @@ func TestPrecompiles(t *testing.T) {
op_e2e.InitParallel(t, op_e2e.UsesCannon) op_e2e.InitParallel(t, op_e2e.UsesCannon)
// precompile test vectors copied from go-ethereum // precompile test vectors copied from go-ethereum
tests := []struct { tests := []struct {
name string name string
address common.Address address common.Address
input []byte input []byte
accelerated bool
}{ }{
{ {
name: "ecrecover", name: "ecrecover",
address: common.BytesToAddress([]byte{0x01}), address: common.BytesToAddress([]byte{0x01}),
input: common.FromHex("18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000000000000000000000000000001c73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549"), input: common.FromHex("18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000000000000000000000000000001c73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549"),
accelerated: true,
}, },
{ {
name: "sha256", name: "sha256",
...@@ -55,9 +58,10 @@ func TestPrecompiles(t *testing.T) { ...@@ -55,9 +58,10 @@ func TestPrecompiles(t *testing.T) {
input: common.FromHex("68656c6c6f20776f726c64"), input: common.FromHex("68656c6c6f20776f726c64"),
}, },
{ {
name: "bn256Pairing", name: "bn256Pairing",
address: common.BytesToAddress([]byte{0x08}), address: common.BytesToAddress([]byte{0x08}),
input: common.FromHex("1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f593034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf704bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a416782bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa"), input: common.FromHex("1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f593034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf704bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a416782bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa"),
accelerated: true,
}, },
{ {
name: "blake2F", name: "blake2F",
...@@ -65,9 +69,10 @@ func TestPrecompiles(t *testing.T) { ...@@ -65,9 +69,10 @@ func TestPrecompiles(t *testing.T) {
input: common.FromHex("0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"), input: common.FromHex("0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"),
}, },
{ {
name: "kzgPointEvaluation", name: "kzgPointEvaluation",
address: common.BytesToAddress([]byte{0x0a}), address: common.BytesToAddress([]byte{0x0a}),
input: common.FromHex("01e798154708fe7789429634053cbf9f99b619f9f084048927333fce637f549b564c0a11a0f704f4fc3e8acfe0f8245f0ad1347b378fbf96e206da11a5d3630624d25032e67a7e6a4910df5834b8fe70e6bcfeeac0352434196bdf4b2485d5a18f59a8d2a1a625a17f3fea0fe5eb8c896db3764f3185481bc22f91b4aaffcca25f26936857bc3a7c2539ea8ec3a952b7873033e038326e87ed3e1276fd140253fa08e9fc25fb2d9a98527fc22a2c9612fbeafdad446cbc7bcdbdcd780af2c16a"), input: common.FromHex("01e798154708fe7789429634053cbf9f99b619f9f084048927333fce637f549b564c0a11a0f704f4fc3e8acfe0f8245f0ad1347b378fbf96e206da11a5d3630624d25032e67a7e6a4910df5834b8fe70e6bcfeeac0352434196bdf4b2485d5a18f59a8d2a1a625a17f3fea0fe5eb8c896db3764f3185481bc22f91b4aaffcca25f26936857bc3a7c2539ea8ec3a952b7873033e038326e87ed3e1276fd140253fa08e9fc25fb2d9a98527fc22a2c9612fbeafdad446cbc7bcdbdcd780af2c16a"),
accelerated: true,
}, },
} }
for _, test := range tests { for _, test := range tests {
...@@ -134,6 +139,45 @@ func TestPrecompiles(t *testing.T) { ...@@ -134,6 +139,45 @@ func TestPrecompiles(t *testing.T) {
} }
runCannon(t, ctx, sys, inputs, "sequencer") runCannon(t, ctx, sys, inputs, "sequencer")
}) })
t.Run("DisputePrecompile-"+test.name, func(t *testing.T) {
op_e2e.InitParallel(t, op_e2e.UsesCannon)
if !test.accelerated {
t.Skipf("%v is not accelerated so no preimgae to upload", test.name)
}
ctx := context.Background()
sys, _ := StartFaultDisputeSystem(t, WithBlobBatches())
defer sys.Close()
l2Seq := sys.Clients["sequencer"]
aliceKey := sys.Cfg.Secrets.Alice
receipt := op_e2e.SendL2Tx(t, sys.Cfg, l2Seq, aliceKey, func(opts *op_e2e.TxOpts) {
opts.Gas = 1_000_000
opts.ToAddr = &test.address
opts.Nonce = 0
opts.Data = test.input
})
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
game := disputeGameFactory.StartOutputCannonGame(ctx, "sequencer", receipt.BlockNumber.Uint64(), common.Hash{0x01, 0xaa})
require.NotNil(t, game)
outputRootClaim := game.DisputeLastBlock(ctx)
game.LogGameData(ctx)
honestChallenger := game.StartChallenger(ctx, "HonestActor", challenger.WithPrivKey(sys.Cfg.Secrets.Alice))
// Wait for the honest challenger to dispute the outputRootClaim. This creates a root of an execution game that we challenge by coercing
// a step at a preimage trace index.
outputRootClaim = outputRootClaim.WaitForCounterClaim(ctx)
// Now the honest challenger is positioned as the defender of the execution game
// We then move to challenge it to induce a preimage load
preimageLoadCheck := game.CreateStepPreimageLoadCheck(ctx)
game.ChallengeToPreimageLoad(ctx, outputRootClaim, sys.Cfg.Secrets.Alice, utils.FirstPreimageLoadOfType("precompile"), preimageLoadCheck, false)
// The above method already verified the image was uploaded and step called successfully
// So we don't waste time resolving the game - that's tested elsewhere.
require.NoError(t, honestChallenger.Close())
})
} }
} }
......
...@@ -95,12 +95,12 @@ func (o *CachingOracle) GetBlob(ref eth.L1BlockRef, blobHash eth.IndexedBlobHash ...@@ -95,12 +95,12 @@ func (o *CachingOracle) GetBlob(ref eth.L1BlockRef, blobHash eth.IndexedBlobHash
return blob return blob
} }
func (o *CachingOracle) Precompile(address common.Address, input []byte) ([]byte, bool) { func (o *CachingOracle) Precompile(address common.Address, input []byte, requiredGas uint64) ([]byte, bool) {
cacheKey := crypto.Keccak256Hash(append(address.Bytes(), input...)) cacheKey := crypto.Keccak256Hash(append(address.Bytes(), input...))
if val, ok := o.pcmps.Get(cacheKey); ok { if val, ok := o.pcmps.Get(cacheKey); ok {
return val.result, val.ok return val.result, val.ok
} }
res, ok := o.oracle.Precompile(address, input) res, ok := o.oracle.Precompile(address, input, requiredGas)
o.pcmps.Add(cacheKey, precompileResult{res, ok}) o.pcmps.Add(cacheKey, precompileResult{res, ok})
return res, ok return res, ok
} }
package l1 package l1
import ( import (
"encoding/binary"
"math/rand" "math/rand"
"testing" "testing"
...@@ -99,18 +100,21 @@ func TestCachingOracle_Precompile(t *testing.T) { ...@@ -99,18 +100,21 @@ func TestCachingOracle_Precompile(t *testing.T) {
oracle := NewCachingOracle(stub) oracle := NewCachingOracle(stub)
input := []byte{0x01, 0x02, 0x03, 0x04} input := []byte{0x01, 0x02, 0x03, 0x04}
requiredGas := uint64(100)
output := []byte{0x0a, 0x0b, 0x0c, 0x0d} output := []byte{0x0a, 0x0b, 0x0c, 0x0d}
addr := common.Address{0x1} addr := common.Address{0x1}
key := crypto.Keccak256Hash(append(append(addr.Bytes(), binary.BigEndian.AppendUint64(nil, requiredGas)...), input...))
// Initial call retrieves from the stub // Initial call retrieves from the stub
stub.PcmpResults[crypto.Keccak256Hash(append(addr.Bytes(), input...))] = output stub.PcmpResults[key] = output
actualResult, actualStatus := oracle.Precompile(addr, input) actualResult, actualStatus := oracle.Precompile(addr, input, requiredGas)
require.True(t, actualStatus) require.True(t, actualStatus)
require.EqualValues(t, output, actualResult) require.EqualValues(t, output, actualResult)
// Later calls should retrieve from cache // Later calls should retrieve from cache
delete(stub.PcmpResults, crypto.Keccak256Hash(append(addr.Bytes(), input...))) delete(stub.PcmpResults, key)
actualResult, actualStatus = oracle.Precompile(addr, input) actualResult, actualStatus = oracle.Precompile(addr, input, requiredGas)
require.True(t, actualStatus) require.True(t, actualStatus)
require.EqualValues(t, output, actualResult) require.EqualValues(t, output, actualResult)
} }
...@@ -13,6 +13,7 @@ const ( ...@@ -13,6 +13,7 @@ const (
HintL1Receipts = "l1-receipts" HintL1Receipts = "l1-receipts"
HintL1Blob = "l1-blob" HintL1Blob = "l1-blob"
HintL1Precompile = "l1-precompile" HintL1Precompile = "l1-precompile"
HintL1PrecompileV2 = "l1-precompile-v2"
) )
type BlockHeaderHint common.Hash type BlockHeaderHint common.Hash
...@@ -54,3 +55,11 @@ var _ preimage.Hint = PrecompileHint{} ...@@ -54,3 +55,11 @@ var _ preimage.Hint = PrecompileHint{}
func (l PrecompileHint) Hint() string { func (l PrecompileHint) Hint() string {
return HintL1Precompile + " " + hexutil.Encode(l) return HintL1Precompile + " " + hexutil.Encode(l)
} }
type PrecompileHintV2 []byte
var _ preimage.Hint = PrecompileHintV2{}
func (l PrecompileHintV2) Hint() string {
return HintL1PrecompileV2 + " " + hexutil.Encode(l)
}
...@@ -29,7 +29,7 @@ type Oracle interface { ...@@ -29,7 +29,7 @@ type Oracle interface {
GetBlob(ref eth.L1BlockRef, blobHash eth.IndexedBlobHash) *eth.Blob GetBlob(ref eth.L1BlockRef, blobHash eth.IndexedBlobHash) *eth.Blob
// Precompile retrieves the result and success indicator of a precompile call for the given input. // Precompile retrieves the result and success indicator of a precompile call for the given input.
Precompile(precompileAddress common.Address, input []byte) ([]byte, bool) Precompile(precompileAddress common.Address, input []byte, requiredGas uint64) ([]byte, bool)
} }
// PreimageOracle implements Oracle using by interfacing with the pure preimage.Oracle // PreimageOracle implements Oracle using by interfacing with the pure preimage.Oracle
...@@ -119,9 +119,10 @@ func (p *PreimageOracle) GetBlob(ref eth.L1BlockRef, blobHash eth.IndexedBlobHas ...@@ -119,9 +119,10 @@ func (p *PreimageOracle) GetBlob(ref eth.L1BlockRef, blobHash eth.IndexedBlobHas
return &blob return &blob
} }
func (p *PreimageOracle) Precompile(address common.Address, input []byte) ([]byte, bool) { func (p *PreimageOracle) Precompile(address common.Address, input []byte, requiredGas uint64) ([]byte, bool) {
hintBytes := append(address.Bytes(), input...) hintBytes := append(address.Bytes(), binary.BigEndian.AppendUint64(nil, requiredGas)...)
p.hint.Hint(PrecompileHint(hintBytes)) hintBytes = append(hintBytes, input...)
p.hint.Hint(PrecompileHintV2(hintBytes))
key := preimage.PrecompileKey(crypto.Keccak256Hash(hintBytes)) key := preimage.PrecompileKey(crypto.Keccak256Hash(hintBytes))
result := p.oracle.Get(key) result := p.oracle.Get(key)
if len(result) == 0 { // must contain at least the status code if len(result) == 0 { // must contain at least the status code
......
package test package test
import ( import (
"encoding/binary"
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/eth"
...@@ -75,8 +76,10 @@ func (o StubOracle) GetBlob(ref eth.L1BlockRef, blobHash eth.IndexedBlobHash) *e ...@@ -75,8 +76,10 @@ func (o StubOracle) GetBlob(ref eth.L1BlockRef, blobHash eth.IndexedBlobHash) *e
return blob return blob
} }
func (o StubOracle) Precompile(addr common.Address, input []byte) ([]byte, bool) { func (o StubOracle) Precompile(addr common.Address, input []byte, requiredGas uint64) ([]byte, bool) {
result, ok := o.PcmpResults[crypto.Keccak256Hash(append(addr.Bytes(), input...))] arg := append(addr.Bytes(), binary.BigEndian.AppendUint64(nil, requiredGas)...)
arg = append(arg, input...)
result, ok := o.PcmpResults[crypto.Keccak256Hash(arg)]
if !ok { if !ok {
o.t.Fatalf("unknown kzg point evaluation %x", input) o.t.Fatalf("unknown kzg point evaluation %x", input)
} }
......
package l2 package l2
import ( import (
"encoding/binary"
"math/big" "math/big"
"testing" "testing"
...@@ -40,6 +41,12 @@ var ( ...@@ -40,6 +41,12 @@ var (
blobPrecompileReturnValue = common.FromHex("000000000000000000000000000000000000000000000000000000000000100073eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001") blobPrecompileReturnValue = common.FromHex("000000000000000000000000000000000000000000000000000000000000100073eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001")
) )
var (
ecRecoverRequiredGas uint64 = 3000
bn256PairingRequiredGas uint64 = 113000
kzgRequiredGas uint64 = 50_000
)
func TestInitialState(t *testing.T) { func TestInitialState(t *testing.T) {
blocks, chain := setupOracleBackedChain(t, 5) blocks, chain := setupOracleBackedChain(t, 5)
head := blocks[5] head := blocks[5]
...@@ -200,28 +207,32 @@ func TestGetHeaderByNumber(t *testing.T) { ...@@ -200,28 +207,32 @@ func TestGetHeaderByNumber(t *testing.T) {
func TestPrecompileOracle(t *testing.T) { func TestPrecompileOracle(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
input []byte input []byte
target common.Address target common.Address
result []byte requiredGas uint64
result []byte
}{ }{
{ {
name: "EcRecover", name: "EcRecover",
input: ecRecoverInputData, input: ecRecoverInputData,
target: common.BytesToAddress([]byte{0x1}), target: common.BytesToAddress([]byte{0x1}),
result: ecRecoverReturnValue, requiredGas: ecRecoverRequiredGas,
result: ecRecoverReturnValue,
}, },
{ {
name: "Bn256Pairing", name: "Bn256Pairing",
input: bn256PairingInputData, input: bn256PairingInputData,
target: common.BytesToAddress([]byte{0x8}), target: common.BytesToAddress([]byte{0x8}),
result: bn256PairingReturnValue, requiredGas: bn256PairingRequiredGas,
result: bn256PairingReturnValue,
}, },
{ {
name: "KZGPointEvaluation", name: "KZGPointEvaluation",
input: kzgInputData, input: kzgInputData,
target: common.BytesToAddress([]byte{0xa}), target: common.BytesToAddress([]byte{0xa}),
result: blobPrecompileReturnValue, requiredGas: kzgRequiredGas,
result: blobPrecompileReturnValue,
}, },
} }
...@@ -234,9 +245,11 @@ func TestPrecompileOracle(t *testing.T) { ...@@ -234,9 +245,11 @@ func TestPrecompileOracle(t *testing.T) {
chainCfg, blocks, oracle := setupOracle(t, blockCount, headBlockNumber, true) chainCfg, blocks, oracle := setupOracle(t, blockCount, headBlockNumber, true)
head := blocks[headBlockNumber].Hash() head := blocks[headBlockNumber].Hash()
stubOutput := eth.OutputV0{BlockHash: head} stubOutput := eth.OutputV0{BlockHash: head}
precompileOracle := new(l2test.StubPrecompileOracle) precompileOracle := l2test.NewStubPrecompileOracle(t)
arg := append(test.target.Bytes(), binary.BigEndian.AppendUint64(nil, test.requiredGas)...)
arg = append(arg, test.input...)
precompileOracle.Results = map[common.Hash]l2test.PrecompileResult{ precompileOracle.Results = map[common.Hash]l2test.PrecompileResult{
crypto.Keccak256Hash(append(test.target.Bytes(), test.input...)): {Result: test.result, Ok: true}, crypto.Keccak256Hash(arg): {Result: test.result, Ok: true},
} }
chain, err := NewOracleBackedL2Chain(logger, oracle, precompileOracle, chainCfg, common.Hash(eth.OutputRoot(&stubOutput))) chain, err := NewOracleBackedL2Chain(logger, oracle, precompileOracle, chainCfg, common.Hash(eth.OutputRoot(&stubOutput)))
require.NoError(t, err) require.NoError(t, err)
...@@ -265,7 +278,7 @@ func setupOracleBackedChainWithLowerHead(t *testing.T, blockCount int, headBlock ...@@ -265,7 +278,7 @@ func setupOracleBackedChainWithLowerHead(t *testing.T, blockCount int, headBlock
chainCfg, blocks, oracle := setupOracle(t, blockCount, headBlockNumber, false) chainCfg, blocks, oracle := setupOracle(t, blockCount, headBlockNumber, false)
head := blocks[headBlockNumber].Hash() head := blocks[headBlockNumber].Hash()
stubOutput := eth.OutputV0{BlockHash: head} stubOutput := eth.OutputV0{BlockHash: head}
precompileOracle := new(l2test.StubPrecompileOracle) precompileOracle := l2test.NewStubPrecompileOracle(t)
chain, err := NewOracleBackedL2Chain(logger, oracle, precompileOracle, chainCfg, common.Hash(eth.OutputRoot(&stubOutput))) chain, err := NewOracleBackedL2Chain(logger, oracle, precompileOracle, chainCfg, common.Hash(eth.OutputRoot(&stubOutput)))
require.NoError(t, err) require.NoError(t, err)
return blocks, chain return blocks, chain
......
...@@ -46,7 +46,7 @@ var ( ...@@ -46,7 +46,7 @@ var (
// PrecompileOracle defines the high-level API used to retrieve the result of a precompile call // PrecompileOracle defines the high-level API used to retrieve the result of a precompile call
// The caller is expected to validate the input to the precompile call // The caller is expected to validate the input to the precompile call
type PrecompileOracle interface { type PrecompileOracle interface {
Precompile(address common.Address, input []byte) ([]byte, bool) Precompile(address common.Address, input []byte, requiredGas uint64) ([]byte, bool)
} }
func CreatePrecompileOverrides(precompileOracle PrecompileOracle) vm.PrecompileOverrides { func CreatePrecompileOverrides(precompileOracle PrecompileOracle) vm.PrecompileOverrides {
...@@ -104,7 +104,7 @@ func (c *ecrecoverOracle) Run(input []byte) ([]byte, error) { ...@@ -104,7 +104,7 @@ func (c *ecrecoverOracle) Run(input []byte) ([]byte, error) {
// v needs to be at the end for libsecp256k1 // v needs to be at the end for libsecp256k1
// Modification note: below replaces the crypto.Ecrecover call // Modification note: below replaces the crypto.Ecrecover call
result, ok := c.Oracle.Precompile(ecrecoverPrecompileAddress, input) result, ok := c.Oracle.Precompile(ecrecoverPrecompileAddress, input, c.RequiredGas(input))
if !ok { if !ok {
return nil, errors.New("invalid ecrecover input") return nil, errors.New("invalid ecrecover input")
} }
...@@ -147,7 +147,7 @@ func (b *bn256PairingOracle) Run(input []byte) ([]byte, error) { ...@@ -147,7 +147,7 @@ func (b *bn256PairingOracle) Run(input []byte) ([]byte, error) {
} }
// Modification note: below replaces point verification and pairing checks // Modification note: below replaces point verification and pairing checks
// Assumes both L2 and the L1 oracle have an identical range of valid points // Assumes both L2 and the L1 oracle have an identical range of valid points
result, ok := b.Oracle.Precompile(bn256PairingPrecompileAddress, input) result, ok := b.Oracle.Precompile(bn256PairingPrecompileAddress, input, b.RequiredGas(input))
if !ok { if !ok {
return nil, errors.New("invalid bn256Pairing check") return nil, errors.New("invalid bn256Pairing check")
} }
...@@ -214,7 +214,7 @@ func (b *kzgPointEvaluationOracle) Run(input []byte) ([]byte, error) { ...@@ -214,7 +214,7 @@ func (b *kzgPointEvaluationOracle) Run(input []byte) ([]byte, error) {
copy(proof[:], input[144:]) copy(proof[:], input[144:])
// Modification note: below replaces the kzg4844.VerifyProof call // Modification note: below replaces the kzg4844.VerifyProof call
result, ok := b.Oracle.Precompile(kzgPointEvaluationPrecompileAddress, input) result, ok := b.Oracle.Precompile(kzgPointEvaluationPrecompileAddress, input, b.RequiredGas(input))
if !ok { if !ok {
return nil, fmt.Errorf("%w: invalid KZG point evaluation", errBlobVerifyKZGProof) return nil, fmt.Errorf("%w: invalid KZG point evaluation", errBlobVerifyKZGProof)
} }
......
package test package test
import ( import (
"encoding/binary"
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/eth"
...@@ -130,15 +131,21 @@ type StubPrecompileOracle struct { ...@@ -130,15 +131,21 @@ type StubPrecompileOracle struct {
Calls int Calls int
} }
func NewStubPrecompileOracle(t *testing.T) *StubPrecompileOracle {
return &StubPrecompileOracle{t: t, Results: make(map[common.Hash]PrecompileResult)}
}
type PrecompileResult struct { type PrecompileResult struct {
Result []byte Result []byte
Ok bool Ok bool
} }
func (o *StubPrecompileOracle) Precompile(address common.Address, input []byte) ([]byte, bool) { func (o *StubPrecompileOracle) Precompile(address common.Address, input []byte, requiredGas uint64) ([]byte, bool) {
result, ok := o.Results[crypto.Keccak256Hash(append(address.Bytes(), input...))] arg := append(address.Bytes(), binary.BigEndian.AppendUint64(nil, requiredGas)...)
arg = append(arg, input...)
result, ok := o.Results[crypto.Keccak256Hash(arg)]
if !ok { if !ok {
o.t.Fatalf("no value for point evaluation %v", input) o.t.Fatalf("no value for point evaluation %x required gas %v", input, requiredGas)
} }
o.Calls++ o.Calls++
return result.Result, result.Ok return result.Result, result.Ok
......
...@@ -205,6 +205,36 @@ func (p *Prefetcher) prefetch(ctx context.Context, hint string) error { ...@@ -205,6 +205,36 @@ func (p *Prefetcher) prefetch(ctx context.Context, hint string) error {
return err return err
} }
return p.kvStore.Put(preimage.PrecompileKey(inputHash).PreimageKey(), result) return p.kvStore.Put(preimage.PrecompileKey(inputHash).PreimageKey(), result)
case l1.HintL1PrecompileV2:
if len(hintBytes) < 28 {
return fmt.Errorf("invalid precompile hint: %x", hint)
}
precompileAddress := common.BytesToAddress(hintBytes[:20])
// requiredGas := hintBytes[20:28] - unused by the host. Since the client already validates gas requirements.
// The requiredGas is only used by the L1 PreimageOracle to enforce complete precompile execution.
// For extra safety, avoid accelerating unexpected precompiles
if !slices.Contains(acceleratedPrecompiles, precompileAddress) {
return fmt.Errorf("unsupported precompile address: %s", precompileAddress)
}
// NOTE: We use the precompiled contracts from Cancun because it's the only set that contains the addresses of all accelerated precompiles
// We assume the precompile Run function behavior does not change across EVM upgrades.
// As such, we must not rely on upgrade-specific behavior such as precompile.RequiredGas.
precompile := getPrecompiledContract(precompileAddress)
// KZG Point Evaluation precompile also verifies its input
result, err := precompile.Run(hintBytes[28:])
if err == nil {
result = append(precompileSuccess[:], result...)
} else {
result = append(precompileFailure[:], result...)
}
inputHash := crypto.Keccak256Hash(hintBytes)
// Put the input preimage so it can be loaded later
if err := p.kvStore.Put(preimage.Keccak256Key(inputHash).PreimageKey(), hintBytes); err != nil {
return err
}
return p.kvStore.Put(preimage.PrecompileKey(inputHash).PreimageKey(), result)
case l2.HintL2BlockHeader, l2.HintL2Transactions: case l2.HintL2BlockHeader, l2.HintL2Transactions:
if len(hintBytes) != 32 { if len(hintBytes) != 32 {
return fmt.Errorf("invalid L2 header/tx hint: %x", hint) return fmt.Errorf("invalid L2 header/tx hint: %x", hint)
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"context" "context"
"crypto/sha256" "crypto/sha256"
"encoding/binary" "encoding/binary"
"fmt"
"math/rand" "math/rand"
"testing" "testing"
...@@ -25,6 +26,11 @@ import ( ...@@ -25,6 +26,11 @@ import (
"github.com/ethereum-optimism/optimism/op-service/testutils" "github.com/ethereum-optimism/optimism/op-service/testutils"
) )
var (
ecRecoverInput = common.FromHex("18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000000000000000000000000000001c73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549")
kzgPointEvalInput = common.FromHex("01e798154708fe7789429634053cbf9f99b619f9f084048927333fce637f549b564c0a11a0f704f4fc3e8acfe0f8245f0ad1347b378fbf96e206da11a5d3630624d25032e67a7e6a4910df5834b8fe70e6bcfeeac0352434196bdf4b2485d5a18f59a8d2a1a625a17f3fea0fe5eb8c896db3764f3185481bc22f91b4aaffcca25f26936857bc3a7c2539ea8ec3a952b7873033e038326e87ed3e1276fd140253fa08e9fc25fb2d9a98527fc22a2c9612fbeafdad446cbc7bcdbdcd780af2c16a")
)
func TestNoHint(t *testing.T) { func TestNoHint(t *testing.T) {
t.Run("NotFound", func(t *testing.T) { t.Run("NotFound", func(t *testing.T) {
prefetcher, _, _, _, _ := createPrefetcher(t) prefetcher, _, _, _, _ := createPrefetcher(t)
...@@ -221,9 +227,6 @@ func TestFetchL1Blob(t *testing.T) { ...@@ -221,9 +227,6 @@ func TestFetchL1Blob(t *testing.T) {
} }
func TestFetchPrecompileResult(t *testing.T) { func TestFetchPrecompileResult(t *testing.T) {
ecRecoverInput := common.FromHex("18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000000000000000000000000000001c73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549")
kzgPointEvalInput := common.FromHex("01e798154708fe7789429634053cbf9f99b619f9f084048927333fce637f549b564c0a11a0f704f4fc3e8acfe0f8245f0ad1347b378fbf96e206da11a5d3630624d25032e67a7e6a4910df5834b8fe70e6bcfeeac0352434196bdf4b2485d5a18f59a8d2a1a625a17f3fea0fe5eb8c896db3764f3185481bc22f91b4aaffcca25f26936857bc3a7c2539ea8ec3a952b7873033e038326e87ed3e1276fd140253fa08e9fc25fb2d9a98527fc22a2c9612fbeafdad446cbc7bcdbdcd780af2c16a")
failure := []byte{0} failure := []byte{0}
success := []byte{1} success := []byte{1}
...@@ -239,6 +242,18 @@ func TestFetchPrecompileResult(t *testing.T) { ...@@ -239,6 +242,18 @@ func TestFetchPrecompileResult(t *testing.T) {
input: ecRecoverInput, input: ecRecoverInput,
result: append(success, common.FromHex("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b")...), result: append(success, common.FromHex("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b")...),
}, },
{
name: "KzgPointEvaluation-Valid",
addr: common.BytesToAddress([]byte{0xa}),
input: kzgPointEvalInput,
result: append(success, common.FromHex("000000000000000000000000000000000000000000000000000000000000100073eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001")...),
},
{
name: "KzgPointEvaluation-Invalid",
addr: common.BytesToAddress([]byte{0xa}),
input: []byte{0x0},
result: failure,
},
{ {
name: "Bn256Pairing-Valid", name: "Bn256Pairing-Valid",
addr: common.BytesToAddress([]byte{0x8}), addr: common.BytesToAddress([]byte{0x8}),
...@@ -251,17 +266,88 @@ func TestFetchPrecompileResult(t *testing.T) { ...@@ -251,17 +266,88 @@ func TestFetchPrecompileResult(t *testing.T) {
input: []byte{0x1}, input: []byte{0x1},
result: failure, result: failure,
}, },
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
prefetcher, _, _, _, _ := createPrefetcher(t)
oracle := newLegacyPrecompileOracle(asOracleFn(t, prefetcher), asHinter(t, prefetcher))
result, ok := oracle.Precompile(test.addr, test.input)
require.Equal(t, test.result[0] == 1, ok)
require.EqualValues(t, test.result[1:], result)
key := crypto.Keccak256Hash(append(test.addr.Bytes(), test.input...))
val, err := prefetcher.kvStore.Get(preimage.Keccak256Key(key).PreimageKey())
require.NoError(t, err)
require.NotEmpty(t, val)
val, err = prefetcher.kvStore.Get(preimage.PrecompileKey(key).PreimageKey())
require.NoError(t, err)
require.EqualValues(t, test.result, val)
})
}
t.Run("Already Known", func(t *testing.T) {
input := []byte("test input")
addr := common.BytesToAddress([]byte{0x1})
result := []byte{0x1}
prefetcher, _, _, _, kv := createPrefetcher(t)
err := kv.Put(preimage.PrecompileKey(crypto.Keccak256Hash(append(addr.Bytes(), input...))).PreimageKey(), append([]byte{1}, result...))
require.NoError(t, err)
oracle := newLegacyPrecompileOracle(asOracleFn(t, prefetcher), asHinter(t, prefetcher))
actualResult, status := oracle.Precompile(addr, input)
require.EqualValues(t, result, actualResult)
require.True(t, status)
})
}
func TestFetchPrecompileResultV2(t *testing.T) {
failure := []byte{0}
success := []byte{1}
tests := []struct {
name string
addr common.Address
input []byte
requiredGas uint64
result []byte
}{
{
name: "EcRecover-Valid",
addr: common.BytesToAddress([]byte{0x1}),
input: ecRecoverInput,
requiredGas: 3000,
result: append(success, common.FromHex("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b")...),
},
{ {
name: "KzgPointEvaluation-Valid", name: "Bn256Pairing-Valid",
addr: common.BytesToAddress([]byte{0xa}), addr: common.BytesToAddress([]byte{0x8}),
input: kzgPointEvalInput, input: []byte{}, // empty is valid
result: append(success, common.FromHex("000000000000000000000000000000000000000000000000000000000000100073eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001")...), requiredGas: 6000,
result: append(success, common.FromHex("0000000000000000000000000000000000000000000000000000000000000001")...),
}, },
{ {
name: "KzgPointEvaluation-Invalid", name: "Bn256Pairing-Invalid",
addr: common.BytesToAddress([]byte{0xa}), addr: common.BytesToAddress([]byte{0x8}),
input: []byte{0x0}, input: []byte{0x1},
result: failure, requiredGas: 6000,
result: failure,
},
{
name: "KzgPointEvaluation-Valid",
addr: common.BytesToAddress([]byte{0xa}),
input: kzgPointEvalInput,
requiredGas: 50_000,
result: append(success, common.FromHex("000000000000000000000000000000000000000000000000000000000000100073eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001")...),
},
{
name: "KzgPointEvaluation-Invalid",
addr: common.BytesToAddress([]byte{0xa}),
input: []byte{0x0},
requiredGas: 50_000,
result: failure,
}, },
} }
for _, test := range tests { for _, test := range tests {
...@@ -270,11 +356,11 @@ func TestFetchPrecompileResult(t *testing.T) { ...@@ -270,11 +356,11 @@ func TestFetchPrecompileResult(t *testing.T) {
prefetcher, _, _, _, _ := createPrefetcher(t) prefetcher, _, _, _, _ := createPrefetcher(t)
oracle := l1.NewPreimageOracle(asOracleFn(t, prefetcher), asHinter(t, prefetcher)) oracle := l1.NewPreimageOracle(asOracleFn(t, prefetcher), asHinter(t, prefetcher))
result, ok := oracle.Precompile(test.addr, test.input) result, ok := oracle.Precompile(test.addr, test.input, test.requiredGas)
require.Equal(t, test.result[0] == 1, ok) require.Equal(t, test.result[0] == 1, ok)
require.EqualValues(t, test.result[1:], result) require.EqualValues(t, test.result[1:], result)
key := crypto.Keccak256Hash(append(test.addr.Bytes(), test.input...)) key := crypto.Keccak256Hash(append(append(test.addr.Bytes(), binary.BigEndian.AppendUint64(nil, test.requiredGas)...), test.input...))
val, err := prefetcher.kvStore.Get(preimage.Keccak256Key(key).PreimageKey()) val, err := prefetcher.kvStore.Get(preimage.Keccak256Key(key).PreimageKey())
require.NoError(t, err) require.NoError(t, err)
require.NotEmpty(t, val) require.NotEmpty(t, val)
...@@ -287,19 +373,35 @@ func TestFetchPrecompileResult(t *testing.T) { ...@@ -287,19 +373,35 @@ func TestFetchPrecompileResult(t *testing.T) {
t.Run("Already Known", func(t *testing.T) { t.Run("Already Known", func(t *testing.T) {
input := []byte("test input") input := []byte("test input")
requiredGas := uint64(3000)
addr := common.BytesToAddress([]byte{0x1}) addr := common.BytesToAddress([]byte{0x1})
result := []byte{0x1} result := []byte{0x1}
prefetcher, _, _, _, kv := createPrefetcher(t) prefetcher, _, _, _, kv := createPrefetcher(t)
err := kv.Put(preimage.PrecompileKey(crypto.Keccak256Hash(append(addr.Bytes(), input...))).PreimageKey(), append([]byte{1}, result...)) keyArg := append(addr.Bytes(), binary.BigEndian.AppendUint64(nil, requiredGas)...)
keyArg = append(keyArg, input...)
err := kv.Put(preimage.PrecompileKey(crypto.Keccak256Hash(keyArg)).PreimageKey(), append([]byte{1}, result...))
require.NoError(t, err) require.NoError(t, err)
oracle := l1.NewPreimageOracle(asOracleFn(t, prefetcher), asHinter(t, prefetcher)) oracle := l1.NewPreimageOracle(asOracleFn(t, prefetcher), asHinter(t, prefetcher))
actualResult, status := oracle.Precompile(addr, input) actualResult, status := oracle.Precompile(addr, input, requiredGas)
require.EqualValues(t, actualResult, result) require.EqualValues(t, actualResult, result)
require.True(t, status) require.True(t, status)
}) })
} }
func TestUnsupportedPrecompile(t *testing.T) {
prefetcher, _, _, _, _ := createPrefetcher(t)
oracleFn := func(t *testing.T, prefetcher *Prefetcher) preimage.OracleFn {
return func(key preimage.Key) []byte {
_, err := prefetcher.GetPreimage(context.Background(), key.PreimageKey())
require.ErrorContains(t, err, "unsupported precompile address")
return []byte{1}
}
}
oracle := newLegacyPrecompileOracle(oracleFn(t, prefetcher), asHinter(t, prefetcher))
oracle.Precompile(common.HexToAddress("0xdead"), nil)
}
func TestRestrictedPrecompileContracts(t *testing.T) { func TestRestrictedPrecompileContracts(t *testing.T) {
for _, addr := range acceleratedPrecompiles { for _, addr := range acceleratedPrecompiles {
require.NotNil(t, getPrecompiledContract(addr)) require.NotNil(t, getPrecompiledContract(addr))
...@@ -596,3 +698,28 @@ func assertReceiptsEqual(t *testing.T, expectedRcpt types.Receipts, actualRcpt t ...@@ -596,3 +698,28 @@ func assertReceiptsEqual(t *testing.T, expectedRcpt types.Receipts, actualRcpt t
require.Equal(t, expected, actual) require.Equal(t, expected, actual)
} }
} }
// legacyOracleImpl is a wrapper around the new preimage.Oracle interface that uses the legacy preimage hint API.
// It's used to test backwards-compatibility with clients using legacy preimage hints.
type legacyPrecompileOracle struct {
oracle preimage.Oracle
hint preimage.Hinter
}
func newLegacyPrecompileOracle(raw preimage.Oracle, hint preimage.Hinter) *legacyPrecompileOracle {
return &legacyPrecompileOracle{
oracle: raw,
hint: hint,
}
}
func (o *legacyPrecompileOracle) Precompile(address common.Address, input []byte) ([]byte, bool) {
hintBytes := append(address.Bytes(), input...)
o.hint.Hint(l1.PrecompileHint(hintBytes))
key := preimage.PrecompileKey(crypto.Keccak256Hash(hintBytes))
result := o.oracle.Get(key)
if len(result) == 0 { // must contain at least the status code
panic(fmt.Errorf("unexpected precompile oracle behavior, got result: %x", result))
}
return result[1:], result[0] == 1
}
...@@ -5,7 +5,7 @@ SCRIPTS_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) ...@@ -5,7 +5,7 @@ SCRIPTS_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
COMPAT_DIR="${SCRIPTS_DIR}/../temp/compat" COMPAT_DIR="${SCRIPTS_DIR}/../temp/compat"
TESTNAME="${1?Must specify compat file to run}" TESTNAME="${1?Must specify compat file to run}"
BASEURL="${2:-https://github.com/ethereum-optimism/chain-test-data/releases/download/2024-03-14.3}" BASEURL="${2:-https://github.com/ethereum-optimism/chain-test-data/releases/download/2024-08-02}"
URL="${BASEURL}/${TESTNAME}.tar.bz" URL="${BASEURL}/${TESTNAME}.tar.bz"
......
...@@ -56,7 +56,7 @@ func (c *expectedCall) Matches(rpcMethod string, args ...interface{}) error { ...@@ -56,7 +56,7 @@ func (c *expectedCall) Matches(rpcMethod string, args ...interface{}) error {
return fmt.Errorf("expected input to have at least 4 bytes but was %v", len(data)) return fmt.Errorf("expected input to have at least 4 bytes but was %v", len(data))
} }
if !slices.Equal(c.abiMethod.ID, data[:4]) { if !slices.Equal(c.abiMethod.ID, data[:4]) {
return fmt.Errorf("expected abi method ID %x but was %x", c.abiMethod.ID, data[:4]) return fmt.Errorf("expected abi method ID %x but was %v", c.abiMethod.ID, data[:4])
} }
if !slices.Equal(c.packedArgs, data[4:]) { if !slices.Equal(c.packedArgs, data[4:]) {
return fmt.Errorf("expected args %x but was %x", c.packedArgs, data[4:]) return fmt.Errorf("expected args %x but was %x", c.packedArgs, data[4:])
......
...@@ -59,7 +59,7 @@ func (r *RpcStub) findExpectedCall(rpcMethod string, args ...interface{}) Expect ...@@ -59,7 +59,7 @@ func (r *RpcStub) findExpectedCall(rpcMethod string, args ...interface{}) Expect
if err := call.Matches(rpcMethod, args...); err == nil { if err := call.Matches(rpcMethod, args...); err == nil {
return call return call
} else { } else {
matchResults += fmt.Sprintf("%v: %v", call, err) matchResults += fmt.Sprintf("%v: %v\n", call, err)
} }
} }
require.Failf(r.t, "No matching expected calls.", matchResults) require.Failf(r.t, "No matching expected calls.", matchResults)
......
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
"requiredProtocolVersion": "0x0000000000000000000000000000000000000004000000000000000000000001", "requiredProtocolVersion": "0x0000000000000000000000000000000000000004000000000000000000000001",
"recommendedProtocolVersion": "0x0000000000000000000000000000000000000004000000000000000000000001", "recommendedProtocolVersion": "0x0000000000000000000000000000000000000004000000000000000000000001",
"fundDevAccounts": false, "fundDevAccounts": false,
"faultGameAbsolutePrestate": "0x0385c3f8ee78491001d92b90b07d0cf387b7b52ab9b83b4d87c994e92cf823ba", "faultGameAbsolutePrestate": "0x030de10d9da911a2b180ecfae2aeaba8758961fc28262ce989458c6f9a547922",
"faultGameMaxDepth": 73, "faultGameMaxDepth": 73,
"faultGameClockExtension": 10800, "faultGameClockExtension": 10800,
"faultGameMaxClockDuration": 302400, "faultGameMaxClockDuration": 302400,
......
...@@ -208,6 +208,32 @@ library ChainAssertions { ...@@ -208,6 +208,32 @@ library ChainAssertions {
} }
} }
/// @notice Asserts that the permissioned DelayedWETH is setup correctly
function checkPermissionedDelayedWETH(
Types.ContractSet memory _contracts,
DeployConfig _cfg,
bool _isProxy,
address _expectedOwner
)
internal
view
{
console.log("Running chain assertions on the permissioned DelayedWETH");
DelayedWETH weth = DelayedWETH(payable(_contracts.PermissionedDelayedWETH));
// Check that the contract is initialized
assertSlotValueIsOne({ _contractAddress: address(weth), _slot: 0, _offset: 0 });
if (_isProxy) {
require(weth.owner() == _expectedOwner);
require(weth.delay() == _cfg.faultGameWithdrawalDelay());
require(weth.config() == SuperchainConfig(_contracts.SuperchainConfig));
} else {
require(weth.owner() == _expectedOwner);
require(weth.delay() == _cfg.faultGameWithdrawalDelay());
}
}
/// @notice Asserts that the L2OutputOracle is setup correctly /// @notice Asserts that the L2OutputOracle is setup correctly
function checkL2OutputOracle( function checkL2OutputOracle(
Types.ContractSet memory _contracts, Types.ContractSet memory _contracts,
......
...@@ -151,6 +151,7 @@ contract Deploy is Deployer { ...@@ -151,6 +151,7 @@ contract Deploy is Deployer {
L2OutputOracle: mustGetAddress("L2OutputOracleProxy"), L2OutputOracle: mustGetAddress("L2OutputOracleProxy"),
DisputeGameFactory: mustGetAddress("DisputeGameFactoryProxy"), DisputeGameFactory: mustGetAddress("DisputeGameFactoryProxy"),
DelayedWETH: mustGetAddress("DelayedWETHProxy"), DelayedWETH: mustGetAddress("DelayedWETHProxy"),
PermissionedDelayedWETH: mustGetAddress("PermissionedDelayedWETHProxy"),
AnchorStateRegistry: mustGetAddress("AnchorStateRegistryProxy"), AnchorStateRegistry: mustGetAddress("AnchorStateRegistryProxy"),
OptimismMintableERC20Factory: mustGetAddress("OptimismMintableERC20FactoryProxy"), OptimismMintableERC20Factory: mustGetAddress("OptimismMintableERC20FactoryProxy"),
OptimismPortal: mustGetAddress("OptimismPortalProxy"), OptimismPortal: mustGetAddress("OptimismPortalProxy"),
...@@ -170,6 +171,7 @@ contract Deploy is Deployer { ...@@ -170,6 +171,7 @@ contract Deploy is Deployer {
L2OutputOracle: getAddress("L2OutputOracleProxy"), L2OutputOracle: getAddress("L2OutputOracleProxy"),
DisputeGameFactory: getAddress("DisputeGameFactoryProxy"), DisputeGameFactory: getAddress("DisputeGameFactoryProxy"),
DelayedWETH: getAddress("DelayedWETHProxy"), DelayedWETH: getAddress("DelayedWETHProxy"),
PermissionedDelayedWETH: getAddress("PermissionedDelayedWETHProxy"),
AnchorStateRegistry: getAddress("AnchorStateRegistryProxy"), AnchorStateRegistry: getAddress("AnchorStateRegistryProxy"),
OptimismMintableERC20Factory: getAddress("OptimismMintableERC20FactoryProxy"), OptimismMintableERC20Factory: getAddress("OptimismMintableERC20FactoryProxy"),
OptimismPortal: getAddress("OptimismPortalProxy"), OptimismPortal: getAddress("OptimismPortalProxy"),
...@@ -369,6 +371,7 @@ contract Deploy is Deployer { ...@@ -369,6 +371,7 @@ contract Deploy is Deployer {
deployERC1967Proxy("DisputeGameFactoryProxy"); deployERC1967Proxy("DisputeGameFactoryProxy");
deployERC1967Proxy("L2OutputOracleProxy"); deployERC1967Proxy("L2OutputOracleProxy");
deployERC1967Proxy("DelayedWETHProxy"); deployERC1967Proxy("DelayedWETHProxy");
deployERC1967Proxy("PermissionedDelayedWETHProxy");
deployERC1967Proxy("AnchorStateRegistryProxy"); deployERC1967Proxy("AnchorStateRegistryProxy");
transferAddressManagerOwnership(); // to the ProxyAdmin transferAddressManagerOwnership(); // to the ProxyAdmin
...@@ -413,6 +416,7 @@ contract Deploy is Deployer { ...@@ -413,6 +416,7 @@ contract Deploy is Deployer {
initializeL2OutputOracle(); initializeL2OutputOracle();
initializeDisputeGameFactory(); initializeDisputeGameFactory();
initializeDelayedWETH(); initializeDelayedWETH();
initializePermissionedDelayedWETH();
initializeAnchorStateRegistry(); initializeAnchorStateRegistry();
} }
...@@ -959,10 +963,34 @@ contract Deploy is Deployer { ...@@ -959,10 +963,34 @@ contract Deploy is Deployer {
}); });
} }
function initializePermissionedDelayedWETH() public broadcast {
console.log("Upgrading and initializing permissioned DelayedWETH proxy");
address delayedWETHProxy = mustGetAddress("PermissionedDelayedWETHProxy");
address delayedWETH = mustGetAddress("DelayedWETH");
address superchainConfigProxy = mustGetAddress("SuperchainConfigProxy");
_upgradeAndCallViaSafe({
_proxy: payable(delayedWETHProxy),
_implementation: delayedWETH,
_innerCallData: abi.encodeCall(DelayedWETH.initialize, (msg.sender, SuperchainConfig(superchainConfigProxy)))
});
string memory version = DelayedWETH(payable(delayedWETHProxy)).version();
console.log("DelayedWETH version: %s", version);
ChainAssertions.checkPermissionedDelayedWETH({
_contracts: _proxiesUnstrict(),
_cfg: cfg,
_isProxy: true,
_expectedOwner: msg.sender
});
}
function initializeAnchorStateRegistry() public broadcast { function initializeAnchorStateRegistry() public broadcast {
console.log("Upgrading and initializing AnchorStateRegistry proxy"); console.log("Upgrading and initializing AnchorStateRegistry proxy");
address anchorStateRegistryProxy = mustGetAddress("AnchorStateRegistryProxy"); address anchorStateRegistryProxy = mustGetAddress("AnchorStateRegistryProxy");
address anchorStateRegistry = mustGetAddress("AnchorStateRegistry"); address anchorStateRegistry = mustGetAddress("AnchorStateRegistry");
SuperchainConfig superchainConfig = SuperchainConfig(mustGetAddress("SuperchainConfigProxy"));
AnchorStateRegistry.StartingAnchorRoot[] memory roots = new AnchorStateRegistry.StartingAnchorRoot[](5); AnchorStateRegistry.StartingAnchorRoot[] memory roots = new AnchorStateRegistry.StartingAnchorRoot[](5);
roots[0] = AnchorStateRegistry.StartingAnchorRoot({ roots[0] = AnchorStateRegistry.StartingAnchorRoot({
...@@ -1004,7 +1032,7 @@ contract Deploy is Deployer { ...@@ -1004,7 +1032,7 @@ contract Deploy is Deployer {
_upgradeAndCallViaSafe({ _upgradeAndCallViaSafe({
_proxy: payable(anchorStateRegistryProxy), _proxy: payable(anchorStateRegistryProxy),
_implementation: anchorStateRegistry, _implementation: anchorStateRegistry,
_innerCallData: abi.encodeCall(AnchorStateRegistry.initialize, (roots)) _innerCallData: abi.encodeCall(AnchorStateRegistry.initialize, (roots, superchainConfig))
}); });
string memory version = AnchorStateRegistry(payable(anchorStateRegistryProxy)).version(); string memory version = AnchorStateRegistry(payable(anchorStateRegistryProxy)).version();
...@@ -1348,6 +1376,25 @@ contract Deploy is Deployer { ...@@ -1348,6 +1376,25 @@ contract Deploy is Deployer {
ChainAssertions.checkDelayedWETH({ _contracts: _proxies(), _cfg: cfg, _isProxy: true, _expectedOwner: safe }); ChainAssertions.checkDelayedWETH({ _contracts: _proxies(), _cfg: cfg, _isProxy: true, _expectedOwner: safe });
} }
/// @notice Transfer ownership of the permissioned DelayedWETH contract to the final system owner
function transferPermissionedDelayedWETHOwnership() public broadcast {
console.log("Transferring permissioned DelayedWETH ownership to Safe");
DelayedWETH weth = DelayedWETH(mustGetAddress("PermissionedDelayedWETHProxy"));
address owner = weth.owner();
address safe = mustGetAddress("SystemOwnerSafe");
if (owner != safe) {
weth.transferOwnership(safe);
console.log("DelayedWETH ownership transferred to Safe at: %s", safe);
}
ChainAssertions.checkPermissionedDelayedWETH({
_contracts: _proxies(),
_cfg: cfg,
_isProxy: true,
_expectedOwner: safe
});
}
/// @notice Loads the mips absolute prestate from the prestate-proof for devnets otherwise /// @notice Loads the mips absolute prestate from the prestate-proof for devnets otherwise
/// from the config. /// from the config.
function loadMipsAbsolutePrestate() internal returns (Claim mipsAbsolutePrestate_) { function loadMipsAbsolutePrestate() internal returns (Claim mipsAbsolutePrestate_) {
...@@ -1401,7 +1448,7 @@ contract Deploy is Deployer { ...@@ -1401,7 +1448,7 @@ contract Deploy is Deployer {
function setPermissionedCannonFaultGameImplementation(bool _allowUpgrade) public broadcast { function setPermissionedCannonFaultGameImplementation(bool _allowUpgrade) public broadcast {
console.log("Setting Cannon PermissionedDisputeGame implementation"); console.log("Setting Cannon PermissionedDisputeGame implementation");
DisputeGameFactory factory = DisputeGameFactory(mustGetAddress("DisputeGameFactoryProxy")); DisputeGameFactory factory = DisputeGameFactory(mustGetAddress("DisputeGameFactoryProxy"));
DelayedWETH weth = DelayedWETH(mustGetAddress("DelayedWETHProxy")); DelayedWETH weth = DelayedWETH(mustGetAddress("PermissionedDelayedWETHProxy"));
// Set the Cannon FaultDisputeGame implementation in the factory. // Set the Cannon FaultDisputeGame implementation in the factory.
_setFaultGameImplementation({ _setFaultGameImplementation({
...@@ -1449,6 +1496,7 @@ contract Deploy is Deployer { ...@@ -1449,6 +1496,7 @@ contract Deploy is Deployer {
DelayedWETH weth = DelayedWETH(mustGetAddress("DelayedWETHProxy")); DelayedWETH weth = DelayedWETH(mustGetAddress("DelayedWETHProxy"));
Claim outputAbsolutePrestate = Claim.wrap(bytes32(cfg.faultGameAbsolutePrestate())); Claim outputAbsolutePrestate = Claim.wrap(bytes32(cfg.faultGameAbsolutePrestate()));
PreimageOracle fastOracle = new PreimageOracle(cfg.preimageOracleMinProposalSize(), 0);
_setFaultGameImplementation({ _setFaultGameImplementation({
_factory: factory, _factory: factory,
_allowUpgrade: _allowUpgrade, _allowUpgrade: _allowUpgrade,
...@@ -1457,7 +1505,7 @@ contract Deploy is Deployer { ...@@ -1457,7 +1505,7 @@ contract Deploy is Deployer {
weth: weth, weth: weth,
gameType: GameTypes.FAST, gameType: GameTypes.FAST,
absolutePrestate: outputAbsolutePrestate, absolutePrestate: outputAbsolutePrestate,
faultVm: IBigStepper(new AlphabetVM(outputAbsolutePrestate, PreimageOracle(mustGetAddress("PreimageOracle")))), faultVm: IBigStepper(new AlphabetVM(outputAbsolutePrestate, fastOracle)),
// The max depth for the alphabet trace is always 3. Add 1 because split depth is fully inclusive. // The max depth for the alphabet trace is always 3. Add 1 because split depth is fully inclusive.
maxGameDepth: cfg.faultGameSplitDepth() + 3 + 1, maxGameDepth: cfg.faultGameSplitDepth() + 3 + 1,
maxClockDuration: Duration.wrap(0) // Resolvable immediately maxClockDuration: Duration.wrap(0) // Resolvable immediately
......
...@@ -92,6 +92,8 @@ contract FPACOPS is Deploy, StdAssertions { ...@@ -92,6 +92,8 @@ contract FPACOPS is Deploy, StdAssertions {
function initializeAnchorStateRegistryProxy() internal broadcast { function initializeAnchorStateRegistryProxy() internal broadcast {
console.log("Initializing AnchorStateRegistryProxy with AnchorStateRegistry."); console.log("Initializing AnchorStateRegistryProxy with AnchorStateRegistry.");
address superchainConfigProxy = mustGetAddress("SuperchainConfigProxy");
SuperchainConfig superchainConfig = SuperchainConfig(superchainConfigProxy);
AnchorStateRegistry.StartingAnchorRoot[] memory roots = new AnchorStateRegistry.StartingAnchorRoot[](2); AnchorStateRegistry.StartingAnchorRoot[] memory roots = new AnchorStateRegistry.StartingAnchorRoot[](2);
roots[0] = AnchorStateRegistry.StartingAnchorRoot({ roots[0] = AnchorStateRegistry.StartingAnchorRoot({
...@@ -111,7 +113,8 @@ contract FPACOPS is Deploy, StdAssertions { ...@@ -111,7 +113,8 @@ contract FPACOPS is Deploy, StdAssertions {
address asrProxy = mustGetAddress("AnchorStateRegistryProxy"); address asrProxy = mustGetAddress("AnchorStateRegistryProxy");
Proxy(payable(asrProxy)).upgradeToAndCall( Proxy(payable(asrProxy)).upgradeToAndCall(
mustGetAddress("AnchorStateRegistry"), abi.encodeCall(AnchorStateRegistry.initialize, (roots)) mustGetAddress("AnchorStateRegistry"),
abi.encodeCall(AnchorStateRegistry.initialize, (roots, superchainConfig))
); );
} }
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { Proxy } from "src/universal/Proxy.sol";
import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol";
import { AnchorStateRegistry, IAnchorStateRegistry } from "src/dispute/AnchorStateRegistry.sol";
import { IDelayedWETH } from "src/dispute/interfaces/IDelayedWETH.sol";
import { StdAssertions } from "forge-std/StdAssertions.sol";
import "src/dispute/lib/Types.sol";
import "scripts/deploy/Deploy.s.sol";
/// @notice Deploys new implementations of the FaultDisputeGame contract and its dependencies
/// assuming that the DisputeGameFactory contract does not need to be modified. Assumes
/// that the System Owner will update the DisputeGameFactory to point to these new
/// contracts at a later point in time.
contract FPACOPS2 is Deploy, StdAssertions {
////////////////////////////////////////////////////////////////
// ENTRYPOINTS //
////////////////////////////////////////////////////////////////
/// @notice Deploys an updated FP system with new FaultDisputeGame contracts and new
/// DelayedWETH contracts. Deploys a new implementation of the
/// AnchorStateRegistry. Does not deploy a new DisputeGameFactory. System
/// Owner is responsible for updating implementations later.
/// @param _proxyAdmin Address of the ProxyAdmin contract to transfer ownership to.
/// @param _systemOwnerSafe Address of the SystemOwner.
/// @param _superchainConfigProxy Address of the SuperchainConfig proxy contract.
/// @param _disputeGameFactoryProxy Address of the DisputeGameFactory proxy contract.
/// @param _anchorStateRegistryProxy Address of the AnchorStateRegistry proxy contract.
function deployFPAC2(
address _proxyAdmin,
address _systemOwnerSafe,
address _superchainConfigProxy,
address _disputeGameFactoryProxy,
address _anchorStateRegistryProxy
)
public
{
console.log("Deploying updated FP contracts.");
// Prank required deployments.
prankDeployment("ProxyAdmin", msg.sender);
prankDeployment("SystemOwnerSafe", msg.sender);
prankDeployment("SuperchainConfigProxy", _superchainConfigProxy);
prankDeployment("DisputeGameFactoryProxy", _disputeGameFactoryProxy);
prankDeployment("AnchorStateRegistryProxy", _anchorStateRegistryProxy);
// Deploy the proxies.
deployERC1967Proxy("DelayedWETHProxy");
deployERC1967Proxy("PermissionedDelayedWETHProxy");
// Deploy implementations.
deployDelayedWETH();
deployAnchorStateRegistry();
deployPreimageOracle();
deployMips();
// Initialize the proxies.
initializeDelayedWETHProxy();
initializePermissionedDelayedWETHProxy();
// Deploy the new game implementations.
deployCannonDisputeGame();
deployPermissionedDisputeGame();
// Transfer ownership of DelayedWETH to ProxyAdmin.
transferWethOwnershipFinal({ _proxyAdmin: _proxyAdmin, _systemOwnerSafe: _systemOwnerSafe });
transferPermissionedWETHOwnershipFinal({ _proxyAdmin: _proxyAdmin, _systemOwnerSafe: _systemOwnerSafe });
// Run post-deployment assertions.
postDeployAssertions({ _proxyAdmin: _proxyAdmin, _systemOwnerSafe: _systemOwnerSafe });
// Print overview.
printConfigReview();
}
////////////////////////////////////////////////////////////////
// HELPERS //
////////////////////////////////////////////////////////////////
/// @notice Deploys the standard Cannon version of the FaultDisputeGame.
function deployCannonDisputeGame() internal broadcast {
console.log("Deploying CannonFaultDisputeGame implementation");
save(
"CannonFaultDisputeGame",
address(
new FaultDisputeGame({
_gameType: GameTypes.CANNON,
_absolutePrestate: loadMipsAbsolutePrestate(),
_maxGameDepth: cfg.faultGameMaxDepth(),
_splitDepth: cfg.faultGameSplitDepth(),
_clockExtension: Duration.wrap(uint64(cfg.faultGameClockExtension())),
_maxClockDuration: Duration.wrap(uint64(cfg.faultGameMaxClockDuration())),
_vm: IBigStepper(mustGetAddress("Mips")),
_weth: DelayedWETH(mustGetAddress("DelayedWETHProxy")),
_anchorStateRegistry: AnchorStateRegistry(mustGetAddress("AnchorStateRegistryProxy")),
_l2ChainId: cfg.l2ChainID()
})
)
);
}
/// @notice Deploys the PermissionedDisputeGame.
function deployPermissionedDisputeGame() internal broadcast {
console.log("Deploying PermissionedDisputeGame implementation");
save(
"PermissionedDisputeGame",
address(
new PermissionedDisputeGame({
_gameType: GameTypes.PERMISSIONED_CANNON,
_absolutePrestate: loadMipsAbsolutePrestate(),
_maxGameDepth: cfg.faultGameMaxDepth(),
_splitDepth: cfg.faultGameSplitDepth(),
_clockExtension: Duration.wrap(uint64(cfg.faultGameClockExtension())),
_maxClockDuration: Duration.wrap(uint64(cfg.faultGameMaxClockDuration())),
_vm: IBigStepper(mustGetAddress("Mips")),
_weth: DelayedWETH(mustGetAddress("PermissionedDelayedWETHProxy")),
_anchorStateRegistry: AnchorStateRegistry(mustGetAddress("AnchorStateRegistryProxy")),
_l2ChainId: cfg.l2ChainID(),
_proposer: cfg.l2OutputOracleProposer(),
_challenger: cfg.l2OutputOracleChallenger()
})
)
);
}
/// @notice Initializes the DelayedWETH proxy.
function initializeDelayedWETHProxy() internal broadcast {
console.log("Initializing DelayedWETHProxy with DelayedWETH.");
address wethProxy = mustGetAddress("DelayedWETHProxy");
address superchainConfigProxy = mustGetAddress("SuperchainConfigProxy");
Proxy(payable(wethProxy)).upgradeToAndCall(
mustGetAddress("DelayedWETH"),
abi.encodeCall(DelayedWETH.initialize, (msg.sender, SuperchainConfig(superchainConfigProxy)))
);
}
/// @notice Initializes the permissioned DelayedWETH proxy.
function initializePermissionedDelayedWETHProxy() internal broadcast {
console.log("Initializing permissioned DelayedWETHProxy with DelayedWETH.");
address wethProxy = mustGetAddress("PermissionedDelayedWETHProxy");
address superchainConfigProxy = mustGetAddress("SuperchainConfigProxy");
Proxy(payable(wethProxy)).upgradeToAndCall(
mustGetAddress("DelayedWETH"),
abi.encodeCall(DelayedWETH.initialize, (msg.sender, SuperchainConfig(superchainConfigProxy)))
);
}
/// @notice Transfers admin rights of the `DelayedWETHProxy` to the `ProxyAdmin` and sets the
/// `DelayedWETH` owner to the `SystemOwnerSafe`.
function transferWethOwnershipFinal(address _proxyAdmin, address _systemOwnerSafe) internal broadcast {
console.log("Transferring ownership of DelayedWETHProxy");
DelayedWETH weth = DelayedWETH(mustGetAddress("DelayedWETHProxy"));
// Transfer the ownership of the DelayedWETH to the SystemOwnerSafe.
weth.transferOwnership(_systemOwnerSafe);
// Transfer the admin rights of the DelayedWETHProxy to the ProxyAdmin.
Proxy prox = Proxy(payable(address(weth)));
prox.changeAdmin(_proxyAdmin);
}
/// @notice Transfers admin rights of the permissioned `DelayedWETHProxy` to the `ProxyAdmin`
/// and sets the `DelayedWETH` owner to the `SystemOwnerSafe`.
function transferPermissionedWETHOwnershipFinal(address _proxyAdmin, address _systemOwnerSafe) internal broadcast {
console.log("Transferring ownership of permissioned DelayedWETHProxy");
DelayedWETH weth = DelayedWETH(mustGetAddress("PermissionedDelayedWETHProxy"));
// Transfer the ownership of the DelayedWETH to the SystemOwnerSafe.
weth.transferOwnership(_systemOwnerSafe);
// Transfer the admin rights of the DelayedWETHProxy to the ProxyAdmin.
Proxy prox = Proxy(payable(address(weth)));
prox.changeAdmin(_proxyAdmin);
}
/// @notice Checks that the deployed system is configured correctly.
function postDeployAssertions(address _proxyAdmin, address _systemOwnerSafe) internal view {
Types.ContractSet memory contracts = _proxiesUnstrict();
// Ensure that `useFaultProofs` is set to `true`.
assertTrue(cfg.useFaultProofs());
// Verify that the DGF is owned by the ProxyAdmin.
address dgfProxyAddr = mustGetAddress("DisputeGameFactoryProxy");
assertEq(address(uint160(uint256(vm.load(dgfProxyAddr, Constants.PROXY_OWNER_ADDRESS)))), _proxyAdmin);
// Verify that DelayedWETH is owned by the ProxyAdmin.
address wethProxyAddr = mustGetAddress("DelayedWETHProxy");
assertEq(address(uint160(uint256(vm.load(wethProxyAddr, Constants.PROXY_OWNER_ADDRESS)))), _proxyAdmin);
// Verify that permissioned DelayedWETH is owned by the ProxyAdmin.
address soyWethProxyAddr = mustGetAddress("PermissionedDelayedWETHProxy");
assertEq(address(uint160(uint256(vm.load(soyWethProxyAddr, Constants.PROXY_OWNER_ADDRESS)))), _proxyAdmin);
// Run standard assertions for DGF and DelayedWETH.
ChainAssertions.checkDisputeGameFactory(contracts, _systemOwnerSafe);
ChainAssertions.checkDelayedWETH(contracts, cfg, true, _systemOwnerSafe);
ChainAssertions.checkPermissionedDelayedWETH(contracts, cfg, true, _systemOwnerSafe);
// Verify PreimageOracle configuration.
PreimageOracle oracle = PreimageOracle(mustGetAddress("PreimageOracle"));
assertEq(oracle.minProposalSize(), cfg.preimageOracleMinProposalSize());
assertEq(oracle.challengePeriod(), cfg.preimageOracleChallengePeriod());
// Verify MIPS configuration.
MIPS mips = MIPS(mustGetAddress("Mips"));
assertEq(address(mips.oracle()), address(oracle));
// Grab ASR
AnchorStateRegistry asr = AnchorStateRegistry(mustGetAddress("AnchorStateRegistryProxy"));
// Verify FaultDisputeGame configuration.
address gameAddr = mustGetAddress("CannonFaultDisputeGame");
FaultDisputeGame gameImpl = FaultDisputeGame(payable(gameAddr));
assertEq(gameImpl.maxGameDepth(), cfg.faultGameMaxDepth());
assertEq(gameImpl.splitDepth(), cfg.faultGameSplitDepth());
assertEq(gameImpl.clockExtension().raw(), cfg.faultGameClockExtension());
assertEq(gameImpl.maxClockDuration().raw(), cfg.faultGameMaxClockDuration());
assertEq(gameImpl.absolutePrestate().raw(), bytes32(cfg.faultGameAbsolutePrestate()));
assertEq(address(gameImpl.weth()), wethProxyAddr);
assertEq(address(gameImpl.anchorStateRegistry()), address(asr));
assertEq(address(gameImpl.vm()), address(mips));
// Verify security override yoke configuration.
address soyGameAddr = mustGetAddress("PermissionedDisputeGame");
PermissionedDisputeGame soyGameImpl = PermissionedDisputeGame(payable(soyGameAddr));
assertEq(soyGameImpl.proposer(), cfg.l2OutputOracleProposer());
assertEq(soyGameImpl.challenger(), cfg.l2OutputOracleChallenger());
assertEq(soyGameImpl.maxGameDepth(), cfg.faultGameMaxDepth());
assertEq(soyGameImpl.splitDepth(), cfg.faultGameSplitDepth());
assertEq(soyGameImpl.clockExtension().raw(), cfg.faultGameClockExtension());
assertEq(soyGameImpl.maxClockDuration().raw(), cfg.faultGameMaxClockDuration());
assertEq(soyGameImpl.absolutePrestate().raw(), bytes32(cfg.faultGameAbsolutePrestate()));
assertEq(address(soyGameImpl.weth()), soyWethProxyAddr);
assertEq(address(soyGameImpl.anchorStateRegistry()), address(asr));
assertEq(address(soyGameImpl.vm()), address(mips));
}
/// @notice Prints a review of the fault proof configuration section of the deploy config.
function printConfigReview() internal view {
console.log(unicode"📖 FaultDisputeGame Config Overview (chainid: %d)", block.chainid);
console.log(" 0. Use Fault Proofs: %s", cfg.useFaultProofs() ? "true" : "false");
console.log(" 1. Absolute Prestate: %x", cfg.faultGameAbsolutePrestate());
console.log(" 2. Max Depth: %d", cfg.faultGameMaxDepth());
console.log(" 3. Output / Execution split Depth: %d", cfg.faultGameSplitDepth());
console.log(" 4. Clock Extension (seconds): %d", cfg.faultGameClockExtension());
console.log(" 5. Max Clock Duration (seconds): %d", cfg.faultGameMaxClockDuration());
console.log(" 6. L2 Genesis block number: %d", cfg.faultGameGenesisBlock());
console.log(" 7. L2 Genesis output root: %x", uint256(cfg.faultGameGenesisOutputRoot()));
console.log(" 8. Proof Maturity Delay (seconds): ", cfg.proofMaturityDelaySeconds());
console.log(" 9. Dispute Game Finality Delay (seconds): ", cfg.disputeGameFinalityDelaySeconds());
console.log(" 10. Respected Game Type: ", cfg.respectedGameType());
console.log(" 11. Preimage Oracle Min Proposal Size (bytes): ", cfg.preimageOracleMinProposalSize());
console.log(" 12. Preimage Oracle Challenge Period (seconds): ", cfg.preimageOracleChallengePeriod());
}
}
...@@ -23,4 +23,9 @@ cannon-prestate: # Generate the cannon prestate, and tar the `op-program` + `can ...@@ -23,4 +23,9 @@ cannon-prestate: # Generate the cannon prestate, and tar the `op-program` + `can
.PHONY: deploy-fresh .PHONY: deploy-fresh
deploy-fresh: cannon-prestate # Deploy a fresh version of the FPAC contracts. Pass `--broadcast` to send to the network. deploy-fresh: cannon-prestate # Deploy a fresh version of the FPAC contracts. Pass `--broadcast` to send to the network.
forge script FPACOPS.sol --sig "deployFPAC(address,address)" $(proxy-admin) $(system-owner-safe) --chain $(chain) -vvv $(args) forge script FPACOPS.s.sol --sig "deployFPAC(address,address,address)" $(proxy-admin) $(system-owner-safe) $(superchain-config-proxy) --chain $(chain) -vvv $(args)
# TODO: Convert this whole file to a justfile
.PHONY: deploy-upgrade
deploy-upgrade: cannon-prestate # Deploy upgraded FP contracts. Pass `--broadcast` to send to the network.
forge script FPACOPS2.s.sol --sig "deployFPAC2(address,address,address,address,address)" $(proxy-admin) $(system-owner-safe) $(superchain-config-proxy) $(dispute-game-factory-proxy) $(anchor-state-registry-proxy) --chain $(chain) -vvv $(args)
...@@ -9,6 +9,7 @@ library Types { ...@@ -9,6 +9,7 @@ library Types {
address L2OutputOracle; address L2OutputOracle;
address DisputeGameFactory; address DisputeGameFactory;
address DelayedWETH; address DelayedWETH;
address PermissionedDelayedWETH;
address AnchorStateRegistry; address AnchorStateRegistry;
address OptimismMintableERC20Factory; address OptimismMintableERC20Factory;
address OptimismPortal; address OptimismPortal;
......
...@@ -128,8 +128,8 @@ ...@@ -128,8 +128,8 @@
"sourceCodeHash": "0xbe200a6cb297a3ca1a7d174a9c886e3f17eb8edf617ad014a2ac4f6c2e2ac7f1" "sourceCodeHash": "0xbe200a6cb297a3ca1a7d174a9c886e3f17eb8edf617ad014a2ac4f6c2e2ac7f1"
}, },
"src/Safe/DeputyGuardianModule.sol": { "src/Safe/DeputyGuardianModule.sol": {
"initCodeHash": "0x433eb7488e613a51c7ff05a76bbecf47f5beac8b8614f5c50001f99e39ae7ed2", "initCodeHash": "0x5e0e2089fa737bfeeeff66cb84f4c0a90cd42f9ef4d4814b4850094f48c2a391",
"sourceCodeHash": "0x5b415dc432a83fb1d5c41585539245997c62acb6bd77c489bf57e9b59be5f983" "sourceCodeHash": "0x6ad81146f59f35f17e1ea432e902de3ef6fc84a4aedcbe0593f5997af6835018"
}, },
"src/Safe/LivenessGuard.sol": { "src/Safe/LivenessGuard.sol": {
"initCodeHash": "0xf54289de5cef7ba0044e0d63310937fa231d6528aac91e13e531c845af42afac", "initCodeHash": "0xf54289de5cef7ba0044e0d63310937fa231d6528aac91e13e531c845af42afac",
...@@ -140,32 +140,32 @@ ...@@ -140,32 +140,32 @@
"sourceCodeHash": "0x3ff4a3f21202478935412d47fd5ef7f94a170402ddc50e5c062013ce5544c83f" "sourceCodeHash": "0x3ff4a3f21202478935412d47fd5ef7f94a170402ddc50e5c062013ce5544c83f"
}, },
"src/cannon/MIPS.sol": { "src/cannon/MIPS.sol": {
"initCodeHash": "0xfc2381da4ad202f1ff7b97776be0e9a510ef3c36c0f557cbada1a0943f376e62", "initCodeHash": "0x826aa41a3f37c3ef6613be21eb9bb275bdf0a48d2196dffc56f494b68b32a156",
"sourceCodeHash": "0x5f5d13be282305b2947079cbf8f01e750e5c88a2b7a7a7267e79cba3ee4616fa" "sourceCodeHash": "0x10cd9132b6c9f45af6b7f11ccc50bb889c62105bdb556bc7fb4100b6136e37df"
}, },
"src/cannon/MIPS2.sol": { "src/cannon/MIPS2.sol": {
"initCodeHash": "0xd75b7541ca736dff03b1fa7116a9835f97bc82507287bf90f3dd739299f5aa03", "initCodeHash": "0xbb425bd1c3cad13a77f5c9676b577606e2f8f320687739f529b257a042f58d85",
"sourceCodeHash": "0x115bd6a4c4d77ed210dfd468675b409fdae9f79b932063c138f0765ba9063462" "sourceCodeHash": "0xe66f19942947f53ccd658b94c1ef6db39e947419d4ec7436067c6cc44452ff73"
}, },
"src/cannon/PreimageOracle.sol": { "src/cannon/PreimageOracle.sol": {
"initCodeHash": "0xe5db668fe41436f53995e910488c7c140766ba8745e19743773ebab508efd090", "initCodeHash": "0x967d730bb1b10a36ee625179734cccd6b6826ce480bad0419272663c460603bd",
"sourceCodeHash": "0x7c8b26cd263f6be144bace1f3faf0ec9265df0efb68ac34fa1fa7df7f608ab42" "sourceCodeHash": "0xa73ce9635a75395c09e00e383f0c9072308813971c8f0dffe445dd9310f7007a"
}, },
"src/dispute/AnchorStateRegistry.sol": { "src/dispute/AnchorStateRegistry.sol": {
"initCodeHash": "0x0305c21e50829b9e07d43358d8c2c82f1449534c90d4391400d46e76d0503a49", "initCodeHash": "0xe064c2d66f48d71a91db81a9f54ea819bc75f90e98efbd3efca1e460a16d8cc5",
"sourceCodeHash": "0x56b069b33d080c2a45ee6fd340e5c5824ab4dc866eadb5b481b9026ebb12aa7c" "sourceCodeHash": "0xb01e484637783b0e4f1e0f20d073659c21ac8232442cc506886720b3ec736cd9"
}, },
"src/dispute/DisputeGameFactory.sol": { "src/dispute/DisputeGameFactory.sol": {
"initCodeHash": "0x7a7cb8f2c95df2f9afb3ce9eaefe4a6f997ccce7ed8ffda5d425a65a2474a792", "initCodeHash": "0x7a7cb8f2c95df2f9afb3ce9eaefe4a6f997ccce7ed8ffda5d425a65a2474a792",
"sourceCodeHash": "0x918c395ac5d77357f2551616aad0613e68893862edd14e554623eb16ee6ba148" "sourceCodeHash": "0x918c395ac5d77357f2551616aad0613e68893862edd14e554623eb16ee6ba148"
}, },
"src/dispute/FaultDisputeGame.sol": { "src/dispute/FaultDisputeGame.sol": {
"initCodeHash": "0x5ea5b544b8d7b32f55f7864c25a2443a5db363ffd1c66e0799cbc7bccaf98526", "initCodeHash": "0xe0b6dae6078d1e7237240ab3c589055190ff4a4bb16280dcbf3919e062ad8f84",
"sourceCodeHash": "0xa0d373c969b78752aefb66b56807490e16ce0d09c8514b485b3d2df29bf8d514" "sourceCodeHash": "0xea69f63d4df701e9dfb73b85692c9a62b8a5819b3f473889c9da3734131b9738"
}, },
"src/dispute/weth/DelayedWETH.sol": { "src/dispute/weth/DelayedWETH.sol": {
"initCodeHash": "0xb9bbe005874922cd8f499e7a0a092967cfca03e012c1e41912b0c77481c71777", "initCodeHash": "0x305074302172657578c3fff485fb2115d608834a3c1447d9a957e4baf55a982c",
"sourceCodeHash": "0x87d00995773d34cc28e81559f4cc5f25890d924df285ec6e9e01b5277f52a9dc" "sourceCodeHash": "0xb3a260564b82992fac71ad21be01ebe7eaae6855f66cafbd4a0b21f53853e17e"
}, },
"src/legacy/DeployerWhitelist.sol": { "src/legacy/DeployerWhitelist.sol": {
"initCodeHash": "0x8de80fb23b26dd9d849f6328e56ea7c173cd9e9ce1f05c9beea559d1720deb3d", "initCodeHash": "0x8de80fb23b26dd9d849f6328e56ea7c173cd9e9ce1f05c9beea559d1720deb3d",
......
...@@ -77,6 +77,11 @@ ...@@ -77,6 +77,11 @@
"internalType": "struct AnchorStateRegistry.StartingAnchorRoot[]", "internalType": "struct AnchorStateRegistry.StartingAnchorRoot[]",
"name": "_startingAnchorRoots", "name": "_startingAnchorRoots",
"type": "tuple[]" "type": "tuple[]"
},
{
"internalType": "contract SuperchainConfig",
"name": "_superchainConfig",
"type": "address"
} }
], ],
"name": "initialize", "name": "initialize",
...@@ -84,6 +89,32 @@ ...@@ -84,6 +89,32 @@
"stateMutability": "nonpayable", "stateMutability": "nonpayable",
"type": "function" "type": "function"
}, },
{
"inputs": [
{
"internalType": "contract IFaultDisputeGame",
"name": "_game",
"type": "address"
}
],
"name": "setAnchorState",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "superchainConfig",
"outputs": [
{
"internalType": "contract SuperchainConfig",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "tryUpdateAnchorState", "name": "tryUpdateAnchorState",
...@@ -116,5 +147,20 @@ ...@@ -116,5 +147,20 @@
], ],
"name": "Initialized", "name": "Initialized",
"type": "event" "type": "event"
},
{
"inputs": [],
"name": "InvalidGameStatus",
"type": "error"
},
{
"inputs": [],
"name": "Unauthorized",
"type": "error"
},
{
"inputs": [],
"name": "UnregisteredGame",
"type": "error"
} }
] ]
\ No newline at end of file
...@@ -71,6 +71,24 @@ ...@@ -71,6 +71,24 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [
{
"internalType": "contract AnchorStateRegistry",
"name": "_registry",
"type": "address"
},
{
"internalType": "contract IFaultDisputeGame",
"name": "_game",
"type": "address"
}
],
"name": "setAnchorState",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{ {
"inputs": [ "inputs": [
{ {
......
...@@ -963,6 +963,11 @@ ...@@ -963,6 +963,11 @@
"name": "IncorrectBondAmount", "name": "IncorrectBondAmount",
"type": "error" "type": "error"
}, },
{
"inputs": [],
"name": "InvalidChallengePeriod",
"type": "error"
},
{ {
"inputs": [], "inputs": [],
"name": "InvalidClockExtension", "name": "InvalidClockExtension",
......
...@@ -1004,6 +1004,11 @@ ...@@ -1004,6 +1004,11 @@
"name": "IncorrectBondAmount", "name": "IncorrectBondAmount",
"type": "error" "type": "error"
}, },
{
"inputs": [],
"name": "InvalidChallengePeriod",
"type": "error"
},
{ {
"inputs": [], "inputs": [],
"name": "InvalidClockExtension", "name": "InvalidClockExtension",
......
...@@ -54,6 +54,19 @@ ...@@ -54,6 +54,19 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "PRECOMPILE_CALL_RESERVED_GAS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [ "inputs": [
{ {
...@@ -378,6 +391,11 @@ ...@@ -378,6 +391,11 @@
"name": "_precompile", "name": "_precompile",
"type": "address" "type": "address"
}, },
{
"internalType": "uint64",
"name": "_requiredGas",
"type": "uint64"
},
{ {
"internalType": "bytes", "internalType": "bytes",
"name": "_input", "name": "_input",
...@@ -833,6 +851,11 @@ ...@@ -833,6 +851,11 @@
"name": "AlreadyFinalized", "name": "AlreadyFinalized",
"type": "error" "type": "error"
}, },
{
"inputs": [],
"name": "AlreadyInitialized",
"type": "error"
},
{ {
"inputs": [], "inputs": [],
"name": "BadProposal", "name": "BadProposal",
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -19,5 +19,12 @@ ...@@ -19,5 +19,12 @@
"offset": 0, "offset": 0,
"slot": "1", "slot": "1",
"type": "mapping(GameType => struct OutputRoot)" "type": "mapping(GameType => struct OutputRoot)"
},
{
"bytes": "20",
"label": "superchainConfig",
"offset": 0,
"slot": "2",
"type": "contract SuperchainConfig"
} }
] ]
\ No newline at end of file
...@@ -4,11 +4,13 @@ pragma solidity 0.8.15; ...@@ -4,11 +4,13 @@ pragma solidity 0.8.15;
import { GnosisSafe as Safe } from "safe-contracts/GnosisSafe.sol"; import { GnosisSafe as Safe } from "safe-contracts/GnosisSafe.sol";
import { Enum } from "safe-contracts/common/Enum.sol"; import { Enum } from "safe-contracts/common/Enum.sol";
import { IFaultDisputeGame } from "src/dispute/interfaces/IFaultDisputeGame.sol";
import { SuperchainConfig } from "src/L1/SuperchainConfig.sol"; import { SuperchainConfig } from "src/L1/SuperchainConfig.sol";
import { OptimismPortal2 } from "src/L1/OptimismPortal2.sol"; import { OptimismPortal2 } from "src/L1/OptimismPortal2.sol";
import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol"; import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol";
import { ISemver } from "src/universal/ISemver.sol"; import { ISemver } from "src/universal/ISemver.sol";
import { Unauthorized } from "src/libraries/PortalErrors.sol"; import { Unauthorized } from "src/libraries/PortalErrors.sol";
import { AnchorStateRegistry } from "src/dispute/AnchorStateRegistry.sol";
import "src/dispute/lib/Types.sol"; import "src/dispute/lib/Types.sol";
...@@ -43,8 +45,8 @@ contract DeputyGuardianModule is ISemver { ...@@ -43,8 +45,8 @@ contract DeputyGuardianModule is ISemver {
address internal immutable DEPUTY_GUARDIAN; address internal immutable DEPUTY_GUARDIAN;
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 1.1.0 /// @custom:semver 2.0.0
string public constant version = "1.1.0"; string public constant version = "2.0.0";
// Constructor to initialize the Safe and baseModule instances // Constructor to initialize the Safe and baseModule instances
constructor(Safe _safe, SuperchainConfig _superchainConfig, address _deputyGuardian) { constructor(Safe _safe, SuperchainConfig _superchainConfig, address _deputyGuardian) {
...@@ -108,6 +110,22 @@ contract DeputyGuardianModule is ISemver { ...@@ -108,6 +110,22 @@ contract DeputyGuardianModule is ISemver {
emit Unpaused(); emit Unpaused();
} }
/// @notice Calls the Security Council Safe's `execTransactionFromModuleReturnData()`, with the arguments
/// necessary to call `setAnchorState()` on the `AnchorStateRegistry` contract.
/// Only the deputy guardian can call this function.
/// @param _registry The `AnchorStateRegistry` contract instance.
/// @param _game The `IFaultDisputeGame` contract instance.
function setAnchorState(AnchorStateRegistry _registry, IFaultDisputeGame _game) external {
_onlyDeputyGuardian();
bytes memory data = abi.encodeCall(AnchorStateRegistry.setAnchorState, (_game));
(bool success, bytes memory returnData) =
SAFE.execTransactionFromModuleReturnData(address(_registry), 0, data, Enum.Operation.Call);
if (!success) {
revert ExecutionFailed(string(returnData));
}
}
/// @notice Calls the Security Council Safe's `execTransactionFromModuleReturnData()`, with the arguments /// @notice Calls the Security Council Safe's `execTransactionFromModuleReturnData()`, with the arguments
/// necessary to call `blacklistDisputeGame()` on the `OptimismPortal2` contract. /// necessary to call `blacklistDisputeGame()` on the `OptimismPortal2` contract.
/// Only the deputy guardian can call this function. /// Only the deputy guardian can call this function.
......
...@@ -44,8 +44,8 @@ contract MIPS is ISemver { ...@@ -44,8 +44,8 @@ contract MIPS is ISemver {
} }
/// @notice The semantic version of the MIPS contract. /// @notice The semantic version of the MIPS contract.
/// @custom:semver 1.0.1 /// @custom:semver 1.1.0
string public constant version = "1.1.0-beta.6"; string public constant version = "1.1.0";
/// @notice The preimage oracle contract. /// @notice The preimage oracle contract.
IPreimageOracle internal immutable ORACLE; IPreimageOracle internal immutable ORACLE;
...@@ -98,6 +98,14 @@ contract MIPS is ISemver { ...@@ -98,6 +98,14 @@ contract MIPS is ISemver {
from, to := copyMem(from, to, 8) // step from, to := copyMem(from, to, 8) // step
from := add(from, 32) // offset to registers from := add(from, 32) // offset to registers
// Verify that the value of exited is valid (0 or 1)
if gt(exited, 1) {
// revert InvalidExitedValue();
let ptr := mload(0x40)
mstore(ptr, shl(224, 0x0136cc76))
revert(ptr, 0x04)
}
// Copy registers // Copy registers
for { let i := 0 } lt(i, 32) { i := add(i, 1) } { from, to := copyMem(from, to, 4) } for { let i := 0 } lt(i, 32) { i := add(i, 1) } { from, to := copyMem(from, to, 4) }
...@@ -149,7 +157,7 @@ contract MIPS is ISemver { ...@@ -149,7 +157,7 @@ contract MIPS is ISemver {
(v0, v1, state.heap) = sys.handleSysMmap(a0, a1, state.heap); (v0, v1, state.heap) = sys.handleSysMmap(a0, a1, state.heap);
} else if (syscall_no == sys.SYS_BRK) { } else if (syscall_no == sys.SYS_BRK) {
// brk: Returns a fixed address for the program break at 0x40000000 // brk: Returns a fixed address for the program break at 0x40000000
v0 = sys.BRK_START; v0 = sys.PROGRAM_BREAK;
} else if (syscall_no == sys.SYS_CLONE) { } else if (syscall_no == sys.SYS_CLONE) {
// clone (not supported) returns 1 // clone (not supported) returns 1
v0 = 1; v0 = 1;
...@@ -243,10 +251,24 @@ contract MIPS is ISemver { ...@@ -243,10 +251,24 @@ contract MIPS is ISemver {
c, m := putField(c, m, 4) // heap c, m := putField(c, m, 4) // heap
c, m := putField(c, m, 1) // exitCode c, m := putField(c, m, 1) // exitCode
c, m := putField(c, m, 1) // exited c, m := putField(c, m, 1) // exited
let exited := mload(sub(m, 32))
c, m := putField(c, m, 8) // step c, m := putField(c, m, 8) // step
// Verify that the value of exited is valid (0 or 1)
if gt(exited, 1) {
// revert InvalidExitedValue();
let ptr := mload(0x40)
mstore(ptr, shl(224, 0x0136cc76))
revert(ptr, 0x04)
}
// Compiler should have done this already
if iszero(eq(mload(m), add(m, 32))) {
// expected registers offset check
revert(0, 0)
}
// Unpack register calldata into memory // Unpack register calldata into memory
mstore(m, add(m, 32)) // offset to registers
m := add(m, 32) m := add(m, 32)
for { let i := 0 } lt(i, 32) { i := add(i, 1) } { c, m := putField(c, m, 4) } for { let i := 0 } lt(i, 32) { i := add(i, 1) } { c, m := putField(c, m, 4) }
} }
......
...@@ -51,8 +51,8 @@ contract MIPS2 is ISemver { ...@@ -51,8 +51,8 @@ contract MIPS2 is ISemver {
} }
/// @notice The semantic version of the MIPS2 contract. /// @notice The semantic version of the MIPS2 contract.
/// @custom:semver 0.0.2-beta /// @custom:semver 1.0.0-beta.4
string public constant version = "1.0.0-beta.3"; string public constant version = "1.0.0-beta.4";
/// @notice The preimage oracle contract. /// @notice The preimage oracle contract.
IPreimageOracle internal immutable ORACLE; IPreimageOracle internal immutable ORACLE;
...@@ -256,7 +256,7 @@ contract MIPS2 is ISemver { ...@@ -256,7 +256,7 @@ contract MIPS2 is ISemver {
(v0, v1, state.heap) = sys.handleSysMmap(a0, a1, state.heap); (v0, v1, state.heap) = sys.handleSysMmap(a0, a1, state.heap);
} else if (syscall_no == sys.SYS_BRK) { } else if (syscall_no == sys.SYS_BRK) {
// brk: Returns a fixed address for the program break at 0x40000000 // brk: Returns a fixed address for the program break at 0x40000000
v0 = sys.BRK_START; v0 = sys.PROGRAM_BREAK;
} else if (syscall_no == sys.SYS_CLONE) { } else if (syscall_no == sys.SYS_CLONE) {
if (sys.VALID_SYS_CLONE_FLAGS != a0) { if (sys.VALID_SYS_CLONE_FLAGS != a0) {
state.exited = true; state.exited = true;
......
...@@ -27,10 +27,12 @@ contract PreimageOracle is IPreimageOracle, ISemver { ...@@ -27,10 +27,12 @@ contract PreimageOracle is IPreimageOracle, ISemver {
uint256 public constant KECCAK_TREE_DEPTH = 16; uint256 public constant KECCAK_TREE_DEPTH = 16;
/// @notice The maximum number of keccak blocks that can fit into the merkle tree. /// @notice The maximum number of keccak blocks that can fit into the merkle tree.
uint256 public constant MAX_LEAF_COUNT = 2 ** KECCAK_TREE_DEPTH - 1; uint256 public constant MAX_LEAF_COUNT = 2 ** KECCAK_TREE_DEPTH - 1;
/// @notice The reserved gas for precompile call setup.
uint256 public constant PRECOMPILE_CALL_RESERVED_GAS = 100_000;
/// @notice The semantic version of the Preimage Oracle contract. /// @notice The semantic version of the Preimage Oracle contract.
/// @custom:semver 1.0.0 /// @custom:semver 1.1.2
string public constant version = "1.0.0"; string public constant version = "1.1.2";
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
// Authorized Preimage Parts // // Authorized Preimage Parts //
...@@ -90,6 +92,11 @@ contract PreimageOracle is IPreimageOracle, ISemver { ...@@ -90,6 +92,11 @@ contract PreimageOracle is IPreimageOracle, ISemver {
MIN_LPP_SIZE_BYTES = _minProposalSize; MIN_LPP_SIZE_BYTES = _minProposalSize;
CHALLENGE_PERIOD = _challengePeriod; CHALLENGE_PERIOD = _challengePeriod;
// Make sure challenge period fits within uint64 so that it can safely be used within the
// FaultDisputeGame contract to compute clock extensions. Adding this check is simpler than
// changing the existing contract ABI.
require(_challengePeriod <= type(uint64).max, "challenge period too large");
// Compute hashes in empty sparse Merkle tree. The first hash is not set, and kept as zero as the identity. // Compute hashes in empty sparse Merkle tree. The first hash is not set, and kept as zero as the identity.
for (uint256 height = 0; height < KECCAK_TREE_DEPTH - 1; height++) { for (uint256 height = 0; height < KECCAK_TREE_DEPTH - 1; height++) {
zeroHashes[height + 1] = keccak256(abi.encodePacked(zeroHashes[height], zeroHashes[height])); zeroHashes[height + 1] = keccak256(abi.encodePacked(zeroHashes[height], zeroHashes[height]));
...@@ -131,7 +138,7 @@ contract PreimageOracle is IPreimageOracle, ISemver { ...@@ -131,7 +138,7 @@ contract PreimageOracle is IPreimageOracle, ISemver {
key_ = PreimageKeyLib.localizeIdent(_ident, _localContext); 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) {
revert PartOffsetOOB(); revert PartOffsetOOB();
} }
...@@ -332,7 +339,14 @@ contract PreimageOracle is IPreimageOracle, ISemver { ...@@ -332,7 +339,14 @@ contract PreimageOracle is IPreimageOracle, ISemver {
} }
/// @inheritdoc IPreimageOracle /// @inheritdoc IPreimageOracle
function loadPrecompilePreimagePart(uint256 _partOffset, address _precompile, bytes calldata _input) external { function loadPrecompilePreimagePart(
uint256 _partOffset,
address _precompile,
uint64 _requiredGas,
bytes calldata _input
)
external
{
bytes32 res; bytes32 res;
bytes32 key; bytes32 key;
bytes32 part; bytes32 part;
...@@ -341,21 +355,32 @@ contract PreimageOracle is IPreimageOracle, ISemver { ...@@ -341,21 +355,32 @@ contract PreimageOracle is IPreimageOracle, ISemver {
// we leave solidity slots 0x40 and 0x60 untouched, and everything after as scratch-memory. // we leave solidity slots 0x40 and 0x60 untouched, and everything after as scratch-memory.
let ptr := 0x80 let ptr := 0x80
// copy precompile address and input into memory // copy precompile address, requiredGas, and input into memory to compute the key
// len(sig) + len(_partOffset) + address-offset-in-slot mstore(ptr, shl(96, _precompile))
calldatacopy(ptr, 48, 20) mstore(add(ptr, 20), shl(192, _requiredGas))
calldatacopy(add(20, ptr), _input.offset, _input.length) calldatacopy(add(28, ptr), _input.offset, _input.length)
// compute the hash // compute the hash
let h := keccak256(ptr, add(20, _input.length)) let h := keccak256(ptr, add(28, _input.length))
// mask out prefix byte, replace with type 6 byte // mask out prefix byte, replace with type 6 byte
key := or(and(h, not(shl(248, 0xFF))), shl(248, 0x06)) key := or(and(h, not(shl(248, 0xFF))), shl(248, 0x06))
// Check if the precompile call has at least the required gas.
// This assumes there are no further memory expansion costs until after the staticall on the precompile
// Also assumes that the gas expended in setting up the staticcall is less than PRECOMPILE_CALL_RESERVED_GAS
// require(gas() >= (requiredGas * 64 / 63) + reservedGas)
if lt(mul(gas(), 63), add(mul(_requiredGas, 64), mul(PRECOMPILE_CALL_RESERVED_GAS, 63))) {
// Store "NotEnoughGas()"
mstore(0, 0xdd629f86)
revert(0x1c, 4)
}
// Call the precompile to get the result. // Call the precompile to get the result.
// SAFETY: Given the above gas check, the staticall cannot fail due to insufficient gas.
res := res :=
staticcall( staticcall(
gas(), // forward all gas gas(), // forward all gas
_precompile, _precompile,
add(20, ptr), // input ptr add(28, ptr), // input ptr
_input.length, _input.length,
0x0, // Unused as we don't copy anything 0x0, // Unused as we don't copy anything
0x00 // don't copy anything 0x00 // don't copy anything
...@@ -429,6 +454,10 @@ contract PreimageOracle is IPreimageOracle, ISemver { ...@@ -429,6 +454,10 @@ contract PreimageOracle is IPreimageOracle, ISemver {
// Initialize the proposal metadata. // Initialize the proposal metadata.
LPPMetaData metaData = proposalMetadata[msg.sender][_uuid]; LPPMetaData metaData = proposalMetadata[msg.sender][_uuid];
// Revert if the proposal has already been initialized. 0-size preimages are *not* allowed.
if (metaData.claimedSize() != 0) revert AlreadyInitialized();
proposalMetadata[msg.sender][_uuid] = metaData.setPartOffset(_partOffset).setClaimedSize(_claimedSize); proposalMetadata[msg.sender][_uuid] = metaData.setPartOffset(_partOffset).setClaimedSize(_claimedSize);
proposals.push(LargePreimageProposalKeys(msg.sender, _uuid)); proposals.push(LargePreimageProposalKeys(msg.sender, _uuid));
...@@ -484,7 +513,7 @@ contract PreimageOracle is IPreimageOracle, ISemver { ...@@ -484,7 +513,7 @@ contract PreimageOracle is IPreimageOracle, ISemver {
let inputPtr := add(input, 0x20) let inputPtr := add(input, 0x20)
// The input length must be a multiple of 136 bytes // The input length must be a multiple of 136 bytes
// The input lenth / 136 must be equal to the number of state commitments. // The input length / 136 must be equal to the number of state commitments.
if or(mod(inputLen, 136), iszero(eq(_stateCommitments.length, div(inputLen, 136)))) { if or(mod(inputLen, 136), iszero(eq(_stateCommitments.length, div(inputLen, 136)))) {
// Store "InvalidInputSize()" error selector // Store "InvalidInputSize()" error selector
mstore(0x00, 0x7b1daf1) mstore(0x00, 0x7b1daf1)
...@@ -653,6 +682,9 @@ contract PreimageOracle is IPreimageOracle, ISemver { ...@@ -653,6 +682,9 @@ contract PreimageOracle is IPreimageOracle, ISemver {
// Check if the proposal was countered. // Check if the proposal was countered.
if (metaData.countered()) revert BadProposal(); if (metaData.countered()) revert BadProposal();
// Check if the proposal has been finalized at all.
if (metaData.timestamp() == 0) revert ActiveProposal();
// Check if the challenge period has passed since the proposal was finalized. // Check if the challenge period has passed since the proposal was finalized.
if (block.timestamp - metaData.timestamp() <= CHALLENGE_PERIOD) revert ActiveProposal(); if (block.timestamp - metaData.timestamp() <= CHALLENGE_PERIOD) revert ActiveProposal();
......
...@@ -4,6 +4,10 @@ pragma solidity 0.8.15; ...@@ -4,6 +4,10 @@ pragma solidity 0.8.15;
/// @title IPreimageOracle /// @title IPreimageOracle
/// @notice Interface for a preimage oracle. /// @notice Interface for a preimage oracle.
interface IPreimageOracle { interface IPreimageOracle {
/// @notice Returns the length of the large preimage proposal challenge period.
/// @return challengePeriod_ The length of the challenge period in seconds.
function challengePeriod() external view returns (uint256 challengePeriod_);
/// @notice Reads a preimage from the oracle. /// @notice Reads a preimage from the oracle.
/// @param _key The key of the preimage to read. /// @param _key The key of the preimage to read.
/// @param _offset The offset of the preimage to read. /// @param _offset The offset of the preimage to read.
...@@ -75,6 +79,13 @@ interface IPreimageOracle { ...@@ -75,6 +79,13 @@ interface IPreimageOracle {
/// The preimage key is `6 ++ keccak256(precompile ++ input)[1:]`. /// The preimage key is `6 ++ keccak256(precompile ++ input)[1:]`.
/// @param _partOffset The offset of the precompile result being loaded. /// @param _partOffset The offset of the precompile result being loaded.
/// @param _precompile The precompile address /// @param _precompile The precompile address
/// @param _requiredGas The gas required to fully execute an L1 precompile.
/// @param _input The input to the precompile call. /// @param _input The input to the precompile call.
function loadPrecompilePreimagePart(uint256 _partOffset, address _precompile, bytes calldata _input) external; function loadPrecompilePreimagePart(
uint256 _partOffset,
address _precompile,
uint64 _requiredGas,
bytes calldata _input
)
external;
} }
...@@ -4,6 +4,9 @@ pragma solidity 0.8.15; ...@@ -4,6 +4,9 @@ pragma solidity 0.8.15;
/// @notice Thrown when a passed part offset is out of bounds. /// @notice Thrown when a passed part offset is out of bounds.
error PartOffsetOOB(); error PartOffsetOOB();
/// @notice Thrown when insufficient gas is provided when loading precompile preimages.
error NotEnoughGas();
/// @notice Thrown when a merkle proof fails to verify. /// @notice Thrown when a merkle proof fails to verify.
error InvalidProof(); error InvalidProof();
...@@ -37,6 +40,9 @@ error BadProposal(); ...@@ -37,6 +40,9 @@ error BadProposal();
/// @notice Thrown when attempting to add leaves to a preimage proposal that has not been initialized. /// @notice Thrown when attempting to add leaves to a preimage proposal that has not been initialized.
error NotInitialized(); error NotInitialized();
/// @notice Thrown when attempting to re-initialize an existing large preimage proposal.
error AlreadyInitialized();
/// @notice Thrown when the caller of a function is not an EOA. /// @notice Thrown when the caller of a function is not an EOA.
error NotEOA(); error NotEOA();
...@@ -45,3 +51,6 @@ error InsufficientBond(); ...@@ -45,3 +51,6 @@ error InsufficientBond();
/// @notice Thrown when a bond transfer fails. /// @notice Thrown when a bond transfer fails.
error BondTransferFailed(); error BondTransferFailed();
/// @notice Thrown when the value of the exited boolean is not 0 or 1.
error InvalidExitedValue();
...@@ -227,7 +227,10 @@ library MIPSInstructions { ...@@ -227,7 +227,10 @@ library MIPSInstructions {
} }
// srav // srav
else if (_fun == 0x07) { else if (_fun == 0x07) {
return signExtend(_rt >> _rs, 32 - _rs); // shamt here is different than the typical shamt which comes from the
// instruction itself, here it comes from the rs register
uint32 shamt = _rs & 0x1F;
return signExtend(_rt >> shamt, 32 - shamt);
} }
// functs in range [0x8, 0x1b] are handled specially by other functions // functs in range [0x8, 0x1b] are handled specially by other functions
// Explicitly enumerate each funct in range to reduce code diff against Go Vm // Explicitly enumerate each funct in range to reduce code diff against Go Vm
......
...@@ -99,7 +99,8 @@ library MIPSSyscalls { ...@@ -99,7 +99,8 @@ library MIPSSyscalls {
uint32 internal constant SCHED_QUANTUM = 100_000; uint32 internal constant SCHED_QUANTUM = 100_000;
/// @notice Start of the data segment. /// @notice Start of the data segment.
uint32 internal constant BRK_START = 0x40000000; uint32 internal constant PROGRAM_BREAK = 0x40000000;
uint32 internal constant HEAP_END = 0x60000000;
// SYS_CLONE flags // SYS_CLONE flags
uint32 internal constant CLONE_VM = 0x100; uint32 internal constant CLONE_VM = 0x100;
...@@ -175,6 +176,12 @@ library MIPSSyscalls { ...@@ -175,6 +176,12 @@ library MIPSSyscalls {
if (_a0 == 0) { if (_a0 == 0) {
v0_ = _heap; v0_ = _heap;
newHeap_ += sz; newHeap_ += sz;
// Fail if new heap exceeds memory limit, newHeap overflows to low memory, or sz overflows
if (newHeap_ > HEAP_END || newHeap_ < _heap || sz < _a1) {
v0_ = SYS_ERROR_SIGNAL;
v1_ = EINVAL;
return (v0_, v1_, _heap);
}
} else { } else {
v0_ = _a0; v0_ = _a0;
} }
......
...@@ -8,8 +8,11 @@ import { IAnchorStateRegistry } from "src/dispute/interfaces/IAnchorStateRegistr ...@@ -8,8 +8,11 @@ import { IAnchorStateRegistry } from "src/dispute/interfaces/IAnchorStateRegistr
import { IFaultDisputeGame } from "src/dispute/interfaces/IFaultDisputeGame.sol"; import { IFaultDisputeGame } from "src/dispute/interfaces/IFaultDisputeGame.sol";
import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol"; import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol";
import { IDisputeGameFactory } from "src/dispute/interfaces/IDisputeGameFactory.sol"; import { IDisputeGameFactory } from "src/dispute/interfaces/IDisputeGameFactory.sol";
import { SuperchainConfig } from "src/L1/SuperchainConfig.sol";
import "src/dispute/lib/Types.sol"; import "src/dispute/lib/Types.sol";
import { Unauthorized } from "src/libraries/errors/CommonErrors.sol";
import { UnregisteredGame, InvalidGameStatus } from "src/dispute/lib/Errors.sol";
/// @title AnchorStateRegistry /// @title AnchorStateRegistry
/// @notice The AnchorStateRegistry is a contract that stores the latest "anchor" state for each available /// @notice The AnchorStateRegistry is a contract that stores the latest "anchor" state for each available
...@@ -24,8 +27,8 @@ contract AnchorStateRegistry is Initializable, IAnchorStateRegistry, ISemver { ...@@ -24,8 +27,8 @@ contract AnchorStateRegistry is Initializable, IAnchorStateRegistry, ISemver {
} }
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 1.0.0 /// @custom:semver 2.0.0
string public constant version = "1.0.0"; string public constant version = "2.0.0";
/// @notice DisputeGameFactory address. /// @notice DisputeGameFactory address.
IDisputeGameFactory internal immutable DISPUTE_GAME_FACTORY; IDisputeGameFactory internal immutable DISPUTE_GAME_FACTORY;
...@@ -33,21 +36,30 @@ contract AnchorStateRegistry is Initializable, IAnchorStateRegistry, ISemver { ...@@ -33,21 +36,30 @@ contract AnchorStateRegistry is Initializable, IAnchorStateRegistry, ISemver {
/// @inheritdoc IAnchorStateRegistry /// @inheritdoc IAnchorStateRegistry
mapping(GameType => OutputRoot) public anchors; mapping(GameType => OutputRoot) public anchors;
/// @notice Address of the SuperchainConfig contract.
SuperchainConfig public superchainConfig;
/// @param _disputeGameFactory DisputeGameFactory address. /// @param _disputeGameFactory DisputeGameFactory address.
constructor(IDisputeGameFactory _disputeGameFactory) { constructor(IDisputeGameFactory _disputeGameFactory) {
DISPUTE_GAME_FACTORY = _disputeGameFactory; DISPUTE_GAME_FACTORY = _disputeGameFactory;
_disableInitializers();
// Initialize the implementation with an empty array of starting anchor roots.
initialize(new StartingAnchorRoot[](0));
} }
/// @notice Initializes the contract. /// @notice Initializes the contract.
/// @param _startingAnchorRoots An array of starting anchor roots. /// @param _startingAnchorRoots An array of starting anchor roots.
function initialize(StartingAnchorRoot[] memory _startingAnchorRoots) public initializer { /// @param _superchainConfig The address of the SuperchainConfig contract.
function initialize(
StartingAnchorRoot[] memory _startingAnchorRoots,
SuperchainConfig _superchainConfig
)
public
initializer
{
for (uint256 i = 0; i < _startingAnchorRoots.length; i++) { for (uint256 i = 0; i < _startingAnchorRoots.length; i++) {
StartingAnchorRoot memory startingAnchorRoot = _startingAnchorRoots[i]; StartingAnchorRoot memory startingAnchorRoot = _startingAnchorRoots[i];
anchors[startingAnchorRoot.gameType] = startingAnchorRoot.outputRoot; anchors[startingAnchorRoot.gameType] = startingAnchorRoot.outputRoot;
} }
superchainConfig = _superchainConfig;
} }
/// @inheritdoc IAnchorStateRegistry /// @inheritdoc IAnchorStateRegistry
...@@ -67,10 +79,7 @@ contract AnchorStateRegistry is Initializable, IAnchorStateRegistry, ISemver { ...@@ -67,10 +79,7 @@ contract AnchorStateRegistry is Initializable, IAnchorStateRegistry, ISemver {
DISPUTE_GAME_FACTORY.games({ _gameType: gameType, _rootClaim: rootClaim, _extraData: extraData }); DISPUTE_GAME_FACTORY.games({ _gameType: gameType, _rootClaim: rootClaim, _extraData: extraData });
// Must be a valid game. // Must be a valid game.
require( if (address(factoryRegisteredGame) != address(game)) revert UnregisteredGame();
address(factoryRegisteredGame) == address(game),
"AnchorStateRegistry: fault dispute game not registered with factory"
);
// No need to update anything if the anchor state is already newer. // No need to update anything if the anchor state is already newer.
if (game.l2BlockNumber() <= anchors[gameType].l2BlockNumber) { if (game.l2BlockNumber() <= anchors[gameType].l2BlockNumber) {
...@@ -85,4 +94,27 @@ contract AnchorStateRegistry is Initializable, IAnchorStateRegistry, ISemver { ...@@ -85,4 +94,27 @@ contract AnchorStateRegistry is Initializable, IAnchorStateRegistry, ISemver {
// Actually update the anchor state. // Actually update the anchor state.
anchors[gameType] = OutputRoot({ l2BlockNumber: game.l2BlockNumber(), root: Hash.wrap(game.rootClaim().raw()) }); anchors[gameType] = OutputRoot({ l2BlockNumber: game.l2BlockNumber(), root: Hash.wrap(game.rootClaim().raw()) });
} }
/// @inheritdoc IAnchorStateRegistry
function setAnchorState(IFaultDisputeGame _game) external {
if (msg.sender != superchainConfig.guardian()) revert Unauthorized();
// Get the metadata of the game.
(GameType gameType, Claim rootClaim, bytes memory extraData) = _game.gameData();
// Grab the verified address of the game based on the game data.
// slither-disable-next-line unused-return
(IDisputeGame factoryRegisteredGame,) =
DISPUTE_GAME_FACTORY.games({ _gameType: gameType, _rootClaim: rootClaim, _extraData: extraData });
// Must be a valid game.
if (address(factoryRegisteredGame) != address(_game)) revert UnregisteredGame();
// The game must have resolved in favor of the root claim.
if (_game.status() != GameStatus.DEFENDER_WINS) revert InvalidGameStatus();
// Update the anchor.
anchors[gameType] =
OutputRoot({ l2BlockNumber: _game.l2BlockNumber(), root: Hash.wrap(_game.rootClaim().raw()) });
}
} }
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { FixedPointMathLib } from "@solady/utils/FixedPointMathLib.sol"; import { FixedPointMathLib } from "@solady/utils/FixedPointMathLib.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { IDelayedWETH } from "src/dispute/interfaces/IDelayedWETH.sol"; import { IDelayedWETH } from "src/dispute/interfaces/IDelayedWETH.sol";
import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol"; import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol";
...@@ -69,8 +70,8 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver { ...@@ -69,8 +70,8 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
uint256 internal constant HEADER_BLOCK_NUMBER_INDEX = 8; uint256 internal constant HEADER_BLOCK_NUMBER_INDEX = 8;
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 1.2.0 /// @custom:semver 1.3.0
string public constant version = "1.2.0"; string public constant version = "1.3.0";
/// @notice The starting timestamp of the game /// @notice The starting timestamp of the game
Timestamp public createdAt; Timestamp public createdAt;
...@@ -136,11 +137,35 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver { ...@@ -136,11 +137,35 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
) { ) {
// The max game depth may not be greater than `LibPosition.MAX_POSITION_BITLEN - 1`. // The max game depth may not be greater than `LibPosition.MAX_POSITION_BITLEN - 1`.
if (_maxGameDepth > LibPosition.MAX_POSITION_BITLEN - 1) revert MaxDepthTooLarge(); if (_maxGameDepth > LibPosition.MAX_POSITION_BITLEN - 1) revert MaxDepthTooLarge();
// The split depth cannot be greater than or equal to the max game depth.
if (_splitDepth >= _maxGameDepth) revert InvalidSplitDepth();
// The clock extension may not be greater than the max clock duration.
if (_clockExtension.raw() > _maxClockDuration.raw()) revert InvalidClockExtension();
// The split depth plus one cannot be greater than or equal to the max game depth. We add
// an additional depth to the split depth to avoid a bug in trace ancestor lookup. We know
// that the case where the split depth is the max value for uint256 is equivalent to the
// second check though we do need to check it explicitly to avoid an overflow.
if (_splitDepth == type(uint256).max || _splitDepth + 1 >= _maxGameDepth) revert InvalidSplitDepth();
// The split depth cannot be 0 or 1 to stay in bounds of clock extension arithmetic.
if (_splitDepth < 2) revert InvalidSplitDepth();
// The PreimageOracle challenge period must fit into uint64 so we can safely use it here.
// Runtime check was added instead of changing the ABI since the contract is already
// deployed in production. We perform the same check within the PreimageOracle for the
// benefit of developers but also perform this check here defensively.
if (_vm.oracle().challengePeriod() > type(uint64).max) revert InvalidChallengePeriod();
// Determine the maximum clock extension which is either the split depth extension or the
// maximum game depth extension depending on the configuration of these contracts.
uint256 splitDepthExtension = uint256(_clockExtension.raw()) * 2;
uint256 maxGameDepthExtension = uint256(_clockExtension.raw()) + uint256(_vm.oracle().challengePeriod());
uint256 maxClockExtension = Math.max(splitDepthExtension, maxGameDepthExtension);
// The maximum clock extension must fit into a uint64.
if (maxClockExtension > type(uint64).max) revert InvalidClockExtension();
// The maximum clock extension may not be greater than the maximum clock duration.
if (uint64(maxClockExtension) > _maxClockDuration.raw()) revert InvalidClockExtension();
// Set up initial game state.
GAME_TYPE = _gameType; GAME_TYPE = _gameType;
ABSOLUTE_PRESTATE = _absolutePrestate; ABSOLUTE_PRESTATE = _absolutePrestate;
MAX_GAME_DEPTH = _maxGameDepth; MAX_GAME_DEPTH = _maxGameDepth;
...@@ -368,17 +393,29 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver { ...@@ -368,17 +393,29 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
// seconds of time. // seconds of time.
if (nextDuration.raw() == MAX_CLOCK_DURATION.raw()) revert ClockTimeExceeded(); if (nextDuration.raw() == MAX_CLOCK_DURATION.raw()) revert ClockTimeExceeded();
// If the remaining clock time has less than `CLOCK_EXTENSION` seconds remaining, grant the potential // Clock extension is a mechanism that automatically extends the clock for a potential
// grandchild's clock `CLOCK_EXTENSION` seconds. This is to ensure that, even if a player has to inherit another // grandchild claim when there would be less than the clock extension time left if a player
// team's clock to counter a freeloader claim, they will always have enough time to respond. This extension // is forced to inherit another team's clock when countering a freeloader claim. Exact
// is bounded by the depth of the tree. If the potential grandchild is an execution trace bisection root, the // amount of clock extension time depends exactly where we are within the game.
// clock extension is doubled. This is to allow for extra time for the off-chain challenge agent to generate uint64 actualExtension;
// the initial instruction trace on the native FPVM. if (nextPositionDepth == MAX_GAME_DEPTH - 1) {
if (nextDuration.raw() > MAX_CLOCK_DURATION.raw() - CLOCK_EXTENSION.raw()) { // If the next position is `MAX_GAME_DEPTH - 1` then we're about to execute a step. Our
// If the potential grandchild is an execution trace bisection root, double the clock extension. // clock extension must therefore account for the LPP challenge period in addition to
uint64 extensionPeriod = // the standard clock extension.
nextPositionDepth == SPLIT_DEPTH - 1 ? CLOCK_EXTENSION.raw() * 2 : CLOCK_EXTENSION.raw(); actualExtension = CLOCK_EXTENSION.raw() + uint64(VM.oracle().challengePeriod());
nextDuration = Duration.wrap(MAX_CLOCK_DURATION.raw() - extensionPeriod); } else if (nextPositionDepth == SPLIT_DEPTH - 1) {
// If the next position is `SPLIT_DEPTH - 1` then we're about to begin an execution
// trace bisection and we need to give extra time for the off-chain challenge agent to
// be able to generate the initial instruction trace on the native FPVM.
actualExtension = CLOCK_EXTENSION.raw() * 2;
} else {
// Otherwise, we just use the standard clock extension.
actualExtension = CLOCK_EXTENSION.raw();
}
// Check if we need to apply the clock extension.
if (nextDuration.raw() > MAX_CLOCK_DURATION.raw() - actualExtension) {
nextDuration = Duration.wrap(MAX_CLOCK_DURATION.raw() - actualExtension);
} }
// Construct the next clock with the new duration and the current block timestamp. // Construct the next clock with the new duration and the current block timestamp.
...@@ -452,6 +489,9 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver { ...@@ -452,6 +489,9 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
// block number. // block number.
uint256 l2Number = startingOutputRoot.l2BlockNumber + disputedPos.traceIndex(SPLIT_DEPTH) + 1; uint256 l2Number = startingOutputRoot.l2BlockNumber + disputedPos.traceIndex(SPLIT_DEPTH) + 1;
// Choose the minimum between the `l2BlockNumber` claim and the bisected-to L2 block number.
l2Number = l2Number < l2BlockNumber() ? l2Number : l2BlockNumber();
oracle.loadLocalData(_ident, uuid.raw(), bytes32(l2Number << 0xC0), 8, _partOffset); oracle.loadLocalData(_ident, uuid.raw(), bytes32(l2Number << 0xC0), 8, _partOffset);
} else if (_ident == LocalPreimageKey.CHAIN_ID) { } else if (_ident == LocalPreimageKey.CHAIN_ID) {
// Load the chain ID as a big-endian uint64 in the high order 8 bytes of the word. // Load the chain ID as a big-endian uint64 in the high order 8 bytes of the word.
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
import { IFaultDisputeGame } from "src/dispute/interfaces/IFaultDisputeGame.sol";
import { IDisputeGameFactory } from "src/dispute/interfaces/IDisputeGameFactory.sol"; import { IDisputeGameFactory } from "src/dispute/interfaces/IDisputeGameFactory.sol";
import "src/dispute/lib/Types.sol"; import "src/dispute/lib/Types.sol";
...@@ -21,4 +22,8 @@ interface IAnchorStateRegistry { ...@@ -21,4 +22,8 @@ interface IAnchorStateRegistry {
/// the FaultDisputeGame contract and stores it in the registry if the new anchor state is valid and the /// the FaultDisputeGame contract and stores it in the registry if the new anchor state is valid and the
/// state is newer than the current anchor state. /// state is newer than the current anchor state.
function tryUpdateAnchorState() external; function tryUpdateAnchorState() external;
/// @notice Sets the anchor state given the game.
/// @param _game The game to set the anchor state for.
function setAnchorState(IFaultDisputeGame _game) external;
} }
...@@ -94,6 +94,9 @@ error InvalidSplitDepth(); ...@@ -94,6 +94,9 @@ error InvalidSplitDepth();
/// @notice Thrown on deployment if the max clock duration is less than or equal to the clock extension. /// @notice Thrown on deployment if the max clock duration is less than or equal to the clock extension.
error InvalidClockExtension(); error InvalidClockExtension();
/// @notice Thrown on deployment if the PreimageOracle challenge period is too high.
error InvalidChallengePeriod();
/// @notice Thrown on deployment if the max depth is greater than `LibPosition.` /// @notice Thrown on deployment if the max depth is greater than `LibPosition.`
error MaxDepthTooLarge(); error MaxDepthTooLarge();
...@@ -123,3 +126,13 @@ error L2BlockNumberChallenged(); ...@@ -123,3 +126,13 @@ error L2BlockNumberChallenged();
/// @notice Thrown when an unauthorized address attempts to interact with the game. /// @notice Thrown when an unauthorized address attempts to interact with the game.
error BadAuth(); error BadAuth();
////////////////////////////////////////////////////////////////
// `AnchorStateRegistry` Errors //
////////////////////////////////////////////////////////////////
/// @notice Thrown when attempting to set an anchor state using an unregistered game.
error UnregisteredGame();
/// @notice Thrown when attempting to set an anchor state using an invalid game result.
error InvalidGameStatus();
...@@ -21,8 +21,8 @@ import { SuperchainConfig } from "src/L1/SuperchainConfig.sol"; ...@@ -21,8 +21,8 @@ import { SuperchainConfig } from "src/L1/SuperchainConfig.sol";
/// Not the prettiest contract in the world, but it gets the job done. /// Not the prettiest contract in the world, but it gets the job done.
contract DelayedWETH is OwnableUpgradeable, WETH98, IDelayedWETH, ISemver { contract DelayedWETH is OwnableUpgradeable, WETH98, IDelayedWETH, ISemver {
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 1.0.0 /// @custom:semver 1.1.0
string public constant version = "1.0.0"; string public constant version = "1.1.0";
/// @inheritdoc IDelayedWETH /// @inheritdoc IDelayedWETH
mapping(address => mapping(address => WithdrawalRequest)) public withdrawals; mapping(address => mapping(address => WithdrawalRequest)) public withdrawals;
...@@ -85,7 +85,8 @@ contract DelayedWETH is OwnableUpgradeable, WETH98, IDelayedWETH, ISemver { ...@@ -85,7 +85,8 @@ contract DelayedWETH is OwnableUpgradeable, WETH98, IDelayedWETH, ISemver {
function recover(uint256 _wad) external { function recover(uint256 _wad) external {
require(msg.sender == owner(), "DelayedWETH: not owner"); require(msg.sender == owner(), "DelayedWETH: not owner");
uint256 amount = _wad < address(this).balance ? _wad : address(this).balance; uint256 amount = _wad < address(this).balance ? _wad : address(this).balance;
payable(msg.sender).transfer(amount); (bool success,) = payable(msg.sender).call{ value: amount }(hex"");
require(success, "DelayedWETH: recover failed");
} }
/// @inheritdoc IDelayedWETH /// @inheritdoc IDelayedWETH
......
...@@ -7,6 +7,8 @@ import { GnosisSafe as Safe } from "safe-contracts/GnosisSafe.sol"; ...@@ -7,6 +7,8 @@ import { GnosisSafe as Safe } from "safe-contracts/GnosisSafe.sol";
import "test/safe-tools/SafeTestTools.sol"; import "test/safe-tools/SafeTestTools.sol";
import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol"; import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol";
import { IFaultDisputeGame } from "src/dispute/interfaces/IFaultDisputeGame.sol";
import { AnchorStateRegistry } from "src/dispute/AnchorStateRegistry.sol";
import { DeputyGuardianModule } from "src/Safe/DeputyGuardianModule.sol"; import { DeputyGuardianModule } from "src/Safe/DeputyGuardianModule.sol";
import "src/dispute/lib/Types.sol"; import "src/dispute/lib/Types.sol";
...@@ -148,6 +150,43 @@ contract DeputyGuardianModule_Unpause_TestFail is DeputyGuardianModule_Unpause_T ...@@ -148,6 +150,43 @@ contract DeputyGuardianModule_Unpause_TestFail is DeputyGuardianModule_Unpause_T
} }
} }
contract DeputyGuardianModule_SetAnchorState_TestFail is DeputyGuardianModule_TestInit {
function test_setAnchorState_notDeputyGuardian_reverts() external {
AnchorStateRegistry asr = AnchorStateRegistry(makeAddr("asr"));
vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector));
deputyGuardianModule.setAnchorState(asr, IFaultDisputeGame(address(0)));
}
function test_setAnchorState_targetReverts_reverts() external {
AnchorStateRegistry asr = AnchorStateRegistry(makeAddr("asr"));
vm.mockCallRevert(
address(asr),
abi.encodeWithSelector(asr.setAnchorState.selector),
"AnchorStateRegistry: setAnchorState reverted"
);
vm.prank(address(deputyGuardian));
vm.expectRevert(
abi.encodeWithSelector(ExecutionFailed.selector, "AnchorStateRegistry: setAnchorState reverted")
);
deputyGuardianModule.setAnchorState(asr, IFaultDisputeGame(address(0)));
}
}
contract DeputyGuardianModule_SetAnchorState_Test is DeputyGuardianModule_TestInit {
function test_setAnchorState_succeeds() external {
AnchorStateRegistry asr = AnchorStateRegistry(makeAddr("asr"));
vm.mockCall(
address(asr),
abi.encodeWithSelector(AnchorStateRegistry.setAnchorState.selector, IFaultDisputeGame(address(0))),
""
);
vm.expectEmit(address(safeInstance.safe));
emit ExecutionFromModuleSuccess(address(deputyGuardianModule));
vm.prank(address(deputyGuardian));
deputyGuardianModule.setAnchorState(asr, IFaultDisputeGame(address(0)));
}
}
contract DeputyGuardianModule_BlacklistDisputeGame_Test is DeputyGuardianModule_TestInit { contract DeputyGuardianModule_BlacklistDisputeGame_Test is DeputyGuardianModule_TestInit {
/// @dev Tests that `blacklistDisputeGame` successfully blacklists a dispute game when called by the deputy /// @dev Tests that `blacklistDisputeGame` successfully blacklists a dispute game when called by the deputy
/// guardian. /// guardian.
......
...@@ -617,9 +617,11 @@ contract Specification_Test is CommonTest { ...@@ -617,9 +617,11 @@ contract Specification_Test is CommonTest {
// AnchorStateRegistry // AnchorStateRegistry
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("anchors(uint32)") }); _addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("anchors(uint32)") });
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("disputeGameFactory()") }); _addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("disputeGameFactory()") });
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("initialize((uint32,(bytes32,uint256))[])") }); _addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("initialize((uint32,(bytes32,uint256))[],address)") });
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("tryUpdateAnchorState()") }); _addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("tryUpdateAnchorState()") });
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("setAnchorState(address)"), _auth: Role.GUARDIAN });
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("version()") }); _addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("version()") });
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("superchainConfig()") });
// PermissionedDisputeGame // PermissionedDisputeGame
_addSpec({ _name: "PermissionedDisputeGame", _sel: _getSel("absolutePrestate()") }); _addSpec({ _name: "PermissionedDisputeGame", _sel: _getSel("absolutePrestate()") });
...@@ -826,6 +828,11 @@ contract Specification_Test is CommonTest { ...@@ -826,6 +828,11 @@ contract Specification_Test is CommonTest {
_sel: _getSel("setRespectedGameType(address,uint32)"), _sel: _getSel("setRespectedGameType(address,uint32)"),
_auth: Role.DEPUTYGUARDIAN _auth: Role.DEPUTYGUARDIAN
}); });
_addSpec({
_name: "DeputyGuardianModule",
_sel: _getSel("setAnchorState(address,address)"),
_auth: Role.DEPUTYGUARDIAN
});
_addSpec({ _name: "DeputyGuardianModule", _sel: _getSel("pause()"), _auth: Role.DEPUTYGUARDIAN }); _addSpec({ _name: "DeputyGuardianModule", _sel: _getSel("pause()"), _auth: Role.DEPUTYGUARDIAN });
_addSpec({ _name: "DeputyGuardianModule", _sel: _getSel("unpause()"), _auth: Role.DEPUTYGUARDIAN }); _addSpec({ _name: "DeputyGuardianModule", _sel: _getSel("unpause()"), _auth: Role.DEPUTYGUARDIAN });
_addSpec({ _name: "DeputyGuardianModule", _sel: _getSel("deputyGuardian()") }); _addSpec({ _name: "DeputyGuardianModule", _sel: _getSel("deputyGuardian()") });
...@@ -940,11 +947,12 @@ contract Specification_Test is CommonTest { ...@@ -940,11 +947,12 @@ contract Specification_Test is CommonTest {
/// @notice Ensures that the DeputyGuardian is authorized to take all Guardian actions. /// @notice Ensures that the DeputyGuardian is authorized to take all Guardian actions.
function testDeputyGuardianAuth() public view { function testDeputyGuardianAuth() public view {
assertEq(specsByRole[Role.DEPUTYGUARDIAN].length, specsByRole[Role.GUARDIAN].length); assertEq(specsByRole[Role.DEPUTYGUARDIAN].length, specsByRole[Role.GUARDIAN].length);
assertEq(specsByRole[Role.DEPUTYGUARDIAN].length, 4); assertEq(specsByRole[Role.DEPUTYGUARDIAN].length, 5);
mapping(bytes4 => Spec) storage dgmFuncSpecs = specs["DeputyGuardianModule"]; mapping(bytes4 => Spec) storage dgmFuncSpecs = specs["DeputyGuardianModule"];
mapping(bytes4 => Spec) storage superchainConfigFuncSpecs = specs["SuperchainConfig"]; mapping(bytes4 => Spec) storage superchainConfigFuncSpecs = specs["SuperchainConfig"];
mapping(bytes4 => Spec) storage portal2FuncSpecs = specs["OptimismPortal2"]; mapping(bytes4 => Spec) storage portal2FuncSpecs = specs["OptimismPortal2"];
mapping(bytes4 => Spec) storage anchorRegFuncSpecs = specs["AnchorStateRegistry"];
// Ensure that for each of the DeputyGuardianModule's methods there is a corresponding method on another // Ensure that for each of the DeputyGuardianModule's methods there is a corresponding method on another
// system contract authed to the Guardian role. // system contract authed to the Guardian role.
...@@ -959,5 +967,8 @@ contract Specification_Test is CommonTest { ...@@ -959,5 +967,8 @@ contract Specification_Test is CommonTest {
_assertRolesEq(dgmFuncSpecs[_getSel("setRespectedGameType(address,uint32)")].auth, Role.DEPUTYGUARDIAN); _assertRolesEq(dgmFuncSpecs[_getSel("setRespectedGameType(address,uint32)")].auth, Role.DEPUTYGUARDIAN);
_assertRolesEq(portal2FuncSpecs[_getSel("setRespectedGameType(uint32)")].auth, Role.GUARDIAN); _assertRolesEq(portal2FuncSpecs[_getSel("setRespectedGameType(uint32)")].auth, Role.GUARDIAN);
_assertRolesEq(dgmFuncSpecs[_getSel("setAnchorState(address,address)")].auth, Role.DEPUTYGUARDIAN);
_assertRolesEq(anchorRegFuncSpecs[_getSel("setAnchorState(address)")].auth, Role.GUARDIAN);
} }
} }
...@@ -4,6 +4,9 @@ pragma solidity 0.8.15; ...@@ -4,6 +4,9 @@ pragma solidity 0.8.15;
import { CommonTest } from "test/setup/CommonTest.sol"; import { CommonTest } from "test/setup/CommonTest.sol";
import { MIPS } from "src/cannon/MIPS.sol"; import { MIPS } from "src/cannon/MIPS.sol";
import { PreimageOracle } from "src/cannon/PreimageOracle.sol"; import { PreimageOracle } from "src/cannon/PreimageOracle.sol";
import { MIPSInstructions } from "src/cannon/libraries/MIPSInstructions.sol";
import { MIPSSyscalls as sys } from "src/cannon/libraries/MIPSSyscalls.sol";
import { InvalidExitedValue } from "src/cannon/libraries/CannonErrors.sol";
import "src/dispute/lib/Types.sol"; import "src/dispute/lib/Types.sol";
contract MIPS_Test is CommonTest { contract MIPS_Test is CommonTest {
...@@ -58,6 +61,30 @@ contract MIPS_Test is CommonTest { ...@@ -58,6 +61,30 @@ contract MIPS_Test is CommonTest {
assertNotEq(postState, bytes32(0)); assertNotEq(postState, bytes32(0));
} }
/// @notice Tests that the mips step function fails when the value of the exited field is
/// invalid (anything greater than 1).
/// @param _exited Value to set the exited field to.
function testFuzz_step_invalidExitedValue_fails(uint8 _exited) external {
// Assume
// Make sure the value of _exited is invalid.
_exited = uint8(bound(uint256(_exited), 2, type(uint8).max));
// Rest of this stuff doesn't matter very much, just setting up some state to edit.
// Here just using the parameters for the ADD test below.
uint32 insn = encodespec(17, 18, 8, 0x20);
(MIPS.State memory state, bytes memory proof) = constructMIPSState(0, insn, 0x4, 0);
// Compute the encoded state and manipulate it.
bytes memory enc = encodeState(state);
assembly {
mstore8(add(add(enc, 0x20), 89), _exited)
}
// Call the step function and expect a revert.
vm.expectRevert(InvalidExitedValue.selector);
mips.step(enc, proof, 0);
}
function test_add_succeeds() external { function test_add_succeeds() external {
uint32 insn = encodespec(17, 18, 8, 0x20); // add t0, s1, s2 uint32 insn = encodespec(17, 18, 8, 0x20); // add t0, s1, s2
(MIPS.State memory state, bytes memory proof) = constructMIPSState(0, insn, 0x4, 0); (MIPS.State memory state, bytes memory proof) = constructMIPSState(0, insn, 0x4, 0);
...@@ -1241,15 +1268,46 @@ contract MIPS_Test is CommonTest { ...@@ -1241,15 +1268,46 @@ contract MIPS_Test is CommonTest {
function test_srav_succeeds() external { function test_srav_succeeds() external {
uint32 insn = encodespec(0xa, 0x9, 0x8, 7); // srav t0, t1, t2 uint32 insn = encodespec(0xa, 0x9, 0x8, 7); // srav t0, t1, t2
(MIPS.State memory state, bytes memory proof) = constructMIPSState(0, insn, 0x4, 0); (MIPS.State memory state, bytes memory proof) = constructMIPSState(0, insn, 0x4, 0);
state.registers[9] = 0x20_00; // t1 state.registers[9] = 0xdeafbeef; // t1
state.registers[10] = 4; // t2 state.registers[10] = 12; // t2
MIPS.State memory expect; MIPS.State memory expect;
expect.memRoot = state.memRoot; expect.memRoot = state.memRoot;
expect.pc = state.nextPC; expect.pc = state.nextPC;
expect.nextPC = state.nextPC + 4; expect.nextPC = state.nextPC + 4;
expect.step = state.step + 1; expect.step = state.step + 1;
expect.registers[8] = state.registers[9] >> state.registers[10]; // t0 expect.registers[8] = 0xfffdeafb; // t0
expect.registers[9] = state.registers[9];
expect.registers[10] = state.registers[10];
bytes memory enc = encodeState(state);
bytes32 postState = mips.step(enc, proof, 0);
assertEq(postState, outputState(expect), "unexpected post state");
}
/// @notice Tests that the SRAV instruction succeeds when it includes extra bits in the shift
/// amount beyond the lower 5 bits that are actually used for the shift. Extra bits
/// need to be ignored but the instruction should still succeed.
/// @param _rs Value to set in the shift register $rs.
function testFuzz_srav_withExtraBits_succeeds(uint32 _rs) external {
// Assume
// Force _rs to have more than 5 bits set.
_rs = uint32(bound(uint256(_rs), 0x20, type(uint32).max));
uint32 insn = encodespec(0xa, 0x9, 0x8, 7); // srav t0, t1, t2
(MIPS.State memory state, bytes memory proof) = constructMIPSState(0, insn, 0x4, 0);
state.registers[9] = 0xdeadbeef; // t1
state.registers[10] = _rs; // t2
// Calculate shamt
uint32 shamt = state.registers[10] & 0x1F;
MIPS.State memory expect;
expect.memRoot = state.memRoot;
expect.pc = state.nextPC;
expect.nextPC = state.nextPC + 4;
expect.step = state.step + 1;
expect.registers[8] = MIPSInstructions.signExtend(state.registers[9] >> shamt, 32 - shamt); // t0
expect.registers[9] = state.registers[9]; expect.registers[9] = state.registers[9];
expect.registers[10] = state.registers[10]; expect.registers[10] = state.registers[10];
...@@ -1426,6 +1484,64 @@ contract MIPS_Test is CommonTest { ...@@ -1426,6 +1484,64 @@ contract MIPS_Test is CommonTest {
assertEq(postState, outputState(expect), "unexpected post state"); assertEq(postState, outputState(expect), "unexpected post state");
} }
function test_mmap_succeeds_justWithinMemLimit() external {
uint32 insn = 0x0000000c; // syscall
(bytes32 memRoot, bytes memory proof) = ffi.getCannonMemoryProof(0, insn);
MIPS.State memory state;
state.memRoot = memRoot;
state.nextPC = 4;
state.heap = sys.HEAP_END - 4096; // Set up to increase heap to its limit
state.registers[2] = 4090; // mmap syscall
state.registers[4] = 0x0; // a0
state.registers[5] = 4095; // a1
bytes memory encodedState = encodeState(state);
MIPS.State memory expect;
expect.memRoot = state.memRoot;
// assert page allocation is aligned to 4k
expect.step = state.step + 1;
expect.pc = state.nextPC;
expect.nextPC = state.nextPC + 4;
expect.heap = sys.HEAP_END;
expect.registers[2] = state.heap; // Return the old heap value
expect.registers[7] = 0; // No error
expect.registers[4] = state.registers[4]; // a0
expect.registers[5] = state.registers[5]; // a1
bytes32 postState = mips.step(encodedState, proof, 0);
assertEq(postState, outputState(expect), "unexpected post state");
}
function test_mmap_fails() external {
uint32 insn = 0x0000000c; // syscall
(bytes32 memRoot, bytes memory proof) = ffi.getCannonMemoryProof(0, insn);
MIPS.State memory state;
state.memRoot = memRoot;
state.nextPC = 4;
state.heap = sys.HEAP_END - 4096; // Set up to increase heap beyond its limit
state.registers[2] = 4090; // mmap syscall
state.registers[4] = 0x0; // a0
state.registers[5] = 4097; // a1
bytes memory encodedState = encodeState(state);
MIPS.State memory expect;
expect.memRoot = state.memRoot;
// assert page allocation is aligned to 4k
expect.step = state.step + 1;
expect.pc = state.nextPC;
expect.nextPC = state.nextPC + 4;
expect.heap = state.heap;
expect.registers[2] = sys.SYS_ERROR_SIGNAL; // signal an stdError
expect.registers[7] = sys.EINVAL; // Return error value
expect.registers[4] = state.registers[4]; // a0
expect.registers[5] = state.registers[5]; // a1
bytes32 postState = mips.step(encodedState, proof, 0);
assertEq(postState, outputState(expect), "unexpected post state");
}
function test_brk_succeeds() external { function test_brk_succeeds() external {
uint32 insn = 0x0000000c; // syscall uint32 insn = 0x0000000c; // syscall
(MIPS.State memory state, bytes memory proof) = constructMIPSState(0, insn, 0x4, 0); (MIPS.State memory state, bytes memory proof) = constructMIPSState(0, insn, 0x4, 0);
......
...@@ -20,6 +20,14 @@ contract PreimageOracle_Test is Test { ...@@ -20,6 +20,14 @@ contract PreimageOracle_Test is Test {
vm.label(address(oracle), "PreimageOracle"); vm.label(address(oracle), "PreimageOracle");
} }
/// @notice Tests that the challenge period cannot be made too large.
/// @param _challengePeriod The challenge period to test.
function testFuzz_constructor_challengePeriodTooLarge_reverts(uint256 _challengePeriod) public {
_challengePeriod = bound(_challengePeriod, uint256(type(uint64).max) + 1, type(uint256).max);
vm.expectRevert("challenge period too large");
new PreimageOracle(0, _challengePeriod);
}
/// @notice Test the pre-image key computation with a known pre-image. /// @notice Test the pre-image key computation with a known pre-image.
function test_keccak256PreimageKey_succeeds() public pure { function test_keccak256PreimageKey_succeeds() public pure {
bytes memory preimage = hex"deadbeef"; bytes memory preimage = hex"deadbeef";
...@@ -92,8 +100,8 @@ contract PreimageOracle_Test is Test { ...@@ -92,8 +100,8 @@ contract PreimageOracle_Test is Test {
{ {
// Bound the size to [0, 32] // Bound the size to [0, 32]
size = bound(size, 0, 32); size = bound(size, 0, 32);
// Bound the part offset to [0, size + 8] // Bound the part offset to [0, size + 8)
partOffset = bound(partOffset, 0, size + 8); partOffset = bound(partOffset, 0, size + 7);
// 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, localContext, word, uint8(size), uint8(partOffset)); bytes32 contextKey = oracle.loadLocalData(ident, localContext, word, uint8(size), uint8(partOffset));
...@@ -178,8 +186,9 @@ contract PreimageOracle_Test is Test { ...@@ -178,8 +186,9 @@ contract PreimageOracle_Test is Test {
bytes memory input = hex"deadbeef"; bytes memory input = hex"deadbeef";
uint256 offset = 0; uint256 offset = 0;
address precompile = address(bytes20(uint160(0x02))); // sha256 address precompile = address(bytes20(uint160(0x02))); // sha256
bytes32 key = precompilePreimageKey(precompile, input); uint64 gas = 72;
oracle.loadPrecompilePreimagePart(offset, precompile, input); bytes32 key = precompilePreimageKey(precompile, gas, input);
oracle.loadPrecompilePreimagePart(offset, precompile, gas, input);
bytes32 part = oracle.preimageParts(key, offset); bytes32 part = oracle.preimageParts(key, offset);
// size prefix - 1-byte result + 32-byte sha return data // size prefix - 1-byte result + 32-byte sha return data
...@@ -203,8 +212,9 @@ contract PreimageOracle_Test is Test { ...@@ -203,8 +212,9 @@ contract PreimageOracle_Test is Test {
bytes memory input = hex"deadbeef"; bytes memory input = hex"deadbeef";
uint256 offset = 9; uint256 offset = 9;
address precompile = address(bytes20(uint160(0x02))); // sha256 address precompile = address(bytes20(uint160(0x02))); // sha256
bytes32 key = precompilePreimageKey(precompile, input); uint64 gas = 72;
oracle.loadPrecompilePreimagePart(offset, precompile, input); bytes32 key = precompilePreimageKey(precompile, gas, input);
oracle.loadPrecompilePreimagePart(offset, precompile, gas, input);
bytes32 part = oracle.preimageParts(key, offset); bytes32 part = oracle.preimageParts(key, offset);
// 32-byte sha return data // 32-byte sha return data
...@@ -224,8 +234,9 @@ contract PreimageOracle_Test is Test { ...@@ -224,8 +234,9 @@ contract PreimageOracle_Test is Test {
bytes memory input = new bytes(193); // invalid input to induce a failed precompile call bytes memory input = new bytes(193); // invalid input to induce a failed precompile call
uint256 offset = 0; uint256 offset = 0;
address precompile = address(bytes20(uint160(0x08))); // bn256Pairing address precompile = address(bytes20(uint160(0x08))); // bn256Pairing
bytes32 key = precompilePreimageKey(precompile, input); uint64 gas = 72;
oracle.loadPrecompilePreimagePart(offset, precompile, input); bytes32 key = precompilePreimageKey(precompile, gas, input);
oracle.loadPrecompilePreimagePart(offset, precompile, gas, input);
bytes32 part = oracle.preimageParts(key, offset); bytes32 part = oracle.preimageParts(key, offset);
// size prefix - 1-byte result + 0-byte sha return data // size prefix - 1-byte result + 0-byte sha return data
...@@ -249,8 +260,9 @@ contract PreimageOracle_Test is Test { ...@@ -249,8 +260,9 @@ contract PreimageOracle_Test is Test {
bytes memory input = hex"deadbeef"; bytes memory input = hex"deadbeef";
uint256 offset = 41; // 8-byte prefix + 1-byte result + 32-byte sha return data uint256 offset = 41; // 8-byte prefix + 1-byte result + 32-byte sha return data
address precompile = address(bytes20(uint160(0x02))); // sha256 address precompile = address(bytes20(uint160(0x02))); // sha256
uint64 gas = 72;
vm.expectRevert(PartOffsetOOB.selector); vm.expectRevert(PartOffsetOOB.selector);
oracle.loadPrecompilePreimagePart(offset, precompile, input); oracle.loadPrecompilePreimagePart(offset, precompile, gas, input);
} }
/// @notice Tests that a global precompile result cannot be set with an out-of-bounds offset. /// @notice Tests that a global precompile result cannot be set with an out-of-bounds offset.
...@@ -258,8 +270,34 @@ contract PreimageOracle_Test is Test { ...@@ -258,8 +270,34 @@ contract PreimageOracle_Test is Test {
bytes memory input = hex"deadbeef"; bytes memory input = hex"deadbeef";
uint256 offset = 42; uint256 offset = 42;
address precompile = address(bytes20(uint160(0x02))); // sha256 address precompile = address(bytes20(uint160(0x02))); // sha256
uint64 gas = 72;
vm.expectRevert(PartOffsetOOB.selector); vm.expectRevert(PartOffsetOOB.selector);
oracle.loadPrecompilePreimagePart(offset, precompile, input); oracle.loadPrecompilePreimagePart(offset, precompile, gas, input);
}
/// @notice Tests that a global precompile load succeeds on a variety of gas inputs.
function testFuzz_loadPrecompilePreimagePart_withVaryingGas_succeeds(uint64 _gas) public {
uint64 requiredGas = 100_000;
bytes memory input = hex"deadbeef";
address precompile = address(uint160(0xdeadbeef));
vm.mockCall(precompile, input, hex"abba");
uint256 offset = 0;
uint64 minGas = uint64(bound(_gas, requiredGas * 3, 20_000_000));
vm.expectCallMinGas(precompile, 0, requiredGas, input);
oracle.loadPrecompilePreimagePart{ gas: minGas }(offset, precompile, requiredGas, input);
}
/// @notice Tests that a global precompile load succeeds on insufficient gas.
function test_loadPrecompilePreimagePart_withInsufficientGas_reverts() public {
uint64 requiredGas = 1_000_000;
bytes memory input = hex"deadbeef";
uint256 offset = 0;
address precompile = address(uint160(0xdeadbeef));
// This gas is sufficient to reach the gas checks in `loadPrecompilePreimagePart` but not enough to pass those
// checks
uint64 insufficientGas = requiredGas * 63 / 64;
vm.expectRevert(NotEnoughGas.selector);
oracle.loadPrecompilePreimagePart{ gas: insufficientGas }(offset, precompile, requiredGas, input);
} }
} }
...@@ -312,6 +350,17 @@ contract PreimageOracle_LargePreimageProposals_Test is Test { ...@@ -312,6 +350,17 @@ contract PreimageOracle_LargePreimageProposals_Test is Test {
oracle.initLPP{ value: bondSize }(TEST_UUID, 0, uint32(data.length)); oracle.initLPP{ value: bondSize }(TEST_UUID, 0, uint32(data.length));
} }
/// @notice Tests that the `initLPP` function reverts if the proposal has already been initialized.
function test_initLPP_alreadyInitialized_reverts() public {
// Initialize the proposal.
uint256 bondSize = oracle.MIN_BOND_SIZE();
oracle.initLPP{ value: bondSize }(TEST_UUID, 0, uint32(500));
// Re-initialize the proposal.
vm.expectRevert(AlreadyInitialized.selector);
oracle.initLPP{ value: bondSize }(TEST_UUID, 0, uint32(500));
}
/// @notice Gas snapshot for `addLeaves` /// @notice Gas snapshot for `addLeaves`
function test_addLeaves_gasSnapshot() public { function test_addLeaves_gasSnapshot() public {
// Allocate the preimage data. // Allocate the preimage data.
...@@ -599,6 +648,63 @@ contract PreimageOracle_LargePreimageProposals_Test is Test { ...@@ -599,6 +648,63 @@ contract PreimageOracle_LargePreimageProposals_Test is Test {
}); });
} }
/// @notice Tests that a proposal cannot be squeezed if the proposal has not been finalized.
function test_squeeze_notFinalized_reverts() public {
// Allocate the preimage data.
bytes memory data = new bytes(136);
for (uint256 i; i < data.length; i++) {
data[i] = 0xFF;
}
// Initialize the proposal.
oracle.initLPP{ value: oracle.MIN_BOND_SIZE() }(TEST_UUID, 0, uint32(data.length));
// Generate the padded input data.
// Since the data is 136 bytes, which is exactly one keccak block, we will add one extra
// keccak block of empty padding to the input data. We need to do this here because the
// addLeavesLPP function will normally perform this padding internally when _finalize is
// set to true but we're explicitly testing the case where _finalize is not true.
bytes memory paddedData = new bytes(136 * 2);
for (uint256 i; i < data.length; i++) {
paddedData[i] = data[i];
}
// Add the leaves to the tree (2 keccak blocks.)
LibKeccak.StateMatrix memory stateMatrix;
bytes32[] memory stateCommitments = _generateStateCommitments(stateMatrix, data);
oracle.addLeavesLPP(TEST_UUID, 0, paddedData, stateCommitments, false);
// Construct the leaf preimage data for the blocks added.
LibKeccak.StateMatrix memory matrix;
PreimageOracle.Leaf[] memory leaves = _generateLeaves(matrix, data);
// Create a proof array with 16 elements.
bytes32[] memory preProof = new bytes32[](16);
preProof[0] = _hashLeaf(leaves[1]);
bytes32[] memory postProof = new bytes32[](16);
postProof[0] = _hashLeaf(leaves[0]);
for (uint256 i = 1; i < preProof.length; i++) {
bytes32 zeroHash = oracle.zeroHashes(i);
preProof[i] = zeroHash;
postProof[i] = zeroHash;
}
// Warp past the challenge period.
vm.warp(block.timestamp + oracle.challengePeriod() + 1 seconds);
// Finalize the proposal.
vm.expectRevert(ActiveProposal.selector);
oracle.squeezeLPP({
_claimant: address(this),
_uuid: TEST_UUID,
_stateMatrix: _stateMatrixAtBlockIndex(data, 1),
_preState: leaves[0],
_preStateProof: preProof,
_postState: leaves[1],
_postStateProof: postProof
});
}
/// @notice Tests that a proposal cannot be finalized until it has passed the challenge period. /// @notice Tests that a proposal cannot be finalized until it has passed the challenge period.
function test_squeeze_challengePeriodActive_reverts() public { function test_squeeze_challengePeriodActive_reverts() public {
// Allocate the preimage data. // Allocate the preimage data.
...@@ -754,7 +860,7 @@ contract PreimageOracle_LargePreimageProposals_Test is Test { ...@@ -754,7 +860,7 @@ contract PreimageOracle_LargePreimageProposals_Test is Test {
/// @notice Tests that squeezing a large preimage proposal after the challenge period has passed always succeeds and /// @notice Tests that squeezing a large preimage proposal after the challenge period has passed always succeeds and
/// persists the correct data. /// persists the correct data.
function testFuzz_squeeze_succeeds(uint256 _numBlocks, uint32 _partOffset) public { function testFuzz_squeezeLPP_succeeds(uint256 _numBlocks, uint32 _partOffset) public {
_numBlocks = bound(_numBlocks, 1, 2 ** 8); _numBlocks = bound(_numBlocks, 1, 2 ** 8);
_partOffset = uint32(bound(_partOffset, 0, _numBlocks * LibKeccak.BLOCK_SIZE_BYTES + 8 - 1)); _partOffset = uint32(bound(_partOffset, 0, _numBlocks * LibKeccak.BLOCK_SIZE_BYTES + 8 - 1));
...@@ -1363,9 +1469,9 @@ function _setStatusByte(bytes32 _hash, uint8 _status) pure returns (bytes32 out_ ...@@ -1363,9 +1469,9 @@ function _setStatusByte(bytes32 _hash, uint8 _status) pure returns (bytes32 out_
} }
/// @notice Computes a precompile key for a given precompile address and input. /// @notice Computes a precompile key for a given precompile address and input.
function precompilePreimageKey(address _precompile, bytes memory _input) pure returns (bytes32 key_) { function precompilePreimageKey(address _precompile, uint64 _gas, bytes memory _input) pure returns (bytes32 key_) {
bytes memory p = abi.encodePacked(_precompile, _input); bytes memory p = abi.encodePacked(_precompile, _gas, _input);
uint256 sz = 20 + _input.length; uint256 sz = 20 + 8 + _input.length;
assembly { assembly {
let h := keccak256(add(0x20, p), sz) let h := keccak256(add(0x20, p), sz)
// Mask out prefix byte, replace with type 6 byte // Mask out prefix byte, replace with type 6 byte
......
...@@ -5,7 +5,7 @@ import "src/dispute/lib/Types.sol"; ...@@ -5,7 +5,7 @@ import "src/dispute/lib/Types.sol";
import "src/dispute/lib/Errors.sol"; import "src/dispute/lib/Errors.sol";
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { FaultDisputeGame_Init, _changeClaimStatus } from "test/dispute/FaultDisputeGame.t.sol"; import { FaultDisputeGame_Init, IDisputeGame, _changeClaimStatus } from "test/dispute/FaultDisputeGame.t.sol";
contract AnchorStateRegistry_Init is FaultDisputeGame_Init { contract AnchorStateRegistry_Init is FaultDisputeGame_Init {
function setUp() public virtual override { function setUp() public virtual override {
...@@ -124,7 +124,7 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In ...@@ -124,7 +124,7 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In
// Try to update the anchor state. // Try to update the anchor state.
vm.prank(address(gameProxy)); vm.prank(address(gameProxy));
vm.expectRevert("AnchorStateRegistry: fault dispute game not registered with factory"); vm.expectRevert(UnregisteredGame.selector);
anchorStateRegistry.tryUpdateAnchorState(); anchorStateRegistry.tryUpdateAnchorState();
// Confirm that the anchor state has not updated. // Confirm that the anchor state has not updated.
...@@ -132,4 +132,88 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In ...@@ -132,4 +132,88 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In
assertEq(updatedL2BlockNumber, l2BlockNumber); assertEq(updatedL2BlockNumber, l2BlockNumber);
assertEq(updatedRoot.raw(), root.raw()); assertEq(updatedRoot.raw(), root.raw());
} }
function test_setAnchorState_invalidGame_fails() public {
// Confirm that the anchor state is older than the game state.
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
require(l2BlockNumber < gameProxy.l2BlockNumber(), "l2BlockNumber < gameProxy.l2BlockNumber()");
// Mock the state that we want.
vm.mockCall(
address(disputeGameFactory),
abi.encodeWithSelector(
disputeGameFactory.games.selector, gameProxy.gameType(), gameProxy.rootClaim(), gameProxy.extraData()
),
abi.encode(address(0), 0)
);
// Try to update the anchor state.
vm.prank(superchainConfig.guardian());
vm.expectRevert(UnregisteredGame.selector);
anchorStateRegistry.setAnchorState(gameProxy);
// Confirm that the anchor state has not updated.
(Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
assertEq(updatedL2BlockNumber, l2BlockNumber);
assertEq(updatedRoot.raw(), root.raw());
}
/// @dev Tests that setting the anchor state fails if the challenger wins.
function test_setAnchorState_challengerWins_fails() public {
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
// Mock the state that we want.
vm.mockCall(
address(gameProxy),
abi.encodeWithSelector(gameProxy.status.selector),
abi.encode(GameStatus.CHALLENGER_WINS)
);
// Set the anchor state.
vm.prank(superchainConfig.guardian());
vm.expectRevert(InvalidGameStatus.selector);
anchorStateRegistry.setAnchorState(gameProxy);
// Confirm that the anchor state has not updated.
(Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
assertEq(updatedL2BlockNumber, l2BlockNumber);
assertEq(updatedRoot.raw(), root.raw());
}
/// @dev Tests that setting the anchor state fails if the game is in progress.
function test_setAnchorState_gameInProgress_fails() public {
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
// Mock the state that we want.
vm.mockCall(
address(gameProxy), abi.encodeWithSelector(gameProxy.status.selector), abi.encode(GameStatus.IN_PROGRESS)
);
// Set the anchor state.
vm.prank(superchainConfig.guardian());
vm.expectRevert(InvalidGameStatus.selector);
anchorStateRegistry.setAnchorState(gameProxy);
// Confirm that the anchor state has not updated.
(Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
assertEq(updatedL2BlockNumber, l2BlockNumber);
assertEq(updatedRoot.raw(), root.raw());
}
/// @dev Tests that setting the anchor state succeeds.
function test_setAnchorState_succeeds() public {
// Mock the state that we want.
vm.mockCall(
address(gameProxy), abi.encodeWithSelector(gameProxy.status.selector), abi.encode(GameStatus.DEFENDER_WINS)
);
// Set the anchor state.
vm.prank(superchainConfig.guardian());
anchorStateRegistry.setAnchorState(gameProxy);
// Confirm that the anchor state has updated.
(Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
assertEq(updatedL2BlockNumber, gameProxy.l2BlockNumber());
assertEq(updatedRoot.raw(), gameProxy.rootClaim().raw());
}
} }
...@@ -9,6 +9,7 @@ import { DisputeGameFactory, IDisputeGameFactory } from "src/dispute/DisputeGame ...@@ -9,6 +9,7 @@ import { DisputeGameFactory, IDisputeGameFactory } from "src/dispute/DisputeGame
import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol"; import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol";
import { DelayedWETH } from "src/dispute/weth/DelayedWETH.sol"; import { DelayedWETH } from "src/dispute/weth/DelayedWETH.sol";
import { Proxy } from "src/universal/Proxy.sol"; import { Proxy } from "src/universal/Proxy.sol";
import { Burn } from "src/libraries/Burn.sol";
import { CommonTest } from "test/setup/CommonTest.sol"; import { CommonTest } from "test/setup/CommonTest.sol";
contract DelayedWETH_Init is CommonTest { contract DelayedWETH_Init is CommonTest {
...@@ -45,7 +46,7 @@ contract DelayedWETH_Unlock_Test is DelayedWETH_Init { ...@@ -45,7 +46,7 @@ contract DelayedWETH_Unlock_Test is DelayedWETH_Init {
assertEq(timestamp, block.timestamp); assertEq(timestamp, block.timestamp);
} }
/// @dev TEsts that unlocking twice is successful and timestamp/amount is updated. /// @dev Tests that unlocking twice is successful and timestamp/amount is updated.
function test_unlock_twice_succeeds() public { function test_unlock_twice_succeeds() public {
// Unlock once. // Unlock once.
uint256 ts = block.timestamp; uint256 ts = block.timestamp;
...@@ -170,24 +171,35 @@ contract DelayedWETH_Withdraw_Test is DelayedWETH_Init { ...@@ -170,24 +171,35 @@ contract DelayedWETH_Withdraw_Test is DelayedWETH_Init {
} }
contract DelayedWETH_Recover_Test is DelayedWETH_Init { contract DelayedWETH_Recover_Test is DelayedWETH_Init {
/// @dev Tests that recovering WETH succeeds. /// @dev Tests that recovering WETH succeeds. Makes sure that doing so succeeds with any amount
function test_recover_succeeds() public { /// of ETH in the contract and any amount of gas used in the fallback function up to a
/// maximum of 20,000,000 gas. Owner contract should never be using that much gas but we
/// might as well set a very large upper bound for ourselves.
/// @param _amount Amount of WETH to recover.
/// @param _fallbackGasUsage Amount of gas to use in the fallback function.
function testFuzz_recover_succeeds(uint256 _amount, uint256 _fallbackGasUsage) public {
// Assume
_fallbackGasUsage = bound(_fallbackGasUsage, 0, 20000000);
// Set up the gas burner.
FallbackGasUser gasUser = new FallbackGasUser(_fallbackGasUsage);
// Transfer ownership to alice. // Transfer ownership to alice.
delayedWeth.transferOwnership(alice); delayedWeth.transferOwnership(address(gasUser));
// Give the contract some WETH to recover. // Give the contract some WETH to recover.
vm.deal(address(delayedWeth), 1 ether); vm.deal(address(delayedWeth), _amount);
// Record the initial balance. // Record the initial balance.
uint256 initialBalance = address(alice).balance; uint256 initialBalance = address(gasUser).balance;
// Recover the WETH. // Recover the WETH.
vm.prank(alice); vm.prank(address(gasUser));
delayedWeth.recover(1 ether); delayedWeth.recover(_amount);
// Verify the WETH was recovered. // Verify the WETH was recovered.
assertEq(address(delayedWeth).balance, 0); assertEq(address(delayedWeth).balance, 0);
assertEq(address(alice).balance, initialBalance + 1 ether); assertEq(address(gasUser).balance, initialBalance + _amount);
} }
/// @dev Tests that recovering WETH by non-owner fails. /// @dev Tests that recovering WETH by non-owner fails.
...@@ -219,6 +231,23 @@ contract DelayedWETH_Recover_Test is DelayedWETH_Init { ...@@ -219,6 +231,23 @@ contract DelayedWETH_Recover_Test is DelayedWETH_Init {
assertEq(address(delayedWeth).balance, 0); assertEq(address(delayedWeth).balance, 0);
assertEq(address(alice).balance, initialBalance + 0.5 ether); assertEq(address(alice).balance, initialBalance + 0.5 ether);
} }
/// @dev Tests that recover reverts when recipient reverts.
function test_recover_whenRecipientReverts_fails() public {
// Set up the reverter.
FallbackReverter reverter = new FallbackReverter();
// Transfer ownership to the reverter.
delayedWeth.transferOwnership(address(reverter));
// Give the contract some WETH to recover.
vm.deal(address(delayedWeth), 1 ether);
// Recover fails.
vm.expectRevert("DelayedWETH: recover failed");
vm.prank(address(reverter));
delayedWeth.recover(1 ether);
}
} }
contract DelayedWETH_Hold_Test is DelayedWETH_Init { contract DelayedWETH_Hold_Test is DelayedWETH_Init {
...@@ -255,3 +284,39 @@ contract DelayedWETH_Hold_Test is DelayedWETH_Init { ...@@ -255,3 +284,39 @@ contract DelayedWETH_Hold_Test is DelayedWETH_Init {
delayedWeth.hold(bob, 1 ether); delayedWeth.hold(bob, 1 ether);
} }
} }
/// @title FallbackGasUser
/// @notice Contract that burns gas in the fallback function.
contract FallbackGasUser {
/// @notice Amount of gas to use in the fallback function.
uint256 public gas;
/// @param _gas Amount of gas to use in the fallback function.
constructor(uint256 _gas) {
gas = _gas;
}
/// @notice Burn gas on fallback;
fallback() external payable {
Burn.gas(gas);
}
/// @notice Burn gas on receive.
receive() external payable {
Burn.gas(gas);
}
}
/// @title FallbackReverter
/// @notice Contract that reverts in the fallback function.
contract FallbackReverter {
/// @notice Revert on fallback.
fallback() external payable {
revert("FallbackReverter: revert");
}
/// @notice Revert on receive.
receive() external payable {
revert("FallbackReverter: revert");
}
}
...@@ -45,7 +45,10 @@ contract FaultDisputeGame_Init is DisputeGameFactory_Init { ...@@ -45,7 +45,10 @@ contract FaultDisputeGame_Init is DisputeGameFactory_Init {
// Set the extra data for the game creation // Set the extra data for the game creation
extraData = abi.encode(l2BlockNumber); extraData = abi.encode(l2BlockNumber);
AlphabetVM _vm = new AlphabetVM(absolutePrestate, new PreimageOracle(0, 0)); // Set preimage oracle challenge period to something arbitrary (4 seconds) just so we can
// actually test the clock extensions later on. This is not a realistic value.
PreimageOracle oracle = new PreimageOracle(0, 4);
AlphabetVM _vm = new AlphabetVM(absolutePrestate, oracle);
// Deploy an implementation of the fault game // Deploy an implementation of the fault game
gameImpl = new FaultDisputeGame({ gameImpl = new FaultDisputeGame({
...@@ -128,12 +131,66 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init { ...@@ -128,12 +131,66 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init {
}); });
} }
/// @dev Tests that the constructor of the `FaultDisputeGame` reverts when the challenge period
// of the preimage oracle being used by the game's VM is too large.
/// @param _challengePeriod The challenge period of the preimage oracle.
function testFuzz_constructor_oracleChallengePeriodTooLarge_reverts(uint256 _challengePeriod) public {
_challengePeriod = bound(_challengePeriod, uint256(type(uint64).max) + 1, type(uint256).max);
PreimageOracle oracle = new PreimageOracle(0, 0);
AlphabetVM alphabetVM = new AlphabetVM(absolutePrestate, oracle);
// PreimageOracle constructor will revert if the challenge period is too large, so we need
// to mock the call to pretend this is a bugged implementation where the challenge period
// is allowed to be too large.
vm.mockCall(
address(oracle), abi.encodeWithSelector(oracle.challengePeriod.selector), abi.encode(_challengePeriod)
);
vm.expectRevert(InvalidChallengePeriod.selector);
new FaultDisputeGame({
_gameType: GAME_TYPE,
_absolutePrestate: absolutePrestate,
_maxGameDepth: 2 ** 3,
_splitDepth: 2 ** 2,
_clockExtension: Duration.wrap(3 hours),
_maxClockDuration: Duration.wrap(3.5 days),
_vm: alphabetVM,
_weth: DelayedWETH(payable(address(0))),
_anchorStateRegistry: IAnchorStateRegistry(address(0)),
_l2ChainId: 10
});
}
/// @dev Tests that the constructor of the `FaultDisputeGame` reverts when the `_splitDepth` /// @dev Tests that the constructor of the `FaultDisputeGame` reverts when the `_splitDepth`
/// parameter is greater than or equal to the `MAX_GAME_DEPTH` /// parameter is greater than or equal to the `MAX_GAME_DEPTH`
function testFuzz_constructor_invalidSplitDepth_reverts(uint256 _splitDepth) public { function testFuzz_constructor_invalidSplitDepth_reverts(uint256 _splitDepth) public {
AlphabetVM alphabetVM = new AlphabetVM(absolutePrestate, new PreimageOracle(0, 0)); AlphabetVM alphabetVM = new AlphabetVM(absolutePrestate, new PreimageOracle(0, 0));
_splitDepth = bound(_splitDepth, 2 ** 3, type(uint256).max); uint256 maxGameDepth = 2 ** 3;
_splitDepth = bound(_splitDepth, maxGameDepth - 1, type(uint256).max);
vm.expectRevert(InvalidSplitDepth.selector);
new FaultDisputeGame({
_gameType: GAME_TYPE,
_absolutePrestate: absolutePrestate,
_maxGameDepth: maxGameDepth,
_splitDepth: _splitDepth,
_clockExtension: Duration.wrap(3 hours),
_maxClockDuration: Duration.wrap(3.5 days),
_vm: alphabetVM,
_weth: DelayedWETH(payable(address(0))),
_anchorStateRegistry: IAnchorStateRegistry(address(0)),
_l2ChainId: 10
});
}
/// @dev Tests that the constructor of the `FaultDisputeGame` reverts when the `_splitDepth`
/// parameter is less than the minimum split depth (currently 2).
function testFuzz_constructor_lowSplitDepth_reverts(uint256 _splitDepth) public {
AlphabetVM alphabetVM = new AlphabetVM(absolutePrestate, new PreimageOracle(0, 0));
uint256 minSplitDepth = 2;
_splitDepth = bound(_splitDepth, 0, minSplitDepth - 1);
vm.expectRevert(InvalidSplitDepth.selector); vm.expectRevert(InvalidSplitDepth.selector);
new FaultDisputeGame({ new FaultDisputeGame({
_gameType: GAME_TYPE, _gameType: GAME_TYPE,
...@@ -149,8 +206,8 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init { ...@@ -149,8 +206,8 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init {
}); });
} }
/// @dev Tests that the constructor of the `FaultDisputeGame` reverts when clock extension is greater than the /// @dev Tests that the constructor of the `FaultDisputeGame` reverts when clock extension * 2 is greater than
/// max clock duration. /// the max clock duration.
function testFuzz_constructor_clockExtensionTooLong_reverts( function testFuzz_constructor_clockExtensionTooLong_reverts(
uint64 _maxClockDuration, uint64 _maxClockDuration,
uint64 _clockExtension uint64 _clockExtension
...@@ -159,8 +216,11 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init { ...@@ -159,8 +216,11 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init {
{ {
AlphabetVM alphabetVM = new AlphabetVM(absolutePrestate, new PreimageOracle(0, 0)); AlphabetVM alphabetVM = new AlphabetVM(absolutePrestate, new PreimageOracle(0, 0));
_maxClockDuration = uint64(bound(_maxClockDuration, 0, type(uint64).max - 1)); // Force the clock extension * 2 to be greater than the max clock duration, but keep things within
_clockExtension = uint64(bound(_clockExtension, _maxClockDuration + 1, type(uint64).max)); // bounds of the uint64 type.
_maxClockDuration = uint64(bound(_maxClockDuration, 0, type(uint64).max / 2 - 1));
_clockExtension = uint64(bound(_clockExtension, _maxClockDuration / 2 + 1, type(uint64).max / 2));
vm.expectRevert(InvalidClockExtension.selector); vm.expectRevert(InvalidClockExtension.selector);
new FaultDisputeGame({ new FaultDisputeGame({
_gameType: GAME_TYPE, _gameType: GAME_TYPE,
...@@ -456,47 +516,126 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init { ...@@ -456,47 +516,126 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init {
assertEq(clock.raw(), LibClock.wrap(Duration.wrap(20), Timestamp.wrap(uint64(block.timestamp))).raw()); assertEq(clock.raw(), LibClock.wrap(Duration.wrap(20), Timestamp.wrap(uint64(block.timestamp))).raw());
} }
/// @notice Static unit test that checks proper clock extension. /// @dev Tests that the standard clock extension is triggered for a move that is not the
function test_move_clockExtensionCorrectness_succeeds() public { /// split depth or the max game depth.
function test_move_standardClockExtension_succeeds() public {
(,,,,,, Clock clock) = gameProxy.claimData(0); (,,,,,, Clock clock) = gameProxy.claimData(0);
assertEq(clock.raw(), LibClock.wrap(Duration.wrap(0), Timestamp.wrap(uint64(block.timestamp))).raw()); assertEq(clock.raw(), LibClock.wrap(Duration.wrap(0), Timestamp.wrap(uint64(block.timestamp))).raw());
uint256 bond;
Claim disputed;
Claim claim = _dummyClaim(); Claim claim = _dummyClaim();
uint256 splitDepth = gameProxy.splitDepth(); uint256 splitDepth = gameProxy.splitDepth();
uint64 halfGameDuration = gameProxy.maxClockDuration().raw(); uint64 halfGameDuration = gameProxy.maxClockDuration().raw();
uint64 clockExtension = gameProxy.clockExtension().raw(); uint64 clockExtension = gameProxy.clockExtension().raw();
// Make an initial attack against the root claim with 1 second left on the clock. The grandchild should be // Warp ahead so that the next move will trigger a clock extension. We warp to the very
// allocated exactly `clockExtension` seconds remaining on their potential clock. // first timestamp where a clock extension should be triggered.
vm.warp(block.timestamp + halfGameDuration - 1 seconds); vm.warp(block.timestamp + halfGameDuration - clockExtension + 1 seconds);
uint256 bond = _getRequiredBond(0);
(,,,, Claim disputed,,) = gameProxy.claimData(0); // Execute a move that should cause a clock extension.
bond = _getRequiredBond(0);
(,,,, disputed,,) = gameProxy.claimData(0);
gameProxy.attack{ value: bond }(disputed, 0, claim); gameProxy.attack{ value: bond }(disputed, 0, claim);
(,,,,,, clock) = gameProxy.claimData(1); (,,,,,, clock) = gameProxy.claimData(1);
// The clock should have been pushed back to the clock extension time.
assertEq(clock.duration().raw(), halfGameDuration - clockExtension); assertEq(clock.duration().raw(), halfGameDuration - clockExtension);
// Warp ahead to the last second of the root claim defender's clock, and bisect all the way down to the move // Warp ahead again so that clock extensions will also trigger for the other team. Here we
// above the `SPLIT_DEPTH`. This warp guarantees that all moves from here on out will have clock extensions. // only warp to the clockExtension time because we'll be warping ahead by one second during
vm.warp(block.timestamp + halfGameDuration - 1 seconds); // each additional move.
vm.warp(block.timestamp + halfGameDuration - clockExtension);
// Work our way down to the split depth.
for (uint256 i = 1; i < splitDepth - 2; i++) { for (uint256 i = 1; i < splitDepth - 2; i++) {
// Warp ahead by one second so that the next move will trigger a clock extension.
vm.warp(block.timestamp + 1 seconds);
// Execute a move that should cause a clock extension.
bond = _getRequiredBond(i); bond = _getRequiredBond(i);
(,,,, disputed,,) = gameProxy.claimData(i); (,,,, disputed,,) = gameProxy.claimData(i);
gameProxy.attack{ value: bond }(disputed, i, claim); gameProxy.attack{ value: bond }(disputed, i, claim);
(,,,,,, clock) = gameProxy.claimData(i + 1);
// The clock should have been pushed back to the clock extension time.
assertEq(clock.duration().raw(), halfGameDuration - clockExtension);
} }
}
// Warp ahead 1 seconds to have `clockExtension - 1 seconds` left on the next move's clock. function test_move_splitDepthClockExtension_succeeds() public {
vm.warp(block.timestamp + 1 seconds); (,,,,,, Clock clock) = gameProxy.claimData(0);
assertEq(clock.raw(), LibClock.wrap(Duration.wrap(0), Timestamp.wrap(uint64(block.timestamp))).raw());
uint256 bond;
Claim disputed;
Claim claim = _dummyClaim();
uint256 splitDepth = gameProxy.splitDepth();
uint64 halfGameDuration = gameProxy.maxClockDuration().raw();
uint64 clockExtension = gameProxy.clockExtension().raw();
// The move above the split depth's grand child is the execution trace bisection root. The grandchild should // Work our way down to the split depth without moving ahead in time, we don't care about
// be allocated `clockExtension * 2` seconds on their potential clock, if currently they have less than // the exact clock here, just don't want take the clock below the clock extension time that
// `clockExtension` seconds left. // we're trying to test here.
for (uint256 i = 0; i < splitDepth - 2; i++) {
bond = _getRequiredBond(i);
(,,,, disputed,,) = gameProxy.claimData(i);
gameProxy.attack{ value: bond }(disputed, i, claim);
}
// Warp ahead to the very first timestamp where a clock extension should be triggered.
vm.warp(block.timestamp + halfGameDuration - clockExtension * 2 + 1 seconds);
// Execute a move that should cause a clock extension.
bond = _getRequiredBond(splitDepth - 2); bond = _getRequiredBond(splitDepth - 2);
(,,,, disputed,,) = gameProxy.claimData(splitDepth - 2); (,,,, disputed,,) = gameProxy.claimData(splitDepth - 2);
gameProxy.attack{ value: bond }(disputed, splitDepth - 2, claim); gameProxy.attack{ value: bond }(disputed, splitDepth - 2, claim);
(,,,,,, clock) = gameProxy.claimData(splitDepth - 1); (,,,,,, clock) = gameProxy.claimData(splitDepth - 1);
// The clock should have been pushed back to the clock extension time.
assertEq(clock.duration().raw(), halfGameDuration - clockExtension * 2); assertEq(clock.duration().raw(), halfGameDuration - clockExtension * 2);
} }
function test_move_maxGameDepthClockExtension_succeeds() public {
(,,,,,, Clock clock) = gameProxy.claimData(0);
assertEq(clock.raw(), LibClock.wrap(Duration.wrap(0), Timestamp.wrap(uint64(block.timestamp))).raw());
uint256 bond;
Claim disputed;
Claim claim = _dummyClaim();
uint256 splitDepth = gameProxy.splitDepth();
uint64 halfGameDuration = gameProxy.maxClockDuration().raw();
uint64 clockExtension = gameProxy.clockExtension().raw();
// Work our way down to the split depth without moving ahead in time, we don't care about
// the exact clock here, just don't want take the clock below the clock extension time that
// we're trying to test here.
for (uint256 i = 0; i < gameProxy.maxGameDepth() - 2; i++) {
bond = _getRequiredBond(i);
(,,,, disputed,,) = gameProxy.claimData(i);
gameProxy.attack{ value: bond }(disputed, i, claim);
// Change the claim status when we're crossing the split depth.
if (i == splitDepth - 2) {
claim = _changeClaimStatus(claim, VMStatuses.PANIC);
}
}
// Warp ahead to the very first timestamp where a clock extension should be triggered.
vm.warp(block.timestamp + halfGameDuration - (clockExtension + gameProxy.vm().oracle().challengePeriod()) + 1);
// Execute a move that should cause a clock extension.
bond = _getRequiredBond(gameProxy.maxGameDepth() - 2);
(,,,, disputed,,) = gameProxy.claimData(gameProxy.maxGameDepth() - 2);
gameProxy.attack{ value: bond }(disputed, gameProxy.maxGameDepth() - 2, claim);
(,,,,,, clock) = gameProxy.claimData(gameProxy.maxGameDepth() - 1);
// The clock should have been pushed back to the clock extension time.
assertEq(
clock.duration().raw(), halfGameDuration - (clockExtension + gameProxy.vm().oracle().challengePeriod())
);
}
/// @dev Tests that an identical claim cannot be made twice. The duplicate claim attempt should /// @dev Tests that an identical claim cannot be made twice. The duplicate claim attempt should
/// revert with the `ClaimAlreadyExists` error. /// revert with the `ClaimAlreadyExists` error.
function test_move_duplicateClaim_reverts() public { function test_move_duplicateClaim_reverts() public {
...@@ -1685,6 +1824,70 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init { ...@@ -1685,6 +1824,70 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init {
} }
} }
/// @dev Tests that the L2 block number claim is favored over the bisected-to block when adding data
///
function test_addLocalData_l2BlockNumberExtension_succeeds() public {
// Deploy a new dispute game with a L2 block number claim of 8. This is directly in the middle of
// the leaves in our output bisection test tree, at SPLIT_DEPTH = 2 ** 2
FaultDisputeGame game = FaultDisputeGame(
address(disputeGameFactory.create(GAME_TYPE, Claim.wrap(bytes32(uint256(0xFF))), abi.encode(uint256(8))))
);
// Get a claim below the split depth so that we can add local data for an execution trace subgame.
{
Claim disputed;
Position parent;
Position pos;
for (uint256 i; i < 4; i++) {
(,,,,, parent,) = game.claimData(i);
pos = parent.move(true);
uint256 bond = game.getRequiredBond(pos);
(,,,, disputed,,) = game.claimData(i);
if (i == 0) {
game.attack{ value: bond }(disputed, i, Claim.wrap(bytes32(i)));
} else {
game.defend{ value: bond }(disputed, i, Claim.wrap(bytes32(i)));
}
}
(,,,,, parent,) = game.claimData(4);
pos = parent.move(true);
uint256 lastBond = game.getRequiredBond(pos);
(,,,, disputed,,) = game.claimData(4);
game.defend{ value: lastBond }(disputed, 4, _changeClaimStatus(ROOT_CLAIM, VMStatuses.INVALID));
}
// Expected start/disputed claims
bytes32 startingClaim = bytes32(uint256(3));
Position startingPos = LibPosition.wrap(4, 14);
bytes32 disputedClaim = bytes32(uint256(0xFF));
Position disputedPos = LibPosition.wrap(0, 0);
// Expected local data. This should be `l2BlockNumber`, and not the actual bisected-to block,
// as we choose the minimum between the two.
bytes32 expectedNumber = bytes32(uint256(8 << 0xC0));
uint256 expectedLen = 8;
uint256 l2NumberIdent = LocalPreimageKey.DISPUTED_L2_BLOCK_NUMBER;
// Compute the preimage key for the local data
bytes32 localContext = keccak256(abi.encode(startingClaim, startingPos, disputedClaim, disputedPos));
bytes32 rawKey = keccak256(abi.encode(l2NumberIdent | (1 << 248), address(game), localContext));
bytes32 key = bytes32((uint256(rawKey) & ~uint256(0xFF << 248)) | (1 << 248));
IPreimageOracle oracle = IPreimageOracle(address(game.vm().oracle()));
game.addLocalData(l2NumberIdent, 5, 0);
(bytes32 dat, uint256 datLen) = oracle.readPreimage(key, 0);
assertEq(dat >> 0xC0, bytes32(expectedLen));
assertEq(datLen, expectedLen + 8);
game.addLocalData(l2NumberIdent, 5, 8);
(dat, datLen) = oracle.readPreimage(key, 8);
assertEq(dat, expectedNumber);
assertEq(datLen, expectedLen);
}
/// @dev Static unit test asserting that resolveClaim isn't possible if there's time /// @dev Static unit test asserting that resolveClaim isn't possible if there's time
/// left for a counter. /// left for a counter.
function test_resolution_lastSecondDisputes_succeeds() public { function test_resolution_lastSecondDisputes_succeeds() public {
......
...@@ -13,9 +13,9 @@ contract DeploymentSummary is DeploymentSummaryCode { ...@@ -13,9 +13,9 @@ contract DeploymentSummary is DeploymentSummaryCode {
Vm private constant vm = Vm(VM_ADDRESS); Vm private constant vm = Vm(VM_ADDRESS);
address internal constant addressManagerAddress = 0x50EEf481cae4250d252Ae577A09bF514f224C6C4; address internal constant addressManagerAddress = 0x50EEf481cae4250d252Ae577A09bF514f224C6C4;
address internal constant anchorStateRegistryAddress = 0xE2a80256d1dAFe06683F231F8e9561639Aa0e9b9; address internal constant anchorStateRegistryAddress = 0x2d0983b9B70A282D344520a78CC3009bd0656aB4;
address internal constant anchorStateRegistryProxyAddress = 0xd6EAF4c146261653EE059077B78ED088Add54309; address internal constant anchorStateRegistryProxyAddress = 0x970670459734a83899773A0fd45941B5afC1200e;
address internal constant delayedWETHAddress = 0x49BBFf1629824A1e7993Ab5c17AFa45D24AB28c9; address internal constant delayedWETHAddress = 0x07F69b19532476c6Cd03056D6BC3F1b110Ab7538;
address internal constant delayedWETHProxyAddress = 0xEF179756ea6525AFade217cA5aB0b1b5CfE0fd92; address internal constant delayedWETHProxyAddress = 0xEF179756ea6525AFade217cA5aB0b1b5CfE0fd92;
address internal constant disputeGameFactoryAddress = 0x20B168142354Cee65a32f6D8cf3033E592299765; address internal constant disputeGameFactoryAddress = 0x20B168142354Cee65a32f6D8cf3033E592299765;
address internal constant disputeGameFactoryProxyAddress = 0x5207CfA0166E8de0FCdFd78B4d17b68587bE306d; address internal constant disputeGameFactoryProxyAddress = 0x5207CfA0166E8de0FCdFd78B4d17b68587bE306d;
...@@ -27,13 +27,14 @@ contract DeploymentSummary is DeploymentSummaryCode { ...@@ -27,13 +27,14 @@ contract DeploymentSummary is DeploymentSummaryCode {
address internal constant l1StandardBridgeProxyAddress = 0x20A42a5a785622c6Ba2576B2D6e924aA82BFA11D; address internal constant l1StandardBridgeProxyAddress = 0x20A42a5a785622c6Ba2576B2D6e924aA82BFA11D;
address internal constant l2OutputOracleAddress = 0x19652082F846171168Daf378C4fD3ee85a0D4A60; address internal constant l2OutputOracleAddress = 0x19652082F846171168Daf378C4fD3ee85a0D4A60;
address internal constant l2OutputOracleProxyAddress = 0x39Af23E00F1e662025aA01b0cEdA19542B78DF99; address internal constant l2OutputOracleProxyAddress = 0x39Af23E00F1e662025aA01b0cEdA19542B78DF99;
address internal constant mipsAddress = 0xB3A0348310a0ff78E5FbDB7f14BB7d3e02d40773; address internal constant mipsAddress = 0xaB5b145Bd477C9Bf42F3Ee3f0d988Abef3a27679;
address internal constant optimismMintableERC20FactoryAddress = 0x39Aea2Dd53f2d01c15877aCc2791af6BDD7aD567; address internal constant optimismMintableERC20FactoryAddress = 0x39Aea2Dd53f2d01c15877aCc2791af6BDD7aD567;
address internal constant optimismMintableERC20FactoryProxyAddress = 0xc7B87b2b892EA5C3CfF47168881FE168C00377FB; address internal constant optimismMintableERC20FactoryProxyAddress = 0xc7B87b2b892EA5C3CfF47168881FE168C00377FB;
address internal constant optimismPortalAddress = 0xbdD90485FCbcac869D5b5752179815a3103d8131; address internal constant optimismPortalAddress = 0xbdD90485FCbcac869D5b5752179815a3103d8131;
address internal constant optimismPortal2Address = 0xae5DadFc48928543f706a9E6Ce25c682aaD2b63b; address internal constant optimismPortal2Address = 0xae5DadFc48928543f706a9E6Ce25c682aaD2b63b;
address internal constant optimismPortalProxyAddress = 0x1c23A6d89F95ef3148BCDA8E242cAb145bf9c0E4; address internal constant optimismPortalProxyAddress = 0x1c23A6d89F95ef3148BCDA8E242cAb145bf9c0E4;
address internal constant preimageOracleAddress = 0x3bd7E801E51d48c5d94Ea68e8B801DFFC275De75; address internal constant permissionedDelayedWETHProxyAddress = 0xd6EAF4c146261653EE059077B78ED088Add54309;
address internal constant preimageOracleAddress = 0x5A996D7C1b5De7C21121F06D99ADFa088d4b779e;
address internal constant protocolVersionsAddress = 0xfbfD64a6C0257F613feFCe050Aa30ecC3E3d7C3F; address internal constant protocolVersionsAddress = 0xfbfD64a6C0257F613feFCe050Aa30ecC3E3d7C3F;
address internal constant protocolVersionsProxyAddress = 0x4C52a6277b1B84121b3072C0c92b6Be0b7CC10F1; address internal constant protocolVersionsProxyAddress = 0x4C52a6277b1B84121b3072C0c92b6Be0b7CC10F1;
address internal constant proxyAdminAddress = 0x62c20Aa1e0272312BC100b4e23B4DC1Ed96dD7D1; address internal constant proxyAdminAddress = 0x62c20Aa1e0272312BC100b4e23B4DC1Ed96dD7D1;
...@@ -44,8 +45,8 @@ contract DeploymentSummary is DeploymentSummaryCode { ...@@ -44,8 +45,8 @@ contract DeploymentSummary is DeploymentSummaryCode {
address internal constant systemConfigAddress = 0x67866A5052E5302aaD08e9f352331fd8622eB6DC; address internal constant systemConfigAddress = 0x67866A5052E5302aaD08e9f352331fd8622eB6DC;
address internal constant systemConfigProxyAddress = 0x0c8b5822b6e02CDa722174F19A1439A7495a3fA6; address internal constant systemConfigProxyAddress = 0x0c8b5822b6e02CDa722174F19A1439A7495a3fA6;
address internal constant systemOwnerSafeAddress = 0x7C0c8a15773ED7B50E7c738D1aF4c5e3a2b210BD; address internal constant systemOwnerSafeAddress = 0x7C0c8a15773ED7B50E7c738D1aF4c5e3a2b210BD;
address internal constant acc32Address = 0x357A483a8923686E7fA454Ee93bbc11aFB114743;
address internal constant acc33Address = 0xb6b1579AA54e2F61e621a40d5F2704D717B3544F; address internal constant acc33Address = 0xb6b1579AA54e2F61e621a40d5F2704D717B3544F;
address internal constant acc34Address = 0x59B99034FBdd5E554661a2100cB2b6b7C5d495F8;
function recreateDeployment() public { function recreateDeployment() public {
bytes32 slot; bytes32 slot;
...@@ -200,6 +201,10 @@ contract DeploymentSummary is DeploymentSummaryCode { ...@@ -200,6 +201,10 @@ contract DeploymentSummary is DeploymentSummaryCode {
slot = hex"b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103"; slot = hex"b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103";
value = hex"00000000000000000000000062c20aa1e0272312bc100b4e23b4dc1ed96dd7d1"; value = hex"00000000000000000000000062c20aa1e0272312bc100b4e23b4dc1ed96dd7d1";
vm.store(delayedWETHProxyAddress, slot, value); vm.store(delayedWETHProxyAddress, slot, value);
vm.etch(permissionedDelayedWETHProxyAddress, permissionedDelayedWETHProxyCode);
slot = hex"b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103";
value = hex"00000000000000000000000062c20aa1e0272312bc100b4e23b4dc1ed96dd7d1";
vm.store(permissionedDelayedWETHProxyAddress, slot, value);
vm.etch(anchorStateRegistryProxyAddress, anchorStateRegistryProxyCode); vm.etch(anchorStateRegistryProxyAddress, anchorStateRegistryProxyCode);
slot = hex"b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103"; slot = hex"b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103";
value = hex"00000000000000000000000062c20aa1e0272312bc100b4e23b4dc1ed96dd7d1"; value = hex"00000000000000000000000062c20aa1e0272312bc100b4e23b4dc1ed96dd7d1";
...@@ -419,13 +424,7 @@ contract DeploymentSummary is DeploymentSummaryCode { ...@@ -419,13 +424,7 @@ contract DeploymentSummary is DeploymentSummaryCode {
vm.etch(mipsAddress, mipsCode); vm.etch(mipsAddress, mipsCode);
vm.etch(anchorStateRegistryAddress, anchorStateRegistryCode); vm.etch(anchorStateRegistryAddress, anchorStateRegistryCode);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000"; slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001"; value = hex"00000000000000000000000000000000000000000000000000000000000000ff";
vm.store(anchorStateRegistryAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000101";
vm.store(anchorStateRegistryAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001";
vm.store(anchorStateRegistryAddress, slot, value); vm.store(anchorStateRegistryAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000005"; slot = hex"0000000000000000000000000000000000000000000000000000000000000005";
value = hex"0000000000000000000000000000000000000000000000000000000000000003"; value = hex"0000000000000000000000000000000000000000000000000000000000000003";
...@@ -698,7 +697,7 @@ contract DeploymentSummary is DeploymentSummaryCode { ...@@ -698,7 +697,7 @@ contract DeploymentSummary is DeploymentSummaryCode {
value = hex"000000000000000000000000000000000000000000000000000000000000000e"; value = hex"000000000000000000000000000000000000000000000000000000000000000e";
vm.store(systemOwnerSafeAddress, slot, value); vm.store(systemOwnerSafeAddress, slot, value);
slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"; slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
value = hex"00000000000000000000000049bbff1629824a1e7993ab5c17afa45d24ab28c9"; value = hex"00000000000000000000000007f69b19532476c6cd03056d6bc3f1b110ab7538";
vm.store(delayedWETHProxyAddress, slot, value); vm.store(delayedWETHProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000"; slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001"; value = hex"0000000000000000000000000000000000000000000000000000000000000001";
...@@ -722,7 +721,31 @@ contract DeploymentSummary is DeploymentSummaryCode { ...@@ -722,7 +721,31 @@ contract DeploymentSummary is DeploymentSummaryCode {
value = hex"000000000000000000000000000000000000000000000000000000000000000f"; value = hex"000000000000000000000000000000000000000000000000000000000000000f";
vm.store(systemOwnerSafeAddress, slot, value); vm.store(systemOwnerSafeAddress, slot, value);
slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"; slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
value = hex"000000000000000000000000e2a80256d1dafe06683f231f8e9561639aa0e9b9"; value = hex"00000000000000000000000007f69b19532476c6cd03056d6bc3f1b110ab7538";
vm.store(permissionedDelayedWETHProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001";
vm.store(permissionedDelayedWETHProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000101";
vm.store(permissionedDelayedWETHProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000033";
value = hex"00000000000000000000000062c20aa1e0272312bc100b4e23b4dc1ed96dd7d1";
vm.store(permissionedDelayedWETHProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000033";
value = hex"0000000000000000000000001804c8ab1f12e6bbf3894d4083f33e07309d1f38";
vm.store(permissionedDelayedWETHProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000068";
value = hex"0000000000000000000000004f559f30f5eb88d635fde1548c4267db8fab0351";
vm.store(permissionedDelayedWETHProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001";
vm.store(permissionedDelayedWETHProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000005";
value = hex"0000000000000000000000000000000000000000000000000000000000000010";
vm.store(systemOwnerSafeAddress, slot, value);
slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
value = hex"0000000000000000000000002d0983b9b70a282d344520a78cc3009bd0656ab4";
vm.store(anchorStateRegistryProxyAddress, slot, value); vm.store(anchorStateRegistryProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000"; slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001"; value = hex"0000000000000000000000000000000000000000000000000000000000000001";
...@@ -745,16 +768,19 @@ contract DeploymentSummary is DeploymentSummaryCode { ...@@ -745,16 +768,19 @@ contract DeploymentSummary is DeploymentSummaryCode {
slot = hex"1d32deecea32fd1365d10df47fc6666a05871102e61a115a5c569bca7e5de14d"; slot = hex"1d32deecea32fd1365d10df47fc6666a05871102e61a115a5c569bca7e5de14d";
value = hex"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; value = hex"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
vm.store(anchorStateRegistryProxyAddress, slot, value); vm.store(anchorStateRegistryProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000002";
value = hex"0000000000000000000000004f559f30f5eb88d635fde1548c4267db8fab0351";
vm.store(anchorStateRegistryProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000"; slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001"; value = hex"0000000000000000000000000000000000000000000000000000000000000001";
vm.store(anchorStateRegistryProxyAddress, slot, value); vm.store(anchorStateRegistryProxyAddress, slot, value);
vm.etch(acc32Address, acc32Code); vm.etch(acc33Address, acc33Code);
slot = hex"ffdfc1249c027f9191656349feb0761381bb32c9f557e01f419fd08754bf5a1b"; slot = hex"ffdfc1249c027f9191656349feb0761381bb32c9f557e01f419fd08754bf5a1b";
value = hex"000000000000000000000000357a483a8923686e7fa454ee93bbc11afb114743"; value = hex"000000000000000000000000b6b1579aa54e2f61e621a40d5f2704d717b3544f";
vm.store(disputeGameFactoryProxyAddress, slot, value); vm.store(disputeGameFactoryProxyAddress, slot, value);
vm.etch(acc33Address, acc33Code); vm.etch(acc34Address, acc34Code);
slot = hex"4d5a9bd2e41301728d41c8e705190becb4e74abe869f75bdb405b63716a35f9e"; slot = hex"4d5a9bd2e41301728d41c8e705190becb4e74abe869f75bdb405b63716a35f9e";
value = hex"000000000000000000000000b6b1579aa54e2f61e621a40d5f2704d717b3544f"; value = hex"00000000000000000000000059b99034fbdd5e554661a2100cb2b6b7c5d495f8";
vm.store(disputeGameFactoryProxyAddress, slot, value); vm.store(disputeGameFactoryProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000033"; slot = hex"0000000000000000000000000000000000000000000000000000000000000033";
value = hex"0000000000000000000000007c0c8a15773ed7b50e7c738d1af4c5e3a2b210bd"; value = hex"0000000000000000000000007c0c8a15773ed7b50e7c738d1af4c5e3a2b210bd";
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -13,9 +13,9 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode { ...@@ -13,9 +13,9 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
Vm private constant vm = Vm(VM_ADDRESS); Vm private constant vm = Vm(VM_ADDRESS);
address internal constant addressManagerAddress = 0x50EEf481cae4250d252Ae577A09bF514f224C6C4; address internal constant addressManagerAddress = 0x50EEf481cae4250d252Ae577A09bF514f224C6C4;
address internal constant anchorStateRegistryAddress = 0xE2a80256d1dAFe06683F231F8e9561639Aa0e9b9; address internal constant anchorStateRegistryAddress = 0x2d0983b9B70A282D344520a78CC3009bd0656aB4;
address internal constant anchorStateRegistryProxyAddress = 0xd6EAF4c146261653EE059077B78ED088Add54309; address internal constant anchorStateRegistryProxyAddress = 0x970670459734a83899773A0fd45941B5afC1200e;
address internal constant delayedWETHAddress = 0x49BBFf1629824A1e7993Ab5c17AFa45D24AB28c9; address internal constant delayedWETHAddress = 0x07F69b19532476c6Cd03056D6BC3F1b110Ab7538;
address internal constant delayedWETHProxyAddress = 0xEF179756ea6525AFade217cA5aB0b1b5CfE0fd92; address internal constant delayedWETHProxyAddress = 0xEF179756ea6525AFade217cA5aB0b1b5CfE0fd92;
address internal constant disputeGameFactoryAddress = 0x20B168142354Cee65a32f6D8cf3033E592299765; address internal constant disputeGameFactoryAddress = 0x20B168142354Cee65a32f6D8cf3033E592299765;
address internal constant disputeGameFactoryProxyAddress = 0x5207CfA0166E8de0FCdFd78B4d17b68587bE306d; address internal constant disputeGameFactoryProxyAddress = 0x5207CfA0166E8de0FCdFd78B4d17b68587bE306d;
...@@ -27,13 +27,14 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode { ...@@ -27,13 +27,14 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
address internal constant l1StandardBridgeProxyAddress = 0x20A42a5a785622c6Ba2576B2D6e924aA82BFA11D; address internal constant l1StandardBridgeProxyAddress = 0x20A42a5a785622c6Ba2576B2D6e924aA82BFA11D;
address internal constant l2OutputOracleAddress = 0x19652082F846171168Daf378C4fD3ee85a0D4A60; address internal constant l2OutputOracleAddress = 0x19652082F846171168Daf378C4fD3ee85a0D4A60;
address internal constant l2OutputOracleProxyAddress = 0x39Af23E00F1e662025aA01b0cEdA19542B78DF99; address internal constant l2OutputOracleProxyAddress = 0x39Af23E00F1e662025aA01b0cEdA19542B78DF99;
address internal constant mipsAddress = 0xB3A0348310a0ff78E5FbDB7f14BB7d3e02d40773; address internal constant mipsAddress = 0xaB5b145Bd477C9Bf42F3Ee3f0d988Abef3a27679;
address internal constant optimismMintableERC20FactoryAddress = 0x39Aea2Dd53f2d01c15877aCc2791af6BDD7aD567; address internal constant optimismMintableERC20FactoryAddress = 0x39Aea2Dd53f2d01c15877aCc2791af6BDD7aD567;
address internal constant optimismMintableERC20FactoryProxyAddress = 0xc7B87b2b892EA5C3CfF47168881FE168C00377FB; address internal constant optimismMintableERC20FactoryProxyAddress = 0xc7B87b2b892EA5C3CfF47168881FE168C00377FB;
address internal constant optimismPortalAddress = 0xbdD90485FCbcac869D5b5752179815a3103d8131; address internal constant optimismPortalAddress = 0xbdD90485FCbcac869D5b5752179815a3103d8131;
address internal constant optimismPortal2Address = 0xae5DadFc48928543f706a9E6Ce25c682aaD2b63b; address internal constant optimismPortal2Address = 0xae5DadFc48928543f706a9E6Ce25c682aaD2b63b;
address internal constant optimismPortalProxyAddress = 0x1c23A6d89F95ef3148BCDA8E242cAb145bf9c0E4; address internal constant optimismPortalProxyAddress = 0x1c23A6d89F95ef3148BCDA8E242cAb145bf9c0E4;
address internal constant preimageOracleAddress = 0x3bd7E801E51d48c5d94Ea68e8B801DFFC275De75; address internal constant permissionedDelayedWETHProxyAddress = 0xd6EAF4c146261653EE059077B78ED088Add54309;
address internal constant preimageOracleAddress = 0x5A996D7C1b5De7C21121F06D99ADFa088d4b779e;
address internal constant protocolVersionsAddress = 0xfbfD64a6C0257F613feFCe050Aa30ecC3E3d7C3F; address internal constant protocolVersionsAddress = 0xfbfD64a6C0257F613feFCe050Aa30ecC3E3d7C3F;
address internal constant protocolVersionsProxyAddress = 0x4C52a6277b1B84121b3072C0c92b6Be0b7CC10F1; address internal constant protocolVersionsProxyAddress = 0x4C52a6277b1B84121b3072C0c92b6Be0b7CC10F1;
address internal constant proxyAdminAddress = 0x62c20Aa1e0272312BC100b4e23B4DC1Ed96dD7D1; address internal constant proxyAdminAddress = 0x62c20Aa1e0272312BC100b4e23B4DC1Ed96dD7D1;
...@@ -44,8 +45,8 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode { ...@@ -44,8 +45,8 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
address internal constant systemConfigAddress = 0x67866A5052E5302aaD08e9f352331fd8622eB6DC; address internal constant systemConfigAddress = 0x67866A5052E5302aaD08e9f352331fd8622eB6DC;
address internal constant systemConfigProxyAddress = 0x0c8b5822b6e02CDa722174F19A1439A7495a3fA6; address internal constant systemConfigProxyAddress = 0x0c8b5822b6e02CDa722174F19A1439A7495a3fA6;
address internal constant systemOwnerSafeAddress = 0x7C0c8a15773ED7B50E7c738D1aF4c5e3a2b210BD; address internal constant systemOwnerSafeAddress = 0x7C0c8a15773ED7B50E7c738D1aF4c5e3a2b210BD;
address internal constant acc32Address = 0x357A483a8923686E7fA454Ee93bbc11aFB114743;
address internal constant acc33Address = 0xb6b1579AA54e2F61e621a40d5F2704D717B3544F; address internal constant acc33Address = 0xb6b1579AA54e2F61e621a40d5F2704D717B3544F;
address internal constant acc34Address = 0x59B99034FBdd5E554661a2100cB2b6b7C5d495F8;
function recreateDeployment() public { function recreateDeployment() public {
bytes32 slot; bytes32 slot;
...@@ -200,6 +201,10 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode { ...@@ -200,6 +201,10 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
slot = hex"b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103"; slot = hex"b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103";
value = hex"00000000000000000000000062c20aa1e0272312bc100b4e23b4dc1ed96dd7d1"; value = hex"00000000000000000000000062c20aa1e0272312bc100b4e23b4dc1ed96dd7d1";
vm.store(delayedWETHProxyAddress, slot, value); vm.store(delayedWETHProxyAddress, slot, value);
vm.etch(permissionedDelayedWETHProxyAddress, permissionedDelayedWETHProxyCode);
slot = hex"b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103";
value = hex"00000000000000000000000062c20aa1e0272312bc100b4e23b4dc1ed96dd7d1";
vm.store(permissionedDelayedWETHProxyAddress, slot, value);
vm.etch(anchorStateRegistryProxyAddress, anchorStateRegistryProxyCode); vm.etch(anchorStateRegistryProxyAddress, anchorStateRegistryProxyCode);
slot = hex"b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103"; slot = hex"b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103";
value = hex"00000000000000000000000062c20aa1e0272312bc100b4e23b4dc1ed96dd7d1"; value = hex"00000000000000000000000062c20aa1e0272312bc100b4e23b4dc1ed96dd7d1";
...@@ -419,13 +424,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode { ...@@ -419,13 +424,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
vm.etch(mipsAddress, mipsCode); vm.etch(mipsAddress, mipsCode);
vm.etch(anchorStateRegistryAddress, anchorStateRegistryCode); vm.etch(anchorStateRegistryAddress, anchorStateRegistryCode);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000"; slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001"; value = hex"00000000000000000000000000000000000000000000000000000000000000ff";
vm.store(anchorStateRegistryAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000101";
vm.store(anchorStateRegistryAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001";
vm.store(anchorStateRegistryAddress, slot, value); vm.store(anchorStateRegistryAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000005"; slot = hex"0000000000000000000000000000000000000000000000000000000000000005";
value = hex"0000000000000000000000000000000000000000000000000000000000000003"; value = hex"0000000000000000000000000000000000000000000000000000000000000003";
...@@ -701,7 +700,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode { ...@@ -701,7 +700,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
value = hex"000000000000000000000000000000000000000000000000000000000000000e"; value = hex"000000000000000000000000000000000000000000000000000000000000000e";
vm.store(systemOwnerSafeAddress, slot, value); vm.store(systemOwnerSafeAddress, slot, value);
slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"; slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
value = hex"00000000000000000000000049bbff1629824a1e7993ab5c17afa45d24ab28c9"; value = hex"00000000000000000000000007f69b19532476c6cd03056d6bc3f1b110ab7538";
vm.store(delayedWETHProxyAddress, slot, value); vm.store(delayedWETHProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000"; slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001"; value = hex"0000000000000000000000000000000000000000000000000000000000000001";
...@@ -725,7 +724,31 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode { ...@@ -725,7 +724,31 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
value = hex"000000000000000000000000000000000000000000000000000000000000000f"; value = hex"000000000000000000000000000000000000000000000000000000000000000f";
vm.store(systemOwnerSafeAddress, slot, value); vm.store(systemOwnerSafeAddress, slot, value);
slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"; slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
value = hex"000000000000000000000000e2a80256d1dafe06683f231f8e9561639aa0e9b9"; value = hex"00000000000000000000000007f69b19532476c6cd03056d6bc3f1b110ab7538";
vm.store(permissionedDelayedWETHProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001";
vm.store(permissionedDelayedWETHProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000101";
vm.store(permissionedDelayedWETHProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000033";
value = hex"00000000000000000000000062c20aa1e0272312bc100b4e23b4dc1ed96dd7d1";
vm.store(permissionedDelayedWETHProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000033";
value = hex"0000000000000000000000001804c8ab1f12e6bbf3894d4083f33e07309d1f38";
vm.store(permissionedDelayedWETHProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000068";
value = hex"0000000000000000000000004f559f30f5eb88d635fde1548c4267db8fab0351";
vm.store(permissionedDelayedWETHProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001";
vm.store(permissionedDelayedWETHProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000005";
value = hex"0000000000000000000000000000000000000000000000000000000000000010";
vm.store(systemOwnerSafeAddress, slot, value);
slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
value = hex"0000000000000000000000002d0983b9b70a282d344520a78cc3009bd0656ab4";
vm.store(anchorStateRegistryProxyAddress, slot, value); vm.store(anchorStateRegistryProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000"; slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001"; value = hex"0000000000000000000000000000000000000000000000000000000000000001";
...@@ -748,16 +771,19 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode { ...@@ -748,16 +771,19 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
slot = hex"1d32deecea32fd1365d10df47fc6666a05871102e61a115a5c569bca7e5de14d"; slot = hex"1d32deecea32fd1365d10df47fc6666a05871102e61a115a5c569bca7e5de14d";
value = hex"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; value = hex"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
vm.store(anchorStateRegistryProxyAddress, slot, value); vm.store(anchorStateRegistryProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000002";
value = hex"0000000000000000000000004f559f30f5eb88d635fde1548c4267db8fab0351";
vm.store(anchorStateRegistryProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000"; slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001"; value = hex"0000000000000000000000000000000000000000000000000000000000000001";
vm.store(anchorStateRegistryProxyAddress, slot, value); vm.store(anchorStateRegistryProxyAddress, slot, value);
vm.etch(acc32Address, acc32Code); vm.etch(acc33Address, acc33Code);
slot = hex"ffdfc1249c027f9191656349feb0761381bb32c9f557e01f419fd08754bf5a1b"; slot = hex"ffdfc1249c027f9191656349feb0761381bb32c9f557e01f419fd08754bf5a1b";
value = hex"000000000000000000000000357a483a8923686e7fa454ee93bbc11afb114743"; value = hex"000000000000000000000000b6b1579aa54e2f61e621a40d5f2704d717b3544f";
vm.store(disputeGameFactoryProxyAddress, slot, value); vm.store(disputeGameFactoryProxyAddress, slot, value);
vm.etch(acc33Address, acc33Code); vm.etch(acc34Address, acc34Code);
slot = hex"4d5a9bd2e41301728d41c8e705190becb4e74abe869f75bdb405b63716a35f9e"; slot = hex"4d5a9bd2e41301728d41c8e705190becb4e74abe869f75bdb405b63716a35f9e";
value = hex"000000000000000000000000b6b1579aa54e2f61e621a40d5f2704d717b3544f"; value = hex"00000000000000000000000059b99034fbdd5e554661a2100cb2b6b7c5d495f8";
vm.store(disputeGameFactoryProxyAddress, slot, value); vm.store(disputeGameFactoryProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000033"; slot = hex"0000000000000000000000000000000000000000000000000000000000000033";
value = hex"0000000000000000000000007c0c8a15773ed7b50e7c738d1af4c5e3a2b210bd"; value = hex"0000000000000000000000007c0c8a15773ed7b50e7c738d1af4c5e3a2b210bd";
......
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