This commit is contained in:
Felföldi Zsolt 2015-11-18 18:53:28 +00:00
commit 1101763a49
64 changed files with 2634 additions and 335 deletions

View file

@ -0,0 +1,447 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package context defines the Context type, which carries deadlines,
// cancelation signals, and other request-scoped values across API boundaries
// and between processes.
//
// Incoming requests to a server should create a Context, and outgoing calls to
// servers should accept a Context. The chain of function calls between must
// propagate the Context, optionally replacing it with a modified copy created
// using WithDeadline, WithTimeout, WithCancel, or WithValue.
//
// Programs that use Contexts should follow these rules to keep interfaces
// consistent across packages and enable static analysis tools to check context
// propagation:
//
// Do not store Contexts inside a struct type; instead, pass a Context
// explicitly to each function that needs it. The Context should be the first
// parameter, typically named ctx:
//
// func DoSomething(ctx context.Context, arg Arg) error {
// // ... use ctx ...
// }
//
// Do not pass a nil Context, even if a function permits it. Pass context.TODO
// if you are unsure about which Context to use.
//
// Use context Values only for request-scoped data that transits processes and
// APIs, not for passing optional parameters to functions.
//
// The same Context may be passed to functions running in different goroutines;
// Contexts are safe for simultaneous use by multiple goroutines.
//
// See http://blog.golang.org/context for example code for a server that uses
// Contexts.
package context // import "golang.org/x/net/context"
import (
"errors"
"fmt"
"sync"
"time"
)
// A Context carries a deadline, a cancelation signal, and other values across
// API boundaries.
//
// Context's methods may be called by multiple goroutines simultaneously.
type Context interface {
// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
Deadline() (deadline time.Time, ok bool)
// Done returns a channel that's closed when work done on behalf of this
// context should be canceled. Done may return nil if this context can
// never be canceled. Successive calls to Done return the same value.
//
// WithCancel arranges for Done to be closed when cancel is called;
// WithDeadline arranges for Done to be closed when the deadline
// expires; WithTimeout arranges for Done to be closed when the timeout
// elapses.
//
// Done is provided for use in select statements:
//
// // Stream generates values with DoSomething and sends them to out
// // until DoSomething returns an error or ctx.Done is closed.
// func Stream(ctx context.Context, out <-chan Value) error {
// for {
// v, err := DoSomething(ctx)
// if err != nil {
// return err
// }
// select {
// case <-ctx.Done():
// return ctx.Err()
// case out <- v:
// }
// }
// }
//
// See http://blog.golang.org/pipelines for more examples of how to use
// a Done channel for cancelation.
Done() <-chan struct{}
// Err returns a non-nil error value after Done is closed. Err returns
// Canceled if the context was canceled or DeadlineExceeded if the
// context's deadline passed. No other values for Err are defined.
// After Done is closed, successive calls to Err return the same value.
Err() error
// Value returns the value associated with this context for key, or nil
// if no value is associated with key. Successive calls to Value with
// the same key returns the same result.
//
// Use context values only for request-scoped data that transits
// processes and API boundaries, not for passing optional parameters to
// functions.
//
// A key identifies a specific value in a Context. Functions that wish
// to store values in Context typically allocate a key in a global
// variable then use that key as the argument to context.WithValue and
// Context.Value. A key can be any type that supports equality;
// packages should define keys as an unexported type to avoid
// collisions.
//
// Packages that define a Context key should provide type-safe accessors
// for the values stores using that key:
//
// // Package user defines a User type that's stored in Contexts.
// package user
//
// import "golang.org/x/net/context"
//
// // User is the type of value stored in the Contexts.
// type User struct {...}
//
// // key is an unexported type for keys defined in this package.
// // This prevents collisions with keys defined in other packages.
// type key int
//
// // userKey is the key for user.User values in Contexts. It is
// // unexported; clients use user.NewContext and user.FromContext
// // instead of using this key directly.
// var userKey key = 0
//
// // NewContext returns a new Context that carries value u.
// func NewContext(ctx context.Context, u *User) context.Context {
// return context.WithValue(ctx, userKey, u)
// }
//
// // FromContext returns the User value stored in ctx, if any.
// func FromContext(ctx context.Context) (*User, bool) {
// u, ok := ctx.Value(userKey).(*User)
// return u, ok
// }
Value(key interface{}) interface{}
}
// Canceled is the error returned by Context.Err when the context is canceled.
var Canceled = errors.New("context canceled")
// DeadlineExceeded is the error returned by Context.Err when the context's
// deadline passes.
var DeadlineExceeded = errors.New("context deadline exceeded")
// An emptyCtx is never canceled, has no values, and has no deadline. It is not
// struct{}, since vars of this type must have distinct addresses.
type emptyCtx int
func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
return
}
func (*emptyCtx) Done() <-chan struct{} {
return nil
}
func (*emptyCtx) Err() error {
return nil
}
func (*emptyCtx) Value(key interface{}) interface{} {
return nil
}
func (e *emptyCtx) String() string {
switch e {
case background:
return "context.Background"
case todo:
return "context.TODO"
}
return "unknown empty Context"
}
var (
background = new(emptyCtx)
todo = new(emptyCtx)
)
// Background returns a non-nil, empty Context. It is never canceled, has no
// values, and has no deadline. It is typically used by the main function,
// initialization, and tests, and as the top-level Context for incoming
// requests.
func Background() Context {
return background
}
// TODO returns a non-nil, empty Context. Code should use context.TODO when
// it's unclear which Context to use or it's is not yet available (because the
// surrounding function has not yet been extended to accept a Context
// parameter). TODO is recognized by static analysis tools that determine
// whether Contexts are propagated correctly in a program.
func TODO() Context {
return todo
}
// A CancelFunc tells an operation to abandon its work.
// A CancelFunc does not wait for the work to stop.
// After the first call, subsequent calls to a CancelFunc do nothing.
type CancelFunc func()
// WithCancel returns a copy of parent with a new Done channel. The returned
// context's Done channel is closed when the returned cancel function is called
// or when the parent context's Done channel is closed, whichever happens first.
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete.
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
c := newCancelCtx(parent)
propagateCancel(parent, &c)
return &c, func() { c.cancel(true, Canceled) }
}
// newCancelCtx returns an initialized cancelCtx.
func newCancelCtx(parent Context) cancelCtx {
return cancelCtx{
Context: parent,
done: make(chan struct{}),
}
}
// propagateCancel arranges for child to be canceled when parent is.
func propagateCancel(parent Context, child canceler) {
if parent.Done() == nil {
return // parent is never canceled
}
if p, ok := parentCancelCtx(parent); ok {
p.mu.Lock()
if p.err != nil {
// parent has already been canceled
child.cancel(false, p.err)
} else {
if p.children == nil {
p.children = make(map[canceler]bool)
}
p.children[child] = true
}
p.mu.Unlock()
} else {
go func() {
select {
case <-parent.Done():
child.cancel(false, parent.Err())
case <-child.Done():
}
}()
}
}
// parentCancelCtx follows a chain of parent references until it finds a
// *cancelCtx. This function understands how each of the concrete types in this
// package represents its parent.
func parentCancelCtx(parent Context) (*cancelCtx, bool) {
for {
switch c := parent.(type) {
case *cancelCtx:
return c, true
case *timerCtx:
return &c.cancelCtx, true
case *valueCtx:
parent = c.Context
default:
return nil, false
}
}
}
// removeChild removes a context from its parent.
func removeChild(parent Context, child canceler) {
p, ok := parentCancelCtx(parent)
if !ok {
return
}
p.mu.Lock()
if p.children != nil {
delete(p.children, child)
}
p.mu.Unlock()
}
// A canceler is a context type that can be canceled directly. The
// implementations are *cancelCtx and *timerCtx.
type canceler interface {
cancel(removeFromParent bool, err error)
Done() <-chan struct{}
}
// A cancelCtx can be canceled. When canceled, it also cancels any children
// that implement canceler.
type cancelCtx struct {
Context
done chan struct{} // closed by the first cancel call.
mu sync.Mutex
children map[canceler]bool // set to nil by the first cancel call
err error // set to non-nil by the first cancel call
}
func (c *cancelCtx) Done() <-chan struct{} {
return c.done
}
func (c *cancelCtx) Err() error {
c.mu.Lock()
defer c.mu.Unlock()
return c.err
}
func (c *cancelCtx) String() string {
return fmt.Sprintf("%v.WithCancel", c.Context)
}
// cancel closes c.done, cancels each of c's children, and, if
// removeFromParent is true, removes c from its parent's children.
func (c *cancelCtx) cancel(removeFromParent bool, err error) {
if err == nil {
panic("context: internal error: missing cancel error")
}
c.mu.Lock()
if c.err != nil {
c.mu.Unlock()
return // already canceled
}
c.err = err
close(c.done)
for child := range c.children {
// NOTE: acquiring the child's lock while holding parent's lock.
child.cancel(false, err)
}
c.children = nil
c.mu.Unlock()
if removeFromParent {
removeChild(c.Context, c)
}
}
// WithDeadline returns a copy of the parent context with the deadline adjusted
// to be no later than d. If the parent's deadline is already earlier than d,
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
// context's Done channel is closed when the deadline expires, when the returned
// cancel function is called, or when the parent context's Done channel is
// closed, whichever happens first.
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete.
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
if cur, ok := parent.Deadline(); ok && cur.Before(deadline) {
// The current deadline is already sooner than the new one.
return WithCancel(parent)
}
c := &timerCtx{
cancelCtx: newCancelCtx(parent),
deadline: deadline,
}
propagateCancel(parent, c)
d := deadline.Sub(time.Now())
if d <= 0 {
c.cancel(true, DeadlineExceeded) // deadline has already passed
return c, func() { c.cancel(true, Canceled) }
}
c.mu.Lock()
defer c.mu.Unlock()
if c.err == nil {
c.timer = time.AfterFunc(d, func() {
c.cancel(true, DeadlineExceeded)
})
}
return c, func() { c.cancel(true, Canceled) }
}
// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
// implement Done and Err. It implements cancel by stopping its timer then
// delegating to cancelCtx.cancel.
type timerCtx struct {
cancelCtx
timer *time.Timer // Under cancelCtx.mu.
deadline time.Time
}
func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {
return c.deadline, true
}
func (c *timerCtx) String() string {
return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now()))
}
func (c *timerCtx) cancel(removeFromParent bool, err error) {
c.cancelCtx.cancel(false, err)
if removeFromParent {
// Remove this timerCtx from its parent cancelCtx's children.
removeChild(c.cancelCtx.Context, c)
}
c.mu.Lock()
if c.timer != nil {
c.timer.Stop()
c.timer = nil
}
c.mu.Unlock()
}
// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete:
//
// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
// defer cancel() // releases resources if slowOperation completes before timeout elapses
// return slowOperation(ctx)
// }
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
return WithDeadline(parent, time.Now().Add(timeout))
}
// WithValue returns a copy of parent in which the value associated with key is
// val.
//
// Use context Values only for request-scoped data that transits processes and
// APIs, not for passing optional parameters to functions.
func WithValue(parent Context, key interface{}, val interface{}) Context {
return &valueCtx{parent, key, val}
}
// A valueCtx carries a key-value pair. It implements Value for that key and
// delegates all other calls to the embedded Context.
type valueCtx struct {
Context
key, val interface{}
}
func (c *valueCtx) String() string {
return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val)
}
func (c *valueCtx) Value(key interface{}) interface{} {
if c.key == key {
return c.val
}
return c.Context.Value(key)
}

View file

@ -0,0 +1,575 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package context
import (
"fmt"
"math/rand"
"runtime"
"strings"
"sync"
"testing"
"time"
)
// otherContext is a Context that's not one of the types defined in context.go.
// This lets us test code paths that differ based on the underlying type of the
// Context.
type otherContext struct {
Context
}
func TestBackground(t *testing.T) {
c := Background()
if c == nil {
t.Fatalf("Background returned nil")
}
select {
case x := <-c.Done():
t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
default:
}
if got, want := fmt.Sprint(c), "context.Background"; got != want {
t.Errorf("Background().String() = %q want %q", got, want)
}
}
func TestTODO(t *testing.T) {
c := TODO()
if c == nil {
t.Fatalf("TODO returned nil")
}
select {
case x := <-c.Done():
t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
default:
}
if got, want := fmt.Sprint(c), "context.TODO"; got != want {
t.Errorf("TODO().String() = %q want %q", got, want)
}
}
func TestWithCancel(t *testing.T) {
c1, cancel := WithCancel(Background())
if got, want := fmt.Sprint(c1), "context.Background.WithCancel"; got != want {
t.Errorf("c1.String() = %q want %q", got, want)
}
o := otherContext{c1}
c2, _ := WithCancel(o)
contexts := []Context{c1, o, c2}
for i, c := range contexts {
if d := c.Done(); d == nil {
t.Errorf("c[%d].Done() == %v want non-nil", i, d)
}
if e := c.Err(); e != nil {
t.Errorf("c[%d].Err() == %v want nil", i, e)
}
select {
case x := <-c.Done():
t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
default:
}
}
cancel()
time.Sleep(100 * time.Millisecond) // let cancelation propagate
for i, c := range contexts {
select {
case <-c.Done():
default:
t.Errorf("<-c[%d].Done() blocked, but shouldn't have", i)
}
if e := c.Err(); e != Canceled {
t.Errorf("c[%d].Err() == %v want %v", i, e, Canceled)
}
}
}
func TestParentFinishesChild(t *testing.T) {
// Context tree:
// parent -> cancelChild
// parent -> valueChild -> timerChild
parent, cancel := WithCancel(Background())
cancelChild, stop := WithCancel(parent)
defer stop()
valueChild := WithValue(parent, "key", "value")
timerChild, stop := WithTimeout(valueChild, 10000*time.Hour)
defer stop()
select {
case x := <-parent.Done():
t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
case x := <-cancelChild.Done():
t.Errorf("<-cancelChild.Done() == %v want nothing (it should block)", x)
case x := <-timerChild.Done():
t.Errorf("<-timerChild.Done() == %v want nothing (it should block)", x)
case x := <-valueChild.Done():
t.Errorf("<-valueChild.Done() == %v want nothing (it should block)", x)
default:
}
// The parent's children should contain the two cancelable children.
pc := parent.(*cancelCtx)
cc := cancelChild.(*cancelCtx)
tc := timerChild.(*timerCtx)
pc.mu.Lock()
if len(pc.children) != 2 || !pc.children[cc] || !pc.children[tc] {
t.Errorf("bad linkage: pc.children = %v, want %v and %v",
pc.children, cc, tc)
}
pc.mu.Unlock()
if p, ok := parentCancelCtx(cc.Context); !ok || p != pc {
t.Errorf("bad linkage: parentCancelCtx(cancelChild.Context) = %v, %v want %v, true", p, ok, pc)
}
if p, ok := parentCancelCtx(tc.Context); !ok || p != pc {
t.Errorf("bad linkage: parentCancelCtx(timerChild.Context) = %v, %v want %v, true", p, ok, pc)
}
cancel()
pc.mu.Lock()
if len(pc.children) != 0 {
t.Errorf("pc.cancel didn't clear pc.children = %v", pc.children)
}
pc.mu.Unlock()
// parent and children should all be finished.
check := func(ctx Context, name string) {
select {
case <-ctx.Done():
default:
t.Errorf("<-%s.Done() blocked, but shouldn't have", name)
}
if e := ctx.Err(); e != Canceled {
t.Errorf("%s.Err() == %v want %v", name, e, Canceled)
}
}
check(parent, "parent")
check(cancelChild, "cancelChild")
check(valueChild, "valueChild")
check(timerChild, "timerChild")
// WithCancel should return a canceled context on a canceled parent.
precanceledChild := WithValue(parent, "key", "value")
select {
case <-precanceledChild.Done():
default:
t.Errorf("<-precanceledChild.Done() blocked, but shouldn't have")
}
if e := precanceledChild.Err(); e != Canceled {
t.Errorf("precanceledChild.Err() == %v want %v", e, Canceled)
}
}
func TestChildFinishesFirst(t *testing.T) {
cancelable, stop := WithCancel(Background())
defer stop()
for _, parent := range []Context{Background(), cancelable} {
child, cancel := WithCancel(parent)
select {
case x := <-parent.Done():
t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
case x := <-child.Done():
t.Errorf("<-child.Done() == %v want nothing (it should block)", x)
default:
}
cc := child.(*cancelCtx)
pc, pcok := parent.(*cancelCtx) // pcok == false when parent == Background()
if p, ok := parentCancelCtx(cc.Context); ok != pcok || (ok && pc != p) {
t.Errorf("bad linkage: parentCancelCtx(cc.Context) = %v, %v want %v, %v", p, ok, pc, pcok)
}
if pcok {
pc.mu.Lock()
if len(pc.children) != 1 || !pc.children[cc] {
t.Errorf("bad linkage: pc.children = %v, cc = %v", pc.children, cc)
}
pc.mu.Unlock()
}
cancel()
if pcok {
pc.mu.Lock()
if len(pc.children) != 0 {
t.Errorf("child's cancel didn't remove self from pc.children = %v", pc.children)
}
pc.mu.Unlock()
}
// child should be finished.
select {
case <-child.Done():
default:
t.Errorf("<-child.Done() blocked, but shouldn't have")
}
if e := child.Err(); e != Canceled {
t.Errorf("child.Err() == %v want %v", e, Canceled)
}
// parent should not be finished.
select {
case x := <-parent.Done():
t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
default:
}
if e := parent.Err(); e != nil {
t.Errorf("parent.Err() == %v want nil", e)
}
}
}
func testDeadline(c Context, wait time.Duration, t *testing.T) {
select {
case <-time.After(wait):
t.Fatalf("context should have timed out")
case <-c.Done():
}
if e := c.Err(); e != DeadlineExceeded {
t.Errorf("c.Err() == %v want %v", e, DeadlineExceeded)
}
}
func TestDeadline(t *testing.T) {
c, _ := WithDeadline(Background(), time.Now().Add(100*time.Millisecond))
if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
t.Errorf("c.String() = %q want prefix %q", got, prefix)
}
testDeadline(c, 200*time.Millisecond, t)
c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond))
o := otherContext{c}
testDeadline(o, 200*time.Millisecond, t)
c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond))
o = otherContext{c}
c, _ = WithDeadline(o, time.Now().Add(300*time.Millisecond))
testDeadline(c, 200*time.Millisecond, t)
}
func TestTimeout(t *testing.T) {
c, _ := WithTimeout(Background(), 100*time.Millisecond)
if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
t.Errorf("c.String() = %q want prefix %q", got, prefix)
}
testDeadline(c, 200*time.Millisecond, t)
c, _ = WithTimeout(Background(), 100*time.Millisecond)
o := otherContext{c}
testDeadline(o, 200*time.Millisecond, t)
c, _ = WithTimeout(Background(), 100*time.Millisecond)
o = otherContext{c}
c, _ = WithTimeout(o, 300*time.Millisecond)
testDeadline(c, 200*time.Millisecond, t)
}
func TestCanceledTimeout(t *testing.T) {
c, _ := WithTimeout(Background(), 200*time.Millisecond)
o := otherContext{c}
c, cancel := WithTimeout(o, 400*time.Millisecond)
cancel()
time.Sleep(100 * time.Millisecond) // let cancelation propagate
select {
case <-c.Done():
default:
t.Errorf("<-c.Done() blocked, but shouldn't have")
}
if e := c.Err(); e != Canceled {
t.Errorf("c.Err() == %v want %v", e, Canceled)
}
}
type key1 int
type key2 int
var k1 = key1(1)
var k2 = key2(1) // same int as k1, different type
var k3 = key2(3) // same type as k2, different int
func TestValues(t *testing.T) {
check := func(c Context, nm, v1, v2, v3 string) {
if v, ok := c.Value(k1).(string); ok == (len(v1) == 0) || v != v1 {
t.Errorf(`%s.Value(k1).(string) = %q, %t want %q, %t`, nm, v, ok, v1, len(v1) != 0)
}
if v, ok := c.Value(k2).(string); ok == (len(v2) == 0) || v != v2 {
t.Errorf(`%s.Value(k2).(string) = %q, %t want %q, %t`, nm, v, ok, v2, len(v2) != 0)
}
if v, ok := c.Value(k3).(string); ok == (len(v3) == 0) || v != v3 {
t.Errorf(`%s.Value(k3).(string) = %q, %t want %q, %t`, nm, v, ok, v3, len(v3) != 0)
}
}
c0 := Background()
check(c0, "c0", "", "", "")
c1 := WithValue(Background(), k1, "c1k1")
check(c1, "c1", "c1k1", "", "")
if got, want := fmt.Sprint(c1), `context.Background.WithValue(1, "c1k1")`; got != want {
t.Errorf("c.String() = %q want %q", got, want)
}
c2 := WithValue(c1, k2, "c2k2")
check(c2, "c2", "c1k1", "c2k2", "")
c3 := WithValue(c2, k3, "c3k3")
check(c3, "c2", "c1k1", "c2k2", "c3k3")
c4 := WithValue(c3, k1, nil)
check(c4, "c4", "", "c2k2", "c3k3")
o0 := otherContext{Background()}
check(o0, "o0", "", "", "")
o1 := otherContext{WithValue(Background(), k1, "c1k1")}
check(o1, "o1", "c1k1", "", "")
o2 := WithValue(o1, k2, "o2k2")
check(o2, "o2", "c1k1", "o2k2", "")
o3 := otherContext{c4}
check(o3, "o3", "", "c2k2", "c3k3")
o4 := WithValue(o3, k3, nil)
check(o4, "o4", "", "c2k2", "")
}
func TestAllocs(t *testing.T) {
bg := Background()
for _, test := range []struct {
desc string
f func()
limit float64
gccgoLimit float64
}{
{
desc: "Background()",
f: func() { Background() },
limit: 0,
gccgoLimit: 0,
},
{
desc: fmt.Sprintf("WithValue(bg, %v, nil)", k1),
f: func() {
c := WithValue(bg, k1, nil)
c.Value(k1)
},
limit: 3,
gccgoLimit: 3,
},
{
desc: "WithTimeout(bg, 15*time.Millisecond)",
f: func() {
c, _ := WithTimeout(bg, 15*time.Millisecond)
<-c.Done()
},
limit: 8,
gccgoLimit: 13,
},
{
desc: "WithCancel(bg)",
f: func() {
c, cancel := WithCancel(bg)
cancel()
<-c.Done()
},
limit: 5,
gccgoLimit: 8,
},
{
desc: "WithTimeout(bg, 100*time.Millisecond)",
f: func() {
c, cancel := WithTimeout(bg, 100*time.Millisecond)
cancel()
<-c.Done()
},
limit: 8,
gccgoLimit: 25,
},
} {
limit := test.limit
if runtime.Compiler == "gccgo" {
// gccgo does not yet do escape analysis.
// TOOD(iant): Remove this when gccgo does do escape analysis.
limit = test.gccgoLimit
}
if n := testing.AllocsPerRun(100, test.f); n > limit {
t.Errorf("%s allocs = %f want %d", test.desc, n, int(limit))
}
}
}
func TestSimultaneousCancels(t *testing.T) {
root, cancel := WithCancel(Background())
m := map[Context]CancelFunc{root: cancel}
q := []Context{root}
// Create a tree of contexts.
for len(q) != 0 && len(m) < 100 {
parent := q[0]
q = q[1:]
for i := 0; i < 4; i++ {
ctx, cancel := WithCancel(parent)
m[ctx] = cancel
q = append(q, ctx)
}
}
// Start all the cancels in a random order.
var wg sync.WaitGroup
wg.Add(len(m))
for _, cancel := range m {
go func(cancel CancelFunc) {
cancel()
wg.Done()
}(cancel)
}
// Wait on all the contexts in a random order.
for ctx := range m {
select {
case <-ctx.Done():
case <-time.After(1 * time.Second):
buf := make([]byte, 10<<10)
n := runtime.Stack(buf, true)
t.Fatalf("timed out waiting for <-ctx.Done(); stacks:\n%s", buf[:n])
}
}
// Wait for all the cancel functions to return.
done := make(chan struct{})
go func() {
wg.Wait()
close(done)
}()
select {
case <-done:
case <-time.After(1 * time.Second):
buf := make([]byte, 10<<10)
n := runtime.Stack(buf, true)
t.Fatalf("timed out waiting for cancel functions; stacks:\n%s", buf[:n])
}
}
func TestInterlockedCancels(t *testing.T) {
parent, cancelParent := WithCancel(Background())
child, cancelChild := WithCancel(parent)
go func() {
parent.Done()
cancelChild()
}()
cancelParent()
select {
case <-child.Done():
case <-time.After(1 * time.Second):
buf := make([]byte, 10<<10)
n := runtime.Stack(buf, true)
t.Fatalf("timed out waiting for child.Done(); stacks:\n%s", buf[:n])
}
}
func TestLayersCancel(t *testing.T) {
testLayers(t, time.Now().UnixNano(), false)
}
func TestLayersTimeout(t *testing.T) {
testLayers(t, time.Now().UnixNano(), true)
}
func testLayers(t *testing.T, seed int64, testTimeout bool) {
rand.Seed(seed)
errorf := func(format string, a ...interface{}) {
t.Errorf(fmt.Sprintf("seed=%d: %s", seed, format), a...)
}
const (
timeout = 200 * time.Millisecond
minLayers = 30
)
type value int
var (
vals []*value
cancels []CancelFunc
numTimers int
ctx = Background()
)
for i := 0; i < minLayers || numTimers == 0 || len(cancels) == 0 || len(vals) == 0; i++ {
switch rand.Intn(3) {
case 0:
v := new(value)
ctx = WithValue(ctx, v, v)
vals = append(vals, v)
case 1:
var cancel CancelFunc
ctx, cancel = WithCancel(ctx)
cancels = append(cancels, cancel)
case 2:
var cancel CancelFunc
ctx, cancel = WithTimeout(ctx, timeout)
cancels = append(cancels, cancel)
numTimers++
}
}
checkValues := func(when string) {
for _, key := range vals {
if val := ctx.Value(key).(*value); key != val {
errorf("%s: ctx.Value(%p) = %p want %p", when, key, val, key)
}
}
}
select {
case <-ctx.Done():
errorf("ctx should not be canceled yet")
default:
}
if s, prefix := fmt.Sprint(ctx), "context.Background."; !strings.HasPrefix(s, prefix) {
t.Errorf("ctx.String() = %q want prefix %q", s, prefix)
}
t.Log(ctx)
checkValues("before cancel")
if testTimeout {
select {
case <-ctx.Done():
case <-time.After(timeout + timeout/10):
errorf("ctx should have timed out")
}
checkValues("after timeout")
} else {
cancel := cancels[rand.Intn(len(cancels))]
cancel()
select {
case <-ctx.Done():
default:
errorf("ctx should be canceled")
}
checkValues("after cancel")
}
}
func TestCancelRemoves(t *testing.T) {
checkChildren := func(when string, ctx Context, want int) {
if got := len(ctx.(*cancelCtx).children); got != want {
t.Errorf("%s: context has %d children, want %d", when, got, want)
}
}
ctx, _ := WithCancel(Background())
checkChildren("after creation", ctx, 0)
_, cancel := WithCancel(ctx)
checkChildren("with WithCancel child ", ctx, 1)
cancel()
checkChildren("after cancelling WithCancel child", ctx, 0)
ctx, _ = WithCancel(Background())
checkChildren("after creation", ctx, 0)
_, cancel = WithTimeout(ctx, 60*time.Minute)
checkChildren("with WithTimeout child ", ctx, 1)
cancel()
checkChildren("after cancelling WithTimeout child", ctx, 0)
}

