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 }