Merge branch 'pr/evmjit' of https://github.com/chfast/go-ethereum into chfast-pr/evmjit

This commit is contained in:
obscuren 2015-01-27 16:30:38 +01:00
commit fe14b0b82e
7 changed files with 209 additions and 133 deletions

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`
@ -46,9 +43,11 @@ Go Ethereum comes with several binaries found in
* `mist` Official Ethereum Browser * `mist` Official Ethereum Browser
* `ethereum` Ethereum CLI * `ethereum` Ethereum CLI
* `ethtest` test tool which runs with the [tests](https://github.com/ethereum/testes) suit: * `ethtest` test tool which runs with the [tests](https://github.com/ethereum/testes) suit:
`ethtest "`cat myfile.json`"`. `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

@ -1,12 +1,20 @@
package crypto package crypto
import ( import (
"crypto/aes"
"crypto/cipher"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"encoding/hex"
"encoding/json"
"errors"
"code.google.com/p/go-uuid/uuid"
"code.google.com/p/go.crypto/pbkdf2"
"code.google.com/p/go.crypto/ripemd160" "code.google.com/p/go.crypto/ripemd160"
"github.com/ethereum/go-ethereum/crypto/secp256k1" "github.com/ethereum/go-ethereum/crypto/secp256k1"
"github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/crypto/sha3"
@ -118,3 +126,100 @@ func Decrypt(prv *ecdsa.PrivateKey, ct []byte) ([]byte, error) {
key := ecies.ImportECDSA(prv) key := ecies.ImportECDSA(prv)
return key.Decrypt(rand.Reader, ct, nil, nil) return key.Decrypt(rand.Reader, ct, nil, nil)
} }
// creates a Key and stores that in the given KeyStore by decrypting a presale key JSON
func ImportPreSaleKey(keyStore KeyStore2, keyJSON []byte, password string) (*Key, error) {
key, err := decryptPreSaleKey(keyJSON, password)
if err != nil {
return nil, err
}
id := uuid.NewRandom()
key.Id = &id
err = keyStore.StoreKey(key, password)
return key, err
}
func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error) {
preSaleKeyStruct := struct {
EncSeed string
EthAddr string
Email string
BtcAddr string
}{}
err = json.Unmarshal(fileContent, &preSaleKeyStruct)
if err != nil {
return nil, err
}
encSeedBytes, err := hex.DecodeString(preSaleKeyStruct.EncSeed)
iv := encSeedBytes[:16]
cipherText := encSeedBytes[16:]
/*
See https://github.com/ethereum/pyethsaletool
pyethsaletool generates the encryption key from password by
2000 rounds of PBKDF2 with HMAC-SHA-256 using password as salt (:().
16 byte key length within PBKDF2 and resulting key is used as AES key
*/
passBytes := []byte(password)
derivedKey := pbkdf2.Key(passBytes, passBytes, 2000, 16, sha256.New)
plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv)
ethPriv := Sha3(plainText)
ecKey := ToECDSA(ethPriv)
key = &Key{
Id: nil,
PrivateKey: ecKey,
}
derivedAddr := ethutil.Bytes2Hex(key.Address())
expectedAddr := preSaleKeyStruct.EthAddr
if derivedAddr != expectedAddr {
err = errors.New("decrypted addr not equal to expected addr")
}
return key, err
}
func aesCBCDecrypt(key []byte, cipherText []byte, iv []byte) (plainText []byte, err error) {
aesBlock, err := aes.NewCipher(key)
if err != nil {
return plainText, err
}
decrypter := cipher.NewCBCDecrypter(aesBlock, iv)
paddedPlainText := make([]byte, len(cipherText))
decrypter.CryptBlocks(paddedPlainText, cipherText)
plainText = PKCS7Unpad(paddedPlainText)
if plainText == nil {
err = errors.New("Decryption failed: PKCS7Unpad failed after decryption")
}
return plainText, err
}
// From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes
func PKCS7Pad(in []byte) []byte {
padding := 16 - (len(in) % 16)
if padding == 0 {
padding = 16
}
for i := 0; i < padding; i++ {
in = append(in, byte(padding))
}
return in
}
func PKCS7Unpad(in []byte) []byte {
if len(in) == 0 {
return nil
}
padding := in[len(in)-1]
if int(padding) > len(in) || padding > aes.BlockSize {
return nil
} else if padding == 0 {
return nil
}
for i := len(in) - 1; i > len(in)-int(padding)-1; i-- {
if in[i] != padding {
return nil
}
}
return in[:len(in)-int(padding)]
}

