Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core/vm, cmd/eofdump: implement eof validation #30418

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

holiman
Copy link
Contributor

@holiman holiman commented Sep 11, 2024

The bulk of this PR is authored by @lightclient , in the original EOF-work.
More recently, the code has been picked up and reworked for the new EOF specification, by @MariusVanDerWijden , in #29518, and also @shemnon has contributed with fixes.

They are the main authors to be credited for the code in this PR.


This PR is an attempt to start eating the elephant one small bite at a time, by selecting only the eof-validation as a standalone piece which can be merged without interfering too much in the core stuff.

In this PR:

  • Validation of eof containers, lifted from WIP mega eof #29518, along with test-vectors from consensus-tests and fuzzing, to ensure that the move did not lose any functionality.
  • Definition of eof opcodes, which is a prerequisite for validation
  • Addition of undefined to a jumptable entry item. I'm not super-happy with this, but for the moment it seems the least invasive way to do it. A better way might be to go back and allowing nil-items or nil execute-functions to denote "undefined".
  • Gas-calculators. These do not strictly need to be here, I can remove them if we want to reduce the diff even further.
  • benchmarks of eof validation speed

It would have been nice to break it up further, core/vm is growing very very large. However, it's not easy to put eof in a submodule.

  • eof validation requires at least the opcodes, and certain opcode-specific info (e.g. stack usage)
  • interpreter requires eof to tell it to validate

So what we might do is move the opcodes, and the jumptable definition to a new low-level module. And in core/vm keep the jumptable instantiations, so we can pass the actual jumptables to eof. Then eof doesn't have to import core/vm, I think.

Even with this smaller piece cherry-picked, there's quite a bit of work to do to improve it, better documentation and test coverage. I'll push commits here and rebase quite a bit.

To do in follow-up PR(s) is to modify the interpreter a bit more in-depth, to operate differently in eof-mode versus non-eof-mode.

Co-authored-by: @lightclient
Co-authored-by: @MariusVanDerWijden
Co-authored-by: @shemnon

Comment on lines +1076 to +1080
INVALID: {
execute: opUndefined,
minStack: minStack(0, 0),
maxStack: maxStack(0, 0),
},
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is IMO very wonky, see discussion here: https://github.com/ethereum/go-ethereum/pull/29518/files#r1753395744 .

Comment on lines +26 to +46
ErrUndefinedInstruction = errors.New("undefined instruction")
ErrTruncatedImmediate = errors.New("truncated immediate")
ErrInvalidSectionArgument = errors.New("invalid section argument")
ErrInvalidCallArgument = errors.New("callf into non-returning section")
ErrInvalidDataloadNArgument = errors.New("invalid dataloadN argument")
ErrInvalidJumpDest = errors.New("invalid jump destination")
ErrInvalidBackwardJump = errors.New("invalid backward jump")
//ErrConflictingStack = errors.New("conflicting stack height")
//ErrInvalidBranchCount = errors.New("invalid number of branches in jump table")
ErrInvalidOutputs = errors.New("invalid number of outputs")
ErrInvalidMaxStackHeight = errors.New("invalid max stack height")
ErrInvalidCodeTermination = errors.New("invalid code termination")
ErrEOFCreateWithTruncatedSection = errors.New("eofcreate with truncated section")
ErrOrphanedSubcontainer = errors.New("subcontainer not referenced at all")
ErrIncompatibleContainerKind = errors.New("incompatible container kind")
ErrStopAndReturnContract = errors.New("Stop/Return and Returncontract in the same code section")
ErrStopInInitCode = errors.New("initcode contains a RETURN or STOP opcode")
ErrTruncatedTopLevelContainer = errors.New("truncated top level container")
ErrUnreachableCode = errors.New("unreachable code")
ErrInvalidNonReturningFlag = errors.New("invalid non-returning flag, bad RETF")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not all of these methods are mapped in the eofparser error-to-number mapping function. Should they be?

core/vm/validate.go Outdated Show resolved Hide resolved
core/vm/memory_table.go Outdated Show resolved Hide resolved
Comment on lines +55 to +56
ErrTooManyInputs = errors.New("invalid type content, too many inputs")
ErrTooManyOutputs = errors.New("invalid type content, too many outputs")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are these needed, when we already have ErrInvalidInputs/Outputs?

