code.vegaprotocol.io/vega@v0.79.0/core/staking/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 staking
    17  
    18  import (
    19  	"context"
    20  	"encoding/hex"
    21  	"strings"
    22  	"sync"
    23  	"time"
    24  
    25  	"code.vegaprotocol.io/vega/core/types"
    26  	"code.vegaprotocol.io/vega/logging"
    27  
    28  	"github.com/ethereum/go-ethereum/accounts/abi/bind"
    29  	ethcmn "github.com/ethereum/go-ethereum/common"
    30  )
    31  
    32  type EthereumClient interface {
    33  	bind.ContractFilterer
    34  }
    35  
    36  type OnChainVerifier struct {
    37  	log              *logging.Logger
    38  	ethClient        EthereumClient
    39  	ethConfirmations EthConfirmations
    40  
    41  	mu                     sync.RWMutex
    42  	stakingBridgeAddresses []ethcmn.Address
    43  }
    44  
    45  func NewOnChainVerifier(
    46  	cfg Config,
    47  	log *logging.Logger,
    48  	ethClient EthereumClient,
    49  	ethConfirmations EthConfirmations,
    50  ) *OnChainVerifier {
    51  	log = log.Named("on-chain-verifier")
    52  	log.SetLevel(cfg.Level.Get())
    53  
    54  	return &OnChainVerifier{
    55  		log:              log,
    56  		ethClient:        ethClient,
    57  		ethConfirmations: ethConfirmations,
    58  	}
    59  }
    60  
    61  func (o *OnChainVerifier) UpdateStakingBridgeAddresses(stakingBridgeAddresses []ethcmn.Address) {
    62  	o.mu.Lock()
    63  	defer o.mu.Unlock()
    64  
    65  	o.stakingBridgeAddresses = stakingBridgeAddresses
    66  
    67  	if o.log.GetLevel() <= logging.DebugLevel {
    68  		var addresses []string
    69  		for _, v := range o.stakingBridgeAddresses {
    70  			addresses = append(addresses, v.Hex())
    71  		}
    72  		o.log.Debug("staking bridge addresses updated",
    73  			logging.Strings("addresses", addresses))
    74  	}
    75  }
    76  
    77  func (o *OnChainVerifier) GetStakingBridgeAddresses() []string {
    78  	o.mu.Lock()
    79  	defer o.mu.Unlock()
    80  
    81  	addresses := make([]string, 0, len(o.stakingBridgeAddresses))
    82  	for _, v := range o.stakingBridgeAddresses {
    83  		addresses = append(addresses, v.Hex())
    84  	}
    85  	return addresses
    86  }
    87  
    88  func (o *OnChainVerifier) CheckStakeDeposited(
    89  	event *types.StakeDeposited,
    90  ) error {
    91  	o.mu.RLock()
    92  	defer o.mu.RUnlock()
    93  
    94  	if o.log.GetLevel() <= logging.DebugLevel {
    95  		o.log.Debug("checking stake deposited event on chain",
    96  			logging.String("event", event.String()),
    97  		)
    98  	}
    99  
   100  	decodedPubKeySlice, err := hex.DecodeString(event.VegaPubKey)
   101  	if err != nil {
   102  		o.log.Error("invalid pubkey in stake deposited event", logging.Error(err))
   103  		return err
   104  	}
   105  	var decodedPubKey [32]byte
   106  	copy(decodedPubKey[:], decodedPubKeySlice[0:32])
   107  
   108  	for _, address := range o.stakingBridgeAddresses {
   109  		if o.log.GetLevel() <= logging.DebugLevel {
   110  			o.log.Debug("checking stake deposited event on chain",
   111  				logging.String("bridge-address", address.Hex()),
   112  				logging.String("event", event.String()),
   113  			)
   114  		}
   115  		filterer, err := NewStakingFilterer(address, o.ethClient)
   116  		if err != nil {
   117  			o.log.Error("could not instantiate staking bridge filterer",
   118  				logging.String("address", address.Hex()))
   119  			continue
   120  		}
   121  
   122  		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
   123  		defer cancel()
   124  		iter, err := filterer.FilterStakeDeposited(
   125  			&bind.FilterOpts{
   126  				Start:   event.BlockNumber,
   127  				End:     &event.BlockNumber,
   128  				Context: ctx,
   129  			},
   130  			// user
   131  			[]ethcmn.Address{ethcmn.HexToAddress(event.EthereumAddress)},
   132  			// vega_public_key
   133  			[][32]byte{decodedPubKey})
   134  		if err != nil {
   135  			o.log.Error("Couldn't start filtering on stake deposited event", logging.Error(err))
   136  			continue
   137  		}
   138  		defer iter.Close()
   139  
   140  		vegaPubKey := strings.TrimPrefix(event.VegaPubKey, "0x")
   141  		amountDeposited := event.Amount.BigInt()
   142  
   143  		for iter.Next() {
   144  			if o.log.GetLevel() <= logging.DebugLevel {
   145  				o.log.Debug("found stake deposited event on chain",
   146  					logging.String("bridge-address", address.Hex()),
   147  					logging.String("amount", iter.Event.Amount.String()),
   148  					logging.String("user", iter.Event.User.Hex()),
   149  				)
   150  			}
   151  
   152  			if !iter.Event.Raw.Removed && // ignore removed events
   153  				hex.EncodeToString(iter.Event.VegaPublicKey[:]) == vegaPubKey &&
   154  				iter.Event.Amount.Cmp(amountDeposited) == 0 &&
   155  				iter.Event.Raw.BlockNumber == event.BlockNumber &&
   156  				uint64(iter.Event.Raw.Index) == event.LogIndex &&
   157  				iter.Event.Raw.TxHash.Hex() == event.TxID {
   158  				// now we know the event is OK,
   159  				// just need to check for confirmations
   160  				return o.ethConfirmations.Check(event.BlockNumber)
   161  			}
   162  		}
   163  	}
   164  
   165  	return ErrNoStakeDepositedEventFound
   166  }
   167  
   168  func (o *OnChainVerifier) CheckStakeRemoved(event *types.StakeRemoved) error {
   169  	o.mu.RLock()
   170  	defer o.mu.RUnlock()
   171  
   172  	if o.log.GetLevel() <= logging.DebugLevel {
   173  		o.log.Debug("checking stake removed event on chain",
   174  			logging.String("event", event.String()),
   175  		)
   176  	}
   177  
   178  	decodedPubKeySlice, err := hex.DecodeString(event.VegaPubKey)
   179  	if err != nil {
   180  		o.log.Error("invalid pubkey inn stake deposited event", logging.Error(err))
   181  		return err
   182  	}
   183  	var decodedPubKey [32]byte
   184  	copy(decodedPubKey[:], decodedPubKeySlice[0:32])
   185  
   186  	for _, address := range o.stakingBridgeAddresses {
   187  		if o.log.GetLevel() <= logging.DebugLevel {
   188  			o.log.Debug("checking stake removed event on chain",
   189  				logging.String("bridge-address", address.Hex()),
   190  				logging.String("event", event.String()),
   191  			)
   192  		}
   193  		filterer, err := NewStakingFilterer(address, o.ethClient)
   194  		if err != nil {
   195  			o.log.Error("could not instantiate staking bridge filterer",
   196  				logging.String("address", address.Hex()))
   197  			continue
   198  		}
   199  
   200  		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
   201  		defer cancel()
   202  
   203  		iter, err := filterer.FilterStakeRemoved(
   204  			&bind.FilterOpts{
   205  				Start:   event.BlockNumber,
   206  				End:     &event.BlockNumber,
   207  				Context: ctx,
   208  			},
   209  			// user
   210  			[]ethcmn.Address{ethcmn.HexToAddress(event.EthereumAddress)},
   211  			// vega_public_key
   212  			[][32]byte{decodedPubKey})
   213  		if err != nil {
   214  			o.log.Error("could not start stake deposited filter",
   215  				logging.Error(err))
   216  			continue
   217  		}
   218  		defer iter.Close()
   219  
   220  		vegaPubKey := strings.TrimPrefix(event.VegaPubKey, "0x")
   221  		amountDeposited := event.Amount.BigInt()
   222  
   223  		for iter.Next() {
   224  			if o.log.GetLevel() <= logging.DebugLevel {
   225  				o.log.Debug("found stake removed event on chain",
   226  					logging.String("bridge-address", address.Hex()),
   227  					logging.String("amount", iter.Event.Amount.String()),
   228  					logging.String("user", iter.Event.User.Hex()),
   229  				)
   230  			}
   231  
   232  			if !iter.Event.Raw.Removed && // ignore removed events
   233  				hex.EncodeToString(iter.Event.VegaPublicKey[:]) == vegaPubKey &&
   234  				iter.Event.Amount.Cmp(amountDeposited) == 0 &&
   235  				iter.Event.Raw.BlockNumber == event.BlockNumber &&
   236  				uint64(iter.Event.Raw.Index) == event.LogIndex &&
   237  				iter.Event.Raw.TxHash.Hex() == event.TxID {
   238  				// now we know the event is OK,
   239  				// just need to check for confirmations
   240  				return o.ethConfirmations.Check(event.BlockNumber)
   241  			}
   242  		}
   243  	}
   244  
   245  	return ErrNoStakeRemovedEventFound
   246  }