github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/storage/badger/payloads.go (about)

     1  package badger
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/dgraph-io/badger/v2"
     8  
     9  	"github.com/onflow/flow-go/model/flow"
    10  	"github.com/onflow/flow-go/storage"
    11  	"github.com/onflow/flow-go/storage/badger/operation"
    12  	"github.com/onflow/flow-go/storage/badger/transaction"
    13  )
    14  
    15  type Payloads struct {
    16  	db         *badger.DB
    17  	index      *Index
    18  	guarantees *Guarantees
    19  	seals      *Seals
    20  	receipts   *ExecutionReceipts
    21  	results    *ExecutionResults
    22  }
    23  
    24  func NewPayloads(db *badger.DB, index *Index, guarantees *Guarantees, seals *Seals, receipts *ExecutionReceipts,
    25  	results *ExecutionResults) *Payloads {
    26  
    27  	p := &Payloads{
    28  		db:         db,
    29  		index:      index,
    30  		guarantees: guarantees,
    31  		seals:      seals,
    32  		receipts:   receipts,
    33  		results:    results,
    34  	}
    35  
    36  	return p
    37  }
    38  
    39  func (p *Payloads) storeTx(blockID flow.Identifier, payload *flow.Payload) func(*transaction.Tx) error {
    40  	// For correct payloads, the execution result is part of the payload or it's already stored
    41  	// in storage. If execution result is not present in either of those places, we error.
    42  	// ATTENTION: this is unnecessarily complex if we have execution receipt which points an execution result
    43  	// which is not included in current payload but was incorporated in one of previous blocks.
    44  
    45  	return func(tx *transaction.Tx) error {
    46  
    47  		resultsByID := payload.Results.Lookup()
    48  		fullReceipts := make([]*flow.ExecutionReceipt, 0, len(payload.Receipts))
    49  		var err error
    50  		for _, meta := range payload.Receipts {
    51  			result, ok := resultsByID[meta.ResultID]
    52  			if !ok {
    53  				result, err = p.results.ByIDTx(meta.ResultID)(tx)
    54  				if err != nil {
    55  					if errors.Is(err, storage.ErrNotFound) {
    56  						err = fmt.Errorf("invalid payload referencing unknown execution result %v, err: %w", meta.ResultID, err)
    57  					}
    58  					return err
    59  				}
    60  			}
    61  			fullReceipts = append(fullReceipts, flow.ExecutionReceiptFromMeta(*meta, *result))
    62  		}
    63  
    64  		// make sure all payload guarantees are stored
    65  		for _, guarantee := range payload.Guarantees {
    66  			err := p.guarantees.storeTx(guarantee)(tx)
    67  			if err != nil {
    68  				return fmt.Errorf("could not store guarantee: %w", err)
    69  			}
    70  		}
    71  
    72  		// make sure all payload seals are stored
    73  		for _, seal := range payload.Seals {
    74  			err := p.seals.storeTx(seal)(tx)
    75  			if err != nil {
    76  				return fmt.Errorf("could not store seal: %w", err)
    77  			}
    78  		}
    79  
    80  		// store all payload receipts
    81  		for _, receipt := range fullReceipts {
    82  			err := p.receipts.storeTx(receipt)(tx)
    83  			if err != nil {
    84  				return fmt.Errorf("could not store receipt: %w", err)
    85  			}
    86  		}
    87  
    88  		// store the index
    89  		err = p.index.storeTx(blockID, payload.Index())(tx)
    90  		if err != nil {
    91  			return fmt.Errorf("could not store index: %w", err)
    92  		}
    93  
    94  		return nil
    95  	}
    96  }
    97  
    98  func (p *Payloads) retrieveTx(blockID flow.Identifier) func(tx *badger.Txn) (*flow.Payload, error) {
    99  	return func(tx *badger.Txn) (*flow.Payload, error) {
   100  
   101  		// retrieve the index
   102  		idx, err := p.index.retrieveTx(blockID)(tx)
   103  		if err != nil {
   104  			return nil, fmt.Errorf("could not retrieve index: %w", err)
   105  		}
   106  
   107  		// retrieve guarantees
   108  		guarantees := make([]*flow.CollectionGuarantee, 0, len(idx.CollectionIDs))
   109  		for _, collID := range idx.CollectionIDs {
   110  			guarantee, err := p.guarantees.retrieveTx(collID)(tx)
   111  			if err != nil {
   112  				return nil, fmt.Errorf("could not retrieve guarantee (%x): %w", collID, err)
   113  			}
   114  			guarantees = append(guarantees, guarantee)
   115  		}
   116  
   117  		// retrieve seals
   118  		seals := make([]*flow.Seal, 0, len(idx.SealIDs))
   119  		for _, sealID := range idx.SealIDs {
   120  			seal, err := p.seals.retrieveTx(sealID)(tx)
   121  			if err != nil {
   122  				return nil, fmt.Errorf("could not retrieve seal (%x): %w", sealID, err)
   123  			}
   124  			seals = append(seals, seal)
   125  		}
   126  
   127  		// retrieve receipts
   128  		receipts := make([]*flow.ExecutionReceiptMeta, 0, len(idx.ReceiptIDs))
   129  		for _, recID := range idx.ReceiptIDs {
   130  			receipt, err := p.receipts.byID(recID)(tx)
   131  			if err != nil {
   132  				return nil, fmt.Errorf("could not retrieve receipt %x: %w", recID, err)
   133  			}
   134  			receipts = append(receipts, receipt.Meta())
   135  		}
   136  
   137  		// retrieve results
   138  		results := make([]*flow.ExecutionResult, 0, len(idx.ResultIDs))
   139  		for _, resID := range idx.ResultIDs {
   140  			result, err := p.results.byID(resID)(tx)
   141  			if err != nil {
   142  				return nil, fmt.Errorf("could not retrieve result %x: %w", resID, err)
   143  			}
   144  			results = append(results, result)
   145  		}
   146  		payload := &flow.Payload{
   147  			Seals:           seals,
   148  			Guarantees:      guarantees,
   149  			Receipts:        receipts,
   150  			Results:         results,
   151  			ProtocolStateID: idx.ProtocolStateID,
   152  		}
   153  
   154  		return payload, nil
   155  	}
   156  }
   157  
   158  func (p *Payloads) Store(blockID flow.Identifier, payload *flow.Payload) error {
   159  	return operation.RetryOnConflictTx(p.db, transaction.Update, p.storeTx(blockID, payload))
   160  }
   161  
   162  func (p *Payloads) ByBlockID(blockID flow.Identifier) (*flow.Payload, error) {
   163  	tx := p.db.NewTransaction(false)
   164  	defer tx.Discard()
   165  	return p.retrieveTx(blockID)(tx)
   166  }