Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions common/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ var (
Name: "geth",
Usage: "Location of go-ethereum 'evm' binary",
}
GethBatchFlag = &cli.StringSliceFlag{
Name: "gethbatch",
Usage: "Location of go-ethereum 'evm' binary",
}
NethermindFlag = &cli.StringSliceFlag{
Name: "nethermind",
Usage: "Location of nethermind 'nethtest' binary",
Expand Down Expand Up @@ -104,6 +108,7 @@ var (
}
VmFlags = []cli.Flag{
GethFlag,
GethBatchFlag,
NethermindFlag,
NethBatchFlag,
BesuFlag,
Expand All @@ -118,6 +123,7 @@ var (
func initVMs(c *cli.Context) []evms.Evm {
var (
gethBins = c.StringSlice(GethFlag.Name)
gethBatchBins = c.StringSlice(GethBatchFlag.Name)
nethBins = c.StringSlice(NethermindFlag.Name)
nethBatchBins = c.StringSlice(NethBatchFlag.Name)
besuBins = c.StringSlice(BesuFlag.Name)
Expand All @@ -130,6 +136,9 @@ func initVMs(c *cli.Context) []evms.Evm {
for i, bin := range gethBins {
vms = append(vms, evms.NewGethEVM(bin, fmt.Sprintf("%d", i)))
}
for i, bin := range gethBatchBins {
vms = append(vms, evms.NewGethBatchVM(bin, fmt.Sprintf("%d", i)))
}
for i, bin := range nethBins {
vms = append(vms, evms.NewNethermindVM(bin, fmt.Sprintf("%d", i)))
}
Expand Down
14 changes: 12 additions & 2 deletions evms/geth.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,15 @@ func (evm *GethEVM) RunStateTest(path string, out io.Writer, speedTest bool) (st
func (vm *GethEVM) Close() {
}

// feed reads from the reader, does some geth-specific filtering and
// Copy reads from the reader, does some geth-specific filtering and
// outputs items onto the channel
func (evm *GethEVM) Copy(out io.Writer, input io.Reader) {
evm.copyUntilEnd(out, input)
}

// copyUntilEnd reads from the reader, does some geth-specific filtering and
// outputs items onto the channel
func (evm *GethEVM) copyUntilEnd(out io.Writer, input io.Reader) stateRoot {
var stateRoot stateRoot
scanner := bufio.NewScanner(input)
// Start with 1MB buffer, allow up to 32 MB
Expand Down Expand Up @@ -158,6 +164,10 @@ func (evm *GethEVM) Copy(out io.Writer, input io.Reader) {
if stateRoot.StateRoot == "" {
_ = json.Unmarshal(data, &stateRoot)
}
// If we have a stateroot, we're done
if len(stateRoot.StateRoot) > 0 {
break
}
continue
}
// When geth encounters end of code, it continues anyway, on a 'virtual' STOP.
Expand All @@ -173,6 +183,6 @@ func (evm *GethEVM) Copy(out io.Writer, input io.Reader) {
root, _ := json.Marshal(stateRoot)
if _, err := out.Write(append(root, '\n')); err != nil {
fmt.Fprintf(os.Stderr, "Error writing to out: %v\n", err)
return
}
return stateRoot
}
108 changes: 108 additions & 0 deletions evms/gethbatcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright 2023 Martin Holst Swende
// This file is part of the goevmlab library.
//
// The library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the goevmlab library. If not, see <http://www.gnu.org/licenses/>.

package evms

import (
"fmt"
"io"
"os/exec"
"sync"
)

// The GethBatchVM spins up one 'master' instance of the VM, and uses that to execute tests
type GethBatchVM struct {
GethEVM
cmd *exec.Cmd // the 'master' process
stdout io.ReadCloser
stdin io.WriteCloser
mu sync.Mutex
}

func NewGethBatchVM(path, name string) *GethBatchVM {
return &GethBatchVM{
GethEVM: GethEVM{path, name},
}
}

func (evm *GethBatchVM) Name() string {
return fmt.Sprintf("gethbatch-%v", evm.name)
}

// RunStateTest implements the Evm interface
func (evm *GethBatchVM) RunStateTest(path string, out io.Writer, speedTest bool) (string, error) {
var (
err error
cmd *exec.Cmd
stdout io.ReadCloser
stdin io.WriteCloser
)
if evm.cmd == nil {
if speedTest {
cmd = exec.Command(evm.path, "--nomemory", "--noreturndata", "--nostack", "statetest")
} else {
cmd = exec.Command(evm.path, "--json", "--noreturndata", "--nomemory", "statetest")
}
if stdout, err = cmd.StderrPipe(); err != nil {
return cmd.String(), err
}
if stdin, err = cmd.StdinPipe(); err != nil {
return cmd.String(), err
}
if err = cmd.Start(); err != nil {
return cmd.String(), err
}
evm.cmd = cmd
evm.stdout = stdout
evm.stdin = stdin
}
evm.mu.Lock()
defer evm.mu.Unlock()
_, _ = evm.stdin.Write([]byte(fmt.Sprintf("%v\n", path)))
// copy everything for the _current_ statetest to the given writer
evm.copyUntilEnd(out, evm.stdout)
// release resources, handle error but ignore non-zero exit codes
return evm.cmd.String(), nil
}

func (vm *GethBatchVM) Close() {
if vm.stdin != nil {
vm.stdin.Close()
}
if vm.cmd != nil {
_ = vm.cmd.Wait()
}
}

func (evm *GethBatchVM) GetStateRoot(path string) (root, command string, err error) {
if evm.cmd == nil {
evm.cmd = exec.Command(evm.path)
if evm.stdout, err = evm.cmd.StdoutPipe(); err != nil {
return "", evm.cmd.String(), err
}
if evm.stdin, err = evm.cmd.StdinPipe(); err != nil {
return "", evm.cmd.String(), err
}
if err = evm.cmd.Start(); err != nil {
return "", evm.cmd.String(), err
}
}
evm.mu.Lock()
defer evm.mu.Unlock()
_, _ = evm.stdin.Write([]byte(fmt.Sprintf("%v\n", path)))
sRoot := evm.copyUntilEnd(devNull{}, evm.stdout)
return sRoot.StateRoot, evm.cmd.String(), nil
}