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 }