diff --git a/internal/db/db.go b/internal/db/db.go
deleted file mode 100644
index 4bd5f3db9..000000000
--- a/internal/db/db.go
+++ /dev/null
@@ -1,242 +0,0 @@
-package db
-
-import (
- "sync"
- "time"
-
- "github.com/harmony-one/harmony/log"
- "github.com/syndtr/goleveldb/leveldb"
- "github.com/syndtr/goleveldb/leveldb/errors"
- "github.com/syndtr/goleveldb/leveldb/filter"
- "github.com/syndtr/goleveldb/leveldb/iterator"
- "github.com/syndtr/goleveldb/leveldb/opt"
- "github.com/syndtr/goleveldb/leveldb/util"
-)
-
-// Constants for db which can be used to customize later.
-const (
- writePauseWarningThrottler = 1 * time.Minute
-)
-
-// LDBDatabase is database based on leveldb.
-type LDBDatabase struct {
- fn string // filename for reporting
- db *leveldb.DB // LevelDB instance
-
- quitLock sync.Mutex // Mutex protecting the quit channel access
- quitChan chan chan error // Quit channel to stop the metrics collection before closing the database
-
- log log.Logger // Contextual logger tracking the database path
-}
-
-// NewLDBDatabase returns a LevelDB wrapped object.
-func NewLDBDatabase(file string, cache int, handles int) (*LDBDatabase, error) {
- logger := log.New("database", file)
-
- // Ensure we have some minimal caching and file guarantees
- if cache < 16 {
- cache = 16
- }
- if handles < 16 {
- handles = 16
- }
- logger.Info("Allocated cache and file handles", "cache", cache, "handles", handles)
-
- // Open the db and recover any potential corruptions
- db, err := leveldb.OpenFile(file, &opt.Options{
- OpenFilesCacheCapacity: handles,
- BlockCacheCapacity: cache / 2 * opt.MiB,
- WriteBuffer: cache / 4 * opt.MiB, // Two of these are used internally
- Filter: filter.NewBloomFilter(10),
- })
- if _, corrupted := err.(*errors.ErrCorrupted); corrupted {
- db, err = leveldb.RecoverFile(file, nil)
- }
- // (Re)check for errors and abort if opening of the db failed
- if err != nil {
- return nil, err
- }
- return &LDBDatabase{
- fn: file,
- db: db,
- log: logger,
- }, nil
-}
-
-// Path returns the path to the database directory.
-func (db *LDBDatabase) Path() string {
- return db.fn
-}
-
-// Put puts the given key / value to the queue
-func (db *LDBDatabase) Put(key []byte, value []byte) error {
- return db.db.Put(key, value, nil)
-}
-
-// Has is used to check if the given key is included into the database.
-func (db *LDBDatabase) Has(key []byte) (bool, error) {
- return db.db.Has(key, nil)
-}
-
-// Get returns the given key if it's present.
-func (db *LDBDatabase) Get(key []byte) ([]byte, error) {
- dat, err := db.db.Get(key, nil)
- if err != nil {
- return nil, err
- }
- return dat, nil
-}
-
-// Delete deletes the key from the queue and database
-func (db *LDBDatabase) Delete(key []byte) error {
- return db.db.Delete(key, nil)
-}
-
-// NewIterator returns the current iterator of the db.
-func (db *LDBDatabase) NewIterator() iterator.Iterator {
- return db.db.NewIterator(nil, nil)
-}
-
-// NewIteratorWithPrefix returns a iterator to iterate over subset of database content with a particular prefix.
-func (db *LDBDatabase) NewIteratorWithPrefix(prefix []byte) iterator.Iterator {
- return db.db.NewIterator(util.BytesPrefix(prefix), nil)
-}
-
-// Close closes the database.
-func (db *LDBDatabase) Close() {
- // Stop the metrics collection to avoid internal database races
- db.quitLock.Lock()
- defer db.quitLock.Unlock()
-
- if db.quitChan != nil {
- errc := make(chan error)
- db.quitChan <- errc
- if err := <-errc; err != nil {
- db.log.Error("Metrics collection failed", "err", err)
- }
- db.quitChan = nil
- }
- err := db.db.Close()
- if err == nil {
- db.log.Info("Database closed")
- } else {
- db.log.Error("Failed to close database", "err", err)
- }
-}
-
-// LDB returns the pointer to leveldb on which the LDBDatabase is built.
-func (db *LDBDatabase) LDB() *leveldb.DB {
- return db.db
-}
-
-/* TODO(minhdoan): Might add meter func from ethereum-go repo
- */
-
-// NewBatch returns Batch interface for a series of leveldb transactions.
-func (db *LDBDatabase) NewBatch() Batch {
- return &ldbBatch{db: db.db, b: new(leveldb.Batch)}
-}
-
-type ldbBatch struct {
- db *leveldb.DB
- b *leveldb.Batch
- size int
-}
-
-// Put is used to put key, value into the batch of transactions.
-func (b *ldbBatch) Put(key, value []byte) error {
- b.b.Put(key, value)
- b.size += len(value)
- return nil
-}
-
-// Delete is used to delete the item associated with the given key as a part of the batch.
-func (b *ldbBatch) Delete(key []byte) error {
- b.b.Delete(key)
- b.size++
- return nil
-}
-
-// Write writes the patch of transactions.
-func (b *ldbBatch) Write() error {
- return b.db.Write(b.b, nil)
-}
-
-// ValueSize returns the size of the patch.
-func (b *ldbBatch) ValueSize() int {
- return b.size
-}
-
-// Reset resets the batch.
-func (b *ldbBatch) Reset() {
- b.b.Reset()
- b.size = 0
-}
-
-type table struct {
- db Database
- prefix string
-}
-
-// NewTable returns a Database object that prefixes all keys with a given
-// string.
-func NewTable(db Database, prefix string) Database {
- return &table{
- db: db,
- prefix: prefix,
- }
-}
-
-func (dt *table) Put(key []byte, value []byte) error {
- return dt.db.Put(append([]byte(dt.prefix), key...), value)
-}
-
-func (dt *table) Has(key []byte) (bool, error) {
- return dt.db.Has(append([]byte(dt.prefix), key...))
-}
-
-func (dt *table) Get(key []byte) ([]byte, error) {
- return dt.db.Get(append([]byte(dt.prefix), key...))
-}
-
-func (dt *table) Delete(key []byte) error {
- return dt.db.Delete(append([]byte(dt.prefix), key...))
-}
-
-func (dt *table) Close() {
- // Do nothing; don't close the underlying DB.
-}
-
-type tableBatch struct {
- batch Batch
- prefix string
-}
-
-// NewTableBatch returns a Batch object which prefixes all keys with a given string.
-func NewTableBatch(db Database, prefix string) Batch {
- return &tableBatch{db.NewBatch(), prefix}
-}
-
-func (dt *table) NewBatch() Batch {
- return &tableBatch{dt.db.NewBatch(), dt.prefix}
-}
-
-func (tb *tableBatch) Put(key, value []byte) error {
- return tb.batch.Put(append([]byte(tb.prefix), key...), value)
-}
-
-func (tb *tableBatch) Delete(key []byte) error {
- return tb.batch.Delete(append([]byte(tb.prefix), key...))
-}
-
-func (tb *tableBatch) Write() error {
- return tb.batch.Write()
-}
-
-func (tb *tableBatch) ValueSize() int {
- return tb.batch.ValueSize()
-}
-
-func (tb *tableBatch) Reset() {
- tb.batch.Reset()
-}
diff --git a/internal/db/db_test.go b/internal/db/db_test.go
deleted file mode 100644
index 13a5bb122..000000000
--- a/internal/db/db_test.go
+++ /dev/null
@@ -1,194 +0,0 @@
-package db
-
-import (
- "bytes"
- "fmt"
- "io/ioutil"
- "os"
- "strconv"
- "sync"
- "testing"
-)
-
-func newTestLDB() (*LDBDatabase, func()) {
- dirname, err := ioutil.TempDir(os.TempDir(), "db_test_")
- if err != nil {
- panic("failed to create test file: " + err.Error())
- }
- db, err := NewLDBDatabase(dirname, 0, 0)
- if err != nil {
- panic("failed to create test database: " + err.Error())
- }
-
- return db, func() {
- db.Close()
- os.RemoveAll(dirname)
- }
-}
-
-var testValues = []string{"", "a", "1251", "\x00123\x00"}
-
-func TestLDB_PutGet(t *testing.T) {
- db, remove := newTestLDB()
- defer remove()
- testPutGet(db, t)
-}
-
-func TestMemoryDB_PutGet(t *testing.T) {
- testPutGet(NewMemDatabase(), t)
-}
-
-func testPutGet(db Database, t *testing.T) {
- t.Parallel()
-
- for _, k := range testValues {
- err := db.Put([]byte(k), nil)
- if err != nil {
- t.Fatalf("put failed: %v", err)
- }
- }
-
- for _, k := range testValues {
- data, err := db.Get([]byte(k))
- if err != nil {
- t.Fatalf("get failed: %v", err)
- }
- if len(data) != 0 {
- t.Fatalf("get returned wrong result, got %q expected nil", string(data))
- }
- }
-
- _, err := db.Get([]byte("non-exist-key"))
- if err == nil {
- t.Fatalf("expect to return a not found error")
- }
-
- for _, v := range testValues {
- err := db.Put([]byte(v), []byte(v))
- if err != nil {
- t.Fatalf("put failed: %v", err)
- }
- }
-
- for _, v := range testValues {
- data, err := db.Get([]byte(v))
- if err != nil {
- t.Fatalf("get failed: %v", err)
- }
- if !bytes.Equal(data, []byte(v)) {
- t.Fatalf("get returned wrong result, got %q expected %q", string(data), v)
- }
- }
-
- for _, v := range testValues {
- err := db.Put([]byte(v), []byte("?"))
- if err != nil {
- t.Fatalf("put override failed: %v", err)
- }
- }
-
- for _, v := range testValues {
- data, err := db.Get([]byte(v))
- if err != nil {
- t.Fatalf("get failed: %v", err)
- }
- if !bytes.Equal(data, []byte("?")) {
- t.Fatalf("get returned wrong result, got %q expected ?", string(data))
- }
- }
-
- for _, v := range testValues {
- orig, err := db.Get([]byte(v))
- if err != nil {
- t.Fatalf("get failed: %v", err)
- }
- orig[0] = byte(0xff)
- data, err := db.Get([]byte(v))
- if err != nil {
- t.Fatalf("get failed: %v", err)
- }
- if !bytes.Equal(data, []byte("?")) {
- t.Fatalf("get returned wrong result, got %q expected ?", string(data))
- }
- }
-
- for _, v := range testValues {
- err := db.Delete([]byte(v))
- if err != nil {
- t.Fatalf("delete %q failed: %v", v, err)
- }
- }
-
- for _, v := range testValues {
- _, err := db.Get([]byte(v))
- if err == nil {
- t.Fatalf("got deleted value %q", v)
- }
- }
-}
-
-func TestLDB_ParallelPutGet(t *testing.T) {
- db, remove := newTestLDB()
- defer remove()
- testParallelPutGet(db, t)
-}
-
-func TestMemoryDB_ParallelPutGet(t *testing.T) {
- testParallelPutGet(NewMemDatabase(), t)
-}
-
-func testParallelPutGet(db Database, t *testing.T) {
- const n = 8
- var pending sync.WaitGroup
-
- pending.Add(n)
- for i := 0; i < n; i++ {
- go func(key string) {
- defer pending.Done()
- err := db.Put([]byte(key), []byte("v"+key))
- if err != nil {
- panic("put failed: " + err.Error())
- }
- }(strconv.Itoa(i))
- }
- pending.Wait()
-
- pending.Add(n)
- for i := 0; i < n; i++ {
- go func(key string) {
- defer pending.Done()
- data, err := db.Get([]byte(key))
- if err != nil {
- panic("get failed: " + err.Error())
- }
- if !bytes.Equal(data, []byte("v"+key)) {
- panic(fmt.Sprintf("get failed, got %q expected %q", []byte(data), []byte("v"+key)))
- }
- }(strconv.Itoa(i))
- }
- pending.Wait()
-
- pending.Add(n)
- for i := 0; i < n; i++ {
- go func(key string) {
- defer pending.Done()
- err := db.Delete([]byte(key))
- if err != nil {
- panic("delete failed: " + err.Error())
- }
- }(strconv.Itoa(i))
- }
- pending.Wait()
-
- pending.Add(n)
- for i := 0; i < n; i++ {
- go func(key string) {
- defer pending.Done()
- _, err := db.Get([]byte(key))
- if err == nil {
- panic("get succeeded")
- }
- }(strconv.Itoa(i))
- }
- pending.Wait()
-}
diff --git a/internal/db/interface.go b/internal/db/interface.go
deleted file mode 100644
index f3d8cb830..000000000
--- a/internal/db/interface.go
+++ /dev/null
@@ -1,36 +0,0 @@
-package db
-
-// IdealBatchSize is the max size of batch transactions.
-// The value was determined empirically.
-const IdealBatchSize = 100 * 1024
-
-// Putter wraps the database write operation supported by both batches and regular databases.
-type Putter interface {
- Put(key []byte, value []byte) error
-}
-
-// Deleter wraps the database delete operation supported by both batches and regular databases.
-type Deleter interface {
- Delete(key []byte) error
-}
-
-// Database wraps all database operations. All methods are safe for concurrent use.
-type Database interface {
- Putter
- Deleter
- Get(key []byte) ([]byte, error)
- Has(key []byte) (bool, error)
- Close()
- NewBatch() Batch
-}
-
-// Batch is a write-only database that commits changes to its host database
-// when Write is called. Batch cannot be used concurrently.
-type Batch interface {
- Putter
- Deleter
- ValueSize() int // amount of data in the batch
- Write() error
- // Reset resets the batch for reuse
- Reset()
-}
diff --git a/internal/db/memory_db.go b/internal/db/memory_db.go
deleted file mode 100644
index f81e0e797..000000000
--- a/internal/db/memory_db.go
+++ /dev/null
@@ -1,135 +0,0 @@
-package db
-
-import (
- "errors"
- "sync"
-
- "github.com/harmony-one/harmony/internal/utils"
-)
-
-// MemDatabase is the test memory database. It won't be used for any production.
-type MemDatabase struct {
- db map[string][]byte
- lock sync.RWMutex
-}
-
-// NewMemDatabase returns a pointer of the new creation of MemDatabase.
-func NewMemDatabase() *MemDatabase {
- return &MemDatabase{
- db: make(map[string][]byte),
- }
-}
-
-// NewMemDatabaseWithCap returns a pointer of the new creation of MemDatabase with the given size.
-func NewMemDatabaseWithCap(size int) *MemDatabase {
- return &MemDatabase{
- db: make(map[string][]byte, size),
- }
-}
-
-// Put puts (key, value) item into MemDatabase.
-func (db *MemDatabase) Put(key []byte, value []byte) error {
- db.lock.Lock()
- defer db.lock.Unlock()
-
- db.db[string(key)] = utils.CopyBytes(value)
- return nil
-}
-
-// Has checks if the key is included into MemDatabase.
-func (db *MemDatabase) Has(key []byte) (bool, error) {
- db.lock.RLock()
- defer db.lock.RUnlock()
-
- _, ok := db.db[string(key)]
- return ok, nil
-}
-
-// Get gets value of the given key.
-func (db *MemDatabase) Get(key []byte) ([]byte, error) {
- db.lock.RLock()
- defer db.lock.RUnlock()
-
- if entry, ok := db.db[string(key)]; ok {
- return utils.CopyBytes(entry), nil
- }
- return nil, errors.New("not found")
-}
-
-// Keys returns all keys of the given MemDatabase.
-func (db *MemDatabase) Keys() [][]byte {
- db.lock.RLock()
- defer db.lock.RUnlock()
-
- keys := [][]byte{}
- for key := range db.db {
- keys = append(keys, []byte(key))
- }
- return keys
-}
-
-// Delete deletes the given key.
-func (db *MemDatabase) Delete(key []byte) error {
- db.lock.Lock()
- defer db.lock.Unlock()
-
- delete(db.db, string(key))
- return nil
-}
-
-// Close closes the given db.
-func (db *MemDatabase) Close() {}
-
-// NewBatch returns a batch of MemDatabase transactions.
-func (db *MemDatabase) NewBatch() Batch {
- return &memBatch{db: db}
-}
-
-// Len returns the length of the given db.
-func (db *MemDatabase) Len() int { return len(db.db) }
-
-type kv struct {
- k, v []byte
- del bool
-}
-
-type memBatch struct {
- db *MemDatabase
- writes []kv
- size int
-}
-
-func (b *memBatch) Put(key, value []byte) error {
- b.writes = append(b.writes, kv{utils.CopyBytes(key), utils.CopyBytes(value), false})
- b.size += len(value)
- return nil
-}
-
-func (b *memBatch) Delete(key []byte) error {
- b.writes = append(b.writes, kv{utils.CopyBytes(key), nil, true})
- b.size++
- return nil
-}
-
-func (b *memBatch) Write() error {
- b.db.lock.Lock()
- defer b.db.lock.Unlock()
-
- for _, kv := range b.writes {
- if kv.del {
- delete(b.db.db, string(kv.k))
- continue
- }
- b.db.db[string(kv.k)] = kv.v
- }
- return nil
-}
-
-func (b *memBatch) ValueSize() int {
- return b.size
-}
-
-func (b *memBatch) Reset() {
- b.writes = b.writes[:0]
- b.size = 0
-}
diff --git a/internal/trie/database.go b/internal/trie/database.go
deleted file mode 100644
index 499282421..000000000
--- a/internal/trie/database.go
+++ /dev/null
@@ -1,782 +0,0 @@
-// Copyright 2018 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 trie
-
-import (
- "fmt"
- "io"
- "sync"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/metrics"
- "github.com/ethereum/go-ethereum/rlp"
- hdb "github.com/harmony-one/harmony/internal/db"
-)
-
-var (
- memcacheFlushTimeTimer = metrics.NewRegisteredResettingTimer("trie/memcache/flush/time", nil)
- memcacheFlushNodesMeter = metrics.NewRegisteredMeter("trie/memcache/flush/nodes", nil)
- memcacheFlushSizeMeter = metrics.NewRegisteredMeter("trie/memcache/flush/size", nil)
-
- memcacheGCTimeTimer = metrics.NewRegisteredResettingTimer("trie/memcache/gc/time", nil)
- memcacheGCNodesMeter = metrics.NewRegisteredMeter("trie/memcache/gc/nodes", nil)
- memcacheGCSizeMeter = metrics.NewRegisteredMeter("trie/memcache/gc/size", nil)
-
- memcacheCommitTimeTimer = metrics.NewRegisteredResettingTimer("trie/memcache/commit/time", nil)
- memcacheCommitNodesMeter = metrics.NewRegisteredMeter("trie/memcache/commit/nodes", nil)
- memcacheCommitSizeMeter = metrics.NewRegisteredMeter("trie/memcache/commit/size", nil)
-)
-
-// secureKeyPrefix is the database key prefix used to store trie node preimages.
-var secureKeyPrefix = []byte("secure-key-")
-
-// secureKeyLength is the length of the above prefix + 32byte hash.
-const secureKeyLength = 11 + 32
-
-// DatabaseReader wraps the Get and Has method of a backing store for the trie.
-type DatabaseReader interface {
- // Get retrieves the value associated with key from the database.
- Get(key []byte) (value []byte, err error)
-
- // Has retrieves whether a key is present in the database.
- Has(key []byte) (bool, error)
-}
-
-// Database is an intermediate write layer between the trie data structures and
-// the disk database. The aim is to accumulate trie writes in-memory and only
-// periodically flush a couple tries to disk, garbage collecting the remainder.
-type Database struct {
- diskdb hdb.Database // Persistent storage for matured trie nodes
-
- nodes map[common.Hash]*cachedNode // Data and references relationships of a node
- oldest common.Hash // Oldest tracked node, flush-list head
- newest common.Hash // Newest tracked node, flush-list tail
-
- preimages map[common.Hash][]byte // Preimages of nodes from the secure trie
- seckeybuf [secureKeyLength]byte // Ephemeral buffer for calculating preimage keys
-
- gctime time.Duration // Time spent on garbage collection since last commit
- gcnodes uint64 // Nodes garbage collected since last commit
- gcsize common.StorageSize // Data storage garbage collected since last commit
-
- flushtime time.Duration // Time spent on data flushing since last commit
- flushnodes uint64 // Nodes flushed since last commit
- flushsize common.StorageSize // Data storage flushed since last commit
-
- nodesSize common.StorageSize // Storage size of the nodes cache (exc. flushlist)
- preimagesSize common.StorageSize // Storage size of the preimages cache
-
- lock sync.RWMutex
-}
-
-// rawNode is a simple binary blob used to differentiate between collapsed trie
-// nodes and already encoded RLP binary blobs (while at the same time store them
-// in the same cache fields).
-type rawNode []byte
-
-func (n rawNode) canUnload(uint16, uint16) bool { panic("this should never end up in a live trie") }
-func (n rawNode) cache() (hashNode, bool) { panic("this should never end up in a live trie") }
-func (n rawNode) fstring(ind string) string { panic("this should never end up in a live trie") }
-
-// rawFullNode represents only the useful data content of a full node, with the
-// caches and flags stripped out to minimize its data storage. This type honors
-// the same RLP encoding as the original parent.
-type rawFullNode [17]node
-
-func (n rawFullNode) canUnload(uint16, uint16) bool { panic("this should never end up in a live trie") }
-func (n rawFullNode) cache() (hashNode, bool) { panic("this should never end up in a live trie") }
-func (n rawFullNode) fstring(ind string) string { panic("this should never end up in a live trie") }
-
-func (n rawFullNode) EncodeRLP(w io.Writer) error {
- var nodes [17]node
-
- for i, child := range n {
- if child != nil {
- nodes[i] = child
- } else {
- nodes[i] = nilValueNode
- }
- }
- return rlp.Encode(w, nodes)
-}
-
-// rawShortNode represents only the useful data content of a short node, with the
-// caches and flags stripped out to minimize its data storage. This type honors
-// the same RLP encoding as the original parent.
-type rawShortNode struct {
- Key []byte
- Val node
-}
-
-func (n rawShortNode) canUnload(uint16, uint16) bool { panic("this should never end up in a live trie") }
-func (n rawShortNode) cache() (hashNode, bool) { panic("this should never end up in a live trie") }
-func (n rawShortNode) fstring(ind string) string { panic("this should never end up in a live trie") }
-
-// cachedNode is all the information we know about a single cached node in the
-// memory database write layer.
-type cachedNode struct {
- node node // Cached collapsed trie node, or raw rlp data
- size uint16 // Byte size of the useful cached data
-
- parents uint16 // Number of live nodes referencing this one
- children map[common.Hash]uint16 // External children referenced by this node
-
- flushPrev common.Hash // Previous node in the flush-list
- flushNext common.Hash // Next node in the flush-list
-}
-
-// rlp returns the raw rlp encoded blob of the cached node, either directly from
-// the cache, or by regenerating it from the collapsed node.
-func (n *cachedNode) rlp() []byte {
- if node, ok := n.node.(rawNode); ok {
- return node
- }
- blob, err := rlp.EncodeToBytes(n.node)
- if err != nil {
- panic(err)
- }
- return blob
-}
-
-// obj returns the decoded and expanded trie node, either directly from the cache,
-// or by regenerating it from the rlp encoded blob.
-func (n *cachedNode) obj(hash common.Hash, cachegen uint16) node {
- if node, ok := n.node.(rawNode); ok {
- return mustDecodeNode(hash[:], node, cachegen)
- }
- return expandNode(hash[:], n.node, cachegen)
-}
-
-// childs returns all the tracked children of this node, both the implicit ones
-// from inside the node as well as the explicit ones from outside the node.
-func (n *cachedNode) childs() []common.Hash {
- children := make([]common.Hash, 0, 16)
- for child := range n.children {
- children = append(children, child)
- }
- if _, ok := n.node.(rawNode); !ok {
- gatherChildren(n.node, &children)
- }
- return children
-}
-
-// gatherChildren traverses the node hierarchy of a collapsed storage node and
-// retrieves all the hashnode children.
-func gatherChildren(n node, children *[]common.Hash) {
- switch n := n.(type) {
- case *rawShortNode:
- gatherChildren(n.Val, children)
-
- case rawFullNode:
- for i := 0; i < 16; i++ {
- gatherChildren(n[i], children)
- }
- case hashNode:
- *children = append(*children, common.BytesToHash(n))
-
- case valueNode, nil:
-
- default:
- panic(fmt.Sprintf("unknown node type: %T", n))
- }
-}
-
-// simplifyNode traverses the hierarchy of an expanded memory node and discards
-// all the internal caches, returning a node that only contains the raw data.
-func simplifyNode(n node) node {
- switch n := n.(type) {
- case *shortNode:
- // Short nodes discard the flags and cascade
- return &rawShortNode{Key: n.Key, Val: simplifyNode(n.Val)}
-
- case *fullNode:
- // Full nodes discard the flags and cascade
- node := rawFullNode(n.Children)
- for i := 0; i < len(node); i++ {
- if node[i] != nil {
- node[i] = simplifyNode(node[i])
- }
- }
- return node
-
- case valueNode, hashNode, rawNode:
- return n
-
- default:
- panic(fmt.Sprintf("unknown node type: %T", n))
- }
-}
-
-// expandNode traverses the node hierarchy of a collapsed storage node and converts
-// all fields and keys into expanded memory form.
-func expandNode(hash hashNode, n node, cachegen uint16) node {
- switch n := n.(type) {
- case *rawShortNode:
- // Short nodes need key and child expansion
- return &shortNode{
- Key: compactToHex(n.Key),
- Val: expandNode(nil, n.Val, cachegen),
- flags: nodeFlag{
- hash: hash,
- gen: cachegen,
- },
- }
-
- case rawFullNode:
- // Full nodes need child expansion
- node := &fullNode{
- flags: nodeFlag{
- hash: hash,
- gen: cachegen,
- },
- }
- for i := 0; i < len(node.Children); i++ {
- if n[i] != nil {
- node.Children[i] = expandNode(nil, n[i], cachegen)
- }
- }
- return node
-
- case valueNode, hashNode:
- return n
-
- default:
- panic(fmt.Sprintf("unknown node type: %T", n))
- }
-}
-
-// NewDatabase creates a new trie database to store ephemeral trie content before
-// its written out to disk or garbage collected.
-func NewDatabase(diskdb hdb.Database) *Database {
- return &Database{
- diskdb: diskdb,
- nodes: map[common.Hash]*cachedNode{{}: {}},
- preimages: make(map[common.Hash][]byte),
- }
-}
-
-// DiskDB retrieves the persistent storage backing the trie database.
-func (db *Database) DiskDB() DatabaseReader {
- return db.diskdb
-}
-
-// InsertBlob writes a new reference tracked blob to the memory database if it's
-// yet unknown. This method should only be used for non-trie nodes that require
-// reference counting, since trie nodes are garbage collected directly through
-// their embedded children.
-func (db *Database) InsertBlob(hash common.Hash, blob []byte) {
- db.lock.Lock()
- defer db.lock.Unlock()
-
- db.insert(hash, blob, rawNode(blob))
-}
-
-// insert inserts a collapsed trie node into the memory database. This method is
-// a more generic version of InsertBlob, supporting both raw blob insertions as
-// well ex trie node insertions. The blob must always be specified to allow proper
-// size tracking.
-func (db *Database) insert(hash common.Hash, blob []byte, node node) {
- // If the node's already cached, skip
- if _, ok := db.nodes[hash]; ok {
- return
- }
- // Create the cached entry for this node
- entry := &cachedNode{
- node: simplifyNode(node),
- size: uint16(len(blob)),
- flushPrev: db.newest,
- }
- for _, child := range entry.childs() {
- if c := db.nodes[child]; c != nil {
- c.parents++
- }
- }
- db.nodes[hash] = entry
-
- // Update the flush-list endpoints
- if db.oldest == (common.Hash{}) {
- db.oldest, db.newest = hash, hash
- } else {
- db.nodes[db.newest].flushNext, db.newest = hash, hash
- }
- db.nodesSize += common.StorageSize(common.HashLength + entry.size)
-}
-
-// insertPreimage writes a new trie node pre-image to the memory database if it's
-// yet unknown. The method will make a copy of the slice.
-//
-// Note, this method assumes that the database's lock is held!
-func (db *Database) insertPreimage(hash common.Hash, preimage []byte) {
- if _, ok := db.preimages[hash]; ok {
- return
- }
- db.preimages[hash] = common.CopyBytes(preimage)
- db.preimagesSize += common.StorageSize(common.HashLength + len(preimage))
-}
-
-// node retrieves a cached trie node from memory, or returns nil if none can be
-// found in the memory cache.
-func (db *Database) node(hash common.Hash, cachegen uint16) node {
- // Retrieve the node from cache if available
- db.lock.RLock()
- node := db.nodes[hash]
- db.lock.RUnlock()
-
- if node != nil {
- return node.obj(hash, cachegen)
- }
- // Content unavailable in memory, attempt to retrieve from disk
- enc, err := db.diskdb.Get(hash[:])
- if err != nil || enc == nil {
- return nil
- }
- return mustDecodeNode(hash[:], enc, cachegen)
-}
-
-// Node retrieves an encoded cached trie node from memory. If it cannot be found
-// cached, the method queries the persistent database for the content.
-func (db *Database) Node(hash common.Hash) ([]byte, error) {
- // Retrieve the node from cache if available
- db.lock.RLock()
- node := db.nodes[hash]
- db.lock.RUnlock()
-
- if node != nil {
- return node.rlp(), nil
- }
- // Content unavailable in memory, attempt to retrieve from disk
- return db.diskdb.Get(hash[:])
-}
-
-// preimage retrieves a cached trie node pre-image from memory. If it cannot be
-// found cached, the method queries the persistent database for the content.
-func (db *Database) preimage(hash common.Hash) ([]byte, error) {
- // Retrieve the node from cache if available
- db.lock.RLock()
- preimage := db.preimages[hash]
- db.lock.RUnlock()
-
- if preimage != nil {
- return preimage, nil
- }
- // Content unavailable in memory, attempt to retrieve from disk
- return db.diskdb.Get(db.secureKey(hash[:]))
-}
-
-// secureKey returns the database key for the preimage of key, as an ephemeral
-// buffer. The caller must not hold onto the return value because it will become
-// invalid on the next call.
-func (db *Database) secureKey(key []byte) []byte {
- buf := append(db.seckeybuf[:0], secureKeyPrefix...)
- buf = append(buf, key...)
- return buf
-}
-
-// Nodes retrieves the hashes of all the nodes cached within the memory database.
-// This method is extremely expensive and should only be used to validate internal
-// states in test code.
-func (db *Database) Nodes() []common.Hash {
- db.lock.RLock()
- defer db.lock.RUnlock()
-
- var hashes = make([]common.Hash, 0, len(db.nodes))
- for hash := range db.nodes {
- if hash != (common.Hash{}) { // Special case for "root" references/nodes
- hashes = append(hashes, hash)
- }
- }
- return hashes
-}
-
-// Reference adds a new reference from a parent node to a child node.
-func (db *Database) Reference(child common.Hash, parent common.Hash) {
- db.lock.RLock()
- defer db.lock.RUnlock()
-
- db.reference(child, parent)
-}
-
-// reference is the private locked version of Reference.
-func (db *Database) reference(child common.Hash, parent common.Hash) {
- // If the node does not exist, it's a node pulled from disk, skip
- node, ok := db.nodes[child]
- if !ok {
- return
- }
- // If the reference already exists, only duplicate for roots
- if db.nodes[parent].children == nil {
- db.nodes[parent].children = make(map[common.Hash]uint16)
- } else if _, ok = db.nodes[parent].children[child]; ok && parent != (common.Hash{}) {
- return
- }
- node.parents++
- db.nodes[parent].children[child]++
-}
-
-// Dereference removes an existing reference from a root node.
-func (db *Database) Dereference(root common.Hash) {
- // Sanity check to ensure that the meta-root is not removed
- if root == (common.Hash{}) {
- log.Error("Attempted to dereference the trie cache meta root")
- return
- }
- db.lock.Lock()
- defer db.lock.Unlock()
-
- nodes, storage, start := len(db.nodes), db.nodesSize, time.Now()
- db.dereference(root, common.Hash{})
-
- db.gcnodes += uint64(nodes - len(db.nodes))
- db.gcsize += storage - db.nodesSize
- db.gctime += time.Since(start)
-
- memcacheGCTimeTimer.Update(time.Since(start))
- memcacheGCSizeMeter.Mark(int64(storage - db.nodesSize))
- memcacheGCNodesMeter.Mark(int64(nodes - len(db.nodes)))
-
- log.Debug("Dereferenced trie from memory database", "nodes", nodes-len(db.nodes), "size", storage-db.nodesSize, "time", time.Since(start),
- "gcnodes", db.gcnodes, "gcsize", db.gcsize, "gctime", db.gctime, "livenodes", len(db.nodes), "livesize", db.nodesSize)
-}
-
-// dereference is the private locked version of Dereference.
-func (db *Database) dereference(child common.Hash, parent common.Hash) {
- // Dereference the parent-child
- node := db.nodes[parent]
-
- if node.children != nil && node.children[child] > 0 {
- node.children[child]--
- if node.children[child] == 0 {
- delete(node.children, child)
- }
- }
- // If the child does not exist, it's a previously committed node.
- node, ok := db.nodes[child]
- if !ok {
- return
- }
- // If there are no more references to the child, delete it and cascade
- if node.parents > 0 {
- // This is a special cornercase where a node loaded from disk (i.e. not in the
- // memcache any more) gets reinjected as a new node (short node split into full,
- // then reverted into short), causing a cached node to have no parents. That is
- // no problem in itself, but don't make maxint parents out of it.
- node.parents--
- }
- if node.parents == 0 {
- // Remove the node from the flush-list
- switch child {
- case db.oldest:
- db.oldest = node.flushNext
- db.nodes[node.flushNext].flushPrev = common.Hash{}
- case db.newest:
- db.newest = node.flushPrev
- db.nodes[node.flushPrev].flushNext = common.Hash{}
- default:
- db.nodes[node.flushPrev].flushNext = node.flushNext
- db.nodes[node.flushNext].flushPrev = node.flushPrev
- }
- // Dereference all children and delete the node
- for _, hash := range node.childs() {
- db.dereference(hash, child)
- }
- delete(db.nodes, child)
- db.nodesSize -= common.StorageSize(common.HashLength + int(node.size))
- }
-}
-
-// Cap iteratively flushes old but still referenced trie nodes until the total
-// memory usage goes below the given threshold.
-func (db *Database) Cap(limit common.StorageSize) error {
- // Create a database batch to flush persistent data out. It is important that
- // outside code doesn't see an inconsistent state (referenced data removed from
- // memory cache during commit but not yet in persistent storage). This is ensured
- // by only uncaching existing data when the database write finalizes.
- db.lock.RLock()
-
- nodes, storage, start := len(db.nodes), db.nodesSize, time.Now()
- batch := db.diskdb.NewBatch()
-
- // db.nodesSize only contains the useful data in the cache, but when reporting
- // the total memory consumption, the maintenance metadata is also needed to be
- // counted. For every useful node, we track 2 extra hashes as the flushlist.
- size := db.nodesSize + common.StorageSize((len(db.nodes)-1)*2*common.HashLength)
-
- // If the preimage cache got large enough, push to disk. If it's still small
- // leave for later to deduplicate writes.
- flushPreimages := db.preimagesSize > 4*1024*1024
- if flushPreimages {
- for hash, preimage := range db.preimages {
- if err := batch.Put(db.secureKey(hash[:]), preimage); err != nil {
- log.Error("Failed to commit preimage from trie database", "err", err)
- db.lock.RUnlock()
- return err
- }
- if batch.ValueSize() > hdb.IdealBatchSize {
- if err := batch.Write(); err != nil {
- db.lock.RUnlock()
- return err
- }
- batch.Reset()
- }
- }
- }
- // Keep committing nodes from the flush-list until we're below allowance
- oldest := db.oldest
- for size > limit && oldest != (common.Hash{}) {
- // Fetch the oldest referenced node and push into the batch
- node := db.nodes[oldest]
- if err := batch.Put(oldest[:], node.rlp()); err != nil {
- db.lock.RUnlock()
- return err
- }
- // If we exceeded the ideal batch size, commit and reset
- if batch.ValueSize() >= hdb.IdealBatchSize {
- if err := batch.Write(); err != nil {
- log.Error("Failed to write flush list to disk", "err", err)
- db.lock.RUnlock()
- return err
- }
- batch.Reset()
- }
- // Iterate to the next flush item, or abort if the size cap was achieved. Size
- // is the total size, including both the useful cached data (hash -> blob), as
- // well as the flushlist metadata (2*hash). When flushing items from the cache,
- // we need to reduce both.
- size -= common.StorageSize(3*common.HashLength + int(node.size))
- oldest = node.flushNext
- }
- // Flush out any remainder data from the last batch
- if err := batch.Write(); err != nil {
- log.Error("Failed to write flush list to disk", "err", err)
- db.lock.RUnlock()
- return err
- }
- db.lock.RUnlock()
-
- // Write successful, clear out the flushed data
- db.lock.Lock()
- defer db.lock.Unlock()
-
- if flushPreimages {
- db.preimages = make(map[common.Hash][]byte)
- db.preimagesSize = 0
- }
- for db.oldest != oldest {
- node := db.nodes[db.oldest]
- delete(db.nodes, db.oldest)
- db.oldest = node.flushNext
-
- db.nodesSize -= common.StorageSize(common.HashLength + int(node.size))
- }
- if db.oldest != (common.Hash{}) {
- db.nodes[db.oldest].flushPrev = common.Hash{}
- }
- db.flushnodes += uint64(nodes - len(db.nodes))
- db.flushsize += storage - db.nodesSize
- db.flushtime += time.Since(start)
-
- memcacheFlushTimeTimer.Update(time.Since(start))
- memcacheFlushSizeMeter.Mark(int64(storage - db.nodesSize))
- memcacheFlushNodesMeter.Mark(int64(nodes - len(db.nodes)))
-
- log.Debug("Persisted nodes from memory database", "nodes", nodes-len(db.nodes), "size", storage-db.nodesSize, "time", time.Since(start),
- "flushnodes", db.flushnodes, "flushsize", db.flushsize, "flushtime", db.flushtime, "livenodes", len(db.nodes), "livesize", db.nodesSize)
-
- return nil
-}
-
-// Commit iterates over all the children of a particular node, writes them out
-// to disk, forcefully tearing down all references in both directions.
-//
-// As a side effect, all pre-images accumulated up to this point are also written.
-func (db *Database) Commit(node common.Hash, report bool) error {
- // Create a database batch to flush persistent data out. It is important that
- // outside code doesn't see an inconsistent state (referenced data removed from
- // memory cache during commit but not yet in persistent storage). This is ensured
- // by only uncaching existing data when the database write finalizes.
- db.lock.RLock()
-
- start := time.Now()
- batch := db.diskdb.NewBatch()
-
- // Move all of the accumulated preimages into a write batch
- for hash, preimage := range db.preimages {
- if err := batch.Put(db.secureKey(hash[:]), preimage); err != nil {
- log.Error("Failed to commit preimage from trie database", "err", err)
- db.lock.RUnlock()
- return err
- }
- if batch.ValueSize() > hdb.IdealBatchSize {
- if err := batch.Write(); err != nil {
- return err
- }
- batch.Reset()
- }
- }
- // Move the trie itself into the batch, flushing if enough data is accumulated
- nodes, storage := len(db.nodes), db.nodesSize
- if err := db.commit(node, batch); err != nil {
- log.Error("Failed to commit trie from trie database", "err", err)
- db.lock.RUnlock()
- return err
- }
- // Write batch ready, unlock for readers during persistence
- if err := batch.Write(); err != nil {
- log.Error("Failed to write trie to disk", "err", err)
- db.lock.RUnlock()
- return err
- }
- db.lock.RUnlock()
-
- // Write successful, clear out the flushed data
- db.lock.Lock()
- defer db.lock.Unlock()
-
- db.preimages = make(map[common.Hash][]byte)
- db.preimagesSize = 0
-
- db.uncache(node)
-
- memcacheCommitTimeTimer.Update(time.Since(start))
- memcacheCommitSizeMeter.Mark(int64(storage - db.nodesSize))
- memcacheCommitNodesMeter.Mark(int64(nodes - len(db.nodes)))
-
- logger := log.Info
- if !report {
- logger = log.Debug
- }
- logger("Persisted trie from memory database", "nodes", nodes-len(db.nodes)+int(db.flushnodes), "size", storage-db.nodesSize+db.flushsize, "time", time.Since(start)+db.flushtime,
- "gcnodes", db.gcnodes, "gcsize", db.gcsize, "gctime", db.gctime, "livenodes", len(db.nodes), "livesize", db.nodesSize)
-
- // Reset the garbage collection statistics
- db.gcnodes, db.gcsize, db.gctime = 0, 0, 0
- db.flushnodes, db.flushsize, db.flushtime = 0, 0, 0
-
- return nil
-}
-
-// commit is the private locked version of Commit.
-func (db *Database) commit(hash common.Hash, batch hdb.Batch) error {
- // If the node does not exist, it's a previously committed node
- node, ok := db.nodes[hash]
- if !ok {
- return nil
- }
- for _, child := range node.childs() {
- if err := db.commit(child, batch); err != nil {
- return err
- }
- }
- if err := batch.Put(hash[:], node.rlp()); err != nil {
- return err
- }
- // If we've reached an optimal batch size, commit and start over
- if batch.ValueSize() >= hdb.IdealBatchSize {
- if err := batch.Write(); err != nil {
- return err
- }
- batch.Reset()
- }
- return nil
-}
-
-// uncache is the post-processing step of a commit operation where the already
-// persisted trie is removed from the cache. The reason behind the two-phase
-// commit is to ensure consistent data availability while moving from memory
-// to disk.
-func (db *Database) uncache(hash common.Hash) {
- // If the node does not exist, we're done on this path
- node, ok := db.nodes[hash]
- if !ok {
- return
- }
- // Node still exists, remove it from the flush-list
- switch hash {
- case db.oldest:
- db.oldest = node.flushNext
- db.nodes[node.flushNext].flushPrev = common.Hash{}
- case db.newest:
- db.newest = node.flushPrev
- db.nodes[node.flushPrev].flushNext = common.Hash{}
- default:
- db.nodes[node.flushPrev].flushNext = node.flushNext
- db.nodes[node.flushNext].flushPrev = node.flushPrev
- }
- // Uncache the node's subtries and remove the node itself too
- for _, child := range node.childs() {
- db.uncache(child)
- }
- delete(db.nodes, hash)
- db.nodesSize -= common.StorageSize(common.HashLength + int(node.size))
-}
-
-// Size returns the current storage size of the memory cache in front of the
-// persistent database layer.
-func (db *Database) Size() (common.StorageSize, common.StorageSize) {
- db.lock.RLock()
- defer db.lock.RUnlock()
-
- // db.nodesSize only contains the useful data in the cache, but when reporting
- // the total memory consumption, the maintenance metadata is also needed to be
- // counted. For every useful node, we track 2 extra hashes as the flushlist.
- var flushlistSize = common.StorageSize((len(db.nodes) - 1) * 2 * common.HashLength)
- return db.nodesSize + flushlistSize, db.preimagesSize
-}
-
-// verifyIntegrity is a debug method to iterate over the entire trie stored in
-// memory and check whether every node is reachable from the meta root. The goal
-// is to find any errors that might cause memory leaks and or trie nodes to go
-// missing.
-//
-// This method is extremely CPU and memory intensive, only use when must.
-func (db *Database) verifyIntegrity() {
- // Iterate over all the cached nodes and accumulate them into a set
- reachable := map[common.Hash]struct{}{{}: {}}
-
- for child := range db.nodes[common.Hash{}].children {
- db.accumulate(child, reachable)
- }
- // Find any unreachable but cached nodes
- unreachable := []string{}
- for hash, node := range db.nodes {
- if _, ok := reachable[hash]; !ok {
- unreachable = append(unreachable, fmt.Sprintf("%x: {Node: %v, Parents: %d, Prev: %x, Next: %x}",
- hash, node.node, node.parents, node.flushPrev, node.flushNext))
- }
- }
- if len(unreachable) != 0 {
- panic(fmt.Sprintf("trie cache memory leak: %v", unreachable))
- }
-}
-
-// accumulate iterates over the trie defined by hash and accumulates all the
-// cached children found in memory.
-func (db *Database) accumulate(hash common.Hash, reachable map[common.Hash]struct{}) {
- // Mark the node reachable if present in the memory cache
- node, ok := db.nodes[hash]
- if !ok {
- return
- }
- reachable[hash] = struct{}{}
-
- // Iterate over all the children and accumulate them too
- for _, child := range node.childs() {
- db.accumulate(child, reachable)
- }
-}
diff --git a/internal/trie/encoding.go b/internal/trie/encoding.go
deleted file mode 100644
index 5f120de63..000000000
--- a/internal/trie/encoding.go
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2014 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package trie
-
-// Trie keys are dealt with in three distinct encodings:
-//
-// KEYBYTES encoding contains the actual key and nothing else. This encoding is the
-// input to most API functions.
-//
-// HEX encoding contains one byte for each nibble of the key and an optional trailing
-// 'terminator' byte of value 0x10 which indicates whether or not the node at the key
-// contains a value. Hex key encoding is used for nodes loaded in memory because it's
-// convenient to access.
-//
-// COMPACT encoding is defined by the Ethereum Yellow Paper (it's called "hex prefix
-// encoding" there) and contains the bytes of the key and a flag. The high nibble of the
-// first byte contains the flag; the lowest bit encoding the oddness of the length and
-// the second-lowest encoding whether the node at the key is a value node. The low nibble
-// of the first byte is zero in the case of an even number of nibbles and the first nibble
-// in the case of an odd number. All remaining nibbles (now an even number) fit properly
-// into the remaining bytes. Compact encoding is used for nodes stored on disk.
-
-func hexToCompact(hex []byte) []byte {
- terminator := byte(0)
- if hasTerm(hex) {
- terminator = 1
- hex = hex[:len(hex)-1]
- }
- buf := make([]byte, len(hex)/2+1)
- buf[0] = terminator << 5 // the flag byte
- if len(hex)&1 == 1 {
- buf[0] |= 1 << 4 // odd flag
- buf[0] |= hex[0] // first nibble is contained in the first byte
- hex = hex[1:]
- }
- decodeNibbles(hex, buf[1:])
- return buf
-}
-
-func compactToHex(compact []byte) []byte {
- base := keybytesToHex(compact)
- // delete terminator flag
- if base[0] < 2 {
- base = base[:len(base)-1]
- }
- // apply odd flag
- chop := 2 - base[0]&1
- return base[chop:]
-}
-
-func keybytesToHex(str []byte) []byte {
- l := len(str)*2 + 1
- var nibbles = make([]byte, l)
- for i, b := range str {
- nibbles[i*2] = b / 16
- nibbles[i*2+1] = b % 16
- }
- nibbles[l-1] = 16
- return nibbles
-}
-
-// hexToKeybytes turns hex nibbles into key bytes.
-// This can only be used for keys of even length.
-func hexToKeybytes(hex []byte) []byte {
- if hasTerm(hex) {
- hex = hex[:len(hex)-1]
- }
- if len(hex)&1 != 0 {
- panic("can't convert hex key of odd length")
- }
- key := make([]byte, len(hex)/2)
- decodeNibbles(hex, key)
- return key
-}
-
-func decodeNibbles(nibbles []byte, bytes []byte) {
- for bi, ni := 0, 0; ni < len(nibbles); bi, ni = bi+1, ni+2 {
- bytes[bi] = nibbles[ni]<<4 | nibbles[ni+1]
- }
-}
-
-// prefixLen returns the length of the common prefix of a and b.
-func prefixLen(a, b []byte) int {
- var i, length = 0, len(a)
- if len(b) < length {
- length = len(b)
- }
- for ; i < length; i++ {
- if a[i] != b[i] {
- break
- }
- }
- return i
-}
-
-// hasTerm returns whether a hex key has the terminator flag.
-func hasTerm(s []byte) bool {
- return len(s) > 0 && s[len(s)-1] == 16
-}
diff --git a/internal/trie/encoding_test.go b/internal/trie/encoding_test.go
deleted file mode 100644
index 97d8da136..000000000
--- a/internal/trie/encoding_test.go
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2014 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package trie
-
-import (
- "bytes"
- "testing"
-)
-
-func TestHexCompact(t *testing.T) {
- tests := []struct{ hex, compact []byte }{
- // empty keys, with and without terminator.
- {hex: []byte{}, compact: []byte{0x00}},
- {hex: []byte{16}, compact: []byte{0x20}},
- // odd length, no terminator
- {hex: []byte{1, 2, 3, 4, 5}, compact: []byte{0x11, 0x23, 0x45}},
- // even length, no terminator
- {hex: []byte{0, 1, 2, 3, 4, 5}, compact: []byte{0x00, 0x01, 0x23, 0x45}},
- // odd length, terminator
- {hex: []byte{15, 1, 12, 11, 8, 16 /*term*/}, compact: []byte{0x3f, 0x1c, 0xb8}},
- // even length, terminator
- {hex: []byte{0, 15, 1, 12, 11, 8, 16 /*term*/}, compact: []byte{0x20, 0x0f, 0x1c, 0xb8}},
- }
- for _, test := range tests {
- if c := hexToCompact(test.hex); !bytes.Equal(c, test.compact) {
- t.Errorf("hexToCompact(%x) -> %x, want %x", test.hex, c, test.compact)
- }
- if h := compactToHex(test.compact); !bytes.Equal(h, test.hex) {
- t.Errorf("compactToHex(%x) -> %x, want %x", test.compact, h, test.hex)
- }
- }
-}
-
-func TestHexKeybytes(t *testing.T) {
- tests := []struct{ key, hexIn, hexOut []byte }{
- {key: []byte{}, hexIn: []byte{16}, hexOut: []byte{16}},
- {key: []byte{}, hexIn: []byte{}, hexOut: []byte{16}},
- {
- key: []byte{0x12, 0x34, 0x56},
- hexIn: []byte{1, 2, 3, 4, 5, 6, 16},
- hexOut: []byte{1, 2, 3, 4, 5, 6, 16},
- },
- {
- key: []byte{0x12, 0x34, 0x5},
- hexIn: []byte{1, 2, 3, 4, 0, 5, 16},
- hexOut: []byte{1, 2, 3, 4, 0, 5, 16},
- },
- {
- key: []byte{0x12, 0x34, 0x56},
- hexIn: []byte{1, 2, 3, 4, 5, 6},
- hexOut: []byte{1, 2, 3, 4, 5, 6, 16},
- },
- }
- for _, test := range tests {
- if h := keybytesToHex(test.key); !bytes.Equal(h, test.hexOut) {
- t.Errorf("keybytesToHex(%x) -> %x, want %x", test.key, h, test.hexOut)
- }
- if k := hexToKeybytes(test.hexIn); !bytes.Equal(k, test.key) {
- t.Errorf("hexToKeybytes(%x) -> %x, want %x", test.hexIn, k, test.key)
- }
- }
-}
-
-func BenchmarkHexToCompact(b *testing.B) {
- testBytes := []byte{0, 15, 1, 12, 11, 8, 16 /*term*/}
- for i := 0; i < b.N; i++ {
- hexToCompact(testBytes)
- }
-}
-
-func BenchmarkCompactToHex(b *testing.B) {
- testBytes := []byte{0, 15, 1, 12, 11, 8, 16 /*term*/}
- for i := 0; i < b.N; i++ {
- compactToHex(testBytes)
- }
-}
-
-func BenchmarkKeybytesToHex(b *testing.B) {
- testBytes := []byte{7, 6, 6, 5, 7, 2, 6, 2, 16}
- for i := 0; i < b.N; i++ {
- keybytesToHex(testBytes)
- }
-}
-
-func BenchmarkHexToKeybytes(b *testing.B) {
- testBytes := []byte{7, 6, 6, 5, 7, 2, 6, 2, 16}
- for i := 0; i < b.N; i++ {
- hexToKeybytes(testBytes)
- }
-}
diff --git a/internal/trie/errors.go b/internal/trie/errors.go
deleted file mode 100644
index 567b80078..000000000
--- a/internal/trie/errors.go
+++ /dev/null
@@ -1,35 +0,0 @@
-// 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 trie
-
-import (
- "fmt"
-
- "github.com/ethereum/go-ethereum/common"
-)
-
-// MissingNodeError is returned by the trie functions (TryGet, TryUpdate, TryDelete)
-// in the case where a trie node is not present in the local database. It contains
-// information necessary for retrieving the missing node.
-type MissingNodeError struct {
- NodeHash common.Hash // hash of the missing node
- Path []byte // hex-encoded path to the missing node
-}
-
-func (err *MissingNodeError) Error() string {
- return fmt.Sprintf("missing trie node %x (path %x)", err.NodeHash, err.Path)
-}
diff --git a/internal/trie/hasher.go b/internal/trie/hasher.go
deleted file mode 100644
index 9d6756b6f..000000000
--- a/internal/trie/hasher.go
+++ /dev/null
@@ -1,218 +0,0 @@
-// Copyright 2016 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 trie
-
-import (
- "hash"
- "sync"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/rlp"
- "golang.org/x/crypto/sha3"
-)
-
-type hasher struct {
- tmp sliceBuffer
- sha keccakState
- cachegen uint16
- cachelimit uint16
- onleaf LeafCallback
-}
-
-// keccakState wraps sha3.state. In addition to the usual hash methods, it also supports
-// Read to get a variable amount of data from the hash state. Read is faster than Sum
-// because it doesn't copy the internal state, but also modifies the internal state.
-type keccakState interface {
- hash.Hash
- Read([]byte) (int, error)
-}
-
-type sliceBuffer []byte
-
-func (b *sliceBuffer) Write(data []byte) (n int, err error) {
- *b = append(*b, data...)
- return len(data), nil
-}
-
-func (b *sliceBuffer) Reset() {
- *b = (*b)[:0]
-}
-
-// hashers live in a global db.
-var hasherPool = sync.Pool{
- New: func() interface{} {
- return &hasher{
- tmp: make(sliceBuffer, 0, 550), // cap is as large as a full fullNode.
- sha: sha3.NewLegacyKeccak256().(keccakState),
- }
- },
-}
-
-func newHasher(cachegen, cachelimit uint16, onleaf LeafCallback) *hasher {
- h := hasherPool.Get().(*hasher)
- h.cachegen, h.cachelimit, h.onleaf = cachegen, cachelimit, onleaf
- return h
-}
-
-func returnHasherToPool(h *hasher) {
- hasherPool.Put(h)
-}
-
-// hash collapses a node down into a hash node, also returning a copy of the
-// original node initialized with the computed hash to replace the original one.
-func (h *hasher) hash(n node, db *Database, force bool) (node, node, error) {
- // If we're not storing the node, just hashing, use available cached data
- if hash, dirty := n.cache(); hash != nil {
- if db == nil {
- return hash, n, nil
- }
- if n.canUnload(h.cachegen, h.cachelimit) {
- // Unload the node from cache. All of its subnodes will have a lower or equal
- // cache generation number.
- cacheUnloadCounter.Inc(1)
- return hash, hash, nil
- }
- if !dirty {
- return hash, n, nil
- }
- }
- // Trie not processed yet or needs storage, walk the children
- collapsed, cached, err := h.hashChildren(n, db)
- if err != nil {
- return hashNode{}, n, err
- }
- hashed, err := h.store(collapsed, db, force)
- if err != nil {
- return hashNode{}, n, err
- }
- // Cache the hash of the node for later reuse and remove
- // the dirty flag in commit mode. It's fine to assign these values directly
- // without copying the node first because hashChildren copies it.
- cachedHash, _ := hashed.(hashNode)
- switch cn := cached.(type) {
- case *shortNode:
- cn.flags.hash = cachedHash
- if db != nil {
- cn.flags.dirty = false
- }
- case *fullNode:
- cn.flags.hash = cachedHash
- if db != nil {
- cn.flags.dirty = false
- }
- }
- return hashed, cached, nil
-}
-
-// hashChildren replaces the children of a node with their hashes if the encoded
-// size of the child is larger than a hash, returning the collapsed node as well
-// as a replacement for the original node with the child hashes cached in.
-func (h *hasher) hashChildren(original node, db *Database) (node, node, error) {
- var err error
-
- switch n := original.(type) {
- case *shortNode:
- // Hash the short node's child, caching the newly hashed subtree
- collapsed, cached := n.copy(), n.copy()
- collapsed.Key = hexToCompact(n.Key)
- cached.Key = common.CopyBytes(n.Key)
-
- if _, ok := n.Val.(valueNode); !ok {
- collapsed.Val, cached.Val, err = h.hash(n.Val, db, false)
- if err != nil {
- return original, original, err
- }
- }
- return collapsed, cached, nil
-
- case *fullNode:
- // Hash the full node's children, caching the newly hashed subtrees
- collapsed, cached := n.copy(), n.copy()
-
- for i := 0; i < 16; i++ {
- if n.Children[i] != nil {
- collapsed.Children[i], cached.Children[i], err = h.hash(n.Children[i], db, false)
- if err != nil {
- return original, original, err
- }
- }
- }
- cached.Children[16] = n.Children[16]
- return collapsed, cached, nil
-
- default:
- // Value and hash nodes don't have children so they're left as were
- return n, original, nil
- }
-}
-
-// store hashes the node n and if we have a storage layer specified, it writes
-// the key/value pair to it and tracks any node->child references as well as any
-// node->external trie references.
-func (h *hasher) store(n node, db *Database, force bool) (node, error) {
- // Don't store hashes or empty nodes.
- if _, isHash := n.(hashNode); n == nil || isHash {
- return n, nil
- }
- // Generate the RLP encoding of the node
- h.tmp.Reset()
- if err := rlp.Encode(&h.tmp, n); err != nil {
- panic("encode error: " + err.Error())
- }
- if len(h.tmp) < 32 && !force {
- return n, nil // Nodes smaller than 32 bytes are stored inside their parent
- }
- // Larger nodes are replaced by their hash and stored in the database.
- hash, _ := n.cache()
- if hash == nil {
- hash = h.makeHashNode(h.tmp)
- }
-
- if db != nil {
- // We are pooling the trie nodes into an intermediate memory cache
- hash := common.BytesToHash(hash)
-
- db.lock.Lock()
- db.insert(hash, h.tmp, n)
- db.lock.Unlock()
-
- // Track external references from account->storage trie
- if h.onleaf != nil {
- switch n := n.(type) {
- case *shortNode:
- if child, ok := n.Val.(valueNode); ok {
- h.onleaf(child, hash)
- }
- case *fullNode:
- for i := 0; i < 16; i++ {
- if child, ok := n.Children[i].(valueNode); ok {
- h.onleaf(child, hash)
- }
- }
- }
- }
- }
- return hash, nil
-}
-
-func (h *hasher) makeHashNode(data []byte) hashNode {
- n := make(hashNode, h.sha.Size())
- h.sha.Reset()
- h.sha.Write(data)
- h.sha.Read(n)
- return n
-}
diff --git a/internal/trie/iterator.go b/internal/trie/iterator.go
deleted file mode 100644
index 00b890eb8..000000000
--- a/internal/trie/iterator.go
+++ /dev/null
@@ -1,575 +0,0 @@
-// Copyright 2014 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package trie
-
-import (
- "bytes"
- "container/heap"
- "errors"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-// Iterator is a key-value trie iterator that traverses a Trie.
-type Iterator struct {
- nodeIt NodeIterator
-
- Key []byte // Current data key on which the iterator is positioned on
- Value []byte // Current data value on which the iterator is positioned on
- Err error
-}
-
-// NewIterator creates a new key-value iterator from a node iterator
-func NewIterator(it NodeIterator) *Iterator {
- return &Iterator{
- nodeIt: it,
- }
-}
-
-// Next moves the iterator forward one key-value entry.
-func (it *Iterator) Next() bool {
- for it.nodeIt.Next(true) {
- if it.nodeIt.Leaf() {
- it.Key = it.nodeIt.LeafKey()
- it.Value = it.nodeIt.LeafBlob()
- return true
- }
- }
- it.Key = nil
- it.Value = nil
- it.Err = it.nodeIt.Error()
- return false
-}
-
-// Prove generates the Merkle proof for the leaf node the iterator is currently
-// positioned on.
-func (it *Iterator) Prove() [][]byte {
- return it.nodeIt.LeafProof()
-}
-
-// NodeIterator is an iterator to traverse the trie pre-order.
-type NodeIterator interface {
- // Next moves the iterator to the next node. If the parameter is false, any child
- // nodes will be skipped.
- Next(bool) bool
-
- // Error returns the error status of the iterator.
- Error() error
-
- // Hash returns the hash of the current node.
- Hash() common.Hash
-
- // Parent returns the hash of the parent of the current node. The hash may be the one
- // grandparent if the immediate parent is an internal node with no hash.
- Parent() common.Hash
-
- // Path returns the hex-encoded path to the current node.
- // Callers must not retain references to the return value after calling Next.
- // For leaf nodes, the last element of the path is the 'terminator symbol' 0x10.
- Path() []byte
-
- // Leaf returns true iff the current node is a leaf node.
- Leaf() bool
-
- // LeafKey returns the key of the leaf. The method panics if the iterator is not
- // positioned at a leaf. Callers must not retain references to the value after
- // calling Next.
- LeafKey() []byte
-
- // LeafBlob returns the content of the leaf. The method panics if the iterator
- // is not positioned at a leaf. Callers must not retain references to the value
- // after calling Next.
- LeafBlob() []byte
-
- // LeafProof returns the Merkle proof of the leaf. The method panics if the
- // iterator is not positioned at a leaf. Callers must not retain references
- // to the value after calling Next.
- LeafProof() [][]byte
-}
-
-// nodeIteratorState represents the iteration state at one particular node of the
-// trie, which can be resumed at a later invocation.
-type nodeIteratorState struct {
- hash common.Hash // Hash of the node being iterated (nil if not standalone)
- node node // Trie node being iterated
- parent common.Hash // Hash of the first full ancestor node (nil if current is the root)
- index int // Child to be processed next
- pathlen int // Length of the path to this node
-}
-
-type nodeIterator struct {
- trie *Trie // Trie being iterated
- stack []*nodeIteratorState // Hierarchy of trie nodes persisting the iteration state
- path []byte // Path to the current node
- err error // Failure set in case of an internal error in the iterator
-}
-
-// errIteratorEnd is stored in nodeIterator.err when iteration is done.
-var errIteratorEnd = errors.New("end of iteration")
-
-// seekError is stored in nodeIterator.err if the initial seek has failed.
-type seekError struct {
- key []byte
- err error
-}
-
-func (e seekError) Error() string {
- return "seek error: " + e.err.Error()
-}
-
-func newNodeIterator(trie *Trie, start []byte) NodeIterator {
- if trie.Hash() == emptyState {
- return new(nodeIterator)
- }
- it := &nodeIterator{trie: trie}
- it.err = it.seek(start)
- return it
-}
-
-func (it *nodeIterator) Hash() common.Hash {
- if len(it.stack) == 0 {
- return common.Hash{}
- }
- return it.stack[len(it.stack)-1].hash
-}
-
-func (it *nodeIterator) Parent() common.Hash {
- if len(it.stack) == 0 {
- return common.Hash{}
- }
- return it.stack[len(it.stack)-1].parent
-}
-
-func (it *nodeIterator) Leaf() bool {
- return hasTerm(it.path)
-}
-
-func (it *nodeIterator) LeafKey() []byte {
- if len(it.stack) > 0 {
- if _, ok := it.stack[len(it.stack)-1].node.(valueNode); ok {
- return hexToKeybytes(it.path)
- }
- }
- panic("not at leaf")
-}
-
-func (it *nodeIterator) LeafBlob() []byte {
- if len(it.stack) > 0 {
- if node, ok := it.stack[len(it.stack)-1].node.(valueNode); ok {
- return []byte(node)
- }
- }
- panic("not at leaf")
-}
-
-func (it *nodeIterator) LeafProof() [][]byte {
- if len(it.stack) > 0 {
- if _, ok := it.stack[len(it.stack)-1].node.(valueNode); ok {
- hasher := newHasher(0, 0, nil)
- proofs := make([][]byte, 0, len(it.stack))
-
- for i, item := range it.stack[:len(it.stack)-1] {
- // Gather nodes that end up as hash nodes (or the root)
- node, _, _ := hasher.hashChildren(item.node, nil)
- hashed, _ := hasher.store(node, nil, false)
- if _, ok := hashed.(hashNode); ok || i == 0 {
- enc, _ := rlp.EncodeToBytes(node)
- proofs = append(proofs, enc)
- }
- }
- return proofs
- }
- }
- panic("not at leaf")
-}
-
-func (it *nodeIterator) Path() []byte {
- return it.path
-}
-
-func (it *nodeIterator) Error() error {
- if it.err == errIteratorEnd {
- return nil
- }
- if seek, ok := it.err.(seekError); ok {
- return seek.err
- }
- return it.err
-}
-
-// Next moves the iterator to the next node, returning whether there are any
-// further nodes. In case of an internal error this method returns false and
-// sets the Error field to the encountered failure. If `descend` is false,
-// skips iterating over any subnodes of the current node.
-func (it *nodeIterator) Next(descend bool) bool {
- if it.err == errIteratorEnd {
- return false
- }
- if seek, ok := it.err.(seekError); ok {
- if it.err = it.seek(seek.key); it.err != nil {
- return false
- }
- }
- // Otherwise step forward with the iterator and report any errors.
- state, parentIndex, path, err := it.peek(descend)
- it.err = err
- if it.err != nil {
- return false
- }
- it.push(state, parentIndex, path)
- return true
-}
-
-func (it *nodeIterator) seek(prefix []byte) error {
- // The path we're looking for is the hex encoded key without terminator.
- key := keybytesToHex(prefix)
- key = key[:len(key)-1]
- // Move forward until we're just before the closest match to key.
- for {
- state, parentIndex, path, err := it.peek(bytes.HasPrefix(key, it.path))
- if err == errIteratorEnd {
- return errIteratorEnd
- } else if err != nil {
- return seekError{prefix, err}
- } else if bytes.Compare(path, key) >= 0 {
- return nil
- }
- it.push(state, parentIndex, path)
- }
-}
-
-// peek creates the next state of the iterator.
-func (it *nodeIterator) peek(descend bool) (*nodeIteratorState, *int, []byte, error) {
- if len(it.stack) == 0 {
- // Initialize the iterator if we've just started.
- root := it.trie.Hash()
- state := &nodeIteratorState{node: it.trie.root, index: -1}
- if root != emptyRoot {
- state.hash = root
- }
- err := state.resolve(it.trie, nil)
- return state, nil, nil, err
- }
- if !descend {
- // If we're skipping children, pop the current node first
- it.pop()
- }
-
- // Continue iteration to the next child
- for len(it.stack) > 0 {
- parent := it.stack[len(it.stack)-1]
- ancestor := parent.hash
- if (ancestor == common.Hash{}) {
- ancestor = parent.parent
- }
- state, path, ok := it.nextChild(parent, ancestor)
- if ok {
- if err := state.resolve(it.trie, path); err != nil {
- return parent, &parent.index, path, err
- }
- return state, &parent.index, path, nil
- }
- // No more child nodes, move back up.
- it.pop()
- }
- return nil, nil, nil, errIteratorEnd
-}
-
-func (st *nodeIteratorState) resolve(tr *Trie, path []byte) error {
- if hash, ok := st.node.(hashNode); ok {
- resolved, err := tr.resolveHash(hash, path)
- if err != nil {
- return err
- }
- st.node = resolved
- st.hash = common.BytesToHash(hash)
- }
- return nil
-}
-
-func (it *nodeIterator) nextChild(parent *nodeIteratorState, ancestor common.Hash) (*nodeIteratorState, []byte, bool) {
- switch node := parent.node.(type) {
- case *fullNode:
- // Full node, move to the first non-nil child.
- for i := parent.index + 1; i < len(node.Children); i++ {
- child := node.Children[i]
- if child != nil {
- hash, _ := child.cache()
- state := &nodeIteratorState{
- hash: common.BytesToHash(hash),
- node: child,
- parent: ancestor,
- index: -1,
- pathlen: len(it.path),
- }
- path := append(it.path, byte(i))
- parent.index = i - 1
- return state, path, true
- }
- }
- case *shortNode:
- // Short node, return the pointer singleton child
- if parent.index < 0 {
- hash, _ := node.Val.cache()
- state := &nodeIteratorState{
- hash: common.BytesToHash(hash),
- node: node.Val,
- parent: ancestor,
- index: -1,
- pathlen: len(it.path),
- }
- path := append(it.path, node.Key...)
- return state, path, true
- }
- }
- return parent, it.path, false
-}
-
-func (it *nodeIterator) push(state *nodeIteratorState, parentIndex *int, path []byte) {
- it.path = path
- it.stack = append(it.stack, state)
- if parentIndex != nil {
- *parentIndex++
- }
-}
-
-func (it *nodeIterator) pop() {
- parent := it.stack[len(it.stack)-1]
- it.path = it.path[:parent.pathlen]
- it.stack = it.stack[:len(it.stack)-1]
-}
-
-func compareNodes(a, b NodeIterator) int {
- if cmp := bytes.Compare(a.Path(), b.Path()); cmp != 0 {
- return cmp
- }
- if a.Leaf() && !b.Leaf() {
- return -1
- } else if b.Leaf() && !a.Leaf() {
- return 1
- }
- if cmp := bytes.Compare(a.Hash().Bytes(), b.Hash().Bytes()); cmp != 0 {
- return cmp
- }
- if a.Leaf() && b.Leaf() {
- return bytes.Compare(a.LeafBlob(), b.LeafBlob())
- }
- return 0
-}
-
-type differenceIterator struct {
- a, b NodeIterator // Nodes returned are those in b - a.
- eof bool // Indicates a has run out of elements
- count int // Number of nodes scanned on either trie
-}
-
-// NewDifferenceIterator constructs a NodeIterator that iterates over elements in b that
-// are not in a. Returns the iterator, and a pointer to an integer recording the number
-// of nodes seen.
-func NewDifferenceIterator(a, b NodeIterator) (NodeIterator, *int) {
- a.Next(true)
- it := &differenceIterator{
- a: a,
- b: b,
- }
- return it, &it.count
-}
-
-func (it *differenceIterator) Hash() common.Hash {
- return it.b.Hash()
-}
-
-func (it *differenceIterator) Parent() common.Hash {
- return it.b.Parent()
-}
-
-func (it *differenceIterator) Leaf() bool {
- return it.b.Leaf()
-}
-
-func (it *differenceIterator) LeafKey() []byte {
- return it.b.LeafKey()
-}
-
-func (it *differenceIterator) LeafBlob() []byte {
- return it.b.LeafBlob()
-}
-
-func (it *differenceIterator) LeafProof() [][]byte {
- return it.b.LeafProof()
-}
-
-func (it *differenceIterator) Path() []byte {
- return it.b.Path()
-}
-
-func (it *differenceIterator) Next(bool) bool {
- // Invariants:
- // - We always advance at least one element in b.
- // - At the start of this function, a's path is lexically greater than b's.
- if !it.b.Next(true) {
- return false
- }
- it.count++
-
- if it.eof {
- // a has reached eof, so we just return all elements from b
- return true
- }
-
- for {
- switch compareNodes(it.a, it.b) {
- case -1:
- // b jumped past a; advance a
- if !it.a.Next(true) {
- it.eof = true
- return true
- }
- it.count++
- case 1:
- // b is before a
- return true
- case 0:
- // a and b are identical; skip this whole subtree if the nodes have hashes
- hasHash := it.a.Hash() == common.Hash{}
- if !it.b.Next(hasHash) {
- return false
- }
- it.count++
- if !it.a.Next(hasHash) {
- it.eof = true
- return true
- }
- it.count++
- }
- }
-}
-
-func (it *differenceIterator) Error() error {
- if err := it.a.Error(); err != nil {
- return err
- }
- return it.b.Error()
-}
-
-type nodeIteratorHeap []NodeIterator
-
-func (h nodeIteratorHeap) Len() int { return len(h) }
-func (h nodeIteratorHeap) Less(i, j int) bool { return compareNodes(h[i], h[j]) < 0 }
-func (h nodeIteratorHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
-func (h *nodeIteratorHeap) Push(x interface{}) { *h = append(*h, x.(NodeIterator)) }
-func (h *nodeIteratorHeap) Pop() interface{} {
- n := len(*h)
- x := (*h)[n-1]
- *h = (*h)[0 : n-1]
- return x
-}
-
-type unionIterator struct {
- items *nodeIteratorHeap // Nodes returned are the union of the ones in these iterators
- count int // Number of nodes scanned across all tries
-}
-
-// NewUnionIterator constructs a NodeIterator that iterates over elements in the union
-// of the provided NodeIterators. Returns the iterator, and a pointer to an integer
-// recording the number of nodes visited.
-func NewUnionIterator(iters []NodeIterator) (NodeIterator, *int) {
- h := make(nodeIteratorHeap, len(iters))
- copy(h, iters)
- heap.Init(&h)
-
- ui := &unionIterator{items: &h}
- return ui, &ui.count
-}
-
-func (it *unionIterator) Hash() common.Hash {
- return (*it.items)[0].Hash()
-}
-
-func (it *unionIterator) Parent() common.Hash {
- return (*it.items)[0].Parent()
-}
-
-func (it *unionIterator) Leaf() bool {
- return (*it.items)[0].Leaf()
-}
-
-func (it *unionIterator) LeafKey() []byte {
- return (*it.items)[0].LeafKey()
-}
-
-func (it *unionIterator) LeafBlob() []byte {
- return (*it.items)[0].LeafBlob()
-}
-
-func (it *unionIterator) LeafProof() [][]byte {
- return (*it.items)[0].LeafProof()
-}
-
-func (it *unionIterator) Path() []byte {
- return (*it.items)[0].Path()
-}
-
-// Next returns the next node in the union of tries being iterated over.
-//
-// It does this by maintaining a heap of iterators, sorted by the iteration
-// order of their next elements, with one entry for each source trie. Each
-// time Next() is called, it takes the least element from the heap to return,
-// advancing any other iterators that also point to that same element. These
-// iterators are called with descend=false, since we know that any nodes under
-// these nodes will also be duplicates, found in the currently selected iterator.
-// Whenever an iterator is advanced, it is pushed back into the heap if it still
-// has elements remaining.
-//
-// In the case that descend=false - eg, we're asked to ignore all subnodes of the
-// current node - we also advance any iterators in the heap that have the current
-// path as a prefix.
-func (it *unionIterator) Next(descend bool) bool {
- if len(*it.items) == 0 {
- return false
- }
-
- // Get the next key from the union
- least := heap.Pop(it.items).(NodeIterator)
-
- // Skip over other nodes as long as they're identical, or, if we're not descending, as
- // long as they have the same prefix as the current node.
- for len(*it.items) > 0 && ((!descend && bytes.HasPrefix((*it.items)[0].Path(), least.Path())) || compareNodes(least, (*it.items)[0]) == 0) {
- skipped := heap.Pop(it.items).(NodeIterator)
- // Skip the whole subtree if the nodes have hashes; otherwise just skip this node
- if skipped.Next(skipped.Hash() == common.Hash{}) {
- it.count++
- // If there are more elements, push the iterator back on the heap
- heap.Push(it.items, skipped)
- }
- }
- if least.Next(descend) {
- it.count++
- heap.Push(it.items, least)
- }
- return len(*it.items) > 0
-}
-
-func (it *unionIterator) Error() error {
- for i := 0; i < len(*it.items); i++ {
- if err := (*it.items)[i].Error(); err != nil {
- return err
- }
- }
- return nil
-}
diff --git a/internal/trie/iterator_test.go b/internal/trie/iterator_test.go
deleted file mode 100644
index f54127efd..000000000
--- a/internal/trie/iterator_test.go
+++ /dev/null
@@ -1,435 +0,0 @@
-// Copyright 2014 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package trie
-
-import (
- "bytes"
- "fmt"
- "math/rand"
- "testing"
-
- "github.com/ethereum/go-ethereum/common"
- hdb "github.com/harmony-one/harmony/internal/db"
-)
-
-func TestIterator(t *testing.T) {
- trie := newEmpty()
- vals := []struct{ k, v string }{
- {"do", "verb"},
- {"ether", "wookiedoo"},
- {"horse", "stallion"},
- {"shaman", "horse"},
- {"doge", "coin"},
- {"dog", "puppy"},
- {"somethingveryoddindeedthis is", "myothernodedata"},
- }
- all := make(map[string]string)
- for _, val := range vals {
- all[val.k] = val.v
- trie.Update([]byte(val.k), []byte(val.v))
- }
- trie.Commit(nil)
-
- found := make(map[string]string)
- it := NewIterator(trie.NodeIterator(nil))
- for it.Next() {
- found[string(it.Key)] = string(it.Value)
- }
-
- for k, v := range all {
- if found[k] != v {
- t.Errorf("iterator value mismatch for %s: got %q want %q", k, found[k], v)
- }
- }
-}
-
-type kv struct {
- k, v []byte
- t bool
-}
-
-func TestIteratorLargeData(t *testing.T) {
- trie := newEmpty()
- vals := make(map[string]*kv)
-
- for i := byte(0); i < 255; i++ {
- value := &kv{common.LeftPadBytes([]byte{i}, 32), []byte{i}, false}
- value2 := &kv{common.LeftPadBytes([]byte{10, i}, 32), []byte{i}, false}
- trie.Update(value.k, value.v)
- trie.Update(value2.k, value2.v)
- vals[string(value.k)] = value
- vals[string(value2.k)] = value2
- }
-
- it := NewIterator(trie.NodeIterator(nil))
- for it.Next() {
- vals[string(it.Key)].t = true
- }
-
- var untouched []*kv
- for _, value := range vals {
- if !value.t {
- untouched = append(untouched, value)
- }
- }
-
- if len(untouched) > 0 {
- t.Errorf("Missed %d nodes", len(untouched))
- for _, value := range untouched {
- t.Error(value)
- }
- }
-}
-
-// Tests that the node iterator indeed walks over the entire database contents.
-func TestNodeIteratorCoverage(t *testing.T) {
- // Create some arbitrary test trie to iterate
- db, trie, _ := makeTestTrie()
-
- // Gather all the node hashes found by the iterator
- hashes := make(map[common.Hash]struct{})
- for it := trie.NodeIterator(nil); it.Next(true); {
- if it.Hash() != (common.Hash{}) {
- hashes[it.Hash()] = struct{}{}
- }
- }
- // Cross check the hashes and the database itself
- for hash := range hashes {
- if _, err := db.Node(hash); err != nil {
- t.Errorf("failed to retrieve reported node %x: %v", hash, err)
- }
- }
- for hash, obj := range db.nodes {
- if obj != nil && hash != (common.Hash{}) {
- if _, ok := hashes[hash]; !ok {
- t.Errorf("state entry not reported %x", hash)
- }
- }
- }
- for _, key := range db.diskdb.(*hdb.MemDatabase).Keys() {
- if _, ok := hashes[common.BytesToHash(key)]; !ok {
- t.Errorf("state entry not reported %x", key)
- }
- }
-}
-
-type kvs struct{ k, v string }
-
-var testdata1 = []kvs{
- {"barb", "ba"},
- {"bard", "bc"},
- {"bars", "bb"},
- {"bar", "b"},
- {"fab", "z"},
- {"food", "ab"},
- {"foos", "aa"},
- {"foo", "a"},
-}
-
-var testdata2 = []kvs{
- {"aardvark", "c"},
- {"bar", "b"},
- {"barb", "bd"},
- {"bars", "be"},
- {"fab", "z"},
- {"foo", "a"},
- {"foos", "aa"},
- {"food", "ab"},
- {"jars", "d"},
-}
-
-func TestIteratorSeek(t *testing.T) {
- trie := newEmpty()
- for _, val := range testdata1 {
- trie.Update([]byte(val.k), []byte(val.v))
- }
-
- // Seek to the middle.
- it := NewIterator(trie.NodeIterator([]byte("fab")))
- if err := checkIteratorOrder(testdata1[4:], it); err != nil {
- t.Fatal(err)
- }
-
- // Seek to a non-existent key.
- it = NewIterator(trie.NodeIterator([]byte("barc")))
- if err := checkIteratorOrder(testdata1[1:], it); err != nil {
- t.Fatal(err)
- }
-
- // Seek beyond the end.
- it = NewIterator(trie.NodeIterator([]byte("z")))
- if err := checkIteratorOrder(nil, it); err != nil {
- t.Fatal(err)
- }
-}
-
-func checkIteratorOrder(want []kvs, it *Iterator) error {
- for it.Next() {
- if len(want) == 0 {
- return fmt.Errorf("didn't expect any more values, got key %q", it.Key)
- }
- if !bytes.Equal(it.Key, []byte(want[0].k)) {
- return fmt.Errorf("wrong key: got %q, want %q", it.Key, want[0].k)
- }
- want = want[1:]
- }
- if len(want) > 0 {
- return fmt.Errorf("iterator ended early, want key %q", want[0])
- }
- return nil
-}
-
-func TestDifferenceIterator(t *testing.T) {
- triea := newEmpty()
- for _, val := range testdata1 {
- triea.Update([]byte(val.k), []byte(val.v))
- }
- triea.Commit(nil)
-
- trieb := newEmpty()
- for _, val := range testdata2 {
- trieb.Update([]byte(val.k), []byte(val.v))
- }
- trieb.Commit(nil)
-
- found := make(map[string]string)
- di, _ := NewDifferenceIterator(triea.NodeIterator(nil), trieb.NodeIterator(nil))
- it := NewIterator(di)
- for it.Next() {
- found[string(it.Key)] = string(it.Value)
- }
-
- all := []struct{ k, v string }{
- {"aardvark", "c"},
- {"barb", "bd"},
- {"bars", "be"},
- {"jars", "d"},
- }
- for _, item := range all {
- if found[item.k] != item.v {
- t.Errorf("iterator value mismatch for %s: got %v want %v", item.k, found[item.k], item.v)
- }
- }
- if len(found) != len(all) {
- t.Errorf("iterator count mismatch: got %d values, want %d", len(found), len(all))
- }
-}
-
-func TestUnionIterator(t *testing.T) {
- triea := newEmpty()
- for _, val := range testdata1 {
- triea.Update([]byte(val.k), []byte(val.v))
- }
- triea.Commit(nil)
-
- trieb := newEmpty()
- for _, val := range testdata2 {
- trieb.Update([]byte(val.k), []byte(val.v))
- }
- trieb.Commit(nil)
-
- di, _ := NewUnionIterator([]NodeIterator{triea.NodeIterator(nil), trieb.NodeIterator(nil)})
- it := NewIterator(di)
-
- all := []struct{ k, v string }{
- {"aardvark", "c"},
- {"barb", "ba"},
- {"barb", "bd"},
- {"bard", "bc"},
- {"bars", "bb"},
- {"bars", "be"},
- {"bar", "b"},
- {"fab", "z"},
- {"food", "ab"},
- {"foos", "aa"},
- {"foo", "a"},
- {"jars", "d"},
- }
-
- for i, kv := range all {
- if !it.Next() {
- t.Errorf("Iterator ends prematurely at element %d", i)
- }
- if kv.k != string(it.Key) {
- t.Errorf("iterator value mismatch for element %d: got key %s want %s", i, it.Key, kv.k)
- }
- if kv.v != string(it.Value) {
- t.Errorf("iterator value mismatch for element %d: got value %s want %s", i, it.Value, kv.v)
- }
- }
- if it.Next() {
- t.Errorf("Iterator returned extra values.")
- }
-}
-
-func TestIteratorNoDups(t *testing.T) {
- var tr Trie
- for _, val := range testdata1 {
- tr.Update([]byte(val.k), []byte(val.v))
- }
- checkIteratorNoDups(t, tr.NodeIterator(nil), nil)
-}
-
-// This test checks that nodeIterator.Next can be retried after inserting missing trie nodes.
-func TestIteratorContinueAfterErrorDisk(t *testing.T) { testIteratorContinueAfterError(t, false) }
-func TestIteratorContinueAfterErrorMemonly(t *testing.T) { testIteratorContinueAfterError(t, true) }
-
-func testIteratorContinueAfterError(t *testing.T, memonly bool) {
- diskdb := hdb.NewMemDatabase()
- triedb := NewDatabase(diskdb)
-
- tr, _ := New(common.Hash{}, triedb)
- for _, val := range testdata1 {
- tr.Update([]byte(val.k), []byte(val.v))
- }
- tr.Commit(nil)
- if !memonly {
- triedb.Commit(tr.Hash(), true)
- }
- wantNodeCount := checkIteratorNoDups(t, tr.NodeIterator(nil), nil)
-
- var (
- diskKeys [][]byte
- memKeys []common.Hash
- )
- if memonly {
- memKeys = triedb.Nodes()
- } else {
- diskKeys = diskdb.Keys()
- }
- for i := 0; i < 20; i++ {
- // Create trie that will load all nodes from DB.
- tr, _ := New(tr.Hash(), triedb)
-
- // Remove a random node from the database. It can't be the root node
- // because that one is already loaded.
- var (
- rkey common.Hash
- rval []byte
- robj *cachedNode
- )
- for {
- if memonly {
- rkey = memKeys[rand.Intn(len(memKeys))]
- } else {
- copy(rkey[:], diskKeys[rand.Intn(len(diskKeys))])
- }
- if rkey != tr.Hash() {
- break
- }
- }
- if memonly {
- robj = triedb.nodes[rkey]
- delete(triedb.nodes, rkey)
- } else {
- rval, _ = diskdb.Get(rkey[:])
- diskdb.Delete(rkey[:])
- }
- // Iterate until the error is hit.
- seen := make(map[string]bool)
- it := tr.NodeIterator(nil)
- checkIteratorNoDups(t, it, seen)
- missing, ok := it.Error().(*MissingNodeError)
- if !ok || missing.NodeHash != rkey {
- t.Fatal("didn't hit missing node, got", it.Error())
- }
-
- // Add the node back and continue iteration.
- if memonly {
- triedb.nodes[rkey] = robj
- } else {
- diskdb.Put(rkey[:], rval)
- }
- checkIteratorNoDups(t, it, seen)
- if it.Error() != nil {
- t.Fatal("unexpected error", it.Error())
- }
- if len(seen) != wantNodeCount {
- t.Fatal("wrong node iteration count, got", len(seen), "want", wantNodeCount)
- }
- }
-}
-
-// Similar to the test above, this one checks that failure to create nodeIterator at a
-// certain key prefix behaves correctly when Next is called. The expectation is that Next
-// should retry seeking before returning true for the first time.
-func TestIteratorContinueAfterSeekErrorDisk(t *testing.T) {
- testIteratorContinueAfterSeekError(t, false)
-}
-func TestIteratorContinueAfterSeekErrorMemonly(t *testing.T) {
- testIteratorContinueAfterSeekError(t, true)
-}
-
-func testIteratorContinueAfterSeekError(t *testing.T, memonly bool) {
- // Commit test trie to db, then remove the node containing "bars".
- diskdb := hdb.NewMemDatabase()
- triedb := NewDatabase(diskdb)
-
- ctr, _ := New(common.Hash{}, triedb)
- for _, val := range testdata1 {
- ctr.Update([]byte(val.k), []byte(val.v))
- }
- root, _ := ctr.Commit(nil)
- if !memonly {
- triedb.Commit(root, true)
- }
- barNodeHash := common.HexToHash("05041990364eb72fcb1127652ce40d8bab765f2bfe53225b1170d276cc101c2e")
- var (
- barNodeBlob []byte
- barNodeObj *cachedNode
- )
- if memonly {
- barNodeObj = triedb.nodes[barNodeHash]
- delete(triedb.nodes, barNodeHash)
- } else {
- barNodeBlob, _ = diskdb.Get(barNodeHash[:])
- diskdb.Delete(barNodeHash[:])
- }
- // Create a new iterator that seeks to "bars". Seeking can't proceed because
- // the node is missing.
- tr, _ := New(root, triedb)
- it := tr.NodeIterator([]byte("bars"))
- missing, ok := it.Error().(*MissingNodeError)
- if !ok {
- t.Fatal("want MissingNodeError, got", it.Error())
- } else if missing.NodeHash != barNodeHash {
- t.Fatal("wrong node missing")
- }
- // Reinsert the missing node.
- if memonly {
- triedb.nodes[barNodeHash] = barNodeObj
- } else {
- diskdb.Put(barNodeHash[:], barNodeBlob)
- }
- // Check that iteration produces the right set of values.
- if err := checkIteratorOrder(testdata1[2:], NewIterator(it)); err != nil {
- t.Fatal(err)
- }
-}
-
-func checkIteratorNoDups(t *testing.T, it NodeIterator, seen map[string]bool) int {
- if seen == nil {
- seen = make(map[string]bool)
- }
- for it.Next(true) {
- if seen[string(it.Path())] {
- t.Fatalf("iterator visited node path %x twice", it.Path())
- }
- seen[string(it.Path())] = true
- }
- return len(seen)
-}
diff --git a/internal/trie/node.go b/internal/trie/node.go
deleted file mode 100644
index 1fafb7a53..000000000
--- a/internal/trie/node.go
+++ /dev/null
@@ -1,237 +0,0 @@
-// Copyright 2014 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package trie
-
-import (
- "fmt"
- "io"
- "strings"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-var indices = []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "[17]"}
-
-type node interface {
- fstring(string) string
- cache() (hashNode, bool)
- canUnload(cachegen, cachelimit uint16) bool
-}
-
-type (
- fullNode struct {
- Children [17]node // Actual trie node data to encode/decode (needs custom encoder)
- flags nodeFlag
- }
- shortNode struct {
- Key []byte
- Val node
- flags nodeFlag
- }
- hashNode []byte
- valueNode []byte
-)
-
-// nilValueNode is used when collapsing internal trie nodes for hashing, since
-// unset children need to serialize correctly.
-var nilValueNode = valueNode(nil)
-
-// EncodeRLP encodes a full node into the consensus RLP format.
-func (n *fullNode) EncodeRLP(w io.Writer) error {
- var nodes [17]node
-
- for i, child := range &n.Children {
- if child != nil {
- nodes[i] = child
- } else {
- nodes[i] = nilValueNode
- }
- }
- return rlp.Encode(w, nodes)
-}
-
-func (n *fullNode) copy() *fullNode { copy := *n; return © }
-func (n *shortNode) copy() *shortNode { copy := *n; return © }
-
-// nodeFlag contains caching-related metadata about a node.
-type nodeFlag struct {
- hash hashNode // cached hash of the node (may be nil)
- gen uint16 // cache generation counter
- dirty bool // whether the node has changes that must be written to the database
-}
-
-// canUnload tells whether a node can be unloaded.
-func (n *nodeFlag) canUnload(cachegen, cachelimit uint16) bool {
- return !n.dirty && cachegen-n.gen >= cachelimit
-}
-
-func (n *fullNode) canUnload(gen, limit uint16) bool { return n.flags.canUnload(gen, limit) }
-func (n *shortNode) canUnload(gen, limit uint16) bool { return n.flags.canUnload(gen, limit) }
-func (n hashNode) canUnload(uint16, uint16) bool { return false }
-func (n valueNode) canUnload(uint16, uint16) bool { return false }
-
-func (n *fullNode) cache() (hashNode, bool) { return n.flags.hash, n.flags.dirty }
-func (n *shortNode) cache() (hashNode, bool) { return n.flags.hash, n.flags.dirty }
-func (n hashNode) cache() (hashNode, bool) { return nil, true }
-func (n valueNode) cache() (hashNode, bool) { return nil, true }
-
-// Pretty printing.
-func (n *fullNode) String() string { return n.fstring("") }
-func (n *shortNode) String() string { return n.fstring("") }
-func (n hashNode) String() string { return n.fstring("") }
-func (n valueNode) String() string { return n.fstring("") }
-
-func (n *fullNode) fstring(ind string) string {
- resp := fmt.Sprintf("[\n%s ", ind)
- for i, node := range &n.Children {
- if node == nil {
- resp += fmt.Sprintf("%s: ", indices[i])
- } else {
- resp += fmt.Sprintf("%s: %v", indices[i], node.fstring(ind+" "))
- }
- }
- return resp + fmt.Sprintf("\n%s] ", ind)
-}
-func (n *shortNode) fstring(ind string) string {
- return fmt.Sprintf("{%x: %v} ", n.Key, n.Val.fstring(ind+" "))
-}
-func (n hashNode) fstring(ind string) string {
- return fmt.Sprintf("<%x> ", []byte(n))
-}
-func (n valueNode) fstring(ind string) string {
- return fmt.Sprintf("%x ", []byte(n))
-}
-
-func mustDecodeNode(hash, buf []byte, cachegen uint16) node {
- n, err := decodeNode(hash, buf, cachegen)
- if err != nil {
- panic(fmt.Sprintf("node %x: %v", hash, err))
- }
- return n
-}
-
-// decodeNode parses the RLP encoding of a trie node.
-func decodeNode(hash, buf []byte, cachegen uint16) (node, error) {
- if len(buf) == 0 {
- return nil, io.ErrUnexpectedEOF
- }
- elems, _, err := rlp.SplitList(buf)
- if err != nil {
- return nil, fmt.Errorf("decode error: %v", err)
- }
- switch c, _ := rlp.CountValues(elems); c {
- case 2:
- n, err := decodeShort(hash, elems, cachegen)
- return n, wrapError(err, "short")
- case 17:
- n, err := decodeFull(hash, elems, cachegen)
- return n, wrapError(err, "full")
- default:
- return nil, fmt.Errorf("invalid number of list elements: %v", c)
- }
-}
-
-func decodeShort(hash, elems []byte, cachegen uint16) (node, error) {
- kbuf, rest, err := rlp.SplitString(elems)
- if err != nil {
- return nil, err
- }
- flag := nodeFlag{hash: hash, gen: cachegen}
- key := compactToHex(kbuf)
- if hasTerm(key) {
- // value node
- val, _, err := rlp.SplitString(rest)
- if err != nil {
- return nil, fmt.Errorf("invalid value node: %v", err)
- }
- return &shortNode{key, append(valueNode{}, val...), flag}, nil
- }
- r, _, err := decodeRef(rest, cachegen)
- if err != nil {
- return nil, wrapError(err, "val")
- }
- return &shortNode{key, r, flag}, nil
-}
-
-func decodeFull(hash, elems []byte, cachegen uint16) (*fullNode, error) {
- n := &fullNode{flags: nodeFlag{hash: hash, gen: cachegen}}
- for i := 0; i < 16; i++ {
- cld, rest, err := decodeRef(elems, cachegen)
- if err != nil {
- return n, wrapError(err, fmt.Sprintf("[%d]", i))
- }
- n.Children[i], elems = cld, rest
- }
- val, _, err := rlp.SplitString(elems)
- if err != nil {
- return n, err
- }
- if len(val) > 0 {
- n.Children[16] = append(valueNode{}, val...)
- }
- return n, nil
-}
-
-const hashLen = len(common.Hash{})
-
-func decodeRef(buf []byte, cachegen uint16) (node, []byte, error) {
- kind, val, rest, err := rlp.Split(buf)
- if err != nil {
- return nil, buf, err
- }
- switch {
- case kind == rlp.List:
- // 'embedded' node reference. The encoding must be smaller
- // than a hash in order to be valid.
- if size := len(buf) - len(rest); size > hashLen {
- err := fmt.Errorf("oversized embedded node (size is %d bytes, want size < %d)", size, hashLen)
- return nil, buf, err
- }
- n, err := decodeNode(nil, buf, cachegen)
- return n, rest, err
- case kind == rlp.String && len(val) == 0:
- // empty node
- return nil, rest, nil
- case kind == rlp.String && len(val) == 32:
- return append(hashNode{}, val...), rest, nil
- default:
- return nil, nil, fmt.Errorf("invalid RLP string size %d (want 0 or 32)", len(val))
- }
-}
-
-// wraps a decoding error with information about the path to the
-// invalid child node (for debugging encoding issues).
-type decodeError struct {
- what error
- stack []string
-}
-
-func wrapError(err error, ctx string) error {
- if err == nil {
- return nil
- }
- if decErr, ok := err.(*decodeError); ok {
- decErr.stack = append(decErr.stack, ctx)
- return decErr
- }
- return &decodeError{err, []string{ctx}}
-}
-
-func (err *decodeError) Error() string {
- return fmt.Sprintf("%v (decode path: %s)", err.what, strings.Join(err.stack, "<-"))
-}
diff --git a/internal/trie/node_test.go b/internal/trie/node_test.go
deleted file mode 100644
index 7ad1ff9e7..000000000
--- a/internal/trie/node_test.go
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2016 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 trie
-
-import "testing"
-
-func TestCanUnload(t *testing.T) {
- tests := []struct {
- flag nodeFlag
- cachegen, cachelimit uint16
- want bool
- }{
- {
- flag: nodeFlag{dirty: true, gen: 0},
- want: false,
- },
- {
- flag: nodeFlag{dirty: false, gen: 0},
- cachegen: 0, cachelimit: 0,
- want: true,
- },
- {
- flag: nodeFlag{dirty: false, gen: 65534},
- cachegen: 65535, cachelimit: 1,
- want: true,
- },
- {
- flag: nodeFlag{dirty: false, gen: 65534},
- cachegen: 0, cachelimit: 1,
- want: true,
- },
- {
- flag: nodeFlag{dirty: false, gen: 1},
- cachegen: 65535, cachelimit: 1,
- want: true,
- },
- }
-
- for _, test := range tests {
- if got := test.flag.canUnload(test.cachegen, test.cachelimit); got != test.want {
- t.Errorf("%+v\n got %t, want %t", test, got, test.want)
- }
- }
-}
diff --git a/internal/trie/proof.go b/internal/trie/proof.go
deleted file mode 100644
index af405eb8b..000000000
--- a/internal/trie/proof.go
+++ /dev/null
@@ -1,153 +0,0 @@
-// 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 trie
-
-import (
- "bytes"
- "fmt"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/rlp"
- "github.com/harmony-one/harmony/internal/db"
-)
-
-// 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 the proof.
-//
-// If the trie does not contain a value for key, the returned proof contains all
-// nodes of the longest existing prefix of the key (at least the root node), ending
-// with the node that proves the absence of the key.
-func (t *Trie) Prove(key []byte, fromLevel uint, proofDb db.Putter) error {
- // Collect all nodes on the path to key.
- key = keybytesToHex(key)
- nodes := []node{}
- tn := t.root
- 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.
- tn = nil
- } else {
- tn = n.Val
- key = key[len(n.Key):]
- }
- nodes = append(nodes, n)
- case *fullNode:
- tn = n.Children[key[0]]
- key = key[1:]
- nodes = append(nodes, n)
- case hashNode:
- var err error
- tn, err = t.resolveHash(n, nil)
- if err != nil {
- log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
- return err
- }
- default:
- panic(fmt.Sprintf("%T: invalid node: %v", tn, tn))
- }
- }
- hasher := newHasher(0, 0, nil)
- for i, n := range nodes {
- // Don't bother checking for errors here since hasher panics
- // if encoding doesn't work and we're not writing to any database.
- n, _, _ = hasher.hashChildren(n, nil)
- hn, _ := hasher.store(n, nil, false)
- if hash, ok := hn.(hashNode); ok || i == 0 {
- // If the node's database encoding is a hash (or is the
- // root node), it becomes a proof element.
- if fromLevel > 0 {
- fromLevel--
- } else {
- enc, _ := rlp.EncodeToBytes(n)
- if !ok {
- hash = crypto.Keccak256(enc)
- }
- proofDb.Put(hash, enc)
- }
- }
- }
- return nil
-}
-
-// 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 the proof.
-//
-// If the trie does not contain a value for key, the returned proof contains all
-// nodes of the longest existing prefix of the key (at least the root node), ending
-// with the node that proves the absence of the key.
-func (t *SecureTrie) Prove(key []byte, fromLevel uint, proofDb db.Putter) error {
- return t.trie.Prove(key, fromLevel, proofDb)
-}
-
-// VerifyProof checks merkle proofs. The given proof must contain the 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, proofDb DatabaseReader) (value []byte, nodes int, err error) {
- key = keybytesToHex(key)
- wantHash := rootHash
- for i := 0; ; i++ {
- buf, _ := proofDb.Get(wantHash[:])
- if buf == nil {
- return nil, i, fmt.Errorf("proof node %d (hash %064x) missing", i, wantHash)
- }
- n, err := decodeNode(wantHash[:], buf, 0)
- if err != nil {
- return nil, i, fmt.Errorf("bad proof node %d: %v", i, err)
- }
- keyrest, cld := get(n, key)
- switch cld := cld.(type) {
- case nil:
- // The trie doesn't contain the key.
- return nil, i, nil
- case hashNode:
- key = keyrest
- copy(wantHash[:], cld)
- case valueNode:
- return cld, i + 1, nil
- }
- }
-}
-
-func get(tn node, key []byte) ([]byte, node) {
- for {
- switch n := tn.(type) {
- case *shortNode:
- if len(key) < len(n.Key) || !bytes.Equal(n.Key, key[:len(n.Key)]) {
- return nil, nil
- }
- tn = n.Val
- key = key[len(n.Key):]
- case *fullNode:
- tn = n.Children[key[0]]
- key = key[1:]
- case hashNode:
- return key, n
- case nil:
- return key, nil
- case valueNode:
- return nil, n
- default:
- panic(fmt.Sprintf("%T: invalid node: %v", tn, tn))
- }
- }
-}
diff --git a/internal/trie/proof_test.go b/internal/trie/proof_test.go
deleted file mode 100644
index 996f87478..000000000
--- a/internal/trie/proof_test.go
+++ /dev/null
@@ -1,218 +0,0 @@
-// 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 trie
-
-import (
- "bytes"
- crand "crypto/rand"
- mrand "math/rand"
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
-)
-
-func init() {
- mrand.Seed(time.Now().Unix())
-}
-
-// makeProvers creates Merkle trie provers based on different implementations to
-// test all variations.
-func makeProvers(trie *Trie) []func(key []byte) *ethdb.MemDatabase {
- var provers []func(key []byte) *ethdb.MemDatabase
-
- // Create a direct trie based Merkle prover
- provers = append(provers, func(key []byte) *ethdb.MemDatabase {
- proof := ethdb.NewMemDatabase()
- trie.Prove(key, 0, proof)
- return proof
- })
- // Create a leaf iterator based Merkle prover
- provers = append(provers, func(key []byte) *ethdb.MemDatabase {
- proof := ethdb.NewMemDatabase()
- if it := NewIterator(trie.NodeIterator(key)); it.Next() && bytes.Equal(key, it.Key) {
- for _, p := range it.Prove() {
- proof.Put(crypto.Keccak256(p), p)
- }
- }
- return proof
- })
- return provers
-}
-
-func TestProof(t *testing.T) {
- trie, vals := randomTrie(500)
- root := trie.Hash()
- for i, prover := range makeProvers(trie) {
- for _, kv := range vals {
- proof := prover(kv.k)
- if proof == nil {
- t.Fatalf("prover %d: missing key %x while constructing proof", i, kv.k)
- }
- val, _, err := VerifyProof(root, kv.k, proof)
- if err != nil {
- t.Fatalf("prover %d: failed to verify proof for key %x: %v\nraw proof: %x", i, kv.k, err, proof)
- }
- if !bytes.Equal(val, kv.v) {
- t.Fatalf("prover %d: verified value mismatch for key %x: have %x, want %x", i, kv.k, val, kv.v)
- }
- }
- }
-}
-
-func TestOneElementProof(t *testing.T) {
- trie := new(Trie)
- updateString(trie, "k", "v")
- for i, prover := range makeProvers(trie) {
- proof := prover([]byte("k"))
- if proof == nil {
- t.Fatalf("prover %d: nil proof", i)
- }
- if proof.Len() != 1 {
- t.Errorf("prover %d: proof should have one element", i)
- }
- val, _, err := VerifyProof(trie.Hash(), []byte("k"), proof)
- if err != nil {
- t.Fatalf("prover %d: failed to verify proof: %v\nraw proof: %x", i, err, proof)
- }
- if !bytes.Equal(val, []byte("v")) {
- t.Fatalf("prover %d: verified value mismatch: have %x, want 'k'", i, val)
- }
- }
-}
-
-func TestBadProof(t *testing.T) {
- trie, vals := randomTrie(800)
- root := trie.Hash()
- for i, prover := range makeProvers(trie) {
- for _, kv := range vals {
- proof := prover(kv.k)
- if proof == nil {
- t.Fatalf("prover %d: nil proof", i)
- }
- key := proof.Keys()[mrand.Intn(proof.Len())]
- val, _ := proof.Get(key)
- proof.Delete(key)
-
- mutateByte(val)
- proof.Put(crypto.Keccak256(val), val)
-
- if _, _, err := VerifyProof(root, kv.k, proof); err == nil {
- t.Fatalf("prover %d: expected proof to fail for key %x", i, kv.k)
- }
- }
- }
-}
-
-// Tests that missing keys can also be proven. The test explicitly uses a single
-// entry trie and checks for missing keys both before and after the single entry.
-func TestMissingKeyProof(t *testing.T) {
- trie := new(Trie)
- updateString(trie, "k", "v")
-
- for i, key := range []string{"a", "j", "l", "z"} {
- proof := ethdb.NewMemDatabase()
- trie.Prove([]byte(key), 0, proof)
-
- if proof.Len() != 1 {
- t.Errorf("test %d: proof should have one element", i)
- }
- val, _, err := VerifyProof(trie.Hash(), []byte(key), proof)
- if err != nil {
- t.Fatalf("test %d: failed to verify proof: %v\nraw proof: %x", i, err, proof)
- }
- if val != nil {
- t.Fatalf("test %d: verified value mismatch: have %x, want nil", i, val)
- }
- }
-}
-
-// mutateByte changes one byte in b.
-func mutateByte(b []byte) {
- for r := mrand.Intn(len(b)); ; {
- new := byte(mrand.Intn(255))
- if new != b[r] {
- b[r] = new
- break
- }
- }
-}
-
-func BenchmarkProve(b *testing.B) {
- trie, vals := randomTrie(100)
- var keys []string
- for k := range vals {
- keys = append(keys, k)
- }
-
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- kv := vals[keys[i%len(keys)]]
- proofs := ethdb.NewMemDatabase()
- if trie.Prove(kv.k, 0, proofs); len(proofs.Keys()) == 0 {
- b.Fatalf("zero length proof for %x", kv.k)
- }
- }
-}
-
-func BenchmarkVerifyProof(b *testing.B) {
- trie, vals := randomTrie(100)
- root := trie.Hash()
- var keys []string
- var proofs []*ethdb.MemDatabase
- for k := range vals {
- keys = append(keys, k)
- proof := ethdb.NewMemDatabase()
- trie.Prove([]byte(k), 0, proof)
- proofs = append(proofs, proof)
- }
-
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- im := i % len(keys)
- if _, _, err := VerifyProof(root, []byte(keys[im]), proofs[im]); err != nil {
- b.Fatalf("key %x: %v", keys[im], err)
- }
- }
-}
-
-func randomTrie(n int) (*Trie, map[string]*kv) {
- trie := new(Trie)
- vals := make(map[string]*kv)
- for i := byte(0); i < 100; i++ {
- value := &kv{common.LeftPadBytes([]byte{i}, 32), []byte{i}, false}
- value2 := &kv{common.LeftPadBytes([]byte{i + 10}, 32), []byte{i}, false}
- trie.Update(value.k, value.v)
- trie.Update(value2.k, value2.v)
- vals[string(value.k)] = value
- vals[string(value2.k)] = value2
- }
- for i := 0; i < n; i++ {
- value := &kv{randBytes(32), randBytes(20), false}
- trie.Update(value.k, value.v)
- vals[string(value.k)] = value
- }
- return trie, vals
-}
-
-func randBytes(n int) []byte {
- r := make([]byte, n)
- crand.Read(r)
- return r
-}
diff --git a/internal/trie/secure_trie.go b/internal/trie/secure_trie.go
deleted file mode 100644
index 6a50cfd5a..000000000
--- a/internal/trie/secure_trie.go
+++ /dev/null
@@ -1,203 +0,0 @@
-// 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 trie
-
-import (
- "fmt"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/log"
-)
-
-// SecureTrie wraps a trie with key hashing. In a secure trie, all
-// access operations hash the key using keccak256. This prevents
-// calling code from creating long chains of nodes that
-// increase the access time.
-//
-// Contrary to a regular trie, a SecureTrie can only be created with
-// New and must have an attached database. The database also stores
-// the preimage of each key.
-//
-// SecureTrie is not safe for concurrent use.
-type SecureTrie struct {
- trie Trie
- hashKeyBuf [common.HashLength]byte
- secKeyCache map[string][]byte
- secKeyCacheOwner *SecureTrie // Pointer to self, replace the key cache on mismatch
-}
-
-// NewSecure creates a trie with an existing root node from a backing database
-// and optional intermediate in-memory node pool.
-//
-// If root is the zero hash or the sha3 hash of an empty string, the
-// trie is initially empty. Otherwise, New will panic if db is nil
-// and returns MissingNodeError if the root node cannot be found.
-//
-// Accessing the trie loads nodes from the database or node pool on demand.
-// Loaded nodes are kept around until their 'cache generation' expires.
-// A new cache generation is created by each call to Commit.
-// cachelimit sets the number of past cache generations to keep.
-func NewSecure(root common.Hash, db *Database, cachelimit uint16) (*SecureTrie, error) {
- if db == nil {
- panic("trie.NewSecure called without a database")
- }
- trie, err := New(root, db)
- if err != nil {
- return nil, err
- }
- trie.SetCacheLimit(cachelimit)
- return &SecureTrie{trie: *trie}, nil
-}
-
-// 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 {
- res, err := t.TryGet(key)
- if err != nil {
- log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
- }
- return res
-}
-
-// TryGet returns the value for key stored in the trie.
-// The value bytes must not be modified by the caller.
-// If a node was not found in the database, a MissingNodeError is returned.
-func (t *SecureTrie) TryGet(key []byte) ([]byte, error) {
- return t.trie.TryGet(t.hashKey(key))
-}
-
-// Update associates key with value in the trie. Subsequent calls to
-// Get will return value. If value has length zero, any existing value
-// is deleted from the trie and calls to Get will return nil.
-//
-// The value bytes must not be modified by the caller while they are
-// stored in the trie.
-func (t *SecureTrie) Update(key, value []byte) {
- if err := t.TryUpdate(key, value); err != nil {
- log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
- }
-}
-
-// TryUpdate associates key with value in the trie. Subsequent calls to
-// Get will return value. If value has length zero, any existing value
-// is deleted from the trie and calls to Get will return nil.
-//
-// The value bytes must not be modified by the caller while they are
-// stored in the trie.
-//
-// If a node was not found in the database, a MissingNodeError is returned.
-func (t *SecureTrie) TryUpdate(key, value []byte) error {
- hk := t.hashKey(key)
- err := t.trie.TryUpdate(hk, value)
- if err != nil {
- return err
- }
- t.getSecKeyCache()[string(hk)] = common.CopyBytes(key)
- return nil
-}
-
-// Delete removes any existing value for key from the trie.
-func (t *SecureTrie) Delete(key []byte) {
- if err := t.TryDelete(key); err != nil {
- log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
- }
-}
-
-// TryDelete removes any existing value for key from the trie.
-// If a node was not found in the database, a MissingNodeError is returned.
-func (t *SecureTrie) TryDelete(key []byte) error {
- hk := t.hashKey(key)
- delete(t.getSecKeyCache(), string(hk))
- return t.trie.TryDelete(hk)
-}
-
-// GetKey returns the sha3 preimage of a hashed key that was
-// previously used to store a value.
-func (t *SecureTrie) GetKey(shaKey []byte) []byte {
- if key, ok := t.getSecKeyCache()[string(shaKey)]; ok {
- return key
- }
- key, _ := t.trie.db.preimage(common.BytesToHash(shaKey))
- return key
-}
-
-// Commit writes all nodes and the secure hash pre-images to the trie's database.
-// Nodes are stored with their sha3 hash as the key.
-//
-// Committing flushes nodes from memory. Subsequent Get calls will load nodes
-// from the database.
-func (t *SecureTrie) Commit(onleaf LeafCallback) (root common.Hash, err error) {
- // Write all the pre-images to the actual disk database
- if len(t.getSecKeyCache()) > 0 {
- t.trie.db.lock.Lock()
- for hk, key := range t.secKeyCache {
- t.trie.db.insertPreimage(common.BytesToHash([]byte(hk)), key)
- }
- t.trie.db.lock.Unlock()
-
- t.secKeyCache = make(map[string][]byte)
- }
- // Commit the trie to its intermediate node database
- return t.trie.Commit(onleaf)
-}
-
-// Hash returns the root hash of SecureTrie. It does not write to the
-// database and can be used even if the trie doesn't have one.
-func (t *SecureTrie) Hash() common.Hash {
- return t.trie.Hash()
-}
-
-// Root returns the root hash of SecureTrie.
-// Deprecated: use Hash instead.
-func (t *SecureTrie) Root() []byte {
- return t.trie.Root()
-}
-
-// Copy returns a copy of SecureTrie.
-func (t *SecureTrie) Copy() *SecureTrie {
- cpy := *t
- return &cpy
-}
-
-// NodeIterator returns an iterator that returns nodes of the underlying trie. Iteration
-// starts at the key after the given start key.
-func (t *SecureTrie) NodeIterator(start []byte) NodeIterator {
- return t.trie.NodeIterator(start)
-}
-
-// hashKey returns the hash of key as an ephemeral buffer.
-// The caller must not hold onto the return value because it will become
-// invalid on the next call to hashKey or secKey.
-func (t *SecureTrie) hashKey(key []byte) []byte {
- h := newHasher(0, 0, nil)
- h.sha.Reset()
- h.sha.Write(key)
- buf := h.sha.Sum(t.hashKeyBuf[:0])
- returnHasherToPool(h)
- return buf
-}
-
-// getSecKeyCache returns the current secure key cache, creating a new one if
-// ownership changed (i.e. the current secure trie is a copy of another owning
-// the actual cache).
-func (t *SecureTrie) getSecKeyCache() map[string][]byte {
- if t != t.secKeyCacheOwner {
- t.secKeyCacheOwner = t
- t.secKeyCache = make(map[string][]byte)
- }
- return t.secKeyCache
-}
diff --git a/internal/trie/secure_trie_test.go b/internal/trie/secure_trie_test.go
deleted file mode 100644
index 5df369f15..000000000
--- a/internal/trie/secure_trie_test.go
+++ /dev/null
@@ -1,145 +0,0 @@
-// 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 trie
-
-import (
- "bytes"
- "runtime"
- "sync"
- "testing"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/harmony-one/harmony/internal/db"
-)
-
-func newEmptySecure() *SecureTrie {
- trie, _ := NewSecure(common.Hash{}, NewDatabase(db.NewMemDatabase()), 0)
- return trie
-}
-
-// makeTestSecureTrie creates a large enough secure trie for testing.
-func makeTestSecureTrie() (*Database, *SecureTrie, map[string][]byte) {
- // Create an empty trie
- triedb := NewDatabase(db.NewMemDatabase())
-
- trie, _ := NewSecure(common.Hash{}, triedb, 0)
-
- // Fill it with some arbitrary data
- content := make(map[string][]byte)
- for i := byte(0); i < 255; i++ {
- // Map the same data under multiple keys
- key, val := common.LeftPadBytes([]byte{1, i}, 32), []byte{i}
- content[string(key)] = val
- trie.Update(key, val)
-
- key, val = common.LeftPadBytes([]byte{2, i}, 32), []byte{i}
- content[string(key)] = val
- trie.Update(key, val)
-
- // Add some other data to inflate the trie
- for j := byte(3); j < 13; j++ {
- key, val = common.LeftPadBytes([]byte{j, i}, 32), []byte{j, i}
- content[string(key)] = val
- trie.Update(key, val)
- }
- }
- trie.Commit(nil)
-
- // Return the generated trie
- return triedb, trie, content
-}
-
-func TestSecureDelete(t *testing.T) {
- trie := newEmptySecure()
- vals := []struct{ k, v string }{
- {"do", "verb"},
- {"ether", "wookiedoo"},
- {"horse", "stallion"},
- {"shaman", "horse"},
- {"doge", "coin"},
- {"ether", ""},
- {"dog", "puppy"},
- {"shaman", ""},
- }
- for _, val := range vals {
- if val.v != "" {
- trie.Update([]byte(val.k), []byte(val.v))
- } else {
- trie.Delete([]byte(val.k))
- }
- }
- hash := trie.Hash()
- exp := common.HexToHash("29b235a58c3c25ab83010c327d5932bcf05324b7d6b1185e650798034783ca9d")
- if hash != exp {
- t.Errorf("expected %x got %x", exp, hash)
- }
-}
-
-func TestSecureGetKey(t *testing.T) {
- trie := newEmptySecure()
- trie.Update([]byte("foo"), []byte("bar"))
-
- key := []byte("foo")
- value := []byte("bar")
- seckey := crypto.Keccak256(key)
-
- if !bytes.Equal(trie.Get(key), value) {
- t.Errorf("Get did not return bar")
- }
- if k := trie.GetKey(seckey); !bytes.Equal(k, key) {
- t.Errorf("GetKey returned %q, want %q", k, key)
- }
-}
-
-func TestSecureTrieConcurrency(t *testing.T) {
- // Create an initial trie and copy if for concurrent access
- _, trie, _ := makeTestSecureTrie()
-
- threads := runtime.NumCPU()
- tries := make([]*SecureTrie, threads)
- for i := 0; i < threads; i++ {
- cpy := *trie
- tries[i] = &cpy
- }
- // Start a batch of goroutines interactng with the trie
- pend := new(sync.WaitGroup)
- pend.Add(threads)
- for i := 0; i < threads; i++ {
- go func(index int) {
- defer pend.Done()
-
- for j := byte(0); j < 255; j++ {
- // Map the same data under multiple keys
- key, val := common.LeftPadBytes([]byte{byte(index), 1, j}, 32), []byte{j}
- tries[index].Update(key, val)
-
- key, val = common.LeftPadBytes([]byte{byte(index), 2, j}, 32), []byte{j}
- tries[index].Update(key, val)
-
- // Add some other data to inflate the trie
- for k := byte(3); k < 13; k++ {
- key, val = common.LeftPadBytes([]byte{byte(index), k, j}, 32), []byte{k, j}
- tries[index].Update(key, val)
- }
- }
- tries[index].Commit(nil)
- }(i)
- }
- // Wait for all threads to finish
- pend.Wait()
-}
diff --git a/internal/trie/sync.go b/internal/trie/sync.go
deleted file mode 100644
index 67dff5a8b..000000000
--- a/internal/trie/sync.go
+++ /dev/null
@@ -1,330 +0,0 @@
-// 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 trie
-
-import (
- "errors"
- "fmt"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/prque"
- "github.com/ethereum/go-ethereum/ethdb"
-)
-
-// ErrNotRequested is returned by the trie sync when it's requested to process a
-// node it did not request.
-var ErrNotRequested = errors.New("not requested")
-
-// ErrAlreadyProcessed is returned by the trie sync when it's requested to process a
-// node it already processed previously.
-var ErrAlreadyProcessed = errors.New("already processed")
-
-// request represents a scheduled or already in-flight state retrieval request.
-type request struct {
- hash common.Hash // Hash of the node data content to retrieve
- data []byte // Data content of the node, cached until all subtrees complete
- raw bool // Whether this is a raw entry (code) or a trie node
-
- parents []*request // Parent state nodes referencing this entry (notify all upon completion)
- depth int // Depth level within the trie the node is located to prioritise DFS
- deps int // Number of dependencies before allowed to commit this node
-
- callback LeafCallback // Callback to invoke if a leaf node it reached on this branch
-}
-
-// SyncResult is a simple list to return missing nodes along with their request
-// hashes.
-type SyncResult struct {
- Hash common.Hash // Hash of the originally unknown trie node
- Data []byte // Data content of the retrieved node
-}
-
-// syncMemBatch is an in-memory buffer of successfully downloaded but not yet
-// persisted data items.
-type syncMemBatch struct {
- batch map[common.Hash][]byte // In-memory membatch of recently completed items
- order []common.Hash // Order of completion to prevent out-of-order data loss
-}
-
-// newSyncMemBatch allocates a new memory-buffer for not-yet persisted trie nodes.
-func newSyncMemBatch() *syncMemBatch {
- return &syncMemBatch{
- batch: make(map[common.Hash][]byte),
- order: make([]common.Hash, 0, 256),
- }
-}
-
-// Sync is the main state trie synchronisation scheduler, which provides yet
-// unknown trie hashes to retrieve, accepts node data associated with said hashes
-// and reconstructs the trie step by step until all is done.
-type Sync struct {
- database DatabaseReader // Persistent database to check for existing entries
- membatch *syncMemBatch // Memory buffer to avoid frequent database writes
- requests map[common.Hash]*request // Pending requests pertaining to a key hash
- queue *prque.Prque // Priority queue with the pending requests
-}
-
-// NewSync creates a new trie data download scheduler.
-func NewSync(root common.Hash, database DatabaseReader, callback LeafCallback) *Sync {
- ts := &Sync{
- database: database,
- membatch: newSyncMemBatch(),
- requests: make(map[common.Hash]*request),
- queue: prque.New(nil),
- }
- ts.AddSubTrie(root, 0, common.Hash{}, callback)
- return ts
-}
-
-// AddSubTrie registers a new trie to the sync code, rooted at the designated parent.
-func (s *Sync) AddSubTrie(root common.Hash, depth int, parent common.Hash, callback LeafCallback) {
- // Short circuit if the trie is empty or already known
- if root == emptyRoot {
- return
- }
- if _, ok := s.membatch.batch[root]; ok {
- return
- }
- key := root.Bytes()
- blob, _ := s.database.Get(key)
- if local, err := decodeNode(key, blob, 0); local != nil && err == nil {
- return
- }
- // Assemble the new sub-trie sync request
- req := &request{
- hash: root,
- depth: depth,
- callback: callback,
- }
- // If this sub-trie has a designated parent, link them together
- if parent != (common.Hash{}) {
- ancestor := s.requests[parent]
- if ancestor == nil {
- panic(fmt.Sprintf("sub-trie ancestor not found: %x", parent))
- }
- ancestor.deps++
- req.parents = append(req.parents, ancestor)
- }
- s.schedule(req)
-}
-
-// AddRawEntry schedules the direct retrieval of a state entry that should not be
-// interpreted as a trie node, but rather accepted and stored into the database
-// as is. This method's goal is to support misc state metadata retrievals (e.g.
-// contract code).
-func (s *Sync) AddRawEntry(hash common.Hash, depth int, parent common.Hash) {
- // Short circuit if the entry is empty or already known
- if hash == emptyState {
- return
- }
- if _, ok := s.membatch.batch[hash]; ok {
- return
- }
- if ok, _ := s.database.Has(hash.Bytes()); ok {
- return
- }
- // Assemble the new sub-trie sync request
- req := &request{
- hash: hash,
- raw: true,
- depth: depth,
- }
- // If this sub-trie has a designated parent, link them together
- if parent != (common.Hash{}) {
- ancestor := s.requests[parent]
- if ancestor == nil {
- panic(fmt.Sprintf("raw-entry ancestor not found: %x", parent))
- }
- ancestor.deps++
- req.parents = append(req.parents, ancestor)
- }
- s.schedule(req)
-}
-
-// Missing retrieves the known missing nodes from the trie for retrieval.
-func (s *Sync) Missing(max int) []common.Hash {
- requests := []common.Hash{}
- for !s.queue.Empty() && (max == 0 || len(requests) < max) {
- requests = append(requests, s.queue.PopItem().(common.Hash))
- }
- return requests
-}
-
-// Process injects a batch of retrieved trie nodes data, returning if something
-// was committed to the database and also the index of an entry if processing of
-// it failed.
-func (s *Sync) Process(results []SyncResult) (bool, int, error) {
- committed := false
-
- for i, item := range results {
- // If the item was not requested, bail out
- request := s.requests[item.Hash]
- if request == nil {
- return committed, i, ErrNotRequested
- }
- if request.data != nil {
- return committed, i, ErrAlreadyProcessed
- }
- // If the item is a raw entry request, commit directly
- if request.raw {
- request.data = item.Data
- s.commit(request)
- committed = true
- continue
- }
- // Decode the node data content and update the request
- node, err := decodeNode(item.Hash[:], item.Data, 0)
- if err != nil {
- return committed, i, err
- }
- request.data = item.Data
-
- // Create and schedule a request for all the children nodes
- requests, err := s.children(request, node)
- if err != nil {
- return committed, i, err
- }
- if len(requests) == 0 && request.deps == 0 {
- s.commit(request)
- committed = true
- continue
- }
- request.deps += len(requests)
- for _, child := range requests {
- s.schedule(child)
- }
- }
- return committed, 0, nil
-}
-
-// Commit flushes the data stored in the internal membatch out to persistent
-// storage, returning the number of items written and any occurred error.
-func (s *Sync) Commit(dbw ethdb.Putter) (int, error) {
- // Dump the membatch into a database dbw
- for i, key := range s.membatch.order {
- if err := dbw.Put(key[:], s.membatch.batch[key]); err != nil {
- return i, err
- }
- }
- written := len(s.membatch.order)
-
- // Drop the membatch data and return
- s.membatch = newSyncMemBatch()
- return written, nil
-}
-
-// Pending returns the number of state entries currently pending for download.
-func (s *Sync) Pending() int {
- return len(s.requests)
-}
-
-// schedule inserts a new state retrieval request into the fetch queue. If there
-// is already a pending request for this node, the new request will be discarded
-// and only a parent reference added to the old one.
-func (s *Sync) schedule(req *request) {
- // If we're already requesting this node, add a new reference and stop
- if old, ok := s.requests[req.hash]; ok {
- old.parents = append(old.parents, req.parents...)
- return
- }
- // Schedule the request for future retrieval
- s.queue.Push(req.hash, int64(req.depth))
- s.requests[req.hash] = req
-}
-
-// children retrieves all the missing children of a state trie entry for future
-// retrieval scheduling.
-func (s *Sync) children(req *request, object node) ([]*request, error) {
- // Gather all the children of the node, irrelevant whether known or not
- type child struct {
- node node
- depth int
- }
- children := []child{}
-
- switch node := (object).(type) {
- case *shortNode:
- children = []child{{
- node: node.Val,
- depth: req.depth + len(node.Key),
- }}
- case *fullNode:
- for i := 0; i < 17; i++ {
- if node.Children[i] != nil {
- children = append(children, child{
- node: node.Children[i],
- depth: req.depth + 1,
- })
- }
- }
- default:
- panic(fmt.Sprintf("unknown node: %+v", node))
- }
- // Iterate over the children, and request all unknown ones
- requests := make([]*request, 0, len(children))
- for _, child := range children {
- // Notify any external watcher of a new key/value node
- if req.callback != nil {
- if node, ok := (child.node).(valueNode); ok {
- if err := req.callback(node, req.hash); err != nil {
- return nil, err
- }
- }
- }
- // If the child references another node, resolve or schedule
- if node, ok := (child.node).(hashNode); ok {
- // Try to resolve the node from the local database
- hash := common.BytesToHash(node)
- if _, ok := s.membatch.batch[hash]; ok {
- continue
- }
- if ok, _ := s.database.Has(node); ok {
- continue
- }
- // Locally unknown node, schedule for retrieval
- requests = append(requests, &request{
- hash: hash,
- parents: []*request{req},
- depth: child.depth,
- callback: req.callback,
- })
- }
- }
- return requests, nil
-}
-
-// commit finalizes a retrieval request and stores it into the membatch. If any
-// of the referencing parent requests complete due to this commit, they are also
-// committed themselves.
-func (s *Sync) commit(req *request) (err error) {
- // Write the node content to the membatch
- s.membatch.batch[req.hash] = req.data
- s.membatch.order = append(s.membatch.order, req.hash)
-
- delete(s.requests, req.hash)
-
- // Check all parents for completion
- for _, parent := range req.parents {
- parent.deps--
- if parent.deps == 0 {
- if err := s.commit(parent); err != nil {
- return err
- }
- }
- }
- return nil
-}
diff --git a/internal/trie/sync_test.go b/internal/trie/sync_test.go
deleted file mode 100644
index f2d9c5504..000000000
--- a/internal/trie/sync_test.go
+++ /dev/null
@@ -1,358 +0,0 @@
-// 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 trie
-
-import (
- "bytes"
- "testing"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/harmony-one/harmony/internal/db"
-)
-
-// makeTestTrie create a sample test trie to test node-wise reconstruction.
-func makeTestTrie() (*Database, *Trie, map[string][]byte) {
- // Create an empty trie
- triedb := NewDatabase(db.NewMemDatabase())
- trie, _ := New(common.Hash{}, triedb)
-
- // Fill it with some arbitrary data
- content := make(map[string][]byte)
- for i := byte(0); i < 255; i++ {
- // Map the same data under multiple keys
- key, val := common.LeftPadBytes([]byte{1, i}, 32), []byte{i}
- content[string(key)] = val
- trie.Update(key, val)
-
- key, val = common.LeftPadBytes([]byte{2, i}, 32), []byte{i}
- content[string(key)] = val
- trie.Update(key, val)
-
- // Add some other data to inflate the trie
- for j := byte(3); j < 13; j++ {
- key, val = common.LeftPadBytes([]byte{j, i}, 32), []byte{j, i}
- content[string(key)] = val
- trie.Update(key, val)
- }
- }
- trie.Commit(nil)
-
- // Return the generated trie
- return triedb, trie, content
-}
-
-// checkTrieContents cross references a reconstructed trie with an expected data
-// content map.
-func checkTrieContents(t *testing.T, db *Database, root []byte, content map[string][]byte) {
- // Check root availability and trie contents
- trie, err := New(common.BytesToHash(root), db)
- if err != nil {
- t.Fatalf("failed to create trie at %x: %v", root, err)
- }
- if err := checkTrieConsistency(db, common.BytesToHash(root)); err != nil {
- t.Fatalf("inconsistent trie at %x: %v", root, err)
- }
- for key, val := range content {
- if have := trie.Get([]byte(key)); !bytes.Equal(have, val) {
- t.Errorf("entry %x: content mismatch: have %x, want %x", key, have, val)
- }
- }
-}
-
-// checkTrieConsistency checks that all nodes in a trie are indeed present.
-func checkTrieConsistency(db *Database, root common.Hash) error {
- // Create and iterate a trie rooted in a subnode
- trie, err := New(root, db)
- if err != nil {
- return nil // Consider a non existent state consistent
- }
- it := trie.NodeIterator(nil)
- for it.Next(true) {
- }
- return it.Error()
-}
-
-// Tests that an empty trie is not scheduled for syncing.
-func TestEmptySync(t *testing.T) {
- dbA := NewDatabase(db.NewMemDatabase())
- dbB := NewDatabase(db.NewMemDatabase())
- emptyA, _ := New(common.Hash{}, dbA)
- emptyB, _ := New(emptyRoot, dbB)
-
- for i, trie := range []*Trie{emptyA, emptyB} {
- if req := NewSync(trie.Hash(), db.NewMemDatabase(), nil).Missing(1); len(req) != 0 {
- t.Errorf("test %d: content requested for empty trie: %v", i, req)
- }
- }
-}
-
-// Tests that given a root hash, a trie can sync iteratively on a single thread,
-// requesting retrieval tasks and returning all of them in one go.
-func TestIterativeSyncIndividual(t *testing.T) { testIterativeSync(t, 1) }
-func TestIterativeSyncBatched(t *testing.T) { testIterativeSync(t, 100) }
-
-func testIterativeSync(t *testing.T, batch int) {
- // Create a random trie to copy
- srcDb, srcTrie, srcData := makeTestTrie()
-
- // Create a destination trie and sync with the scheduler
- diskdb := db.NewMemDatabase()
- triedb := NewDatabase(diskdb)
- sched := NewSync(srcTrie.Hash(), diskdb, nil)
-
- queue := append([]common.Hash{}, sched.Missing(batch)...)
- for len(queue) > 0 {
- results := make([]SyncResult, len(queue))
- for i, hash := range queue {
- data, err := srcDb.Node(hash)
- if err != nil {
- t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
- }
- results[i] = SyncResult{hash, data}
- }
- if _, index, err := sched.Process(results); err != nil {
- t.Fatalf("failed to process result #%d: %v", index, err)
- }
- if index, err := sched.Commit(diskdb); err != nil {
- t.Fatalf("failed to commit data #%d: %v", index, err)
- }
- queue = append(queue[:0], sched.Missing(batch)...)
- }
- // Cross check that the two tries are in sync
- checkTrieContents(t, triedb, srcTrie.Root(), srcData)
-}
-
-// Tests that the trie scheduler can correctly reconstruct the state even if only
-// partial results are returned, and the others sent only later.
-func TestIterativeDelayedSync(t *testing.T) {
- // Create a random trie to copy
- srcDb, srcTrie, srcData := makeTestTrie()
-
- // Create a destination trie and sync with the scheduler
- diskdb := db.NewMemDatabase()
- triedb := NewDatabase(diskdb)
- sched := NewSync(srcTrie.Hash(), diskdb, nil)
-
- queue := append([]common.Hash{}, sched.Missing(10000)...)
- for len(queue) > 0 {
- // Sync only half of the scheduled nodes
- results := make([]SyncResult, len(queue)/2+1)
- for i, hash := range queue[:len(results)] {
- data, err := srcDb.Node(hash)
- if err != nil {
- t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
- }
- results[i] = SyncResult{hash, data}
- }
- if _, index, err := sched.Process(results); err != nil {
- t.Fatalf("failed to process result #%d: %v", index, err)
- }
- if index, err := sched.Commit(diskdb); err != nil {
- t.Fatalf("failed to commit data #%d: %v", index, err)
- }
- queue = append(queue[len(results):], sched.Missing(10000)...)
- }
- // Cross check that the two tries are in sync
- checkTrieContents(t, triedb, srcTrie.Root(), srcData)
-}
-
-// Tests that given a root hash, a trie can sync iteratively on a single thread,
-// requesting retrieval tasks and returning all of them in one go, however in a
-// random order.
-func TestIterativeRandomSyncIndividual(t *testing.T) { testIterativeRandomSync(t, 1) }
-func TestIterativeRandomSyncBatched(t *testing.T) { testIterativeRandomSync(t, 100) }
-
-func testIterativeRandomSync(t *testing.T, batch int) {
- // Create a random trie to copy
- srcDb, srcTrie, srcData := makeTestTrie()
-
- // Create a destination trie and sync with the scheduler
- diskdb := db.NewMemDatabase()
- triedb := NewDatabase(diskdb)
- sched := NewSync(srcTrie.Hash(), diskdb, nil)
-
- queue := make(map[common.Hash]struct{})
- for _, hash := range sched.Missing(batch) {
- queue[hash] = struct{}{}
- }
- for len(queue) > 0 {
- // Fetch all the queued nodes in a random order
- results := make([]SyncResult, 0, len(queue))
- for hash := range queue {
- data, err := srcDb.Node(hash)
- if err != nil {
- t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
- }
- results = append(results, SyncResult{hash, data})
- }
- // Feed the retrieved results back and queue new tasks
- if _, index, err := sched.Process(results); err != nil {
- t.Fatalf("failed to process result #%d: %v", index, err)
- }
- if index, err := sched.Commit(diskdb); err != nil {
- t.Fatalf("failed to commit data #%d: %v", index, err)
- }
- queue = make(map[common.Hash]struct{})
- for _, hash := range sched.Missing(batch) {
- queue[hash] = struct{}{}
- }
- }
- // Cross check that the two tries are in sync
- checkTrieContents(t, triedb, srcTrie.Root(), srcData)
-}
-
-// Tests that the trie scheduler can correctly reconstruct the state even if only
-// partial results are returned (Even those randomly), others sent only later.
-func TestIterativeRandomDelayedSync(t *testing.T) {
- // Create a random trie to copy
- srcDb, srcTrie, srcData := makeTestTrie()
-
- // Create a destination trie and sync with the scheduler
- diskdb := db.NewMemDatabase()
- triedb := NewDatabase(diskdb)
- sched := NewSync(srcTrie.Hash(), diskdb, nil)
-
- queue := make(map[common.Hash]struct{})
- for _, hash := range sched.Missing(10000) {
- queue[hash] = struct{}{}
- }
- for len(queue) > 0 {
- // Sync only half of the scheduled nodes, even those in random order
- results := make([]SyncResult, 0, len(queue)/2+1)
- for hash := range queue {
- data, err := srcDb.Node(hash)
- if err != nil {
- t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
- }
- results = append(results, SyncResult{hash, data})
-
- if len(results) >= cap(results) {
- break
- }
- }
- // Feed the retrieved results back and queue new tasks
- if _, index, err := sched.Process(results); err != nil {
- t.Fatalf("failed to process result #%d: %v", index, err)
- }
- if index, err := sched.Commit(diskdb); err != nil {
- t.Fatalf("failed to commit data #%d: %v", index, err)
- }
- for _, result := range results {
- delete(queue, result.Hash)
- }
- for _, hash := range sched.Missing(10000) {
- queue[hash] = struct{}{}
- }
- }
- // Cross check that the two tries are in sync
- checkTrieContents(t, triedb, srcTrie.Root(), srcData)
-}
-
-// Tests that a trie sync will not request nodes multiple times, even if they
-// have such references.
-func TestDuplicateAvoidanceSync(t *testing.T) {
- // Create a random trie to copy
- srcDb, srcTrie, srcData := makeTestTrie()
-
- // Create a destination trie and sync with the scheduler
- diskdb := db.NewMemDatabase()
- triedb := NewDatabase(diskdb)
- sched := NewSync(srcTrie.Hash(), diskdb, nil)
-
- queue := append([]common.Hash{}, sched.Missing(0)...)
- requested := make(map[common.Hash]struct{})
-
- for len(queue) > 0 {
- results := make([]SyncResult, len(queue))
- for i, hash := range queue {
- data, err := srcDb.Node(hash)
- if err != nil {
- t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
- }
- if _, ok := requested[hash]; ok {
- t.Errorf("hash %x already requested once", hash)
- }
- requested[hash] = struct{}{}
-
- results[i] = SyncResult{hash, data}
- }
- if _, index, err := sched.Process(results); err != nil {
- t.Fatalf("failed to process result #%d: %v", index, err)
- }
- if index, err := sched.Commit(diskdb); err != nil {
- t.Fatalf("failed to commit data #%d: %v", index, err)
- }
- queue = append(queue[:0], sched.Missing(0)...)
- }
- // Cross check that the two tries are in sync
- checkTrieContents(t, triedb, srcTrie.Root(), srcData)
-}
-
-// Tests that at any point in time during a sync, only complete sub-tries are in
-// the database.
-func TestIncompleteSync(t *testing.T) {
- // Create a random trie to copy
- srcDb, srcTrie, _ := makeTestTrie()
-
- // Create a destination trie and sync with the scheduler
- diskdb := db.NewMemDatabase()
- triedb := NewDatabase(diskdb)
- sched := NewSync(srcTrie.Hash(), diskdb, nil)
-
- added := []common.Hash{}
- queue := append([]common.Hash{}, sched.Missing(1)...)
- for len(queue) > 0 {
- // Fetch a batch of trie nodes
- results := make([]SyncResult, len(queue))
- for i, hash := range queue {
- data, err := srcDb.Node(hash)
- if err != nil {
- t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
- }
- results[i] = SyncResult{hash, data}
- }
- // Process each of the trie nodes
- if _, index, err := sched.Process(results); err != nil {
- t.Fatalf("failed to process result #%d: %v", index, err)
- }
- if index, err := sched.Commit(diskdb); err != nil {
- t.Fatalf("failed to commit data #%d: %v", index, err)
- }
- for _, result := range results {
- added = append(added, result.Hash)
- }
- // Check that all known sub-tries in the synced trie are complete
- for _, root := range added {
- if err := checkTrieConsistency(triedb, root); err != nil {
- t.Fatalf("trie inconsistent: %v", err)
- }
- }
- // Fetch the next batch to retrieve
- queue = append(queue[:0], sched.Missing(1)...)
- }
- // Sanity check that removing any node from the database is detected
- for _, node := range added[1:] {
- key := node.Bytes()
- value, _ := diskdb.Get(key)
-
- diskdb.Delete(key)
- if err := checkTrieConsistency(triedb, added[0]); err == nil {
- t.Fatalf("trie inconsistency not caught, missing: %x", key)
- }
- diskdb.Put(key, value)
- }
-}
diff --git a/internal/trie/trie.go b/internal/trie/trie.go
deleted file mode 100644
index af424d4ac..000000000
--- a/internal/trie/trie.go
+++ /dev/null
@@ -1,474 +0,0 @@
-// Copyright 2014 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Package trie implements Merkle Patricia Tries.
-package trie
-
-import (
- "bytes"
- "fmt"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/metrics"
-)
-
-var (
- // emptyRoot is the known root hash of an empty trie.
- emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
-
- // emptyState is the known hash of an empty state trie entry.
- emptyState = crypto.Keccak256Hash(nil)
-)
-
-var (
- cacheMissCounter = metrics.NewRegisteredCounter("trie/cachemiss", nil)
- cacheUnloadCounter = metrics.NewRegisteredCounter("trie/cacheunload", nil)
-)
-
-// CacheMisses retrieves a global counter measuring the number of cache misses
-// the trie had since process startup. This isn't useful for anything apart from
-// trie debugging purposes.
-func CacheMisses() int64 {
- return cacheMissCounter.Count()
-}
-
-// CacheUnloads retrieves a global counter measuring the number of cache unloads
-// the trie did since process startup. This isn't useful for anything apart from
-// trie debugging purposes.
-func CacheUnloads() int64 {
- return cacheUnloadCounter.Count()
-}
-
-// LeafCallback is a callback type invoked when a trie operation reaches a leaf
-// node. It's used by state sync and commit to allow handling external references
-// between account and storage tries.
-type LeafCallback func(leaf []byte, parent common.Hash) error
-
-// Trie is a Merkle Patricia Trie.
-// The zero value is an empty trie with no database.
-// Use New to create a trie that sits on top of a database.
-//
-// Trie is not safe for concurrent use.
-type Trie struct {
- db *Database
- root node
-
- // Cache generation values.
- // cachegen increases by one with each commit operation.
- // new nodes are tagged with the current generation and unloaded
- // when their generation is older than than cachegen-cachelimit.
- cachegen, cachelimit uint16
-}
-
-// SetCacheLimit sets the number of 'cache generations' to keep.
-// A cache generation is created by a call to Commit.
-func (t *Trie) SetCacheLimit(l uint16) {
- t.cachelimit = l
-}
-
-// newFlag returns the cache flag value for a newly created node.
-func (t *Trie) newFlag() nodeFlag {
- return nodeFlag{dirty: true, gen: t.cachegen}
-}
-
-// New creates a trie with an existing root node from db.
-//
-// If root is the zero hash or the sha3 hash of an empty string, the
-// trie is initially empty and does not require a database. Otherwise,
-// New will panic if db is nil and returns a MissingNodeError if 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) {
- if db == nil {
- panic("trie.New called without a database")
- }
- trie := &Trie{
- db: db,
- }
- if root != (common.Hash{}) && root != emptyRoot {
- rootnode, err := trie.resolveHash(root[:], nil)
- if err != nil {
- return nil, err
- }
- trie.root = rootnode
- }
- return trie, nil
-}
-
-// NodeIterator returns an iterator that returns nodes of the trie. Iteration starts at
-// the key after the given start key.
-func (t *Trie) NodeIterator(start []byte) NodeIterator {
- return newNodeIterator(t, start)
-}
-
-// Get returns the value for key stored in the trie.
-// The value bytes must not be modified by the caller.
-func (t *Trie) Get(key []byte) []byte {
- res, err := t.TryGet(key)
- if err != nil {
- log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
- }
- return res
-}
-
-// TryGet returns the value for key stored in the trie.
-// The value bytes must not be modified by the caller.
-// If a node was not found in the database, a MissingNodeError is returned.
-func (t *Trie) TryGet(key []byte) ([]byte, error) {
- key = keybytesToHex(key)
- value, newroot, didResolve, err := t.tryGet(t.root, key, 0)
- if err == nil && didResolve {
- t.root = newroot
- }
- return value, err
-}
-
-func (t *Trie) tryGet(origNode node, key []byte, pos int) (value []byte, newnode node, didResolve bool, err error) {
- switch n := (origNode).(type) {
- case nil:
- return nil, nil, false, nil
- case valueNode:
- return n, n, false, nil
- case *shortNode:
- if len(key)-pos < len(n.Key) || !bytes.Equal(n.Key, key[pos:pos+len(n.Key)]) {
- // key not found in trie
- return nil, n, false, nil
- }
- value, newnode, didResolve, err = t.tryGet(n.Val, key, pos+len(n.Key))
- if err == nil && didResolve {
- n = n.copy()
- n.Val = newnode
- n.flags.gen = t.cachegen
- }
- return value, n, didResolve, err
- case *fullNode:
- value, newnode, didResolve, err = t.tryGet(n.Children[key[pos]], key, pos+1)
- if err == nil && didResolve {
- n = n.copy()
- n.flags.gen = t.cachegen
- n.Children[key[pos]] = newnode
- }
- return value, n, didResolve, err
- case hashNode:
- child, err := t.resolveHash(n, key[:pos])
- if err != nil {
- return nil, n, true, err
- }
- value, newnode, _, err := t.tryGet(child, key, pos)
- return value, newnode, true, err
- default:
- panic(fmt.Sprintf("%T: invalid node: %v", origNode, origNode))
- }
-}
-
-// Update associates key with value in the trie. Subsequent calls to
-// Get will return value. If value has length zero, any existing value
-// is deleted from the trie and calls to Get will return nil.
-//
-// The value bytes must not be modified by the caller while they are
-// stored in the trie.
-func (t *Trie) Update(key, value []byte) {
- if err := t.TryUpdate(key, value); err != nil {
- log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
- }
-}
-
-// TryUpdate associates key with value in the trie. Subsequent calls to
-// Get will return value. If value has length zero, any existing value
-// is deleted from the trie and calls to Get will return nil.
-//
-// The value bytes must not be modified by the caller while they are
-// stored in the trie.
-//
-// If a node was not found in the database, a MissingNodeError is returned.
-func (t *Trie) TryUpdate(key, value []byte) error {
- k := keybytesToHex(key)
- if len(value) != 0 {
- _, n, err := t.insert(t.root, nil, k, valueNode(value))
- if err != nil {
- return err
- }
- t.root = n
- } else {
- _, n, err := t.delete(t.root, nil, k)
- if err != nil {
- return err
- }
- t.root = n
- }
- return nil
-}
-
-func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error) {
- if len(key) == 0 {
- if v, ok := n.(valueNode); ok {
- return !bytes.Equal(v, value.(valueNode)), value, nil
- }
- return true, value, nil
- }
- switch n := n.(type) {
- case *shortNode:
- matchlen := prefixLen(key, n.Key)
- // If the whole key matches, keep this short node as is
- // and only update the value.
- if matchlen == len(n.Key) {
- dirty, nn, err := t.insert(n.Val, append(prefix, key[:matchlen]...), key[matchlen:], value)
- if !dirty || err != nil {
- return false, n, err
- }
- return true, &shortNode{n.Key, nn, t.newFlag()}, nil
- }
- // Otherwise branch out at the index where they differ.
- branch := &fullNode{flags: t.newFlag()}
- var err error
- _, branch.Children[n.Key[matchlen]], err = t.insert(nil, append(prefix, n.Key[:matchlen+1]...), n.Key[matchlen+1:], n.Val)
- if err != nil {
- return false, nil, err
- }
- _, branch.Children[key[matchlen]], err = t.insert(nil, append(prefix, key[:matchlen+1]...), key[matchlen+1:], value)
- if err != nil {
- return false, nil, err
- }
- // Replace this shortNode with the branch if it occurs at index 0.
- if matchlen == 0 {
- return true, branch, nil
- }
- // Otherwise, replace it with a short node leading up to the branch.
- return true, &shortNode{key[:matchlen], branch, t.newFlag()}, nil
-
- case *fullNode:
- dirty, nn, err := t.insert(n.Children[key[0]], append(prefix, key[0]), key[1:], value)
- if !dirty || err != nil {
- return false, n, err
- }
- n = n.copy()
- n.flags = t.newFlag()
- n.Children[key[0]] = nn
- return true, n, nil
-
- case nil:
- return true, &shortNode{key, value, t.newFlag()}, nil
-
- case hashNode:
- // We've hit a part of the trie that isn't loaded yet. Load
- // the node and insert into it. This leaves all child nodes on
- // the path to the value in the trie.
- rn, err := t.resolveHash(n, prefix)
- if err != nil {
- return false, nil, err
- }
- dirty, nn, err := t.insert(rn, prefix, key, value)
- if !dirty || err != nil {
- return false, rn, err
- }
- return true, nn, nil
-
- default:
- panic(fmt.Sprintf("%T: invalid node: %v", n, n))
- }
-}
-
-// Delete removes any existing value for key from the trie.
-func (t *Trie) Delete(key []byte) {
- if err := t.TryDelete(key); err != nil {
- log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
- }
-}
-
-// TryDelete removes any existing value for key from the trie.
-// If a node was not found in the database, a MissingNodeError is returned.
-func (t *Trie) TryDelete(key []byte) error {
- k := keybytesToHex(key)
- _, n, err := t.delete(t.root, nil, k)
- if err != nil {
- return err
- }
- t.root = n
- return nil
-}
-
-// 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, prefix, key []byte) (bool, node, error) {
- switch n := n.(type) {
- case *shortNode:
- matchlen := prefixLen(key, n.Key)
- if matchlen < len(n.Key) {
- return false, n, nil // don't replace n on mismatch
- }
- if matchlen == len(key) {
- return true, nil, nil // remove n entirely for whole matches
- }
- // The key is longer than n.Key. Remove the remaining suffix
- // 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.
- dirty, child, err := t.delete(n.Val, append(prefix, key[:len(n.Key)]...), key[len(n.Key):])
- if !dirty || err != nil {
- return false, n, err
- }
- switch child := child.(type) {
- case *shortNode:
- // Deleting from the subtrie reduced it to another
- // short node. Merge the nodes to avoid creating a
- // shortNode{..., shortNode{...}}. Use concat (which
- // always creates a new slice) instead of append to
- // avoid modifying n.Key since it might be shared with
- // other nodes.
- return true, &shortNode{concat(n.Key, child.Key...), child.Val, t.newFlag()}, nil
- default:
- return true, &shortNode{n.Key, child, t.newFlag()}, nil
- }
-
- case *fullNode:
- dirty, nn, err := t.delete(n.Children[key[0]], append(prefix, key[0]), key[1:])
- if !dirty || err != nil {
- return false, n, err
- }
- n = n.copy()
- n.flags = t.newFlag()
- n.Children[key[0]] = nn
-
- // 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
- // before deletion (otherwise it would not be a full node) n
- // can never be reduced to nil.
- //
- // When the loop is done, pos contains the index of the single
- // value that is left in n or -2 if n contains at least two
- // values.
- pos := -1
- for i, cld := range &n.Children {
- if cld != nil {
- if pos == -1 {
- pos = i
- } else {
- pos = -2
- break
- }
- }
- }
- if pos >= 0 {
- if pos != 16 {
- // If the remaining entry is a short node, it replaces
- // n and its key gets the missing nibble tacked to the
- // front. This avoids creating an invalid
- // shortNode{..., shortNode{...}}. Since the entry
- // might not be loaded yet, resolve it just for this
- // check.
- cnode, err := t.resolve(n.Children[pos], prefix)
- if err != nil {
- return false, nil, err
- }
- if cnode, ok := cnode.(*shortNode); ok {
- k := append([]byte{byte(pos)}, cnode.Key...)
- return true, &shortNode{k, cnode.Val, t.newFlag()}, nil
- }
- }
- // Otherwise, n is replaced by a one-nibble short node
- // containing the child.
- return true, &shortNode{[]byte{byte(pos)}, n.Children[pos], t.newFlag()}, nil
- }
- // n still contains at least two values and cannot be reduced.
- return true, n, nil
-
- case valueNode:
- return true, nil, nil
-
- case nil:
- return false, nil, nil
-
- case hashNode:
- // We've hit a part of the trie that isn't loaded yet. Load
- // the node and delete from it. This leaves all child nodes on
- // the path to the value in the trie.
- rn, err := t.resolveHash(n, prefix)
- if err != nil {
- return false, nil, err
- }
- dirty, nn, err := t.delete(rn, prefix, key)
- if !dirty || err != nil {
- return false, rn, err
- }
- return true, nn, nil
-
- default:
- panic(fmt.Sprintf("%T: invalid node: %v (%v)", n, n, key))
- }
-}
-
-func concat(s1 []byte, s2 ...byte) []byte {
- r := make([]byte, len(s1)+len(s2))
- copy(r, s1)
- copy(r[len(s1):], s2)
- return r
-}
-
-func (t *Trie) resolve(n node, prefix []byte) (node, error) {
- if n, ok := n.(hashNode); ok {
- return t.resolveHash(n, prefix)
- }
- return n, nil
-}
-
-func (t *Trie) resolveHash(n hashNode, prefix []byte) (node, error) {
- cacheMissCounter.Inc(1)
-
- hash := common.BytesToHash(n)
- if node := t.db.node(hash, t.cachegen); node != nil {
- return node, nil
- }
- return nil, &MissingNodeError{NodeHash: hash, Path: prefix}
-}
-
-// Root returns the root hash of the trie.
-// Deprecated: use Hash instead.
-func (t *Trie) Root() []byte { return t.Hash().Bytes() }
-
-// Hash returns the root hash of the trie. It does not write to the
-// database and can be used even if the trie doesn't have one.
-func (t *Trie) Hash() common.Hash {
- hash, cached, _ := t.hashRoot(nil, nil)
- t.root = cached
- return common.BytesToHash(hash.(hashNode))
-}
-
-// Commit writes all nodes to the trie's memory database, tracking the internal
-// and external (for account tries) references.
-func (t *Trie) Commit(onleaf LeafCallback) (root common.Hash, err error) {
- if t.db == nil {
- panic("commit called on trie with nil database")
- }
- hash, cached, err := t.hashRoot(t.db, onleaf)
- if err != nil {
- return common.Hash{}, err
- }
- t.root = cached
- t.cachegen++
- return common.BytesToHash(hash.(hashNode)), nil
-}
-
-func (t *Trie) hashRoot(db *Database, onleaf LeafCallback) (node, node, error) {
- if t.root == nil {
- return hashNode(emptyRoot.Bytes()), nil, nil
- }
- h := newHasher(t.cachegen, t.cachelimit, onleaf)
- defer returnHasherToPool(h)
- return h.hash(t.root, db, true)
-}
diff --git a/internal/trie/trie_test.go b/internal/trie/trie_test.go
deleted file mode 100644
index cff9a4c86..000000000
--- a/internal/trie/trie_test.go
+++ /dev/null
@@ -1,615 +0,0 @@
-// Copyright 2014 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package trie
-
-import (
- "bytes"
- "encoding/binary"
- "errors"
- "fmt"
- "io/ioutil"
- "math/big"
- "math/rand"
- "os"
- "reflect"
- "testing"
- "testing/quick"
-
- "github.com/davecgh/go-spew/spew"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/rlp"
- "github.com/harmony-one/harmony/internal/db"
-)
-
-func init() {
- spew.Config.Indent = " "
- spew.Config.DisableMethods = false
-}
-
-// Used for testing
-func newEmpty() *Trie {
- trie, _ := New(common.Hash{}, NewDatabase(db.NewMemDatabase()))
- return trie
-}
-
-func TestEmptyTrie(t *testing.T) {
- var trie Trie
- res := trie.Hash()
- exp := emptyRoot
- if res != common.Hash(exp) {
- t.Errorf("expected %x got %x", exp, res)
- }
-}
-
-func TestNull(t *testing.T) {
- var trie Trie
- key := make([]byte, 32)
- value := []byte("test")
- trie.Update(key, value)
- if !bytes.Equal(trie.Get(key), value) {
- t.Fatal("wrong value")
- }
-}
-
-func TestMissingRoot(t *testing.T) {
- trie, err := New(common.HexToHash("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"), NewDatabase(db.NewMemDatabase()))
- if trie != nil {
- t.Error("New returned non-nil trie for invalid root")
- }
- if _, ok := err.(*MissingNodeError); !ok {
- t.Errorf("New returned wrong error: %v", err)
- }
-}
-
-func TestMissingNodeDisk(t *testing.T) { testMissingNode(t, false) }
-func TestMissingNodeMemonly(t *testing.T) { testMissingNode(t, true) }
-
-func testMissingNode(t *testing.T, memonly bool) {
- diskdb := db.NewMemDatabase()
- triedb := NewDatabase(diskdb)
-
- trie, _ := New(common.Hash{}, triedb)
- updateString(trie, "120000", "qwerqwerqwerqwerqwerqwerqwerqwer")
- updateString(trie, "123456", "asdfasdfasdfasdfasdfasdfasdfasdf")
- root, _ := trie.Commit(nil)
- if !memonly {
- triedb.Commit(root, true)
- }
-
- trie, _ = New(root, triedb)
- _, err := trie.TryGet([]byte("120000"))
- if err != nil {
- t.Errorf("Unexpected error: %v", err)
- }
- trie, _ = New(root, triedb)
- _, err = trie.TryGet([]byte("120099"))
- if err != nil {
- t.Errorf("Unexpected error: %v", err)
- }
- trie, _ = New(root, triedb)
- _, err = trie.TryGet([]byte("123456"))
- if err != nil {
- t.Errorf("Unexpected error: %v", err)
- }
- trie, _ = New(root, triedb)
- err = trie.TryUpdate([]byte("120099"), []byte("zxcvzxcvzxcvzxcvzxcvzxcvzxcvzxcv"))
- if err != nil {
- t.Errorf("Unexpected error: %v", err)
- }
- trie, _ = New(root, triedb)
- err = trie.TryDelete([]byte("123456"))
- if err != nil {
- t.Errorf("Unexpected error: %v", err)
- }
-
- hash := common.HexToHash("0xe1d943cc8f061a0c0b98162830b970395ac9315654824bf21b73b891365262f9")
- if memonly {
- delete(triedb.nodes, hash)
- } else {
- diskdb.Delete(hash[:])
- }
-
- trie, _ = New(root, triedb)
- _, err = trie.TryGet([]byte("120000"))
- if _, ok := err.(*MissingNodeError); !ok {
- t.Errorf("Wrong error: %v", err)
- }
- trie, _ = New(root, triedb)
- _, err = trie.TryGet([]byte("120099"))
- if _, ok := err.(*MissingNodeError); !ok {
- t.Errorf("Wrong error: %v", err)
- }
- trie, _ = New(root, triedb)
- _, err = trie.TryGet([]byte("123456"))
- if err != nil {
- t.Errorf("Unexpected error: %v", err)
- }
- trie, _ = New(root, triedb)
- err = trie.TryUpdate([]byte("120099"), []byte("zxcv"))
- if _, ok := err.(*MissingNodeError); !ok {
- t.Errorf("Wrong error: %v", err)
- }
- trie, _ = New(root, triedb)
- err = trie.TryDelete([]byte("123456"))
- if _, ok := err.(*MissingNodeError); !ok {
- t.Errorf("Wrong error: %v", err)
- }
-}
-
-func TestInsert(t *testing.T) {
- trie := newEmpty()
-
- updateString(trie, "doe", "reindeer")
- updateString(trie, "dog", "puppy")
- updateString(trie, "dogglesworth", "cat")
-
- exp := common.HexToHash("8aad789dff2f538bca5d8ea56e8abe10f4c7ba3a5dea95fea4cd6e7c3a1168d3")
- root := trie.Hash()
- if root != exp {
- t.Errorf("exp %x got %x", exp, root)
- }
-
- trie = newEmpty()
- updateString(trie, "A", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
-
- exp = common.HexToHash("d23786fb4a010da3ce639d66d5e904a11dbc02746d1ce25029e53290cabf28ab")
- root, err := trie.Commit(nil)
- if err != nil {
- t.Fatalf("commit error: %v", err)
- }
- if root != exp {
- t.Errorf("exp %x got %x", exp, root)
- }
-}
-
-func TestGet(t *testing.T) {
- trie := newEmpty()
- updateString(trie, "doe", "reindeer")
- updateString(trie, "dog", "puppy")
- updateString(trie, "dogglesworth", "cat")
-
- for i := 0; i < 2; i++ {
- res := getString(trie, "dog")
- if !bytes.Equal(res, []byte("puppy")) {
- t.Errorf("expected puppy got %x", res)
- }
-
- unknown := getString(trie, "unknown")
- if unknown != nil {
- t.Errorf("expected nil got %x", unknown)
- }
-
- if i == 1 {
- return
- }
- trie.Commit(nil)
- }
-}
-
-func TestDelete(t *testing.T) {
- trie := newEmpty()
- vals := []struct{ k, v string }{
- {"do", "verb"},
- {"ether", "wookiedoo"},
- {"horse", "stallion"},
- {"shaman", "horse"},
- {"doge", "coin"},
- {"ether", ""},
- {"dog", "puppy"},
- {"shaman", ""},
- }
- for _, val := range vals {
- if val.v != "" {
- updateString(trie, val.k, val.v)
- } else {
- deleteString(trie, val.k)
- }
- }
-
- hash := trie.Hash()
- exp := common.HexToHash("5991bb8c6514148a29db676a14ac506cd2cd5775ace63c30a4fe457715e9ac84")
- if hash != exp {
- t.Errorf("expected %x got %x", exp, hash)
- }
-}
-
-func TestEmptyValues(t *testing.T) {
- trie := newEmpty()
-
- vals := []struct{ k, v string }{
- {"do", "verb"},
- {"ether", "wookiedoo"},
- {"horse", "stallion"},
- {"shaman", "horse"},
- {"doge", "coin"},
- {"ether", ""},
- {"dog", "puppy"},
- {"shaman", ""},
- }
- for _, val := range vals {
- updateString(trie, val.k, val.v)
- }
-
- hash := trie.Hash()
- exp := common.HexToHash("5991bb8c6514148a29db676a14ac506cd2cd5775ace63c30a4fe457715e9ac84")
- if hash != exp {
- t.Errorf("expected %x got %x", exp, hash)
- }
-}
-
-func TestReplication(t *testing.T) {
- trie := newEmpty()
- vals := []struct{ k, v string }{
- {"do", "verb"},
- {"ether", "wookiedoo"},
- {"horse", "stallion"},
- {"shaman", "horse"},
- {"doge", "coin"},
- {"dog", "puppy"},
- {"somethingveryoddindeedthis is", "myothernodedata"},
- }
- for _, val := range vals {
- updateString(trie, val.k, val.v)
- }
- exp, err := trie.Commit(nil)
- if err != nil {
- t.Fatalf("commit error: %v", err)
- }
-
- // create a new trie on top of the database and check that lookups work.
- trie2, err := New(exp, trie.db)
- if err != nil {
- t.Fatalf("can't recreate trie at %x: %v", exp, err)
- }
- for _, kv := range vals {
- if string(getString(trie2, kv.k)) != kv.v {
- t.Errorf("trie2 doesn't have %q => %q", kv.k, kv.v)
- }
- }
- hash, err := trie2.Commit(nil)
- if err != nil {
- t.Fatalf("commit error: %v", err)
- }
- if hash != exp {
- t.Errorf("root failure. expected %x got %x", exp, hash)
- }
-
- // perform some insertions on the new trie.
- vals2 := []struct{ k, v string }{
- {"do", "verb"},
- {"ether", "wookiedoo"},
- {"horse", "stallion"},
- // {"shaman", "horse"},
- // {"doge", "coin"},
- // {"ether", ""},
- // {"dog", "puppy"},
- // {"somethingveryoddindeedthis is", "myothernodedata"},
- // {"shaman", ""},
- }
- for _, val := range vals2 {
- updateString(trie2, val.k, val.v)
- }
- if hash := trie2.Hash(); hash != exp {
- t.Errorf("root failure. expected %x got %x", exp, hash)
- }
-}
-
-func TestLargeValue(t *testing.T) {
- trie := newEmpty()
- trie.Update([]byte("key1"), []byte{99, 99, 99, 99})
- trie.Update([]byte("key2"), bytes.Repeat([]byte{1}, 32))
- trie.Hash()
-}
-
-type countingDB struct {
- db.Database
- gets map[string]int
-}
-
-func (db *countingDB) Get(key []byte) ([]byte, error) {
- db.gets[string(key)]++
- return db.Database.Get(key)
-}
-
-// TestCacheUnload checks that decoded nodes are unloaded after a
-// certain number of commit operations.
-func TestCacheUnload(t *testing.T) {
- // Create test trie with two branches.
- trie := newEmpty()
- key1 := "---------------------------------"
- key2 := "---some other branch"
- updateString(trie, key1, "this is the branch of key1.")
- updateString(trie, key2, "this is the branch of key2.")
-
- root, _ := trie.Commit(nil)
- trie.db.Commit(root, true)
-
- // Commit the trie repeatedly and access key1.
- // The branch containing it is loaded from DB exactly two times:
- // in the 0th and 6th iteration.
- db := &countingDB{Database: trie.db.diskdb, gets: make(map[string]int)}
- trie, _ = New(root, NewDatabase(db))
- trie.SetCacheLimit(5)
- for i := 0; i < 12; i++ {
- getString(trie, key1)
- trie.Commit(nil)
- }
- // Check that it got loaded two times.
- for dbkey, count := range db.gets {
- if count != 2 {
- t.Errorf("db key %x loaded %d times, want %d times", []byte(dbkey), count, 2)
- }
- }
-}
-
-// randTest performs random trie operations.
-// Instances of this test are created by Generate.
-type randTest []randTestStep
-
-type randTestStep struct {
- op int
- key []byte // for opUpdate, opDelete, opGet
- value []byte // for opUpdate
- err error // for debugging
-}
-
-const (
- opUpdate = iota
- opDelete
- opGet
- opCommit
- opHash
- opReset
- opItercheckhash
- opCheckCacheInvariant
- opMax // boundary value, not an actual op
-)
-
-func (randTest) Generate(r *rand.Rand, size int) reflect.Value {
- var allKeys [][]byte
- genKey := func() []byte {
- if len(allKeys) < 2 || r.Intn(100) < 10 {
- // new key
- key := make([]byte, r.Intn(50))
- r.Read(key)
- allKeys = append(allKeys, key)
- return key
- }
- // use existing key
- return allKeys[r.Intn(len(allKeys))]
- }
-
- var steps randTest
- for i := 0; i < size; i++ {
- step := randTestStep{op: r.Intn(opMax)}
- switch step.op {
- case opUpdate:
- step.key = genKey()
- step.value = make([]byte, 8)
- binary.BigEndian.PutUint64(step.value, uint64(i))
- case opGet, opDelete:
- step.key = genKey()
- }
- steps = append(steps, step)
- }
- return reflect.ValueOf(steps)
-}
-
-func runRandTest(rt randTest) bool {
- triedb := NewDatabase(db.NewMemDatabase())
-
- tr, _ := New(common.Hash{}, triedb)
- values := make(map[string]string) // tracks content of the trie
-
- for i, step := range rt {
- switch step.op {
- case opUpdate:
- tr.Update(step.key, step.value)
- values[string(step.key)] = string(step.value)
- case opDelete:
- tr.Delete(step.key)
- delete(values, string(step.key))
- case opGet:
- v := tr.Get(step.key)
- want := values[string(step.key)]
- if string(v) != want {
- rt[i].err = fmt.Errorf("mismatch for key 0x%x, got 0x%x want 0x%x", step.key, v, want)
- }
- case opCommit:
- _, rt[i].err = tr.Commit(nil)
- case opHash:
- tr.Hash()
- case opReset:
- hash, err := tr.Commit(nil)
- if err != nil {
- rt[i].err = err
- return false
- }
- newtr, err := New(hash, triedb)
- if err != nil {
- rt[i].err = err
- return false
- }
- tr = newtr
- case opItercheckhash:
- checktr, _ := New(common.Hash{}, triedb)
- it := NewIterator(tr.NodeIterator(nil))
- for it.Next() {
- checktr.Update(it.Key, it.Value)
- }
- if tr.Hash() != checktr.Hash() {
- rt[i].err = fmt.Errorf("hash mismatch in opItercheckhash")
- }
- case opCheckCacheInvariant:
- rt[i].err = checkCacheInvariant(tr.root, nil, tr.cachegen, false, 0)
- }
- // Abort the test on error.
- if rt[i].err != nil {
- return false
- }
- }
- return true
-}
-
-func checkCacheInvariant(n, parent node, parentCachegen uint16, parentDirty bool, depth int) error {
- var children []node
- var flag nodeFlag
- switch n := n.(type) {
- case *shortNode:
- flag = n.flags
- children = []node{n.Val}
- case *fullNode:
- flag = n.flags
- children = n.Children[:]
- default:
- return nil
- }
-
- errorf := func(format string, args ...interface{}) error {
- msg := fmt.Sprintf(format, args...)
- msg += fmt.Sprintf("\nat depth %d node %s", depth, spew.Sdump(n))
- msg += fmt.Sprintf("parent: %s", spew.Sdump(parent))
- return errors.New(msg)
- }
- if flag.gen > parentCachegen {
- return errorf("cache invariant violation: %d > %d\n", flag.gen, parentCachegen)
- }
- if depth > 0 && !parentDirty && flag.dirty {
- return errorf("cache invariant violation: %d > %d\n", flag.gen, parentCachegen)
- }
- for _, child := range children {
- if err := checkCacheInvariant(child, n, flag.gen, flag.dirty, depth+1); err != nil {
- return err
- }
- }
- return nil
-}
-
-func TestRandom(t *testing.T) {
- if err := quick.Check(runRandTest, nil); err != nil {
- if cerr, ok := err.(*quick.CheckError); ok {
- t.Fatalf("random test iteration %d failed: %s", cerr.Count, spew.Sdump(cerr.In))
- }
- t.Fatal(err)
- }
-}
-
-func BenchmarkGet(b *testing.B) { benchGet(b, false) }
-func BenchmarkGetDB(b *testing.B) { benchGet(b, true) }
-func BenchmarkUpdateBE(b *testing.B) { benchUpdate(b, binary.BigEndian) }
-func BenchmarkUpdateLE(b *testing.B) { benchUpdate(b, binary.LittleEndian) }
-
-const benchElemCount = 20000
-
-func benchGet(b *testing.B, commit bool) {
- trie := new(Trie)
- if commit {
- _, tmpdb := tempDB()
- trie, _ = New(common.Hash{}, tmpdb)
- }
- k := make([]byte, 32)
- for i := 0; i < benchElemCount; i++ {
- binary.LittleEndian.PutUint64(k, uint64(i))
- trie.Update(k, k)
- }
- binary.LittleEndian.PutUint64(k, benchElemCount/2)
- if commit {
- trie.Commit(nil)
- }
-
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- trie.Get(k)
- }
- b.StopTimer()
-
- if commit {
- ldb := trie.db.diskdb.(*db.LDBDatabase)
- ldb.Close()
- os.RemoveAll(ldb.Path())
- }
-}
-
-func benchUpdate(b *testing.B, e binary.ByteOrder) *Trie {
- trie := newEmpty()
- k := make([]byte, 32)
- for i := 0; i < b.N; i++ {
- e.PutUint64(k, uint64(i))
- trie.Update(k, k)
- }
- return trie
-}
-
-// Benchmarks the trie hashing. Since the trie caches the result of any operation,
-// we cannot use b.N as the number of hashing rouns, since all rounds apart from
-// the first one will be NOOP. As such, we'll use b.N as the number of account to
-// insert into the trie before measuring the hashing.
-func BenchmarkHash(b *testing.B) {
- // Make the random benchmark deterministic
- random := rand.New(rand.NewSource(0))
-
- // Create a realistic account trie to hash
- addresses := make([][20]byte, b.N)
- for i := 0; i < len(addresses); i++ {
- for j := 0; j < len(addresses[i]); j++ {
- addresses[i][j] = byte(random.Intn(256))
- }
- }
- accounts := make([][]byte, len(addresses))
- for i := 0; i < len(accounts); i++ {
- var (
- nonce = uint64(random.Int63())
- balance = new(big.Int).Rand(random, new(big.Int).Exp(common.Big2, common.Big256, nil))
- root = emptyRoot
- code = crypto.Keccak256(nil)
- )
- accounts[i], _ = rlp.EncodeToBytes([]interface{}{nonce, balance, root, code})
- }
- // Insert the accounts into the trie and hash it
- trie := newEmpty()
- for i := 0; i < len(addresses); i++ {
- trie.Update(crypto.Keccak256(addresses[i][:]), accounts[i])
- }
- b.ResetTimer()
- b.ReportAllocs()
- trie.Hash()
-}
-
-func tempDB() (string, *Database) {
- dir, err := ioutil.TempDir("", "trie-bench")
- if err != nil {
- panic(fmt.Sprintf("can't create temporary directory: %v", err))
- }
- diskdb, err := db.NewLDBDatabase(dir, 256, 0)
- if err != nil {
- panic(fmt.Sprintf("can't create temporary database: %v", err))
- }
- return dir, NewDatabase(diskdb)
-}
-
-func getString(trie *Trie, k string) []byte {
- return trie.Get([]byte(k))
-}
-
-func updateString(trie *Trie, k, v string) {
- trie.Update([]byte(k), []byte(v))
-}
-
-func deleteString(trie *Trie, k string) {
- trie.Delete([]byte(k))
-}
diff --git a/log/CONTRIBUTORS b/log/CONTRIBUTORS
deleted file mode 100644
index a0866713b..000000000
--- a/log/CONTRIBUTORS
+++ /dev/null
@@ -1,11 +0,0 @@
-Contributors to log15:
-
-- Aaron L
-- Alan Shreve
-- Chris Hines
-- Ciaran Downey
-- Dmitry Chestnykh
-- Evan Shaw
-- Péter Szilágyi
-- Trevor Gattis
-- Vincent Vanackere
diff --git a/log/LICENSE b/log/LICENSE
deleted file mode 100644
index 5f0d1fb6a..000000000
--- a/log/LICENSE
+++ /dev/null
@@ -1,13 +0,0 @@
-Copyright 2014 Alan Shreve
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
diff --git a/log/README.md b/log/README.md
deleted file mode 100644
index b4476577b..000000000
--- a/log/README.md
+++ /dev/null
@@ -1,77 +0,0 @@
-![obligatory xkcd](http://imgs.xkcd.com/comics/standards.png)
-
-# log15 [![godoc reference](https://godoc.org/github.com/inconshreveable/log15?status.png)](https://godoc.org/github.com/inconshreveable/log15) [![Build Status](https://travis-ci.org/inconshreveable/log15.svg?branch=master)](https://travis-ci.org/inconshreveable/log15)
-
-Package log15 provides an opinionated, simple toolkit for best-practice logging in Go (golang) that is both human and machine readable. It is modeled after the Go standard library's [`io`](http://golang.org/pkg/io/) and [`net/http`](http://golang.org/pkg/net/http/) packages and is an alternative to the standard library's [`log`](http://golang.org/pkg/log/) package.
-
-## Features
-- A simple, easy-to-understand API
-- Promotes structured logging by encouraging use of key/value pairs
-- Child loggers which inherit and add their own private context
-- Lazy evaluation of expensive operations
-- Simple Handler interface allowing for construction of flexible, custom logging configurations with a tiny API.
-- Color terminal support
-- Built-in support for logging to files, streams, syslog, and the network
-- Support for forking records to multiple handlers, buffering records for output, failing over from failed handler writes, + more
-
-## Versioning
-The API of the master branch of log15 should always be considered unstable. If you want to rely on a stable API,
-you must vendor the library.
-
-## Importing
-
-```go
-import log "github.com/inconshreveable/log15"
-```
-
-## Examples
-
-```go
-// all loggers can have key/value context
-srvlog := log.New("module", "app/server")
-
-// all log messages can have key/value context
-srvlog.Warn("abnormal conn rate", "rate", curRate, "low", lowRate, "high", highRate)
-
-// child loggers with inherited context
-connlog := srvlog.New("raddr", c.RemoteAddr())
-connlog.Info("connection open")
-
-// lazy evaluation
-connlog.Debug("ping remote", "latency", log.Lazy{pingRemote})
-
-// flexible configuration
-srvlog.SetHandler(log.MultiHandler(
- log.StreamHandler(os.Stderr, log.LogfmtFormat()),
- log.LvlFilterHandler(
- log.LvlError,
- log.Must.FileHandler("errors.json", log.JSONFormat()))))
-```
-
-Will result in output that looks like this:
-
-```
-WARN[06-17|21:58:10] abnormal conn rate module=app/server rate=0.500 low=0.100 high=0.800
-INFO[06-17|21:58:10] connection open module=app/server raddr=10.0.0.1
-```
-
-## Breaking API Changes
-The following commits broke API stability. This reference is intended to help you understand the consequences of updating to a newer version
-of log15.
-
-- 57a084d014d4150152b19e4e531399a7145d1540 - Added a `Get()` method to the `Logger` interface to retrieve the current handler
-- 93404652ee366648fa622b64d1e2b67d75a3094a - `Record` field `Call` changed to `stack.Call` with switch to `github.com/go-stack/stack`
-- a5e7613673c73281f58e15a87d2cf0cf111e8152 - Restored `syslog.Priority` argument to the `SyslogXxx` handler constructors
-
-## FAQ
-
-### The varargs style is brittle and error prone! Can I have type safety please?
-Yes. Use `log.Ctx`:
-
-```go
-srvlog := log.New(log.Ctx{"module": "app/server"})
-srvlog.Warn("abnormal conn rate", log.Ctx{"rate": curRate, "low": lowRate, "high": highRate})
-```
-
-## License
-Apache
diff --git a/log/README_ETHEREUM.md b/log/README_ETHEREUM.md
deleted file mode 100644
index f6c42ccc0..000000000
--- a/log/README_ETHEREUM.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This package is a fork of https://github.com/inconshreveable/log15, with some
-minor modifications required by the go-ethereum codebase:
-
- * Support for log level `trace`
- * Modified behavior to exit on `critical` failure
diff --git a/log/doc.go b/log/doc.go
deleted file mode 100644
index 9aa72bf42..000000000
--- a/log/doc.go
+++ /dev/null
@@ -1,334 +0,0 @@
-package log
-
-/*
-Package log15 provides an opinionated, simple toolkit for best-practice logging that is
-both human and machine readable. It is modeled after the standard library's io and net/http
-packages.
-
-This package enforces you to only log key/value pairs. Keys must be strings. Values may be
-any type that you like. The default output format is logfmt, but you may also choose to use
-JSON instead if that suits you. Here's how you log:
-
- log.Info("page accessed", "path", r.URL.Path, "user_id", user.id)
-
-This will output a line that looks like:
-
- lvl=info t=2014-05-02T16:07:23-0700 msg="page accessed" path=/org/71/profile user_id=9
-
-Getting Started
-
-To get started, you'll want to import the library:
-
- import log "github.com/inconshreveable/log15"
-
-
-Now you're ready to start logging:
-
- func main() {
- log.Info("Program starting", "args", os.Args())
- }
-
-
-Convention
-
-Because recording a human-meaningful message is common and good practice, the first argument to every
-logging method is the value to the *implicit* key 'msg'.
-
-Additionally, the level you choose for a message will be automatically added with the key 'lvl', and so
-will the current timestamp with key 't'.
-
-You may supply any additional context as a set of key/value pairs to the logging function. log15 allows
-you to favor terseness, ordering, and speed over safety. This is a reasonable tradeoff for
-logging functions. You don't need to explicitly state keys/values, log15 understands that they alternate
-in the variadic argument list:
-
- log.Warn("size out of bounds", "low", lowBound, "high", highBound, "val", val)
-
-If you really do favor your type-safety, you may choose to pass a log.Ctx instead:
-
- log.Warn("size out of bounds", log.Ctx{"low": lowBound, "high": highBound, "val": val})
-
-
-Context loggers
-
-Frequently, you want to add context to a logger so that you can track actions associated with it. An http
-request is a good example. You can easily create new loggers that have context that is automatically included
-with each log line:
-
- requestlogger := log.New("path", r.URL.Path)
-
- // later
- requestlogger.Debug("db txn commit", "duration", txnTimer.Finish())
-
-This will output a log line that includes the path context that is attached to the logger:
-
- lvl=dbug t=2014-05-02T16:07:23-0700 path=/repo/12/add_hook msg="db txn commit" duration=0.12
-
-
-Handlers
-
-The Handler interface defines where log lines are printed to and how they are formated. Handler is a
-single interface that is inspired by net/http's handler interface:
-
- type Handler interface {
- Log(r *Record) error
- }
-
-
-Handlers can filter records, format them, or dispatch to multiple other Handlers.
-This package implements a number of Handlers for common logging patterns that are
-easily composed to create flexible, custom logging structures.
-
-Here's an example handler that prints logfmt output to Stdout:
-
- handler := log.StreamHandler(os.Stdout, log.LogfmtFormat())
-
-Here's an example handler that defers to two other handlers. One handler only prints records
-from the rpc package in logfmt to standard out. The other prints records at Error level
-or above in JSON formatted output to the file /var/log/service.json
-
- handler := log.MultiHandler(
- log.LvlFilterHandler(log.LvlError, log.Must.FileHandler("/var/log/service.json", log.JSONFormat())),
- log.MatchFilterHandler("pkg", "app/rpc" log.StdoutHandler())
- )
-
-Logging File Names and Line Numbers
-
-This package implements three Handlers that add debugging information to the
-context, CallerFileHandler, CallerFuncHandler and CallerStackHandler. Here's
-an example that adds the source file and line number of each logging call to
-the context.
-
- h := log.CallerFileHandler(log.StdoutHandler)
- log.Root().SetHandler(h)
- ...
- log.Error("open file", "err", err)
-
-This will output a line that looks like:
-
- lvl=eror t=2014-05-02T16:07:23-0700 msg="open file" err="file not found" caller=data.go:42
-
-Here's an example that logs the call stack rather than just the call site.
-
- h := log.CallerStackHandler("%+v", log.StdoutHandler)
- log.Root().SetHandler(h)
- ...
- log.Error("open file", "err", err)
-
-This will output a line that looks like:
-
- lvl=eror t=2014-05-02T16:07:23-0700 msg="open file" err="file not found" stack="[pkg/data.go:42 pkg/cmd/main.go]"
-
-The "%+v" format instructs the handler to include the path of the source file
-relative to the compile time GOPATH. The github.com/go-stack/stack package
-documents the full list of formatting verbs and modifiers available.
-
-Custom Handlers
-
-The Handler interface is so simple that it's also trivial to write your own. Let's create an
-example handler which tries to write to one handler, but if that fails it falls back to
-writing to another handler and includes the error that it encountered when trying to write
-to the primary. This might be useful when trying to log over a network socket, but if that
-fails you want to log those records to a file on disk.
-
- type BackupHandler struct {
- Primary Handler
- Secondary Handler
- }
-
- func (h *BackupHandler) Log (r *Record) error {
- err := h.Primary.Log(r)
- if err != nil {
- r.Ctx = append(ctx, "primary_err", err)
- return h.Secondary.Log(r)
- }
- return nil
- }
-
-This pattern is so useful that a generic version that handles an arbitrary number of Handlers
-is included as part of this library called FailoverHandler.
-
-Logging Expensive Operations
-
-Sometimes, you want to log values that are extremely expensive to compute, but you don't want to pay
-the price of computing them if you haven't turned up your logging level to a high level of detail.
-
-This package provides a simple type to annotate a logging operation that you want to be evaluated
-lazily, just when it is about to be logged, so that it would not be evaluated if an upstream Handler
-filters it out. Just wrap any function which takes no arguments with the log.Lazy type. For example:
-
- func factorRSAKey() (factors []int) {
- // return the factors of a very large number
- }
-
- log.Debug("factors", log.Lazy{factorRSAKey})
-
-If this message is not logged for any reason (like logging at the Error level), then
-factorRSAKey is never evaluated.
-
-Dynamic context values
-
-The same log.Lazy mechanism can be used to attach context to a logger which you want to be
-evaluated when the message is logged, but not when the logger is created. For example, let's imagine
-a game where you have Player objects:
-
- type Player struct {
- name string
- alive bool
- log.Logger
- }
-
-You always want to log a player's name and whether they're alive or dead, so when you create the player
-object, you might do:
-
- p := &Player{name: name, alive: true}
- p.Logger = log.New("name", p.name, "alive", p.alive)
-
-Only now, even after a player has died, the logger will still report they are alive because the logging
-context is evaluated when the logger was created. By using the Lazy wrapper, we can defer the evaluation
-of whether the player is alive or not to each log message, so that the log records will reflect the player's
-current state no matter when the log message is written:
-
- p := &Player{name: name, alive: true}
- isAlive := func() bool { return p.alive }
- player.Logger = log.New("name", p.name, "alive", log.Lazy{isAlive})
-
-Terminal Format
-
-If log15 detects that stdout is a terminal, it will configure the default
-handler for it (which is log.StdoutHandler) to use TerminalFormat. This format
-logs records nicely for your terminal, including color-coded output based
-on log level.
-
-Error Handling
-
-Becasuse log15 allows you to step around the type system, there are a few ways you can specify
-invalid arguments to the logging functions. You could, for example, wrap something that is not
-a zero-argument function with log.Lazy or pass a context key that is not a string. Since logging libraries
-are typically the mechanism by which errors are reported, it would be onerous for the logging functions
-to return errors. Instead, log15 handles errors by making these guarantees to you:
-
-- Any log record containing an error will still be printed with the error explained to you as part of the log record.
-
-- Any log record containing an error will include the context key LOG15_ERROR, enabling you to easily
-(and if you like, automatically) detect if any of your logging calls are passing bad values.
-
-Understanding this, you might wonder why the Handler interface can return an error value in its Log method. Handlers
-are encouraged to return errors only if they fail to write their log records out to an external source like if the
-syslog daemon is not responding. This allows the construction of useful handlers which cope with those failures
-like the FailoverHandler.
-
-Library Use
-
-log15 is intended to be useful for library authors as a way to provide configurable logging to
-users of their library. Best practice for use in a library is to always disable all output for your logger
-by default and to provide a public Logger instance that consumers of your library can configure. Like so:
-
- package yourlib
-
- import "github.com/inconshreveable/log15"
-
- var Log = log.New()
-
- func init() {
- Log.SetHandler(log.DiscardHandler())
- }
-
-Users of your library may then enable it if they like:
-
- import "github.com/inconshreveable/log15"
- import "example.com/yourlib"
-
- func main() {
- handler := // custom handler setup
- yourlib.Log.SetHandler(handler)
- }
-
-Best practices attaching logger context
-
-The ability to attach context to a logger is a powerful one. Where should you do it and why?
-I favor embedding a Logger directly into any persistent object in my application and adding
-unique, tracing context keys to it. For instance, imagine I am writing a web browser:
-
- type Tab struct {
- url string
- render *RenderingContext
- // ...
-
- Logger
- }
-
- func NewTab(url string) *Tab {
- return &Tab {
- // ...
- url: url,
-
- Logger: log.New("url", url),
- }
- }
-
-When a new tab is created, I assign a logger to it with the url of
-the tab as context so it can easily be traced through the logs.
-Now, whenever we perform any operation with the tab, we'll log with its
-embedded logger and it will include the tab title automatically:
-
- tab.Debug("moved position", "idx", tab.idx)
-
-There's only one problem. What if the tab url changes? We could
-use log.Lazy to make sure the current url is always written, but that
-would mean that we couldn't trace a tab's full lifetime through our
-logs after the user navigate to a new URL.
-
-Instead, think about what values to attach to your loggers the
-same way you think about what to use as a key in a SQL database schema.
-If it's possible to use a natural key that is unique for the lifetime of the
-object, do so. But otherwise, log15's ext package has a handy RandId
-function to let you generate what you might call "surrogate keys"
-They're just random hex identifiers to use for tracing. Back to our
-Tab example, we would prefer to set up our Logger like so:
-
- import logext "github.com/inconshreveable/log15/ext"
-
- t := &Tab {
- // ...
- url: url,
- }
-
- t.Logger = log.New("id", logext.RandId(8), "url", log.Lazy{t.getUrl})
- return t
-
-Now we'll have a unique traceable identifier even across loading new urls, but
-we'll still be able to see the tab's current url in the log messages.
-
-Must
-
-For all Handler functions which can return an error, there is a version of that
-function which will return no error but panics on failure. They are all available
-on the Must object. For example:
-
- log.Must.FileHandler("/path", log.JSONFormat)
- log.Must.NetHandler("tcp", ":1234", log.JSONFormat)
-
-Inspiration and Credit
-
-All of the following excellent projects inspired the design of this library:
-
-code.google.com/p/log4go
-
-github.com/op/go-logging
-
-github.com/technoweenie/grohl
-
-github.com/Sirupsen/logrus
-
-github.com/kr/logfmt
-
-github.com/spacemonkeygo/spacelog
-
-golang's stdlib, notably io and net/http
-
-The Name
-
-https://xkcd.com/927/
-
-*/
diff --git a/log/format.go b/log/format.go
deleted file mode 100644
index 2db2e6bf6..000000000
--- a/log/format.go
+++ /dev/null
@@ -1,364 +0,0 @@
-package log
-
-import (
- "bytes"
- "encoding/json"
- "fmt"
- "reflect"
- "strconv"
- "strings"
- "sync"
- "sync/atomic"
- "time"
- "unicode/utf8"
-)
-
-const (
- timeFormat = "2006-01-02T15:04:05-0700"
- termTimeFormat = "01-02|15:04:05.999999"
- floatFormat = 'f'
- termMsgJust = 40
-)
-
-// locationTrims are trimmed for display to avoid unwieldy log lines.
-var locationTrims = []string{
- "github.com/ethereum/go-ethereum/",
-}
-
-// PrintOrigins sets or unsets log location (file:line) printing for terminal
-// format output.
-func PrintOrigins(print bool) {
- if print {
- atomic.StoreUint32(&locationEnabled, 1)
- } else {
- atomic.StoreUint32(&locationEnabled, 0)
- }
-}
-
-// locationEnabled is an atomic flag controlling whether the terminal formatter
-// should append the log locations too when printing entries.
-var locationEnabled uint32
-
-// locationLength is the maxmimum path length encountered, which all logs are
-// padded to to aid in alignment.
-var locationLength uint32
-
-// fieldPadding is a global map with maximum field value lengths seen until now
-// to allow padding log contexts in a bit smarter way.
-var fieldPadding = make(map[string]int)
-
-// fieldPaddingLock is a global mutex protecting the field padding map.
-var fieldPaddingLock sync.RWMutex
-
-// Format interface.
-type Format interface {
- Format(r *Record) []byte
-}
-
-// FormatFunc returns a new Format object which uses
-// the given function to perform record formatting.
-func FormatFunc(f func(*Record) []byte) Format {
- return formatFunc(f)
-}
-
-type formatFunc func(*Record) []byte
-
-func (f formatFunc) Format(r *Record) []byte {
- return f(r)
-}
-
-// TerminalStringer is an analogous interface to the stdlib stringer, allowing
-// own types to have custom shortened serialization formats when printed to the
-// screen.
-type TerminalStringer interface {
- TerminalString() string
-}
-
-// TerminalFormat formats log records optimized for human readability on
-// a terminal with color-coded level output and terser human friendly timestamp.
-// This format should only be used for interactive programs or while developing.
-//
-// [TIME] [LEVEL] MESAGE key=value key=value ...
-//
-// Example:
-//
-// [May 16 20:58:45] [DBUG] remove route ns=haproxy addr=127.0.0.1:50002
-//
-func TerminalFormat(usecolor bool) Format {
- return FormatFunc(func(r *Record) []byte {
- var color = 0
- if usecolor {
- switch r.Lvl {
- case LvlCrit:
- color = 35
- case LvlError:
- color = 31
- case LvlWarn:
- color = 33
- case LvlInfo:
- color = 32
- case LvlDebug:
- color = 36
- case LvlTrace:
- color = 34
- }
- }
-
- b := &bytes.Buffer{}
- lvl := r.Lvl.AlignedString()
- if atomic.LoadUint32(&locationEnabled) != 0 {
- // Log origin printing was requested, format the location path and line number
- location := fmt.Sprintf("%+v", r.Call)
- for _, prefix := range locationTrims {
- location = strings.TrimPrefix(location, prefix)
- }
- // Maintain the maximum location length for fancyer alignment
- align := int(atomic.LoadUint32(&locationLength))
- if align < len(location) {
- align = len(location)
- atomic.StoreUint32(&locationLength, uint32(align))
- }
- padding := strings.Repeat(" ", align-len(location))
-
- // Assemble and print the log heading
- if color > 0 {
- fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s|%s]%s %s ", color, lvl, r.Time.Format(termTimeFormat), location, padding, r.Msg)
- } else {
- fmt.Fprintf(b, "%s[%s|%s]%s %s ", lvl, r.Time.Format(termTimeFormat), location, padding, r.Msg)
- }
- } else {
- if color > 0 {
- fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %s ", color, lvl, r.Time.Format(termTimeFormat), r.Msg)
- } else {
- fmt.Fprintf(b, "%s[%s] %s ", lvl, r.Time.Format(termTimeFormat), r.Msg)
- }
- }
- // try to justify the log output for short messages
- length := utf8.RuneCountInString(r.Msg)
- if len(r.Ctx) > 0 && length < termMsgJust {
- b.Write(bytes.Repeat([]byte{' '}, termMsgJust-length))
- }
- // print the keys logfmt style
- logfmt(b, r.Ctx, color, true)
- return b.Bytes()
- })
-}
-
-// LogfmtFormat prints records in logfmt format, an easy machine-parseable but human-readable
-// format for key/value pairs.
-//
-// For more details see: http://godoc.org/github.com/kr/logfmt
-//
-func LogfmtFormat() Format {
- return FormatFunc(func(r *Record) []byte {
- common := []interface{}{r.KeyNames.Time, r.Time, r.KeyNames.Lvl, r.Lvl, r.KeyNames.Msg, r.Msg}
- buf := &bytes.Buffer{}
- logfmt(buf, append(common, r.Ctx...), 0, false)
- return buf.Bytes()
- })
-}
-
-func logfmt(buf *bytes.Buffer, ctx []interface{}, color int, term bool) {
- for i := 0; i < len(ctx); i += 2 {
- if i != 0 {
- buf.WriteByte(' ')
- }
-
- k, ok := ctx[i].(string)
- v := formatLogfmtValue(ctx[i+1], term)
- if !ok {
- k, v = errorKey, formatLogfmtValue(k, term)
- }
-
- // XXX: we should probably check that all of your key bytes aren't invalid
- fieldPaddingLock.RLock()
- padding := fieldPadding[k]
- fieldPaddingLock.RUnlock()
-
- length := utf8.RuneCountInString(v)
- if padding < length {
- padding = length
-
- fieldPaddingLock.Lock()
- fieldPadding[k] = padding
- fieldPaddingLock.Unlock()
- }
- if color > 0 {
- fmt.Fprintf(buf, "\x1b[%dm%s\x1b[0m=", color, k)
- } else {
- buf.WriteString(k)
- buf.WriteByte('=')
- }
- buf.WriteString(v)
- if i < len(ctx)-2 {
- buf.Write(bytes.Repeat([]byte{' '}, padding-length))
- }
- }
- buf.WriteByte('\n')
-}
-
-// JSONFormat formats log records as JSON objects separated by newlines.
-// It is the equivalent of JSONFormatEx(false, true).
-func JSONFormat() Format {
- return JSONFormatEx(false, true)
-}
-
-// JSONFormatEx formats log records as JSON objects. If pretty is true,
-// records will be pretty-printed. If lineSeparated is true, records
-// will be logged with a new line between each record.
-func JSONFormatEx(pretty, lineSeparated bool) Format {
- jsonMarshal := json.Marshal
- if pretty {
- jsonMarshal = func(v interface{}) ([]byte, error) {
- return json.MarshalIndent(v, "", " ")
- }
- }
-
- return FormatFunc(func(r *Record) []byte {
- props := make(map[string]interface{})
-
- props[r.KeyNames.Time] = r.Time
- props[r.KeyNames.Lvl] = r.Lvl.String()
- props[r.KeyNames.Msg] = r.Msg
-
- for i := 0; i < len(r.Ctx); i += 2 {
- k, ok := r.Ctx[i].(string)
- if !ok {
- props[errorKey] = fmt.Sprintf("%+v is not a string key", r.Ctx[i])
- }
- props[k] = formatJSONValue(r.Ctx[i+1])
- }
-
- b, err := jsonMarshal(props)
- if err != nil {
- b, _ = jsonMarshal(map[string]string{
- errorKey: err.Error(),
- })
- return b
- }
-
- if lineSeparated {
- b = append(b, '\n')
- }
-
- return b
- })
-}
-
-func formatShared(value interface{}) (result interface{}) {
- defer func() {
- if err := recover(); err != nil {
- if v := reflect.ValueOf(value); v.Kind() == reflect.Ptr && v.IsNil() {
- result = "nil"
- } else {
- panic(err)
- }
- }
- }()
-
- switch v := value.(type) {
- case time.Time:
- return v.Format(timeFormat)
-
- case error:
- return v.Error()
-
- case fmt.Stringer:
- return v.String()
-
- default:
- return v
- }
-}
-
-func formatJSONValue(value interface{}) interface{} {
- value = formatShared(value)
- switch value.(type) {
- case int, int8, int16, int32, int64, float32, float64, uint, uint8, uint16, uint32, uint64, string:
- return value
- default:
- return fmt.Sprintf("%+v", value)
- }
-}
-
-// formatValue formats a value for serialization
-func formatLogfmtValue(value interface{}, term bool) string {
- if value == nil {
- return "nil"
- }
-
- if t, ok := value.(time.Time); ok {
- // Performance optimization: No need for escaping since the provided
- // timeFormat doesn't have any escape characters, and escaping is
- // expensive.
- return t.Format(timeFormat)
- }
- if term {
- if s, ok := value.(TerminalStringer); ok {
- // Custom terminal stringer provided, use that
- return escapeString(s.TerminalString())
- }
- }
- value = formatShared(value)
- switch v := value.(type) {
- case bool:
- return strconv.FormatBool(v)
- case float32:
- return strconv.FormatFloat(float64(v), floatFormat, 3, 64)
- case float64:
- return strconv.FormatFloat(v, floatFormat, 3, 64)
- case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
- return fmt.Sprintf("%d", value)
- case string:
- return escapeString(v)
- default:
- return escapeString(fmt.Sprintf("%+v", value))
- }
-}
-
-var stringBufPool = sync.Pool{
- New: func() interface{} { return new(bytes.Buffer) },
-}
-
-func escapeString(s string) string {
- needsQuotes := false
- needsEscape := false
- for _, r := range s {
- if r <= ' ' || r == '=' || r == '"' {
- needsQuotes = true
- }
- if r == '\\' || r == '"' || r == '\n' || r == '\r' || r == '\t' {
- needsEscape = true
- }
- }
- if !needsEscape && !needsQuotes {
- return s
- }
- e := stringBufPool.Get().(*bytes.Buffer)
- e.WriteByte('"')
- for _, r := range s {
- switch r {
- case '\\', '"':
- e.WriteByte('\\')
- e.WriteByte(byte(r))
- case '\n':
- e.WriteString("\\n")
- case '\r':
- e.WriteString("\\r")
- case '\t':
- e.WriteString("\\t")
- default:
- e.WriteRune(r)
- }
- }
- e.WriteByte('"')
- var ret string
- if needsQuotes {
- ret = e.String()
- } else {
- ret = string(e.Bytes()[1 : e.Len()-1])
- }
- e.Reset()
- stringBufPool.Put(e)
- return ret
-}
diff --git a/log/handler.go b/log/handler.go
deleted file mode 100644
index 3c99114dc..000000000
--- a/log/handler.go
+++ /dev/null
@@ -1,359 +0,0 @@
-package log
-
-import (
- "fmt"
- "io"
- "net"
- "os"
- "reflect"
- "sync"
-
- "github.com/go-stack/stack"
-)
-
-// Handler defines where and how log records are written.
-// A Logger prints its log records by writing to a Handler.
-// Handlers are composable, providing you great flexibility in combining
-// them to achieve the logging structure that suits your applications.
-type Handler interface {
- Log(r *Record) error
-}
-
-// FuncHandler returns a Handler that logs records with the given
-// function.
-func FuncHandler(fn func(r *Record) error) Handler {
- return funcHandler(fn)
-}
-
-type funcHandler func(r *Record) error
-
-func (h funcHandler) Log(r *Record) error {
- return h(r)
-}
-
-// StreamHandler writes log records to an io.Writer
-// with the given format. StreamHandler can be used
-// to easily begin writing log records to other
-// outputs.
-//
-// StreamHandler wraps itself with LazyHandler and SyncHandler
-// to evaluate Lazy objects and perform safe concurrent writes.
-func StreamHandler(wr io.Writer, fmtr Format) Handler {
- h := FuncHandler(func(r *Record) error {
- _, err := wr.Write(fmtr.Format(r))
- return err
- })
- return LazyHandler(SyncHandler(h))
-}
-
-// SyncHandler can be wrapped around a handler to guarantee that
-// only a single Log operation can proceed at a time. It's necessary
-// for thread-safe concurrent writes.
-func SyncHandler(h Handler) Handler {
- var mu sync.Mutex
- return FuncHandler(func(r *Record) error {
- defer mu.Unlock()
- mu.Lock()
- return h.Log(r)
- })
-}
-
-// FileHandler returns a handler which writes log records to the give file
-// using the given format. If the path
-// already exists, FileHandler will append to the given file. If it does not,
-// FileHandler will create the file with mode 0644.
-func FileHandler(path string, fmtr Format) (Handler, error) {
- f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
- if err != nil {
- return nil, err
- }
- return closingHandler{f, StreamHandler(f, fmtr)}, nil
-}
-
-// NetHandler opens a socket to the given address and writes records
-// over the connection.
-func NetHandler(network, addr string, fmtr Format) (Handler, error) {
- conn, err := net.Dial(network, addr)
- if err != nil {
- return nil, err
- }
-
- return closingHandler{conn, StreamHandler(conn, fmtr)}, nil
-}
-
-// XXX: closingHandler is essentially unused at the moment
-// it's meant for a future time when the Handler interface supports
-// a possible Close() operation
-type closingHandler struct {
- io.WriteCloser
- Handler
-}
-
-func (h *closingHandler) Close() error {
- return h.WriteCloser.Close()
-}
-
-// CallerFileHandler returns a Handler that adds the line number and file of
-// the calling function to the context with key "caller".
-func CallerFileHandler(h Handler) Handler {
- return FuncHandler(func(r *Record) error {
- r.Ctx = append(r.Ctx, "caller", fmt.Sprint(r.Call))
- return h.Log(r)
- })
-}
-
-// CallerFuncHandler returns a Handler that adds the calling function name to
-// the context with key "fn".
-func CallerFuncHandler(h Handler) Handler {
- return FuncHandler(func(r *Record) error {
- r.Ctx = append(r.Ctx, "fn", formatCall("%+n", r.Call))
- return h.Log(r)
- })
-}
-
-// This function is here to please go vet on Go < 1.8.
-func formatCall(format string, c stack.Call) string {
- return fmt.Sprintf(format, c)
-}
-
-// CallerStackHandler returns a Handler that adds a stack trace to the context
-// with key "stack". The stack trace is formated as a space separated list of
-// call sites inside matching []'s. The most recent call site is listed first.
-// Each call site is formatted according to format. See the documentation of
-// package github.com/go-stack/stack for the list of supported formats.
-func CallerStackHandler(format string, h Handler) Handler {
- return FuncHandler(func(r *Record) error {
- s := stack.Trace().TrimBelow(r.Call).TrimRuntime()
- if len(s) > 0 {
- r.Ctx = append(r.Ctx, "stack", fmt.Sprintf(format, s))
- }
- return h.Log(r)
- })
-}
-
-// FilterHandler returns a Handler that only writes records to the
-// wrapped Handler if the given function evaluates true. For example,
-// to only log records where the 'err' key is not nil:
-//
-// logger.SetHandler(FilterHandler(func(r *Record) bool {
-// for i := 0; i < len(r.Ctx); i += 2 {
-// if r.Ctx[i] == "err" {
-// return r.Ctx[i+1] != nil
-// }
-// }
-// return false
-// }, h))
-//
-func FilterHandler(fn func(r *Record) bool, h Handler) Handler {
- return FuncHandler(func(r *Record) error {
- if fn(r) {
- return h.Log(r)
- }
- return nil
- })
-}
-
-// MatchFilterHandler returns a Handler that only writes records
-// to the wrapped Handler if the given key in the logged
-// context matches the value. For example, to only log records
-// from your ui package:
-//
-// log.MatchFilterHandler("pkg", "app/ui", log.StdoutHandler)
-//
-func MatchFilterHandler(key string, value interface{}, h Handler) Handler {
- return FilterHandler(func(r *Record) (pass bool) {
- switch key {
- case r.KeyNames.Lvl:
- return r.Lvl == value
- case r.KeyNames.Time:
- return r.Time == value
- case r.KeyNames.Msg:
- return r.Msg == value
- }
-
- for i := 0; i < len(r.Ctx); i += 2 {
- if r.Ctx[i] == key {
- return r.Ctx[i+1] == value
- }
- }
- return false
- }, h)
-}
-
-// LvlFilterHandler returns a Handler that only writes
-// records which are less than the given verbosity
-// level to the wrapped Handler. For example, to only
-// log Error/Crit records:
-//
-// log.LvlFilterHandler(log.LvlError, log.StdoutHandler)
-//
-func LvlFilterHandler(maxLvl Lvl, h Handler) Handler {
- return FilterHandler(func(r *Record) (pass bool) {
- return r.Lvl <= maxLvl
- }, h)
-}
-
-// MultiHandler dispatches any write to each of its handlers.
-// This is useful for writing different types of log information
-// to different locations. For example, to log to a file and
-// standard error:
-//
-// log.MultiHandler(
-// log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()),
-// log.StderrHandler)
-//
-func MultiHandler(hs ...Handler) Handler {
- return FuncHandler(func(r *Record) error {
- for _, h := range hs {
- // what to do about failures?
- h.Log(r)
- }
- return nil
- })
-}
-
-// FailoverHandler writes all log records to the first handler
-// specified, but will failover and write to the second handler if
-// the first handler has failed, and so on for all handlers specified.
-// For example you might want to log to a network socket, but failover
-// to writing to a file if the network fails, and then to
-// standard out if the file write fails:
-//
-// log.FailoverHandler(
-// log.Must.NetHandler("tcp", ":9090", log.JSONFormat()),
-// log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()),
-// log.StdoutHandler)
-//
-// All writes that do not go to the first handler will add context with keys of
-// the form "failover_err_{idx}" which explain the error encountered while
-// trying to write to the handlers before them in the list.
-func FailoverHandler(hs ...Handler) Handler {
- return FuncHandler(func(r *Record) error {
- var err error
- for i, h := range hs {
- err = h.Log(r)
- if err == nil {
- return nil
- }
- r.Ctx = append(r.Ctx, fmt.Sprintf("failover_err_%d", i), err)
- }
-
- return err
- })
-}
-
-// ChannelHandler writes all records to the given channel.
-// It blocks if the channel is full. Useful for async processing
-// of log messages, it's used by BufferedHandler.
-func ChannelHandler(recs chan<- *Record) Handler {
- return FuncHandler(func(r *Record) error {
- recs <- r
- return nil
- })
-}
-
-// BufferedHandler writes all records to a buffered
-// channel of the given size which flushes into the wrapped
-// handler whenever it is available for writing. Since these
-// writes happen asynchronously, all writes to a BufferedHandler
-// never return an error and any errors from the wrapped handler are ignored.
-func BufferedHandler(bufSize int, h Handler) Handler {
- recs := make(chan *Record, bufSize)
- go func() {
- for m := range recs {
- _ = h.Log(m)
- }
- }()
- return ChannelHandler(recs)
-}
-
-// LazyHandler writes all values to the wrapped handler after evaluating
-// any lazy functions in the record's context. It is already wrapped
-// around StreamHandler and SyslogHandler in this library, you'll only need
-// it if you write your own Handler.
-func LazyHandler(h Handler) Handler {
- return FuncHandler(func(r *Record) error {
- // go through the values (odd indices) and reassign
- // the values of any lazy fn to the result of its execution
- hadErr := false
- for i := 1; i < len(r.Ctx); i += 2 {
- lz, ok := r.Ctx[i].(Lazy)
- if ok {
- v, err := evaluateLazy(lz)
- if err != nil {
- hadErr = true
- r.Ctx[i] = err
- } else {
- if cs, ok := v.(stack.CallStack); ok {
- v = cs.TrimBelow(r.Call).TrimRuntime()
- }
- r.Ctx[i] = v
- }
- }
- }
-
- if hadErr {
- r.Ctx = append(r.Ctx, errorKey, "bad lazy")
- }
-
- return h.Log(r)
- })
-}
-
-func evaluateLazy(lz Lazy) (interface{}, error) {
- t := reflect.TypeOf(lz.Fn)
-
- if t.Kind() != reflect.Func {
- return nil, fmt.Errorf("INVALID_LAZY, not func: %+v", lz.Fn)
- }
-
- if t.NumIn() > 0 {
- return nil, fmt.Errorf("INVALID_LAZY, func takes args: %+v", lz.Fn)
- }
-
- if t.NumOut() == 0 {
- return nil, fmt.Errorf("INVALID_LAZY, no func return val: %+v", lz.Fn)
- }
-
- value := reflect.ValueOf(lz.Fn)
- results := value.Call([]reflect.Value{})
- if len(results) == 1 {
- return results[0].Interface(), nil
- }
- values := make([]interface{}, len(results))
- for i, v := range results {
- values[i] = v.Interface()
- }
- return values, nil
-}
-
-// DiscardHandler reports success for all writes but does nothing.
-// It is useful for dynamically disabling logging at runtime via
-// a Logger's SetHandler method.
-func DiscardHandler() Handler {
- return FuncHandler(func(r *Record) error {
- return nil
- })
-}
-
-// Must provides the following Handler creation functions
-// which instead of returning an error parameter only return a Handler
-// and panic on failure: FileHandler, NetHandler, SyslogHandler, SyslogNetHandler
-var Must muster
-
-func must(h Handler, err error) Handler {
- if err != nil {
- panic(err)
- }
- return h
-}
-
-type muster struct{}
-
-func (m muster) FileHandler(path string, fmtr Format) Handler {
- return must(FileHandler(path, fmtr))
-}
-
-func (m muster) NetHandler(network, addr string, fmtr Format) Handler {
- return must(NetHandler(network, addr, fmtr))
-}
diff --git a/log/handler_glog.go b/log/handler_glog.go
deleted file mode 100644
index f8b932fd1..000000000
--- a/log/handler_glog.go
+++ /dev/null
@@ -1,227 +0,0 @@
-// Copyright 2017 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 log
-
-import (
- "errors"
- "fmt"
- "regexp"
- "runtime"
- "strconv"
- "strings"
- "sync"
- "sync/atomic"
-)
-
-// errVmoduleSyntax is returned when a user vmodule pattern is invalid.
-var errVmoduleSyntax = errors.New("expect comma-separated list of filename=N")
-
-// errTraceSyntax is returned when a user backtrace pattern is invalid.
-var errTraceSyntax = errors.New("expect file.go:234")
-
-// GlogHandler is a log handler that mimics the filtering features of Google's
-// glog logger: setting global log levels; overriding with callsite pattern
-// matches; and requesting backtraces at certain positions.
-type GlogHandler struct {
- origin Handler // The origin handler this wraps
-
- level uint32 // Current log level, atomically accessible
- override uint32 // Flag whether overrides are used, atomically accessible
- backtrace uint32 // Flag whether backtrace location is set
-
- patterns []pattern // Current list of patterns to override with
- siteCache map[uintptr]Lvl // Cache of callsite pattern evaluations
- location string // file:line location where to do a stackdump at
- lock sync.RWMutex // Lock protecting the override pattern list
-}
-
-// NewGlogHandler creates a new log handler with filtering functionality similar
-// to Google's glog logger. The returned handler implements Handler.
-func NewGlogHandler(h Handler) *GlogHandler {
- return &GlogHandler{
- origin: h,
- }
-}
-
-// pattern contains a filter for the Vmodule option, holding a verbosity level
-// and a file pattern to match.
-type pattern struct {
- pattern *regexp.Regexp
- level Lvl
-}
-
-// Verbosity sets the glog verbosity ceiling. The verbosity of individual packages
-// and source files can be raised using Vmodule.
-func (h *GlogHandler) Verbosity(level Lvl) {
- atomic.StoreUint32(&h.level, uint32(level))
-}
-
-// Vmodule sets the glog verbosity pattern.
-//
-// The syntax of the argument is a comma-separated list of pattern=N, where the
-// pattern is a literal file name or "glob" pattern matching and N is a V level.
-//
-// For instance:
-//
-// pattern="gopher.go=3"
-// sets the V level to 3 in all Go files named "gopher.go"
-//
-// pattern="foo=3"
-// sets V to 3 in all files of any packages whose import path ends in "foo"
-//
-// pattern="foo/*=3"
-// sets V to 3 in all files of any packages whose import path contains "foo"
-func (h *GlogHandler) Vmodule(ruleset string) error {
- var filter []pattern
- for _, rule := range strings.Split(ruleset, ",") {
- // Empty strings such as from a trailing comma can be ignored
- if len(rule) == 0 {
- continue
- }
- // Ensure we have a pattern = level filter rule
- parts := strings.Split(rule, "=")
- if len(parts) != 2 {
- return errVmoduleSyntax
- }
- parts[0] = strings.TrimSpace(parts[0])
- parts[1] = strings.TrimSpace(parts[1])
- if len(parts[0]) == 0 || len(parts[1]) == 0 {
- return errVmoduleSyntax
- }
- // Parse the level and if correct, assemble the filter rule
- level, err := strconv.Atoi(parts[1])
- if err != nil {
- return errVmoduleSyntax
- }
- if level <= 0 {
- continue // Ignore. It's harmless but no point in paying the overhead.
- }
- // Compile the rule pattern into a regular expression
- matcher := ".*"
- for _, comp := range strings.Split(parts[0], "/") {
- if comp == "*" {
- matcher += "(/.*)?"
- } else if comp != "" {
- matcher += "/" + regexp.QuoteMeta(comp)
- }
- }
- if !strings.HasSuffix(parts[0], ".go") {
- matcher += "/[^/]+\\.go"
- }
- matcher = matcher + "$"
-
- re, _ := regexp.Compile(matcher)
- filter = append(filter, pattern{re, Lvl(level)})
- }
- // Swap out the vmodule pattern for the new filter system
- h.lock.Lock()
- defer h.lock.Unlock()
-
- h.patterns = filter
- h.siteCache = make(map[uintptr]Lvl)
- atomic.StoreUint32(&h.override, uint32(len(filter)))
-
- return nil
-}
-
-// BacktraceAt sets the glog backtrace location. When set to a file and line
-// number holding a logging statement, a stack trace will be written to the Info
-// log whenever execution hits that statement.
-//
-// Unlike with Vmodule, the ".go" must be present.
-func (h *GlogHandler) BacktraceAt(location string) error {
- // Ensure the backtrace location contains two non-empty elements
- parts := strings.Split(location, ":")
- if len(parts) != 2 {
- return errTraceSyntax
- }
- parts[0] = strings.TrimSpace(parts[0])
- parts[1] = strings.TrimSpace(parts[1])
- if len(parts[0]) == 0 || len(parts[1]) == 0 {
- return errTraceSyntax
- }
- // Ensure the .go prefix is present and the line is valid
- if !strings.HasSuffix(parts[0], ".go") {
- return errTraceSyntax
- }
- if _, err := strconv.Atoi(parts[1]); err != nil {
- return errTraceSyntax
- }
- // All seems valid
- h.lock.Lock()
- defer h.lock.Unlock()
-
- h.location = location
- atomic.StoreUint32(&h.backtrace, uint32(len(location)))
-
- return nil
-}
-
-// Log implements Handler.Log, filtering a log record through the global, local
-// and backtrace filters, finally emitting it if either allow it through.
-func (h *GlogHandler) Log(r *Record) error {
- // If backtracing is requested, check whether this is the callsite
- if atomic.LoadUint32(&h.backtrace) > 0 {
- // Everything below here is slow. Although we could cache the call sites the
- // same way as for vmodule, backtracing is so rare it's not worth the extra
- // complexity.
- h.lock.RLock()
- match := h.location == r.Call.String()
- h.lock.RUnlock()
-
- if match {
- // Callsite matched, raise the log level to info and gather the stacks
- r.Lvl = LvlInfo
-
- buf := make([]byte, 1024*1024)
- buf = buf[:runtime.Stack(buf, true)]
- r.Msg += "\n\n" + string(buf)
- }
- }
- // If the global log level allows, fast track logging
- if atomic.LoadUint32(&h.level) >= uint32(r.Lvl) {
- return h.origin.Log(r)
- }
- // If no local overrides are present, fast track skipping
- if atomic.LoadUint32(&h.override) == 0 {
- return nil
- }
- // Check callsite cache for previously calculated log levels
- h.lock.RLock()
- lvl, ok := h.siteCache[r.Call.PC()]
- h.lock.RUnlock()
-
- // If we didn't cache the callsite yet, calculate it
- if !ok {
- h.lock.Lock()
- for _, rule := range h.patterns {
- if rule.pattern.MatchString(fmt.Sprintf("%+s", r.Call)) {
- h.siteCache[r.Call.PC()], lvl, ok = rule.level, rule.level, true
- break
- }
- }
- // If no rule matched, remember to drop log the next time
- if !ok {
- h.siteCache[r.Call.PC()] = 0
- }
- h.lock.Unlock()
- }
- if lvl >= r.Lvl {
- return h.origin.Log(r)
- }
- return nil
-}
diff --git a/log/handler_go13.go b/log/handler_go13.go
deleted file mode 100644
index 0843ed0e5..000000000
--- a/log/handler_go13.go
+++ /dev/null
@@ -1,26 +0,0 @@
-// +build !go1.4
-
-package log
-
-import (
- "sync/atomic"
- "unsafe"
-)
-
-// swapHandler wraps another handler that may be swapped out
-// dynamically at runtime in a thread-safe fashion.
-type swapHandler struct {
- handler unsafe.Pointer
-}
-
-func (h *swapHandler) Log(r *Record) error {
- return h.Get().Log(r)
-}
-
-func (h *swapHandler) Get() Handler {
- return *(*Handler)(atomic.LoadPointer(&h.handler))
-}
-
-func (h *swapHandler) Swap(newHandler Handler) {
- atomic.StorePointer(&h.handler, unsafe.Pointer(&newHandler))
-}
diff --git a/log/handler_go14.go b/log/handler_go14.go
deleted file mode 100644
index 05dedbf2a..000000000
--- a/log/handler_go14.go
+++ /dev/null
@@ -1,23 +0,0 @@
-// +build go1.4
-
-package log
-
-import "sync/atomic"
-
-// swapHandler wraps another handler that may be swapped out
-// dynamically at runtime in a thread-safe fashion.
-type swapHandler struct {
- handler atomic.Value
-}
-
-func (h *swapHandler) Log(r *Record) error {
- return (*h.handler.Load().(*Handler)).Log(r)
-}
-
-func (h *swapHandler) Swap(newHandler Handler) {
- h.handler.Store(&newHandler)
-}
-
-func (h *swapHandler) Get() Handler {
- return *h.handler.Load().(*Handler)
-}
diff --git a/log/logger.go b/log/logger.go
deleted file mode 100644
index 5e5924a3d..000000000
--- a/log/logger.go
+++ /dev/null
@@ -1,244 +0,0 @@
-package log
-
-import (
- "fmt"
- "os"
- "time"
-
- "github.com/go-stack/stack"
-)
-
-const timeKey = "t"
-const lvlKey = "lvl"
-const msgKey = "msg"
-const errorKey = "LOG15_ERROR"
-const skipLevel = 2
-
-// Lvl type.
-type Lvl int
-
-// Constants.
-const (
- LvlCrit Lvl = iota
- LvlError
- LvlWarn
- LvlInfo
- LvlDebug
- LvlTrace
-)
-
-// AlignedString returns a 5-character string containing the name of a Lvl.
-func (l Lvl) AlignedString() string {
- switch l {
- case LvlTrace:
- return "TRACE"
- case LvlDebug:
- return "DEBUG"
- case LvlInfo:
- return "INFO "
- case LvlWarn:
- return "WARN "
- case LvlError:
- return "ERROR"
- case LvlCrit:
- return "CRIT "
- default:
- panic("bad level")
- }
-}
-
-// Strings returns the name of a Lvl.
-func (l Lvl) String() string {
- switch l {
- case LvlTrace:
- return "trce"
- case LvlDebug:
- return "dbug"
- case LvlInfo:
- return "info"
- case LvlWarn:
- return "warn"
- case LvlError:
- return "eror"
- case LvlCrit:
- return "crit"
- default:
- panic("bad level")
- }
-}
-
-// LvlFromString returns the appropriate Lvl from a string name.
-// Useful for parsing command line args and configuration files.
-func LvlFromString(lvlString string) (Lvl, error) {
- switch lvlString {
- case "trace", "trce":
- return LvlTrace, nil
- case "debug", "dbug":
- return LvlDebug, nil
- case "info":
- return LvlInfo, nil
- case "warn":
- return LvlWarn, nil
- case "error", "eror":
- return LvlError, nil
- case "crit":
- return LvlCrit, nil
- default:
- return LvlDebug, fmt.Errorf("Unknown level: %v", lvlString)
- }
-}
-
-// A Record is what a Logger asks its handler to write
-type Record struct {
- Time time.Time
- Lvl Lvl
- Msg string
- Ctx []interface{}
- Call stack.Call
- KeyNames RecordKeyNames
-}
-
-// RecordKeyNames gets stored in a Record when the write function is executed.
-type RecordKeyNames struct {
- Time string
- Msg string
- Lvl string
-}
-
-// A Logger writes key/value pairs to a Handler
-type Logger interface {
- // New returns a new Logger that has this logger's context plus the given context
- New(ctx ...interface{}) Logger
-
- // GetHandler gets the handler associated with the logger.
- GetHandler() Handler
-
- // SetHandler updates the logger to write records to the specified handler.
- SetHandler(h Handler)
-
- // Log a message at the given level with context key/value pairs
- Trace(msg string, ctx ...interface{})
- Debug(msg string, ctx ...interface{})
- Info(msg string, ctx ...interface{})
- Warn(msg string, ctx ...interface{})
- Error(msg string, ctx ...interface{})
- Crit(msg string, ctx ...interface{})
-}
-
-type logger struct {
- ctx []interface{}
- h *swapHandler
-}
-
-func (l *logger) write(msg string, lvl Lvl, ctx []interface{}, skip int) {
- l.h.Log(&Record{
- Time: time.Now(),
- Lvl: lvl,
- Msg: msg,
- Ctx: newContext(l.ctx, ctx),
- Call: stack.Caller(skip),
- KeyNames: RecordKeyNames{
- Time: timeKey,
- Msg: msgKey,
- Lvl: lvlKey,
- },
- })
-}
-
-func (l *logger) New(ctx ...interface{}) Logger {
- child := &logger{newContext(l.ctx, ctx), new(swapHandler)}
- child.SetHandler(l.h)
- return child
-}
-
-func newContext(prefix []interface{}, suffix []interface{}) []interface{} {
- normalizedSuffix := normalize(suffix)
- newCtx := make([]interface{}, len(prefix)+len(normalizedSuffix))
- n := copy(newCtx, prefix)
- copy(newCtx[n:], normalizedSuffix)
- return newCtx
-}
-
-func (l *logger) Trace(msg string, ctx ...interface{}) {
- l.write(msg, LvlTrace, ctx, skipLevel)
-}
-
-func (l *logger) Debug(msg string, ctx ...interface{}) {
- l.write(msg, LvlDebug, ctx, skipLevel)
-}
-
-func (l *logger) Info(msg string, ctx ...interface{}) {
- l.write(msg, LvlInfo, ctx, skipLevel)
-}
-
-func (l *logger) Warn(msg string, ctx ...interface{}) {
- l.write(msg, LvlWarn, ctx, skipLevel)
-}
-
-func (l *logger) Error(msg string, ctx ...interface{}) {
- l.write(msg, LvlError, ctx, skipLevel)
-}
-
-func (l *logger) Crit(msg string, ctx ...interface{}) {
- l.write(msg, LvlCrit, ctx, skipLevel)
- os.Exit(1)
-}
-
-func (l *logger) GetHandler() Handler {
- return l.h.Get()
-}
-
-func (l *logger) SetHandler(h Handler) {
- l.h.Swap(h)
-}
-
-func normalize(ctx []interface{}) []interface{} {
- // if the caller passed a Ctx object, then expand it
- if len(ctx) == 1 {
- if ctxMap, ok := ctx[0].(Ctx); ok {
- ctx = ctxMap.toArray()
- }
- }
-
- // ctx needs to be even because it's a series of key/value pairs
- // no one wants to check for errors on logging functions,
- // so instead of erroring on bad input, we'll just make sure
- // that things are the right length and users can fix bugs
- // when they see the output looks wrong
- if len(ctx)%2 != 0 {
- ctx = append(ctx, nil, errorKey, "Normalized odd number of arguments by adding nil")
- }
-
- return ctx
-}
-
-// Lazy allows you to defer calculation of a logged value that is expensive
-// to compute until it is certain that it must be evaluated with the given filters.
-//
-// Lazy may also be used in conjunction with a Logger's New() function
-// to generate a child logger which always reports the current value of changing
-// state.
-//
-// You may wrap any function which takes no arguments to Lazy. It may return any
-// number of values of any type.
-type Lazy struct {
- Fn interface{}
-}
-
-// Ctx is a map of key/value pairs to pass as context to a log function
-// Use this only if you really need greater safety around the arguments you pass
-// to the logging functions.
-type Ctx map[string]interface{}
-
-func (c Ctx) toArray() []interface{} {
- arr := make([]interface{}, len(c)*2)
-
- i := 0
- for k, v := range c {
- arr[i] = k
- arr[i+1] = v
- i += 2
- }
-
- return arr
-}
diff --git a/log/root.go b/log/root.go
deleted file mode 100644
index 84d73b6f6..000000000
--- a/log/root.go
+++ /dev/null
@@ -1,72 +0,0 @@
-package log
-
-import (
- "os"
-)
-
-var (
- root = &logger{[]interface{}{}, new(swapHandler)}
- // StdoutHandler handles stdout
- StdoutHandler = StreamHandler(os.Stdout, TerminalFormat(false))
- // StderrHandler handles stderr
- StderrHandler = StreamHandler(os.Stderr, LogfmtFormat())
-)
-
-func init() {
- root.SetHandler(StdoutHandler)
-}
-
-// New returns a new logger with the given context.
-// New is a convenient alias for Root().New
-func New(ctx ...interface{}) Logger {
- return root.New(ctx...)
-}
-
-// Root returns the root logger
-func Root() Logger {
- return root
-}
-
-// The following functions bypass the exported logger methods (logger.Debug,
-// etc.) to keep the call depth the same for all paths to logger.write so
-// runtime.Caller(2) always refers to the call site in client code.
-
-// Trace is a convenient alias for Root().Trace
-func Trace(msg string, ctx ...interface{}) {
- root.write(msg, LvlTrace, ctx, skipLevel)
-}
-
-// Debug is a convenient alias for Root().Debug
-func Debug(msg string, ctx ...interface{}) {
- root.write(msg, LvlDebug, ctx, skipLevel)
-}
-
-// Info is a convenient alias for Root().Info
-func Info(msg string, ctx ...interface{}) {
- root.write(msg, LvlInfo, ctx, skipLevel)
-}
-
-// Warn is a convenient alias for Root().Warn
-func Warn(msg string, ctx ...interface{}) {
- root.write(msg, LvlWarn, ctx, skipLevel)
-}
-
-// Error is a convenient alias for Root().Error
-func Error(msg string, ctx ...interface{}) {
- root.write(msg, LvlError, ctx, skipLevel)
-}
-
-// Crit is a convenient alias for Root().Crit
-func Crit(msg string, ctx ...interface{}) {
- root.write(msg, LvlCrit, ctx, skipLevel)
- os.Exit(1)
-}
-
-// Output is a convenient alias for write, allowing for the modification of
-// the calldepth (number of stack frames to skip).
-// calldepth influences the reported line number of the log message.
-// A calldepth of zero reports the immediate caller of Output.
-// Non-zero calldepth skips as many stack frames.
-func Output(msg string, lvl Lvl, calldepth int, ctx ...interface{}) {
- root.write(msg, lvl, ctx, calldepth+skipLevel)
-}
diff --git a/log/syslog.go b/log/syslog.go
deleted file mode 100644
index 71a17b30b..000000000
--- a/log/syslog.go
+++ /dev/null
@@ -1,57 +0,0 @@
-// +build !windows,!plan9
-
-package log
-
-import (
- "log/syslog"
- "strings"
-)
-
-// SyslogHandler opens a connection to the system syslog daemon by calling
-// syslog.New and writes all records to it.
-func SyslogHandler(priority syslog.Priority, tag string, fmtr Format) (Handler, error) {
- wr, err := syslog.New(priority, tag)
- return sharedSyslog(fmtr, wr, err)
-}
-
-// SyslogNetHandler opens a connection to a log daemon over the network and writes
-// all log records to it.
-func SyslogNetHandler(net, addr string, priority syslog.Priority, tag string, fmtr Format) (Handler, error) {
- wr, err := syslog.Dial(net, addr, priority, tag)
- return sharedSyslog(fmtr, wr, err)
-}
-
-func sharedSyslog(fmtr Format, sysWr *syslog.Writer, err error) (Handler, error) {
- if err != nil {
- return nil, err
- }
- h := FuncHandler(func(r *Record) error {
- var syslogFn = sysWr.Info
- switch r.Lvl {
- case LvlCrit:
- syslogFn = sysWr.Crit
- case LvlError:
- syslogFn = sysWr.Err
- case LvlWarn:
- syslogFn = sysWr.Warning
- case LvlInfo:
- syslogFn = sysWr.Info
- case LvlDebug:
- syslogFn = sysWr.Debug
- case LvlTrace:
- syslogFn = func(m string) error { return nil } // There's no syslog level for trace
- }
-
- s := strings.TrimSpace(string(fmtr.Format(r)))
- return syslogFn(s)
- })
- return LazyHandler(&closingHandler{sysWr, h}), nil
-}
-
-func (m muster) SyslogHandler(priority syslog.Priority, tag string, fmtr Format) Handler {
- return must(SyslogHandler(priority, tag, fmtr))
-}
-
-func (m muster) SyslogNetHandler(net, addr string, priority syslog.Priority, tag string, fmtr Format) Handler {
- return must(SyslogNetHandler(net, addr, priority, tag, fmtr))
-}
diff --git a/log/term/LICENSE b/log/term/LICENSE
deleted file mode 100644
index f090cb42f..000000000
--- a/log/term/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2014 Simon Eskildsen
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
diff --git a/log/term/terminal_appengine.go b/log/term/terminal_appengine.go
deleted file mode 100644
index c1b5d2a3b..000000000
--- a/log/term/terminal_appengine.go
+++ /dev/null
@@ -1,13 +0,0 @@
-// Based on ssh/terminal:
-// Copyright 2013 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.
-
-// +build appengine
-
-package term
-
-// IsTty always returns false on AppEngine.
-func IsTty(fd uintptr) bool {
- return false
-}
diff --git a/log/term/terminal_darwin.go b/log/term/terminal_darwin.go
deleted file mode 100644
index 9e5b18668..000000000
--- a/log/term/terminal_darwin.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// Based on ssh/terminal:
-// Copyright 2013 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.
-// +build !appengine
-
-package term
-
-import "syscall"
-
-const ioctlReadTermios = syscall.TIOCGETA
-
-// Termios is syscall.Termios.
-type Termios syscall.Termios
diff --git a/log/term/terminal_freebsd.go b/log/term/terminal_freebsd.go
deleted file mode 100644
index cfaceab33..000000000
--- a/log/term/terminal_freebsd.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package term
-
-import (
- "syscall"
-)
-
-const ioctlReadTermios = syscall.TIOCGETA
-
-// Go 1.2 doesn't include Termios for FreeBSD. This should be added in 1.3 and this could be merged with terminal_darwin.
-type Termios struct {
- Iflag uint32
- Oflag uint32
- Cflag uint32
- Lflag uint32
- Cc [20]uint8
- Ispeed uint32
- Ospeed uint32
-}
diff --git a/log/term/terminal_linux.go b/log/term/terminal_linux.go
deleted file mode 100644
index 6cb862c05..000000000
--- a/log/term/terminal_linux.go
+++ /dev/null
@@ -1,15 +0,0 @@
-// Based on ssh/terminal:
-// Copyright 2013 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.
-
-// +build !appengine
-
-package term
-
-import "syscall"
-
-const ioctlReadTermios = syscall.TCGETS
-
-// Termios ...
-type Termios syscall.Termios
diff --git a/log/term/terminal_netbsd.go b/log/term/terminal_netbsd.go
deleted file mode 100644
index f9bb9e1c2..000000000
--- a/log/term/terminal_netbsd.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package term
-
-import "syscall"
-
-const ioctlReadTermios = syscall.TIOCGETA
-
-type Termios syscall.Termios
diff --git a/log/term/terminal_notwindows.go b/log/term/terminal_notwindows.go
deleted file mode 100644
index c9af534f6..000000000
--- a/log/term/terminal_notwindows.go
+++ /dev/null
@@ -1,20 +0,0 @@
-// Based on ssh/terminal:
-// Copyright 2011 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.
-
-// +build linux,!appengine darwin freebsd openbsd netbsd
-
-package term
-
-import (
- "syscall"
- "unsafe"
-)
-
-// IsTty returns true if the given file descriptor is a terminal.
-func IsTty(fd uintptr) bool {
- var termios Termios
- _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
- return err == 0
-}
diff --git a/log/term/terminal_openbsd.go b/log/term/terminal_openbsd.go
deleted file mode 100644
index f9bb9e1c2..000000000
--- a/log/term/terminal_openbsd.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package term
-
-import "syscall"
-
-const ioctlReadTermios = syscall.TIOCGETA
-
-type Termios syscall.Termios
diff --git a/log/term/terminal_solaris.go b/log/term/terminal_solaris.go
deleted file mode 100644
index 033c16324..000000000
--- a/log/term/terminal_solaris.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package term
-
-import "golang.org/x/sys/unix"
-
-// IsTty returns true if the given file descriptor is a terminal.
-func IsTty(fd uintptr) bool {
- _, err := unix.IoctlGetTermios(int(fd), unix.TCGETA)
- return err == nil
-}
diff --git a/log/term/terminal_windows.go b/log/term/terminal_windows.go
deleted file mode 100644
index df3c30c15..000000000
--- a/log/term/terminal_windows.go
+++ /dev/null
@@ -1,26 +0,0 @@
-// Based on ssh/terminal:
-// Copyright 2011 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.
-
-// +build windows
-
-package term
-
-import (
- "syscall"
- "unsafe"
-)
-
-var kernel32 = syscall.NewLazyDLL("kernel32.dll")
-
-var (
- procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
-)
-
-// IsTty returns true if the given file descriptor is a terminal.
-func IsTty(fd uintptr) bool {
- var st uint32
- r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0)
- return r != 0 && e == 0
-}