github.com/koko1123/flow-go-1@v0.29.6/utils/unittest/chain_suite.go (about) 1 package unittest 2 3 import ( 4 "fmt" 5 6 "github.com/stretchr/testify/mock" 7 "github.com/stretchr/testify/suite" 8 9 "github.com/koko1123/flow-go-1/model/chunks" 10 "github.com/koko1123/flow-go-1/model/flow" 11 mempool "github.com/koko1123/flow-go-1/module/mempool/mock" 12 module "github.com/koko1123/flow-go-1/module/mock" 13 realproto "github.com/koko1123/flow-go-1/state/protocol" 14 protocol "github.com/koko1123/flow-go-1/state/protocol/mock" 15 storerr "github.com/koko1123/flow-go-1/storage" 16 storage "github.com/koko1123/flow-go-1/storage/mock" 17 ) 18 19 type BaseChainSuite struct { 20 suite.Suite 21 22 // IDENTITIES 23 ConID flow.Identifier 24 ExeID flow.Identifier 25 VerID flow.Identifier 26 27 Identities map[flow.Identifier]*flow.Identity 28 Approvers flow.IdentityList 29 30 // BLOCKS 31 RootBlock flow.Block 32 LatestSealedBlock flow.Block 33 LatestFinalizedBlock *flow.Block 34 UnfinalizedBlock flow.Block 35 LatestExecutionResult *flow.ExecutionResult 36 Blocks map[flow.Identifier]*flow.Block 37 38 // PROTOCOL STATE 39 State *protocol.State 40 SealedSnapshot *protocol.Snapshot 41 FinalSnapshot *protocol.Snapshot 42 43 // MEMPOOLS and STORAGE which are injected into Matching Engine 44 // mock storage.ExecutionReceipts: backed by in-memory map PersistedReceipts 45 ReceiptsDB *storage.ExecutionReceipts 46 47 ResultsDB *storage.ExecutionResults 48 PersistedResults map[flow.Identifier]*flow.ExecutionResult 49 50 // mock mempool.IncorporatedResultSeals: backed by in-memory map PendingSeals 51 SealsPL *mempool.IncorporatedResultSeals 52 PendingSeals map[flow.Identifier]*flow.IncorporatedResultSeal 53 54 // mock BLOCK STORAGE: backed by in-memory map Blocks 55 HeadersDB *storage.Headers // backed by map Blocks 56 IndexDB *storage.Index // backed by map Blocks 57 PayloadsDB *storage.Payloads // backed by map Blocks 58 SealsDB *storage.Seals // backed by map SealsIndex 59 SealsIndex map[flow.Identifier]*flow.Seal // last valid seal for block 60 61 // mock mempool.ReceiptsForest: used to test whether or not Matching Engine stores receipts 62 ReceiptsPL *mempool.ExecutionTree 63 64 Assigner *module.ChunkAssigner 65 Assignments map[flow.Identifier]*chunks.Assignment // index for assignments for given execution result 66 67 PendingReceipts *mempool.PendingReceipts 68 } 69 70 func (bc *BaseChainSuite) SetupChain() { 71 72 // ~~~~~~~~~~~~~~~~~~~~~~~~~~ SETUP IDENTITIES ~~~~~~~~~~~~~~~~~~~~~~~~~~ // 73 74 // asign node Identities 75 con := IdentityFixture(WithRole(flow.RoleConsensus)) 76 exe := IdentityFixture(WithRole(flow.RoleExecution)) 77 ver := IdentityFixture(WithRole(flow.RoleVerification)) 78 79 bc.ConID = con.NodeID 80 bc.ExeID = exe.NodeID 81 bc.VerID = ver.NodeID 82 83 bc.Identities = make(map[flow.Identifier]*flow.Identity) 84 bc.Identities[bc.ConID] = con 85 bc.Identities[bc.ExeID] = exe 86 bc.Identities[bc.VerID] = ver 87 88 // assign 4 nodes to the verification role 89 bc.Approvers = IdentityListFixture(4, WithRole(flow.RoleVerification)) 90 for _, verifier := range bc.Approvers { 91 bc.Identities[verifier.ID()] = verifier 92 } 93 94 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SETUP BLOCKS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // 95 // RootBlock <- LatestSealedBlock <- LatestFinalizedBlock <- UnfinalizedBlock 96 bc.RootBlock = BlockFixture() 97 bc.LatestSealedBlock = *BlockWithParentFixture(bc.RootBlock.Header) 98 latestFinalizedBlock := BlockWithParentFixture(bc.LatestSealedBlock.Header) 99 bc.LatestFinalizedBlock = latestFinalizedBlock 100 bc.UnfinalizedBlock = *BlockWithParentFixture(bc.LatestFinalizedBlock.Header) 101 102 bc.Blocks = make(map[flow.Identifier]*flow.Block) 103 bc.Blocks[bc.RootBlock.ID()] = &bc.RootBlock 104 bc.Blocks[bc.LatestSealedBlock.ID()] = &bc.LatestSealedBlock 105 bc.Blocks[bc.LatestFinalizedBlock.ID()] = bc.LatestFinalizedBlock 106 bc.Blocks[bc.UnfinalizedBlock.ID()] = &bc.UnfinalizedBlock 107 108 // ~~~~~~~~~~~~~~~~~~~~~~~~ SETUP PROTOCOL STATE ~~~~~~~~~~~~~~~~~~~~~~~~ // 109 bc.State = &protocol.State{} 110 111 // define the protocol state snapshot of the latest finalized block 112 bc.State.On("Final").Return( 113 func() realproto.Snapshot { 114 return bc.FinalSnapshot 115 }, 116 nil, 117 ) 118 bc.FinalSnapshot = &protocol.Snapshot{} 119 bc.FinalSnapshot.On("Head").Return( 120 func() *flow.Header { 121 return bc.LatestFinalizedBlock.Header 122 }, 123 nil, 124 ) 125 bc.FinalSnapshot.On("SealedResult").Return( 126 func() *flow.ExecutionResult { 127 blockID := bc.LatestFinalizedBlock.ID() 128 seal, found := bc.SealsIndex[blockID] 129 if !found { 130 return nil 131 } 132 result, found := bc.PersistedResults[seal.ResultID] 133 if !found { 134 return nil 135 } 136 return result 137 }, 138 func() *flow.Seal { 139 blockID := bc.LatestFinalizedBlock.ID() 140 seal, found := bc.SealsIndex[blockID] 141 if !found { 142 return nil 143 } 144 return seal 145 }, 146 func() error { 147 blockID := bc.LatestFinalizedBlock.ID() 148 seal, found := bc.SealsIndex[blockID] 149 if !found { 150 return storerr.ErrNotFound 151 } 152 _, found = bc.PersistedResults[seal.ResultID] 153 if !found { 154 return storerr.ErrNotFound 155 } 156 return nil 157 }, 158 ) 159 160 // define the protocol state snapshot of the latest sealed block 161 bc.State.On("Sealed").Return( 162 func() realproto.Snapshot { 163 return bc.SealedSnapshot 164 }, 165 nil, 166 ) 167 bc.SealedSnapshot = &protocol.Snapshot{} 168 bc.SealedSnapshot.On("Head").Return( 169 func() *flow.Header { 170 return bc.LatestSealedBlock.Header 171 }, 172 nil, 173 ) 174 175 findBlockByHeight := func(blocks map[flow.Identifier]*flow.Block, height uint64) (*flow.Block, bool) { 176 for _, block := range blocks { 177 if block.Header.Height == height { 178 return block, true 179 } 180 } 181 return nil, false 182 } 183 184 // define the protocol state snapshot for any block in `bc.Blocks` 185 bc.State.On("AtBlockID", mock.Anything).Return( 186 func(blockID flow.Identifier) realproto.Snapshot { 187 block, found := bc.Blocks[blockID] 188 if !found { 189 return StateSnapshotForUnknownBlock() 190 } 191 return StateSnapshotForKnownBlock(block.Header, bc.Identities) 192 }, 193 ) 194 195 bc.State.On("AtHeight", mock.Anything).Return( 196 func(height uint64) realproto.Snapshot { 197 block, found := findBlockByHeight(bc.Blocks, height) 198 if found { 199 snapshot := &protocol.Snapshot{} 200 snapshot.On("Head").Return( 201 func() *flow.Header { 202 return block.Header 203 }, 204 nil, 205 ) 206 return snapshot 207 } 208 panic(fmt.Sprintf("unknown height: %v, final: %v, sealed: %v", height, bc.LatestFinalizedBlock.Header.Height, bc.LatestSealedBlock.Header.Height)) 209 }, 210 ) 211 212 // ~~~~~~~~~~~~~~~~~~~~~~~ SETUP RESULTS STORAGE ~~~~~~~~~~~~~~~~~~~~~~~~ // 213 bc.PersistedResults = make(map[flow.Identifier]*flow.ExecutionResult) 214 bc.LatestExecutionResult = ExecutionResultFixture(WithBlock(&bc.LatestSealedBlock)) 215 bc.PersistedResults[bc.LatestExecutionResult.ID()] = bc.LatestExecutionResult 216 bc.ResultsDB = &storage.ExecutionResults{} 217 bc.ResultsDB.On("ByID", mock.Anything).Return( 218 func(resultID flow.Identifier) *flow.ExecutionResult { 219 return bc.PersistedResults[resultID] 220 }, 221 func(resultID flow.Identifier) error { 222 _, found := bc.PersistedResults[resultID] 223 if !found { 224 return storerr.ErrNotFound 225 } 226 return nil 227 }, 228 ).Maybe() 229 bc.ResultsDB.On("Store", mock.Anything).Return( 230 func(result *flow.ExecutionResult) error { 231 _, found := bc.PersistedResults[result.ID()] 232 if found { 233 return storerr.ErrAlreadyExists 234 } 235 return nil 236 }, 237 ).Maybe() // this call is optional 238 // ~~~~~~~~~~~~~~~~~~~~~~~ SETUP RECEIPTS STORAGE ~~~~~~~~~~~~~~~~~~~~~~~~ // 239 bc.ReceiptsDB = &storage.ExecutionReceipts{} 240 241 // ~~~~~~~~~~~~~~~~~~~~ SETUP BLOCK HEADER STORAGE ~~~~~~~~~~~~~~~~~~~~~ // 242 bc.HeadersDB = &storage.Headers{} 243 bc.HeadersDB.On("ByBlockID", mock.Anything).Return( 244 func(blockID flow.Identifier) *flow.Header { 245 block, found := bc.Blocks[blockID] 246 if !found { 247 return nil 248 } 249 return block.Header 250 }, 251 func(blockID flow.Identifier) error { 252 _, found := bc.Blocks[blockID] 253 if !found { 254 return storerr.ErrNotFound 255 } 256 return nil 257 }, 258 ) 259 bc.HeadersDB.On("ByHeight", mock.Anything).Return( 260 func(blockHeight uint64) *flow.Header { 261 for _, b := range bc.Blocks { 262 if b.Header.Height == blockHeight { 263 return b.Header 264 } 265 } 266 return nil 267 }, 268 func(blockHeight uint64) error { 269 for _, b := range bc.Blocks { 270 if b.Header.Height == blockHeight { 271 return nil 272 } 273 } 274 return storerr.ErrNotFound 275 }, 276 ) 277 278 // ~~~~~~~~~~~~~~~~~~~~ SETUP BLOCK PAYLOAD STORAGE ~~~~~~~~~~~~~~~~~~~~~ // 279 bc.IndexDB = &storage.Index{} 280 bc.IndexDB.On("ByBlockID", mock.Anything).Return( 281 func(blockID flow.Identifier) *flow.Index { 282 block, found := bc.Blocks[blockID] 283 if !found { 284 return nil 285 } 286 if block.Payload == nil { 287 return nil 288 } 289 return block.Payload.Index() 290 }, 291 func(blockID flow.Identifier) error { 292 block, found := bc.Blocks[blockID] 293 if !found { 294 return storerr.ErrNotFound 295 } 296 if block.Payload == nil { 297 return storerr.ErrNotFound 298 } 299 return nil 300 }, 301 ) 302 303 bc.SealsIndex = make(map[flow.Identifier]*flow.Seal) 304 firtSeal := Seal.Fixture(Seal.WithBlock(bc.LatestSealedBlock.Header), 305 Seal.WithResult(bc.LatestExecutionResult)) 306 for id, block := range bc.Blocks { 307 if id != bc.RootBlock.ID() { 308 bc.SealsIndex[block.ID()] = firtSeal 309 } 310 } 311 312 bc.PayloadsDB = &storage.Payloads{} 313 bc.PayloadsDB.On("ByBlockID", mock.Anything).Return( 314 func(blockID flow.Identifier) *flow.Payload { 315 block, found := bc.Blocks[blockID] 316 if !found { 317 return nil 318 } 319 if block.Payload == nil { 320 return nil 321 } 322 return block.Payload 323 }, 324 func(blockID flow.Identifier) error { 325 block, found := bc.Blocks[blockID] 326 if !found { 327 return storerr.ErrNotFound 328 } 329 if block.Payload == nil { 330 return storerr.ErrNotFound 331 } 332 return nil 333 }, 334 ) 335 336 bc.SealsDB = &storage.Seals{} 337 bc.SealsDB.On("HighestInFork", mock.Anything).Return( 338 func(blockID flow.Identifier) *flow.Seal { 339 seal, found := bc.SealsIndex[blockID] 340 if !found { 341 return nil 342 } 343 return seal 344 }, 345 func(blockID flow.Identifier) error { 346 seal, found := bc.SealsIndex[blockID] 347 if !found { 348 return storerr.ErrNotFound 349 } 350 if seal == nil { 351 return storerr.ErrNotFound 352 } 353 return nil 354 }, 355 ) 356 357 // ~~~~~~~~~~~~~~~~~~~~~~~ SETUP RECEIPTS MEMPOOL ~~~~~~~~~~~~~~~~~~~~~~ // 358 bc.ReceiptsPL = &mempool.ExecutionTree{} 359 bc.ReceiptsPL.On("Size").Return(uint(0)).Maybe() // only for metrics 360 bc.ReceiptsPL.On("HasReceipt", mock.AnythingOfType("*flow.ExecutionReceipt")).Return(false) 361 362 bc.PendingReceipts = &mempool.PendingReceipts{} 363 364 // ~~~~~~~~~~~~~~~~~~~~~~~~ SETUP SEALS MEMPOOL ~~~~~~~~~~~~~~~~~~~~~~~~ // 365 bc.PendingSeals = make(map[flow.Identifier]*flow.IncorporatedResultSeal) 366 bc.SealsPL = &mempool.IncorporatedResultSeals{} 367 bc.SealsPL.On("Size").Return(uint(0)).Maybe() // only for metrics 368 bc.SealsPL.On("Limit").Return(uint(1000)).Maybe() 369 bc.SealsPL.On("ByID", mock.Anything).Return( 370 func(sealID flow.Identifier) *flow.IncorporatedResultSeal { 371 return bc.PendingSeals[sealID] 372 }, 373 func(sealID flow.Identifier) bool { 374 _, found := bc.PendingSeals[sealID] 375 return found 376 }, 377 ).Maybe() 378 bc.SealsPL.On("All").Return( 379 func() []*flow.IncorporatedResultSeal { 380 seals := make([]*flow.IncorporatedResultSeal, 0, len(bc.PendingSeals)) 381 for _, seal := range bc.PendingSeals { 382 seals = append(seals, seal) 383 } 384 return seals 385 }, 386 ).Maybe() 387 388 bc.Assigner = &module.ChunkAssigner{} 389 bc.Assignments = make(map[flow.Identifier]*chunks.Assignment) 390 } 391 392 func StateSnapshotForUnknownBlock() *protocol.Snapshot { 393 snapshot := &protocol.Snapshot{} 394 snapshot.On("Identity", mock.Anything).Return( 395 nil, storerr.ErrNotFound, 396 ) 397 snapshot.On("Identities", mock.Anything).Return( 398 nil, storerr.ErrNotFound, 399 ) 400 snapshot.On("Head", mock.Anything).Return( 401 nil, storerr.ErrNotFound, 402 ) 403 return snapshot 404 } 405 406 func StateSnapshotForKnownBlock(block *flow.Header, identities map[flow.Identifier]*flow.Identity) *protocol.Snapshot { 407 snapshot := &protocol.Snapshot{} 408 snapshot.On("Identity", mock.Anything).Return( 409 func(nodeID flow.Identifier) *flow.Identity { 410 return identities[nodeID] 411 }, 412 func(nodeID flow.Identifier) error { 413 _, found := identities[nodeID] 414 if !found { 415 return realproto.IdentityNotFoundError{NodeID: nodeID} 416 } 417 return nil 418 }, 419 ) 420 snapshot.On("Identities", mock.Anything).Return( 421 func(selector flow.IdentityFilter) flow.IdentityList { 422 var idts flow.IdentityList 423 for _, i := range identities { 424 if selector(i) { 425 idts = append(idts, i) 426 } 427 } 428 return idts 429 }, 430 func(selector flow.IdentityFilter) error { 431 return nil 432 }, 433 ) 434 snapshot.On("Head").Return(block, nil) 435 return snapshot 436 } 437 438 func ApprovalFor(result *flow.ExecutionResult, chunkIdx uint64, approverID flow.Identifier) *flow.ResultApproval { 439 return ResultApprovalFixture( 440 WithBlockID(result.BlockID), 441 WithExecutionResultID(result.ID()), 442 WithApproverID(approverID), 443 WithChunk(chunkIdx), 444 ) 445 } 446 447 func EntityWithID(expectedID flow.Identifier) interface{} { 448 return mock.MatchedBy( 449 func(entity flow.Entity) bool { 450 return expectedID == entity.ID() 451 }) 452 } 453 454 // subgraphFixture represents a subgraph of the blockchain: 455 // 456 // Result -----------------------------------> Block 457 // | | 458 // | v 459 // | ParentBlock 460 // v 461 // PreviousResult ---> PreviousResult.BlockID 462 // 463 // Depending on validity of the subgraph: 464 // - valid: PreviousResult.BlockID == ParentBlock.ID() 465 // - invalid: PreviousResult.BlockID != ParentBlock.ID() 466 type subgraphFixture struct { 467 Block *flow.Block 468 ParentBlock *flow.Block 469 Result *flow.ExecutionResult 470 PreviousResult *flow.ExecutionResult 471 IncorporatedResult *flow.IncorporatedResult 472 Assignment *chunks.Assignment 473 Approvals map[uint64]map[flow.Identifier]*flow.ResultApproval // chunkIndex -> Verifier Node ID -> Approval 474 } 475 476 // Generates a valid subgraph: 477 // let 478 // - R1 be a result which pertains to blockA 479 // - R2 be R1's previous result, 480 // where R2 pertains to blockB 481 // 482 // The execution results form a valid subgraph if and only if: 483 // 484 // blockA.ParentID == blockB.ID 485 func (bc *BaseChainSuite) ValidSubgraphFixture() subgraphFixture { 486 // BLOCKS: <- previousBlock <- block 487 parentBlock := BlockFixture() 488 parentBlock.SetPayload(PayloadFixture(WithGuarantees(CollectionGuaranteesFixture(12)...))) 489 block := BlockWithParentFixture(parentBlock.Header) 490 block.SetPayload(PayloadFixture(WithGuarantees(CollectionGuaranteesFixture(12)...))) 491 492 // RESULTS for Blocks: 493 previousResult := ExecutionResultFixture(WithBlock(&parentBlock)) 494 result := ExecutionResultFixture( 495 WithBlock(block), 496 WithPreviousResult(*previousResult), 497 ) 498 499 // Exec Receipt for block with valid subgraph 500 incorporatedResult := IncorporatedResult.Fixture(IncorporatedResult.WithResult(result)) 501 502 // assign each chunk to 50% of validation Nodes and generate respective approvals 503 assignment := chunks.NewAssignment() 504 assignedVerifiersPerChunk := uint(len(bc.Approvers) / 2) 505 approvals := make(map[uint64]map[flow.Identifier]*flow.ResultApproval) 506 for _, chunk := range incorporatedResult.Result.Chunks { 507 assignedVerifiers := bc.Approvers.Sample(assignedVerifiersPerChunk) 508 assignment.Add(chunk, assignedVerifiers.NodeIDs()) 509 510 // generate approvals 511 chunkApprovals := make(map[flow.Identifier]*flow.ResultApproval) 512 for _, approver := range assignedVerifiers { 513 chunkApprovals[approver.NodeID] = ApprovalFor(incorporatedResult.Result, chunk.Index, approver.NodeID) 514 } 515 approvals[chunk.Index] = chunkApprovals 516 } 517 518 return subgraphFixture{ 519 Block: block, 520 ParentBlock: &parentBlock, 521 Result: result, 522 PreviousResult: previousResult, 523 IncorporatedResult: incorporatedResult, 524 Assignment: assignment, 525 Approvals: approvals, 526 } 527 } 528 529 func (bc *BaseChainSuite) Extend(block *flow.Block) { 530 blockID := block.ID() 531 bc.Blocks[blockID] = block 532 if seal, ok := bc.SealsIndex[block.Header.ParentID]; ok { 533 bc.SealsIndex[block.ID()] = seal 534 } 535 536 for _, result := range block.Payload.Results { 537 // Exec Receipt for block with valid subgraph 538 incorporatedResult := IncorporatedResult.Fixture(IncorporatedResult.WithResult(result), 539 IncorporatedResult.WithIncorporatedBlockID(blockID)) 540 541 // assign each chunk to 50% of validation Nodes and generate respective approvals 542 assignment := chunks.NewAssignment() 543 assignedVerifiersPerChunk := uint(len(bc.Approvers) / 2) 544 approvals := make(map[uint64]map[flow.Identifier]*flow.ResultApproval) 545 for _, chunk := range incorporatedResult.Result.Chunks { 546 assignedVerifiers := bc.Approvers.Sample(assignedVerifiersPerChunk) 547 assignment.Add(chunk, assignedVerifiers.NodeIDs()) 548 549 // generate approvals 550 chunkApprovals := make(map[flow.Identifier]*flow.ResultApproval) 551 for _, approver := range assignedVerifiers { 552 chunkApprovals[approver.NodeID] = ApprovalFor(incorporatedResult.Result, chunk.Index, approver.NodeID) 553 } 554 approvals[chunk.Index] = chunkApprovals 555 } 556 bc.Assigner.On("Assign", incorporatedResult.Result, incorporatedResult.IncorporatedBlockID).Return(assignment, nil).Maybe() 557 bc.Assignments[incorporatedResult.Result.ID()] = assignment 558 bc.PersistedResults[result.ID()] = result 559 } 560 for _, seal := range block.Payload.Seals { 561 bc.SealsIndex[blockID] = seal 562 } 563 } 564 565 // addSubgraphFixtureToMempools adds add entities in subgraph to mempools and persistent storage mocks 566 func (bc *BaseChainSuite) AddSubgraphFixtureToMempools(subgraph subgraphFixture) { 567 bc.Blocks[subgraph.ParentBlock.ID()] = subgraph.ParentBlock 568 bc.Blocks[subgraph.Block.ID()] = subgraph.Block 569 bc.PersistedResults[subgraph.PreviousResult.ID()] = subgraph.PreviousResult 570 bc.PersistedResults[subgraph.Result.ID()] = subgraph.Result 571 bc.Assigner.On("Assign", subgraph.IncorporatedResult.Result, subgraph.IncorporatedResult.IncorporatedBlockID).Return(subgraph.Assignment, nil).Maybe() 572 }