diff --git a/Godeps/_workspace/src/github.com/rcrowley/go-metrics/ewma.go b/Godeps/_workspace/src/github.com/rcrowley/go-metrics/ewma.go
index 7c152a174a03..694a1d03307a 100644
--- a/Godeps/_workspace/src/github.com/rcrowley/go-metrics/ewma.go
+++ b/Godeps/_workspace/src/github.com/rcrowley/go-metrics/ewma.go
@@ -77,7 +77,7 @@ func (NilEWMA) Update(n int64) {}
// of uncounted events and processes them on each tick. It uses the
// sync/atomic package to manage uncounted events.
type StandardEWMA struct {
- uncounted int64 // /!\ this should be the first member to ensure 64-bit alignment
+ uncounted int64 // /!\ this should be the first member to ensure 64-bit alignment
alpha float64
rate float64
init bool
diff --git a/Godeps/_workspace/src/github.com/rcrowley/go-metrics/graphite_test.go b/Godeps/_workspace/src/github.com/rcrowley/go-metrics/graphite_test.go
index b49dc4bb5655..c797c781df6f 100644
--- a/Godeps/_workspace/src/github.com/rcrowley/go-metrics/graphite_test.go
+++ b/Godeps/_workspace/src/github.com/rcrowley/go-metrics/graphite_test.go
@@ -17,6 +17,6 @@ func ExampleGraphiteWithConfig() {
Registry: DefaultRegistry,
FlushInterval: 1 * time.Second,
DurationUnit: time.Millisecond,
- Percentiles: []float64{ 0.5, 0.75, 0.99, 0.999 },
+ Percentiles: []float64{0.5, 0.75, 0.99, 0.999},
})
}
diff --git a/Godeps/_workspace/src/github.com/rcrowley/go-metrics/opentsdb_test.go b/Godeps/_workspace/src/github.com/rcrowley/go-metrics/opentsdb_test.go
index 6173d61ab73a..c43728960ed5 100644
--- a/Godeps/_workspace/src/github.com/rcrowley/go-metrics/opentsdb_test.go
+++ b/Godeps/_workspace/src/github.com/rcrowley/go-metrics/opentsdb_test.go
@@ -19,4 +19,3 @@ func ExampleOpenTSDBWithConfig() {
DurationUnit: time.Millisecond,
})
}
-
diff --git a/Godeps/_workspace/src/github.com/robertkrimen/otto/dbg/dbg.go b/Godeps/_workspace/src/github.com/robertkrimen/otto/dbg/dbg.go
index 83bf6c573416..8c27fa293ec8 100644
--- a/Godeps/_workspace/src/github.com/robertkrimen/otto/dbg/dbg.go
+++ b/Godeps/_workspace/src/github.com/robertkrimen/otto/dbg/dbg.go
@@ -282,7 +282,7 @@ func (self *Dbgr) getEmit() _emit {
// SetOutput will accept the following as a destination for output:
//
// *log.Logger Print*/Panic*/Fatal* of the logger
-// io.Writer -
+// io.Writer -
// nil Reset to the default output (os.Stderr)
// "log" Print*/Panic*/Fatal* via the "log" package
//
diff --git a/Godeps/_workspace/src/github.com/robertkrimen/otto/underscore/source.go b/Godeps/_workspace/src/github.com/robertkrimen/otto/underscore/source.go
index 65754ab715f5..7c5df9714516 100644
--- a/Godeps/_workspace/src/github.com/robertkrimen/otto/underscore/source.go
+++ b/Godeps/_workspace/src/github.com/robertkrimen/otto/underscore/source.go
@@ -3456,6 +3456,7 @@ func underscore() []byte {
0x69, 0x73, 0x29, 0x3b, 0x0a,
}
}
+
// Underscore.js 1.4.4
// http://underscorejs.org
// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc.
diff --git a/Godeps/_workspace/src/golang.org/x/net/context/context.go b/Godeps/_workspace/src/golang.org/x/net/context/context.go
new file mode 100644
index 000000000000..e7ee376c47d6
--- /dev/null
+++ b/Godeps/_workspace/src/golang.org/x/net/context/context.go
@@ -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)
+}
diff --git a/Godeps/_workspace/src/golang.org/x/net/context/context_test.go b/Godeps/_workspace/src/golang.org/x/net/context/context_test.go
new file mode 100644
index 000000000000..faf67722a0f9
--- /dev/null
+++ b/Godeps/_workspace/src/golang.org/x/net/context/context_test.go
@@ -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)
+}
diff --git a/Godeps/_workspace/src/golang.org/x/net/context/withtimeout_test.go b/Godeps/_workspace/src/golang.org/x/net/context/withtimeout_test.go
new file mode 100644
index 000000000000..a6754dc36892
--- /dev/null
+++ b/Godeps/_workspace/src/golang.org/x/net/context/withtimeout_test.go
@@ -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
+}
diff --git a/Godeps/_workspace/src/gopkg.in/check.v1/benchmark_test.go b/Godeps/_workspace/src/gopkg.in/check.v1/benchmark_test.go
index 4dd827c160da..0114c5b1ab70 100644
--- a/Godeps/_workspace/src/gopkg.in/check.v1/benchmark_test.go
+++ b/Godeps/_workspace/src/gopkg.in/check.v1/benchmark_test.go
@@ -3,8 +3,8 @@
package check_test
import (
- "time"
. "gopkg.in/check.v1"
+ "time"
)
var benchmarkS = Suite(&BenchmarkS{})
diff --git a/Godeps/_workspace/src/gopkg.in/check.v1/printer_test.go b/Godeps/_workspace/src/gopkg.in/check.v1/printer_test.go
index 538b2d52e3b7..daa6a737ae4a 100644
--- a/Godeps/_workspace/src/gopkg.in/check.v1/printer_test.go
+++ b/Godeps/_workspace/src/gopkg.in/check.v1/printer_test.go
@@ -1,7 +1,7 @@
package check_test
import (
- . "gopkg.in/check.v1"
+ . "gopkg.in/check.v1"
)
var _ = Suite(&PrinterS{})
@@ -9,96 +9,101 @@ var _ = Suite(&PrinterS{})
type PrinterS struct{}
func (s *PrinterS) TestCountSuite(c *C) {
- suitesRun += 1
+ suitesRun += 1
}
var printTestFuncLine int
func init() {
- printTestFuncLine = getMyLine() + 3
+ printTestFuncLine = getMyLine() + 3
}
func printTestFunc() {
- println(1) // Comment1
- if 2 == 2 { // Comment2
- println(3) // Comment3
- }
- switch 5 {
- case 6: println(6) // Comment6
- println(7)
- }
- switch interface{}(9).(type) {// Comment9
- case int: println(10)
- println(11)
- }
- select {
- case <-(chan bool)(nil): println(14)
- println(15)
- default: println(16)
- println(17)
- }
- println(19,
- 20)
- _ = func() { println(21)
- println(22)
- }
- println(24, func() {
- println(25)
- })
- // Leading comment
- // with multiple lines.
- println(29) // Comment29
+ println(1) // Comment1
+ if 2 == 2 { // Comment2
+ println(3) // Comment3
+ }
+ switch 5 {
+ case 6:
+ println(6) // Comment6
+ println(7)
+ }
+ switch interface{}(9).(type) { // Comment9
+ case int:
+ println(10)
+ println(11)
+ }
+ select {
+ case <-(chan bool)(nil):
+ println(14)
+ println(15)
+ default:
+ println(16)
+ println(17)
+ }
+ println(19,
+ 20)
+ _ = func() {
+ println(21)
+ println(22)
+ }
+ println(24, func() {
+ println(25)
+ })
+ // Leading comment
+ // with multiple lines.
+ println(29) // Comment29
}
var printLineTests = []struct {
- line int
- output string
+ line int
+ output string
}{
- {1, "println(1) // Comment1"},
- {2, "if 2 == 2 { // Comment2\n ...\n}"},
- {3, "println(3) // Comment3"},
- {5, "switch 5 {\n...\n}"},
- {6, "case 6:\n println(6) // Comment6\n ..."},
- {7, "println(7)"},
- {9, "switch interface{}(9).(type) { // Comment9\n...\n}"},
- {10, "case int:\n println(10)\n ..."},
- {14, "case <-(chan bool)(nil):\n println(14)\n ..."},
- {15, "println(15)"},
- {16, "default:\n println(16)\n ..."},
- {17, "println(17)"},
- {19, "println(19,\n 20)"},
- {20, "println(19,\n 20)"},
- {21, "_ = func() {\n println(21)\n println(22)\n}"},
- {22, "println(22)"},
- {24, "println(24, func() {\n println(25)\n})"},
- {25, "println(25)"},
- {26, "println(24, func() {\n println(25)\n})"},
- {29, "// Leading comment\n// with multiple lines.\nprintln(29) // Comment29"},
+ {1, "println(1) // Comment1"},
+ {2, "if 2 == 2 { // Comment2\n ...\n}"},
+ {3, "println(3) // Comment3"},
+ {5, "switch 5 {\n...\n}"},
+ {6, "case 6:\n println(6) // Comment6\n ..."},
+ {7, "println(7)"},
+ {9, "switch interface{}(9).(type) { // Comment9\n...\n}"},
+ {10, "case int:\n println(10)\n ..."},
+ {14, "case <-(chan bool)(nil):\n println(14)\n ..."},
+ {15, "println(15)"},
+ {16, "default:\n println(16)\n ..."},
+ {17, "println(17)"},
+ {19, "println(19,\n 20)"},
+ {20, "println(19,\n 20)"},
+ {21, "_ = func() {\n println(21)\n println(22)\n}"},
+ {22, "println(22)"},
+ {24, "println(24, func() {\n println(25)\n})"},
+ {25, "println(25)"},
+ {26, "println(24, func() {\n println(25)\n})"},
+ {29, "// Leading comment\n// with multiple lines.\nprintln(29) // Comment29"},
}
func (s *PrinterS) TestPrintLine(c *C) {
- for _, test := range printLineTests {
- output, err := PrintLine("printer_test.go", printTestFuncLine+test.line)
- c.Assert(err, IsNil)
- c.Assert(output, Equals, test.output)
- }
+ for _, test := range printLineTests {
+ output, err := PrintLine("printer_test.go", printTestFuncLine+test.line)
+ c.Assert(err, IsNil)
+ c.Assert(output, Equals, test.output)
+ }
}
var indentTests = []struct {
- in, out string
+ in, out string
}{
- {"", ""},
- {"\n", "\n"},
- {"a", ">>>a"},
- {"a\n", ">>>a\n"},
- {"a\nb", ">>>a\n>>>b"},
- {" ", ">>> "},
+ {"", ""},
+ {"\n", "\n"},
+ {"a", ">>>a"},
+ {"a\n", ">>>a\n"},
+ {"a\nb", ">>>a\n>>>b"},
+ {" ", ">>> "},
}
func (s *PrinterS) TestIndent(c *C) {
- for _, test := range indentTests {
- out := Indent(test.in, ">>>")
- c.Assert(out, Equals, test.out)
- }
+ for _, test := range indentTests {
+ out := Indent(test.in, ">>>")
+ c.Assert(out, Equals, test.out)
+ }
}
diff --git a/Godeps/_workspace/src/gopkg.in/check.v1/run_test.go b/Godeps/_workspace/src/gopkg.in/check.v1/run_test.go
index f41fffc3f5b5..3ccb0eed7566 100644
--- a/Godeps/_workspace/src/gopkg.in/check.v1/run_test.go
+++ b/Godeps/_workspace/src/gopkg.in/check.v1/run_test.go
@@ -400,7 +400,7 @@ func (s *RunS) TestStreamModeWithMiss(c *C) {
// -----------------------------------------------------------------------
// Verify that that the keep work dir request indeed does so.
-type WorkDirSuite struct {}
+type WorkDirSuite struct{}
func (s *WorkDirSuite) Test(c *C) {
c.MkDir()
@@ -411,7 +411,7 @@ func (s *RunS) TestKeepWorkDir(c *C) {
runConf := RunConf{Output: &output, Verbose: true, KeepWorkDir: true}
result := Run(&WorkDirSuite{}, &runConf)
- c.Assert(result.String(), Matches, ".*\nWORK=" + result.WorkDir)
+ c.Assert(result.String(), Matches, ".*\nWORK="+result.WorkDir)
stat, err := os.Stat(result.WorkDir)
c.Assert(err, IsNil)
diff --git a/ethdb/.gitignore b/access/.gitignore
similarity index 100%
rename from ethdb/.gitignore
rename to access/.gitignore
diff --git a/ethdb/README.md b/access/README.md
similarity index 100%
rename from ethdb/README.md
rename to access/README.md
diff --git a/access/context.go b/access/context.go
new file mode 100644
index 000000000000..9d68e129f6c1
--- /dev/null
+++ b/access/context.go
@@ -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 .
+
+// 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}
+}
diff --git a/ethdb/database.go b/access/database.go
similarity index 99%
rename from ethdb/database.go
rename to access/database.go
index 047821c303fa..2d2b7d0e8ad5 100644
--- a/ethdb/database.go
+++ b/access/database.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package ethdb
+package access
import (
"path/filepath"
diff --git a/ethdb/database_test.go b/access/database_test.go
similarity index 98%
rename from ethdb/database_test.go
rename to access/database_test.go
index ae490616605c..02c942593767 100644
--- a/ethdb/database_test.go
+++ b/access/database_test.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package ethdb
+package access
import (
"os"
diff --git a/ethdb/interface.go b/access/interface.go
similarity index 98%
rename from ethdb/interface.go
rename to access/interface.go
index f4b787a52a1a..069912cfc629 100644
--- a/ethdb/interface.go
+++ b/access/interface.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package ethdb
+package access
type Database interface {
Put(key []byte, value []byte) error
diff --git a/ethdb/memory_database.go b/access/memory_database.go
similarity index 99%
rename from ethdb/memory_database.go
rename to access/memory_database.go
index 01273b9db309..b49bb6ad51bb 100644
--- a/ethdb/memory_database.go
+++ b/access/memory_database.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package ethdb
+package access
import (
"errors"
diff --git a/access/odr.go b/access/odr.go
new file mode 100644
index 000000000000..cadc7c732f3a
--- /dev/null
+++ b/access/odr.go
@@ -0,0 +1,84 @@
+// 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 .
+
+// Package access provides a layer to handle local blockchain database and
+// on-demand network retrieval
+package access
+
+import (
+ "errors"
+
+ "github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/logger/glog"
+ "golang.org/x/net/context"
+)
+
+var (
+ errNoOdr = errors.New("ODR request is not possible")
+)
+
+type OdrFunction func(req Request) error
+
+// ChainAccess provides access to blockchain and state data through local
+// database and optionally also on-demand network retrieval
+type ChainAccess struct {
+ db Database
+ odrFn OdrFunction
+}
+
+// NewDbChainAccess creates a ChainAccess with ODR disabled
+func NewDbChainAccess(db Database) *ChainAccess {
+ return NewChainAccess(db, nil)
+}
+
+// NewChainAccess create a ChainAccess with optional ODR
+func NewChainAccess(db Database, odr OdrFunction) *ChainAccess {
+ return &ChainAccess{
+ db: db,
+ odrFn: odr,
+ }
+}
+
+// Db returns the local database assigned to the ChainAccess object
+func (self *ChainAccess) Db() Database {
+ return self.db
+}
+
+// OdrEnabled returns true if this ChainAccess is capable of doing ODR requests
+func (self *ChainAccess) OdrEnabled() bool {
+ return self.odrFn != nil
+}
+
+type Request interface {
+ Ctx() context.Context
+ StoreResult(db Database)
+}
+
+// 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(req Request) (err error) {
+ if !self.OdrEnabled() || !IsOdrContext(req.Ctx()) {
+ return errNoOdr
+ }
+ err = self.odrFn(req)
+ if err == nil {
+ // retrieved from network, store in db
+ req.StoreResult(self.Db())
+ } else {
+ glog.V(logger.Info).Infof("ODR retrieve err = %v", err)
+ }
+ return
+}
diff --git a/cmd/evm/main.go b/cmd/evm/main.go
index 64044c42145f..546b1144d6b0 100644
--- a/cmd/evm/main.go
+++ b/cmd/evm/main.go
@@ -25,13 +25,13 @@ import (
"time"
"github.com/codegangsta/cli"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger/glog"
)
@@ -112,8 +112,8 @@ func run(ctx *cli.Context) {
glog.SetToStderr(true)
glog.SetV(ctx.GlobalInt(VerbosityFlag.Name))
- db, _ := ethdb.NewMemDatabase()
- statedb, _ := state.New(common.Hash{}, db)
+ db, _ := access.NewMemDatabase()
+ statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db))
sender := statedb.CreateAccount(common.StringToAddress("sender"))
receiver := statedb.CreateAccount(common.StringToAddress("receiver"))
receiver.SetCode(common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name)))
diff --git a/cmd/geth/blocktestcmd.go b/cmd/geth/blocktestcmd.go
index e4d97aa531d5..66770e30d9b8 100644
--- a/cmd/geth/blocktestcmd.go
+++ b/cmd/geth/blocktestcmd.go
@@ -21,9 +21,9 @@ import (
"os"
"github.com/codegangsta/cli"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/eth"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/tests"
)
@@ -101,8 +101,8 @@ func runBlockTest(ctx *cli.Context) {
func runOneBlockTest(ctx *cli.Context, test *tests.BlockTest) (*eth.Ethereum, error) {
cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx)
- db, _ := ethdb.NewMemDatabase()
- cfg.NewDB = func(path string) (ethdb.Database, error) { return db, nil }
+ db, _ := access.NewMemDatabase()
+ cfg.NewDB = func(path string) (access.Database, error) { return db, nil }
cfg.MaxPeers = 0 // disable network
cfg.Shh = false // disable whisper
cfg.NAT = nil // disable port mapping
diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go
index 80f3777d6dc4..3757a6be37f5 100644
--- a/cmd/geth/chaincmd.go
+++ b/cmd/geth/chaincmd.go
@@ -24,12 +24,12 @@ import (
"time"
"github.com/codegangsta/cli"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger/glog"
)
@@ -179,7 +179,7 @@ func dump(ctx *cli.Context) {
fmt.Println("{}")
utils.Fatalf("block not found")
} else {
- state, err := state.New(block.Root(), chainDb)
+ state, err := state.New(block.Root(), access.NewDbChainAccess(chainDb))
if err != nil {
utils.Fatalf("could not create new state: %v", err)
return
@@ -196,7 +196,7 @@ func hashish(x string) bool {
return err != nil
}
-func closeAll(dbs ...ethdb.Database) {
+func closeAll(dbs ...access.Database) {
for _, db := range dbs {
db.Close()
}
diff --git a/cmd/geth/js_test.go b/cmd/geth/js_test.go
index 477079706253..ad81051836de 100644
--- a/cmd/geth/js_test.go
+++ b/cmd/geth/js_test.go
@@ -28,6 +28,7 @@ import (
"testing"
"time"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/compiler"
@@ -37,7 +38,6 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/comms"
)
@@ -90,7 +90,7 @@ func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *eth
t.Fatal(err)
}
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
core.WriteGenesisBlockForTesting(db, core.GenesisAccount{common.HexToAddress(testAddress), common.String2Big(testBalance)})
ks := crypto.NewKeyStorePlain(filepath.Join(tmp, "keystore"))
@@ -104,7 +104,7 @@ func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *eth
DocRoot: "/",
SolcPath: testSolcPath,
PowTest: true,
- NewDB: func(path string) (ethdb.Database, error) { return db, nil },
+ NewDB: func(path string) (access.Database, error) { return db, nil },
}
if config != nil {
config(conf)
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index d63d20580abf..41954c12cd59 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -30,13 +30,13 @@ import (
"github.com/codegangsta/cli"
"github.com/ethereum/ethash"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/metrics"
@@ -551,16 +551,17 @@ func blockRecovery(ctx *cli.Context) {
cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
utils.CheckLegalese(cfg.DataDir)
- blockDb, err := ethdb.NewLDBDatabase(filepath.Join(cfg.DataDir, "blockchain"), cfg.DatabaseCache)
+ blockDb, err := access.NewLDBDatabase(filepath.Join(cfg.DataDir, "blockchain"), cfg.DatabaseCache)
if err != nil {
glog.Fatalln("could not open db:", err)
}
+ ca := access.NewDbChainAccess(blockDb)
var block *types.Block
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 {
- block = core.GetBlock(blockDb, common.HexToHash(arg))
+ block = core.GetBlock(ca, common.HexToHash(arg))
}
if block == nil {
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index 8335517dffc3..a3ca0c67fed7 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -31,13 +31,13 @@ import (
"github.com/codegangsta/cli"
"github.com/ethereum/ethash"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
@@ -534,12 +534,12 @@ func SetupVM(ctx *cli.Context) {
}
// MakeChain creates a chain manager from set command line flags.
-func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database) {
+func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb access.Database) {
datadir := MustDataDir(ctx)
cache := ctx.GlobalInt(CacheFlag.Name)
var err error
- if chainDb, err = ethdb.NewLDBDatabase(filepath.Join(datadir, "chaindata"), cache); err != nil {
+ if chainDb, err = access.NewLDBDatabase(filepath.Join(datadir, "chaindata"), cache); err != nil {
Fatalf("Could not open database: %v", err)
}
if ctx.GlobalBool(OlympicFlag.Name) {
@@ -552,12 +552,12 @@ func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database
eventMux := new(event.TypeMux)
pow := ethash.New()
//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 {
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)
return chain, chainDb
}
diff --git a/common/natspec/natspec_e2e_test.go b/common/natspec/natspec_e2e_test.go
index 5c0d43091c77..e049e52cea07 100644
--- a/common/natspec/natspec_e2e_test.go
+++ b/common/natspec/natspec_e2e_test.go
@@ -26,6 +26,7 @@ import (
"testing"
"time"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/httpclient"
@@ -33,7 +34,6 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
- "github.com/ethereum/go-ethereum/ethdb"
xe "github.com/ethereum/go-ethereum/xeth"
)
@@ -125,7 +125,7 @@ func testEth(t *testing.T) (ethereum *eth.Ethereum, err error) {
if err != nil {
t.Fatal(err)
}
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
addr := common.HexToAddress(testAddress)
core.WriteGenesisBlockForTesting(db, core.GenesisAccount{addr, common.String2Big(testBalance)})
ks := crypto.NewKeyStorePassphrase(filepath.Join(tmp, "keystore"), crypto.LightScryptN, crypto.LightScryptP)
@@ -152,7 +152,7 @@ func testEth(t *testing.T) (ethereum *eth.Ethereum, err error) {
Etherbase: common.HexToAddress(testAddress),
MaxPeers: 0,
PowTest: true,
- NewDB: func(path string) (ethdb.Database, error) { return db, nil },
+ NewDB: func(path string) (access.Database, error) { return db, nil },
GpoMinGasPrice: common.Big1,
GpobaseCorrectionFactor: 1,
GpoMaxGasPrice: common.Big1,
diff --git a/core/bench_test.go b/core/bench_test.go
index b5eb518033da..1964d3571ad6 100644
--- a/core/bench_test.go
+++ b/core/bench_test.go
@@ -23,10 +23,10 @@ import (
"os"
"testing"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params"
)
@@ -144,16 +144,16 @@ func genUncles(i int, gen *BlockGen) {
func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
// Create the database in memory or in a temporary directory.
- var db ethdb.Database
+ var db access.Database
if !disk {
- db, _ = ethdb.NewMemDatabase()
+ db, _ = access.NewMemDatabase()
} else {
dir, err := ioutil.TempDir("", "eth-core-bench")
if err != nil {
b.Fatalf("cannot create temporary directory: %v", err)
}
defer os.RemoveAll(dir)
- db, err = ethdb.NewLDBDatabase(dir, 0)
+ db, err = access.NewLDBDatabase(dir, 0)
if err != nil {
b.Fatalf("cannot create temporary database: %v", err)
}
@@ -168,8 +168,9 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
// Time the insertion of the new chain.
// State and blocks are stored in the same DB.
evmux := new(event.TypeMux)
- chainman, _ := NewBlockChain(db, FakePow{}, evmux)
- chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux))
+ ca := access.NewDbChainAccess(db)
+ chainman, _ := NewBlockChain(ca, FakePow{}, evmux)
+ chainman.SetProcessor(NewBlockProcessor(ca, FakePow{}, chainman, evmux))
defer chainman.Stop()
b.ReportAllocs()
b.ResetTimer()
diff --git a/core/block_processor.go b/core/block_processor.go
index e7b2f63e5a0c..f42064f29746 100644
--- a/core/block_processor.go
+++ b/core/block_processor.go
@@ -22,12 +22,12 @@ import (
"sync"
"time"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
@@ -43,7 +43,7 @@ const (
)
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 sync.Mutex
// Canonical block chain
@@ -85,13 +85,13 @@ func (gp *GasPool) String() 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{
- chainDb: db,
- mem: make(map[string]*big.Int),
- Pow: pow,
- bc: blockchain,
- eventMux: eventMux,
+ chainAccess: ca,
+ mem: make(map[string]*big.Int),
+ Pow: pow,
+ bc: blockchain,
+ eventMux: eventMux,
}
return sm
}
@@ -212,12 +212,12 @@ func (sm *BlockProcessor) Process(block *types.Block) (logs vm.Logs, receipts ty
defer sm.mutex.Unlock()
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()}
}
}
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)
}
}
@@ -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) {
// 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 {
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
func (sm *BlockProcessor) GetBlockReceipts(bhash common.Hash) types.Receipts {
if block := sm.BlockChain().GetBlock(bhash); block != nil {
- return GetBlockReceipts(sm.chainDb, block.Hash())
+ return GetBlockReceipts(sm.chainAccess, block.Hash())
}
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
// the depricated way by re-processing the block.
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
for _, receipt := range receipts {
logs = append(logs, receipt.Logs...)
diff --git a/core/block_processor_test.go b/core/block_processor_test.go
index 3050456b413b..4d18c81a425a 100644
--- a/core/block_processor_test.go
+++ b/core/block_processor_test.go
@@ -21,32 +21,33 @@ import (
"math/big"
"testing"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/pow/ezp"
)
func proc() (*BlockProcessor, *BlockChain) {
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
+ ca := access.NewDbChainAccess(db)
var mux event.TypeMux
WriteTestNetGenesisBlock(db, 0)
- blockchain, err := NewBlockChain(db, thePow(), &mux)
+ blockchain, err := NewBlockChain(ca, thePow(), &mux)
if err != nil {
fmt.Println(err)
}
- return NewBlockProcessor(db, ezp.New(), blockchain, &mux), blockchain
+ return NewBlockProcessor(ca, ezp.New(), blockchain, &mux), blockchain
}
func TestNumber(t *testing.T) {
pow := ezp.New()
_, 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.Number = big.NewInt(3)
err := ValidateHeader(pow, header, chain.Genesis().Header(), false, false)
@@ -62,7 +63,7 @@ func TestNumber(t *testing.T) {
}
func TestPutReceipt(t *testing.T) {
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
var addr common.Address
addr[0] = 1
@@ -82,7 +83,7 @@ func TestPutReceipt(t *testing.T) {
}}
PutReceipts(db, types.Receipts{receipt})
- receipt = GetReceipt(db, common.Hash{})
+ receipt = GetReceipt(access.NewDbChainAccess(db), common.Hash{})
if receipt == nil {
t.Error("expected to get 1 receipt, got none.")
}
diff --git a/core/blockchain.go b/core/blockchain.go
index cea346e3870f..a23e4f41beb5 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -30,11 +30,11 @@ import (
"sync/atomic"
"time"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
@@ -43,6 +43,7 @@ import (
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"github.com/hashicorp/golang-lru"
+ "golang.org/x/net/context"
)
var (
@@ -64,7 +65,8 @@ const (
)
type BlockChain struct {
- chainDb ethdb.Database
+ chainAccess *access.ChainAccess
+ chainDb access.Database
processor types.BlockProcessor
eventMux *event.TypeMux
genesisBlock *types.Block
@@ -95,7 +97,7 @@ type BlockChain struct {
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)
bodyCache, _ := lru.New(bodyCacheLimit)
bodyRLPCache, _ := lru.New(bodyCacheLimit)
@@ -104,7 +106,8 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl
futureBlocks, _ := lru.New(maxFutureBlocks)
bc := &BlockChain{
- chainDb: chainDb,
+ chainDb: chainAccess.Db(),
+ chainAccess: chainAccess,
eventMux: mux,
quit: make(chan struct{}),
headerCache: headerCache,
@@ -128,7 +131,7 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl
if err != nil {
return nil, err
}
- bc.genesisBlock, err = WriteGenesisBlock(chainDb, reader)
+ bc.genesisBlock, err = WriteGenesisBlock(bc.chainDb, reader)
if err != nil {
return nil, err
}
@@ -344,8 +347,14 @@ func (self *BlockChain) SetProcessor(proc types.BlockProcessor) {
self.processor = proc
}
+// State returns a new StateDB for the current block
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.
@@ -482,12 +491,18 @@ func (self *BlockChain) GetHeaderByNumber(number uint64) *types.Header {
// GetBody retrieves a block body (transactions and uncles) from the database by
// hash, caching it if found.
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
if cached, ok := self.bodyCache.Get(hash); ok {
body := cached.(*types.Body)
return body
}
- body := GetBody(self.chainDb, hash)
+ body := GetBodyOdr(ctx, self.chainAccess, hash)
if body == nil {
return nil
}
@@ -499,11 +514,17 @@ func (self *BlockChain) GetBody(hash common.Hash) *types.Body {
// GetBodyRLP retrieves a block body in RLP encoding from the database by hash,
// caching it if found.
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
if cached, ok := self.bodyRLPCache.Get(hash); ok {
return cached.(rlp.RawValue)
}
- body := GetBodyRLP(self.chainDb, hash)
+ body := GetBodyRLPOdr(ctx, self.chainAccess, hash)
if len(body) == 0 {
return nil
}
@@ -536,11 +557,16 @@ func (bc *BlockChain) HasBlock(hash common.Hash) bool {
// GetBlock retrieves a block from the database by hash, caching it if found.
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
if block, ok := self.blockCache.Get(hash); ok {
return block.(*types.Block)
}
- block := GetBlock(self.chainDb, hash)
+ block := GetBlockOdr(ctx, self.chainAccess, hash)
if block == nil {
return nil
}
@@ -552,11 +578,17 @@ func (self *BlockChain) GetBlock(hash common.Hash) *types.Block {
// GetBlockByNumber retrieves a block from the database by number, caching it
// (associated with its hash) if found.
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)
if hash == (common.Hash{}) {
return nil
}
- return self.GetBlock(hash)
+ return self.GetBlockOdr(ctx, hash)
}
// GetBlockHashesFromHash retrieves a number of block hashes starting at a given
@@ -1209,7 +1241,7 @@ func (self *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
if err := PutTransactions(self.chainDb, block, block.Transactions()); err != nil {
return err
}
- receipts := GetBlockReceipts(self.chainDb, block.Hash())
+ receipts := GetBlockReceipts(self.chainAccess, block.Hash())
// write receipts
if err := PutReceipts(self.chainDb, receipts); err != nil {
return err
diff --git a/core/blockchain_test.go b/core/blockchain_test.go
index 8ddc5032b1fa..e68a946ac3fc 100644
--- a/core/blockchain_test.go
+++ b/core/blockchain_test.go
@@ -27,11 +27,11 @@ import (
"testing"
"github.com/ethereum/ethash"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/pow"
@@ -48,16 +48,17 @@ func thePow() pow.PoW {
return pow
}
-func theBlockChain(db ethdb.Database, t *testing.T) *BlockChain {
+func theBlockChain(db access.Database, t *testing.T) *BlockChain {
var eventMux event.TypeMux
WriteTestNetGenesisBlock(db, 0)
- blockchain, err := NewBlockChain(db, thePow(), &eventMux)
+ ca := access.NewDbChainAccess(db)
+ blockchain, err := NewBlockChain(ca, thePow(), &eventMux)
if err != nil {
t.Error("failed creating chainmanager:", err)
t.FailNow()
return nil
}
- blockMan := NewBlockProcessor(db, nil, blockchain, &eventMux)
+ blockMan := NewBlockProcessor(ca, nil, blockchain, &eventMux)
blockchain.SetProcessor(blockMan)
return blockchain
@@ -138,8 +139,8 @@ func testBlockChainImport(chain []*types.Block, processor *BlockProcessor) error
}
// Manually insert the block into the database, but don't reorganize (allows subsequent testing)
processor.bc.mu.Lock()
- WriteTd(processor.chainDb, block.Hash(), new(big.Int).Add(block.Difficulty(), processor.bc.GetTd(block.ParentHash())))
- WriteBlock(processor.chainDb, block)
+ WriteTd(processor.chainAccess.Db(), block.Hash(), new(big.Int).Add(block.Difficulty(), processor.bc.GetTd(block.ParentHash())))
+ WriteBlock(processor.chainAccess.Db(), block)
processor.bc.mu.Unlock()
}
return nil
@@ -155,8 +156,8 @@ func testHeaderChainImport(chain []*types.Header, processor *BlockProcessor) err
}
// Manually insert the header into the database, but don't reorganize (allows subsequent testing)
processor.bc.mu.Lock()
- WriteTd(processor.chainDb, header.Hash(), new(big.Int).Add(header.Difficulty, processor.bc.GetTd(header.ParentHash)))
- WriteHeader(processor.chainDb, header)
+ WriteTd(processor.chainAccess.Db(), header.Hash(), new(big.Int).Add(header.Difficulty, processor.bc.GetTd(header.ParentHash)))
+ WriteHeader(processor.chainAccess.Db(), header)
processor.bc.mu.Unlock()
}
return nil
@@ -187,7 +188,7 @@ func insertChain(done chan bool, blockchain *BlockChain, chain types.Blocks, t *
}
func TestLastBlock(t *testing.T) {
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
bchain := theBlockChain(db, t)
block := makeBlockChain(bchain.CurrentBlock(), 1, db, 0)[0]
@@ -334,7 +335,7 @@ func testBrokenChain(t *testing.T, full bool) {
func TestChainInsertions(t *testing.T) {
t.Skip("Skipped: outdated test files")
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
chain1, err := loadChain("valid1", t)
if err != nil {
@@ -372,7 +373,7 @@ func TestChainInsertions(t *testing.T) {
func TestChainMultipleInsertions(t *testing.T) {
t.Skip("Skipped: outdated test files")
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
const max = 4
chains := make([]types.Blocks, max)
@@ -450,9 +451,10 @@ func makeBlockChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.B
return chain
}
-func chm(genesis *types.Block, db ethdb.Database) *BlockChain {
+func chm(genesis *types.Block, db access.Database) *BlockChain {
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.bodyCache, _ = lru.New(100)
bc.bodyRLPCache, _ = lru.New(100)
@@ -485,7 +487,7 @@ func testReorgShort(t *testing.T, full bool) {
func testReorg(t *testing.T, first, second []int, td int64, full bool) {
// Create a pristine block chain
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
genesis, _ := WriteTestNetGenesisBlock(db, 0)
bc := chm(genesis, db)
@@ -532,7 +534,7 @@ func TestBadBlockHashes(t *testing.T) { testBadHashes(t, true) }
func testBadHashes(t *testing.T, full bool) {
// Create a pristine block chain
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
genesis, _ := WriteTestNetGenesisBlock(db, 0)
bc := chm(genesis, db)
@@ -559,7 +561,7 @@ func TestReorgBadBlockHashes(t *testing.T) { testReorgBadHashes(t, true) }
func testReorgBadHashes(t *testing.T, full bool) {
// Create a pristine block chain
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
genesis, _ := WriteTestNetGenesisBlock(db, 0)
bc := chm(genesis, db)
@@ -587,7 +589,7 @@ func testReorgBadHashes(t *testing.T, full bool) {
defer func() { delete(BadHashes, headers[3].Hash()) }()
}
// 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 {
t.Fatalf("failed to create new chain manager: %v", err)
}
@@ -682,7 +684,7 @@ func testInsertNonceError(t *testing.T, full bool) {
func TestFastVsFullChains(t *testing.T) {
// Configure and generate a sample block chain
var (
- gendb, _ = ethdb.NewMemDatabase()
+ gendb, _ = access.NewMemDatabase()
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
address = crypto.PubkeyToAddress(key.PublicKey)
funds = big.NewInt(1000000000)
@@ -707,20 +709,22 @@ func TestFastVsFullChains(t *testing.T) {
}
})
// Import the chain as an archive node for the comparison baseline
- archiveDb, _ := ethdb.NewMemDatabase()
+ archiveDb, _ := access.NewMemDatabase()
+ archiveCa := access.NewDbChainAccess(archiveDb)
WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds})
- archive, _ := NewBlockChain(archiveDb, FakePow{}, new(event.TypeMux))
- archive.SetProcessor(NewBlockProcessor(archiveDb, FakePow{}, archive, new(event.TypeMux)))
+ archive, _ := NewBlockChain(archiveCa, FakePow{}, new(event.TypeMux))
+ archive.SetProcessor(NewBlockProcessor(archiveCa, FakePow{}, archive, new(event.TypeMux)))
if n, err := archive.InsertChain(blocks); err != nil {
t.Fatalf("failed to process block %d: %v", n, err)
}
// Fast import the chain as a non-archive node to test
- fastDb, _ := ethdb.NewMemDatabase()
+ fastDb, _ := access.NewMemDatabase()
+ fastCa := access.NewDbChainAccess(fastDb)
WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds})
- fast, _ := NewBlockChain(fastDb, FakePow{}, new(event.TypeMux))
- fast.SetProcessor(NewBlockProcessor(fastDb, FakePow{}, fast, new(event.TypeMux)))
+ fast, _ := NewBlockChain(fastCa, FakePow{}, new(event.TypeMux))
+ fast.SetProcessor(NewBlockProcessor(fastCa, FakePow{}, fast, new(event.TypeMux)))
headers := make([]*types.Header, len(blocks))
for i, block := range blocks {
@@ -749,7 +753,7 @@ func TestFastVsFullChains(t *testing.T) {
} 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())
}
- 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)
}
}
@@ -766,7 +770,7 @@ func TestFastVsFullChains(t *testing.T) {
func TestLightVsFastVsFullChainHeads(t *testing.T) {
// Configure and generate a sample block chain
var (
- gendb, _ = ethdb.NewMemDatabase()
+ gendb, _ = access.NewMemDatabase()
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
address = crypto.PubkeyToAddress(key.PublicKey)
funds = big.NewInt(1000000000)
@@ -793,11 +797,11 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
}
}
// Import the chain as an archive node and ensure all pointers are updated
- archiveDb, _ := ethdb.NewMemDatabase()
+ archiveDb, _ := access.NewMemDatabase()
+ archiveCa := access.NewDbChainAccess(archiveDb)
WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds})
-
- archive, _ := NewBlockChain(archiveDb, FakePow{}, new(event.TypeMux))
- archive.SetProcessor(NewBlockProcessor(archiveDb, FakePow{}, archive, new(event.TypeMux)))
+ archive, _ := NewBlockChain(archiveCa, FakePow{}, new(event.TypeMux))
+ archive.SetProcessor(NewBlockProcessor(archiveCa, FakePow{}, archive, new(event.TypeMux)))
if n, err := archive.InsertChain(blocks); err != nil {
t.Fatalf("failed to process block %d: %v", n, err)
@@ -807,10 +811,11 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
assert(t, "archive", archive, height/2, height/2, height/2)
// Import the chain as a non-archive node and ensure all pointers are updated
- fastDb, _ := ethdb.NewMemDatabase()
+ fastDb, _ := access.NewMemDatabase()
+ fastCa := access.NewDbChainAccess(fastDb)
WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds})
- fast, _ := NewBlockChain(fastDb, FakePow{}, new(event.TypeMux))
- fast.SetProcessor(NewBlockProcessor(fastDb, FakePow{}, fast, new(event.TypeMux)))
+ fast, _ := NewBlockChain(fastCa, FakePow{}, new(event.TypeMux))
+ fast.SetProcessor(NewBlockProcessor(fastCa, FakePow{}, fast, new(event.TypeMux)))
headers := make([]*types.Header, len(blocks))
for i, block := range blocks {
@@ -827,10 +832,11 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
assert(t, "fast", fast, height/2, height/2, 0)
// Import the chain as a light node and ensure all pointers are updated
- lightDb, _ := ethdb.NewMemDatabase()
+ lightDb, _ := access.NewMemDatabase()
+ lightCa := access.NewDbChainAccess(lightDb)
WriteGenesisBlockForTesting(lightDb, GenesisAccount{address, funds})
- light, _ := NewBlockChain(lightDb, FakePow{}, new(event.TypeMux))
- light.SetProcessor(NewBlockProcessor(lightDb, FakePow{}, light, new(event.TypeMux)))
+ light, _ := NewBlockChain(lightCa, FakePow{}, new(event.TypeMux))
+ light.SetProcessor(NewBlockProcessor(lightCa, FakePow{}, light, new(event.TypeMux)))
if n, err := light.InsertHeaderChain(headers, 1); err != nil {
t.Fatalf("failed to insert header %d: %v", n, err)
@@ -852,7 +858,7 @@ func TestChainTxReorgs(t *testing.T) {
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
addr3 = crypto.PubkeyToAddress(key3.PublicKey)
- db, _ = ethdb.NewMemDatabase()
+ db, _ = access.NewMemDatabase()
)
genesis := WriteGenesisBlockForTesting(db,
GenesisAccount{addr1, big.NewInt(1000000)},
@@ -895,8 +901,9 @@ func TestChainTxReorgs(t *testing.T) {
})
// Import the chain. This runs all block validation rules.
evmux := &event.TypeMux{}
- chainman, _ := NewBlockChain(db, FakePow{}, evmux)
- chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux))
+ ca := access.NewDbChainAccess(db)
+ chainman, _ := NewBlockChain(ca, FakePow{}, evmux)
+ chainman.SetProcessor(NewBlockProcessor(ca, FakePow{}, chainman, evmux))
if i, err := chainman.InsertChain(chain); err != nil {
t.Fatalf("failed to insert original chain[%d]: %v", i, err)
}
@@ -929,7 +936,7 @@ func TestChainTxReorgs(t *testing.T) {
if GetTransaction(db, tx.Hash()) != nil {
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)
}
}
@@ -938,7 +945,7 @@ func TestChainTxReorgs(t *testing.T) {
if GetTransaction(db, tx.Hash()) == nil {
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)
}
}
@@ -947,7 +954,7 @@ func TestChainTxReorgs(t *testing.T) {
if GetTransaction(db, tx.Hash()) == nil {
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)
}
}
diff --git a/core/chain_makers.go b/core/chain_makers.go
index 56e37a0fc221..104ab1a58479 100644
--- a/core/chain_makers.go
+++ b/core/chain_makers.go
@@ -20,10 +20,10 @@ import (
"fmt"
"math/big"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/pow"
)
@@ -163,8 +163,8 @@ func (b *BlockGen) OffsetTime(seconds int64) {
// Blocks created by GenerateChain do not contain valid proof of work
// values. Inserting them into BlockChain requires use of FakePow or
// 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) {
- statedb, err := state.New(parent.Root(), db)
+func GenerateChain(parent *types.Block, db access.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) {
+ statedb, err := state.New(parent.Root(), access.NewDbChainAccess(db))
if err != nil {
panic(err)
}
@@ -214,16 +214,17 @@ func makeHeader(parent *types.Block, state *state.StateDB) *types.Header {
// newCanonical creates a chain database, and injects a deterministic canonical
// chain. Depending on the full flag, if creates either a full block chain or a
// header only chain.
-func newCanonical(n int, full bool) (ethdb.Database, *BlockProcessor, error) {
+func newCanonical(n int, full bool) (access.Database, *BlockProcessor, error) {
// Create te new chain database
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
evmux := &event.TypeMux{}
// Initialize a fresh chain with only a genesis block
genesis, _ := WriteTestNetGenesisBlock(db, 0)
- blockchain, _ := NewBlockChain(db, FakePow{}, evmux)
- processor := NewBlockProcessor(db, FakePow{}, blockchain, evmux)
+ ca := access.NewDbChainAccess(db)
+ blockchain, _ := NewBlockChain(ca, FakePow{}, evmux)
+ processor := NewBlockProcessor(ca, FakePow{}, blockchain, evmux)
processor.bc.SetProcessor(processor)
// Create and inject the requested chain
@@ -243,7 +244,7 @@ func newCanonical(n int, full bool) (ethdb.Database, *BlockProcessor, error) {
}
// makeHeaderChain creates a deterministic chain of headers rooted at parent.
-func makeHeaderChain(parent *types.Header, n int, db ethdb.Database, seed int) []*types.Header {
+func makeHeaderChain(parent *types.Header, n int, db access.Database, seed int) []*types.Header {
blocks := makeBlockChain(types.NewBlockWithHeader(parent), n, db, seed)
headers := make([]*types.Header, len(blocks))
for i, block := range blocks {
@@ -253,7 +254,7 @@ func makeHeaderChain(parent *types.Header, n int, db ethdb.Database, seed int) [
}
// makeBlockChain creates a deterministic chain of blocks rooted at parent.
-func makeBlockChain(parent *types.Block, n int, db ethdb.Database, seed int) []*types.Block {
+func makeBlockChain(parent *types.Block, n int, db access.Database, seed int) []*types.Block {
blocks, _ := GenerateChain(parent, db, n, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)})
})
diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go
index 7f47cf28889d..868e0502babb 100644
--- a/core/chain_makers_test.go
+++ b/core/chain_makers_test.go
@@ -20,9 +20,9 @@ import (
"fmt"
"math/big"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params"
)
@@ -38,7 +38,7 @@ func ExampleGenerateChain() {
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
addr3 = crypto.PubkeyToAddress(key3.PublicKey)
- db, _ = ethdb.NewMemDatabase()
+ db, _ = access.NewMemDatabase()
)
// Ensure that key1 has some funds in the genesis block.
@@ -77,8 +77,9 @@ func ExampleGenerateChain() {
// Import the chain. This runs all block validation rules.
evmux := &event.TypeMux{}
- chainman, _ := NewBlockChain(db, FakePow{}, evmux)
- chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux))
+ ca := access.NewDbChainAccess(db)
+ chainman, _ := NewBlockChain(ca, FakePow{}, evmux)
+ chainman.SetProcessor(NewBlockProcessor(ca, FakePow{}, chainman, evmux))
if i, err := chainman.InsertChain(chain); err != nil {
fmt.Printf("insert error (block %d): %v\n", i, err)
return
diff --git a/core/chain_pow_test.go b/core/chain_pow_test.go
index d2b0bd1443dc..1d5c786719c9 100644
--- a/core/chain_pow_test.go
+++ b/core/chain_pow_test.go
@@ -22,9 +22,9 @@ import (
"testing"
"time"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/pow"
)
@@ -58,7 +58,7 @@ func (pow delayedPow) Turbo(bool) {}
func TestPowVerification(t *testing.T) {
// Create a simple chain to verify
var (
- testdb, _ = ethdb.NewMemDatabase()
+ testdb, _ = access.NewMemDatabase()
genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int))
blocks, _ = GenerateChain(genesis, testdb, 8, nil)
)
@@ -113,7 +113,7 @@ func TestPowConcurrentVerification32(t *testing.T) { testPowConcurrentVerificati
func testPowConcurrentVerification(t *testing.T, threads int) {
// Create a simple chain to verify
var (
- testdb, _ = ethdb.NewMemDatabase()
+ testdb, _ = access.NewMemDatabase()
genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int))
blocks, _ = GenerateChain(genesis, testdb, 8, nil)
)
@@ -184,7 +184,7 @@ func TestPowConcurrentAbortion32(t *testing.T) { testPowConcurrentAbortion(t, 32
func testPowConcurrentAbortion(t *testing.T, threads int) {
// Create a simple chain to verify
var (
- testdb, _ = ethdb.NewMemDatabase()
+ testdb, _ = access.NewMemDatabase()
genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int))
blocks, _ = GenerateChain(genesis, testdb, 1024, nil)
)
diff --git a/core/chain_util.go b/core/chain_util.go
index ddff381a1d42..d477e19046ea 100644
--- a/core/chain_util.go
+++ b/core/chain_util.go
@@ -22,13 +22,14 @@ import (
"fmt"
"math/big"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
+ "golang.org/x/net/context"
)
var (
@@ -118,7 +119,7 @@ func CalcGasLimit(parent *types.Block) *big.Int {
}
// GetCanonicalHash retrieves a hash assigned to a canonical block number.
-func GetCanonicalHash(db ethdb.Database, number uint64) common.Hash {
+func GetCanonicalHash(db access.Database, number uint64) common.Hash {
data, _ := db.Get(append(blockNumPrefix, big.NewInt(int64(number)).Bytes()...))
if len(data) == 0 {
return common.Hash{}
@@ -131,7 +132,7 @@ func GetCanonicalHash(db ethdb.Database, number uint64) common.Hash {
// last block hash is only updated upon a full block import, the last header
// hash is updated already at header import, allowing head tracking for the
// light synchronization mechanism.
-func GetHeadHeaderHash(db ethdb.Database) common.Hash {
+func GetHeadHeaderHash(db access.Database) common.Hash {
data, _ := db.Get(headHeaderKey)
if len(data) == 0 {
return common.Hash{}
@@ -140,7 +141,7 @@ func GetHeadHeaderHash(db ethdb.Database) common.Hash {
}
// GetHeadBlockHash retrieves the hash of the current canonical head block.
-func GetHeadBlockHash(db ethdb.Database) common.Hash {
+func GetHeadBlockHash(db access.Database) common.Hash {
data, _ := db.Get(headBlockKey)
if len(data) == 0 {
return common.Hash{}
@@ -152,7 +153,7 @@ func GetHeadBlockHash(db ethdb.Database) common.Hash {
// fast synchronization. The difference between this and GetHeadBlockHash is that
// whereas the last block hash is only updated upon a full block import, the last
// fast hash is updated when importing pre-processed blocks.
-func GetHeadFastBlockHash(db ethdb.Database) common.Hash {
+func GetHeadFastBlockHash(db access.Database) common.Hash {
data, _ := db.Get(headFastKey)
if len(data) == 0 {
return common.Hash{}
@@ -162,14 +163,14 @@ func GetHeadFastBlockHash(db ethdb.Database) common.Hash {
// GetHeaderRLP retrieves a block header in its raw RLP database encoding, or nil
// if the header's not found.
-func GetHeaderRLP(db ethdb.Database, hash common.Hash) rlp.RawValue {
+func GetHeaderRLP(db access.Database, hash common.Hash) rlp.RawValue {
data, _ := db.Get(append(append(blockPrefix, hash[:]...), headerSuffix...))
return data
}
// GetHeader retrieves the block header corresponding to the hash, nil if none
// found.
-func GetHeader(db ethdb.Database, hash common.Hash) *types.Header {
+func GetHeader(db access.Database, hash common.Hash) *types.Header {
data := GetHeaderRLP(db, hash)
if len(data) == 0 {
return nil
@@ -182,16 +183,35 @@ func GetHeader(db ethdb.Database, hash common.Hash) *types.Header {
return header
}
-// GetBodyRLP retrieves the block body (transactions and uncles) in RLP encoding.
-func GetBodyRLP(db ethdb.Database, hash common.Hash) rlp.RawValue {
- data, _ := db.Get(append(append(blockPrefix, hash[:]...), bodySuffix...))
+// GetBodyRLP retrieves the block body (transactions and uncles) from the
+// database in RLP encoding.
+func GetBodyRLP(ca *access.ChainAccess, hash common.Hash) rlp.RawValue {
+ data, _ := ca.Db().Get(append(append(blockPrefix, hash[:]...), bodySuffix...))
return data
}
+// 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 {
+ res := GetBodyRLP(ca, hash)
+ if res != nil || !access.IsOdrContext(ctx) {
+ return res
+ }
+ r := &BlockRequest{ctx: ctx, blockHash: hash}
+ ca.Retrieve(r)
+ return r.GetRlp()
+}
+
// GetBody retrieves the block body (transactons, uncles) corresponding to the
-// hash, nil if none found.
-func GetBody(db ethdb.Database, hash common.Hash) *types.Body {
- data := GetBodyRLP(db, hash)
+// hash from the database, nil if none found.
+func GetBody(ca *access.ChainAccess, hash common.Hash) *types.Body {
+ 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 {
return nil
}
@@ -205,7 +225,7 @@ func GetBody(db ethdb.Database, hash common.Hash) *types.Body {
// GetTd retrieves a block's total difficulty corresponding to the hash, nil if
// none found.
-func GetTd(db ethdb.Database, hash common.Hash) *big.Int {
+func GetTd(db access.Database, hash common.Hash) *big.Int {
data, _ := db.Get(append(append(blockPrefix, hash.Bytes()...), tdSuffix...))
if len(data) == 0 {
return nil
@@ -218,15 +238,21 @@ func GetTd(db ethdb.Database, hash common.Hash) *big.Int {
return td
}
-// GetBlock retrieves an entire block corresponding to the hash, assembling it
-// back from the stored header and body.
-func GetBlock(db ethdb.Database, hash common.Hash) *types.Block {
+// GetBlock retrieves an entire block from the database corresponding to the
+// hash, assembling it back from the stored header and body.
+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
- header := GetHeader(db, hash)
+ header := GetHeader(ca.Db(), hash)
if header == nil {
return nil
}
- body := GetBody(db, hash)
+ body := GetBodyOdr(ctx, ca, hash)
if body == nil {
return nil
}
@@ -235,7 +261,7 @@ func GetBlock(db ethdb.Database, hash common.Hash) *types.Block {
}
// WriteCanonicalHash stores the canonical hash for the given block number.
-func WriteCanonicalHash(db ethdb.Database, hash common.Hash, number uint64) error {
+func WriteCanonicalHash(db access.Database, hash common.Hash, number uint64) error {
key := append(blockNumPrefix, big.NewInt(int64(number)).Bytes()...)
if err := db.Put(key, hash.Bytes()); err != nil {
glog.Fatalf("failed to store number to hash mapping into database: %v", err)
@@ -245,7 +271,7 @@ func WriteCanonicalHash(db ethdb.Database, hash common.Hash, number uint64) erro
}
// WriteHeadHeaderHash stores the head header's hash.
-func WriteHeadHeaderHash(db ethdb.Database, hash common.Hash) error {
+func WriteHeadHeaderHash(db access.Database, hash common.Hash) error {
if err := db.Put(headHeaderKey, hash.Bytes()); err != nil {
glog.Fatalf("failed to store last header's hash into database: %v", err)
return err
@@ -254,7 +280,7 @@ func WriteHeadHeaderHash(db ethdb.Database, hash common.Hash) error {
}
// WriteHeadBlockHash stores the head block's hash.
-func WriteHeadBlockHash(db ethdb.Database, hash common.Hash) error {
+func WriteHeadBlockHash(db access.Database, hash common.Hash) error {
if err := db.Put(headBlockKey, hash.Bytes()); err != nil {
glog.Fatalf("failed to store last block's hash into database: %v", err)
return err
@@ -263,7 +289,7 @@ func WriteHeadBlockHash(db ethdb.Database, hash common.Hash) error {
}
// WriteHeadFastBlockHash stores the fast head block's hash.
-func WriteHeadFastBlockHash(db ethdb.Database, hash common.Hash) error {
+func WriteHeadFastBlockHash(db access.Database, hash common.Hash) error {
if err := db.Put(headFastKey, hash.Bytes()); err != nil {
glog.Fatalf("failed to store last fast block's hash into database: %v", err)
return err
@@ -272,7 +298,7 @@ func WriteHeadFastBlockHash(db ethdb.Database, hash common.Hash) error {
}
// WriteHeader serializes a block header into the database.
-func WriteHeader(db ethdb.Database, header *types.Header) error {
+func WriteHeader(db access.Database, header *types.Header) error {
data, err := rlp.EncodeToBytes(header)
if err != nil {
return err
@@ -286,12 +312,8 @@ func WriteHeader(db ethdb.Database, header *types.Header) error {
return nil
}
-// WriteBody serializes the body of a block into the database.
-func WriteBody(db ethdb.Database, hash common.Hash, body *types.Body) error {
- data, err := rlp.EncodeToBytes(body)
- if err != nil {
- return err
- }
+// WriteBodyRlp stores the serialized body of a block into the database.
+func WriteBodyRlp(db access.Database, hash common.Hash, data []byte) error {
key := append(append(blockPrefix, hash.Bytes()...), bodySuffix...)
if err := db.Put(key, data); err != nil {
glog.Fatalf("failed to store block body into database: %v", err)
@@ -301,8 +323,17 @@ func WriteBody(db ethdb.Database, hash common.Hash, body *types.Body) error {
return nil
}
+// WriteBody serializes the body of a block into the database.
+func WriteBody(db access.Database, hash common.Hash, body *types.Body) error {
+ data, err := rlp.EncodeToBytes(body)
+ if err != nil {
+ return err
+ }
+ return WriteBodyRlp(db, hash, data)
+}
+
// WriteTd serializes the total difficulty of a block into the database.
-func WriteTd(db ethdb.Database, hash common.Hash, td *big.Int) error {
+func WriteTd(db access.Database, hash common.Hash, td *big.Int) error {
data, err := rlp.EncodeToBytes(td)
if err != nil {
return err
@@ -317,7 +348,7 @@ func WriteTd(db ethdb.Database, hash common.Hash, td *big.Int) error {
}
// WriteBlock serializes a block into the database, header and body separately.
-func WriteBlock(db ethdb.Database, block *types.Block) error {
+func WriteBlock(db access.Database, block *types.Block) error {
// Store the body first to retain database consistency
if err := WriteBody(db, block.Hash(), &types.Body{block.Transactions(), block.Uncles()}); err != nil {
return err
@@ -330,27 +361,27 @@ func WriteBlock(db ethdb.Database, block *types.Block) error {
}
// DeleteCanonicalHash removes the number to hash canonical mapping.
-func DeleteCanonicalHash(db ethdb.Database, number uint64) {
+func DeleteCanonicalHash(db access.Database, number uint64) {
db.Delete(append(blockNumPrefix, big.NewInt(int64(number)).Bytes()...))
}
// DeleteHeader removes all block header data associated with a hash.
-func DeleteHeader(db ethdb.Database, hash common.Hash) {
+func DeleteHeader(db access.Database, hash common.Hash) {
db.Delete(append(append(blockPrefix, hash.Bytes()...), headerSuffix...))
}
// DeleteBody removes all block body data associated with a hash.
-func DeleteBody(db ethdb.Database, hash common.Hash) {
+func DeleteBody(db access.Database, hash common.Hash) {
db.Delete(append(append(blockPrefix, hash.Bytes()...), bodySuffix...))
}
// DeleteTd removes all block total difficulty data associated with a hash.
-func DeleteTd(db ethdb.Database, hash common.Hash) {
+func DeleteTd(db access.Database, hash common.Hash) {
db.Delete(append(append(blockPrefix, hash.Bytes()...), tdSuffix...))
}
// DeleteBlock removes all block data associated with a hash.
-func DeleteBlock(db ethdb.Database, hash common.Hash) {
+func DeleteBlock(db access.Database, hash common.Hash) {
DeleteHeader(db, hash)
DeleteBody(db, hash)
DeleteTd(db, hash)
@@ -361,7 +392,7 @@ func DeleteBlock(db ethdb.Database, hash common.Hash) {
// or nil if not found. This method is only used by the upgrade mechanism to
// access the old combined block representation. It will be dropped after the
// network transitions to eth/63.
-func GetBlockByHashOld(db ethdb.Database, hash common.Hash) *types.Block {
+func GetBlockByHashOld(db access.Database, hash common.Hash) *types.Block {
data, _ := db.Get(append(blockHashPre, hash[:]...))
if len(data) == 0 {
return nil
@@ -387,7 +418,7 @@ func mipmapKey(num, level uint64) []byte {
// WriteMapmapBloom writes each address included in the receipts' logs to the
// MIP bloom bin.
-func WriteMipmapBloom(db ethdb.Database, number uint64, receipts types.Receipts) error {
+func WriteMipmapBloom(db access.Database, number uint64, receipts types.Receipts) error {
batch := db.NewBatch()
for _, level := range MIPMapLevels {
key := mipmapKey(number, level)
@@ -408,7 +439,7 @@ func WriteMipmapBloom(db ethdb.Database, number uint64, receipts types.Receipts)
// GetMipmapBloom returns a bloom filter using the number and level as input
// parameters. For available levels see MIPMapLevels.
-func GetMipmapBloom(db ethdb.Database, number, level uint64) types.Bloom {
+func GetMipmapBloom(db access.Database, number, level uint64) types.Bloom {
bloomDat, _ := db.Get(mipmapKey(number, level))
return types.BytesToBloom(bloomDat)
}
diff --git a/core/chain_util_test.go b/core/chain_util_test.go
index 0bbcbbe53d58..5810ab3efa6f 100644
--- a/core/chain_util_test.go
+++ b/core/chain_util_test.go
@@ -23,12 +23,12 @@ import (
"os"
"testing"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/sha3"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/rlp"
)
@@ -85,7 +85,7 @@ func TestDifficulty(t *testing.T) {
// Tests block header storage and retrieval operations.
func TestHeaderStorage(t *testing.T) {
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
// Create a test header to move around the database and make sure it's really new
header := &types.Header{Extra: []byte("test header")}
@@ -120,7 +120,8 @@ func TestHeaderStorage(t *testing.T) {
// Tests block body storage and retrieval operations.
func TestBodyStorage(t *testing.T) {
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
+ ca := access.NewDbChainAccess(db)
// 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")}}}
@@ -129,19 +130,19 @@ func TestBodyStorage(t *testing.T) {
rlp.Encode(hasher, body)
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)
}
// Write and verify the body in the database
if err := WriteBody(db, hash, body); err != nil {
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")
} 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)
}
- if entry := GetBodyRLP(db, hash); entry == nil {
+ if entry := GetBodyRLP(ca, hash); entry == nil {
t.Fatalf("Stored body RLP not found")
} else {
hasher := sha3.NewKeccak256()
@@ -153,14 +154,15 @@ func TestBodyStorage(t *testing.T) {
}
// Delete the body and verify the execution
DeleteBody(db, hash)
- if entry := GetBody(db, hash); entry != nil {
+ if entry := GetBody(ca, hash); entry != nil {
t.Fatalf("Deleted body returned: %v", entry)
}
}
// Tests block storage and retrieval operations.
func TestBlockStorage(t *testing.T) {
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
+ ca := access.NewDbChainAccess(db)
// Create a test block to move around the database and make sure it's really new
block := types.NewBlockWithHeader(&types.Header{
@@ -169,20 +171,20 @@ func TestBlockStorage(t *testing.T) {
TxHash: 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)
}
if entry := GetHeader(db, block.Hash()); entry != nil {
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)
}
// Write and verify the block in the database
if err := WriteBlock(db, block); err != nil {
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")
} else if entry.Hash() != block.Hash() {
t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block)
@@ -192,27 +194,28 @@ func TestBlockStorage(t *testing.T) {
} else if entry.Hash() != block.Header().Hash() {
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")
} 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()})
}
// Delete the block and verify the execution
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)
}
if entry := GetHeader(db, block.Hash()); entry != nil {
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)
}
}
// Tests that partial block contents don't get reassembled into full blocks.
func TestPartialBlockStorage(t *testing.T) {
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
+ ca := access.NewDbChainAccess(db)
block := types.NewBlockWithHeader(&types.Header{
Extra: []byte("test block"),
UncleHash: types.EmptyUncleHash,
@@ -223,7 +226,7 @@ func TestPartialBlockStorage(t *testing.T) {
if err := WriteHeader(db, block.Header()); err != nil {
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)
}
DeleteHeader(db, block.Hash())
@@ -232,7 +235,7 @@ func TestPartialBlockStorage(t *testing.T) {
if err := WriteBody(db, block.Hash(), &types.Body{block.Transactions(), block.Uncles()}); err != nil {
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)
}
DeleteBody(db, block.Hash())
@@ -244,7 +247,7 @@ func TestPartialBlockStorage(t *testing.T) {
if err := WriteBody(db, block.Hash(), &types.Body{block.Transactions(), block.Uncles()}); err != nil {
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")
} else if entry.Hash() != block.Hash() {
t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block)
@@ -253,7 +256,7 @@ func TestPartialBlockStorage(t *testing.T) {
// Tests block total difficulty storage and retrieval operations.
func TestTdStorage(t *testing.T) {
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
// Create a test TD to move around the database and make sure it's really new
hash, td := common.Hash{}, big.NewInt(314)
@@ -278,7 +281,7 @@ func TestTdStorage(t *testing.T) {
// Tests that canonical numbers can be mapped to hashes and retrieved.
func TestCanonicalMappingStorage(t *testing.T) {
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
// Create a test canonical number and assinged hash to move around
hash, number := common.Hash{0: 0xff}, uint64(314)
@@ -303,7 +306,7 @@ func TestCanonicalMappingStorage(t *testing.T) {
// Tests that head headers and head blocks can be assigned, individually.
func TestHeadStorage(t *testing.T) {
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
blockHead := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block header")})
blockFull := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block full")})
@@ -342,7 +345,7 @@ func TestHeadStorage(t *testing.T) {
}
func TestMipmapBloom(t *testing.T) {
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
receipt1 := new(types.Receipt)
receipt1.Logs = vm.Logs{
@@ -366,7 +369,7 @@ func TestMipmapBloom(t *testing.T) {
}
// reset
- db, _ = ethdb.NewMemDatabase()
+ db, _ = access.NewMemDatabase()
receipt := new(types.Receipt)
receipt.Logs = vm.Logs{
&vm.Log{Address: common.BytesToAddress([]byte("test"))},
@@ -393,7 +396,7 @@ func TestMipmapChain(t *testing.T) {
defer os.RemoveAll(dir)
var (
- db, _ = ethdb.NewLDBDatabase(dir, 16)
+ db, _ = access.NewLDBDatabase(dir, 16)
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
addr = crypto.PubkeyToAddress(key1.PublicKey)
addr2 = common.BytesToAddress([]byte("jeff"))
diff --git a/core/genesis.go b/core/genesis.go
index dac5de92fff3..9edae819ca27 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -24,17 +24,17 @@ import (
"math/big"
"strings"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params"
)
// WriteGenesisBlock writes the genesis block to the database as block number 0
-func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block, error) {
+func WriteGenesisBlock(chainDb access.Database, reader io.Reader) (*types.Block, error) {
contents, err := ioutil.ReadAll(reader)
if err != nil {
return nil, err
@@ -61,7 +61,8 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
}
// 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 {
address := common.HexToAddress(addr)
statedb.AddBalance(address, common.String2Big(account.Balance))
@@ -85,7 +86,7 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
Root: root,
}, 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")
err := WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64())
if err != nil {
@@ -117,8 +118,8 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
// 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
-func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big.Int) *types.Block {
- statedb, _ := state.New(common.Hash{}, db)
+func GenesisBlockForTesting(db access.Database, addr common.Address, balance *big.Int) *types.Block {
+ statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db))
obj := statedb.GetOrNewStateObject(addr)
obj.SetBalance(balance)
root, err := statedb.Commit()
@@ -138,7 +139,7 @@ type GenesisAccount struct {
Balance *big.Int
}
-func WriteGenesisBlockForTesting(db ethdb.Database, accounts ...GenesisAccount) *types.Block {
+func WriteGenesisBlockForTesting(db access.Database, accounts ...GenesisAccount) *types.Block {
accountJson := "{"
for i, account := range accounts {
if i != 0 {
@@ -158,7 +159,7 @@ func WriteGenesisBlockForTesting(db ethdb.Database, accounts ...GenesisAccount)
return block
}
-func WriteTestNetGenesisBlock(chainDb ethdb.Database, nonce uint64) (*types.Block, error) {
+func WriteTestNetGenesisBlock(chainDb access.Database, nonce uint64) (*types.Block, error) {
testGenesis := fmt.Sprintf(`{
"nonce": "0x%x",
"difficulty": "0x20000",
@@ -179,7 +180,7 @@ func WriteTestNetGenesisBlock(chainDb ethdb.Database, nonce uint64) (*types.Bloc
return WriteGenesisBlock(chainDb, strings.NewReader(testGenesis))
}
-func WriteOlympicGenesisBlock(chainDb ethdb.Database, nonce uint64) (*types.Block, error) {
+func WriteOlympicGenesisBlock(chainDb access.Database, nonce uint64) (*types.Block, error) {
testGenesis := fmt.Sprintf(`{
"nonce":"0x%x",
"gasLimit":"0x%x",
diff --git a/core/helper_test.go b/core/helper_test.go
index fd6a5491c5d9..41e06c7e48c7 100644
--- a/core/helper_test.go
+++ b/core/helper_test.go
@@ -23,7 +23,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
// "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/event"
)
@@ -32,7 +32,7 @@ type TestManager struct {
// stateManager *StateManager
eventMux *event.TypeMux
- db ethdb.Database
+ db access.Database
txPool *TxPool
blockChain *BlockChain
Blocks []*types.Block
@@ -74,12 +74,12 @@ func (tm *TestManager) EventMux() *event.TypeMux {
// return nil
// }
-func (tm *TestManager) Db() ethdb.Database {
+func (tm *TestManager) Db() access.Database {
return tm.db
}
func NewTestManager() *TestManager {
- db, err := ethdb.NewMemDatabase()
+ db, err := access.NewMemDatabase()
if err != nil {
fmt.Println("Could not create mem-db, failing")
return nil
diff --git a/core/manager.go b/core/manager.go
index 289c87c11205..536a7a9ac6d1 100644
--- a/core/manager.go
+++ b/core/manager.go
@@ -17,8 +17,8 @@
package core
import (
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/accounts"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
)
@@ -28,7 +28,8 @@ type Backend interface {
BlockProcessor() *BlockProcessor
BlockChain() *BlockChain
TxPool() *TxPool
- ChainDb() ethdb.Database
- DappDb() ethdb.Database
+ ChainDb() access.Database
+ ChainAccess() *access.ChainAccess
+ DappDb() access.Database
EventMux() *event.TypeMux
}
diff --git a/core/requests.go b/core/requests.go
new file mode 100644
index 000000000000..102b722808f5
--- /dev/null
+++ b/core/requests.go
@@ -0,0 +1,60 @@
+// 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 .
+
+package core
+
+import (
+ "github.com/ethereum/go-ethereum/access"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "golang.org/x/net/context"
+)
+
+// BlockRequest is the ODR request type for block bodies
+type BlockRequest struct {
+ access.Request
+ ctx context.Context
+ blockHash common.Hash
+ data []byte
+}
+
+func (req *BlockRequest) Ctx() context.Context { return req.ctx }
+
+func (req *BlockRequest) GetRlp() []byte {
+ return req.data
+}
+
+func (req *BlockRequest) StoreResult(db access.Database) {
+ WriteBodyRlp(db, req.blockHash, req.GetRlp())
+}
+
+// ReceiptsRequest is the ODR request type for block receipts by block hash
+type ReceiptsRequest struct {
+ access.Request
+ ctx context.Context
+ blockHash common.Hash
+ data types.Receipts
+}
+
+func (req *ReceiptsRequest) Ctx() context.Context { return req.ctx }
+
+func (req *ReceiptsRequest) GetReceipts() types.Receipts {
+ return req.data
+}
+
+func (req *ReceiptsRequest) StoreResult(db access.Database) {
+ PutBlockReceipts(db, req.blockHash, req.GetReceipts())
+}
diff --git a/core/state/dump.go b/core/state/dump.go
index 9acb8a024454..83a45a800cee 100644
--- a/core/state/dump.go
+++ b/core/state/dump.go
@@ -20,6 +20,7 @@ import (
"encoding/json"
"fmt"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
)
@@ -45,7 +46,7 @@ func (self *StateDB) RawDump() World {
it := self.trie.Iterator()
for it.Next() {
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.Storage = make(map[string]string)
diff --git a/core/state/managed_state_test.go b/core/state/managed_state_test.go
index 0b53a42c541e..f548a595742c 100644
--- a/core/state/managed_state_test.go
+++ b/core/state/managed_state_test.go
@@ -19,15 +19,15 @@ package state
import (
"testing"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/ethdb"
)
var addr = common.BytesToAddress([]byte("test"))
func create() (*ManagedState, *account) {
- db, _ := ethdb.NewMemDatabase()
- statedb, _ := New(common.Hash{}, db)
+ db, _ := access.NewMemDatabase()
+ statedb, _ := New(common.Hash{}, access.NewDbChainAccess(db))
ms := ManageState(statedb)
so := &StateObject{address: addr, nonce: 100}
ms.StateDB.stateObjects[addr.Str()] = so
diff --git a/core/state/requests.go b/core/state/requests.go
new file mode 100644
index 000000000000..0de9b03a91b6
--- /dev/null
+++ b/core/state/requests.go
@@ -0,0 +1,105 @@
+// 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 .
+
+package state
+
+import (
+ "bytes"
+
+ "github.com/ethereum/go-ethereum/access"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto/sha3"
+ "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 {
+ r := &TrieRequest{ctx: ctx, root: self.root, key: key}
+ return self.ca.Retrieve(r) == nil
+}
+
+// OdrEnabled returns true if this TrieAccess is capable of doing network requests
+func (self *TrieAccess) OdrEnabled() bool {
+ return self.ca.OdrEnabled()
+}
+
+// TrieRequest is the ODR request type for state/storage trie entries
+type TrieRequest struct {
+ access.Request
+ ctx context.Context
+ root common.Hash
+ key []byte
+ proof trie.MerkleProof
+}
+
+func (req *TrieRequest) Ctx() context.Context { return req.ctx }
+
+func (req *TrieRequest) StoreResult(db access.Database) {
+ trie.StoreProof(db, req.proof)
+}
+
+// NodeDataRequest is the ODR request type for node data (used for retrieving contract code)
+type NodeDataRequest struct {
+ access.Request
+ ctx context.Context
+ hash common.Hash
+ data []byte
+}
+
+func (req *NodeDataRequest) Ctx() context.Context { return req.ctx }
+
+func (req *NodeDataRequest) GetData() []byte {
+ return req.data
+}
+
+func (req *NodeDataRequest) StoreResult(db access.Database) {
+ db.Put(req.hash[:], req.GetData())
+}
+
+var sha3_nil = sha3.NewKeccak256().Sum(nil)
+
+func RetrieveNodeData(ctx context.Context, ca *access.ChainAccess, hash common.Hash) []byte {
+ if bytes.Compare(hash[:], sha3_nil) == 0 {
+ return nil
+ }
+ res, _ := ca.Db().Get(hash[:])
+ if res != nil || !access.IsOdrContext(ctx) {
+ return res
+ }
+ r := &NodeDataRequest{ctx: ctx, hash: hash}
+ ca.Retrieve(r)
+ return r.GetData()
+}
diff --git a/core/state/state_object.go b/core/state/state_object.go
index c06e3d2276f2..ccf5244290db 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -21,13 +21,14 @@ import (
"fmt"
"math/big"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
+ "golang.org/x/net/context"
)
type Code []byte
@@ -57,7 +58,8 @@ func (self Storage) Copy() Storage {
type StateObject struct {
// State database for storing state changes
- db ethdb.Database
+ ca *access.ChainAccess
+ ctx context.Context
trie *trie.SecureTrie
// Address belonging to this account
@@ -83,14 +85,14 @@ type StateObject struct {
dirty bool
}
-func NewStateObject(address common.Address, db ethdb.Database) *StateObject {
- object := &StateObject{db: db, address: address, balance: new(big.Int), dirty: true}
- object.trie, _ = trie.NewSecure(common.Hash{}, db)
+func NewStateObject(ctx context.Context, address common.Address, ca *access.ChainAccess) *StateObject {
+ object := &StateObject{ca: ca, ctx: ctx, address: address, balance: new(big.Int), dirty: true}
+ object.trie, _ = trie.NewSecure(common.Hash{}, ca.Db())
object.storage = make(Storage)
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 {
Nonce uint64
Balance *big.Int
@@ -102,20 +104,20 @@ func NewStateObjectFromBytes(address common.Address, data []byte, db ethdb.Datab
glog.Errorf("can't decode state object %x: %v", address, err)
return nil
}
- trie, err := trie.NewSecure(extobject.Root, db)
+ trie, err := trie.NewSecureOdr(ctx, extobject.Root, ca.Db(), NewTrieAccess(ca, extobject.Root, ca.Db()))
if err != nil {
// TODO: bubble this up or panic
glog.Errorf("can't create account trie with root %x: %v", extobject.Root[:], err)
return nil
}
- object := &StateObject{address: address, db: db}
+ object := &StateObject{address: address, ca: ca, ctx: ctx}
object.nonce = extobject.Nonce
object.balance = extobject.Balance
object.codeHash = extobject.CodeHash
object.trie = trie
object.storage = make(map[string]common.Hash)
- object.code, _ = db.Get(extobject.CodeHash)
+ object.code = RetrieveNodeData(ctx, ca, common.BytesToHash(extobject.CodeHash))
return object
}
@@ -130,7 +132,8 @@ func (self *StateObject) MarkForDeletion() {
func (c *StateObject) getAddr(addr common.Hash) common.Hash {
var ret []byte
- rlp.DecodeBytes(c.trie.Get(addr[:]), &ret)
+ value := c.trie.Get(addr[:])
+ rlp.DecodeBytes(value, &ret)
return common.BytesToHash(ret)
}
@@ -205,12 +208,22 @@ func (c *StateObject) St() Storage {
// Return the gas back to the origin. Used by the Virtual machine or Closures
func (c *StateObject) ReturnGas(gas, price *big.Int) {}
+// Copy creates a copy of the state object
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.codeHash = common.CopyBytes(self.codeHash)
stateObject.nonce = self.nonce
- stateObject.trie = self.trie
+ if access.IsOdrContext(ctx) {
+ stateObject.trie = self.trie.CopySecureWithOdr(ctx, NewTrieAccess(self.ca, common.BytesToHash(self.trie.Root()), self.ca.Db()))
+ } else {
+ stateObject.trie = self.trie
+ }
stateObject.code = common.CopyBytes(self.code)
stateObject.initCode = common.CopyBytes(self.initCode)
stateObject.storage = self.storage.Copy()
diff --git a/core/state/state_test.go b/core/state/state_test.go
index 7ddbe11a1345..30654814e53d 100644
--- a/core/state/state_test.go
+++ b/core/state/state_test.go
@@ -23,8 +23,8 @@ import (
checker "gopkg.in/check.v1"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/ethdb"
)
type StateSuite struct {
@@ -76,13 +76,13 @@ func (s *StateSuite) TestDump(c *checker.C) {
}
func (s *StateSuite) SetUpTest(c *checker.C) {
- db, _ := ethdb.NewMemDatabase()
- s.state, _ = New(common.Hash{}, db)
+ db, _ := access.NewMemDatabase()
+ s.state, _ = New(common.Hash{}, access.NewDbChainAccess(db))
}
func TestNull(t *testing.T) {
- db, _ := ethdb.NewMemDatabase()
- state, _ := New(common.Hash{}, db)
+ db, _ := access.NewMemDatabase()
+ state, _ := New(common.Hash{}, access.NewDbChainAccess(db))
address := common.HexToAddress("0x823140710bf13990e4500136726d8b55")
state.CreateAccount(address)
@@ -121,8 +121,8 @@ func (s *StateSuite) TestSnapshot(c *checker.C) {
// use testing instead of checker because checker does not support
// printing/logging in tests (-check.vv does not work)
func TestSnapshot2(t *testing.T) {
- db, _ := ethdb.NewMemDatabase()
- state, _ := New(common.Hash{}, db)
+ db, _ := access.NewMemDatabase()
+ state, _ := New(common.Hash{}, access.NewDbChainAccess(db))
stateobjaddr0 := toAddr([]byte("so0"))
stateobjaddr1 := toAddr([]byte("so1"))
diff --git a/core/state/statedb.go b/core/state/statedb.go
index a9de71409c89..c7ddec441297 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -20,12 +20,13 @@ package state
import (
"math/big"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/trie"
+ "golang.org/x/net/context"
)
// The starting nonce determines the default nonce when new accounts are being
@@ -38,8 +39,9 @@ var StartingNonce uint64
// * Contracts
// * Accounts
type StateDB struct {
- db ethdb.Database
+ ca *access.ChainAccess
trie *trie.SecureTrie
+ ctx context.Context
stateObjects map[string]*StateObject
@@ -51,22 +53,33 @@ type StateDB struct {
logSize uint
}
-// Create a new state from a given trie
-func New(root common.Hash, db ethdb.Database) (*StateDB, error) {
- tr, err := trie.NewSecure(root, db)
+// New creates a new state from a given trie
+func New(root common.Hash, ca *access.ChainAccess) (*StateDB, error) {
+ 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(), NewTrieAccess(ca, root, ca.Db()))
if err != nil {
glog.Errorf("can't create state trie with root %x: %v", root[:], err)
return nil, err
}
return &StateDB{
- db: db,
+ ca: ca,
trie: tr,
+ ctx: ctx,
stateObjects: make(map[string]*StateObject),
refund: new(big.Int),
logs: make(map[common.Hash]vm.Logs),
}, 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) {
self.thash = thash
self.bhash = bhash
@@ -208,7 +221,7 @@ func (self *StateDB) UpdateStateObject(stateObject *StateObject) {
//addr := stateObject.Address()
if len(stateObject.CodeHash()) > 0 {
- self.db.Put(stateObject.CodeHash(), stateObject.code)
+ self.ca.Db().Put(stateObject.CodeHash(), stateObject.code)
}
addr := stateObject.Address()
self.trie.Update(addr[:], stateObject.RlpEncode())
@@ -220,7 +233,6 @@ func (self *StateDB) DeleteStateObject(stateObject *StateObject) {
addr := stateObject.Address()
self.trie.Delete(addr[:])
- //delete(self.stateObjects, addr.Str())
}
// Retrieve a state object given my the address. Nil if not found
@@ -239,7 +251,7 @@ func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObje
return nil
}
- stateObject = NewStateObjectFromBytes(addr, []byte(data), self.db)
+ stateObject = NewStateObjectFromBytes(self.ctx, addr, []byte(data), self.ca)
self.SetStateObject(stateObject)
return stateObject
@@ -265,7 +277,7 @@ func (self *StateDB) newStateObject(addr common.Address) *StateObject {
glog.Infof("(+) %x\n", addr)
}
- stateObject := NewStateObject(addr, self.db)
+ stateObject := NewStateObject(self.ctx, addr, self.ca)
stateObject.SetNonce(StartingNonce)
self.stateObjects[addr.Str()] = stateObject
@@ -295,12 +307,27 @@ func (self *StateDB) CreateAccount(addr common.Address) vm.Account {
// 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 {
+ 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
- state, _ := New(common.Hash{}, self.db)
- state.trie = self.trie
+ state, _ := NewOdr(ctx, common.Hash{}, self.ca)
+ if access.IsOdrContext(ctx) {
+ state.trie = self.trie.CopySecureWithOdr(ctx, NewTrieAccess(self.ca, common.BytesToHash(self.trie.Root()), self.ca.Db()))
+ } else {
+ state.trie = self.trie
+ }
for k, stateObject := range self.stateObjects {
- state.stateObjects[k] = stateObject.Copy()
+ state.stateObjects[k] = stateObject.CopyOdr(ctx)
}
state.refund.Set(self.refund)
@@ -348,14 +375,14 @@ func (s *StateDB) IntermediateRoot() common.Hash {
// Commit commits all state changes to the database.
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
// execute the batch. It is used to validate state changes against
// the root hash stored in a block.
-func (s *StateDB) CommitBatch() (root common.Hash, batch ethdb.Batch) {
- batch = s.db.NewBatch()
+func (s *StateDB) CommitBatch() (root common.Hash, batch access.Batch) {
+ batch = s.ca.Db().NewBatch()
root, _ = s.commit(batch)
return root, batch
}
diff --git a/core/state/sync.go b/core/state/sync.go
index ef2b4b84c503..86526d3eaceb 100644
--- a/core/state/sync.go
+++ b/core/state/sync.go
@@ -20,8 +20,8 @@ import (
"bytes"
"math/big"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
)
@@ -32,7 +32,7 @@ import (
type StateSync trie.TrieSync
// NewStateSync create a new state trie download scheduler.
-func NewStateSync(root common.Hash, database ethdb.Database) *StateSync {
+func NewStateSync(root common.Hash, database access.Database) *StateSync {
var syncer *trie.TrieSync
callback := func(leaf []byte, parent common.Hash) error {
diff --git a/core/state/sync_test.go b/core/state/sync_test.go
index 0dab372ba055..9d027a48b419 100644
--- a/core/state/sync_test.go
+++ b/core/state/sync_test.go
@@ -21,8 +21,8 @@ import (
"math/big"
"testing"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/trie"
)
@@ -35,10 +35,10 @@ type testAccount struct {
}
// makeTestState create a sample test state to test node-wise reconstruction.
-func makeTestState() (ethdb.Database, common.Hash, []*testAccount) {
+func makeTestState() (access.Database, common.Hash, []*testAccount) {
// Create an empty state
- db, _ := ethdb.NewMemDatabase()
- state, _ := New(common.Hash{}, db)
+ db, _ := access.NewMemDatabase()
+ state, _ := New(common.Hash{}, access.NewDbChainAccess(db))
// Fill it with some arbitrary data
accounts := []*testAccount{}
@@ -67,8 +67,8 @@ func makeTestState() (ethdb.Database, common.Hash, []*testAccount) {
// checkStateAccounts cross references a reconstructed state with an expected
// account array.
-func checkStateAccounts(t *testing.T, db ethdb.Database, root common.Hash, accounts []*testAccount) {
- state, _ := New(root, db)
+func checkStateAccounts(t *testing.T, db access.Database, root common.Hash, accounts []*testAccount) {
+ state, _ := New(root, access.NewDbChainAccess(db))
for i, acc := range accounts {
if balance := state.GetBalance(acc.address); balance.Cmp(acc.balance) != 0 {
@@ -86,7 +86,7 @@ func checkStateAccounts(t *testing.T, db ethdb.Database, root common.Hash, accou
// Tests that an empty state is not scheduled for syncing.
func TestEmptyStateSync(t *testing.T) {
empty := common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
if req := NewStateSync(empty, db).Missing(1); len(req) != 0 {
t.Errorf("content requested for empty state: %v", req)
}
@@ -102,7 +102,7 @@ func testIterativeStateSync(t *testing.T, batch int) {
srcDb, srcRoot, srcAccounts := makeTestState()
// Create a destination state and sync with the scheduler
- dstDb, _ := ethdb.NewMemDatabase()
+ dstDb, _ := access.NewMemDatabase()
sched := NewStateSync(srcRoot, dstDb)
queue := append([]common.Hash{}, sched.Missing(batch)...)
@@ -131,7 +131,7 @@ func TestIterativeDelayedStateSync(t *testing.T) {
srcDb, srcRoot, srcAccounts := makeTestState()
// Create a destination state and sync with the scheduler
- dstDb, _ := ethdb.NewMemDatabase()
+ dstDb, _ := access.NewMemDatabase()
sched := NewStateSync(srcRoot, dstDb)
queue := append([]common.Hash{}, sched.Missing(0)...)
@@ -165,7 +165,7 @@ func testIterativeRandomStateSync(t *testing.T, batch int) {
srcDb, srcRoot, srcAccounts := makeTestState()
// Create a destination state and sync with the scheduler
- dstDb, _ := ethdb.NewMemDatabase()
+ dstDb, _ := access.NewMemDatabase()
sched := NewStateSync(srcRoot, dstDb)
queue := make(map[common.Hash]struct{})
@@ -202,7 +202,7 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
srcDb, srcRoot, srcAccounts := makeTestState()
// Create a destination state and sync with the scheduler
- dstDb, _ := ethdb.NewMemDatabase()
+ dstDb, _ := access.NewMemDatabase()
sched := NewStateSync(srcRoot, dstDb)
queue := make(map[common.Hash]struct{})
diff --git a/core/transaction_pool_test.go b/core/transaction_pool_test.go
index 229dcacf37f6..3b2f6aefcfe6 100644
--- a/core/transaction_pool_test.go
+++ b/core/transaction_pool_test.go
@@ -21,11 +21,11 @@ import (
"math/big"
"testing"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
)
@@ -35,8 +35,8 @@ func transaction(nonce uint64, gaslimit *big.Int, key *ecdsa.PrivateKey) *types.
}
func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
- db, _ := ethdb.NewMemDatabase()
- statedb, _ := state.New(common.Hash{}, db)
+ db, _ := access.NewMemDatabase()
+ statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db))
var m event.TypeMux
key, _ := crypto.GenerateKey()
@@ -162,8 +162,8 @@ func TestTransactionChainFork(t *testing.T) {
pool, key := setupTxPool()
addr := crypto.PubkeyToAddress(key.PublicKey)
resetState := func() {
- db, _ := ethdb.NewMemDatabase()
- statedb, _ := state.New(common.Hash{}, db)
+ db, _ := access.NewMemDatabase()
+ statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db))
pool.currentState = func() (*state.StateDB, error) { return statedb, nil }
currentState, _ := pool.currentState()
currentState.AddBalance(addr, big.NewInt(100000000000000))
@@ -188,8 +188,8 @@ func TestTransactionDoubleNonce(t *testing.T) {
pool, key := setupTxPool()
addr := crypto.PubkeyToAddress(key.PublicKey)
resetState := func() {
- db, _ := ethdb.NewMemDatabase()
- statedb, _ := state.New(common.Hash{}, db)
+ db, _ := access.NewMemDatabase()
+ statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db))
pool.currentState = func() (*state.StateDB, error) { return statedb, nil }
currentState, _ := pool.currentState()
currentState.AddBalance(addr, big.NewInt(100000000000000))
diff --git a/core/transaction_util.go b/core/transaction_util.go
index e2e5b9aeea88..acbe18c4199d 100644
--- a/core/transaction_util.go
+++ b/core/transaction_util.go
@@ -19,13 +19,14 @@ package core
import (
"fmt"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rlp"
"github.com/syndtr/goleveldb/leveldb"
+ "golang.org/x/net/context"
)
var (
@@ -34,7 +35,7 @@ var (
)
// PutTransactions stores the transactions in the given database
-func PutTransactions(db ethdb.Database, block *types.Block, txs types.Transactions) error {
+func PutTransactions(db access.Database, block *types.Block, txs types.Transactions) error {
batch := db.NewBatch()
for i, tx := range block.Transactions() {
@@ -67,11 +68,11 @@ func PutTransactions(db ethdb.Database, block *types.Block, txs types.Transactio
return nil
}
-func DeleteTransaction(db ethdb.Database, txHash common.Hash) {
+func DeleteTransaction(db access.Database, txHash common.Hash) {
db.Delete(txHash[:])
}
-func GetTransaction(db ethdb.Database, txhash common.Hash) *types.Transaction {
+func GetTransaction(db access.Database, txhash common.Hash) *types.Transaction {
data, _ := db.Get(txhash[:])
if len(data) != 0 {
var tx types.Transaction
@@ -84,9 +85,9 @@ func GetTransaction(db ethdb.Database, txhash common.Hash) *types.Transaction {
}
// PutReceipts stores the receipts in the current database
-func PutReceipts(db ethdb.Database, receipts types.Receipts) error {
+func PutReceipts(db access.Database, receipts types.Receipts) error {
batch := new(leveldb.Batch)
- _, batchWrite := db.(*ethdb.LDBDatabase)
+ _, batchWrite := db.(*access.LDBDatabase)
for _, receipt := range receipts {
storageReceipt := (*types.ReceiptForStorage)(receipt)
@@ -104,7 +105,7 @@ func PutReceipts(db ethdb.Database, receipts types.Receipts) error {
}
}
}
- if db, ok := db.(*ethdb.LDBDatabase); ok {
+ if db, ok := db.(*access.LDBDatabase); ok {
if err := db.LDB().Write(batch, nil); err != nil {
return err
}
@@ -114,13 +115,13 @@ func PutReceipts(db ethdb.Database, receipts types.Receipts) error {
}
// Delete a receipts from the database
-func DeleteReceipt(db ethdb.Database, txHash common.Hash) {
+func DeleteReceipt(db access.Database, txHash common.Hash) {
db.Delete(append(receiptsPre, txHash[:]...))
}
// GetReceipt returns a receipt by hash
-func GetReceipt(db ethdb.Database, txHash common.Hash) *types.Receipt {
- data, _ := db.Get(append(receiptsPre, txHash[:]...))
+func GetReceipt(ca *access.ChainAccess, txHash common.Hash) *types.Receipt {
+ data, _ := ca.Db().Get(append(receiptsPre, txHash[:]...))
if len(data) == 0 {
return nil
}
@@ -132,29 +133,41 @@ func GetReceipt(db ethdb.Database, txHash common.Hash) *types.Receipt {
return (*types.Receipt)(&receipt)
}
-// GetBlockReceipts returns the receipts generated by the transactions
-// included in block's given hash.
-func GetBlockReceipts(db ethdb.Database, hash common.Hash) types.Receipts {
- data, _ := db.Get(append(blockReceiptsPre, hash[:]...))
+// GetBlockReceipts retrieves the receipts generated by the transactions included
+// in a block given by its hash.
+func GetBlockReceipts(ca *access.ChainAccess, hash common.Hash) types.Receipts {
+ data, _ := ca.Db().Get(append(blockReceiptsPre, hash[:]...))
if len(data) == 0 {
return nil
}
- rs := []*types.ReceiptForStorage{}
- if err := rlp.DecodeBytes(data, &rs); err != nil {
+ storageReceipts := []*types.ReceiptForStorage{}
+ if err := rlp.DecodeBytes(data, &storageReceipts); err != nil {
glog.V(logger.Error).Infof("invalid receipt array RLP for hash %x: %v", hash, err)
return nil
}
- receipts := make(types.Receipts, len(rs))
- for i, receipt := range rs {
+ receipts := make(types.Receipts, len(storageReceipts))
+ for i, receipt := range storageReceipts {
receipts[i] = (*types.Receipt)(receipt)
}
return receipts
}
+// GetBlockReceiptsOdr returns the receipts generated by the transactions
+// included in block's given hash from the database or network.
+func GetBlockReceiptsOdr(ctx context.Context, ca *access.ChainAccess, hash common.Hash) types.Receipts {
+ res := GetBlockReceipts(ca, hash)
+ if res != nil || !access.IsOdrContext(ctx) {
+ return res
+ }
+ r := &ReceiptsRequest{ctx: ctx, blockHash: hash}
+ ca.Retrieve(r)
+ return r.GetReceipts()
+}
+
// PutBlockReceipts stores the block's transactions associated receipts
// and stores them by block hash in a single slice. This is required for
// forks and chain reorgs
-func PutBlockReceipts(db ethdb.Database, hash common.Hash, receipts types.Receipts) error {
+func PutBlockReceipts(db access.Database, hash common.Hash, receipts types.Receipts) error {
rs := make([]*types.ReceiptForStorage, len(receipts))
for i, receipt := range receipts {
rs[i] = (*types.ReceiptForStorage)(receipt)
diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go
index dd3aa1b0b5d0..900670263f06 100644
--- a/core/vm/runtime/runtime.go
+++ b/core/vm/runtime/runtime.go
@@ -20,11 +20,11 @@ import (
"math/big"
"time"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
)
// Config is a basic type specifing certain configuration flags for running
@@ -95,8 +95,8 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
vm.Debug = cfg.Debug
var (
- db, _ = ethdb.NewMemDatabase()
- statedb, _ = state.New(common.Hash{}, db)
+ db, _ = access.NewMemDatabase()
+ statedb, _ = state.New(common.Hash{}, access.NewDbChainAccess(db))
vmenv = NewEnv(cfg, statedb)
sender = statedb.CreateAccount(cfg.Origin)
receiver = statedb.CreateAccount(common.StringToAddress("contract"))
diff --git a/core/vm_env.go b/core/vm_env.go
index c8b50debc678..d1fb9ae41d42 100644
--- a/core/vm_env.go
+++ b/core/vm_env.go
@@ -76,7 +76,7 @@ func (self *VMEnv) CanTransfer(from common.Address, balance *big.Int) bool {
}
func (self *VMEnv) MakeSnapshot() vm.Database {
- return self.state.Copy()
+ return self.state.CopyWithCtx()
}
func (self *VMEnv) SetSnapshot(copy vm.Database) {
diff --git a/eth/backend.go b/eth/backend.go
index 761a17a8ff27..58621fcfbeb9 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -32,6 +32,7 @@ import (
"time"
"github.com/ethereum/ethash"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/compiler"
@@ -42,7 +43,6 @@ import (
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/downloader"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
@@ -142,7 +142,7 @@ type Config struct {
// NewDB is used to create databases.
// If nil, the default is to create leveldb databases on disk.
- NewDB func(path string) (ethdb.Database, error)
+ NewDB func(path string) (access.Database, error)
}
func (cfg *Config) parseBootNodes() []*discover.Node {
@@ -228,8 +228,10 @@ type Ethereum struct {
shutdownChan chan bool
// DB interfaces
- chainDb ethdb.Database // Block chain database
- dappDb ethdb.Database // Dapp database
+ chainDb access.Database // Block chain database
+ dappDb access.Database // Dapp database
+
+ chainAccess *access.ChainAccess // blockchain access layer
//*** SERVICES ***
// State manager for processing new blocks and managing the over all states
@@ -276,11 +278,11 @@ func New(config *Config) (*Ethereum, error) {
// Let the database take 3/4 of the max open files (TODO figure out a way to get the actual limit of the open files)
const dbCount = 3
- ethdb.OpenFileLimit = 128 / (dbCount + 1)
+ access.OpenFileLimit = 128 / (dbCount + 1)
newdb := config.NewDB
if newdb == nil {
- newdb = func(path string) (ethdb.Database, error) { return ethdb.NewLDBDatabase(path, config.DatabaseCache) }
+ newdb = func(path string) (access.Database, error) { return access.NewLDBDatabase(path, config.DatabaseCache) }
}
// Open the chain database and perform any upgrades needed
@@ -291,16 +293,16 @@ func New(config *Config) (*Ethereum, error) {
}
return nil, fmt.Errorf("blockchain db err: %v", err)
}
- if db, ok := chainDb.(*ethdb.LDBDatabase); ok {
+ if db, ok := chainDb.(*access.LDBDatabase); ok {
db.Meter("eth/db/chaindata/")
}
if err := upgradeChainDatabase(chainDb); err != nil {
return nil, err
}
- if err := addMipmapBloomBins(chainDb); err != nil {
+ chainAccess := access.NewChainAccess(chainDb, nil)
+ if err := addMipmapBloomBins(chainAccess); err != nil {
return nil, err
}
-
dappDb, err := newdb(filepath.Join(config.DataDir, "dapp"))
if err != nil {
if errno, ok := err.(syscall.Errno); ok && datadirInUseErrnos[uint(errno)] {
@@ -308,7 +310,7 @@ func New(config *Config) (*Ethereum, error) {
}
return nil, fmt.Errorf("dapp db err: %v", err)
}
- if db, ok := dappDb.(*ethdb.LDBDatabase); ok {
+ if db, ok := dappDb.(*access.LDBDatabase); ok {
db.Meter("eth/db/dapp/")
}
@@ -366,6 +368,7 @@ func New(config *Config) (*Ethereum, error) {
eth := &Ethereum{
shutdownChan: make(chan bool),
chainDb: chainDb,
+ chainAccess: chainAccess,
dappDb: dappDb,
eventMux: &event.TypeMux{},
accountManager: config.AccountManager,
@@ -397,7 +400,7 @@ func New(config *Config) (*Ethereum, error) {
eth.pow = ethash.New()
}
//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 == core.ErrNoGenesis {
return nil, fmt.Errorf(`Genesis block not found. Please supply a genesis block with the "--genesis /path/to/file" argument`)
@@ -407,11 +410,13 @@ func New(config *Config) (*Ethereum, error) {
newPool := core.NewTxPool(eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit)
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)
- 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
}
+
eth.miner = miner.New(eth, eth.EventMux(), eth.pow)
eth.miner.SetGasPrice(config.GasPrice)
eth.miner.SetExtra(config.ExtraData)
@@ -492,8 +497,9 @@ func (s *Ethereum) BlockProcessor() *core.BlockProcessor { return s.blockProcess
func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
func (s *Ethereum) Whisper() *whisper.Whisper { return s.whisper }
func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
-func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
-func (s *Ethereum) DappDb() ethdb.Database { return s.dappDb }
+func (s *Ethereum) ChainDb() access.Database { return s.chainDb }
+func (s *Ethereum) ChainAccess() *access.ChainAccess { return s.chainAccess }
+func (s *Ethereum) DappDb() access.Database { return s.dappDb }
func (s *Ethereum) IsListening() bool { return true } // Always listening
func (s *Ethereum) PeerCount() int { return s.net.PeerCount() }
func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() }
@@ -664,7 +670,7 @@ func dagFiles(epoch uint64) (string, string) {
return dag, "full-R" + dag
}
-func saveBlockchainVersion(db ethdb.Database, bcVersion int) {
+func saveBlockchainVersion(db access.Database, bcVersion int) {
d, _ := db.Get([]byte("BlockchainVersion"))
blockchainVersion := common.NewValue(d).Uint()
@@ -675,7 +681,7 @@ func saveBlockchainVersion(db ethdb.Database, bcVersion int) {
// upgradeChainDatabase ensures that the chain database stores block split into
// separate header and body entries.
-func upgradeChainDatabase(db ethdb.Database) error {
+func upgradeChainDatabase(db access.Database) error {
// Short circuit if the head block is stored already as separate header and body
data, err := db.Get([]byte("LastBlock"))
if err != nil {
@@ -689,7 +695,7 @@ func upgradeChainDatabase(db ethdb.Database) error {
// At least some of the database is still the old format, upgrade (skip the head block!)
glog.V(logger.Info).Info("Old database detected, upgrading...")
- if db, ok := db.(*ethdb.LDBDatabase); ok {
+ if db, ok := db.(*access.LDBDatabase); ok {
blockPrefix := []byte("block-hash-")
for it := db.NewIterator(); it.Next(); {
// Skip anything other than a combined block
@@ -732,9 +738,10 @@ func upgradeChainDatabase(db ethdb.Database) error {
return nil
}
-func addMipmapBloomBins(db ethdb.Database) (err error) {
+func addMipmapBloomBins(ca *access.ChainAccess) (err error) {
const mipmapVersion uint = 2
+ db := ca.Db()
// 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
var data []byte
@@ -756,7 +763,7 @@ func addMipmapBloomBins(db ethdb.Database) (err error) {
return
}
}()
- latestBlock := core.GetBlock(db, core.GetHeadBlockHash(db))
+ latestBlock := core.GetBlock(ca, core.GetHeadBlockHash(db))
if latestBlock == nil { // clean database
return
}
@@ -768,7 +775,7 @@ func addMipmapBloomBins(db ethdb.Database) (err error) {
if (hash == common.Hash{}) {
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))
return nil
diff --git a/eth/backend_test.go b/eth/backend_test.go
index 0379fc843a4c..4151364b5bcd 100644
--- a/eth/backend_test.go
+++ b/eth/backend_test.go
@@ -4,15 +4,15 @@ import (
"math/big"
"testing"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/ethdb"
)
func TestMipmapUpgrade(t *testing.T) {
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
addr := common.BytesToAddress([]byte("jeff"))
genesis := core.WriteGenesisBlockForTesting(db)
@@ -50,7 +50,7 @@ func TestMipmapUpgrade(t *testing.T) {
}
}
- err := addMipmapBloomBins(db)
+ err := addMipmapBloomBins(access.NewDbChainAccess(db))
if err != nil {
t.Fatal(err)
}
diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go
index 153427ee48e3..839b296a277d 100644
--- a/eth/downloader/downloader.go
+++ b/eth/downloader/downloader.go
@@ -28,9 +28,9 @@ import (
"sync/atomic"
"time"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
@@ -156,7 +156,7 @@ type Downloader struct {
}
// New creates a new downloader to fetch hashes and blocks from remote peers.
-func New(stateDb ethdb.Database, mux *event.TypeMux, hasHeader headerCheckFn, hasBlock blockCheckFn, getHeader headerRetrievalFn,
+func New(stateDb access.Database, mux *event.TypeMux, hasHeader headerCheckFn, hasBlock blockCheckFn, getHeader headerRetrievalFn,
getBlock blockRetrievalFn, headHeader headHeaderRetrievalFn, headBlock headBlockRetrievalFn, headFastBlock headFastBlockRetrievalFn,
commitHeadBlock headBlockCommitterFn, getTd tdRetrievalFn, insertHeaders headerChainInsertFn, insertBlocks blockChainInsertFn,
insertReceipts receiptChainInsertFn, rollback chainRollbackFn, dropPeer peerDropFn) *Downloader {
diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go
index ef6f74a6baf4..a693bec7f911 100644
--- a/eth/downloader/downloader_test.go
+++ b/eth/downloader/downloader_test.go
@@ -25,19 +25,19 @@ import (
"testing"
"time"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
)
var (
- testdb, _ = ethdb.NewMemDatabase()
+ testdb, _ = access.NewMemDatabase()
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
testAddress = crypto.PubkeyToAddress(testKey.PublicKey)
genesis = core.GenesisBlockForTesting(testdb, testAddress, big.NewInt(1000000000))
@@ -117,7 +117,7 @@ func makeChainFork(n, f int, parent *types.Block, parentReceipts types.Receipts)
// downloadTester is a test simulator for mocking out local block chain.
type downloadTester struct {
- stateDb ethdb.Database
+ stateDb access.Database
downloader *Downloader
ownHashes []common.Hash // Hash chain belonging to the tester
@@ -149,7 +149,7 @@ func newTester() *downloadTester {
peerReceipts: make(map[string]map[common.Hash]types.Receipts),
peerChainTds: make(map[string]map[common.Hash]*big.Int),
}
- tester.stateDb, _ = ethdb.NewMemDatabase()
+ tester.stateDb, _ = access.NewMemDatabase()
tester.downloader = New(tester.stateDb, new(event.TypeMux), tester.hasHeader, tester.hasBlock, tester.getHeader,
tester.getBlock, tester.headHeader, tester.headBlock, tester.headFastBlock, tester.commitHeadBlock, tester.getTd,
tester.insertHeaders, tester.insertBlocks, tester.insertReceipts, tester.rollback, tester.dropPeer)
@@ -682,7 +682,7 @@ func assertOwnForkedChain(t *testing.T, tester *downloadTester, common int, leng
index = len(tester.ownHashes) - lengths[len(lengths)-1] + int(tester.downloader.queue.fastSyncPivot)
}
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)
}
}
diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go
index 1fb5b6e12ff0..931bfd59c0e6 100644
--- a/eth/downloader/queue.go
+++ b/eth/downloader/queue.go
@@ -26,11 +26,11 @@ import (
"sync/atomic"
"time"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/trie"
@@ -93,7 +93,7 @@ type queue struct {
stateTaskQueue *prque.Prque // [eth/63] Priority queue of the hashes to fetch the node data for
statePendPool map[string]*fetchRequest // [eth/63] Currently pending node data retrieval operations
- stateDatabase ethdb.Database // [eth/63] Trie database to populate during state reassembly
+ stateDatabase access.Database // [eth/63] Trie database to populate during state reassembly
stateScheduler *state.StateSync // [eth/63] State trie synchronisation scheduler and integrator
stateProcessors int32 // [eth/63] Number of currently running state processors
stateSchedLock sync.RWMutex // [eth/63] Lock serialising access to the state scheduler
@@ -105,7 +105,7 @@ type queue struct {
}
// newQueue creates a new download queue for scheduling block retrieval.
-func newQueue(stateDb ethdb.Database) *queue {
+func newQueue(stateDb access.Database) *queue {
return &queue{
hashPool: make(map[common.Hash]int),
hashQueue: prque.New(),
diff --git a/eth/fetcher/fetcher_test.go b/eth/fetcher/fetcher_test.go
index 2404c8cfa71a..56409d907380 100644
--- a/eth/fetcher/fetcher_test.go
+++ b/eth/fetcher/fetcher_test.go
@@ -24,16 +24,16 @@ import (
"testing"
"time"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
)
var (
- testdb, _ = ethdb.NewMemDatabase()
+ testdb, _ = access.NewMemDatabase()
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
testAddress = crypto.PubkeyToAddress(testKey.PublicKey)
genesis = core.GenesisBlockForTesting(testdb, testAddress, big.NewInt(1000000000))
diff --git a/eth/filters/filter.go b/eth/filters/filter.go
index ff192cdf6b60..fc745a49b47f 100644
--- a/eth/filters/filter.go
+++ b/eth/filters/filter.go
@@ -19,11 +19,11 @@ package filters
import (
"math"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/ethdb"
)
type AccountChange struct {
@@ -32,7 +32,8 @@ type AccountChange struct {
// Filtering interface
type Filter struct {
- db ethdb.Database
+ ca *access.ChainAccess
+ db access.Database
begin, end int64
addresses []common.Address
topics [][]common.Hash
@@ -42,10 +43,15 @@ type Filter struct {
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.
-func New(db ethdb.Database) *Filter {
- return &Filter{db: db}
+func New(ca *access.ChainAccess) *Filter {
+ return &Filter{ca: ca, db: ca.Db()}
+}
+
+// NewWithDb creates a filter with no ODR option
+func NewWithDb(db access.Database) *Filter {
+ return &Filter{ca: access.NewDbChainAccess(db), db: db}
}
// Set the earliest and latest block for filtering.
@@ -69,7 +75,7 @@ func (self *Filter) SetTopics(topics [][]common.Hash) {
// Run filters logs with the current parameters set
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)
if self.begin == -1 {
beginBlockNo = latestBlock.NumberU64()
@@ -124,7 +130,7 @@ func (self *Filter) getLogs(start, end uint64) (logs vm.Logs) {
for i := start; i <= end; i++ {
hash := core.GetCanonicalHash(self.db, i)
if hash != (common.Hash{}) {
- block = core.GetBlock(self.db, hash)
+ block = core.GetBlock(self.ca, hash)
} else { // block not found
return logs
}
@@ -134,7 +140,7 @@ func (self *Filter) getLogs(start, end uint64) (logs vm.Logs) {
if self.bloomFilter(block) {
// Get the logs of the block
var (
- receipts = core.GetBlockReceipts(self.db, block.Hash())
+ receipts = core.GetBlockReceipts(self.ca, block.Hash())
unfiltered vm.Logs
)
for _, receipt := range receipts {
@@ -142,6 +148,7 @@ func (self *Filter) getLogs(start, end uint64) (logs vm.Logs) {
}
logs = append(logs, self.FilterLogs(unfiltered)...)
}
+ block = core.GetBlock(self.ca, block.ParentHash())
}
return logs
diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go
index a5418e2e74c3..713648b93f85 100644
--- a/eth/filters/filter_test.go
+++ b/eth/filters/filter_test.go
@@ -6,12 +6,12 @@ import (
"os"
"testing"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
)
func makeReceipt(addr common.Address) *types.Receipt {
@@ -31,7 +31,7 @@ func BenchmarkMipmaps(b *testing.B) {
defer os.RemoveAll(dir)
var (
- db, _ = ethdb.NewLDBDatabase(dir, 16)
+ db, _ = access.NewLDBDatabase(dir, 16)
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
addr2 = common.BytesToAddress([]byte("jeff"))
@@ -84,7 +84,7 @@ func BenchmarkMipmaps(b *testing.B) {
}
b.ResetTimer()
- filter := New(db)
+ filter := NewWithDb(db)
filter.SetAddresses([]common.Address{addr1, addr2, addr3, addr4})
filter.SetBeginBlock(0)
filter.SetEndBlock(-1)
@@ -105,7 +105,7 @@ func TestFilters(t *testing.T) {
defer os.RemoveAll(dir)
var (
- db, _ = ethdb.NewLDBDatabase(dir, 16)
+ db, _ = access.NewLDBDatabase(dir, 16)
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
addr = crypto.PubkeyToAddress(key1.PublicKey)
@@ -185,7 +185,7 @@ func TestFilters(t *testing.T) {
}
}
- filter := New(db)
+ filter := NewWithDb(db)
filter.SetAddresses([]common.Address{addr})
filter.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2, hash3, hash4}})
filter.SetBeginBlock(0)
@@ -196,7 +196,7 @@ func TestFilters(t *testing.T) {
t.Error("expected 4 log, got", len(logs))
}
- filter = New(db)
+ filter = NewWithDb(db)
filter.SetAddresses([]common.Address{addr})
filter.SetTopics([][]common.Hash{[]common.Hash{hash3}})
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])
}
- filter = New(db)
+ filter = NewWithDb(db)
filter.SetAddresses([]common.Address{addr})
filter.SetTopics([][]common.Hash{[]common.Hash{hash3}})
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])
}
- filter = New(db)
+ filter = NewWithDb(db)
filter.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2}})
filter.SetBeginBlock(1)
filter.SetEndBlock(10)
@@ -233,7 +233,7 @@ func TestFilters(t *testing.T) {
}
failHash := common.BytesToHash([]byte("fail"))
- filter = New(db)
+ filter = NewWithDb(db)
filter.SetTopics([][]common.Hash{[]common.Hash{failHash}})
filter.SetBeginBlock(0)
filter.SetEndBlock(-1)
@@ -244,7 +244,7 @@ func TestFilters(t *testing.T) {
}
failAddr := common.BytesToAddress([]byte("failmenow"))
- filter = New(db)
+ filter = NewWithDb(db)
filter.SetAddresses([]common.Address{failAddr})
filter.SetBeginBlock(0)
filter.SetEndBlock(-1)
@@ -254,7 +254,7 @@ func TestFilters(t *testing.T) {
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.SetBeginBlock(0)
filter.SetEndBlock(-1)
diff --git a/eth/handler.go b/eth/handler.go
index d8c5b4b648ef..3f5eb7755c8e 100644
--- a/eth/handler.go
+++ b/eth/handler.go
@@ -24,12 +24,12 @@ import (
"sync"
"time"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/fetcher"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
@@ -58,10 +58,10 @@ type blockFetcherFn func([]common.Hash) error
type ProtocolManager struct {
networkId int
- fastSync bool
- txpool txPool
- blockchain *core.BlockChain
- chaindb ethdb.Database
+ fastSync bool
+ txpool txPool
+ blockchain *core.BlockChain
+ chainAccess *access.ChainAccess
downloader *downloader.Downloader
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
// 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
if fastSync && blockchain.CurrentBlock().NumberU64() > 0 {
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
manager := &ProtocolManager{
- networkId: networkId,
- fastSync: fastSync,
- eventMux: mux,
- txpool: txpool,
- blockchain: blockchain,
- chaindb: chaindb,
- peers: newPeerSet(),
- newPeerCh: make(chan *peer, 1),
- txsyncCh: make(chan *txsync),
- quitSync: make(chan struct{}),
+ networkId: networkId,
+ fastSync: fastSync,
+ eventMux: mux,
+ txpool: txpool,
+ blockchain: blockchain,
+ chainAccess: ca,
+ peers: newPeerSet(),
+ newPeerCh: make(chan *peer, 1),
+ txsyncCh: make(chan *txsync),
+ quitSync: make(chan struct{}),
}
// Initiate a sub-protocol for every implemented version we can handle
manager.SubProtocols = make([]p2p.Protocol, 0, len(ProtocolVersions))
for i, version := range ProtocolVersions {
// Skip protocol version if incompatible with the mode of operation
- if fastSync && version < eth63 {
+ if manager.fastSync && version < eth63 {
continue
}
// Compatible; initialise the sub-protocol
@@ -137,9 +137,8 @@ func NewProtocolManager(fastSync bool, networkId int, mux *event.TypeMux, txpool
if len(manager.SubProtocols) == 0 {
return nil, errIncompatibleConfig
}
- // Construct the different synchronisation mechanisms
- manager.downloader = downloader.New(chaindb, manager.eventMux, blockchain.HasHeader, blockchain.HasBlock, blockchain.GetHeader, blockchain.GetBlock,
- blockchain.CurrentHeader, blockchain.CurrentBlock, blockchain.CurrentFastBlock, blockchain.FastSyncCommitHead, blockchain.GetTd,
+ manager.downloader = downloader.New(ca.Db(), manager.eventMux, blockchain.HasHeader, blockchain.HasBlock, blockchain.GetHeader,
+ blockchain.GetBlock, blockchain.CurrentHeader, blockchain.CurrentBlock, blockchain.CurrentFastBlock, blockchain.FastSyncCommitHead, blockchain.GetTd,
blockchain.InsertHeaderChain, blockchain.InsertChain, blockchain.InsertReceiptChain, blockchain.Rollback, manager.removePeer)
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 {
return err
}
+
// Propagate existing transactions. new transactions appearing
// after this will be sent via broadcasts.
pm.syncTransactions(p)
@@ -517,7 +517,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
return errResp(ErrDecode, "msg %v: %v", msg, err)
}
// 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)
bytes += len(entry)
}
@@ -555,7 +555,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
return errResp(ErrDecode, "msg %v: %v", msg, err)
}
// 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 header := pm.blockchain.GetHeader(hash); header == nil || header.ReceiptHash != types.EmptyRootHash {
continue
diff --git a/eth/handler_test.go b/eth/handler_test.go
index ab2ce54b1385..370c6541d167 100644
--- a/eth/handler_test.go
+++ b/eth/handler_test.go
@@ -6,13 +6,13 @@ import (
"math/rand"
"testing"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/downloader"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/params"
)
@@ -35,7 +35,6 @@ func TestProtocolCompatibility(t *testing.T) {
// Try all available compatibility configs and check for errors
for i, tt := range tests {
ProtocolVersions = []uint{tt.version}
-
pm, err := newTestProtocolManager(tt.fastSync, 0, nil, nil)
if pm != nil {
defer pm.Stop()
@@ -365,7 +364,7 @@ func testGetBlockBodies(t *testing.T, protocol int) {
for i, tt := range tests {
// Collect the hashes to request, and the response to expect
hashes, seen := []common.Hash{}, make(map[int64]bool)
- bodies := []*blockBody{}
+ bodies := []*types.Body{}
for j := 0; j < tt.random; j++ {
for {
@@ -376,7 +375,7 @@ func testGetBlockBodies(t *testing.T, protocol int) {
block := pm.blockchain.GetBlockByNumber(uint64(num))
hashes = append(hashes, block.Hash())
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
}
@@ -386,7 +385,7 @@ func testGetBlockBodies(t *testing.T, protocol int) {
hashes = append(hashes, hash)
if tt.available[j] && len(bodies) < tt.expected {
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
@@ -442,7 +441,7 @@ func testGetNodeData(t *testing.T, protocol int) {
// Fetch for now the entire chain db
hashes := []common.Hash{}
- for _, key := range pm.chaindb.(*ethdb.MemDatabase).Keys() {
+ for _, key := range pm.chainAccess.Db().(*access.MemDatabase).Keys() {
if len(key) == len(common.Hash{}) {
hashes = append(hashes, common.BytesToHash(key))
}
@@ -465,13 +464,13 @@ func testGetNodeData(t *testing.T, protocol int) {
fmt.Errorf("data hash mismatch: have %x, want %x", hash, want)
}
}
- statedb, _ := ethdb.NewMemDatabase()
+ statedb, _ := access.NewMemDatabase()
for i := 0; i < len(data); i++ {
statedb.Put(hashes[i].Bytes(), data[i])
}
accounts := []common.Address{testBankAddress, acc1Addr, acc2Addr}
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 {
state, _ := pm.blockchain.State()
@@ -537,7 +536,7 @@ func testGetReceipt(t *testing.T, protocol int) {
block := pm.blockchain.GetBlockByNumber(i)
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
p2p.Send(peer.app, 0x0f, hashes)
diff --git a/eth/helper_test.go b/eth/helper_test.go
index 65fccf7b4d8e..a48afcc83467 100644
--- a/eth/helper_test.go
+++ b/eth/helper_test.go
@@ -9,11 +9,11 @@ import (
"sync"
"testing"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover"
@@ -32,17 +32,18 @@ func newTestProtocolManager(fastSync bool, blocks int, generator func(int, *core
var (
evmux = new(event.TypeMux)
pow = new(core.FakePow)
- db, _ = ethdb.NewMemDatabase()
+ db, _ = access.NewMemDatabase()
+ ca = access.NewDbChainAccess(db)
genesis = core.WriteGenesisBlockForTesting(db, core.GenesisAccount{testBankAddress, testBankFunds})
- blockchain, _ = core.NewBlockChain(db, pow, evmux)
- blockproc = core.NewBlockProcessor(db, pow, blockchain, evmux)
+ blockchain, _ = core.NewBlockChain(ca, pow, evmux)
+ blockproc = core.NewBlockProcessor(ca, pow, blockchain, evmux)
)
blockchain.SetProcessor(blockproc)
chain, _ := core.GenerateChain(genesis, db, blocks, generator)
if _, err := blockchain.InsertChain(chain); err != nil {
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 {
return nil, err
}
diff --git a/eth/peer.go b/eth/peer.go
index 15ba22ff536c..0e20d084c78e 100644
--- a/eth/peer.go
+++ b/eth/peer.go
@@ -196,7 +196,7 @@ func (p *peer) SendBlockHeaders(headers []*types.Header) error {
}
// 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))
}
diff --git a/eth/protocol.go b/eth/protocol.go
index 808ac0601174..0d03c12ed6a5 100644
--- a/eth/protocol.go
+++ b/eth/protocol.go
@@ -201,14 +201,8 @@ type newBlockData struct {
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.
-type blockBodiesData []*blockBody
+type blockBodiesData []*types.Body
// nodeDataData is the network response packet for a node data retrieval.
type nodeDataData []struct {
diff --git a/jsre/jsre.go b/jsre/jsre.go
index af7d507c6d0a..7d8b028ed6e6 100644
--- a/jsre/jsre.go
+++ b/jsre/jsre.go
@@ -154,7 +154,7 @@ loop:
if err != nil {
fmt.Println("js error:", err, arguments)
}
-
+
_, inreg := registry[timer] // when clearInterval is called from within the callback don't reset it
if timer.interval && inreg {
timer.timer.Reset(timer.duration)
diff --git a/miner/worker.go b/miner/worker.go
index 2d072ef60b2e..a37ba5414b9d 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -24,13 +24,13 @@ import (
"sync/atomic"
"time"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
@@ -101,7 +101,7 @@ type worker struct {
eth core.Backend
chain *core.BlockChain
proc *core.BlockProcessor
- chainDb ethdb.Database
+ chainDb access.Database
coinbase common.Address
gasPrice *big.Int
@@ -359,7 +359,7 @@ func (self *worker) push(work *Work) {
// makeCurrent creates a new environment for the current cycle.
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 {
return err
}
diff --git a/rpc/api/debug.go b/rpc/api/debug.go
index d2cbc7f19d6c..b48ef5463498 100644
--- a/rpc/api/debug.go
+++ b/rpc/api/debug.go
@@ -119,7 +119,7 @@ func (self *debugApi) DumpBlock(req *shared.Request) (interface{}, error) {
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 {
return nil, err
}
diff --git a/rpc/api/eth.go b/rpc/api/eth.go
index b84ae31da28b..da75fa1525dd 100644
--- a/rpc/api/eth.go
+++ b/rpc/api/eth.go
@@ -19,9 +19,8 @@ package api
import (
"bytes"
"encoding/json"
- "math/big"
-
"fmt"
+ "math/big"
"github.com/ethereum/go-ethereum/common"
"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/shared"
"github.com/ethereum/go-ethereum/xeth"
+ "golang.org/x/net/context"
"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 {
return nil, shared.NewDecodeParamError(err.Error())
}
-
- return self.xeth.AtStateNum(args.BlockNumber).BalanceAt(args.Address), nil
+ res := self.xeth.WithCtx(req.GetCtx()).AtStateNum(args.BlockNumber).BalanceAt(args.Address)
+ return res, nil
}
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) {
+ if self.ethereum.Downloader() == nil {
+ return false, nil
+ }
origin, current, height := self.ethereum.Downloader().Progress()
if current < height {
return map[string]interface{}{
@@ -191,7 +194,7 @@ func (self *ethApi) GetStorage(req *shared.Request) (interface{}, 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) {
@@ -200,7 +203,7 @@ func (self *ethApi) GetStorageAt(req *shared.Request) (interface{}, 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) {
@@ -209,7 +212,7 @@ func (self *ethApi) GetTransactionCount(req *shared.Request) (interface{}, 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
}
@@ -218,7 +221,7 @@ func (self *ethApi) GetBlockTransactionCountByHash(req *shared.Request) (interfa
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error())
}
- block := self.xeth.EthBlockByHash(args.Hash)
+ block := self.xeth.WithCtx(req.GetCtx()).EthBlockByHash(args.Hash)
if block == nil {
return nil, nil
}
@@ -231,7 +234,7 @@ func (self *ethApi) GetBlockTransactionCountByNumber(req *shared.Request) (inter
return nil, shared.NewDecodeParamError(err.Error())
}
- block := self.xeth.EthBlockByNumber(args.BlockNumber)
+ block := self.xeth.WithCtx(req.GetCtx()).EthBlockByNumber(args.BlockNumber)
if block == nil {
return nil, nil
}
@@ -244,7 +247,7 @@ func (self *ethApi) GetUncleCountByBlockHash(req *shared.Request) (interface{},
return nil, shared.NewDecodeParamError(err.Error())
}
- block := self.xeth.EthBlockByHash(args.Hash)
+ block := self.xeth.WithCtx(req.GetCtx()).EthBlockByHash(args.Hash)
if block == nil {
return nil, nil
}
@@ -257,7 +260,7 @@ func (self *ethApi) GetUncleCountByBlockNumber(req *shared.Request) (interface{}
return nil, shared.NewDecodeParamError(err.Error())
}
- block := self.xeth.EthBlockByNumber(args.BlockNumber)
+ block := self.xeth.WithCtx(req.GetCtx()).EthBlockByNumber(args.BlockNumber)
if block == 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 {
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
}
@@ -337,7 +340,7 @@ func (self *ethApi) GetNatSpec(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 {
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) {
- v, _, err := self.doCall(req.Params)
+ v, _, err := self.doCall(req.GetCtx(), req.Params)
if err != nil {
return nil, err
}
@@ -368,13 +371,13 @@ func (self *ethApi) Flush(req *shared.Request) (interface{}, error) {
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)
if err := self.codec.Decode(params, &args); err != nil {
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) {
@@ -382,7 +385,7 @@ func (self *ethApi) GetBlockByHash(req *shared.Request) (interface{}, error) {
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error())
}
- block := self.xeth.EthBlockByHash(args.BlockHash)
+ block := self.xeth.WithCtx(req.GetCtx()).EthBlockByHash(args.BlockHash)
if block == nil {
return nil, nil
}
@@ -395,7 +398,7 @@ func (self *ethApi) GetBlockByNumber(req *shared.Request) (interface{}, 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 {
return nil, nil
}
@@ -408,7 +411,7 @@ func (self *ethApi) GetTransactionByHash(req *shared.Request) (interface{}, erro
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 {
v := NewTransactionRes(tx)
// 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())
}
- raw := self.xeth.EthBlockByHash(args.Hash)
+ raw := self.xeth.WithCtx(req.GetCtx()).EthBlockByHash(args.Hash)
if raw == nil {
return nil, nil
}
@@ -446,7 +449,7 @@ func (self *ethApi) GetTransactionByBlockNumberAndIndex(req *shared.Request) (in
return nil, shared.NewDecodeParamError(err.Error())
}
- raw := self.xeth.EthBlockByNumber(args.BlockNumber)
+ raw := self.xeth.WithCtx(req.GetCtx()).EthBlockByNumber(args.BlockNumber)
if raw == nil {
return nil, nil
}
@@ -464,7 +467,7 @@ func (self *ethApi) GetUncleByBlockHashAndIndex(req *shared.Request) (interface{
return nil, shared.NewDecodeParamError(err.Error())
}
- raw := self.xeth.EthBlockByHash(args.Hash)
+ raw := self.xeth.WithCtx(req.GetCtx()).EthBlockByHash(args.Hash)
if raw == nil {
return nil, nil
}
@@ -482,7 +485,7 @@ func (self *ethApi) GetUncleByBlockNumberAndIndex(req *shared.Request) (interfac
return nil, shared.NewDecodeParamError(err.Error())
}
- raw := self.xeth.EthBlockByNumber(args.BlockNumber)
+ raw := self.xeth.WithCtx(req.GetCtx()).EthBlockByNumber(args.BlockNumber)
if raw == nil {
return nil, nil
}
@@ -662,7 +665,7 @@ func (self *ethApi) GetTransactionReceipt(req *shared.Request) (interface{}, err
txhash := common.BytesToHash(common.FromHex(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
// if err != nil {
// return err, nil
diff --git a/rpc/codec/json.go b/rpc/codec/json.go
index cfc449143bbb..f5d1b1c8148c 100644
--- a/rpc/codec/json.go
+++ b/rpc/codec/json.go
@@ -20,8 +20,8 @@ import (
"encoding/json"
"fmt"
"net"
- "time"
"strings"
+ "time"
"github.com/ethereum/go-ethereum/rpc/shared"
)
diff --git a/rpc/comms/comms.go b/rpc/comms/comms.go
index 61fba5722c9d..492a65d72765 100644
--- a/rpc/comms/comms.go
+++ b/rpc/comms/comms.go
@@ -17,14 +17,14 @@
package comms
import (
+ "fmt"
"io"
"net"
-
- "fmt"
- "strings"
-
"strconv"
+ "strings"
+ "time"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"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()
}()
+ channelID := access.NewChannelID(time.Second)
+
for {
requests, isBatch, err := codec.ReadRequest()
if err == io.EOF {
@@ -78,11 +80,21 @@ func handle(id int, conn net.Conn, api shared.EthereumApi, c codec.Codec) {
return
}
+ ctx, _ := access.NewContext(channelID)
+
if isBatch {
responses := make([]*interface{}, len(requests))
responseCount := 0
+ var res interface{}
+ var err error
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 {
rpcResponse := shared.NewRpcResponse(req.Id, req.Jsonrpc, res, err)
responses[responseCount] = rpcResponse
@@ -97,7 +109,12 @@ func handle(id int, conn net.Conn, api shared.EthereumApi, c codec.Codec) {
}
} else {
var rpcResponse interface{}
+ requests[0].SetCtx(ctx)
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)
err = codec.WriteResponse(rpcResponse)
diff --git a/rpc/comms/http.go b/rpc/comms/http.go
index f4a930d0ef5d..82d2d31b867d 100644
--- a/rpc/comms/http.go
+++ b/rpc/comms/http.go
@@ -29,6 +29,7 @@ import (
"io"
"io/ioutil"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rpc/codec"
@@ -65,8 +66,9 @@ type stopServer struct {
}
type handler struct {
- codec codec.Codec
- api shared.EthereumApi
+ codec codec.Codec
+ api shared.EthereumApi
+ channelID *access.OdrChannelID
}
// StartHTTP starts listening for RPC requests sent via HTTP.
@@ -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
}
// 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 {
opts := cors.Options{
AllowedMethods: []string{"POST"},
@@ -121,9 +124,15 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
c := h.codec.New(nil)
+ ctx, _ := access.NewContext(h.channelID)
var rpcReq shared.Request
if err = c.Decode(payload, &rpcReq); err == nil {
+ rpcReq.SetCtx(ctx)
reply, err := h.api.Execute(&rpcReq)
+ if access.Terminated(ctx) {
+ reply = nil
+ err = ctx.Err()
+ }
res := shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err)
sendJSON(w, &res)
return
@@ -133,8 +142,16 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if err = c.Decode(payload, &reqBatch); err == nil {
resBatch := make([]*interface{}, len(reqBatch))
resCount := 0
+ var reply interface{}
+ var err error
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
resBatch[i] = shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err)
resCount += 1
diff --git a/rpc/comms/inproc.go b/rpc/comms/inproc.go
index e8058e32bf53..d6725b045fc2 100644
--- a/rpc/comms/inproc.go
+++ b/rpc/comms/inproc.go
@@ -18,7 +18,9 @@ package comms
import (
"fmt"
+ "time"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/shared"
)
@@ -30,12 +32,14 @@ type InProcClient struct {
lastJsonrpc string
lastErr error
lastRes interface{}
+ channelID *access.OdrChannelID
}
// Create a new in process client
func NewInProcClient(codec codec.Codec) *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 {
self.lastId = r.Id
self.lastJsonrpc = r.Jsonrpc
+ ctx, _ := access.NewContext(self.channelID)
+ r.SetCtx(ctx)
self.lastRes, self.lastErr = self.api.Execute(r)
+ if access.Terminated(ctx) {
+ self.lastRes = nil
+ self.lastErr = ctx.Err()
+ }
return self.lastErr
}
diff --git a/rpc/shared/types.go b/rpc/shared/types.go
index db328234d2e6..1e5db461ef25 100644
--- a/rpc/shared/types.go
+++ b/rpc/shared/types.go
@@ -21,6 +21,7 @@ import (
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
+ "golang.org/x/net/context"
)
// Ethereum RPC API interface
@@ -39,11 +40,14 @@ type EthereumApi interface {
}
// 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 {
Id interface{} `json:"id"`
Jsonrpc string `json:"jsonrpc"`
Method string `json:"method"`
Params json.RawMessage `json:"params"`
+ ctx context.Context
}
// RPC response
@@ -73,6 +77,16 @@ type ErrorObject struct {
// 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
func NewRpcErrorResponse(id interface{}, jsonrpcver string, errCode int, err error) *ErrorResponse {
jsonerr := &ErrorObject{errCode, err.Error()}
diff --git a/tests/block_test_util.go b/tests/block_test_util.go
index 6a2eb96a424c..1ae940699c10 100644
--- a/tests/block_test_util.go
+++ b/tests/block_test_util.go
@@ -28,6 +28,7 @@ import (
"strings"
"time"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
@@ -35,7 +36,6 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rlp"
)
@@ -164,13 +164,13 @@ func runBlockTests(bt map[string]*BlockTest, skipTests []string) error {
func runBlockTest(test *BlockTest) error {
ks := crypto.NewKeyStorePassphrase(filepath.Join(common.DefaultDataDir(), "keystore"), crypto.StandardScryptN, crypto.StandardScryptP)
am := accounts.NewManager(ks)
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
cfg := ð.Config{
DataDir: common.DefaultDataDir(),
Verbosity: 5,
Etherbase: common.Address{},
AccountManager: am,
- NewDB: func(path string) (ethdb.Database, error) { return db, nil },
+ NewDB: func(path string) (access.Database, error) { return db, nil },
}
cfg.GenesisBlock = test.Genesis
@@ -216,8 +216,8 @@ func runBlockTest(test *BlockTest) error {
// InsertPreState populates the given database with the genesis
// accounts defined by the test.
-func (t *BlockTest) InsertPreState(db ethdb.Database, am *accounts.Manager) (*state.StateDB, error) {
- statedb, err := state.New(common.Hash{}, db)
+func (t *BlockTest) InsertPreState(db access.Database, am *accounts.Manager) (*state.StateDB, error) {
+ statedb, err := state.New(common.Hash{}, access.NewDbChainAccess(db))
if err != nil {
return nil, err
}
diff --git a/tests/state_test_util.go b/tests/state_test_util.go
index 352fe3570ba2..cbdaa849b60d 100644
--- a/tests/state_test_util.go
+++ b/tests/state_test_util.go
@@ -25,12 +25,12 @@ import (
"strconv"
"testing"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger/glog"
)
@@ -102,8 +102,8 @@ func BenchStateTest(p string, conf bconf, b *testing.B) error {
func benchStateTest(test VmTest, env map[string]string, b *testing.B) {
b.StopTimer()
- db, _ := ethdb.NewMemDatabase()
- statedb, _ := state.New(common.Hash{}, db)
+ db, _ := access.NewMemDatabase()
+ statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db))
for addr, account := range test.Pre {
obj := StateObjectFromAccount(db, addr, account)
statedb.SetStateObject(obj)
@@ -141,8 +141,8 @@ func runStateTests(tests map[string]VmTest, skipTests []string) error {
}
func runStateTest(test VmTest) error {
- db, _ := ethdb.NewMemDatabase()
- statedb, _ := state.New(common.Hash{}, db)
+ db, _ := access.NewMemDatabase()
+ statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db))
for addr, account := range test.Pre {
obj := StateObjectFromAccount(db, addr, account)
statedb.SetStateObject(obj)
diff --git a/tests/util.go b/tests/util.go
index 571161683fe4..81afc32e254b 100644
--- a/tests/util.go
+++ b/tests/util.go
@@ -21,13 +21,13 @@ import (
"fmt"
"math/big"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
)
func checkLogs(tlog []Log, logs vm.Logs) error {
@@ -88,8 +88,8 @@ func (self Log) Topics() [][]byte {
return t
}
-func StateObjectFromAccount(db ethdb.Database, addr string, account Account) *state.StateObject {
- obj := state.NewStateObject(common.HexToAddress(addr), db)
+func StateObjectFromAccount(db access.Database, addr string, account Account) *state.StateObject {
+ obj := state.NewStateObject(access.NoOdr, common.HexToAddress(addr), access.NewDbChainAccess(db))
obj.SetBalance(common.Big(account.Balance))
if common.IsHex(account.Code) {
diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go
index ddd14b1a3e4d..4a2a2fc35099 100644
--- a/tests/vm_test_util.go
+++ b/tests/vm_test_util.go
@@ -24,10 +24,10 @@ import (
"strconv"
"testing"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger/glog"
)
@@ -107,8 +107,8 @@ func BenchVmTest(p string, conf bconf, b *testing.B) error {
func benchVmTest(test VmTest, env map[string]string, b *testing.B) {
b.StopTimer()
- db, _ := ethdb.NewMemDatabase()
- statedb, _ := state.New(common.Hash{}, db)
+ db, _ := access.NewMemDatabase()
+ statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db))
for addr, account := range test.Pre {
obj := StateObjectFromAccount(db, addr, account)
statedb.SetStateObject(obj)
@@ -158,8 +158,8 @@ func runVmTests(tests map[string]VmTest, skipTests []string) error {
}
func runVmTest(test VmTest) error {
- db, _ := ethdb.NewMemDatabase()
- statedb, _ := state.New(common.Hash{}, db)
+ db, _ := access.NewMemDatabase()
+ statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db))
for addr, account := range test.Pre {
obj := StateObjectFromAccount(db, addr, account)
statedb.SetStateObject(obj)
diff --git a/trie/arc.go b/trie/arc.go
index 9da012e168ba..229420a53829 100644
--- a/trie/arc.go
+++ b/trie/arc.go
@@ -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.
// This optimizes future access to this entry (side effect).
func (a *arc) Put(key hashNode, value node) bool {
diff --git a/trie/encoding.go b/trie/encoding.go
index 3c172b8438ad..98ad8ee8b358 100644
--- a/trie/encoding.go
+++ b/trie/encoding.go
@@ -58,6 +58,7 @@ func compactDecode(str []byte) []byte {
return base
}
+// compactHexDecode decodes a series of nibbles from a byte array
func compactHexDecode(str []byte) []byte {
l := len(str)*2 + 1
var nibbles = make([]byte, l)
@@ -69,6 +70,24 @@ func compactHexDecode(str []byte) []byte {
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 {
l := len(key) / 2
var res = make([]byte, l)
diff --git a/trie/encoding_test.go b/trie/encoding_test.go
index 061d48d58bd8..2f125ef2f822 100644
--- a/trie/encoding_test.go
+++ b/trie/encoding_test.go
@@ -57,6 +57,12 @@ func (s *TrieEncodingSuite) TestCompactHexDecode(c *checker.C) {
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) {
// odd compact decode
exp := []byte{1, 2, 3, 4, 5}
diff --git a/trie/iterator.go b/trie/iterator.go
index 38555fe08df8..3f1d0b0d28ed 100644
--- a/trie/iterator.go
+++ b/trie/iterator.go
@@ -100,7 +100,7 @@ func (self *Iterator) next(node interface{}, key []byte, isIterStart bool) []byt
}
case hashNode:
- return self.next(self.trie.resolveHash(node), key, isIterStart)
+ return self.next(self.trie.resolveHash(node, nil, nil), key, isIterStart)
}
return nil
}
@@ -127,7 +127,7 @@ func (self *Iterator) key(node interface{}) []byte {
}
}
case hashNode:
- return self.key(self.trie.resolveHash(node))
+ return self.key(self.trie.resolveHash(node, nil, nil))
}
return nil
diff --git a/trie/proof.go b/trie/proof.go
index a705c49db0bd..830bc486e10d 100644
--- a/trie/proof.go
+++ b/trie/proof.go
@@ -10,6 +10,8 @@ import (
"github.com/ethereum/go-ethereum/rlp"
)
+type MerkleProof []rlp.RawValue
+
// 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
// 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.
// 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.
key = compactHexDecode(key)
nodes := []node{}
tn := t.root
- for len(key) > 0 {
+ for len(key) > 0 && tn != nil {
switch n := tn.(type) {
case shortNode:
if len(key) < len(n.Key) || !bytes.Equal(n.Key, key[:len(n.Key)]) {
// The trie doesn't contain the key.
- return nil
+ tn = nil
+ } else {
+ tn = n.Val
}
- tn = n.Val
key = key[len(n.Key):]
nodes = append(nodes, n)
case fullNode:
tn = n[key[0]]
key = key[1:]
nodes = append(nodes, n)
- case nil:
- return nil
case hashNode:
- tn = t.resolveHash(n)
+ tn = t.resolveHash(n, nil, nil)
default:
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
// returns an error if the proof contains invalid trie nodes or the
// 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)
sha := sha3.NewKeccak256()
wantHash := rootHash.Bytes()
@@ -84,7 +85,12 @@ func VerifyProof(rootHash common.Hash, key []byte, proof []rlp.RawValue) (value
keyrest, cld := get(n, key)
switch cld := cld.(type) {
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:
key = keyrest
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")
}
+// 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) {
for len(key) > 0 {
switch n := tn.(type) {
diff --git a/trie/proof_test.go b/trie/proof_test.go
index 6b5bef05c43f..ecb53826bc1b 100644
--- a/trie/proof_test.go
+++ b/trie/proof_test.go
@@ -7,6 +7,7 @@ import (
"testing"
"time"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"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, _ := access.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) {
trie := new(Trie)
updateString(trie, "k", "v")
diff --git a/trie/secure_trie.go b/trie/secure_trie.go
index 47d1934d0532..b20637b48f24 100644
--- a/trie/secure_trie.go
+++ b/trie/secure_trie.go
@@ -19,8 +19,10 @@ package trie
import (
"hash"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto/sha3"
+ "golang.org/x/net/context"
)
var secureKeyPrefix = []byte("secure-key-")
@@ -50,16 +52,32 @@ type SecureTrie struct {
// and returns ErrMissingRoot if the root node cannpt be found.
// Accessing the trie loads nodes from db on demand.
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 {
panic("NewSecure called with nil database")
}
- trie, err := New(root, db)
+ trie, err := NewOdr(ctx, root, db, access)
if err != nil {
return nil, err
}
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.
// The value bytes must not be modified by the caller.
func (t *SecureTrie) Get(key []byte) []byte {
diff --git a/trie/secure_trie_test.go b/trie/secure_trie_test.go
index 13c6cd02e9e3..c070363cdc05 100644
--- a/trie/secure_trie_test.go
+++ b/trie/secure_trie_test.go
@@ -20,13 +20,13 @@ import (
"bytes"
"testing"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
)
func newEmptySecure() *SecureTrie {
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
trie, _ := NewSecure(common.Hash{}, db)
return trie
}
diff --git a/trie/sync.go b/trie/sync.go
index d55399d06b8e..9854466a401b 100644
--- a/trie/sync.go
+++ b/trie/sync.go
@@ -19,8 +19,8 @@ package trie
import (
"fmt"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/ethdb"
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
)
@@ -53,13 +53,13 @@ type TrieSyncLeafCallback func(leaf []byte, parent common.Hash) error
// unknown trie hashes to retrieve, accepts node data associated with said hashes
// and reconstructs the trie step by step until all is done.
type TrieSync struct {
- database ethdb.Database // State database for storing all the assembled node data
+ database access.Database // State database for storing all the assembled node data
requests map[common.Hash]*request // Pending requests pertaining to a key hash
queue *prque.Prque // Priority queue with the pending requests
}
// NewTrieSync creates a new trie data download scheduler.
-func NewTrieSync(root common.Hash, database ethdb.Database, callback TrieSyncLeafCallback) *TrieSync {
+func NewTrieSync(root common.Hash, database access.Database, callback TrieSyncLeafCallback) *TrieSync {
ts := &TrieSync{
database: database,
requests: make(map[common.Hash]*request),
@@ -258,7 +258,7 @@ func (s *TrieSync) children(req *request) ([]*request, error) {
// commit finalizes a retrieval request and stores it into the database. If any
// of the referencing parent requests complete due to this commit, they are also
// committed themselves.
-func (s *TrieSync) commit(req *request, batch ethdb.Batch) (err error) {
+func (s *TrieSync) commit(req *request, batch access.Batch) (err error) {
// Create a new batch if none was specified
if batch == nil {
batch = s.database.NewBatch()
diff --git a/trie/sync_test.go b/trie/sync_test.go
index 9c036a3a9261..0ba2a5d26b15 100644
--- a/trie/sync_test.go
+++ b/trie/sync_test.go
@@ -20,14 +20,14 @@ import (
"bytes"
"testing"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/ethdb"
)
// makeTestTrie create a sample test trie to test node-wise reconstruction.
-func makeTestTrie() (ethdb.Database, *Trie, map[string][]byte) {
+func makeTestTrie() (access.Database, *Trie, map[string][]byte) {
// Create an empty trie
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
trie, _ := New(common.Hash{}, db)
// Fill it with some arbitrary data
@@ -67,7 +67,7 @@ func TestEmptyTrieSync(t *testing.T) {
emptyB, _ := New(emptyRoot, nil)
for i, trie := range []*Trie{emptyA, emptyB} {
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
if req := NewTrieSync(common.BytesToHash(trie.Root()), db, nil).Missing(1); len(req) != 0 {
t.Errorf("test %d: content requested for empty trie: %v", i, req)
}
@@ -84,7 +84,7 @@ func testIterativeTrieSync(t *testing.T, batch int) {
srcDb, srcTrie, srcData := makeTestTrie()
// Create a destination trie and sync with the scheduler
- dstDb, _ := ethdb.NewMemDatabase()
+ dstDb, _ := access.NewMemDatabase()
sched := NewTrieSync(common.BytesToHash(srcTrie.Root()), dstDb, nil)
queue := append([]common.Hash{}, sched.Missing(batch)...)
@@ -113,7 +113,7 @@ func TestIterativeDelayedTrieSync(t *testing.T) {
srcDb, srcTrie, srcData := makeTestTrie()
// Create a destination trie and sync with the scheduler
- dstDb, _ := ethdb.NewMemDatabase()
+ dstDb, _ := access.NewMemDatabase()
sched := NewTrieSync(common.BytesToHash(srcTrie.Root()), dstDb, nil)
queue := append([]common.Hash{}, sched.Missing(10000)...)
@@ -147,7 +147,7 @@ func testIterativeRandomTrieSync(t *testing.T, batch int) {
srcDb, srcTrie, srcData := makeTestTrie()
// Create a destination trie and sync with the scheduler
- dstDb, _ := ethdb.NewMemDatabase()
+ dstDb, _ := access.NewMemDatabase()
sched := NewTrieSync(common.BytesToHash(srcTrie.Root()), dstDb, nil)
queue := make(map[common.Hash]struct{})
@@ -184,7 +184,7 @@ func TestIterativeRandomDelayedTrieSync(t *testing.T) {
srcDb, srcTrie, srcData := makeTestTrie()
// Create a destination trie and sync with the scheduler
- dstDb, _ := ethdb.NewMemDatabase()
+ dstDb, _ := access.NewMemDatabase()
sched := NewTrieSync(common.BytesToHash(srcTrie.Root()), dstDb, nil)
queue := make(map[common.Hash]struct{})
@@ -227,7 +227,7 @@ func TestDuplicateAvoidanceTrieSync(t *testing.T) {
srcDb, srcTrie, srcData := makeTestTrie()
// Create a destination trie and sync with the scheduler
- dstDb, _ := ethdb.NewMemDatabase()
+ dstDb, _ := access.NewMemDatabase()
sched := NewTrieSync(common.BytesToHash(srcTrie.Root()), dstDb, nil)
queue := append([]common.Hash{}, sched.Missing(0)...)
diff --git a/trie/trie.go b/trie/trie.go
index a3a383fb588b..e9d4fc29aeb2 100644
--- a/trie/trie.go
+++ b/trie/trie.go
@@ -23,12 +23,14 @@ import (
"fmt"
"hash"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rlp"
+ "golang.org/x/net/context"
)
const defaultCacheCapacity = 800
@@ -44,8 +46,18 @@ var (
emptyState = crypto.Sha3Hash(nil)
)
+func ClearGlobalCache() {
+ globalCache.Clear()
+}
+
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.
type Database interface {
DatabaseWriter
@@ -67,8 +79,10 @@ type DatabaseWriter interface {
//
// Trie is not safe for concurrent use.
type Trie struct {
- root node
- db Database
+ root node
+ db Database
+ access OdrAccess
+ ctx context.Context
*hasher
}
@@ -79,19 +93,32 @@ type Trie struct {
// New will panics if db is nil or root does not exist in the
// database. Accessing the trie loads nodes from db on demand.
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 db == nil {
panic("trie.New: cannot use existing root without a database")
}
- if v, _ := trie.db.Get(root[:]); len(v) == 0 {
- return nil, ErrMissingRoot
+ if access == nil || !access.OdrEnabled() {
+ if v, _ := trie.db.Get(root[:]); len(v) == 0 {
+ return nil, ErrMissingRoot
+ }
}
trie.root = hashNode(root.Bytes())
}
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.
func (t *Trie) Iterator() *Iterator {
return NewIterator(t)
@@ -101,22 +128,23 @@ func (t *Trie) Iterator() *Iterator {
// The value bytes must not be modified by the caller.
func (t *Trie) Get(key []byte) []byte {
key = compactHexDecode(key)
+ pos := 0
tn := t.root
- for len(key) > 0 {
+ for pos < len(key) {
switch n := tn.(type) {
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
}
tn = n.Val
- key = key[len(n.Key):]
+ pos += len(n.Key)
case fullNode:
- tn = n[key[0]]
- key = key[1:]
+ tn = n[key[pos]]
+ pos++
case nil:
return nil
case hashNode:
- tn = t.resolveHash(n)
+ tn = t.resolveHash(n, key[:pos], key[pos:])
default:
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) {
k := compactHexDecode(key)
if len(value) != 0 {
- t.root = t.insert(t.root, k, valueNode(value))
+ t.root = t.insert(t.root, nil, k, valueNode(value))
} 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 {
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
// and only update the value.
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.
var branch fullNode
- branch[n.Key[matchlen]] = t.insert(nil, n.Key[matchlen+1:], n.Val)
- branch[key[matchlen]] = t.insert(nil, key[matchlen+1:], value)
+ 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, append(prefix, key[:matchlen+1]...), key[matchlen+1:], value)
// Replace this shortNode with the branch if it occurs at index 0.
if matchlen == 0 {
return branch
@@ -163,7 +191,7 @@ func (t *Trie) insert(n node, key []byte, value node) node {
return shortNode{key[:matchlen], branch}
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
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
// 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:
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.
func (t *Trie) Delete(key []byte) {
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.
// It reduces the trie to minimal form by simplifying
// 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) {
case shortNode:
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
// subtrie must contain at least two other values with keys
// 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) {
case shortNode:
// Deleting from the subtrie reduced it to another
@@ -221,7 +249,7 @@ func (t *Trie) delete(n node, key []byte) node {
}
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
// reduce the full node to a short node if only one entry is
// 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
// might not be loaded yet, resolve it just for this
// check.
- cnode := t.resolve(n[pos])
+ cnode := t.resolve(n[pos], prefix, []byte{byte(pos)})
if cnode, ok := cnode.(shortNode); ok {
k := append([]byte{byte(pos)}, cnode.Key...)
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
// 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:
panic(fmt.Sprintf("%T: invalid node: %v (%v)", n, n, key))
@@ -287,19 +315,28 @@ func concat(s1 []byte, s2 ...byte) []byte {
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 {
- return t.resolveHash(n)
+ return t.resolveHash(n, prefix, suffix)
}
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 {
return v
}
enc, err := t.db.Get(n)
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.
// Disk I/O errors shouldn't produce nil (and cause a
// 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
// database before using the trie.
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)
if err != nil {
return (common.Hash{}), err
diff --git a/trie/trie_test.go b/trie/trie_test.go
index c96861bedb22..61e457e998be 100644
--- a/trie/trie_test.go
+++ b/trie/trie_test.go
@@ -25,8 +25,8 @@ import (
"testing"
"github.com/davecgh/go-spew/spew"
+ "github.com/ethereum/go-ethereum/access"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/ethdb"
)
func init() {
@@ -36,7 +36,7 @@ func init() {
// Used for testing
func newEmpty() *Trie {
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
trie, _ := New(common.Hash{}, db)
return trie
}
@@ -59,7 +59,7 @@ func TestNull(t *testing.T) {
}
func TestMissingRoot(t *testing.T) {
- db, _ := ethdb.NewMemDatabase()
+ db, _ := access.NewMemDatabase()
trie, err := New(common.HexToHash("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"), db)
if trie != nil {
t.Error("New returned non-nil trie for invalid root")
@@ -388,7 +388,7 @@ func tempDB() (string, Database) {
if err != nil {
panic(fmt.Sprintf("can't create temporary directory: %v", err))
}
- db, err := ethdb.NewLDBDatabase(dir, 300*1024)
+ db, err := access.NewLDBDatabase(dir, 300*1024)
if err != nil {
panic(fmt.Sprintf("can't create temporary database: %v", err))
}
diff --git a/xeth/state.go b/xeth/state.go
index 981fe63b7e1a..e087ba8ecb79 100644
--- a/xeth/state.go
+++ b/xeth/state.go
@@ -45,7 +45,7 @@ func (self *State) SafeGet(addr string) *Object {
func (self *State) safeGet(addr string) *state.StateObject {
object := self.state.GetStateObject(common.HexToAddress(addr))
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
diff --git a/xeth/xeth.go b/xeth/xeth.go
index 35e6dd52d806..75ca91838e21 100644
--- a/xeth/xeth.go
+++ b/xeth/xeth.go
@@ -41,6 +41,7 @@ import (
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/rlp"
+ "golang.org/x/net/context"
)
var (
@@ -84,6 +85,7 @@ type XEth struct {
state *State
whisper *Whisper
filterManager *filters.FilterSystem
+ ctx context.Context
}
func NewTest(eth *eth.Ethereum, frontend Frontend) *XEth {
@@ -207,15 +209,15 @@ func (self *XEth) AtStateNum(num int64) *XEth {
var err error
switch num {
case -2:
- st = self.backend.Miner().PendingState().Copy()
+ st = self.backend.Miner().PendingState().CopyOdr(self.ctx)
default:
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 {
return nil
}
} 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 {
return nil
}
@@ -238,6 +240,19 @@ func (self *XEth) WithState(statedb *state.StateDB) *XEth {
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
// waits until blockchain height is greater n at any time
// given the current head, waits for the next chain event
@@ -270,7 +285,7 @@ func (self *XEth) UpdateState() (wait chan *big.Int) {
wait <- n
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 {
glog.V(logger.Error).Infoln("Could not create new state: %v", err)
return
@@ -305,19 +320,19 @@ func (self *XEth) getBlockByHeight(height int64) *types.Block {
num = uint64(height)
}
- return self.backend.BlockChain().GetBlockByNumber(num)
+ return self.backend.BlockChain().GetBlockByNumberOdr(self.ctx, num)
}
func (self *XEth) BlockByHash(strHash string) *Block {
hash := common.HexToHash(strHash)
- block := self.backend.BlockChain().GetBlock(hash)
+ block := self.backend.BlockChain().GetBlockOdr(self.ctx, hash)
return NewBlock(block)
}
func (self *XEth) EthBlockByHash(strHash string) *types.Block {
hash := common.HexToHash(strHash)
- block := self.backend.BlockChain().GetBlock(hash)
+ block := self.backend.BlockChain().GetBlockOdr(self.ctx, hash)
return block
}
@@ -379,11 +394,11 @@ func (self *XEth) CurrentBlock() *types.Block {
}
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 {
- return core.GetReceipt(self.backend.ChainDb(), txhash)
+func (self *XEth) GetTxReceipt(txhash common.Hash) *types.Receipt { //ODR
+ return core.GetReceipt(self.backend.ChainAccess(), txhash) //TODO
}
func (self *XEth) GasLimit() *big.Int {
@@ -547,7 +562,7 @@ func (self *XEth) NewLogFilter(earliest, latest int64, skip, max int, address []
self.logMu.Lock()
defer self.logMu.Unlock()
- filter := filters.New(self.backend.ChainDb())
+ filter := filters.New(self.backend.ChainAccess())
id := self.filterManager.Add(filter)
self.logQueue[id] = &logQueue{timeout: time.Now()}
@@ -571,7 +586,7 @@ func (self *XEth) NewTransactionFilter() int {
self.transactionMu.Lock()
defer self.transactionMu.Unlock()
- filter := filters.New(self.backend.ChainDb())
+ filter := filters.New(self.backend.ChainAccess())
id := self.filterManager.Add(filter)
self.transactionQueue[id] = &hashQueue{timeout: time.Now()}
@@ -590,7 +605,7 @@ func (self *XEth) NewBlockFilter() int {
self.blockMu.Lock()
defer self.blockMu.Unlock()
- filter := filters.New(self.backend.ChainDb())
+ filter := filters.New(self.backend.ChainAccess())
id := self.filterManager.Add(filter)
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 {
- filter := filters.New(self.backend.ChainDb())
+ filter := filters.New(self.backend.ChainAccess())
filter.SetBeginBlock(earliest)
filter.SetEndBlock(latest)
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) {
- statedb := self.State().State().Copy()
+ statedb := self.State().State().CopyWithCtx()
var from *state.StateObject
if len(fromStr) == 0 {
accounts, err := self.backend.AccountManager().Accounts()