github.com/MetalBlockchain/metalgo@v1.11.9/vms/platformvm/block/parse_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 block
     5  
     6  import (
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/MetalBlockchain/metalgo/codec"
    13  	"github.com/MetalBlockchain/metalgo/ids"
    14  	"github.com/MetalBlockchain/metalgo/utils/crypto/secp256k1"
    15  	"github.com/MetalBlockchain/metalgo/vms/components/avax"
    16  	"github.com/MetalBlockchain/metalgo/vms/platformvm/txs"
    17  	"github.com/MetalBlockchain/metalgo/vms/secp256k1fx"
    18  )
    19  
    20  var preFundedKeys = secp256k1.TestKeys()
    21  
    22  func TestStandardBlocks(t *testing.T) {
    23  	// check Apricot standard block can be built and parsed
    24  	require := require.New(t)
    25  	blkTimestamp := time.Now()
    26  	parentID := ids.ID{'p', 'a', 'r', 'e', 'n', 't', 'I', 'D'}
    27  	height := uint64(2022)
    28  	decisionTxs, err := testDecisionTxs()
    29  	require.NoError(err)
    30  
    31  	for _, cdc := range []codec.Manager{Codec, GenesisCodec} {
    32  		// build block
    33  		apricotStandardBlk, err := NewApricotStandardBlock(parentID, height, decisionTxs)
    34  		require.NoError(err)
    35  
    36  		// parse block
    37  		parsed, err := Parse(cdc, apricotStandardBlk.Bytes())
    38  		require.NoError(err)
    39  
    40  		// compare content
    41  		require.Equal(apricotStandardBlk.ID(), parsed.ID())
    42  		require.Equal(apricotStandardBlk.Bytes(), parsed.Bytes())
    43  		require.Equal(apricotStandardBlk.Parent(), parsed.Parent())
    44  		require.Equal(apricotStandardBlk.Height(), parsed.Height())
    45  
    46  		require.IsType(&ApricotStandardBlock{}, parsed)
    47  		require.Equal(decisionTxs, parsed.Txs())
    48  
    49  		// check that banff standard block can be built and parsed
    50  		banffStandardBlk, err := NewBanffStandardBlock(blkTimestamp, parentID, height, decisionTxs)
    51  		require.NoError(err)
    52  
    53  		// parse block
    54  		parsed, err = Parse(cdc, banffStandardBlk.Bytes())
    55  		require.NoError(err)
    56  
    57  		// compare content
    58  		require.Equal(banffStandardBlk.ID(), parsed.ID())
    59  		require.Equal(banffStandardBlk.Bytes(), parsed.Bytes())
    60  		require.Equal(banffStandardBlk.Parent(), parsed.Parent())
    61  		require.Equal(banffStandardBlk.Height(), parsed.Height())
    62  		require.IsType(&BanffStandardBlock{}, parsed)
    63  		parsedBanffStandardBlk := parsed.(*BanffStandardBlock)
    64  		require.Equal(decisionTxs, parsedBanffStandardBlk.Txs())
    65  
    66  		// timestamp check for banff blocks only
    67  		require.Equal(banffStandardBlk.Timestamp(), parsedBanffStandardBlk.Timestamp())
    68  
    69  		// backward compatibility check
    70  		require.Equal(parsed.Txs(), parsedBanffStandardBlk.Txs())
    71  	}
    72  }
    73  
    74  func TestProposalBlocks(t *testing.T) {
    75  	// check Apricot proposal block can be built and parsed
    76  	require := require.New(t)
    77  	blkTimestamp := time.Now()
    78  	parentID := ids.ID{'p', 'a', 'r', 'e', 'n', 't', 'I', 'D'}
    79  	height := uint64(2022)
    80  	proposalTx, err := testProposalTx()
    81  	require.NoError(err)
    82  	decisionTxs, err := testDecisionTxs()
    83  	require.NoError(err)
    84  
    85  	for _, cdc := range []codec.Manager{Codec, GenesisCodec} {
    86  		// build block
    87  		apricotProposalBlk, err := NewApricotProposalBlock(
    88  			parentID,
    89  			height,
    90  			proposalTx,
    91  		)
    92  		require.NoError(err)
    93  
    94  		// parse block
    95  		parsed, err := Parse(cdc, apricotProposalBlk.Bytes())
    96  		require.NoError(err)
    97  
    98  		// compare content
    99  		require.Equal(apricotProposalBlk.ID(), parsed.ID())
   100  		require.Equal(apricotProposalBlk.Bytes(), parsed.Bytes())
   101  		require.Equal(apricotProposalBlk.Parent(), parsed.Parent())
   102  		require.Equal(apricotProposalBlk.Height(), parsed.Height())
   103  
   104  		require.IsType(&ApricotProposalBlock{}, parsed)
   105  		parsedApricotProposalBlk := parsed.(*ApricotProposalBlock)
   106  		require.Equal([]*txs.Tx{proposalTx}, parsedApricotProposalBlk.Txs())
   107  
   108  		// check that banff proposal block can be built and parsed
   109  		banffProposalBlk, err := NewBanffProposalBlock(
   110  			blkTimestamp,
   111  			parentID,
   112  			height,
   113  			proposalTx,
   114  			[]*txs.Tx{},
   115  		)
   116  		require.NoError(err)
   117  
   118  		// parse block
   119  		parsed, err = Parse(cdc, banffProposalBlk.Bytes())
   120  		require.NoError(err)
   121  
   122  		// compare content
   123  		require.Equal(banffProposalBlk.ID(), parsed.ID())
   124  		require.Equal(banffProposalBlk.Bytes(), parsed.Bytes())
   125  		require.Equal(banffProposalBlk.Parent(), parsed.Parent())
   126  		require.Equal(banffProposalBlk.Height(), parsed.Height())
   127  		require.IsType(&BanffProposalBlock{}, parsed)
   128  		parsedBanffProposalBlk := parsed.(*BanffProposalBlock)
   129  		require.Equal([]*txs.Tx{proposalTx}, parsedBanffProposalBlk.Txs())
   130  
   131  		// timestamp check for banff blocks only
   132  		require.Equal(banffProposalBlk.Timestamp(), parsedBanffProposalBlk.Timestamp())
   133  
   134  		// backward compatibility check
   135  		require.Equal(parsedApricotProposalBlk.Txs(), parsedBanffProposalBlk.Txs())
   136  
   137  		// check that banff proposal block with decisionTxs can be built and parsed
   138  		banffProposalBlkWithDecisionTxs, err := NewBanffProposalBlock(
   139  			blkTimestamp,
   140  			parentID,
   141  			height,
   142  			proposalTx,
   143  			decisionTxs,
   144  		)
   145  		require.NoError(err)
   146  
   147  		// parse block
   148  		parsed, err = Parse(cdc, banffProposalBlkWithDecisionTxs.Bytes())
   149  		require.NoError(err)
   150  
   151  		// compare content
   152  		require.Equal(banffProposalBlkWithDecisionTxs.ID(), parsed.ID())
   153  		require.Equal(banffProposalBlkWithDecisionTxs.Bytes(), parsed.Bytes())
   154  		require.Equal(banffProposalBlkWithDecisionTxs.Parent(), parsed.Parent())
   155  		require.Equal(banffProposalBlkWithDecisionTxs.Height(), parsed.Height())
   156  		require.IsType(&BanffProposalBlock{}, parsed)
   157  		parsedBanffProposalBlkWithDecisionTxs := parsed.(*BanffProposalBlock)
   158  
   159  		l := len(decisionTxs)
   160  		expectedTxs := make([]*txs.Tx, l+1)
   161  		copy(expectedTxs, decisionTxs)
   162  		expectedTxs[l] = proposalTx
   163  		require.Equal(expectedTxs, parsedBanffProposalBlkWithDecisionTxs.Txs())
   164  
   165  		require.Equal(banffProposalBlkWithDecisionTxs.Timestamp(), parsedBanffProposalBlkWithDecisionTxs.Timestamp())
   166  	}
   167  }
   168  
   169  func TestCommitBlock(t *testing.T) {
   170  	// check Apricot commit block can be built and parsed
   171  	require := require.New(t)
   172  	blkTimestamp := time.Now()
   173  	parentID := ids.ID{'p', 'a', 'r', 'e', 'n', 't', 'I', 'D'}
   174  	height := uint64(2022)
   175  
   176  	for _, cdc := range []codec.Manager{Codec, GenesisCodec} {
   177  		// build block
   178  		apricotCommitBlk, err := NewApricotCommitBlock(parentID, height)
   179  		require.NoError(err)
   180  
   181  		// parse block
   182  		parsed, err := Parse(cdc, apricotCommitBlk.Bytes())
   183  		require.NoError(err)
   184  
   185  		// compare content
   186  		require.Equal(apricotCommitBlk.ID(), parsed.ID())
   187  		require.Equal(apricotCommitBlk.Bytes(), parsed.Bytes())
   188  		require.Equal(apricotCommitBlk.Parent(), parsed.Parent())
   189  		require.Equal(apricotCommitBlk.Height(), parsed.Height())
   190  
   191  		// check that banff commit block can be built and parsed
   192  		banffCommitBlk, err := NewBanffCommitBlock(blkTimestamp, parentID, height)
   193  		require.NoError(err)
   194  
   195  		// parse block
   196  		parsed, err = Parse(cdc, banffCommitBlk.Bytes())
   197  		require.NoError(err)
   198  
   199  		// compare content
   200  		require.Equal(banffCommitBlk.ID(), parsed.ID())
   201  		require.Equal(banffCommitBlk.Bytes(), parsed.Bytes())
   202  		require.Equal(banffCommitBlk.Parent(), parsed.Parent())
   203  		require.Equal(banffCommitBlk.Height(), parsed.Height())
   204  
   205  		// timestamp check for banff blocks only
   206  		require.IsType(&BanffCommitBlock{}, parsed)
   207  		parsedBanffCommitBlk := parsed.(*BanffCommitBlock)
   208  		require.Equal(banffCommitBlk.Timestamp(), parsedBanffCommitBlk.Timestamp())
   209  	}
   210  }
   211  
   212  func TestAbortBlock(t *testing.T) {
   213  	// check Apricot abort block can be built and parsed
   214  	require := require.New(t)
   215  	blkTimestamp := time.Now()
   216  	parentID := ids.ID{'p', 'a', 'r', 'e', 'n', 't', 'I', 'D'}
   217  	height := uint64(2022)
   218  
   219  	for _, cdc := range []codec.Manager{Codec, GenesisCodec} {
   220  		// build block
   221  		apricotAbortBlk, err := NewApricotAbortBlock(parentID, height)
   222  		require.NoError(err)
   223  
   224  		// parse block
   225  		parsed, err := Parse(cdc, apricotAbortBlk.Bytes())
   226  		require.NoError(err)
   227  
   228  		// compare content
   229  		require.Equal(apricotAbortBlk.ID(), parsed.ID())
   230  		require.Equal(apricotAbortBlk.Bytes(), parsed.Bytes())
   231  		require.Equal(apricotAbortBlk.Parent(), parsed.Parent())
   232  		require.Equal(apricotAbortBlk.Height(), parsed.Height())
   233  
   234  		// check that banff abort block can be built and parsed
   235  		banffAbortBlk, err := NewBanffAbortBlock(blkTimestamp, parentID, height)
   236  		require.NoError(err)
   237  
   238  		// parse block
   239  		parsed, err = Parse(cdc, banffAbortBlk.Bytes())
   240  		require.NoError(err)
   241  
   242  		// compare content
   243  		require.Equal(banffAbortBlk.ID(), parsed.ID())
   244  		require.Equal(banffAbortBlk.Bytes(), parsed.Bytes())
   245  		require.Equal(banffAbortBlk.Parent(), parsed.Parent())
   246  		require.Equal(banffAbortBlk.Height(), parsed.Height())
   247  
   248  		// timestamp check for banff blocks only
   249  		require.IsType(&BanffAbortBlock{}, parsed)
   250  		parsedBanffAbortBlk := parsed.(*BanffAbortBlock)
   251  		require.Equal(banffAbortBlk.Timestamp(), parsedBanffAbortBlk.Timestamp())
   252  	}
   253  }
   254  
   255  func TestAtomicBlock(t *testing.T) {
   256  	// check atomic block can be built and parsed
   257  	require := require.New(t)
   258  	parentID := ids.ID{'p', 'a', 'r', 'e', 'n', 't', 'I', 'D'}
   259  	height := uint64(2022)
   260  	atomicTx, err := testAtomicTx()
   261  	require.NoError(err)
   262  
   263  	for _, cdc := range []codec.Manager{Codec, GenesisCodec} {
   264  		// build block
   265  		atomicBlk, err := NewApricotAtomicBlock(
   266  			parentID,
   267  			height,
   268  			atomicTx,
   269  		)
   270  		require.NoError(err)
   271  
   272  		// parse block
   273  		parsed, err := Parse(cdc, atomicBlk.Bytes())
   274  		require.NoError(err)
   275  
   276  		// compare content
   277  		require.Equal(atomicBlk.ID(), parsed.ID())
   278  		require.Equal(atomicBlk.Bytes(), parsed.Bytes())
   279  		require.Equal(atomicBlk.Parent(), parsed.Parent())
   280  		require.Equal(atomicBlk.Height(), parsed.Height())
   281  
   282  		require.IsType(&ApricotAtomicBlock{}, parsed)
   283  		parsedAtomicBlk := parsed.(*ApricotAtomicBlock)
   284  		require.Equal([]*txs.Tx{atomicTx}, parsedAtomicBlk.Txs())
   285  	}
   286  }
   287  
   288  func testAtomicTx() (*txs.Tx, error) {
   289  	utx := &txs.ImportTx{
   290  		BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{
   291  			NetworkID:    10,
   292  			BlockchainID: ids.ID{'c', 'h', 'a', 'i', 'n', 'I', 'D'},
   293  			Outs: []*avax.TransferableOutput{{
   294  				Asset: avax.Asset{ID: ids.ID{'a', 's', 's', 'e', 'r', 't'}},
   295  				Out: &secp256k1fx.TransferOutput{
   296  					Amt: uint64(1234),
   297  					OutputOwners: secp256k1fx.OutputOwners{
   298  						Threshold: 1,
   299  						Addrs:     []ids.ShortID{preFundedKeys[0].PublicKey().Address()},
   300  					},
   301  				},
   302  			}},
   303  			Ins: []*avax.TransferableInput{{
   304  				UTXOID: avax.UTXOID{
   305  					TxID:        ids.ID{'t', 'x', 'I', 'D'},
   306  					OutputIndex: 2,
   307  				},
   308  				Asset: avax.Asset{ID: ids.ID{'a', 's', 's', 'e', 'r', 't'}},
   309  				In: &secp256k1fx.TransferInput{
   310  					Amt:   uint64(5678),
   311  					Input: secp256k1fx.Input{SigIndices: []uint32{0}},
   312  				},
   313  			}},
   314  			Memo: []byte{1, 2, 3, 4, 5, 6, 7, 8},
   315  		}},
   316  		SourceChain: ids.ID{'c', 'h', 'a', 'i', 'n'},
   317  		ImportedInputs: []*avax.TransferableInput{{
   318  			UTXOID: avax.UTXOID{
   319  				TxID:        ids.Empty.Prefix(1),
   320  				OutputIndex: 1,
   321  			},
   322  			Asset: avax.Asset{ID: ids.ID{'a', 's', 's', 'e', 'r', 't'}},
   323  			In: &secp256k1fx.TransferInput{
   324  				Amt:   50000,
   325  				Input: secp256k1fx.Input{SigIndices: []uint32{0}},
   326  			},
   327  		}},
   328  	}
   329  	signers := [][]*secp256k1.PrivateKey{{preFundedKeys[0]}}
   330  	return txs.NewSigned(utx, txs.Codec, signers)
   331  }
   332  
   333  func testDecisionTxs() ([]*txs.Tx, error) {
   334  	countTxs := 2
   335  	decisionTxs := make([]*txs.Tx, 0, countTxs)
   336  	for i := 0; i < countTxs; i++ {
   337  		// Create the tx
   338  		utx := &txs.CreateChainTx{
   339  			BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{
   340  				NetworkID:    10,
   341  				BlockchainID: ids.ID{'c', 'h', 'a', 'i', 'n', 'I', 'D'},
   342  				Outs: []*avax.TransferableOutput{{
   343  					Asset: avax.Asset{ID: ids.ID{'a', 's', 's', 'e', 'r', 't'}},
   344  					Out: &secp256k1fx.TransferOutput{
   345  						Amt: uint64(1234),
   346  						OutputOwners: secp256k1fx.OutputOwners{
   347  							Threshold: 1,
   348  							Addrs:     []ids.ShortID{preFundedKeys[0].PublicKey().Address()},
   349  						},
   350  					},
   351  				}},
   352  				Ins: []*avax.TransferableInput{{
   353  					UTXOID: avax.UTXOID{
   354  						TxID:        ids.ID{'t', 'x', 'I', 'D'},
   355  						OutputIndex: 2,
   356  					},
   357  					Asset: avax.Asset{ID: ids.ID{'a', 's', 's', 'e', 'r', 't'}},
   358  					In: &secp256k1fx.TransferInput{
   359  						Amt:   uint64(5678),
   360  						Input: secp256k1fx.Input{SigIndices: []uint32{0}},
   361  					},
   362  				}},
   363  				Memo: []byte{1, 2, 3, 4, 5, 6, 7, 8},
   364  			}},
   365  			SubnetID:    ids.ID{'s', 'u', 'b', 'n', 'e', 't', 'I', 'D'},
   366  			ChainName:   "a chain",
   367  			VMID:        ids.GenerateTestID(),
   368  			FxIDs:       []ids.ID{ids.GenerateTestID()},
   369  			GenesisData: []byte{'g', 'e', 'n', 'D', 'a', 't', 'a'},
   370  			SubnetAuth:  &secp256k1fx.Input{SigIndices: []uint32{1}},
   371  		}
   372  
   373  		signers := [][]*secp256k1.PrivateKey{{preFundedKeys[0]}}
   374  		tx, err := txs.NewSigned(utx, txs.Codec, signers)
   375  		if err != nil {
   376  			return nil, err
   377  		}
   378  		decisionTxs = append(decisionTxs, tx)
   379  	}
   380  	return decisionTxs, nil
   381  }
   382  
   383  func testProposalTx() (*txs.Tx, error) {
   384  	utx := &txs.RewardValidatorTx{
   385  		TxID: ids.ID{'r', 'e', 'w', 'a', 'r', 'd', 'I', 'D'},
   386  	}
   387  
   388  	signers := [][]*secp256k1.PrivateKey{{preFundedKeys[0]}}
   389  	return txs.NewSigned(utx, txs.Codec, signers)
   390  }