code.vegaprotocol.io/vega@v0.79.0/core/validators/erc20multisig/topology.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 erc20multisig 17 18 import ( 19 "context" 20 "errors" 21 "sort" 22 "sync" 23 "time" 24 25 "code.vegaprotocol.io/vega/core/broker" 26 "code.vegaprotocol.io/vega/core/events" 27 "code.vegaprotocol.io/vega/core/types" 28 "code.vegaprotocol.io/vega/core/validators" 29 "code.vegaprotocol.io/vega/logging" 30 ) 31 32 //go:generate go run github.com/golang/mock/mockgen -destination mocks/mocks.go -package mocks code.vegaprotocol.io/vega/core/validators/erc20multisig Witness,MultiSigOnChainVerifier,EthConfirmations,EthereumEventSource 33 34 const ( 35 // 3 weeks, duration of the whole network at first? 36 timeTilCancel = 24 * 21 * time.Hour 37 ) 38 39 var ( 40 ErrDuplicatedSignerEvent = errors.New("duplicated signer event") 41 ErrDuplicatedThresholdEvent = errors.New("duplicated threshold event") 42 ) 43 44 // Witness provide foreign chain resources validations. 45 type Witness interface { 46 StartCheck(validators.Resource, func(interface{}, bool), time.Time) error 47 RestoreResource(validators.Resource, func(interface{}, bool)) error 48 } 49 50 type MultiSigOnChainVerifier interface { 51 CheckSignerEvent(*types.SignerEvent) error 52 CheckThresholdSetEvent(*types.SignerThresholdSetEvent) error 53 GetMultiSigAddress() string 54 } 55 56 type EthereumEventSource interface { 57 UpdateContractBlock(string, string, uint64) 58 } 59 60 // Topology keeps track of all the validators 61 // registered in the erc20 bridge. 62 type Topology struct { 63 config Config 64 log *logging.Logger 65 chainID string 66 67 currentTime time.Time 68 69 witness Witness 70 broker broker.Interface 71 ocv MultiSigOnChainVerifier 72 73 // use to access both the pendingEvents and pendingThresholds maps 74 mu sync.Mutex 75 76 // the current map of all the signer on the bridge 77 signers map[string]struct{} 78 // signer address to list of all events related to it 79 // order by block time. 80 eventsPerAddress map[string][]*types.SignerEvent 81 // a map of all pending events waiting to be processed 82 pendingSigners map[string]*pendingSigner 83 84 // the signer required treshold 85 // last one is always kept 86 threshold *types.SignerThresholdSetEvent 87 pendingThresholds map[string]*pendingThresholdSet 88 89 // a map of all seen events 90 seen map[string]struct{} 91 92 witnessedThresholds map[string]struct{} 93 witnessedSigners map[string]struct{} 94 95 ethEventSource EthereumEventSource 96 } 97 98 type pendingSigner struct { 99 *types.SignerEvent 100 101 check func() error 102 } 103 104 func (p pendingSigner) GetID() string { return p.ID } 105 func (p pendingSigner) GetChainID() string { return p.ChainID } 106 func (p pendingSigner) GetType() types.NodeVoteType { 107 var ty types.NodeVoteType 108 switch p.Kind { 109 case types.SignerEventKindAdded: 110 ty = types.NodeVoteTypeSignerAdded 111 case types.SignerEventKindRemoved: 112 ty = types.NodeVoteTypeSignerRemoved 113 } 114 115 return ty 116 } 117 118 func (p *pendingSigner) Check(ctx context.Context) error { return p.check() } 119 120 type pendingThresholdSet struct { 121 *types.SignerThresholdSetEvent 122 check func() error 123 } 124 125 func (p pendingThresholdSet) GetID() string { return p.ID } 126 func (p pendingThresholdSet) GetChainID() string { return p.ChainID } 127 func (p pendingThresholdSet) GetType() types.NodeVoteType { 128 return types.NodeVoteTypeSignerThresholdSet 129 } 130 131 func (p *pendingThresholdSet) Check(ctx context.Context) error { return p.check() } 132 133 func NewTopology( 134 config Config, 135 log *logging.Logger, 136 witness Witness, 137 ocv MultiSigOnChainVerifier, 138 broker broker.Interface, 139 scope string, 140 ) *Topology { 141 log = log.Named(namedLogger + ".topology") 142 log.SetLevel(config.Level.Get()) 143 t := &Topology{ 144 config: config, 145 log: log, 146 witness: witness, 147 ocv: ocv, 148 broker: broker, 149 signers: map[string]struct{}{}, 150 eventsPerAddress: map[string][]*types.SignerEvent{}, 151 pendingSigners: map[string]*pendingSigner{}, 152 pendingThresholds: map[string]*pendingThresholdSet{}, 153 seen: map[string]struct{}{}, 154 witnessedThresholds: map[string]struct{}{}, 155 witnessedSigners: map[string]struct{}{}, 156 } 157 return t 158 } 159 160 // SetChainID sets the chainID of the EVM chain this multisig tracker belongs to. 161 func (t *Topology) SetChainID(chainID string) { 162 t.chainID = chainID 163 } 164 165 func (t *Topology) ChainID() string { 166 return t.chainID 167 } 168 169 func (t *Topology) SetWitness(w Witness) { 170 t.witness = w 171 } 172 173 func (t *Topology) SetEthereumEventSource(e EthereumEventSource) { 174 t.ethEventSource = e 175 } 176 177 func (t *Topology) ExcessSigners(addresses []string) bool { 178 addressesMap := map[string]struct{}{} 179 for _, v := range addresses { 180 addressesMap[v] = struct{}{} 181 } 182 183 for k := range t.signers { 184 if _, ok := addressesMap[k]; !ok { 185 return true 186 } 187 } 188 189 return false 190 } 191 192 func (t *Topology) GetSigners() []string { 193 t.mu.Lock() 194 defer t.mu.Unlock() 195 196 out := make([]string, 0, len(t.signers)) 197 for k := range t.signers { 198 out = append(out, k) 199 } 200 sort.Strings(out) 201 202 return out 203 } 204 205 func (t *Topology) IsSigner(address string) bool { 206 t.mu.Lock() 207 defer t.mu.Unlock() 208 _, ok := t.signers[address] 209 return ok 210 } 211 212 func (t *Topology) GetThreshold() uint32 { 213 t.mu.Lock() 214 defer t.mu.Unlock() 215 if t.threshold != nil { 216 return t.threshold.Threshold 217 } 218 return 0 219 } 220 221 func (t *Topology) ProcessSignerEvent(event *types.SignerEvent) error { 222 if ok := t.ensureNotDuplicate(event.Hash()); !ok { 223 t.log.Error("signer event already exists", 224 logging.String("chain-id", t.chainID), 225 logging.String("event", event.String())) 226 return ErrDuplicatedSignerEvent 227 } 228 229 pending := &pendingSigner{ 230 SignerEvent: event, 231 check: func() error { return t.ocv.CheckSignerEvent(event) }, 232 } 233 t.pendingSigners[event.ID] = pending 234 t.log.Info("signer event received, starting validation", 235 logging.String("chain-id", t.chainID), 236 logging.String("event", event.String())) 237 238 return t.witness.StartCheck( 239 pending, t.onEventVerified, t.currentTime.Add(timeTilCancel)) 240 } 241 242 func (t *Topology) ProcessThresholdEvent(event *types.SignerThresholdSetEvent) error { 243 if ok := t.ensureNotDuplicate(event.Hash()); !ok { 244 t.log.Error("threshold event already exists", 245 logging.String("chain-id", t.chainID), 246 logging.String("event", event.String())) 247 return ErrDuplicatedThresholdEvent 248 } 249 250 pending := &pendingThresholdSet{ 251 SignerThresholdSetEvent: event, 252 check: func() error { return t.ocv.CheckThresholdSetEvent(event) }, 253 } 254 t.pendingThresholds[event.ID] = pending 255 t.log.Info("signer threshold set event received, starting validation", 256 logging.String("chain-id", t.chainID), 257 logging.String("event", event.String())) 258 259 return t.witness.StartCheck( 260 pending, t.onEventVerified, t.currentTime.Add(timeTilCancel)) 261 } 262 263 func (t *Topology) ensureNotDuplicate(h string) bool { 264 t.mu.Lock() 265 defer t.mu.Unlock() 266 267 if _, ok := t.seen[h]; ok { 268 return false 269 } 270 t.seen[h] = struct{}{} 271 return true 272 } 273 274 func (t *Topology) onEventVerified(event interface{}, ok bool) { 275 switch e := event.(type) { 276 case *pendingSigner: 277 if !ok { 278 // invalid, just delete from the map 279 delete(t.pendingSigners, e.ID) 280 return 281 } 282 t.witnessedSigners[e.ID] = struct{}{} 283 case *pendingThresholdSet: 284 if !ok { 285 // invalid, just delete from the map 286 delete(t.pendingThresholds, e.ID) 287 return 288 } 289 t.witnessedThresholds[e.ID] = struct{}{} 290 default: 291 t.log.Error("multisig verifier received invalid event", 292 logging.String("chain-id", t.chainID), 293 ) 294 return 295 } 296 } 297 298 func (t *Topology) OnTick(ctx context.Context, ct time.Time) { 299 t.currentTime = ct 300 t.updateThreshold(ctx) 301 t.updateSigners(ctx) 302 } 303 304 func (t *Topology) updateThreshold(ctx context.Context) { 305 t.mu.Lock() 306 defer t.mu.Unlock() 307 308 if len(t.witnessedThresholds) <= 0 { 309 return 310 } 311 312 // sort all IDs to access pendings events in order 313 ids := []string{} 314 for k := range t.witnessedThresholds { 315 ids = append(ids, k) 316 delete(t.witnessedThresholds, k) 317 } 318 sort.Strings(ids) 319 320 // now iterate over all events and update the 321 // threshold if we get an event with a more recent 322 // block time. 323 for _, v := range ids { 324 event := t.pendingThresholds[v] 325 t.setThresholdSetEvent(ctx, event.SignerThresholdSetEvent) 326 delete(t.pendingThresholds, v) 327 } 328 } 329 330 func (t *Topology) setThresholdSetEvent( 331 ctx context.Context, event *types.SignerThresholdSetEvent, 332 ) { 333 // if it's out first time here 334 if t.threshold == nil || event.BlockTime > t.threshold.BlockTime { 335 t.threshold = event 336 } 337 338 // send the event anyway so APIs can be aware of past thresholds 339 t.broker.Send(events.NewERC20MultiSigThresholdSet(ctx, *event)) 340 } 341 342 func (t *Topology) updateSigners(ctx context.Context) { 343 t.mu.Lock() 344 defer t.mu.Unlock() 345 346 if len(t.witnessedSigners) <= 0 { 347 return 348 } 349 // sort all IDs to access pendings events in order 350 ids := []string{} 351 for k := range t.witnessedSigners { 352 ids = append(ids, k) 353 delete(t.witnessedSigners, k) 354 } 355 sort.Strings(ids) 356 357 // first add all events to the map of events per addresses 358 for _, id := range ids { 359 // get the event 360 event := t.pendingSigners[id] 361 362 t.addSignerEvent(ctx, event.SignerEvent) 363 364 // delete from pending then 365 delete(t.pendingSigners, id) 366 } 367 } 368 369 func (t *Topology) addSignerEvent(ctx context.Context, event *types.SignerEvent) { 370 epa, ok := t.eventsPerAddress[event.Address] 371 if !ok { 372 epa = []*types.SignerEvent{} 373 } 374 375 // now add the event to the list for this address 376 epa = append(epa, event) 377 // sort them in arrival order 378 sort.Slice(epa, func(i, j int) bool { 379 return epa[i].BlockTime < epa[j].BlockTime 380 }) 381 382 t.eventsPerAddress[event.Address] = epa 383 384 // now depending of the last event received, 385 // we add or remove from the list of signers 386 switch epa[len(epa)-1].Kind { 387 case types.SignerEventKindRemoved: 388 delete(t.signers, event.Address) 389 case types.SignerEventKindAdded: 390 t.signers[event.Address] = struct{}{} 391 } 392 393 t.ethEventSource.UpdateContractBlock(t.ocv.GetMultiSigAddress(), t.chainID, event.BlockNumber) 394 395 // send the event anyway so APIs can be aware of past thresholds 396 t.broker.Send(events.NewERC20MultiSigSigner(ctx, *event)) 397 }