core/vm/validate.go Outdated Show resolved Hide resolved
@holiman
Copy link
Contributor Author

holiman commented Sep 11, 2024

Added benchmarks, based on some of the worst-cases from the consensus-tests + fuzzing vectors.

goos: linux
goarch: amd64
pkg: github.com/ethereum/go-ethereum/cmd/eofdump
cpu: 12th Gen Intel(R) Core(TM) i7-1270P
BenchmarkEofParse
BenchmarkEofParse/test-1
BenchmarkEofParse/test-1-8               1043824              1145 ns/op          17.47 MB/s         352 B/op          9 allocs/op
BenchmarkEofParse/test-2
BenchmarkEofParse/test-2-8                717020              1526 ns/op          13.77 MB/s         416 B/op         12 allocs/op
BenchmarkEofParse/test-3
BenchmarkEofParse/test-3-8                645865              1830 ns/op          12.56 MB/s         464 B/op         16 allocs/op
BenchmarkEofParse/test-4
BenchmarkEofParse/test-4-8                666870              1875 ns/op          12.27 MB/s         464 B/op         16 allocs/op
BenchmarkEofParse/test-5
BenchmarkEofParse/test-5-8                614787              2180 ns/op          11.01 MB/s         488 B/op         18 allocs/op
BenchmarkEofParse/test-6
BenchmarkEofParse/test-6-8                337408              3773 ns/op          21.20 MB/s        1064 B/op         35 allocs/op
BenchmarkEofParse/test-7
BenchmarkEofParse/test-7-8                   282           4462357 ns/op          11.00 MB/s     1058652 B/op      38282 allocs/op
BenchmarkEofParse/test-8
BenchmarkEofParse/test-8-8                342518              4078 ns/op          15.69 MB/s         934 B/op         35 allocs/op
BenchmarkEofParse/test-9
BenchmarkEofParse/test-9-8                301110              3716 ns/op          17.22 MB/s         934 B/op         35 allocs/op
BenchmarkEofParse/test-10
BenchmarkEofParse/test-10-8               288266              4832 ns/op           6.83 MB/s        1031 B/op         37 allocs/op
BenchmarkEofParse/test-11
BenchmarkEofParse/test-11-8               277754              4582 ns/op           7.20 MB/s        1031 B/op         37 allocs/op
BenchmarkEofParse/test-12
BenchmarkEofParse/test-12-8                  632           1874380 ns/op          13.11 MB/s      453325 B/op      18881 allocs/op
BenchmarkEofParse/test-13
BenchmarkEofParse/test-13-8               185851              6186 ns/op           5.50 MB/s        1696 B/op         40 allocs/op
BenchmarkEofParse/test-14
BenchmarkEofParse/test-14-8                97939             13215 ns/op           4.09 MB/s        3557 B/op         80 allocs/op
BenchmarkEofParse/test-15
BenchmarkEofParse/test-15-8               104028             13065 ns/op          11.48 MB/s        3557 B/op         80 allocs/op
BenchmarkEofParse/test-16
BenchmarkEofParse/test-16-8                36811             36740 ns/op          14.59 MB/s       12777 B/op        279 allocs/op
BenchmarkEofParse/test-17
BenchmarkEofParse/test-17-8                 9140            117813 ns/op           6.25 MB/s       30370 B/op        739 allocs/op
BenchmarkEofParse/test-18
BenchmarkEofParse/test-18-8                 3262            368707 ns/op           2.83 MB/s      110391 B/op       2091 allocs/op
BenchmarkEofParse/test-19
BenchmarkEofParse/test-19-8               720025              1415 ns/op          22.62 MB/s         408 B/op         12 allocs/op
PASS
ok      github.com/ethereum/go-ethereum/cmd/eofdump     31.290s

@holiman
Copy link
Contributor Author

holiman commented Sep 11, 2024

Sped it up quite a bit, the slowest vector is now improved by a factor of 10 :)

goos: linux
goarch: amd64
pkg: github.com/ethereum/go-ethereum/cmd/eofdump
cpu: 12th Gen Intel(R) Core(TM) i7-1270P
                   │  eof.bench.1  │             eof.bench.2             │
                   │    sec/op     │    sec/op     vs base               │
