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

EVMC support #17050

Closed
wants to merge 2 commits into from
Closed

EVMC support #17050

wants to merge 2 commits into from

Conversation

chfast
Copy link
Member

@chfast chfast commented Jun 21, 2018

  • The meat is in Go bindings evmc#41, here included as a git submodule in vendor (to be copy&paste later with some vendor magic, but submodule is good for now because the code under review and is going to change).
  • Here we just use the Go API from evmc package. Some improvements can be made on both sides in the future.
  • The minus of having clean Go API is that the state tests execution bumped from 19 s to 24 s.
  • The geth+aleth-interpreter is running on the mainnet as "aleth" on https://ethstats.net.

Usage:

  • The path to EVMC VM DLL can be provided with EVMC_PATH env var.
  • The EVMC options can be passed to the VM via EVMC_OPTIONS env var, e.g. EVMC_OPTIONS='debug=on evm2wasm=true'.

@chfast
Copy link
Member Author

chfast commented Jun 21, 2018

cc @gballet (I don't have the rights to make you the reviewer).

core/vm/evmc.go Outdated
defer C.free(unsafe.Pointer((centrypoint)))

C.dlerror()
createSymbol = C.dlsym(handle, centrypoint)
Copy link
Member

Choose a reason for hiding this comment

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

Cannot this iterate symbols here and find the first starting with evmc_create_?

Copy link
Member Author

Choose a reason for hiding this comment

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

The dl library does not have any way to iterate over symbols. We'd have to use ELF library on Linux and something equivalent for MACHO on macOS. Possible in the future, but definitely overkill at the moment.

@gballet gballet self-requested a review June 22, 2018 09:06
@@ -380,11 +380,12 @@ func (s *Service) login(conn *websocket.Conn) error {
network = fmt.Sprintf("%d", infos.Protocols["les"].(*les.NodeInfo).Network)
protocol = fmt.Sprintf("les/%d", les.ClientProtocolVersions[0])
}
node := strings.Replace(infos.Name, "Geth", "Geth+EVMC", 1)
Copy link
Member

Choose a reason for hiding this comment

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

Please don't modify the reported name here. If yu need a different name, update it where the name is created to include any additional info you'd like.

Copy link
Member Author

Choose a reason for hiding this comment

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

I could not find where the name comes from, but this is not going to stay.

@axic
Copy link
Member

axic commented Jun 26, 2018

core/vm/evmc.go:21:10: fatal error: 'evmc/evmc.h' file not found
#include <evmc/evmc.h>
         ^~~~~~~~~~~~~

Don’t seem to have a submodule, does this rely on having evmc installed system wide?

@axic
Copy link
Member

axic commented Jun 26, 2018

To compile: changed the include path in core/vm/evmc.go to ../../evmc/include and added evmc as a submodule in the root directory.

@axic
Copy link
Member

axic commented Jun 26, 2018

One issue I find with this that the existence of the requested VM is not checked on startup, e.g. geth --vm invalid will not result in a failure until code is executed.

@chfast
Copy link
Member Author

chfast commented Jun 27, 2018

To compile: changed the include path in core/vm/evmc.go to ../../evmc/include and added evmc as a submodule in the root directory.

I will pull your change to make it easier for now. The final solution would be to include evmc Go wrapper in ethereum/evmc repo and use that as a Go package here. No git submodule would be needed then.

One issue I find with this that the existence of the requested VM is not checked on startup, e.g. geth --vm invalid will not result in a failure until code is executed.

Yes. Command line arguments in geth are quite flaky and to pass the path to VM you would have to change ~20 places (not all of them are covered at the moment, I added some workarounds). That requires discussion with Go team.

@chfast
Copy link
Member Author

chfast commented Jul 13, 2018

Big update! See the top comment.

@axic
Copy link
Member

axic commented Jul 25, 2018

Needs rebase after #17093 was merged.

Copy link
Member

@gballet gballet left a comment

Choose a reason for hiding this comment

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

Please update the code to implement the Interpreter interface, as it has been added especially to support this use case.

.gitmodules Outdated
@@ -1,3 +1,6 @@
[submodule "tests"]
path = tests/testdata
url = https://github.com/ethereum/tests
[submodule "evmc"]
Copy link
Member

Choose a reason for hiding this comment

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

This creates a strong dependency on an optional component. Since you have the EVMCPathFlag parameter, you can use it to download. Most clients will not use the evmc at this stage, so forcing them to download the code for it is overkill.

Copy link
Member

Choose a reason for hiding this comment

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

EVMC is only the C header plus the Go binding for it, doesn't include any VM implementation. I think it is needed, due it being a compile time dependency?

Copy link
Member

Choose a reason for hiding this comment

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

My bad, the previous comment was very poorly worded: you should use go's own opinionated package system and add the dependency to vendor/vendor.json with the govendor utility.

core/vm/evm.go Outdated
@@ -122,7 +122,7 @@ func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmCon
chainRules: chainConfig.Rules(ctx.BlockNumber),
}

