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

     1  package approvals
     2  
     3  import (
     4  	"github.com/gammazero/workerpool"
     5  	"github.com/onflow/crypto/hash"
     6  	"github.com/stretchr/testify/mock"
     7  	"github.com/stretchr/testify/suite"
     8  
     9  	"github.com/onflow/flow-go/model/chunks"
    10  	"github.com/onflow/flow-go/model/flow"
    11  	mempool "github.com/onflow/flow-go/module/mempool/mock"
    12  	module "github.com/onflow/flow-go/module/mock"
    13  	msig "github.com/onflow/flow-go/module/signature"
    14  	"github.com/onflow/flow-go/network/mocknetwork"
    15  	realproto "github.com/onflow/flow-go/state/protocol"
    16  	protocol "github.com/onflow/flow-go/state/protocol/mock"
    17  	realstorage "github.com/onflow/flow-go/storage"
    18  	storage "github.com/onflow/flow-go/storage/mock"
    19  	"github.com/onflow/flow-go/utils/unittest"
    20  )
    21  
    22  // BaseApprovalsTestSuite is a base suite for testing approvals processing related functionality
    23  // At nutshell generates mock data that can be used to create approvals and provides all needed
    24  // data to validate those approvals for respected execution result.
    25  type BaseApprovalsTestSuite struct {
    26  	suite.Suite
    27  
    28  	ParentBlock         *flow.Header    // parent of sealing candidate
    29  	Block               *flow.Header    // candidate for sealing
    30  	IncorporatedBlock   *flow.Header    // block that incorporated result
    31  	VerID               flow.Identifier // for convenience, node id of first verifier
    32  	Chunks              flow.ChunkList  // list of chunks of execution result
    33  	ChunksAssignment    *chunks.Assignment
    34  	AuthorizedVerifiers map[flow.Identifier]*flow.Identity // map of authorized verifier identities for execution result
    35  	PublicKey           *module.PublicKey                  // public key used to mock signature verifications
    36  	SigHasher           hash.Hasher                        // used to verify signatures
    37  	IncorporatedResult  *flow.IncorporatedResult
    38  }
    39  
    40  func (s *BaseApprovalsTestSuite) SetupTest() {
    41  	s.ParentBlock = unittest.BlockHeaderFixture()
    42  	s.Block = unittest.BlockHeaderWithParentFixture(s.ParentBlock)
    43  	verifiers := make(flow.IdentifierList, 0)
    44  	s.AuthorizedVerifiers = make(map[flow.Identifier]*flow.Identity)
    45  	s.ChunksAssignment = chunks.NewAssignment()
    46  	s.Chunks = unittest.ChunkListFixture(50, s.Block.ID())
    47  	// mock public key to mock signature verifications
    48  	s.PublicKey = &module.PublicKey{}
    49  
    50  	// setup identities
    51  	for j := 0; j < 5; j++ {
    52  		identity := unittest.IdentityFixture(unittest.WithRole(flow.RoleVerification))
    53  		verifiers = append(verifiers, identity.NodeID)
    54  		s.AuthorizedVerifiers[identity.NodeID] = identity
    55  		// mock all verifier's valid signatures
    56  		identity.StakingPubKey = s.PublicKey
    57  	}
    58  	s.SigHasher = msig.NewBLSHasher("test_tag")
    59  
    60  	// create assignment
    61  	for _, chunk := range s.Chunks {
    62  		s.ChunksAssignment.Add(chunk, verifiers)
    63  	}
    64  
    65  	s.VerID = verifiers[0]
    66  	result := unittest.ExecutionResultFixture()
    67  	result.BlockID = s.Block.ID()
    68  	result.Chunks = s.Chunks
    69  
    70  	s.IncorporatedBlock = unittest.BlockHeaderWithParentFixture(s.Block)
    71  
    72  	// compose incorporated result
    73  	s.IncorporatedResult = unittest.IncorporatedResult.Fixture(
    74  		unittest.IncorporatedResult.WithResult(result),
    75  		unittest.IncorporatedResult.WithIncorporatedBlockID(s.IncorporatedBlock.ID()))
    76  }
    77  
    78  // BaseAssignmentCollectorTestSuite is a base suite for testing assignment collectors, contains mocks for all
    79  // classes that are used in base assignment collector and can be reused in different test suites.
    80  type BaseAssignmentCollectorTestSuite struct {
    81  	BaseApprovalsTestSuite
    82  
    83  	WorkerPool        *workerpool.WorkerPool
    84  	Blocks            map[flow.Identifier]*flow.Header
    85  	State             *protocol.State
    86  	Snapshots         map[flow.Identifier]*protocol.Snapshot
    87  	Headers           *storage.Headers
    88  	Assigner          *module.ChunkAssigner
    89  	SealsPL           *mempool.IncorporatedResultSeals
    90  	Conduit           *mocknetwork.Conduit
    91  	FinalizedAtHeight map[uint64]*flow.Header
    92  	IdentitiesCache   map[flow.Identifier]map[flow.Identifier]*flow.Identity // helper map to store identities for given block
    93  	RequestTracker    *RequestTracker
    94  }
    95  
    96  func (s *BaseAssignmentCollectorTestSuite) SetupTest() {
    97  	s.BaseApprovalsTestSuite.SetupTest()
    98  
    99  	s.WorkerPool = workerpool.New(4)
   100  	s.SealsPL = &mempool.IncorporatedResultSeals{}
   101  	s.State = &protocol.State{}
   102  	s.Assigner = &module.ChunkAssigner{}
   103  	s.Conduit = &mocknetwork.Conduit{}
   104  	s.Headers = &storage.Headers{}
   105  
   106  	s.RequestTracker = NewRequestTracker(s.Headers, 1, 3)
   107  
   108  	s.FinalizedAtHeight = make(map[uint64]*flow.Header)
   109  	s.FinalizedAtHeight[s.ParentBlock.Height] = s.ParentBlock
   110  	s.FinalizedAtHeight[s.Block.Height] = s.Block
   111  
   112  	// setup blocks cache for protocol state
   113  	s.Blocks = make(map[flow.Identifier]*flow.Header)
   114  	s.Blocks[s.ParentBlock.ID()] = s.ParentBlock
   115  	s.Blocks[s.Block.ID()] = s.Block
   116  	s.Blocks[s.IncorporatedBlock.ID()] = s.IncorporatedBlock
   117  	s.Snapshots = make(map[flow.Identifier]*protocol.Snapshot)
   118  
   119  	// setup identities for each block
   120  	s.IdentitiesCache = make(map[flow.Identifier]map[flow.Identifier]*flow.Identity)
   121  	s.IdentitiesCache[s.IncorporatedResult.Result.BlockID] = s.AuthorizedVerifiers
   122  
   123  	s.Assigner.On("Assign", mock.Anything, mock.Anything).Return(func(result *flow.ExecutionResult, blockID flow.Identifier) *chunks.Assignment {
   124  		return s.ChunksAssignment
   125  	}, func(result *flow.ExecutionResult, blockID flow.Identifier) error { return nil })
   126  
   127  	s.Headers.On("ByBlockID", mock.Anything).Return(func(blockID flow.Identifier) *flow.Header {
   128  		return s.Blocks[blockID]
   129  	}, func(blockID flow.Identifier) error {
   130  		_, found := s.Blocks[blockID]
   131  		if found {
   132  			return nil
   133  		} else {
   134  			return realstorage.ErrNotFound
   135  		}
   136  	})
   137  	s.Headers.On("BlockIDByHeight", mock.Anything).Return(
   138  		func(height uint64) (flow.Identifier, error) {
   139  			if block, found := s.FinalizedAtHeight[height]; found {
   140  				return block.ID(), nil
   141  			} else {
   142  				return flow.ZeroID, realstorage.ErrNotFound
   143  			}
   144  		},
   145  	)
   146  	s.Headers.On("ByHeight", mock.Anything).Return(
   147  		func(height uint64) (*flow.Header, error) {
   148  			if block, found := s.FinalizedAtHeight[height]; found {
   149  				return block, nil
   150  			} else {
   151  				return nil, realstorage.ErrNotFound
   152  			}
   153  		},
   154  	)
   155  
   156  	s.State.On("AtBlockID", mock.Anything).Return(
   157  		func(blockID flow.Identifier) realproto.Snapshot {
   158  			if snapshot, found := s.Snapshots[blockID]; found {
   159  				return snapshot
   160  			}
   161  			if block, found := s.Blocks[blockID]; found {
   162  				snapshot := unittest.StateSnapshotForKnownBlock(block, s.IdentitiesCache[blockID])
   163  				s.Snapshots[blockID] = snapshot
   164  				return snapshot
   165  			}
   166  			return unittest.StateSnapshotForUnknownBlock()
   167  		},
   168  	)
   169  
   170  	s.SealsPL.On("Size").Return(uint(0)).Maybe()                       // for metrics
   171  	s.SealsPL.On("PruneUpToHeight", mock.Anything).Return(nil).Maybe() // noop on pruning
   172  }
   173  
   174  func (s *BaseAssignmentCollectorTestSuite) MarkFinalized(block *flow.Header) {
   175  	s.FinalizedAtHeight[block.Height] = block
   176  }
   177  
   178  func (s *BaseAssignmentCollectorTestSuite) TearDownTest() {
   179  	// Without this line we are risking running into weird situations where one test has finished but there are active workers
   180  	// that are executing some work on the shared pool. Need to ensure that all pending work has been executed before
   181  	// starting next test.
   182  	s.WorkerPool.StopWait()
   183  }