github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/module/mempool/consensus/receipt_equivalence_class.go (about) 1 package consensus 2 3 import ( 4 "errors" 5 "fmt" 6 7 "github.com/onflow/flow-go/model/flow" 8 ) 9 10 // ReceiptsOfSameResult represents a set of ExecutionReceipt all committing to the same 11 // ExecutionResult. As an ExecutionResult contains the Block ID, all results with the same 12 // ID must be for the same block. For optimized storage, we only store the result once. 13 // Mathematically, a ReceiptsOfSameResult struct represents an Equivalence Class of 14 // Execution Receipts. 15 // Implements LevelledForest's Vertex interface. 16 type ReceiptsOfSameResult struct { 17 receipts map[flow.Identifier]*flow.ExecutionReceiptMeta // map from ExecutionReceipt.ID -> ExecutionReceiptMeta 18 result *flow.ExecutionResult 19 resultID flow.Identifier // precomputed ID of result to avoid expensive hashing on each call 20 blockHeader *flow.Header // header of the block which the result is for 21 } 22 23 // NewReceiptsOfSameResult instantiates an empty Equivalence Class (without any receipts) 24 func NewReceiptsOfSameResult(result *flow.ExecutionResult, block *flow.Header) (*ReceiptsOfSameResult, error) { 25 //sanity check: initial result should be for block 26 if block.ID() != result.BlockID { 27 return nil, fmt.Errorf("initial result is for different block") 28 } 29 30 // construct ReceiptsOfSameResult only containing initialReceipt 31 rcpts := make(map[flow.Identifier]*flow.ExecutionReceiptMeta) 32 rs := &ReceiptsOfSameResult{ 33 receipts: rcpts, 34 result: result, 35 resultID: result.ID(), 36 blockHeader: block, 37 } 38 return rs, nil 39 } 40 41 // AddReceipt adds the receipt to the ReceiptsOfSameResult (if not already stored). 42 // Returns: 43 // - uint: number of receipts added (consistent API with AddReceipts()), 44 // Possible values: 0 or 1 45 // - error in case of unforeseen problems 46 func (rsr *ReceiptsOfSameResult) AddReceipt(receipt *flow.ExecutionReceipt) (uint, error) { 47 if receipt.ExecutionResult.ID() != rsr.resultID { 48 return 0, errors.New("cannot add receipt for different result") 49 } 50 51 receiptID := receipt.ID() 52 if rsr.Has(receiptID) { 53 return 0, nil 54 } 55 rsr.receipts[receipt.ID()] = receipt.Meta() 56 return 1, nil 57 } 58 59 // AddReceipts adds the receipts to the ReceiptsOfSameResult (the ones not already stored). 60 // Returns: 61 // - uint: number of receipts added 62 // - error in case of unforeseen problems 63 func (rsr *ReceiptsOfSameResult) AddReceipts(receipts ...*flow.ExecutionReceipt) (uint, error) { 64 receiptsAdded := uint(0) 65 for i := 0; i < len(receipts); i++ { 66 added, err := rsr.AddReceipt(receipts[i]) 67 if err != nil { 68 return receiptsAdded, fmt.Errorf("failed to add receipt (%x) to equivalence class: %w", receipts[i].ID(), err) 69 } 70 receiptsAdded += added 71 } 72 return receiptsAdded, nil 73 } 74 75 func (rsr *ReceiptsOfSameResult) Has(receiptID flow.Identifier) bool { 76 _, found := rsr.receipts[receiptID] 77 return found 78 } 79 80 // Size returns the number of receipts in the equivalence class (i.e. the number of 81 // receipts known for that particular result) 82 func (rsr *ReceiptsOfSameResult) Size() uint { 83 return uint(len(rsr.receipts)) 84 } 85 86 /* Methods implementing LevelledForest's Vertex interface */ 87 88 func (rsr *ReceiptsOfSameResult) VertexID() flow.Identifier { return rsr.resultID } 89 func (rsr *ReceiptsOfSameResult) Level() uint64 { return rsr.blockHeader.Height } 90 func (rsr *ReceiptsOfSameResult) Parent() (flow.Identifier, uint64) { 91 return rsr.result.PreviousResultID, rsr.blockHeader.Height - 1 92 }