evm.interpreter = NewInterpreter(evm, vmConfig)
evm.interpreter = NewEVMC(evm, vmConfig)
Copy link
Member

Choose a reason for hiding this comment

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

We should make it depend on a switch, like:

if vmConfig.EVMCPath != "" {
  evm.interpreter = NewEVMC(evm, vmConfig)
} else {
  evm.interpreter = newInterpreter(evm, vmConfig)
}

core/vm/evmc.go Show resolved Hide resolved
@@ -355,6 +355,11 @@ var (
Name: "vmdebug",
Usage: "Record information useful for VM and contract debugging",
}
EVMCPathFlag = cli.StringFlag{
Name: "vm",
Copy link
Member

Choose a reason for hiding this comment

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

This should then be --evmc or --evmc-vm, there are other, non-EVMC compliant VMs.

Copy link
Member Author

Choose a reason for hiding this comment

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

We can make it nicer by allowing options like:

--vm auto
--vm interpreter
--vm ewasm
--vm /path/to/evmc.so

This flag does not work anyway, so I'm removing it for now.

core/vm/evm.go Outdated
@@ -139,7 +139,7 @@ func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmCon
interpreters: make([]Interpreter, 1),
}

evm.interpreters[0] = NewEVMInterpreter(evm, vmConfig)
evm.interpreters[0] = NewEVMC(evm, vmConfig)
Copy link
Member

Choose a reason for hiding this comment

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

Since the merge of #17093, you can support other interpreters; this disables the evm interpreter which is not acceptable. What should happen is that if --evmc-vm is passed as an argument, then an EVMC interpreter should be inserted before. Something like:

if cfg.EVMCPath != nil {
  old := evm.interpreters
  evm.interpreters = make([]Interpreter, len(evm.interpreters)+1)
  evm.interpreters[0] = NewEVMC(evm, vmConfig)
  copy(evm.interpreters[1:], old)
}

core/vm/evmc.go Outdated
return evmcInstance
}

func NewEVMC(env *EVM, cfg Config) *EVMC {
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

It does.

output, gasLeftU, err = env.Call(host.contract, destination, input, gasU, value)
}
case evmc.DelegateCall:
output, gasLeftU, err = env.DelegateCall(host.contract, destination, input, gasU)
Copy link
Member

Choose a reason for hiding this comment

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

Should any of these panic if static is set or is it handled up in the interface?

Copy link
Member Author

Choose a reason for hiding this comment

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

static is magically handled inside, but this seems to be bad design. Probably should be changed later on to be handled internally in any VM.

@chfast
Copy link
Member Author

chfast commented Aug 22, 2018

@gballet I believe I addressed all your comments.

@axic
Copy link
Member

axic commented Sep 4, 2018

#17461 could simplify this a bit.

core/vm/evmc.go Outdated

