github.com/koko1123/flow-go-1@v0.29.6/module/validation/receipt_validator.go (about)

     1  package validation
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/onflow/flow-go/crypto/hash"
     8  	"github.com/koko1123/flow-go-1/engine"
     9  	"github.com/koko1123/flow-go-1/model/flow"
    10  	"github.com/koko1123/flow-go-1/module/signature"
    11  	"github.com/koko1123/flow-go-1/state/fork"
    12  	"github.com/koko1123/flow-go-1/state/protocol"
    13  	"github.com/koko1123/flow-go-1/storage"
    14  )
    15  
    16  // receiptValidator holds all needed context for checking
    17  // receipt validity against current protocol state.
    18  type receiptValidator struct {
    19  	headers         storage.Headers
    20  	seals           storage.Seals
    21  	state           protocol.State
    22  	index           storage.Index
    23  	results         storage.ExecutionResults
    24  	signatureHasher hash.Hasher
    25  }
    26  
    27  func NewReceiptValidator(state protocol.State,
    28  	headers storage.Headers,
    29  	index storage.Index,
    30  	results storage.ExecutionResults,
    31  	seals storage.Seals,
    32  ) *receiptValidator {
    33  	rv := &receiptValidator{
    34  		state:           state,
    35  		headers:         headers,
    36  		index:           index,
    37  		results:         results,
    38  		signatureHasher: signature.NewBLSHasher(signature.ExecutionReceiptTag),
    39  		seals:           seals,
    40  	}
    41  
    42  	return rv
    43  }
    44  
    45  func (v *receiptValidator) verifySignature(receipt *flow.ExecutionReceiptMeta, nodeIdentity *flow.Identity) error {
    46  	id := receipt.ID()
    47  	valid, err := nodeIdentity.StakingPubKey.Verify(receipt.ExecutorSignature, id[:], v.signatureHasher)
    48  	if err != nil {
    49  		return fmt.Errorf("failed to verify signature: %w", err)
    50  	}
    51  
    52  	if !valid {
    53  		return engine.NewInvalidInputErrorf("invalid signature for (%x)", nodeIdentity.NodeID)
    54  	}
    55  
    56  	return nil
    57  }
    58  
    59  func (v *receiptValidator) verifyChunksFormat(result *flow.ExecutionResult) error {
    60  	for index, chunk := range result.Chunks.Items() {
    61  		if uint(index) != chunk.CollectionIndex {
    62  			return engine.NewInvalidInputErrorf("invalid CollectionIndex, expected %d got %d", index, chunk.CollectionIndex)
    63  		}
    64  
    65  		if chunk.BlockID != result.BlockID {
    66  			return engine.NewInvalidInputErrorf("invalid blockID, expected %v got %v", result.BlockID, chunk.BlockID)
    67  		}
    68  	}
    69  
    70  	// we create one chunk per collection, plus the
    71  	// system chunk. so we can check if the chunk number matches with the
    72  	// number of guarantees plus one; this will ensure the execution receipt
    73  	// cannot lie about having less chunks and having the remaining ones
    74  	// approved
    75  	requiredChunks := 1 // system chunk: must exist for block's ExecutionResult, even if block payload itself is empty
    76  
    77  	index, err := v.index.ByBlockID(result.BlockID)
    78  	if err != nil {
    79  		// the mutator will always create payload index for a valid block
    80  		return fmt.Errorf("could not find payload index for executed block %v: %w", result.BlockID, err)
    81  	}
    82  
    83  	requiredChunks += len(index.CollectionIDs)
    84  
    85  	if result.Chunks.Len() != requiredChunks {
    86  		return engine.NewInvalidInputErrorf("invalid number of chunks, expected %d got %d",
    87  			requiredChunks, result.Chunks.Len())
    88  	}
    89  
    90  	return nil
    91  }
    92  
    93  func (v *receiptValidator) fetchResult(resultID flow.Identifier) (*flow.ExecutionResult, error) {
    94  	prevResult, err := v.results.ByID(resultID)
    95  	if err != nil {
    96  		if errors.Is(err, storage.ErrNotFound) {
    97  			return nil, engine.NewUnverifiableInputError("cannot retrieve result: %v", resultID)
    98  		}
    99  		return nil, err
   100  	}
   101  	return prevResult, nil
   102  }
   103  
   104  // subgraphCheck enforces that result forms a valid sub-graph:
   105  // Let R1 be a result that references block A, and R2 be R1's parent result.
   106  // The execution results form a valid subgraph if and only if R2 references
   107  // A's parent.
   108  func (v *receiptValidator) subgraphCheck(result *flow.ExecutionResult, prevResult *flow.ExecutionResult) error {
   109  	block, err := v.state.AtBlockID(result.BlockID).Head()
   110  	if err != nil {
   111  		if errors.Is(err, storage.ErrNotFound) {
   112  			return engine.NewInvalidInputErrorf("no block found %v %w", result.BlockID, err)
   113  		}
   114  		return err
   115  	}
   116  
   117  	// validating the PreviousResultID field
   118  	// ExecutionResult_X.PreviousResult.BlockID must equal to Block_X.ParentBlockID
   119  	// for instance: given the following chain
   120  	// A <- B <- C (ER_A) <- D
   121  	// a result ER_C with `ID(ER_A)` as its ER_C.Result.PreviousResultID
   122  	// would be invalid, because `ER_C.Result.PreviousResultID` must be ID(ER_B)
   123  	if prevResult.BlockID != block.ParentID {
   124  		return engine.NewInvalidInputErrorf("invalid block for previous result %v", prevResult.BlockID)
   125  	}
   126  
   127  	return nil
   128  }
   129  
   130  // resultChainCheck enforces that the end state of the parent result
   131  // matches the current result's start state
   132  func (v *receiptValidator) resultChainCheck(result *flow.ExecutionResult, prevResult *flow.ExecutionResult) error {
   133  	finalState, err := prevResult.FinalStateCommitment()
   134  	if err != nil {
   135  		return fmt.Errorf("missing final state commitment in parent result %v", prevResult.ID())
   136  	}
   137  	initialState, err := result.InitialStateCommit()
   138  	if err != nil {
   139  		return engine.NewInvalidInputErrorf("missing initial state commitment in execution result %v", result.ID())
   140  	}
   141  	if initialState != finalState {
   142  		return engine.NewInvalidInputErrorf("execution results do not form chain: expecting init state %x, but got %x",
   143  			finalState, initialState)
   144  	}
   145  	return nil
   146  }
   147  
   148  // Validate verifies that the ExecutionReceipt satisfies
   149  // the following conditions:
   150  //   - is from Execution node with positive weight
   151  //   - has valid signature
   152  //   - chunks are in correct format
   153  //   - execution result has a valid parent and satisfies the subgraph check
   154  //
   155  // Returns nil if all checks passed successfully.
   156  // Expected errors during normal operations:
   157  //   - engine.InvalidInputError
   158  //     if receipt violates protocol condition
   159  //   - engine.UnverifiableInputError
   160  //     if receipt's parent result is unknown
   161  func (v *receiptValidator) Validate(receipt *flow.ExecutionReceipt) error {
   162  	// TODO: this can be optimized by checking if result was already stored and validated.
   163  	// This needs to be addressed later since many tests depend on this behavior.
   164  	prevResult, err := v.fetchResult(receipt.ExecutionResult.PreviousResultID)
   165  	if err != nil {
   166  		return fmt.Errorf("error fetching parent result of receipt %v: %w", receipt.ID(), err)
   167  	}
   168  
   169  	// first validate result to avoid signature check in in `validateReceipt` in case result is invalid.
   170  	err = v.validateResult(&receipt.ExecutionResult, prevResult)
   171  	if err != nil {
   172  		return fmt.Errorf("could not validate single result %v at index: %w", receipt.ExecutionResult.ID(), err)
   173  	}
   174  
   175  	err = v.validateReceipt(receipt.Meta(), receipt.ExecutionResult.BlockID)
   176  	if err != nil {
   177  		// It's very important that we fail the whole validation if one of the receipts is invalid.
   178  		// It allows us to make assumptions as stated in previous comment.
   179  		return fmt.Errorf("could not validate single receipt %v: %w", receipt.ID(), err)
   180  	}
   181  
   182  	return nil
   183  }
   184  
   185  // ValidatePayload verifies the ExecutionReceipts and ExecutionResults
   186  // in the payload for compliance with the protocol:
   187  // Receipts:
   188  //   - are from Execution node with positive weight
   189  //   - have valid signature
   190  //   - chunks are in correct format
   191  //   - no duplicates in fork
   192  //
   193  // Results:
   194  //   - have valid parents and satisfy the subgraph check
   195  //   - extend the execution tree, where the tree root is the latest
   196  //     finalized block and only results from this fork are included
   197  //   - no duplicates in fork
   198  //
   199  // Expected errors during normal operations:
   200  //   - engine.InvalidInputError
   201  //     if some receipts in the candidate block violate protocol condition
   202  //   - engine.UnverifiableInputError
   203  //     if for some of the receipts, their respective parent result is unknown
   204  func (v *receiptValidator) ValidatePayload(candidate *flow.Block) error {
   205  	header := candidate.Header
   206  	payload := candidate.Payload
   207  
   208  	// return if nothing to validate
   209  	if len(payload.Receipts) == 0 && len(payload.Results) == 0 {
   210  		return nil
   211  	}
   212  
   213  	// Get the latest sealed result on this fork and the corresponding block,
   214  	// whose result is sealed. This block is not necessarily finalized.
   215  	lastSeal, err := v.seals.HighestInFork(header.ParentID)
   216  	if err != nil {
   217  		return fmt.Errorf("could not retrieve latest seal for fork with head %x: %w", header.ParentID, err)
   218  	}
   219  	latestSealedResult, err := v.results.ByID(lastSeal.ResultID)
   220  	if err != nil {
   221  		return fmt.Errorf("could not retrieve latest sealed result %x: %w", lastSeal.ResultID, err)
   222  	}
   223  
   224  	// forkBlocks is the set of all _unsealed_ blocks on the fork. We
   225  	// use it to identify receipts that are for blocks not in the fork.
   226  	forkBlocks := make(map[flow.Identifier]struct{})
   227  
   228  	// Sub-Set of the execution tree: only contains `ExecutionResult`s that descent from latestSealedResult.
   229  	// Used for detecting duplicates and results with invalid parent results.
   230  	executionTree := make(map[flow.Identifier]*flow.ExecutionResult)
   231  	executionTree[lastSeal.ResultID] = latestSealedResult
   232  
   233  	// Set of previously included receipts. Used for detecting duplicates.
   234  	forkReceipts := make(map[flow.Identifier]struct{})
   235  
   236  	// Start from the lowest unsealed block and walk the chain upwards until we
   237  	// hit the candidate's parent. For each visited block track:
   238  	bookKeeper := func(block *flow.Header) error {
   239  		blockID := block.ID()
   240  		// track encountered blocks
   241  		forkBlocks[blockID] = struct{}{}
   242  
   243  		payloadIndex, err := v.index.ByBlockID(blockID)
   244  		if err != nil {
   245  			return fmt.Errorf("could not retrieve payload index: %w", err)
   246  		}
   247  
   248  		// track encountered receipts
   249  		for _, recID := range payloadIndex.ReceiptIDs {
   250  			forkReceipts[recID] = struct{}{}
   251  		}
   252  
   253  		// extend execution tree
   254  		for _, resultID := range payloadIndex.ResultIDs {
   255  			result, err := v.results.ByID(resultID)
   256  			if err != nil {
   257  				return fmt.Errorf("could not retrieve result %v: %w", resultID, err)
   258  			}
   259  			if _, ok := executionTree[result.PreviousResultID]; !ok {
   260  				// We only collect results that directly descend from the last sealed result.
   261  				// Because Results are listed in an order that satisfies the parent-first
   262  				// relationship, we can skip all results whose parents are unknown.
   263  				continue
   264  			}
   265  			executionTree[resultID] = result
   266  		}
   267  		return nil
   268  	}
   269  	err = fork.TraverseForward(v.headers, header.ParentID, bookKeeper, fork.ExcludingBlock(lastSeal.BlockID))
   270  	if err != nil {
   271  		return fmt.Errorf("internal error while traversing the ancestor fork of unsealed blocks: %w", err)
   272  	}
   273  
   274  	// first validate all results that were included into payload
   275  	// if one of results is invalid we fail the whole check because it could be violating
   276  	// parent-children relationship
   277  	for i, result := range payload.Results {
   278  		resultID := result.ID()
   279  
   280  		// check for duplicated results
   281  		if _, isDuplicate := executionTree[resultID]; isDuplicate {
   282  			return engine.NewInvalidInputErrorf("duplicate result %v at index %d", resultID, i)
   283  		}
   284  
   285  		// any result must extend the execution tree with root latestSealedResult
   286  		prevResult, extendsTree := executionTree[result.PreviousResultID]
   287  		if !extendsTree {
   288  			return engine.NewInvalidInputErrorf("results %v at index %d does not extend execution tree", resultID, i)
   289  		}
   290  
   291  		// result must be for block on fork
   292  		if _, forBlockOnFork := forkBlocks[result.BlockID]; !forBlockOnFork {
   293  			return engine.NewInvalidInputErrorf("results %v at index %d is for block not on fork (%x)", resultID, i, result.BlockID)
   294  		}
   295  
   296  		// validate result
   297  		err = v.validateResult(result, prevResult)
   298  		if err != nil {
   299  			return fmt.Errorf("could not validate result %v at index %d: %w", resultID, i, err)
   300  		}
   301  		executionTree[resultID] = result
   302  	}
   303  
   304  	// check receipts:
   305  	// * no duplicates
   306  	// * must commit to a result in the execution tree with root latestSealedResult,
   307  	//   but not latestSealedResult
   308  	// It's very important that we fail the whole validation if one of the receipts is invalid.
   309  	delete(executionTree, lastSeal.ResultID)
   310  	for i, receipt := range payload.Receipts {
   311  		receiptID := receipt.ID()
   312  
   313  		// error if the result is not part of the execution tree with root latestSealedResult
   314  		result, isForLegitimateResult := executionTree[receipt.ResultID]
   315  		if !isForLegitimateResult {
   316  			return engine.NewInvalidInputErrorf("receipt %v at index %d commits to unexpected result", receiptID, i)
   317  		}
   318  
   319  		// error if the receipt is duplicated in the fork
   320  		if _, isDuplicate := forkReceipts[receiptID]; isDuplicate {
   321  			return engine.NewInvalidInputErrorf("duplicate receipt %v at index %d", receiptID, i)
   322  		}
   323  		forkReceipts[receiptID] = struct{}{}
   324  
   325  		err = v.validateReceipt(receipt, result.BlockID)
   326  		if err != nil {
   327  			return fmt.Errorf("receipt %v at index %d failed validation: %w", receiptID, i, err)
   328  		}
   329  	}
   330  
   331  	return nil
   332  }
   333  
   334  func (v *receiptValidator) validateResult(result *flow.ExecutionResult, prevResult *flow.ExecutionResult) error {
   335  	err := v.verifyChunksFormat(result)
   336  	if err != nil {
   337  		return fmt.Errorf("invalid chunks format for result %v: %w", result.ID(), err)
   338  	}
   339  
   340  	err = v.subgraphCheck(result, prevResult)
   341  	if err != nil {
   342  		return fmt.Errorf("invalid execution result: %w", err)
   343  	}
   344  
   345  	err = v.resultChainCheck(result, prevResult)
   346  	if err != nil {
   347  		return fmt.Errorf("invalid execution results chain: %w", err)
   348  	}
   349  
   350  	return nil
   351  }
   352  
   353  func (v *receiptValidator) validateReceipt(receipt *flow.ExecutionReceiptMeta, blockID flow.Identifier) error {
   354  	identity, err := identityForNode(v.state, blockID, receipt.ExecutorID)
   355  	if err != nil {
   356  		return fmt.Errorf(
   357  			"failed to get executor identity %v at block %v: %w",
   358  			receipt.ExecutorID,
   359  			blockID,
   360  			err)
   361  	}
   362  
   363  	err = ensureNodeHasWeightAndRole(identity, flow.RoleExecution)
   364  	if err != nil {
   365  		return fmt.Errorf("node is not authorized execution node: %w", err)
   366  	}
   367  
   368  	err = v.verifySignature(receipt, identity)
   369  	if err != nil {
   370  		return fmt.Errorf("invalid receipt signature: %w", err)
   371  	}
   372  
   373  	return nil
   374  }