mirror of https://github.com/poanetwork/gecko.git
395 lines
12 KiB
Go
395 lines
12 KiB
Go
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package router
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
|
|
"github.com/ava-labs/gecko/ids"
|
|
"github.com/ava-labs/gecko/snow"
|
|
"github.com/ava-labs/gecko/snow/engine/common"
|
|
)
|
|
|
|
// Handler passes incoming messages from the network to the consensus engine
|
|
// (Actually, it receives the incoming messages from a ChainRouter, but same difference)
|
|
type Handler struct {
|
|
metrics
|
|
|
|
msgs chan message
|
|
reliableMsgsSema chan struct{}
|
|
reliableMsgsLock sync.Mutex
|
|
reliableMsgs []message
|
|
closed chan struct{}
|
|
msgChan <-chan common.Message
|
|
|
|
ctx *snow.Context
|
|
engine common.Engine
|
|
|
|
toClose func()
|
|
closing bool
|
|
}
|
|
|
|
// Initialize this consensus handler
|
|
func (h *Handler) Initialize(
|
|
engine common.Engine,
|
|
msgChan <-chan common.Message,
|
|
bufferSize int,
|
|
namespace string,
|
|
metrics prometheus.Registerer,
|
|
) {
|
|
h.metrics.Initialize(namespace, metrics)
|
|
h.msgs = make(chan message, bufferSize)
|
|
h.reliableMsgsSema = make(chan struct{}, 1)
|
|
h.closed = make(chan struct{})
|
|
h.msgChan = msgChan
|
|
|
|
h.ctx = engine.Context()
|
|
h.engine = engine
|
|
}
|
|
|
|
// Context of this Handler
|
|
func (h *Handler) Context() *snow.Context { return h.engine.Context() }
|
|
|
|
// Engine returns the engine this handler dispatches to
|
|
func (h *Handler) Engine() common.Engine { return h.engine }
|
|
|
|
// SetEngine sets the engine this handler dispatches to
|
|
func (h *Handler) SetEngine(engine common.Engine) { h.engine = engine }
|
|
|
|
// Dispatch waits for incoming messages from the network
|
|
// and, when they arrive, sends them to the consensus engine
|
|
func (h *Handler) Dispatch() {
|
|
defer func() {
|
|
h.ctx.Log.Info("finished shutting down chain")
|
|
close(h.closed)
|
|
}()
|
|
|
|
for {
|
|
select {
|
|
case msg, ok := <-h.msgs:
|
|
if !ok {
|
|
// the msgs channel has been closed, so this dispatcher should exit
|
|
return
|
|
}
|
|
|
|
h.metrics.pending.Dec()
|
|
h.dispatchMsg(msg)
|
|
case <-h.reliableMsgsSema:
|
|
// get all the reliable messages
|
|
h.reliableMsgsLock.Lock()
|
|
msgs := h.reliableMsgs
|
|
h.reliableMsgs = nil
|
|
h.reliableMsgsLock.Unlock()
|
|
|
|
// fire all the reliable messages
|
|
for _, msg := range msgs {
|
|
h.metrics.pending.Dec()
|
|
h.dispatchMsg(msg)
|
|
}
|
|
case msg := <-h.msgChan:
|
|
// handle a message from the VM
|
|
h.dispatchMsg(message{messageType: notifyMsg, notification: msg})
|
|
}
|
|
if h.closing && h.toClose != nil {
|
|
go h.toClose()
|
|
}
|
|
}
|
|
}
|
|
|
|
// Dispatch a message to the consensus engine.
|
|
// Returns true iff this consensus handler (and its associated engine) should shutdown
|
|
// (due to receipt of a shutdown message)
|
|
func (h *Handler) dispatchMsg(msg message) {
|
|
if h.closing {
|
|
h.ctx.Log.Debug("dropping message due to closing:\n%s", msg)
|
|
h.metrics.dropped.Inc()
|
|
return
|
|
}
|
|
|
|
startTime := time.Now()
|
|
|
|
h.ctx.Lock.Lock()
|
|
defer h.ctx.Lock.Unlock()
|
|
|
|
h.ctx.Log.Verbo("Forwarding message to consensus: %s", msg)
|
|
var (
|
|
err error
|
|
done bool
|
|
)
|
|
switch msg.messageType {
|
|
case getAcceptedFrontierMsg:
|
|
err = h.engine.GetAcceptedFrontier(msg.validatorID, msg.requestID)
|
|
h.getAcceptedFrontier.Observe(float64(time.Now().Sub(startTime)))
|
|
case acceptedFrontierMsg:
|
|
err = h.engine.AcceptedFrontier(msg.validatorID, msg.requestID, msg.containerIDs)
|
|
h.acceptedFrontier.Observe(float64(time.Now().Sub(startTime)))
|
|
case getAcceptedFrontierFailedMsg:
|
|
err = h.engine.GetAcceptedFrontierFailed(msg.validatorID, msg.requestID)
|
|
h.getAcceptedFrontierFailed.Observe(float64(time.Now().Sub(startTime)))
|
|
case getAcceptedMsg:
|
|
err = h.engine.GetAccepted(msg.validatorID, msg.requestID, msg.containerIDs)
|
|
h.getAccepted.Observe(float64(time.Now().Sub(startTime)))
|
|
case acceptedMsg:
|
|
err = h.engine.Accepted(msg.validatorID, msg.requestID, msg.containerIDs)
|
|
h.accepted.Observe(float64(time.Now().Sub(startTime)))
|
|
case getAcceptedFailedMsg:
|
|
err = h.engine.GetAcceptedFailed(msg.validatorID, msg.requestID)
|
|
h.getAcceptedFailed.Observe(float64(time.Now().Sub(startTime)))
|
|
case getAncestorsMsg:
|
|
err = h.engine.GetAncestors(msg.validatorID, msg.requestID, msg.containerID)
|
|
h.getAncestors.Observe(float64(time.Now().Sub(startTime)))
|
|
case getAncestorsFailedMsg:
|
|
err = h.engine.GetAncestorsFailed(msg.validatorID, msg.requestID)
|
|
h.getAncestorsFailed.Observe(float64(time.Now().Sub(startTime)))
|
|
case multiPutMsg:
|
|
err = h.engine.MultiPut(msg.validatorID, msg.requestID, msg.containers)
|
|
h.multiPut.Observe(float64(time.Now().Sub(startTime)))
|
|
case getMsg:
|
|
err = h.engine.Get(msg.validatorID, msg.requestID, msg.containerID)
|
|
h.get.Observe(float64(time.Now().Sub(startTime)))
|
|
case getFailedMsg:
|
|
err = h.engine.GetFailed(msg.validatorID, msg.requestID)
|
|
h.getFailed.Observe(float64(time.Now().Sub(startTime)))
|
|
case putMsg:
|
|
err = h.engine.Put(msg.validatorID, msg.requestID, msg.containerID, msg.container)
|
|
h.put.Observe(float64(time.Now().Sub(startTime)))
|
|
case pushQueryMsg:
|
|
err = h.engine.PushQuery(msg.validatorID, msg.requestID, msg.containerID, msg.container)
|
|
h.pushQuery.Observe(float64(time.Now().Sub(startTime)))
|
|
case pullQueryMsg:
|
|
err = h.engine.PullQuery(msg.validatorID, msg.requestID, msg.containerID)
|
|
h.pullQuery.Observe(float64(time.Now().Sub(startTime)))
|
|
case queryFailedMsg:
|
|
err = h.engine.QueryFailed(msg.validatorID, msg.requestID)
|
|
h.queryFailed.Observe(float64(time.Now().Sub(startTime)))
|
|
case chitsMsg:
|
|
err = h.engine.Chits(msg.validatorID, msg.requestID, msg.containerIDs)
|
|
h.chits.Observe(float64(time.Now().Sub(startTime)))
|
|
case notifyMsg:
|
|
err = h.engine.Notify(msg.notification)
|
|
h.notify.Observe(float64(time.Now().Sub(startTime)))
|
|
case gossipMsg:
|
|
err = h.engine.Gossip()
|
|
h.gossip.Observe(float64(time.Now().Sub(startTime)))
|
|
case shutdownMsg:
|
|
err = h.engine.Shutdown()
|
|
h.shutdown.Observe(float64(time.Now().Sub(startTime)))
|
|
done = true
|
|
}
|
|
|
|
if err != nil {
|
|
h.ctx.Log.Fatal("forcing chain to shutdown due to %s", err)
|
|
}
|
|
|
|
h.closing = done || err != nil
|
|
}
|
|
|
|
// GetAcceptedFrontier passes a GetAcceptedFrontier message received from the
|
|
// network to the consensus engine.
|
|
func (h *Handler) GetAcceptedFrontier(validatorID ids.ShortID, requestID uint32) bool {
|
|
return h.sendMsg(message{
|
|
messageType: getAcceptedFrontierMsg,
|
|
validatorID: validatorID,
|
|
requestID: requestID,
|
|
})
|
|
}
|
|
|
|
// AcceptedFrontier passes a AcceptedFrontier message received from the network
|
|
// to the consensus engine.
|
|
func (h *Handler) AcceptedFrontier(validatorID ids.ShortID, requestID uint32, containerIDs ids.Set) bool {
|
|
return h.sendMsg(message{
|
|
messageType: acceptedFrontierMsg,
|
|
validatorID: validatorID,
|
|
requestID: requestID,
|
|
containerIDs: containerIDs,
|
|
})
|
|
}
|
|
|
|
// GetAcceptedFrontierFailed passes a GetAcceptedFrontierFailed message received
|
|
// from the network to the consensus engine.
|
|
func (h *Handler) GetAcceptedFrontierFailed(validatorID ids.ShortID, requestID uint32) {
|
|
h.sendReliableMsg(message{
|
|
messageType: getAcceptedFrontierFailedMsg,
|
|
validatorID: validatorID,
|
|
requestID: requestID,
|
|
})
|
|
}
|
|
|
|
// GetAccepted passes a GetAccepted message received from the
|
|
// network to the consensus engine.
|
|
func (h *Handler) GetAccepted(validatorID ids.ShortID, requestID uint32, containerIDs ids.Set) bool {
|
|
return h.sendMsg(message{
|
|
messageType: getAcceptedMsg,
|
|
validatorID: validatorID,
|
|
requestID: requestID,
|
|
containerIDs: containerIDs,
|
|
})
|
|
}
|
|
|
|
// Accepted passes a Accepted message received from the network to the consensus
|
|
// engine.
|
|
func (h *Handler) Accepted(validatorID ids.ShortID, requestID uint32, containerIDs ids.Set) bool {
|
|
return h.sendMsg(message{
|
|
messageType: acceptedMsg,
|
|
validatorID: validatorID,
|
|
requestID: requestID,
|
|
containerIDs: containerIDs,
|
|
})
|
|
}
|
|
|
|
// GetAcceptedFailed passes a GetAcceptedFailed message received from the
|
|
// network to the consensus engine.
|
|
func (h *Handler) GetAcceptedFailed(validatorID ids.ShortID, requestID uint32) {
|
|
h.sendReliableMsg(message{
|
|
messageType: getAcceptedFailedMsg,
|
|
validatorID: validatorID,
|
|
requestID: requestID,
|
|
})
|
|
}
|
|
|
|
// GetAncestors passes a GetAncestors message received from the network to the consensus engine.
|
|
func (h *Handler) GetAncestors(validatorID ids.ShortID, requestID uint32, containerID ids.ID) bool {
|
|
return h.sendMsg(message{
|
|
messageType: getAncestorsMsg,
|
|
validatorID: validatorID,
|
|
requestID: requestID,
|
|
containerID: containerID,
|
|
})
|
|
}
|
|
|
|
// MultiPut passes a MultiPut message received from the network to the consensus engine.
|
|
func (h *Handler) MultiPut(validatorID ids.ShortID, requestID uint32, containers [][]byte) bool {
|
|
return h.sendMsg(message{
|
|
messageType: multiPutMsg,
|
|
validatorID: validatorID,
|
|
requestID: requestID,
|
|
containers: containers,
|
|
})
|
|
}
|
|
|
|
// GetAncestorsFailed passes a GetAncestorsFailed message to the consensus engine.
|
|
func (h *Handler) GetAncestorsFailed(validatorID ids.ShortID, requestID uint32) {
|
|
h.sendReliableMsg(message{
|
|
messageType: getAncestorsFailedMsg,
|
|
validatorID: validatorID,
|
|
requestID: requestID,
|
|
})
|
|
}
|
|
|
|
// Get passes a Get message received from the network to the consensus engine.
|
|
func (h *Handler) Get(validatorID ids.ShortID, requestID uint32, containerID ids.ID) bool {
|
|
return h.sendMsg(message{
|
|
messageType: getMsg,
|
|
validatorID: validatorID,
|
|
requestID: requestID,
|
|
containerID: containerID,
|
|
})
|
|
}
|
|
|
|
// Put passes a Put message received from the network to the consensus engine.
|
|
func (h *Handler) Put(validatorID ids.ShortID, requestID uint32, containerID ids.ID, container []byte) bool {
|
|
return h.sendMsg(message{
|
|
messageType: putMsg,
|
|
validatorID: validatorID,
|
|
requestID: requestID,
|
|
containerID: containerID,
|
|
container: container,
|
|
})
|
|
}
|
|
|
|
// GetFailed passes a GetFailed message to the consensus engine.
|
|
func (h *Handler) GetFailed(validatorID ids.ShortID, requestID uint32) {
|
|
h.sendReliableMsg(message{
|
|
messageType: getFailedMsg,
|
|
validatorID: validatorID,
|
|
requestID: requestID,
|
|
})
|
|
}
|
|
|
|
// PushQuery passes a PushQuery message received from the network to the consensus engine.
|
|
func (h *Handler) PushQuery(validatorID ids.ShortID, requestID uint32, blockID ids.ID, block []byte) bool {
|
|
return h.sendMsg(message{
|
|
messageType: pushQueryMsg,
|
|
validatorID: validatorID,
|
|
requestID: requestID,
|
|
containerID: blockID,
|
|
container: block,
|
|
})
|
|
}
|
|
|
|
// PullQuery passes a PullQuery message received from the network to the consensus engine.
|
|
func (h *Handler) PullQuery(validatorID ids.ShortID, requestID uint32, blockID ids.ID) bool {
|
|
return h.sendMsg(message{
|
|
messageType: pullQueryMsg,
|
|
validatorID: validatorID,
|
|
requestID: requestID,
|
|
containerID: blockID,
|
|
})
|
|
}
|
|
|
|
// Chits passes a Chits message received from the network to the consensus engine.
|
|
func (h *Handler) Chits(validatorID ids.ShortID, requestID uint32, votes ids.Set) bool {
|
|
return h.sendMsg(message{
|
|
messageType: chitsMsg,
|
|
validatorID: validatorID,
|
|
requestID: requestID,
|
|
containerIDs: votes,
|
|
})
|
|
}
|
|
|
|
// QueryFailed passes a QueryFailed message received from the network to the consensus engine.
|
|
func (h *Handler) QueryFailed(validatorID ids.ShortID, requestID uint32) {
|
|
h.sendReliableMsg(message{
|
|
messageType: queryFailedMsg,
|
|
validatorID: validatorID,
|
|
requestID: requestID,
|
|
})
|
|
}
|
|
|
|
// Gossip passes a gossip request to the consensus engine
|
|
func (h *Handler) Gossip() bool {
|
|
return h.sendMsg(message{messageType: gossipMsg})
|
|
}
|
|
|
|
// Notify ...
|
|
func (h *Handler) Notify(msg common.Message) bool {
|
|
return h.sendMsg(message{
|
|
messageType: notifyMsg,
|
|
notification: msg,
|
|
})
|
|
}
|
|
|
|
// Shutdown shuts down the dispatcher
|
|
func (h *Handler) Shutdown() {
|
|
h.sendReliableMsg(message{
|
|
messageType: shutdownMsg,
|
|
})
|
|
}
|
|
|
|
func (h *Handler) sendMsg(msg message) bool {
|
|
select {
|
|
case h.msgs <- msg:
|
|
h.metrics.pending.Inc()
|
|
return true
|
|
default:
|
|
h.metrics.dropped.Inc()
|
|
return false
|
|
}
|
|
}
|
|
|
|
func (h *Handler) sendReliableMsg(msg message) {
|
|
h.reliableMsgsLock.Lock()
|
|
defer h.reliableMsgsLock.Unlock()
|
|
|
|
h.metrics.pending.Inc()
|
|
h.reliableMsgs = append(h.reliableMsgs, msg)
|
|
select {
|
|
case h.reliableMsgsSema <- struct{}{}:
|
|
default:
|
|
}
|
|
}
|