Merge branch 'develop' into jsonrpc

This commit is contained in:
obscuren 2015-01-28 10:23:58 +01:00
commit 159c4d56cd
6 changed files with 389 additions and 31 deletions

2
.gitignore vendored
View File

@ -15,3 +15,5 @@
.#* .#*
*# *#
*~ *~
.project
.settings

View File

@ -1,30 +1,27 @@
[![Bugs](https://badge.waffle.io/ethereum/go-ethereum.png?label=bug&title=Bugs)](https://waffle.io/ethereum/go-ethereum) [![Bugs](https://badge.waffle.io/ethereum/go-ethereum.png?label=bug&title=Bugs)](https://waffle.io/ethereum/go-ethereum)
[![Stories in Ready](https://badge.waffle.io/ethereum/go-ethereum.png?label=ready&title=Ready)](https://waffle.io/ethereum/go-ethereum) [![Stories in Ready](https://badge.waffle.io/ethereum/go-ethereum.png?label=ready&title=Ready)](https://waffle.io/ethereum/go-ethereum)
[![Stories in [![Stories in Progress](https://badge.waffle.io/ethereum/go-ethereum.svg?label=in%20progress&title=In Progress)](http://waffle.io/ethereum/go-ethereum)
Progress](https://badge.waffle.io/ethereum/go-ethereum.svg?label=in%20progress&title=In Progress)](http://waffle.io/ethereum/go-ethereum) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/go-ethereum?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
Ethereum
Ethereum PoC-8
======== ========
[![Build * [![Build Status](http://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20master%20branch)](http://build.ethdev.com:8010/builders/Linux%20Go%20master%20branch/builds/-1) master
Status](http://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20master%20branch)](http://build.ethdev.com:8010/builders/Linux%20Go%20master%20branch/builds/-1) master [![Build * [![Build Status](http://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20develop%20branch)](http://build.ethdev.com:8010/builders/Linux%20Go%20develop%20branch/builds/-1) develop
Status](http://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20develop%20branch)](http://build.ethdev.com:8010/builders/Linux%20Go%20develop%20branch/builds/-1) develop * [![Travis-ci](https://api.travis-ci.org/ethereum/go-ethereum.svg)](https://travis-ci.org/ethereum/go-ethereum) travis-ci
[![Coverage Status](https://coveralls.io/repos/ethereum/go-ethereum/badge.png?branch=tests)](https://coveralls.io/r/ethereum/go-ethereum?branch=tests) tests * [![Coverage Status](https://coveralls.io/repos/ethereum/go-ethereum/badge.png?branch=tests)](https://coveralls.io/r/ethereum/go-ethereum?branch=tests)
Ethereum Go Client © 2014 Jeffrey Wilcke. Ethereum Go Client © 2014 Jeffrey Wilcke.
Current state: Proof of Concept 0.8
Ethereum is currently in its testing phase.
Build Build
===== =====
To build Mist (GUI): Mist (GUI):
`go get github.com/ethereum/go-ethereum/cmd/mist` `go get github.com/ethereum/go-ethereum/cmd/mist`
To build the node (CLI): Ethereum (CLI):
`go get github.com/ethereum/go-ethereum/cmd/ethereum` `go get github.com/ethereum/go-ethereum/cmd/ethereum`
@ -49,6 +46,8 @@ Go Ethereum comes with several binaries found in
`cat file | ethtest`. `cat file | ethtest`.
* `evm` is a generic Ethereum Virtual Machine: `evm -code 60ff60ff -gas * `evm` is a generic Ethereum Virtual Machine: `evm -code 60ff60ff -gas
10000 -price 0 -dump`. See `-h` for a detailed description. 10000 -price 0 -dump`. See `-h` for a detailed description.
* `rlpdump` converts a rlp stream to `interface{}`.
* `peerserver` simple P2P (noi-ethereum) peer server.
General command line options General command line options
============================ ============================
@ -125,3 +124,4 @@ expect you to write tests for me so I don't have to test your code
manually. (If you want to contribute by just writing tests that's fine manually. (If you want to contribute by just writing tests that's fine
too!) too!)

View File

@ -46,6 +46,8 @@ func insertChain(done chan bool, chainMan *ChainManager, chain types.Blocks, t *
} }
func TestChainInsertions(t *testing.T) { func TestChainInsertions(t *testing.T) {
t.Skip() // travil fails.
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
chain1, err := loadChain("valid1", t) chain1, err := loadChain("valid1", t)
@ -86,6 +88,8 @@ func TestChainInsertions(t *testing.T) {
} }
func TestChainMultipleInsertions(t *testing.T) { func TestChainMultipleInsertions(t *testing.T) {
t.Skip() // travil fails.
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
const max = 4 const max = 4
@ -130,6 +134,8 @@ func TestChainMultipleInsertions(t *testing.T) {
} }
func TestGetAncestors(t *testing.T) { func TestGetAncestors(t *testing.T) {
t.Skip() // travil fails.
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
var eventMux event.TypeMux var eventMux event.TypeMux
chainMan := NewChainManager(db, &eventMux) chainMan := NewChainManager(db, &eventMux)

View File

@ -172,47 +172,47 @@ func RunVmTest(p string, t *testing.T) {
// I've created a new function for each tests so it's easier to identify where the problem lies if any of them fail. // I've created a new function for each tests so it's easier to identify where the problem lies if any of them fail.
func TestVMArithmetic(t *testing.T) { func TestVMArithmetic(t *testing.T) {
const fn = "../files/vmtests/vmArithmeticTest.json" const fn = "../files/VMTests/vmArithmeticTest.json"
RunVmTest(fn, t) RunVmTest(fn, t)
} }
func TestBitwiseLogicOperation(t *testing.T) { func TestBitwiseLogicOperation(t *testing.T) {
const fn = "../files/vmtests/vmBitwiseLogicOperationTest.json" const fn = "../files/VMTests/vmBitwiseLogicOperationTest.json"
RunVmTest(fn, t) RunVmTest(fn, t)
} }
func TestBlockInfo(t *testing.T) { func TestBlockInfo(t *testing.T) {
const fn = "../files/vmtests/vmBlockInfoTest.json" const fn = "../files/VMTests/vmBlockInfoTest.json"
RunVmTest(fn, t) RunVmTest(fn, t)
} }
func TestEnvironmentalInfo(t *testing.T) { func TestEnvironmentalInfo(t *testing.T) {
const fn = "../files/vmtests/vmEnvironmentalInfoTest.json" const fn = "../files/VMTests/vmEnvironmentalInfoTest.json"
RunVmTest(fn, t) RunVmTest(fn, t)
} }
func TestFlowOperation(t *testing.T) { func TestFlowOperation(t *testing.T) {
const fn = "../files/vmtests/vmIOandFlowOperationsTest.json" const fn = "../files/VMTests/vmIOandFlowOperationsTest.json"
RunVmTest(fn, t) RunVmTest(fn, t)
} }
func TestPushDupSwap(t *testing.T) { func TestPushDupSwap(t *testing.T) {
const fn = "../files/vmtests/vmPushDupSwapTest.json" const fn = "../files/VMTests/vmPushDupSwapTest.json"
RunVmTest(fn, t) RunVmTest(fn, t)
} }
func TestVMSha3(t *testing.T) { func TestVMSha3(t *testing.T) {
const fn = "../files/vmtests/vmSha3Test.json" const fn = "../files/VMTests/vmSha3Test.json"
RunVmTest(fn, t) RunVmTest(fn, t)
} }
func TestVm(t *testing.T) { func TestVm(t *testing.T) {
const fn = "../files/vmtests/vmtests.json" const fn = "../files/VMTests/vmtests.json"
RunVmTest(fn, t) RunVmTest(fn, t)
} }
func TestVmLog(t *testing.T) { func TestVmLog(t *testing.T) {
const fn = "../files/vmtests/vmLogTest.json" const fn = "../files/VMTests/vmLogTest.json"
RunVmTest(fn, t) RunVmTest(fn, t)
} }

View File

@ -1,31 +1,371 @@
// +build evmjit
package vm package vm
import "math/big" /*
void* evmjit_create();
int evmjit_run(void* _jit, void* _data, void* _env);
void evmjit_destroy(void* _jit);
// Shared library evmjit (e.g. libevmjit.so) is expected to be installed in /usr/local/lib
// More: https://github.com/ethereum/evmjit
#cgo LDFLAGS: -levmjit
*/
import "C"
import (
"bytes"
"errors"
"fmt"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/state"
"math/big"
"unsafe"
)
type JitVm struct { type JitVm struct {
env Environment env Environment
backup *Vm me ContextRef
callerAddr []byte
price *big.Int
data RuntimeData
}
type i256 [32]byte
type RuntimeData struct {
gas int64
gasPrice int64
callData *byte
callDataSize uint64
address i256
caller i256
origin i256
callValue i256
coinBase i256
difficulty i256
gasLimit i256
number uint64
timestamp int64
code *byte
codeSize uint64
}
func hash2llvm(h []byte) i256 {
var m i256
copy(m[len(m)-len(h):], h) // right aligned copy
return m
}
func llvm2hash(m *i256) []byte {
return C.GoBytes(unsafe.Pointer(m), C.int(len(m)))
}
func llvm2hashRef(m *i256) []byte {
return (*[1 << 30]byte)(unsafe.Pointer(m))[:len(m):len(m)]
}
func address2llvm(addr []byte) i256 {
n := hash2llvm(addr)
bswap(&n)
return n
}
// bswap swap bytes of the 256-bit integer on LLVM side
// TODO: Do not change memory on LLVM side, that can conflict with memory access optimizations
func bswap(m *i256) *i256 {
for i, l := 0, len(m); i < l/2; i++ {
m[i], m[l-i-1] = m[l-i-1], m[i]
}
return m
}
func trim(m []byte) []byte {
skip := 0
for i := 0; i < len(m); i++ {
if m[i] == 0 {
skip++
} else {
break
}
}
return m[skip:]
}
func getDataPtr(m []byte) *byte {
var p *byte
if len(m) > 0 {
p = &m[0]
}
return p
}
func big2llvm(n *big.Int) i256 {
m := hash2llvm(n.Bytes())
bswap(&m)
return m
}
func llvm2big(m *i256) *big.Int {
n := big.NewInt(0)
for i := 0; i < len(m); i++ {
b := big.NewInt(int64(m[i]))
b.Lsh(b, uint(i)*8)
n.Add(n, b)
}
return n
}
// llvm2bytesRef creates a []byte slice that references byte buffer on LLVM side (as of that not controller by GC)
// User must asure that referenced memory is available to Go until the data is copied or not needed any more
func llvm2bytesRef(data *byte, length uint64) []byte {
if length == 0 {
return nil
}
if data == nil {
panic("Unexpected nil data pointer")
}
return (*[1 << 30]byte)(unsafe.Pointer(data))[:length:length]
}
func untested(condition bool, message string) {
if condition {
panic("Condition `" + message + "` tested. Remove assert.")
}
}
func assert(condition bool, message string) {
if !condition {
panic("Assert `" + message + "` failed!")
}
} }
func NewJitVm(env Environment) *JitVm { func NewJitVm(env Environment) *JitVm {
backupVm := New(env) return &JitVm{env: env}
return &JitVm{env: env, backup: backupVm}
} }
func (self *JitVm) Run(me, caller ContextRef, code []byte, value, gas, price *big.Int, callData []byte) (ret []byte, err error) { func (self *JitVm) Run(me, caller ContextRef, code []byte, value, gas, price *big.Int, callData []byte) (ret []byte, err error) {
return self.backup.Run(me, caller, code, value, gas, price, callData) // TODO: depth is increased but never checked by VM. VM should not know about it at all.
self.env.SetDepth(self.env.Depth() + 1)
// TODO: Move it to Env.Call() or sth
if Precompiled[string(me.Address())] != nil {
// if it's address of precopiled contract
// fallback to standard VM
stdVm := New(self.env)
return stdVm.Run(me, caller, code, value, gas, price, callData)
}
if self.me != nil {
panic("JitVm.Run() can be called only once per JitVm instance")
}
self.me = me
self.callerAddr = caller.Address()
self.price = price
self.data.gas = gas.Int64()
self.data.gasPrice = price.Int64()
self.data.callData = getDataPtr(callData)
self.data.callDataSize = uint64(len(callData))
self.data.address = address2llvm(self.me.Address())
self.data.caller = address2llvm(caller.Address())
self.data.origin = address2llvm(self.env.Origin())
self.data.callValue = big2llvm(value)
self.data.coinBase = address2llvm(self.env.Coinbase())
self.data.difficulty = big2llvm(self.env.Difficulty())
self.data.gasLimit = big2llvm(self.env.GasLimit())
self.data.number = self.env.BlockNumber().Uint64()
self.data.timestamp = self.env.Time()
self.data.code = getDataPtr(code)
self.data.codeSize = uint64(len(code))
jit := C.evmjit_create()
retCode := C.evmjit_run(jit, unsafe.Pointer(&self.data), unsafe.Pointer(self))
if retCode < 0 {
err = errors.New("OOG from JIT")
gas.SetInt64(0) // Set gas to 0, JIT does not bother
} else {
gas.SetInt64(self.data.gas)
if retCode == 1 { // RETURN
ret = C.GoBytes(unsafe.Pointer(self.data.callData), C.int(self.data.callDataSize))
} else if retCode == 2 { // SUICIDE
// TODO: Suicide support logic should be moved to Env to be shared by VM implementations
state := self.Env().State()
receiverAddr := llvm2hashRef(bswap(&self.data.address))
receiver := state.GetOrNewStateObject(receiverAddr)
balance := state.GetBalance(me.Address())
receiver.AddAmount(balance)
state.Delete(me.Address())
}
}
C.evmjit_destroy(jit);
return
} }
func (self *JitVm) Printf(format string, v ...interface{}) VirtualMachine { func (self *JitVm) Printf(format string, v ...interface{}) VirtualMachine {
return self.backup.Printf(format, v) return self
} }
func (self *JitVm) Endl() VirtualMachine { func (self *JitVm) Endl() VirtualMachine {
return self.backup.Endl() return self
} }
func (self *JitVm) Env() Environment { func (self *JitVm) Env() Environment {
return self.env return self.env
} }
//go is nice //export env_sha3
func env_sha3(dataPtr *byte, length uint64, resultPtr unsafe.Pointer) {
data := llvm2bytesRef(dataPtr, length)
hash := crypto.Sha3(data)
result := (*i256)(resultPtr)
*result = hash2llvm(hash)
}
//export env_sstore
func env_sstore(vmPtr unsafe.Pointer, indexPtr unsafe.Pointer, valuePtr unsafe.Pointer) {
vm := (*JitVm)(vmPtr)
index := llvm2hash(bswap((*i256)(indexPtr)))
value := llvm2hash(bswap((*i256)(valuePtr)))
value = trim(value)
if len(value) == 0 {
prevValue := vm.env.State().GetState(vm.me.Address(), index)
if len(prevValue) != 0 {
vm.Env().State().Refund(vm.callerAddr, GasSStoreRefund)
}
}
vm.env.State().SetState(vm.me.Address(), index, value)
}
//export env_sload
func env_sload(vmPtr unsafe.Pointer, indexPtr unsafe.Pointer, resultPtr unsafe.Pointer) {
vm := (*JitVm)(vmPtr)
index := llvm2hash(bswap((*i256)(indexPtr)))
value := vm.env.State().GetState(vm.me.Address(), index)
result := (*i256)(resultPtr)
*result = hash2llvm(value)
bswap(result)
}
//export env_balance
func env_balance(_vm unsafe.Pointer, _addr unsafe.Pointer, _result unsafe.Pointer) {
vm := (*JitVm)(_vm)
addr := llvm2hash((*i256)(_addr))
balance := vm.Env().State().GetBalance(addr)
result := (*i256)(_result)
*result = big2llvm(balance)
}
//export env_blockhash
func env_blockhash(_vm unsafe.Pointer, _number unsafe.Pointer, _result unsafe.Pointer) {
vm := (*JitVm)(_vm)
number := llvm2big((*i256)(_number))
result := (*i256)(_result)
currNumber := vm.Env().BlockNumber()
limit := big.NewInt(0).Sub(currNumber, big.NewInt(256))
if number.Cmp(limit) >= 0 && number.Cmp(currNumber) < 0 {
hash := vm.Env().GetHash(uint64(number.Int64()))
*result = hash2llvm(hash)
} else {
*result = i256{}
}
}
//export env_call
func env_call(_vm unsafe.Pointer, _gas unsafe.Pointer, _receiveAddr unsafe.Pointer, _value unsafe.Pointer, inDataPtr unsafe.Pointer, inDataLen uint64, outDataPtr *byte, outDataLen uint64, _codeAddr unsafe.Pointer) bool {
vm := (*JitVm)(_vm)
//fmt.Printf("env_call (depth %d)\n", vm.Env().Depth())
defer func() {
if r := recover(); r != nil {
fmt.Printf("Recovered in env_call (depth %d, out %p %d): %s\n", vm.Env().Depth(), outDataPtr, outDataLen, r)
}
}()
balance := vm.Env().State().GetBalance(vm.me.Address())
value := llvm2big((*i256)(_value))
if balance.Cmp(value) >= 0 {
receiveAddr := llvm2hash((*i256)(_receiveAddr))
inData := C.GoBytes(inDataPtr, C.int(inDataLen))
outData := llvm2bytesRef(outDataPtr, outDataLen)
codeAddr := llvm2hash((*i256)(_codeAddr))
llvmGas := (*i256)(_gas)
gas := llvm2big(llvmGas)
var out []byte
var err error
if bytes.Equal(codeAddr, receiveAddr) {
out, err = vm.env.Call(vm.me, codeAddr, inData, gas, vm.price, value)
} else {
out, err = vm.env.CallCode(vm.me, codeAddr, inData, gas, vm.price, value)
}
*llvmGas = big2llvm(gas)
if err == nil {
copy(outData, out)
return true
}
}
return false
}
//export env_create
func env_create(_vm unsafe.Pointer, _gas unsafe.Pointer, _value unsafe.Pointer, initDataPtr unsafe.Pointer, initDataLen uint64, _result unsafe.Pointer) {
vm := (*JitVm)(_vm)
value := llvm2big((*i256)(_value))
initData := C.GoBytes(initDataPtr, C.int(initDataLen)) // TODO: Unnecessary if low balance
result := (*i256)(_result)
*result = i256{}
llvmGas := (*i256)(_gas)
gas := llvm2big(llvmGas)
ret, suberr, ref := vm.env.Create(vm.me, nil, initData, gas, vm.price, value)
if suberr == nil {
dataGas := big.NewInt(int64(len(ret))) // TODO: Nto the best design. env.Create can do it, it has the reference to gas counter
dataGas.Mul(dataGas, GasCreateByte)
gas.Sub(gas, dataGas)
*result = hash2llvm(ref.Address())
}
*llvmGas = big2llvm(gas)
}
//export env_log
func env_log(_vm unsafe.Pointer, dataPtr unsafe.Pointer, dataLen uint64, _topic1 unsafe.Pointer, _topic2 unsafe.Pointer, _topic3 unsafe.Pointer, _topic4 unsafe.Pointer) {
vm := (*JitVm)(_vm)
data := C.GoBytes(dataPtr, C.int(dataLen))
topics := make([][]byte, 0, 4)
if _topic1 != nil {
topics = append(topics, llvm2hash((*i256)(_topic1)))
}
if _topic2 != nil {
topics = append(topics, llvm2hash((*i256)(_topic2)))
}
if _topic3 != nil {
topics = append(topics, llvm2hash((*i256)(_topic3)))
}
if _topic4 != nil {
topics = append(topics, llvm2hash((*i256)(_topic4)))
}
vm.Env().AddLog(state.NewLog(vm.me.Address(), topics, data))
}
//export env_extcode
func env_extcode(_vm unsafe.Pointer, _addr unsafe.Pointer, o_size *uint64) *byte {
vm := (*JitVm)(_vm)
addr := llvm2hash((*i256)(_addr))
code := vm.Env().State().GetCode(addr)
*o_size = uint64(len(code))
return getDataPtr(code)
}

10
vm/vm_jit_fake.go Normal file
View File

@ -0,0 +1,10 @@
// +build !evmjit
package vm
import "fmt"
func NewJitVm(env Environment) VirtualMachine {
fmt.Printf("Warning! EVM JIT not enabled.\n")
return New(env)
}