View File

@ -57,7 +57,7 @@ type encryptedKeyJSON struct {
func (k *Key) Address() []byte { func (k *Key) Address() []byte {
pubBytes := FromECDSAPub(&k.PrivateKey.PublicKey) pubBytes := FromECDSAPub(&k.PrivateKey.PublicKey)
return Sha3(pubBytes)[12:] return Sha3(pubBytes[1:])[12:]
} }
func (k *Key) MarshalJSON() (j []byte, err error) { func (k *Key) MarshalJSON() (j []byte, err error) {
@ -99,9 +99,10 @@ func NewKey(rand io.Reader) *Key {
privateKeyMarshalled := elliptic.Marshal(S256(), x, y) privateKeyMarshalled := elliptic.Marshal(S256(), x, y)
privateKeyECDSA := ToECDSA(privateKeyMarshalled) privateKeyECDSA := ToECDSA(privateKeyMarshalled)
key := new(Key)
id := uuid.NewRandom() id := uuid.NewRandom()
key.Id = &id key := &Key{
key.PrivateKey = privateKeyECDSA Id: &id,
PrivateKey: privateKeyECDSA,
}
return key return key
} }

View File

@ -178,22 +178,10 @@ func DecryptKey(ks keyStorePassphrase, keyId *uuid.UUID, auth string) (keyBytes
if err != nil { if err != nil {
return nil, err return nil, err
} }
plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv)
AES256Block, err := aes.NewCipher(derivedKey)
if err != nil { if err != nil {
return nil, err return nil, err
} }
AES256CBCDecrypter := cipher.NewCBCDecrypter(AES256Block, iv)
paddedPlainText := make([]byte, len(cipherText))
AES256CBCDecrypter.CryptBlocks(paddedPlainText, cipherText)
plainText := PKCS7Unpad(paddedPlainText)
if plainText == nil {
err = errors.New("Decryption failed: PKCS7Unpad failed after decryption")
return nil, err
}
keyBytes = plainText[:len(plainText)-32] keyBytes = plainText[:len(plainText)-32]
keyBytesHash := plainText[len(plainText)-32:] keyBytesHash := plainText[len(plainText)-32:]
if !bytes.Equal(Sha3(keyBytes), keyBytesHash) { if !bytes.Equal(Sha3(keyBytes), keyBytesHash) {
@ -211,35 +199,3 @@ func getEntropyCSPRNG(n int) []byte {
} }
return mainBuff return mainBuff
} }
// From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes
func PKCS7Pad(in []byte) []byte {
padding := 16 - (len(in) % 16)
if padding == 0 {
padding = 16
}
for i := 0; i < padding; i++ {
in = append(in, byte(padding))
}
return in
}
func PKCS7Unpad(in []byte) []byte {
if len(in) == 0 {
return nil
}
padding := in[len(in)-1]
if int(padding) > len(in) || padding > aes.BlockSize {
return nil
} else if padding == 0 {
return nil
}
for i := len(in) - 1; i > len(in)-int(padding)-1; i-- {
if in[i] != padding {
return nil
}
}
return in[:len(in)-int(padding)]
}

View File

@ -83,3 +83,16 @@ func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestImportPreSaleKey(t *testing.T) {
// file content of a presale key file generated with:
// python pyethsaletool.py genwallet
// with password "foo"
fileContent := "{\"encseed\": \"26d87f5f2bf9835f9a47eefae571bc09f9107bb13d54ff12a4ec095d01f83897494cf34f7bed2ed34126ecba9db7b62de56c9d7cd136520a0427bfb11b8954ba7ac39b90d4650d3448e31185affcd74226a68f1e94b1108e6e0a4a91cdd83eba\", \"ethaddr\": \"d4584b5f6229b7be90727b0fc8c6b91bb427821f\", \"email\": \"gustav.simonsson@gmail.com\", \"btcaddr\": \"1EVknXyFC68kKNLkh6YnKzW41svSRoaAcx\"}"
ks := NewKeyStorePassphrase(DefaultDataDir())
pass := "foo"
_, err := ImportPreSaleKey(ks, []byte(fileContent), pass)
if err != nil {
t.Fatal(err)
}
}

View File

@ -3,18 +3,13 @@
package vm package vm
/* /*
#include <stdint.h>
#include <stdlib.h>
struct evmjit_result void* evmjit_create();
{ int evmjit_run(void* _jit, void* _data, void* _env);
int32_t returnCode; void evmjit_destroy(void* _jit);
uint64_t returnDataSize;
void* returnData;
};
struct evmjit_result evmjit_run(void* _data, void* _env);
// 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 #cgo LDFLAGS: -levmjit
*/ */
import "C" import "C"
@ -39,32 +34,22 @@ type JitVm struct {
type i256 [32]byte type i256 [32]byte
const (
Gas = iota
address
Caller
Origin
CallValue
CallDataSize
GasPrice
CoinBase
TimeStamp
Number
Difficulty
GasLimit
CodeSize
_size
ReturnDataOffset = CallValue // Reuse 2 fields for return data reference
ReturnDataSize = CallDataSize
SuicideDestAddress = address ///< Suicide balance destination address
)
type RuntimeData struct { type RuntimeData struct {
elems [_size]i256 gas int64
gasPrice int64
callData *byte callData *byte
callDataSize uint64
address i256
caller i256
origin i256
callValue i256
coinBase i256
difficulty i256
gasLimit i256
number uint64
timestamp int64
code *byte code *byte
codeSize uint64
} }
func hash2llvm(h []byte) i256 { func hash2llvm(h []byte) i256 {
@ -74,10 +59,11 @@ func hash2llvm(h []byte) i256 {
} }
func llvm2hash(m *i256) []byte { func llvm2hash(m *i256) []byte {
if len(m) != 32 { return C.GoBytes(unsafe.Pointer(m), C.int(len(m)))
panic("I don't know Go!") }
}
return C.GoBytes(unsafe.Pointer(m), 32) func llvm2hashRef(m *i256) []byte {
return (*[1 << 30]byte)(unsafe.Pointer(m))[:len(m):len(m)]
} }
func address2llvm(addr []byte) i256 { func address2llvm(addr []byte) i256 {
@ -86,6 +72,8 @@ func address2llvm(addr []byte) i256 {
return 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 { func bswap(m *i256) *i256 {
for i, l := 0, len(m); i < l/2; i++ { for i, l := 0, len(m); i < l/2; i++ {
m[i], m[l-i-1] = m[l-i-1], m[i] m[i], m[l-i-1] = m[l-i-1], m[i]
@ -129,12 +117,14 @@ func llvm2big(m *i256) *big.Int {
return n return n
} }
func llvm2bytes(data *byte, length uint64) []byte { // 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 { if length == 0 {
return nil return nil
} }
if data == nil { if data == nil {
panic("llvm2bytes: nil pointer to data") panic("Unexpected nil data pointer")
} }
return (*[1 << 30]byte)(unsafe.Pointer(data))[:length:length] return (*[1 << 30]byte)(unsafe.Pointer(data))[:length:length]
} }
@ -156,8 +146,10 @@ func NewJitVm(env Environment) *JitVm {
} }
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) {
// TODO: depth is increased but never checked by VM. VM should not know about it at all.
self.env.SetDepth(self.env.Depth() + 1) self.env.SetDepth(self.env.Depth() + 1)
// TODO: Move it to Env.Call() or sth
if Precompiled[string(me.Address())] != nil { if Precompiled[string(me.Address())] != nil {
// if it's address of precopiled contract // if it's address of precopiled contract
// fallback to standard VM // fallback to standard VM
@ -165,42 +157,44 @@ func (self *JitVm) Run(me, caller ContextRef, code []byte, value, gas, price *bi
return stdVm.Run(me, caller, code, value, gas, price, callData) return stdVm.Run(me, caller, code, value, gas, price, callData)
} }
self.me = me // FIXME: Make sure Run() is not used more than once if self.me != nil {
panic("JitVm.Run() can be called only once per JitVm instance")
}
self.me = me
self.callerAddr = caller.Address() self.callerAddr = caller.Address()
self.price = price self.price = price
self.data.elems[Gas] = big2llvm(gas) self.data.gas = gas.Int64()
self.data.elems[address] = address2llvm(self.me.Address()) self.data.gasPrice = price.Int64()
self.data.elems[Caller] = address2llvm(caller.Address())
self.data.elems[Origin] = address2llvm(self.env.Origin())
self.data.elems[CallValue] = big2llvm(value)
self.data.elems[CallDataSize] = big2llvm(big.NewInt(int64(len(callData)))) // TODO: Keep call data size as i64
self.data.elems[GasPrice] = big2llvm(price)
self.data.elems[CoinBase] = address2llvm(self.env.Coinbase())
self.data.elems[TimeStamp] = big2llvm(big.NewInt(self.env.Time())) // TODO: Keep timestamp as i64
self.data.elems[Number] = big2llvm(self.env.BlockNumber())
self.data.elems[Difficulty] = big2llvm(self.env.Difficulty())
self.data.elems[GasLimit] = big2llvm(self.env.GasLimit())
self.data.elems[CodeSize] = big2llvm(big.NewInt(int64(len(code)))) // TODO: Keep code size as i64
self.data.callData = getDataPtr(callData) 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.code = getDataPtr(code)
self.data.codeSize = uint64(len(code))
result := C.evmjit_run(unsafe.Pointer(&self.data), unsafe.Pointer(self)) jit := C.evmjit_create()
//fmt.Printf("JIT result: %d\n", r) retCode := C.evmjit_run(jit, unsafe.Pointer(&self.data), unsafe.Pointer(self))
if result.returnCode >= 100 { if retCode < 0 {
err = errors.New("OOG from JIT") err = errors.New("OOG from JIT")
gas.SetInt64(0) // Set gas to 0, JIT does not bother gas.SetInt64(0) // Set gas to 0, JIT does not bother
} else { } else {
gasLeft := llvm2big(&self.data.elems[Gas]) // TODO: Set value directly to gas instance gas.SetInt64(self.data.gas)
gas.Set(gasLeft) if retCode == 1 { // RETURN
if result.returnCode == 1 { // RETURN ret = C.GoBytes(unsafe.Pointer(self.data.callData), C.int(self.data.callDataSize))
ret = C.GoBytes(result.returnData, C.int(result.returnDataSize)) } else if retCode == 2 { // SUICIDE
C.free(result.returnData) // TODO: Suicide support logic should be moved to Env to be shared by VM implementations
} else if result.returnCode == 2 { // SUICIDE
state := self.Env().State() state := self.Env().State()
receiverAddr := llvm2hash(bswap(&self.data.elems[address])) receiverAddr := llvm2hashRef(bswap(&self.data.address))
receiverAddr = trim(receiverAddr) // TODO: trim all zeros or subslice 160bits?
receiver := state.GetOrNewStateObject(receiverAddr) receiver := state.GetOrNewStateObject(receiverAddr)
balance := state.GetBalance(me.Address()) balance := state.GetBalance(me.Address())
receiver.AddAmount(balance) receiver.AddAmount(balance)
@ -208,6 +202,7 @@ func (self *JitVm) Run(me, caller ContextRef, code []byte, value, gas, price *bi
} }
} }
C.evmjit_destroy(jit);
return return
} }
@ -224,8 +219,8 @@ func (self *JitVm) Env() Environment {
} }
//export env_sha3 //export env_sha3
func env_sha3(dataPtr unsafe.Pointer, length uint64, resultPtr unsafe.Pointer) { func env_sha3(dataPtr *byte, length uint64, resultPtr unsafe.Pointer) {
data := C.GoBytes(dataPtr, C.int(length)) data := llvm2bytesRef(dataPtr, length)
hash := crypto.Sha3(data) hash := crypto.Sha3(data)
result := (*i256)(resultPtr) result := (*i256)(resultPtr)
*result = hash2llvm(hash) *result = hash2llvm(hash)
@ -300,7 +295,7 @@ func env_call(_vm unsafe.Pointer, _gas unsafe.Pointer, _receiveAddr unsafe.Point
if balance.Cmp(value) >= 0 { if balance.Cmp(value) >= 0 {
receiveAddr := llvm2hash((*i256)(_receiveAddr)) receiveAddr := llvm2hash((*i256)(_receiveAddr))
inData := C.GoBytes(inDataPtr, C.int(inDataLen)) inData := C.GoBytes(inDataPtr, C.int(inDataLen))
outData := llvm2bytes(outDataPtr, outDataLen) outData := llvm2bytesRef(outDataPtr, outDataLen)
codeAddr := llvm2hash((*i256)(_codeAddr)) codeAddr := llvm2hash((*i256)(_codeAddr))
llvmGas := (*i256)(_gas) llvmGas := (*i256)(_gas)
gas := llvm2big(llvmGas) gas := llvm2big(llvmGas)