github.com/MetalBlockchain/metalgo@v1.11.9/snow/engine/avalanche/state/unique_vertex.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  	"context"
     8  	"errors"
     9  	"fmt"
    10  	"strings"
    11  
    12  	"github.com/MetalBlockchain/metalgo/cache"
    13  	"github.com/MetalBlockchain/metalgo/ids"
    14  	"github.com/MetalBlockchain/metalgo/snow/choices"
    15  	"github.com/MetalBlockchain/metalgo/snow/consensus/avalanche"
    16  	"github.com/MetalBlockchain/metalgo/snow/consensus/snowstorm"
    17  	"github.com/MetalBlockchain/metalgo/snow/engine/avalanche/vertex"
    18  	"github.com/MetalBlockchain/metalgo/utils/formatting"
    19  	"github.com/MetalBlockchain/metalgo/utils/hashing"
    20  )
    21  
    22  var (
    23  	_ cache.Evictable[ids.ID] = (*uniqueVertex)(nil)
    24  	_ avalanche.Vertex        = (*uniqueVertex)(nil)
    25  
    26  	errGetParents = errors.New("failed to get parents for vertex")
    27  	errGetHeight  = errors.New("failed to get height for vertex")
    28  	errGetTxs     = errors.New("failed to get txs for vertex")
    29  )
    30  
    31  // uniqueVertex acts as a cache for vertices in the database.
    32  //
    33  // If a vertex is loaded, it will have one canonical uniqueVertex. The vertex
    34  // will eventually be evicted from memory, when the uniqueVertex is evicted from
    35  // the cache. If the uniqueVertex has a function called again after this
    36  // eviction, the vertex will be re-loaded from the database.
    37  type uniqueVertex struct {
    38  	serializer *Serializer
    39  
    40  	id ids.ID
    41  	v  *vertexState
    42  }
    43  
    44  // newUniqueVertex returns a uniqueVertex instance from [b] by checking the cache
    45  // and then parsing the vertex bytes on a cache miss.
    46  func newUniqueVertex(ctx context.Context, s *Serializer, b []byte) (*uniqueVertex, error) {
    47  	vtx := &uniqueVertex{
    48  		id:         hashing.ComputeHash256Array(b),
    49  		serializer: s,
    50  	}
    51  	vtx.shallowRefresh()
    52  
    53  	// If the vtx exists, then the vertex is already known
    54  	if vtx.v.vtx != nil {
    55  		return vtx, nil
    56  	}
    57  
    58  	// If it wasn't in the cache parse the vertex and set it
    59  	innerVertex, err := s.parseVertex(b)
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  	if err := innerVertex.Verify(); err != nil {
    64  		return nil, err
    65  	}
    66  
    67  	unparsedTxs := innerVertex.Txs()
    68  	txs := make([]snowstorm.Tx, len(unparsedTxs))
    69  	for i, txBytes := range unparsedTxs {
    70  		tx, err := vtx.serializer.VM.ParseTx(ctx, txBytes)
    71  		if err != nil {
    72  			return nil, err
    73  		}
    74  		txs[i] = tx
    75  	}
    76  
    77  	vtx.v.vtx = innerVertex
    78  	vtx.v.txs = txs
    79  
    80  	// If the vertex has already been fetched,
    81  	// skip persisting the vertex.
    82  	if vtx.v.status.Fetched() {
    83  		return vtx, nil
    84  	}
    85  
    86  	// The vertex is newly parsed, so set the status
    87  	// and persist it.
    88  	vtx.v.status = choices.Processing
    89  	return vtx, vtx.persist()
    90  }
    91  
    92  func (vtx *uniqueVertex) refresh() {
    93  	vtx.shallowRefresh()
    94  
    95  	if vtx.v.vtx == nil && vtx.v.status.Fetched() {
    96  		vtx.v.vtx = vtx.serializer.state.Vertex(vtx.ID())
    97  	}
    98  }
    99  
   100  // shallowRefresh checks the cache for the uniqueVertex and gets the
   101  // most up-to-date status for [vtx]
   102  // ensures that the status is up-to-date for this vertex
   103  // inner vertex may be nil after calling shallowRefresh
   104  func (vtx *uniqueVertex) shallowRefresh() {
   105  	if vtx.v == nil {
   106  		vtx.v = &vertexState{}
   107  	}
   108  	if vtx.v.latest {
   109  		return
   110  	}
   111  
   112  	latest := vtx.serializer.state.UniqueVertex(vtx)
   113  	prevVtx := vtx.v.vtx
   114  	if latest == vtx {
   115  		vtx.v.status = vtx.serializer.state.Status(vtx.ID())
   116  		vtx.v.latest = true
   117  	} else {
   118  		// If someone is in the cache, they must be up-to-date
   119  		*vtx = *latest
   120  	}
   121  
   122  	if vtx.v.vtx == nil {
   123  		vtx.v.vtx = prevVtx
   124  	}
   125  }
   126  
   127  func (vtx *uniqueVertex) Evict() {
   128  	if vtx.v != nil {
   129  		vtx.v.latest = false
   130  		// make sure the parents can be garbage collected
   131  		vtx.v.parents = nil
   132  	}
   133  }
   134  
   135  func (vtx *uniqueVertex) setVertex(ctx context.Context, innerVtx vertex.StatelessVertex) error {
   136  	vtx.shallowRefresh()
   137  	vtx.v.vtx = innerVtx
   138  
   139  	if vtx.v.status.Fetched() {
   140  		return nil
   141  	}
   142  
   143  	if _, err := vtx.Txs(ctx); err != nil {
   144  		return err
   145  	}
   146  
   147  	vtx.v.status = choices.Processing
   148  	return vtx.persist()
   149  }
   150  
   151  func (vtx *uniqueVertex) persist() error {
   152  	if err := vtx.serializer.state.SetVertex(vtx.v.vtx); err != nil {
   153  		return err
   154  	}
   155  	if err := vtx.serializer.state.SetStatus(vtx.ID(), vtx.v.status); err != nil {
   156  		return err
   157  	}
   158  	return vtx.serializer.versionDB.Commit()
   159  }
   160  
   161  func (vtx *uniqueVertex) setStatus(status choices.Status) error {
   162  	vtx.shallowRefresh()
   163  	if vtx.v.status == status {
   164  		return nil
   165  	}
   166  	vtx.v.status = status
   167  	return vtx.serializer.state.SetStatus(vtx.ID(), status)
   168  }
   169  
   170  func (vtx *uniqueVertex) ID() ids.ID {
   171  	return vtx.id
   172  }
   173  
   174  func (vtx *uniqueVertex) Key() ids.ID {
   175  	return vtx.id
   176  }
   177  
   178  func (vtx *uniqueVertex) Accept(ctx context.Context) error {
   179  	if err := vtx.setStatus(choices.Accepted); err != nil {
   180  		return err
   181  	}
   182  
   183  	vtx.serializer.edge.Add(vtx.id)
   184  	parents, err := vtx.Parents()
   185  	if err != nil {
   186  		return err
   187  	}
   188  
   189  	for _, parent := range parents {
   190  		vtx.serializer.edge.Remove(parent.ID())
   191  	}
   192  
   193  	if err := vtx.serializer.state.SetEdge(vtx.serializer.Edge(ctx)); err != nil {
   194  		return fmt.Errorf("failed to set edge while accepting vertex %s due to %w", vtx.id, err)
   195  	}
   196  
   197  	// Should never traverse into parents of a decided vertex. Allows for the
   198  	// parents to be garbage collected
   199  	vtx.v.parents = nil
   200  
   201  	return vtx.serializer.versionDB.Commit()
   202  }
   203  
   204  func (vtx *uniqueVertex) Reject(context.Context) error {
   205  	if err := vtx.setStatus(choices.Rejected); err != nil {
   206  		return err
   207  	}
   208  
   209  	// Should never traverse into parents of a decided vertex. Allows for the
   210  	// parents to be garbage collected
   211  	vtx.v.parents = nil
   212  
   213  	return vtx.serializer.versionDB.Commit()
   214  }
   215  
   216  // TODO: run performance test to see if shallow refreshing
   217  // (which will mean that refresh must be called in Bytes and Verify)
   218  // improves performance
   219  func (vtx *uniqueVertex) Status() choices.Status {
   220  	vtx.refresh()
   221  	return vtx.v.status
   222  }
   223  
   224  func (vtx *uniqueVertex) Parents() ([]avalanche.Vertex, error) {
   225  	vtx.refresh()
   226  
   227  	if vtx.v.vtx == nil {
   228  		return nil, fmt.Errorf("%w with status: %s", errGetParents, vtx.v.status)
   229  	}
   230  
   231  	parentIDs := vtx.v.vtx.ParentIDs()
   232  	if len(vtx.v.parents) != len(parentIDs) {
   233  		vtx.v.parents = make([]avalanche.Vertex, len(parentIDs))
   234  		for i, parentID := range parentIDs {
   235  			vtx.v.parents[i] = &uniqueVertex{
   236  				serializer: vtx.serializer,
   237  				id:         parentID,
   238  			}
   239  		}
   240  	}
   241  
   242  	return vtx.v.parents, nil
   243  }
   244  
   245  func (vtx *uniqueVertex) Height() (uint64, error) {
   246  	vtx.refresh()
   247  
   248  	if vtx.v.vtx == nil {
   249  		return 0, fmt.Errorf("%w with status: %s", errGetHeight, vtx.v.status)
   250  	}
   251  
   252  	return vtx.v.vtx.Height(), nil
   253  }
   254  
   255  func (vtx *uniqueVertex) Txs(ctx context.Context) ([]snowstorm.Tx, error) {
   256  	vtx.refresh()
   257  
   258  	if vtx.v.vtx == nil {
   259  		return nil, fmt.Errorf("%w with status: %s", errGetTxs, vtx.v.status)
   260  	}
   261  
   262  	txs := vtx.v.vtx.Txs()
   263  	if len(txs) != len(vtx.v.txs) {
   264  		vtx.v.txs = make([]snowstorm.Tx, len(txs))
   265  		for i, txBytes := range txs {
   266  			tx, err := vtx.serializer.VM.ParseTx(ctx, txBytes)
   267  			if err != nil {
   268  				return nil, err
   269  			}
   270  			vtx.v.txs[i] = tx
   271  		}
   272  	}
   273  
   274  	return vtx.v.txs, nil
   275  }
   276  
   277  func (vtx *uniqueVertex) Bytes() []byte {
   278  	return vtx.v.vtx.Bytes()
   279  }
   280  
   281  func (vtx *uniqueVertex) String() string {
   282  	sb := strings.Builder{}
   283  
   284  	parents, err := vtx.Parents()
   285  	if err != nil {
   286  		sb.WriteString(fmt.Sprintf("Vertex(ID = %s, Error=error while retrieving vertex parents: %s)", vtx.ID(), err))
   287  		return sb.String()
   288  	}
   289  	txs, err := vtx.Txs(context.Background())
   290  	if err != nil {
   291  		sb.WriteString(fmt.Sprintf("Vertex(ID = %s, Error=error while retrieving vertex txs: %s)", vtx.ID(), err))
   292  		return sb.String()
   293  	}
   294  
   295  	sb.WriteString(fmt.Sprintf(
   296  		"Vertex(ID = %s, Status = %s, Number of Dependencies = %d, Number of Transactions = %d)",
   297  		vtx.ID(),
   298  		vtx.Status(),
   299  		len(parents),
   300  		len(txs),
   301  	))
   302  
   303  	parentFormat := fmt.Sprintf("\n    Parent[%s]: ID = %%s, Status = %%s", //nolint:perfsprint
   304  		formatting.IntFormat(len(parents)-1))
   305  	for i, parent := range parents {
   306  		sb.WriteString(fmt.Sprintf(parentFormat, i, parent.ID(), parent.Status()))
   307  	}
   308  
   309  	txFormat := fmt.Sprintf("\n    Transaction[%s]: ID = %%s, Status = %%s", //nolint:perfsprint
   310  		formatting.IntFormat(len(txs)-1))
   311  	for i, tx := range txs {
   312  		sb.WriteString(fmt.Sprintf(txFormat, i, tx.ID(), tx.Status()))
   313  	}
   314  
   315  	return sb.String()
   316  }
   317  
   318  type vertexState struct {
   319  	latest bool
   320  
   321  	vtx    vertex.StatelessVertex
   322  	status choices.Status
   323  
   324  	parents []avalanche.Vertex
   325  	txs     []snowstorm.Tx
   326  }