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

Go bindings #41

Merged
merged 1 commit into from
Aug 20, 2018
Merged

Go bindings #41

merged 1 commit into from
Aug 20, 2018

Conversation

chfast
Copy link
Member

@chfast chfast commented Jul 12, 2018

Can you help with the review @gballet, @fjl?

Appropriate go-ethereum PR: ethereum/go-ethereum#17050

@chfast chfast force-pushed the go branch 2 times, most recently from d946800 to d52cb9f Compare July 13, 2018 15:22
@chfast chfast requested review from axic and gumb0 July 13, 2018 15:24
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);
Copy link
Member

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?

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's easier to use _GoString_ type here, than use malloc/free to pass the string to the original helper.

Copy link
Member

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?

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh, yes. Good idea.

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};
Copy link
Member

Choose a reason for hiding this comment

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

Indentation?

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));
Copy link
Member

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?

Copy link
Member Author

Choose a reason for hiding this comment

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

  1. I'm not sure using a C assert is good idea here.
  2. Because only this Go module can use this function we know exactly what size it is (still prone to future mistakes).
  3. Because this does not compile with Go 1.9, this might be rewritten anyway.

{
if (instance->set_option)
return instance->set_option(instance, name.p, value.p);
return -1;
Copy link
Member

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?

Copy link
Member Author

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.

@axic
Copy link
Member

axic commented Jul 13, 2018

@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++ {
Copy link
Member

@axic axic Jul 13, 2018

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?

Copy link
Member Author

@chfast chfast Jul 13, 2018

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).

Copy link
Member

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++ {
Copy link
Member

Choose a reason for hiding this comment

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

Same here.

Copy link
Member

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]
Copy link
Member

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? :)

Copy link
Member Author

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.


#include <stdlib.h>

const struct evmc_context_fn_table fn_table = {
Copy link
Member

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.

Copy link
Member Author

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.

#include <stdlib.h>
#include <string.h>

inline int set_option(struct evmc_instance* instance, _GoString_ name, _GoString_ value)
Copy link
Member

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?

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 should.

@ethereum ethereum deleted a comment from axic Jul 16, 2018
@chfast chfast force-pushed the go branch 3 times, most recently from a9ebe2f to 0b5d0e4 Compare July 16, 2018 12:24
_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;
Copy link
Member

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?)

Copy link
Member Author

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.

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")
Copy link
Member

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

Copy link
Member

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.)?

@axic
Copy link
Member

axic commented Jul 25, 2018

Basically I would like to have some guarantees that memory sizes equal between the C and Go data structures. Is that achievable somehow?

DelegateCall = C.EVMC_DELEGATECALL
CallCode = C.EVMC_CALLCODE
Create = C.EVMC_CREATE
)
Copy link
Member

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.

@axic
Copy link
Member

axic commented Jul 25, 2018

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,
Copy link
Member

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?

Copy link
Member

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...

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 should be passed back to the Host interface. I have not done this yet.

}

const (
Failure = Error(C.EVMC_FAILURE)
Copy link
Member

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?

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'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)
Copy link
Member

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 ?

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 should be always valid. Otherwise Go will raise an exception.

}

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,
Copy link
Member

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?

Copy link
Member Author

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 {
Copy link
Member

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 {
Copy link
Member

@axic axic Jul 27, 2018

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?

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 is implementation of error interface.

type error interface {
    Error() string
}

Copy link
Member

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.

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 means the evmc.Error type can used as the builtin error type.

msg.input_size = input.n;
msg.gas = gas;
msg.depth = depth;
msg.kind = EVMC_CALL;
Copy link
Member

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?

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 VM does not care. I'm not sure if this information is available in go-ethereum context.

Copy link
Member

@axic axic Jul 27, 2018

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)))
Copy link
Member

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?

* Licensed under the MIT License. See the LICENSE file.
*/

#include "../../../lib/loader/loader.c"
Copy link
Member

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?

Copy link
Member Author

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?

Copy link
Member

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) {
Copy link
Member

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?

Copy link
Member Author

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.

@gumb0
Copy link
Member

gumb0 commented Jul 31, 2018

Needs to be udated for EXTCODEHASH support

@chfast chfast force-pushed the go branch 3 times, most recently from 66d2208 to 9242887 Compare August 16, 2018 13:38
@chfast
Copy link
Member Author

chfast commented Aug 16, 2018

Needs to be udated for EXTCODEHASH support

Needs to be udated for EXTCODEHASH support

EXTCODEHASH is included.

@axic
Copy link
Member

axic commented Aug 18, 2018

@chfast is anything blocking the merging of this?

@chfast
Copy link
Member Author

chfast commented Aug 18, 2018

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.

@axic
Copy link
Member

axic commented Aug 18, 2018

Even if the geth team hasn't reviewed this I'd merge it for the reasons you've said.

if (instance->set_option)
return instance->set_option(instance, name.p, value.p);
return 0;
int r = evmc_set_option(instance, name, value);
Copy link
Member

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.

return 0;
int r = evmc_set_option(instance, name, value);
free(name);
free(value);
Copy link
Member

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?

Copy link
Member Author

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

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,
Copy link
Member

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,
Copy link
Member

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?

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'm counting on compiler warning in case a field is missed.

@chfast chfast added this to the 5.1 milestone Aug 20, 2018
@chfast
Copy link
Member Author

chfast commented Aug 20, 2018

Rebased on master and squashed.
Works fine with geth.
If not objections I'm going to merge this.

@axic axic merged commit 58a8d22 into master Aug 20, 2018
@axic axic deleted the go branch August 20, 2018 13:31
(evmc_emit_log_fn)emitLog,
};

void evmc_go_free_result_output(const struct evmc_result* result)
Copy link
Member

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.

Copy link
Member Author

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.

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, too early.

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.

3 participants