Commit 7c4ed805 authored by Jeffrey Wilcke's avatar Jeffrey Wilcke

Merge pull request #1357 from obscuren/core-optimisations-2

core: optimisations
parents 9d8b512b 992e4f83
{ {
"ImportPath": "github.com/ethereum/go-ethereum", "ImportPath": "github.com/ethereum/go-ethereum",
"GoVersion": "go1.4.2", "GoVersion": "go1.4",
"Packages": [ "Packages": [
"./..." "./..."
], ],
...@@ -28,6 +28,11 @@ ...@@ -28,6 +28,11 @@
"ImportPath": "github.com/gizak/termui", "ImportPath": "github.com/gizak/termui",
"Rev": "bab8dce01c193d82bc04888a0a9a7814d505f532" "Rev": "bab8dce01c193d82bc04888a0a9a7814d505f532"
}, },
{
"ImportPath": "github.com/howeyc/fsnotify",
"Comment": "v0.9.0-11-g6b1ef89",
"Rev": "6b1ef893dc11e0447abda6da20a5203481878dda"
},
{ {
"ImportPath": "github.com/huin/goupnp", "ImportPath": "github.com/huin/goupnp",
"Rev": "5cff77a69fb22f5f1774c4451ea2aab63d4d2f20" "Rev": "5cff77a69fb22f5f1774c4451ea2aab63d4d2f20"
......
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
This diff is collapsed.
golang-lru
==========
This provides the `lru` package which implements a fixed-size
thread safe LRU cache. It is based on the cache in Groupcache.
Documentation
=============
Full docs are available on [Godoc](http://godoc.org/github.com/hashicorp/golang-lru)
Example
=======
Using the LRU is very simple:
```go
l, _ := New(128)
for i := 0; i < 256; i++ {
l.Add(i, nil)
}
if l.Len() != 128 {
panic(fmt.Sprintf("bad len: %v", l.Len()))
}
```
// This package provides a simple LRU cache. It is based on the
// LRU implementation in groupcache:
// https://github.com/golang/groupcache/tree/master/lru
package lru
import (
"container/list"
"errors"
"sync"
)
// Cache is a thread-safe fixed size LRU cache.
type Cache struct {
size int
evictList *list.List
items map[interface{}]*list.Element
lock sync.RWMutex
onEvicted func(key interface{}, value interface{})
}
// entry is used to hold a value in the evictList
type entry struct {
key interface{}
value interface{}
}
// New creates an LRU of the given size
func New(size int) (*Cache, error) {
return NewWithEvict(size, nil)
}
func NewWithEvict(size int, onEvicted func(key interface{}, value interface{})) (*Cache, error) {
if size <= 0 {
return nil, errors.New("Must provide a positive size")
}
c := &Cache{
size: size,
evictList: list.New(),
items: make(map[interface{}]*list.Element, size),
onEvicted: onEvicted,
}
return c, nil
}
// Purge is used to completely clear the cache
func (c *Cache) Purge() {
c.lock.Lock()
defer c.lock.Unlock()
if c.onEvicted != nil {
for k, v := range c.items {
c.onEvicted(k, v.Value.(*entry).value)
}
}
c.evictList = list.New()
c.items = make(map[interface{}]*list.Element, c.size)
}
// Add adds a value to the cache. Returns true if an eviction occured.
func (c *Cache) Add(key, value interface{}) bool {
c.lock.Lock()
defer c.lock.Unlock()
// Check for existing item
if ent, ok := c.items[key]; ok {
c.evictList.MoveToFront(ent)
ent.Value.(*entry).value = value
return false
}
// Add new item
ent := &entry{key, value}
entry := c.evictList.PushFront(ent)
c.items[key] = entry
evict := c.evictList.Len() > c.size
// Verify size not exceeded
if evict {
c.removeOldest()
}
return evict
}
// Get looks up a key's value from the cache.
func (c *Cache) Get(key interface{}) (value interface{}, ok bool) {
c.lock.Lock()
defer c.lock.Unlock()
if ent, ok := c.items[key]; ok {
c.evictList.MoveToFront(ent)
return ent.Value.(*entry).value, true
}
return
}
// Check if a key is in the cache, without updating the recent-ness or deleting it for being stale.
func (c *Cache) Contains(key interface{}) (ok bool) {
c.lock.RLock()
defer c.lock.RUnlock()
_, ok = c.items[key]
return ok
}
// Returns the key value (or undefined if not found) without updating the "recently used"-ness of the key.
// (If you find yourself using this a lot, you might be using the wrong sort of data structure, but there are some use cases where it's handy.)
func (c *Cache) Peek(key interface{}) (value interface{}, ok bool) {
c.lock.RLock()
defer c.lock.RUnlock()
if ent, ok := c.items[key]; ok {
return ent.Value.(*entry).value, true
}
return nil, ok
}
// Remove removes the provided key from the cache.
func (c *Cache) Remove(key interface{}) {
c.lock.Lock()
defer c.lock.Unlock()
if ent, ok := c.items[key]; ok {
c.removeElement(ent)
}
}
// RemoveOldest removes the oldest item from the cache.
func (c *Cache) RemoveOldest() {
c.lock.Lock()
defer c.lock.Unlock()
c.removeOldest()
}
// Keys returns a slice of the keys in the cache, from oldest to newest.
func (c *Cache) Keys() []interface{} {
c.lock.RLock()
defer c.lock.RUnlock()
keys := make([]interface{}, len(c.items))
ent := c.evictList.Back()
i := 0
for ent != nil {
keys[i] = ent.Value.(*entry).key
ent = ent.Prev()
i++
}
return keys
}
// Len returns the number of items in the cache.
func (c *Cache) Len() int {
c.lock.RLock()
defer c.lock.RUnlock()
return c.evictList.Len()
}
// removeOldest removes the oldest item from the cache.
func (c *Cache) removeOldest() {
ent := c.evictList.Back()
if ent != nil {
c.removeElement(ent)
}
}
// removeElement is used to remove a given list element from the cache
func (c *Cache) removeElement(e *list.Element) {
c.evictList.Remove(e)
kv := e.Value.(*entry)
delete(c.items, kv.key)
if c.onEvicted != nil {
c.onEvicted(kv.key, kv.value)
}
}
package lru
import "testing"
func TestLRU(t *testing.T) {
evictCounter := 0
onEvicted := func(k interface{}, v interface{}) {
if k != v {
t.Fatalf("Evict values not equal (%v!=%v)", k, v)
}
evictCounter += 1
}
l, err := NewWithEvict(128, onEvicted)
if err != nil {
t.Fatalf("err: %v", err)
}
for i := 0; i < 256; i++ {
l.Add(i, i)
}
if l.Len() != 128 {
t.Fatalf("bad len: %v", l.Len())
}
if evictCounter != 128 {
t.Fatalf("bad evict count: %v", evictCounter)
}
for i, k := range l.Keys() {
if v, ok := l.Get(k); !ok || v != k || v != i+128 {
t.Fatalf("bad key: %v", k)
}
}
for i := 0; i < 128; i++ {
_, ok := l.Get(i)
if ok {
t.Fatalf("should be evicted")
}
}
for i := 128; i < 256; i++ {
_, ok := l.Get(i)
if !ok {
t.Fatalf("should not be evicted")
}
}
for i := 128; i < 192; i++ {
l.Remove(i)
_, ok := l.Get(i)
if ok {
t.Fatalf("should be deleted")
}
}
l.Get(192) // expect 192 to be last key in l.Keys()
for i, k := range l.Keys() {
if (i < 63 && k != i+193) || (i == 63 && k != 192) {
t.Fatalf("out of order key: %v", k)
}
}
l.Purge()
if l.Len() != 0 {
t.Fatalf("bad len: %v", l.Len())
}
if _, ok := l.Get(200); ok {
t.Fatalf("should contain nothing")
}
}
// test that Add returns true/false if an eviction occured
func TestLRUAdd(t *testing.T) {
evictCounter := 0
onEvicted := func(k interface{}, v interface{}) {
evictCounter += 1
}
l, err := NewWithEvict(1, onEvicted)
if err != nil {
t.Fatalf("err: %v", err)
}
if l.Add(1, 1) == true || evictCounter != 0 {
t.Errorf("should not have an eviction")
}
if l.Add(2, 2) == false || evictCounter != 1 {
t.Errorf("should have an eviction")
}
}
// test that Contains doesn't update recent-ness
func TestLRUContains(t *testing.T) {
l, err := New(2)
if err != nil {
t.Fatalf("err: %v", err)
}
l.Add(1, 1)
l.Add(2, 2)
if !l.Contains(1) {
t.Errorf("1 should be contained")
}
l.Add(3, 3)
if l.Contains(1) {
t.Errorf("Contains should not have updated recent-ness of 1")
}
}
// test that Peek doesn't update recent-ness
func TestLRUPeek(t *testing.T) {
l, err := New(2)
if err != nil {
t.Fatalf("err: %v", err)
}
l.Add(1, 1)
l.Add(2, 2)
if v, ok := l.Peek(1); !ok || v != 1 {
t.Errorf("1 should be set to 1: %v, %v", v, ok)
}
l.Add(3, 3)
if l.Contains(1) {
t.Errorf("should not have updated recent-ness of 1")
}
}
package core
import (
"crypto/ecdsa"
"io/ioutil"
"math/big"
"os"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params"
)
func BenchmarkInsertChain_empty_memdb(b *testing.B) {
benchInsertChain(b, false, nil)
}
func BenchmarkInsertChain_empty_diskdb(b *testing.B) {
benchInsertChain(b, true, nil)
}
func BenchmarkInsertChain_valueTx_memdb(b *testing.B) {
benchInsertChain(b, false, genValueTx(0))
}
func BenchmarkInsertChain_valueTx_diskdb(b *testing.B) {
benchInsertChain(b, true, genValueTx(0))
}
func BenchmarkInsertChain_valueTx_100kB_memdb(b *testing.B) {
benchInsertChain(b, false, genValueTx(100*1024))
}
func BenchmarkInsertChain_valueTx_100kB_diskdb(b *testing.B) {
benchInsertChain(b, true, genValueTx(100*1024))
}
func BenchmarkInsertChain_uncles_memdb(b *testing.B) {
benchInsertChain(b, false, genUncles)
}
func BenchmarkInsertChain_uncles_diskdb(b *testing.B) {
benchInsertChain(b, true, genUncles)
}
func BenchmarkInsertChain_ring200_memdb(b *testing.B) {
benchInsertChain(b, false, genTxRing(200))
}
func BenchmarkInsertChain_ring200_diskdb(b *testing.B) {
benchInsertChain(b, true, genTxRing(200))
}
func BenchmarkInsertChain_ring1000_memdb(b *testing.B) {
benchInsertChain(b, false, genTxRing(1000))
}
func BenchmarkInsertChain_ring1000_diskdb(b *testing.B) {
benchInsertChain(b, true, genTxRing(1000))
}
var (
// This is the content of the genesis block used by the benchmarks.
benchRootKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
benchRootAddr = crypto.PubkeyToAddress(benchRootKey.PublicKey)
benchRootFunds = common.BigPow(2, 100)
)
// genValueTx returns a block generator that includes a single
// value-transfer transaction with n bytes of extra data in each
// block.
func genValueTx(nbytes int) func(int, *BlockGen) {
return func(i int, gen *BlockGen) {
toaddr := common.Address{}
data := make([]byte, nbytes)
gas := IntrinsicGas(data)
tx, _ := types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data).SignECDSA(benchRootKey)
gen.AddTx(tx)
}
}
var (
ringKeys = make([]*ecdsa.PrivateKey, 1000)
ringAddrs = make([]common.Address, len(ringKeys))
)
func init() {
ringKeys[0] = benchRootKey
ringAddrs[0] = benchRootAddr
for i := 1; i < len(ringKeys); i++ {
ringKeys[i], _ = crypto.GenerateKey()
ringAddrs[i] = crypto.PubkeyToAddress(ringKeys[i].PublicKey)
}
}
// genTxRing returns a block generator that sends ether in a ring
// among n accounts. This is creates n entries in the state database
// and fills the blocks with many small transactions.
func genTxRing(naccounts int) func(int, *BlockGen) {
from := 0
return func(i int, gen *BlockGen) {
gas := CalcGasLimit(gen.PrevBlock(i - 1))
for {
gas.Sub(gas, params.TxGas)
if gas.Cmp(params.TxGas) < 0 {
break
}
to := (from + 1) % naccounts
tx := types.NewTransaction(
gen.TxNonce(ringAddrs[from]),
ringAddrs[to],
benchRootFunds,
params.TxGas,
nil,
nil,
)
tx, _ = tx.SignECDSA(ringKeys[from])
gen.AddTx(tx)
from = to
}
}
}
// genUncles generates blocks with two uncle headers.
func genUncles(i int, gen *BlockGen) {
if i >= 6 {
b2 := gen.PrevBlock(i - 6).Header()
b2.Extra = []byte("foo")
gen.AddUncle(b2)
b3 := gen.PrevBlock(i - 6).Header()
b3.Extra = []byte("bar")
gen.AddUncle(b3)
}
}
func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
// Create the database in memory or in a temporary directory.
var db common.Database
if !disk {
db, _ = ethdb.NewMemDatabase()
} else {
dir, err := ioutil.TempDir("", "eth-core-bench")
if err != nil {
b.Fatalf("cannot create temporary directory: %v", err)
}
defer os.RemoveAll(dir)
db, err = ethdb.NewLDBDatabase(dir)
if err != nil {
b.Fatalf("cannot create temporary database: %v", err)
}
defer db.Close()
}
// Generate a chain of b.N blocks using the supplied block
// generator function.
genesis := GenesisBlockForTesting(db, benchRootAddr, benchRootFunds)
chain := GenerateChain(genesis, db, b.N, gen)
// Time the insertion of the new chain.
// State and blocks are stored in the same DB.
evmux := new(event.TypeMux)
chainman, _ := NewChainManager(genesis, db, db, FakePow{}, evmux)
chainman.SetProcessor(NewBlockProcessor(db, db, FakePow{}, chainman, evmux))
defer chainman.Stop()
b.ReportAllocs()
b.ResetTimer()
if i, err := chainman.InsertChain(chain); err != nil {
b.Fatalf("insert error (block %d): %v\n", i, err)
}
}
...@@ -11,12 +11,12 @@ import ( ...@@ -11,12 +11,12 @@ import (
func newChain(size int) (chain []*types.Block) { func newChain(size int) (chain []*types.Block) {
var parentHash common.Hash var parentHash common.Hash
for i := 0; i < size; i++ { for i := 0; i < size; i++ {
block := types.NewBlock(parentHash, common.Address{}, common.Hash{}, new(big.Int), 0, nil) head := &types.Header{ParentHash: parentHash, Number: big.NewInt(int64(i))}
block.Header().Number = big.NewInt(int64(i)) block := types.NewBlock(head, nil, nil, nil)
chain = append(chain, block) chain = append(chain, block)
parentHash = block.Hash() parentHash = block.Hash()
} }
return return chain
} }
func insertChainCache(cache *BlockCache, chain []*types.Block) { func insertChainCache(cache *BlockCache, chain []*types.Block) {
......
This diff is collapsed.
...@@ -26,20 +26,19 @@ func proc() (*BlockProcessor, *ChainManager) { ...@@ -26,20 +26,19 @@ func proc() (*BlockProcessor, *ChainManager) {
} }
func TestNumber(t *testing.T) { func TestNumber(t *testing.T) {
_, chain := proc()
block1 := chain.NewBlock(common.Address{})
block1.Header().Number = big.NewInt(3)
block1.Header().Time--
pow := ezp.New() pow := ezp.New()
_, chain := proc()
err := ValidateHeader(pow, block1.Header(), chain.Genesis().Header(), false) statedb := state.New(chain.Genesis().Root(), chain.stateDb)
header := makeHeader(chain.Genesis(), statedb)
header.Number = big.NewInt(3)
err := ValidateHeader(pow, header, chain.Genesis(), false)
if err != BlockNumberErr { if err != BlockNumberErr {
t.Errorf("expected block number error %v", err) t.Errorf("expected block number error, got %q", err)
} }
block1 = chain.NewBlock(common.Address{}) header = makeHeader(chain.Genesis(), statedb)
err = ValidateHeader(pow, block1.Header(), chain.Genesis().Header(), false) err = ValidateHeader(pow, header, chain.Genesis(), false)
if err == BlockNumberErr { if err == BlockNumberErr {
t.Errorf("didn't expect block number error") t.Errorf("didn't expect block number error")
} }
......
package core
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
)
var (
jeff = common.HexToAddress("9d38997c624a71b21278389ea2fdc460d000e4b2")
vitalik = common.HexToAddress("b1e570be07eaa673e4fd0c8265b64ef739385709")
christoph = common.HexToAddress("529bc43a5d93789fa28de1961db6a07e752204ae")
gav = common.HexToAddress("e3e942b2aa524293c84ff6c7f87a6635790ad5e4")
)
// Canary will check the 0'd address of the 4 contracts above.
// If two or more are set to anything other than a 0 the canary
// dies a horrible death.
func Canary(statedb *state.StateDB) bool {
r := new(big.Int)
r.Add(r, statedb.GetState(jeff, common.Hash{}).Big())
r.Add(r, statedb.GetState(vitalik, common.Hash{}).Big())
r.Add(r, statedb.GetState(christoph, common.Hash{}).Big())
r.Add(r, statedb.GetState(gav, common.Hash{}).Big())
return r.Cmp(big.NewInt(1)) > 0
}
package core package core
import ( import (
"fmt"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -11,7 +10,8 @@ import ( ...@@ -11,7 +10,8 @@ import (
"github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/pow"
) )
// So we can generate blocks easily // FakePow is a non-validating proof of work implementation.
// It returns true from Verify for any block.
type FakePow struct{} type FakePow struct{}
func (f FakePow) Search(block pow.Block, stop <-chan struct{}) (uint64, []byte) { func (f FakePow) Search(block pow.Block, stop <-chan struct{}) (uint64, []byte) {
...@@ -23,81 +23,125 @@ func (f FakePow) Turbo(bool) {} ...@@ -23,81 +23,125 @@ func (f FakePow) Turbo(bool) {}
// So we can deterministically seed different blockchains // So we can deterministically seed different blockchains
var ( var (
CanonicalSeed = 1 canonicalSeed = 1
ForkSeed = 2 forkSeed = 2
) )
// Utility functions for making chains on the fly // BlockGen creates blocks for testing.
// Exposed for sake of testing from other packages (eg. go-ethash) // See GenerateChain for a detailed explanation.
func NewBlockFromParent(addr common.Address, parent *types.Block) *types.Block { type BlockGen struct {
return newBlockFromParent(addr, parent) i int
parent *types.Block
chain []*types.Block
header *types.Header
statedb *state.StateDB
coinbase *state.StateObject
txs []*types.Transaction
receipts []*types.Receipt
uncles []*types.Header
} }
func MakeBlock(bman *BlockProcessor, parent *types.Block, i int, db common.Database, seed int) *types.Block { // SetCoinbase sets the coinbase of the generated block.
return makeBlock(bman, parent, i, db, seed) // It can be called at most once.
} func (b *BlockGen) SetCoinbase(addr common.Address) {
if b.coinbase != nil {
func MakeChain(bman *BlockProcessor, parent *types.Block, max int, db common.Database, seed int) types.Blocks { if len(b.txs) > 0 {
return makeChain(bman, parent, max, db, seed) panic("coinbase must be set before adding transactions")
}
panic("coinbase can only be set once")
}
b.header.Coinbase = addr
b.coinbase = b.statedb.GetOrNewStateObject(addr)
b.coinbase.SetGasLimit(b.header.GasLimit)
} }
func NewChainMan(block *types.Block, eventMux *event.TypeMux, db common.Database) *ChainManager { // SetExtra sets the extra data field of the generated block.
return newChainManager(block, eventMux, db) func (b *BlockGen) SetExtra(data []byte) {
b.header.Extra = data
} }
func NewBlockProc(db common.Database, cman *ChainManager, eventMux *event.TypeMux) *BlockProcessor { // AddTx adds a transaction to the generated block. If no coinbase has
return newBlockProcessor(db, cman, eventMux) // been set, the block's coinbase is set to the zero address.
//
// AddTx panics if the transaction cannot be executed. In addition to
// the protocol-imposed limitations (gas limit, etc.), there are some
// further limitations on the content of transactions that can be
// added. Notably, contract code relying on the BLOCKHASH instruction
// will panic during execution.
func (b *BlockGen) AddTx(tx *types.Transaction) {
if b.coinbase == nil {
b.SetCoinbase(common.Address{})
}
_, gas, err := ApplyMessage(NewEnv(b.statedb, nil, tx, b.header), tx, b.coinbase)
if err != nil {
panic(err)
}
b.statedb.Update()
b.header.GasUsed.Add(b.header.GasUsed, gas)
receipt := types.NewReceipt(b.statedb.Root().Bytes(), b.header.GasUsed)
logs := b.statedb.GetLogs(tx.Hash())
receipt.SetLogs(logs)
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
b.txs = append(b.txs, tx)
b.receipts = append(b.receipts, receipt)
} }
func NewCanonical(n int, db common.Database) (*BlockProcessor, error) { // TxNonce returns the next valid transaction nonce for the
return newCanonical(n, db) // account at addr. It panics if the account does not exist.
func (b *BlockGen) TxNonce(addr common.Address) uint64 {
if !b.statedb.HasAccount(addr) {
panic("account does not exist")
}
return b.statedb.GetNonce(addr)
} }
// block time is fixed at 10 seconds // AddUncle adds an uncle header to the generated block.
func newBlockFromParent(addr common.Address, parent *types.Block) *types.Block { func (b *BlockGen) AddUncle(h *types.Header) {
block := types.NewBlock(parent.Hash(), addr, parent.Root(), common.BigPow(2, 32), 0, nil) b.uncles = append(b.uncles, h)
block.SetUncles(nil)
block.SetTransactions(nil)
block.SetReceipts(nil)
header := block.Header()
header.Difficulty = CalcDifficulty(block.Header(), parent.Header())
header.Number = new(big.Int).Add(parent.Header().Number, common.Big1)
header.Time = parent.Header().Time + 10
header.GasLimit = CalcGasLimit(parent)
block.Td = parent.Td
return block
} }
// Actually make a block by simulating what miner would do // PrevBlock returns a previously generated block by number. It panics if
// we seed chains by the first byte of the coinbase // num is greater or equal to the number of the block being generated.
func makeBlock(bman *BlockProcessor, parent *types.Block, i int, db common.Database, seed int) *types.Block { // For index -1, PrevBlock returns the parent block given to GenerateChain.
var addr common.Address func (b *BlockGen) PrevBlock(index int) *types.Block {
addr[0], addr[19] = byte(seed), byte(i) if index >= b.i {
block := newBlockFromParent(addr, parent) panic("block index out of range")
state := state.New(block.Root(), db) }
cbase := state.GetOrNewStateObject(addr) if index == -1 {
cbase.SetGasLimit(CalcGasLimit(parent)) return b.parent
cbase.AddBalance(BlockReward) }
state.Update() return b.chain[index]
block.SetRoot(state.Root())
return block
} }
// Make a chain with real blocks // GenerateChain creates a chain of n blocks. The first block's
// Runs ProcessWithParent to get proper state roots // parent will be the provided parent. db is used to store
func makeChain(bman *BlockProcessor, parent *types.Block, max int, db common.Database, seed int) types.Blocks { // intermediate states and should contain the parent's state trie.
bman.bc.currentBlock = parent //
blocks := make(types.Blocks, max) // The generator function is called with a new block generator for
for i := 0; i < max; i++ { // every block. Any transactions and uncles added to the generator
block := makeBlock(bman, parent, i, db, seed) // become part of the block. If gen is nil, the blocks will be empty
_, err := bman.processWithParent(block, parent) // and their coinbase will be the zero address.
if err != nil { //
fmt.Println("process with parent failed", err) // Blocks created by GenerateChain do not contain valid proof of work
panic(err) // values. Inserting them into ChainManager requires use of FakePow or
// a similar non-validating proof of work implementation.
func GenerateChain(parent *types.Block, db common.Database, n int, gen func(int, *BlockGen)) []*types.Block {
statedb := state.New(parent.Root(), db)
blocks := make(types.Blocks, n)
genblock := func(i int, h *types.Header) *types.Block {
b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, statedb: statedb}
if gen != nil {
gen(i, b)
} }
AccumulateRewards(statedb, h, b.uncles)
statedb.Update()
h.Root = statedb.Root()
return types.NewBlock(h, b.txs, b.uncles, b.receipts)
}
for i := 0; i < n; i++ {
header := makeHeader(parent, statedb)
block := genblock(i, header)
block.Td = CalcTD(block, parent) block.Td = CalcTD(block, parent)
blocks[i] = block blocks[i] = block
parent = block parent = block
...@@ -105,41 +149,38 @@ func makeChain(bman *BlockProcessor, parent *types.Block, max int, db common.Dat ...@@ -105,41 +149,38 @@ func makeChain(bman *BlockProcessor, parent *types.Block, max int, db common.Dat
return blocks return blocks
} }
// Create a new chain manager starting from given block func makeHeader(parent *types.Block, state *state.StateDB) *types.Header {
// Effectively a fork factory time := parent.Time() + 10 // block time is fixed at 10 seconds
func newChainManager(block *types.Block, eventMux *event.TypeMux, db common.Database) *ChainManager { return &types.Header{
genesis := GenesisBlock(0, db) Root: state.Root(),
bc := &ChainManager{blockDb: db, stateDb: db, genesisBlock: genesis, eventMux: eventMux, pow: FakePow{}} ParentHash: parent.Hash(),
bc.txState = state.ManageState(state.New(genesis.Root(), db)) Coinbase: parent.Coinbase(),
bc.futureBlocks = NewBlockCache(1000) Difficulty: CalcDifficulty(time, parent.Time(), parent.Difficulty()),
if block == nil { GasLimit: CalcGasLimit(parent),
bc.Reset() GasUsed: new(big.Int),
} else { Number: new(big.Int).Add(parent.Number(), common.Big1),
bc.currentBlock = block Time: uint64(time),
bc.td = block.Td
} }
return bc
} }
// block processor with fake pow // newCanonical creates a new deterministic canonical chain by running
func newBlockProcessor(db common.Database, cman *ChainManager, eventMux *event.TypeMux) *BlockProcessor { // InsertChain on the result of makeChain.
chainMan := newChainManager(nil, eventMux, db)
bman := NewBlockProcessor(db, db, FakePow{}, chainMan, eventMux)
return bman
}
// Make a new, deterministic canonical chain by running InsertChain
// on result of makeChain
func newCanonical(n int, db common.Database) (*BlockProcessor, error) { func newCanonical(n int, db common.Database) (*BlockProcessor, error) {
eventMux := &event.TypeMux{} evmux := &event.TypeMux{}
chainman, _ := NewChainManager(GenesisBlock(0, db), db, db, FakePow{}, evmux)
bman := newBlockProcessor(db, newChainManager(nil, eventMux, db), eventMux) bman := NewBlockProcessor(db, db, FakePow{}, chainman, evmux)
bman.bc.SetProcessor(bman) bman.bc.SetProcessor(bman)
parent := bman.bc.CurrentBlock() parent := bman.bc.CurrentBlock()
if n == 0 { if n == 0 {
return bman, nil return bman, nil
} }
lchain := makeChain(bman, parent, n, db, CanonicalSeed) lchain := makeChain(parent, n, db, canonicalSeed)
_, err := bman.bc.InsertChain(lchain) _, err := bman.bc.InsertChain(lchain)
return bman, err return bman, err
} }
func makeChain(parent *types.Block, n int, db common.Database, seed int) []*types.Block {
return GenerateChain(parent, db, n, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)})
})
}
package core
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params"
)
func ExampleGenerateChain() {
var (
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
key3, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
addr3 = crypto.PubkeyToAddress(key3.PublicKey)
db, _ = ethdb.NewMemDatabase()
)
// Ensure that key1 has some funds in the genesis block.
genesis := GenesisBlockForTesting(db, addr1, big.NewInt(1000000))
// This call generates a chain of 5 blocks. The function runs for
// each block and adds different features to gen based on the
// block index.
chain := GenerateChain(genesis, db, 5, func(i int, gen *BlockGen) {
switch i {
case 0:
// In block 1, addr1 sends addr2 some ether.
tx, _ := types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil).SignECDSA(key1)
gen.AddTx(tx)
case 1:
// In block 2, addr1 sends some more ether to addr2.
// addr2 passes it on to addr3.
tx1, _ := types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key1)
tx2, _ := types.NewTransaction(gen.TxNonce(addr2), addr3, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key2)
gen.AddTx(tx1)
gen.AddTx(tx2)
case 2:
// Block 3 is empty but was mined by addr3.
gen.SetCoinbase(addr3)
gen.SetExtra([]byte("yeehaw"))
case 3:
// Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data).
b2 := gen.PrevBlock(1).Header()
b2.Extra = []byte("foo")
gen.AddUncle(b2)
b3 := gen.PrevBlock(2).Header()
b3.Extra = []byte("foo")
gen.AddUncle(b3)
}
})
// Import the chain. This runs all block validation rules.
evmux := &event.TypeMux{}
chainman, _ := NewChainManager(genesis, db, db, FakePow{}, evmux)
chainman.SetProcessor(NewBlockProcessor(db, db, FakePow{}, chainman, evmux))
if i, err := chainman.InsertChain(chain); err != nil {
fmt.Printf("insert error (block %d): %v\n", i, err)
return
}
state := chainman.State()
fmt.Printf("last block: #%d\n", chainman.CurrentBlock().Number())
fmt.Println("balance of addr1:", state.GetBalance(addr1))
fmt.Println("balance of addr2:", state.GetBalance(addr2))
fmt.Println("balance of addr3:", state.GetBalance(addr3))
// Output:
// last block: #5
// balance of addr1: 989000
// balance of addr2: 10000
// balance of addr3: 5906250000000001000
}
This diff is collapsed.
...@@ -18,6 +18,7 @@ import ( ...@@ -18,6 +18,7 @@ import (
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/pow"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/hashicorp/golang-lru"
) )
func init() { func init() {
...@@ -62,12 +63,11 @@ func testFork(t *testing.T, bman *BlockProcessor, i, N int, f func(td1, td2 *big ...@@ -62,12 +63,11 @@ func testFork(t *testing.T, bman *BlockProcessor, i, N int, f func(td1, td2 *big
if bi1 != bi2 { if bi1 != bi2 {
t.Fatal("chains do not have the same hash at height", i) t.Fatal("chains do not have the same hash at height", i)
} }
bman2.bc.SetProcessor(bman2) bman2.bc.SetProcessor(bman2)
// extend the fork // extend the fork
parent := bman2.bc.CurrentBlock() parent := bman2.bc.CurrentBlock()
chainB := makeChain(bman2, parent, N, db, ForkSeed) chainB := makeChain(parent, N, db, forkSeed)
_, err = bman2.bc.InsertChain(chainB) _, err = bman2.bc.InsertChain(chainB)
if err != nil { if err != nil {
t.Fatal("Insert chain error for fork:", err) t.Fatal("Insert chain error for fork:", err)
...@@ -109,7 +109,8 @@ func testChain(chainB types.Blocks, bman *BlockProcessor) (*big.Int, error) { ...@@ -109,7 +109,8 @@ func testChain(chainB types.Blocks, bman *BlockProcessor) (*big.Int, error) {
bman.bc.mu.Lock() bman.bc.mu.Lock()
{ {
bman.bc.write(block) bman.bc.enqueueForWrite(block)
//bman.bc.write(block)
} }
bman.bc.mu.Unlock() bman.bc.mu.Unlock()
} }
...@@ -117,7 +118,7 @@ func testChain(chainB types.Blocks, bman *BlockProcessor) (*big.Int, error) { ...@@ -117,7 +118,7 @@ func testChain(chainB types.Blocks, bman *BlockProcessor) (*big.Int, error) {
} }
func loadChain(fn string, t *testing.T) (types.Blocks, error) { func loadChain(fn string, t *testing.T) (types.Blocks, error) {
fh, err := os.OpenFile(filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "_data", fn), os.O_RDONLY, os.ModePerm) fh, err := os.OpenFile(filepath.Join("..", "_data", fn), os.O_RDONLY, os.ModePerm)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -256,7 +257,7 @@ func TestBrokenChain(t *testing.T) { ...@@ -256,7 +257,7 @@ func TestBrokenChain(t *testing.T) {
} }
bman2.bc.SetProcessor(bman2) bman2.bc.SetProcessor(bman2)
parent := bman2.bc.CurrentBlock() parent := bman2.bc.CurrentBlock()
chainB := makeChain(bman2, parent, 5, db2, ForkSeed) chainB := makeChain(parent, 5, db2, forkSeed)
chainB = chainB[1:] chainB = chainB[1:]
_, err = testChain(chainB, bman) _, err = testChain(chainB, bman)
if err == nil { if err == nil {
...@@ -265,7 +266,7 @@ func TestBrokenChain(t *testing.T) { ...@@ -265,7 +266,7 @@ func TestBrokenChain(t *testing.T) {
} }
func TestChainInsertions(t *testing.T) { func TestChainInsertions(t *testing.T) {
t.Skip() // travil fails. t.Skip("Skipped: outdated test files")
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
...@@ -303,7 +304,7 @@ func TestChainInsertions(t *testing.T) { ...@@ -303,7 +304,7 @@ func TestChainInsertions(t *testing.T) {
} }
func TestChainMultipleInsertions(t *testing.T) { func TestChainMultipleInsertions(t *testing.T) {
t.Skip() // travil fails. t.Skip("Skipped: outdated test files")
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
...@@ -346,8 +347,8 @@ func TestChainMultipleInsertions(t *testing.T) { ...@@ -346,8 +347,8 @@ func TestChainMultipleInsertions(t *testing.T) {
} }
} }
func TestGetAncestors(t *testing.T) { func TestGetBlocksFromHash(t *testing.T) {
t.Skip() // travil fails. t.Skip("Skipped: outdated test files")
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
chainMan := theChainManager(db, t) chainMan := theChainManager(db, t)
...@@ -361,8 +362,8 @@ func TestGetAncestors(t *testing.T) { ...@@ -361,8 +362,8 @@ func TestGetAncestors(t *testing.T) {
chainMan.write(block) chainMan.write(block)
} }
ancestors := chainMan.GetAncestors(chain[len(chain)-1], 4) blocks := chainMan.GetBlocksFromHash(chain[len(chain)-1].Hash(), 4)
fmt.Println(ancestors) fmt.Println(blocks)
} }
type bproc struct{} type bproc struct{}
...@@ -372,15 +373,17 @@ func (bproc) Process(*types.Block) (state.Logs, error) { return nil, nil } ...@@ -372,15 +373,17 @@ func (bproc) Process(*types.Block) (state.Logs, error) { return nil, nil }
func makeChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Block { func makeChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Block {
var chain []*types.Block var chain []*types.Block
for i, difficulty := range d { for i, difficulty := range d {
header := &types.Header{Number: big.NewInt(int64(i + 1)), Difficulty: big.NewInt(int64(difficulty))} header := &types.Header{
block := types.NewBlockWithHeader(header) Coinbase: common.Address{seed},
copy(block.HeaderHash[:2], []byte{byte(i + 1), seed}) Number: big.NewInt(int64(i + 1)),
Difficulty: big.NewInt(int64(difficulty)),
}
if i == 0 { if i == 0 {
block.ParentHeaderHash = genesis.Hash() header.ParentHash = genesis.Hash()
} else { } else {
copy(block.ParentHeaderHash[:2], []byte{byte(i), seed}) header.ParentHash = chain[i-1].Hash()
} }
block := types.NewBlockWithHeader(header)
chain = append(chain, block) chain = append(chain, block)
} }
return chain return chain
...@@ -389,8 +392,8 @@ func makeChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Block ...@@ -389,8 +392,8 @@ func makeChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Block
func chm(genesis *types.Block, db common.Database) *ChainManager { func chm(genesis *types.Block, db common.Database) *ChainManager {
var eventMux event.TypeMux var eventMux event.TypeMux
bc := &ChainManager{blockDb: db, stateDb: db, genesisBlock: genesis, eventMux: &eventMux, pow: FakePow{}} bc := &ChainManager{blockDb: db, stateDb: db, genesisBlock: genesis, eventMux: &eventMux, pow: FakePow{}}
bc.cache = NewBlockCache(100) bc.cache, _ = lru.New(100)
bc.futureBlocks = NewBlockCache(100) bc.futureBlocks, _ = lru.New(100)
bc.processor = bproc{} bc.processor = bproc{}
bc.ResetWithGenesisBlock(genesis) bc.ResetWithGenesisBlock(genesis)
bc.txState = state.ManageState(bc.State()) bc.txState = state.ManageState(bc.State())
...@@ -399,7 +402,6 @@ func chm(genesis *types.Block, db common.Database) *ChainManager { ...@@ -399,7 +402,6 @@ func chm(genesis *types.Block, db common.Database) *ChainManager {
} }
func TestReorgLongest(t *testing.T) { func TestReorgLongest(t *testing.T) {
t.Skip("skipped while cache is removed")
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
genesis := GenesisBlock(0, db) genesis := GenesisBlock(0, db)
bc := chm(genesis, db) bc := chm(genesis, db)
...@@ -419,7 +421,6 @@ func TestReorgLongest(t *testing.T) { ...@@ -419,7 +421,6 @@ func TestReorgLongest(t *testing.T) {
} }
func TestReorgShortest(t *testing.T) { func TestReorgShortest(t *testing.T) {
t.Skip("skipped while cache is removed")
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
genesis := GenesisBlock(0, db) genesis := GenesisBlock(0, db)
bc := chm(genesis, db) bc := chm(genesis, db)
...@@ -444,7 +445,7 @@ func TestInsertNonceError(t *testing.T) { ...@@ -444,7 +445,7 @@ func TestInsertNonceError(t *testing.T) {
genesis := GenesisBlock(0, db) genesis := GenesisBlock(0, db)
bc := chm(genesis, db) bc := chm(genesis, db)
bc.processor = NewBlockProcessor(db, db, bc.pow, bc, bc.eventMux) bc.processor = NewBlockProcessor(db, db, bc.pow, bc, bc.eventMux)
blocks := makeChain(bc.processor.(*BlockProcessor), bc.currentBlock, i, db, 0) blocks := makeChain(bc.currentBlock, i, db, 0)
fail := rand.Int() % len(blocks) fail := rand.Int() % len(blocks)
failblock := blocks[fail] failblock := blocks[fail]
......
...@@ -3,6 +3,7 @@ package core ...@@ -3,6 +3,7 @@ package core
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"math/big"
"os" "os"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -11,38 +12,18 @@ import ( ...@@ -11,38 +12,18 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
/* // GenesisBlock creates a genesis block with the given nonce.
* This is the special genesis block.
*/
var ZeroHash256 = make([]byte, 32)
var ZeroHash160 = make([]byte, 20)
var ZeroHash512 = make([]byte, 64)
func GenesisBlock(nonce uint64, db common.Database) *types.Block { func GenesisBlock(nonce uint64, db common.Database) *types.Block {
genesis := types.NewBlock(common.Hash{}, common.Address{}, common.Hash{}, params.GenesisDifficulty, nonce, nil)
genesis.Header().Number = common.Big0
genesis.Header().GasLimit = params.GenesisGasLimit
genesis.Header().GasUsed = common.Big0
genesis.Header().Time = 0
genesis.Td = common.Big0
genesis.SetUncles([]*types.Header{})
genesis.SetTransactions(types.Transactions{})
genesis.SetReceipts(types.Receipts{})
var accounts map[string]struct { var accounts map[string]struct {
Balance string Balance string
Code string Code string
} }
err := json.Unmarshal(GenesisAccounts, &accounts) err := json.Unmarshal(GenesisAccounts, &accounts)
if err != nil { if err != nil {
fmt.Println("enable to decode genesis json data:", err) fmt.Println("unable to decode genesis json data:", err)
os.Exit(1) os.Exit(1)
} }
statedb := state.New(common.Hash{}, db)
statedb := state.New(genesis.Root(), db)
for addr, account := range accounts { for addr, account := range accounts {
codedAddr := common.Hex2Bytes(addr) codedAddr := common.Hex2Bytes(addr)
accountState := statedb.CreateAccount(common.BytesToAddress(codedAddr)) accountState := statedb.CreateAccount(common.BytesToAddress(codedAddr))
...@@ -51,10 +32,15 @@ func GenesisBlock(nonce uint64, db common.Database) *types.Block { ...@@ -51,10 +32,15 @@ func GenesisBlock(nonce uint64, db common.Database) *types.Block {
statedb.UpdateStateObject(accountState) statedb.UpdateStateObject(accountState)
} }
statedb.Sync() statedb.Sync()
genesis.Header().Root = statedb.Root()
genesis.Td = params.GenesisDifficulty
return genesis block := types.NewBlock(&types.Header{
Difficulty: params.GenesisDifficulty,
GasLimit: params.GenesisGasLimit,
Nonce: types.EncodeNonce(nonce),
Root: statedb.Root(),
}, nil, nil, nil)
block.Td = params.GenesisDifficulty
return block
} }
var GenesisAccounts = []byte(`{ var GenesisAccounts = []byte(`{
...@@ -71,3 +57,20 @@ var GenesisAccounts = []byte(`{ ...@@ -71,3 +57,20 @@ var GenesisAccounts = []byte(`{
"e6716f9544a56c530d868e4bfbacb172315bdead": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, "e6716f9544a56c530d868e4bfbacb172315bdead": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4": {"balance": "1606938044258990275541962092341162602522202993782792835301376"} "1a26338f0d905e295fccb71fa9ea849ffa12aaf4": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}
}`) }`)
// GenesisBlockForTesting creates a block in which addr has the given wei balance.
// The state trie of the block is written to db.
func GenesisBlockForTesting(db common.Database, addr common.Address, balance *big.Int) *types.Block {
statedb := state.New(common.Hash{}, db)
obj := statedb.GetOrNewStateObject(addr)
obj.SetBalance(balance)
statedb.Update()
statedb.Sync()
block := types.NewBlock(&types.Header{
Difficulty: params.GenesisDifficulty,
GasLimit: params.GenesisGasLimit,
Root: statedb.Root(),
}, nil, nil, nil)
block.Td = params.GenesisDifficulty
return block
}
...@@ -13,10 +13,6 @@ import ( ...@@ -13,10 +13,6 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
const tryJit = false
var ()
/* /*
* The State transitioning model * The State transitioning model
* *
...@@ -69,20 +65,24 @@ func MessageCreatesContract(msg Message) bool { ...@@ -69,20 +65,24 @@ func MessageCreatesContract(msg Message) bool {
return msg.To() == nil return msg.To() == nil
} }
func MessageGasValue(msg Message) *big.Int { // IntrinsicGas computes the 'intrisic gas' for a message
return new(big.Int).Mul(msg.Gas(), msg.GasPrice()) // with the given data.
} func IntrinsicGas(data []byte) *big.Int {
func IntrinsicGas(msg Message) *big.Int {
igas := new(big.Int).Set(params.TxGas) igas := new(big.Int).Set(params.TxGas)
for _, byt := range msg.Data() { if len(data) > 0 {
if byt != 0 { var nz int64
igas.Add(igas, params.TxDataNonZeroGas) for _, byt := range data {
} else { if byt != 0 {
igas.Add(igas, params.TxDataZeroGas) nz++
}
} }
m := big.NewInt(nz)
m.Mul(m, params.TxDataNonZeroGas)
igas.Add(igas, m)
m.SetInt64(int64(len(data)) - nz)
m.Mul(m, params.TxDataZeroGas)
igas.Add(igas, m)
} }
return igas return igas
} }
...@@ -96,7 +96,7 @@ func NewStateTransition(env vm.Environment, msg Message, coinbase *state.StateOb ...@@ -96,7 +96,7 @@ func NewStateTransition(env vm.Environment, msg Message, coinbase *state.StateOb
env: env, env: env,
msg: msg, msg: msg,
gas: new(big.Int), gas: new(big.Int),
gasPrice: new(big.Int).Set(msg.GasPrice()), gasPrice: msg.GasPrice(),
initialGas: new(big.Int), initialGas: new(big.Int),
value: msg.Value(), value: msg.Value(),
data: msg.Data(), data: msg.Data(),
...@@ -140,26 +140,22 @@ func (self *StateTransition) AddGas(amount *big.Int) { ...@@ -140,26 +140,22 @@ func (self *StateTransition) AddGas(amount *big.Int) {
} }
func (self *StateTransition) BuyGas() error { func (self *StateTransition) BuyGas() error {
var err error mgas := self.msg.Gas()
mgval := new(big.Int).Mul(mgas, self.gasPrice)
sender, err := self.From() sender, err := self.From()
if err != nil { if err != nil {
return err return err
} }
if sender.Balance().Cmp(MessageGasValue(self.msg)) < 0 { if sender.Balance().Cmp(mgval) < 0 {
return fmt.Errorf("insufficient ETH for gas (%x). Req %v, has %v", sender.Address().Bytes()[:4], MessageGasValue(self.msg), sender.Balance()) return fmt.Errorf("insufficient ETH for gas (%x). Req %v, has %v", sender.Address().Bytes()[:4], mgval, sender.Balance())
} }
if err = self.Coinbase().SubGas(mgas, self.gasPrice); err != nil {
coinbase := self.Coinbase()
err = coinbase.SubGas(self.msg.Gas(), self.msg.GasPrice())
if err != nil {
return err return err
} }
self.AddGas(mgas)
self.AddGas(self.msg.Gas()) self.initialGas.Set(mgas)
self.initialGas.Set(self.msg.Gas()) sender.SubBalance(mgval)
sender.SubBalance(MessageGasValue(self.msg))
return nil return nil
} }
...@@ -195,14 +191,14 @@ func (self *StateTransition) transitionState() (ret []byte, usedGas *big.Int, er ...@@ -195,14 +191,14 @@ func (self *StateTransition) transitionState() (ret []byte, usedGas *big.Int, er
sender, _ := self.From() // err checked in preCheck sender, _ := self.From() // err checked in preCheck
// Pay intrinsic gas // Pay intrinsic gas
if err = self.UseGas(IntrinsicGas(self.msg)); err != nil { if err = self.UseGas(IntrinsicGas(self.data)); err != nil {
return nil, nil, InvalidTxError(err) return nil, nil, InvalidTxError(err)
} }
vmenv := self.env vmenv := self.env
var ref vm.ContextRef var ref vm.ContextRef
if MessageCreatesContract(msg) { if MessageCreatesContract(msg) {
ret, err, ref = vmenv.Create(sender, self.msg.Data(), self.gas, self.gasPrice, self.value) ret, err, ref = vmenv.Create(sender, self.data, self.gas, self.gasPrice, self.value)
if err == nil { if err == nil {
dataGas := big.NewInt(int64(len(ret))) dataGas := big.NewInt(int64(len(ret)))
dataGas.Mul(dataGas, params.CreateDataGas) dataGas.Mul(dataGas, params.CreateDataGas)
...@@ -216,7 +212,7 @@ func (self *StateTransition) transitionState() (ret []byte, usedGas *big.Int, er ...@@ -216,7 +212,7 @@ func (self *StateTransition) transitionState() (ret []byte, usedGas *big.Int, er
} else { } else {
// Increment the nonce for the next transaction // Increment the nonce for the next transaction
self.state.SetNonce(sender.Address(), sender.Nonce()+1) self.state.SetNonce(sender.Address(), sender.Nonce()+1)
ret, err = vmenv.Call(sender, self.To().Address(), self.msg.Data(), self.gas, self.gasPrice, self.value) ret, err = vmenv.Call(sender, self.To().Address(), self.data, self.gas, self.gasPrice, self.value)
} }
if err != nil && IsValueTransferErr(err) { if err != nil && IsValueTransferErr(err) {
...@@ -237,15 +233,15 @@ func (self *StateTransition) refundGas() { ...@@ -237,15 +233,15 @@ func (self *StateTransition) refundGas() {
coinbase := self.Coinbase() coinbase := self.Coinbase()
sender, _ := self.From() // err already checked sender, _ := self.From() // err already checked
// Return remaining gas // Return remaining gas
remaining := new(big.Int).Mul(self.gas, self.msg.GasPrice()) remaining := new(big.Int).Mul(self.gas, self.gasPrice)
sender.AddBalance(remaining) sender.AddBalance(remaining)
uhalf := new(big.Int).Div(self.gasUsed(), common.Big2) uhalf := remaining.Div(self.gasUsed(), common.Big2)
refund := common.BigMin(uhalf, self.state.Refunds()) refund := common.BigMin(uhalf, self.state.Refunds())
self.gas.Add(self.gas, refund) self.gas.Add(self.gas, refund)
self.state.AddBalance(sender.Address(), refund.Mul(refund, self.msg.GasPrice())) self.state.AddBalance(sender.Address(), refund.Mul(refund, self.gasPrice))
coinbase.AddGas(self.gas, self.msg.GasPrice()) coinbase.AddGas(self.gas, self.gasPrice)
} }
func (self *StateTransition) gasUsed() *big.Int { func (self *StateTransition) gasUsed() *big.Int {
......
...@@ -162,27 +162,25 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error { ...@@ -162,27 +162,25 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
// Check the transaction doesn't exceed the current // Check the transaction doesn't exceed the current
// block limit gas. // block limit gas.
if pool.gasLimit().Cmp(tx.GasLimit) < 0 { if pool.gasLimit().Cmp(tx.Gas()) < 0 {
return ErrGasLimit return ErrGasLimit
} }
// Transactions can't be negative. This may never happen // Transactions can't be negative. This may never happen
// using RLP decoded transactions but may occur if you create // using RLP decoded transactions but may occur if you create
// a transaction using the RPC for example. // a transaction using the RPC for example.
if tx.Amount.Cmp(common.Big0) < 0 { if tx.Value().Cmp(common.Big0) < 0 {
return ErrNegativeValue return ErrNegativeValue
} }
// Transactor should have enough funds to cover the costs // Transactor should have enough funds to cover the costs
// cost == V + GP * GL // cost == V + GP * GL
total := new(big.Int).Mul(tx.Price, tx.GasLimit) if pool.currentState().GetBalance(from).Cmp(tx.Cost()) < 0 {
total.Add(total, tx.Value())
if pool.currentState().GetBalance(from).Cmp(total) < 0 {
return ErrInsufficientFunds return ErrInsufficientFunds
} }
// Should supply enough intrinsic gas // Should supply enough intrinsic gas
if tx.GasLimit.Cmp(IntrinsicGas(tx)) < 0 { if tx.Gas().Cmp(IntrinsicGas(tx.Data())) < 0 {
return ErrIntrinsicGas return ErrIntrinsicGas
} }
...@@ -238,7 +236,7 @@ func (pool *TxPool) addTx(hash common.Hash, addr common.Address, tx *types.Trans ...@@ -238,7 +236,7 @@ func (pool *TxPool) addTx(hash common.Hash, addr common.Address, tx *types.Trans
// Increment the nonce on the pending state. This can only happen if // Increment the nonce on the pending state. This can only happen if
// the nonce is +1 to the previous one. // the nonce is +1 to the previous one.
pool.pendingState.SetNonce(addr, tx.AccountNonce+1) pool.pendingState.SetNonce(addr, tx.Nonce()+1)
// Notify the subscribers. This event is posted in a goroutine // Notify the subscribers. This event is posted in a goroutine
// because it's possible that somewhere during the post "Remove transaction" // because it's possible that somewhere during the post "Remove transaction"
// gets called which will then wait for the global tx pool lock and deadlock. // gets called which will then wait for the global tx pool lock and deadlock.
...@@ -341,7 +339,7 @@ func (pool *TxPool) checkQueue() { ...@@ -341,7 +339,7 @@ func (pool *TxPool) checkQueue() {
trueNonce := pool.currentState().GetNonce(address) trueNonce := pool.currentState().GetNonce(address)
addq := addq[:0] addq := addq[:0]
for hash, tx := range txs { for hash, tx := range txs {
if tx.AccountNonce < trueNonce { if tx.Nonce() < trueNonce {
// Drop queued transactions whose nonce is lower than // Drop queued transactions whose nonce is lower than
// the account nonce because they have been processed. // the account nonce because they have been processed.
delete(txs, hash) delete(txs, hash)
...@@ -362,8 +360,7 @@ func (pool *TxPool) checkQueue() { ...@@ -362,8 +360,7 @@ func (pool *TxPool) checkQueue() {
delete(pool.queue[address], e.hash) delete(pool.queue[address], e.hash)
continue continue
} }
if e.Nonce() > guessedNonce {
if e.AccountNonce > guessedNonce {
break break
} }
delete(txs, e.hash) delete(txs, e.hash)
...@@ -418,4 +415,4 @@ type txQueueEntry struct { ...@@ -418,4 +415,4 @@ type txQueueEntry struct {
func (q txQueue) Len() int { return len(q) } func (q txQueue) Len() int { return len(q) }
func (q txQueue) Swap(i, j int) { q[i], q[j] = q[j], q[i] } func (q txQueue) Swap(i, j int) { q[i], q[j] = q[j], q[i] }
func (q txQueue) Less(i, j int) bool { return q[i].AccountNonce < q[j].AccountNonce } func (q txQueue) Less(i, j int) bool { return q[i].Nonce() < q[j].Nonce() }
...@@ -13,8 +13,9 @@ import ( ...@@ -13,8 +13,9 @@ import (
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
) )
func transaction() *types.Transaction { func transaction(nonce uint64, gaslimit *big.Int, key *ecdsa.PrivateKey) *types.Transaction {
return types.NewTransactionMessage(common.Address{}, big.NewInt(100), big.NewInt(100), big.NewInt(100), nil) tx, _ := types.NewTransaction(nonce, common.Address{}, big.NewInt(100), gaslimit, big.NewInt(1), nil).SignECDSA(key)
return tx
} }
func setupTxPool() (*TxPool, *ecdsa.PrivateKey) { func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
...@@ -29,43 +30,34 @@ func setupTxPool() (*TxPool, *ecdsa.PrivateKey) { ...@@ -29,43 +30,34 @@ func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
func TestInvalidTransactions(t *testing.T) { func TestInvalidTransactions(t *testing.T) {
pool, key := setupTxPool() pool, key := setupTxPool()
tx := transaction() tx := transaction(0, big.NewInt(100), key)
tx.SignECDSA(key) if err := pool.Add(tx); err != ErrNonExistentAccount {
err := pool.Add(tx)
if err != ErrNonExistentAccount {
t.Error("expected", ErrNonExistentAccount) t.Error("expected", ErrNonExistentAccount)
} }
from, _ := tx.From() from, _ := tx.From()
pool.currentState().AddBalance(from, big.NewInt(1)) pool.currentState().AddBalance(from, big.NewInt(1))
err = pool.Add(tx) if err := pool.Add(tx); err != ErrInsufficientFunds {
if err != ErrInsufficientFunds {
t.Error("expected", ErrInsufficientFunds) t.Error("expected", ErrInsufficientFunds)
} }
balance := new(big.Int).Add(tx.Value(), new(big.Int).Mul(tx.Gas(), tx.GasPrice())) balance := new(big.Int).Add(tx.Value(), new(big.Int).Mul(tx.Gas(), tx.GasPrice()))
pool.currentState().AddBalance(from, balance) pool.currentState().AddBalance(from, balance)
err = pool.Add(tx) if err := pool.Add(tx); err != ErrIntrinsicGas {
if err != ErrIntrinsicGas {
t.Error("expected", ErrIntrinsicGas, "got", err) t.Error("expected", ErrIntrinsicGas, "got", err)
} }
pool.currentState().SetNonce(from, 1) pool.currentState().SetNonce(from, 1)
pool.currentState().AddBalance(from, big.NewInt(0xffffffffffffff)) pool.currentState().AddBalance(from, big.NewInt(0xffffffffffffff))
tx.GasLimit = big.NewInt(100000) tx = transaction(0, big.NewInt(100000), key)
tx.Price = big.NewInt(1) if err := pool.Add(tx); err != ErrNonce {
tx.SignECDSA(key)
err = pool.Add(tx)
if err != ErrNonce {
t.Error("expected", ErrNonce) t.Error("expected", ErrNonce)
} }
} }
func TestTransactionQueue(t *testing.T) { func TestTransactionQueue(t *testing.T) {
pool, key := setupTxPool() pool, key := setupTxPool()
tx := transaction() tx := transaction(0, big.NewInt(100), key)
tx.SignECDSA(key)
from, _ := tx.From() from, _ := tx.From()
pool.currentState().AddBalance(from, big.NewInt(1)) pool.currentState().AddBalance(from, big.NewInt(1))
pool.queueTx(tx.Hash(), tx) pool.queueTx(tx.Hash(), tx)
...@@ -75,9 +67,7 @@ func TestTransactionQueue(t *testing.T) { ...@@ -75,9 +67,7 @@ func TestTransactionQueue(t *testing.T) {
t.Error("expected valid txs to be 1 is", len(pool.pending)) t.Error("expected valid txs to be 1 is", len(pool.pending))
} }
tx = transaction() tx = transaction(1, big.NewInt(100), key)
tx.SetNonce(1)
tx.SignECDSA(key)
from, _ = tx.From() from, _ = tx.From()
pool.currentState().SetNonce(from, 2) pool.currentState().SetNonce(from, 2)
pool.queueTx(tx.Hash(), tx) pool.queueTx(tx.Hash(), tx)
...@@ -91,12 +81,9 @@ func TestTransactionQueue(t *testing.T) { ...@@ -91,12 +81,9 @@ func TestTransactionQueue(t *testing.T) {
} }
pool, key = setupTxPool() pool, key = setupTxPool()
tx1, tx2, tx3 := transaction(), transaction(), transaction() tx1 := transaction(0, big.NewInt(100), key)
tx2.SetNonce(10) tx2 := transaction(10, big.NewInt(100), key)
tx3.SetNonce(11) tx3 := transaction(11, big.NewInt(100), key)
tx1.SignECDSA(key)
tx2.SignECDSA(key)
tx3.SignECDSA(key)
pool.queueTx(tx1.Hash(), tx1) pool.queueTx(tx1.Hash(), tx1)
pool.queueTx(tx2.Hash(), tx2) pool.queueTx(tx2.Hash(), tx2)
pool.queueTx(tx3.Hash(), tx3) pool.queueTx(tx3.Hash(), tx3)
...@@ -114,8 +101,7 @@ func TestTransactionQueue(t *testing.T) { ...@@ -114,8 +101,7 @@ func TestTransactionQueue(t *testing.T) {
func TestRemoveTx(t *testing.T) { func TestRemoveTx(t *testing.T) {
pool, key := setupTxPool() pool, key := setupTxPool()
tx := transaction() tx := transaction(0, big.NewInt(100), key)
tx.SignECDSA(key)
from, _ := tx.From() from, _ := tx.From()
pool.currentState().AddBalance(from, big.NewInt(1)) pool.currentState().AddBalance(from, big.NewInt(1))
pool.queueTx(tx.Hash(), tx) pool.queueTx(tx.Hash(), tx)
...@@ -142,13 +128,10 @@ func TestRemoveTx(t *testing.T) { ...@@ -142,13 +128,10 @@ func TestRemoveTx(t *testing.T) {
func TestNegativeValue(t *testing.T) { func TestNegativeValue(t *testing.T) {
pool, key := setupTxPool() pool, key := setupTxPool()
tx := transaction() tx, _ := types.NewTransaction(0, common.Address{}, big.NewInt(-1), big.NewInt(100), big.NewInt(1), nil).SignECDSA(key)
tx.Value().Set(big.NewInt(-1))
tx.SignECDSA(key)
from, _ := tx.From() from, _ := tx.From()
pool.currentState().AddBalance(from, big.NewInt(1)) pool.currentState().AddBalance(from, big.NewInt(1))
err := pool.Add(tx) if err := pool.Add(tx); err != ErrNegativeValue {
if err != ErrNegativeValue {
t.Error("expected", ErrNegativeValue, "got", err) t.Error("expected", ErrNegativeValue, "got", err)
} }
} }
...@@ -165,20 +148,15 @@ func TestTransactionChainFork(t *testing.T) { ...@@ -165,20 +148,15 @@ func TestTransactionChainFork(t *testing.T) {
} }
resetState() resetState()
tx := transaction() tx := transaction(0, big.NewInt(100000), key)
tx.GasLimit = big.NewInt(100000) if err := pool.add(tx); err != nil {
tx.SignECDSA(key)
err := pool.add(tx)
if err != nil {
t.Error("didn't expect error", err) t.Error("didn't expect error", err)
} }
pool.RemoveTransactions([]*types.Transaction{tx}) pool.RemoveTransactions([]*types.Transaction{tx})
// reset the pool's internal state // reset the pool's internal state
resetState() resetState()
err = pool.add(tx) if err := pool.add(tx); err != nil {
if err != nil {
t.Error("didn't expect error", err) t.Error("didn't expect error", err)
} }
} }
...@@ -195,24 +173,14 @@ func TestTransactionDoubleNonce(t *testing.T) { ...@@ -195,24 +173,14 @@ func TestTransactionDoubleNonce(t *testing.T) {
} }
resetState() resetState()
tx := transaction() tx := transaction(0, big.NewInt(100000), key)
tx.GasLimit = big.NewInt(100000) tx2 := transaction(0, big.NewInt(1000000), key)
tx.SignECDSA(key) if err := pool.add(tx); err != nil {
err := pool.add(tx)
if err != nil {
t.Error("didn't expect error", err) t.Error("didn't expect error", err)
} }
if err := pool.add(tx2); err != nil {
tx2 := transaction()
tx2.GasLimit = big.NewInt(1000000)
tx2.SignECDSA(key)
err = pool.add(tx2)
if err != nil {
t.Error("didn't expect error", err) t.Error("didn't expect error", err)
} }
if len(pool.pending) != 2 { if len(pool.pending) != 2 {
t.Error("expected 2 pending txs. Got", len(pool.pending)) t.Error("expected 2 pending txs. Got", len(pool.pending))
} }
...@@ -222,20 +190,13 @@ func TestMissingNonce(t *testing.T) { ...@@ -222,20 +190,13 @@ func TestMissingNonce(t *testing.T) {
pool, key := setupTxPool() pool, key := setupTxPool()
addr := crypto.PubkeyToAddress(key.PublicKey) addr := crypto.PubkeyToAddress(key.PublicKey)
pool.currentState().AddBalance(addr, big.NewInt(100000000000000)) pool.currentState().AddBalance(addr, big.NewInt(100000000000000))
tx := transaction() tx := transaction(1, big.NewInt(100000), key)
tx.AccountNonce = 1 if err := pool.add(tx); err != nil {
tx.GasLimit = big.NewInt(100000)
tx.SignECDSA(key)
err := pool.add(tx)
if err != nil {
t.Error("didn't expect error", err) t.Error("didn't expect error", err)
} }
if len(pool.pending) != 0 { if len(pool.pending) != 0 {
t.Error("expected 0 pending transactions, got", len(pool.pending)) t.Error("expected 0 pending transactions, got", len(pool.pending))
} }
if len(pool.queue[addr]) != 1 { if len(pool.queue[addr]) != 1 {
t.Error("expected 1 queued transaction, got", len(pool.queue[addr])) t.Error("expected 1 queued transaction, got", len(pool.queue[addr]))
} }
......
This diff is collapsed.
...@@ -13,7 +13,6 @@ import ( ...@@ -13,7 +13,6 @@ import (
// from bcValidBlockTest.json, "SimpleTx" // from bcValidBlockTest.json, "SimpleTx"
func TestBlockEncoding(t *testing.T) { func TestBlockEncoding(t *testing.T) {
blockEnc := common.FromHex("f90260f901f9a083cafc574e1f51ba9dc0568fc617a08ea2429fb384059c972f13b19fa1c8dd55a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a05fe50b260da6308036625b850b5d6ced6d0a9f814c0688bc91ffb7b7a3a54b67a0bc37d79753ad738a6dac4921e57392f145d8887476de3f783dfa7edae9283e52b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd8825208845506eb0780a0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4f861f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1c0") blockEnc := common.FromHex("f90260f901f9a083cafc574e1f51ba9dc0568fc617a08ea2429fb384059c972f13b19fa1c8dd55a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a05fe50b260da6308036625b850b5d6ced6d0a9f814c0688bc91ffb7b7a3a54b67a0bc37d79753ad738a6dac4921e57392f145d8887476de3f783dfa7edae9283e52b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd8825208845506eb0780a0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4f861f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1c0")
var block Block var block Block
if err := rlp.DecodeBytes(blockEnc, &block); err != nil { if err := rlp.DecodeBytes(blockEnc, &block); err != nil {
t.Fatal("decode error: ", err) t.Fatal("decode error: ", err)
...@@ -35,20 +34,10 @@ func TestBlockEncoding(t *testing.T) { ...@@ -35,20 +34,10 @@ func TestBlockEncoding(t *testing.T) {
check("Time", block.Time(), int64(1426516743)) check("Time", block.Time(), int64(1426516743))
check("Size", block.Size(), common.StorageSize(len(blockEnc))) check("Size", block.Size(), common.StorageSize(len(blockEnc)))
to := common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87") tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), big.NewInt(50000), big.NewInt(10), nil)
check("Transactions", block.Transactions(), Transactions{ tx1, _ = tx1.WithSignature(common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100"))
{ check("len(Transactions)", len(block.Transactions()), 1)
Payload: []byte{}, check("Transactions[0].Hash", block.Transactions()[0].Hash(), tx1.Hash())
Amount: big.NewInt(10),
Price: big.NewInt(10),
GasLimit: big.NewInt(50000),
AccountNonce: 0,
V: 27,
R: common.String2Big("0x9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f"),
S: common.String2Big("0x8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1"),
Recipient: &to,
},
})
ourBlockEnc, err := rlp.EncodeToBytes(&block) ourBlockEnc, err := rlp.EncodeToBytes(&block)
if err != nil { if err != nil {
......
...@@ -4,7 +4,9 @@ import ( ...@@ -4,7 +4,9 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
"errors" "errors"
"fmt" "fmt"
"io"
"math/big" "math/big"
"sync/atomic"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
...@@ -18,38 +20,63 @@ func IsContractAddr(addr []byte) bool { ...@@ -18,38 +20,63 @@ func IsContractAddr(addr []byte) bool {
} }
type Transaction struct { type Transaction struct {
AccountNonce uint64 data txdata
Price *big.Int // caches
GasLimit *big.Int hash atomic.Value
Recipient *common.Address `rlp:"nil"` // nil means contract creation size atomic.Value
Amount *big.Int from atomic.Value
Payload []byte
V byte
R, S *big.Int
} }
func NewContractCreationTx(amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction { type txdata struct {
return &Transaction{ AccountNonce uint64
Recipient: nil, Price, GasLimit *big.Int
Amount: amount, Recipient *common.Address `rlp:"nil"` // nil means contract creation
GasLimit: gasLimit, Amount *big.Int
Price: gasPrice, Payload []byte
Payload: data, V byte // signature
R: new(big.Int), R, S *big.Int // signature
S: new(big.Int), }
func NewContractCreation(nonce uint64, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
if len(data) > 0 {
data = common.CopyBytes(data)
} }
return &Transaction{data: txdata{
AccountNonce: nonce,
Recipient: nil,
Amount: new(big.Int).Set(amount),
GasLimit: new(big.Int).Set(gasLimit),
Price: new(big.Int).Set(gasPrice),
Payload: data,
R: new(big.Int),
S: new(big.Int),
}}
} }
func NewTransactionMessage(to common.Address, amount, gasAmount, gasPrice *big.Int, data []byte) *Transaction { func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
return &Transaction{ if len(data) > 0 {
Recipient: &to, data = common.CopyBytes(data)
Amount: amount, }
GasLimit: gasAmount, d := txdata{
Price: gasPrice, AccountNonce: nonce,
Payload: data, Recipient: &to,
R: new(big.Int), Payload: data,
S: new(big.Int), Amount: new(big.Int),
GasLimit: new(big.Int),
Price: new(big.Int),
R: new(big.Int),
S: new(big.Int),
}
if amount != nil {
d.Amount.Set(amount)
}
if gasLimit != nil {
d.GasLimit.Set(gasLimit)
} }
if gasPrice != nil {
d.Price.Set(gasPrice)
}
return &Transaction{data: d}
} }
func NewTransactionFromBytes(data []byte) *Transaction { func NewTransactionFromBytes(data []byte) *Transaction {
...@@ -61,112 +88,128 @@ func NewTransactionFromBytes(data []byte) *Transaction { ...@@ -61,112 +88,128 @@ func NewTransactionFromBytes(data []byte) *Transaction {
return tx return tx
} }
func (tx *Transaction) Hash() common.Hash { func (tx *Transaction) EncodeRLP(w io.Writer) error {
return rlpHash([]interface{}{ return rlp.Encode(w, &tx.data)
tx.AccountNonce, tx.Price, tx.GasLimit, tx.Recipient, tx.Amount, tx.Payload,
})
}
// Size returns the encoded RLP size of tx.
func (self *Transaction) Size() common.StorageSize {
c := writeCounter(0)
rlp.Encode(&c, self)
return common.StorageSize(c)
} }
func (self *Transaction) Data() []byte { func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
return self.Payload _, size, _ := s.Kind()
} err := s.Decode(&tx.data)
if err == nil {
func (self *Transaction) Gas() *big.Int { tx.size.Store(common.StorageSize(rlp.ListSize(size)))
return self.GasLimit }
return err
} }
func (self *Transaction) GasPrice() *big.Int { func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) }
return self.Price func (tx *Transaction) Gas() *big.Int { return new(big.Int).Set(tx.data.GasLimit) }
} func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) }
func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amount) }
func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce }
func (self *Transaction) Value() *big.Int { func (tx *Transaction) To() *common.Address {
return self.Amount if tx.data.Recipient == nil {
return nil
} else {
to := *tx.data.Recipient
return &to
}
} }
func (self *Transaction) Nonce() uint64 { func (tx *Transaction) Hash() common.Hash {
return self.AccountNonce if hash := tx.hash.Load(); hash != nil {
return hash.(common.Hash)
}
v := rlpHash([]interface{}{
tx.data.AccountNonce,
tx.data.Price,
tx.data.GasLimit,
tx.data.Recipient,
tx.data.Amount,
tx.data.Payload,
})
tx.hash.Store(v)
return v
} }
func (self *Transaction) SetNonce(AccountNonce uint64) { func (tx *Transaction) Size() common.StorageSize {
self.AccountNonce = AccountNonce if size := tx.size.Load(); size != nil {
return size.(common.StorageSize)
}
c := writeCounter(0)
rlp.Encode(&c, &tx.data)
tx.size.Store(common.StorageSize(c))
return common.StorageSize(c)
} }
func (self *Transaction) From() (common.Address, error) { func (tx *Transaction) From() (common.Address, error) {
pubkey, err := self.PublicKey() if from := tx.from.Load(); from != nil {
return from.(common.Address), nil
}
pubkey, err := tx.publicKey()
if err != nil { if err != nil {
return common.Address{}, err return common.Address{}, err
} }
var addr common.Address var addr common.Address
copy(addr[:], crypto.Sha3(pubkey[1:])[12:]) copy(addr[:], crypto.Sha3(pubkey[1:])[12:])
tx.from.Store(addr)
return addr, nil return addr, nil
} }
// To returns the recipient of the transaction. // Cost returns amount + gasprice * gaslimit.
// If transaction is a contract creation (with no recipient address) func (tx *Transaction) Cost() *big.Int {
// To returns nil. total := new(big.Int).Mul(tx.data.Price, tx.data.GasLimit)
func (tx *Transaction) To() *common.Address { total.Add(total, tx.data.Amount)
return tx.Recipient return total
} }
func (tx *Transaction) GetSignatureValues() (v byte, r []byte, s []byte) { func (tx *Transaction) SignatureValues() (v byte, r *big.Int, s *big.Int) {
v = byte(tx.V) return tx.data.V, new(big.Int).Set(tx.data.R), new(big.Int).Set(tx.data.S)
r = common.LeftPadBytes(tx.R.Bytes(), 32)
s = common.LeftPadBytes(tx.S.Bytes(), 32)
return
} }
func (tx *Transaction) PublicKey() ([]byte, error) { func (tx *Transaction) publicKey() ([]byte, error) {
if !crypto.ValidateSignatureValues(tx.V, tx.R, tx.S) { if !crypto.ValidateSignatureValues(tx.data.V, tx.data.R, tx.data.S) {
return nil, errors.New("invalid v, r, s values") return nil, errors.New("invalid v, r, s values")
} }
hash := tx.Hash() // encode the signature in uncompressed format
v, r, s := tx.GetSignatureValues() r, s := tx.data.R.Bytes(), tx.data.S.Bytes()
sig := append(r, s...) sig := make([]byte, 65)
sig = append(sig, v-27) copy(sig[32-len(r):32], r)
copy(sig[64-len(s):64], s)
sig[64] = tx.data.V - 27
p, err := crypto.SigToPub(hash[:], sig) // recover the public key from the signature
hash := tx.Hash()
pub, err := crypto.Ecrecover(hash[:], sig)
if err != nil { if err != nil {
glog.V(logger.Error).Infof("Could not get pubkey from signature: ", err) glog.V(logger.Error).Infof("Could not get pubkey from signature: ", err)
return nil, err return nil, err
} }
if len(pub) == 0 || pub[0] != 4 {
pubkey := crypto.FromECDSAPub(p)
if len(pubkey) == 0 || pubkey[0] != 4 {
return nil, errors.New("invalid public key") return nil, errors.New("invalid public key")
} }
return pubkey, nil return pub, nil
} }
func (tx *Transaction) SetSignatureValues(sig []byte) error { func (tx *Transaction) WithSignature(sig []byte) (*Transaction, error) {
tx.R = common.Bytes2Big(sig[:32]) if len(sig) != 65 {
tx.S = common.Bytes2Big(sig[32:64]) panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig)))
tx.V = sig[64] + 27 }
return nil cpy := &Transaction{data: tx.data}
cpy.data.R = new(big.Int).SetBytes(sig[:32])
cpy.data.S = new(big.Int).SetBytes(sig[32:64])
cpy.data.V = sig[64] + 27
return cpy, nil
} }
func (tx *Transaction) SignECDSA(prv *ecdsa.PrivateKey) error { func (tx *Transaction) SignECDSA(prv *ecdsa.PrivateKey) (*Transaction, error) {
h := tx.Hash() h := tx.Hash()
sig, err := crypto.Sign(h[:], prv) sig, err := crypto.Sign(h[:], prv)
if err != nil { if err != nil {
return err return nil, err
} }
tx.SetSignatureValues(sig) return tx.WithSignature(sig)
return nil
}
// TODO: remove
func (tx *Transaction) RlpData() interface{} {
data := []interface{}{tx.AccountNonce, tx.Price, tx.GasLimit, tx.Recipient, tx.Amount, tx.Payload}
return append(data, tx.V, tx.R.Bytes(), tx.S.Bytes())
} }
func (tx *Transaction) String() string { func (tx *Transaction) String() string {
...@@ -176,12 +219,12 @@ func (tx *Transaction) String() string { ...@@ -176,12 +219,12 @@ func (tx *Transaction) String() string {
} else { } else {
from = fmt.Sprintf("%x", f[:]) from = fmt.Sprintf("%x", f[:])
} }
if t := tx.To(); t == nil { if tx.data.Recipient == nil {
to = "[contract creation]" to = "[contract creation]"
} else { } else {
to = fmt.Sprintf("%x", t[:]) to = fmt.Sprintf("%x", tx.data.Recipient[:])
} }
enc, _ := rlp.EncodeToBytes(tx) enc, _ := rlp.EncodeToBytes(&tx.data)
return fmt.Sprintf(` return fmt.Sprintf(`
TX(%x) TX(%x)
Contract: %v Contract: %v
...@@ -198,36 +241,24 @@ func (tx *Transaction) String() string { ...@@ -198,36 +241,24 @@ func (tx *Transaction) String() string {
Hex: %x Hex: %x
`, `,
tx.Hash(), tx.Hash(),
len(tx.Recipient) == 0, len(tx.data.Recipient) == 0,
from, from,
to, to,
tx.AccountNonce, tx.data.AccountNonce,
tx.Price, tx.data.Price,
tx.GasLimit, tx.data.GasLimit,
tx.Amount, tx.data.Amount,
tx.Payload, tx.data.Payload,
tx.V, tx.data.V,
tx.R, tx.data.R,
tx.S, tx.data.S,
enc, enc,
) )
} }
// Transaction slice type for basic sorting // Transaction slice type for basic sorting.
type Transactions []*Transaction type Transactions []*Transaction
// TODO: remove
func (self Transactions) RlpData() interface{} {
// Marshal the transactions of this block
enc := make([]interface{}, len(self))
for i, tx := range self {
// Cast it to a string (safe)
enc[i] = tx.RlpData()
}
return enc
}
func (s Transactions) Len() int { return len(s) } func (s Transactions) Len() int { return len(s) }
func (s Transactions) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s Transactions) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
...@@ -239,5 +270,5 @@ func (s Transactions) GetRlp(i int) []byte { ...@@ -239,5 +270,5 @@ func (s Transactions) GetRlp(i int) []byte {
type TxByNonce struct{ Transactions } type TxByNonce struct{ Transactions }
func (s TxByNonce) Less(i, j int) bool { func (s TxByNonce) Less(i, j int) bool {
return s.Transactions[i].AccountNonce < s.Transactions[j].AccountNonce return s.Transactions[i].data.AccountNonce < s.Transactions[j].data.AccountNonce
} }
...@@ -15,40 +15,35 @@ import ( ...@@ -15,40 +15,35 @@ import (
// at github.com/ethereum/tests. // at github.com/ethereum/tests.
var ( var (
emptyTx = NewTransactionMessage( emptyTx = NewTransaction(
0,
common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0),
nil, nil,
) )
rightvrsRecipient = common.HexToAddress("b94f5374fce5edbc8e2a8697c15331677e6ebf0b") rightvrsTx, _ = NewTransaction(
rightvrsTx = &Transaction{ 3,
Recipient: &rightvrsRecipient, common.HexToAddress("b94f5374fce5edbc8e2a8697c15331677e6ebf0b"),
AccountNonce: 3, big.NewInt(10),
Price: big.NewInt(1), big.NewInt(2000),
GasLimit: big.NewInt(2000), big.NewInt(1),
Amount: big.NewInt(10), common.FromHex("5544"),
Payload: common.FromHex("5544"), ).WithSignature(
V: 28, common.Hex2Bytes("98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a301"),
R: common.String2Big("0x98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a"), )
S: common.String2Big("0x8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3"),
}
) )
func TestTransactionHash(t *testing.T) { func TestTransactionHash(t *testing.T) {
// "EmptyTransaction"
if emptyTx.Hash() != common.HexToHash("c775b99e7ad12f50d819fcd602390467e28141316969f4b57f0626f74fe3b386") { if emptyTx.Hash() != common.HexToHash("c775b99e7ad12f50d819fcd602390467e28141316969f4b57f0626f74fe3b386") {
t.Errorf("empty transaction hash mismatch, got %x", emptyTx.Hash()) t.Errorf("empty transaction hash mismatch, got %x", emptyTx.Hash())
} }
// "RightVRSTest"
if rightvrsTx.Hash() != common.HexToHash("fe7a79529ed5f7c3375d06b26b186a8644e0e16c373d7a12be41c62d6042b77a") { if rightvrsTx.Hash() != common.HexToHash("fe7a79529ed5f7c3375d06b26b186a8644e0e16c373d7a12be41c62d6042b77a") {
t.Errorf("RightVRS transaction hash mismatch, got %x", rightvrsTx.Hash()) t.Errorf("RightVRS transaction hash mismatch, got %x", rightvrsTx.Hash())
} }
} }
func TestTransactionEncode(t *testing.T) { func TestTransactionEncode(t *testing.T) {
// "RightVRSTest"
txb, err := rlp.EncodeToBytes(rightvrsTx) txb, err := rlp.EncodeToBytes(rightvrsTx)
if err != nil { if err != nil {
t.Fatalf("encode error: %v", err) t.Fatalf("encode error: %v", err)
...@@ -72,19 +67,16 @@ func defaultTestKey() (*ecdsa.PrivateKey, common.Address) { ...@@ -72,19 +67,16 @@ func defaultTestKey() (*ecdsa.PrivateKey, common.Address) {
func TestRecipientEmpty(t *testing.T) { func TestRecipientEmpty(t *testing.T) {
_, addr := defaultTestKey() _, addr := defaultTestKey()
tx, err := decodeTx(common.Hex2Bytes("f8498080808080011ca09b16de9d5bdee2cf56c28d16275a4da68cd30273e2525f3959f5d62557489921a0372ebd8fb3345f7db7b5a86d42e24d36e983e259b0664ceb8c227ec9af572f3d")) tx, err := decodeTx(common.Hex2Bytes("f8498080808080011ca09b16de9d5bdee2cf56c28d16275a4da68cd30273e2525f3959f5d62557489921a0372ebd8fb3345f7db7b5a86d42e24d36e983e259b0664ceb8c227ec9af572f3d"))
if err != nil { if err != nil {
t.Error(err) t.Error(err)
t.FailNow() t.FailNow()
} }
from, err := tx.From() from, err := tx.From()
if err != nil { if err != nil {
t.Error(err) t.Error(err)
t.FailNow() t.FailNow()
} }
if addr != from { if addr != from {
t.Error("derived address doesn't match") t.Error("derived address doesn't match")
} }
......
...@@ -10,32 +10,32 @@ import ( ...@@ -10,32 +10,32 @@ import (
) )
type VMEnv struct { type VMEnv struct {
state *state.StateDB state *state.StateDB
block *types.Block header *types.Header
msg Message msg Message
depth int depth int
chain *ChainManager chain *ChainManager
typ vm.Type typ vm.Type
// structured logging // structured logging
logs []vm.StructLog logs []vm.StructLog
} }
func NewEnv(state *state.StateDB, chain *ChainManager, msg Message, block *types.Block) *VMEnv { func NewEnv(state *state.StateDB, chain *ChainManager, msg Message, header *types.Header) *VMEnv {
return &VMEnv{ return &VMEnv{
chain: chain, chain: chain,
state: state, state: state,
block: block, header: header,
msg: msg, msg: msg,
typ: vm.StdVmTy, typ: vm.StdVmTy,
} }
} }
func (self *VMEnv) Origin() common.Address { f, _ := self.msg.From(); return f } func (self *VMEnv) Origin() common.Address { f, _ := self.msg.From(); return f }
func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number() } func (self *VMEnv) BlockNumber() *big.Int { return self.header.Number }
func (self *VMEnv) Coinbase() common.Address { return self.block.Coinbase() } func (self *VMEnv) Coinbase() common.Address { return self.header.Coinbase }
func (self *VMEnv) Time() int64 { return self.block.Time() } func (self *VMEnv) Time() int64 { return int64(self.header.Time) }
func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty() } func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty }
func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit() } func (self *VMEnv) GasLimit() *big.Int { return self.header.GasLimit }
func (self *VMEnv) Value() *big.Int { return self.msg.Value() } func (self *VMEnv) Value() *big.Int { return self.msg.Value() }
func (self *VMEnv) State() *state.StateDB { return self.state } func (self *VMEnv) State() *state.StateDB { return self.state }
func (self *VMEnv) Depth() int { return self.depth } func (self *VMEnv) Depth() int { return self.depth }
......
This diff is collapsed.
package fetcher package fetcher
import ( import (
"encoding/binary"
"errors" "errors"
"math/big" "math/big"
"sync" "sync"
...@@ -10,58 +9,32 @@ import ( ...@@ -10,58 +9,32 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
) )
var ( var (
knownHash = common.Hash{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} testdb, _ = ethdb.NewMemDatabase()
unknownHash = common.Hash{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2} genesis = core.GenesisBlockForTesting(testdb, common.Address{}, big.NewInt(0))
bannedHash = common.Hash{3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3} unknownBlock = types.NewBlock(&types.Header{}, nil, nil, nil)
genesis = createBlock(1, common.Hash{}, knownHash)
) )
// idCounter is used by the createHashes method the generate deterministic but unique hashes // makeChain creates a chain of n blocks starting at and including parent.
var idCounter = int64(2) // #1 is the genesis block // the returned hash chain is ordered head->parent.
func makeChain(n int, seed byte, parent *types.Block) ([]common.Hash, map[common.Hash]*types.Block) {
// createHashes generates a batch of hashes rooted at a specific point in the chain. blocks := core.GenerateChain(parent, testdb, n, func(i int, gen *core.BlockGen) {
func createHashes(amount int, root common.Hash) (hashes []common.Hash) { gen.SetCoinbase(common.Address{seed})
hashes = make([]common.Hash, amount+1) })
hashes[len(hashes)-1] = root hashes := make([]common.Hash, n+1)
hashes[len(hashes)-1] = parent.Hash()
for i := 0; i < len(hashes)-1; i++ { blockm := make(map[common.Hash]*types.Block, n+1)
binary.BigEndian.PutUint64(hashes[i][:8], uint64(idCounter)) blockm[parent.Hash()] = parent
idCounter++ for i, b := range blocks {
} hashes[len(hashes)-i-2] = b.Hash()
return blockm[b.Hash()] = b
} }
return hashes, blockm
// createBlock assembles a new block at the given chain height.
func createBlock(i int, parent, hash common.Hash) *types.Block {
header := &types.Header{Number: big.NewInt(int64(i))}
block := types.NewBlockWithHeader(header)
block.HeaderHash = hash
block.ParentHeaderHash = parent
return block
}
// copyBlock makes a deep copy of a block suitable for local modifications.
func copyBlock(block *types.Block) *types.Block {
return createBlock(int(block.Number().Int64()), block.ParentHeaderHash, block.HeaderHash)
}
// createBlocksFromHashes assembles a collection of blocks, each having a correct
// place in the given hash chain.
func createBlocksFromHashes(hashes []common.Hash) map[common.Hash]*types.Block {
blocks := make(map[common.Hash]*types.Block)
for i := 0; i < len(hashes); i++ {
parent := knownHash
if i < len(hashes)-1 {
parent = hashes[i+1]
}
blocks[hashes[i]] = createBlock(len(hashes)-i, parent, hashes[i])
}
return blocks
} }
// fetcherTester is a test simulator for mocking out local block chain. // fetcherTester is a test simulator for mocking out local block chain.
...@@ -77,8 +50,8 @@ type fetcherTester struct { ...@@ -77,8 +50,8 @@ type fetcherTester struct {
// newTester creates a new fetcher test mocker. // newTester creates a new fetcher test mocker.
func newTester() *fetcherTester { func newTester() *fetcherTester {
tester := &fetcherTester{ tester := &fetcherTester{
hashes: []common.Hash{knownHash}, hashes: []common.Hash{genesis.Hash()},
blocks: map[common.Hash]*types.Block{knownHash: genesis}, blocks: map[common.Hash]*types.Block{genesis.Hash(): genesis},
} }
tester.fetcher = New(tester.getBlock, tester.verifyBlock, tester.broadcastBlock, tester.chainHeight, tester.insertChain, tester.dropPeer) tester.fetcher = New(tester.getBlock, tester.verifyBlock, tester.broadcastBlock, tester.chainHeight, tester.insertChain, tester.dropPeer)
tester.fetcher.Start() tester.fetcher.Start()
...@@ -138,10 +111,9 @@ func (f *fetcherTester) dropPeer(peer string) { ...@@ -138,10 +111,9 @@ func (f *fetcherTester) dropPeer(peer string) {
// peerFetcher retrieves a fetcher associated with a simulated peer. // peerFetcher retrieves a fetcher associated with a simulated peer.
func (f *fetcherTester) makeFetcher(blocks map[common.Hash]*types.Block) blockRequesterFn { func (f *fetcherTester) makeFetcher(blocks map[common.Hash]*types.Block) blockRequesterFn {
// Copy all the blocks to ensure they are not tampered with
closure := make(map[common.Hash]*types.Block) closure := make(map[common.Hash]*types.Block)
for hash, block := range blocks { for hash, block := range blocks {
closure[hash] = copyBlock(block) closure[hash] = block
} }
// Create a function that returns blocks from the closure // Create a function that returns blocks from the closure
return func(hashes []common.Hash) error { return func(hashes []common.Hash) error {
...@@ -195,8 +167,7 @@ func verifyImportDone(t *testing.T, imported chan *types.Block) { ...@@ -195,8 +167,7 @@ func verifyImportDone(t *testing.T, imported chan *types.Block) {
func TestSequentialAnnouncements(t *testing.T) { func TestSequentialAnnouncements(t *testing.T) {
// Create a chain of blocks to import // Create a chain of blocks to import
targetBlocks := 4 * hashLimit targetBlocks := 4 * hashLimit
hashes := createHashes(targetBlocks, knownHash) hashes, blocks := makeChain(targetBlocks, 0, genesis)
blocks := createBlocksFromHashes(hashes)
tester := newTester() tester := newTester()
fetcher := tester.makeFetcher(blocks) fetcher := tester.makeFetcher(blocks)
...@@ -217,8 +188,7 @@ func TestSequentialAnnouncements(t *testing.T) { ...@@ -217,8 +188,7 @@ func TestSequentialAnnouncements(t *testing.T) {
func TestConcurrentAnnouncements(t *testing.T) { func TestConcurrentAnnouncements(t *testing.T) {
// Create a chain of blocks to import // Create a chain of blocks to import
targetBlocks := 4 * hashLimit targetBlocks := 4 * hashLimit
hashes := createHashes(targetBlocks, knownHash) hashes, blocks := makeChain(targetBlocks, 0, genesis)
blocks := createBlocksFromHashes(hashes)
// Assemble a tester with a built in counter for the requests // Assemble a tester with a built in counter for the requests
tester := newTester() tester := newTester()
...@@ -253,8 +223,7 @@ func TestConcurrentAnnouncements(t *testing.T) { ...@@ -253,8 +223,7 @@ func TestConcurrentAnnouncements(t *testing.T) {
func TestOverlappingAnnouncements(t *testing.T) { func TestOverlappingAnnouncements(t *testing.T) {
// Create a chain of blocks to import // Create a chain of blocks to import
targetBlocks := 4 * hashLimit targetBlocks := 4 * hashLimit
hashes := createHashes(targetBlocks, knownHash) hashes, blocks := makeChain(targetBlocks, 0, genesis)
blocks := createBlocksFromHashes(hashes)
tester := newTester() tester := newTester()
fetcher := tester.makeFetcher(blocks) fetcher := tester.makeFetcher(blocks)
...@@ -280,8 +249,7 @@ func TestOverlappingAnnouncements(t *testing.T) { ...@@ -280,8 +249,7 @@ func TestOverlappingAnnouncements(t *testing.T) {
// Tests that announces already being retrieved will not be duplicated. // Tests that announces already being retrieved will not be duplicated.
func TestPendingDeduplication(t *testing.T) { func TestPendingDeduplication(t *testing.T) {
// Create a hash and corresponding block // Create a hash and corresponding block
hashes := createHashes(1, knownHash) hashes, blocks := makeChain(1, 0, genesis)
blocks := createBlocksFromHashes(hashes)
// Assemble a tester with a built in counter and delayed fetcher // Assemble a tester with a built in counter and delayed fetcher
tester := newTester() tester := newTester()
...@@ -319,9 +287,9 @@ func TestPendingDeduplication(t *testing.T) { ...@@ -319,9 +287,9 @@ func TestPendingDeduplication(t *testing.T) {
// imported when all the gaps are filled in. // imported when all the gaps are filled in.
func TestRandomArrivalImport(t *testing.T) { func TestRandomArrivalImport(t *testing.T) {
// Create a chain of blocks to import, and choose one to delay // Create a chain of blocks to import, and choose one to delay
hashes := createHashes(maxQueueDist, knownHash) targetBlocks := maxQueueDist
blocks := createBlocksFromHashes(hashes) hashes, blocks := makeChain(targetBlocks, 0, genesis)
skip := maxQueueDist / 2 skip := targetBlocks / 2
tester := newTester() tester := newTester()
fetcher := tester.makeFetcher(blocks) fetcher := tester.makeFetcher(blocks)
...@@ -345,9 +313,9 @@ func TestRandomArrivalImport(t *testing.T) { ...@@ -345,9 +313,9 @@ func TestRandomArrivalImport(t *testing.T) {
// are correctly schedule, filling and import queue gaps. // are correctly schedule, filling and import queue gaps.
func TestQueueGapFill(t *testing.T) { func TestQueueGapFill(t *testing.T) {
// Create a chain of blocks to import, and choose one to not announce at all // Create a chain of blocks to import, and choose one to not announce at all
hashes := createHashes(maxQueueDist, knownHash) targetBlocks := maxQueueDist
blocks := createBlocksFromHashes(hashes) hashes, blocks := makeChain(targetBlocks, 0, genesis)
skip := maxQueueDist / 2 skip := targetBlocks / 2
tester := newTester() tester := newTester()
fetcher := tester.makeFetcher(blocks) fetcher := tester.makeFetcher(blocks)
...@@ -371,8 +339,7 @@ func TestQueueGapFill(t *testing.T) { ...@@ -371,8 +339,7 @@ func TestQueueGapFill(t *testing.T) {
// announces, etc) do not get scheduled for import multiple times. // announces, etc) do not get scheduled for import multiple times.
func TestImportDeduplication(t *testing.T) { func TestImportDeduplication(t *testing.T) {
// Create two blocks to import (one for duplication, the other for stalling) // Create two blocks to import (one for duplication, the other for stalling)
hashes := createHashes(2, knownHash) hashes, blocks := makeChain(2, 0, genesis)
blocks := createBlocksFromHashes(hashes)
// Create the tester and wrap the importer with a counter // Create the tester and wrap the importer with a counter
tester := newTester() tester := newTester()
...@@ -410,9 +377,7 @@ func TestImportDeduplication(t *testing.T) { ...@@ -410,9 +377,7 @@ func TestImportDeduplication(t *testing.T) {
// discarded no prevent wasting resources on useless blocks from faulty peers. // discarded no prevent wasting resources on useless blocks from faulty peers.
func TestDistantDiscarding(t *testing.T) { func TestDistantDiscarding(t *testing.T) {
// Create a long chain to import // Create a long chain to import
hashes := createHashes(3*maxQueueDist, knownHash) hashes, blocks := makeChain(3*maxQueueDist, 0, genesis)
blocks := createBlocksFromHashes(hashes)
head := hashes[len(hashes)/2] head := hashes[len(hashes)/2]
// Create a tester and simulate a head block being the middle of the above chain // Create a tester and simulate a head block being the middle of the above chain
...@@ -445,11 +410,11 @@ func TestHashMemoryExhaustionAttack(t *testing.T) { ...@@ -445,11 +410,11 @@ func TestHashMemoryExhaustionAttack(t *testing.T) {
tester.fetcher.importedHook = func(block *types.Block) { imported <- block } tester.fetcher.importedHook = func(block *types.Block) { imported <- block }
// Create a valid chain and an infinite junk chain // Create a valid chain and an infinite junk chain
hashes := createHashes(hashLimit+2*maxQueueDist, knownHash) targetBlocks := hashLimit + 2*maxQueueDist
blocks := createBlocksFromHashes(hashes) hashes, blocks := makeChain(targetBlocks, 0, genesis)
valid := tester.makeFetcher(blocks) valid := tester.makeFetcher(blocks)
attack := createHashes(hashLimit+2*maxQueueDist, unknownHash) attack, _ := makeChain(targetBlocks, 0, unknownBlock)
attacker := tester.makeFetcher(nil) attacker := tester.makeFetcher(nil)
// Feed the tester a huge hashset from the attacker, and a limited from the valid peer // Feed the tester a huge hashset from the attacker, and a limited from the valid peer
...@@ -484,13 +449,11 @@ func TestBlockMemoryExhaustionAttack(t *testing.T) { ...@@ -484,13 +449,11 @@ func TestBlockMemoryExhaustionAttack(t *testing.T) {
tester.fetcher.importedHook = func(block *types.Block) { imported <- block } tester.fetcher.importedHook = func(block *types.Block) { imported <- block }
// Create a valid chain and a batch of dangling (but in range) blocks // Create a valid chain and a batch of dangling (but in range) blocks
hashes := createHashes(blockLimit+2*maxQueueDist, knownHash) targetBlocks := hashLimit + 2*maxQueueDist
blocks := createBlocksFromHashes(hashes) hashes, blocks := makeChain(targetBlocks, 0, genesis)
attack := make(map[common.Hash]*types.Block) attack := make(map[common.Hash]*types.Block)
for len(attack) < blockLimit+2*maxQueueDist { for i := byte(0); len(attack) < blockLimit+2*maxQueueDist; i++ {
hashes := createHashes(maxQueueDist-1, unknownHash) hashes, blocks := makeChain(maxQueueDist-1, i, unknownBlock)
blocks := createBlocksFromHashes(hashes)
for _, hash := range hashes[:maxQueueDist-2] { for _, hash := range hashes[:maxQueueDist-2] {
attack[hash] = blocks[hash] attack[hash] = blocks[hash]
} }
...@@ -499,7 +462,7 @@ func TestBlockMemoryExhaustionAttack(t *testing.T) { ...@@ -499,7 +462,7 @@ func TestBlockMemoryExhaustionAttack(t *testing.T) {
for _, block := range attack { for _, block := range attack {
tester.fetcher.Enqueue("attacker", block) tester.fetcher.Enqueue("attacker", block)
} }
time.Sleep(100 * time.Millisecond) time.Sleep(200 * time.Millisecond)
if queued := tester.fetcher.queue.Size(); queued != blockLimit { if queued := tester.fetcher.queue.Size(); queued != blockLimit {
t.Fatalf("queued block count mismatch: have %d, want %d", queued, blockLimit) t.Fatalf("queued block count mismatch: have %d, want %d", queued, blockLimit)
} }
......
...@@ -47,14 +47,21 @@ func NewGasPriceOracle(eth *Ethereum) (self *GasPriceOracle) { ...@@ -47,14 +47,21 @@ func NewGasPriceOracle(eth *Ethereum) (self *GasPriceOracle) {
} }
func (self *GasPriceOracle) processPastBlocks() { func (self *GasPriceOracle) processPastBlocks() {
last := self.chain.CurrentBlock().NumberU64() last := int64(-1)
first := uint64(0) cblock := self.chain.CurrentBlock()
if cblock != nil {
last = int64(cblock.NumberU64())
}
first := int64(0)
if last > gpoProcessPastBlocks { if last > gpoProcessPastBlocks {
first = last - gpoProcessPastBlocks first = last - gpoProcessPastBlocks
} }
self.firstProcessed = first self.firstProcessed = uint64(first)
for i := first; i <= last; i++ { for i := first; i <= last; i++ {
self.processBlock(self.chain.GetBlockByNumber(i)) block := self.chain.GetBlockByNumber(uint64(i))
if block != nil {
self.processBlock(block)
}
} }
} }
...@@ -133,20 +140,20 @@ func (self *GasPriceOracle) lowestPrice(block *types.Block) *big.Int { ...@@ -133,20 +140,20 @@ func (self *GasPriceOracle) lowestPrice(block *types.Block) *big.Int {
gasUsed = recepits[len(recepits)-1].CumulativeGasUsed gasUsed = recepits[len(recepits)-1].CumulativeGasUsed
} }
if new(big.Int).Mul(gasUsed, big.NewInt(100)).Cmp(new(big.Int).Mul(block.Header().GasLimit, if new(big.Int).Mul(gasUsed, big.NewInt(100)).Cmp(new(big.Int).Mul(block.GasLimit(),
big.NewInt(int64(self.eth.GpoFullBlockRatio)))) < 0 { big.NewInt(int64(self.eth.GpoFullBlockRatio)))) < 0 {
// block is not full, could have posted a tx with MinGasPrice // block is not full, could have posted a tx with MinGasPrice
return self.eth.GpoMinGasPrice return self.eth.GpoMinGasPrice
} }
if len(block.Transactions()) < 1 { txs := block.Transactions()
if len(txs) == 0 {
return self.eth.GpoMinGasPrice return self.eth.GpoMinGasPrice
} }
// block is full, find smallest gasPrice // block is full, find smallest gasPrice
minPrice := block.Transactions()[0].GasPrice() minPrice := txs[0].GasPrice()
for i := 1; i < len(block.Transactions()); i++ { for i := 1; i < len(txs); i++ {
price := block.Transactions()[i].GasPrice() price := txs[i].GasPrice()
if price.Cmp(minPrice) < 0 { if price.Cmp(minPrice) < 0 {
minPrice = price minPrice = price
} }
......
...@@ -93,7 +93,7 @@ func NewProtocolManager(protocolVersion, networkId int, mux *event.TypeMux, txpo ...@@ -93,7 +93,7 @@ func NewProtocolManager(protocolVersion, networkId int, mux *event.TypeMux, txpo
manager.downloader = downloader.New(manager.eventMux, manager.chainman.HasBlock, manager.chainman.GetBlock, manager.chainman.InsertChain, manager.removePeer) manager.downloader = downloader.New(manager.eventMux, manager.chainman.HasBlock, manager.chainman.GetBlock, manager.chainman.InsertChain, manager.removePeer)
validator := func(block *types.Block, parent *types.Block) error { validator := func(block *types.Block, parent *types.Block) error {
return core.ValidateHeader(pow, block.Header(), parent.Header(), true) return core.ValidateHeader(pow, block.Header(), parent, true)
} }
heighter := func() uint64 { heighter := func() uint64 {
return manager.chainman.CurrentBlock().NumberU64() return manager.chainman.CurrentBlock().NumberU64()
......
...@@ -234,7 +234,7 @@ func (pool *fakeTxPool) GetTransactions() types.Transactions { ...@@ -234,7 +234,7 @@ func (pool *fakeTxPool) GetTransactions() types.Transactions {
func newtx(from *crypto.Key, nonce uint64, datasize int) *types.Transaction { func newtx(from *crypto.Key, nonce uint64, datasize int) *types.Transaction {
data := make([]byte, datasize) data := make([]byte, datasize)
tx := types.NewTransactionMessage(common.Address{}, big.NewInt(0), big.NewInt(100000), big.NewInt(0), data) tx := types.NewTransaction(nonce, common.Address{}, big.NewInt(0), big.NewInt(100000), big.NewInt(0), data)
tx.SetNonce(nonce) tx, _ = tx.SignECDSA(from.PrivateKey)
return tx return tx
} }
...@@ -115,3 +115,7 @@ func (self *LDBDatabase) Close() { ...@@ -115,3 +115,7 @@ func (self *LDBDatabase) Close() {
self.db.Close() self.db.Close()
glog.V(logger.Error).Infoln("flushed and closed db:", self.fn) glog.V(logger.Error).Infoln("flushed and closed db:", self.fn)
} }
func (self *LDBDatabase) LDB() *leveldb.DB {
return self.db
}
...@@ -90,15 +90,13 @@ done: ...@@ -90,15 +90,13 @@ done:
} }
} }
func (self *CpuAgent) mine(block *types.Block, stop <- chan struct{}) { func (self *CpuAgent) mine(block *types.Block, stop <-chan struct{}) {
glog.V(logger.Debug).Infof("(re)started agent[%d]. mining...\n", self.index) glog.V(logger.Debug).Infof("(re)started agent[%d]. mining...\n", self.index)
// Mine // Mine
nonce, mixDigest := self.pow.Search(block, stop) nonce, mixDigest := self.pow.Search(block, stop)
if nonce != 0 { if nonce != 0 {
block.SetNonce(nonce) self.returnCh <- block.WithMiningResult(nonce, common.BytesToHash(mixDigest))
block.Header().MixDigest = common.BytesToHash(mixDigest)
self.returnCh <- block
} else { } else {
self.returnCh <- nil self.returnCh <- nil
} }
......
...@@ -81,9 +81,7 @@ func (a *RemoteAgent) SubmitWork(nonce uint64, mixDigest, seedHash common.Hash) ...@@ -81,9 +81,7 @@ func (a *RemoteAgent) SubmitWork(nonce uint64, mixDigest, seedHash common.Hash)
// Make sure the external miner was working on the right hash // Make sure the external miner was working on the right hash
if a.currentWork != nil && a.work != nil { if a.currentWork != nil && a.work != nil {
a.currentWork.SetNonce(nonce) a.returnCh <- a.currentWork.WithMiningResult(nonce, mixDigest)
a.currentWork.Header().MixDigest = mixDigest
a.returnCh <- a.currentWork
//a.returnCh <- Work{a.currentWork.Number().Uint64(), nonce, mixDigest.Bytes(), seedHash.Bytes()} //a.returnCh <- Work{a.currentWork.Number().Uint64(), nonce, mixDigest.Bytes(), seedHash.Bytes()}
return true return true
} }
......
This diff is collapsed.
...@@ -5,12 +5,9 @@ import ( ...@@ -5,12 +5,9 @@ import (
"io" "io"
"math/big" "math/big"
"reflect" "reflect"
"sync"
) )
// TODO: put encbufs in a sync.Pool.
// Doing that requires zeroing the buffers after use.
// encReader will need to drop it's buffer when done.
var ( var (
// Common encoded values. // Common encoded values.
// These are useful when implementing EncodeRLP. // These are useful when implementing EncodeRLP.
...@@ -32,46 +29,10 @@ type Encoder interface { ...@@ -32,46 +29,10 @@ type Encoder interface {
EncodeRLP(io.Writer) error EncodeRLP(io.Writer) error
} }
// Flat wraps a value (which must encode as a list) so // ListSize returns the encoded size of an RLP list with the given
// it encodes as the list's elements. // content size.
// func ListSize(contentSize uint64) uint64 {
// Example: suppose you have defined a type return uint64(headsize(contentSize)) + contentSize
//
// type foo struct { A, B uint }
//
// Under normal encoding rules,
//
// rlp.Encode(foo{1, 2}) --> 0xC20102
//
// This function can help you achieve the following encoding:
//
// rlp.Encode(rlp.Flat(foo{1, 2})) --> 0x0102
func Flat(val interface{}) Encoder {
return flatenc{val}
}
type flatenc struct{ val interface{} }
func (e flatenc) EncodeRLP(out io.Writer) error {
// record current output position
var (
eb = out.(*encbuf)
prevstrsize = len(eb.str)
prevnheads = len(eb.lheads)
)
if err := eb.encode(e.val); err != nil {
return err
}
// check that a new list header has appeared
if len(eb.lheads) == prevnheads || eb.lheads[prevnheads].offset == prevstrsize-1 {
return fmt.Errorf("rlp.Flat: %T did not encode as list", e.val)
}
// remove the new list header
newhead := eb.lheads[prevnheads]
copy(eb.lheads[prevnheads:], eb.lheads[prevnheads+1:])
eb.lheads = eb.lheads[:len(eb.lheads)-1]
eb.lhsize -= headsize(uint64(newhead.size))
return nil
} }
// Encode writes the RLP encoding of val to w. Note that Encode may // Encode writes the RLP encoding of val to w. Note that Encode may
...@@ -112,7 +73,9 @@ func Encode(w io.Writer, val interface{}) error { ...@@ -112,7 +73,9 @@ func Encode(w io.Writer, val interface{}) error {
// Avoid copying by writing to the outer encbuf directly. // Avoid copying by writing to the outer encbuf directly.
return outer.encode(val) return outer.encode(val)
} }
eb := newencbuf() eb := encbufPool.Get().(*encbuf)
eb.reset()
defer encbufPool.Put(eb)
if err := eb.encode(val); err != nil { if err := eb.encode(val); err != nil {
return err return err
} }
...@@ -122,7 +85,9 @@ func Encode(w io.Writer, val interface{}) error { ...@@ -122,7 +85,9 @@ func Encode(w io.Writer, val interface{}) error {
// EncodeBytes returns the RLP encoding of val. // EncodeBytes returns the RLP encoding of val.
// Please see the documentation of Encode for the encoding rules. // Please see the documentation of Encode for the encoding rules.
func EncodeToBytes(val interface{}) ([]byte, error) { func EncodeToBytes(val interface{}) ([]byte, error) {
eb := newencbuf() eb := encbufPool.Get().(*encbuf)
eb.reset()
defer encbufPool.Put(eb)
if err := eb.encode(val); err != nil { if err := eb.encode(val); err != nil {
return nil, err return nil, err
} }
...@@ -135,7 +100,8 @@ func EncodeToBytes(val interface{}) ([]byte, error) { ...@@ -135,7 +100,8 @@ func EncodeToBytes(val interface{}) ([]byte, error) {
// //
// Please see the documentation of Encode for the encoding rules. // Please see the documentation of Encode for the encoding rules.
func EncodeToReader(val interface{}) (size int, r io.Reader, err error) { func EncodeToReader(val interface{}) (size int, r io.Reader, err error) {
eb := newencbuf() eb := encbufPool.Get().(*encbuf)
eb.reset()
if err := eb.encode(val); err != nil { if err := eb.encode(val); err != nil {
return 0, nil, err return 0, nil, err
} }
...@@ -182,8 +148,19 @@ func puthead(buf []byte, smalltag, largetag byte, size uint64) int { ...@@ -182,8 +148,19 @@ func puthead(buf []byte, smalltag, largetag byte, size uint64) int {
} }
} }
func newencbuf() *encbuf { // encbufs are pooled.
return &encbuf{sizebuf: make([]byte, 9)} var encbufPool = sync.Pool{
New: func() interface{} { return &encbuf{sizebuf: make([]byte, 9)} },
}
func (w *encbuf) reset() {
w.lhsize = 0
if w.str != nil {
w.str = w.str[:0]
}
if w.lheads != nil {
w.lheads = w.lheads[:0]
}
} }
// encbuf implements io.Writer so it can be passed it into EncodeRLP. // encbuf implements io.Writer so it can be passed it into EncodeRLP.
...@@ -295,6 +272,8 @@ type encReader struct { ...@@ -295,6 +272,8 @@ type encReader struct {
func (r *encReader) Read(b []byte) (n int, err error) { func (r *encReader) Read(b []byte) (n int, err error) {
for { for {
if r.piece = r.next(); r.piece == nil { if r.piece = r.next(); r.piece == nil {
encbufPool.Put(r.buf)
r.buf = nil
return n, io.EOF return n, io.EOF
} }
nn := copy(b[n:], r.piece) nn := copy(b[n:], r.piece)
...@@ -313,6 +292,9 @@ func (r *encReader) Read(b []byte) (n int, err error) { ...@@ -313,6 +292,9 @@ func (r *encReader) Read(b []byte) (n int, err error) {
// it returns nil at EOF. // it returns nil at EOF.
func (r *encReader) next() []byte { func (r *encReader) next() []byte {
switch { switch {
case r.buf == nil:
return nil
case r.piece != nil: case r.piece != nil:
// There is still data available for reading. // There is still data available for reading.
return r.piece return r.piece
......
...@@ -189,15 +189,6 @@ var encTests = []encTest{ ...@@ -189,15 +189,6 @@ var encTests = []encTest{
{val: &recstruct{5, nil}, output: "C205C0"}, {val: &recstruct{5, nil}, output: "C205C0"},
{val: &recstruct{5, &recstruct{4, &recstruct{3, nil}}}, output: "C605C404C203C0"}, {val: &recstruct{5, &recstruct{4, &recstruct{3, nil}}}, output: "C605C404C203C0"},
// flat
{val: Flat(uint(1)), error: "rlp.Flat: uint did not encode as list"},
{val: Flat(simplestruct{A: 3, B: "foo"}), output: "0383666F6F"},
{
// value generates more list headers after the Flat
val: []interface{}{"foo", []uint{1, 2}, Flat([]uint{3, 4}), []uint{5, 6}, "bar"},
output: "D083666F6FC201020304C2050683626172",
},
// nil // nil
{val: (*uint)(nil), output: "80"}, {val: (*uint)(nil), output: "80"},
{val: (*string)(nil), output: "80"}, {val: (*string)(nil), output: "80"},
......
...@@ -348,14 +348,6 @@ func (self *ethApi) GetBlockByNumber(req *shared.Request) (interface{}, error) { ...@@ -348,14 +348,6 @@ func (self *ethApi) GetBlockByNumber(req *shared.Request) (interface{}, error) {
block := self.xeth.EthBlockByNumber(args.BlockNumber) block := self.xeth.EthBlockByNumber(args.BlockNumber)
br := NewBlockRes(block, args.IncludeTxs) br := NewBlockRes(block, args.IncludeTxs)
// If request was for "pending", nil nonsensical fields
if args.BlockNumber == -2 {
br.BlockHash = nil
br.BlockNumber = nil
br.Miner = nil
br.Nonce = nil
br.LogsBloom = nil
}
return br, nil return br, nil
} }
......
...@@ -270,29 +270,31 @@ func NewBlockRes(block *types.Block, fullTx bool) *BlockRes { ...@@ -270,29 +270,31 @@ func NewBlockRes(block *types.Block, fullTx bool) *BlockRes {
res.BlockHash = newHexData(block.Hash()) res.BlockHash = newHexData(block.Hash())
res.ParentHash = newHexData(block.ParentHash()) res.ParentHash = newHexData(block.ParentHash())
res.Nonce = newHexData(block.Nonce()) res.Nonce = newHexData(block.Nonce())
res.Sha3Uncles = newHexData(block.Header().UncleHash) res.Sha3Uncles = newHexData(block.UncleHash())
res.LogsBloom = newHexData(block.Bloom()) res.LogsBloom = newHexData(block.Bloom())
res.TransactionRoot = newHexData(block.Header().TxHash) res.TransactionRoot = newHexData(block.TxHash())
res.StateRoot = newHexData(block.Root()) res.StateRoot = newHexData(block.Root())
res.Miner = newHexData(block.Header().Coinbase) res.Miner = newHexData(block.Coinbase())
res.Difficulty = newHexNum(block.Difficulty()) res.Difficulty = newHexNum(block.Difficulty())
res.TotalDifficulty = newHexNum(block.Td) res.TotalDifficulty = newHexNum(block.Td)
res.Size = newHexNum(block.Size().Int64()) res.Size = newHexNum(block.Size().Int64())
res.ExtraData = newHexData(block.Header().Extra) res.ExtraData = newHexData(block.Extra())
res.GasLimit = newHexNum(block.GasLimit()) res.GasLimit = newHexNum(block.GasLimit())
res.GasUsed = newHexNum(block.GasUsed()) res.GasUsed = newHexNum(block.GasUsed())
res.UnixTimestamp = newHexNum(block.Time()) res.UnixTimestamp = newHexNum(block.Time())
res.Transactions = make([]*TransactionRes, len(block.Transactions())) txs := block.Transactions()
for i, tx := range block.Transactions() { res.Transactions = make([]*TransactionRes, len(txs))
for i, tx := range txs {
res.Transactions[i] = NewTransactionRes(tx) res.Transactions[i] = NewTransactionRes(tx)
res.Transactions[i].BlockHash = res.BlockHash res.Transactions[i].BlockHash = res.BlockHash
res.Transactions[i].BlockNumber = res.BlockNumber res.Transactions[i].BlockNumber = res.BlockNumber
res.Transactions[i].TxIndex = newHexNum(i) res.Transactions[i].TxIndex = newHexNum(i)
} }
res.Uncles = make([]*UncleRes, len(block.Uncles())) uncles := block.Uncles()
for i, uncle := range block.Uncles() { res.Uncles = make([]*UncleRes, len(uncles))
for i, uncle := range uncles {
res.Uncles[i] = NewUncleRes(uncle) res.Uncles[i] = NewUncleRes(uncle)
} }
......
...@@ -245,7 +245,7 @@ func (t *BlockTest) TryBlocksInsert(chainManager *core.ChainManager) error { ...@@ -245,7 +245,7 @@ func (t *BlockTest) TryBlocksInsert(chainManager *core.ChainManager) error {
if b.BlockHeader == nil { if b.BlockHeader == nil {
continue // OK - block is supposed to be invalid, continue with next block continue // OK - block is supposed to be invalid, continue with next block
} else { } else {
return fmt.Errorf("Block RLP decoding failed when expected to succeed: ", err) return fmt.Errorf("Block RLP decoding failed when expected to succeed: %v", err)
} }
} }
// RLP decoding worked, try to insert into chain: // RLP decoding worked, try to insert into chain:
...@@ -254,7 +254,7 @@ func (t *BlockTest) TryBlocksInsert(chainManager *core.ChainManager) error { ...@@ -254,7 +254,7 @@ func (t *BlockTest) TryBlocksInsert(chainManager *core.ChainManager) error {
if b.BlockHeader == nil { if b.BlockHeader == nil {
continue // OK - block is supposed to be invalid, continue with next block continue // OK - block is supposed to be invalid, continue with next block
} else { } else {
return fmt.Errorf("Block insertion into chain failed: ", err) return fmt.Errorf("Block insertion into chain failed: %v", err)
} }
} }
if b.BlockHeader == nil { if b.BlockHeader == nil {
...@@ -262,7 +262,7 @@ func (t *BlockTest) TryBlocksInsert(chainManager *core.ChainManager) error { ...@@ -262,7 +262,7 @@ func (t *BlockTest) TryBlocksInsert(chainManager *core.ChainManager) error {
} }
err = t.validateBlockHeader(b.BlockHeader, cb.Header()) err = t.validateBlockHeader(b.BlockHeader, cb.Header())
if err != nil { if err != nil {
return fmt.Errorf("Block header validation failed: ", err) return fmt.Errorf("Block header validation failed: %v", err)
} }
} }
return nil return nil
...@@ -286,7 +286,7 @@ func (s *BlockTest) validateBlockHeader(h *btHeader, h2 *types.Header) error { ...@@ -286,7 +286,7 @@ func (s *BlockTest) validateBlockHeader(h *btHeader, h2 *types.Header) error {
expectedNonce := mustConvertBytes(h.Nonce) expectedNonce := mustConvertBytes(h.Nonce)
if !bytes.Equal(expectedNonce, h2.Nonce[:]) { if !bytes.Equal(expectedNonce, h2.Nonce[:]) {
return fmt.Errorf("Nonce: expected: %v, decoded: %v", expectedNonce, h2.Nonce[:]) return fmt.Errorf("Nonce: expected: %v, decoded: %v", expectedNonce, h2.Nonce)
} }
expectedNumber := mustConvertBigInt(h.Number, 16) expectedNumber := mustConvertBigInt(h.Number, 16)
...@@ -423,9 +423,8 @@ func mustConvertHeader(in btHeader) *types.Header { ...@@ -423,9 +423,8 @@ func mustConvertHeader(in btHeader) *types.Header {
GasLimit: mustConvertBigInt(in.GasLimit, 16), GasLimit: mustConvertBigInt(in.GasLimit, 16),
Difficulty: mustConvertBigInt(in.Difficulty, 16), Difficulty: mustConvertBigInt(in.Difficulty, 16),
Time: mustConvertUint(in.Timestamp, 16), Time: mustConvertUint(in.Timestamp, 16),
Nonce: types.EncodeNonce(mustConvertUint(in.Nonce, 16)),
} }
// XXX cheats? :-)
header.SetNonce(mustConvertUint(in.Nonce, 16))
return header return header
} }
......
...@@ -152,54 +152,53 @@ func verifyTxFields(txTest TransactionTest, decodedTx *types.Transaction) (err e ...@@ -152,54 +152,53 @@ func verifyTxFields(txTest TransactionTest, decodedTx *types.Transaction) (err e
} }
expectedData := mustConvertBytes(txTest.Transaction.Data) expectedData := mustConvertBytes(txTest.Transaction.Data)
if !bytes.Equal(expectedData, decodedTx.Payload) { if !bytes.Equal(expectedData, decodedTx.Data()) {
return fmt.Errorf("Tx input data mismatch: %#v %#v", expectedData, decodedTx.Payload) return fmt.Errorf("Tx input data mismatch: %#v %#v", expectedData, decodedTx.Data())
} }
expectedGasLimit := mustConvertBigInt(txTest.Transaction.GasLimit, 16) expectedGasLimit := mustConvertBigInt(txTest.Transaction.GasLimit, 16)
if expectedGasLimit.Cmp(decodedTx.GasLimit) != 0 { if expectedGasLimit.Cmp(decodedTx.Gas()) != 0 {
return fmt.Errorf("GasLimit mismatch: %v %v", expectedGasLimit, decodedTx.GasLimit) return fmt.Errorf("GasLimit mismatch: %v %v", expectedGasLimit, decodedTx.Gas())
} }
expectedGasPrice := mustConvertBigInt(txTest.Transaction.GasPrice, 16) expectedGasPrice := mustConvertBigInt(txTest.Transaction.GasPrice, 16)
if expectedGasPrice.Cmp(decodedTx.Price) != 0 { if expectedGasPrice.Cmp(decodedTx.GasPrice()) != 0 {
return fmt.Errorf("GasPrice mismatch: %v %v", expectedGasPrice, decodedTx.Price) return fmt.Errorf("GasPrice mismatch: %v %v", expectedGasPrice, decodedTx.GasPrice())
} }
expectedNonce := mustConvertUint(txTest.Transaction.Nonce, 16) expectedNonce := mustConvertUint(txTest.Transaction.Nonce, 16)
if expectedNonce != decodedTx.AccountNonce { if expectedNonce != decodedTx.Nonce() {
return fmt.Errorf("Nonce mismatch: %v %v", expectedNonce, decodedTx.AccountNonce) return fmt.Errorf("Nonce mismatch: %v %v", expectedNonce, decodedTx.Nonce())
} }
expectedR := common.Bytes2Big(mustConvertBytes(txTest.Transaction.R)) v, r, s := decodedTx.SignatureValues()
if expectedR.Cmp(decodedTx.R) != 0 { expectedR := mustConvertBigInt(txTest.Transaction.R, 16)
return fmt.Errorf("R mismatch: %v %v", expectedR, decodedTx.R) if r.Cmp(expectedR) != 0 {
return fmt.Errorf("R mismatch: %v %v", expectedR, r)
} }
expectedS := mustConvertBigInt(txTest.Transaction.S, 16)
expectedS := common.Bytes2Big(mustConvertBytes(txTest.Transaction.S)) if s.Cmp(expectedS) != 0 {
if expectedS.Cmp(decodedTx.S) != 0 { return fmt.Errorf("S mismatch: %v %v", expectedS, s)
return fmt.Errorf("S mismatch: %v %v", expectedS, decodedTx.S)
} }
expectedV := mustConvertUint(txTest.Transaction.V, 16) expectedV := mustConvertUint(txTest.Transaction.V, 16)
if expectedV != uint64(decodedTx.V) { if uint64(v) != expectedV {
return fmt.Errorf("V mismatch: %v %v", expectedV, uint64(decodedTx.V)) return fmt.Errorf("V mismatch: %v %v", expectedV, v)
} }
expectedTo := mustConvertAddress(txTest.Transaction.To) expectedTo := mustConvertAddress(txTest.Transaction.To)
if decodedTx.Recipient == nil { if decodedTx.To() == nil {
if expectedTo != common.BytesToAddress([]byte{}) { // "empty" or "zero" address if expectedTo != common.BytesToAddress([]byte{}) { // "empty" or "zero" address
return fmt.Errorf("To mismatch when recipient is nil (contract creation): %v", expectedTo) return fmt.Errorf("To mismatch when recipient is nil (contract creation): %v", expectedTo)
} }
} else { } else {
if expectedTo != *decodedTx.Recipient { if expectedTo != *decodedTx.To() {
return fmt.Errorf("To mismatch: %v %v", expectedTo, *decodedTx.Recipient) return fmt.Errorf("To mismatch: %v %v", expectedTo, *decodedTx.To())
} }
} }
expectedValue := mustConvertBigInt(txTest.Transaction.Value, 16) expectedValue := mustConvertBigInt(txTest.Transaction.Value, 16)
if expectedValue.Cmp(decodedTx.Amount) != 0 { if expectedValue.Cmp(decodedTx.Value()) != 0 {
return fmt.Errorf("Value mismatch: %v %v", expectedValue, decodedTx.Amount) return fmt.Errorf("Value mismatch: %v %v", expectedValue, decodedTx.Value())
} }
return nil return nil
......
package trie package trie
import "github.com/ethereum/go-ethereum/logger/glog" import (
"github.com/ethereum/go-ethereum/compression/rle"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/syndtr/goleveldb/leveldb"
)
type Backend interface { type Backend interface {
Get([]byte) ([]byte, error) Get([]byte) ([]byte, error)
...@@ -8,12 +13,13 @@ type Backend interface { ...@@ -8,12 +13,13 @@ type Backend interface {
} }
type Cache struct { type Cache struct {
batch *leveldb.Batch
store map[string][]byte store map[string][]byte
backend Backend backend Backend
} }
func NewCache(backend Backend) *Cache { func NewCache(backend Backend) *Cache {
return &Cache{make(map[string][]byte), backend} return &Cache{new(leveldb.Batch), make(map[string][]byte), backend}
} }
func (self *Cache) Get(key []byte) []byte { func (self *Cache) Get(key []byte) []byte {
...@@ -26,19 +32,23 @@ func (self *Cache) Get(key []byte) []byte { ...@@ -26,19 +32,23 @@ func (self *Cache) Get(key []byte) []byte {
} }
func (self *Cache) Put(key []byte, data []byte) { func (self *Cache) Put(key []byte, data []byte) {
// write the data to the ldb batch
self.batch.Put(key, rle.Compress(data))
self.store[string(key)] = data self.store[string(key)] = data
} }
// Flush flushes the trie to the backing layer. If this is a leveldb instance
// we'll use a batched write, otherwise we'll use regular put.
func (self *Cache) Flush() { func (self *Cache) Flush() {
for k, v := range self.store { if db, ok := self.backend.(*ethdb.LDBDatabase); ok {
if err := self.backend.Put([]byte(k), v); err != nil { if err := db.LDB().Write(self.batch, nil); err != nil {
glog.Fatal("db write err:", err) glog.Fatal("db write err:", err)
} }
} else {
for k, v := range self.store {
self.backend.Put([]byte(k), v)
}
} }
// This will eventually grow too large. We'd could
// do a make limit on storage and push out not-so-popular nodes.
//self.Reset()
} }
func (self *Cache) Copy() *Cache { func (self *Cache) Copy() *Cache {
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment