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 }