code.vegaprotocol.io/vega@v0.79.0/core/volumediscount/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 volumediscount 17 18 import ( 19 "context" 20 "sort" 21 "strings" 22 23 "code.vegaprotocol.io/vega/core/types" 24 "code.vegaprotocol.io/vega/libs/num" 25 "code.vegaprotocol.io/vega/libs/proto" 26 vegapb "code.vegaprotocol.io/vega/protos/vega" 27 snapshotpb "code.vegaprotocol.io/vega/protos/vega/snapshot/v1" 28 29 "golang.org/x/exp/slices" 30 ) 31 32 var ( 33 key = (&types.PayloadVolumeDiscountProgram{}).Key() 34 hashKeys = []string{key} 35 ) 36 37 type SnapshottedEngine struct { 38 *Engine 39 40 pl types.Payload 41 } 42 43 func (e *SnapshottedEngine) Namespace() types.SnapshotNamespace { 44 return types.VolumeDiscountProgramSnapshot 45 } 46 47 func (e *SnapshottedEngine) Keys() []string { 48 return hashKeys 49 } 50 51 func (e *SnapshottedEngine) GetState(k string) ([]byte, []types.StateProvider, error) { 52 state, err := e.serialise(k) 53 return state, nil, err 54 } 55 56 func (e *Engine) loadCurrentProgramFromSnapshot(program *vegapb.VolumeDiscountProgram) { 57 if program == nil { 58 e.currentProgram = nil 59 return 60 } 61 62 e.currentProgram = types.NewVolumeDiscountProgramFromProto(program) 63 } 64 65 func (e *Engine) loadNewProgramFromSnapshot(program *vegapb.VolumeDiscountProgram) { 66 if program == nil { 67 e.newProgram = nil 68 return 69 } 70 71 e.newProgram = types.NewVolumeDiscountProgramFromProto(program) 72 } 73 74 func (e *SnapshottedEngine) restore(vdp *snapshotpb.VolumeDiscountProgram) error { 75 e.latestProgramVersion = vdp.LastProgramVersion 76 e.programHasEnded = vdp.ProgramHasEnded 77 e.loadCurrentProgramFromSnapshot(vdp.CurrentProgram) 78 e.loadNewProgramFromSnapshot(vdp.NewProgram) 79 for _, v := range vdp.Parties { 80 e.parties[types.PartyID(v)] = struct{}{} 81 } 82 for _, pv := range vdp.AveragePartyVolume { 83 volume, err := num.UnmarshalBinaryDecimal(pv.Volume) 84 if err != nil { 85 return err 86 } 87 e.avgVolumePerParty[types.PartyID(pv.Party)] = volume 88 } 89 e.epochDataIndex = int(vdp.EpochDataIndex) 90 for i, epv := range vdp.EpochPartyVolumes { 91 if len(epv.PartyVolume) > 0 { 92 volumes := map[types.PartyID]*num.Uint{} 93 for _, pv := range epv.PartyVolume { 94 var v *num.Uint 95 if len(pv.Volume) > 0 { 96 v = num.UintFromBytes(pv.Volume) 97 } 98 volumes[types.PartyID(pv.Party)] = v 99 } 100 e.epochData[i] = volumes 101 } 102 } 103 104 for _, stats := range vdp.FactorsByParty { 105 factors := types.FactorsFromDiscountFactorsWithDefault(stats.DiscountFactors, stats.DiscountFactor) 106 e.factorsByParty[types.PartyID(stats.Party)] = types.VolumeDiscountStats{ 107 DiscountFactors: factors, 108 } 109 } 110 111 return nil 112 } 113 114 func (e *SnapshottedEngine) LoadState(_ context.Context, p *types.Payload) ([]types.StateProvider, error) { 115 if e.Namespace() != p.Data.Namespace() { 116 return nil, types.ErrInvalidSnapshotNamespace 117 } 118 119 switch data := p.Data.(type) { 120 case *types.PayloadVolumeDiscountProgram: 121 return nil, e.restore(data.VolumeDiscountProgram) 122 default: 123 return nil, types.ErrUnknownSnapshotType 124 } 125 } 126 127 func (e *SnapshottedEngine) Stopped() bool { 128 return false 129 } 130 131 func (e *SnapshottedEngine) serialise(k string) ([]byte, error) { 132 switch k { 133 case key: 134 return e.serialiseDiscountVolumeProgram() 135 default: 136 return nil, types.ErrSnapshotKeyDoesNotExist 137 } 138 } 139 140 func (e *SnapshottedEngine) serialiseDiscountVolumeProgram() ([]byte, error) { 141 parties := make([]string, 0, len(e.parties)) 142 for pi := range e.parties { 143 parties = append(parties, string(pi)) 144 } 145 sort.Strings(parties) 146 147 avgPartyVolumes := make([]*snapshotpb.PartyVolume, 0, len(e.avgVolumePerParty)) 148 for pi, d := range e.avgVolumePerParty { 149 b, _ := d.MarshalBinary() 150 avgPartyVolumes = append(avgPartyVolumes, &snapshotpb.PartyVolume{Party: string(pi), Volume: b}) 151 } 152 sort.Slice(avgPartyVolumes, func(i, j int) bool { 153 return avgPartyVolumes[i].Party < avgPartyVolumes[j].Party 154 }) 155 156 epochData := make([]*snapshotpb.EpochPartyVolumes, 0, len(e.epochData)) 157 for _, epv := range e.epochData { 158 ed := &snapshotpb.EpochPartyVolumes{} 159 if len(epv) > 0 { 160 ed.PartyVolume = make([]*snapshotpb.PartyVolume, 0, len(epv)) 161 for pi, u := range epv { 162 var b []byte 163 if u != nil { 164 bb := u.Bytes() 165 b = bb[:] 166 } 167 ed.PartyVolume = append(ed.PartyVolume, &snapshotpb.PartyVolume{Party: string(pi), Volume: b}) 168 } 169 sort.Slice(ed.PartyVolume, func(i, j int) bool { 170 return ed.PartyVolume[i].Party < ed.PartyVolume[j].Party 171 }) 172 } 173 epochData = append(epochData, ed) 174 } 175 176 stats := make([]*snapshotpb.VolumeDiscountStats, 0, len(e.factorsByParty)) 177 for partyID, discountStats := range e.factorsByParty { 178 stats = append(stats, &snapshotpb.VolumeDiscountStats{ 179 Party: partyID.String(), 180 DiscountFactors: discountStats.DiscountFactors.IntoDiscountFactorsProto(), 181 }) 182 } 183 slices.SortStableFunc(stats, func(a, b *snapshotpb.VolumeDiscountStats) int { 184 return strings.Compare(a.Party, b.Party) 185 }) 186 187 payload := &snapshotpb.Payload{ 188 Data: &snapshotpb.Payload_VolumeDiscountProgram{ 189 VolumeDiscountProgram: &snapshotpb.VolumeDiscountProgram{ 190 CurrentProgram: e.getProgram(e.currentProgram), 191 NewProgram: e.getProgram(e.newProgram), 192 Parties: parties, 193 EpochDataIndex: uint64(e.epochDataIndex), 194 AveragePartyVolume: avgPartyVolumes, 195 EpochPartyVolumes: epochData, 196 FactorsByParty: stats, 197 LastProgramVersion: e.latestProgramVersion, 198 ProgramHasEnded: e.programHasEnded, 199 }, 200 }, 201 } 202 return proto.Marshal(payload) 203 } 204 205 func (e *SnapshottedEngine) getProgram(program *types.VolumeDiscountProgram) *vegapb.VolumeDiscountProgram { 206 if program == nil { 207 return nil 208 } 209 return program.IntoProto() 210 } 211 212 func NewSnapshottedEngine(broker Broker, mat MarketActivityTracker) *SnapshottedEngine { 213 se := &SnapshottedEngine{ 214 Engine: New(broker, mat), 215 pl: types.Payload{}, 216 } 217 218 return se 219 }