code.vegaprotocol.io/vega@v0.79.0/core/evtforward/forwarder.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package evtforward
    17  
    18  import (
    19  	"context"
    20  	"encoding/hex"
    21  	"errors"
    22  	"fmt"
    23  	"hash/fnv"
    24  	"sort"
    25  	"sync"
    26  	"sync/atomic"
    27  	"time"
    28  
    29  	"code.vegaprotocol.io/vega/core/metrics"
    30  	"code.vegaprotocol.io/vega/core/txn"
    31  	"code.vegaprotocol.io/vega/libs/crypto"
    32  	vgproto "code.vegaprotocol.io/vega/libs/proto"
    33  	"code.vegaprotocol.io/vega/logging"
    34  	commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1"
    35  
    36  	"github.com/cenkalti/backoff"
    37  	"github.com/emirpasic/gods/sets/treeset"
    38  	"github.com/golang/protobuf/proto"
    39  )
    40  
    41  var (
    42  	// ErrEvtAlreadyExist we have already handled this event.
    43  	ErrEvtAlreadyExist = errors.New("event already exist")
    44  	// ErrPubKeyNotAllowlisted this pubkey is not part of the allowlist.
    45  	ErrPubKeyNotAllowlisted = errors.New("pubkey not allowlisted")
    46  )
    47  
    48  //go:generate go run github.com/golang/mock/mockgen -destination mocks/time_service_mock.go -package mocks code.vegaprotocol.io/vega/core/evtforward TimeService
    49  type TimeService interface {
    50  	GetTimeNow() time.Time
    51  }
    52  
    53  //go:generate go run github.com/golang/mock/mockgen -destination mocks/commander_mock.go -package mocks code.vegaprotocol.io/vega/core/evtforward Commander
    54  type Commander interface {
    55  	Command(ctx context.Context, cmd txn.Command, payload proto.Message, f func(string, error), bo *backoff.ExponentialBackOff)
    56  	CommandSync(ctx context.Context, cmd txn.Command, payload proto.Message, f func(string, error), bo *backoff.ExponentialBackOff)
    57  }
    58  
    59  //go:generate go run github.com/golang/mock/mockgen -destination mocks/validator_topology_mock.go -package mocks code.vegaprotocol.io/vega/core/evtforward ValidatorTopology
    60  type ValidatorTopology interface {
    61  	SelfNodeID() string
    62  	AllNodeIDs() []string
    63  }
    64  
    65  // Forwarder receive events from the blockchain queue
    66  // and will try to send them to the vega chain.
    67  // this will select a node in the network to forward the event.
    68  type Forwarder struct {
    69  	log  *logging.Logger
    70  	cfg  Config
    71  	cmd  Commander
    72  	self string
    73  
    74  	ackedEvts *ackedEvents
    75  	evtsmu    sync.Mutex
    76  	evts      map[string]tsEvt
    77  
    78  	mu               sync.RWMutex
    79  	bcQueueAllowlist atomic.Value // this is actually an map[string]struct{}
    80  	timeService      TimeService
    81  	nodes            []string
    82  
    83  	top ValidatorTopology
    84  }
    85  
    86  type tsEvt struct {
    87  	ts  time.Time // timestamp of the block when the event has been added
    88  	evt *commandspb.ChainEvent
    89  }
    90  
    91  // New creates a new instance of the event forwarder.
    92  func New(log *logging.Logger,
    93  	cfg Config,
    94  	cmd Commander,
    95  	timeService TimeService,
    96  	top ValidatorTopology,
    97  ) *Forwarder {
    98  	log = log.Named(forwarderLogger)
    99  	log.SetLevel(cfg.Level.Get())
   100  	var allowlist atomic.Value
   101  	allowlist.Store(buildAllowlist(cfg))
   102  	forwarder := &Forwarder{
   103  		cfg:         cfg,
   104  		log:         log,
   105  		cmd:         cmd,
   106  		timeService: timeService,
   107  		nodes:       []string{},
   108  		self:        top.SelfNodeID(),
   109  		ackedEvts: &ackedEvents{
   110  			timeService: timeService,
   111  			events:      treeset.NewWith(ackedEvtBucketComparator),
   112  		},
   113  		evts:             map[string]tsEvt{},
   114  		top:              top,
   115  		bcQueueAllowlist: allowlist,
   116  	}
   117  	forwarder.updateValidatorsList()
   118  	return forwarder
   119  }
   120  
   121  func buildAllowlist(cfg Config) map[string]struct{} {
   122  	allowlist := make(map[string]struct{}, len(cfg.BlockchainQueueAllowlist))
   123  	for _, v := range cfg.BlockchainQueueAllowlist {
   124  		allowlist[v] = struct{}{}
   125  	}
   126  	return allowlist
   127  }
   128  
   129  // ReloadConf updates the internal configuration of the Event Forwarder engine.
   130  func (f *Forwarder) ReloadConf(cfg Config) {
   131  	f.log.Info("reloading configuration")
   132  	if f.log.GetLevel() != cfg.Level.Get() {
   133  		f.log.Info("updating log level",
   134  			logging.String("old", f.log.GetLevel().String()),
   135  			logging.String("new", cfg.Level.String()),
   136  		)
   137  		f.log.SetLevel(cfg.Level.Get())
   138  	}
   139  
   140  	f.cfg = cfg
   141  	// update the allowlist
   142  	f.log.Info("evtforward allowlist updated",
   143  		logging.Reflect("list", cfg.BlockchainQueueAllowlist))
   144  	f.bcQueueAllowlist.Store(buildAllowlist(cfg))
   145  }
   146  
   147  // Ack will return true if the event is newly acknowledged.
   148  // If the event already exist and was already acknowledged, this will return
   149  // false.
   150  func (f *Forwarder) Ack(evt *commandspb.ChainEvent) bool {
   151  	res := "ok"
   152  	defer func() {
   153  		metrics.EvtForwardInc("ack", res)
   154  	}()
   155  
   156  	f.evtsmu.Lock()
   157  	defer f.evtsmu.Unlock()
   158  
   159  	key, err := f.getEvtKey(evt)
   160  	if err != nil {
   161  		f.log.Error("could not get event key", logging.Error(err))
   162  		return false
   163  	}
   164  	ok, acked := f.getEvt(key)
   165  	if ok && acked {
   166  		f.log.Error("event already acknowledged", logging.String("event", evt.String()))
   167  		res = "alreadyacked"
   168  		// this was already acknowledged, nothing to be done, return false
   169  		return false
   170  	}
   171  	if ok {
   172  		// exists but was not acknowledged
   173  		// we just remove it from the non-acked table
   174  		delete(f.evts, key)
   175  	}
   176  
   177  	// now add it to the acknowledged evts
   178  	f.ackedEvts.Add(key)
   179  	f.log.Info("new event acknowledged", logging.String("event", evt.String()))
   180  	return true
   181  }
   182  
   183  func (f *Forwarder) isAllowlisted(pubkey string) bool {
   184  	allowlist := f.bcQueueAllowlist.Load().(map[string]struct{})
   185  	_, ok := allowlist[pubkey]
   186  	return ok
   187  }
   188  
   189  // Forward will forward a ChainEvent to the tendermint network.
   190  // We expect the pubkey to be an ed25519, hex encoded, key.
   191  func (f *Forwarder) Forward(ctx context.Context, evt *commandspb.ChainEvent, pubkey string) error {
   192  	res := "ok"
   193  	defer func() {
   194  		metrics.EvtForwardInc("forward", res)
   195  	}()
   196  
   197  	if f.log.IsDebug() {
   198  		f.log.Debug("new event received to be forwarded",
   199  			logging.String("event", evt.String()),
   200  		)
   201  	}
   202  
   203  	// check if the sender of the event is whitelisted
   204  	if !f.isAllowlisted(pubkey) {
   205  		res = "pubkeynotallowed"
   206  		return ErrPubKeyNotAllowlisted
   207  	}
   208  
   209  	f.evtsmu.Lock()
   210  	defer f.evtsmu.Unlock()
   211  
   212  	key, err := f.getEvtKey(evt)
   213  	if err != nil {
   214  		return err
   215  	}
   216  	ok, ack := f.getEvt(key)
   217  	if ok {
   218  		f.log.Error("event already processed",
   219  			logging.String("evt", evt.String()),
   220  			logging.Bool("acknowledged", ack),
   221  		)
   222  		res = "dupevt"
   223  		return ErrEvtAlreadyExist
   224  	}
   225  
   226  	f.evts[key] = tsEvt{ts: f.timeService.GetTimeNow(), evt: evt}
   227  	if f.isSender(evt) {
   228  		// we are selected to send the event, let's do it.
   229  		f.send(ctx, evt)
   230  	}
   231  	return nil
   232  }
   233  
   234  // ForwardFromSelf will forward event seen by the node itself, not from
   235  // an external service like the eef for example.
   236  func (f *Forwarder) ForwardFromSelf(evt *commandspb.ChainEvent) {
   237  	f.evtsmu.Lock()
   238  	defer f.evtsmu.Unlock()
   239  
   240  	key, err := f.getEvtKey(evt)
   241  	if err != nil {
   242  		// no way this event would be badly formatted
   243  		// it is sent by the node, a badly formatted event
   244  		// would mean a code bug
   245  		f.log.Panic("invalid event to be forwarded",
   246  			logging.String("event", evt.String()),
   247  			logging.Error(err),
   248  		)
   249  	}
   250  
   251  	ok, ack := f.getEvt(key)
   252  	if ok {
   253  		f.log.Error("event already processed",
   254  			logging.String("event", evt.String()),
   255  			logging.Bool("acknowledged", ack),
   256  		)
   257  		// nothing to do, just a log here.
   258  		return
   259  	}
   260  
   261  	f.evts[key] = tsEvt{ts: f.timeService.GetTimeNow(), evt: evt}
   262  }
   263  
   264  func (f *Forwarder) updateValidatorsList() {
   265  	f.mu.Lock()
   266  	defer f.mu.Unlock()
   267  
   268  	f.self = f.top.SelfNodeID()
   269  	f.nodes = f.top.AllNodeIDs()
   270  	sort.SliceStable(f.nodes, func(i, j int) bool {
   271  		return f.nodes[i] < f.nodes[j]
   272  	})
   273  }
   274  
   275  // getEvt assumes the lock is acquired before being called.
   276  func (f *Forwarder) getEvt(key string) (ok bool, acked bool) {
   277  	if f.ackedEvts.Contains(key) {
   278  		return true, true
   279  	}
   280  
   281  	if _, ok := f.evts[key]; ok {
   282  		return true, false
   283  	}
   284  
   285  	return false, false
   286  }
   287  
   288  func (f *Forwarder) send(ctx context.Context, evt *commandspb.ChainEvent) {
   289  	if f.log.IsDebug() {
   290  		f.log.Debug("trying to send event",
   291  			logging.String("event", evt.String()),
   292  		)
   293  	}
   294  
   295  	// error doesn't matter here
   296  	f.cmd.Command(ctx, txn.ChainEventCommand, evt, func(_ string, err error) {
   297  		if err != nil {
   298  			f.log.Error("could not send command", logging.String("tx-id", evt.TxId), logging.Error(err))
   299  		}
   300  	}, nil)
   301  }
   302  
   303  func (f *Forwarder) isSender(evt *commandspb.ChainEvent) bool {
   304  	key, err := f.makeEvtHashKey(evt)
   305  	if err != nil {
   306  		f.log.Error("could not marshal event", logging.Error(err))
   307  		return false
   308  	}
   309  	h := f.hash(key) + uint64(f.timeService.GetTimeNow().Unix())
   310  
   311  	f.mu.RLock()
   312  	if len(f.nodes) <= 0 {
   313  		f.mu.RUnlock()
   314  		return false
   315  	}
   316  	node := f.nodes[h%uint64(len(f.nodes))]
   317  	f.mu.RUnlock()
   318  
   319  	return node == f.self
   320  }
   321  
   322  func (f *Forwarder) OnTick(ctx context.Context, t time.Time) {
   323  	// get an updated list of validators from the topology
   324  	f.updateValidatorsList()
   325  
   326  	f.mu.RLock()
   327  	retryRate := f.cfg.RetryRate.Duration
   328  	f.mu.RUnlock()
   329  
   330  	f.evtsmu.Lock()
   331  	defer f.evtsmu.Unlock()
   332  
   333  	// try to send all event that are not acknowledged at the moment
   334  	for k, evt := range f.evts {
   335  		// do we need to try to forward the event again?
   336  		if evt.ts.Add(retryRate).Before(t) {
   337  			// set next retry
   338  			f.evts[k] = tsEvt{ts: t, evt: evt.evt}
   339  			if f.isSender(evt.evt) {
   340  				// we are selected to send the event, let's do it.
   341  				f.send(ctx, evt.evt)
   342  			}
   343  		}
   344  	}
   345  
   346  	// now delete old events
   347  	// get the oldest to remove
   348  	removeBefore := t.Add(-f.cfg.KeepHashesDurationForTestOnlyDoNotChange.Duration)
   349  	f.ackedEvts.RemoveBefore(removeBefore.Unix())
   350  }
   351  
   352  func (f *Forwarder) getEvtKey(evt *commandspb.ChainEvent) (string, error) {
   353  	mevt, err := f.marshalEvt(evt)
   354  	if err != nil {
   355  		return "", fmt.Errorf("invalid event: %w", err)
   356  	}
   357  
   358  	return hex.EncodeToString(crypto.Hash(mevt)), nil
   359  }
   360  
   361  func (f *Forwarder) marshalEvt(evt *commandspb.ChainEvent) ([]byte, error) {
   362  	buf, err := vgproto.Marshal(evt)
   363  	if err != nil {
   364  		return nil, err
   365  	}
   366  	return buf, nil
   367  }
   368  
   369  func (f *Forwarder) makeEvtHashKey(evt *commandspb.ChainEvent) ([]byte, error) {
   370  	// deterministic marshal of the event
   371  	pbuf, err := f.marshalEvt(evt)
   372  	if err != nil {
   373  		return nil, err
   374  	}
   375  	return pbuf, nil
   376  }
   377  
   378  func (f *Forwarder) hash(key []byte) uint64 {
   379  	h := fnv.New64a()
   380  	h.Write(key)
   381  
   382  	return h.Sum64()
   383  }