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

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