Skip to content

Commit 52d0e60

Browse files
authored
cannon: Support F_GETFD cmd to fcntl (ethereum-optimism#12050)
* cannon: Support F_GETFD cmd to fcntl. * cannon: Update fuzz test expectations. * cannon: Update MIPS.t.sol * cannon: Introduce a new state version for supporting get_fd. Switches singlethreaded prestate to use .bin.gz instead of json since it now needs to detect the new state version. * cannon: Don't override the cannon version. * Update semver-lock. * cannon: Update tests to detect old versions but only check writing and parsing for the currently supported versions. * cannon: Load old version from cannon docker image * cannon: Improve logging. * cannon: Restore cannon version arg. * Fix contrac semvers. * cannon: Rename singlethreaded-getfd to just singlethreaded-2. We could just go to using the state version number directly, but particularly the difference between singlethreaded and multithreaded feels useful to keep. * cannon: Fix comment. * Update semver again.
1 parent 3aabfe6 commit 52d0e60

File tree

31 files changed

+199
-70
lines changed

31 files changed

+199
-70
lines changed

.circleci/config.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1058,7 +1058,7 @@ jobs:
10581058
key: cannon-prestate-{{ checksum "./cannon/bin/cannon" }}-{{ checksum "op-program/bin/op-program-client.elf" }}
10591059
name: Save Cannon prestate to cache
10601060
paths:
1061-
- "op-program/bin/prestate.json"
1061+
- "op-program/bin/prestate.bin.gz"
10621062
- "op-program/bin/meta.json"
10631063
- "op-program/bin/prestate-proof.json"
10641064
- run:
@@ -1079,7 +1079,7 @@ jobs:
10791079
- persist_to_workspace:
10801080
root: .
10811081
paths:
1082-
- "op-program/bin/prestate.json"
1082+
- "op-program/bin/prestate.bin.gz"
10831083
- "op-program/bin/meta.json"
10841084
- "op-program/bin/prestate-proof.json"
10851085

Makefile

+3-3
Original file line numberDiff line numberDiff line change
@@ -134,16 +134,16 @@ reproducible-prestate: ## Builds reproducible-prestate binary
134134
.PHONY: reproducible-prestate
135135

136136
# Include any files required for the devnet to build and run.
137-
DEVNET_CANNON_PRESTATE_FILES := op-program/bin/prestate-proof.json op-program/bin/prestate.json op-program/bin/prestate-proof-mt.json op-program/bin/prestate-mt.bin.gz
137+
DEVNET_CANNON_PRESTATE_FILES := op-program/bin/prestate-proof.json op-program/bin/prestate.bin.gz op-program/bin/prestate-proof-mt.json op-program/bin/prestate-mt.bin.gz
138138

139139

140140
$(DEVNET_CANNON_PRESTATE_FILES):
141141
make cannon-prestate
142142
make cannon-prestate-mt
143143

144144
cannon-prestate: op-program cannon ## Generates prestate using cannon and op-program
145-
./cannon/bin/cannon load-elf --type singlethreaded --path op-program/bin/op-program-client.elf --out op-program/bin/prestate.json --meta op-program/bin/meta.json
146-
./cannon/bin/cannon run --proof-at '=0' --stop-at '=1' --input op-program/bin/prestate.json --meta op-program/bin/meta.json --proof-fmt 'op-program/bin/%d.json' --output ""
145+
./cannon/bin/cannon load-elf --type singlethreaded-2 --path op-program/bin/op-program-client.elf --out op-program/bin/prestate.bin.gz --meta op-program/bin/meta.json
146+
./cannon/bin/cannon run --proof-at '=0' --stop-at '=1' --input op-program/bin/prestate.bin.gz --meta op-program/bin/meta.json --proof-fmt 'op-program/bin/%d.json' --output ""
147147
mv op-program/bin/0.json op-program/bin/prestate-proof.json
148148
.PHONY: cannon-prestate
149149

cannon/.gitignore

-3
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@ venv
77
*.log
88
testdata/example/bin
99
contracts/out
10-
state.json
11-
*.json
12-
*.json.gz
1310
*.pprof
1411
*.out
1512
bin

cannon/Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ cannon-impl:
1919
env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build -v $(LDFLAGS) -o ./bin/cannon-impl .
2020

2121
cannon-embeds: cannon-impl
22-
@cp bin/cannon-impl ./multicannon/embeds/cannon-0
22+
@cp bin/cannon-impl ./multicannon/embeds/cannon-2
2323
@cp bin/cannon-impl ./multicannon/embeds/cannon-1
2424

2525
cannon: cannon-embeds

cannon/cmd/load_elf.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func LoadELF(ctx *cli.Context) error {
6969
return err
7070
}
7171
switch ver {
72-
case versions.VersionSingleThreaded:
72+
case versions.VersionSingleThreaded2:
7373
createInitialState = func(f *elf.File) (mipsevm.FPVMState, error) {
7474
return program.LoadELF(f, singlethreaded.CreateInitialState)
7575
}

cannon/cmd/run.go

+1
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ func Run(ctx *cli.Context) error {
373373
if err != nil {
374374
return fmt.Errorf("failed to load state: %w", err)
375375
}
376+
l.Info("Loaded input state", "version", state.Version)
376377
vm := state.CreateVM(l, po, outLog, errLog, meta)
377378
debugProgram := ctx.Bool(RunDebugFlag.Name)
378379
if debugProgram {

cannon/mipsevm/exec/mips_syscalls.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,15 @@ func HandleSysFcntl(a0, a1 uint32) (v0, v1 uint32) {
286286
// args: a0 = fd, a1 = cmd
287287
v1 = uint32(0)
288288

289-
if a1 == 3 { // F_GETFL: get file descriptor flags
289+
if a1 == 1 { // F_GETFD: get file descriptor flags
290+
switch a0 {
291+
case FdStdin, FdStdout, FdStderr, FdPreimageRead, FdHintRead, FdPreimageWrite, FdHintWrite:
292+
v0 = 0 // No flags set
293+
default:
294+
v0 = 0xFFffFFff
295+
v1 = MipsEBADF
296+
}
297+
} else if a1 == 3 { // F_GETFL: get file status flags
290298
switch a0 {
291299
case FdStdin, FdPreimageRead, FdHintRead:
292300
v0 = 0 // O_RDONLY

cannon/mipsevm/multithreaded/state.go

-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ func CreateInitialState(pc, heapStart uint32) *State {
9797
}
9898

9999
func (s *State) CreateVM(logger log.Logger, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, meta mipsevm.Metadata) mipsevm.FPVM {
100-
logger.Info("Using cannon multithreaded VM")
101100
return NewInstrumentedState(s, po, stdOut, stdErr, logger, meta)
102101
}
103102

cannon/mipsevm/singlethreaded/state.go

-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ func CreateInitialState(pc, heapStart uint32) *State {
6969
}
7070

7171
func (s *State) CreateVM(logger log.Logger, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, meta mipsevm.Metadata) mipsevm.FPVM {
72-
logger.Info("Using cannon VM")
7372
return NewInstrumentedState(s, po, stdOut, stdErr, meta)
7473
}
7574

cannon/mipsevm/tests/fuzz_evm_common_test.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,17 @@ func FuzzStateSyscallFcntl(f *testing.F) {
150150
expected.Step += 1
151151
expected.PC = state.GetCpu().NextPC
152152
expected.NextPC = state.GetCpu().NextPC + 4
153-
if cmd == 3 {
153+
if cmd == 1 {
154+
switch fd {
155+
case exec.FdStdin, exec.FdStdout, exec.FdStderr,
156+
exec.FdPreimageRead, exec.FdHintRead, exec.FdPreimageWrite, exec.FdHintWrite:
157+
expected.Registers[2] = 0
158+
expected.Registers[7] = 0
159+
default:
160+
expected.Registers[2] = 0xFF_FF_FF_FF
161+
expected.Registers[7] = exec.MipsEBADF
162+
}
163+
} else if cmd == 3 {
154164
switch fd {
155165
case exec.FdStdin, exec.FdPreimageRead, exec.FdHintRead:
156166
expected.Registers[2] = 0

cannon/mipsevm/versions/detect.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ func DetectVersion(path string) (StateVersion, error) {
2727
}
2828

2929
switch ver {
30-
case VersionSingleThreaded, VersionMultiThreaded:
30+
case VersionSingleThreaded, VersionMultiThreaded, VersionSingleThreaded2:
3131
return ver, nil
3232
default:
3333
return 0, fmt.Errorf("%w: %d", ErrUnknownVersion, ver)

cannon/mipsevm/versions/detect_test.go

+32-7
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package versions
22

33
import (
4+
"embed"
45
"os"
56
"path/filepath"
7+
"strconv"
68
"testing"
79

810
"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
@@ -11,23 +13,46 @@ import (
1113
"github.com/stretchr/testify/require"
1214
)
1315

16+
const statesPath = "testdata/states"
17+
18+
//go:embed testdata/states
19+
var historicStates embed.FS
20+
1421
func TestDetectVersion(t *testing.T) {
15-
t.Run("SingleThreadedJSON", func(t *testing.T) {
16-
state, err := NewFromState(singlethreaded.CreateEmptyState())
22+
testDetection := func(t *testing.T, version StateVersion, ext string) {
23+
filename := strconv.Itoa(int(version)) + ext
24+
dir := t.TempDir()
25+
path := filepath.Join(dir, filename)
26+
in, err := historicStates.ReadFile(filepath.Join(statesPath, filename))
1727
require.NoError(t, err)
18-
path := writeToFile(t, "state.json", state)
19-
version, err := DetectVersion(path)
28+
require.NoError(t, os.WriteFile(path, in, 0o644))
29+
30+
detectedVersion, err := DetectVersion(path)
2031
require.NoError(t, err)
21-
require.Equal(t, VersionSingleThreaded, version)
22-
})
32+
require.Equal(t, version, detectedVersion)
33+
}
34+
// Iterate all known versions to ensure we have a test case to detect every state version
35+
for _, version := range StateVersionTypes {
36+
version := version
37+
t.Run(version.String(), func(t *testing.T) {
38+
testDetection(t, version, ".bin.gz")
39+
})
40+
41+
if version == VersionSingleThreaded {
42+
t.Run(version.String()+".json", func(t *testing.T) {
43+
testDetection(t, version, ".json")
44+
})
45+
}
46+
}
2347

48+
// Additionally, check that the latest supported versions write new states in a way that is detected correctly
2449
t.Run("SingleThreadedBinary", func(t *testing.T) {
2550
state, err := NewFromState(singlethreaded.CreateEmptyState())
2651
require.NoError(t, err)
2752
path := writeToFile(t, "state.bin.gz", state)
2853
version, err := DetectVersion(path)
2954
require.NoError(t, err)
30-
require.Equal(t, VersionSingleThreaded, version)
55+
require.Equal(t, VersionSingleThreaded2, version)
3156
})
3257

3358
t.Run("MultiThreadedBinary", func(t *testing.T) {

cannon/mipsevm/versions/state.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,16 @@ const (
1919
// VersionSingleThreaded is the version of the Cannon STF found in op-contracts/v1.6.0 - https://github.com/ethereum-optimism/optimism/blob/op-contracts/v1.6.0/packages/contracts-bedrock/src/cannon/MIPS.sol
2020
VersionSingleThreaded StateVersion = iota
2121
VersionMultiThreaded
22+
// VersionSingleThreaded2 is based on VersionSingleThreaded with the addition of support for fcntl(F_GETFD) syscall
23+
VersionSingleThreaded2
2224
)
2325

2426
var (
2527
ErrUnknownVersion = errors.New("unknown version")
2628
ErrJsonNotSupported = errors.New("json not supported")
2729
)
2830

29-
var StateVersionTypes = []StateVersion{VersionSingleThreaded, VersionMultiThreaded}
31+
var StateVersionTypes = []StateVersion{VersionSingleThreaded, VersionMultiThreaded, VersionSingleThreaded2}
3032

3133
func LoadStateFromFile(path string) (*VersionedState, error) {
3234
if !serialize.IsBinaryFile(path) {
@@ -44,7 +46,7 @@ func NewFromState(state mipsevm.FPVMState) (*VersionedState, error) {
4446
switch state := state.(type) {
4547
case *singlethreaded.State:
4648
return &VersionedState{
47-
Version: VersionSingleThreaded,
49+
Version: VersionSingleThreaded2,
4850
FPVMState: state,
4951
}, nil
5052
case *multithreaded.State:
@@ -79,7 +81,7 @@ func (s *VersionedState) Deserialize(in io.Reader) error {
7981
}
8082

8183
switch s.Version {
82-
case VersionSingleThreaded:
84+
case VersionSingleThreaded2:
8385
state := &singlethreaded.State{}
8486
if err := state.Deserialize(in); err != nil {
8587
return err
@@ -113,6 +115,8 @@ func (s StateVersion) String() string {
113115
return "singlethreaded"
114116
case VersionMultiThreaded:
115117
return "multithreaded"
118+
case VersionSingleThreaded2:
119+
return "singlethreaded-2"
116120
default:
117121
return "unknown"
118122
}
@@ -124,6 +128,8 @@ func ParseStateVersion(ver string) (StateVersion, error) {
124128
return VersionSingleThreaded, nil
125129
case "multithreaded":
126130
return VersionMultiThreaded, nil
131+
case "singlethreaded-2":
132+
return VersionSingleThreaded2, nil
127133
default:
128134
return StateVersion(0), errors.New("unknown state version")
129135
}

cannon/mipsevm/versions/state_test.go

+22-19
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,19 @@ import (
44
"path/filepath"
55
"testing"
66

7+
"github.com/ethereum-optimism/optimism/cannon/mipsevm"
78
"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
89
"github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded"
910
"github.com/ethereum-optimism/optimism/cannon/serialize"
1011
"github.com/stretchr/testify/require"
1112
)
1213

1314
func TestNewFromState(t *testing.T) {
14-
t.Run("singlethreaded", func(t *testing.T) {
15+
t.Run("singlethreaded-2", func(t *testing.T) {
1516
actual, err := NewFromState(singlethreaded.CreateEmptyState())
1617
require.NoError(t, err)
1718
require.IsType(t, &singlethreaded.State{}, actual.FPVMState)
18-
require.Equal(t, VersionSingleThreaded, actual.Version)
19+
require.Equal(t, VersionSingleThreaded2, actual.Version)
1920
})
2021

2122
t.Run("multithreaded", func(t *testing.T) {
@@ -27,16 +28,6 @@ func TestNewFromState(t *testing.T) {
2728
}
2829

2930
func TestLoadStateFromFile(t *testing.T) {
30-
t.Run("SinglethreadedFromJSON", func(t *testing.T) {
31-
expected, err := NewFromState(singlethreaded.CreateEmptyState())
32-
require.NoError(t, err)
33-
34-
path := writeToFile(t, "state.json", expected)
35-
actual, err := LoadStateFromFile(path)
36-
require.NoError(t, err)
37-
require.Equal(t, expected, actual)
38-
})
39-
4031
t.Run("SinglethreadedFromBinary", func(t *testing.T) {
4132
expected, err := NewFromState(singlethreaded.CreateEmptyState())
4233
require.NoError(t, err)
@@ -58,14 +49,26 @@ func TestLoadStateFromFile(t *testing.T) {
5849
})
5950
}
6051

61-
func TestMultithreadedDoesNotSupportJSON(t *testing.T) {
62-
state, err := NewFromState(multithreaded.CreateEmptyState())
63-
require.NoError(t, err)
52+
func TestVersionsOtherThanZeroDoNotSupportJSON(t *testing.T) {
53+
tests := []struct {
54+
version StateVersion
55+
createState func() mipsevm.FPVMState
56+
}{
57+
{VersionSingleThreaded2, func() mipsevm.FPVMState { return singlethreaded.CreateEmptyState() }},
58+
{VersionMultiThreaded, func() mipsevm.FPVMState { return multithreaded.CreateEmptyState() }},
59+
}
60+
for _, test := range tests {
61+
test := test
62+
t.Run(test.version.String(), func(t *testing.T) {
63+
state, err := NewFromState(test.createState())
64+
require.NoError(t, err)
6465

65-
dir := t.TempDir()
66-
path := filepath.Join(dir, "test.json")
67-
err = serialize.Write(path, state, 0o644)
68-
require.ErrorIs(t, err, ErrJsonNotSupported)
66+
dir := t.TempDir()
67+
path := filepath.Join(dir, "test.json")
68+
err = serialize.Write(path, state, 0o644)
69+
require.ErrorIs(t, err, ErrJsonNotSupported)
70+
})
71+
}
6972
}
7073

7174
func writeToFile(t *testing.T, filename string, data serialize.Serializable) string {
32 Bytes
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"memory": [],
3+
"preimageKey": "0x0000000000000000000000000000000000000000000000000000000000000000",
4+
"preimageOffset": 0,
5+
"pc": 0,
6+
"nextPC": 4,
7+
"lo": 0,
8+
"hi": 0,
9+
"heap": 0,
10+
"exit": 0,
11+
"exited": false,
12+
"step": 0,
13+
"registers": [
14+
0,
15+
0,
16+
0,
17+
0,
18+
0,
19+
0,
20+
0,
21+
0,
22+
0,
23+
0,
24+
0,
25+
0,
26+
0,
27+
0,
28+
0,
29+
0,
30+
0,
31+
0,
32+
0,
33+
0,
34+
0,
35+
0,
36+
0,
37+
0,
38+
0,
39+
0,
40+
0,
41+
0,
42+
0,
43+
0,
44+
0,
45+
0
46+
]
47+
}
48+
46 Bytes
Binary file not shown.
33 Bytes
Binary file not shown.

cannon/multicannon/exec.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const baseDir = "embeds"
2121

2222
func ExecuteCannon(ctx context.Context, args []string, ver versions.StateVersion) error {
2323
switch ver {
24-
case versions.VersionSingleThreaded, versions.VersionMultiThreaded:
24+
case versions.VersionSingleThreaded, versions.VersionSingleThreaded2, versions.VersionMultiThreaded:
2525
default:
2626
return errors.New("unsupported version")
2727
}

op-challenger/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ DISPUTE_GAME_FACTORY=$(jq -r .DisputeGameFactoryProxy .devnet/addresses.json)
4343
--cannon-l2-genesis .devnet/genesis-l2.json \
4444
--cannon-bin ./cannon/bin/cannon \
4545
--cannon-server ./op-program/bin/op-program \
46-
--cannon-prestate ./op-program/bin/prestate.json \
46+
--cannon-prestate ./op-program/bin/prestate.bin.gz \
4747
--l2-eth-rpc http://localhost:9545 \
4848
--mnemonic "test test test test test test test test test test test junk" \
4949
--hd-path "m/44'/60'/0'/0/8" \

0 commit comments

Comments
 (0)