Merge pull request #197 from ethereum/go-txcontext

go: Combine tx context into TxContext struct
This commit is contained in:
Alex Beregszaszi 2019-03-13 15:39:12 +01:00 committed by GitHub
commit f6b10594ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 174 additions and 24 deletions

View File

@ -47,6 +47,8 @@ build_script:
copy lib/loader/loader.c bindings/go/evmc
go build ./bindings/go/evmc
go generate ./bindings/go/evmc
go test -v ./bindings/go/evmc
}
after_build:

1
bindings/go/evmc/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.so

View File

@ -1,6 +1,6 @@
// EVMC: Ethereum Client-VM Connector API.
// Copyright 2018 The EVMC Authors.
// Licensed under the Apache License, Version 2.0. See the LICENSE file.
// Copyright 2019 The EVMC Authors.
// Licensed under the Apache License, Version 2.0.
package evmc

View File

@ -1,24 +1,49 @@
// EVMC: Ethereum Client-VM Connector API.
// Copyright 2018 The EVMC Authors.
// Licensed under the Apache License, Version 2.0. See the LICENSE file.
// Copyright 2019 The EVMC Authors.
// Licensed under the Apache License, Version 2.0.
//go:generate gcc -shared ../../../examples/example_vm.c -I../../../include -o example_vm.so
package evmc
import (
"os"
"bytes"
"testing"
"github.com/ethereum/go-ethereum/common"
)
var modulePath = "./example_vm.so"
func TestLoad(t *testing.T) {
i, err := Load(os.Getenv("EVMC_PATH"))
i, err := Load(modulePath)
if err != nil {
t.Fatal(err.Error())
}
defer i.Destroy()
if i.Name() != "interpreter" {
t.Fatal("name is not 'interpreter'")
if i.Name() != "example_vm" {
t.Fatalf("name is %s", i.Name())
}
if i.Version()[0] != '1' {
t.Fatalf("version is %s", i.Version())
if i.Version()[0] < '0' || i.Version()[0] > '9' {
t.Fatalf("version number is weird: %s", i.Version())
}
}
func TestExecute(t *testing.T) {
vm, _ := Load(modulePath)
defer vm.Destroy()
addr := common.Address{}
h := common.Hash{}
output, gasLeft, err := vm.Execute(nil, Byzantium, Call, false, 1, 999, addr, addr, nil, h, nil, h)
if bytes.Compare(output, []byte("Welcome to Byzantium!")) != 0 {
t.Errorf("execution unexpected output: %s", output)
}
if gasLeft != 99 {
t.Error("execution gas left is incorrect")
}
if err != Failure {
t.Error("execution returned unexpected error")
}
}

View File

@ -1,6 +1,6 @@
// EVMC: Ethereum Client-VM Connector API.
// Copyright 2018 The EVMC Authors.
// Licensed under the Apache License, Version 2.0. See the LICENSE file.
// Copyright 2019 The EVMC Authors.
// Licensed under the Apache License, Version 2.0.
package evmc
@ -69,6 +69,17 @@ func goByteSlice(data *C.uint8_t, size C.size_t) []byte {
return (*[1 << 30]byte)(unsafe.Pointer(data))[:size:size]
}
// TxContext contains information about current transaction and block.
type TxContext struct {
GasPrice common.Hash
Origin common.Address
Coinbase common.Address
Number int64
Timestamp int64
GasLimit int64
Difficulty common.Hash
}
type HostContext interface {
AccountExists(addr common.Address) bool
GetStorage(addr common.Address, key common.Hash) common.Hash
@ -78,8 +89,7 @@ type HostContext interface {
GetCodeHash(addr common.Address) common.Hash
GetCode(addr common.Address) []byte
Selfdestruct(addr common.Address, beneficiary common.Address)
GetTxContext() (gasPrice common.Hash, origin common.Address, coinbase common.Address, number int64, timestamp int64,
gasLimit int64, difficulty common.Hash)
GetTxContext() TxContext
GetBlockHash(number int64) common.Hash
EmitLog(addr common.Address, topics []common.Hash, data []byte)
Call(kind CallKind,
@ -162,16 +172,16 @@ func getTxContext(pCtx unsafe.Pointer) C.struct_evmc_tx_context {
idx := int((*C.struct_extended_context)(pCtx).index)
ctx := getHostContext(idx)
gasPrice, origin, coinbase, number, timestamp, gasLimit, difficulty := ctx.GetTxContext()
txContext := ctx.GetTxContext()
return C.struct_evmc_tx_context{
evmcBytes32(gasPrice),
evmcAddress(origin),
evmcAddress(coinbase),
C.int64_t(number),
C.int64_t(timestamp),
C.int64_t(gasLimit),
evmcBytes32(difficulty),
evmcBytes32(txContext.GasPrice),
evmcAddress(txContext.Origin),
evmcAddress(txContext.Coinbase),
C.int64_t(txContext.Number),
C.int64_t(txContext.Timestamp),
C.int64_t(txContext.GasLimit),
evmcBytes32(txContext.Difficulty),
}
}

View File

@ -0,0 +1,90 @@
// EVMC: Ethereum Client-VM Connector API.
// Copyright 2019 The EVMC Authors.
// Licensed under the Apache License, Version 2.0.
package evmc
import (
"bytes"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
)
type testHostContext struct{}
func (host *testHostContext) AccountExists(addr common.Address) bool {
return false
}
func (host *testHostContext) GetStorage(addr common.Address, key common.Hash) common.Hash {
return common.Hash{}
}
func (host *testHostContext) SetStorage(addr common.Address, key common.Hash, value common.Hash) (status StorageStatus) {
return StorageUnchanged
}
func (host *testHostContext) GetBalance(addr common.Address) common.Hash {
return common.Hash{}
}
func (host *testHostContext) GetCodeSize(addr common.Address) int {
return 0
}
func (host *testHostContext) GetCodeHash(addr common.Address) common.Hash {
return common.Hash{}
}
func (host *testHostContext) GetCode(addr common.Address) []byte {
return nil
}
func (host *testHostContext) Selfdestruct(addr common.Address, beneficiary common.Address) {
}
func (host *testHostContext) GetTxContext() TxContext {
txContext := TxContext{}
txContext.Number = 42
return txContext
}
func (host *testHostContext) GetBlockHash(number int64) common.Hash {
return common.Hash{}
}
func (host *testHostContext) EmitLog(addr common.Address, topics []common.Hash, data []byte) {
}
func (host *testHostContext) Call(kind CallKind,
destination common.Address, sender common.Address, value *big.Int, input []byte, gas int64, depth int,
static bool, salt *big.Int) (output []byte, gasLeft int64, createAddr common.Address, err error) {
return nil, gas, common.Address{}, nil
}
func TestGetTxContext(t *testing.T) {
vm, _ := Load(modulePath)
defer vm.Destroy()
host := &testHostContext{}
code := []byte("\x43\x60\x00\x52\x59\x60\x00\xf3")
addr := common.Address{}
h := common.Hash{}
output, gasLeft, err := vm.Execute(host, Byzantium, Call, false, 1, 100, addr, addr, nil, h, code, h)
if len(output) != 20 {
t.Errorf("unexpected output size: %d", len(output))
}
if bytes.Compare(output[0:3], []byte("42\x00")) != 0 {
t.Errorf("execution unexpected output: %s", output)
}
if gasLeft != 50 {
t.Errorf("execution gas left is incorrect: %x", gasLeft)
}
if err != nil {
t.Error("execution returned unexpected error")
}
}

View File

@ -147,6 +147,8 @@ jobs:
go get -v github.com/ethereum/go-ethereum/common
go build -v ./bindings/go/evmc
go vet -v ./bindings/go/evmc
go generate -v ./bindings/go/evmc
go test -v ./bindings/go/evmc
bindings-go-1.9:
docker:

View File

@ -91,6 +91,7 @@ static struct evmc_result execute(struct evmc_instance* instance,
ret.output_data = (const uint8_t*)error;
ret.output_size = strlen(error);
ret.status_code = EVMC_FAILURE;
ret.gas_left = msg->gas / 10;
ret.release = NULL; // We don't need to release the constant messages.
return ret;
}
@ -106,7 +107,10 @@ static struct evmc_result execute(struct evmc_instance* instance,
// Assembly: `{ sstore(0, add(sload(0), 1)) }`
const char counter[] = "\x60\x01\x60\x00\x54\x01\x60\x00\x55";
if (code_size == strlen(return_address) &&
// Assembly: `{ mstore(0, number()) return(0, msize()) }`
const char return_block_number[] = "\x43\x60\x00\x52\x59\x60\x00\xf3";
if (code_size == (sizeof(return_address) - 1) &&
strncmp((const char*)code, return_address, code_size) == 0)
{
static const size_t address_size = sizeof(msg->destination);
@ -124,7 +128,8 @@ static struct evmc_result execute(struct evmc_instance* instance,
ret.release = &free_result_output_data;
return ret;
}
else if (code_size == strlen(counter) && strncmp((const char*)code, counter, code_size) == 0)
else if (code_size == (sizeof(counter) - 1) &&
strncmp((const char*)code, counter, code_size) == 0)
{
const evmc_bytes32 key = {{0}};
evmc_bytes32 value = context->host->get_storage(context, &msg->destination, &key);
@ -133,6 +138,21 @@ static struct evmc_result execute(struct evmc_instance* instance,
ret.status_code = EVMC_SUCCESS;
return ret;
}
else if (code_size == (sizeof(return_block_number) - 1) &&
strncmp((const char*)code, return_block_number, code_size) == 0)
{
const struct evmc_tx_context tx_context = context->host->get_tx_context(context);
const size_t output_size = 20;
uint8_t* output_data = (uint8_t*)calloc(1, output_size);
snprintf((char*)output_data, output_size, "%u", (unsigned)tx_context.block_number);
ret.status_code = EVMC_SUCCESS;
ret.gas_left = msg->gas / 2;
ret.output_data = output_data;
ret.output_size = output_size;
ret.release = &free_result_output_data;
return ret;
}
ret.status_code = EVMC_FAILURE;
ret.gas_left = 0;