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