code.vegaprotocol.io/vega@v0.79.0/core/volumerebate/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 volumerebate 17 18 import ( 19 "context" 20 "fmt" 21 "sort" 22 "strings" 23 24 "code.vegaprotocol.io/vega/core/types" 25 "code.vegaprotocol.io/vega/libs/num" 26 "code.vegaprotocol.io/vega/libs/proto" 27 vegapb "code.vegaprotocol.io/vega/protos/vega" 28 snapshotpb "code.vegaprotocol.io/vega/protos/vega/snapshot/v1" 29 30 "golang.org/x/exp/slices" 31 ) 32 33 var ( 34 key = (&types.PayloadVolumeRebateProgram{}).Key() 35 hashKeys = []string{key} 36 ) 37 38 type SnapshottedEngine struct { 39 *Engine 40 41 pl types.Payload 42 } 43 44 func (e *SnapshottedEngine) Namespace() types.SnapshotNamespace { 45 return types.VolumeRebateProgramSnapshot 46 } 47 48 func (e *SnapshottedEngine) Keys() []string { 49 return hashKeys 50 } 51 52 func (e *SnapshottedEngine) GetState(k string) ([]byte, []types.StateProvider, error) { 53 state, err := e.serialise(k) 54 return state, nil, err 55 } 56 57 func (e *Engine) loadCurrentProgramFromSnapshot(program *vegapb.VolumeRebateProgram) { 58 if program == nil { 59 e.currentProgram = nil 60 return 61 } 62 63 e.currentProgram = types.NewVolumeRebateProgramFromProto(program) 64 } 65 66 func (e *Engine) loadNewProgramFromSnapshot(program *vegapb.VolumeRebateProgram) { 67 if program == nil { 68 e.newProgram = nil 69 return 70 } 71 72 e.newProgram = types.NewVolumeRebateProgramFromProto(program) 73 } 74 75 func (e *SnapshottedEngine) restore(vdp *snapshotpb.VolumeRebateProgram) error { 76 e.latestProgramVersion = vdp.LastProgramVersion 77 e.programHasEnded = vdp.ProgramHasEnded 78 e.loadCurrentProgramFromSnapshot(vdp.CurrentProgram) 79 e.loadNewProgramFromSnapshot(vdp.NewProgram) 80 for _, v := range vdp.Parties { 81 e.parties[v] = struct{}{} 82 } 83 for _, pv := range vdp.PartyRebateData { 84 fraction, err := num.DecimalFromString(pv.Fraction) 85 if err != nil { 86 return err 87 } 88 e.fractionPerParty[pv.Party] = fraction 89 90 makerFee, overflow := num.UintFromString(pv.MakerFeeReceived, 10) 91 if overflow { 92 return err 93 } 94 e.makerFeesReceivedInWindowPerParty[pv.Party] = makerFee 95 } 96 97 for _, stats := range vdp.FactorsByParty { 98 factor, err := num.DecimalFromString(stats.RebateFactor) 99 if err != nil { 100 return fmt.Errorf("could not parse string %q into decimal: %w", stats.RebateFactor, err) 101 } 102 e.factorsByParty[types.PartyID(stats.Party)] = types.VolumeRebateStats{ 103 RebateFactor: factor, 104 } 105 } 106 107 return nil 108 } 109 110 func (e *SnapshottedEngine) LoadState(_ context.Context, p *types.Payload) ([]types.StateProvider, error) { 111 if e.Namespace() != p.Data.Namespace() { 112 return nil, types.ErrInvalidSnapshotNamespace 113 } 114 115 switch data := p.Data.(type) { 116 case *types.PayloadVolumeRebateProgram: 117 return nil, e.restore(data.VolumeRebateProgram) 118 default: 119 return nil, types.ErrUnknownSnapshotType 120 } 121 } 122 123 func (e *SnapshottedEngine) Stopped() bool { 124 return false 125 } 126 127 func (e *SnapshottedEngine) serialise(k string) ([]byte, error) { 128 switch k { 129 case key: 130 return e.serialiseRebateVolumeProgram() 131 default: 132 return nil, types.ErrSnapshotKeyDoesNotExist 133 } 134 } 135 136 func (e *SnapshottedEngine) serialiseRebateVolumeProgram() ([]byte, error) { 137 parties := make([]string, 0, len(e.parties)) 138 for pi := range e.parties { 139 parties = append(parties, pi) 140 } 141 sort.Strings(parties) 142 143 partyData := make([]*snapshotpb.PartyRebateData, 0, len(e.fractionPerParty)) 144 for pi, d := range e.fractionPerParty { 145 partyData = append(partyData, &snapshotpb.PartyRebateData{ 146 Party: pi, 147 Fraction: d.String(), 148 MakerFeeReceived: e.makerFeesReceivedInWindowPerParty[pi].String(), 149 }) 150 } 151 sort.Slice(partyData, func(i, j int) bool { 152 return partyData[i].Party < partyData[j].Party 153 }) 154 155 stats := make([]*snapshotpb.VolumeRebateStats, 0, len(e.factorsByParty)) 156 for partyID, rebateStats := range e.factorsByParty { 157 stats = append(stats, &snapshotpb.VolumeRebateStats{ 158 Party: partyID.String(), 159 RebateFactor: rebateStats.RebateFactor.String(), 160 }) 161 } 162 slices.SortStableFunc(stats, func(a, b *snapshotpb.VolumeRebateStats) int { 163 return strings.Compare(a.Party, b.Party) 164 }) 165 166 payload := &snapshotpb.Payload{ 167 Data: &snapshotpb.Payload_VolumeRebateProgram{ 168 VolumeRebateProgram: &snapshotpb.VolumeRebateProgram{ 169 CurrentProgram: e.getProgram(e.currentProgram), 170 NewProgram: e.getProgram(e.newProgram), 171 Parties: parties, 172 PartyRebateData: partyData, 173 FactorsByParty: stats, 174 LastProgramVersion: e.latestProgramVersion, 175 ProgramHasEnded: e.programHasEnded, 176 }, 177 }, 178 } 179 return proto.Marshal(payload) 180 } 181 182 func (e *SnapshottedEngine) getProgram(program *types.VolumeRebateProgram) *vegapb.VolumeRebateProgram { 183 if program == nil { 184 return nil 185 } 186 return program.IntoProto() 187 } 188 189 func NewSnapshottedEngine(broker Broker, mat MarketActivityTracker) *SnapshottedEngine { 190 se := &SnapshottedEngine{ 191 Engine: New(broker, mat), 192 pl: types.Payload{}, 193 } 194 195 return se 196 }