github.com/MetalBlockchain/metalgo@v1.11.9/snow/engine/avalanche/state/unique_vertex_test.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  	"bytes"
     8  	"context"
     9  	"errors"
    10  	"testing"
    11  
    12  	"github.com/stretchr/testify/require"
    13  
    14  	"github.com/MetalBlockchain/metalgo/database/memdb"
    15  	"github.com/MetalBlockchain/metalgo/ids"
    16  	"github.com/MetalBlockchain/metalgo/snow/choices"
    17  	"github.com/MetalBlockchain/metalgo/snow/consensus/snowstorm"
    18  	"github.com/MetalBlockchain/metalgo/snow/engine/avalanche/vertex"
    19  	"github.com/MetalBlockchain/metalgo/utils/hashing"
    20  	"github.com/MetalBlockchain/metalgo/utils/logging"
    21  )
    22  
    23  var errUnknownTx = errors.New("unknown tx")
    24  
    25  func newTestSerializer(t *testing.T, parse func(context.Context, []byte) (snowstorm.Tx, error)) *Serializer {
    26  	vm := vertex.TestVM{}
    27  	vm.T = t
    28  	vm.Default(true)
    29  	vm.ParseTxF = parse
    30  
    31  	baseDB := memdb.New()
    32  	s := NewSerializer(
    33  		SerializerConfig{
    34  			ChainID: ids.Empty,
    35  			VM:      &vm,
    36  			DB:      baseDB,
    37  			Log:     logging.NoLog{},
    38  		},
    39  	)
    40  
    41  	return s.(*Serializer)
    42  }
    43  
    44  func TestUnknownUniqueVertexErrors(t *testing.T) {
    45  	require := require.New(t)
    46  	s := newTestSerializer(t, nil)
    47  
    48  	uVtx := &uniqueVertex{
    49  		serializer: s,
    50  		id:         ids.Empty,
    51  	}
    52  
    53  	status := uVtx.Status()
    54  	require.Equal(choices.Unknown, status)
    55  
    56  	_, err := uVtx.Parents()
    57  	require.ErrorIs(err, errGetParents)
    58  
    59  	_, err = uVtx.Height()
    60  	require.ErrorIs(err, errGetHeight)
    61  
    62  	_, err = uVtx.Txs(context.Background())
    63  	require.ErrorIs(err, errGetTxs)
    64  }
    65  
    66  func TestUniqueVertexCacheHit(t *testing.T) {
    67  	require := require.New(t)
    68  
    69  	testTx := &snowstorm.TestTx{TestDecidable: choices.TestDecidable{
    70  		IDV: ids.ID{1},
    71  	}}
    72  
    73  	s := newTestSerializer(t, func(_ context.Context, b []byte) (snowstorm.Tx, error) {
    74  		require.Equal([]byte{0}, b)
    75  		return testTx, nil
    76  	})
    77  
    78  	id := ids.ID{2}
    79  	parentID := ids.ID{'p', 'a', 'r', 'e', 'n', 't'}
    80  	parentIDs := []ids.ID{parentID}
    81  	height := uint64(1)
    82  	vtx, err := vertex.Build( // regular, non-stop vertex
    83  		s.ChainID,
    84  		height,
    85  		parentIDs,
    86  		[][]byte{{0}},
    87  	)
    88  	require.NoError(err)
    89  
    90  	uVtx := &uniqueVertex{
    91  		id:         id,
    92  		serializer: s,
    93  	}
    94  	require.NoError(uVtx.setVertex(context.Background(), vtx))
    95  
    96  	newUVtx := &uniqueVertex{
    97  		id:         id,
    98  		serializer: s,
    99  	}
   100  
   101  	parents, err := newUVtx.Parents()
   102  	require.NoError(err)
   103  	require.Len(parents, 1)
   104  	require.Equal(parentID, parents[0].ID())
   105  
   106  	newHeight, err := newUVtx.Height()
   107  	require.NoError(err)
   108  	require.Equal(height, newHeight)
   109  
   110  	txs, err := newUVtx.Txs(context.Background())
   111  	require.NoError(err)
   112  	require.Len(txs, 1)
   113  	require.Equal(testTx, txs[0])
   114  
   115  	require.Equal(uVtx.v, newUVtx.v)
   116  }
   117  
   118  func TestUniqueVertexCacheMiss(t *testing.T) {
   119  	require := require.New(t)
   120  
   121  	txBytesParent := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}
   122  	testTxParent := &snowstorm.TestTx{
   123  		TestDecidable: choices.TestDecidable{
   124  			IDV:     ids.ID{1},
   125  			StatusV: choices.Accepted,
   126  		},
   127  		BytesV: txBytesParent,
   128  	}
   129  
   130  	txBytes := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}
   131  	testTx := &snowstorm.TestTx{
   132  		TestDecidable: choices.TestDecidable{
   133  			IDV: ids.ID{1},
   134  		},
   135  		BytesV: txBytes,
   136  	}
   137  	parseTx := func(_ context.Context, b []byte) (snowstorm.Tx, error) {
   138  		if bytes.Equal(txBytesParent, b) {
   139  			return testTxParent, nil
   140  		}
   141  		if bytes.Equal(txBytes, b) {
   142  			return testTx, nil
   143  		}
   144  		require.FailNow("asked to parse unexpected transaction")
   145  		return nil, nil
   146  	}
   147  
   148  	s := newTestSerializer(t, parseTx)
   149  
   150  	uvtxParent := newTestUniqueVertex(t, s, nil, [][]byte{txBytesParent}, false)
   151  	require.NoError(uvtxParent.Accept(context.Background()))
   152  
   153  	parentID := uvtxParent.ID()
   154  	parentIDs := []ids.ID{parentID}
   155  	height := uint64(1)
   156  	innerVertex, err := vertex.Build( // regular, non-stop vertex
   157  		s.ChainID,
   158  		height,
   159  		parentIDs,
   160  		[][]byte{txBytes},
   161  	)
   162  	require.NoError(err)
   163  
   164  	id := innerVertex.ID()
   165  	vtxBytes := innerVertex.Bytes()
   166  
   167  	uVtx := uniqueVertex{
   168  		id:         id,
   169  		serializer: s,
   170  	}
   171  
   172  	// Register a cache miss
   173  	require.Equal(choices.Unknown, uVtx.Status())
   174  
   175  	// Register cache hit
   176  	vtx, err := newUniqueVertex(context.Background(), s, vtxBytes)
   177  	require.NoError(err)
   178  
   179  	require.Equal(choices.Processing, vtx.Status())
   180  
   181  	validateVertex := func(vtx *uniqueVertex, expectedStatus choices.Status) {
   182  		require.Equal(expectedStatus, vtx.Status())
   183  
   184  		// Call bytes first to check for regression bug
   185  		// where it's unsafe to call Bytes or Verify directly
   186  		// after calling Status to refresh a vertex
   187  		require.Equal(vtxBytes, vtx.Bytes())
   188  
   189  		vtxParents, err := vtx.Parents()
   190  		require.NoError(err)
   191  		require.Len(vtxParents, 1)
   192  		require.Equal(parentID, vtxParents[0].ID())
   193  
   194  		vtxHeight, err := vtx.Height()
   195  		require.NoError(err)
   196  		require.Equal(height, vtxHeight)
   197  
   198  		vtxTxs, err := vtx.Txs(context.Background())
   199  		require.NoError(err)
   200  		require.Len(vtxTxs, 1)
   201  		require.Equal(txBytes, vtxTxs[0].Bytes())
   202  	}
   203  
   204  	// Replace the vertex, so that it loses reference to parents, etc.
   205  	vtx = &uniqueVertex{
   206  		id:         id,
   207  		serializer: s,
   208  	}
   209  
   210  	// Check that the vertex refreshed from the cache is valid
   211  	validateVertex(vtx, choices.Processing)
   212  
   213  	// Check that a newly parsed vertex refreshed from the cache is valid
   214  	vtx, err = newUniqueVertex(context.Background(), s, vtxBytes)
   215  	require.NoError(err)
   216  	validateVertex(vtx, choices.Processing)
   217  
   218  	// Check that refreshing a vertex when it has been removed from
   219  	// the cache works correctly
   220  
   221  	s.state.uniqueVtx.Flush()
   222  	vtx = &uniqueVertex{
   223  		id:         id,
   224  		serializer: s,
   225  	}
   226  	validateVertex(vtx, choices.Processing)
   227  
   228  	s.state.uniqueVtx.Flush()
   229  	vtx, err = newUniqueVertex(context.Background(), s, vtxBytes)
   230  	require.NoError(err)
   231  	validateVertex(vtx, choices.Processing)
   232  }
   233  
   234  func TestParseVertexWithIncorrectChainID(t *testing.T) {
   235  	require := require.New(t)
   236  
   237  	statelessVertex, err := vertex.Build( // regular, non-stop vertex
   238  		ids.GenerateTestID(),
   239  		0,
   240  		nil,
   241  		[][]byte{{1}},
   242  	)
   243  	require.NoError(err)
   244  	vtxBytes := statelessVertex.Bytes()
   245  
   246  	s := newTestSerializer(t, func(_ context.Context, b []byte) (snowstorm.Tx, error) {
   247  		if bytes.Equal(b, []byte{1}) {
   248  			return &snowstorm.TestTx{}, nil
   249  		}
   250  		return nil, errUnknownTx
   251  	})
   252  
   253  	_, err = s.ParseVtx(context.Background(), vtxBytes)
   254  	require.ErrorIs(err, errWrongChainID)
   255  }
   256  
   257  func TestParseVertexWithInvalidTxs(t *testing.T) {
   258  	require := require.New(t)
   259  
   260  	s := newTestSerializer(t, func(_ context.Context, b []byte) (snowstorm.Tx, error) {
   261  		switch {
   262  		case bytes.Equal(b, []byte{2}):
   263  			return &snowstorm.TestTx{}, nil
   264  		default:
   265  			return nil, errUnknownTx
   266  		}
   267  	})
   268  
   269  	statelessVertex, err := vertex.Build( // regular, non-stop vertex
   270  		s.ChainID,
   271  		0,
   272  		nil,
   273  		[][]byte{{1}},
   274  	)
   275  	require.NoError(err)
   276  	vtxBytes := statelessVertex.Bytes()
   277  
   278  	_, err = s.ParseVtx(context.Background(), vtxBytes)
   279  	require.ErrorIs(err, errUnknownTx)
   280  
   281  	_, err = s.ParseVtx(context.Background(), vtxBytes)
   282  	require.ErrorIs(err, errUnknownTx)
   283  
   284  	id := hashing.ComputeHash256Array(vtxBytes)
   285  	_, err = s.GetVtx(context.Background(), id)
   286  	require.ErrorIs(err, errUnknownVertex)
   287  
   288  	childStatelessVertex, err := vertex.Build( // regular, non-stop vertex
   289  		s.ChainID,
   290  		1,
   291  		[]ids.ID{id},
   292  		[][]byte{{2}},
   293  	)
   294  	require.NoError(err)
   295  	childVtxBytes := childStatelessVertex.Bytes()
   296  
   297  	childVtx, err := s.ParseVtx(context.Background(), childVtxBytes)
   298  	require.NoError(err)
   299  
   300  	parents, err := childVtx.Parents()
   301  	require.NoError(err)
   302  	require.Len(parents, 1)
   303  	parent := parents[0]
   304  
   305  	require.False(parent.Status().Fetched())
   306  }
   307  
   308  func newTestUniqueVertex(
   309  	t *testing.T,
   310  	s *Serializer,
   311  	parentIDs []ids.ID,
   312  	txs [][]byte,
   313  	stopVertex bool,
   314  ) *uniqueVertex {
   315  	require := require.New(t)
   316  
   317  	var (
   318  		vtx vertex.StatelessVertex
   319  		err error
   320  	)
   321  	if !stopVertex {
   322  		vtx, err = vertex.Build(
   323  			s.ChainID,
   324  			uint64(1),
   325  			parentIDs,
   326  			txs,
   327  		)
   328  	} else {
   329  		vtx, err = vertex.BuildStopVertex(
   330  			s.ChainID,
   331  			uint64(1),
   332  			parentIDs,
   333  		)
   334  	}
   335  	require.NoError(err)
   336  	uvtx, err := newUniqueVertex(context.Background(), s, vtx.Bytes())
   337  	require.NoError(err)
   338  	return uvtx
   339  }