func getRevision(env *EVM) evmc.Revision {
n := env.BlockNumber
if env.ChainConfig().IsByzantium(n) {
Copy link
Member

Choose a reason for hiding this comment

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

Is there a constantinople setting now in master, which needs to be supported here?

@axic
Copy link
Member

axic commented Sep 4, 2018

Please update to EVMC 5.2.0.

@chfast should support for EVMC_REJECTED implemented in this PR or a subsequent PR?

}

func (evm *EVMC) CanRun([]byte) bool {
return true
Copy link
Member

Choose a reason for hiding this comment

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

There are two issues here:

  • The EVMC will always try to execute the contract. There could be another interpreter that supports a format that the EVMC doesn't, and the native Go interpreter should be the default one in any case.
  • In the case of a mixed network that supports EVM+EWASM contracts, you need to have an external switch to be able to execute hera in mode evm1mode=fallback as discussed here, because right now it will always try to execute everything as WASM and that behavior is controlled by something outside of geth, which is horrible usability-wise.

So determining the proper format should be more advanced in any case.

Copy link
Member Author

Choose a reason for hiding this comment

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

So it should be false and then never run? EVMC is not for ewasm only, I'm using it with C++ EVM interpreter. I believe if an user specifies the execution as geth --vm myvm.so he really wants to use myvm.so.

You should rethink this CanRun() feature.

Copy link
Member

Choose a reason for hiding this comment

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

the feature has been thought through enough, thank you. If you have geth --vm myvm.so you are stuck with only one interpreter, whereas this supports a mixed chain with different formats.

Copy link
Member Author

Choose a reason for hiding this comment

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

This will be handled with EVMC 6.

Copy link
Member

Choose a reason for hiding this comment

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

then in EVMC-6, and provided the right command line switch, the return value here should be true. Until then, it should check if the format is wasm and, if not, return false.

Copy link
Member Author

Choose a reason for hiding this comment

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

This would break geth + aleth-interpreter.

Copy link
Member

Choose a reason for hiding this comment

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

Please be more specific. How would that break geth + aleth ?

Copy link
Member Author

Choose a reason for hiding this comment

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

I need geth + aleth-interpreter for testing. Aleth is EVM and if we check for wasm bytecode here it will be skip and the execution fallback to Go Interpreter.

Copy link
Member Author

Choose a reason for hiding this comment

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

Anyway, I'm not aiming to merge this PR now. We should definitely wait for EVMC 6 which should be soon.

Copy link
Member

Choose a reason for hiding this comment

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

ok let's do that. In the mean time, here is the PR that implements all the command line switches: #17687

core/vm/evm.go Outdated
@@ -139,7 +140,11 @@ func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmCon
interpreters: make([]Interpreter, 1),
}

evm.interpreters[0] = NewEVMInterpreter(evm, vmConfig)
if len(os.Getenv("EVMC_PATH")) != 0 {
Copy link
Member

Choose a reason for hiding this comment

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

Let's use a command line switch instead, env variables are transient and they lead to multi-steps instructions; it's better to have all non-privacy-sensitive configuration in one place.

furthermore, this de-activates the EVM and that's a no go: the EVM needs to remain as a backup. I suggest:

if vmConfig.evmcPath != 0 {
  evm.interpreters = append(evm.interpreters, NewEVMC(evm))
}
evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, vmConfig))

Then line 140 should be replaced with

interpreters: []Interpreter{}

Copy link
Member

Choose a reason for hiding this comment

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

What exactly is geth doing with that list?

Copy link
Member Author

Choose a reason for hiding this comment

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

I tried the command line switch but this is not doable currently (at least not in this PR) because the VMConfig{} is created in 100 places usually with default value just because the it is required to create an EVM.

Copy link
Member

Choose a reason for hiding this comment

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

@axic it lists the interpreters, so as to be able to find the best format for a given executable. This is how you can mix EVM&EWASM contracts.

core/vm/evmc.go Show resolved Hide resolved
@chfast
Copy link
Member Author

chfast commented Sep 11, 2018

Please update to EVMC 5.2.0.

Done.

@chfast should support for EVMC_REJECTED implemented in this PR or a subsequent PR?

Let's wait for capabilities from EVMC 6.

@axic
Copy link
Member

axic commented Sep 20, 2018

Please rebase over #17687 (well, master) so that we can support fallback to EVM.

core/vm/evmc.go Outdated
defer func() { evm.readOnly = false }()
}

fmt.Printf("depth: %d kind: %d static: %t\n", evm.env.depth, kind, readOnly)
Copy link
Member

Choose a reason for hiding this comment

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

Debug to be removed?

govendor fetch github.com/ethereum/evmc/bindings/go/evmc@=v5.2.0
@chfast
Copy link
Member Author

chfast commented Oct 21, 2018

Replaced with #17954

@chfast chfast closed this Oct 21, 2018
@chfast chfast deleted the evmc branch January 15, 2019 11:06
@chfast chfast mentioned this pull request Jul 3, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants