github.com/koko1123/flow-go-1@v0.29.6/engine/consensus/provider/engine.go (about)

     1  // (c) 2019 Dapper Labs - ALL RIGHTS RESERVED
     2  
     3  package provider
     4  
     5  import (
     6  	"fmt"
     7  
     8  	"github.com/rs/zerolog"
     9  
    10  	"github.com/koko1123/flow-go-1/engine"
    11  	"github.com/koko1123/flow-go-1/model/flow"
    12  	"github.com/koko1123/flow-go-1/model/flow/filter"
    13  	"github.com/koko1123/flow-go-1/model/messages"
    14  	"github.com/koko1123/flow-go-1/module"
    15  	"github.com/koko1123/flow-go-1/module/metrics"
    16  	"github.com/koko1123/flow-go-1/network"
    17  	"github.com/koko1123/flow-go-1/network/channels"
    18  	"github.com/koko1123/flow-go-1/state/protocol"
    19  	"github.com/koko1123/flow-go-1/utils/logging"
    20  )
    21  
    22  // Engine represents the provider engine, used to spread block proposals across
    23  // the flow system, to non-consensus nodes. It makes sense to use a separate
    24  // engine to isolate the consensus algorithm from other processes, which allows
    25  // to create a different underlying protocol for consensus nodes, which have a
    26  // higher priority to receive block proposals, and other nodes
    27  type Engine struct {
    28  	unit    *engine.Unit         // used for concurrency & shutdown
    29  	log     zerolog.Logger       // used to log relevant actions with context
    30  	message module.EngineMetrics // used to track sent & received messages
    31  	tracer  module.Tracer
    32  	con     network.Conduit // used to talk to other nodes on the network
    33  	state   protocol.State  // used to access the  protocol state
    34  	me      module.Local    // used to access local node information
    35  }
    36  
    37  // New creates a new block provider engine.
    38  func New(
    39  	log zerolog.Logger,
    40  	message module.EngineMetrics,
    41  	tracer module.Tracer,
    42  	net network.Network,
    43  	state protocol.State,
    44  	me module.Local,
    45  ) (*Engine, error) {
    46  
    47  	// initialize the propagation engine with its dependencies
    48  	e := &Engine{
    49  		unit:    engine.NewUnit(),
    50  		log:     log.With().Str("engine", "provider").Logger(),
    51  		message: message,
    52  		tracer:  tracer,
    53  		state:   state,
    54  		me:      me,
    55  	}
    56  
    57  	// register the engine with the network layer and store the conduit
    58  	con, err := net.Register(channels.PushBlocks, e)
    59  	if err != nil {
    60  		return nil, fmt.Errorf("could not register engine: %w", err)
    61  	}
    62  
    63  	e.con = con
    64  
    65  	return e, nil
    66  }
    67  
    68  // Ready returns a ready channel that is closed once the engine has fully
    69  // started. For the provider engine, we consider the engine up and running
    70  // upon initialization.
    71  func (e *Engine) Ready() <-chan struct{} {
    72  	return e.unit.Ready()
    73  }
    74  
    75  // Done returns a done channel that is closed once the engine has fully stopped.
    76  // For the ingestion engine, it only waits for all submit goroutines to end.
    77  func (e *Engine) Done() <-chan struct{} {
    78  	return e.unit.Done()
    79  }
    80  
    81  // SubmitLocal submits an event originating on the local node.
    82  func (e *Engine) SubmitLocal(event interface{}) {
    83  	e.unit.Launch(func() {
    84  		err := e.ProcessLocal(event)
    85  		if err != nil {
    86  			engine.LogError(e.log, err)
    87  		}
    88  	})
    89  }
    90  
    91  // Submit submits the given event from the node with the given origin ID
    92  // for processing in a non-blocking manner. It returns instantly and logs
    93  // a potential processing error internally when done.
    94  func (e *Engine) Submit(channel channels.Channel, originID flow.Identifier, event interface{}) {
    95  	e.unit.Launch(func() {
    96  		err := e.Process(channel, originID, event)
    97  		if err != nil {
    98  			engine.LogError(e.log, err)
    99  		}
   100  	})
   101  }
   102  
   103  // ProcessLocal processes an event originating on the local node.
   104  func (e *Engine) ProcessLocal(event interface{}) error {
   105  	return e.unit.Do(func() error {
   106  		return e.process(e.me.NodeID(), event)
   107  	})
   108  }
   109  
   110  // Process processes the given event from the node with the given origin ID in
   111  // a blocking manner. It returns the potential processing error when done.
   112  func (e *Engine) Process(channel channels.Channel, originID flow.Identifier, event interface{}) error {
   113  	return e.unit.Do(func() error {
   114  		return e.process(originID, event)
   115  	})
   116  }
   117  
   118  // process processes the given ingestion engine event. Events that are given
   119  // to this function originate within the provider engine on the node with the
   120  // given origin ID.
   121  func (e *Engine) process(originID flow.Identifier, event interface{}) error {
   122  	switch ev := event.(type) {
   123  	case *messages.BlockProposal:
   124  		return e.onBlockProposal(originID, ev)
   125  	default:
   126  		return fmt.Errorf("invalid event type (%T)", event)
   127  	}
   128  }
   129  
   130  // onBlockProposal is used when we want to broadcast a local block to the network.
   131  func (e *Engine) onBlockProposal(originID flow.Identifier, proposal *messages.BlockProposal) error {
   132  	block := proposal.Block.ToInternal()
   133  	log := e.log.With().
   134  		Hex("origin_id", originID[:]).
   135  		Uint64("block_view", block.Header.View).
   136  		Hex("block_id", logging.Entity(block.Header)).
   137  		Hex("parent_id", block.Header.ParentID[:]).
   138  		Hex("signer", block.Header.ProposerID[:]).
   139  		Logger()
   140  
   141  	log.Info().Msg("block proposal submitted for propagation")
   142  
   143  	// currently, only accept blocks that come from our local consensus
   144  	localID := e.me.NodeID()
   145  	if originID != localID {
   146  		return engine.NewInvalidInputErrorf("non-local block (nodeID: %x)", originID)
   147  	}
   148  
   149  	// determine the nodes we should send the block to
   150  	recipients, err := e.state.Final().Identities(filter.And(
   151  		filter.Not(filter.Ejected),
   152  		filter.Not(filter.HasRole(flow.RoleConsensus)),
   153  	))
   154  	if err != nil {
   155  		return fmt.Errorf("could not get recipients: %w", err)
   156  	}
   157  
   158  	// submit the block to the targets
   159  	err = e.con.Publish(proposal, recipients.NodeIDs()...)
   160  	if err != nil {
   161  		return fmt.Errorf("could not broadcast block: %w", err)
   162  	}
   163  
   164  	e.message.MessageSent(metrics.EngineConsensusProvider, metrics.MessageBlockProposal)
   165  
   166  	log.Info().Msg("block proposal propagated to non-consensus nodes")
   167  
   168  	return nil
   169  }