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

core: optimisations
This commit is contained in:
Jeffrey Wilcke 2015-06-29 15:44:23 -07:00
commit 7c4ed8055c
41 changed files with 2302 additions and 1429 deletions

7
Godeps/Godeps.json generated
View File

@ -1,6 +1,6 @@
{ {
"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 @@
"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"

View File

@ -0,0 +1,23 @@
# 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

View File

@ -0,0 +1,362 @@
Mozilla Public License, version 2.0
1. Definitions
1.1. "Contributor"
means each individual or legal entity that creates, contributes to the
creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used by a
Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached the
notice in Exhibit A, the Executable Form of such Source Code Form, and
Modifications of such Source Code Form, in each case including portions
thereof.
1.5. "Incompatible With Secondary Licenses"
means
a. that the initial Contributor has attached the notice described in
Exhibit B to the Covered Software; or
b. that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the terms of
a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in a
separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible, whether
at the time of the initial grant or subsequently, any and all of the
rights conveyed by this License.
1.10. "Modifications"
means any of the following:
a. any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered Software; or
b. any new file in Source Code Form that contains any Covered Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the License,
by the making, using, selling, offering for sale, having made, import,
or transfer of either its Contributions or its Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU Lesser
General Public License, Version 2.1, the GNU Affero General Public
License, Version 3.0, or any later versions of those licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that controls, is
controlled by, or is under common control with You. For purposes of this
definition, "control" means (a) the power, direct or indirect, to cause
the direction or management of such entity, whether by contract or
otherwise, or (b) ownership of more than fifty percent (50%) of the
outstanding shares or beneficial ownership of such entity.
2. License Grants and Conditions
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
a. under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
b. under Patent Claims of such Contributor to make, use, sell, offer for
sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
a. for any code that a Contributor has removed from Covered Software; or
b. for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
c. under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights to
grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
Section 2.1.
3. Responsibilities
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
a. such Covered Software must also be made available in Source Code Form,
as described in Section 3.1, and You must inform recipients of the
Executable Form how they can obtain a copy of such Source Code Form by
reasonable means in a timely manner, at a charge no more than the cost
of distribution to the recipient; and
b. You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter the
recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty, or
limitations of liability) contained within the Source Code Form of the
Covered Software, except that You may alter any license notices to the
extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
If it is impossible for You to comply with any of the terms of this License
with respect to some or all of the Covered Software due to statute,
judicial order, or regulation then You must: (a) comply with the terms of
this License to the maximum extent possible; and (b) describe the
limitations and the code they affect. Such description must be placed in a
text file included with all distributions of the Covered Software under
this License. Except to the extent prohibited by statute or regulation,
such description must be sufficiently detailed for a recipient of ordinary
skill to be able to understand it.
5. Termination
5.1. The rights granted under this License will terminate automatically if You
fail to comply with any of its terms. However, if You become compliant,
then the rights granted under this License from a particular Contributor
are reinstated (a) provisionally, unless and until such Contributor
explicitly and finally terminates Your grants, and (b) on an ongoing
basis, if such Contributor fails to notify You of the non-compliance by
some reasonable means prior to 60 days after You have come back into
compliance. Moreover, Your grants from a particular Contributor are
reinstated on an ongoing basis if such Contributor notifies You of the
non-compliance by some reasonable means, this is the first time You have
received notice of non-compliance with this License from such
Contributor, and You become compliant prior to 30 days after Your receipt
of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
license agreements (excluding distributors and resellers) which have been
validly granted by You or Your distributors under this License prior to
termination shall survive termination.
6. Disclaimer of Warranty
Covered Software is provided under this License on an "as is" basis,
without warranty of any kind, either expressed, implied, or statutory,
including, without limitation, warranties that the Covered Software is free
of defects, merchantable, fit for a particular purpose or non-infringing.
The entire risk as to the quality and performance of the Covered Software
is with You. Should any Covered Software prove defective in any respect,
You (not any Contributor) assume the cost of any necessary servicing,
repair, or correction. This disclaimer of warranty constitutes an essential
part of this License. No use of any Covered Software is authorized under
this License except under this disclaimer.
7. Limitation of Liability
Under no circumstances and under no legal theory, whether tort (including
negligence), contract, or otherwise, shall any Contributor, or anyone who
distributes Covered Software as permitted above, be liable to You for any
direct, indirect, special, incidental, or consequential damages of any
character including, without limitation, damages for lost profits, loss of
goodwill, work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses, even if such party shall have been
informed of the possibility of such damages. This limitation of liability
shall not apply to liability for death or personal injury resulting from
such party's negligence to the extent applicable law prohibits such
limitation. Some jurisdictions do not allow the exclusion or limitation of
incidental or consequential damages, so this exclusion and limitation may
not apply to You.
8. Litigation
Any litigation relating to this License may be brought only in the courts
of a jurisdiction where the defendant maintains its principal place of
business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions. Nothing
in this Section shall prevent a party's ability to bring cross-claims or
counter-claims.
9. Miscellaneous
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides that
the language of a contract shall be construed against the drafter shall not
be used to construe this License against a Contributor.
10. Versions of the License
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses If You choose to distribute Source Code Form that is
Incompatible With Secondary Licenses under the terms of this version of
the License, the notice described in Exhibit B of this License must be
attached.
Exhibit A - Source Code Form License Notice
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular file,
then You may include the notice in a location (such as a LICENSE file in a
relevant directory) where a recipient would be likely to look for such a
notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
This Source Code Form is "Incompatible
With Secondary Licenses", as defined by
the Mozilla Public License, v. 2.0.

View File

@ -0,0 +1,25 @@
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()))
}
```

View File

@ -0,0 +1,175 @@
// 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)
}
}

View File

@ -0,0 +1,127 @@
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")
}
}

163
core/bench_test.go Normal file
View File

@ -0,0 +1,163 @@
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)
}
}

View File

@ -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) {

View File

@ -57,8 +57,8 @@ func NewBlockProcessor(db, extra common.Database, pow pow.PoW, chainManager *Cha
} }
func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block *types.Block, transientProcess bool) (receipts types.Receipts, err error) { func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block *types.Block, transientProcess bool) (receipts types.Receipts, err error) {
coinbase := statedb.GetOrNewStateObject(block.Header().Coinbase) coinbase := statedb.GetOrNewStateObject(block.Coinbase())
coinbase.SetGasLimit(block.Header().GasLimit) coinbase.SetGasLimit(block.GasLimit())
// Process the transactions on to parent state // Process the transactions on to parent state
receipts, err = sm.ApplyTransactions(coinbase, statedb, block, block.Transactions(), transientProcess) receipts, err = sm.ApplyTransactions(coinbase, statedb, block, block.Transactions(), transientProcess)
@ -69,11 +69,11 @@ func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block
return receipts, nil return receipts, nil
} }
func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, statedb *state.StateDB, block *types.Block, tx *types.Transaction, usedGas *big.Int, transientProcess bool) (*types.Receipt, *big.Int, error) { func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, transientProcess bool) (*types.Receipt, *big.Int, error) {
// If we are mining this block and validating we want to set the logs back to 0 // If we are mining this block and validating we want to set the logs back to 0
cb := statedb.GetStateObject(coinbase.Address()) cb := statedb.GetStateObject(coinbase.Address())
_, gas, err := ApplyMessage(NewEnv(statedb, self.bc, tx, block), tx, cb) _, gas, err := ApplyMessage(NewEnv(statedb, self.bc, tx, header), tx, cb)
if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err) || IsInvalidTxErr(err)) { if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err) || IsInvalidTxErr(err)) {
return nil, nil, err return nil, nil, err
} }
@ -81,9 +81,8 @@ func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, stated
// Update the state with pending changes // Update the state with pending changes
statedb.Update() statedb.Update()
cumulative := new(big.Int).Set(usedGas.Add(usedGas, gas)) usedGas.Add(usedGas, gas)
receipt := types.NewReceipt(statedb.Root().Bytes(), cumulative) receipt := types.NewReceipt(statedb.Root().Bytes(), usedGas)
logs := statedb.GetLogs(tx.Hash()) logs := statedb.GetLogs(tx.Hash())
receipt.SetLogs(logs) receipt.SetLogs(logs)
receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
@ -108,12 +107,13 @@ func (self *BlockProcessor) ApplyTransactions(coinbase *state.StateObject, state
totalUsedGas = big.NewInt(0) totalUsedGas = big.NewInt(0)
err error err error
cumulativeSum = new(big.Int) cumulativeSum = new(big.Int)
header = block.Header()
) )
for i, tx := range txs { for i, tx := range txs {
statedb.StartRecord(tx.Hash(), block.Hash(), i) statedb.StartRecord(tx.Hash(), block.Hash(), i)
receipt, txGas, err := self.ApplyTransaction(coinbase, statedb, block, tx, totalUsedGas, transientProcess) receipt, txGas, err := self.ApplyTransaction(coinbase, statedb, header, tx, totalUsedGas, transientProcess)
if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err) || IsInvalidTxErr(err)) { if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err) || IsInvalidTxErr(err)) {
return nil, err return nil, err
} }
@ -142,11 +142,10 @@ func (sm *BlockProcessor) RetryProcess(block *types.Block) (logs state.Logs, err
sm.mutex.Lock() sm.mutex.Lock()
defer sm.mutex.Unlock() defer sm.mutex.Unlock()
header := block.Header() if !sm.bc.HasBlock(block.ParentHash()) {
if !sm.bc.HasBlock(header.ParentHash) { return nil, ParentError(block.ParentHash())
return nil, ParentError(header.ParentHash)
} }
parent := sm.bc.GetBlock(header.ParentHash) parent := sm.bc.GetBlock(block.ParentHash())
// FIXME Change to full header validation. See #1225 // FIXME Change to full header validation. See #1225
errch := make(chan bool) errch := make(chan bool)
@ -168,30 +167,32 @@ func (sm *BlockProcessor) Process(block *types.Block) (logs state.Logs, err erro
sm.mutex.Lock() sm.mutex.Lock()
defer sm.mutex.Unlock() defer sm.mutex.Unlock()
header := block.Header() if sm.bc.HasBlock(block.Hash()) {
if sm.bc.HasBlock(header.Hash()) { return nil, &KnownBlockError{block.Number(), block.Hash()}
return nil, &KnownBlockError{header.Number, header.Hash()}
} }
if !sm.bc.HasBlock(header.ParentHash) { if !sm.bc.HasBlock(block.ParentHash()) {
return nil, ParentError(header.ParentHash) return nil, ParentError(block.ParentHash())
} }
parent := sm.bc.GetBlock(header.ParentHash) parent := sm.bc.GetBlock(block.ParentHash())
return sm.processWithParent(block, parent) return sm.processWithParent(block, parent)
} }
func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs state.Logs, err error) { func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs state.Logs, err error) {
// Create a new state based on the parent's root (e.g., create copy) // Create a new state based on the parent's root (e.g., create copy)
state := state.New(parent.Root(), sm.db) state := state.New(parent.Root(), sm.db)
header := block.Header()
uncles := block.Uncles()
txs := block.Transactions()
// Block validation // Block validation
if err = ValidateHeader(sm.Pow, block.Header(), parent.Header(), false); err != nil { if err = ValidateHeader(sm.Pow, header, parent, false); err != nil {
return return
} }
// There can be at most two uncles // There can be at most two uncles
if len(block.Uncles()) > 2 { if len(uncles) > 2 {
return nil, ValidationError("Block can only contain maximum 2 uncles (contained %v)", len(block.Uncles())) return nil, ValidationError("Block can only contain maximum 2 uncles (contained %v)", len(uncles))
} }
receipts, err := sm.TransitionState(state, parent, block, false) receipts, err := sm.TransitionState(state, parent, block, false)
@ -199,8 +200,6 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st
return return
} }
header := block.Header()
// Validate the received block's bloom with the one derived from the generated receipts. // Validate the received block's bloom with the one derived from the generated receipts.
// For valid blocks this should always validate to true. // For valid blocks this should always validate to true.
rbloom := types.CreateBloom(receipts) rbloom := types.CreateBloom(receipts)
@ -211,7 +210,7 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st
// The transactions Trie's root (R = (Tr [[i, RLP(T1)], [i, RLP(T2)], ... [n, RLP(Tn)]])) // The transactions Trie's root (R = (Tr [[i, RLP(T1)], [i, RLP(T2)], ... [n, RLP(Tn)]]))
// can be used by light clients to make sure they've received the correct Txs // can be used by light clients to make sure they've received the correct Txs
txSha := types.DeriveSha(block.Transactions()) txSha := types.DeriveSha(txs)
if txSha != header.TxHash { if txSha != header.TxHash {
err = fmt.Errorf("invalid transaction root hash. received=%x calculated=%x", header.TxHash, txSha) err = fmt.Errorf("invalid transaction root hash. received=%x calculated=%x", header.TxHash, txSha)
return return
@ -225,7 +224,7 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st
} }
// Verify UncleHash before running other uncle validations // Verify UncleHash before running other uncle validations
unclesSha := block.CalculateUnclesHash() unclesSha := types.CalcUncleHash(uncles)
if unclesSha != header.UncleHash { if unclesSha != header.UncleHash {
err = fmt.Errorf("invalid uncles root hash. received=%x calculated=%x", header.UncleHash, unclesSha) err = fmt.Errorf("invalid uncles root hash. received=%x calculated=%x", header.UncleHash, unclesSha)
return return
@ -236,7 +235,7 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st
return return
} }
// Accumulate static rewards; block reward, uncle's and uncle inclusion. // Accumulate static rewards; block reward, uncle's and uncle inclusion.
AccumulateRewards(state, block) AccumulateRewards(state, header, uncles)
// Commit state objects/accounts to a temporary trie (does not save) // Commit state objects/accounts to a temporary trie (does not save)
// used to calculate the state root. // used to calculate the state root.
@ -260,20 +259,44 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st
return state.Logs(), nil return state.Logs(), nil
} }
var (
big8 = big.NewInt(8)
big32 = big.NewInt(32)
)
// AccumulateRewards credits the coinbase of the given block with the
// mining reward. The total reward consists of the static block reward
// and rewards for included uncles. The coinbase of each uncle block is
// also rewarded.
func AccumulateRewards(statedb *state.StateDB, header *types.Header, uncles []*types.Header) {
reward := new(big.Int).Set(BlockReward)
r := new(big.Int)
for _, uncle := range uncles {
r.Add(uncle.Number, big8)
r.Sub(r, header.Number)
r.Mul(r, BlockReward)
r.Div(r, big8)
statedb.AddBalance(uncle.Coinbase, r)
r.Div(BlockReward, big32)
reward.Add(reward, r)
}
statedb.AddBalance(header.Coinbase, reward)
}
func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *types.Block) error { func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *types.Block) error {
ancestors := set.New()
uncles := set.New() uncles := set.New()
ancestorHeaders := make(map[common.Hash]*types.Header) ancestors := make(map[common.Hash]*types.Block)
for _, ancestor := range sm.bc.GetAncestors(block, 7) { for _, ancestor := range sm.bc.GetBlocksFromHash(block.ParentHash(), 7) {
ancestorHeaders[ancestor.Hash()] = ancestor.Header() ancestors[ancestor.Hash()] = ancestor
ancestors.Add(ancestor.Hash())
// Include ancestors uncles in the uncle set. Uncles must be unique. // Include ancestors uncles in the uncle set. Uncles must be unique.
for _, uncle := range ancestor.Uncles() { for _, uncle := range ancestor.Uncles() {
uncles.Add(uncle.Hash()) uncles.Add(uncle.Hash())
} }
} }
ancestors[block.Hash()] = block
uncles.Add(block.Hash()) uncles.Add(block.Hash())
for i, uncle := range block.Uncles() { for i, uncle := range block.Uncles() {
hash := uncle.Hash() hash := uncle.Hash()
if uncles.Has(hash) { if uncles.Has(hash) {
@ -282,22 +305,20 @@ func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *ty
} }
uncles.Add(hash) uncles.Add(hash)
if ancestors.Has(hash) { if ancestors[hash] != nil {
branch := fmt.Sprintf(" O - %x\n |\n", block.Hash()) branch := fmt.Sprintf(" O - %x\n |\n", block.Hash())
ancestors.Each(func(item interface{}) bool { for h := range ancestors {
branch += fmt.Sprintf(" O - %x\n |\n", hash) branch += fmt.Sprintf(" O - %x\n |\n", h)
return true }
})
glog.Infoln(branch) glog.Infoln(branch)
return UncleError("uncle[%d](%x) is ancestor", i, hash[:4]) return UncleError("uncle[%d](%x) is ancestor", i, hash[:4])
} }
if !ancestors.Has(uncle.ParentHash) || uncle.ParentHash == parent.Hash() { if ancestors[uncle.ParentHash] == nil || uncle.ParentHash == parent.Hash() {
return UncleError("uncle[%d](%x)'s parent is not ancestor (%x)", i, hash[:4], uncle.ParentHash[0:4]) return UncleError("uncle[%d](%x)'s parent is not ancestor (%x)", i, hash[:4], uncle.ParentHash[0:4])
} }
if err := ValidateHeader(sm.Pow, uncle, ancestorHeaders[uncle.ParentHash], true); err != nil { if err := ValidateHeader(sm.Pow, uncle, ancestors[uncle.ParentHash], true); err != nil {
return ValidationError(fmt.Sprintf("uncle[%d](%x) header invalid: %v", i, hash[:4], err)) return ValidationError(fmt.Sprintf("uncle[%d](%x) header invalid: %v", i, hash[:4], err))
} }
} }
@ -325,7 +346,7 @@ func (sm *BlockProcessor) GetLogs(block *types.Block) (logs state.Logs, err erro
// TODO: remove backward compatibility // TODO: remove backward compatibility
var ( var (
parent = sm.bc.GetBlock(block.Header().ParentHash) parent = sm.bc.GetBlock(block.ParentHash())
state = state.New(parent.Root(), sm.db) state = state.New(parent.Root(), sm.db)
) )
@ -336,19 +357,22 @@ func (sm *BlockProcessor) GetLogs(block *types.Block) (logs state.Logs, err erro
// See YP section 4.3.4. "Block Header Validity" // See YP section 4.3.4. "Block Header Validity"
// Validates a block. Returns an error if the block is invalid. // Validates a block. Returns an error if the block is invalid.
func ValidateHeader(pow pow.PoW, block, parent *types.Header, checkPow bool) error { func ValidateHeader(pow pow.PoW, block *types.Header, parent *types.Block, checkPow bool) error {
if big.NewInt(int64(len(block.Extra))).Cmp(params.MaximumExtraDataSize) == 1 { if big.NewInt(int64(len(block.Extra))).Cmp(params.MaximumExtraDataSize) == 1 {
return fmt.Errorf("Block extra data too long (%d)", len(block.Extra)) return fmt.Errorf("Block extra data too long (%d)", len(block.Extra))
} }
expd := CalcDifficulty(block, parent) expd := CalcDifficulty(int64(block.Time), int64(parent.Time()), parent.Difficulty())
if expd.Cmp(block.Difficulty) != 0 { if expd.Cmp(block.Difficulty) != 0 {
return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd) return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd)
} }
a := new(big.Int).Sub(block.GasLimit, parent.GasLimit) var a, b *big.Int
a = parent.GasLimit()
a = a.Sub(a, block.GasLimit)
a.Abs(a) a.Abs(a)
b := new(big.Int).Div(parent.GasLimit, params.GasLimitBoundDivisor) b = parent.GasLimit()
b = b.Div(b, params.GasLimitBoundDivisor)
if !(a.Cmp(b) < 0) || (block.GasLimit.Cmp(params.MinGasLimit) == -1) { if !(a.Cmp(b) < 0) || (block.GasLimit.Cmp(params.MinGasLimit) == -1) {
return fmt.Errorf("GasLimit check failed for block %v (%v > %v)", block.GasLimit, a, b) return fmt.Errorf("GasLimit check failed for block %v (%v > %v)", block.GasLimit, a, b)
} }
@ -357,11 +381,13 @@ func ValidateHeader(pow pow.PoW, block, parent *types.Header, checkPow bool) err
return BlockFutureErr return BlockFutureErr
} }
if new(big.Int).Sub(block.Number, parent.Number).Cmp(big.NewInt(1)) != 0 { num := parent.Number()
num.Sub(block.Number, num)
if num.Cmp(big.NewInt(1)) != 0 {
return BlockNumberErr return BlockNumberErr
} }
if block.Time <= parent.Time { if block.Time <= uint64(parent.Time()) {
return BlockEqualTSErr //ValidationError("Block timestamp equal or less than previous block (%v - %v)", block.Time, parent.Time) return BlockEqualTSErr //ValidationError("Block timestamp equal or less than previous block (%v - %v)", block.Time, parent.Time)
} }
@ -375,26 +401,6 @@ func ValidateHeader(pow pow.PoW, block, parent *types.Header, checkPow bool) err
return nil return nil
} }
func AccumulateRewards(statedb *state.StateDB, block *types.Block) {
reward := new(big.Int).Set(BlockReward)
for _, uncle := range block.Uncles() {
num := new(big.Int).Add(big.NewInt(8), uncle.Number)
num.Sub(num, block.Number())
r := new(big.Int)
r.Mul(BlockReward, num)
r.Div(r, big.NewInt(8))
statedb.AddBalance(uncle.Coinbase, r)
reward.Add(reward, new(big.Int).Div(BlockReward, big.NewInt(32)))
}
// Get the account associated with the coinbase
statedb.AddBalance(block.Header().Coinbase, reward)
}
func getBlockReceipts(db common.Database, bhash common.Hash) (receipts types.Receipts, err error) { func getBlockReceipts(db common.Database, bhash common.Hash) (receipts types.Receipts, err error) {
var rdata []byte var rdata []byte
rdata, err = db.Get(append(receiptsPre, bhash[:]...)) rdata, err = db.Get(append(receiptsPre, bhash[:]...))

View File

@ -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")
} }

28
core/canary.go Normal file
View File

@ -0,0 +1,28 @@
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
}

View File

@ -1,7 +1,6 @@
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 (
"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) {}
// 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")
}
func NewChainMan(block *types.Block, eventMux *event.TypeMux, db common.Database) *ChainManager {
return newChainManager(block, eventMux, db)
}
func NewBlockProc(db common.Database, cman *ChainManager, eventMux *event.TypeMux) *BlockProcessor {
return newBlockProcessor(db, cman, eventMux)
}
func NewCanonical(n int, db common.Database) (*BlockProcessor, error) {
return newCanonical(n, db)
}
// block time is fixed at 10 seconds
func newBlockFromParent(addr common.Address, parent *types.Block) *types.Block {
block := types.NewBlock(parent.Hash(), addr, parent.Root(), common.BigPow(2, 32), 0, nil)
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
// we seed chains by the first byte of the coinbase
func makeBlock(bman *BlockProcessor, parent *types.Block, i int, db common.Database, seed int) *types.Block {
var addr common.Address
addr[0], addr[19] = byte(seed), byte(i)
block := newBlockFromParent(addr, parent)
state := state.New(block.Root(), db)
cbase := state.GetOrNewStateObject(addr)
cbase.SetGasLimit(CalcGasLimit(parent))
cbase.AddBalance(BlockReward)
state.Update()
block.SetRoot(state.Root())
return block
}
// Make a chain with real blocks
// Runs ProcessWithParent to get proper state roots
func makeChain(bman *BlockProcessor, parent *types.Block, max int, db common.Database, seed int) types.Blocks {
bman.bc.currentBlock = parent
blocks := make(types.Blocks, max)
for i := 0; i < max; i++ {
block := makeBlock(bman, parent, i, db, seed)
_, err := bman.processWithParent(block, parent)
if err != nil {
fmt.Println("process with parent failed", err)
panic(err)
} }
panic("coinbase can only be set once")
}
b.header.Coinbase = addr
b.coinbase = b.statedb.GetOrNewStateObject(addr)
b.coinbase.SetGasLimit(b.header.GasLimit)
}
// SetExtra sets the extra data field of the generated block.
func (b *BlockGen) SetExtra(data []byte) {
b.header.Extra = data
}
// AddTx adds a transaction to the generated block. If no coinbase has
// 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)
}
// TxNonce returns the next valid transaction nonce for the
// 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)
}
// AddUncle adds an uncle header to the generated block.
func (b *BlockGen) AddUncle(h *types.Header) {
b.uncles = append(b.uncles, h)
}
// PrevBlock returns a previously generated block by number. It panics if
// num is greater or equal to the number of the block being generated.
// For index -1, PrevBlock returns the parent block given to GenerateChain.
func (b *BlockGen) PrevBlock(index int) *types.Block {
if index >= b.i {
panic("block index out of range")
}
if index == -1 {
return b.parent
}
return b.chain[index]
}
// GenerateChain creates a chain of n blocks. The first block's
// parent will be the provided parent. db is used to store
// intermediate states and should contain the parent's state trie.
//
// The generator function is called with a new block generator for
// every block. Any transactions and uncles added to the generator
// become part of the block. If gen is nil, the blocks will be empty
// and their coinbase will be the zero address.
//
// Blocks created by GenerateChain do not contain valid proof of work
// 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
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)})
})
}

78
core/chain_makers_test.go Normal file
View File

@ -0,0 +1,78 @@
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
}

View File

@ -11,15 +11,19 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/compression/rle"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"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"
"github.com/rcrowley/go-metrics" "github.com/rcrowley/go-metrics"
"github.com/syndtr/goleveldb/leveldb"
) )
var ( var (
@ -33,35 +37,40 @@ var (
) )
const ( const (
blockCacheLimit = 10000 blockCacheLimit = 256
maxFutureBlocks = 256 maxFutureBlocks = 256
maxTimeFutureBlocks = 30 maxTimeFutureBlocks = 30
) )
func CalcDifficulty(block, parent *types.Header) *big.Int { // CalcDifficulty is the difficulty adjustment algorithm. It returns
// the difficulty that a new block b should have when created at time
// given the parent block's time and difficulty.
func CalcDifficulty(time int64, parentTime int64, parentDiff *big.Int) *big.Int {
diff := new(big.Int) diff := new(big.Int)
adjust := new(big.Int).Div(parentDiff, params.DifficultyBoundDivisor)
adjust := new(big.Int).Div(parent.Difficulty, params.DifficultyBoundDivisor) if big.NewInt(time-parentTime).Cmp(params.DurationLimit) < 0 {
if big.NewInt(int64(block.Time)-int64(parent.Time)).Cmp(params.DurationLimit) < 0 { diff.Add(parentDiff, adjust)
diff.Add(parent.Difficulty, adjust)
} else { } else {
diff.Sub(parent.Difficulty, adjust) diff.Sub(parentDiff, adjust)
} }
if diff.Cmp(params.MinimumDifficulty) < 0 { if diff.Cmp(params.MinimumDifficulty) < 0 {
return params.MinimumDifficulty return params.MinimumDifficulty
} }
return diff return diff
} }
// CalcTD computes the total difficulty of block.
func CalcTD(block, parent *types.Block) *big.Int { func CalcTD(block, parent *types.Block) *big.Int {
if parent == nil { if parent == nil {
return block.Difficulty() return block.Difficulty()
} }
return new(big.Int).Add(parent.Td, block.Header().Difficulty) d := block.Difficulty()
d.Add(d, parent.Td)
return d
} }
// CalcGasLimit computes the gas limit of the next block after parent.
// The result may be modified by the caller.
func CalcGasLimit(parent *types.Block) *big.Int { func CalcGasLimit(parent *types.Block) *big.Int {
decay := new(big.Int).Div(parent.GasLimit(), params.GasLimitBoundDivisor) decay := new(big.Int).Div(parent.GasLimit(), params.GasLimitBoundDivisor)
contrib := new(big.Int).Mul(parent.GasUsed(), big.NewInt(3)) contrib := new(big.Int).Mul(parent.GasUsed(), big.NewInt(3))
@ -71,11 +80,11 @@ func CalcGasLimit(parent *types.Block) *big.Int {
gl := new(big.Int).Sub(parent.GasLimit(), decay) gl := new(big.Int).Sub(parent.GasLimit(), decay)
gl = gl.Add(gl, contrib) gl = gl.Add(gl, contrib)
gl = gl.Add(gl, big.NewInt(1)) gl = gl.Add(gl, big.NewInt(1))
gl = common.BigMax(gl, params.MinGasLimit) gl.Set(common.BigMax(gl, params.MinGasLimit))
if gl.Cmp(params.GenesisGasLimit) < 0 { if gl.Cmp(params.GenesisGasLimit) < 0 {
gl2 := new(big.Int).Add(parent.GasLimit(), decay) gl.Add(parent.GasLimit(), decay)
return common.BigMin(params.GenesisGasLimit, gl2) gl.Set(common.BigMin(gl, params.GenesisGasLimit))
} }
return gl return gl
} }
@ -100,8 +109,9 @@ type ChainManager struct {
transState *state.StateDB transState *state.StateDB
txState *state.ManagedState txState *state.ManagedState
cache *BlockCache cache *lru.Cache // cache is the LRU caching
futureBlocks *BlockCache futureBlocks *lru.Cache // future blocks are blocks added for later processing
pendingBlocks *lru.Cache // pending blocks contain blocks not yet written to the db
quit chan struct{} quit chan struct{}
// procInterrupt must be atomically called // procInterrupt must be atomically called
@ -112,13 +122,14 @@ type ChainManager struct {
} }
func NewChainManager(genesis *types.Block, blockDb, stateDb common.Database, pow pow.PoW, mux *event.TypeMux) (*ChainManager, error) { func NewChainManager(genesis *types.Block, blockDb, stateDb common.Database, pow pow.PoW, mux *event.TypeMux) (*ChainManager, error) {
cache, _ := lru.New(blockCacheLimit)
bc := &ChainManager{ bc := &ChainManager{
blockDb: blockDb, blockDb: blockDb,
stateDb: stateDb, stateDb: stateDb,
genesisBlock: GenesisBlock(42, stateDb), genesisBlock: GenesisBlock(42, stateDb),
eventMux: mux, eventMux: mux,
quit: make(chan struct{}), quit: make(chan struct{}),
cache: NewBlockCache(blockCacheLimit), cache: cache,
pow: pow, pow: pow,
} }
// Check the genesis block given to the chain manager. If the genesis block mismatches block number 0 // Check the genesis block given to the chain manager. If the genesis block mismatches block number 0
@ -147,7 +158,7 @@ func NewChainManager(genesis *types.Block, blockDb, stateDb common.Database, pow
// Take ownership of this particular state // Take ownership of this particular state
bc.txState = state.ManageState(bc.State().Copy()) bc.txState = state.ManageState(bc.State().Copy())
bc.futureBlocks = NewBlockCache(maxFutureBlocks) bc.futureBlocks, _ = lru.New(maxFutureBlocks)
bc.makeCache() bc.makeCache()
go bc.update() go bc.update()
@ -159,11 +170,11 @@ func (bc *ChainManager) SetHead(head *types.Block) {
bc.mu.Lock() bc.mu.Lock()
defer bc.mu.Unlock() defer bc.mu.Unlock()
for block := bc.currentBlock; block != nil && block.Hash() != head.Hash(); block = bc.GetBlock(block.Header().ParentHash) { for block := bc.currentBlock; block != nil && block.Hash() != head.Hash(); block = bc.GetBlock(block.ParentHash()) {
bc.removeBlock(block) bc.removeBlock(block)
} }
bc.cache = NewBlockCache(blockCacheLimit) bc.cache, _ = lru.New(blockCacheLimit)
bc.currentBlock = head bc.currentBlock = head
bc.makeCache() bc.makeCache()
@ -251,65 +262,23 @@ func (bc *ChainManager) setLastState() {
} }
func (bc *ChainManager) makeCache() { func (bc *ChainManager) makeCache() {
if bc.cache == nil { bc.cache, _ = lru.New(blockCacheLimit)
bc.cache = NewBlockCache(blockCacheLimit)
}
// load in last `blockCacheLimit` - 1 blocks. Last block is the current. // load in last `blockCacheLimit` - 1 blocks. Last block is the current.
ancestors := bc.GetAncestors(bc.currentBlock, blockCacheLimit-1) bc.cache.Add(bc.genesisBlock.Hash(), bc.genesisBlock)
ancestors = append(ancestors, bc.currentBlock) for _, block := range bc.GetBlocksFromHash(bc.currentBlock.Hash(), blockCacheLimit) {
for _, block := range ancestors { bc.cache.Add(block.Hash(), block)
bc.cache.Push(block)
} }
} }
// Block creation & chain handling
func (bc *ChainManager) NewBlock(coinbase common.Address) *types.Block {
bc.mu.RLock()
defer bc.mu.RUnlock()
var (
root common.Hash
parentHash common.Hash
)
if bc.currentBlock != nil {
root = bc.currentBlock.Header().Root
parentHash = bc.lastBlockHash
}
block := types.NewBlock(
parentHash,
coinbase,
root,
common.BigPow(2, 32),
0,
nil)
block.SetUncles(nil)
block.SetTransactions(nil)
block.SetReceipts(nil)
parent := bc.currentBlock
if parent != nil {
header := block.Header()
header.Difficulty = CalcDifficulty(block.Header(), parent.Header())
header.Number = new(big.Int).Add(parent.Header().Number, common.Big1)
header.GasLimit = CalcGasLimit(parent)
}
return block
}
func (bc *ChainManager) Reset() { func (bc *ChainManager) Reset() {
bc.mu.Lock() bc.mu.Lock()
defer bc.mu.Unlock() defer bc.mu.Unlock()
for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.Header().ParentHash) { for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.ParentHash()) {
bc.removeBlock(block) bc.removeBlock(block)
} }
if bc.cache == nil { bc.cache, _ = lru.New(blockCacheLimit)
bc.cache = NewBlockCache(blockCacheLimit)
}
// Prepare the genesis block // Prepare the genesis block
bc.write(bc.genesisBlock) bc.write(bc.genesisBlock)
@ -328,7 +297,7 @@ func (bc *ChainManager) ResetWithGenesisBlock(gb *types.Block) {
bc.mu.Lock() bc.mu.Lock()
defer bc.mu.Unlock() defer bc.mu.Unlock()
for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.Header().ParentHash) { for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.ParentHash()) {
bc.removeBlock(block) bc.removeBlock(block)
} }
@ -393,15 +362,20 @@ func (bc *ChainManager) insert(block *types.Block) {
} }
func (bc *ChainManager) write(block *types.Block) { func (bc *ChainManager) write(block *types.Block) {
enc, _ := rlp.EncodeToBytes((*types.StorageBlock)(block)) tstart := time.Now()
key := append(blockHashPre, block.Hash().Bytes()...)
err := bc.blockDb.Put(key, enc)
if err != nil {
glog.Fatal("db write fail:", err)
}
// Push block to cache go func() {
bc.cache.Push(block) enc, _ := rlp.EncodeToBytes((*types.StorageBlock)(block))
key := append(blockHashPre, block.Hash().Bytes()...)
err := bc.blockDb.Put(key, enc)
if err != nil {
glog.Fatal("db write fail:", err)
}
}()
if glog.V(logger.Debug) {
glog.Infof("wrote block #%v %s. Took %v\n", block.Number(), common.PP(block.Hash().Bytes()), time.Since(tstart))
}
} }
// Accessors // Accessors
@ -411,6 +385,16 @@ func (bc *ChainManager) Genesis() *types.Block {
// Block fetching methods // Block fetching methods
func (bc *ChainManager) HasBlock(hash common.Hash) bool { func (bc *ChainManager) HasBlock(hash common.Hash) bool {
if bc.cache.Contains(hash) {
return true
}
if bc.pendingBlocks != nil {
if _, exist := bc.pendingBlocks.Get(hash); exist {
return true
}
}
data, _ := bc.blockDb.Get(append(blockHashPre, hash[:]...)) data, _ := bc.blockDb.Get(append(blockHashPre, hash[:]...))
return len(data) != 0 return len(data) != 0
} }
@ -437,11 +421,15 @@ func (self *ChainManager) GetBlockHashesFromHash(hash common.Hash, max uint64) (
} }
func (self *ChainManager) GetBlock(hash common.Hash) *types.Block { func (self *ChainManager) GetBlock(hash common.Hash) *types.Block {
/* if block, ok := self.cache.Get(hash); ok {
if block := self.cache.Get(hash); block != nil { return block.(*types.Block)
return block }
if self.pendingBlocks != nil {
if block, _ := self.pendingBlocks.Get(hash); block != nil {
return block.(*types.Block)
} }
*/ }
data, _ := self.blockDb.Get(append(blockHashPre, hash[:]...)) data, _ := self.blockDb.Get(append(blockHashPre, hash[:]...))
if len(data) == 0 { if len(data) == 0 {
@ -452,6 +440,10 @@ func (self *ChainManager) GetBlock(hash common.Hash) *types.Block {
glog.V(logger.Error).Infof("invalid block RLP for hash %x: %v", hash, err) glog.V(logger.Error).Infof("invalid block RLP for hash %x: %v", hash, err)
return nil return nil
} }
// Add the block to the cache
self.cache.Add(hash, (*types.Block)(&block))
return (*types.Block)(&block) return (*types.Block)(&block)
} }
@ -463,6 +455,19 @@ func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block {
} }
// GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors.
func (self *ChainManager) GetBlocksFromHash(hash common.Hash, n int) (blocks []*types.Block) {
for i := 0; i < n; i++ {
block := self.GetBlock(hash)
if block == nil {
break
}
blocks = append(blocks, block)
hash = block.ParentHash()
}
return
}
// non blocking version // non blocking version
func (self *ChainManager) getBlockByNumber(num uint64) *types.Block { func (self *ChainManager) getBlockByNumber(num uint64) *types.Block {
key, _ := self.blockDb.Get(append(blockNumPre, big.NewInt(int64(num)).Bytes()...)) key, _ := self.blockDb.Get(append(blockNumPre, big.NewInt(int64(num)).Bytes()...))
@ -482,45 +487,12 @@ func (self *ChainManager) GetUnclesInChain(block *types.Block, length int) (uncl
return return
} }
func (self *ChainManager) GetAncestors(block *types.Block, length int) (blocks []*types.Block) {
for i := 0; i < length; i++ {
block = self.GetBlock(block.ParentHash())
if block == nil {
break
}
blocks = append(blocks, block)
}
return
}
// setTotalDifficulty updates the TD of the chain manager. Note, this function // setTotalDifficulty updates the TD of the chain manager. Note, this function
// assumes that the `mu` mutex is held! // assumes that the `mu` mutex is held!
func (bc *ChainManager) setTotalDifficulty(td *big.Int) { func (bc *ChainManager) setTotalDifficulty(td *big.Int) {
bc.td = new(big.Int).Set(td) bc.td = new(big.Int).Set(td)
} }
func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) {
parent := self.GetBlock(block.Header().ParentHash)
if parent == nil {
return nil, fmt.Errorf("Unable to calculate total diff without known parent %x", block.Header().ParentHash)
}
parentTd := parent.Td
uncleDiff := new(big.Int)
for _, uncle := range block.Uncles() {
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
}
td := new(big.Int)
td = td.Add(parentTd, uncleDiff)
td = td.Add(td, block.Header().Difficulty)
return td, nil
}
func (bc *ChainManager) Stop() { func (bc *ChainManager) Stop() {
close(bc.quit) close(bc.quit)
atomic.StoreInt32(&bc.procInterrupt, 1) atomic.StoreInt32(&bc.procInterrupt, 1)
@ -538,16 +510,94 @@ type queueEvent struct {
} }
func (self *ChainManager) procFutureBlocks() { func (self *ChainManager) procFutureBlocks() {
var blocks []*types.Block blocks := make([]*types.Block, self.futureBlocks.Len())
self.futureBlocks.Each(func(i int, block *types.Block) { for i, hash := range self.futureBlocks.Keys() {
blocks = append(blocks, block) block, _ := self.futureBlocks.Get(hash)
}) blocks[i] = block.(*types.Block)
}
if len(blocks) > 0 { if len(blocks) > 0 {
types.BlockBy(types.Number).Sort(blocks) types.BlockBy(types.Number).Sort(blocks)
self.InsertChain(blocks) self.InsertChain(blocks)
} }
} }
func (self *ChainManager) enqueueForWrite(block *types.Block) {
self.pendingBlocks.Add(block.Hash(), block)
}
func (self *ChainManager) flushQueuedBlocks() {
db, batchWrite := self.blockDb.(*ethdb.LDBDatabase)
batch := new(leveldb.Batch)
for _, key := range self.pendingBlocks.Keys() {
b, _ := self.pendingBlocks.Get(key)
block := b.(*types.Block)
enc, _ := rlp.EncodeToBytes((*types.StorageBlock)(block))
key := append(blockHashPre, block.Hash().Bytes()...)
if batchWrite {
batch.Put(key, rle.Compress(enc))
} else {
self.blockDb.Put(key, enc)
}
}
if batchWrite {
db.LDB().Write(batch, nil)
}
}
type writeStatus byte
const (
nonStatTy writeStatus = iota
canonStatTy
splitStatTy
sideStatTy
)
func (self *ChainManager) WriteBlock(block *types.Block) (status writeStatus, err error) {
self.wg.Add(1)
defer self.wg.Done()
cblock := self.currentBlock
// Compare the TD of the last known block in the canonical chain to make sure it's greater.
// At this point it's possible that a different chain (fork) becomes the new canonical chain.
if block.Td.Cmp(self.Td()) > 0 {
// chain fork
if block.ParentHash() != cblock.Hash() {
// during split we merge two different chains and create the new canonical chain
err := self.merge(cblock, block)
if err != nil {
return nonStatTy, err
}
status = splitStatTy
}
self.mu.Lock()
self.setTotalDifficulty(block.Td)
self.insert(block)
self.mu.Unlock()
self.setTransState(state.New(block.Root(), self.stateDb))
self.txState.SetState(state.New(block.Root(), self.stateDb))
status = canonStatTy
} else {
status = sideStatTy
}
// Write block to database. Eventually we'll have to improve on this and throw away blocks that are
// not in the canonical chain.
self.mu.Lock()
self.enqueueForWrite(block)
self.mu.Unlock()
// Delete from future blocks
self.futureBlocks.Remove(block.Hash())
return
}
// InsertChain will attempt to insert the given chain in to the canonical chain or, otherwise, create a fork. It an error is returned // InsertChain will attempt to insert the given chain in to the canonical chain or, otherwise, create a fork. It an error is returned
// it will return the index number of the failing block as well an error describing what went wrong (for possible errors see core/errors.go). // it will return the index number of the failing block as well an error describing what went wrong (for possible errors see core/errors.go).
func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) { func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
@ -557,6 +607,8 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
self.chainmu.Lock() self.chainmu.Lock()
defer self.chainmu.Unlock() defer self.chainmu.Unlock()
self.pendingBlocks, _ = lru.New(len(chain))
// A queued approach to delivering events. This is generally // A queued approach to delivering events. This is generally
// faster than direct delivery and requires much less mutex // faster than direct delivery and requires much less mutex
// acquiring. // acquiring.
@ -574,6 +626,7 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
// Start the parallel nonce verifier. // Start the parallel nonce verifier.
go verifyNonces(self.pow, chain, nonceQuit, nonceDone) go verifyNonces(self.pow, chain, nonceQuit, nonceDone)
defer close(nonceQuit) defer close(nonceQuit)
defer self.flushQueuedBlocks()
txcount := 0 txcount := 0
for i, block := range chain { for i, block := range chain {
@ -621,15 +674,13 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
return i, fmt.Errorf("%v: BlockFutureErr, %v > %v", BlockFutureErr, block.Time(), max) return i, fmt.Errorf("%v: BlockFutureErr, %v > %v", BlockFutureErr, block.Time(), max)
} }
block.SetQueued(true) self.futureBlocks.Add(block.Hash(), block)
self.futureBlocks.Push(block)
stats.queued++ stats.queued++
continue continue
} }
if IsParentErr(err) && self.futureBlocks.Has(block.ParentHash()) { if IsParentErr(err) && self.futureBlocks.Contains(block.ParentHash()) {
block.SetQueued(true) self.futureBlocks.Add(block.Hash(), block)
self.futureBlocks.Push(block)
stats.queued++ stats.queued++
continue continue
} }
@ -641,59 +692,29 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
txcount += len(block.Transactions()) txcount += len(block.Transactions())
cblock := self.currentBlock // write the block to the chain and get the status
// Compare the TD of the last known block in the canonical chain to make sure it's greater. status, err := self.WriteBlock(block)
// At this point it's possible that a different chain (fork) becomes the new canonical chain. if err != nil {
if block.Td.Cmp(self.Td()) > 0 { return i, err
// chain fork }
if block.ParentHash() != cblock.Hash() { switch status {
// during split we merge two different chains and create the new canonical chain case canonStatTy:
err := self.merge(cblock, block)
if err != nil {
return i, err
}
queue[i] = ChainSplitEvent{block, logs}
queueEvent.splitCount++
}
self.mu.Lock()
self.setTotalDifficulty(block.Td)
self.insert(block)
self.mu.Unlock()
jsonlogger.LogJson(&logger.EthChainNewHead{
BlockHash: block.Hash().Hex(),
BlockNumber: block.Number(),
ChainHeadHash: cblock.Hash().Hex(),
BlockPrevHash: block.ParentHash().Hex(),
})
self.setTransState(state.New(block.Root(), self.stateDb))
self.txState.SetState(state.New(block.Root(), self.stateDb))
queue[i] = ChainEvent{block, block.Hash(), logs}
queueEvent.canonicalCount++
if glog.V(logger.Debug) { if glog.V(logger.Debug) {
glog.Infof("[%v] inserted block #%d (%d TXs %d UNCs) (%x...). Took %v\n", time.Now().UnixNano(), block.Number(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4], time.Since(bstart)) glog.Infof("[%v] inserted block #%d (%d TXs %d UNCs) (%x...). Took %v\n", time.Now().UnixNano(), block.Number(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4], time.Since(bstart))
} }
} else { queue[i] = ChainEvent{block, block.Hash(), logs}
queueEvent.canonicalCount++
case sideStatTy:
if glog.V(logger.Detail) { if glog.V(logger.Detail) {
glog.Infof("inserted forked block #%d (TD=%v) (%d TXs %d UNCs) (%x...). Took %v\n", block.Number(), block.Difficulty(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4], time.Since(bstart)) glog.Infof("inserted forked block #%d (TD=%v) (%d TXs %d UNCs) (%x...). Took %v\n", block.Number(), block.Difficulty(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4], time.Since(bstart))
} }
queue[i] = ChainSideEvent{block, logs} queue[i] = ChainSideEvent{block, logs}
queueEvent.sideCount++ queueEvent.sideCount++
case splitStatTy:
queue[i] = ChainSplitEvent{block, logs}
queueEvent.splitCount++
} }
// Write block to database. Eventually we'll have to improve on this and throw away blocks that are
// not in the canonical chain.
self.write(block)
// Delete from future blocks
self.futureBlocks.Delete(block.Hash())
stats.processed++ stats.processed++
blockInsertTimer.UpdateSince(bstart)
} }
if (stats.queued > 0 || stats.processed > 0 || stats.ignored > 0) && bool(glog.V(logger.Info)) { if (stats.queued > 0 || stats.processed > 0 || stats.ignored > 0) && bool(glog.V(logger.Info)) {
@ -752,9 +773,9 @@ func (self *ChainManager) diff(oldBlock, newBlock *types.Block) (types.Blocks, e
} }
} }
if glog.V(logger.Info) { if glog.V(logger.Debug) {
commonHash := commonBlock.Hash() commonHash := commonBlock.Hash()
glog.Infof("Fork detected @ %x. Reorganising chain from #%v %x to %x", commonHash[:4], numSplit, oldStart.Hash().Bytes()[:4], newStart.Hash().Bytes()[:4]) glog.Infof("Chain split detected @ %x. Reorganising chain from #%v %x to %x", commonHash[:4], numSplit, oldStart.Hash().Bytes()[:4], newStart.Hash().Bytes()[:4])
} }
return newChain, nil return newChain, nil

View File

@ -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
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) {
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) {
} }
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) {
} }
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) {
} }
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) {
} }
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) {
} }
} }
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) {
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 }
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)),
if i == 0 { Difficulty: big.NewInt(int64(difficulty)),
block.ParentHeaderHash = genesis.Hash()
} else {
copy(block.ParentHeaderHash[:2], []byte{byte(i), seed})
} }
if i == 0 {
header.ParentHash = genesis.Hash()
} else {
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
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 {
} }
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) {
} }
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) {
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]

View File

@ -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 (
"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 {
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(`{
"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
}