View file

@ -0,0 +1,26 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package context_test
import (
"fmt"
"time"
"golang.org/x/net/context"
)
func ExampleWithTimeout() {
// Pass a context with a timeout to tell a blocking function that it
// should abandon its work after the timeout elapses.
ctx, _ := context.WithTimeout(context.Background(), 100*time.Millisecond)
select {
case <-time.After(200 * time.Millisecond):
fmt.Println("overslept")
case <-ctx.Done():
fmt.Println(ctx.Err()) // prints "context deadline exceeded"
}
// Output:
// context deadline exceeded
}

View file

@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"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/access"
"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/core/vm" "github.com/ethereum/go-ethereum/core/vm"
@ -113,7 +114,7 @@ func run(ctx *cli.Context) {
glog.SetV(ctx.GlobalInt(VerbosityFlag.Name)) glog.SetV(ctx.GlobalInt(VerbosityFlag.Name))
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db))
sender := statedb.CreateAccount(common.StringToAddress("sender")) sender := statedb.CreateAccount(common.StringToAddress("sender"))
receiver := statedb.CreateAccount(common.StringToAddress("receiver")) receiver := statedb.CreateAccount(common.StringToAddress("receiver"))
receiver.SetCode(common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name))) receiver.SetCode(common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name)))

View file

