github.com/MetalBlockchain/metalgo@v1.11.9/snow/engine/avalanche/state/state.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package state
     5  
     6  import (
     7  	"go.uber.org/zap"
     8  
     9  	"github.com/MetalBlockchain/metalgo/cache"
    10  	"github.com/MetalBlockchain/metalgo/database"
    11  	"github.com/MetalBlockchain/metalgo/ids"
    12  	"github.com/MetalBlockchain/metalgo/snow/choices"
    13  	"github.com/MetalBlockchain/metalgo/snow/engine/avalanche/vertex"
    14  	"github.com/MetalBlockchain/metalgo/utils/logging"
    15  	"github.com/MetalBlockchain/metalgo/utils/wrappers"
    16  )
    17  
    18  type state struct {
    19  	serializer *Serializer
    20  	log        logging.Logger
    21  
    22  	dbCache cache.Cacher[ids.ID, any]
    23  	db      database.Database
    24  }
    25  
    26  // Vertex retrieves the vertex with the given id from cache/disk.
    27  // Returns nil if it's not found.
    28  // TODO this should return an error
    29  func (s *state) Vertex(id ids.ID) vertex.StatelessVertex {
    30  	if vtxIntf, found := s.dbCache.Get(id); found {
    31  		vtx, _ := vtxIntf.(vertex.StatelessVertex)
    32  		return vtx
    33  	}
    34  
    35  	bytes, err := s.db.Get(id[:])
    36  	if err != nil {
    37  		s.log.Verbo("failed to get vertex from database",
    38  			zap.Binary("key", id[:]),
    39  			zap.Error(err),
    40  		)
    41  		s.dbCache.Put(id, nil)
    42  		return nil
    43  	}
    44  
    45  	vtx, err := s.serializer.parseVertex(bytes)
    46  	if err != nil {
    47  		s.log.Error("failed parsing saved vertex",
    48  			zap.Binary("key", id[:]),
    49  			zap.Binary("vertex", bytes),
    50  			zap.Error(err),
    51  		)
    52  		s.dbCache.Put(id, nil)
    53  		return nil
    54  	}
    55  
    56  	s.dbCache.Put(id, vtx)
    57  	return vtx
    58  }
    59  
    60  // SetVertex persists the vertex to the database and returns an error if it
    61  // fails to write to the db
    62  func (s *state) SetVertex(id ids.ID, vtx vertex.StatelessVertex) error {
    63  	s.dbCache.Put(id, vtx)
    64  
    65  	if vtx == nil {
    66  		return s.db.Delete(id[:])
    67  	}
    68  
    69  	return s.db.Put(id[:], vtx.Bytes())
    70  }
    71  
    72  func (s *state) Status(id ids.ID) choices.Status {
    73  	if statusIntf, found := s.dbCache.Get(id); found {
    74  		status, _ := statusIntf.(choices.Status)
    75  		return status
    76  	}
    77  
    78  	if val, err := database.GetUInt32(s.db, id[:]); err == nil {
    79  		// The key was in the database
    80  		status := choices.Status(val)
    81  		s.dbCache.Put(id, status)
    82  		return status
    83  	}
    84  
    85  	s.dbCache.Put(id, choices.Unknown)
    86  	return choices.Unknown
    87  }
    88  
    89  // SetStatus sets the status of the vertex and returns an error if it fails to write to the db
    90  func (s *state) SetStatus(id ids.ID, status choices.Status) error {
    91  	s.dbCache.Put(id, status)
    92  
    93  	if status == choices.Unknown {
    94  		return s.db.Delete(id[:])
    95  	}
    96  	return database.PutUInt32(s.db, id[:], uint32(status))
    97  }
    98  
    99  func (s *state) Edge(id ids.ID) []ids.ID {
   100  	if frontierIntf, found := s.dbCache.Get(id); found {
   101  		frontier, _ := frontierIntf.([]ids.ID)
   102  		return frontier
   103  	}
   104  
   105  	if b, err := s.db.Get(id[:]); err == nil {
   106  		p := wrappers.Packer{Bytes: b}
   107  
   108  		frontierSize := p.UnpackInt()
   109  		frontier := make([]ids.ID, frontierSize)
   110  		for i := 0; i < int(frontierSize) && !p.Errored(); i++ {
   111  			id, err := ids.ToID(p.UnpackFixedBytes(ids.IDLen))
   112  			p.Add(err)
   113  			frontier[i] = id
   114  		}
   115  
   116  		if p.Offset == len(b) && !p.Errored() {
   117  			s.dbCache.Put(id, frontier)
   118  			return frontier
   119  		}
   120  		s.log.Error("failed parsing saved edge",
   121  			zap.Binary("key", id[:]),
   122  			zap.Binary("edge", b),
   123  			zap.Error(err),
   124  		)
   125  	}
   126  
   127  	s.dbCache.Put(id, nil) // Cache the miss
   128  	return nil
   129  }
   130  
   131  // SetEdge sets the frontier and returns an error if it fails to write to the db
   132  func (s *state) SetEdge(id ids.ID, frontier []ids.ID) error {
   133  	s.dbCache.Put(id, frontier)
   134  
   135  	if len(frontier) == 0 {
   136  		return s.db.Delete(id[:])
   137  	}
   138  
   139  	size := wrappers.IntLen + ids.IDLen*len(frontier)
   140  	p := wrappers.Packer{Bytes: make([]byte, size)}
   141  	p.PackInt(uint32(len(frontier)))
   142  	for _, id := range frontier {
   143  		p.PackFixedBytes(id[:])
   144  	}
   145  
   146  	return s.db.Put(id[:], p.Bytes)
   147  }