EofParse/test-1-8     1.062µ ± ∞ ¹   1.105µ ± ∞ ¹        ~ (p=0.151 n=5)
EofParse/test-2-8     1.451µ ± ∞ ¹   1.442µ ± ∞ ¹        ~ (p=0.151 n=5)
EofParse/test-3-8     1.770µ ± ∞ ¹   1.533µ ± ∞ ¹  -13.39% (p=0.008 n=5)
EofParse/test-4-8     1.775µ ± ∞ ¹   1.550µ ± ∞ ¹  -12.68% (p=0.008 n=5)
EofParse/test-5-8     2.035µ ± ∞ ¹   1.631µ ± ∞ ¹  -19.85% (p=0.008 n=5)
EofParse/test-6-8     3.484µ ± ∞ ¹   3.039µ ± ∞ ¹  -12.77% (p=0.008 n=5)
EofParse/test-7-8     3.990m ± ∞ ¹   2.896m ± ∞ ¹  -27.43% (p=0.008 n=5)
EofParse/test-8-8     3.868µ ± ∞ ¹   1.225µ ± ∞ ¹  -68.33% (p=0.008 n=5)
EofParse/test-9-8     3.765µ ± ∞ ¹   1.170µ ± ∞ ¹  -68.92% (p=0.008 n=5)
EofParse/test-10-8    4.252µ ± ∞ ¹   1.838µ ± ∞ ¹  -56.77% (p=0.008 n=5)
EofParse/test-11-8    4.727µ ± ∞ ¹   1.829µ ± ∞ ¹  -61.31% (p=0.008 n=5)
EofParse/test-12-8    1.745m ± ∞ ¹   1.106m ± ∞ ¹  -36.63% (p=0.008 n=5)
EofParse/test-13-8    5.778µ ± ∞ ¹   1.836µ ± ∞ ¹  -68.22% (p=0.008 n=5)
EofParse/test-14-8   12.637µ ± ∞ ¹   2.335µ ± ∞ ¹  -81.52% (p=0.008 n=5)
EofParse/test-15-8   12.224µ ± ∞ ¹   2.856µ ± ∞ ¹  -76.64% (p=0.008 n=5)
EofParse/test-16-8    33.30µ ± ∞ ¹   15.61µ ± ∞ ¹  -53.12% (p=0.008 n=5)
EofParse/test-17-8   106.47µ ± ∞ ¹   11.42µ ± ∞ ¹  -89.27% (p=0.008 n=5)
EofParse/test-18-8   337.85µ ± ∞ ¹   27.20µ ± ∞ ¹  -91.95% (p=0.008 n=5)
EofParse/test-19-8    1.368µ ± ∞ ¹   1.365µ ± ∞ ¹        ~ (p=0.889 n=5)
geomean               11.30µ         5.055µ        -55.27%
¹ need >= 6 samples for confidence interval at level 0.95

                   │  eof.bench.1  │               eof.bench.2               │
                   │      B/s      │      B/s        vs base                 │
