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  }