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 copy lib/loader/loader.c bindings/go/evmc
go build ./bindings/go/evmc go build ./bindings/go/evmc
go generate ./bindings/go/evmc
go test -v ./bindings/go/evmc
} }
after_build: 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. // EVMC: Ethereum Client-VM Connector API.
// Copyright 2018 The EVMC Authors. // Copyright 2019 The EVMC Authors.
// Licensed under the Apache License, Version 2.0. See the LICENSE file. // Licensed under the Apache License, Version 2.0.
package evmc package evmc

View File

@ -1,24 +1,49 @@
// EVMC: Ethereum Client-VM Connector API. // EVMC: Ethereum Client-VM Connector API.
// Copyright 2018 The EVMC Authors. // Copyright 2019 The EVMC Authors.
// Licensed under the Apache License, Version 2.0. See the LICENSE file. // Licensed under the Apache License, Version 2.0.
//go:generate gcc -shared ../../../examples/example_vm.c -I../../../include -o example_vm.so
package evmc package evmc
import ( import (
"os" "bytes"
"testing" "testing"
"github.com/ethereum/go-ethereum/common"
) )
var modulePath = "./example_vm.so"
func TestLoad(t *testing.T) { func TestLoad(t *testing.T) {
i, err := Load(os.Getenv("EVMC_PATH")) i, err := Load(modulePath)
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatal(err.Error())
} }
defer i.Destroy() defer i.Destroy()
if i.Name() != "interpreter" { if i.Name() != "example_vm" {
t.Fatal("name is not 'interpreter'") t.Fatalf("name is %s", i.Name())
} }
if i.Version()[0] != '1' { if i.Version()[0] < '0' || i.Version()[0] > '9' {
t.Fatalf("version is %s", i.Version()) 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. // EVMC: Ethereum Client-VM Connector API.
// Copyright 2018 The EVMC Authors. // Copyright 2019 The EVMC Authors.
// Licensed under the Apache License, Version 2.0. See the LICENSE file. // Licensed under the Apache License, Version 2.0.
package evmc 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] 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 { type HostContext interface {
AccountExists(addr common.Address) bool AccountExists(addr common.Address) bool
GetStorage(addr common.Address, key common.Hash) common.Hash GetStorage(addr common.Address, key common.Hash) common.Hash
@ -78,8 +89,7 @@ type HostContext interface {
GetCodeHash(addr common.Address) common.Hash GetCodeHash(addr common.Address) common.Hash
GetCode(addr common.Address) []byte GetCode(addr common.Address) []byte
Selfdestruct(addr common.Address, beneficiary common.Address) Selfdestruct(addr common.Address, beneficiary common.Address)
GetTxContext() (gasPrice common.Hash, origin common.Address, coinbase common.Address, number int64, timestamp int64, GetTxContext() TxContext
gasLimit int64, difficulty common.Hash)
GetBlockHash(number int64) common.Hash GetBlockHash(number int64) common.Hash
EmitLog(addr common.Address, topics []common.Hash, data []byte) EmitLog(addr common.Address, topics []common.Hash, data []byte)
Call(kind CallKind, 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) idx := int((*C.struct_extended_context)(pCtx).index)
ctx := getHostContext(idx) ctx := getHostContext(idx)
gasPrice, origin, coinbase, number, timestamp, gasLimit, difficulty := ctx.GetTxContext() txContext := ctx.GetTxContext()
return C.struct_evmc_tx_context{ return C.struct_evmc_tx_context{
evmcBytes32(gasPrice), evmcBytes32(txContext.GasPrice),
evmcAddress(origin), evmcAddress(txContext.Origin),
evmcAddress(coinbase), evmcAddress(txContext.Coinbase),
C.int64_t(number), C.int64_t(txContext.Number),
C.int64_t(timestamp), C.int64_t(txContext.Timestamp),
C.int64_t(gasLimit), C.int64_t(txContext.GasLimit),
evmcBytes32(difficulty), 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 get -v github.com/ethereum/go-ethereum/common
go build -v ./bindings/go/evmc go build -v ./bindings/go/evmc
go vet -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: bindings-go-1.9:
docker: 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_data = (const uint8_t*)error;
ret.output_size = strlen(error); ret.output_size = strlen(error);
ret.status_code = EVMC_FAILURE; ret.status_code = EVMC_FAILURE;
ret.gas_left = msg->gas / 10;
ret.release = NULL; // We don't need to release the constant messages. ret.release = NULL; // We don't need to release the constant messages.
return ret; return ret;
} }
@ -106,7 +107,10 @@ static struct evmc_result execute(struct evmc_instance* instance,
// Assembly: `{ sstore(0, add(sload(0), 1)) }` // Assembly: `{ sstore(0, add(sload(0), 1)) }`
const char counter[] = "\x60\x01\x60\x00\x54\x01\x60\x00\x55"; 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) strncmp((const char*)code, return_address, code_size) == 0)
{ {
static const size_t address_size = sizeof(msg->destination); 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; ret.release = &free_result_output_data;
return ret; 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}}; const evmc_bytes32 key = {{0}};
evmc_bytes32 value = context->host->get_storage(context, &msg->destination, &key); 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; ret.status_code = EVMC_SUCCESS;
return ret; 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.status_code = EVMC_FAILURE;
ret.gas_left = 0; ret.gas_left = 0;