EofParse/test-1-8    17.96Mi ± ∞ ¹    17.27Mi ± ∞ ¹          ~ (p=0.151 n=5)
EofParse/test-2-8    13.80Mi ± ∞ ¹    13.89Mi ± ∞ ¹          ~ (p=0.151 n=5)
EofParse/test-3-8    12.39Mi ± ∞ ¹    14.31Mi ± ∞ ¹    +15.47% (p=0.008 n=5)
EofParse/test-4-8    12.36Mi ± ∞ ¹    14.15Mi ± ∞ ¹    +14.51% (p=0.008 n=5)
EofParse/test-5-8    11.24Mi ± ∞ ¹    14.04Mi ± ∞ ¹    +24.85% (p=0.008 n=5)
EofParse/test-6-8    21.90Mi ± ∞ ¹    25.11Mi ± ∞ ¹    +14.68% (p=0.008 n=5)
EofParse/test-7-8    11.74Mi ± ∞ ¹    16.17Mi ± ∞ ¹    +37.77% (p=0.008 n=5)
EofParse/test-8-8    15.78Mi ± ∞ ¹    49.84Mi ± ∞ ¹   +215.77% (p=0.008 n=5)
EofParse/test-9-8    16.21Mi ± ∞ ¹    52.18Mi ± ∞ ¹   +221.82% (p=0.008 n=5)
EofParse/test-10-8   7.401Mi ± ∞ ¹   17.118Mi ± ∞ ¹   +131.31% (p=0.008 n=5)
EofParse/test-11-8   6.657Mi ± ∞ ¹   17.204Mi ± ∞ ¹   +158.45% (p=0.008 n=5)
EofParse/test-12-8   13.43Mi ± ∞ ¹    21.20Mi ± ∞ ¹    +57.88% (p=0.008 n=5)
EofParse/test-13-8   5.608Mi ± ∞ ¹   17.662Mi ± ∞ ¹   +214.97% (p=0.008 n=5)
EofParse/test-14-8   4.072Mi ± ∞ ¹   22.058Mi ± ∞ ¹   +441.69% (p=0.008 n=5)
EofParse/test-15-8   11.70Mi ± ∞ ¹    50.09Mi ± ∞ ¹   +328.04% (p=0.008 n=5)
EofParse/test-16-8   15.35Mi ± ∞ ¹    32.74Mi ± ∞ ¹   +113.23% (p=0.008 n=5)
EofParse/test-17-8   6.590Mi ± ∞ ¹   61.455Mi ± ∞ ¹   +832.56% (p=0.008 n=5)
EofParse/test-18-8   2.947Mi ± ∞ ¹   36.602Mi ± ∞ ¹  +1142.07% (p=0.008 n=5)
EofParse/test-19-8   22.31Mi ± ∞ ¹    22.36Mi ± ∞ ¹          ~ (p=0.841 n=5)
geomean              10.65Mi          23.81Mi         +123.64%
¹ need >= 6 samples for confidence interval at level 0.95

                   │   eof.bench.1   │              eof.bench.2               │
                   │      B/op       │     B/op       vs base                 │
EofParse/test-1-8        352.0 ± ∞ ¹     352.0 ± ∞ ¹        ~ (p=1.000 n=5) ²
EofParse/test-2-8        416.0 ± ∞ ¹     416.0 ± ∞ ¹        ~ (p=1.000 n=5) ²
EofParse/test-3-8        464.0 ± ∞ ¹     424.0 ± ∞ ¹   -8.62% (p=0.008 n=5)
EofParse/test-4-8        464.0 ± ∞ ¹     424.0 ± ∞ ¹   -8.62% (p=0.008 n=5)
EofParse/test-5-8        488.0 ± ∞ ¹     440.0 ± ∞ ¹   -9.84% (p=0.008 n=5)
EofParse/test-6-8       1064.0 ± ∞ ¹    1016.0 ± ∞ ¹   -4.51% (p=0.008 n=5)
EofParse/test-7-8     1033.8Ki ± ∞ ¹   910.3Ki ± ∞ ¹  -11.95% (p=0.008 n=5)
EofParse/test-8-8        934.0 ± ∞ ¹     368.0 ± ∞ ¹  -60.60% (p=0.008 n=5)
EofParse/test-9-8        934.0 ± ∞ ¹     368.0 ± ∞ ¹  -60.60% (p=0.008 n=5)
EofParse/test-10-8      1031.0 ± ∞ ¹     472.0 ± ∞ ¹  -54.22% (p=0.008 n=5)
EofParse/test-11-8      1031.0 ± ∞ ¹     472.0 ± ∞ ¹  -54.22% (p=0.008 n=5)
EofParse/test-12-8     442.7Ki ± ∞ ¹   369.0Ki ± ∞ ¹  -16.64% (p=0.008 n=5)
EofParse/test-13-8      1697.0 ± ∞ ¹     472.0 ± ∞ ¹  -72.19% (p=0.008 n=5)
EofParse/test-14-8      3557.0 ± ∞ ¹     568.0 ± ∞ ¹  -84.03% (p=0.008 n=5)
EofParse/test-15-8      3557.0 ± ∞ ¹     984.0 ± ∞ ¹  -72.34% (p=0.008 n=5)
EofParse/test-16-8     12.48Ki ± ∞ ¹   10.69Ki ± ∞ ¹  -14.34% (p=0.008 n=5)
EofParse/test-17-8    29.664Ki ± ∞ ¹   3.297Ki ± ∞ ¹  -88.89% (p=0.008 n=5)
EofParse/test-18-8   107.798Ki ± ∞ ¹   4.789Ki ± ∞ ¹  -95.56% (p=0.008 n=5)
EofParse/test-19-8       408.0 ± ∞ ¹     408.0 ± ∞ ¹        ~ (p=1.000 n=5) ²
geomean                3.065Ki         1.472Ki        -51.96%
¹ need >= 6 samples for confidence interval at level 0.95
² all samples are equal

                   │  eof.bench.1  │              eof.bench.2              │
                   │   allocs/op   │  allocs/op    vs base                 │
