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  }