github.com/MetalBlockchain/metalgo@v1.11.9/vms/avm/state/state_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  	"testing"
     8  	"time"
     9  
    10  	"github.com/prometheus/client_golang/prometheus"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	"github.com/MetalBlockchain/metalgo/database"
    14  	"github.com/MetalBlockchain/metalgo/database/memdb"
    15  	"github.com/MetalBlockchain/metalgo/database/versiondb"
    16  	"github.com/MetalBlockchain/metalgo/ids"
    17  	"github.com/MetalBlockchain/metalgo/version"
    18  	"github.com/MetalBlockchain/metalgo/vms/avm/block"
    19  	"github.com/MetalBlockchain/metalgo/vms/avm/fxs"
    20  	"github.com/MetalBlockchain/metalgo/vms/avm/txs"
    21  	"github.com/MetalBlockchain/metalgo/vms/components/avax"
    22  	"github.com/MetalBlockchain/metalgo/vms/secp256k1fx"
    23  )
    24  
    25  const trackChecksums = false
    26  
    27  var (
    28  	parser             block.Parser
    29  	populatedUTXO      *avax.UTXO
    30  	populatedUTXOID    ids.ID
    31  	populatedTx        *txs.Tx
    32  	populatedTxID      ids.ID
    33  	populatedBlk       block.Block
    34  	populatedBlkHeight uint64
    35  	populatedBlkID     ids.ID
    36  )
    37  
    38  func init() {
    39  	var err error
    40  	parser, err = block.NewParser(
    41  		[]fxs.Fx{
    42  			&secp256k1fx.Fx{},
    43  		},
    44  	)
    45  	if err != nil {
    46  		panic(err)
    47  	}
    48  
    49  	populatedUTXO = &avax.UTXO{
    50  		UTXOID: avax.UTXOID{
    51  			TxID: ids.GenerateTestID(),
    52  		},
    53  		Asset: avax.Asset{
    54  			ID: ids.GenerateTestID(),
    55  		},
    56  		Out: &secp256k1fx.TransferOutput{
    57  			Amt: 1,
    58  		},
    59  	}
    60  	populatedUTXOID = populatedUTXO.InputID()
    61  
    62  	populatedTx = &txs.Tx{Unsigned: &txs.BaseTx{BaseTx: avax.BaseTx{
    63  		BlockchainID: ids.GenerateTestID(),
    64  	}}}
    65  	err = populatedTx.Initialize(parser.Codec())
    66  	if err != nil {
    67  		panic(err)
    68  	}
    69  	populatedTxID = populatedTx.ID()
    70  
    71  	populatedBlk, err = block.NewStandardBlock(
    72  		ids.GenerateTestID(),
    73  		1,
    74  		time.Now(),
    75  		[]*txs.Tx{
    76  			{
    77  				Unsigned: &txs.BaseTx{BaseTx: avax.BaseTx{
    78  					BlockchainID: ids.GenerateTestID(),
    79  				}},
    80  			},
    81  		},
    82  		parser.Codec(),
    83  	)
    84  	if err != nil {
    85  		panic(err)
    86  	}
    87  	populatedBlkHeight = populatedBlk.Height()
    88  	populatedBlkID = populatedBlk.ID()
    89  }
    90  
    91  type versions struct {
    92  	chains map[ids.ID]Chain
    93  }
    94  
    95  func (v *versions) GetState(blkID ids.ID) (Chain, bool) {
    96  	c, ok := v.chains[blkID]
    97  	return c, ok
    98  }
    99  
   100  func TestState(t *testing.T) {
   101  	require := require.New(t)
   102  
   103  	db := memdb.New()
   104  	vdb := versiondb.New(db)
   105  	s, err := New(vdb, parser, prometheus.NewRegistry(), trackChecksums)
   106  	require.NoError(err)
   107  
   108  	s.AddUTXO(populatedUTXO)
   109  	s.AddTx(populatedTx)
   110  	s.AddBlock(populatedBlk)
   111  	require.NoError(s.Commit())
   112  
   113  	s, err = New(vdb, parser, prometheus.NewRegistry(), trackChecksums)
   114  	require.NoError(err)
   115  
   116  	ChainUTXOTest(t, s)
   117  	ChainTxTest(t, s)
   118  	ChainBlockTest(t, s)
   119  }
   120  
   121  func TestDiff(t *testing.T) {
   122  	require := require.New(t)
   123  
   124  	db := memdb.New()
   125  	vdb := versiondb.New(db)
   126  	s, err := New(vdb, parser, prometheus.NewRegistry(), trackChecksums)
   127  	require.NoError(err)
   128  
   129  	s.AddUTXO(populatedUTXO)
   130  	s.AddTx(populatedTx)
   131  	s.AddBlock(populatedBlk)
   132  	require.NoError(s.Commit())
   133  
   134  	parentID := ids.GenerateTestID()
   135  	d, err := NewDiff(parentID, &versions{
   136  		chains: map[ids.ID]Chain{
   137  			parentID: s,
   138  		},
   139  	})
   140  	require.NoError(err)
   141  
   142  	ChainUTXOTest(t, d)
   143  	ChainTxTest(t, d)
   144  	ChainBlockTest(t, d)
   145  }
   146  
   147  func ChainUTXOTest(t *testing.T, c Chain) {
   148  	require := require.New(t)
   149  
   150  	fetchedUTXO, err := c.GetUTXO(populatedUTXOID)
   151  	require.NoError(err)
   152  
   153  	// Compare IDs because [fetchedUTXO] isn't initialized
   154  	require.Equal(populatedUTXO.InputID(), fetchedUTXO.InputID())
   155  
   156  	utxo := &avax.UTXO{
   157  		UTXOID: avax.UTXOID{
   158  			TxID: ids.GenerateTestID(),
   159  		},
   160  		Asset: avax.Asset{
   161  			ID: ids.GenerateTestID(),
   162  		},
   163  		Out: &secp256k1fx.TransferOutput{
   164  			Amt: 1,
   165  		},
   166  	}
   167  	utxoID := utxo.InputID()
   168  
   169  	_, err = c.GetUTXO(utxoID)
   170  	require.ErrorIs(err, database.ErrNotFound)
   171  
   172  	c.AddUTXO(utxo)
   173  
   174  	fetchedUTXO, err = c.GetUTXO(utxoID)
   175  	require.NoError(err)
   176  	require.Equal(utxo, fetchedUTXO)
   177  
   178  	c.DeleteUTXO(utxoID)
   179  
   180  	_, err = c.GetUTXO(utxoID)
   181  	require.ErrorIs(err, database.ErrNotFound)
   182  }
   183  
   184  func ChainTxTest(t *testing.T, c Chain) {
   185  	require := require.New(t)
   186  
   187  	fetchedTx, err := c.GetTx(populatedTxID)
   188  	require.NoError(err)
   189  
   190  	// Compare IDs because [fetchedTx] differs between nil and empty fields
   191  	require.Equal(populatedTx.ID(), fetchedTx.ID())
   192  
   193  	// Pull again for the cached path
   194  	fetchedTx, err = c.GetTx(populatedTxID)
   195  	require.NoError(err)
   196  	require.Equal(populatedTx.ID(), fetchedTx.ID())
   197  
   198  	tx := &txs.Tx{Unsigned: &txs.BaseTx{BaseTx: avax.BaseTx{
   199  		BlockchainID: ids.GenerateTestID(),
   200  	}}}
   201  	require.NoError(tx.Initialize(parser.Codec()))
   202  	txID := tx.ID()
   203  
   204  	_, err = c.GetTx(txID)
   205  	require.ErrorIs(err, database.ErrNotFound)
   206  
   207  	// Pull again for the cached path
   208  	_, err = c.GetTx(txID)
   209  	require.ErrorIs(err, database.ErrNotFound)
   210  
   211  	c.AddTx(tx)
   212  
   213  	fetchedTx, err = c.GetTx(txID)
   214  	require.NoError(err)
   215  	require.Equal(tx, fetchedTx)
   216  }
   217  
   218  func ChainBlockTest(t *testing.T, c Chain) {
   219  	require := require.New(t)
   220  
   221  	fetchedBlkID, err := c.GetBlockIDAtHeight(populatedBlkHeight)
   222  	require.NoError(err)
   223  	require.Equal(populatedBlkID, fetchedBlkID)
   224  
   225  	fetchedBlk, err := c.GetBlock(populatedBlkID)
   226  	require.NoError(err)
   227  	require.Equal(populatedBlk.ID(), fetchedBlk.ID())
   228  
   229  	// Pull again for the cached path
   230  	fetchedBlkID, err = c.GetBlockIDAtHeight(populatedBlkHeight)
   231  	require.NoError(err)
   232  	require.Equal(populatedBlkID, fetchedBlkID)
   233  
   234  	fetchedBlk, err = c.GetBlock(populatedBlkID)
   235  	require.NoError(err)
   236  	require.Equal(populatedBlk.ID(), fetchedBlk.ID())
   237  
   238  	blk, err := block.NewStandardBlock(
   239  		ids.GenerateTestID(),
   240  		10,
   241  		time.Now(),
   242  		[]*txs.Tx{
   243  			{
   244  				Unsigned: &txs.BaseTx{BaseTx: avax.BaseTx{
   245  					BlockchainID: ids.GenerateTestID(),
   246  				}},
   247  			},
   248  		},
   249  		parser.Codec(),
   250  	)
   251  	if err != nil {
   252  		panic(err)
   253  	}
   254  	blkID := blk.ID()
   255  	blkHeight := blk.Height()
   256  
   257  	_, err = c.GetBlockIDAtHeight(blkHeight)
   258  	require.ErrorIs(err, database.ErrNotFound)
   259  
   260  	_, err = c.GetBlock(blkID)
   261  	require.ErrorIs(err, database.ErrNotFound)
   262  
   263  	// Pull again for the cached path
   264  	_, err = c.GetBlockIDAtHeight(blkHeight)
   265  	require.ErrorIs(err, database.ErrNotFound)
   266  
   267  	_, err = c.GetBlock(blkID)
   268  	require.ErrorIs(err, database.ErrNotFound)
   269  
   270  	c.AddBlock(blk)
   271  
   272  	fetchedBlkID, err = c.GetBlockIDAtHeight(blkHeight)
   273  	require.NoError(err)
   274  	require.Equal(blkID, fetchedBlkID)
   275  
   276  	fetchedBlk, err = c.GetBlock(blkID)
   277  	require.NoError(err)
   278  	require.Equal(blk, fetchedBlk)
   279  }
   280  
   281  func TestInitializeChainState(t *testing.T) {
   282  	require := require.New(t)
   283  
   284  	db := memdb.New()
   285  	vdb := versiondb.New(db)
   286  	s, err := New(vdb, parser, prometheus.NewRegistry(), trackChecksums)
   287  	require.NoError(err)
   288  
   289  	stopVertexID := ids.GenerateTestID()
   290  	genesisTimestamp := version.DefaultUpgradeTime
   291  	require.NoError(s.InitializeChainState(stopVertexID, genesisTimestamp))
   292  
   293  	lastAcceptedID := s.GetLastAccepted()
   294  	genesis, err := s.GetBlock(lastAcceptedID)
   295  	require.NoError(err)
   296  	require.Equal(stopVertexID, genesis.Parent())
   297  	require.Equal(genesisTimestamp.UnixNano(), genesis.Timestamp().UnixNano())
   298  
   299  	childBlock, err := block.NewStandardBlock(
   300  		genesis.ID(),
   301  		genesis.Height()+1,
   302  		genesisTimestamp,
   303  		nil,
   304  		parser.Codec(),
   305  	)
   306  	require.NoError(err)
   307  
   308  	s.AddBlock(childBlock)
   309  	s.SetLastAccepted(childBlock.ID())
   310  	require.NoError(s.Commit())
   311  
   312  	require.NoError(s.InitializeChainState(stopVertexID, genesisTimestamp))
   313  
   314  	lastAcceptedID = s.GetLastAccepted()
   315  	lastAccepted, err := s.GetBlock(lastAcceptedID)
   316  	require.NoError(err)
   317  	require.Equal(genesis.ID(), lastAccepted.Parent())
   318  }