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 }