github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/consensus/approvals/approval_collector_test.go (about)

     1  package approvals
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/mock"
     7  	"github.com/stretchr/testify/require"
     8  	"github.com/stretchr/testify/suite"
     9  
    10  	"github.com/onflow/flow-go/engine"
    11  	"github.com/onflow/flow-go/model/flow"
    12  	mempool "github.com/onflow/flow-go/module/mempool/mock"
    13  	"github.com/onflow/flow-go/utils/unittest"
    14  )
    15  
    16  // TestApprovalCollector performs isolated testing of ApprovalCollector
    17  // ApprovalCollector is responsible for delegating approval processing to ChunkApprovalCollector
    18  // ApprovalCollector stores aggregated signatures for every chunk, once there is a signature for each chunk it is responsible
    19  // for creating IncorporatedResultSeal and submitting it to the mempool.
    20  // ApprovalCollector should reject approvals with invalid chunk index.
    21  func TestApprovalCollector(t *testing.T) {
    22  	suite.Run(t, new(ApprovalCollectorTestSuite))
    23  }
    24  
    25  type ApprovalCollectorTestSuite struct {
    26  	BaseApprovalsTestSuite
    27  
    28  	sealsPL   *mempool.IncorporatedResultSeals
    29  	collector *ApprovalCollector
    30  }
    31  
    32  func (s *ApprovalCollectorTestSuite) SetupTest() {
    33  	s.BaseApprovalsTestSuite.SetupTest()
    34  	s.sealsPL = &mempool.IncorporatedResultSeals{}
    35  
    36  	var err error
    37  	s.collector, err = NewApprovalCollector(unittest.Logger(), s.IncorporatedResult, s.IncorporatedBlock, s.Block, s.ChunksAssignment, s.sealsPL, uint(len(s.AuthorizedVerifiers)))
    38  	require.NoError(s.T(), err)
    39  }
    40  
    41  // TestProcessApproval_ValidApproval tests that valid approval is processed without error
    42  func (s *ApprovalCollectorTestSuite) TestProcessApproval_ValidApproval() {
    43  	approval := unittest.ResultApprovalFixture(unittest.WithChunk(s.Chunks[0].Index), unittest.WithApproverID(s.VerID))
    44  	err := s.collector.ProcessApproval(approval)
    45  	require.NoError(s.T(), err)
    46  }
    47  
    48  // TestProcessApproval_SealResult tests that after collecting enough approvals for every chunk ApprovalCollector will
    49  // generate a seal and put it into seals mempool. This logic should be event driven and happen as soon as required threshold is
    50  // met for each chunk.
    51  func (s *ApprovalCollectorTestSuite) TestProcessApproval_SealResult() {
    52  	expectedSignatures := make([]flow.AggregatedSignature, s.IncorporatedResult.Result.Chunks.Len())
    53  	s.sealsPL.On("Add", mock.Anything).Run(
    54  		func(args mock.Arguments) {
    55  			seal := args.Get(0).(*flow.IncorporatedResultSeal)
    56  			require.Equal(s.T(), s.Block.ID(), seal.Seal.BlockID)
    57  			require.Equal(s.T(), s.IncorporatedResult.Result.ID(), seal.Seal.ResultID)
    58  			require.Equal(s.T(), s.IncorporatedResult.Result.BlockID, seal.Seal.BlockID)
    59  			require.Equal(s.T(), seal.Seal.BlockID, seal.Header.ID())
    60  		},
    61  	).Return(true, nil).Once()
    62  
    63  	for i, chunk := range s.Chunks {
    64  		var err error
    65  		sigCollector := NewSignatureCollector()
    66  		for verID := range s.AuthorizedVerifiers {
    67  			approval := unittest.ResultApprovalFixture(unittest.WithChunk(chunk.Index), unittest.WithApproverID(verID))
    68  			err = s.collector.ProcessApproval(approval)
    69  			require.NoError(s.T(), err)
    70  			sigCollector.Add(approval.Body.ApproverID, approval.Body.AttestationSignature)
    71  		}
    72  		expectedSignatures[i] = sigCollector.ToAggregatedSignature()
    73  	}
    74  
    75  	s.sealsPL.AssertExpectations(s.T())
    76  }
    77  
    78  // TestProcessApproval_InvalidChunk tests that approval with invalid chunk index will be rejected without
    79  // processing.
    80  func (s *ApprovalCollectorTestSuite) TestProcessApproval_InvalidChunk() {
    81  	approval := unittest.ResultApprovalFixture(unittest.WithChunk(uint64(s.Chunks.Len()+1)),
    82  		unittest.WithApproverID(s.VerID))
    83  	err := s.collector.ProcessApproval(approval)
    84  	require.Error(s.T(), err)
    85  	require.True(s.T(), engine.IsInvalidInputError(err))
    86  }
    87  
    88  // TestCollectMissingVerifiers tests that approval collector correctly assembles list of verifiers that haven't provided approvals
    89  // for each chunk
    90  func (s *ApprovalCollectorTestSuite) TestCollectMissingVerifiers() {
    91  	s.sealsPL.On("Add", mock.Anything).Return(true, nil).Maybe()
    92  
    93  	assignedVerifiers := make(map[uint64]flow.IdentifierList)
    94  	for _, chunk := range s.Chunks {
    95  		assignedVerifiers[chunk.Index] = s.ChunksAssignment.Verifiers(chunk)
    96  	}
    97  
    98  	// no approvals processed
    99  	for index, ids := range s.collector.CollectMissingVerifiers() {
   100  		require.ElementsMatch(s.T(), ids, assignedVerifiers[index])
   101  	}
   102  
   103  	// process one approval for one each chunk
   104  	for _, chunk := range s.Chunks {
   105  		verID := assignedVerifiers[chunk.Index][0]
   106  		approval := unittest.ResultApprovalFixture(unittest.WithChunk(chunk.Index),
   107  			unittest.WithApproverID(verID))
   108  		err := s.collector.ProcessApproval(approval)
   109  		require.NoError(s.T(), err)
   110  	}
   111  
   112  	for index, ids := range s.collector.CollectMissingVerifiers() {
   113  		// skip first ID since we should have approval for it
   114  		require.ElementsMatch(s.T(), ids, assignedVerifiers[index][1:])
   115  	}
   116  
   117  	// process remaining approvals for each chunk
   118  	for _, chunk := range s.Chunks {
   119  		for _, verID := range assignedVerifiers[chunk.Index] {
   120  			approval := unittest.ResultApprovalFixture(unittest.WithChunk(chunk.Index),
   121  				unittest.WithApproverID(verID))
   122  			err := s.collector.ProcessApproval(approval)
   123  			require.NoError(s.T(), err)
   124  		}
   125  	}
   126  
   127  	// skip first ID since we should have approval for it
   128  	require.Empty(s.T(), s.collector.CollectMissingVerifiers())
   129  }