View File

@ -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 {
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
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) {
} }
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
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
} 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() {
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 {

View File

@ -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
// 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() {
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() {
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 {
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() }

View File

@ -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) {
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) {
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) {
} }
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) {
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) {
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) {
} }
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) {
} }
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) {
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]))
} }

View File

@ -8,6 +8,7 @@ import (
"io" "io"
"math/big" "math/big"
"sort" "sort"
"sync/atomic"
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -15,71 +16,59 @@ import (
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
// A BlockNonce is a 64-bit hash which proves (combined with the
// mix-hash) that a suffcient amount of computation has been carried
// out on a block.
type BlockNonce [8]byte
func EncodeNonce(i uint64) BlockNonce {
var n BlockNonce
binary.BigEndian.PutUint64(n[:], i)
return n
}
func (n BlockNonce) Uint64() uint64 {
return binary.BigEndian.Uint64(n[:])
}
type Header struct { type Header struct {
// Hash to the previous block ParentHash common.Hash // Hash to the previous block
ParentHash common.Hash UncleHash common.Hash // Uncles of this block
// Uncles of this block Coinbase common.Address // The coin base address
UncleHash common.Hash Root common.Hash // Block Trie state
// The coin base address TxHash common.Hash // Tx sha
Coinbase common.Address ReceiptHash common.Hash // Receipt sha
// Block Trie state Bloom Bloom // Bloom
Root common.Hash Difficulty *big.Int // Difficulty for the current block
// Tx sha Number *big.Int // The block number
TxHash common.Hash GasLimit *big.Int // Gas limit
// Receipt sha GasUsed *big.Int // Gas used
ReceiptHash common.Hash Time uint64 // Creation time
// Bloom Extra []byte // Extra data
Bloom Bloom MixDigest common.Hash // for quick difficulty verification
// Difficulty for the current block Nonce BlockNonce
Difficulty *big.Int
// The block number
Number *big.Int
// Gas limit
GasLimit *big.Int
// Gas used
GasUsed *big.Int
// Creation time
Time uint64
// Extra data
Extra []byte
// Mix digest for quick checking to prevent DOS
MixDigest common.Hash
// Nonce
Nonce [8]byte
} }
func (self *Header) Hash() common.Hash { func (h *Header) Hash() common.Hash {
return rlpHash(self.rlpData(true)) return rlpHash(h)
} }
func (self *Header) HashNoNonce() common.Hash { func (h *Header) HashNoNonce() common.Hash {
return rlpHash(self.rlpData(false)) return rlpHash([]interface{}{
} h.ParentHash,
h.UncleHash,
func (self *Header) rlpData(withNonce bool) []interface{} { h.Coinbase,
fields := []interface{}{ h.Root,
self.ParentHash, h.TxHash,
self.UncleHash, h.ReceiptHash,
self.Coinbase, h.Bloom,
self.Root, h.Difficulty,
self.TxHash, h.Number,
self.ReceiptHash, h.GasLimit,
self.Bloom, h.GasUsed,
self.Difficulty, h.Time,
self.Number, h.Extra,
self.GasLimit, })
self.GasUsed,
self.Time,
self.Extra,
}
if withNonce {
fields = append(fields, self.MixDigest, self.Nonce)
}
return fields
}
func (self *Header) RlpData() interface{} {
return self.rlpData(true)
} }
func (h *Header) UnmarshalJSON(data []byte) error { func (h *Header) UnmarshalJSON(data []byte) error {
@ -112,20 +101,21 @@ func rlpHash(x interface{}) (h common.Hash) {
} }
type Block struct { type Block struct {
// Preset Hash for mock (Tests)
HeaderHash common.Hash
ParentHeaderHash common.Hash
// ^^^^ ignore ^^^^
header *Header header *Header
uncles []*Header uncles []*Header
transactions Transactions transactions Transactions
Td *big.Int receipts Receipts
queued bool // flag for blockpool to skip TD check
// caches
hash atomic.Value
size atomic.Value
// Td is used by package core to store the total difficulty
// of the chain up to and including the block.
Td *big.Int
// ReceivedAt is used by package eth to track block propagation time.
ReceivedAt time.Time ReceivedAt time.Time
receipts Receipts
} }
// StorageBlock defines the RLP encoding of a Block stored in the // StorageBlock defines the RLP encoding of a Block stored in the
@ -148,43 +138,90 @@ type storageblock struct {
TD *big.Int TD *big.Int
} }
func NewBlock(parentHash common.Hash, coinbase common.Address, root common.Hash, difficulty *big.Int, nonce uint64, extra []byte) *Block { var (
header := &Header{ emptyRootHash = DeriveSha(Transactions{})
Root: root, emptyUncleHash = CalcUncleHash(nil)
ParentHash: parentHash, )
Coinbase: coinbase,
Difficulty: difficulty, // NewBlock creates a new block. The input data is copied,
Time: uint64(time.Now().Unix()), // changes to header and to the field values will not affect the
Extra: extra, // block.
GasUsed: new(big.Int), //
GasLimit: new(big.Int), // The values of TxHash, UncleHash, ReceiptHash and Bloom in header
Number: new(big.Int), // are ignored and set to values derived from the given txs, uncles
// and receipts.
func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt) *Block {
b := &Block{header: copyHeader(header), Td: new(big.Int)}
// TODO: panic if len(txs) != len(receipts)
if len(txs) == 0 {
b.header.TxHash = emptyRootHash
} else {
b.header.TxHash = DeriveSha(Transactions(txs))
b.transactions = make(Transactions, len(txs))
copy(b.transactions, txs)
} }
header.SetNonce(nonce)
block := &Block{header: header}
block.Td = new(big.Int)
return block if len(receipts) == 0 {
} b.header.ReceiptHash = emptyRootHash
} else {
func (self *Header) SetNonce(nonce uint64) { b.header.ReceiptHash = DeriveSha(Receipts(receipts))
binary.BigEndian.PutUint64(self.Nonce[:], nonce) b.header.Bloom = CreateBloom(receipts)
b.receipts = make([]*Receipt, len(receipts))
copy(b.receipts, receipts)
}
if len(uncles) == 0 {
b.header.UncleHash = emptyUncleHash
} else {
b.header.UncleHash = CalcUncleHash(uncles)
b.uncles = make([]*Header, len(uncles))
for i := range uncles {
b.uncles[i] = copyHeader(uncles[i])
}
}
return b
} }
// NewBlockWithHeader creates a block with the given header data. The
// header data is copied, changes to header and to the field values
// will not affect the block.
func NewBlockWithHeader(header *Header) *Block { func NewBlockWithHeader(header *Header) *Block {
return &Block{header: header} return &Block{header: copyHeader(header)}
} }
func (self *Block) ValidateFields() error { func copyHeader(h *Header) *Header {
if self.header == nil { cpy := *h
if cpy.Difficulty = new(big.Int); h.Difficulty != nil {
cpy.Difficulty.Set(h.Difficulty)
}
if cpy.Number = new(big.Int); h.Number != nil {
cpy.Number.Set(h.Number)
}
if cpy.GasLimit = new(big.Int); h.GasLimit != nil {
cpy.GasLimit.Set(h.GasLimit)
}
if cpy.GasUsed = new(big.Int); h.GasUsed != nil {
cpy.GasUsed.Set(h.GasUsed)
}
if len(h.Extra) > 0 {
cpy.Extra = make([]byte, len(h.Extra))
copy(cpy.Extra, h.Extra)
}
return &cpy
}
func (b *Block) ValidateFields() error {
if b.header == nil {
return fmt.Errorf("header is nil") return fmt.Errorf("header is nil")
} }
for i, transaction := range self.transactions { for i, transaction := range b.transactions {
if transaction == nil { if transaction == nil {
return fmt.Errorf("transaction %d is nil", i) return fmt.Errorf("transaction %d is nil", i)
} }
} }
for i, uncle := range self.uncles { for i, uncle := range b.uncles {
if uncle == nil { if uncle == nil {
return fmt.Errorf("uncle %d is nil", i) return fmt.Errorf("uncle %d is nil", i)
} }
@ -192,64 +229,50 @@ func (self *Block) ValidateFields() error {
return nil return nil
} }
func (self *Block) DecodeRLP(s *rlp.Stream) error { func (b *Block) DecodeRLP(s *rlp.Stream) error {
var eb extblock var eb extblock
_, size, _ := s.Kind()
if err := s.Decode(&eb); err != nil { if err := s.Decode(&eb); err != nil {
return err return err
} }
self.header, self.uncles, self.transactions = eb.Header, eb.Uncles, eb.Txs b.header, b.uncles, b.transactions = eb.Header, eb.Uncles, eb.Txs
b.size.Store(common.StorageSize(rlp.ListSize(size)))
return nil return nil
} }
func (self Block) EncodeRLP(w io.Writer) error { func (b Block) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, extblock{ return rlp.Encode(w, extblock{
Header: self.header, Header: b.header,
Txs: self.transactions, Txs: b.transactions,
Uncles: self.uncles, Uncles: b.uncles,
}) })
} }
func (self *StorageBlock) DecodeRLP(s *rlp.Stream) error { func (b *StorageBlock) DecodeRLP(s *rlp.Stream) error {
var sb storageblock var sb storageblock
if err := s.Decode(&sb); err != nil { if err := s.Decode(&sb); err != nil {
return err return err
} }
self.header, self.uncles, self.transactions, self.Td = sb.Header, sb.Uncles, sb.Txs, sb.TD b.header, b.uncles, b.transactions, b.Td = sb.Header, sb.Uncles, sb.Txs, sb.TD
return nil return nil
} }
func (self StorageBlock) EncodeRLP(w io.Writer) error { func (b StorageBlock) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, storageblock{ return rlp.Encode(w, storageblock{
Header: self.header, Header: b.header,
Txs: self.transactions, Txs: b.transactions,
Uncles: self.uncles, Uncles: b.uncles,
TD: self.Td, TD: b.Td,
}) })
} }
func (self *Block) Header() *Header { // TODO: copies
return self.header func (b *Block) Uncles() []*Header { return b.uncles }
} func (b *Block) Transactions() Transactions { return b.transactions }
func (b *Block) Receipts() Receipts { return b.receipts }
func (self *Block) Uncles() []*Header { func (b *Block) Transaction(hash common.Hash) *Transaction {
return self.uncles for _, transaction := range b.transactions {
}
func (self *Block) CalculateUnclesHash() common.Hash {
return rlpHash(self.uncles)
}
func (self *Block) SetUncles(uncleHeaders []*Header) {
self.uncles = uncleHeaders
self.header.UncleHash = rlpHash(uncleHeaders)
}
func (self *Block) Transactions() Transactions {
return self.transactions
}
func (self *Block) Transaction(hash common.Hash) *Transaction {
for _, transaction := range self.transactions {
if transaction.Hash() == hash { if transaction.Hash() == hash {
return transaction return transaction
} }
@ -257,74 +280,37 @@ func (self *Block) Transaction(hash common.Hash) *Transaction {
return nil return nil
} }
func (self *Block) SetTransactions(transactions Transactions) { func (b *Block) Number() *big.Int { return new(big.Int).Set(b.header.Number) }
self.transactions = transactions func (b *Block) GasLimit() *big.Int { return new(big.Int).Set(b.header.GasLimit) }
self.header.TxHash = DeriveSha(transactions) func (b *Block) GasUsed() *big.Int { return new(big.Int).Set(b.header.GasUsed) }
} func (b *Block) Difficulty() *big.Int { return new(big.Int).Set(b.header.Difficulty) }
func (self *Block) AddTransaction(transaction *Transaction) {
self.transactions = append(self.transactions, transaction) func (b *Block) NumberU64() uint64 { return b.header.Number.Uint64() }
self.SetTransactions(self.transactions) func (b *Block) MixDigest() common.Hash { return b.header.MixDigest }
func (b *Block) Nonce() uint64 { return binary.BigEndian.Uint64(b.header.Nonce[:]) }
func (b *Block) Bloom() Bloom { return b.header.Bloom }
func (b *Block) Coinbase() common.Address { return b.header.Coinbase }
func (b *Block) Time() int64 { return int64(b.header.Time) }
func (b *Block) Root() common.Hash { return b.header.Root }
func (b *Block) ParentHash() common.Hash { return b.header.ParentHash }
func (b *Block) TxHash() common.Hash { return b.header.TxHash }
func (b *Block) ReceiptHash() common.Hash { return b.header.ReceiptHash }
func (b *Block) UncleHash() common.Hash { return b.header.UncleHash }
func (b *Block) Extra() []byte { return common.CopyBytes(b.header.Extra) }
func (b *Block) Header() *Header { return copyHeader(b.header) }
func (b *Block) HashNoNonce() common.Hash {
return b.header.HashNoNonce()
} }
func (self *Block) Receipts() Receipts { func (b *Block) Size() common.StorageSize {
return self.receipts if size := b.size.Load(); size != nil {
} return size.(common.StorageSize)
func (self *Block) SetReceipts(receipts Receipts) {
self.receipts = receipts
self.header.ReceiptHash = DeriveSha(receipts)
self.header.Bloom = CreateBloom(receipts)
}
func (self *Block) AddReceipt(receipt *Receipt) {
self.receipts = append(self.receipts, receipt)
self.SetReceipts(self.receipts)
}
func (self *Block) RlpData() interface{} {
return []interface{}{self.header, self.transactions, self.uncles}
}
func (self *Block) RlpDataForStorage() interface{} {
return []interface{}{self.header, self.transactions, self.uncles, self.Td /* TODO receipts */}
}
// Header accessors (add as you need them)
func (self *Block) Number() *big.Int { return self.header.Number }
func (self *Block) NumberU64() uint64 { return self.header.Number.Uint64() }
func (self *Block) MixDigest() common.Hash { return self.header.MixDigest }
func (self *Block) Nonce() uint64 {
return binary.BigEndian.Uint64(self.header.Nonce[:])
}
func (self *Block) SetNonce(nonce uint64) {
self.header.SetNonce(nonce)
}
func (self *Block) Queued() bool { return self.queued }
func (self *Block) SetQueued(q bool) { self.queued = q }
func (self *Block) Bloom() Bloom { return self.header.Bloom }
func (self *Block) Coinbase() common.Address { return self.header.Coinbase }
func (self *Block) Time() int64 { return int64(self.header.Time) }
func (self *Block) GasLimit() *big.Int { return self.header.GasLimit }
func (self *Block) GasUsed() *big.Int { return self.header.GasUsed }
func (self *Block) Root() common.Hash { return self.header.Root }
func (self *Block) SetRoot(root common.Hash) { self.header.Root = root }
func (self *Block) GetTransaction(i int) *Transaction {
if len(self.transactions) > i {
return self.transactions[i]
} }
return nil
}
func (self *Block) GetUncle(i int) *Header {
if len(self.uncles) > i {
return self.uncles[i]
}
return nil
}
func (self *Block) Size() common.StorageSize {
c := writeCounter(0) c := writeCounter(0)
rlp.Encode(&c, self) rlp.Encode(&c, b)
b.size.Store(common.StorageSize(c))
return common.StorageSize(c) return common.StorageSize(c)
} }
@ -335,48 +321,37 @@ func (c *writeCounter) Write(b []byte) (int, error) {
return len(b), nil return len(b), nil
} }
func CalcUncleHash(uncles []*Header) common.Hash {
return rlpHash(uncles)
}
// WithMiningResult returns a new block with the data from b
// where nonce and mix digest are set to the provided values.
func (b *Block) WithMiningResult(nonce uint64, mixDigest common.Hash) *Block {
cpy := *b.header
binary.BigEndian.PutUint64(cpy.Nonce[:], nonce)
cpy.MixDigest = mixDigest
return &Block{
header: &cpy,
transactions: b.transactions,
receipts: b.receipts,
uncles: b.uncles,
Td: b.Td,
}
}
// Implement pow.Block // Implement pow.Block
func (self *Block) Difficulty() *big.Int { return self.header.Difficulty }
func (self *Block) HashNoNonce() common.Hash { return self.header.HashNoNonce() }
func (self *Block) Hash() common.Hash { func (b *Block) Hash() common.Hash {
if (self.HeaderHash != common.Hash{}) { if hash := b.hash.Load(); hash != nil {
return self.HeaderHash return hash.(common.Hash)
} else {
return self.header.Hash()
} }
v := rlpHash(b.header)
b.hash.Store(v)
return v
} }
func (self *Block) ParentHash() common.Hash { func (b *Block) String() string {
if (self.ParentHeaderHash != common.Hash{}) {
return self.ParentHeaderHash
} else {
return self.header.ParentHash
}
}
func (self *Block) Copy() *Block {
block := NewBlock(self.header.ParentHash, self.Coinbase(), self.Root(), new(big.Int), self.Nonce(), self.header.Extra)
block.header.Bloom = self.header.Bloom
block.header.TxHash = self.header.TxHash
block.transactions = self.transactions
block.header.UncleHash = self.header.UncleHash
block.uncles = self.uncles
block.header.GasLimit.Set(self.header.GasLimit)
block.header.GasUsed.Set(self.header.GasUsed)
block.header.ReceiptHash = self.header.ReceiptHash
block.header.Difficulty.Set(self.header.Difficulty)
block.header.Number.Set(self.header.Number)
block.header.Time = self.header.Time
block.header.MixDigest = self.header.MixDigest
if self.Td != nil {
block.Td.Set(self.Td)
}
return block
}
func (self *Block) String() string {
str := fmt.Sprintf(`Block(#%v): Size: %v TD: %v { str := fmt.Sprintf(`Block(#%v): Size: %v TD: %v {
MinerHash: %x MinerHash: %x
%v %v
@ -385,20 +360,11 @@ Transactions:
Uncles: Uncles:
%v %v
} }
`, self.Number(), self.Size(), self.Td, self.header.HashNoNonce(), self.header, self.transactions, self.uncles) `, b.Number(), b.Size(), b.Td, b.header.HashNoNonce(), b.header, b.transactions, b.uncles)
if (self.HeaderHash != common.Hash{}) {
str += fmt.Sprintf("\nFake hash = %x", self.HeaderHash)
}
if (self.ParentHeaderHash != common.Hash{}) {
str += fmt.Sprintf("\nFake parent hash = %x", self.ParentHeaderHash)
}
return str return str
} }
func (self *Header) String() string { func (h *Header) String() string {
return fmt.Sprintf(`Header(%x): return fmt.Sprintf(`Header(%x):
[ [
ParentHash: %x ParentHash: %x
@ -414,9 +380,9 @@ func (self *Header) String() string {
GasUsed: %v GasUsed: %v
Time: %v Time: %v
Extra: %s Extra: %s
MixDigest: %x MixDigest: %x
Nonce: %x Nonce: %x
]`, self.Hash(), self.ParentHash, self.UncleHash, self.Coinbase, self.Root, self.TxHash, self.ReceiptHash, self.Bloom, self.Difficulty, self.Number, self.GasLimit, self.GasUsed, self.Time, self.Extra, self.MixDigest, self.Nonce) ]`, h.Hash(), h.ParentHash, h.UncleHash, h.Coinbase, h.Root, h.TxHash, h.ReceiptHash, h.Bloom, h.Difficulty, h.Number, h.GasLimit, h.GasUsed, h.Time, h.Extra, h.MixDigest, h.Nonce)
} }
type Blocks []*Block type Blocks []*Block
@ -442,4 +408,4 @@ func (self blockSorter) Swap(i, j int) {
} }
func (self blockSorter) Less(i, j int) bool { return self.by(self.blocks[i], self.blocks[j]) } func (self blockSorter) Less(i, j int) bool { return self.by(self.blocks[i], self.blocks[j]) }
func Number(b1, b2 *Block) bool { return b1.Header().Number.Cmp(b2.Header().Number) < 0 } func Number(b1, b2 *Block) bool { return b1.header.Number.Cmp(b2.header.Number) < 0 }

View File

@ -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) {
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 {

View File

@ -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 {
} }
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 NewTransactionMessage(to common.Address, amount, gasAmount, gasPrice *big.Int, data []byte) *Transaction { func NewContractCreation(nonce uint64, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
return &Transaction{ if len(data) > 0 {
Recipient: &to, data = common.CopyBytes(data)
Amount: amount,
GasLimit: gasAmount,
Price: gasPrice,
Payload: data,
R: new(big.Int),
S: new(big.Int),
} }
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 NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
if len(data) > 0 {
data = common.CopyBytes(data)
}
d := txdata{
AccountNonce: nonce,
Recipient: &to,
Payload: data,
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 {
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 (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
func (self *Transaction) Size() common.StorageSize { _, size, _ := s.Kind()
err := s.Decode(&tx.data)
if err == nil {
tx.size.Store(common.StorageSize(rlp.ListSize(size)))
}
return err
}
func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) }
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 (tx *Transaction) To() *common.Address {
if tx.data.Recipient == nil {
return nil
} else {
to := *tx.data.Recipient
return &to
}
}
func (tx *Transaction) Hash() common.Hash {
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 (tx *Transaction) Size() common.StorageSize {
if size := tx.size.Load(); size != nil {
return size.(common.StorageSize)
}
c := writeCounter(0) c := writeCounter(0)
rlp.Encode(&c, self) rlp.Encode(&c, &tx.data)
tx.size.Store(common.StorageSize(c))
return common.StorageSize(c) return common.StorageSize(c)
} }
func (self *Transaction) Data() []byte { func (tx *Transaction) From() (common.Address, error) {
return self.Payload if from := tx.from.Load(); from != nil {
} return from.(common.Address), nil
}
func (self *Transaction) Gas() *big.Int { pubkey, err := tx.publicKey()
return self.GasLimit
}
func (self *Transaction) GasPrice() *big.Int {
return self.Price
}
func (self *Transaction) Value() *big.Int {
return self.Amount
}
func (self *Transaction) Nonce() uint64 {
return self.AccountNonce
}
func (self *Transaction) SetNonce(AccountNonce uint64) {
self.AccountNonce = AccountNonce
}
func (self *Transaction) From() (common.Address, error) {
pubkey, err := self.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 {
} 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 {
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 {
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
} }

View File

@ -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) {
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")
} }

View File

@ -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 }

View File

@ -1,7 +1,7 @@
package downloader package downloader
import ( import (
"encoding/binary" "crypto/rand"
"errors" "errors"
"fmt" "fmt"
"math/big" "math/big"
@ -12,58 +12,47 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "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"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
) )
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}
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
var idCounter = int64(2) // #1 is the genesis block // parent. 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()
blockm[b.Hash()] = b
} }
return return hashes, blockm
} }
// createBlock assembles a new block at the given chain height. // makeChainFork creates two chains of length n, such that h1[:f] and
func createBlock(i int, parent, hash common.Hash) *types.Block { // h2[:f] are different but have a common suffix of length n-f.
header := &types.Header{Number: big.NewInt(int64(i))} func makeChainFork(n, f int, parent *types.Block) (h1, h2 []common.Hash, b1, b2 map[common.Hash]*types.Block) {
block := types.NewBlockWithHeader(header) // Create the common suffix.
block.HeaderHash = hash h, b := makeChain(n-f-1, 0, parent)
block.ParentHeaderHash = parent // Create the forks.
return block h1, b1 = makeChain(f, 1, b[h[0]])
} h1 = append(h1, h[1:]...)
h2, b2 = makeChain(f, 2, b[h[0]])
// copyBlock makes a deep copy of a block suitable for local modifications. h2 = append(h2, h[1:]...)
func copyBlock(block *types.Block) *types.Block { for hash, block := range b {
return createBlock(int(block.Number().Int64()), block.ParentHeaderHash, block.HeaderHash) b1[hash] = block
} b2[hash] = block
// 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 return h1, h2, b1, b2
} }
// downloadTester is a test simulator for mocking out local block chain. // downloadTester is a test simulator for mocking out local block chain.
@ -81,8 +70,8 @@ type downloadTester struct {
// newTester creates a new downloader test mocker. // newTester creates a new downloader test mocker.
func newTester() *downloadTester { func newTester() *downloadTester {
tester := &downloadTester{ tester := &downloadTester{
ownHashes: []common.Hash{knownHash}, ownHashes: []common.Hash{genesis.Hash()},
ownBlocks: map[common.Hash]*types.Block{knownHash: genesis}, ownBlocks: map[common.Hash]*types.Block{genesis.Hash(): genesis},
peerHashes: make(map[string][]common.Hash), peerHashes: make(map[string][]common.Hash),
peerBlocks: make(map[string]map[common.Hash]*types.Block), peerBlocks: make(map[string]map[common.Hash]*types.Block),
} }
@ -136,10 +125,9 @@ func (dl *downloadTester) newSlowPeer(id string, hashes []common.Hash, blocks ma
// Assign the owned hashes and blocks to the peer (deep copy) // Assign the owned hashes and blocks to the peer (deep copy)
dl.peerHashes[id] = make([]common.Hash, len(hashes)) dl.peerHashes[id] = make([]common.Hash, len(hashes))
copy(dl.peerHashes[id], hashes) copy(dl.peerHashes[id], hashes)
dl.peerBlocks[id] = make(map[common.Hash]*types.Block) dl.peerBlocks[id] = make(map[common.Hash]*types.Block)
for hash, block := range blocks { for hash, block := range blocks {
dl.peerBlocks[id][hash] = copyBlock(block) dl.peerBlocks[id][hash] = block
} }
} }
return err return err
@ -210,8 +198,7 @@ func (dl *downloadTester) peerGetBlocksFn(id string, delay time.Duration) func([
func TestSynchronisation(t *testing.T) { func TestSynchronisation(t *testing.T) {
// Create a small enough block chain to download and the tester // Create a small enough block chain to download and the tester
targetBlocks := blockCacheLimit - 15 targetBlocks := blockCacheLimit - 15
hashes := createHashes(targetBlocks, knownHash) hashes, blocks := makeChain(targetBlocks, 0, genesis)
blocks := createBlocksFromHashes(hashes)
tester := newTester() tester := newTester()
tester.newPeer("peer", hashes, blocks) tester.newPeer("peer", hashes, blocks)
@ -242,8 +229,7 @@ func TestInactiveDownloader(t *testing.T) {
func TestCancel(t *testing.T) { func TestCancel(t *testing.T) {
// Create a small enough block chain to download and the tester // Create a small enough block chain to download and the tester
targetBlocks := blockCacheLimit - 15 targetBlocks := blockCacheLimit - 15
hashes := createHashes(targetBlocks, knownHash) hashes, blocks := makeChain(targetBlocks, 0, genesis)
blocks := createBlocksFromHashes(hashes)
tester := newTester() tester := newTester()
tester.newPeer("peer", hashes, blocks) tester.newPeer("peer", hashes, blocks)
@ -270,8 +256,7 @@ func TestCancel(t *testing.T) {
func TestThrottling(t *testing.T) { func TestThrottling(t *testing.T) {
// Create a long block chain to download and the tester // Create a long block chain to download and the tester
targetBlocks := 8 * blockCacheLimit targetBlocks := 8 * blockCacheLimit
hashes := createHashes(targetBlocks, knownHash) hashes, blocks := makeChain(targetBlocks, 0, genesis)
blocks := createBlocksFromHashes(hashes)
tester := newTester() tester := newTester()
tester.newPeer("peer", hashes, blocks) tester.newPeer("peer", hashes, blocks)
@ -327,9 +312,7 @@ func TestMultiSynchronisation(t *testing.T) {
// Create various peers with various parts of the chain // Create various peers with various parts of the chain
targetPeers := 16 targetPeers := 16
targetBlocks := targetPeers*blockCacheLimit - 15 targetBlocks := targetPeers*blockCacheLimit - 15
hashes, blocks := makeChain(targetBlocks, 0, genesis)
hashes := createHashes(targetBlocks, knownHash)
blocks := createBlocksFromHashes(hashes)
tester := newTester() tester := newTester()
for i := 0; i < targetPeers; i++ { for i := 0; i < targetPeers; i++ {
@ -362,9 +345,7 @@ func TestSlowSynchronisation(t *testing.T) {
targetCycles := 2 targetCycles := 2
targetBlocks := targetCycles*blockCacheLimit - 15 targetBlocks := targetCycles*blockCacheLimit - 15
targetIODelay := time.Second targetIODelay := time.Second
hashes, blocks := makeChain(targetBlocks, 0, genesis)
hashes := createHashes(targetBlocks, knownHash)
blocks := createBlocksFromHashes(hashes)
tester.newSlowPeer("fast", hashes, blocks, 0) tester.newSlowPeer("fast", hashes, blocks, 0)
tester.newSlowPeer("slow", hashes, blocks, targetIODelay) tester.newSlowPeer("slow", hashes, blocks, targetIODelay)
@ -389,14 +370,12 @@ func TestSlowSynchronisation(t *testing.T) {
func TestNonExistingParentAttack(t *testing.T) { func TestNonExistingParentAttack(t *testing.T) {
tester := newTester() tester := newTester()
// Forge a single-link chain with a forged header hashes, blocks := makeChain(1, 0, genesis)
hashes := createHashes(1, knownHash)
blocks := createBlocksFromHashes(hashes)
tester.newPeer("valid", hashes, blocks) tester.newPeer("valid", hashes, blocks)
hashes = createHashes(1, knownHash) wrongblock := types.NewBlock(&types.Header{}, nil, nil, nil)
blocks = createBlocksFromHashes(hashes) wrongblock.Td = blocks[hashes[0]].Td
blocks[hashes[0]].ParentHeaderHash = unknownHash hashes, blocks = makeChain(1, 0, wrongblock)
tester.newPeer("attack", hashes, blocks) tester.newPeer("attack", hashes, blocks)
// Try and sync with the malicious node and check that it fails // Try and sync with the malicious node and check that it fails
@ -421,8 +400,7 @@ func TestRepeatingHashAttack(t *testing.T) { // TODO: Is this thing valid??
tester := newTester() tester := newTester()
// Create a valid chain, but drop the last link // Create a valid chain, but drop the last link
hashes := createHashes(blockCacheLimit, knownHash) hashes, blocks := makeChain(blockCacheLimit, 0, genesis)
blocks := createBlocksFromHashes(hashes)
tester.newPeer("valid", hashes, blocks) tester.newPeer("valid", hashes, blocks)
tester.newPeer("attack", hashes[:len(hashes)-1], blocks) tester.newPeer("attack", hashes[:len(hashes)-1], blocks)
@ -452,11 +430,10 @@ func TestNonExistingBlockAttack(t *testing.T) {
tester := newTester() tester := newTester()
// Create a valid chain, but forge the last link // Create a valid chain, but forge the last link
hashes := createHashes(blockCacheLimit, knownHash) hashes, blocks := makeChain(blockCacheLimit, 0, genesis)
blocks := createBlocksFromHashes(hashes)
tester.newPeer("valid", hashes, blocks) tester.newPeer("valid", hashes, blocks)
hashes[len(hashes)/2] = unknownHash hashes[len(hashes)/2] = common.Hash{}
tester.newPeer("attack", hashes, blocks) tester.newPeer("attack", hashes, blocks)
// Try and sync with the malicious node and check that it fails // Try and sync with the malicious node and check that it fails
@ -475,8 +452,7 @@ func TestInvalidHashOrderAttack(t *testing.T) {
tester := newTester() tester := newTester()
// Create a valid long chain, but reverse some hashes within // Create a valid long chain, but reverse some hashes within
hashes := createHashes(4*blockCacheLimit, knownHash) hashes, blocks := makeChain(4*blockCacheLimit, 0, genesis)
blocks := createBlocksFromHashes(hashes)
tester.newPeer("valid", hashes, blocks) tester.newPeer("valid", hashes, blocks)
chunk1 := make([]common.Hash, blockCacheLimit) chunk1 := make([]common.Hash, blockCacheLimit)
@ -506,11 +482,15 @@ func TestMadeupHashChainAttack(t *testing.T) {
crossCheckCycle = 25 * time.Millisecond crossCheckCycle = 25 * time.Millisecond
// Create a long chain of hashes without backing blocks // Create a long chain of hashes without backing blocks
hashes := createHashes(4*blockCacheLimit, knownHash) hashes, blocks := makeChain(4*blockCacheLimit, 0, genesis)
blocks := createBlocksFromHashes(hashes)
randomHashes := make([]common.Hash, 1024*blockCacheLimit)
for i := range randomHashes {
rand.Read(randomHashes[i][:])
}
tester.newPeer("valid", hashes, blocks) tester.newPeer("valid", hashes, blocks)
tester.newPeer("attack", createHashes(1024*blockCacheLimit, knownHash), nil) tester.newPeer("attack", randomHashes, nil)
// Try and sync with the malicious node and check that it fails // Try and sync with the malicious node and check that it fails
if err := tester.sync("attack"); err != errCrossCheckFailed { if err := tester.sync("attack"); err != errCrossCheckFailed {
@ -528,12 +508,16 @@ func TestMadeupHashChainAttack(t *testing.T) {
// one by one prevents reliable block/parent verification. // one by one prevents reliable block/parent verification.
func TestMadeupHashChainDrippingAttack(t *testing.T) { func TestMadeupHashChainDrippingAttack(t *testing.T) {
// Create a random chain of hashes to drip // Create a random chain of hashes to drip
hashes := createHashes(16*blockCacheLimit, knownHash) randomHashes := make([]common.Hash, 16*blockCacheLimit)
for i := range randomHashes {
rand.Read(randomHashes[i][:])
}
randomHashes[len(randomHashes)-1] = genesis.Hash()
tester := newTester() tester := newTester()
// Try and sync with the attacker, one hash at a time // Try and sync with the attacker, one hash at a time
tester.maxHashFetch = 1 tester.maxHashFetch = 1
tester.newPeer("attack", hashes, nil) tester.newPeer("attack", randomHashes, nil)
if err := tester.sync("attack"); err != errStallingPeer { if err := tester.sync("attack"); err != errStallingPeer {
t.Fatalf("synchronisation error mismatch: have %v, want %v", err, errStallingPeer) t.Fatalf("synchronisation error mismatch: have %v, want %v", err, errStallingPeer)
} }
@ -549,9 +533,7 @@ func TestMadeupBlockChainAttack(t *testing.T) {
crossCheckCycle = 25 * time.Millisecond crossCheckCycle = 25 * time.Millisecond
// Create a long chain of blocks and simulate an invalid chain by dropping every second // Create a long chain of blocks and simulate an invalid chain by dropping every second
hashes := createHashes(16*blockCacheLimit, knownHash) hashes, blocks := makeChain(16*blockCacheLimit, 0, genesis)
blocks := createBlocksFromHashes(hashes)
gapped := make([]common.Hash, len(hashes)/2) gapped := make([]common.Hash, len(hashes)/2)
for i := 0; i < len(gapped); i++ { for i := 0; i < len(gapped); i++ {
gapped[i] = hashes[2*i] gapped[i] = hashes[2*i]
@ -572,65 +554,26 @@ func TestMadeupBlockChainAttack(t *testing.T) {
} }
} }
// Advanced form of the above forged blockchain attack, where not only does the // tests that if one/multiple malicious peers try to feed a banned blockchain to
// attacker make up a valid hashes for random blocks, but also forges the block
// parents to point to existing hashes.
func TestMadeupParentBlockChainAttack(t *testing.T) {
tester := newTester()
defaultBlockTTL := blockSoftTTL
defaultCrossCheckCycle := crossCheckCycle
blockSoftTTL = 100 * time.Millisecond
crossCheckCycle = 25 * time.Millisecond
// Create a long chain of blocks and simulate an invalid chain by dropping every second
hashes := createHashes(16*blockCacheLimit, knownHash)
blocks := createBlocksFromHashes(hashes)
tester.newPeer("valid", hashes, blocks)
for _, block := range blocks {
block.ParentHeaderHash = knownHash // Simulate pointing to already known hash
}
tester.newPeer("attack", hashes, blocks)
// Try and sync with the malicious node and check that it fails
if err := tester.sync("attack"); err != errCrossCheckFailed {
t.Fatalf("synchronisation error mismatch: have %v, want %v", err, errCrossCheckFailed)
}
// Ensure that a valid chain can still pass sync
blockSoftTTL = defaultBlockTTL
crossCheckCycle = defaultCrossCheckCycle
if err := tester.sync("valid"); err != nil {
t.Fatalf("failed to synchronise blocks: %v", err)
}
}
// Tests that if one/multiple malicious peers try to feed a banned blockchain to
// the downloader, it will not keep refetching the same chain indefinitely, but // the downloader, it will not keep refetching the same chain indefinitely, but
// gradually block pieces of it, until it's head is also blocked. // gradually block pieces of it, until its head is also blocked.
func TestBannedChainStarvationAttack(t *testing.T) { func TestBannedChainStarvationAttack(t *testing.T) {
// Create the tester and ban the selected hash n := 8 * blockCacheLimit
fork := n/2 - 23
hashes, forkHashes, blocks, forkBlocks := makeChainFork(n, fork, genesis)
// Create the tester and ban the selected hash.
tester := newTester() tester := newTester()
tester.downloader.banned.Add(bannedHash) tester.downloader.banned.Add(forkHashes[fork-1])
// Construct a valid chain, for it and ban the fork
hashes := createHashes(8*blockCacheLimit, knownHash)
blocks := createBlocksFromHashes(hashes)
tester.newPeer("valid", hashes, blocks) tester.newPeer("valid", hashes, blocks)
tester.newPeer("attack", forkHashes, forkBlocks)
fork := len(hashes)/2 - 23
hashes = append(createHashes(4*blockCacheLimit, bannedHash), hashes[fork:]...)
blocks = createBlocksFromHashes(hashes)
tester.newPeer("attack", hashes, blocks)
// Iteratively try to sync, and verify that the banned hash list grows until // Iteratively try to sync, and verify that the banned hash list grows until
// the head of the invalid chain is blocked too. // the head of the invalid chain is blocked too.
for banned := tester.downloader.banned.Size(); ; { for banned := tester.downloader.banned.Size(); ; {
// Try to sync with the attacker, check hash chain failure // Try to sync with the attacker, check hash chain failure
if err := tester.sync("attack"); err != errInvalidChain { if err := tester.sync("attack"); err != errInvalidChain {
if tester.downloader.banned.Has(hashes[0]) && err == errBannedHead { if tester.downloader.banned.Has(forkHashes[0]) && err == errBannedHead {
break break
} }
t.Fatalf("synchronisation error mismatch: have %v, want %v", err, errInvalidChain) t.Fatalf("synchronisation error mismatch: have %v, want %v", err, errInvalidChain)
@ -643,7 +586,7 @@ func TestBannedChainStarvationAttack(t *testing.T) {
banned = bans banned = bans
} }
// Check that after banning an entire chain, bad peers get dropped // Check that after banning an entire chain, bad peers get dropped
if err := tester.newPeer("new attacker", hashes, blocks); err != errBannedHead { if err := tester.newPeer("new attacker", forkHashes, forkBlocks); err != errBannedHead {
t.Fatalf("peer registration mismatch: have %v, want %v", err, errBannedHead) t.Fatalf("peer registration mismatch: have %v, want %v", err, errBannedHead)
} }
if peer := tester.downloader.peers.Peer("new attacker"); peer != nil { if peer := tester.downloader.peers.Peer("new attacker"); peer != nil {
@ -659,9 +602,14 @@ func TestBannedChainStarvationAttack(t *testing.T) {
// gradually banned, it will have an upper limit on the consumed memory and also // gradually banned, it will have an upper limit on the consumed memory and also
// the origin bad hashes will not be evacuated. // the origin bad hashes will not be evacuated.
func TestBannedChainMemoryExhaustionAttack(t *testing.T) { func TestBannedChainMemoryExhaustionAttack(t *testing.T) {
// Create the tester and ban the selected hash // Construct a banned chain with more chunks than the ban limit
n := 8 * blockCacheLimit
fork := n/2 - 23
hashes, forkHashes, blocks, forkBlocks := makeChainFork(n, fork, genesis)
// Create the tester and ban the root hash of the fork.
tester := newTester() tester := newTester()
tester.downloader.banned.Add(bannedHash) tester.downloader.banned.Add(forkHashes[fork-1])
// Reduce the test size a bit // Reduce the test size a bit
defaultMaxBlockFetch := MaxBlockFetch defaultMaxBlockFetch := MaxBlockFetch
@ -670,15 +618,8 @@ func TestBannedChainMemoryExhaustionAttack(t *testing.T) {
MaxBlockFetch = 4 MaxBlockFetch = 4
maxBannedHashes = 256 maxBannedHashes = 256
// Construct a banned chain with more chunks than the ban limit
hashes := createHashes(8*blockCacheLimit, knownHash)
blocks := createBlocksFromHashes(hashes)
tester.newPeer("valid", hashes, blocks) tester.newPeer("valid", hashes, blocks)
tester.newPeer("attack", forkHashes, forkBlocks)
fork := len(hashes)/2 - 23
hashes = append(createHashes(maxBannedHashes*MaxBlockFetch, bannedHash), hashes[fork:]...)
blocks = createBlocksFromHashes(hashes)
tester.newPeer("attack", hashes, blocks)
// Iteratively try to sync, and verify that the banned hash list grows until // Iteratively try to sync, and verify that the banned hash list grows until
// the head of the invalid chain is blocked too. // the head of the invalid chain is blocked too.
@ -687,8 +628,8 @@ func TestBannedChainMemoryExhaustionAttack(t *testing.T) {
if err := tester.sync("attack"); err != errInvalidChain { if err := tester.sync("attack"); err != errInvalidChain {
t.Fatalf("synchronisation error mismatch: have %v, want %v", err, errInvalidChain) t.Fatalf("synchronisation error mismatch: have %v, want %v", err, errInvalidChain)
} }
// Short circuit if the entire chain was banned // Short circuit if the entire chain was banned.
if tester.downloader.banned.Has(hashes[0]) { if tester.downloader.banned.Has(forkHashes[0]) {
break break
} }
// Otherwise ensure we never exceed the memory allowance and the hard coded bans are untouched // Otherwise ensure we never exceed the memory allowance and the hard coded bans are untouched
@ -719,8 +660,7 @@ func TestBannedChainMemoryExhaustionAttack(t *testing.T) {
func TestOverlappingDeliveryAttack(t *testing.T) { func TestOverlappingDeliveryAttack(t *testing.T) {
// Create an arbitrary batch of blocks ( < cache-size not to block) // Create an arbitrary batch of blocks ( < cache-size not to block)
targetBlocks := blockCacheLimit - 23 targetBlocks := blockCacheLimit - 23
hashes := createHashes(targetBlocks, knownHash) hashes, blocks := makeChain(targetBlocks, 0, genesis)
blocks := createBlocksFromHashes(hashes)
// Register an attacker that always returns non-requested blocks too // Register an attacker that always returns non-requested blocks too
tester := newTester() tester := newTester()
@ -772,7 +712,7 @@ func TestHashAttackerDropping(t *testing.T) {
for i, tt := range tests { for i, tt := range tests {
// Register a new peer and ensure it's presence // Register a new peer and ensure it's presence
id := fmt.Sprintf("test %d", i) id := fmt.Sprintf("test %d", i)
if err := tester.newPeer(id, []common.Hash{knownHash}, nil); err != nil { if err := tester.newPeer(id, []common.Hash{genesis.Hash()}, nil); err != nil {
t.Fatalf("test %d: failed to register new peer: %v", i, err) t.Fatalf("test %d: failed to register new peer: %v", i, err)
} }
if _, ok := tester.peerHashes[id]; !ok { if _, ok := tester.peerHashes[id]; !ok {
@ -781,7 +721,7 @@ func TestHashAttackerDropping(t *testing.T) {
// Simulate a synchronisation and check the required result // Simulate a synchronisation and check the required result
tester.downloader.synchroniseMock = func(string, common.Hash) error { return tt.result } tester.downloader.synchroniseMock = func(string, common.Hash) error { return tt.result }
tester.downloader.Synchronise(id, knownHash) tester.downloader.Synchronise(id, genesis.Hash())
if _, ok := tester.peerHashes[id]; !ok != tt.drop { if _, ok := tester.peerHashes[id]; !ok != tt.drop {
t.Errorf("test %d: peer drop mismatch for %v: have %v, want %v", i, tt.result, !ok, tt.drop) t.Errorf("test %d: peer drop mismatch for %v: have %v, want %v", i, tt.result, !ok, tt.drop)
} }
@ -794,7 +734,10 @@ func TestBlockAttackerDropping(t *testing.T) {
tests := []struct { tests := []struct {
failure bool failure bool
drop bool drop bool
}{{true, true}, {false, false}} }{
{true, true},
{false, false},
}
// Run the tests and check disconnection status // Run the tests and check disconnection status
tester := newTester() tester := newTester()
@ -808,9 +751,10 @@ func TestBlockAttackerDropping(t *testing.T) {
t.Fatalf("test %d: registered peer not found", i) t.Fatalf("test %d: registered peer not found", i)
} }
// Assemble a good or bad block, depending of the test // Assemble a good or bad block, depending of the test
raw := createBlock(1, knownHash, common.Hash{}) raw := core.GenerateChain(genesis, testdb, 1, nil)[0]
if tt.failure { if tt.failure {
raw = createBlock(1, unknownHash, common.Hash{}) parent := types.NewBlock(&types.Header{}, nil, nil, nil)
raw = core.GenerateChain(parent, testdb, 1, nil)[0]
} }
block := &Block{OriginPeer: id, RawBlock: raw} block := &Block{OriginPeer: id, RawBlock: raw}

View File

@ -1,7 +1,6 @@
package fetcher package fetcher
import ( import (
"encoding/binary"
"errors" "errors"
"math/big" "math/big"
"sync" "sync"
@ -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()
blockm[b.Hash()] = b
} }
return 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 {
// 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) {
// 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) {
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) {
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) {
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) {
// 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) {
// 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) {
// 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) {
// 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) {
// 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) {
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) {
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) {
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)
} }

View File

@ -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 {
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
} }

View File

@ -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()

View File

@ -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
} }

View File

@ -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
}

View File

@ -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
} }

View File

@ -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
} }

View File

@ -49,10 +49,8 @@ type uint64RingBuffer struct {
// environment is the workers current environment and holds // environment is the workers current environment and holds
// all of the current state information // all of the current state information
type environment struct { type environment struct {
totalUsedGas *big.Int // total gas usage in the cycle
state *state.StateDB // apply state changes here state *state.StateDB // apply state changes here
coinbase *state.StateObject // the miner's account coinbase *state.StateObject // the miner's account
block *types.Block // the new block
ancestors *set.Set // ancestor set (used for checking uncle parent validity) ancestors *set.Set // ancestor set (used for checking uncle parent validity)
family *set.Set // family set (used for checking uncle invalidity) family *set.Set // family set (used for checking uncle invalidity)
uncles *set.Set // uncle set uncles *set.Set // uncle set
@ -63,22 +61,12 @@ type environment struct {
ownedAccounts *set.Set ownedAccounts *set.Set
lowGasTxs types.Transactions lowGasTxs types.Transactions
localMinedBlocks *uint64RingBuffer // the most recent block numbers that were mined locally (used to check block inclusion) localMinedBlocks *uint64RingBuffer // the most recent block numbers that were mined locally (used to check block inclusion)
}
// env returns a new environment for the current cycle block *types.Block // the new block
func env(block *types.Block, eth core.Backend) *environment {
state := state.New(block.Root(), eth.StateDb())
env := &environment{
totalUsedGas: new(big.Int),
state: state,
block: block,
ancestors: set.New(),
family: set.New(),
uncles: set.New(),
coinbase: state.GetOrNewStateObject(block.Coinbase()),
}
return env header *types.Header
txs []*types.Transaction
receipts []*types.Receipt
} }
// worker is the main object which takes care of applying messages to the new state // worker is the main object which takes care of applying messages to the new state
@ -137,14 +125,20 @@ func newWorker(coinbase common.Address, eth core.Backend) *worker {
func (self *worker) pendingState() *state.StateDB { func (self *worker) pendingState() *state.StateDB {
self.currentMu.Lock() self.currentMu.Lock()
defer self.currentMu.Unlock() defer self.currentMu.Unlock()
return self.current.state return self.current.state
} }
func (self *worker) pendingBlock() *types.Block { func (self *worker) pendingBlock() *types.Block {
self.currentMu.Lock() self.currentMu.Lock()
defer self.currentMu.Unlock() defer self.currentMu.Unlock()
if atomic.LoadInt32(&self.mining) == 0 {
return types.NewBlock(
self.current.header,
self.current.txs,
nil,
self.current.receipts,
)
}
return self.current.block return self.current.block
} }
@ -206,7 +200,7 @@ out:
// Apply transaction to the pending state if we're not mining // Apply transaction to the pending state if we're not mining
if atomic.LoadInt32(&self.mining) == 0 { if atomic.LoadInt32(&self.mining) == 0 {
self.mu.Lock() self.mu.Lock()
self.commitTransactions(types.Transactions{ev.Tx}) self.current.commitTransactions(types.Transactions{ev.Tx}, self.gasPrice, self.proc)
self.mu.Unlock() self.mu.Unlock()
} }
} }
@ -239,46 +233,46 @@ func (self *worker) wait() {
continue continue
} }
if _, err := self.chain.InsertChain(types.Blocks{block}); err == nil { _, err := self.chain.WriteBlock(block)
for _, uncle := range block.Uncles() { if err != nil {
delete(self.possibleUncles, uncle.Hash()) glog.V(logger.Error).Infoln("error writing block to chain", err)
} continue
self.mux.Post(core.NewMinedBlockEvent{block})
var stale, confirm string
canonBlock := self.chain.GetBlockByNumber(block.NumberU64())
if canonBlock != nil && canonBlock.Hash() != block.Hash() {
stale = "stale "
} else {
confirm = "Wait 5 blocks for confirmation"
self.current.localMinedBlocks = newLocalMinedBlock(block.Number().Uint64(), self.current.localMinedBlocks)
}
glog.V(logger.Info).Infof("🔨 Mined %sblock (#%v / %x). %s", stale, block.Number(), block.Hash().Bytes()[:4], confirm)
jsonlogger.LogJson(&logger.EthMinerNewBlock{
BlockHash: block.Hash().Hex(),
BlockNumber: block.Number(),
ChainHeadHash: block.ParentHeaderHash.Hex(),
BlockPrevHash: block.ParentHeaderHash.Hex(),
})
} else {
self.commitNewWork()
} }
// check staleness and display confirmation
var stale, confirm string
canonBlock := self.chain.GetBlockByNumber(block.NumberU64())
if canonBlock != nil && canonBlock.Hash() != block.Hash() {
stale = "stale "
} else {
confirm = "Wait 5 blocks for confirmation"
self.current.localMinedBlocks = newLocalMinedBlock(block.Number().Uint64(), self.current.localMinedBlocks)
}
glog.V(logger.Info).Infof("🔨 Mined %sblock (#%v / %x). %s", stale, block.Number(), block.Hash().Bytes()[:4], confirm)
// broadcast before waiting for validation
go self.mux.Post(core.NewMinedBlockEvent{block})
self.commitNewWork()
} }
} }
} }
func (self *worker) push() { func (self *worker) push() {
if atomic.LoadInt32(&self.mining) == 1 { if atomic.LoadInt32(&self.mining) == 1 {
self.current.block.SetRoot(self.current.state.Root()) if core.Canary(self.current.state) {
glog.Infoln("Toxicity levels rising to deadly levels. Your canary has died. You can go back or continue down the mineshaft --more--")
glog.Infoln("You turn back and abort mining")
return
}
// push new work to agents // push new work to agents
for _, agent := range self.agents { for _, agent := range self.agents {
atomic.AddInt32(&self.atWork, 1) atomic.AddInt32(&self.atWork, 1)
if agent.Work() != nil { if agent.Work() != nil {
agent.Work() <- self.current.block.Copy() agent.Work() <- self.current.block
} else { } else {
common.Report(fmt.Sprintf("%v %T\n", agent, agent)) common.Report(fmt.Sprintf("%v %T\n", agent, agent))
} }
@ -286,22 +280,20 @@ func (self *worker) push() {
} }
} }
func (self *worker) makeCurrent() { // makeCurrent creates a new environment for the current cycle.
block := self.chain.NewBlock(self.coinbase) func (self *worker) makeCurrent(parent *types.Block, header *types.Header) {
parent := self.chain.GetBlock(block.ParentHash()) state := state.New(parent.Root(), self.eth.StateDb())
// TMP fix for build server ... current := &environment{
if parent == nil { state: state,
return ancestors: set.New(),
family: set.New(),
uncles: set.New(),
header: header,
coinbase: state.GetOrNewStateObject(self.coinbase),
} }
if block.Time() <= parent.Time() {
block.Header().Time = parent.Header().Time + 1
}
block.Header().Extra = self.extra
// when 08 is processed ancestors contain 07 (quick block) // when 08 is processed ancestors contain 07 (quick block)
current := env(block, self.eth) for _, ancestor := range self.chain.GetBlocksFromHash(parent.Hash(), 7) {
for _, ancestor := range self.chain.GetAncestors(block, 7) {
for _, uncle := range ancestor.Uncles() { for _, uncle := range ancestor.Uncles() {
current.family.Add(uncle.Hash()) current.family.Add(uncle.Hash())
} }
@ -309,6 +301,7 @@ func (self *worker) makeCurrent() {
current.ancestors.Add(ancestor.Hash()) current.ancestors.Add(ancestor.Hash())
} }
accounts, _ := self.eth.AccountManager().Accounts() accounts, _ := self.eth.AccountManager().Accounts()
// Keep track of transactions which return errors so they can be removed // Keep track of transactions which return errors so they can be removed
current.remove = set.New() current.remove = set.New()
current.tcount = 0 current.tcount = 0
@ -318,9 +311,6 @@ func (self *worker) makeCurrent() {
if self.current != nil { if self.current != nil {
current.localMinedBlocks = self.current.localMinedBlocks current.localMinedBlocks = self.current.localMinedBlocks
} }
current.coinbase.SetGasLimit(core.CalcGasLimit(parent))
self.current = current self.current = current
} }
@ -352,13 +342,13 @@ func (self *worker) isBlockLocallyMined(deepBlockNum uint64) bool {
//Does the block at {deepBlockNum} send earnings to my coinbase? //Does the block at {deepBlockNum} send earnings to my coinbase?
var block = self.chain.GetBlockByNumber(deepBlockNum) var block = self.chain.GetBlockByNumber(deepBlockNum)
return block != nil && block.Header().Coinbase == self.coinbase return block != nil && block.Coinbase() == self.coinbase
} }
func (self *worker) logLocalMinedBlocks(previous *environment) { func (self *worker) logLocalMinedBlocks(previous *environment) {
if previous != nil && self.current.localMinedBlocks != nil { if previous != nil && self.current.localMinedBlocks != nil {
nextBlockNum := self.current.block.Number().Uint64() nextBlockNum := self.current.block.NumberU64()
for checkBlockNum := previous.block.Number().Uint64(); checkBlockNum < nextBlockNum; checkBlockNum++ { for checkBlockNum := previous.block.NumberU64(); checkBlockNum < nextBlockNum; checkBlockNum++ {
inspectBlockNum := checkBlockNum - miningLogAtDepth inspectBlockNum := checkBlockNum - miningLogAtDepth
if self.isBlockLocallyMined(inspectBlockNum) { if self.isBlockLocallyMined(inspectBlockNum) {
glog.V(logger.Info).Infof("🔨 🔗 Mined %d blocks back: block #%v", miningLogAtDepth, inspectBlockNum) glog.V(logger.Info).Infof("🔨 🔗 Mined %d blocks back: block #%v", miningLogAtDepth, inspectBlockNum)
@ -376,18 +366,42 @@ func (self *worker) commitNewWork() {
defer self.currentMu.Unlock() defer self.currentMu.Unlock()
tstart := time.Now() tstart := time.Now()
parent := self.chain.CurrentBlock()
tstamp := tstart.Unix()
if tstamp <= parent.Time() {
tstamp = parent.Time() + 1
}
// this will ensure we're not going off too far in the future
if now := time.Now().Unix(); tstamp > now+4 {
wait := time.Duration(tstamp-now) * time.Second
glog.V(logger.Info).Infoln("We are too far in the future. Waiting for", wait)
time.Sleep(wait)
}
num := parent.Number()
header := &types.Header{
ParentHash: parent.Hash(),
Number: num.Add(num, common.Big1),
Difficulty: core.CalcDifficulty(tstamp, parent.Time(), parent.Difficulty()),
GasLimit: core.CalcGasLimit(parent),
GasUsed: new(big.Int),
Coinbase: self.coinbase,
Extra: self.extra,
Time: uint64(tstamp),
}
previous := self.current previous := self.current
self.makeCurrent() self.makeCurrent(parent, header)
current := self.current current := self.current
// commit transactions for this run.
transactions := self.eth.TxPool().GetTransactions() transactions := self.eth.TxPool().GetTransactions()
sort.Sort(types.TxByNonce{transactions}) sort.Sort(types.TxByNonce{transactions})
current.coinbase.SetGasLimit(header.GasLimit)
// commit transactions for this run current.commitTransactions(transactions, self.gasPrice, self.proc)
self.commitTransactions(transactions)
self.eth.TxPool().RemoveTransactions(current.lowGasTxs) self.eth.TxPool().RemoveTransactions(current.lowGasTxs)
// compute uncles for the new block.
var ( var (
uncles []*types.Header uncles []*types.Header
badUncles []common.Hash badUncles []common.Hash
@ -396,88 +410,80 @@ func (self *worker) commitNewWork() {
if len(uncles) == 2 { if len(uncles) == 2 {
break break
} }
if err := self.commitUncle(uncle.Header()); err != nil { if err := self.commitUncle(uncle.Header()); err != nil {
if glog.V(logger.Ridiculousness) { if glog.V(logger.Ridiculousness) {
glog.V(logger.Detail).Infof("Bad uncle found and will be removed (%x)\n", hash[:4]) glog.V(logger.Detail).Infof("Bad uncle found and will be removed (%x)\n", hash[:4])
glog.V(logger.Detail).Infoln(uncle) glog.V(logger.Detail).Infoln(uncle)
} }
badUncles = append(badUncles, hash) badUncles = append(badUncles, hash)
} else { } else {
glog.V(logger.Debug).Infof("commiting %x as uncle\n", hash[:4]) glog.V(logger.Debug).Infof("commiting %x as uncle\n", hash[:4])
uncles = append(uncles, uncle.Header()) uncles = append(uncles, uncle.Header())
} }
} }
for _, hash := range badUncles {
delete(self.possibleUncles, hash)
}
// We only care about logging if we're actually mining if atomic.LoadInt32(&self.mining) == 1 {
// commit state root after all state transitions.
core.AccumulateRewards(self.current.state, header, uncles)
current.state.Update()
self.current.state.Sync()
header.Root = current.state.Root()
}
// create the new block whose nonce will be mined.
current.block = types.NewBlock(header, current.txs, uncles, current.receipts)
self.current.block.Td = new(big.Int).Set(core.CalcTD(self.current.block, self.chain.GetBlock(self.current.block.ParentHash())))
// We only care about logging if we're actually mining.
if atomic.LoadInt32(&self.mining) == 1 { if atomic.LoadInt32(&self.mining) == 1 {
glog.V(logger.Info).Infof("commit new work on block %v with %d txs & %d uncles. Took %v\n", current.block.Number(), current.tcount, len(uncles), time.Since(tstart)) glog.V(logger.Info).Infof("commit new work on block %v with %d txs & %d uncles. Took %v\n", current.block.Number(), current.tcount, len(uncles), time.Since(tstart))
self.logLocalMinedBlocks(previous) self.logLocalMinedBlocks(previous)
} }
for _, hash := range badUncles {
delete(self.possibleUncles, hash)
}
self.current.block.SetUncles(uncles)
core.AccumulateRewards(self.current.state, self.current.block)
self.current.state.Update()
self.push() self.push()
} }
var (
inclusionReward = new(big.Int).Div(core.BlockReward, big.NewInt(32))
_uncleReward = new(big.Int).Mul(core.BlockReward, big.NewInt(15))
uncleReward = new(big.Int).Div(_uncleReward, big.NewInt(16))
)
func (self *worker) commitUncle(uncle *types.Header) error { func (self *worker) commitUncle(uncle *types.Header) error {
if self.current.uncles.Has(uncle.Hash()) { hash := uncle.Hash()
// Error not unique if self.current.uncles.Has(hash) {
return core.UncleError("Uncle not unique") return core.UncleError("Uncle not unique")
} }
if !self.current.ancestors.Has(uncle.ParentHash) { if !self.current.ancestors.Has(uncle.ParentHash) {
return core.UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4])) return core.UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4]))
} }
if self.current.family.Has(hash) {
if self.current.family.Has(uncle.Hash()) { return core.UncleError(fmt.Sprintf("Uncle already in family (%x)", hash))
return core.UncleError(fmt.Sprintf("Uncle already in family (%x)", uncle.Hash()))
} }
self.current.uncles.Add(uncle.Hash()) self.current.uncles.Add(uncle.Hash())
return nil return nil
} }
func (self *worker) commitTransactions(transactions types.Transactions) { func (env *environment) commitTransactions(transactions types.Transactions, gasPrice *big.Int, proc *core.BlockProcessor) {
current := self.current
for _, tx := range transactions { for _, tx := range transactions {
// We can skip err. It has already been validated in the tx pool // We can skip err. It has already been validated in the tx pool
from, _ := tx.From() from, _ := tx.From()
// Check if it falls within margin. Txs from owned accounts are always processed. // Check if it falls within margin. Txs from owned accounts are always processed.
if tx.GasPrice().Cmp(self.gasPrice) < 0 && !current.ownedAccounts.Has(from) { if tx.GasPrice().Cmp(gasPrice) < 0 && !env.ownedAccounts.Has(from) {
// ignore the transaction and transactor. We ignore the transactor // ignore the transaction and transactor. We ignore the transactor
// because nonce will fail after ignoring this transaction so there's // because nonce will fail after ignoring this transaction so there's
// no point // no point
current.lowGasTransactors.Add(from) env.lowGasTransactors.Add(from)
glog.V(logger.Info).Infof("transaction(%x) below gas price (tx=%v ask=%v). All sequential txs from this address(%x) will be ignored\n", tx.Hash().Bytes()[:4], common.CurrencyToString(tx.GasPrice()), common.CurrencyToString(self.gasPrice), from[:4]) glog.V(logger.Info).Infof("transaction(%x) below gas price (tx=%v ask=%v). All sequential txs from this address(%x) will be ignored\n", tx.Hash().Bytes()[:4], common.CurrencyToString(tx.GasPrice()), common.CurrencyToString(gasPrice), from[:4])
} }
// Continue with the next transaction if the transaction sender is included in // Continue with the next transaction if the transaction sender is included in
// the low gas tx set. This will also remove the tx and all sequential transaction // the low gas tx set. This will also remove the tx and all sequential transaction
// from this transactor // from this transactor
if current.lowGasTransactors.Has(from) { if env.lowGasTransactors.Has(from) {
// add tx to the low gas set. This will be removed at the end of the run // add tx to the low gas set. This will be removed at the end of the run
// owned accounts are ignored // owned accounts are ignored
if !current.ownedAccounts.Has(from) { if !env.ownedAccounts.Has(from) {
current.lowGasTxs = append(current.lowGasTxs, tx) env.lowGasTxs = append(env.lowGasTxs, tx)
} }
continue continue
} }
@ -487,46 +493,41 @@ func (self *worker) commitTransactions(transactions types.Transactions) {
// the transaction is processed (that could potentially be included in the block) it // the transaction is processed (that could potentially be included in the block) it
// will throw a nonce error because the previous transaction hasn't been processed. // will throw a nonce error because the previous transaction hasn't been processed.
// Therefor we need to ignore any transaction after the ignored one. // Therefor we need to ignore any transaction after the ignored one.
if current.ignoredTransactors.Has(from) { if env.ignoredTransactors.Has(from) {
continue continue
} }
self.current.state.StartRecord(tx.Hash(), common.Hash{}, 0) env.state.StartRecord(tx.Hash(), common.Hash{}, 0)
err := self.commitTransaction(tx) err := env.commitTransaction(tx, proc)
switch { switch {
case core.IsNonceErr(err) || core.IsInvalidTxErr(err): case core.IsNonceErr(err) || core.IsInvalidTxErr(err):
current.remove.Add(tx.Hash()) env.remove.Add(tx.Hash())
if glog.V(logger.Detail) { if glog.V(logger.Detail) {
glog.Infof("TX (%x) failed, will be removed: %v\n", tx.Hash().Bytes()[:4], err) glog.Infof("TX (%x) failed, will be removed: %v\n", tx.Hash().Bytes()[:4], err)
} }
case state.IsGasLimitErr(err): case state.IsGasLimitErr(err):
from, _ := tx.From()
// ignore the transactor so no nonce errors will be thrown for this account // ignore the transactor so no nonce errors will be thrown for this account
// next time the worker is run, they'll be picked up again. // next time the worker is run, they'll be picked up again.
current.ignoredTransactors.Add(from) env.ignoredTransactors.Add(from)
glog.V(logger.Detail).Infof("Gas limit reached for (%x) in this block. Continue to try smaller txs\n", from[:4]) glog.V(logger.Detail).Infof("Gas limit reached for (%x) in this block. Continue to try smaller txs\n", from[:4])
default: default:
current.tcount++ env.tcount++
} }
} }
self.current.block.Header().GasUsed = self.current.totalUsedGas
} }
func (self *worker) commitTransaction(tx *types.Transaction) error { func (env *environment) commitTransaction(tx *types.Transaction, proc *core.BlockProcessor) error {
snap := self.current.state.Copy() snap := env.state.Copy()
receipt, _, err := self.proc.ApplyTransaction(self.current.coinbase, self.current.state, self.current.block, tx, self.current.totalUsedGas, true) receipt, _, err := proc.ApplyTransaction(env.coinbase, env.state, env.header, tx, env.header.GasUsed, true)
if err != nil && (core.IsNonceErr(err) || state.IsGasLimitErr(err) || core.IsInvalidTxErr(err)) { if err != nil && (core.IsNonceErr(err) || state.IsGasLimitErr(err) || core.IsInvalidTxErr(err)) {
self.current.state.Set(snap) env.state.Set(snap)
return err return err
} }
env.txs = append(env.txs, tx)
self.current.block.AddTransaction(tx) env.receipts = append(env.receipts, receipt)
self.current.block.AddReceipt(receipt)
return nil return nil
} }

View File

@ -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 {
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 {
// 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 {
// 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) {
// //
// 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 {
} }
} }
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 {
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) {
// 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

View File

@ -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"},

View File

@ -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
} }

View File

@ -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)
} }

View File

@ -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 {
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 {
} }
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 {
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 {
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
} }

View File

@ -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

View File

@ -1,6 +1,11 @@
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 {
} }
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 {
} }
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 {

View File

@ -209,8 +209,8 @@ func (self *XEth) AtStateNum(num int64) *XEth {
// - could be removed in favour of mining on testdag (natspec e2e + networking) // - could be removed in favour of mining on testdag (natspec e2e + networking)
// + filters // + filters
func (self *XEth) ApplyTestTxs(statedb *state.StateDB, address common.Address, txc uint64) (uint64, *XEth) { func (self *XEth) ApplyTestTxs(statedb *state.StateDB, address common.Address, txc uint64) (uint64, *XEth) {
chain := self.backend.ChainManager()
block := self.backend.ChainManager().NewBlock(address) header := chain.CurrentBlock().Header()
coinbase := statedb.GetStateObject(address) coinbase := statedb.GetStateObject(address)
coinbase.SetGasLimit(big.NewInt(10000000)) coinbase.SetGasLimit(big.NewInt(10000000))
txs := self.backend.TxPool().GetQueuedTransactions() txs := self.backend.TxPool().GetQueuedTransactions()
@ -218,7 +218,7 @@ func (self *XEth) ApplyTestTxs(statedb *state.StateDB, address common.Address, t
for i := 0; i < len(txs); i++ { for i := 0; i < len(txs); i++ {
for _, tx := range txs { for _, tx := range txs {
if tx.Nonce() == txc { if tx.Nonce() == txc {
_, _, err := core.ApplyMessage(core.NewEnv(statedb, self.backend.ChainManager(), tx, block), tx, coinbase) _, _, err := core.ApplyMessage(core.NewEnv(statedb, self.backend.ChainManager(), tx, header), tx, coinbase)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -845,8 +845,8 @@ func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr st
msg.gasPrice = self.DefaultGasPrice() msg.gasPrice = self.DefaultGasPrice()
} }
block := self.CurrentBlock() header := self.CurrentBlock().Header()
vmenv := core.NewEnv(statedb, self.backend.ChainManager(), msg, block) vmenv := core.NewEnv(statedb, self.backend.ChainManager(), msg, header)
res, gas, err := core.ApplyMessage(vmenv, msg, from) res, gas, err := core.ApplyMessage(vmenv, msg, from)
return common.ToHex(res), gas.String(), err return common.ToHex(res), gas.String(), err
@ -946,51 +946,45 @@ func (self *XEth) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceS
// TODO: align default values to have the same type, e.g. not depend on // TODO: align default values to have the same type, e.g. not depend on
// common.Value conversions later on // common.Value conversions later on
var tx *types.Transaction
if contractCreation {
tx = types.NewContractCreationTx(value, gas, price, data)
} else {
tx = types.NewTransactionMessage(to, value, gas, price, data)
}
state := self.backend.TxPool().State()
var nonce uint64 var nonce uint64
if len(nonceStr) != 0 { if len(nonceStr) != 0 {
nonce = common.Big(nonceStr).Uint64() nonce = common.Big(nonceStr).Uint64()
} else { } else {
state := self.backend.TxPool().State()
nonce = state.GetNonce(from) nonce = state.GetNonce(from)
} }
tx.SetNonce(nonce) var tx *types.Transaction
if contractCreation {
tx = types.NewContractCreation(nonce, value, gas, price, data)
} else {
tx = types.NewTransaction(nonce, to, value, gas, price, data)
}
if err := self.sign(tx, from, false); err != nil { signed, err := self.sign(tx, from, false)
if err != nil {
return "", err return "", err
} }
if err := self.backend.TxPool().Add(tx); err != nil { if err = self.backend.TxPool().Add(signed); err != nil {
return "", err return "", err
} }
//state.SetNonce(from, nonce+1)
if contractCreation { if contractCreation {
addr := core.AddressFromMessage(tx) addr := core.AddressFromMessage(tx)
glog.V(logger.Info).Infof("Tx(%x) created: %x\n", tx.Hash(), addr) glog.V(logger.Info).Infof("Tx(%x) created: %x\n", tx.Hash(), addr)
return addr.Hex(), nil
return core.AddressFromMessage(tx).Hex(), nil
} else { } else {
glog.V(logger.Info).Infof("Tx(%x) to: %x\n", tx.Hash(), tx.To()) glog.V(logger.Info).Infof("Tx(%x) to: %x\n", tx.Hash(), tx.To())
} }
return tx.Hash().Hex(), nil return tx.Hash().Hex(), nil
} }
func (self *XEth) sign(tx *types.Transaction, from common.Address, didUnlock bool) error { func (self *XEth) sign(tx *types.Transaction, from common.Address, didUnlock bool) (*types.Transaction, error) {
hash := tx.Hash() hash := tx.Hash()
sig, err := self.doSign(from, hash, didUnlock) sig, err := self.doSign(from, hash, didUnlock)
if err != nil { if err != nil {
return err return tx, err
} }
tx.SetSignatureValues(sig) return tx.WithSignature(sig)
return nil
} }
// callmsg is the message type used for call transations. // callmsg is the message type used for call transations.