github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/gossip/store.go (about)

     1  package gossip
     2  
     3  import (
     4  	"sync"
     5  	"sync/atomic"
     6  	"time"
     7  
     8  	"github.com/unicornultrafoundation/go-u2u/common"
     9  	"github.com/unicornultrafoundation/go-u2u/log"
    10  
    11  	"github.com/unicornultrafoundation/go-helios/common/bigendian"
    12  	"github.com/unicornultrafoundation/go-helios/u2udb"
    13  	"github.com/unicornultrafoundation/go-helios/u2udb/flushable"
    14  	"github.com/unicornultrafoundation/go-helios/u2udb/memorydb"
    15  	"github.com/unicornultrafoundation/go-helios/u2udb/table"
    16  	"github.com/unicornultrafoundation/go-helios/utils/wlru"
    17  	"github.com/unicornultrafoundation/go-u2u/gossip/evmstore"
    18  	txtracer "github.com/unicornultrafoundation/go-u2u/gossip/txtracer"
    19  	"github.com/unicornultrafoundation/go-u2u/logger"
    20  	"github.com/unicornultrafoundation/go-u2u/utils/adapters/snap2udb"
    21  	"github.com/unicornultrafoundation/go-u2u/utils/dbutil/switchable"
    22  	"github.com/unicornultrafoundation/go-u2u/utils/eventid"
    23  	"github.com/unicornultrafoundation/go-u2u/utils/randat"
    24  	"github.com/unicornultrafoundation/go-u2u/utils/rlpstore"
    25  )
    26  
    27  // Store is a node persistent storage working over physical key-value database.
    28  type Store struct {
    29  	dbs u2udb.FlushableDBProducer
    30  	cfg StoreConfig
    31  
    32  	snapshotedEVMDB *switchable.Snapshot
    33  	evm             *evmstore.Store
    34  	txtracer        *txtracer.Store
    35  	table           struct {
    36  		Version u2udb.Store `table:"_"`
    37  
    38  		// Main DAG tables
    39  		BlockEpochState        u2udb.Store `table:"D"`
    40  		BlockEpochStateHistory u2udb.Store `table:"h"`
    41  		Events                 u2udb.Store `table:"e"`
    42  		Blocks                 u2udb.Store `table:"b"`
    43  		EpochBlocks            u2udb.Store `table:"P"`
    44  		Genesis                u2udb.Store `table:"g"`
    45  		UpgradeHeights         u2udb.Store `table:"U"`
    46  
    47  		// Transaction traces
    48  		TransactionTraces u2udb.Store `table:"t"`
    49  
    50  		// P2P-only
    51  		HighestLamport u2udb.Store `table:"l"`
    52  
    53  		// Network version
    54  		NetworkVersion u2udb.Store `table:"V"`
    55  
    56  		// API-only
    57  		BlockHashes u2udb.Store `table:"B"`
    58  
    59  		LlrState           u2udb.Store `table:"S"`
    60  		LlrBlockResults    u2udb.Store `table:"R"`
    61  		LlrEpochResults    u2udb.Store `table:"Q"`
    62  		LlrBlockVotes      u2udb.Store `table:"T"`
    63  		LlrBlockVotesIndex u2udb.Store `table:"J"`
    64  		LlrEpochVotes      u2udb.Store `table:"E"`
    65  		LlrEpochVoteIndex  u2udb.Store `table:"I"`
    66  		LlrLastBlockVotes  u2udb.Store `table:"G"`
    67  		LlrLastEpochVote   u2udb.Store `table:"F"`
    68  	}
    69  
    70  	prevFlushTime time.Time
    71  
    72  	epochStore atomic.Value
    73  
    74  	cache struct {
    75  		Events                 *wlru.Cache `cache:"-"` // store by pointer
    76  		EventIDs               *eventid.Cache
    77  		EventsHeaders          *wlru.Cache  `cache:"-"` // store by pointer
    78  		Blocks                 *wlru.Cache  `cache:"-"` // store by pointer
    79  		BlockHashes            *wlru.Cache  `cache:"-"` // store by value
    80  		BlockRecordHashes      *wlru.Cache  `cache:"-"` // store by value
    81  		EvmBlocks              *wlru.Cache  `cache:"-"` // store by pointer
    82  		BlockEpochStateHistory *wlru.Cache  `cache:"-"` // store by pointer
    83  		BlockEpochState        atomic.Value // store by value
    84  		HighestLamport         atomic.Value // store by value
    85  		LastBVs                atomic.Value // store by pointer
    86  		LastEV                 atomic.Value // store by pointer
    87  		LlrState               atomic.Value // store by value
    88  		KvdbEvmSnap            atomic.Value // store by pointer
    89  		UpgradeHeights         atomic.Value // store by pointer
    90  		Genesis                atomic.Value // store by value
    91  		LlrBlockVotesIndex     *VotesCache  // store by pointer
    92  		LlrEpochVoteIndex      *VotesCache  // store by pointer
    93  	}
    94  
    95  	mutex struct {
    96  		WriteLlrState sync.Mutex
    97  	}
    98  
    99  	rlp rlpstore.Helper
   100  
   101  	logger.Instance
   102  }
   103  
   104  // NewMemStore creates store over memory map.
   105  func NewMemStore() *Store {
   106  	mems := memorydb.NewProducer("")
   107  	dbs := flushable.NewSyncedPool(mems, []byte{0})
   108  	cfg := LiteStoreConfig()
   109  
   110  	return NewStore(dbs, cfg)
   111  }
   112  
   113  // NewStore creates store over key-value db.
   114  func NewStore(dbs u2udb.FlushableDBProducer, cfg StoreConfig) *Store {
   115  	s := &Store{
   116  		dbs:           dbs,
   117  		cfg:           cfg,
   118  		Instance:      logger.New("gossip-store"),
   119  		prevFlushTime: time.Now(),
   120  		rlp:           rlpstore.Helper{logger.New("rlp")},
   121  	}
   122  
   123  	err := table.OpenTables(&s.table, dbs, "gossip")
   124  	if err != nil {
   125  		log.Crit("Failed to open DB", "name", "gossip", "err", err)
   126  	}
   127  
   128  	s.initCache()
   129  	s.evm = evmstore.NewStore(dbs, cfg.EVM)
   130  
   131  	if cfg.TraceTransactions {
   132  		s.txtracer = txtracer.NewStore(s.table.TransactionTraces)
   133  	}
   134  
   135  	if err := s.migrateData(); err != nil {
   136  		s.Log.Crit("Failed to migrate Gossip DB", "err", err)
   137  	}
   138  
   139  	return s
   140  }
   141  
   142  func (s *Store) initCache() {
   143  	s.cache.Events = s.makeCache(s.cfg.Cache.EventsSize, s.cfg.Cache.EventsNum)
   144  	s.cache.Blocks = s.makeCache(s.cfg.Cache.BlocksSize, s.cfg.Cache.BlocksNum)
   145  
   146  	blockHashesNum := s.cfg.Cache.BlocksNum
   147  	blockHashesCacheSize := nominalSize * uint(blockHashesNum)
   148  	s.cache.BlockHashes = s.makeCache(blockHashesCacheSize, blockHashesNum)
   149  	s.cache.BlockRecordHashes = s.makeCache(blockHashesCacheSize, blockHashesNum)
   150  
   151  	eventsHeadersNum := s.cfg.Cache.EventsNum
   152  	eventsHeadersCacheSize := nominalSize * uint(eventsHeadersNum)
   153  	s.cache.EventsHeaders = s.makeCache(eventsHeadersCacheSize, eventsHeadersNum)
   154  
   155  	s.cache.EventIDs = eventid.NewCache(s.cfg.Cache.EventsIDsNum)
   156  
   157  	blockEpochStatesNum := s.cfg.Cache.BlockEpochStateNum
   158  	blockEpochStatesSize := nominalSize * uint(blockEpochStatesNum)
   159  	s.cache.BlockEpochStateHistory = s.makeCache(blockEpochStatesSize, blockEpochStatesNum)
   160  
   161  	s.cache.LlrBlockVotesIndex = NewVotesCache(s.cfg.Cache.LlrBlockVotesIndexes, s.flushLlrBlockVoteWeight)
   162  	s.cache.LlrEpochVoteIndex = NewVotesCache(s.cfg.Cache.LlrEpochVotesIndexes, s.flushLlrEpochVoteWeight)
   163  }
   164  
   165  // Close closes underlying database.
   166  func (s *Store) Close() {
   167  	setnil := func() interface{} {
   168  		return nil
   169  	}
   170  
   171  	if s.snapshotedEVMDB != nil {
   172  		s.snapshotedEVMDB.Release()
   173  	}
   174  	_ = table.CloseTables(&s.table)
   175  	table.MigrateTables(&s.table, nil)
   176  	table.MigrateCaches(&s.cache, setnil)
   177  
   178  	if s.txtracer != nil {
   179  		s.txtracer.Close()
   180  	}
   181  	_ = s.closeEpochStore()
   182  	s.evm.Close()
   183  }
   184  
   185  func (s *Store) IsCommitNeeded() bool {
   186  	// randomize flushing criteria for each epoch so that nodes would desynchronize flushes
   187  	ratio := 900 + randat.RandAt(uint64(s.GetEpoch()))%100
   188  	return s.isCommitNeeded(ratio, ratio)
   189  }
   190  
   191  func (s *Store) isCommitNeeded(sc, tc uint64) bool {
   192  	period := s.cfg.MaxNonFlushedPeriod * time.Duration(sc) / 1000
   193  	size := (uint64(s.cfg.MaxNonFlushedSize) / 2) * tc / 1000
   194  	return time.Since(s.prevFlushTime) > period ||
   195  		uint64(s.dbs.NotFlushedSizeEst()) > size
   196  }
   197  
   198  // commitEVM commits EVM storage
   199  func (s *Store) commitEVM(flush bool) {
   200  	bs := s.GetBlockState()
   201  	err := s.evm.Commit(bs.LastBlock.Idx, bs.FinalizedStateRoot, flush)
   202  	if err != nil {
   203  		s.Log.Crit("Failed to commit EVM storage", "err", err)
   204  	}
   205  	s.evm.Cap()
   206  }
   207  
   208  func (s *Store) cleanCommitEVM() {
   209  	err := s.evm.CleanCommit(s.GetBlockState())
   210  	if err != nil {
   211  		s.Log.Crit("Failed to commit EVM storage", "err", err)
   212  	}
   213  	s.evm.Cap()
   214  }
   215  
   216  func (s *Store) GenerateSnapshotAt(root common.Hash, async bool) (err error) {
   217  	err = s.generateSnapshotAt(s.evm, root, true, async)
   218  	if err != nil {
   219  		s.Log.Error("EVM snapshot", "at", root, "err", err)
   220  	} else {
   221  		gen, _ := s.evm.Snaps.Generating()
   222  		s.Log.Info("EVM snapshot", "at", root, "generating", gen)
   223  	}
   224  	return err
   225  }
   226  
   227  func (s *Store) generateSnapshotAt(evmStore *evmstore.Store, root common.Hash, rebuild, async bool) (err error) {
   228  	return evmStore.GenerateEvmSnapshot(root, rebuild, async)
   229  }
   230  
   231  // Commit changes.
   232  func (s *Store) Commit() error {
   233  	s.FlushBlockEpochState()
   234  	s.FlushHighestLamport()
   235  	s.FlushLastBVs()
   236  	s.FlushLastEV()
   237  	s.FlushLlrState()
   238  	s.cache.LlrBlockVotesIndex.FlushMutated(s.flushLlrBlockVoteWeight)
   239  	s.cache.LlrEpochVoteIndex.FlushMutated(s.flushLlrEpochVoteWeight)
   240  	es := s.getAnyEpochStore()
   241  	if es != nil {
   242  		es.FlushHeads()
   243  		es.FlushLastEvents()
   244  	}
   245  	return s.flushDBs()
   246  }
   247  
   248  func (s *Store) flushDBs() error {
   249  	s.prevFlushTime = time.Now()
   250  	flushID := bigendian.Uint64ToBytes(uint64(s.prevFlushTime.UnixNano()))
   251  	return s.dbs.Flush(flushID)
   252  }
   253  
   254  func (s *Store) EvmStore() *evmstore.Store {
   255  	return s.evm
   256  }
   257  
   258  func (s *Store) TxTraceStore() *txtracer.Store {
   259  	return s.txtracer
   260  }
   261  
   262  func (s *Store) CaptureEvmKvdbSnapshot() {
   263  	if s.evm.Snaps == nil {
   264  		return
   265  	}
   266  	gen, err := s.evm.Snaps.Generating()
   267  	if err != nil {
   268  		s.Log.Error("Failed to check EVM snapshot generation", "err", err)
   269  		return
   270  	}
   271  	if gen {
   272  		return
   273  	}
   274  	newEvmKvdbSnap, err := s.evm.EVMDB().GetSnapshot()
   275  	if err != nil {
   276  		s.Log.Error("Failed to initialize frozen KVDB", "err", err)
   277  		return
   278  	}
   279  	if s.snapshotedEVMDB == nil {
   280  		s.snapshotedEVMDB = switchable.Wrap(newEvmKvdbSnap)
   281  	} else {
   282  		old := s.snapshotedEVMDB.SwitchTo(newEvmKvdbSnap)
   283  		// release only after DB is atomically switched
   284  		if old != nil {
   285  			old.Release()
   286  		}
   287  	}
   288  	newStore := s.evm.ResetWithEVMDB(snap2udb.Wrap(s.snapshotedEVMDB))
   289  	newStore.Snaps = nil
   290  	root := s.GetBlockState().FinalizedStateRoot
   291  	err = s.generateSnapshotAt(newStore, common.Hash(root), false, false)
   292  	if err != nil {
   293  		s.Log.Error("Failed to initialize EVM snapshot for frozen KVDB", "err", err)
   294  		return
   295  	}
   296  	s.cache.KvdbEvmSnap.Store(newStore)
   297  }
   298  
   299  func (s *Store) LastKvdbEvmSnapshot() *evmstore.Store {
   300  	if v := s.cache.KvdbEvmSnap.Load(); v != nil {
   301  		return v.(*evmstore.Store)
   302  	}
   303  	return s.evm
   304  }
   305  
   306  /*
   307   * Utils:
   308   */
   309  
   310  func (s *Store) makeCache(weight uint, size int) *wlru.Cache {
   311  	cache, err := wlru.New(weight, size)
   312  	if err != nil {
   313  		s.Log.Crit("Failed to create LRU cache", "err", err)
   314  		return nil
   315  	}
   316  	return cache
   317  }