gitlab.com/flarenetwork/coreth@v0.1.1/core/state_manager.go (about)

     1  // (c) 2019-2020, Ava Labs, Inc.
     2  //
     3  // This file is a derived work, based on the go-ethereum library whose original
     4  // notices appear below.
     5  //
     6  // It is distributed under a license compatible with the licensing terms of the
     7  // original code from which it is derived.
     8  //
     9  // Much love to the original authors for their work.
    10  // **********
    11  // Copyright 2014 The go-ethereum Authors
    12  // This file is part of the go-ethereum library.
    13  //
    14  // The go-ethereum library is free software: you can redistribute it and/or modify
    15  // it under the terms of the GNU Lesser General Public License as published by
    16  // the Free Software Foundation, either version 3 of the License, or
    17  // (at your option) any later version.
    18  //
    19  // The go-ethereum library is distributed in the hope that it will be useful,
    20  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    21  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    22  // GNU Lesser General Public License for more details.
    23  //
    24  // You should have received a copy of the GNU Lesser General Public License
    25  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    26  
    27  package core
    28  
    29  import (
    30  	"fmt"
    31  	"math/rand"
    32  
    33  	"github.com/ethereum/go-ethereum/common"
    34  	"github.com/ethereum/go-ethereum/ethdb"
    35  	"gitlab.com/flarenetwork/coreth/core/types"
    36  )
    37  
    38  const (
    39  	commitInterval = 4096
    40  	tipBufferSize  = 16
    41  )
    42  
    43  type TrieWriter interface {
    44  	InsertTrie(block *types.Block) error // Insert reference to trie [root]
    45  	AcceptTrie(block *types.Block) error // Mark [root] as part of an accepted block
    46  	RejectTrie(block *types.Block) error // Notify TrieWriter that the block containing [root] has been rejected
    47  	Shutdown() error
    48  }
    49  
    50  type TrieDB interface {
    51  	Reference(child common.Hash, parent common.Hash)
    52  	Dereference(root common.Hash)
    53  	Commit(root common.Hash, report bool, callback func(common.Hash)) error
    54  	Size() (common.StorageSize, common.StorageSize)
    55  	Cap(limit common.StorageSize) error
    56  }
    57  
    58  func NewTrieWriter(db TrieDB, config *CacheConfig) TrieWriter {
    59  	if config.Pruning {
    60  		return &cappedMemoryTrieWriter{
    61  			TrieDB:             db,
    62  			memoryCap:          common.StorageSize(config.TrieDirtyLimit) * 1024 * 1024,
    63  			imageCap:           4 * 1024 * 1024,
    64  			commitInterval:     commitInterval,
    65  			tipBuffer:          make([]common.Hash, tipBufferSize),
    66  			randomizedInterval: uint64(rand.Int63n(commitInterval)) + commitInterval,
    67  		}
    68  	} else {
    69  		return &noPruningTrieWriter{
    70  			TrieDB: db,
    71  		}
    72  	}
    73  }
    74  
    75  type noPruningTrieWriter struct {
    76  	TrieDB
    77  }
    78  
    79  func (np *noPruningTrieWriter) InsertTrie(block *types.Block) error {
    80  	return np.TrieDB.Commit(block.Root(), false, nil)
    81  }
    82  
    83  func (np *noPruningTrieWriter) AcceptTrie(block *types.Block) error { return nil }
    84  
    85  func (np *noPruningTrieWriter) RejectTrie(block *types.Block) error { return nil }
    86  
    87  func (np *noPruningTrieWriter) Shutdown() error { return nil }
    88  
    89  type cappedMemoryTrieWriter struct {
    90  	TrieDB
    91  	memoryCap                          common.StorageSize
    92  	imageCap                           common.StorageSize
    93  	commitInterval, randomizedInterval uint64
    94  
    95  	lastPos   int
    96  	tipBuffer []common.Hash
    97  }
    98  
    99  func (cm *cappedMemoryTrieWriter) InsertTrie(block *types.Block) error {
   100  	cm.TrieDB.Reference(block.Root(), common.Hash{})
   101  
   102  	nodes, imgs := cm.TrieDB.Size()
   103  	if nodes > cm.memoryCap || imgs > cm.imageCap {
   104  		return cm.TrieDB.Cap(cm.memoryCap - ethdb.IdealBatchSize)
   105  	}
   106  
   107  	return nil
   108  }
   109  
   110  func (cm *cappedMemoryTrieWriter) AcceptTrie(block *types.Block) error {
   111  	root := block.Root()
   112  
   113  	// Attempt to dereference roots at least [tipBufferSize] old (so queries at tip
   114  	// can still be completed).
   115  	//
   116  	// Note: It is safe to dereference roots that have been committed to disk
   117  	// (they are no-ops).
   118  	nextPos := (cm.lastPos + 1) % tipBufferSize
   119  	if cm.tipBuffer[nextPos] != (common.Hash{}) {
   120  		cm.TrieDB.Dereference(cm.tipBuffer[nextPos])
   121  	}
   122  	cm.tipBuffer[nextPos] = root
   123  	cm.lastPos = nextPos
   124  
   125  	// Commit this root if we haven't committed an accepted block root within
   126  	// the desired interval.
   127  	// Note: a randomized interval is added here to ensure that pruning nodes
   128  	// do not all only commit at the exact same heights.
   129  	if height := block.NumberU64(); height%cm.commitInterval == 0 || height%cm.randomizedInterval == 0 {
   130  		if err := cm.TrieDB.Commit(root, true, nil); err != nil {
   131  			return fmt.Errorf("failed to commit trie for block %s: %w", block.Hash().Hex(), err)
   132  		}
   133  	}
   134  	return nil
   135  }
   136  
   137  func (cm *cappedMemoryTrieWriter) RejectTrie(block *types.Block) error {
   138  	cm.TrieDB.Dereference(block.Root())
   139  	return nil
   140  }
   141  
   142  func (cm *cappedMemoryTrieWriter) Shutdown() error {
   143  	// If [tipBuffer] entry is empty, no need to do any cleanup on
   144  	// shutdown.
   145  	if cm.tipBuffer[cm.lastPos] == (common.Hash{}) {
   146  		return nil
   147  	}
   148  
   149  	// Attempt to commit last item added to [dereferenceQueue] on shutdown to avoid
   150  	// re-processing the state on the next startup.
   151  	return cm.TrieDB.Commit(cm.tipBuffer[cm.lastPos], true, nil)
   152  }