code.vegaprotocol.io/vega@v0.79.0/core/processor/process_chain_event.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 processor 17 18 import ( 19 "context" 20 "errors" 21 "strconv" 22 "strings" 23 24 "code.vegaprotocol.io/vega/core/datasource/external/ethcall" 25 "code.vegaprotocol.io/vega/core/types" 26 "code.vegaprotocol.io/vega/logging" 27 vgproto "code.vegaprotocol.io/vega/protos/vega" 28 commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1" 29 ) 30 31 var ( 32 ErrNotAnERC20Event = errors.New("not an erc20 event") 33 ErrNotABuiltinAssetEvent = errors.New("not an builtin asset event") 34 ErrUnsupportedEventAction = errors.New("unsupported event action") 35 ErrChainEventAssetListERC20WithoutEnoughSignature = errors.New("chain event for erc20 asset list received with missing node signatures") 36 ErrNotBridgeChainID = errors.New("not the chainID of any bridge") 37 ) 38 39 func (app *App) processChainEvent( 40 ctx context.Context, ce *commandspb.ChainEvent, pubkey string, id string, 41 ) error { 42 if app.log.GetLevel() <= logging.DebugLevel { 43 app.log.Debug("received chain event", 44 logging.String("event", ce.String()), 45 logging.String("pubkey", pubkey), 46 ) 47 } 48 49 // first verify the event was emitted by a validator 50 if !app.top.IsValidatorVegaPubKey(pubkey) { 51 app.log.Debug("received chain event from non-validator", 52 logging.String("event", ce.String()), 53 logging.String("pubkey", pubkey), 54 ) 55 return ErrChainEventFromNonValidator 56 } 57 58 // let the topology know who was the validator that forwarded the event 59 app.top.AddForwarder(pubkey) 60 if !app.evtForwarder.Ack(ce) { 61 // there was an error, or this was already acked 62 // but that's not a big issue we just going to ignore that. 63 return nil 64 } 65 66 // OK the event was newly acknowledged, so now we need to 67 // figure out what to do with it. 68 switch c := ce.Event.(type) { 69 case *commandspb.ChainEvent_Heartbeat: 70 app.evtHeartbeat.ProcessHeartbeat( 71 c.Heartbeat.ContractAddress, 72 c.Heartbeat.SourceChainId, 73 c.Heartbeat.BlockHeight, 74 c.Heartbeat.BlockTime, 75 ) 76 return nil 77 case *commandspb.ChainEvent_StakingEvent: 78 blockNumber := c.StakingEvent.Block 79 logIndex := c.StakingEvent.Index 80 switch evt := c.StakingEvent.Action.(type) { 81 case *vgproto.StakingEvent_TotalSupply: 82 stakeTotalSupply, err := types.StakeTotalSupplyFromProto(evt.TotalSupply) 83 if err != nil { 84 return err 85 } 86 return app.stakingAccounts.ProcessStakeTotalSupply(ctx, stakeTotalSupply) 87 case *vgproto.StakingEvent_StakeDeposited: 88 stakeDeposited, err := types.StakeDepositedFromProto( 89 evt.StakeDeposited, blockNumber, logIndex, ce.TxId, id) 90 if err != nil { 91 return err 92 } 93 return app.stake.ProcessStakeDeposited(ctx, stakeDeposited) 94 case *vgproto.StakingEvent_StakeRemoved: 95 stakeRemoved, err := types.StakeRemovedFromProto( 96 evt.StakeRemoved, blockNumber, logIndex, ce.TxId, id) 97 if err != nil { 98 return err 99 } 100 return app.stake.ProcessStakeRemoved(ctx, stakeRemoved) 101 default: 102 return errors.New("unsupported StakingEvent") 103 } 104 case *commandspb.ChainEvent_Builtin: 105 // Convert from protobuf to local domain type 106 ceb, err := types.NewChainEventBuiltinFromProto(c) 107 if err != nil { 108 return err 109 } 110 return app.processChainEventBuiltinAsset(ctx, ceb, id, ce.Nonce) 111 case *commandspb.ChainEvent_Erc20: 112 // Convert from protobuf to local domain type 113 ceErc, err := types.NewChainEventERC20FromProto(c) 114 if err != nil { 115 return err 116 } 117 return app.processChainEventERC20(ctx, ceErc, id, ce.TxId) 118 case *commandspb.ChainEvent_Erc20Multisig: 119 return app.processChainEventMultisig(c, id, ce.TxId) 120 case *commandspb.ChainEvent_ContractCall: 121 callResult, err := ethcall.EthereumContractCallResultFromProto(c.ContractCall) 122 if err != nil { 123 app.log.Error("received invalid contract call", logging.Error(err), logging.String("call", c.ContractCall.String())) 124 return err 125 } 126 127 chainID := app.primaryChainID 128 if callResult.SourceChainID != nil { 129 chainID = *callResult.SourceChainID 130 } 131 132 if chainID == app.primaryChainID { 133 return app.oracles.EthereumOraclesVerifier.ProcessEthereumContractCallResult(callResult) 134 } 135 136 // use l2 then 137 return app.oracles.EthereumL2OraclesVerifier.ProcessEthereumContractCallResult(callResult) 138 139 default: 140 return ErrUnsupportedChainEvent 141 } 142 } 143 144 func (app *App) processChainEventBuiltinAsset(ctx context.Context, ce *types.ChainEventBuiltin, id string, nonce uint64) error { 145 evt := ce.Builtin // nolint 146 if evt == nil { 147 return ErrNotABuiltinAssetEvent 148 } 149 150 switch act := evt.Action.(type) { 151 case *types.BuiltinAssetEventDeposit: 152 if err := app.checkVegaAssetID(act.Deposit, "BuiltinAsset.Deposit"); err != nil { 153 return err 154 } 155 return app.banking.DepositBuiltinAsset(ctx, act.Deposit, id, nonce) 156 case *types.BuiltinAssetEventWithdrawal: 157 if err := app.checkVegaAssetID(act.Withdrawal, "BuiltinAsset.Withdrawal"); err != nil { 158 return err 159 } 160 return errors.New("unreachable") 161 default: 162 return ErrUnsupportedEventAction 163 } 164 } 165 166 func (app *App) processChainEventMultisig( 167 c *commandspb.ChainEvent_Erc20Multisig, id, txID string, 168 ) error { 169 if c.Erc20Multisig == nil { 170 return ErrNotAnERC20Event 171 } 172 173 cid, err := strconv.ParseUint(c.Erc20Multisig.ChainId, 10, 64) 174 if err != nil { 175 return err 176 } 177 178 var multisig ERC20MultiSigTopology 179 switch cid { 180 case app.primaryChainID: 181 multisig = app.primaryErc20MultiSigTopology 182 case app.secondaryChainID: 183 multisig = app.secondaryErc20MultiSigTopology 184 default: 185 return ErrNotBridgeChainID 186 } 187 188 blockNumber := c.Erc20Multisig.Block 189 logIndex := c.Erc20Multisig.Index 190 switch pevt := c.Erc20Multisig.Action.(type) { 191 case *vgproto.ERC20MultiSigEvent_SignerAdded: 192 evt, err := types.SignerEventFromSignerAddedProto( 193 pevt.SignerAdded, blockNumber, logIndex, txID, id, c.Erc20Multisig.ChainId) 194 if err != nil { 195 return err 196 } 197 198 return multisig.ProcessSignerEvent(evt) 199 case *vgproto.ERC20MultiSigEvent_SignerRemoved: 200 evt, err := types.SignerEventFromSignerRemovedProto( 201 pevt.SignerRemoved, blockNumber, logIndex, txID, id, c.Erc20Multisig.ChainId) 202 if err != nil { 203 return err 204 } 205 return multisig.ProcessSignerEvent(evt) 206 case *vgproto.ERC20MultiSigEvent_ThresholdSet: 207 evt, err := types.SignerThresholdSetEventFromProto( 208 pevt.ThresholdSet, blockNumber, logIndex, txID, id, c.Erc20Multisig.ChainId) 209 if err != nil { 210 return err 211 } 212 return multisig.ProcessThresholdEvent(evt) 213 default: 214 return errors.New("unsupported erc20 multisig event") 215 } 216 } 217 218 func (app *App) processChainEventERC20( 219 ctx context.Context, ce *types.ChainEventERC20, id, txID string, 220 ) error { 221 evt := ce.ERC20 // nolint 222 if evt == nil { 223 return ErrNotAnERC20Event 224 } 225 226 switch act := evt.Action.(type) { 227 case *types.ERC20EventAssetList: 228 act.AssetList.VegaAssetID = strings.TrimPrefix(act.AssetList.VegaAssetID, "0x") 229 if err := app.checkVegaAssetID(act.AssetList, "ERC20.AssetList"); err != nil { 230 return err 231 } 232 // now check that the notary is GO for this asset 233 _, ok := app.notary.IsSigned( 234 ctx, 235 act.AssetList.VegaAssetID, 236 commandspb.NodeSignatureKind_NODE_SIGNATURE_KIND_ASSET_NEW) 237 if !ok { 238 return ErrChainEventAssetListERC20WithoutEnoughSignature 239 } 240 return app.banking.EnableERC20(ctx, act.AssetList, id, evt.Block, evt.Index, txID, evt.ChainID) 241 case *types.ERC20EventAssetDelist: 242 return errors.New("ERC20.AssetDelist not implemented") 243 case *types.ERC20EventDeposit: 244 act.Deposit.VegaAssetID = strings.TrimPrefix(act.Deposit.VegaAssetID, "0x") 245 if err := app.checkVegaAssetID(act.Deposit, "ERC20.AssetDeposit"); err != nil { 246 return err 247 } 248 return app.banking.DepositERC20(ctx, act.Deposit, id, evt.Block, evt.Index, txID, evt.ChainID) 249 case *types.ERC20EventWithdrawal: 250 act.Withdrawal.VegaAssetID = strings.TrimPrefix(act.Withdrawal.VegaAssetID, "0x") 251 if err := app.checkVegaAssetID(act.Withdrawal, "ERC20.AssetWithdrawal"); err != nil { 252 return err 253 } 254 return app.banking.ERC20WithdrawalEvent(ctx, act.Withdrawal, evt.Block, txID, evt.ChainID) 255 case *types.ERC20EventAssetLimitsUpdated: 256 act.AssetLimitsUpdated.VegaAssetID = strings.TrimPrefix(act.AssetLimitsUpdated.VegaAssetID, "0x") 257 if err := app.checkVegaAssetID(act.AssetLimitsUpdated, "ERC20.AssetLimitsUpdated"); err != nil { 258 return err 259 } 260 return app.banking.UpdateERC20(ctx, act.AssetLimitsUpdated, id, evt.Block, evt.Index, txID, evt.ChainID) 261 case *types.ERC20EventBridgeStopped: 262 return app.banking.BridgeStopped(ctx, act.BridgeStopped, id, evt.Block, evt.Index, txID, evt.ChainID) 263 case *types.ERC20EventBridgeResumed: 264 return app.banking.BridgeResumed(ctx, act.BridgeResumed, id, evt.Block, evt.Index, txID, evt.ChainID) 265 default: 266 return ErrUnsupportedEventAction 267 } 268 } 269 270 type HasVegaAssetID interface { 271 GetVegaAssetID() string 272 } 273 274 func (app *App) checkVegaAssetID(a HasVegaAssetID, action string) error { 275 id := a.GetVegaAssetID() 276 if _, err := app.assets.Get(id); err != nil { 277 app.log.Error("invalid vega asset ID", 278 logging.String("action", action), 279 logging.Error(err), 280 logging.String("asset-id", id)) 281 return err 282 } 283 return nil 284 }