code.vegaprotocol.io/vega@v0.79.0/core/statevar/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 statevar
    17  
    18  import (
    19  	"context"
    20  	"errors"
    21  	"sort"
    22  	"time"
    23  
    24  	"code.vegaprotocol.io/vega/core/types"
    25  	"code.vegaprotocol.io/vega/core/types/statevar"
    26  	"code.vegaprotocol.io/vega/libs/proto"
    27  	"code.vegaprotocol.io/vega/logging"
    28  	snapshot "code.vegaprotocol.io/vega/protos/vega/snapshot/v1"
    29  )
    30  
    31  var (
    32  	key                        = (&types.PayloadFloatingPointConsensus{}).Key()
    33  	ErrSnapshotKeyDoesNotExist = errors.New("unknown key for floating point consensus snapshot")
    34  	hashKeys                   = []string{key}
    35  )
    36  
    37  type snapshotState struct {
    38  	serialised []byte
    39  }
    40  
    41  func (e *Engine) Namespace() types.SnapshotNamespace {
    42  	return types.FloatingPointConsensusSnapshot
    43  }
    44  
    45  func (e *Engine) Keys() []string {
    46  	return hashKeys
    47  }
    48  
    49  func (e *Engine) Stopped() bool {
    50  	return false
    51  }
    52  
    53  func (e *Engine) serialiseNextTimeTrigger() []*snapshot.NextTimeTrigger {
    54  	e.log.Debug("serialising statevar snapshot", logging.Int("n_triggers", len(e.stateVarToNextCalc)))
    55  	timeTriggers := make([]*snapshot.NextTimeTrigger, 0, len(e.stateVarToNextCalc))
    56  
    57  	ids := make([]string, 0, len(e.stateVarToNextCalc))
    58  	for id := range e.stateVarToNextCalc {
    59  		ids = append(ids, id)
    60  	}
    61  	sort.Strings(ids)
    62  
    63  	for _, id := range ids {
    64  		if sv, ok := e.stateVars[id]; ok {
    65  			data := &snapshot.NextTimeTrigger{
    66  				Asset:       sv.asset,
    67  				Market:      sv.market,
    68  				Id:          id,
    69  				NextTrigger: e.stateVarToNextCalc[id].UnixNano(),
    70  			}
    71  			timeTriggers = append(timeTriggers, data)
    72  		}
    73  	}
    74  
    75  	return timeTriggers
    76  }
    77  
    78  func mapToResults(m map[string]*statevar.KeyValueBundle) []*snapshot.FloatingPointValidatorResult {
    79  	if m == nil {
    80  		return []*snapshot.FloatingPointValidatorResult{}
    81  	}
    82  	res := make([]*snapshot.FloatingPointValidatorResult, 0, len(m))
    83  	for k, kvb := range m {
    84  		res = append(res, &snapshot.FloatingPointValidatorResult{Id: k, Bundle: kvb.ToProto()})
    85  	}
    86  	sort.Slice(res, func(i, j int) bool { return res[i].Id < res[j].Id })
    87  	return res
    88  }
    89  
    90  func (sv *StateVariable) serialise() *snapshot.StateVarInternalState {
    91  	return &snapshot.StateVarInternalState{
    92  		Id:                          sv.ID,
    93  		EventId:                     sv.eventID,
    94  		State:                       int32(sv.state),
    95  		ValidatorsResults:           mapToResults(sv.validatorResults),
    96  		RoundsSinceMeaningfulUpdate: int32(sv.roundsSinceMeaningfulUpdate),
    97  	}
    98  }
    99  
   100  // get the serialised form of the given key.
   101  func (e *Engine) serialise(k string) ([]byte, error) {
   102  	if k != key {
   103  		return nil, ErrSnapshotKeyDoesNotExist
   104  	}
   105  
   106  	stateVariablesState := make([]*snapshot.StateVarInternalState, 0, len(e.stateVars))
   107  	for _, sv := range e.stateVars {
   108  		stateVariablesState = append(stateVariablesState, sv.serialise())
   109  	}
   110  	sort.SliceStable(stateVariablesState, func(i, j int) bool { return stateVariablesState[i].Id < stateVariablesState[j].Id })
   111  
   112  	payload := types.Payload{
   113  		Data: &types.PayloadFloatingPointConsensus{
   114  			ConsensusData:               e.serialiseNextTimeTrigger(),
   115  			StateVariablesInternalState: stateVariablesState,
   116  		},
   117  	}
   118  	data, err := proto.Marshal(payload.IntoProto())
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  
   123  	e.ss.serialised = data
   124  	return data, nil
   125  }
   126  
   127  func (e *Engine) GetState(k string) ([]byte, []types.StateProvider, error) {
   128  	state, err := e.serialise(k)
   129  	return state, nil, err
   130  }
   131  
   132  func (e *Engine) LoadState(ctx context.Context, p *types.Payload) ([]types.StateProvider, error) {
   133  	if e.Namespace() != p.Data.Namespace() {
   134  		return nil, types.ErrInvalidSnapshotNamespace
   135  	}
   136  	// see what we're reloading
   137  	switch pl := p.Data.(type) {
   138  	case *types.PayloadFloatingPointConsensus:
   139  		return nil, e.restore(pl.ConsensusData, p)
   140  	default:
   141  		return nil, types.ErrUnknownSnapshotType
   142  	}
   143  }
   144  
   145  func (e *Engine) restore(nextTimeTrigger []*snapshot.NextTimeTrigger, p *types.Payload) error {
   146  	e.log.Debug("restoring statevar snapshot", logging.Int("n_triggers", len(nextTimeTrigger)))
   147  	for _, data := range nextTimeTrigger {
   148  		e.readyForTimeTrigger[data.Asset+data.Market] = struct{}{}
   149  		e.stateVarToNextCalc[data.Id] = time.Unix(0, data.NextTrigger)
   150  		e.log.Debug("restoring", logging.String("id", data.Id), logging.Time("time", time.Unix(0, data.NextTrigger)))
   151  	}
   152  	var err error
   153  	e.ss.serialised, err = proto.Marshal(p.IntoProto())
   154  	return err
   155  }
   156  
   157  // postRestore sets the internal state of all state variables from a snapshot. If there is an active event it will initiate the calculation.
   158  func (e *Engine) postRestore(stateVariablesInternalState []*snapshot.StateVarInternalState) {
   159  	for _, svis := range stateVariablesInternalState {
   160  		sv, ok := e.stateVars[svis.Id]
   161  		if !ok {
   162  			e.log.Panic("expecting a state variable with id to exist during post restore", logging.String("ID", svis.Id))
   163  			continue
   164  		}
   165  		sv.eventID = svis.EventId
   166  		sv.state = ConsensusState(svis.State)
   167  		sv.roundsSinceMeaningfulUpdate = uint(svis.RoundsSinceMeaningfulUpdate)
   168  		if len(svis.ValidatorsResults) > 0 {
   169  			sv.validatorResults = make(map[string]*statevar.KeyValueBundle, len(svis.ValidatorsResults))
   170  		}
   171  		for _, fpvr := range svis.ValidatorsResults {
   172  			kvb, err := statevar.KeyValueBundleFromProto(fpvr.Bundle)
   173  			if err != nil {
   174  				e.log.Panic("restoring malformed statevar kvb", logging.String("id", fpvr.Id), logging.Error(err))
   175  			}
   176  			sv.validatorResults[fpvr.Id] = kvb
   177  		}
   178  	}
   179  }
   180  
   181  // OnStateLoaded is called after all snapshots have been loaded and hence all state variables have been created and sets the internal state for all state variables.
   182  func (e *Engine) OnStateLoaded(ctx context.Context) error {
   183  	var p snapshot.Payload
   184  	err := proto.Unmarshal(e.ss.serialised, &p)
   185  	if err != nil {
   186  		e.log.Error("failed to deserialise state var payload", logging.String("error", err.Error()))
   187  		return err
   188  	}
   189  	payload := types.PayloadFromProto(&p)
   190  	switch pl := payload.Data.(type) {
   191  	case *types.PayloadFloatingPointConsensus:
   192  		e.postRestore(pl.StateVariablesInternalState)
   193  	}
   194  	return nil
   195  }