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 }