code.vegaprotocol.io/vega@v0.79.0/core/validators/erc20multisig/on_chain_verifier.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  	"math/big"
    22  	"sync"
    23  	"time"
    24  
    25  	multisig "code.vegaprotocol.io/vega/core/contracts/multisig_control"
    26  	"code.vegaprotocol.io/vega/core/types"
    27  	"code.vegaprotocol.io/vega/logging"
    28  
    29  	"github.com/ethereum/go-ethereum/accounts/abi/bind"
    30  	ethcmn "github.com/ethereum/go-ethereum/common"
    31  )
    32  
    33  var (
    34  	ErrNoSignerEventFound       = errors.New("no signer event found")
    35  	ErrNoThresholdSetEventFound = errors.New("no threshold set event found")
    36  	ErrUnsupportedSignerEvent   = errors.New("unsupported signer event")
    37  )
    38  
    39  type EthereumClient interface {
    40  	bind.ContractFilterer
    41  }
    42  
    43  type EthConfirmations interface {
    44  	Check(uint64) error
    45  }
    46  
    47  type OnChainVerifier struct {
    48  	config           Config
    49  	log              *logging.Logger
    50  	ethClient        EthereumClient
    51  	ethConfirmations EthConfirmations
    52  
    53  	mu              sync.RWMutex
    54  	multiSigAddress ethcmn.Address
    55  	chainID         string
    56  }
    57  
    58  func NewOnChainVerifier(
    59  	config Config,
    60  	log *logging.Logger,
    61  	ethClient EthereumClient,
    62  	ethConfirmations EthConfirmations,
    63  ) *OnChainVerifier {
    64  	log = log.Named(namedLogger + ".on-chain-verifier")
    65  	log.SetLevel(config.Level.Get())
    66  
    67  	return &OnChainVerifier{
    68  		config:           config,
    69  		log:              log,
    70  		ethClient:        ethClient,
    71  		ethConfirmations: ethConfirmations,
    72  	}
    73  }
    74  
    75  func (o *OnChainVerifier) GetMultiSigAddress() string {
    76  	o.mu.Lock()
    77  	defer o.mu.Unlock()
    78  	return o.multiSigAddress.Hex()
    79  }
    80  
    81  func (o *OnChainVerifier) UpdateMultiSigAddress(multiSigAddress ethcmn.Address, chainID string) {
    82  	o.mu.Lock()
    83  	defer o.mu.Unlock()
    84  
    85  	o.multiSigAddress = multiSigAddress
    86  	o.chainID = chainID
    87  
    88  	if o.log.GetLevel() <= logging.DebugLevel {
    89  		o.log.Debug("multi sig bridge addresses updated",
    90  			logging.String("chain-id", chainID),
    91  			logging.String("addresses", o.multiSigAddress.Hex()))
    92  	}
    93  }
    94  
    95  func (o *OnChainVerifier) CheckSignerEvent(event *types.SignerEvent) error {
    96  	o.mu.RLock()
    97  	defer o.mu.RUnlock()
    98  
    99  	if o.log.GetLevel() <= logging.DebugLevel {
   100  		o.log.Debug("checking signer event on chain",
   101  			logging.String("chain-id", o.chainID),
   102  			logging.String("contract-address", o.multiSigAddress.Hex()),
   103  			logging.String("event", event.String()),
   104  		)
   105  	}
   106  
   107  	filterer, err := multisig.NewMultisigControlFilterer(
   108  		o.multiSigAddress,
   109  		o.ethClient,
   110  	)
   111  	if err != nil {
   112  		o.log.Error("could not instantiate multisig control filterer",
   113  			logging.String("chain-id", o.chainID),
   114  			logging.Error(err))
   115  		return err
   116  	}
   117  
   118  	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
   119  	defer cancel()
   120  
   121  	switch event.Kind {
   122  	case types.SignerEventKindAdded:
   123  		return o.filterSignerAdded(ctx, filterer, event)
   124  	case types.SignerEventKindRemoved:
   125  		return o.filterSignerRemoved(ctx, filterer, event)
   126  	default:
   127  		return ErrUnsupportedSignerEvent
   128  	}
   129  }
   130  
   131  func (o *OnChainVerifier) CheckThresholdSetEvent(
   132  	event *types.SignerThresholdSetEvent,
   133  ) error {
   134  	o.mu.RLock()
   135  	defer o.mu.RUnlock()
   136  
   137  	if o.log.GetLevel() <= logging.DebugLevel {
   138  		o.log.Debug("checking threshold set event on chain",
   139  			logging.String("chain-id", o.chainID),
   140  			logging.String("contract-address", o.multiSigAddress.Hex()),
   141  			logging.String("event", event.String()),
   142  		)
   143  	}
   144  
   145  	filterer, err := multisig.NewMultisigControlFilterer(
   146  		o.multiSigAddress,
   147  		o.ethClient,
   148  	)
   149  	if err != nil {
   150  		o.log.Error("could not instantiate multisig control filterer",
   151  			logging.String("chain-id", o.chainID),
   152  			logging.Error(err))
   153  		return err
   154  	}
   155  
   156  	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
   157  	defer cancel()
   158  
   159  	iter, err := filterer.FilterThresholdSet(
   160  		&bind.FilterOpts{
   161  			Start:   event.BlockNumber,
   162  			End:     &event.BlockNumber,
   163  			Context: ctx,
   164  		},
   165  	)
   166  	if err != nil {
   167  		o.log.Error("Couldn't start filtering on signer added event",
   168  			logging.String("chain-id", o.chainID),
   169  			logging.Error(err))
   170  		return err
   171  	}
   172  	defer iter.Close()
   173  
   174  	for iter.Next() {
   175  		if o.log.GetLevel() <= logging.DebugLevel {
   176  			o.log.Debug("found threshold set event on chain",
   177  				logging.String("chain-id", o.chainID),
   178  				logging.Uint16("new-threshold", iter.Event.NewThreshold),
   179  			)
   180  		}
   181  
   182  		nonce, _ := big.NewInt(0).SetString(event.Nonce, 10)
   183  		if !iter.Event.Raw.Removed &&
   184  			iter.Event.Raw.BlockNumber == event.BlockNumber &&
   185  			uint64(iter.Event.Raw.Index) == event.LogIndex &&
   186  			iter.Event.NewThreshold == uint16(event.Threshold) &&
   187  			nonce.Cmp(iter.Event.Nonce) == 0 &&
   188  			iter.Event.Raw.TxHash.Hex() == event.TxHash {
   189  			// now we know the event is OK,
   190  			// just need to check for confirmations
   191  			return o.ethConfirmations.Check(event.BlockNumber)
   192  		}
   193  	}
   194  
   195  	return ErrNoThresholdSetEventFound
   196  }
   197  
   198  func (o *OnChainVerifier) filterSignerAdded(
   199  	ctx context.Context,
   200  	filterer *multisig.MultisigControlFilterer,
   201  	event *types.SignerEvent,
   202  ) error {
   203  	iter, err := filterer.FilterSignerAdded(
   204  		&bind.FilterOpts{
   205  			Start:   event.BlockNumber,
   206  			End:     &event.BlockNumber,
   207  			Context: ctx,
   208  		},
   209  	)
   210  	if err != nil {
   211  		o.log.Error("Couldn't start filtering on signer added event",
   212  			logging.String("chain-id", o.chainID),
   213  			logging.Error(err))
   214  		return err
   215  	}
   216  	defer iter.Close()
   217  
   218  	for iter.Next() {
   219  		if o.log.GetLevel() <= logging.DebugLevel {
   220  			o.log.Debug("found signer added event on chain",
   221  				logging.String("chain-id", o.chainID),
   222  				logging.String("new-signer", iter.Event.NewSigner.Hex()),
   223  			)
   224  		}
   225  
   226  		nonce, _ := big.NewInt(0).SetString(event.Nonce, 10)
   227  		if !iter.Event.Raw.Removed &&
   228  			iter.Event.Raw.BlockNumber == event.BlockNumber &&
   229  			uint64(iter.Event.Raw.Index) == event.LogIndex &&
   230  			iter.Event.NewSigner.Hex() == event.Address &&
   231  			nonce.Cmp(iter.Event.Nonce) == 0 &&
   232  			iter.Event.Raw.TxHash.Hex() == event.TxHash {
   233  			// now we know the event is OK,
   234  			// just need to check for confirmations
   235  			return o.ethConfirmations.Check(event.BlockNumber)
   236  		}
   237  	}
   238  
   239  	return ErrNoSignerEventFound
   240  }
   241  
   242  func (o *OnChainVerifier) filterSignerRemoved(
   243  	ctx context.Context,
   244  	filterer *multisig.MultisigControlFilterer,
   245  	event *types.SignerEvent,
   246  ) error {
   247  	iter, err := filterer.FilterSignerRemoved(
   248  		&bind.FilterOpts{
   249  			Start:   event.BlockNumber,
   250  			End:     &event.BlockNumber,
   251  			Context: ctx,
   252  		},
   253  	)
   254  	if err != nil {
   255  		o.log.Error("Couldn't start filtering on signer removed event",
   256  			logging.String("chain-id", o.chainID),
   257  			logging.Error(err))
   258  		return err
   259  	}
   260  	defer iter.Close()
   261  
   262  	for iter.Next() {
   263  		if o.log.GetLevel() <= logging.DebugLevel {
   264  			o.log.Debug("found signer removed event on chain",
   265  				logging.String("chain-id", o.chainID),
   266  				logging.String("old-signer", iter.Event.OldSigner.Hex()),
   267  			)
   268  		}
   269  
   270  		nonce, _ := big.NewInt(0).SetString(event.Nonce, 10)
   271  		if !iter.Event.Raw.Removed &&
   272  			iter.Event.Raw.BlockNumber == event.BlockNumber &&
   273  			uint64(iter.Event.Raw.Index) == event.LogIndex &&
   274  			iter.Event.OldSigner.Hex() == event.Address &&
   275  			nonce.Cmp(iter.Event.Nonce) == 0 &&
   276  			iter.Event.Raw.TxHash.Hex() == event.TxHash {
   277  			// now we know the event is OK,
   278  			// just need to check for confirmations
   279  			return o.ethConfirmations.Check(event.BlockNumber)
   280  		}
   281  	}
   282  
   283  	return ErrNoSignerEventFound
   284  }