code.vegaprotocol.io/vega@v0.79.0/core/banking/checkpoint.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 banking
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  	"sort"
    22  	"sync/atomic"
    23  	"time"
    24  
    25  	"code.vegaprotocol.io/vega/core/assets"
    26  	"code.vegaprotocol.io/vega/core/events"
    27  	"code.vegaprotocol.io/vega/core/types"
    28  	"code.vegaprotocol.io/vega/libs/proto"
    29  	"code.vegaprotocol.io/vega/logging"
    30  	"code.vegaprotocol.io/vega/protos/vega"
    31  	checkpoint "code.vegaprotocol.io/vega/protos/vega/checkpoint/v1"
    32  	eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1"
    33  
    34  	"github.com/emirpasic/gods/sets/treeset"
    35  	"go.uber.org/zap"
    36  )
    37  
    38  func (e *Engine) Name() types.CheckpointName {
    39  	return types.BankingCheckpoint
    40  }
    41  
    42  func (e *Engine) Checkpoint() ([]byte, error) {
    43  	msg := &checkpoint.Banking{
    44  		TransfersAtTime:              e.getScheduledTransfers(),
    45  		RecurringTransfers:           e.getRecurringTransfers(),
    46  		PrimaryBridgeState:           e.getPrimaryBridgeState(),
    47  		LastSeenPrimaryEthBlock:      e.lastSeenPrimaryEthBlock,
    48  		GovernanceTransfersAtTime:    e.getScheduledGovernanceTransfers(),
    49  		RecurringGovernanceTransfers: e.getRecurringGovernanceTransfers(),
    50  		SecondaryBridgeState:         e.getSecondaryBridgeState(),
    51  		LastSeenSecondaryEthBlock:    e.lastSeenSecondaryEthBlock,
    52  	}
    53  
    54  	msg.SeenRefs = make([]string, 0, e.seenAssetActions.Size())
    55  	iter := e.seenAssetActions.Iterator()
    56  	for iter.Next() {
    57  		msg.SeenRefs = append(msg.SeenRefs, iter.Value().(string))
    58  	}
    59  
    60  	msg.AssetActions = make([]*checkpoint.AssetAction, 0, len(e.assetActions))
    61  	for _, aa := range e.getAssetActions() {
    62  		msg.AssetActions = append(msg.AssetActions, aa.IntoProto())
    63  	}
    64  
    65  	ret, err := proto.Marshal(msg)
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  	return ret, nil
    70  }
    71  
    72  func (e *Engine) Load(ctx context.Context, data []byte) error {
    73  	b := checkpoint.Banking{}
    74  	if err := proto.Unmarshal(data, &b); err != nil {
    75  		return err
    76  	}
    77  
    78  	evts, err := e.loadScheduledTransfers(ctx, b.TransfersAtTime)
    79  	if err != nil {
    80  		return err
    81  	}
    82  
    83  	evts = append(evts, e.loadRecurringTransfers(ctx, b.RecurringTransfers)...)
    84  
    85  	evts = append(evts, e.loadScheduledGovernanceTransfers(ctx, b.GovernanceTransfersAtTime)...)
    86  	evts = append(evts, e.loadRecurringGovernanceTransfers(ctx, b.RecurringGovernanceTransfers)...)
    87  
    88  	e.loadPrimaryBridgeState(b.PrimaryBridgeState)
    89  	e.loadSecondaryBridgeState(b.SecondaryBridgeState)
    90  
    91  	e.seenAssetActions = treeset.NewWithStringComparator()
    92  	for _, v := range b.SeenRefs {
    93  		e.seenAssetActions.Add(v)
    94  	}
    95  
    96  	e.lastSeenPrimaryEthBlock = b.LastSeenPrimaryEthBlock
    97  	if e.lastSeenPrimaryEthBlock != 0 {
    98  		e.log.Info("restoring primary collateral bridge starting block", logging.Uint64("block", e.lastSeenPrimaryEthBlock))
    99  		e.ethEventSource.UpdateContractBlock(e.bridgeAddresses[e.primaryEthChainID], e.primaryEthChainID, e.lastSeenPrimaryEthBlock)
   100  	}
   101  
   102  	e.lastSeenSecondaryEthBlock = b.LastSeenSecondaryEthBlock
   103  	if e.lastSeenSecondaryEthBlock != 0 {
   104  		e.log.Info("restoring secondary collateral bridge starting block", logging.Uint64("block", e.lastSeenSecondaryEthBlock))
   105  		e.ethEventSource.UpdateContractBlock(e.bridgeAddresses[e.secondaryEthChainID], e.secondaryEthChainID, e.lastSeenSecondaryEthBlock)
   106  	}
   107  
   108  	aa := make([]*types.AssetAction, 0, len(b.AssetActions))
   109  	for _, a := range b.AssetActions {
   110  		aa = append(aa, types.AssetActionFromProto(a))
   111  	}
   112  	if err := e.loadAssetActions(aa); err != nil {
   113  		return fmt.Errorf("could not load asset actions: %w", err)
   114  	}
   115  	for _, aa := range e.assetActions {
   116  		e.witness.StartCheck(aa, e.onCheckDone, e.timeService.GetTimeNow().Add(defaultValidationDuration))
   117  	}
   118  
   119  	if len(evts) > 0 {
   120  		e.broker.SendBatch(evts)
   121  	}
   122  
   123  	return nil
   124  }
   125  
   126  func (e *Engine) loadAssetActions(aa []*types.AssetAction) error {
   127  	for _, v := range aa {
   128  		var (
   129  			err           error
   130  			asset         *assets.Asset
   131  			bridgeStopped *types.ERC20EventBridgeStopped
   132  			bridgeResumed *types.ERC20EventBridgeResumed
   133  		)
   134  
   135  		// only others action than bridge stop and resume
   136  		// have an actual asset associated
   137  		if !v.BridgeResume && !v.BridgeStopped {
   138  			asset, err = e.assets.Get(v.Asset)
   139  			if err != nil {
   140  				e.log.Panic("trying to restore an assetAction with no asset", logging.String("asset", v.Asset))
   141  			}
   142  		}
   143  
   144  		if v.BridgeStopped {
   145  			bridgeStopped = &types.ERC20EventBridgeStopped{BridgeStopped: true}
   146  		}
   147  
   148  		if v.BridgeResume {
   149  			bridgeResumed = &types.ERC20EventBridgeResumed{BridgeResumed: true}
   150  		}
   151  
   152  		// This handle old asset actions that are not associated to a chain ID
   153  		// yet, that are likely asset actions that should be associated to the
   154  		// primary bridge.
   155  		var bridgeView ERC20BridgeView
   156  		if v.ChainID == "" {
   157  			bridgeView = e.primaryBridgeView
   158  		} else {
   159  			bridgeView, err = e.bridgeViewForChainID(v.ChainID)
   160  			if err != nil {
   161  				return err
   162  			}
   163  		}
   164  
   165  		state := &atomic.Uint32{}
   166  		state.Store(v.State)
   167  		aa := &assetAction{
   168  			id:                      v.ID,
   169  			state:                   state,
   170  			blockHeight:             v.BlockNumber,
   171  			asset:                   asset,
   172  			logIndex:                v.TxIndex,
   173  			txHash:                  v.Hash,
   174  			chainID:                 v.ChainID,
   175  			builtinD:                v.BuiltinD,
   176  			erc20AL:                 v.Erc20AL,
   177  			erc20D:                  v.Erc20D,
   178  			erc20AssetLimitsUpdated: v.ERC20AssetLimitsUpdated,
   179  			erc20BridgeStopped:      bridgeStopped,
   180  			erc20BridgeResumed:      bridgeResumed,
   181  			// this is needed every time now
   182  			bridgeView: bridgeView,
   183  		}
   184  
   185  		if len(aa.getRef().Hash) == 0 {
   186  			// if we're here it means that the IntoProto code has not done its job properly for a particular asset action type
   187  			e.log.Panic("asset action has not been serialised correct and is empty", logging.String("txHash", aa.txHash))
   188  		}
   189  
   190  		e.assetActions[v.ID] = aa
   191  		// store the deposit in the deposits
   192  		if v.BuiltinD != nil {
   193  			e.deposits[v.ID] = e.newDeposit(v.ID, v.BuiltinD.PartyID, v.BuiltinD.VegaAssetID, v.BuiltinD.Amount, v.Hash)
   194  		} else if v.Erc20D != nil {
   195  			e.deposits[v.ID] = e.newDeposit(v.ID, v.Erc20D.TargetPartyID, v.Erc20D.VegaAssetID, v.Erc20D.Amount, v.Hash)
   196  		}
   197  	}
   198  	return nil
   199  }
   200  
   201  func (e *Engine) loadPrimaryBridgeState(state *checkpoint.BridgeState) {
   202  	// this would eventually be nil if we restore from a checkpoint
   203  	// which have been produce from an old version of the core.
   204  	// we set it to active by default in the case
   205  	if state == nil {
   206  		e.primaryBridgeState = &bridgeState{
   207  			active: true,
   208  		}
   209  		return
   210  	}
   211  
   212  	e.primaryBridgeState = &bridgeState{
   213  		active:   state.Active,
   214  		block:    state.BlockHeight,
   215  		logIndex: state.LogIndex,
   216  	}
   217  }
   218  
   219  func (e *Engine) loadSecondaryBridgeState(state *checkpoint.BridgeState) {
   220  	// this would eventually be nil if we restore from a checkpoint
   221  	// which have been produce from an old version of the core.
   222  	// we set it to active by default in the case
   223  	if state == nil {
   224  		e.secondaryBridgeState = &bridgeState{
   225  			active: true,
   226  		}
   227  		return
   228  	}
   229  
   230  	e.secondaryBridgeState = &bridgeState{
   231  		active:   state.Active,
   232  		block:    state.BlockHeight,
   233  		logIndex: state.LogIndex,
   234  	}
   235  }
   236  
   237  func (e *Engine) loadScheduledGovernanceTransfers(ctx context.Context, r []*checkpoint.ScheduledGovernanceTransferAtTime) []events.Event {
   238  	evts := []events.Event{}
   239  	for _, v := range r {
   240  		transfers := make([]*types.GovernanceTransfer, 0, len(v.Transfers))
   241  		for _, g := range v.Transfers {
   242  			transfer := types.GovernanceTransferFromProto(g)
   243  			transfers = append(transfers, transfer)
   244  			evts = append(evts, events.NewGovTransferFundsEvent(ctx, transfer, transfer.Config.MaxAmount, e.getGovGameID(transfer)))
   245  		}
   246  		e.scheduledGovernanceTransfers[v.DeliverOn] = transfers
   247  	}
   248  	return evts
   249  }
   250  
   251  func (e *Engine) loadScheduledTransfers(
   252  	ctx context.Context, r []*checkpoint.ScheduledTransferAtTime,
   253  ) ([]events.Event, error) {
   254  	evts := []events.Event{}
   255  	for _, v := range r {
   256  		transfers := make([]scheduledTransfer, 0, len(v.Transfers))
   257  		for _, v := range v.Transfers {
   258  			transfer, err := scheduledTransferFromProto(v)
   259  			if err != nil {
   260  				return nil, err
   261  			}
   262  			evts = append(evts, events.NewOneOffTransferFundsEvent(ctx, transfer.oneoff))
   263  			transfers = append(transfers, transfer)
   264  		}
   265  		e.scheduledTransfers[v.DeliverOn] = transfers
   266  	}
   267  
   268  	return evts, nil
   269  }
   270  
   271  func (e *Engine) loadRecurringTransfers(
   272  	ctx context.Context, r *checkpoint.RecurringTransfers,
   273  ) []events.Event {
   274  	evts := []events.Event{}
   275  	e.nextMetricUpdate = time.Unix(0, r.NextMetricUpdate)
   276  	for _, v := range r.RecurringTransfers {
   277  		transfer := types.RecurringTransferFromEvent(v)
   278  		e.recurringTransfers = append(e.recurringTransfers, transfer)
   279  		e.recurringTransfersMap[transfer.ID] = transfer
   280  		// reload the dispatch strategy to the hash cache
   281  		if transfer.DispatchStrategy != nil {
   282  			// reset defaults for new dispatch strategy params:
   283  			if transfer.DispatchStrategy.EntityScope == vega.EntityScope_ENTITY_SCOPE_UNSPECIFIED {
   284  				e.applyMigrationDefaults(transfer.DispatchStrategy)
   285  			}
   286  			e.registerDispatchStrategy(transfer.DispatchStrategy)
   287  		}
   288  		evts = append(evts, events.NewRecurringTransferFundsEvent(ctx, transfer, e.getGameID(transfer)))
   289  	}
   290  	return evts
   291  }
   292  
   293  func (e *Engine) applyMigrationDefaults(ds *vega.DispatchStrategy) {
   294  	ds.EntityScope = vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS
   295  	ds.LockPeriod = 0
   296  	ds.WindowLength = 1
   297  	ds.DistributionStrategy = vega.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA
   298  }
   299  
   300  func (e *Engine) loadRecurringGovernanceTransfers(ctx context.Context, transfers []*checkpoint.GovernanceTransfer) []events.Event {
   301  	evts := []events.Event{}
   302  	for _, v := range transfers {
   303  		transfer := types.GovernanceTransferFromProto(v)
   304  		e.recurringGovernanceTransfers = append(e.recurringGovernanceTransfers, transfer)
   305  		e.recurringGovernanceTransfersMap[transfer.ID] = transfer
   306  		if transfer.Config.RecurringTransferConfig.DispatchStrategy != nil {
   307  			e.registerDispatchStrategy(transfer.Config.RecurringTransferConfig.DispatchStrategy)
   308  		}
   309  		evts = append(evts, events.NewGovTransferFundsEvent(ctx, transfer, transfer.Config.MaxAmount, e.getGovGameID(transfer)))
   310  	}
   311  	return evts
   312  }
   313  
   314  func (e *Engine) getPrimaryBridgeState() *checkpoint.BridgeState {
   315  	return &checkpoint.BridgeState{
   316  		Active:      e.primaryBridgeState.active,
   317  		BlockHeight: e.primaryBridgeState.block,
   318  		LogIndex:    e.primaryBridgeState.logIndex,
   319  	}
   320  }
   321  
   322  func (e *Engine) getSecondaryBridgeState() *checkpoint.BridgeState {
   323  	return &checkpoint.BridgeState{
   324  		Active:      e.secondaryBridgeState.active,
   325  		BlockHeight: e.secondaryBridgeState.block,
   326  		LogIndex:    e.secondaryBridgeState.logIndex,
   327  	}
   328  }
   329  
   330  func (e *Engine) getRecurringTransfers() *checkpoint.RecurringTransfers {
   331  	out := &checkpoint.RecurringTransfers{
   332  		RecurringTransfers: make([]*eventspb.Transfer, 0, len(e.recurringTransfers)),
   333  	}
   334  
   335  	for _, v := range e.recurringTransfers {
   336  		out.RecurringTransfers = append(out.RecurringTransfers, v.IntoEvent(nil, e.getGameID(v)))
   337  	}
   338  	out.NextMetricUpdate = e.nextMetricUpdate.UnixNano()
   339  	return out
   340  }
   341  
   342  func (e *Engine) getRecurringGovernanceTransfers() []*checkpoint.GovernanceTransfer {
   343  	out := make([]*checkpoint.GovernanceTransfer, 0, len(e.recurringGovernanceTransfers))
   344  	for _, v := range e.recurringGovernanceTransfers {
   345  		out = append(out, v.IntoProto())
   346  	}
   347  	return out
   348  }
   349  
   350  func (e *Engine) getScheduledTransfers() []*checkpoint.ScheduledTransferAtTime {
   351  	out := make([]*checkpoint.ScheduledTransferAtTime, 0, len(e.scheduledTransfers))
   352  
   353  	for k, v := range e.scheduledTransfers {
   354  		transfers := make([]*checkpoint.ScheduledTransfer, 0, len(v))
   355  		for _, v := range v {
   356  			transfers = append(transfers, v.ToProto())
   357  		}
   358  		out = append(out, &checkpoint.ScheduledTransferAtTime{DeliverOn: k, Transfers: transfers})
   359  	}
   360  
   361  	sort.SliceStable(out, func(i, j int) bool { return out[i].DeliverOn < out[j].DeliverOn })
   362  
   363  	return out
   364  }
   365  
   366  func (e *Engine) getScheduledGovernanceTransfers() []*checkpoint.ScheduledGovernanceTransferAtTime {
   367  	out := make([]*checkpoint.ScheduledGovernanceTransferAtTime, 0, len(e.scheduledGovernanceTransfers))
   368  
   369  	for k, v := range e.scheduledGovernanceTransfers {
   370  		transfers := make([]*checkpoint.GovernanceTransfer, 0, len(v))
   371  		for _, v := range v {
   372  			transfers = append(transfers, v.IntoProto())
   373  		}
   374  		out = append(out, &checkpoint.ScheduledGovernanceTransferAtTime{DeliverOn: k, Transfers: transfers})
   375  	}
   376  	sort.SliceStable(out, func(i, j int) bool { return out[i].DeliverOn < out[j].DeliverOn })
   377  	return out
   378  }
   379  
   380  func (e *Engine) getAssetActions() []*types.AssetAction {
   381  	aa := make([]*types.AssetAction, 0, len(e.assetActions))
   382  	for _, v := range e.assetActions {
   383  		// this is optional as bridge action don't have one
   384  		var assetID string
   385  		if v.asset != nil {
   386  			assetID = v.asset.ToAssetType().ID
   387  		}
   388  
   389  		var bridgeStopped bool
   390  		if v.erc20BridgeStopped != nil {
   391  			bridgeStopped = true
   392  		}
   393  
   394  		var bridgeResumed bool
   395  		if v.erc20BridgeResumed != nil {
   396  			bridgeResumed = true
   397  		}
   398  
   399  		action := types.AssetAction{
   400  			ID:                      v.id,
   401  			State:                   v.state.Load(),
   402  			BlockNumber:             v.blockHeight,
   403  			Asset:                   assetID,
   404  			TxIndex:                 v.logIndex,
   405  			Hash:                    v.txHash,
   406  			ChainID:                 v.chainID,
   407  			BuiltinD:                v.builtinD,
   408  			Erc20AL:                 v.erc20AL,
   409  			Erc20D:                  v.erc20D,
   410  			ERC20AssetLimitsUpdated: v.erc20AssetLimitsUpdated,
   411  			BridgeStopped:           bridgeStopped,
   412  			BridgeResume:            bridgeResumed,
   413  		}
   414  
   415  		e.log.Info("getAssetActions",
   416  			zap.Any("action", fmt.Sprintf("%+v", action)),
   417  			zap.String("ref", v.getRef().Hash),
   418  		)
   419  
   420  		aa = append(aa, &action)
   421  	}
   422  
   423  	sort.SliceStable(aa, func(i, j int) bool { return aa[i].ID < aa[j].ID })
   424  	return aa
   425  }