@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"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/access"
"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/ethdb"
@ -179,7 +180,7 @@ func dump(ctx *cli.Context) {
fmt.Println("{}") fmt.Println("{}")
utils.Fatalf("block not found") utils.Fatalf("block not found")
} else { } else {
state, err := state.New(block.Root(), chainDb) state, err := state.New(block.Root(), access.NewDbChainAccess(chainDb))
if err != nil { if err != nil {
utils.Fatalf("could not create new state: %v", err) utils.Fatalf("could not create new state: %v", err)
return return

View file

@ -34,6 +34,7 @@ import (
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"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/access"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
@ -555,12 +556,13 @@ func blockRecovery(ctx *cli.Context) {
if err != nil { if err != nil {
glog.Fatalln("could not open db:", err) glog.Fatalln("could not open db:", err)
} }
ca := access.NewDbChainAccess(blockDb)
var block *types.Block var block *types.Block
if arg[0] == '#' { if arg[0] == '#' {
block = core.GetBlock(blockDb, core.GetCanonicalHash(blockDb, common.String2Big(arg[1:]).Uint64())) block = core.GetBlock(ca, core.GetCanonicalHash(blockDb, common.String2Big(arg[1:]).Uint64()))
} else { } else {
block = core.GetBlock(blockDb, common.HexToHash(arg)) block = core.GetBlock(ca, common.HexToHash(arg))
} }
if block == nil { if block == nil {

View file

@ -34,6 +34,7 @@ import (
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"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/access"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
@ -552,12 +553,12 @@ func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database
eventMux := new(event.TypeMux) eventMux := new(event.TypeMux)
pow := ethash.New() pow := ethash.New()
//genesis := core.GenesisBlock(uint64(ctx.GlobalInt(GenesisNonceFlag.Name)), blockDB) //genesis := core.GenesisBlock(uint64(ctx.GlobalInt(GenesisNonceFlag.Name)), blockDB)
chain, err = core.NewBlockChain(chainDb, pow, eventMux) chain, err = core.NewBlockChain(access.NewDbChainAccess(chainDb), pow, eventMux)
if err != nil { if err != nil {
Fatalf("Could not start chainmanager: %v", err) Fatalf("Could not start chainmanager: %v", err)
} }
proc := core.NewBlockProcessor(chainDb, pow, chain, eventMux) proc := core.NewBlockProcessor(access.NewDbChainAccess(chainDb), pow, chain, eventMux)
chain.SetProcessor(proc) chain.SetProcessor(proc)
return chain, chainDb return chain, chainDb
} }
@ -665,4 +666,4 @@ func ParamToAddress(addr string, am *accounts.Manager) (addrHex string, err erro
addrHex = addr addrHex = addr
} }
return return
} }

219
core/access/chain_access.go Normal file
View file

@ -0,0 +1,219 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Package access provides a layer to handle local blockchain database and
// on-demand network retrieval
package access
import (
"errors"
"sync"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"golang.org/x/net/context"
)
var (
errNotInDb = errors.New("object not found in database")
)
const LogLevel = logger.Debug
var (
requestTimeout = time.Millisecond * 300
retryPeers = time.Second * 1
)
// ChainAccess provides access to blockchain and state data through local
// database and optionally also on-demand network retrieval
type ChainAccess struct {
db ethdb.Database
odr bool // light client mode, odr enabled
lock sync.Mutex
sentReqs map[uint64]*sentReq
sentReqCnt uint64
peers *peerSet
}
// requestFunc is a function that requests some data from a peer
type requestFunc func(*Peer) error
// validatorFunc is a function that processes a message and returns true if
// it was a meaningful answer to a given request
type validatorFunc func(*Msg) bool
// sentReq is a request waiting for an answer that satisfies its valFunc
type sentReq struct {
valFunc validatorFunc
deliverChan chan *Msg
}
// NewDbChainAccess creates a ChainAccess with ODR disabled
func NewDbChainAccess(db ethdb.Database) *ChainAccess {
return NewChainAccess(db, false)
}
// NewChainAccess create a ChainAccess with optional ODR
func NewChainAccess(db ethdb.Database, odr bool) *ChainAccess {
return &ChainAccess{
db: db,
peers: newPeerSet(),
sentReqs: make(map[uint64]*sentReq),
odr: odr,
}
}
// Db returns the local database assigned to the ChainAccess object
func (self *ChainAccess) Db() ethdb.Database {
return self.db
}
// OdrEnabled returns true if this ChainAccess is capable of doing ODR requests
func (self *ChainAccess) OdrEnabled() bool {
return self.odr
}
// RegisterPeer registers a new LES peer to the ODR capable peer set
func (self *ChainAccess) RegisterPeer(id string, version int, head common.Hash, getBlockBodies getBlockBodiesFn, getNodeData getNodeDataFn, getReceipts getReceiptsFn, getProofs getProofsFn) error {
glog.V(logger.Detail).Infoln("Registering peer", id)
if err := self.peers.Register(newPeer(id, version, head, getBlockBodies, getNodeData, getReceipts, getProofs)); err != nil {
glog.V(logger.Error).Infoln("Register failed:", err)
return err
}
return nil
}
// UnregisterPeer removes a peer from the ODR capable peer set
func (self *ChainAccess) UnregisterPeer(id string) {
self.peers.Unregister(id)
}
const (
MsgBlockBodies = iota
MsgNodeData
MsgReceipts
MsgProofs
)
// Msg encodes a LES message that delivers reply data for a request
type Msg struct {
MsgType int
Obj interface{}
}
// ObjectAccess is the ODR request interface (passed to Retrieve, functions called by Retrieve and Deliver)
// DbGet() tries to retrieve the object from the local database (object is stored by the request struct in memory if retrieved)
// DbPut() stores it in the local database
// Request(*Peer) requests it from a LES peer
// Valid(*Msg) checks if a message is a valid answer to this request and stores the retrieved object in memory
type ObjectAccess interface {
// database storage
DbGet() bool
DbPut()
// network retrieval
Request(*Peer) error
Valid(*Msg) bool // if true, keeps the retrieved object
}
// Deliver is called by the LES protocol manager to deliver ODR reply messages to waiting requests
func (self *ChainAccess) Deliver(id string, msg *Msg) (processed bool) {
self.lock.Lock()
defer self.lock.Unlock()
for i, req := range self.sentReqs {
if req.valFunc(msg) {
req.deliverChan <- msg
delete(self.sentReqs, i)
return true
}
}
return false
}
// networkRequest sends a request to known peers until an answer is received
// or the context is cancelled
func (self *ChainAccess) networkRequest(ctx context.Context, rqFunc requestFunc, valFunc validatorFunc) (*Msg, error) {
req := &sentReq{
deliverChan: make(chan *Msg),
valFunc: valFunc,
}
self.lock.Lock()
reqCnt := self.sentReqCnt
self.sentReqCnt++
self.sentReqs[reqCnt] = req
self.lock.Unlock()
defer func() {
self.lock.Lock()
delete(self.sentReqs, reqCnt)
self.lock.Unlock()
}()
var msg *Msg
for {
peers := self.peers.BestPeers()
if len(peers) == 0 {
select {
case <-ctx.Done():
setTerminated(ctx)
return nil, ctx.Err()
case <-time.After(retryPeers):
}
}
for _, peer := range peers {
rqFunc(peer)
select {
case <-ctx.Done():
setTerminated(ctx)
return nil, ctx.Err()
case msg = <-req.deliverChan:
peer.Promote()
glog.V(LogLevel).Infof("networkRequest success")
return msg, nil
case <-time.After(requestTimeout):
peer.Demote()
glog.V(LogLevel).Infof("networkRequest timeout")
}
}
}
}
// Retrieve tries to fetch an object from the local db, then from the LES network.
// If the network retrieval was successful, it stores the object in local db.
func (self *ChainAccess) Retrieve(ctx context.Context, obj ObjectAccess) (err error) {
// look in db
if obj.DbGet() {
return nil
}
if IsOdrContext(ctx) {
// not found in db, trying the network
_, err = self.networkRequest(ctx, obj.Request, obj.Valid)
if err == nil {
// retrieved from network, store in db
obj.DbPut()
} else {
glog.V(LogLevel).Infof("networkRequest err = %v", err)
}
return
} else {
return errNotInDb
}
}

76
core/access/context.go Normal file
View file

@ -0,0 +1,76 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Package access provides a layer to handle local blockchain database and
// on-demand network retrieval
package access
import (
"sync/atomic"
"time"
"golang.org/x/net/context"
)
// NoOdr is the default context when ODR is not used
var NoOdr = context.Background()
// NewContext creates an ODR context, carrying channel ID and a "terminated" flag
func NewContext(id *OdrChannelID) (context.Context, context.CancelFunc) {
ctx := context.Background()
ctx = context.WithValue(ctx, 0, id)
ctx = context.WithValue(ctx, 1, new(int32))
return context.WithTimeout(ctx, id.timeout)
}
// IsOdrContext returns true if ctx is an ODR-enabled context
func IsOdrContext(ctx context.Context) bool {
_, ok := ctx.Value(0).(*OdrChannelID)
return ok
}
// setTerminated switches the "terminated" flag on, notifying caller that any
// result it received should be considered invalid
func setTerminated(ctx context.Context) {
ptr, ok := ctx.Value(1).(*int32)
if ok {
atomic.StoreInt32(ptr, 1)
}
}
// Terminated returns true if the "terminated" flag was switched on, meaning
// that any result received should be considered invalid
func Terminated(ctx context.Context) bool {
ptr, ok := ctx.Value(1).(*int32)
if ok {
return atomic.LoadInt32(ptr) == 1
} else {
return false
}
}
// OdrChannelID is a permanent identifier of a source from where
// requests can come (like an RPC channel).
// (needed for future functions like "list of my waiting requests" and
// "cancel all requests from this channel")
type OdrChannelID struct {
timeout time.Duration
}
// NewChannelID creates a new OdrChannelID with a channel-specific timeout parameter
func NewChannelID(timeout time.Duration) *OdrChannelID {
return &OdrChannelID{timeout: timeout}
}

181
core/access/peer.go Normal file
View file

@ -0,0 +1,181 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Package access provides a layer to handle local blockchain database and
// on-demand network retrieval
package access
import (
"errors"
"sync"
"sync/atomic"
"github.com/ethereum/go-ethereum/common"
)
var (
errAlreadyRegistered = errors.New("peer is already registered")
errNotRegistered = errors.New("peer is not registered")
errNoOdr = errors.New("peer cannot serve on-demand requests")
)
type ProofReq struct {
Root common.Hash
Key []byte
}
type getBlockBodiesFn func([]common.Hash) error
type getNodeDataFn func([]common.Hash) error
type getReceiptsFn func([]common.Hash) error
type getProofsFn func([]*ProofReq) error
// Peer stores ODR-specific information about LES peers that are able to serve requests
type Peer struct {
id string // Unique identifier of the peer
head common.Hash // Hash of the peers latest known block
rep int32 // Simple peer reputation
GetBlockBodies getBlockBodiesFn
GetNodeData getNodeDataFn
GetReceipts getReceiptsFn
GetProofs getProofsFn
version int // LES protocol version number
}
// newPeer creates a new ODR peer
func newPeer(id string, version int, head common.Hash, getBlockBodies getBlockBodiesFn, getNodeData getNodeDataFn, getReceipts getReceiptsFn, getProofs getProofsFn) *Peer {
return &Peer{
id: id,
head: head,
GetBlockBodies: getBlockBodies,
GetNodeData: getNodeData,
GetReceipts: getReceipts,
GetProofs: getProofs,
version: version,
}
}
// Id returns a peer's ID string
func (p *Peer) Id() string {
return p.id
}
// Promote increases the peer's reputation
func (p *Peer) Promote() {
atomic.AddInt32(&p.rep, 1)
}
// Demote decreases the peer's reputation or leaves it at 0.
func (p *Peer) Demote() {
for {
// Calculate the new reputation value
prev := atomic.LoadInt32(&p.rep)
next := prev / 2
// Try to update the old value
if atomic.CompareAndSwapInt32(&p.rep, prev, next) {
return
}
}
}
// peerSet represents the collection of active peer participating in the block
// download procedure.
type peerSet struct {
peers map[string]*Peer
lock sync.RWMutex
}
// newPeerSet creates a new peer set top track the active download sources.
func newPeerSet() *peerSet {
return &peerSet{
peers: make(map[string]*Peer),
}
}
// Register injects a new peer into the working set, or returns an error if the
// peer is already known.
func (ps *peerSet) Register(p *Peer) error {
ps.lock.Lock()
defer ps.lock.Unlock()
if _, ok := ps.peers[p.id]; ok {
return errAlreadyRegistered
}
ps.peers[p.id] = p
return nil
}
// Unregister removes a remote peer from the active set, disabling any further
// actions to/from that particular entity.
func (ps *peerSet) Unregister(id string) error {
ps.lock.Lock()
defer ps.lock.Unlock()
if _, ok := ps.peers[id]; !ok {
return errNotRegistered
}
delete(ps.peers, id)
return nil
}
// Peer retrieves the registered peer with the given id.
func (ps *peerSet) Peer(id string) *Peer {
ps.lock.RLock()
defer ps.lock.RUnlock()
return ps.peers[id]
}
// Len returns if the current number of peers in the set.
func (ps *peerSet) Len() int {
ps.lock.RLock()
defer ps.lock.RUnlock()
return len(ps.peers)
}
// AllPeers retrieves a flat list of all the peers within the set.
func (ps *peerSet) AllPeers() []*Peer {
ps.lock.RLock()
defer ps.lock.RUnlock()
list := make([]*Peer, 0, len(ps.peers))
for _, p := range ps.peers {
list = append(list, p)
}
return list
}
// BestPeers returns an ordered list of available peers, starting with the
// highest reputation
func (ps *peerSet) BestPeers() []*Peer {
list := ps.AllPeers()
ps.lock.RLock()
defer ps.lock.RUnlock()
for i := 0; i < len(list); i++ {
for j := i + 1; j < len(list); j++ {
if atomic.LoadInt32(&list[i].rep) < atomic.LoadInt32(&list[j].rep) {
list[i], list[j] = list[j], list[i]
}
}
}
return list
}

View file

@ -24,6 +24,7 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/access"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
@ -168,8 +169,9 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
// Time the insertion of the new chain. // Time the insertion of the new chain.
// State and blocks are stored in the same DB. // State and blocks are stored in the same DB.
evmux := new(event.TypeMux) evmux := new(event.TypeMux)
chainman, _ := NewBlockChain(db, FakePow{}, evmux) ca := access.NewDbChainAccess(db)
chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux)) chainman, _ := NewBlockChain(ca, FakePow{}, evmux)
chainman.SetProcessor(NewBlockProcessor(ca, FakePow{}, chainman, evmux))
defer chainman.Stop() defer chainman.Stop()
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()

View file

@ -23,11 +23,11 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/access"
"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/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"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"
@ -43,7 +43,7 @@ const (
) )
type BlockProcessor struct { type BlockProcessor struct {
chainDb ethdb.Database chainAccess *access.ChainAccess
// Mutex for locking the block processor. Blocks can only be handled one at a time // Mutex for locking the block processor. Blocks can only be handled one at a time
mutex sync.Mutex mutex sync.Mutex
// Canonical block chain // Canonical block chain
@ -85,9 +85,9 @@ func (gp *GasPool) String() string {
return (*big.Int)(gp).String() return (*big.Int)(gp).String()
} }
func NewBlockProcessor(db ethdb.Database, pow pow.PoW, blockchain *BlockChain, eventMux *event.TypeMux) *BlockProcessor { func NewBlockProcessor(ca *access.ChainAccess, pow pow.PoW, blockchain *BlockChain, eventMux *event.TypeMux) *BlockProcessor {
sm := &BlockProcessor{ sm := &BlockProcessor{
chainDb: db, chainAccess: ca,
mem: make(map[string]*big.Int), mem: make(map[string]*big.Int),
Pow: pow, Pow: pow,
bc: blockchain, bc: blockchain,
@ -212,12 +212,12 @@ func (sm *BlockProcessor) Process(block *types.Block) (logs vm.Logs, receipts ty
defer sm.mutex.Unlock() defer sm.mutex.Unlock()
if sm.bc.HasBlock(block.Hash()) { if sm.bc.HasBlock(block.Hash()) {
if _, err := state.New(block.Root(), sm.chainDb); err == nil { if _, err := state.New(block.Root(), sm.chainAccess); err == nil {
return nil, nil, &KnownBlockError{block.Number(), block.Hash()} return nil, nil, &KnownBlockError{block.Number(), block.Hash()}
} }
} }
if parent := sm.bc.GetBlock(block.ParentHash()); parent != nil { if parent := sm.bc.GetBlock(block.ParentHash()); parent != nil {
if _, err := state.New(parent.Root(), sm.chainDb); err == nil { if _, err := state.New(parent.Root(), sm.chainAccess); err == nil {
return sm.processWithParent(block, parent) return sm.processWithParent(block, parent)
} }
} }
@ -226,7 +226,7 @@ func (sm *BlockProcessor) Process(block *types.Block) (logs vm.Logs, receipts ty
func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs vm.Logs, receipts types.Receipts, err error) { func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs vm.Logs, receipts types.Receipts, 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, err := state.New(parent.Root(), sm.chainDb) state, err := state.New(parent.Root(), sm.chainAccess)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -370,7 +370,7 @@ func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *ty
// GetBlockReceipts returns the receipts beloniging to the block hash // GetBlockReceipts returns the receipts beloniging to the block hash
func (sm *BlockProcessor) GetBlockReceipts(bhash common.Hash) types.Receipts { func (sm *BlockProcessor) GetBlockReceipts(bhash common.Hash) types.Receipts {
if block := sm.BlockChain().GetBlock(bhash); block != nil { if block := sm.BlockChain().GetBlock(bhash); block != nil {
return GetBlockReceipts(sm.chainDb, block.Hash()) return GetBlockReceipts(sm.chainAccess, block.Hash())
} }
return nil return nil
@ -380,7 +380,7 @@ func (sm *BlockProcessor) GetBlockReceipts(bhash common.Hash) types.Receipts {
// where it tries to get it from the (updated) method which gets them from the receipts or // where it tries to get it from the (updated) method which gets them from the receipts or
// the depricated way by re-processing the block. // the depricated way by re-processing the block.
func (sm *BlockProcessor) GetLogs(block *types.Block) (logs vm.Logs, err error) { func (sm *BlockProcessor) GetLogs(block *types.Block) (logs vm.Logs, err error) {
receipts := GetBlockReceipts(sm.chainDb, block.Hash()) receipts := GetBlockReceipts(sm.chainAccess, block.Hash())
// coalesce logs // coalesce logs
for _, receipt := range receipts { for _, receipt := range receipts {
logs = append(logs, receipt.Logs...) logs = append(logs, receipt.Logs...)

View file

@ -22,6 +22,7 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/access"
"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/core/vm" "github.com/ethereum/go-ethereum/core/vm"
@ -32,21 +33,22 @@ import (
func proc() (*BlockProcessor, *BlockChain) { func proc() (*BlockProcessor, *BlockChain) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
ca := access.NewDbChainAccess(db)
var mux event.TypeMux var mux event.TypeMux
WriteTestNetGenesisBlock(db, 0) WriteTestNetGenesisBlock(db, 0)
blockchain, err := NewBlockChain(db, thePow(), &mux) blockchain, err := NewBlockChain(ca, thePow(), &mux)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} }
return NewBlockProcessor(db, ezp.New(), blockchain, &mux), blockchain return NewBlockProcessor(ca, ezp.New(), blockchain, &mux), blockchain
} }
func TestNumber(t *testing.T) { func TestNumber(t *testing.T) {
pow := ezp.New() pow := ezp.New()
_, chain := proc() _, chain := proc()
statedb, _ := state.New(chain.Genesis().Root(), chain.chainDb) statedb, _ := state.New(chain.Genesis().Root(), access.NewDbChainAccess(chain.chainDb))
header := makeHeader(chain.Genesis(), statedb) header := makeHeader(chain.Genesis(), statedb)
header.Number = big.NewInt(3) header.Number = big.NewInt(3)
err := ValidateHeader(pow, header, chain.Genesis().Header(), false, false) err := ValidateHeader(pow, header, chain.Genesis().Header(), false, false)
@ -82,7 +84,7 @@ func TestPutReceipt(t *testing.T) {
}} }}
PutReceipts(db, types.Receipts{receipt}) PutReceipts(db, types.Receipts{receipt})
receipt = GetReceipt(db, common.Hash{}) receipt = GetReceipt(access.NewDbChainAccess(db), common.Hash{})
if receipt == nil { if receipt == nil {
t.Error("expected to get 1 receipt, got none.") t.Error("expected to get 1 receipt, got none.")
} }

View file

@ -31,6 +31,7 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/access"
"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/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -43,6 +44,7 @@ import (
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/hashicorp/golang-lru" "github.com/hashicorp/golang-lru"
"golang.org/x/net/context"
) )
var ( var (
@ -64,6 +66,7 @@ const (
) )
type BlockChain struct { type BlockChain struct {
chainAccess *access.ChainAccess
chainDb ethdb.Database chainDb ethdb.Database
processor types.BlockProcessor processor types.BlockProcessor
eventMux *event.TypeMux eventMux *event.TypeMux
@ -95,7 +98,7 @@ type BlockChain struct {
rand *mrand.Rand rand *mrand.Rand
} }
func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*BlockChain, error) { func NewBlockChain(chainAccess *access.ChainAccess, pow pow.PoW, mux *event.TypeMux) (*BlockChain, error) {
headerCache, _ := lru.New(headerCacheLimit) headerCache, _ := lru.New(headerCacheLimit)
bodyCache, _ := lru.New(bodyCacheLimit) bodyCache, _ := lru.New(bodyCacheLimit)
bodyRLPCache, _ := lru.New(bodyCacheLimit) bodyRLPCache, _ := lru.New(bodyCacheLimit)
@ -104,7 +107,8 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl
futureBlocks, _ := lru.New(maxFutureBlocks) futureBlocks, _ := lru.New(maxFutureBlocks)
bc := &BlockChain{ bc := &BlockChain{
chainDb: chainDb, chainDb: chainAccess.Db(),
chainAccess: chainAccess,
eventMux: mux, eventMux: mux,
quit: make(chan struct{}), quit: make(chan struct{}),
headerCache: headerCache, headerCache: headerCache,
@ -128,7 +132,7 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl
if err != nil { if err != nil {
return nil, err return nil, err
} }
bc.genesisBlock, err = WriteGenesisBlock(chainDb, reader) bc.genesisBlock, err = WriteGenesisBlock(bc.chainDb, reader)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -344,8 +348,14 @@ func (self *BlockChain) SetProcessor(proc types.BlockProcessor) {
self.processor = proc self.processor = proc
} }
// State returns a new StateDB for the current block
func (self *BlockChain) State() (*state.StateDB, error) { func (self *BlockChain) State() (*state.StateDB, error) {
return state.New(self.CurrentBlock().Root(), self.chainDb) return state.New(self.CurrentBlock().Root(), self.chainAccess)
}
// StateOdr returns a new StateDB for the current block with ODR enabled
func (self *BlockChain) StateOdr(ctx context.Context) (*state.StateDB, error) {
return state.NewOdr(ctx, self.CurrentBlock().Root(), self.chainAccess)
} }
// Reset purges the entire blockchain, restoring it to its genesis state. // Reset purges the entire blockchain, restoring it to its genesis state.
@ -482,12 +492,18 @@ func (self *BlockChain) GetHeaderByNumber(number uint64) *types.Header {
// GetBody retrieves a block body (transactions and uncles) from the database by // GetBody retrieves a block body (transactions and uncles) from the database by
// hash, caching it if found. // hash, caching it if found.
func (self *BlockChain) GetBody(hash common.Hash) *types.Body { func (self *BlockChain) GetBody(hash common.Hash) *types.Body {
return self.GetBodyOdr(access.NoOdr, hash)
}
// GetBodyOdr retrieves a block body (transactions and uncles) from the database
// or network by hash, caching it if found.
func (self *BlockChain) GetBodyOdr(ctx context.Context, hash common.Hash) *types.Body {
// Short circuit if the body's already in the cache, retrieve otherwise // Short circuit if the body's already in the cache, retrieve otherwise
if cached, ok := self.bodyCache.Get(hash); ok { if cached, ok := self.bodyCache.Get(hash); ok {
body := cached.(*types.Body) body := cached.(*types.Body)
return body return body
} }
body := GetBody(self.chainDb, hash) body := GetBodyOdr(ctx, self.chainAccess, hash)
if body == nil { if body == nil {
return nil return nil
} }
@ -499,11 +515,17 @@ func (self *BlockChain) GetBody(hash common.Hash) *types.Body {
// GetBodyRLP retrieves a block body in RLP encoding from the database by hash, // GetBodyRLP retrieves a block body in RLP encoding from the database by hash,
// caching it if found. // caching it if found.
func (self *BlockChain) GetBodyRLP(hash common.Hash) rlp.RawValue { func (self *BlockChain) GetBodyRLP(hash common.Hash) rlp.RawValue {
return self.GetBodyRLPOdr(access.NoOdr, hash)
}
// GetBodyRLPOdr retrieves a block body in RLP encoding from the database or
// network by hash, caching it if found.
func (self *BlockChain) GetBodyRLPOdr(ctx context.Context, hash common.Hash) rlp.RawValue {
// Short circuit if the body's already in the cache, retrieve otherwise // Short circuit if the body's already in the cache, retrieve otherwise
if cached, ok := self.bodyRLPCache.Get(hash); ok { if cached, ok := self.bodyRLPCache.Get(hash); ok {
return cached.(rlp.RawValue) return cached.(rlp.RawValue)
} }
body := GetBodyRLP(self.chainDb, hash) body := GetBodyRLPOdr(ctx, self.chainAccess, hash)
if len(body) == 0 { if len(body) == 0 {
return nil return nil
} }
@ -536,11 +558,16 @@ func (bc *BlockChain) HasBlock(hash common.Hash) bool {
// GetBlock retrieves a block from the database by hash, caching it if found. // GetBlock retrieves a block from the database by hash, caching it if found.
func (self *BlockChain) GetBlock(hash common.Hash) *types.Block { func (self *BlockChain) GetBlock(hash common.Hash) *types.Block {
return self.GetBlockOdr(access.NoOdr, hash)
}
// GetBlockOdr retrieves a block from the database or network by hash, caching it if found.
func (self *BlockChain) GetBlockOdr(ctx context.Context, hash common.Hash) *types.Block {
// Short circuit if the block's already in the cache, retrieve otherwise // Short circuit if the block's already in the cache, retrieve otherwise
if block, ok := self.blockCache.Get(hash); ok { if block, ok := self.blockCache.Get(hash); ok {
return block.(*types.Block) return block.(*types.Block)
} }
block := GetBlock(self.chainDb, hash) block := GetBlockOdr(ctx, self.chainAccess, hash)
if block == nil { if block == nil {
return nil return nil
} }
@ -552,11 +579,17 @@ func (self *BlockChain) GetBlock(hash common.Hash) *types.Block {
// GetBlockByNumber retrieves a block from the database by number, caching it // GetBlockByNumber retrieves a block from the database by number, caching it
// (associated with its hash) if found. // (associated with its hash) if found.
func (self *BlockChain) GetBlockByNumber(number uint64) *types.Block { func (self *BlockChain) GetBlockByNumber(number uint64) *types.Block {
return self.GetBlockByNumberOdr(access.NoOdr, number)
}
// GetBlockByNumberOdr retrieves a block from the database or network by number,
// caching it (associated with its hash) if found.
func (self *BlockChain) GetBlockByNumberOdr(ctx context.Context, number uint64) *types.Block {
hash := GetCanonicalHash(self.chainDb, number) hash := GetCanonicalHash(self.chainDb, number)
if hash == (common.Hash{}) { if hash == (common.Hash{}) {
return nil return nil
} }
return self.GetBlock(hash) return self.GetBlockOdr(ctx, hash)
} }
// GetBlockHashesFromHash retrieves a number of block hashes starting at a given // GetBlockHashesFromHash retrieves a number of block hashes starting at a given
@ -1209,7 +1242,7 @@ func (self *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
if err := PutTransactions(self.chainDb, block, block.Transactions()); err != nil { if err := PutTransactions(self.chainDb, block, block.Transactions()); err != nil {
return err return err
} }
receipts := GetBlockReceipts(self.chainDb, block.Hash()) receipts := GetBlockReceipts(self.chainAccess, block.Hash())
// write receipts // write receipts
if err := PutReceipts(self.chainDb, receipts); err != nil { if err := PutReceipts(self.chainDb, receipts); err != nil {
return err return err
@ -1270,4 +1303,4 @@ func blockErr(block *types.Block, err error) {
glog.Errorf("Bad block #%v (%s)\n", block.Number(), block.Hash().Hex()) glog.Errorf("Bad block #%v (%s)\n", block.Number(), block.Hash().Hex())
glog.Errorf(" %v", err) glog.Errorf(" %v", err)
} }
} }

View file

@ -28,6 +28,7 @@ import (
"github.com/ethereum/ethash" "github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/access"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -51,13 +52,14 @@ func thePow() pow.PoW {
func theBlockChain(db ethdb.Database, t *testing.T) *BlockChain { func theBlockChain(db ethdb.Database, t *testing.T) *BlockChain {
var eventMux event.TypeMux var eventMux event.TypeMux
WriteTestNetGenesisBlock(db, 0) WriteTestNetGenesisBlock(db, 0)
blockchain, err := NewBlockChain(db, thePow(), &eventMux) ca := access.NewDbChainAccess(db)
blockchain, err := NewBlockChain(ca, thePow(), &eventMux)
if err != nil { if err != nil {
t.Error("failed creating chainmanager:", err) t.Error("failed creating chainmanager:", err)
t.FailNow() t.FailNow()
return nil return nil
} }
blockMan := NewBlockProcessor(db, nil, blockchain, &eventMux) blockMan := NewBlockProcessor(ca, nil, blockchain, &eventMux)
blockchain.SetProcessor(blockMan) blockchain.SetProcessor(blockMan)
return blockchain return blockchain
@ -138,8 +140,8 @@ func testBlockChainImport(chain []*types.Block, processor *BlockProcessor) error
} }
// Manually insert the block into the database, but don't reorganize (allows subsequent testing) // Manually insert the block into the database, but don't reorganize (allows subsequent testing)
processor.bc.mu.Lock() processor.bc.mu.Lock()
WriteTd(processor.chainDb, block.Hash(), new(big.Int).Add(block.Difficulty(), processor.bc.GetTd(block.ParentHash()))) WriteTd(processor.chainAccess.Db(), block.Hash(), new(big.Int).Add(block.Difficulty(), processor.bc.GetTd(block.ParentHash())))
WriteBlock(processor.chainDb, block) WriteBlock(processor.chainAccess.Db(), block)
processor.bc.mu.Unlock() processor.bc.mu.Unlock()
} }
return nil return nil
@ -155,8 +157,8 @@ func testHeaderChainImport(chain []*types.Header, processor *BlockProcessor) err
} }
// Manually insert the header into the database, but don't reorganize (allows subsequent testing) // Manually insert the header into the database, but don't reorganize (allows subsequent testing)
processor.bc.mu.Lock() processor.bc.mu.Lock()
WriteTd(processor.chainDb, header.Hash(), new(big.Int).Add(header.Difficulty, processor.bc.GetTd(header.ParentHash))) WriteTd(processor.chainAccess.Db(), header.Hash(), new(big.Int).Add(header.Difficulty, processor.bc.GetTd(header.ParentHash)))
WriteHeader(processor.chainDb, header) WriteHeader(processor.chainAccess.Db(), header)
processor.bc.mu.Unlock() processor.bc.mu.Unlock()
} }
return nil return nil
@ -452,7 +454,8 @@ func makeBlockChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.B
func chm(genesis *types.Block, db ethdb.Database) *BlockChain { func chm(genesis *types.Block, db ethdb.Database) *BlockChain {
var eventMux event.TypeMux var eventMux event.TypeMux
bc := &BlockChain{chainDb: db, genesisBlock: genesis, eventMux: &eventMux, pow: FakePow{}, rand: rand.New(rand.NewSource(0))} ca := access.NewDbChainAccess(db)
bc := &BlockChain{chainAccess: ca, chainDb: db, genesisBlock: genesis, eventMux: &eventMux, pow: FakePow{}, rand: rand.New(rand.NewSource(0))}
bc.headerCache, _ = lru.New(100) bc.headerCache, _ = lru.New(100)
bc.bodyCache, _ = lru.New(100) bc.bodyCache, _ = lru.New(100)
bc.bodyRLPCache, _ = lru.New(100) bc.bodyRLPCache, _ = lru.New(100)
@ -500,7 +503,7 @@ func testReorg(t *testing.T, first, second []int, td int64, full bool) {
// Check that the chain is valid number and link wise // Check that the chain is valid number and link wise
if full { if full {
prev := bc.CurrentBlock() prev := bc.CurrentBlock()
for block := bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 1); block.NumberU64() != 0; prev, block = block, bc.GetBlockByNumber(block.NumberU64()-1) { for block := bc.GetBlockByNumber(bc.CurrentBlock().NumberU64()-1); block.NumberU64() != 0; prev, block = block, bc.GetBlockByNumber(block.NumberU64()-1) {
if prev.ParentHash() != block.Hash() { if prev.ParentHash() != block.Hash() {
t.Errorf("parent block hash mismatch: have %x, want %x", prev.ParentHash(), block.Hash()) t.Errorf("parent block hash mismatch: have %x, want %x", prev.ParentHash(), block.Hash())
} }
@ -587,7 +590,7 @@ func testReorgBadHashes(t *testing.T, full bool) {
defer func() { delete(BadHashes, headers[3].Hash()) }() defer func() { delete(BadHashes, headers[3].Hash()) }()
} }
// Create a new chain manager and check it rolled back the state // Create a new chain manager and check it rolled back the state
ncm, err := NewBlockChain(db, FakePow{}, new(event.TypeMux)) ncm, err := NewBlockChain(access.NewDbChainAccess(db), FakePow{}, new(event.TypeMux))
if err != nil { if err != nil {
t.Fatalf("failed to create new chain manager: %v", err) t.Fatalf("failed to create new chain manager: %v", err)
} }
@ -665,7 +668,7 @@ func testInsertNonceError(t *testing.T, full bool) {
// Check that all no blocks after the failing block have been inserted. // Check that all no blocks after the failing block have been inserted.
for j := 0; j < i-failAt; j++ { for j := 0; j < i-failAt; j++ {
if full { if full {
if block := bc.GetBlockByNumber(failNum + uint64(j)); block != nil { if block := bc.GetBlockByNumber(failNum+uint64(j)); block != nil {
t.Errorf("test %d: invalid block in chain: %v", i, block) t.Errorf("test %d: invalid block in chain: %v", i, block)
} }
} else { } else {
@ -708,19 +711,21 @@ func TestFastVsFullChains(t *testing.T) {
}) })
// Import the chain as an archive node for the comparison baseline // Import the chain as an archive node for the comparison baseline
archiveDb, _ := ethdb.NewMemDatabase() archiveDb, _ := ethdb.NewMemDatabase()
archiveCa := access.NewDbChainAccess(archiveDb)
WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds}) WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds})
archive, _ := NewBlockChain(archiveDb, FakePow{}, new(event.TypeMux)) archive, _ := NewBlockChain(archiveCa, FakePow{}, new(event.TypeMux))
archive.SetProcessor(NewBlockProcessor(archiveDb, FakePow{}, archive, new(event.TypeMux))) archive.SetProcessor(NewBlockProcessor(archiveCa, FakePow{}, archive, new(event.TypeMux)))
if n, err := archive.InsertChain(blocks); err != nil { if n, err := archive.InsertChain(blocks); err != nil {
t.Fatalf("failed to process block %d: %v", n, err) t.Fatalf("failed to process block %d: %v", n, err)
} }
// Fast import the chain as a non-archive node to test // Fast import the chain as a non-archive node to test
fastDb, _ := ethdb.NewMemDatabase() fastDb, _ := ethdb.NewMemDatabase()
fastCa := access.NewDbChainAccess(fastDb)
WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds}) WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds})
fast, _ := NewBlockChain(fastDb, FakePow{}, new(event.TypeMux)) fast, _ := NewBlockChain(fastCa, FakePow{}, new(event.TypeMux))
fast.SetProcessor(NewBlockProcessor(fastDb, FakePow{}, fast, new(event.TypeMux))) fast.SetProcessor(NewBlockProcessor(fastCa, FakePow{}, fast, new(event.TypeMux)))
headers := make([]*types.Header, len(blocks)) headers := make([]*types.Header, len(blocks))
for i, block := range blocks { for i, block := range blocks {
@ -749,7 +754,7 @@ func TestFastVsFullChains(t *testing.T) {
} else if types.CalcUncleHash(fblock.Uncles()) != types.CalcUncleHash(ablock.Uncles()) { } else if types.CalcUncleHash(fblock.Uncles()) != types.CalcUncleHash(ablock.Uncles()) {
t.Errorf("block #%d [%x]: uncles mismatch: have %v, want %v", num, hash, fblock.Uncles(), ablock.Uncles()) t.Errorf("block #%d [%x]: uncles mismatch: have %v, want %v", num, hash, fblock.Uncles(), ablock.Uncles())
} }
if freceipts, areceipts := GetBlockReceipts(fastDb, hash), GetBlockReceipts(archiveDb, hash); types.DeriveSha(freceipts) != types.DeriveSha(areceipts) { if freceipts, areceipts := GetBlockReceipts(fastCa, hash), GetBlockReceipts(archiveCa, hash); types.DeriveSha(freceipts) != types.DeriveSha(areceipts) {
t.Errorf("block #%d [%x]: receipts mismatch: have %v, want %v", num, hash, freceipts, areceipts) t.Errorf("block #%d [%x]: receipts mismatch: have %v, want %v", num, hash, freceipts, areceipts)
} }
} }
@ -794,10 +799,10 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
} }
// Import the chain as an archive node and ensure all pointers are updated // Import the chain as an archive node and ensure all pointers are updated
archiveDb, _ := ethdb.NewMemDatabase() archiveDb, _ := ethdb.NewMemDatabase()
archiveCa := access.NewDbChainAccess(archiveDb)
WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds}) WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds})
archive, _ := NewBlockChain(archiveCa, FakePow{}, new(event.TypeMux))
archive, _ := NewBlockChain(archiveDb, FakePow{}, new(event.TypeMux)) archive.SetProcessor(NewBlockProcessor(archiveCa, FakePow{}, archive, new(event.TypeMux)))
archive.SetProcessor(NewBlockProcessor(archiveDb, FakePow{}, archive, new(event.TypeMux)))
if n, err := archive.InsertChain(blocks); err != nil { if n, err := archive.InsertChain(blocks); err != nil {
t.Fatalf("failed to process block %d: %v", n, err) t.Fatalf("failed to process block %d: %v", n, err)
@ -808,9 +813,10 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
// Import the chain as a non-archive node and ensure all pointers are updated // Import the chain as a non-archive node and ensure all pointers are updated
fastDb, _ := ethdb.NewMemDatabase() fastDb, _ := ethdb.NewMemDatabase()
fastCa := access.NewDbChainAccess(fastDb)
WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds}) WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds})
fast, _ := NewBlockChain(fastDb, FakePow{}, new(event.TypeMux)) fast, _ := NewBlockChain(fastCa, FakePow{}, new(event.TypeMux))
fast.SetProcessor(NewBlockProcessor(fastDb, FakePow{}, fast, new(event.TypeMux))) fast.SetProcessor(NewBlockProcessor(fastCa, FakePow{}, fast, new(event.TypeMux)))
headers := make([]*types.Header, len(blocks)) headers := make([]*types.Header, len(blocks))
for i, block := range blocks { for i, block := range blocks {
@ -828,9 +834,10 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
// Import the chain as a light node and ensure all pointers are updated // Import the chain as a light node and ensure all pointers are updated
lightDb, _ := ethdb.NewMemDatabase() lightDb, _ := ethdb.NewMemDatabase()
lightCa := access.NewDbChainAccess(lightDb)
WriteGenesisBlockForTesting(lightDb, GenesisAccount{address, funds}) WriteGenesisBlockForTesting(lightDb, GenesisAccount{address, funds})
light, _ := NewBlockChain(lightDb, FakePow{}, new(event.TypeMux)) light, _ := NewBlockChain(lightCa, FakePow{}, new(event.TypeMux))
light.SetProcessor(NewBlockProcessor(lightDb, FakePow{}, light, new(event.TypeMux))) light.SetProcessor(NewBlockProcessor(lightCa, FakePow{}, light, new(event.TypeMux)))
if n, err := light.InsertHeaderChain(headers, 1); err != nil { if n, err := light.InsertHeaderChain(headers, 1); err != nil {
t.Fatalf("failed to insert header %d: %v", n, err) t.Fatalf("failed to insert header %d: %v", n, err)
@ -895,8 +902,9 @@ func TestChainTxReorgs(t *testing.T) {
}) })
// Import the chain. This runs all block validation rules. // Import the chain. This runs all block validation rules.
evmux := &event.TypeMux{} evmux := &event.TypeMux{}
chainman, _ := NewBlockChain(db, FakePow{}, evmux) ca := access.NewDbChainAccess(db)
chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux)) chainman, _ := NewBlockChain(ca, FakePow{}, evmux)
chainman.SetProcessor(NewBlockProcessor(ca, FakePow{}, chainman, evmux))
if i, err := chainman.InsertChain(chain); err != nil { if i, err := chainman.InsertChain(chain); err != nil {
t.Fatalf("failed to insert original chain[%d]: %v", i, err) t.Fatalf("failed to insert original chain[%d]: %v", i, err)
} }
@ -929,7 +937,7 @@ func TestChainTxReorgs(t *testing.T) {
if GetTransaction(db, tx.Hash()) != nil { if GetTransaction(db, tx.Hash()) != nil {
t.Errorf("drop %d: tx found while shouldn't have been", i) t.Errorf("drop %d: tx found while shouldn't have been", i)
} }
if GetReceipt(db, tx.Hash()) != nil { if GetReceipt(ca, tx.Hash()) != nil {
t.Errorf("drop %d: receipt found while shouldn't have been", i) t.Errorf("drop %d: receipt found while shouldn't have been", i)
} }
} }
@ -938,7 +946,7 @@ func TestChainTxReorgs(t *testing.T) {
if GetTransaction(db, tx.Hash()) == nil { if GetTransaction(db, tx.Hash()) == nil {
t.Errorf("add %d: expected tx to be found", i) t.Errorf("add %d: expected tx to be found", i)
} }
if GetReceipt(db, tx.Hash()) == nil { if GetReceipt(ca, tx.Hash()) == nil {
t.Errorf("add %d: expected receipt to be found", i) t.Errorf("add %d: expected receipt to be found", i)
} }
} }
@ -947,7 +955,7 @@ func TestChainTxReorgs(t *testing.T) {
if GetTransaction(db, tx.Hash()) == nil { if GetTransaction(db, tx.Hash()) == nil {
t.Errorf("share %d: expected tx to be found", i) t.Errorf("share %d: expected tx to be found", i)
} }
if GetReceipt(db, tx.Hash()) == nil { if GetReceipt(ca, tx.Hash()) == nil {
t.Errorf("share %d: expected receipt to be found", i) t.Errorf("share %d: expected receipt to be found", i)
} }
} }

View file

@ -21,6 +21,7 @@ import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/access"
"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/ethdb"
@ -164,7 +165,7 @@ func (b *BlockGen) OffsetTime(seconds int64) {
// values. Inserting them into BlockChain requires use of FakePow or // values. Inserting them into BlockChain requires use of FakePow or
// a similar non-validating proof of work implementation. // a similar non-validating proof of work implementation.
func GenerateChain(parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) { func GenerateChain(parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) {
statedb, err := state.New(parent.Root(), db) statedb, err := state.New(parent.Root(), access.NewDbChainAccess(db))
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -222,8 +223,9 @@ func newCanonical(n int, full bool) (ethdb.Database, *BlockProcessor, error) {
// Initialize a fresh chain with only a genesis block // Initialize a fresh chain with only a genesis block
genesis, _ := WriteTestNetGenesisBlock(db, 0) genesis, _ := WriteTestNetGenesisBlock(db, 0)
blockchain, _ := NewBlockChain(db, FakePow{}, evmux) ca := access.NewDbChainAccess(db)
processor := NewBlockProcessor(db, FakePow{}, blockchain, evmux) blockchain, _ := NewBlockChain(ca, FakePow{}, evmux)
processor := NewBlockProcessor(ca, FakePow{}, blockchain, evmux)
processor.bc.SetProcessor(processor) processor.bc.SetProcessor(processor)
// Create and inject the requested chain // Create and inject the requested chain

View file

@ -20,6 +20,7 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/core/access"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
@ -77,8 +78,9 @@ func ExampleGenerateChain() {
// Import the chain. This runs all block validation rules. // Import the chain. This runs all block validation rules.
evmux := &event.TypeMux{} evmux := &event.TypeMux{}
chainman, _ := NewBlockChain(db, FakePow{}, evmux) ca := access.NewDbChainAccess(db)
chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux)) chainman, _ := NewBlockChain(ca, FakePow{}, evmux)
chainman.SetProcessor(NewBlockProcessor(ca, FakePow{}, chainman, evmux))
if i, err := chainman.InsertChain(chain); err != nil { if i, err := chainman.InsertChain(chain); err != nil {
fmt.Printf("insert error (block %d): %v\n", i, err) fmt.Printf("insert error (block %d): %v\n", i, err)
return return

View file

@ -23,12 +23,15 @@ import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/access"
"github.com/ethereum/go-ethereum/core/requests"
"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/ethdb"
"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/rlp" "github.com/ethereum/go-ethereum/rlp"
"golang.org/x/net/context"
) )
var ( var (
@ -182,16 +185,31 @@ func GetHeader(db ethdb.Database, hash common.Hash) *types.Header {
return header return header
} }
// GetBodyRLP retrieves the block body (transactions and uncles) in RLP encoding. // GetBodyRLP retrieves the block body (transactions and uncles) from the
func GetBodyRLP(db ethdb.Database, hash common.Hash) rlp.RawValue { // database in RLP encoding.
data, _ := db.Get(append(append(blockPrefix, hash[:]...), bodySuffix...)) func GetBodyRLP(ca *access.ChainAccess, hash common.Hash) rlp.RawValue {
return data return GetBodyRLPOdr(access.NoOdr, ca, hash)
}
// GetBodyRLPOdr retrieves the block body (transactions and uncles) from the
// database or network in RLP encoding.
func GetBodyRLPOdr(ctx context.Context, ca *access.ChainAccess, hash common.Hash) rlp.RawValue {
//fmt.Println("request block %v", hash)
r := requests.NewBlockAccess(ca.Db(), hash, GetHeader)
ca.Retrieve(ctx, r)
return r.GetRlp()
} }
// GetBody retrieves the block body (transactons, uncles) corresponding to the // GetBody retrieves the block body (transactons, uncles) corresponding to the
// hash, nil if none found. // hash from the database, nil if none found.
func GetBody(db ethdb.Database, hash common.Hash) *types.Body { func GetBody(ca *access.ChainAccess, hash common.Hash) *types.Body {
data := GetBodyRLP(db, hash) return GetBodyOdr(access.NoOdr, ca, hash)
}
// GetBodyOdr retrieves the block body (transactons, uncles) corresponding to the
// hash from the database or network, nil if none found.
func GetBodyOdr(ctx context.Context, ca *access.ChainAccess, hash common.Hash) *types.Body {
data := GetBodyRLPOdr(ctx, ca, hash)
if len(data) == 0 { if len(data) == 0 {
return nil return nil
} }
@ -218,15 +236,21 @@ func GetTd(db ethdb.Database, hash common.Hash) *big.Int {
return td return td
} }
// GetBlock retrieves an entire block corresponding to the hash, assembling it // GetBlock retrieves an entire block from the database corresponding to the
// back from the stored header and body. // hash, assembling it back from the stored header and body.
func GetBlock(db ethdb.Database, hash common.Hash) *types.Block { func GetBlock(ca *access.ChainAccess, hash common.Hash) *types.Block {
return GetBlockOdr(access.NoOdr, ca, hash)
}
// GetBlockOdr retrieves an entire block from database or network corresponding
// to the hash, assembling it back from the stored header and body.
func GetBlockOdr(ctx context.Context, ca *access.ChainAccess, hash common.Hash) *types.Block {
// Retrieve the block header and body contents // Retrieve the block header and body contents
header := GetHeader(db, hash) header := GetHeader(ca.Db(), hash)
if header == nil { if header == nil {
return nil return nil
} }
body := GetBody(db, hash) body := GetBodyOdr(ctx, ca, hash)
if body == nil { if body == nil {
return nil return nil
} }

View file

@ -24,6 +24,7 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/access"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -121,6 +122,7 @@ func TestHeaderStorage(t *testing.T) {
// Tests block body storage and retrieval operations. // Tests block body storage and retrieval operations.
func TestBodyStorage(t *testing.T) { func TestBodyStorage(t *testing.T) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
ca := access.NewDbChainAccess(db)
// Create a test body to move around the database and make sure it's really new // Create a test body to move around the database and make sure it's really new
body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header")}}} body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header")}}}
@ -129,19 +131,19 @@ func TestBodyStorage(t *testing.T) {
rlp.Encode(hasher, body) rlp.Encode(hasher, body)
hash := common.BytesToHash(hasher.Sum(nil)) hash := common.BytesToHash(hasher.Sum(nil))
if entry := GetBody(db, hash); entry != nil { if entry := GetBody(ca, hash); entry != nil {
t.Fatalf("Non existent body returned: %v", entry) t.Fatalf("Non existent body returned: %v", entry)
} }
// Write and verify the body in the database // Write and verify the body in the database
if err := WriteBody(db, hash, body); err != nil { if err := WriteBody(db, hash, body); err != nil {
t.Fatalf("Failed to write body into database: %v", err) t.Fatalf("Failed to write body into database: %v", err)
} }
if entry := GetBody(db, hash); entry == nil { if entry := GetBody(ca, hash); entry == nil {
t.Fatalf("Stored body not found") t.Fatalf("Stored body not found")
} else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(types.Transactions(body.Transactions)) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(body.Uncles) { } else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(types.Transactions(body.Transactions)) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(body.Uncles) {
t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, body) t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, body)
} }
if entry := GetBodyRLP(db, hash); entry == nil { if entry := GetBodyRLP(ca, hash); entry == nil {
t.Fatalf("Stored body RLP not found") t.Fatalf("Stored body RLP not found")
} else { } else {
hasher := sha3.NewKeccak256() hasher := sha3.NewKeccak256()
@ -153,7 +155,7 @@ func TestBodyStorage(t *testing.T) {
} }
// Delete the body and verify the execution // Delete the body and verify the execution
DeleteBody(db, hash) DeleteBody(db, hash)
if entry := GetBody(db, hash); entry != nil { if entry := GetBody(ca, hash); entry != nil {
t.Fatalf("Deleted body returned: %v", entry) t.Fatalf("Deleted body returned: %v", entry)
} }
} }
@ -161,6 +163,7 @@ func TestBodyStorage(t *testing.T) {
// Tests block storage and retrieval operations. // Tests block storage and retrieval operations.
func TestBlockStorage(t *testing.T) { func TestBlockStorage(t *testing.T) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
ca := access.NewDbChainAccess(db)
// Create a test block to move around the database and make sure it's really new // Create a test block to move around the database and make sure it's really new
block := types.NewBlockWithHeader(&types.Header{ block := types.NewBlockWithHeader(&types.Header{
@ -169,20 +172,20 @@ func TestBlockStorage(t *testing.T) {
TxHash: types.EmptyRootHash, TxHash: types.EmptyRootHash,
ReceiptHash: types.EmptyRootHash, ReceiptHash: types.EmptyRootHash,
}) })
if entry := GetBlock(db, block.Hash()); entry != nil { if entry := GetBlock(ca, block.Hash()); entry != nil {
t.Fatalf("Non existent block returned: %v", entry) t.Fatalf("Non existent block returned: %v", entry)
} }
if entry := GetHeader(db, block.Hash()); entry != nil { if entry := GetHeader(db, block.Hash()); entry != nil {
t.Fatalf("Non existent header returned: %v", entry) t.Fatalf("Non existent header returned: %v", entry)
} }
if entry := GetBody(db, block.Hash()); entry != nil { if entry := GetBody(ca, block.Hash()); entry != nil {
t.Fatalf("Non existent body returned: %v", entry) t.Fatalf("Non existent body returned: %v", entry)
} }
// Write and verify the block in the database // Write and verify the block in the database
if err := WriteBlock(db, block); err != nil { if err := WriteBlock(db, block); err != nil {
t.Fatalf("Failed to write block into database: %v", err) t.Fatalf("Failed to write block into database: %v", err)
} }
if entry := GetBlock(db, block.Hash()); entry == nil { if entry := GetBlock(ca, block.Hash()); entry == nil {
t.Fatalf("Stored block not found") t.Fatalf("Stored block not found")
} else if entry.Hash() != block.Hash() { } else if entry.Hash() != block.Hash() {
t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block) t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block)
@ -192,20 +195,20 @@ func TestBlockStorage(t *testing.T) {
} else if entry.Hash() != block.Header().Hash() { } else if entry.Hash() != block.Header().Hash() {
t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, block.Header()) t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, block.Header())
} }
if entry := GetBody(db, block.Hash()); entry == nil { if entry := GetBody(ca, block.Hash()); entry == nil {
t.Fatalf("Stored body not found") t.Fatalf("Stored body not found")
} else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(block.Transactions()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(block.Uncles()) { } else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(block.Transactions()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(block.Uncles()) {
t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, &types.Body{block.Transactions(), block.Uncles()}) t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, &types.Body{block.Transactions(), block.Uncles()})
} }
// Delete the block and verify the execution // Delete the block and verify the execution
DeleteBlock(db, block.Hash()) DeleteBlock(db, block.Hash())
if entry := GetBlock(db, block.Hash()); entry != nil { if entry := GetBlock(ca, block.Hash()); entry != nil {
t.Fatalf("Deleted block returned: %v", entry) t.Fatalf("Deleted block returned: %v", entry)
} }
if entry := GetHeader(db, block.Hash()); entry != nil { if entry := GetHeader(db, block.Hash()); entry != nil {
t.Fatalf("Deleted header returned: %v", entry) t.Fatalf("Deleted header returned: %v", entry)
} }
if entry := GetBody(db, block.Hash()); entry != nil { if entry := GetBody(ca, block.Hash()); entry != nil {
t.Fatalf("Deleted body returned: %v", entry) t.Fatalf("Deleted body returned: %v", entry)
} }
} }
@ -213,6 +216,7 @@ func TestBlockStorage(t *testing.T) {
// Tests that partial block contents don't get reassembled into full blocks. // Tests that partial block contents don't get reassembled into full blocks.
func TestPartialBlockStorage(t *testing.T) { func TestPartialBlockStorage(t *testing.T) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
ca := access.NewDbChainAccess(db)
block := types.NewBlockWithHeader(&types.Header{ block := types.NewBlockWithHeader(&types.Header{
Extra: []byte("test block"), Extra: []byte("test block"),
UncleHash: types.EmptyUncleHash, UncleHash: types.EmptyUncleHash,
@ -223,7 +227,7 @@ func TestPartialBlockStorage(t *testing.T) {
if err := WriteHeader(db, block.Header()); err != nil { if err := WriteHeader(db, block.Header()); err != nil {
t.Fatalf("Failed to write header into database: %v", err) t.Fatalf("Failed to write header into database: %v", err)
} }
if entry := GetBlock(db, block.Hash()); entry != nil { if entry := GetBlock(ca, block.Hash()); entry != nil {
t.Fatalf("Non existent block returned: %v", entry) t.Fatalf("Non existent block returned: %v", entry)
} }
DeleteHeader(db, block.Hash()) DeleteHeader(db, block.Hash())
@ -232,7 +236,7 @@ func TestPartialBlockStorage(t *testing.T) {
if err := WriteBody(db, block.Hash(), &types.Body{block.Transactions(), block.Uncles()}); err != nil { if err := WriteBody(db, block.Hash(), &types.Body{block.Transactions(), block.Uncles()}); err != nil {
t.Fatalf("Failed to write body into database: %v", err) t.Fatalf("Failed to write body into database: %v", err)
} }
if entry := GetBlock(db, block.Hash()); entry != nil { if entry := GetBlock(ca, block.Hash()); entry != nil {
t.Fatalf("Non existent block returned: %v", entry) t.Fatalf("Non existent block returned: %v", entry)
} }
DeleteBody(db, block.Hash()) DeleteBody(db, block.Hash())
@ -244,7 +248,7 @@ func TestPartialBlockStorage(t *testing.T) {
if err := WriteBody(db, block.Hash(), &types.Body{block.Transactions(), block.Uncles()}); err != nil { if err := WriteBody(db, block.Hash(), &types.Body{block.Transactions(), block.Uncles()}); err != nil {
t.Fatalf("Failed to write body into database: %v", err) t.Fatalf("Failed to write body into database: %v", err)
} }
if entry := GetBlock(db, block.Hash()); entry == nil { if entry := GetBlock(ca, block.Hash()); entry == nil {
t.Fatalf("Stored block not found") t.Fatalf("Stored block not found")
} else if entry.Hash() != block.Hash() { } else if entry.Hash() != block.Hash() {
t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block) t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block)

View file

@ -25,6 +25,7 @@ import (
"strings" "strings"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/access"
"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/ethdb"
@ -61,7 +62,8 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
} }
// creating with empty hash always works // creating with empty hash always works
statedb, _ := state.New(common.Hash{}, chainDb) ca := access.NewDbChainAccess(chainDb)
statedb, _ := state.New(common.Hash{}, ca)
for addr, account := range genesis.Alloc { for addr, account := range genesis.Alloc {
address := common.HexToAddress(addr) address := common.HexToAddress(addr)
statedb.AddBalance(address, common.String2Big(account.Balance)) statedb.AddBalance(address, common.String2Big(account.Balance))
@ -85,7 +87,7 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
Root: root, Root: root,
}, nil, nil, nil) }, nil, nil, nil)
if block := GetBlock(chainDb, block.Hash()); block != nil { if block := GetBlock(ca, block.Hash()); block != nil {
glog.V(logger.Info).Infoln("Genesis block already in chain. Writing canonical number") glog.V(logger.Info).Infoln("Genesis block already in chain. Writing canonical number")
err := WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) err := WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64())
if err != nil { if err != nil {
@ -118,7 +120,7 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
// GenesisBlockForTesting creates a block in which addr has the given wei balance. // GenesisBlockForTesting creates a block in which addr has the given wei balance.
// The state trie of the block is written to db. the passed db needs to contain a state root // The state trie of the block is written to db. the passed db needs to contain a state root
func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big.Int) *types.Block { func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big.Int) *types.Block {
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db))
obj := statedb.GetOrNewStateObject(addr) obj := statedb.GetOrNewStateObject(addr)
obj.SetBalance(balance) obj.SetBalance(balance)
root, err := statedb.Commit() root, err := statedb.Commit()

View file

@ -18,6 +18,7 @@ package core
import ( import (
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/core/access"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
) )
@ -29,6 +30,7 @@ type Backend interface {
BlockChain() *BlockChain BlockChain() *BlockChain
TxPool() *TxPool TxPool() *TxPool
ChainDb() ethdb.Database ChainDb() ethdb.Database
ChainAccess() *access.ChainAccess
DappDb() ethdb.Database DappDb() ethdb.Database
EventMux() *event.TypeMux EventMux() *event.TypeMux
} }

View file

@ -0,0 +1,204 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package requests
import (
"bytes"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/core/access"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rlp"
)
var (
blockReceiptsPre = []byte("receipts-block-")
blockPrefix = []byte("block-")
bodySuffix = []byte("-body")
)
// BlockAccess is the ODR request type for block bodies
type BlockAccess struct {
access.ObjectAccess
db ethdb.Database
blockHash common.Hash
rlp []byte
getHeader getHeaderFn
}
type getHeaderFn func(db ethdb.Database, hash common.Hash) *types.Header
// NewBlockAccess creates a new BlockAccess request
func NewBlockAccess(db ethdb.Database, blockHash common.Hash, getHeader getHeaderFn) *BlockAccess {
return &BlockAccess{db: db, blockHash: blockHash, getHeader: getHeader}
}
// GetRlp returns the block body rlp data stored in memory after a successful request
func (self *BlockAccess) GetRlp() []byte {
return self.rlp
}
// Request sends an ODR request to the LES network (implementation of access.ObjectAccess)
func (self *BlockAccess) Request(peer *access.Peer) error {
glog.V(access.LogLevel).Infof("ODR: requesting body of block %08x from peer %v", self.blockHash[:4], peer.Id())
return peer.GetBlockBodies([]common.Hash{self.blockHash})
}
// Valid processes an ODR request reply message from the LES network
// returns true and stores results in memory if the message was a valid reply
// to the request (implementation of access.ObjectAccess)
func (self *BlockAccess) Valid(msg *access.Msg) bool {
glog.V(access.LogLevel).Infof("ODR: validating body of block %08x", self.blockHash[:4])
if msg.MsgType != access.MsgBlockBodies {
glog.V(access.LogLevel).Infof("ODR: invalid message type")
return false
}
bodies := msg.Obj.([]*types.Body)
if len(bodies) != 1 {
glog.V(access.LogLevel).Infof("ODR: invalid number of entries: %d", len(bodies))
return false
}
body := bodies[0]
header := self.getHeader(self.db, self.blockHash)
if header == nil {
glog.V(access.LogLevel).Infof("ODR: header not found for block %08x", self.blockHash[:4])
return false
}
txHash := types.DeriveSha(types.Transactions(body.Transactions))
if header.TxHash != txHash {
glog.V(access.LogLevel).Infof("ODR: header.TxHash %08x does not match received txHash %08x", header.TxHash[:4], txHash[:4])
return false
}
uncleHash := types.CalcUncleHash(body.Uncles)
if header.UncleHash != uncleHash {
glog.V(access.LogLevel).Infof("ODR: header.UncleHash %08x does not match received uncleHash %08x", header.UncleHash[:4], uncleHash[:4])
return false
}
data, err := rlp.EncodeToBytes(body)
if err != nil {
glog.V(access.LogLevel).Infof("ODR: body RLP encode error: %v", err)
return false
}
self.rlp = data
glog.V(access.LogLevel).Infof("ODR: validation successful")
return true
}
// DbGet tries to retrieve requested data from the local database, returns
// true and stores results in memory if successful
// (implementation of access.ObjectAccess)
func (self *BlockAccess) DbGet() bool {
self.rlp, _ = self.db.Get(append(append(blockPrefix, self.blockHash[:]...), bodySuffix...))
glog.V(access.LogLevel).Infof("ODR: get body %08x len = %d", self.blockHash[:4], len(self.rlp))
return len(self.rlp) != 0
}
// DbPut stores the results of a successful request in the local database
// (implementation of access.ObjectAccess)
func (self *BlockAccess) DbPut() {
self.db.Put(append(append(blockPrefix, self.blockHash[:]...), bodySuffix...), self.rlp)
glog.V(access.LogLevel).Infof("ODR: put body %08x len = %d", self.blockHash[:4], len(self.rlp))
}
// ReceiptsAccess is the ODR request type for block receipts by block hash
type ReceiptsAccess struct {
access.ObjectAccess
db ethdb.Database
blockHash common.Hash
receipts types.Receipts
getHeader getHeaderFn
putReceipts putReceiptsFn
putBlockReceipts putBlockReceiptsFn
}
type putReceiptsFn func(db ethdb.Database, receipts types.Receipts) error
type putBlockReceiptsFn func(db ethdb.Database, hash common.Hash, receipts types.Receipts) error
// NewReceiptsAccess creates a new ReceiptsAccess request
func NewReceiptsAccess(db ethdb.Database, blockHash common.Hash, getHeader getHeaderFn, putReceipts putReceiptsFn, putBlockReceipts putBlockReceiptsFn) *ReceiptsAccess {
return &ReceiptsAccess{db: db, blockHash: blockHash, getHeader: getHeader, putReceipts: putReceipts, putBlockReceipts: putBlockReceipts}
}
// GetReceipts returns the block receipts stored in memory after a successful request
func (self *ReceiptsAccess) GetReceipts() types.Receipts {
return self.receipts
}
// Request sends an ODR request to the LES network (implementation of access.ObjectAccess)
func (self *ReceiptsAccess) Request(peer *access.Peer) error {
glog.V(access.LogLevel).Infof("ODR: requesting receipts for block %08x from peer %v", self.blockHash[:4], peer.Id())
return peer.GetReceipts([]common.Hash{self.blockHash})
}
// Valid processes an ODR request reply message from the LES network
// returns true and stores results in memory if the message was a valid reply
// to the request (implementation of access.ObjectAccess)
func (self *ReceiptsAccess) Valid(msg *access.Msg) bool {
glog.V(access.LogLevel).Infof("ODR: validating receipts for block %08x", self.blockHash[:4])
if msg.MsgType != access.MsgReceipts {
glog.V(access.LogLevel).Infof("ODR: invalid message type")
return false
}
receipts := msg.Obj.([]types.Receipts)
if len(receipts) != 1 {
glog.V(access.LogLevel).Infof("ODR: invalid number of entries: %d", len(receipts))
return false
}
hash := types.DeriveSha(receipts[0])
header := self.getHeader(self.db, self.blockHash)
if header == nil {
glog.V(access.LogLevel).Infof("ODR: header not found for block %08x", self.blockHash[:4])
return false
}
if !bytes.Equal(header.ReceiptHash[:], hash[:]) {
glog.V(access.LogLevel).Infof("ODR: header receipts hash %08x does not match calculated RLP hash %08x", header.ReceiptHash[:4], hash[:4])
return false
}
self.receipts = receipts[0]
glog.V(access.LogLevel).Infof("ODR: validation successful")
return true
}
// DbGet tries to retrieve requested data from the local database, returns
// true and stores results in memory if successful
// (implementation of access.ObjectAccess)
func (self *ReceiptsAccess) DbGet() bool {
data, _ := self.db.Get(append(blockReceiptsPre, self.blockHash[:]...))
if len(data) == 0 {
return false
}
rs := []*types.ReceiptForStorage{}
if err := rlp.DecodeBytes(data, &rs); err != nil {
glog.V(logger.Error).Infof("invalid receipt array RLP for hash %x: %v", self.blockHash, err)
return false
}
self.receipts = make(types.Receipts, len(rs))
for i, receipt := range rs {
self.receipts[i] = (*types.Receipt)(receipt)
}
return true
}
// DbPut stores the results of a successful request in the local database
// (implementation of access.ObjectAccess)
func (self *ReceiptsAccess) DbPut() {
self.putBlockReceipts(self.db, self.blockHash, self.receipts)
self.putReceipts(self.db, self.receipts)
}

View file

@ -0,0 +1,200 @@
// Copyright 2014 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Package state provides a caching layer atop the Ethereum state trie.
package requests
import (
"bytes"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/core/access"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/trie"
"golang.org/x/net/context"
)
// TrieAccess implements trie.OdrAccess, providing database/network access for
// a trie identified by root hash
type TrieAccess struct {
trie.OdrAccess
ca *access.ChainAccess
root common.Hash
trieDb trie.Database
}
// NewTrieAccess creates a new TrieAccess
func NewTrieAccess(ca *access.ChainAccess, root common.Hash, trieDb trie.Database) *TrieAccess {
return &TrieAccess{
ca: ca,
root: root,
trieDb: trieDb,
}
}
// RetrieveKey retrieves a single key, returns true and stores nodes in local
// database if successful
func (self *TrieAccess) RetrieveKey(ctx context.Context, key []byte) bool {
//fmt.Println("request trie %v key %v", self.root, key)
r := NewTrieEntryAccess(self.root, self.trieDb, key)
return self.ca.Retrieve(ctx, r) == nil
}
// OdrEnabled returns true if this TrieAccess is capable of doing network requests
func (self *TrieAccess) OdrEnabled() bool {
return self.ca.OdrEnabled()
}
// TrieEntryAccess is the ODR request type for state/storage trie entries
type TrieEntryAccess struct {
access.ObjectAccess
root common.Hash
trieDb trie.Database
key, value []byte
proof trie.MerkleProof
skipLevels int // set by DbGet() if unsuccessful
}
// NewTrieEntryAccess creates a new TrieEntryAccess request
func NewTrieEntryAccess(root common.Hash, trieDb trie.Database, key []byte) *TrieEntryAccess {
return &TrieEntryAccess{root: root, trieDb: trieDb, key: key}
}
// Request sends an ODR request to the LES network (implementation of access.ObjectAccess)
func (self *TrieEntryAccess) Request(peer *access.Peer) error {
glog.V(access.LogLevel).Infof("ODR: requesting trie root %08x key %08x from peer %v", self.root[:4], self.key[:4], peer.Id())
req := &access.ProofReq{
Root: self.root,
Key: self.key,
}
return peer.GetProofs([]*access.ProofReq{req})
}
// Valid processes an ODR request reply message from the LES network
// returns true and stores results in memory if the message was a valid reply
// to the request (implementation of access.ObjectAccess)
func (self *TrieEntryAccess) Valid(msg *access.Msg) bool {
glog.V(access.LogLevel).Infof("ODR: validating trie root %08x key %08x", self.root[:4], self.key[:4])
if msg.MsgType != access.MsgProofs {
glog.V(access.LogLevel).Infof("ODR: invalid message type")
return false
}
proofs := msg.Obj.([]trie.MerkleProof)
if len(proofs) != 1 {
glog.V(access.LogLevel).Infof("ODR: invalid number of entries: %d", len(proofs))
return false
}
value, err := trie.VerifyProof(self.root, self.key, proofs[0])
if err != nil {
glog.V(access.LogLevel).Infof("ODR: merkle proof verification error: %v", err)
return false
}
self.proof = proofs[0]
self.value = value
glog.V(access.LogLevel).Infof("ODR: validation successful")
return true
}
// DbGet tries to retrieve requested data from the local database, returns
// true and stores results in memory if successful
// (implementation of access.ObjectAccess)
func (self *TrieEntryAccess) DbGet() bool {
return false // not used
}
// DbPut stores the results of a successful request in the local database
// (implementation of access.ObjectAccess)
func (self *TrieEntryAccess) DbPut() {
trie.StoreProof(self.trieDb, self.proof)
}
// NodeDataBlockAccess is the ODR request type for node data (used for retrieving contract code)
type NodeDataAccess struct {
access.ObjectAccess
db ethdb.Database
hash common.Hash
data []byte
}
// NewNodeDataAccess creates a new NodeDataAccess request
func NewNodeDataAccess(db ethdb.Database, hash common.Hash) *NodeDataAccess {
return &NodeDataAccess{db: db, hash: hash}
}
// Request sends an ODR request to the LES network (implementation of access.ObjectAccess)
func (self *NodeDataAccess) Request(peer *access.Peer) error {
glog.V(access.LogLevel).Infof("ODR: requesting node data for hash %08x from peer %v", self.hash[:4], peer.Id())
return peer.GetNodeData([]common.Hash{self.hash})
}
// Valid processes an ODR request reply message from the LES network
// returns true and stores results in memory if the message was a valid reply
// to the request (implementation of access.ObjectAccess)
func (self *NodeDataAccess) Valid(msg *access.Msg) bool {
glog.V(access.LogLevel).Infof("ODR: validating node data for hash %08x", self.hash[:4])
if msg.MsgType != access.MsgNodeData {
glog.V(access.LogLevel).Infof("ODR: invalid message type")
return false
}
reply := msg.Obj.([][]byte)
if len(reply) != 1 {
glog.V(access.LogLevel).Infof("ODR: invalid number of entries: %d", len(reply))
return false
}
data := reply[0]
hash := crypto.Sha3Hash(data)
if bytes.Compare(self.hash[:], hash[:]) != 0 {
glog.V(access.LogLevel).Infof("ODR: requested hash %08x does not match received data hash %08x", self.hash[:4], hash[:4])
return false
}
self.data = data
glog.V(access.LogLevel).Infof("ODR: validation successful")
return true
}
// DbGet tries to retrieve requested data from the local database, returns
// true and stores results in memory if successful
// (implementation of access.ObjectAccess)
func (self *NodeDataAccess) DbGet() bool {
data, _ := self.db.Get(self.hash[:])
if len(data) == 0 {
return false
}
self.data = data
return true
}
// DbPut stores the results of a successful request in the local database
// (implementation of access.ObjectAccess)
func (self *NodeDataAccess) DbPut() {
self.db.Put(self.hash[:], self.data)
}
var sha3_nil = sha3.NewKeccak256().Sum(nil)
func RetrieveNodeData(ctx context.Context, ca *access.ChainAccess, hash common.Hash) []byte {
//fmt.Println("request node data %v", hash)
if bytes.Compare(hash[:], sha3_nil) == 0 {
return nil
}
r := NewNodeDataAccess(ca.Db(), hash)
ca.Retrieve(ctx, r)
return r.data
}

View file

@ -21,6 +21,7 @@ import (
"fmt" "fmt"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/access"
) )
type Account struct { type Account struct {
@ -45,7 +46,7 @@ func (self *StateDB) RawDump() World {
it := self.trie.Iterator() it := self.trie.Iterator()
for it.Next() { for it.Next() {
addr := self.trie.GetKey(it.Key) addr := self.trie.GetKey(it.Key)
stateObject := NewStateObjectFromBytes(common.BytesToAddress(addr), it.Value, self.db) stateObject := NewStateObjectFromBytes(access.NoOdr, common.BytesToAddress(addr), it.Value, self.ca)
account := Account{Balance: stateObject.balance.String(), Nonce: stateObject.nonce, Root: common.Bytes2Hex(stateObject.Root()), CodeHash: common.Bytes2Hex(stateObject.codeHash)} account := Account{Balance: stateObject.balance.String(), Nonce: stateObject.nonce, Root: common.Bytes2Hex(stateObject.Root()), CodeHash: common.Bytes2Hex(stateObject.codeHash)}
account.Storage = make(map[string]string) account.Storage = make(map[string]string)

View file

@ -20,6 +20,7 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/access"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
) )
@ -27,7 +28,7 @@ var addr = common.BytesToAddress([]byte("test"))
func create() (*ManagedState, *account) { func create() (*ManagedState, *account) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := New(common.Hash{}, db) statedb, _ := New(common.Hash{}, access.NewDbChainAccess(db))
ms := ManageState(statedb) ms := ManageState(statedb)
so := &StateObject{address: addr, nonce: 100} so := &StateObject{address: addr, nonce: 100}
ms.StateDB.stateObjects[addr.Str()] = so ms.StateDB.stateObjects[addr.Str()] = so

View file

@ -22,12 +22,14 @@ import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/access"
"github.com/ethereum/go-ethereum/core/requests"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"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/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"golang.org/x/net/context"
) )
type Code []byte type Code []byte
@ -57,7 +59,8 @@ func (self Storage) Copy() Storage {
type StateObject struct { type StateObject struct {
// State database for storing state changes // State database for storing state changes
db ethdb.Database ca *access.ChainAccess
ctx context.Context
trie *trie.SecureTrie trie *trie.SecureTrie
// Address belonging to this account // Address belonging to this account
@ -83,14 +86,14 @@ type StateObject struct {
dirty bool dirty bool
} }
func NewStateObject(address common.Address, db ethdb.Database) *StateObject { func NewStateObject(ctx context.Context, address common.Address, ca *access.ChainAccess) *StateObject {
object := &StateObject{db: db, address: address, balance: new(big.Int), dirty: true} object := &StateObject{ca: ca, ctx: ctx, address: address, balance: new(big.Int), dirty: true}
object.trie, _ = trie.NewSecure(common.Hash{}, db) object.trie, _ = trie.NewSecure(common.Hash{}, ca.Db())
object.storage = make(Storage) object.storage = make(Storage)
return object return object
} }
func NewStateObjectFromBytes(address common.Address, data []byte, db ethdb.Database) *StateObject { func NewStateObjectFromBytes(ctx context.Context, address common.Address, data []byte, ca *access.ChainAccess) *StateObject {
var extobject struct { var extobject struct {
Nonce uint64 Nonce uint64
Balance *big.Int Balance *big.Int
@ -102,20 +105,20 @@ func NewStateObjectFromBytes(address common.Address, data []byte, db ethdb.Datab
glog.Errorf("can't decode state object %x: %v", address, err) glog.Errorf("can't decode state object %x: %v", address, err)
return nil return nil
} }
trie, err := trie.NewSecure(extobject.Root, db) trie, err := trie.NewSecureOdr(ctx, extobject.Root, ca.Db(), requests.NewTrieAccess(ca, extobject.Root, ca.Db()))
if err != nil { if err != nil {
// TODO: bubble this up or panic // TODO: bubble this up or panic
glog.Errorf("can't create account trie with root %x: %v", extobject.Root[:], err) glog.Errorf("can't create account trie with root %x: %v", extobject.Root[:], err)
return nil return nil
} }
object := &StateObject{address: address, db: db} object := &StateObject{address: address, ca: ca, ctx: ctx}
object.nonce = extobject.Nonce object.nonce = extobject.Nonce
object.balance = extobject.Balance object.balance = extobject.Balance
object.codeHash = extobject.CodeHash object.codeHash = extobject.CodeHash
object.trie = trie object.trie = trie
object.storage = make(map[string]common.Hash) object.storage = make(map[string]common.Hash)
object.code, _ = db.Get(extobject.CodeHash) object.code = requests.RetrieveNodeData(ctx, ca, common.BytesToHash(extobject.CodeHash))
return object return object
} }
@ -130,7 +133,8 @@ func (self *StateObject) MarkForDeletion() {
func (c *StateObject) getAddr(addr common.Hash) common.Hash { func (c *StateObject) getAddr(addr common.Hash) common.Hash {
var ret []byte var ret []byte
rlp.DecodeBytes(c.trie.Get(addr[:]), &ret) value := c.trie.Get(addr[:])
rlp.DecodeBytes(value, &ret)
return common.BytesToHash(ret) return common.BytesToHash(ret)
} }
@ -205,12 +209,22 @@ func (c *StateObject) St() Storage {
// Return the gas back to the origin. Used by the Virtual machine or Closures // Return the gas back to the origin. Used by the Virtual machine or Closures
func (c *StateObject) ReturnGas(gas, price *big.Int) {} func (c *StateObject) ReturnGas(gas, price *big.Int) {}
// Copy creates a copy of the state object
func (self *StateObject) Copy() *StateObject { func (self *StateObject) Copy() *StateObject {
stateObject := NewStateObject(self.Address(), self.db) return self.CopyOdr(access.NoOdr)
}
// CopyOdr creates a copy of the state object with ODR option
func (self *StateObject) CopyOdr(ctx context.Context) *StateObject {
stateObject := NewStateObject(ctx, self.Address(), self.ca)
stateObject.balance.Set(self.balance) stateObject.balance.Set(self.balance)
stateObject.codeHash = common.CopyBytes(self.codeHash) stateObject.codeHash = common.CopyBytes(self.codeHash)
stateObject.nonce = self.nonce stateObject.nonce = self.nonce
stateObject.trie = self.trie if access.IsOdrContext(ctx) {
stateObject.trie = self.trie.CopySecureWithOdr(ctx, requests.NewTrieAccess(self.ca, common.BytesToHash(self.trie.Root()), self.ca.Db()))
} else {
stateObject.trie = self.trie
}
stateObject.code = common.CopyBytes(self.code) stateObject.code = common.CopyBytes(self.code)
stateObject.initCode = common.CopyBytes(self.initCode) stateObject.initCode = common.CopyBytes(self.initCode)
stateObject.storage = self.storage.Copy() stateObject.storage = self.storage.Copy()

View file

@ -24,6 +24,7 @@ import (
checker "gopkg.in/check.v1" checker "gopkg.in/check.v1"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/access"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
) )
@ -77,12 +78,12 @@ func (s *StateSuite) TestDump(c *checker.C) {
func (s *StateSuite) SetUpTest(c *checker.C) { func (s *StateSuite) SetUpTest(c *checker.C) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
s.state, _ = New(common.Hash{}, db) s.state, _ = New(common.Hash{}, access.NewDbChainAccess(db))
} }
func TestNull(t *testing.T) { func TestNull(t *testing.T) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
state, _ := New(common.Hash{}, db) state, _ := New(common.Hash{}, access.NewDbChainAccess(db))
address := common.HexToAddress("0x823140710bf13990e4500136726d8b55") address := common.HexToAddress("0x823140710bf13990e4500136726d8b55")
state.CreateAccount(address) state.CreateAccount(address)
@ -122,7 +123,7 @@ func (s *StateSuite) TestSnapshot(c *checker.C) {
// printing/logging in tests (-check.vv does not work) // printing/logging in tests (-check.vv does not work)
func TestSnapshot2(t *testing.T) { func TestSnapshot2(t *testing.T) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
state, _ := New(common.Hash{}, db) state, _ := New(common.Hash{}, access.NewDbChainAccess(db))
stateobjaddr0 := toAddr([]byte("so0")) stateobjaddr0 := toAddr([]byte("so0"))
stateobjaddr1 := toAddr([]byte("so1")) stateobjaddr1 := toAddr([]byte("so1"))

View file

@ -22,10 +22,13 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/core/access"
"github.com/ethereum/go-ethereum/core/requests"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"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/trie" "github.com/ethereum/go-ethereum/trie"
"golang.org/x/net/context"
) )
// The starting nonce determines the default nonce when new accounts are being // The starting nonce determines the default nonce when new accounts are being
@ -38,8 +41,9 @@ var StartingNonce uint64
// * Contracts // * Contracts
// * Accounts // * Accounts
type StateDB struct { type StateDB struct {
db ethdb.Database ca *access.ChainAccess
trie *trie.SecureTrie trie *trie.SecureTrie
ctx context.Context
stateObjects map[string]*StateObject stateObjects map[string]*StateObject
@ -51,22 +55,33 @@ type StateDB struct {
logSize uint logSize uint
} }
// Create a new state from a given trie // New creates a new state from a given trie
func New(root common.Hash, db ethdb.Database) (*StateDB, error) { func New(root common.Hash, ca *access.ChainAccess) (*StateDB, error) {
tr, err := trie.NewSecure(root, db) return NewOdr(access.NoOdr, root, ca)
}
// NewOdr creates a new state from a given trie with ODR option
func NewOdr(ctx context.Context, root common.Hash, ca *access.ChainAccess) (*StateDB, error) {
tr, err := trie.NewSecureOdr(ctx, root, ca.Db(), requests.NewTrieAccess(ca, root, ca.Db()))
if err != nil { if err != nil {
glog.Errorf("can't create state trie with root %x: %v", root[:], err) glog.Errorf("can't create state trie with root %x: %v", root[:], err)
return nil, err return nil, err
} }
return &StateDB{ return &StateDB{
db: db, ca: ca,
trie: tr, trie: tr,
ctx: ctx,
stateObjects: make(map[string]*StateObject), stateObjects: make(map[string]*StateObject),
refund: new(big.Int), refund: new(big.Int),
logs: make(map[common.Hash]vm.Logs), logs: make(map[common.Hash]vm.Logs),
}, nil }, nil
} }
// Ctx returns the ODR context of the state
func (self *StateDB) Ctx() context.Context {
return self.ctx
}
func (self *StateDB) StartRecord(thash, bhash common.Hash, ti int) { func (self *StateDB) StartRecord(thash, bhash common.Hash, ti int) {
self.thash = thash self.thash = thash
self.bhash = bhash self.bhash = bhash
@ -208,7 +223,7 @@ func (self *StateDB) UpdateStateObject(stateObject *StateObject) {
//addr := stateObject.Address() //addr := stateObject.Address()
if len(stateObject.CodeHash()) > 0 { if len(stateObject.CodeHash()) > 0 {
self.db.Put(stateObject.CodeHash(), stateObject.code) self.ca.Db().Put(stateObject.CodeHash(), stateObject.code)
} }
addr := stateObject.Address() addr := stateObject.Address()
self.trie.Update(addr[:], stateObject.RlpEncode()) self.trie.Update(addr[:], stateObject.RlpEncode())
@ -220,7 +235,6 @@ func (self *StateDB) DeleteStateObject(stateObject *StateObject) {
addr := stateObject.Address() addr := stateObject.Address()
self.trie.Delete(addr[:]) self.trie.Delete(addr[:])
//delete(self.stateObjects, addr.Str())
} }
// Retrieve a state object given my the address. Nil if not found // Retrieve a state object given my the address. Nil if not found
@ -239,7 +253,7 @@ func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObje
return nil return nil
} }
stateObject = NewStateObjectFromBytes(addr, []byte(data), self.db) stateObject = NewStateObjectFromBytes(self.ctx, addr, []byte(data), self.ca)
self.SetStateObject(stateObject) self.SetStateObject(stateObject)
return stateObject return stateObject
@ -265,7 +279,7 @@ func (self *StateDB) newStateObject(addr common.Address) *StateObject {
glog.Infof("(+) %x\n", addr) glog.Infof("(+) %x\n", addr)
} }
stateObject := NewStateObject(addr, self.db) stateObject := NewStateObject(self.ctx, addr, self.ca)
stateObject.SetNonce(StartingNonce) stateObject.SetNonce(StartingNonce)
self.stateObjects[addr.Str()] = stateObject self.stateObjects[addr.Str()] = stateObject
@ -295,12 +309,27 @@ func (self *StateDB) CreateAccount(addr common.Address) vm.Account {
// Setting, copying of the state methods // Setting, copying of the state methods
// //
// CopyWithCtx creates a copy of the state, keeping the original ODR context
func (self *StateDB) CopyWithCtx() *StateDB {
return self.CopyOdr(self.ctx)
}
// Copy creates a copy of the state with no ODR option
func (self *StateDB) Copy() *StateDB { func (self *StateDB) Copy() *StateDB {
return self.CopyOdr(access.NoOdr)
}
// CopyOdr creates a copy of the state with a new ODR context
func (self *StateDB) CopyOdr(ctx context.Context) *StateDB {
// ignore error - we assume state-to-be-copied always exists // ignore error - we assume state-to-be-copied always exists
state, _ := New(common.Hash{}, self.db) state, _ := NewOdr(ctx, common.Hash{}, self.ca)
state.trie = self.trie if access.IsOdrContext(ctx) {
state.trie = self.trie.CopySecureWithOdr(ctx, requests.NewTrieAccess(self.ca, common.BytesToHash(self.trie.Root()), self.ca.Db()))
} else {
state.trie = self.trie
}
for k, stateObject := range self.stateObjects { for k, stateObject := range self.stateObjects {
state.stateObjects[k] = stateObject.Copy() state.stateObjects[k] = stateObject.CopyOdr(ctx)
} }
state.refund.Set(self.refund) state.refund.Set(self.refund)
@ -348,14 +377,14 @@ func (s *StateDB) IntermediateRoot() common.Hash {
// Commit commits all state changes to the database. // Commit commits all state changes to the database.
func (s *StateDB) Commit() (root common.Hash, err error) { func (s *StateDB) Commit() (root common.Hash, err error) {
return s.commit(s.db) return s.commit(s.ca.Db())
} }
// CommitBatch commits all state changes to a write batch but does not // CommitBatch commits all state changes to a write batch but does not
// execute the batch. It is used to validate state changes against // execute the batch. It is used to validate state changes against
// the root hash stored in a block. // the root hash stored in a block.
func (s *StateDB) CommitBatch() (root common.Hash, batch ethdb.Batch) { func (s *StateDB) CommitBatch() (root common.Hash, batch ethdb.Batch) {
batch = s.db.NewBatch() batch = s.ca.Db().NewBatch()
root, _ = s.commit(batch) root, _ = s.commit(batch)
return root, batch return root, batch
} }

View file

@ -22,6 +22,7 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/access"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
) )
@ -38,7 +39,7 @@ type testAccount struct {
func makeTestState() (ethdb.Database, common.Hash, []*testAccount) { func makeTestState() (ethdb.Database, common.Hash, []*testAccount) {
// Create an empty state // Create an empty state
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
state, _ := New(common.Hash{}, db) state, _ := New(common.Hash{}, access.NewDbChainAccess(db))
// Fill it with some arbitrary data // Fill it with some arbitrary data
accounts := []*testAccount{} accounts := []*testAccount{}
@ -68,7 +69,7 @@ func makeTestState() (ethdb.Database, common.Hash, []*testAccount) {
// checkStateAccounts cross references a reconstructed state with an expected // checkStateAccounts cross references a reconstructed state with an expected
// account array. // account array.
func checkStateAccounts(t *testing.T, db ethdb.Database, root common.Hash, accounts []*testAccount) { func checkStateAccounts(t *testing.T, db ethdb.Database, root common.Hash, accounts []*testAccount) {
state, _ := New(root, db) state, _ := New(root, access.NewDbChainAccess(db))
for i, acc := range accounts { for i, acc := range accounts {
if balance := state.GetBalance(acc.address); balance.Cmp(acc.balance) != 0 { if balance := state.GetBalance(acc.address); balance.Cmp(acc.balance) != 0 {

View file

@ -22,6 +22,7 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/access"
"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/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -36,7 +37,7 @@ func transaction(nonce uint64, gaslimit *big.Int, key *ecdsa.PrivateKey) *types.
func setupTxPool() (*TxPool, *ecdsa.PrivateKey) { func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db))
var m event.TypeMux var m event.TypeMux
key, _ := crypto.GenerateKey() key, _ := crypto.GenerateKey()
@ -163,7 +164,7 @@ func TestTransactionChainFork(t *testing.T) {
addr := crypto.PubkeyToAddress(key.PublicKey) addr := crypto.PubkeyToAddress(key.PublicKey)
resetState := func() { resetState := func() {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db))
pool.currentState = func() (*state.StateDB, error) { return statedb, nil } pool.currentState = func() (*state.StateDB, error) { return statedb, nil }
currentState, _ := pool.currentState() currentState, _ := pool.currentState()
currentState.AddBalance(addr, big.NewInt(100000000000000)) currentState.AddBalance(addr, big.NewInt(100000000000000))
@ -189,7 +190,7 @@ func TestTransactionDoubleNonce(t *testing.T) {
addr := crypto.PubkeyToAddress(key.PublicKey) addr := crypto.PubkeyToAddress(key.PublicKey)
resetState := func() { resetState := func() {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db))
pool.currentState = func() (*state.StateDB, error) { return statedb, nil } pool.currentState = func() (*state.StateDB, error) { return statedb, nil }
currentState, _ := pool.currentState() currentState, _ := pool.currentState()
currentState.AddBalance(addr, big.NewInt(100000000000000)) currentState.AddBalance(addr, big.NewInt(100000000000000))

View file

@ -20,17 +20,20 @@ import (
"fmt" "fmt"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/access"
"github.com/ethereum/go-ethereum/core/requests"
"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/ethdb"
"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/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb"
"golang.org/x/net/context"
) )
var ( var (
receiptsPre = []byte("receipts-")
blockReceiptsPre = []byte("receipts-block-") blockReceiptsPre = []byte("receipts-block-")
receiptsPre = []byte("receipts-")
) )
// PutTransactions stores the transactions in the given database // PutTransactions stores the transactions in the given database
@ -119,8 +122,8 @@ func DeleteReceipt(db ethdb.Database, txHash common.Hash) {
} }
// GetReceipt returns a receipt by hash // GetReceipt returns a receipt by hash
func GetReceipt(db ethdb.Database, txHash common.Hash) *types.Receipt { func GetReceipt(ca *access.ChainAccess, txHash common.Hash) *types.Receipt {
data, _ := db.Get(append(receiptsPre, txHash[:]...)) data, _ := ca.Db().Get(append(receiptsPre, txHash[:]...))
if len(data) == 0 { if len(data) == 0 {
return nil return nil
} }
@ -134,21 +137,16 @@ func GetReceipt(db ethdb.Database, txHash common.Hash) *types.Receipt {
// GetBlockReceipts returns the receipts generated by the transactions // GetBlockReceipts returns the receipts generated by the transactions
// included in block's given hash. // included in block's given hash.
func GetBlockReceipts(db ethdb.Database, hash common.Hash) types.Receipts { func GetBlockReceipts(ca *access.ChainAccess, hash common.Hash) types.Receipts {
data, _ := db.Get(append(blockReceiptsPre, hash[:]...)) return GetBlockReceiptsOdr(access.NoOdr, ca, hash)
if len(data) == 0 { }
return nil
} // GetBlockReceiptsOdr returns the receipts generated by the transactions
rs := []*types.ReceiptForStorage{} // included in block's given hash from the database or network.
if err := rlp.DecodeBytes(data, &rs); err != nil { func GetBlockReceiptsOdr(ctx context.Context, ca *access.ChainAccess, hash common.Hash) types.Receipts {
glog.V(logger.Error).Infof("invalid receipt array RLP for hash %x: %v", hash, err) r := requests.NewReceiptsAccess(ca.Db(), hash, GetHeader, PutReceipts, PutBlockReceipts)
return nil ca.Retrieve(ctx, r)
} return r.GetReceipts()
receipts := make(types.Receipts, len(rs))
for i, receipt := range rs {
receipts[i] = (*types.Receipt)(receipt)
}
return receipts
} }
// PutBlockReceipts stores the block's transactions associated receipts // PutBlockReceipts stores the block's transactions associated receipts

View file

@ -21,6 +21,7 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/access"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -96,7 +97,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
var ( var (
db, _ = ethdb.NewMemDatabase() db, _ = ethdb.NewMemDatabase()
statedb, _ = state.New(common.Hash{}, db) statedb, _ = state.New(common.Hash{}, access.NewDbChainAccess(db))
vmenv = NewEnv(cfg, statedb) vmenv = NewEnv(cfg, statedb)
sender = statedb.CreateAccount(cfg.Origin) sender = statedb.CreateAccount(cfg.Origin)
receiver = statedb.CreateAccount(common.StringToAddress("contract")) receiver = statedb.CreateAccount(common.StringToAddress("contract"))

View file

@ -76,7 +76,7 @@ func (self *VMEnv) CanTransfer(from common.Address, balance *big.Int) bool {
} }
func (self *VMEnv) MakeSnapshot() vm.Database { func (self *VMEnv) MakeSnapshot() vm.Database {
return self.state.Copy() return self.state.CopyWithCtx()
} }
func (self *VMEnv) SetSnapshot(copy vm.Database) { func (self *VMEnv) SetSnapshot(copy vm.Database) {

View file

@ -37,6 +37,7 @@ import (
"github.com/ethereum/go-ethereum/common/compiler" "github.com/ethereum/go-ethereum/common/compiler"
"github.com/ethereum/go-ethereum/common/httpclient" "github.com/ethereum/go-ethereum/common/httpclient"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/access"
"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/core/vm" "github.com/ethereum/go-ethereum/core/vm"
@ -231,6 +232,8 @@ type Ethereum struct {
chainDb ethdb.Database // Block chain database chainDb ethdb.Database // Block chain database
dappDb ethdb.Database // Dapp database dappDb ethdb.Database // Dapp database
chainAccess *access.ChainAccess // blockchain access layer
//*** SERVICES *** //*** SERVICES ***
// State manager for processing new blocks and managing the over all states // State manager for processing new blocks and managing the over all states
blockProcessor *core.BlockProcessor blockProcessor *core.BlockProcessor
@ -297,10 +300,10 @@ func New(config *Config) (*Ethereum, error) {
if err := upgradeChainDatabase(chainDb); err != nil { if err := upgradeChainDatabase(chainDb); err != nil {
return nil, err return nil, err
} }
if err := addMipmapBloomBins(chainDb); err != nil { chainAccess := access.NewChainAccess(chainDb, false)
if err := addMipmapBloomBins(chainAccess); err != nil {
return nil, err return nil, err
} }
dappDb, err := newdb(filepath.Join(config.DataDir, "dapp")) dappDb, err := newdb(filepath.Join(config.DataDir, "dapp"))
if err != nil { if err != nil {
if errno, ok := err.(syscall.Errno); ok && datadirInUseErrnos[uint(errno)] { if errno, ok := err.(syscall.Errno); ok && datadirInUseErrnos[uint(errno)] {
@ -366,6 +369,7 @@ func New(config *Config) (*Ethereum, error) {
eth := &Ethereum{ eth := &Ethereum{
shutdownChan: make(chan bool), shutdownChan: make(chan bool),
chainDb: chainDb, chainDb: chainDb,
chainAccess: chainAccess,
dappDb: dappDb, dappDb: dappDb,
eventMux: &event.TypeMux{}, eventMux: &event.TypeMux{},
accountManager: config.AccountManager, accountManager: config.AccountManager,
@ -397,7 +401,7 @@ func New(config *Config) (*Ethereum, error) {
eth.pow = ethash.New() eth.pow = ethash.New()
} }
//genesis := core.GenesisBlock(uint64(config.GenesisNonce), stateDb) //genesis := core.GenesisBlock(uint64(config.GenesisNonce), stateDb)
eth.blockchain, err = core.NewBlockChain(chainDb, eth.pow, eth.EventMux()) eth.blockchain, err = core.NewBlockChain(chainAccess, eth.pow, eth.EventMux())
if err != nil { if err != nil {
if err == core.ErrNoGenesis { if err == core.ErrNoGenesis {
return nil, fmt.Errorf(`Genesis block not found. Please supply a genesis block with the "--genesis /path/to/file" argument`) return nil, fmt.Errorf(`Genesis block not found. Please supply a genesis block with the "--genesis /path/to/file" argument`)
@ -407,11 +411,13 @@ func New(config *Config) (*Ethereum, error) {
newPool := core.NewTxPool(eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit) newPool := core.NewTxPool(eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit)
eth.txPool = newPool eth.txPool = newPool
eth.blockProcessor = core.NewBlockProcessor(chainDb, eth.pow, eth.blockchain, eth.EventMux()) eth.blockProcessor = core.NewBlockProcessor(chainAccess, eth.pow, eth.blockchain, eth.EventMux())
eth.blockchain.SetProcessor(eth.blockProcessor) eth.blockchain.SetProcessor(eth.blockProcessor)
if eth.protocolManager, err = NewProtocolManager(config.FastSync, config.NetworkId, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainDb); err != nil {
if eth.protocolManager, err = NewProtocolManager(config.FastSync, config.NetworkId, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainAccess); err != nil {
return nil, err return nil, err
} }
eth.miner = miner.New(eth, eth.EventMux(), eth.pow) eth.miner = miner.New(eth, eth.EventMux(), eth.pow)
eth.miner.SetGasPrice(config.GasPrice) eth.miner.SetGasPrice(config.GasPrice)
eth.miner.SetExtra(config.ExtraData) eth.miner.SetExtra(config.ExtraData)
@ -493,6 +499,7 @@ func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
func (s *Ethereum) Whisper() *whisper.Whisper { return s.whisper } func (s *Ethereum) Whisper() *whisper.Whisper { return s.whisper }
func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux } func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb } func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
func (s *Ethereum) ChainAccess() *access.ChainAccess { return s.chainAccess }
func (s *Ethereum) DappDb() ethdb.Database { return s.dappDb } func (s *Ethereum) DappDb() ethdb.Database { return s.dappDb }
func (s *Ethereum) IsListening() bool { return true } // Always listening func (s *Ethereum) IsListening() bool { return true } // Always listening
func (s *Ethereum) PeerCount() int { return s.net.PeerCount() } func (s *Ethereum) PeerCount() int { return s.net.PeerCount() }
@ -732,9 +739,10 @@ func upgradeChainDatabase(db ethdb.Database) error {
return nil return nil
} }
func addMipmapBloomBins(db ethdb.Database) (err error) { func addMipmapBloomBins(ca *access.ChainAccess) (err error) {
const mipmapVersion uint = 2 const mipmapVersion uint = 2
db := ca.Db()
// check if the version is set. We ignore data for now since there's // check if the version is set. We ignore data for now since there's
// only one version so we can easily ignore it for now // only one version so we can easily ignore it for now
var data []byte var data []byte
@ -756,7 +764,7 @@ func addMipmapBloomBins(db ethdb.Database) (err error) {
return return
} }
}() }()
latestBlock := core.GetBlock(db, core.GetHeadBlockHash(db)) latestBlock := core.GetBlock(ca, core.GetHeadBlockHash(db))
if latestBlock == nil { // clean database if latestBlock == nil { // clean database
return return
} }
@ -768,7 +776,7 @@ func addMipmapBloomBins(db ethdb.Database) (err error) {
if (hash == common.Hash{}) { if (hash == common.Hash{}) {
return fmt.Errorf("chain db corrupted. Could not find block %d.", i) return fmt.Errorf("chain db corrupted. Could not find block %d.", i)
} }
core.WriteMipmapBloom(db, i, core.GetBlockReceipts(db, hash)) core.WriteMipmapBloom(db, i, core.GetBlockReceipts(ca, hash))
} }
glog.V(logger.Info).Infoln("upgrade completed in", time.Since(tstart)) glog.V(logger.Info).Infoln("upgrade completed in", time.Since(tstart))
return nil return nil

View file

@ -6,6 +6,7 @@ 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/access"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
@ -50,7 +51,7 @@ func TestMipmapUpgrade(t *testing.T) {
} }
} }
err := addMipmapBloomBins(db) err := addMipmapBloomBins(access.NewDbChainAccess(db))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -27,6 +27,7 @@ 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/access"
"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/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -682,7 +683,7 @@ func assertOwnForkedChain(t *testing.T, tester *downloadTester, common int, leng
index = len(tester.ownHashes) - lengths[len(lengths)-1] + int(tester.downloader.queue.fastSyncPivot) index = len(tester.ownHashes) - lengths[len(lengths)-1] + int(tester.downloader.queue.fastSyncPivot)
} }
if index > 0 { if index > 0 {
if statedb, err := state.New(tester.ownHeaders[tester.ownHashes[index]].Root, tester.stateDb); statedb == nil || err != nil { if statedb, err := state.New(tester.ownHeaders[tester.ownHashes[index]].Root, access.NewDbChainAccess(tester.stateDb)); statedb == nil || err != nil {
t.Fatalf("state reconstruction failed: %v", err) t.Fatalf("state reconstruction failed: %v", err)
} }
} }

View file

@ -21,6 +21,7 @@ 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/access"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
@ -32,6 +33,7 @@ type AccountChange struct {
// Filtering interface // Filtering interface
type Filter struct { type Filter struct {
ca *access.ChainAccess
db ethdb.Database db ethdb.Database
begin, end int64 begin, end int64
addresses []common.Address addresses []common.Address
@ -42,10 +44,15 @@ type Filter struct {
LogsCallback func(vm.Logs) LogsCallback func(vm.Logs)
} }
// Create a new filter which uses a bloom filter on blocks to figure out whether a particular block // New creates a new filter which uses a bloom filter on blocks to figure out whether a particular block
// is interesting or not. // is interesting or not.
func New(db ethdb.Database) *Filter { func New(ca *access.ChainAccess) *Filter {
return &Filter{db: db} return &Filter{ca: ca, db: ca.Db()}
}
// NewWithDb creates a filter with no ODR option
func NewWithDb(db ethdb.Database) *Filter {
return &Filter{ca: access.NewDbChainAccess(db), db: db}
} }
// Set the earliest and latest block for filtering. // Set the earliest and latest block for filtering.
@ -69,7 +76,7 @@ func (self *Filter) SetTopics(topics [][]common.Hash) {
// Run filters logs with the current parameters set // Run filters logs with the current parameters set
func (self *Filter) Find() vm.Logs { func (self *Filter) Find() vm.Logs {
latestBlock := core.GetBlock(self.db, core.GetHeadBlockHash(self.db)) latestBlock := core.GetBlock(self.ca, core.GetHeadBlockHash(self.db))
var beginBlockNo uint64 = uint64(self.begin) var beginBlockNo uint64 = uint64(self.begin)
if self.begin == -1 { if self.begin == -1 {
beginBlockNo = latestBlock.NumberU64() beginBlockNo = latestBlock.NumberU64()
@ -124,7 +131,7 @@ func (self *Filter) getLogs(start, end uint64) (logs vm.Logs) {
for i := start; i <= end; i++ { for i := start; i <= end; i++ {
hash := core.GetCanonicalHash(self.db, i) hash := core.GetCanonicalHash(self.db, i)
if hash != (common.Hash{}) { if hash != (common.Hash{}) {
block = core.GetBlock(self.db, hash) block = core.GetBlock(self.ca, hash)
} else { // block not found } else { // block not found
return logs return logs
} }
@ -134,7 +141,7 @@ func (self *Filter) getLogs(start, end uint64) (logs vm.Logs) {
if self.bloomFilter(block) { if self.bloomFilter(block) {
// Get the logs of the block // Get the logs of the block
var ( var (
receipts = core.GetBlockReceipts(self.db, block.Hash()) receipts = core.GetBlockReceipts(self.ca, block.Hash())
unfiltered vm.Logs unfiltered vm.Logs
) )
for _, receipt := range receipts { for _, receipt := range receipts {
@ -142,6 +149,7 @@ func (self *Filter) getLogs(start, end uint64) (logs vm.Logs) {
} }
logs = append(logs, self.FilterLogs(unfiltered)...) logs = append(logs, self.FilterLogs(unfiltered)...)
} }
block = core.GetBlock(self.ca, block.ParentHash())
} }
return logs return logs

View file

@ -84,7 +84,7 @@ func BenchmarkMipmaps(b *testing.B) {
} }
b.ResetTimer() b.ResetTimer()
filter := New(db) filter := NewWithDb(db)
filter.SetAddresses([]common.Address{addr1, addr2, addr3, addr4}) filter.SetAddresses([]common.Address{addr1, addr2, addr3, addr4})
filter.SetBeginBlock(0) filter.SetBeginBlock(0)
filter.SetEndBlock(-1) filter.SetEndBlock(-1)
@ -185,7 +185,7 @@ func TestFilters(t *testing.T) {
} }
} }
filter := New(db) filter := NewWithDb(db)
filter.SetAddresses([]common.Address{addr}) filter.SetAddresses([]common.Address{addr})
filter.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2, hash3, hash4}}) filter.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2, hash3, hash4}})
filter.SetBeginBlock(0) filter.SetBeginBlock(0)
@ -196,7 +196,7 @@ func TestFilters(t *testing.T) {
t.Error("expected 4 log, got", len(logs)) t.Error("expected 4 log, got", len(logs))
} }
filter = New(db) filter = NewWithDb(db)
filter.SetAddresses([]common.Address{addr}) filter.SetAddresses([]common.Address{addr})
filter.SetTopics([][]common.Hash{[]common.Hash{hash3}}) filter.SetTopics([][]common.Hash{[]common.Hash{hash3}})
filter.SetBeginBlock(900) filter.SetBeginBlock(900)
@ -209,7 +209,7 @@ func TestFilters(t *testing.T) {
t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0]) t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0])
} }
filter = New(db) filter = NewWithDb(db)
filter.SetAddresses([]common.Address{addr}) filter.SetAddresses([]common.Address{addr})
filter.SetTopics([][]common.Hash{[]common.Hash{hash3}}) filter.SetTopics([][]common.Hash{[]common.Hash{hash3}})
filter.SetBeginBlock(990) filter.SetBeginBlock(990)
@ -222,7 +222,7 @@ func TestFilters(t *testing.T) {
t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0]) t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0])
} }
filter = New(db) filter = NewWithDb(db)
filter.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2}}) filter.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2}})
filter.SetBeginBlock(1) filter.SetBeginBlock(1)
filter.SetEndBlock(10) filter.SetEndBlock(10)
@ -233,7 +233,7 @@ func TestFilters(t *testing.T) {
} }
failHash := common.BytesToHash([]byte("fail")) failHash := common.BytesToHash([]byte("fail"))
filter = New(db) filter = NewWithDb(db)
filter.SetTopics([][]common.Hash{[]common.Hash{failHash}}) filter.SetTopics([][]common.Hash{[]common.Hash{failHash}})
filter.SetBeginBlock(0) filter.SetBeginBlock(0)
filter.SetEndBlock(-1) filter.SetEndBlock(-1)
@ -244,7 +244,7 @@ func TestFilters(t *testing.T) {
} }
failAddr := common.BytesToAddress([]byte("failmenow")) failAddr := common.BytesToAddress([]byte("failmenow"))
filter = New(db) filter = NewWithDb(db)
filter.SetAddresses([]common.Address{failAddr}) filter.SetAddresses([]common.Address{failAddr})
filter.SetBeginBlock(0) filter.SetBeginBlock(0)
filter.SetEndBlock(-1) filter.SetEndBlock(-1)
@ -254,7 +254,7 @@ func TestFilters(t *testing.T) {
t.Error("expected 0 log, got", len(logs)) t.Error("expected 0 log, got", len(logs))
} }
filter = New(db) filter = NewWithDb(db)
filter.SetTopics([][]common.Hash{[]common.Hash{failHash}, []common.Hash{hash1}}) filter.SetTopics([][]common.Hash{[]common.Hash{failHash}, []common.Hash{hash1}})
filter.SetBeginBlock(0) filter.SetBeginBlock(0)
filter.SetEndBlock(-1) filter.SetEndBlock(-1)

View file

@ -26,10 +26,10 @@ 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/access"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/fetcher" "github.com/ethereum/go-ethereum/eth/fetcher"
"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"
@ -58,10 +58,10 @@ type blockFetcherFn func([]common.Hash) error
type ProtocolManager struct { type ProtocolManager struct {
networkId int networkId int
fastSync bool fastSync bool
txpool txPool txpool txPool
blockchain *core.BlockChain blockchain *core.BlockChain
chaindb ethdb.Database chainAccess *access.ChainAccess
downloader *downloader.Downloader downloader *downloader.Downloader
fetcher *fetcher.Fetcher fetcher *fetcher.Fetcher
@ -86,7 +86,7 @@ type ProtocolManager struct {
// NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable // NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable
// with the ethereum network. // with the ethereum network.
func NewProtocolManager(fastSync bool, networkId int, mux *event.TypeMux, txpool txPool, pow pow.PoW, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) { func NewProtocolManager(fastSync bool, networkId int, mux *event.TypeMux, txpool txPool, pow pow.PoW, blockchain *core.BlockChain, ca *access.ChainAccess) (*ProtocolManager, error) {
// Figure out whether to allow fast sync or not // Figure out whether to allow fast sync or not
if fastSync && blockchain.CurrentBlock().NumberU64() > 0 { if fastSync && blockchain.CurrentBlock().NumberU64() > 0 {
glog.V(logger.Info).Infof("blockchain not empty, fast sync disabled") glog.V(logger.Info).Infof("blockchain not empty, fast sync disabled")
@ -94,22 +94,22 @@ func NewProtocolManager(fastSync bool, networkId int, mux *event.TypeMux, txpool
} }
// Create the protocol manager with the base fields // Create the protocol manager with the base fields
manager := &ProtocolManager{ manager := &ProtocolManager{
networkId: networkId, networkId: networkId,
fastSync: fastSync, fastSync: fastSync,
eventMux: mux, eventMux: mux,
txpool: txpool, txpool: txpool,
blockchain: blockchain, blockchain: blockchain,
chaindb: chaindb, chainAccess: ca,
peers: newPeerSet(), peers: newPeerSet(),
newPeerCh: make(chan *peer, 1), newPeerCh: make(chan *peer, 1),
txsyncCh: make(chan *txsync), txsyncCh: make(chan *txsync),
quitSync: make(chan struct{}), quitSync: make(chan struct{}),
} }
// Initiate a sub-protocol for every implemented version we can handle // Initiate a sub-protocol for every implemented version we can handle
manager.SubProtocols = make([]p2p.Protocol, 0, len(ProtocolVersions)) manager.SubProtocols = make([]p2p.Protocol, 0, len(ProtocolVersions))
for i, version := range ProtocolVersions { for i, version := range ProtocolVersions {
// Skip protocol version if incompatible with the mode of operation // Skip protocol version if incompatible with the mode of operation
if fastSync && version < eth63 { if manager.fastSync && version < eth63 {
continue continue
} }
// Compatible; initialise the sub-protocol // Compatible; initialise the sub-protocol
@ -137,9 +137,8 @@ func NewProtocolManager(fastSync bool, networkId int, mux *event.TypeMux, txpool
if len(manager.SubProtocols) == 0 { if len(manager.SubProtocols) == 0 {
return nil, errIncompatibleConfig return nil, errIncompatibleConfig
} }
// Construct the different synchronisation mechanisms manager.downloader = downloader.New(ca.Db(), manager.eventMux, blockchain.HasHeader, blockchain.HasBlock, blockchain.GetHeader,
manager.downloader = downloader.New(chaindb, manager.eventMux, blockchain.HasHeader, blockchain.HasBlock, blockchain.GetHeader, blockchain.GetBlock, blockchain.GetBlock, blockchain.CurrentHeader, blockchain.CurrentBlock, blockchain.CurrentFastBlock, blockchain.FastSyncCommitHead, blockchain.GetTd,
blockchain.CurrentHeader, blockchain.CurrentBlock, blockchain.CurrentFastBlock, blockchain.FastSyncCommitHead, blockchain.GetTd,
blockchain.InsertHeaderChain, blockchain.InsertChain, blockchain.InsertReceiptChain, blockchain.Rollback, manager.removePeer) blockchain.InsertHeaderChain, blockchain.InsertChain, blockchain.InsertReceiptChain, blockchain.Rollback, manager.removePeer)
validator := func(block *types.Block, parent *types.Block) error { validator := func(block *types.Block, parent *types.Block) error {
@ -233,6 +232,7 @@ func (pm *ProtocolManager) handle(p *peer) error {
p.RequestHeadersByNumber, p.RequestBodies, p.RequestReceipts, p.RequestNodeData); err != nil { p.RequestHeadersByNumber, p.RequestBodies, p.RequestReceipts, p.RequestNodeData); err != nil {
return err return err
} }
// Propagate existing transactions. new transactions appearing // Propagate existing transactions. new transactions appearing
// after this will be sent via broadcasts. // after this will be sent via broadcasts.
pm.syncTransactions(p) pm.syncTransactions(p)
@ -292,7 +292,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
request.Amount = uint64(downloader.MaxHashFetch) request.Amount = uint64(downloader.MaxHashFetch)
} }
// Calculate the last block that should be retrieved, and short circuit if unavailable // Calculate the last block that should be retrieved, and short circuit if unavailable
last := pm.blockchain.GetBlockByNumber(request.Number + request.Amount - 1) last := pm.blockchain.GetBlockByNumber(request.Number+request.Amount-1)
if last == nil { if last == nil {
last = pm.blockchain.CurrentBlock() last = pm.blockchain.CurrentBlock()
request.Amount = last.NumberU64() - request.Number + 1 request.Amount = last.NumberU64() - request.Number + 1
@ -517,7 +517,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
return errResp(ErrDecode, "msg %v: %v", msg, err) return errResp(ErrDecode, "msg %v: %v", msg, err)
} }
// Retrieve the requested state entry, stopping if enough was found // Retrieve the requested state entry, stopping if enough was found
if entry, err := pm.chaindb.Get(hash.Bytes()); err == nil { if entry, err := pm.chainAccess.Db().Get(hash.Bytes()); err == nil {
data = append(data, entry) data = append(data, entry)
bytes += len(entry) bytes += len(entry)
} }
@ -555,7 +555,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
return errResp(ErrDecode, "msg %v: %v", msg, err) return errResp(ErrDecode, "msg %v: %v", msg, err)
} }
// Retrieve the requested block's receipts, skipping if unknown to us // Retrieve the requested block's receipts, skipping if unknown to us
results := core.GetBlockReceipts(pm.chaindb, hash) results := core.GetBlockReceipts(pm.chainAccess, hash)
if results == nil { if results == nil {
if header := pm.blockchain.GetHeader(hash); header == nil || header.ReceiptHash != types.EmptyRootHash { if header := pm.blockchain.GetHeader(hash); header == nil || header.ReceiptHash != types.EmptyRootHash {
continue continue

View file

@ -8,6 +8,7 @@ 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/access"
"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/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -35,7 +36,6 @@ func TestProtocolCompatibility(t *testing.T) {
// Try all available compatibility configs and check for errors // Try all available compatibility configs and check for errors
for i, tt := range tests { for i, tt := range tests {
ProtocolVersions = []uint{tt.version} ProtocolVersions = []uint{tt.version}
pm, err := newTestProtocolManager(tt.fastSync, 0, nil, nil) pm, err := newTestProtocolManager(tt.fastSync, 0, nil, nil)
if pm != nil { if pm != nil {
defer pm.Stop() defer pm.Stop()
@ -62,14 +62,14 @@ func testGetBlockHashes(t *testing.T, protocol int) {
number int number int
result int result int
}{ }{
{common.Hash{}, 1, 0}, // Make sure non existent hashes don't return results {common.Hash{}, 1, 0}, // Make sure non existent hashes don't return results
{pm.blockchain.Genesis().Hash(), 1, 0}, // There are no hashes to retrieve up from the genesis {pm.blockchain.Genesis().Hash(), 1, 0}, // There are no hashes to retrieve up from the genesis
{pm.blockchain.GetBlockByNumber(5).Hash(), 5, 5}, // All the hashes including the genesis requested {pm.blockchain.GetBlockByNumber(5).Hash(), 5, 5}, // All the hashes including the genesis requested
{pm.blockchain.GetBlockByNumber(5).Hash(), 10, 5}, // More hashes than available till the genesis requested {pm.blockchain.GetBlockByNumber(5).Hash(), 10, 5}, // More hashes than available till the genesis requested
{pm.blockchain.GetBlockByNumber(100).Hash(), 10, 10}, // All hashes available from the middle of the chain {pm.blockchain.GetBlockByNumber(100).Hash(), 10, 10}, // All hashes available from the middle of the chain
{pm.blockchain.CurrentBlock().Hash(), 10, 10}, // All hashes available from the head of the chain {pm.blockchain.CurrentBlock().Hash(), 10, 10}, // All hashes available from the head of the chain
{pm.blockchain.CurrentBlock().Hash(), limit, limit}, // Request the maximum allowed hash count {pm.blockchain.CurrentBlock().Hash(), limit, limit}, // Request the maximum allowed hash count
{pm.blockchain.CurrentBlock().Hash(), limit + 1, limit}, // Request more than the maximum allowed hash count {pm.blockchain.CurrentBlock().Hash(), limit + 1, limit}, // Request more than the maximum allowed hash count
} }
// Run each of the tests and verify the results against the chain // Run each of the tests and verify the results against the chain
for i, tt := range tests { for i, tt := range tests {
@ -78,7 +78,7 @@ func testGetBlockHashes(t *testing.T, protocol int) {
if len(resp) > 0 { if len(resp) > 0 {
from := pm.blockchain.GetBlock(tt.origin).NumberU64() - 1 from := pm.blockchain.GetBlock(tt.origin).NumberU64() - 1
for j := 0; j < len(resp); j++ { for j := 0; j < len(resp); j++ {
resp[j] = pm.blockchain.GetBlockByNumber(uint64(int(from) - j)).Hash() resp[j] = pm.blockchain.GetBlockByNumber(uint64(int(from)-j)).Hash()
} }
} }
// Send the hash request and verify the response // Send the hash request and verify the response
@ -120,7 +120,7 @@ func testGetBlockHashesFromNumber(t *testing.T, protocol int) {
// Assemble the hash response we would like to receive // Assemble the hash response we would like to receive
resp := make([]common.Hash, tt.result) resp := make([]common.Hash, tt.result)
for j := 0; j < len(resp); j++ { for j := 0; j < len(resp); j++ {
resp[j] = pm.blockchain.GetBlockByNumber(tt.origin + uint64(j)).Hash() resp[j] = pm.blockchain.GetBlockByNumber(tt.origin+uint64(j)).Hash()
} }
// Send the hash request and verify the response // Send the hash request and verify the response
p2p.Send(peer.app, 0x08, getBlockHashesFromNumberData{tt.origin, uint64(tt.number)}) p2p.Send(peer.app, 0x08, getBlockHashesFromNumberData{tt.origin, uint64(tt.number)})
@ -222,42 +222,42 @@ func testGetBlockHeaders(t *testing.T, protocol int) {
}{ }{
// A single random block should be retrievable by hash and number too // A single random block should be retrievable by hash and number too
{ {
&getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, Amount: 1}, &getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(limit/2).Hash()}, Amount: 1},
[]common.Hash{pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, []common.Hash{pm.blockchain.GetBlockByNumber(limit/2).Hash()},
}, { }, {
&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 1}, &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 1},
[]common.Hash{pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, []common.Hash{pm.blockchain.GetBlockByNumber(limit/2).Hash()},
}, },
// Multiple headers should be retrievable in both directions // Multiple headers should be retrievable in both directions
{ {
&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3}, &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3},
[]common.Hash{ []common.Hash{
pm.blockchain.GetBlockByNumber(limit / 2).Hash(), pm.blockchain.GetBlockByNumber(limit/2).Hash(),
pm.blockchain.GetBlockByNumber(limit/2 + 1).Hash(), pm.blockchain.GetBlockByNumber(limit/2+1).Hash(),
pm.blockchain.GetBlockByNumber(limit/2 + 2).Hash(), pm.blockchain.GetBlockByNumber(limit/2+2).Hash(),
}, },
}, { }, {
&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3, Reverse: true}, &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3, Reverse: true},
[]common.Hash{ []common.Hash{
pm.blockchain.GetBlockByNumber(limit / 2).Hash(), pm.blockchain.GetBlockByNumber(limit/2).Hash(),
pm.blockchain.GetBlockByNumber(limit/2 - 1).Hash(), pm.blockchain.GetBlockByNumber(limit/2-1).Hash(),
pm.blockchain.GetBlockByNumber(limit/2 - 2).Hash(), pm.blockchain.GetBlockByNumber(limit/2-2).Hash(),
}, },
}, },
// Multiple headers with skip lists should be retrievable // Multiple headers with skip lists should be retrievable
{ {
&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3}, &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3},
[]common.Hash{ []common.Hash{
pm.blockchain.GetBlockByNumber(limit / 2).Hash(), pm.blockchain.GetBlockByNumber(limit/2).Hash(),
pm.blockchain.GetBlockByNumber(limit/2 + 4).Hash(), pm.blockchain.GetBlockByNumber(limit/2+4).Hash(),
pm.blockchain.GetBlockByNumber(limit/2 + 8).Hash(), pm.blockchain.GetBlockByNumber(limit/2+8).Hash(),
}, },
}, { }, {
&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3, Reverse: true}, &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3, Reverse: true},
[]common.Hash{ []common.Hash{
pm.blockchain.GetBlockByNumber(limit / 2).Hash(), pm.blockchain.GetBlockByNumber(limit/2).Hash(),
pm.blockchain.GetBlockByNumber(limit/2 - 4).Hash(), pm.blockchain.GetBlockByNumber(limit/2-4).Hash(),
pm.blockchain.GetBlockByNumber(limit/2 - 8).Hash(), pm.blockchain.GetBlockByNumber(limit/2-8).Hash(),
}, },
}, },
// The chain endpoints should be retrievable // The chain endpoints should be retrievable
@ -277,7 +277,7 @@ func testGetBlockHeaders(t *testing.T, protocol int) {
{ {
&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 3, Amount: 3}, &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 3, Amount: 3},
[]common.Hash{ []common.Hash{
pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 4).Hash(), pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64()-4).Hash(),
pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64()).Hash(), pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64()).Hash(),
}, },
}, { }, {
@ -291,8 +291,8 @@ func testGetBlockHeaders(t *testing.T, protocol int) {
{ {
&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 2, Amount: 3}, &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 2, Amount: 3},
[]common.Hash{ []common.Hash{
pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 4).Hash(), pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64()-4).Hash(),
pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 1).Hash(), pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64()-1).Hash(),
}, },
}, { }, {
&getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 2, Amount: 3, Reverse: true}, &getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 2, Amount: 3, Reverse: true},
@ -365,7 +365,7 @@ func testGetBlockBodies(t *testing.T, protocol int) {
for i, tt := range tests { for i, tt := range tests {
// Collect the hashes to request, and the response to expect // Collect the hashes to request, and the response to expect
hashes, seen := []common.Hash{}, make(map[int64]bool) hashes, seen := []common.Hash{}, make(map[int64]bool)
bodies := []*blockBody{} bodies := []*types.Body{}
for j := 0; j < tt.random; j++ { for j := 0; j < tt.random; j++ {
for { for {
@ -376,7 +376,7 @@ func testGetBlockBodies(t *testing.T, protocol int) {
block := pm.blockchain.GetBlockByNumber(uint64(num)) block := pm.blockchain.GetBlockByNumber(uint64(num))
hashes = append(hashes, block.Hash()) hashes = append(hashes, block.Hash())
if len(bodies) < tt.expected { if len(bodies) < tt.expected {
bodies = append(bodies, &blockBody{Transactions: block.Transactions(), Uncles: block.Uncles()}) bodies = append(bodies, &types.Body{Transactions: block.Transactions(), Uncles: block.Uncles()})
} }
break break
} }
@ -386,7 +386,7 @@ func testGetBlockBodies(t *testing.T, protocol int) {
hashes = append(hashes, hash) hashes = append(hashes, hash)
if tt.available[j] && len(bodies) < tt.expected { if tt.available[j] && len(bodies) < tt.expected {
block := pm.blockchain.GetBlock(hash) block := pm.blockchain.GetBlock(hash)
bodies = append(bodies, &blockBody{Transactions: block.Transactions(), Uncles: block.Uncles()}) bodies = append(bodies, &types.Body{Transactions: block.Transactions(), Uncles: block.Uncles()})
} }
} }
// Send the hash request and verify the response // Send the hash request and verify the response
@ -442,7 +442,7 @@ func testGetNodeData(t *testing.T, protocol int) {
// Fetch for now the entire chain db // Fetch for now the entire chain db
hashes := []common.Hash{} hashes := []common.Hash{}
for _, key := range pm.chaindb.(*ethdb.MemDatabase).Keys() { for _, key := range pm.chainAccess.Db().(*ethdb.MemDatabase).Keys() {
if len(key) == len(common.Hash{}) { if len(key) == len(common.Hash{}) {
hashes = append(hashes, common.BytesToHash(key)) hashes = append(hashes, common.BytesToHash(key))
} }
@ -471,7 +471,7 @@ func testGetNodeData(t *testing.T, protocol int) {
} }
accounts := []common.Address{testBankAddress, acc1Addr, acc2Addr} accounts := []common.Address{testBankAddress, acc1Addr, acc2Addr}
for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ { for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ {
trie, _ := state.New(pm.blockchain.GetBlockByNumber(i).Root(), statedb) trie, _ := state.New(pm.blockchain.GetBlockByNumber(i).Root(), access.NewDbChainAccess(statedb))
for j, acc := range accounts { for j, acc := range accounts {
state, _ := pm.blockchain.State() state, _ := pm.blockchain.State()
@ -537,7 +537,7 @@ func testGetReceipt(t *testing.T, protocol int) {
block := pm.blockchain.GetBlockByNumber(i) block := pm.blockchain.GetBlockByNumber(i)
hashes = append(hashes, block.Hash()) hashes = append(hashes, block.Hash())
receipts = append(receipts, core.GetBlockReceipts(pm.chaindb, block.Hash())) receipts = append(receipts, core.GetBlockReceipts(pm.chainAccess, block.Hash()))
} }
// Send the hash request and verify the response // Send the hash request and verify the response
p2p.Send(peer.app, 0x0f, hashes) p2p.Send(peer.app, 0x0f, hashes)

View file

@ -11,6 +11,7 @@ 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/access"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
@ -33,16 +34,17 @@ func newTestProtocolManager(fastSync bool, blocks int, generator func(int, *core
evmux = new(event.TypeMux) evmux = new(event.TypeMux)
pow = new(core.FakePow) pow = new(core.FakePow)
db, _ = ethdb.NewMemDatabase() db, _ = ethdb.NewMemDatabase()
ca = access.NewDbChainAccess(db)
genesis = core.WriteGenesisBlockForTesting(db, core.GenesisAccount{testBankAddress, testBankFunds}) genesis = core.WriteGenesisBlockForTesting(db, core.GenesisAccount{testBankAddress, testBankFunds})
blockchain, _ = core.NewBlockChain(db, pow, evmux) blockchain, _ = core.NewBlockChain(ca, pow, evmux)
blockproc = core.NewBlockProcessor(db, pow, blockchain, evmux) blockproc = core.NewBlockProcessor(ca, pow, blockchain, evmux)
) )
blockchain.SetProcessor(blockproc) blockchain.SetProcessor(blockproc)
chain, _ := core.GenerateChain(genesis, db, blocks, generator) chain, _ := core.GenerateChain(genesis, db, blocks, generator)
if _, err := blockchain.InsertChain(chain); err != nil { if _, err := blockchain.InsertChain(chain); err != nil {
panic(err) panic(err)
} }
pm, err := NewProtocolManager(fastSync, NetworkId, evmux, &testTxPool{added: newtx}, pow, blockchain, db) pm, err := NewProtocolManager(fastSync, NetworkId, evmux, &testTxPool{added: newtx}, pow, blockchain, ca)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -196,7 +196,7 @@ func (p *peer) SendBlockHeaders(headers []*types.Header) error {
} }
// SendBlockBodies sends a batch of block contents to the remote peer. // SendBlockBodies sends a batch of block contents to the remote peer.
func (p *peer) SendBlockBodies(bodies []*blockBody) error { func (p *peer) SendBlockBodies(bodies []*types.Body) error {
return p2p.Send(p.rw, BlockBodiesMsg, blockBodiesData(bodies)) return p2p.Send(p.rw, BlockBodiesMsg, blockBodiesData(bodies))
} }

View file

@ -201,14 +201,8 @@ type newBlockData struct {
TD *big.Int TD *big.Int
} }
// blockBody represents the data content of a single block.
type blockBody struct {
Transactions []*types.Transaction // Transactions contained within a block
Uncles []*types.Header // Uncles contained within a block
}
// blockBodiesData is the network packet for block content distribution. // blockBodiesData is the network packet for block content distribution.
type blockBodiesData []*blockBody type blockBodiesData []*types.Body
// nodeDataData is the network response packet for a node data retrieval. // nodeDataData is the network response packet for a node data retrieval.
type nodeDataData []struct { type nodeDataData []struct {

View file

@ -359,7 +359,7 @@ func (self *worker) push(work *Work) {
// makeCurrent creates a new environment for the current cycle. // makeCurrent creates a new environment for the current cycle.
func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error { func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error {
state, err := state.New(parent.Root(), self.eth.ChainDb()) state, err := state.New(parent.Root(), self.eth.ChainAccess())
if err != nil { if err != nil {
return err return err
} }

View file

@ -119,7 +119,7 @@ func (self *debugApi) DumpBlock(req *shared.Request) (interface{}, error) {
return nil, fmt.Errorf("block #%d not found", args.BlockNumber) return nil, fmt.Errorf("block #%d not found", args.BlockNumber)
} }
stateDb, err := state.New(block.Root(), self.ethereum.ChainDb()) stateDb, err := state.New(block.Root(), self.ethereum.ChainAccess())
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -19,9 +19,8 @@ package api
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"math/big"
"fmt" "fmt"
"math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/natspec" "github.com/ethereum/go-ethereum/common/natspec"
@ -29,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/rpc/codec" "github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/shared" "github.com/ethereum/go-ethereum/rpc/shared"
"github.com/ethereum/go-ethereum/xeth" "github.com/ethereum/go-ethereum/xeth"
"golang.org/x/net/context"
"gopkg.in/fatih/set.v0" "gopkg.in/fatih/set.v0"
) )
@ -153,8 +153,8 @@ func (self *ethApi) GetBalance(req *shared.Request) (interface{}, error) {
if err := self.codec.Decode(req.Params, &args); err != nil { if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
res := self.xeth.WithCtx(req.GetCtx()).AtStateNum(args.BlockNumber).BalanceAt(args.Address)
return self.xeth.AtStateNum(args.BlockNumber).BalanceAt(args.Address), nil return res, nil
} }
func (self *ethApi) ProtocolVersion(req *shared.Request) (interface{}, error) { func (self *ethApi) ProtocolVersion(req *shared.Request) (interface{}, error) {
@ -170,6 +170,9 @@ func (self *ethApi) IsMining(req *shared.Request) (interface{}, error) {
} }
func (self *ethApi) IsSyncing(req *shared.Request) (interface{}, error) { func (self *ethApi) IsSyncing(req *shared.Request) (interface{}, error) {
if self.ethereum.Downloader() == nil {
return false, nil
}
origin, current, height := self.ethereum.Downloader().Progress() origin, current, height := self.ethereum.Downloader().Progress()
if current < height { if current < height {
return map[string]interface{}{ return map[string]interface{}{
@ -191,7 +194,7 @@ func (self *ethApi) GetStorage(req *shared.Request) (interface{}, error) {
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
return self.xeth.AtStateNum(args.BlockNumber).State().SafeGet(args.Address).Storage(), nil return self.xeth.WithCtx(req.GetCtx()).AtStateNum(args.BlockNumber).State().SafeGet(args.Address).Storage(), nil
} }
func (self *ethApi) GetStorageAt(req *shared.Request) (interface{}, error) { func (self *ethApi) GetStorageAt(req *shared.Request) (interface{}, error) {
@ -200,7 +203,7 @@ func (self *ethApi) GetStorageAt(req *shared.Request) (interface{}, error) {
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
return self.xeth.AtStateNum(args.BlockNumber).StorageAt(args.Address, args.Key), nil return self.xeth.WithCtx(req.GetCtx()).AtStateNum(args.BlockNumber).StorageAt(args.Address, args.Key), nil
} }
func (self *ethApi) GetTransactionCount(req *shared.Request) (interface{}, error) { func (self *ethApi) GetTransactionCount(req *shared.Request) (interface{}, error) {
@ -209,7 +212,7 @@ func (self *ethApi) GetTransactionCount(req *shared.Request) (interface{}, error
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
count := self.xeth.AtStateNum(args.BlockNumber).TxCountAt(args.Address) count := self.xeth.WithCtx(req.GetCtx()).AtStateNum(args.BlockNumber).TxCountAt(args.Address)
return fmt.Sprintf("%#x", count), nil return fmt.Sprintf("%#x", count), nil
} }
@ -218,7 +221,7 @@ func (self *ethApi) GetBlockTransactionCountByHash(req *shared.Request) (interfa
if err := self.codec.Decode(req.Params, &args); err != nil { if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
block := self.xeth.EthBlockByHash(args.Hash) block := self.xeth.WithCtx(req.GetCtx()).EthBlockByHash(args.Hash)
if block == nil { if block == nil {
return nil, nil return nil, nil
} }
@ -231,7 +234,7 @@ func (self *ethApi) GetBlockTransactionCountByNumber(req *shared.Request) (inter
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
block := self.xeth.EthBlockByNumber(args.BlockNumber) block := self.xeth.WithCtx(req.GetCtx()).EthBlockByNumber(args.BlockNumber)
if block == nil { if block == nil {
return nil, nil return nil, nil
} }
@ -244,7 +247,7 @@ func (self *ethApi) GetUncleCountByBlockHash(req *shared.Request) (interface{},
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
block := self.xeth.EthBlockByHash(args.Hash) block := self.xeth.WithCtx(req.GetCtx()).EthBlockByHash(args.Hash)
if block == nil { if block == nil {
return nil, nil return nil, nil
} }
@ -257,7 +260,7 @@ func (self *ethApi) GetUncleCountByBlockNumber(req *shared.Request) (interface{}
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
block := self.xeth.EthBlockByNumber(args.BlockNumber) block := self.xeth.WithCtx(req.GetCtx()).EthBlockByNumber(args.BlockNumber)
if block == nil { if block == nil {
return nil, nil return nil, nil
} }
@ -269,7 +272,7 @@ func (self *ethApi) GetData(req *shared.Request) (interface{}, error) {
if err := self.codec.Decode(req.Params, &args); err != nil { if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
v := self.xeth.AtStateNum(args.BlockNumber).CodeAtBytes(args.Address) v := self.xeth.WithCtx(req.GetCtx()).AtStateNum(args.BlockNumber).CodeAtBytes(args.Address)
return newHexData(v), nil return newHexData(v), nil
} }
@ -337,7 +340,7 @@ func (self *ethApi) GetNatSpec(req *shared.Request) (interface{}, error) {
} }
func (self *ethApi) EstimateGas(req *shared.Request) (interface{}, error) { func (self *ethApi) EstimateGas(req *shared.Request) (interface{}, error) {
_, gas, err := self.doCall(req.Params) _, gas, err := self.doCall(req.GetCtx(), req.Params)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -351,7 +354,7 @@ func (self *ethApi) EstimateGas(req *shared.Request) (interface{}, error) {
} }
func (self *ethApi) Call(req *shared.Request) (interface{}, error) { func (self *ethApi) Call(req *shared.Request) (interface{}, error) {
v, _, err := self.doCall(req.Params) v, _, err := self.doCall(req.GetCtx(), req.Params)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -368,13 +371,13 @@ func (self *ethApi) Flush(req *shared.Request) (interface{}, error) {
return nil, shared.NewNotImplementedError(req.Method) return nil, shared.NewNotImplementedError(req.Method)
} }
func (self *ethApi) doCall(params json.RawMessage) (string, string, error) { func (self *ethApi) doCall(ctx context.Context, params json.RawMessage) (string, string, error) {
args := new(CallArgs) args := new(CallArgs)
if err := self.codec.Decode(params, &args); err != nil { if err := self.codec.Decode(params, &args); err != nil {
return "", "", err return "", "", err
} }
return self.xeth.AtStateNum(args.BlockNumber).Call(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data) return self.xeth.WithCtx(ctx).AtStateNum(args.BlockNumber).Call(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data)
} }
func (self *ethApi) GetBlockByHash(req *shared.Request) (interface{}, error) { func (self *ethApi) GetBlockByHash(req *shared.Request) (interface{}, error) {
@ -382,7 +385,7 @@ func (self *ethApi) GetBlockByHash(req *shared.Request) (interface{}, error) {
if err := self.codec.Decode(req.Params, &args); err != nil { if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
block := self.xeth.EthBlockByHash(args.BlockHash) block := self.xeth.WithCtx(req.GetCtx()).EthBlockByHash(args.BlockHash)
if block == nil { if block == nil {
return nil, nil return nil, nil
} }
@ -395,7 +398,7 @@ func (self *ethApi) GetBlockByNumber(req *shared.Request) (interface{}, error) {
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
block := self.xeth.EthBlockByNumber(args.BlockNumber) block := self.xeth.WithCtx(req.GetCtx()).EthBlockByNumber(args.BlockNumber)
if block == nil { if block == nil {
return nil, nil return nil, nil
} }
@ -408,7 +411,7 @@ func (self *ethApi) GetTransactionByHash(req *shared.Request) (interface{}, erro
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
tx, bhash, bnum, txi := self.xeth.EthTransactionByHash(args.Hash) tx, bhash, bnum, txi := self.xeth.EthTransactionByHash(args.Hash) //ODR
if tx != nil { if tx != nil {
v := NewTransactionRes(tx) v := NewTransactionRes(tx)
// if the blockhash is 0, assume this is a pending transaction // if the blockhash is 0, assume this is a pending transaction
@ -428,7 +431,7 @@ func (self *ethApi) GetTransactionByBlockHashAndIndex(req *shared.Request) (inte
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
raw := self.xeth.EthBlockByHash(args.Hash) raw := self.xeth.WithCtx(req.GetCtx()).EthBlockByHash(args.Hash)
if raw == nil { if raw == nil {
return nil, nil return nil, nil
} }
@ -446,7 +449,7 @@ func (self *ethApi) GetTransactionByBlockNumberAndIndex(req *shared.Request) (in
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
raw := self.xeth.EthBlockByNumber(args.BlockNumber) raw := self.xeth.WithCtx(req.GetCtx()).EthBlockByNumber(args.BlockNumber)
if raw == nil { if raw == nil {
return nil, nil return nil, nil
} }
@ -464,7 +467,7 @@ func (self *ethApi) GetUncleByBlockHashAndIndex(req *shared.Request) (interface{
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
raw := self.xeth.EthBlockByHash(args.Hash) raw := self.xeth.WithCtx(req.GetCtx()).EthBlockByHash(args.Hash)
if raw == nil { if raw == nil {
return nil, nil return nil, nil
} }
@ -482,7 +485,7 @@ func (self *ethApi) GetUncleByBlockNumberAndIndex(req *shared.Request) (interfac
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
raw := self.xeth.EthBlockByNumber(args.BlockNumber) raw := self.xeth.WithCtx(req.GetCtx()).EthBlockByNumber(args.BlockNumber)
if raw == nil { if raw == nil {
return nil, nil return nil, nil
} }
@ -662,7 +665,7 @@ func (self *ethApi) GetTransactionReceipt(req *shared.Request) (interface{}, err
txhash := common.BytesToHash(common.FromHex(args.Hash)) txhash := common.BytesToHash(common.FromHex(args.Hash))
tx, bhash, bnum, txi := self.xeth.EthTransactionByHash(args.Hash) tx, bhash, bnum, txi := self.xeth.EthTransactionByHash(args.Hash)
rec := self.xeth.GetTxReceipt(txhash) rec := self.xeth.GetTxReceipt(txhash) //ODR
// We could have an error of "not found". Should disambiguate // We could have an error of "not found". Should disambiguate
// if err != nil { // if err != nil {
// return err, nil // return err, nil

View file

@ -19,12 +19,12 @@ package comms
import ( import (
"io" "io"
"net" "net"
"fmt" "fmt"
"strings" "strings"
"strconv" "strconv"
"time"
"github.com/ethereum/go-ethereum/core/access"
"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/rpc/codec" "github.com/ethereum/go-ethereum/rpc/codec"
@ -69,6 +69,8 @@ func handle(id int, conn net.Conn, api shared.EthereumApi, c codec.Codec) {
codec.Close() codec.Close()
}() }()
channelID := access.NewChannelID(time.Second)
for { for {
requests, isBatch, err := codec.ReadRequest() requests, isBatch, err := codec.ReadRequest()
if err == io.EOF { if err == io.EOF {
@ -78,11 +80,21 @@ func handle(id int, conn net.Conn, api shared.EthereumApi, c codec.Codec) {
return return
} }
ctx, _ := access.NewContext(channelID)
if isBatch { if isBatch {
responses := make([]*interface{}, len(requests)) responses := make([]*interface{}, len(requests))
responseCount := 0 responseCount := 0
var res interface{}
var err error
for _, req := range requests { for _, req := range requests {
res, err := api.Execute(req) if access.Terminated(ctx) {
res = nil
err = ctx.Err()
} else {
req.SetCtx(ctx)
res, err = api.Execute(req)
}
if req.Id != nil { if req.Id != nil {
rpcResponse := shared.NewRpcResponse(req.Id, req.Jsonrpc, res, err) rpcResponse := shared.NewRpcResponse(req.Id, req.Jsonrpc, res, err)
responses[responseCount] = rpcResponse responses[responseCount] = rpcResponse
@ -97,7 +109,12 @@ func handle(id int, conn net.Conn, api shared.EthereumApi, c codec.Codec) {
} }
} else { } else {
var rpcResponse interface{} var rpcResponse interface{}
requests[0].SetCtx(ctx)
res, err := api.Execute(requests[0]) res, err := api.Execute(requests[0])
if access.Terminated(ctx) {
res = nil
err = ctx.Err()
}
rpcResponse = shared.NewRpcResponse(requests[0].Id, requests[0].Jsonrpc, res, err) rpcResponse = shared.NewRpcResponse(requests[0].Id, requests[0].Jsonrpc, res, err)
err = codec.WriteResponse(rpcResponse) err = codec.WriteResponse(rpcResponse)

View file

@ -29,6 +29,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"github.com/ethereum/go-ethereum/core/access"
"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/rpc/codec" "github.com/ethereum/go-ethereum/rpc/codec"
@ -67,13 +68,14 @@ type stopServer struct {
type handler struct { type handler struct {
codec codec.Codec codec codec.Codec
api shared.EthereumApi api shared.EthereumApi
channelID *access.OdrChannelID
} }
// StartHTTP starts listening for RPC requests sent via HTTP. // StartHTTP starts listening for RPC requests sent via HTTP.
func StartHttp(cfg HttpConfig, codec codec.Codec, api shared.EthereumApi) error { func StartHttp(cfg HttpConfig, codec codec.Codec, api shared.EthereumApi) error {
httpServerMu.Lock() httpServerMu.Lock()
defer httpServerMu.Unlock() defer httpServerMu.Unlock()
addr := fmt.Sprintf("%s:%d", cfg.ListenAddress, cfg.ListenPort) addr := fmt.Sprintf("%s:%d", cfg.ListenAddress, cfg.ListenPort)
if httpServer != nil { if httpServer != nil {
if addr != httpServer.Addr { if addr != httpServer.Addr {
@ -82,7 +84,8 @@ func StartHttp(cfg HttpConfig, codec codec.Codec, api shared.EthereumApi) error
return nil // RPC service already running on given host/port return nil // RPC service already running on given host/port
} }
// Set up the request handler, wrapping it with CORS headers if configured. // Set up the request handler, wrapping it with CORS headers if configured.
handler := http.Handler(&handler{codec, api}) channelID := access.NewChannelID(time.Second)
handler := http.Handler(&handler{codec, api, channelID})
if len(cfg.CorsDomain) > 0 { if len(cfg.CorsDomain) > 0 {
opts := cors.Options{ opts := cors.Options{
AllowedMethods: []string{"POST"}, AllowedMethods: []string{"POST"},
@ -121,9 +124,15 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
} }
c := h.codec.New(nil) c := h.codec.New(nil)
ctx, _ := access.NewContext(h.channelID)
var rpcReq shared.Request var rpcReq shared.Request
if err = c.Decode(payload, &rpcReq); err == nil { if err = c.Decode(payload, &rpcReq); err == nil {
rpcReq.SetCtx(ctx)
reply, err := h.api.Execute(&rpcReq) reply, err := h.api.Execute(&rpcReq)
if access.Terminated(ctx) {
reply = nil
err = ctx.Err()
}
res := shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err) res := shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err)
sendJSON(w, &res) sendJSON(w, &res)
return return
@ -133,8 +142,16 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if err = c.Decode(payload, &reqBatch); err == nil { if err = c.Decode(payload, &reqBatch); err == nil {
resBatch := make([]*interface{}, len(reqBatch)) resBatch := make([]*interface{}, len(reqBatch))
resCount := 0 resCount := 0
var reply interface{}
var err error
for i, rpcReq := range reqBatch { for i, rpcReq := range reqBatch {
reply, err := h.api.Execute(&rpcReq) if access.Terminated(ctx) {
reply = nil
err = ctx.Err()
} else {
rpcReq.SetCtx(ctx)
reply, err = h.api.Execute(&rpcReq)
}
if rpcReq.Id != nil { // this leaves nil entries in the response batch for later removal if rpcReq.Id != nil { // this leaves nil entries in the response batch for later removal
resBatch[i] = shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err) resBatch[i] = shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err)
resCount += 1 resCount += 1

View file

@ -18,7 +18,9 @@ package comms
import ( import (
"fmt" "fmt"
"time"
"github.com/ethereum/go-ethereum/core/access"
"github.com/ethereum/go-ethereum/rpc/codec" "github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/shared" "github.com/ethereum/go-ethereum/rpc/shared"
) )
@ -30,12 +32,14 @@ type InProcClient struct {
lastJsonrpc string lastJsonrpc string
lastErr error lastErr error
lastRes interface{} lastRes interface{}
channelID *access.OdrChannelID
} }
// Create a new in process client // Create a new in process client
func NewInProcClient(codec codec.Codec) *InProcClient { func NewInProcClient(codec codec.Codec) *InProcClient {
return &InProcClient{ return &InProcClient{
codec: codec, codec: codec,
channelID: access.NewChannelID(time.Second),
} }
} }
@ -52,7 +56,13 @@ func (self *InProcClient) Send(req interface{}) error {
if r, ok := req.(*shared.Request); ok { if r, ok := req.(*shared.Request); ok {
self.lastId = r.Id self.lastId = r.Id
self.lastJsonrpc = r.Jsonrpc self.lastJsonrpc = r.Jsonrpc
ctx, _ := access.NewContext(self.channelID)
r.SetCtx(ctx)
self.lastRes, self.lastErr = self.api.Execute(r) self.lastRes, self.lastErr = self.api.Execute(r)
if access.Terminated(ctx) {
self.lastRes = nil
self.lastErr = ctx.Err()
}
return self.lastErr return self.lastErr
} }

View file

@ -21,6 +21,7 @@ import (
"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"
"golang.org/x/net/context"
) )
// Ethereum RPC API interface // Ethereum RPC API interface
@ -39,11 +40,14 @@ type EthereumApi interface {
} }
// RPC request // RPC request
// (ODR context is propagated inside the request struct because every
// request has its own context. It does not influence JSON encoding.)
type Request struct { type Request struct {
Id interface{} `json:"id"` Id interface{} `json:"id"`
Jsonrpc string `json:"jsonrpc"` Jsonrpc string `json:"jsonrpc"`
Method string `json:"method"` Method string `json:"method"`
Params json.RawMessage `json:"params"` Params json.RawMessage `json:"params"`
ctx context.Context
} }
// RPC response // RPC response
@ -73,6 +77,16 @@ type ErrorObject struct {
// Data interface{} `json:"data"` // Data interface{} `json:"data"`
} }
// GetCtx returns the ODR context assigned to the request
func (req *Request) GetCtx() context.Context {
return req.ctx
}
// SetCtx assigns an ODR context to the request
func (req *Request) SetCtx(ctx context.Context) {
req.ctx = ctx
}
// Create RPC error response, this allows for custom error codes // Create RPC error response, this allows for custom error codes
func NewRpcErrorResponse(id interface{}, jsonrpcver string, errCode int, err error) *ErrorResponse { func NewRpcErrorResponse(id interface{}, jsonrpcver string, errCode int, err error) *ErrorResponse {
jsonerr := &ErrorObject{errCode, err.Error()} jsonerr := &ErrorObject{errCode, err.Error()}

View file

@ -31,6 +31,7 @@ import (
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"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/access"
"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/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -217,7 +218,7 @@ func runBlockTest(test *BlockTest) error {
// InsertPreState populates the given database with the genesis // InsertPreState populates the given database with the genesis
// accounts defined by the test. // accounts defined by the test.
func (t *BlockTest) InsertPreState(db ethdb.Database, am *accounts.Manager) (*state.StateDB, error) { func (t *BlockTest) InsertPreState(db ethdb.Database, am *accounts.Manager) (*state.StateDB, error) {
statedb, err := state.New(common.Hash{}, db) statedb, err := state.New(common.Hash{}, access.NewDbChainAccess(db))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -27,6 +27,7 @@ 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/access"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -103,7 +104,7 @@ func BenchStateTest(p string, conf bconf, b *testing.B) error {
func benchStateTest(test VmTest, env map[string]string, b *testing.B) { func benchStateTest(test VmTest, env map[string]string, b *testing.B) {
b.StopTimer() b.StopTimer()
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db))
for addr, account := range test.Pre { for addr, account := range test.Pre {
obj := StateObjectFromAccount(db, addr, account) obj := StateObjectFromAccount(db, addr, account)
statedb.SetStateObject(obj) statedb.SetStateObject(obj)
@ -142,7 +143,7 @@ func runStateTests(tests map[string]VmTest, skipTests []string) error {
func runStateTest(test VmTest) error { func runStateTest(test VmTest) error {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db))
for addr, account := range test.Pre { for addr, account := range test.Pre {
obj := StateObjectFromAccount(db, addr, account) obj := StateObjectFromAccount(db, addr, account)
statedb.SetStateObject(obj) statedb.SetStateObject(obj)

View file

@ -23,6 +23,7 @@ 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/access"
"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/core/vm" "github.com/ethereum/go-ethereum/core/vm"
@ -89,7 +90,7 @@ func (self Log) Topics() [][]byte {
} }
func StateObjectFromAccount(db ethdb.Database, addr string, account Account) *state.StateObject { func StateObjectFromAccount(db ethdb.Database, addr string, account Account) *state.StateObject {
obj := state.NewStateObject(common.HexToAddress(addr), db) obj := state.NewStateObject(access.NoOdr, common.HexToAddress(addr), access.NewDbChainAccess(db))
obj.SetBalance(common.Big(account.Balance)) obj.SetBalance(common.Big(account.Balance))
if common.IsHex(account.Code) { if common.IsHex(account.Code) {

View file

@ -25,6 +25,7 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/access"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
@ -108,7 +109,7 @@ func BenchVmTest(p string, conf bconf, b *testing.B) error {
func benchVmTest(test VmTest, env map[string]string, b *testing.B) { func benchVmTest(test VmTest, env map[string]string, b *testing.B) {
b.StopTimer() b.StopTimer()
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db))
for addr, account := range test.Pre { for addr, account := range test.Pre {
obj := StateObjectFromAccount(db, addr, account) obj := StateObjectFromAccount(db, addr, account)
statedb.SetStateObject(obj) statedb.SetStateObject(obj)
@ -159,7 +160,7 @@ func runVmTests(tests map[string]VmTest, skipTests []string) error {
func runVmTest(test VmTest) error { func runVmTest(test VmTest) error {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db))
for addr, account := range test.Pre { for addr, account := range test.Pre {
obj := StateObjectFromAccount(db, addr, account) obj := StateObjectFromAccount(db, addr, account)
statedb.SetStateObject(obj) statedb.SetStateObject(obj)

View file

@ -62,6 +62,17 @@ func newARC(c int) *arc {
} }
} }
func (a *arc) Clear() {
a.mutex.Lock()
defer a.mutex.Unlock()
a.p = 0
a.t1 = list.New()
a.b1 = list.New()
a.t2 = list.New()
a.b2 = list.New()
a.cache = make(map[string]*entry, a.c)
}
// Put inserts a new key-value pair into the cache. // Put inserts a new key-value pair into the cache.
// This optimizes future access to this entry (side effect). // This optimizes future access to this entry (side effect).
func (a *arc) Put(key hashNode, value node) bool { func (a *arc) Put(key hashNode, value node) bool {

View file

@ -58,6 +58,7 @@ func compactDecode(str []byte) []byte {
return base return base
} }
// compactHexDecode decodes a series of nibbles from a byte array
func compactHexDecode(str []byte) []byte { func compactHexDecode(str []byte) []byte {
l := len(str)*2 + 1 l := len(str)*2 + 1
var nibbles = make([]byte, l) var nibbles = make([]byte, l)
@ -69,6 +70,24 @@ func compactHexDecode(str []byte) []byte {
return nibbles return nibbles
} }
// compactHexEncode encodes a series of nibbles into a byte array
func compactHexEncode(nibbles []byte) []byte {
nl := len(nibbles)
if nibbles[nl-1] == 16 {
nl--
}
l := (nl + 1) / 2
var str = make([]byte, l)
for i, _ := range str {
b := nibbles[i*2] * 16
if nl > i*2 {
b += nibbles[i*2+1]
}
str[i] = b
}
return str
}
func decodeCompact(key []byte) []byte { func decodeCompact(key []byte) []byte {
l := len(key) / 2 l := len(key) / 2
var res = make([]byte, l) var res = make([]byte, l)

View file

@ -57,6 +57,12 @@ func (s *TrieEncodingSuite) TestCompactHexDecode(c *checker.C) {
c.Assert(res, checker.DeepEquals, exp) c.Assert(res, checker.DeepEquals, exp)
} }
func (s *TrieEncodingSuite) TestCompactHexEncode(c *checker.C) {
exp := []byte("verb")
res := compactHexEncode([]byte{7, 6, 6, 5, 7, 2, 6, 2, 16})
c.Assert(res, checker.DeepEquals, exp)
}
func (s *TrieEncodingSuite) TestCompactDecode(c *checker.C) { func (s *TrieEncodingSuite) TestCompactDecode(c *checker.C) {
// odd compact decode // odd compact decode
exp := []byte{1, 2, 3, 4, 5} exp := []byte{1, 2, 3, 4, 5}

View file

@ -100,7 +100,7 @@ func (self *Iterator) next(node interface{}, key []byte, isIterStart bool) []byt
} }
case hashNode: case hashNode:
return self.next(self.trie.resolveHash(node), key, isIterStart) return self.next(self.trie.resolveHash(node, nil, nil), key, isIterStart)
} }
return nil return nil
} }
@ -127,7 +127,7 @@ func (self *Iterator) key(node interface{}) []byte {
} }
} }
case hashNode: case hashNode:
return self.key(self.trie.resolveHash(node)) return self.key(self.trie.resolveHash(node, nil, nil))
} }
return nil return nil

View file

@ -10,6 +10,8 @@ import (
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
type MerkleProof []rlp.RawValue
// Prove constructs a merkle proof for key. The result contains all // Prove constructs a merkle proof for key. The result contains all
// encoded nodes on the path to the value at key. The value itself is // encoded nodes on the path to the value at key. The value itself is
// also included in the last node and can be retrieved by verifying // also included in the last node and can be retrieved by verifying
@ -17,29 +19,28 @@ import (
// //
// The returned proof is nil if the trie does not contain a value for key. // The returned proof is nil if the trie does not contain a value for key.
// For existing keys, the proof will have at least one element. // For existing keys, the proof will have at least one element.
func (t *Trie) Prove(key []byte) []rlp.RawValue { func (t *Trie) Prove(key []byte) MerkleProof {
// Collect all nodes on the path to key. // Collect all nodes on the path to key.
key = compactHexDecode(key) key = compactHexDecode(key)
nodes := []node{} nodes := []node{}
tn := t.root tn := t.root
for len(key) > 0 { for len(key) > 0 && tn != nil {
switch n := tn.(type) { switch n := tn.(type) {
case shortNode: case shortNode:
if len(key) < len(n.Key) || !bytes.Equal(n.Key, key[:len(n.Key)]) { if len(key) < len(n.Key) || !bytes.Equal(n.Key, key[:len(n.Key)]) {
// The trie doesn't contain the key. // The trie doesn't contain the key.
return nil tn = nil
} else {
tn = n.Val
} }
tn = n.Val
key = key[len(n.Key):] key = key[len(n.Key):]
nodes = append(nodes, n) nodes = append(nodes, n)
case fullNode: case fullNode:
tn = n[key[0]] tn = n[key[0]]
key = key[1:] key = key[1:]
nodes = append(nodes, n) nodes = append(nodes, n)
case nil:
return nil
case hashNode: case hashNode:
tn = t.resolveHash(n) tn = t.resolveHash(n, nil, nil)
default: default:
panic(fmt.Sprintf("%T: invalid node: %v", tn, tn)) panic(fmt.Sprintf("%T: invalid node: %v", tn, tn))
} }
@ -67,7 +68,7 @@ func (t *Trie) Prove(key []byte) []rlp.RawValue {
// value for key in a trie with the given root hash. VerifyProof // value for key in a trie with the given root hash. VerifyProof
// returns an error if the proof contains invalid trie nodes or the // returns an error if the proof contains invalid trie nodes or the
// wrong value. // wrong value.
func VerifyProof(rootHash common.Hash, key []byte, proof []rlp.RawValue) (value []byte, err error) { func VerifyProof(rootHash common.Hash, key []byte, proof MerkleProof) (value []byte, err error) {
key = compactHexDecode(key) key = compactHexDecode(key)
sha := sha3.NewKeccak256() sha := sha3.NewKeccak256()
wantHash := rootHash.Bytes() wantHash := rootHash.Bytes()
@ -84,7 +85,12 @@ func VerifyProof(rootHash common.Hash, key []byte, proof []rlp.RawValue) (value
keyrest, cld := get(n, key) keyrest, cld := get(n, key)
switch cld := cld.(type) { switch cld := cld.(type) {
case nil: case nil:
return nil, fmt.Errorf("key mismatch at proof node %d", i) if i != len(proof)-1 {
return nil, fmt.Errorf("key mismatch at proof node %d", i)
} else {
// The trie doesn't contain the key.
return nil, nil
}
case hashNode: case hashNode:
key = keyrest key = keyrest
wantHash = cld wantHash = cld
@ -98,6 +104,20 @@ func VerifyProof(rootHash common.Hash, key []byte, proof []rlp.RawValue) (value
return nil, errors.New("unexpected end of proof") return nil, errors.New("unexpected end of proof")
} }
// StoreProof stores the new trie nodes obtained from a merkle proof in the database
func StoreProof(db Database, proof MerkleProof) {
sha := sha3.NewKeccak256()
for _, buf := range proof {
sha.Reset()
sha.Write(buf)
hash := sha.Sum(nil)
val, _ := db.Get(hash)
if val == nil {
db.Put(hash, buf)
}
}
}
func get(tn node, key []byte) ([]byte, node) { func get(tn node, key []byte) ([]byte, node) {
for len(key) > 0 { for len(key) > 0 {
switch n := tn.(type) { switch n := tn.(type) {

View file

@ -8,6 +8,7 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
@ -33,6 +34,33 @@ func TestProof(t *testing.T) {
} }
} }
func TestStoreProof(t *testing.T) {
trie, vals := randomTrie(500)
root := trie.Hash()
mdb, _ := ethdb.NewMemDatabase()
for _, kv := range vals {
proof := trie.Prove(kv.k)
if proof == nil {
t.Fatalf("missing key %x while constructing proof", kv.k)
}
val, err := VerifyProof(root, kv.k, proof)
if err != nil {
t.Fatalf("VerifyProof error for key %x: %v\nraw proof: %x", kv.k, err, proof)
}
if !bytes.Equal(val, kv.v) {
t.Fatalf("VerifyProof returned wrong value for key %x: got %x, want %x", kv.k, val, kv.v)
}
StoreProof(mdb, proof)
}
ClearGlobalCache()
mtrie, _ := New(root, mdb)
for _, kv := range vals {
if !bytes.Equal(mtrie.Get(kv.k), kv.v) {
t.Fatalf("Can't retrieve stored proof")
}
}
}
func TestOneElementProof(t *testing.T) { func TestOneElementProof(t *testing.T) {
trie := new(Trie) trie := new(Trie)
updateString(trie, "k", "v") updateString(trie, "k", "v")

View file

@ -20,7 +20,9 @@ import (
"hash" "hash"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/access"
"github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/crypto/sha3"
"golang.org/x/net/context"
) )
var secureKeyPrefix = []byte("secure-key-") var secureKeyPrefix = []byte("secure-key-")
@ -50,16 +52,32 @@ type SecureTrie struct {
// and returns ErrMissingRoot if the root node cannpt be found. // and returns ErrMissingRoot if the root node cannpt be found.
// Accessing the trie loads nodes from db on demand. // Accessing the trie loads nodes from db on demand.
func NewSecure(root common.Hash, db Database) (*SecureTrie, error) { func NewSecure(root common.Hash, db Database) (*SecureTrie, error) {
return NewSecureOdr(access.NoOdr, root, db, nil)
}
// NewSecureOdr creates a trie with optional ODR. If ODR is enabled, an existing
// root node in db is not required.
func NewSecureOdr(ctx context.Context, root common.Hash, db Database, access OdrAccess) (*SecureTrie, error) {
if db == nil { if db == nil {
panic("NewSecure called with nil database") panic("NewSecure called with nil database")
} }
trie, err := New(root, db) trie, err := NewOdr(ctx, root, db, access)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &SecureTrie{Trie: trie}, nil return &SecureTrie{Trie: trie}, nil
} }
// CopySecureWithOdr creates a copy of a trie with additional ODR capability
func (t *SecureTrie) CopySecureWithOdr(ctx context.Context, access OdrAccess) *SecureTrie {
return &SecureTrie{
Trie: t.Trie.CopyWithOdr(ctx, access),
hash: t.hash,
secKeyBuf: t.secKeyBuf,
hashKeyBuf: t.hashKeyBuf,
}
}
// Get returns the value for key stored in the trie. // Get returns the value for key stored in the trie.
// The value bytes must not be modified by the caller. // The value bytes must not be modified by the caller.
func (t *SecureTrie) Get(key []byte) []byte { func (t *SecureTrie) Get(key []byte) []byte {

View file

@ -24,11 +24,13 @@ import (
"hash" "hash"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/access"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/crypto/sha3"
"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/rlp" "github.com/ethereum/go-ethereum/rlp"
"golang.org/x/net/context"
) )
const defaultCacheCapacity = 800 const defaultCacheCapacity = 800
@ -44,8 +46,18 @@ var (
emptyState = crypto.Sha3Hash(nil) emptyState = crypto.Sha3Hash(nil)
) )
func ClearGlobalCache() {
globalCache.Clear()
}
var ErrMissingRoot = errors.New("missing root node") var ErrMissingRoot = errors.New("missing root node")
// OdrAccess is an interface to on-demand network access layer
type OdrAccess interface {
OdrEnabled() bool // is network access actually enabled
RetrieveKey(ctx context.Context, key []byte) bool // retrieve key from network and store it in local db
}
// Database must be implemented by backing stores for the trie. // Database must be implemented by backing stores for the trie.
type Database interface { type Database interface {
DatabaseWriter DatabaseWriter
@ -67,8 +79,10 @@ type DatabaseWriter interface {
// //
// Trie is not safe for concurrent use. // Trie is not safe for concurrent use.
type Trie struct { type Trie struct {
root node root node
db Database db Database
access OdrAccess
ctx context.Context
*hasher *hasher
} }
@ -79,19 +93,32 @@ type Trie struct {
// New will panics if db is nil or root does not exist in the // New will panics if db is nil or root does not exist in the
// database. Accessing the trie loads nodes from db on demand. // database. Accessing the trie loads nodes from db on demand.
func New(root common.Hash, db Database) (*Trie, error) { func New(root common.Hash, db Database) (*Trie, error) {
trie := &Trie{db: db} return NewOdr(access.NoOdr, root, db, nil)
}
// NewOdr creates a trie with optional ODR. If ODR is enabled, an existing
// root node in db is not required.
func NewOdr(ctx context.Context, root common.Hash, db Database, access OdrAccess) (*Trie, error) {
trie := &Trie{db: db, access: access, ctx: ctx}
if (root != common.Hash{}) && root != emptyRoot { if (root != common.Hash{}) && root != emptyRoot {
if db == nil { if db == nil {
panic("trie.New: cannot use existing root without a database") panic("trie.New: cannot use existing root without a database")
} }
if v, _ := trie.db.Get(root[:]); len(v) == 0 { if access == nil || !access.OdrEnabled() {
return nil, ErrMissingRoot if v, _ := trie.db.Get(root[:]); len(v) == 0 {
return nil, ErrMissingRoot
}
} }
trie.root = hashNode(root.Bytes()) trie.root = hashNode(root.Bytes())
} }
return trie, nil return trie, nil
} }
// CopyWithOdr creates a copy of a trie with additional ODR capability
func (t *Trie) CopyWithOdr(ctx context.Context, access OdrAccess) *Trie {
return &Trie{db: t.db, root: t.root, access: access, ctx: ctx}
}
// Iterator returns an iterator over all mappings in the trie. // Iterator returns an iterator over all mappings in the trie.
func (t *Trie) Iterator() *Iterator { func (t *Trie) Iterator() *Iterator {
return NewIterator(t) return NewIterator(t)
@ -101,22 +128,23 @@ func (t *Trie) Iterator() *Iterator {
// The value bytes must not be modified by the caller. // The value bytes must not be modified by the caller.
func (t *Trie) Get(key []byte) []byte { func (t *Trie) Get(key []byte) []byte {
key = compactHexDecode(key) key = compactHexDecode(key)
pos := 0
tn := t.root tn := t.root
for len(key) > 0 { for pos < len(key) {
switch n := tn.(type) { switch n := tn.(type) {
case shortNode: case shortNode:
if len(key) < len(n.Key) || !bytes.Equal(n.Key, key[:len(n.Key)]) { if len(key)-pos < len(n.Key) || !bytes.Equal(n.Key, key[pos:pos+len(n.Key)]) {
return nil return nil
} }
tn = n.Val tn = n.Val
key = key[len(n.Key):] pos += len(n.Key)
case fullNode: case fullNode:
tn = n[key[0]] tn = n[key[pos]]
key = key[1:] pos++
case nil: case nil:
return nil return nil
case hashNode: case hashNode:
tn = t.resolveHash(n) tn = t.resolveHash(n, key[:pos], key[pos:])
default: default:
panic(fmt.Sprintf("%T: invalid node: %v", tn, tn)) panic(fmt.Sprintf("%T: invalid node: %v", tn, tn))
} }
@ -133,13 +161,13 @@ func (t *Trie) Get(key []byte) []byte {
func (t *Trie) Update(key, value []byte) { func (t *Trie) Update(key, value []byte) {
k := compactHexDecode(key) k := compactHexDecode(key)
if len(value) != 0 { if len(value) != 0 {
t.root = t.insert(t.root, k, valueNode(value)) t.root = t.insert(t.root, nil, k, valueNode(value))
} else { } else {
t.root = t.delete(t.root, k) t.root = t.delete(t.root, nil, k)
} }
} }
func (t *Trie) insert(n node, key []byte, value node) node { func (t *Trie) insert(n node, prefix, key []byte, value node) node {
if len(key) == 0 { if len(key) == 0 {
return value return value
} }
@ -149,12 +177,12 @@ func (t *Trie) insert(n node, key []byte, value node) node {
// If the whole key matches, keep this short node as is // If the whole key matches, keep this short node as is
// and only update the value. // and only update the value.
if matchlen == len(n.Key) { if matchlen == len(n.Key) {
return shortNode{n.Key, t.insert(n.Val, key[matchlen:], value)} return shortNode{n.Key, t.insert(n.Val, append(prefix, key[:matchlen]...), key[matchlen:], value)}
} }
// Otherwise branch out at the index where they differ. // Otherwise branch out at the index where they differ.
var branch fullNode var branch fullNode
branch[n.Key[matchlen]] = t.insert(nil, n.Key[matchlen+1:], n.Val) branch[n.Key[matchlen]] = t.insert(nil, append(prefix, n.Key[:matchlen+1]...), n.Key[matchlen+1:], n.Val)
branch[key[matchlen]] = t.insert(nil, key[matchlen+1:], value) branch[key[matchlen]] = t.insert(nil, append(prefix, key[:matchlen+1]...), key[matchlen+1:], value)
// Replace this shortNode with the branch if it occurs at index 0. // Replace this shortNode with the branch if it occurs at index 0.
if matchlen == 0 { if matchlen == 0 {
return branch return branch
@ -163,7 +191,7 @@ func (t *Trie) insert(n node, key []byte, value node) node {
return shortNode{key[:matchlen], branch} return shortNode{key[:matchlen], branch}
case fullNode: case fullNode:
n[key[0]] = t.insert(n[key[0]], key[1:], value) n[key[0]] = t.insert(n[key[0]], append(prefix, key[0]), key[1:], value)
return n return n
case nil: case nil:
@ -176,7 +204,7 @@ func (t *Trie) insert(n node, key []byte, value node) node {
// //
// TODO: track whether insertion changed the value and keep // TODO: track whether insertion changed the value and keep
// n as a hash node if it didn't. // n as a hash node if it didn't.
return t.insert(t.resolveHash(n), key, value) return t.insert(t.resolveHash(n, prefix, key), prefix, key, value)
default: default:
panic(fmt.Sprintf("%T: invalid node: %v", n, n)) panic(fmt.Sprintf("%T: invalid node: %v", n, n))
@ -186,13 +214,13 @@ func (t *Trie) insert(n node, key []byte, value node) node {
// Delete removes any existing value for key from the trie. // Delete removes any existing value for key from the trie.
func (t *Trie) Delete(key []byte) { func (t *Trie) Delete(key []byte) {
k := compactHexDecode(key) k := compactHexDecode(key)
t.root = t.delete(t.root, k) t.root = t.delete(t.root, nil, k)
} }
// delete returns the new root of the trie with key deleted. // delete returns the new root of the trie with key deleted.
// It reduces the trie to minimal form by simplifying // It reduces the trie to minimal form by simplifying
// nodes on the way up after deleting recursively. // nodes on the way up after deleting recursively.
func (t *Trie) delete(n node, key []byte) node { func (t *Trie) delete(n node, prefix, key []byte) node {
switch n := n.(type) { switch n := n.(type) {
case shortNode: case shortNode:
matchlen := prefixLen(key, n.Key) matchlen := prefixLen(key, n.Key)
@ -206,7 +234,7 @@ func (t *Trie) delete(n node, key []byte) node {
// from the subtrie. Child can never be nil here since the // from the subtrie. Child can never be nil here since the
// subtrie must contain at least two other values with keys // subtrie must contain at least two other values with keys
// longer than n.Key. // longer than n.Key.
child := t.delete(n.Val, key[len(n.Key):]) child := t.delete(n.Val, append(prefix, key[:len(n.Key)]...), key[len(n.Key):])
switch child := child.(type) { switch child := child.(type) {
case shortNode: case shortNode:
// Deleting from the subtrie reduced it to another // Deleting from the subtrie reduced it to another
@ -221,7 +249,7 @@ func (t *Trie) delete(n node, key []byte) node {
} }
case fullNode: case fullNode:
n[key[0]] = t.delete(n[key[0]], key[1:]) n[key[0]] = t.delete(n[key[0]], append(prefix, key[0]), key[1:])
// Check how many non-nil entries are left after deleting and // Check how many non-nil entries are left after deleting and
// reduce the full node to a short node if only one entry is // reduce the full node to a short node if only one entry is
// left. Since n must've contained at least two children // left. Since n must've contained at least two children
@ -250,7 +278,7 @@ func (t *Trie) delete(n node, key []byte) node {
// shortNode{..., shortNode{...}}. Since the entry // shortNode{..., shortNode{...}}. Since the entry
// might not be loaded yet, resolve it just for this // might not be loaded yet, resolve it just for this
// check. // check.
cnode := t.resolve(n[pos]) cnode := t.resolve(n[pos], prefix, []byte{byte(pos)})
if cnode, ok := cnode.(shortNode); ok { if cnode, ok := cnode.(shortNode); ok {
k := append([]byte{byte(pos)}, cnode.Key...) k := append([]byte{byte(pos)}, cnode.Key...)
return shortNode{k, cnode.Val} return shortNode{k, cnode.Val}
@ -273,7 +301,7 @@ func (t *Trie) delete(n node, key []byte) node {
// //
// TODO: track whether deletion actually hit a key and keep // TODO: track whether deletion actually hit a key and keep
// n as a hash node if it didn't. // n as a hash node if it didn't.
return t.delete(t.resolveHash(n), key) return t.delete(t.resolveHash(n, prefix, key), prefix, key)
default: default:
panic(fmt.Sprintf("%T: invalid node: %v (%v)", n, n, key)) panic(fmt.Sprintf("%T: invalid node: %v (%v)", n, n, key))
@ -287,19 +315,28 @@ func concat(s1 []byte, s2 ...byte) []byte {
return r return r
} }
func (t *Trie) resolve(n node) node { func (t *Trie) resolve(n node, prefix, suffix []byte) node {
if n, ok := n.(hashNode); ok { if n, ok := n.(hashNode); ok {
return t.resolveHash(n) return t.resolveHash(n, prefix, suffix)
} }
return n return n
} }
func (t *Trie) resolveHash(n hashNode) node { func (t *Trie) resolveHash(n hashNode, prefix, suffix []byte) node {
if v, ok := globalCache.Get(n); ok { if v, ok := globalCache.Get(n); ok {
return v return v
} }
enc, err := t.db.Get(n) enc, err := t.db.Get(n)
if err != nil || enc == nil { if err != nil || enc == nil {
if prefix != nil || suffix != nil {
key := compactHexEncode(append(prefix, suffix...))
if t.access != nil && t.access.RetrieveKey(t.ctx, key) {
enc, err = t.db.Get(n)
}
}
}
if err != nil || enc == nil {
// TODO: This needs to be improved to properly distinguish errors. // TODO: This needs to be improved to properly distinguish errors.
// Disk I/O errors shouldn't produce nil (and cause a // Disk I/O errors shouldn't produce nil (and cause a
// consensus failure or weird crash), but it is unclear how // consensus failure or weird crash), but it is unclear how
@ -348,6 +385,11 @@ func (t *Trie) Commit() (root common.Hash, err error) {
// the changes made to db are written back to the trie's attached // the changes made to db are written back to the trie's attached
// database before using the trie. // database before using the trie.
func (t *Trie) CommitTo(db DatabaseWriter) (root common.Hash, err error) { func (t *Trie) CommitTo(db DatabaseWriter) (root common.Hash, err error) {
if access.Terminated(t.ctx) {
// we should never commit potentially corrupt trie nodes to the db
return common.Hash{}, t.ctx.Err()
}
n, err := t.hashRoot(db) n, err := t.hashRoot(db)
if err != nil { if err != nil {
return (common.Hash{}), err return (common.Hash{}), err

View file

@ -45,7 +45,7 @@ func (self *State) SafeGet(addr string) *Object {
func (self *State) safeGet(addr string) *state.StateObject { func (self *State) safeGet(addr string) *state.StateObject {
object := self.state.GetStateObject(common.HexToAddress(addr)) object := self.state.GetStateObject(common.HexToAddress(addr))
if object == nil { if object == nil {
object = state.NewStateObject(common.HexToAddress(addr), self.xeth.backend.ChainDb()) object = state.NewStateObject(self.state.Ctx(), common.HexToAddress(addr), self.xeth.backend.ChainAccess())
} }
return object return object

View file

@ -41,6 +41,7 @@ import (
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"golang.org/x/net/context"
) )
var ( var (
@ -84,6 +85,7 @@ type XEth struct {
state *State state *State
whisper *Whisper whisper *Whisper
filterManager *filters.FilterSystem filterManager *filters.FilterSystem
ctx context.Context
} }
func NewTest(eth *eth.Ethereum, frontend Frontend) *XEth { func NewTest(eth *eth.Ethereum, frontend Frontend) *XEth {
@ -207,15 +209,15 @@ func (self *XEth) AtStateNum(num int64) *XEth {
var err error var err error
switch num { switch num {
case -2: case -2:
st = self.backend.Miner().PendingState().Copy() st = self.backend.Miner().PendingState().CopyOdr(self.ctx)
default: default:
if block := self.getBlockByHeight(num); block != nil { if block := self.getBlockByHeight(num); block != nil {
st, err = state.New(block.Root(), self.backend.ChainDb()) st, err = state.NewOdr(self.ctx, block.Root(), self.backend.ChainAccess())
if err != nil { if err != nil {
return nil return nil
} }
} else { } else {
st, err = state.New(self.backend.BlockChain().GetBlockByNumber(0).Root(), self.backend.ChainDb()) st, err = state.NewOdr(self.ctx, self.backend.BlockChain().GetBlockByNumberOdr(self.ctx, 0).Root(), self.backend.ChainAccess())
if err != nil { if err != nil {
return nil return nil
} }
@ -238,6 +240,19 @@ func (self *XEth) WithState(statedb *state.StateDB) *XEth {
func (self *XEth) State() *State { return self.state } func (self *XEth) State() *State { return self.state }
// WithCtx creates a copy of the XEth object with a new ODR context
func (self *XEth) WithCtx(ctx context.Context) *XEth {
xeth := &XEth{
backend: self.backend,
frontend: self.frontend,
gpo: self.gpo,
ctx: ctx,
}
xeth.state = NewState(xeth, self.state.State().CopyOdr(ctx))
return xeth
}
// subscribes to new head block events and // subscribes to new head block events and
// waits until blockchain height is greater n at any time // waits until blockchain height is greater n at any time
// given the current head, waits for the next chain event // given the current head, waits for the next chain event
@ -270,7 +285,7 @@ func (self *XEth) UpdateState() (wait chan *big.Int) {
wait <- n wait <- n
n = nil n = nil
} }
statedb, err := state.New(event.Block.Root(), self.backend.ChainDb()) statedb, err := state.New(event.Block.Root(), self.backend.ChainAccess())
if err != nil { if err != nil {
glog.V(logger.Error).Infoln("Could not create new state: %v", err) glog.V(logger.Error).Infoln("Could not create new state: %v", err)
return return
@ -305,19 +320,19 @@ func (self *XEth) getBlockByHeight(height int64) *types.Block {
num = uint64(height) num = uint64(height)
} }
return self.backend.BlockChain().GetBlockByNumber(num) return self.backend.BlockChain().GetBlockByNumberOdr(self.ctx, num)
} }
func (self *XEth) BlockByHash(strHash string) *Block { func (self *XEth) BlockByHash(strHash string) *Block {
hash := common.HexToHash(strHash) hash := common.HexToHash(strHash)
block := self.backend.BlockChain().GetBlock(hash) block := self.backend.BlockChain().GetBlockOdr(self.ctx, hash)
return NewBlock(block) return NewBlock(block)
} }
func (self *XEth) EthBlockByHash(strHash string) *types.Block { func (self *XEth) EthBlockByHash(strHash string) *types.Block {
hash := common.HexToHash(strHash) hash := common.HexToHash(strHash)
block := self.backend.BlockChain().GetBlock(hash) block := self.backend.BlockChain().GetBlockOdr(self.ctx, hash)
return block return block
} }
@ -379,11 +394,11 @@ func (self *XEth) CurrentBlock() *types.Block {
} }
func (self *XEth) GetBlockReceipts(bhash common.Hash) types.Receipts { func (self *XEth) GetBlockReceipts(bhash common.Hash) types.Receipts {
return self.backend.BlockProcessor().GetBlockReceipts(bhash) return self.backend.BlockProcessor().GetBlockReceipts(bhash) //ODR
} }
func (self *XEth) GetTxReceipt(txhash common.Hash) *types.Receipt { func (self *XEth) GetTxReceipt(txhash common.Hash) *types.Receipt { //ODR
return core.GetReceipt(self.backend.ChainDb(), txhash) return core.GetReceipt(self.backend.ChainAccess(), txhash) //TODO
} }
func (self *XEth) GasLimit() *big.Int { func (self *XEth) GasLimit() *big.Int {
@ -547,7 +562,7 @@ func (self *XEth) NewLogFilter(earliest, latest int64, skip, max int, address []
self.logMu.Lock() self.logMu.Lock()
defer self.logMu.Unlock() defer self.logMu.Unlock()
filter := filters.New(self.backend.ChainDb()) filter := filters.New(self.backend.ChainAccess())
id := self.filterManager.Add(filter) id := self.filterManager.Add(filter)
self.logQueue[id] = &logQueue{timeout: time.Now()} self.logQueue[id] = &logQueue{timeout: time.Now()}
@ -571,7 +586,7 @@ func (self *XEth) NewTransactionFilter() int {
self.transactionMu.Lock() self.transactionMu.Lock()
defer self.transactionMu.Unlock() defer self.transactionMu.Unlock()
filter := filters.New(self.backend.ChainDb()) filter := filters.New(self.backend.ChainAccess())
id := self.filterManager.Add(filter) id := self.filterManager.Add(filter)
self.transactionQueue[id] = &hashQueue{timeout: time.Now()} self.transactionQueue[id] = &hashQueue{timeout: time.Now()}
@ -590,7 +605,7 @@ func (self *XEth) NewBlockFilter() int {
self.blockMu.Lock() self.blockMu.Lock()
defer self.blockMu.Unlock() defer self.blockMu.Unlock()
filter := filters.New(self.backend.ChainDb()) filter := filters.New(self.backend.ChainAccess())
id := self.filterManager.Add(filter) id := self.filterManager.Add(filter)
self.blockQueue[id] = &hashQueue{timeout: time.Now()} self.blockQueue[id] = &hashQueue{timeout: time.Now()}
@ -657,7 +672,7 @@ func (self *XEth) Logs(id int) vm.Logs {
} }
func (self *XEth) AllLogs(earliest, latest int64, skip, max int, address []string, topics [][]string) vm.Logs { func (self *XEth) AllLogs(earliest, latest int64, skip, max int, address []string, topics [][]string) vm.Logs {
filter := filters.New(self.backend.ChainDb()) filter := filters.New(self.backend.ChainAccess())
filter.SetBeginBlock(earliest) filter.SetBeginBlock(earliest)
filter.SetEndBlock(latest) filter.SetEndBlock(latest)
filter.SetAddresses(cAddress(address)) filter.SetAddresses(cAddress(address))
@ -829,7 +844,7 @@ func (self *XEth) PushTx(encodedTx string) (string, error) {
} }
func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, string, error) { func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, string, error) {
statedb := self.State().State().Copy() statedb := self.State().State().CopyWithCtx()
var from *state.StateObject var from *state.StateObject
if len(fromStr) == 0 { if len(fromStr) == 0 {
accounts, err := self.backend.AccountManager().Accounts() accounts, err := self.backend.AccountManager().Accounts()