code.vegaprotocol.io/vega@v0.79.0/core/referral/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 referral
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  	"sort"
    22  
    23  	"code.vegaprotocol.io/vega/core/types"
    24  	"code.vegaprotocol.io/vega/libs/proto"
    25  	snapshotpb "code.vegaprotocol.io/vega/protos/vega/snapshot/v1"
    26  
    27  	"golang.org/x/exp/maps"
    28  )
    29  
    30  type SnapshottedEngine struct {
    31  	*Engine
    32  
    33  	pl types.Payload
    34  
    35  	stopped bool
    36  
    37  	// Keys need to be computed when the engine is instantiated as they are dynamic.
    38  	hashKeys []string
    39  	key      string
    40  }
    41  
    42  func (e *SnapshottedEngine) Namespace() types.SnapshotNamespace {
    43  	return types.ReferralProgramSnapshot
    44  }
    45  
    46  func (e *SnapshottedEngine) Keys() []string {
    47  	return e.hashKeys
    48  }
    49  
    50  func (e *SnapshottedEngine) GetState(k string) ([]byte, []types.StateProvider, error) {
    51  	state, err := e.serialise(k)
    52  	return state, nil, err
    53  }
    54  
    55  func (e *SnapshottedEngine) LoadState(_ context.Context, p *types.Payload) ([]types.StateProvider, error) {
    56  	if e.Namespace() != p.Data.Namespace() {
    57  		return nil, types.ErrInvalidSnapshotNamespace
    58  	}
    59  
    60  	switch data := p.Data.(type) {
    61  	case *types.PayloadReferralProgramState:
    62  		e.load(data)
    63  		return nil, nil
    64  	default:
    65  		return nil, types.ErrUnknownSnapshotType
    66  	}
    67  }
    68  
    69  func (e *SnapshottedEngine) Stopped() bool {
    70  	return e.stopped
    71  }
    72  
    73  func (e *SnapshottedEngine) StopSnapshots() {
    74  	e.stopped = true
    75  }
    76  
    77  func (e *SnapshottedEngine) serialise(k string) ([]byte, error) {
    78  	if e.stopped {
    79  		return nil, nil
    80  	}
    81  
    82  	switch k {
    83  	case e.key:
    84  		return e.serialiseReferralProgram()
    85  	default:
    86  		return nil, types.ErrSnapshotKeyDoesNotExist
    87  	}
    88  }
    89  
    90  func (e *SnapshottedEngine) serialiseReferralProgram() ([]byte, error) {
    91  	referralProgramData := &snapshotpb.ReferralProgramData{
    92  		LastProgramVersion: e.latestProgramVersion,
    93  		ProgramHasEnded:    e.programHasEnded,
    94  	}
    95  
    96  	payload := &snapshotpb.Payload{
    97  		Data: &snapshotpb.Payload_ReferralProgram{
    98  			ReferralProgram: referralProgramData,
    99  		},
   100  	}
   101  
   102  	if e.currentProgram != nil {
   103  		referralProgramData.CurrentProgram = e.currentProgram.IntoProto()
   104  	}
   105  	if e.newProgram != nil {
   106  		referralProgramData.NewProgram = e.newProgram.IntoProto()
   107  	}
   108  
   109  	referralProgramData.FactorByReferee = make([]*snapshotpb.FactorByReferee, 0, len(e.factorsByReferee))
   110  	for pi, rs := range e.factorsByReferee {
   111  		tv := rs.TakerVolume.Bytes()
   112  		referralProgramData.FactorByReferee = append(referralProgramData.FactorByReferee, &snapshotpb.FactorByReferee{
   113  			Party: pi.String(), DiscountFactors: rs.DiscountFactors.IntoDiscountFactorsProto(), TakerVolume: tv[:],
   114  		})
   115  	}
   116  
   117  	sort.Slice(referralProgramData.FactorByReferee, func(i, j int) bool {
   118  		return referralProgramData.FactorByReferee[i].Party < referralProgramData.FactorByReferee[j].Party
   119  	})
   120  
   121  	referralProgramData.Sets = make([]*snapshotpb.ReferralSet, 0, len(e.sets))
   122  	setIDs := maps.Keys(e.sets)
   123  
   124  	sort.SliceStable(setIDs, func(i, j int) bool {
   125  		return setIDs[i] < setIDs[j]
   126  	})
   127  
   128  	for _, setID := range setIDs {
   129  		set := e.sets[setID]
   130  		setProto := &snapshotpb.ReferralSet{
   131  			Id:        string(set.ID),
   132  			CreatedAt: set.CreatedAt.UnixNano(),
   133  			UpdatedAt: set.UpdatedAt.UnixNano(),
   134  			Referrer: &snapshotpb.Membership{
   135  				PartyId:        string(set.Referrer.PartyID),
   136  				JoinedAt:       set.Referrer.JoinedAt.UnixNano(),
   137  				StartedAtEpoch: set.Referrer.StartedAtEpoch,
   138  			},
   139  			CurrentRewardFactors:            set.CurrentRewardFactors.IntoRewardFactorsProto(),
   140  			CurrentRewardsMultiplier:        set.CurrentRewardsMultiplier.String(),
   141  			CurrentRewardsFactorsMultiplier: set.CurrentRewardsFactorMultiplier.IntoRewardFactorsProto(),
   142  		}
   143  
   144  		for _, r := range set.Referees {
   145  			setProto.Referees = append(setProto.Referees,
   146  				&snapshotpb.Membership{
   147  					PartyId:        string(r.PartyID),
   148  					JoinedAt:       r.JoinedAt.UnixNano(),
   149  					StartedAtEpoch: r.StartedAtEpoch,
   150  				},
   151  			)
   152  		}
   153  
   154  		runningVolumes, isTracked := e.referralSetsNotionalVolumes.runningVolumesBySet[set.ID]
   155  		if isTracked {
   156  			runningVolumesProto := make([]*snapshotpb.RunningVolume, 0, len(runningVolumes))
   157  			for _, volume := range runningVolumes {
   158  				var b []byte
   159  				if volume != nil {
   160  					bb := volume.value.Bytes()
   161  					b = bb[:]
   162  				}
   163  				runningVolumesProto = append(runningVolumesProto, &snapshotpb.RunningVolume{
   164  					Epoch:  volume.epoch,
   165  					Volume: b,
   166  				})
   167  			}
   168  			setProto.RunningVolumes = runningVolumesProto
   169  		}
   170  
   171  		referralProgramData.Sets = append(referralProgramData.Sets, setProto)
   172  	}
   173  
   174  	serialised, err := proto.Marshal(payload)
   175  	if err != nil {
   176  		return nil, fmt.Errorf("could not serialize referral misc payload: %w", err)
   177  	}
   178  
   179  	return serialised, nil
   180  }
   181  
   182  func (e *SnapshottedEngine) buildHashKeys() {
   183  	e.key = (&types.PayloadReferralProgramState{}).Key()
   184  	e.hashKeys = append([]string{}, e.key)
   185  }
   186  
   187  func NewSnapshottedEngine(broker Broker, timeSvc TimeService, mat MarketActivityTracker, staking StakingBalances) *SnapshottedEngine {
   188  	se := &SnapshottedEngine{
   189  		Engine:  NewEngine(broker, timeSvc, mat, staking),
   190  		pl:      types.Payload{},
   191  		stopped: false,
   192  	}
   193  
   194  	se.buildHashKeys()
   195  
   196  	return se
   197  }