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 }