-
Notifications
You must be signed in to change notification settings - Fork 314
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
Go bindings #41
Go bindings #41
Conversation
d946800
to
d52cb9f
Compare
bindings/go/evmc/evmc.go
Outdated
inline int set_option(struct evmc_instance* instance, _GoString_ name, _GoString_ value) | ||
{ | ||
if (instance->set_option) | ||
return instance->set_option(instance, name.p, value.p); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could use the helper now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's easier to use _GoString_
type here, than use malloc/free to pass the string to the original helper.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean instead of the if and return here, just wrap evmc_set_option
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, yes. Good idea.
bindings/go/evmc/evmc.go
Outdated
memcpy(msg.value.bytes, value.p, sizeof(msg.value)); | ||
memcpy(msg.code_hash.bytes, code_hash.p, sizeof(msg.code_hash)); | ||
|
||
struct extended_context ctx = {{&fn_table}, context_index}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indentation?
bindings/go/evmc/evmc.go
Outdated
memcpy(msg.destination.bytes, destination.p, sizeof(msg.destination)); | ||
memcpy(msg.sender.bytes, sender.p, sizeof(msg.sender)); | ||
memcpy(msg.value.bytes, value.p, sizeof(msg.value)); | ||
memcpy(msg.code_hash.bytes, code_hash.p, sizeof(msg.code_hash)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should these have assertions for the length property (.n
?) on the GoBytes
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- I'm not sure using a C assert is good idea here.
- Because only this Go module can use this function we know exactly what size it is (still prone to future mistakes).
- Because this does not compile with Go 1.9, this might be rewritten anyway.
bindings/go/evmc/evmc.go
Outdated
{ | ||
if (instance->set_option) | ||
return instance->set_option(instance, name.p, value.p); | ||
return -1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: the evmc_set_option
wrapper returns 0 in this case. Which one is preferred?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here it returns 1, 0 or -1. This is not super needed, but I have different warnings for 0 and -1.
@gballet would you mind having a look at the Go code? :) Also please tag anyone else from the Go team who you think makes sense. |
|
||
func evmcUint256be(in common.Hash) C.struct_evmc_uint256be { | ||
out := C.struct_evmc_uint256be{} | ||
for i := 0; i < len(in); i++ { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we assert that len(in)
equals that of uint256be
?
Is there an option for static (compile time) assert in Go?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no static assert. You can't even check the struct size (without "unsafe" package). But this is not a big problem here, because the array access is safe (will panic if wrong, but I have not checked if the go compiler is able to optimize this).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has been fixed by the assertion commit.
|
||
func evmcAddress(address common.Address) C.struct_evmc_address { | ||
r := C.struct_evmc_address{} | ||
for i := 0; i < len(address); i++ { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has been fixed by the assertion commit.
if size == 0 { | ||
return []byte{} | ||
} | ||
return (*[1 << 30]byte)(unsafe.Pointer(data))[:size:size] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this magic doing? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Simple. You cast the data
pointer to a pointer to a huge array and then take a slice of size size
of it. This is a cgo why to treat C array as a Go slice.
bindings/go/evmc/host.c
Outdated
|
||
#include <stdlib.h> | ||
|
||
const struct evmc_context_fn_table fn_table = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure how linking works and whether this global symbol would pollute any consumer of this go library, but probably it makes sense prefixing these.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I agree. I can also add "hidden" attribute to it.
bindings/go/evmc/evmc.go
Outdated
#include <stdlib.h> | ||
#include <string.h> | ||
|
||
inline int set_option(struct evmc_instance* instance, _GoString_ name, _GoString_ value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure about linking visibility in cgo, but perhaps this should be static to limit the scope to this file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should.
a9ebe2f
to
0b5d0e4
Compare
bindings/go/evmc/evmc.go
Outdated
_GoBytes_ destination, _GoBytes_ sender, _GoBytes_ value, _GoBytes_ input, _GoBytes_ code_hash, int64_t gas, | ||
int32_t depth, uint32_t flags, _GoBytes_ code) | ||
{ | ||
struct evmc_message msg; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be good idea to default-initialize it here first? ( = {};
is it valid in C?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not in the pedantic C, but here it should work.
bindings/go/evmc/evmc.go
Outdated
case C.EVMC_LOADER_SYMBOL_NOT_FOUND: | ||
err = fmt.Errorf("evmc loader: the EVMC create function not found in %s", filename) | ||
case C.EVMC_LOADER_INVALID_ARGUMENT: | ||
panic("evmc loader: filename argument is empty") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also can be too long
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not filename argument is invalid (empty, too long, etc.)
?
Basically I would like to have some guarantees that memory sizes equal between the C and Go data structures. Is that achievable somehow? |
bindings/go/evmc/host.go
Outdated
DelegateCall = C.EVMC_DELEGATECALL | ||
CallCode = C.EVMC_CALLCODE | ||
Create = C.EVMC_CREATE | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to add Create2
here.
Needs to be rebased to pick up latest ABI. |
|
||
extern const struct evmc_context_fn_table evmc_go_fn_table; | ||
|
||
static struct evmc_result execute_wrapper(struct evmc_instance* instance, int64_t context_index, enum evmc_revision rev, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this now have create2_salt
parameter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah it's only for calls...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be passed back to the Host interface. I have not done this yet.
} | ||
|
||
const ( | ||
Failure = Error(C.EVMC_FAILURE) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why consts only for two status codes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm waiting for a comment from Go devs, because sometimes they predefined errors as consts and sometimes create errors at hoc.
//export accountExists | ||
func accountExists(pCtx unsafe.Pointer, pAddr *C.struct_evmc_address) C.int { | ||
idx := int((*C.struct_extended_context)(pCtx).index) | ||
ctx := getHostContext(idx) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible that idx
points to non-existing context? Should we check it here or in getHostContext
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be always valid. Otherwise Go will raise an exception.
bindings/go/evmc/evmc.go
Outdated
} | ||
|
||
func (instance *Instance) Execute(ctx HostContext, rev Revision, | ||
destination common.Address, sender common.Address, value common.Hash, input []byte, codeHash common.Hash, gas int64, depth int, static bool, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is value
of Hash
type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because this is more closely to big-endian representation of integer in EVM. go-ethereum uses big.Big
type for it but can easily convert with common.BigToHash()
: https://github.com/ethereum/go-ethereum/pull/17050/files#diff-9633a3aa7f6fd345efaab4431bab4ae8R253.
However, this is inconsistent with Host.Call()
where big.Int
is used.
|
||
type Error int32 | ||
|
||
func (err Error) IsInternalError() bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't seem to be used below (or anywhere?).
return err < 0 | ||
} | ||
|
||
func (err Error) Error() string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would a name like ErrorToString
or EVMCErrorString
be more descriptive?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is implementation of error
interface.
type error interface {
Error() string
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah ok, so all the wrappers return a standard Error
object which contain an internal error value and this can be used to turn that into a string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It means the evmc.Error
type can used as the builtin error
type.
bindings/go/evmc/evmc.go
Outdated
msg.input_size = input.n; | ||
msg.gas = gas; | ||
msg.depth = depth; | ||
msg.kind = EVMC_CALL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This means there's no way to report a CREATE transaction?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The VM does not care. I'm not sure if this information is available in go-ethereum context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hera is because if it is a create it must do the metering on the input and the returned data.
return fmt.Sprintf("evmc: unknown internal error (%d)", int32(code)) | ||
} | ||
|
||
panic(fmt.Sprintf("evmc: unknown status code %d", int32(code))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure it is a good idea making this a panic?
bindings/go/evmc/loader.c
Outdated
* Licensed under the MIT License. See the LICENSE file. | ||
*/ | ||
|
||
#include "../../../lib/loader/loader.c" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not make this file a symlink instead? Is that not allowed by govendor or package managers?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do symlinks work on Windows?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Git allows symlinks, I am sure it resolves it somehow on Windows. @gumb0 do you know by any chance what git is doing in that case?
"testing" | ||
) | ||
|
||
func TestLoad(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is this used? What file is it loading?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are unit tests. You can run them with go test ...
. It will load file pointed by EVMC_PATH
.
Needs to be udated for EXTCODEHASH support |
66d2208
to
9242887
Compare
EXTCODEHASH is included. |
@chfast is anything blocking the merging of this? |
Missing reviews? I'd go and merge this if no more obvious issues. Maybe I should fix one FIXME comment from go-ethereum. Also this might be not compatible with the master branch. I base this on v5.0.0. But I can handle this. The benefit of merging this is to have it tested on CI in new PRs so the Go bindings will match the recent changes in the EVMC API. |
Even if the geth team hasn't reviewed this I'd merge it for the reasons you've said. |
bindings/go/evmc/evmc.go
Outdated
if (instance->set_option) | ||
return instance->set_option(instance, name.p, value.p); | ||
return 0; | ||
int r = evmc_set_option(instance, name, value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ret
instead of r
? It doesn't matter too much.
bindings/go/evmc/evmc.go
Outdated
return 0; | ||
int r = evmc_set_option(instance, name, value); | ||
free(name); | ||
free(value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So C.CString
allocates a new pointer but throws away ownership of it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.
// Go string to C string
// The C string is allocated in the C heap using malloc.
// It is the caller's responsibility to arrange for it to be
// freed, such as by calling C.free (be sure to include stdlib.h
// if C.free is needed).
func C.CString(string) *C.char
bindings/go/evmc/evmc.go
Outdated
s := evmcAddress(sender) | ||
v := evmcUint256be(value) | ||
ch := evmcUint256be(codeHash) | ||
r := C.execute_wrapper(instance.handle, C.int64_t(ctxId), uint32(rev), &d, &s, &v, input, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use more meaningful variable names?
gas, | ||
depth, | ||
kind, | ||
flags, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not keep the named version? Can you have named members here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm counting on compiler warning in case a field is missed.
Rebased on master and squashed. |
(evmc_emit_log_fn)emitLog, | ||
}; | ||
|
||
void evmc_go_free_result_output(const struct evmc_result* result) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually instead of this one could use evmc_release_result
from helpers.h
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually not. This is the implementation. evmc_release_result
is just a wrapper that calls the implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My bad, too early.
Can you help with the review @gballet, @fjl?
Appropriate go-ethereum PR: ethereum/go-ethereum#17050