github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/core/state/database.go (about)

     1  // This file is part of the go-sberex library. The go-sberex library is 
     2  // free software: you can redistribute it and/or modify it under the terms 
     3  // of the GNU Lesser General Public License as published by the Free 
     4  // Software Foundation, either version 3 of the License, or (at your option)
     5  // any later version.
     6  //
     7  // The go-sberex library is distributed in the hope that it will be useful, 
     8  // but WITHOUT ANY WARRANTY; without even the implied warranty of
     9  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 
    10  // General Public License <http://www.gnu.org/licenses/> for more details.
    11  
    12  package state
    13  
    14  import (
    15  	"fmt"
    16  	"sync"
    17  
    18  	"github.com/Sberex/go-sberex/common"
    19  	"github.com/Sberex/go-sberex/ethdb"
    20  	"github.com/Sberex/go-sberex/trie"
    21  	lru "github.com/hashicorp/golang-lru"
    22  )
    23  
    24  // Trie cache generation limit after which to evic trie nodes from memory.
    25  var MaxTrieCacheGen = uint16(120)
    26  
    27  const (
    28  	// Number of past tries to keep. This value is chosen such that
    29  	// reasonable chain reorg depths will hit an existing trie.
    30  	maxPastTries = 12
    31  
    32  	// Number of codehash->size associations to keep.
    33  	codeSizeCacheSize = 100000
    34  )
    35  
    36  // Database wraps access to tries and contract code.
    37  type Database interface {
    38  	// OpenTrie opens the main account trie.
    39  	OpenTrie(root common.Hash) (Trie, error)
    40  
    41  	// OpenStorageTrie opens the storage trie of an account.
    42  	OpenStorageTrie(addrHash, root common.Hash) (Trie, error)
    43  
    44  	// CopyTrie returns an independent copy of the given trie.
    45  	CopyTrie(Trie) Trie
    46  
    47  	// ContractCode retrieves a particular contract's code.
    48  	ContractCode(addrHash, codeHash common.Hash) ([]byte, error)
    49  
    50  	// ContractCodeSize retrieves a particular contracts code's size.
    51  	ContractCodeSize(addrHash, codeHash common.Hash) (int, error)
    52  
    53  	// TrieDB retrieves the low level trie database used for data storage.
    54  	TrieDB() *trie.Database
    55  }
    56  
    57  // Trie is a Sberex Merkle Trie.
    58  type Trie interface {
    59  	TryGet(key []byte) ([]byte, error)
    60  	TryUpdate(key, value []byte) error
    61  	TryDelete(key []byte) error
    62  	Commit(onleaf trie.LeafCallback) (common.Hash, error)
    63  	Hash() common.Hash
    64  	NodeIterator(startKey []byte) trie.NodeIterator
    65  	GetKey([]byte) []byte // TODO(fjl): remove this when SecureTrie is removed
    66  	Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error
    67  }
    68  
    69  // NewDatabase creates a backing store for state. The returned database is safe for
    70  // concurrent use and retains cached trie nodes in memory. The pool is an optional
    71  // intermediate trie-node memory pool between the low level storage layer and the
    72  // high level trie abstraction.
    73  func NewDatabase(db ethdb.Database) Database {
    74  	csc, _ := lru.New(codeSizeCacheSize)
    75  	return &cachingDB{
    76  		db:            trie.NewDatabase(db),
    77  		codeSizeCache: csc,
    78  	}
    79  }
    80  
    81  type cachingDB struct {
    82  	db            *trie.Database
    83  	mu            sync.Mutex
    84  	pastTries     []*trie.SecureTrie
    85  	codeSizeCache *lru.Cache
    86  }
    87  
    88  // OpenTrie opens the main account trie.
    89  func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
    90  	db.mu.Lock()
    91  	defer db.mu.Unlock()
    92  
    93  	for i := len(db.pastTries) - 1; i >= 0; i-- {
    94  		if db.pastTries[i].Hash() == root {
    95  			return cachedTrie{db.pastTries[i].Copy(), db}, nil
    96  		}
    97  	}
    98  	tr, err := trie.NewSecure(root, db.db, MaxTrieCacheGen)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  	return cachedTrie{tr, db}, nil
   103  }
   104  
   105  func (db *cachingDB) pushTrie(t *trie.SecureTrie) {
   106  	db.mu.Lock()
   107  	defer db.mu.Unlock()
   108  
   109  	if len(db.pastTries) >= maxPastTries {
   110  		copy(db.pastTries, db.pastTries[1:])
   111  		db.pastTries[len(db.pastTries)-1] = t
   112  	} else {
   113  		db.pastTries = append(db.pastTries, t)
   114  	}
   115  }
   116  
   117  // OpenStorageTrie opens the storage trie of an account.
   118  func (db *cachingDB) OpenStorageTrie(addrHash, root common.Hash) (Trie, error) {
   119  	return trie.NewSecure(root, db.db, 0)
   120  }
   121  
   122  // CopyTrie returns an independent copy of the given trie.
   123  func (db *cachingDB) CopyTrie(t Trie) Trie {
   124  	switch t := t.(type) {
   125  	case cachedTrie:
   126  		return cachedTrie{t.SecureTrie.Copy(), db}
   127  	case *trie.SecureTrie:
   128  		return t.Copy()
   129  	default:
   130  		panic(fmt.Errorf("unknown trie type %T", t))
   131  	}
   132  }
   133  
   134  // ContractCode retrieves a particular contract's code.
   135  func (db *cachingDB) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) {
   136  	code, err := db.db.Node(codeHash)
   137  	if err == nil {
   138  		db.codeSizeCache.Add(codeHash, len(code))
   139  	}
   140  	return code, err
   141  }
   142  
   143  // ContractCodeSize retrieves a particular contracts code's size.
   144  func (db *cachingDB) ContractCodeSize(addrHash, codeHash common.Hash) (int, error) {
   145  	if cached, ok := db.codeSizeCache.Get(codeHash); ok {
   146  		return cached.(int), nil
   147  	}
   148  	code, err := db.ContractCode(addrHash, codeHash)
   149  	if err == nil {
   150  		db.codeSizeCache.Add(codeHash, len(code))
   151  	}
   152  	return len(code), err
   153  }
   154  
   155  // TrieDB retrieves any intermediate trie-node caching layer.
   156  func (db *cachingDB) TrieDB() *trie.Database {
   157  	return db.db
   158  }
   159  
   160  // cachedTrie inserts its trie into a cachingDB on commit.
   161  type cachedTrie struct {
   162  	*trie.SecureTrie
   163  	db *cachingDB
   164  }
   165  
   166  func (m cachedTrie) Commit(onleaf trie.LeafCallback) (common.Hash, error) {
   167  	root, err := m.SecureTrie.Commit(onleaf)
   168  	if err == nil {
   169  		m.db.pushTrie(m.SecureTrie)
   170  	}
   171  	return root, err
   172  }
   173  
   174  func (m cachedTrie) Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error {
   175  	return m.SecureTrie.Prove(key, fromLevel, proofDb)
   176  }