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  }