Skip to content

Commit

Permalink
Merge pull request #197 from ethereum/go-txcontext
Browse files Browse the repository at this point in the history
go: Combine tx context into TxContext struct
  • Loading branch information
axic authored Mar 13, 2019
2 parents 8bf667c + 1ad5f5e commit f6b1059
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 24 deletions.
2 changes: 2 additions & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions bindings/go/evmc/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.so
4 changes: 2 additions & 2 deletions bindings/go/evmc/evmc.go
Original file line number Diff line number Diff line change
@@ -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

Expand Down
41 changes: 33 additions & 8 deletions bindings/go/evmc/evmc_test.go
Original file line number Diff line number Diff line change
@@ -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] < '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 i.Version()[0] != '1' {
t.Fatalf("version is %s", i.Version())
if err != Failure {
t.Error("execution returned unexpected error")
}
}
34 changes: 22 additions & 12 deletions bindings/go/evmc/host.go
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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),
}
}

Expand Down
90 changes: 90 additions & 0 deletions bindings/go/evmc/host_test.go
Original file line number Diff line number Diff line change
@@ -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")
}
}
2 changes: 2 additions & 0 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
24 changes: 22 additions & 2 deletions examples/example_vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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;
Expand Down

0 comments on commit f6b1059

Please # to comment.