github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/verification/verifier/engine.go (about)

     1  package verifier
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/onflow/crypto"
     8  	"github.com/onflow/crypto/hash"
     9  	"github.com/rs/zerolog"
    10  	"go.opentelemetry.io/otel/attribute"
    11  
    12  	"github.com/onflow/flow-go/engine"
    13  	"github.com/onflow/flow-go/engine/verification/utils"
    14  	chmodels "github.com/onflow/flow-go/model/chunks"
    15  	"github.com/onflow/flow-go/model/flow"
    16  	"github.com/onflow/flow-go/model/flow/filter"
    17  	"github.com/onflow/flow-go/model/messages"
    18  	"github.com/onflow/flow-go/model/verification"
    19  	"github.com/onflow/flow-go/module"
    20  	"github.com/onflow/flow-go/module/signature"
    21  	"github.com/onflow/flow-go/module/trace"
    22  	"github.com/onflow/flow-go/network"
    23  	"github.com/onflow/flow-go/network/channels"
    24  	"github.com/onflow/flow-go/state/protocol"
    25  	"github.com/onflow/flow-go/storage"
    26  	"github.com/onflow/flow-go/utils/logging"
    27  )
    28  
    29  // Engine (verifier engine) verifies chunks, generates result approvals or raises challenges.
    30  // as input it accepts verifiable chunks (chunk + all data needed) and perform verification by
    31  // constructing a partial trie, executing transactions and check the final state commitment and
    32  // other chunk meta data (e.g. tx count)
    33  type Engine struct {
    34  	unit           *engine.Unit               // used to control startup/shutdown
    35  	log            zerolog.Logger             // used to log relevant actions
    36  	metrics        module.VerificationMetrics // used to capture the performance metrics
    37  	tracer         module.Tracer              // used for tracing
    38  	pushConduit    network.Conduit            // used to push result approvals
    39  	pullConduit    network.Conduit            // used to respond to requests for result approvals
    40  	me             module.Local               // used to access local node information
    41  	state          protocol.State             // used to access the protocol state
    42  	approvalHasher hash.Hasher                // used as hasher to sign the result approvals
    43  	chVerif        module.ChunkVerifier       // used to verify chunks
    44  	spockHasher    hash.Hasher                // used for generating spocks
    45  	approvals      storage.ResultApprovals    // used to store result approvals
    46  }
    47  
    48  // New creates and returns a new instance of a verifier engine.
    49  func New(
    50  	log zerolog.Logger,
    51  	metrics module.VerificationMetrics,
    52  	tracer module.Tracer,
    53  	net network.EngineRegistry,
    54  	state protocol.State,
    55  	me module.Local,
    56  	chVerif module.ChunkVerifier,
    57  	approvals storage.ResultApprovals,
    58  ) (*Engine, error) {
    59  
    60  	e := &Engine{
    61  		unit:           engine.NewUnit(),
    62  		log:            log.With().Str("engine", "verifier").Logger(),
    63  		metrics:        metrics,
    64  		tracer:         tracer,
    65  		state:          state,
    66  		me:             me,
    67  		chVerif:        chVerif,
    68  		approvalHasher: utils.NewResultApprovalHasher(),
    69  		spockHasher:    signature.NewBLSHasher(signature.SPOCKTag),
    70  		approvals:      approvals,
    71  	}
    72  
    73  	var err error
    74  	e.pushConduit, err = net.Register(channels.PushApprovals, e)
    75  	if err != nil {
    76  		return nil, fmt.Errorf("could not register engine on approval push channel: %w", err)
    77  	}
    78  
    79  	e.pullConduit, err = net.Register(channels.ProvideApprovalsByChunk, e)
    80  	if err != nil {
    81  		return nil, fmt.Errorf("could not register engine on approval pull channel: %w", err)
    82  	}
    83  
    84  	return e, nil
    85  }
    86  
    87  // Ready returns a channel that is closed when the verifier engine is ready.
    88  func (e *Engine) Ready() <-chan struct{} {
    89  	return e.unit.Ready()
    90  }
    91  
    92  // Done returns a channel that is closed when the verifier engine is done.
    93  func (e *Engine) Done() <-chan struct{} {
    94  	return e.unit.Done()
    95  }
    96  
    97  // SubmitLocal submits an event originating on the local node.
    98  func (e *Engine) SubmitLocal(event interface{}) {
    99  	e.unit.Launch(func() {
   100  		err := e.ProcessLocal(event)
   101  		if err != nil {
   102  			engine.LogError(e.log, err)
   103  		}
   104  	})
   105  }
   106  
   107  // Submit submits the given event from the node with the given origin ID
   108  // for processing in a non-blocking manner. It returns instantly and logs
   109  // a potential processing error internally when done.
   110  func (e *Engine) Submit(channel channels.Channel, originID flow.Identifier, event interface{}) {
   111  	e.unit.Launch(func() {
   112  		err := e.Process(channel, originID, event)
   113  		if err != nil {
   114  			engine.LogError(e.log, err)
   115  		}
   116  	})
   117  }
   118  
   119  // ProcessLocal processes an event originating on the local node.
   120  func (e *Engine) ProcessLocal(event interface{}) error {
   121  	return e.unit.Do(func() error {
   122  		return e.process(e.me.NodeID(), event)
   123  	})
   124  }
   125  
   126  // Process processes the given event from the node with the given origin ID in
   127  // a blocking manner. It returns the potential processing error when done.
   128  func (e *Engine) Process(channel channels.Channel, originID flow.Identifier, event interface{}) error {
   129  	return e.unit.Do(func() error {
   130  		return e.process(originID, event)
   131  	})
   132  }
   133  
   134  // process receives verifiable chunks, evaluate them and send them for chunk verifier
   135  func (e *Engine) process(originID flow.Identifier, event interface{}) error {
   136  	var err error
   137  
   138  	switch resource := event.(type) {
   139  	case *verification.VerifiableChunkData:
   140  		err = e.verifiableChunkHandler(originID, resource)
   141  	case *messages.ApprovalRequest:
   142  		err = e.approvalRequestHandler(originID, resource)
   143  	default:
   144  		return fmt.Errorf("invalid event type (%T)", event)
   145  	}
   146  
   147  	if err != nil {
   148  		// logs the error instead of returning that.
   149  		// returning error would be projected at a higher level by network layer.
   150  		// however, this is an engine-level error, and not network layer error.
   151  		e.log.Warn().Err(err).Msg("engine could not process event successfully")
   152  	}
   153  
   154  	return nil
   155  }
   156  
   157  // verify handles the core verification process. It accepts a verifiable chunk
   158  // and all dependent resources, verifies the chunk, and emits a
   159  // result approval if applicable.
   160  //
   161  // If any part of verification fails, an error is returned, indicating to the
   162  // initiating engine that the verification must be re-tried.
   163  func (e *Engine) verify(ctx context.Context, originID flow.Identifier,
   164  	vc *verification.VerifiableChunkData) error {
   165  	// log it first
   166  	log := e.log.With().Timestamp().
   167  		Hex("origin", logging.ID(originID)).
   168  		Uint64("chunk_index", vc.Chunk.Index).
   169  		Hex("result_id", logging.Entity(vc.Result)).
   170  		Logger()
   171  
   172  	log.Info().Msg("verifiable chunk received by verifier engine")
   173  
   174  	// only accept internal calls
   175  	if originID != e.me.NodeID() {
   176  		return fmt.Errorf("invalid remote origin for verify")
   177  	}
   178  
   179  	var err error
   180  
   181  	// extracts chunk ID
   182  	ch, ok := vc.Result.Chunks.ByIndex(vc.Chunk.Index)
   183  	if !ok {
   184  		return engine.NewInvalidInputErrorf("chunk out of range requested: %v", vc.Chunk.Index)
   185  	}
   186  	log.With().Hex("chunk_id", logging.Entity(ch)).Logger()
   187  
   188  	// execute the assigned chunk
   189  	span, _ := e.tracer.StartSpanFromContext(ctx, trace.VERVerChunkVerify)
   190  
   191  	spockSecret, err := e.chVerif.Verify(vc)
   192  	span.End()
   193  
   194  	if err != nil {
   195  		// any error besides a ChunkFaultError is a system error
   196  		if !chmodels.IsChunkFaultError(err) {
   197  			return fmt.Errorf("cannot verify chunk: %w", err)
   198  		}
   199  
   200  		// if any fault found with the chunk
   201  		switch chFault := err.(type) {
   202  		case *chmodels.CFMissingRegisterTouch:
   203  			e.log.Warn().
   204  				Str("chunk_fault_type", "missing_register_touch").
   205  				Str("chunk_fault", chFault.Error()).
   206  				Msg("chunk fault found, could not verify chunk")
   207  			// still create approvals for this case
   208  		case *chmodels.CFNonMatchingFinalState:
   209  			// TODO raise challenge
   210  			e.log.Warn().
   211  				Str("chunk_fault_type", "final_state_mismatch").
   212  				Str("chunk_fault", chFault.Error()).
   213  				Msg("chunk fault found, could not verify chunk")
   214  			return nil
   215  		case *chmodels.CFInvalidVerifiableChunk:
   216  			// TODO raise challenge
   217  			e.log.Error().
   218  				Str("chunk_fault_type", "invalid_verifiable_chunk").
   219  				Str("chunk_fault", chFault.Error()).
   220  				Msg("chunk fault found, could not verify chunk")
   221  			return nil
   222  		case *chmodels.CFInvalidEventsCollection:
   223  			// TODO raise challenge
   224  			e.log.Error().
   225  				Str("chunk_fault_type", "invalid_event_collection").
   226  				Str("chunk_fault", chFault.Error()).
   227  				Msg("chunk fault found, could not verify chunk")
   228  			return nil
   229  		case *chmodels.CFSystemChunkIncludedCollection:
   230  			e.log.Error().
   231  				Str("chunk_fault_type", "system_chunk_includes_collection").
   232  				Str("chunk_fault", chFault.Error()).
   233  				Msg("chunk fault found, could not verify chunk")
   234  			return nil
   235  		case *chmodels.CFExecutionDataBlockIDMismatch:
   236  			e.log.Error().
   237  				Str("chunk_fault_type", "execution_data_block_id_mismatch").
   238  				Str("chunk_fault", chFault.Error()).
   239  				Msg("chunk fault found, could not verify chunk")
   240  			return nil
   241  		case *chmodels.CFExecutionDataChunksLengthMismatch:
   242  			e.log.Error().
   243  				Str("chunk_fault_type", "execution_data_chunks_count_mismatch").
   244  				Str("chunk_fault", chFault.Error()).
   245  				Msg("chunk fault found, could not verify chunk")
   246  			return nil
   247  		case *chmodels.CFExecutionDataInvalidChunkCID:
   248  			e.log.Error().
   249  				Str("chunk_fault_type", "execution_data_chunk_cid_mismatch").
   250  				Str("chunk_fault", chFault.Error()).
   251  				Msg("chunk fault found, could not verify chunk")
   252  			return nil
   253  		case *chmodels.CFInvalidExecutionDataID:
   254  			e.log.Error().
   255  				Str("chunk_fault_type", "execution_data_root_cid_mismatch").
   256  				Str("chunk_fault", chFault.Error()).
   257  				Msg("chunk fault found, could not verify chunk")
   258  			return nil
   259  		default:
   260  			return engine.NewInvalidInputErrorf("unknown type of chunk fault is received (type: %T) : %v",
   261  				chFault, chFault.Error())
   262  		}
   263  	}
   264  
   265  	// Generate result approval
   266  	span, _ = e.tracer.StartSpanFromContext(ctx, trace.VERVerGenerateResultApproval)
   267  	attestation := &flow.Attestation{
   268  		BlockID:           vc.Header.ID(),
   269  		ExecutionResultID: vc.Result.ID(),
   270  		ChunkIndex:        vc.Chunk.Index,
   271  	}
   272  	approval, err := GenerateResultApproval(
   273  		e.me,
   274  		e.approvalHasher,
   275  		e.spockHasher,
   276  		attestation,
   277  		spockSecret)
   278  
   279  	span.End()
   280  	if err != nil {
   281  		return fmt.Errorf("couldn't generate a result approval: %w", err)
   282  	}
   283  
   284  	err = e.approvals.Store(approval)
   285  	if err != nil {
   286  		return fmt.Errorf("could not store approval: %w", err)
   287  	}
   288  
   289  	err = e.approvals.Index(approval.Body.ExecutionResultID, approval.Body.ChunkIndex, approval.ID())
   290  	if err != nil {
   291  		return fmt.Errorf("could not index approval: %w", err)
   292  	}
   293  
   294  	// Extracting consensus node ids
   295  	// TODO state extraction should be done based on block references
   296  	consensusNodes, err := e.state.Final().
   297  		Identities(filter.HasRole[flow.Identity](flow.RoleConsensus))
   298  	if err != nil {
   299  		// TODO this error needs more advance handling after MVP
   300  		return fmt.Errorf("could not load consensus node IDs: %w", err)
   301  	}
   302  
   303  	// broadcast result approval to the consensus nodes
   304  	err = e.pushConduit.Publish(approval, consensusNodes.NodeIDs()...)
   305  	if err != nil {
   306  		// TODO this error needs more advance handling after MVP
   307  		return fmt.Errorf("could not submit result approval: %w", err)
   308  	}
   309  	log.Info().Msg("result approval submitted")
   310  	// increases number of sent result approvals for sake of metrics
   311  	e.metrics.OnResultApprovalDispatchedInNetworkByVerifier()
   312  
   313  	return nil
   314  }
   315  
   316  // GenerateResultApproval generates result approval for specific chunk of an execution receipt.
   317  func GenerateResultApproval(
   318  	me module.Local,
   319  	approvalHasher hash.Hasher,
   320  	spockHasher hash.Hasher,
   321  	attestation *flow.Attestation,
   322  	spockSecret []byte,
   323  ) (*flow.ResultApproval, error) {
   324  
   325  	// generates a signature over the attestation part of approval
   326  	atstID := attestation.ID()
   327  	atstSign, err := me.Sign(atstID[:], approvalHasher)
   328  	if err != nil {
   329  		return nil, fmt.Errorf("could not sign attestation: %w", err)
   330  	}
   331  
   332  	// generates spock
   333  	spock, err := me.SignFunc(spockSecret, spockHasher, crypto.SPOCKProve)
   334  	if err != nil {
   335  		return nil, fmt.Errorf("could not generate SPoCK: %w", err)
   336  	}
   337  
   338  	// result approval body
   339  	body := flow.ResultApprovalBody{
   340  		Attestation:          *attestation,
   341  		ApproverID:           me.NodeID(),
   342  		AttestationSignature: atstSign,
   343  		Spock:                spock,
   344  	}
   345  
   346  	// generates a signature over result approval body
   347  	bodyID := body.ID()
   348  	bodySign, err := me.Sign(bodyID[:], approvalHasher)
   349  	if err != nil {
   350  		return nil, fmt.Errorf("could not sign result approval body: %w", err)
   351  	}
   352  
   353  	return &flow.ResultApproval{
   354  		Body:              body,
   355  		VerifierSignature: bodySign,
   356  	}, nil
   357  }
   358  
   359  // verifiableChunkHandler acts as a wrapper around the verify method that captures its performance-related metrics
   360  func (e *Engine) verifiableChunkHandler(originID flow.Identifier, ch *verification.VerifiableChunkData) error {
   361  
   362  	span, ctx := e.tracer.StartBlockSpan(context.Background(), ch.Chunk.BlockID, trace.VERVerVerifyWithMetrics)
   363  	span.SetAttributes(
   364  		attribute.Int64("chunk_index", int64(ch.Chunk.Index)),
   365  		attribute.String("result_id", ch.Result.ID().String()),
   366  		attribute.String("origin_id", originID.String()),
   367  	)
   368  	defer span.End()
   369  
   370  	// increments number of received verifiable chunks
   371  	// for sake of metrics
   372  	e.metrics.OnVerifiableChunkReceivedAtVerifierEngine()
   373  
   374  	log := e.log.With().
   375  		Hex("result_id", logging.ID(ch.Result.ID())).
   376  		Hex("chunk_id", logging.ID(ch.Chunk.ID())).
   377  		Uint64("chunk_index", ch.Chunk.Index).Logger()
   378  
   379  	log.Info().Msg("verifiable chunk received")
   380  
   381  	// starts verification of chunk
   382  	err := e.verify(ctx, originID, ch)
   383  
   384  	if err != nil {
   385  		log.Info().Err(err).Msg("could not verify chunk")
   386  	}
   387  
   388  	// closes verification performance metrics trackers
   389  	return nil
   390  }
   391  
   392  func (e *Engine) approvalRequestHandler(originID flow.Identifier, req *messages.ApprovalRequest) error {
   393  
   394  	log := e.log.With().
   395  		Hex("origin_id", logging.ID(originID)).
   396  		Hex("result_id", logging.ID(req.ResultID)).
   397  		Uint64("chunk_index", req.ChunkIndex).
   398  		Logger()
   399  
   400  	origin, err := e.state.Final().Identity(originID)
   401  	if err != nil {
   402  		return engine.NewInvalidInputErrorf("invalid origin id (%s): %w", originID, err)
   403  	}
   404  
   405  	if origin.Role != flow.RoleConsensus {
   406  		return engine.NewInvalidInputErrorf("invalid role for requesting approvals: %s", origin.Role)
   407  	}
   408  
   409  	approval, err := e.approvals.ByChunk(req.ResultID, req.ChunkIndex)
   410  	if err != nil {
   411  		return fmt.Errorf("could not retrieve approval for chunk (result: %s, chunk index: %d): %w",
   412  			req.ResultID,
   413  			req.ChunkIndex,
   414  			err)
   415  	}
   416  
   417  	response := &messages.ApprovalResponse{
   418  		Nonce:    req.Nonce,
   419  		Approval: *approval,
   420  	}
   421  
   422  	err = e.pullConduit.Unicast(response, originID)
   423  	if err != nil {
   424  		return fmt.Errorf("could not send requested approval to %s: %w",
   425  			originID,
   426  			err)
   427  	}
   428  
   429  	log.Debug().Msg("succesfully replied to approval request")
   430  
   431  	return nil
   432  }