EofParse/test-1-8      9.000 ± ∞ ¹    9.000 ± ∞ ¹        ~ (p=1.000 n=5) ²
EofParse/test-2-8      12.00 ± ∞ ¹    14.00 ± ∞ ¹  +16.67% (p=0.008 n=5)
EofParse/test-3-8      16.00 ± ∞ ¹    14.00 ± ∞ ¹  -12.50% (p=0.008 n=5)
EofParse/test-4-8      16.00 ± ∞ ¹    14.00 ± ∞ ¹  -12.50% (p=0.008 n=5)
EofParse/test-5-8      18.00 ± ∞ ¹    14.00 ± ∞ ¹  -22.22% (p=0.008 n=5)
EofParse/test-6-8      35.00 ± ∞ ¹    31.00 ± ∞ ¹  -11.43% (p=0.008 n=5)
EofParse/test-7-8     38.28k ± ∞ ¹   28.30k ± ∞ ¹  -26.08% (p=0.008 n=5)
EofParse/test-8-8      35.00 ± ∞ ¹    11.00 ± ∞ ¹  -68.57% (p=0.008 n=5)
EofParse/test-9-8      35.00 ± ∞ ¹    11.00 ± ∞ ¹  -68.57% (p=0.008 n=5)
EofParse/test-10-8     37.00 ± ∞ ¹    14.00 ± ∞ ¹  -62.16% (p=0.008 n=5)
EofParse/test-11-8     37.00 ± ∞ ¹    14.00 ± ∞ ¹  -62.16% (p=0.008 n=5)
EofParse/test-12-8    18.88k ± ∞ ¹   10.71k ± ∞ ¹  -43.29% (p=0.008 n=5)
EofParse/test-13-8     40.00 ± ∞ ¹    14.00 ± ∞ ¹  -65.00% (p=0.008 n=5)
EofParse/test-14-8     80.00 ± ∞ ¹    14.00 ± ∞ ¹  -82.50% (p=0.008 n=5)
EofParse/test-15-8     80.00 ± ∞ ¹    14.00 ± ∞ ¹  -82.50% (p=0.008 n=5)
EofParse/test-16-8    279.00 ± ∞ ¹    22.00 ± ∞ ¹  -92.11% (p=0.008 n=5)
EofParse/test-17-8    739.00 ± ∞ ¹    11.00 ± ∞ ¹  -98.51% (p=0.008 n=5)
EofParse/test-18-8   2091.00 ± ∞ ¹    11.00 ± ∞ ¹  -99.47% (p=0.008 n=5)
EofParse/test-19-8     12.00 ± ∞ ¹    12.00 ± ∞ ¹        ~ (p=1.000 n=5) ²
geomean                93.51          29.15        -68.82%
¹ need >= 6 samples for confidence interval at level 0.95
² all samples are equal

cmd/eofdump: benchmarks of eof validation speeds
core/vm: move eof instructions to separate file
core/vm: unexport fields

Co-authored-by: lightclient <lightclient@protonmail.com>
Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de>
Co-authored-by: Danno Ferrin <danno.ferrin@shemnon.com>
}
}

func BenchmarkEOFValidation2(b *testing.B) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the benchmark-function in this file appears to be non-functional, should we nuke them or salvage them, @MariusVanDerWijden ?

example

[user@work vm]$ go test . -run - -bench EOFV
--- FAIL: BenchmarkEOFValidation
    validate_test.go:353: callf into non-returning section: section 1
--- FAIL: BenchmarkEOFValidation2
    validate_test.go:403: callf into non-returning section: section 1
--- FAIL: BenchmarkEOFValidation3
    validate_test.go:452: callf into non-returning section: section 1

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will try to salvage

core/vm: some clarifications in the eof code
core/vm: clarifications + minor speedup
core/vm: clarifications + lint + minor speedup
core/vm, core/asm: support eof in asm instruction iteration
core/vm: comment out unused
core/vm: remove gasfunctions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants