code.vegaprotocol.io/vega@v0.79.0/core/assets/snapshot.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 assets
    17  
    18  import (
    19  	"context"
    20  
    21  	"code.vegaprotocol.io/vega/core/types"
    22  	vgcontext "code.vegaprotocol.io/vega/libs/context"
    23  	"code.vegaprotocol.io/vega/libs/proto"
    24  	"code.vegaprotocol.io/vega/logging"
    25  )
    26  
    27  var (
    28  	activeKey         = (&types.PayloadActiveAssets{}).Key()
    29  	pendingKey        = (&types.PayloadPendingAssets{}).Key()
    30  	pendingUpdatesKey = (&types.PayloadPendingAssetUpdates{}).Key()
    31  
    32  	hashKeys = []string{
    33  		activeKey,
    34  		pendingKey,
    35  		pendingUpdatesKey,
    36  	}
    37  )
    38  
    39  type assetsSnapshotState struct {
    40  	serialisedActive         []byte
    41  	serialisedPending        []byte
    42  	serialisedPendingUpdates []byte
    43  }
    44  
    45  func (s *Service) Namespace() types.SnapshotNamespace {
    46  	return types.AssetsSnapshot
    47  }
    48  
    49  func (s *Service) Keys() []string {
    50  	return hashKeys
    51  }
    52  
    53  func (s *Service) Stopped() bool {
    54  	return false
    55  }
    56  
    57  func (s *Service) serialiseActive() ([]byte, error) {
    58  	enabled := s.GetEnabledAssets()
    59  	payload := types.Payload{
    60  		Data: &types.PayloadActiveAssets{
    61  			ActiveAssets: &types.ActiveAssets{
    62  				Assets: enabled,
    63  			},
    64  		},
    65  	}
    66  	return proto.Marshal(payload.IntoProto())
    67  }
    68  
    69  func (s *Service) serialisePending() ([]byte, error) {
    70  	pending := s.getPendingAssets()
    71  	payload := types.Payload{
    72  		Data: &types.PayloadPendingAssets{
    73  			PendingAssets: &types.PendingAssets{
    74  				Assets: pending,
    75  			},
    76  		},
    77  	}
    78  
    79  	return proto.Marshal(payload.IntoProto())
    80  }
    81  
    82  func (s *Service) serialisePendingUpdates() ([]byte, error) {
    83  	pendingUpdates := s.getPendingAssetUpdates()
    84  	payload := types.Payload{
    85  		Data: &types.PayloadPendingAssetUpdates{
    86  			PendingAssetUpdates: &types.PendingAssetUpdates{
    87  				Assets: pendingUpdates,
    88  			},
    89  		},
    90  	}
    91  
    92  	return proto.Marshal(payload.IntoProto())
    93  }
    94  
    95  func (s *Service) serialiseK(serialFunc func() ([]byte, error), dataField *[]byte) ([]byte, error) {
    96  	data, err := serialFunc()
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  	*dataField = data
   101  	return data, nil
   102  }
   103  
   104  func (s *Service) serialise(k string) ([]byte, error) {
   105  	switch k {
   106  	case activeKey:
   107  		return s.serialiseK(s.serialiseActive, &s.ass.serialisedActive)
   108  	case pendingKey:
   109  		return s.serialiseK(s.serialisePending, &s.ass.serialisedPending)
   110  	case pendingUpdatesKey:
   111  		return s.serialiseK(s.serialisePendingUpdates, &s.ass.serialisedPendingUpdates)
   112  	default:
   113  		return nil, types.ErrSnapshotKeyDoesNotExist
   114  	}
   115  }
   116  
   117  func (s *Service) GetState(k string) ([]byte, []types.StateProvider, error) {
   118  	state, err := s.serialise(k)
   119  	return state, nil, err
   120  }
   121  
   122  func (s *Service) LoadState(ctx context.Context, p *types.Payload) ([]types.StateProvider, error) {
   123  	if s.Namespace() != p.Data.Namespace() {
   124  		return nil, types.ErrInvalidSnapshotNamespace
   125  	}
   126  
   127  	switch pl := p.Data.(type) {
   128  	case *types.PayloadActiveAssets:
   129  		return nil, s.restoreActive(ctx, pl.ActiveAssets, p)
   130  	case *types.PayloadPendingAssets:
   131  		return nil, s.restorePending(ctx, pl.PendingAssets, p)
   132  	case *types.PayloadPendingAssetUpdates:
   133  		return nil, s.restorePendingUpdates(ctx, pl.PendingAssetUpdates, p)
   134  	default:
   135  		return nil, types.ErrUnknownSnapshotType
   136  	}
   137  }
   138  
   139  func (s *Service) restoreActive(ctx context.Context, active *types.ActiveAssets, p *types.Payload) error {
   140  	var err error
   141  	s.assets = map[string]*Asset{}
   142  	for _, p := range active.Assets {
   143  		s.applyMigrations(ctx, p)
   144  		if _, err = s.NewAsset(ctx, p.ID, p.Details); err != nil {
   145  			return err
   146  		}
   147  
   148  		pa, _ := s.Get(p.ID)
   149  
   150  		// at this point asset is always valid because we've loaded from a snapshot and have validated it when it was proposed
   151  		pa.SetValid()
   152  
   153  		if err = s.Enable(ctx, p.ID); err != nil {
   154  			return err
   155  		}
   156  	}
   157  	s.ass.serialisedActive, err = proto.Marshal(p.IntoProto())
   158  
   159  	return err
   160  }
   161  
   162  func (s *Service) restorePending(ctx context.Context, pending *types.PendingAssets, p *types.Payload) error {
   163  	var err error
   164  	s.pendingAssets = map[string]*Asset{}
   165  	for _, p := range pending.Assets {
   166  		s.applyMigrations(ctx, p)
   167  		assetID, err := s.NewAsset(ctx, p.ID, p.Details)
   168  		if err != nil {
   169  			return err
   170  		}
   171  
   172  		if p.Status == types.AssetStatusPendingListing {
   173  			s.SetPendingListing(ctx, assetID)
   174  		}
   175  	}
   176  
   177  	s.ass.serialisedPending, err = proto.Marshal(p.IntoProto())
   178  
   179  	return err
   180  }
   181  
   182  func (s *Service) restorePendingUpdates(ctx context.Context, pending *types.PendingAssetUpdates, p *types.Payload) error {
   183  	var err error
   184  	s.pendingAssetUpdates = map[string]*Asset{}
   185  	for _, p := range pending.Assets {
   186  		s.applyMigrations(ctx, p)
   187  		if err = s.StageAssetUpdate(p); err != nil {
   188  			return err
   189  		}
   190  	}
   191  	s.ass.serialisedPendingUpdates, err = proto.Marshal(p.IntoProto())
   192  
   193  	return err
   194  }
   195  
   196  func (s *Service) OnStateLoaded(ctx context.Context) error {
   197  	if !vgcontext.InProgressUpgrade(ctx) || s.isValidator {
   198  		return nil
   199  	}
   200  
   201  	// note that non-validator nodes do not know the chain-id for the bridges until the network parameters are propagated, but also *validator* nodes need to
   202  	// restore assets before the network parameters. So for the non-validator nodes only, we have to do the migration to include the chain-id here, after everything
   203  	// else is restored.
   204  	s.log.Info("migrating chain-id in existing active assets for non-validator nodes")
   205  	for k, a := range s.assets {
   206  		eth, ok := a.ERC20()
   207  		if !ok || eth.ChainID() != "" {
   208  			continue
   209  		}
   210  		s.log.Info("setting chain-id for active asset",
   211  			logging.String("asset-id", k),
   212  			logging.String("chain-id", s.primaryEthChainID),
   213  		)
   214  		eth.SetChainID(s.primaryEthChainID)
   215  	}
   216  
   217  	s.log.Info("migrating chain-id in existing pending assets for non-validator nodes")
   218  	for k, p := range s.pendingAssets {
   219  		eth, ok := p.ERC20()
   220  		if !ok || eth.ChainID() != "" {
   221  			continue
   222  		}
   223  		s.log.Info("setting chain-id for pending asset",
   224  			logging.String("asset-id", k),
   225  			logging.String("chain-id", s.primaryEthChainID),
   226  		)
   227  		eth.SetChainID(s.primaryEthChainID)
   228  	}
   229  
   230  	s.log.Info("migrating chain-id in existing update-pending assets for non-validator nodes")
   231  	for k, pu := range s.pendingAssetUpdates {
   232  		eth, ok := pu.ERC20()
   233  		if !ok || eth.ChainID() != "" {
   234  			continue
   235  		}
   236  		s.log.Info("setting chain-id for pending update asset",
   237  			logging.String("asset-id", k),
   238  			logging.String("chain-id", s.primaryEthChainID),
   239  		)
   240  		eth.SetChainID(s.primaryEthChainID)
   241  	}
   242  
   243  	return nil
   244  }
   245  
   246  func (s *Service) applyMigrations(ctx context.Context, p *types.Asset) {
   247  	// TODO when we know what versions we are upgrading from we can put in a upgrade from tag
   248  	if vgcontext.InProgressUpgrade(ctx) && s.isValidator {
   249  		// Prior the introduction of the second bridge, existing assets did not track
   250  		// the chain ID they originated from. So, when loaded, assets without a chain
   251  		// ID are automatically considered to originate from Ethereum Mainnet.
   252  		if erc20 := p.Details.GetERC20(); erc20 != nil && erc20.ChainID == "" {
   253  			s.log.Info("migrating chain-id in existin asset for validator nodes",
   254  				logging.String("asset-id", p.ID),
   255  				logging.String("chain-id", s.primaryEthChainID),
   256  			)
   257  			erc20.ChainID = s.primaryEthChainID
   258  		}
   259  	}
   260  }