mirror of
https://github.com/luxfi/node.git
synced 2026-07-27 03:39:39 +00:00
129 lines
3.4 KiB
Go
129 lines
3.4 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package consensus
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/luxfi/ids"
|
|
"github.com/luxfi/log"
|
|
"github.com/luxfi/runtime"
|
|
)
|
|
|
|
var (
|
|
_ Acceptor = acceptorWrapper{}
|
|
|
|
_ AcceptorGroup = (*acceptorGroup)(nil)
|
|
)
|
|
|
|
// Acceptor is implemented when a struct is monitoring if a message is accepted
|
|
type Acceptor interface {
|
|
// Accept must be called before [containerID] is committed to the VM as
|
|
// accepted.
|
|
//
|
|
// If the returned error is non-nil, the chain associated with [ctx] should
|
|
// shut down and not commit [container] or any other container to its
|
|
// database as accepted.
|
|
Accept(rt *runtime.Runtime, containerID ids.ID, container []byte) error
|
|
}
|
|
|
|
type acceptorWrapper struct {
|
|
Acceptor
|
|
|
|
// If true and Accept returns an error, the chain this callback corresponds
|
|
// to will stop.
|
|
dieOnError bool
|
|
}
|
|
|
|
type AcceptorGroup interface {
|
|
// Calling Accept() calls all of the registered acceptors for the relevant
|
|
// chain.
|
|
Acceptor
|
|
|
|
// RegisterAcceptor causes [acceptor] to be called every time an operation
|
|
// is accepted on chain [chainID].
|
|
// If [dieOnError], chain [chainID] stops if Accept returns a non-nil error.
|
|
RegisterAcceptor(chainID ids.ID, acceptorName string, acceptor Acceptor, dieOnError bool) error
|
|
|
|
// DeregisterAcceptor removes an acceptor from the group.
|
|
DeregisterAcceptor(chainID ids.ID, acceptorName string) error
|
|
}
|
|
|
|
type acceptorGroup struct {
|
|
log log.Logger
|
|
|
|
lock sync.RWMutex
|
|
// Chain ID --> Acceptor Name --> Acceptor
|
|
acceptors map[ids.ID]map[string]acceptorWrapper
|
|
}
|
|
|
|
func NewAcceptorGroup(log log.Logger) AcceptorGroup {
|
|
return &acceptorGroup{
|
|
log: log,
|
|
acceptors: make(map[ids.ID]map[string]acceptorWrapper),
|
|
}
|
|
}
|
|
|
|
func (a *acceptorGroup) Accept(rt *runtime.Runtime, containerID ids.ID, container []byte) error {
|
|
a.lock.RLock()
|
|
defer a.lock.RUnlock()
|
|
|
|
for acceptorName, acceptor := range a.acceptors[rt.ChainID] {
|
|
if err := acceptor.Accept(rt, containerID, container); err != nil {
|
|
a.log.Error("failed accepting container",
|
|
zap.String("acceptorName", acceptorName),
|
|
zap.Stringer("chainID", rt.ChainID),
|
|
zap.Stringer("containerID", containerID),
|
|
zap.Error(err),
|
|
)
|
|
if acceptor.dieOnError {
|
|
return fmt.Errorf("acceptor %s on chain %s erred while accepting %s: %w", acceptorName, rt.ChainID, containerID, err)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (a *acceptorGroup) RegisterAcceptor(chainID ids.ID, acceptorName string, acceptor Acceptor, dieOnError bool) error {
|
|
a.lock.Lock()
|
|
defer a.lock.Unlock()
|
|
|
|
acceptors, exist := a.acceptors[chainID]
|
|
if !exist {
|
|
acceptors = make(map[string]acceptorWrapper)
|
|
a.acceptors[chainID] = acceptors
|
|
}
|
|
|
|
if _, ok := acceptors[acceptorName]; ok {
|
|
return fmt.Errorf("callback %s already exists on chain %s", acceptorName, chainID)
|
|
}
|
|
|
|
acceptors[acceptorName] = acceptorWrapper{
|
|
Acceptor: acceptor,
|
|
dieOnError: dieOnError,
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (a *acceptorGroup) DeregisterAcceptor(chainID ids.ID, acceptorName string) error {
|
|
a.lock.Lock()
|
|
defer a.lock.Unlock()
|
|
|
|
acceptors, exist := a.acceptors[chainID]
|
|
if !exist {
|
|
return fmt.Errorf("chain %s has no callbacks", chainID)
|
|
}
|
|
if _, ok := acceptors[acceptorName]; !ok {
|
|
return fmt.Errorf("callback %s does not exist on chain %s", acceptorName, chainID)
|
|
}
|
|
delete(acceptors, acceptorName)
|
|
if len(acceptors) == 0 {
|
|
delete(a.acceptors, chainID)
|
|
}
|
|
return nil
|
|
}
|