github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/module/validation/seal_validator.go (about)

     1  package validation
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/onflow/crypto/hash"
     7  
     8  	"github.com/onflow/flow-go/engine"
     9  	"github.com/onflow/flow-go/model/flow"
    10  	"github.com/onflow/flow-go/module"
    11  	"github.com/onflow/flow-go/module/signature"
    12  	"github.com/onflow/flow-go/state/fork"
    13  	"github.com/onflow/flow-go/state/protocol"
    14  	"github.com/onflow/flow-go/storage"
    15  )
    16  
    17  // sealValidator holds all needed context for checking seal
    18  // validity against current protocol state.
    19  type sealValidator struct {
    20  	state                protocol.State
    21  	assigner             module.ChunkAssigner
    22  	signatureHasher      hash.Hasher
    23  	seals                storage.Seals
    24  	headers              storage.Headers
    25  	index                storage.Index
    26  	results              storage.ExecutionResults
    27  	sealingConfigsGetter module.SealingConfigsGetter // number of required approvals per chunk to construct a seal
    28  	metrics              module.ConsensusMetrics
    29  }
    30  
    31  func NewSealValidator(
    32  	state protocol.State,
    33  	headers storage.Headers,
    34  	index storage.Index,
    35  	results storage.ExecutionResults,
    36  	seals storage.Seals,
    37  	assigner module.ChunkAssigner,
    38  	sealingConfigsGetter module.SealingConfigsGetter,
    39  	metrics module.ConsensusMetrics,
    40  ) *sealValidator {
    41  	return &sealValidator{
    42  		state:                state,
    43  		assigner:             assigner,
    44  		signatureHasher:      signature.NewBLSHasher(signature.ResultApprovalTag),
    45  		headers:              headers,
    46  		results:              results,
    47  		seals:                seals,
    48  		index:                index,
    49  		sealingConfigsGetter: sealingConfigsGetter,
    50  		metrics:              metrics,
    51  	}
    52  }
    53  
    54  func (s *sealValidator) verifySealSignature(aggregatedSignatures *flow.AggregatedSignature,
    55  	chunk *flow.Chunk, executionResultID flow.Identifier) error {
    56  	// TODO: replace implementation once proper aggregation is used for Verifiers' attestation signatures.
    57  
    58  	atst := flow.Attestation{
    59  		BlockID:           chunk.BlockID,
    60  		ExecutionResultID: executionResultID,
    61  		ChunkIndex:        chunk.Index,
    62  	}
    63  	atstID := atst.ID()
    64  
    65  	for i, signature := range aggregatedSignatures.VerifierSignatures {
    66  		signerId := aggregatedSignatures.SignerIDs[i]
    67  
    68  		nodeIdentity, err := identityForNode(s.state, chunk.BlockID, signerId)
    69  		if err != nil {
    70  			return err
    71  		}
    72  
    73  		valid, err := nodeIdentity.StakingPubKey.Verify(signature, atstID[:], s.signatureHasher)
    74  		if err != nil {
    75  			return fmt.Errorf("failed to verify signature: %w", err)
    76  		}
    77  
    78  		if !valid {
    79  			return engine.NewInvalidInputErrorf("Invalid signature for (%x)", nodeIdentity.NodeID)
    80  		}
    81  	}
    82  
    83  	return nil
    84  }
    85  
    86  // Validate checks the compliance of the payload seals and returns the last
    87  // valid seal on the fork up to and including `candidate`. To be valid, we
    88  // require that seals
    89  // 1) form a valid chain on top of the last seal as of the parent of `candidate` and
    90  // 2) correspond to blocks and execution results incorporated on the current fork.
    91  // 3) has valid signatures for all of its chunks.
    92  //
    93  // Note that we don't explicitly check that sealed results satisfy the sub-graph
    94  // check. Nevertheless, correctness in this regard is guaranteed because:
    95  //   - We only allow seals that correspond to ExecutionReceipts that were
    96  //     incorporated in this fork.
    97  //   - We only include ExecutionReceipts whose results pass the sub-graph check
    98  //     (as part of ReceiptValidator).
    99  //
   100  // => Therefore, only seals whose results pass the sub-graph check will be
   101  // allowed.
   102  func (s *sealValidator) Validate(candidate *flow.Block) (*flow.Seal, error) {
   103  	header := candidate.Header
   104  	payload := candidate.Payload
   105  	parentID := header.ParentID
   106  
   107  	// Get the latest seal in the fork that ends with the candidate's parent.
   108  	// The protocol state saves this information for each block that has been
   109  	// successfully added to the chain tree (even when the added block does not
   110  	// itself contain a seal). Per prerequisite of this method, the candidate block's parent must
   111  	// be part of the main chain (without any missing ancestors). For every block B that is
   112  	// attached to the main chain, we store the latest seal in the fork that ends with B.
   113  	// Therefore, _not_ finding the latest sealed block of the parent constitutes
   114  	// a fatal internal error.
   115  	lastSealUpToParent, err := s.seals.HighestInFork(parentID)
   116  	if err != nil {
   117  		return nil, fmt.Errorf("could not retrieve parent seal (%x): %w", parentID, err)
   118  	}
   119  
   120  	// if there is no seal in the block payload, use the last sealed block of
   121  	// the parent block as the last sealed block of the given block.
   122  	if len(payload.Seals) == 0 {
   123  		return lastSealUpToParent, nil
   124  	}
   125  
   126  	// map each seal to the block it is sealing for easy lookup; we will need to
   127  	// successfully connect _all_ of these seals to the last sealed block for
   128  	// the payload to be valid
   129  	byBlock := make(map[flow.Identifier]*flow.Seal)
   130  	for _, seal := range payload.Seals {
   131  		byBlock[seal.BlockID] = seal
   132  	}
   133  	if len(payload.Seals) != len(byBlock) {
   134  		return nil, engine.NewInvalidInputError("multiple seals for the same block")
   135  	}
   136  
   137  	// incorporatedResults collects execution results that are incorporated in unsealed
   138  	// blocks; CAUTION: some of these incorporated results might already be sealed.
   139  	incorporatedResults := make(map[flow.Identifier]*flow.IncorporatedResult)
   140  
   141  	// IDs of unsealed blocks on the fork
   142  	var unsealedBlockIDs []flow.Identifier
   143  
   144  	// Traverse fork starting from the lowest unsealed block (included) up to the parent block (included).
   145  	// For each visited block collect: IncorporatedResults and block ID
   146  	forkCollector := func(header *flow.Header) error {
   147  		blockID := header.ID()
   148  		if blockID == parentID {
   149  			// Important protocol edge case: There must be at least one block in between the block incorporating
   150  			// a result and the block sealing the result. This is because we need the Source of Randomness for
   151  			// the block that _incorporates_ the result, to compute the verifier assignment. Therefore, we require
   152  			// that the block _incorporating_ the result has at least one child in the fork, _before_ we include
   153  			// the seal. Thereby, we guarantee that a verifier assignment can be computed without needing
   154  			// information from the block that we are just constructing. Hence, we don't allow results to be
   155  			// sealed that were incorporated in the immediate parent which is being extended.
   156  			return nil
   157  		}
   158  
   159  		// keep track of blocks on the fork
   160  		unsealedBlockIDs = append(unsealedBlockIDs, blockID)
   161  
   162  		// Collect incorporated results
   163  		payloadIndex, err := s.index.ByBlockID(blockID)
   164  		if err != nil {
   165  			return fmt.Errorf("could not get block payload %x: %w", blockID, err)
   166  		}
   167  		for _, resultID := range payloadIndex.ResultIDs {
   168  			result, err := s.results.ByID(resultID)
   169  			if err != nil {
   170  				return fmt.Errorf("internal error fetching result %v incorporated in block %v: %w", resultID, blockID, err)
   171  			}
   172  			incorporatedResults[resultID] = flow.NewIncorporatedResult(blockID, result)
   173  		}
   174  		return nil
   175  	}
   176  	err = fork.TraverseForward(s.headers, parentID, forkCollector, fork.ExcludingBlock(lastSealUpToParent.BlockID))
   177  	if err != nil {
   178  		return nil, fmt.Errorf("internal error collecting incorporated results from unsealed fork: %w", err)
   179  	}
   180  
   181  	// We do _not_ add the results from the candidate block's own payload to incorporatedResults.
   182  	// That's because a result requires to be added to a block first in order to determine
   183  	// its chunk assignment for verification. Therefore a seal can only be added in the
   184  	// next block or after. In other words, a receipt and its seal can't be the same block.
   185  
   186  	// Iterate through the unsealed blocks, starting at the one with lowest
   187  	// height and try to create a chain of valid seals.
   188  	latestSeal := lastSealUpToParent
   189  	for _, blockID := range unsealedBlockIDs {
   190  		// if there are no more seals left, we can exit earlier
   191  		if len(byBlock) == 0 {
   192  			return latestSeal, nil
   193  		}
   194  
   195  		// the chain of seals should not skip blocks
   196  		seal, found := byBlock[blockID]
   197  		if !found {
   198  			return nil, engine.NewInvalidInputErrorf("chain of seals broken (missing seal for block %x)", blockID)
   199  		}
   200  		delete(byBlock, blockID)
   201  
   202  		// the sealed result must be previously incorporated in the fork:
   203  		incorporatedResult, ok := incorporatedResults[seal.ResultID]
   204  		if !ok {
   205  			return nil, engine.NewInvalidInputErrorf("seal %x does not correspond to a result on this fork", seal.ID())
   206  		}
   207  
   208  		// check the integrity of the seal (by itself)
   209  		err := s.validateSeal(seal, incorporatedResult)
   210  		if err != nil {
   211  			if !engine.IsInvalidInputError(err) {
   212  				return nil, fmt.Errorf("unexpected internal error while validating seal %x for result %x for block %x: %w",
   213  					seal.ID(), seal.ResultID, seal.BlockID, err)
   214  			}
   215  			return nil, fmt.Errorf("invalid seal %x for result %x for block %x: %w", seal.ID(), seal.ResultID, seal.BlockID, err)
   216  		}
   217  
   218  		// check that the sealed execution results form a chain
   219  		if incorporatedResult.Result.PreviousResultID != latestSeal.ResultID {
   220  			return nil, engine.NewInvalidInputErrorf("sealed execution results for block %x does not connect to previously sealed result", blockID)
   221  		}
   222  
   223  		latestSeal = seal
   224  	}
   225  
   226  	// it is illegal to include more seals than there are unsealed blocks in the fork
   227  	if len(byBlock) > 0 {
   228  		return nil, engine.NewInvalidInputErrorf("more seals then unsealed blocks in fork (left: %d)", len(byBlock))
   229  	}
   230  
   231  	return latestSeal, nil
   232  }
   233  
   234  // validateSeal performs integrity checks of single seal. To be valid, we
   235  // require that seal:
   236  // 1) Contains correct number of approval signatures, one aggregated sig for each chunk.
   237  // 2) Every aggregated signature contains valid signer ids. module.ChunkAssigner is used to perform this check.
   238  // 3) Every aggregated signature contains valid signatures.
   239  // Returns:
   240  // * nil - in case of success
   241  // * engine.InvalidInputError - in case of malformed seal
   242  // * exception - in case of unexpected error
   243  func (s *sealValidator) validateSeal(seal *flow.Seal, incorporatedResult *flow.IncorporatedResult) error {
   244  	executionResult := incorporatedResult.Result
   245  
   246  	// check that each chunk has an AggregatedSignature
   247  	if len(seal.AggregatedApprovalSigs) != executionResult.Chunks.Len() {
   248  		return engine.NewInvalidInputErrorf("mismatching signatures, expected: %d, got: %d",
   249  			executionResult.Chunks.Len(),
   250  			len(seal.AggregatedApprovalSigs))
   251  	}
   252  
   253  	assignments, err := s.assigner.Assign(executionResult, incorporatedResult.IncorporatedBlockID)
   254  	if err != nil {
   255  		return fmt.Errorf("failed to retrieve verifier assignment for result %x incorporated in block %x: %w",
   256  			executionResult.ID(), incorporatedResult.IncorporatedBlockID, err)
   257  	}
   258  
   259  	// Check that each AggregatedSignature has enough valid signatures from
   260  	// verifiers that were assigned to the corresponding chunk.
   261  	executionResultID := executionResult.ID()
   262  	emergencySealed := false
   263  	for _, chunk := range executionResult.Chunks {
   264  		chunkSigs := &seal.AggregatedApprovalSigs[chunk.Index]
   265  
   266  		// for each approving Verification Node (SignerID), we expect exactly one signature
   267  		numberApprovers := chunkSigs.CardinalitySignerSet()
   268  		if len(chunkSigs.SignerIDs) != numberApprovers {
   269  			return engine.NewInvalidInputErrorf("chunk %d contains repeated approvals from the same verifier", chunk.Index)
   270  		}
   271  		if len(chunkSigs.VerifierSignatures) != numberApprovers {
   272  			return engine.NewInvalidInputErrorf("expecting signatures from %d approvers but got %d", numberApprovers, len(chunkSigs.VerifierSignatures))
   273  		}
   274  
   275  		// the chunk must have been approved by at least the minimally
   276  		// required number of Verification Nodes
   277  		requireApprovalsForSealConstruction := s.sealingConfigsGetter.RequireApprovalsForSealConstructionDynamicValue()
   278  		requireApprovalsForSealVerification := s.sealingConfigsGetter.RequireApprovalsForSealVerificationConst()
   279  		if uint(numberApprovers) < requireApprovalsForSealConstruction {
   280  			if uint(numberApprovers) >= requireApprovalsForSealVerification {
   281  				// Emergency sealing is a _temporary_ fallback to reduce the probability of
   282  				// sealing halts due to bugs in the verification nodes, where they don't
   283  				// approve a chunk even though they should (false-negative).
   284  				// TODO: remove this fallback for BFT
   285  				emergencySealed = true
   286  			} else {
   287  				return engine.NewInvalidInputErrorf("chunk %d has %d approvals but require at least %d",
   288  					chunk.Index, numberApprovers, requireApprovalsForSealVerification)
   289  			}
   290  		}
   291  
   292  		// only Verification Nodes that were assigned to the chunk are allowed to approve it
   293  		for _, signerId := range chunkSigs.SignerIDs {
   294  			if !assignments.HasVerifier(chunk, signerId) {
   295  				return engine.NewInvalidInputErrorf("invalid signer id at chunk: %d", chunk.Index)
   296  			}
   297  		}
   298  
   299  		// Verification Nodes' approval signatures must be valid
   300  		err := s.verifySealSignature(chunkSigs, chunk, executionResultID)
   301  		if err != nil {
   302  			return fmt.Errorf("invalid seal signature: %w", err)
   303  		}
   304  	}
   305  
   306  	// TODO: remove this metric after emergency-sealing development
   307  	if emergencySealed {
   308  		s.metrics.EmergencySeal()
   309  	}
   310  
   311  	return nil
   312  }