github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/db/kv/finalized_block_roots_test.go (about)

     1  package kv
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	types "github.com/prysmaticlabs/eth2-types"
     8  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
     9  	"github.com/prysmaticlabs/prysm/proto/eth/v1alpha1/wrapper"
    10  	"github.com/prysmaticlabs/prysm/proto/interfaces"
    11  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    12  	"github.com/prysmaticlabs/prysm/shared/params"
    13  	"github.com/prysmaticlabs/prysm/shared/testutil"
    14  	"github.com/prysmaticlabs/prysm/shared/testutil/assert"
    15  	"github.com/prysmaticlabs/prysm/shared/testutil/require"
    16  )
    17  
    18  var genesisBlockRoot = bytesutil.ToBytes32([]byte{'G', 'E', 'N', 'E', 'S', 'I', 'S'})
    19  
    20  func TestStore_IsFinalizedBlock(t *testing.T) {
    21  	slotsPerEpoch := uint64(params.BeaconConfig().SlotsPerEpoch)
    22  	db := setupDB(t)
    23  	ctx := context.Background()
    24  
    25  	require.NoError(t, db.SaveGenesisBlockRoot(ctx, genesisBlockRoot))
    26  
    27  	blks := makeBlocks(t, 0, slotsPerEpoch*3, genesisBlockRoot)
    28  	require.NoError(t, db.SaveBlocks(ctx, blks))
    29  
    30  	root, err := blks[slotsPerEpoch].Block().HashTreeRoot()
    31  	require.NoError(t, err)
    32  
    33  	cp := &ethpb.Checkpoint{
    34  		Epoch: 1,
    35  		Root:  root[:],
    36  	}
    37  
    38  	st, err := testutil.NewBeaconState()
    39  	require.NoError(t, err)
    40  	// a state is required to save checkpoint
    41  	require.NoError(t, db.SaveState(ctx, st, root))
    42  	require.NoError(t, db.SaveFinalizedCheckpoint(ctx, cp))
    43  
    44  	// All blocks up to slotsPerEpoch*2 should be in the finalized index.
    45  	for i := uint64(0); i < slotsPerEpoch*2; i++ {
    46  		root, err := blks[i].Block().HashTreeRoot()
    47  		require.NoError(t, err)
    48  		assert.Equal(t, true, db.IsFinalizedBlock(ctx, root), "Block at index %d was not considered finalized in the index", i)
    49  	}
    50  	for i := slotsPerEpoch * 3; i < uint64(len(blks)); i++ {
    51  		root, err := blks[i].Block().HashTreeRoot()
    52  		require.NoError(t, err)
    53  		assert.Equal(t, false, db.IsFinalizedBlock(ctx, root), "Block at index %d was considered finalized in the index, but should not have", i)
    54  	}
    55  }
    56  
    57  func TestStore_IsFinalizedBlockGenesis(t *testing.T) {
    58  	db := setupDB(t)
    59  	ctx := context.Background()
    60  
    61  	blk := testutil.NewBeaconBlock()
    62  	blk.Block.Slot = 0
    63  	root, err := blk.Block.HashTreeRoot()
    64  	require.NoError(t, err)
    65  	require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(blk)))
    66  	require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
    67  	assert.Equal(t, true, db.IsFinalizedBlock(ctx, root), "Finalized genesis block doesn't exist in db")
    68  }
    69  
    70  // This test scenario is to test a specific edge case where the finalized block root is not part of
    71  // the finalized and canonical chain.
    72  //
    73  // Example:
    74  // 0    1  2  3   4     5   6     slot
    75  // a <- b <-- d <- e <- f <- g    roots
    76  //      ^- c
    77  // Imagine that epochs are 2 slots and that epoch 1, 2, and 3 are finalized. Checkpoint roots would
    78  // be c, e, and g. In this scenario, c was a finalized checkpoint root but no block built upon it so
    79  // it should not be considered "final and canonical" in the view at slot 6.
    80  func TestStore_IsFinalized_ForkEdgeCase(t *testing.T) {
    81  	slotsPerEpoch := uint64(params.BeaconConfig().SlotsPerEpoch)
    82  	blocks0 := makeBlocks(t, slotsPerEpoch*0, slotsPerEpoch, genesisBlockRoot)
    83  	blocks1 := append(
    84  		makeBlocks(t, slotsPerEpoch*1, 1, bytesutil.ToBytes32(sszRootOrDie(t, blocks0[len(blocks0)-1]))), // No block builds off of the first block in epoch.
    85  		makeBlocks(t, slotsPerEpoch*1+1, slotsPerEpoch-1, bytesutil.ToBytes32(sszRootOrDie(t, blocks0[len(blocks0)-1])))...,
    86  	)
    87  	blocks2 := makeBlocks(t, slotsPerEpoch*2, slotsPerEpoch, bytesutil.ToBytes32(sszRootOrDie(t, blocks1[len(blocks1)-1])))
    88  
    89  	db := setupDB(t)
    90  	ctx := context.Background()
    91  
    92  	require.NoError(t, db.SaveGenesisBlockRoot(ctx, genesisBlockRoot))
    93  	require.NoError(t, db.SaveBlocks(ctx, blocks0))
    94  	require.NoError(t, db.SaveBlocks(ctx, blocks1))
    95  	require.NoError(t, db.SaveBlocks(ctx, blocks2))
    96  
    97  	// First checkpoint
    98  	checkpoint1 := &ethpb.Checkpoint{
    99  		Root:  sszRootOrDie(t, blocks1[0]),
   100  		Epoch: 1,
   101  	}
   102  
   103  	st, err := testutil.NewBeaconState()
   104  	require.NoError(t, err)
   105  	// A state is required to save checkpoint
   106  	require.NoError(t, db.SaveState(ctx, st, bytesutil.ToBytes32(checkpoint1.Root)))
   107  	require.NoError(t, db.SaveFinalizedCheckpoint(ctx, checkpoint1))
   108  	// All blocks in blocks0 and blocks1 should be finalized and canonical.
   109  	for i, block := range append(blocks0, blocks1...) {
   110  		root := sszRootOrDie(t, block)
   111  		assert.Equal(t, true, db.IsFinalizedBlock(ctx, bytesutil.ToBytes32(root)), "%d - Expected block %#x to be finalized", i, root)
   112  	}
   113  
   114  	// Second checkpoint
   115  	checkpoint2 := &ethpb.Checkpoint{
   116  		Root:  sszRootOrDie(t, blocks2[0]),
   117  		Epoch: 2,
   118  	}
   119  	// A state is required to save checkpoint
   120  	require.NoError(t, db.SaveState(ctx, st, bytesutil.ToBytes32(checkpoint2.Root)))
   121  	require.NoError(t, db.SaveFinalizedCheckpoint(ctx, checkpoint2))
   122  	// All blocks in blocks0 and blocks2 should be finalized and canonical.
   123  	for i, block := range append(blocks0, blocks2...) {
   124  		root := sszRootOrDie(t, block)
   125  		assert.Equal(t, true, db.IsFinalizedBlock(ctx, bytesutil.ToBytes32(root)), "%d - Expected block %#x to be finalized", i, root)
   126  	}
   127  	// All blocks in blocks1 should be finalized and canonical, except blocks1[0].
   128  	for i, block := range blocks1 {
   129  		root := sszRootOrDie(t, block)
   130  		if db.IsFinalizedBlock(ctx, bytesutil.ToBytes32(root)) == (i == 0) {
   131  			t.Errorf("Expected db.IsFinalizedBlock(ctx, blocks1[%d]) to be %v", i, i != 0)
   132  		}
   133  	}
   134  }
   135  
   136  func TestStore_IsFinalizedChildBlock(t *testing.T) {
   137  	slotsPerEpoch := uint64(params.BeaconConfig().SlotsPerEpoch)
   138  	db := setupDB(t)
   139  	ctx := context.Background()
   140  
   141  	require.NoError(t, db.SaveGenesisBlockRoot(ctx, genesisBlockRoot))
   142  
   143  	blks := makeBlocks(t, 0, slotsPerEpoch*3, genesisBlockRoot)
   144  
   145  	require.NoError(t, db.SaveBlocks(ctx, blks))
   146  	root, err := blks[slotsPerEpoch].Block().HashTreeRoot()
   147  	require.NoError(t, err)
   148  
   149  	cp := &ethpb.Checkpoint{
   150  		Epoch: 1,
   151  		Root:  root[:],
   152  	}
   153  
   154  	st, err := testutil.NewBeaconState()
   155  	require.NoError(t, err)
   156  	// a state is required to save checkpoint
   157  	require.NoError(t, db.SaveState(ctx, st, root))
   158  	require.NoError(t, db.SaveFinalizedCheckpoint(ctx, cp))
   159  
   160  	// All blocks up to slotsPerEpoch should have a finalized child block.
   161  	for i := uint64(0); i < slotsPerEpoch; i++ {
   162  		root, err := blks[i].Block().HashTreeRoot()
   163  		require.NoError(t, err)
   164  		assert.Equal(t, true, db.IsFinalizedBlock(ctx, root), "Block at index %d was not considered finalized in the index", i)
   165  		blk, err := db.FinalizedChildBlock(ctx, root)
   166  		assert.NoError(t, err)
   167  		if blk == nil {
   168  			t.Error("Child block doesn't exist for valid finalized block.")
   169  		}
   170  	}
   171  }
   172  
   173  func sszRootOrDie(t *testing.T, block interfaces.SignedBeaconBlock) []byte {
   174  	root, err := block.Block().HashTreeRoot()
   175  	require.NoError(t, err)
   176  	return root[:]
   177  }
   178  
   179  func makeBlocks(t *testing.T, i, n uint64, previousRoot [32]byte) []interfaces.SignedBeaconBlock {
   180  	blocks := make([]*ethpb.SignedBeaconBlock, n)
   181  	ifaceBlocks := make([]interfaces.SignedBeaconBlock, n)
   182  	for j := i; j < n+i; j++ {
   183  		parentRoot := make([]byte, 32)
   184  		copy(parentRoot, previousRoot[:])
   185  		blocks[j-i] = testutil.NewBeaconBlock()
   186  		blocks[j-i].Block.Slot = types.Slot(j + 1)
   187  		blocks[j-i].Block.ParentRoot = parentRoot
   188  		var err error
   189  		previousRoot, err = blocks[j-i].Block.HashTreeRoot()
   190  		require.NoError(t, err)
   191  		ifaceBlocks[j-i] = wrapper.WrappedPhase0SignedBeaconBlock(blocks[j-i])
   192  	}
   193  	return ifaceBlocks
   194  }