code.vegaprotocol.io/vega@v0.79.0/core/collateral/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 collateral 17 18 import ( 19 "context" 20 "sort" 21 "time" 22 23 "code.vegaprotocol.io/vega/core/events" 24 "code.vegaprotocol.io/vega/core/types" 25 "code.vegaprotocol.io/vega/libs/num" 26 "code.vegaprotocol.io/vega/libs/proto" 27 "code.vegaprotocol.io/vega/logging" 28 29 "github.com/pkg/errors" 30 ) 31 32 type accState struct { 33 accPL types.PayloadCollateralAccounts 34 assPL types.PayloadCollateralAssets 35 assets map[string]types.Asset 36 assetIDs []string 37 serialisedAccounts []byte 38 serialisedAssets []byte 39 hashKeys []string 40 accountsKey string 41 assetsKey string 42 } 43 44 var ( 45 ErrInvalidSnapshotNamespace = errors.New("invalid snapshot namespace") 46 ErrUnknownSnapshotType = errors.New("snapshot data type not known") 47 ) 48 49 func (e *Engine) Namespace() types.SnapshotNamespace { 50 return types.CollateralSnapshot 51 } 52 53 func (e *Engine) Keys() []string { 54 return e.state.hashKeys 55 } 56 57 func (e *Engine) Stopped() bool { 58 return false 59 } 60 61 func (e *Engine) GetState(k string) ([]byte, []types.StateProvider, error) { 62 state, err := e.state.getState(k) 63 return state, nil, err 64 } 65 66 func (e *Engine) LoadState(ctx context.Context, p *types.Payload) ([]types.StateProvider, error) { 67 if e.Namespace() != p.Data.Namespace() { 68 return nil, ErrInvalidSnapshotNamespace 69 } 70 // see what we're reloading 71 switch pl := p.Data.(type) { 72 case *types.PayloadCollateralAssets: 73 err := e.restoreAssets(pl.CollateralAssets, p) 74 return nil, err 75 case *types.PayloadCollateralAccounts: 76 err := e.restoreAccounts(ctx, pl.CollateralAccounts, p) 77 return nil, err 78 default: 79 return nil, ErrUnknownSnapshotType 80 } 81 } 82 83 func (e *Engine) restoreAccounts(ctx context.Context, accs *types.CollateralAccounts, p *types.Payload) error { 84 e.log.Debug("restoring accounts snapshot", logging.Int("n_accounts", len(accs.Accounts))) 85 86 evts := []events.Event{} 87 pevts := []events.Event{} 88 e.accs = make(map[string]*types.Account, len(accs.Accounts)) 89 e.partiesAccs = map[string]map[string]*types.Account{} 90 e.hashableAccs = make([]*types.Account, 0, len(accs.Accounts)) 91 assets := map[string]struct{}{} 92 for _, acc := range accs.Accounts { 93 e.accs[acc.ID] = acc 94 assets[acc.Asset] = struct{}{} 95 if _, ok := e.partiesAccs[acc.Owner]; !ok { 96 e.partiesAccs[acc.Owner] = map[string]*types.Account{} 97 } 98 e.partiesAccs[acc.Owner][acc.ID] = acc 99 e.hashableAccs = append(e.hashableAccs, acc) 100 e.addAccountToHashableSlice(acc) 101 102 evts = append(evts, events.NewAccountEvent(ctx, *acc)) 103 104 if acc.Owner != systemOwner { 105 pevts = append(pevts, events.NewPartyEvent(ctx, types.Party{Id: acc.Owner})) 106 } 107 } 108 e.state.updateAccs(e.hashableAccs) 109 e.broker.SendBatch(evts) 110 e.broker.SendBatch(pevts) 111 var err error 112 e.state.serialisedAccounts, err = proto.Marshal(p.IntoProto()) 113 e.updateNextBalanceSnapshot(accs.NextBalanceSnapshot) 114 e.snapshotBalances() 115 e.earmarkedBalance = accs.Earmarked 116 e.state.updateEarmarked(e.earmarkedBalance) 117 return err 118 } 119 120 func (e *Engine) restoreAssets(assets *types.CollateralAssets, p *types.Payload) error { 121 // @TODO the ID and name might not be the same, perhaps we need 122 // to wrap the asset details to preserve that data 123 e.log.Debug("restoring assets snapshot", logging.Int("n_assets", len(assets.Assets))) 124 e.enabledAssets = make(map[string]types.Asset, len(assets.Assets)) 125 e.state.assetIDs = make([]string, 0, len(assets.Assets)) 126 e.state.assets = make(map[string]types.Asset, len(assets.Assets)) 127 for _, a := range assets.Assets { 128 ast := types.Asset{ 129 ID: a.ID, 130 Details: a.Details, 131 Status: a.Status, 132 } 133 e.enabledAssets[a.ID] = ast 134 e.state.enableAsset(ast) 135 } 136 var err error 137 e.state.serialisedAssets, err = proto.Marshal(p.IntoProto()) 138 e.snapshotBalances() 139 return err 140 } 141 142 func newAccState() *accState { 143 state := &accState{ 144 accPL: types.PayloadCollateralAccounts{ 145 CollateralAccounts: &types.CollateralAccounts{ 146 NextBalanceSnapshot: time.Time{}, 147 }, 148 }, 149 assPL: types.PayloadCollateralAssets{ 150 CollateralAssets: &types.CollateralAssets{}, 151 }, 152 assets: map[string]types.Asset{}, 153 assetIDs: []string{}, 154 } 155 state.accountsKey = state.accPL.Key() 156 state.assetsKey = state.assPL.Key() 157 state.hashKeys = []string{ 158 state.assetsKey, 159 state.accountsKey, 160 } 161 162 return state 163 } 164 165 func (a *accState) enableAsset(asset types.Asset) { 166 a.assets[asset.ID] = asset 167 a.assetIDs = append(a.assetIDs, asset.ID) 168 sort.Strings(a.assetIDs) 169 } 170 171 func (a *accState) updateAsset(asset types.Asset) { 172 a.assets[asset.ID] = asset 173 } 174 175 func (a *accState) updateAccs(accs []*types.Account) { 176 a.accPL.CollateralAccounts.Accounts = accs[:] 177 } 178 179 func (a *accState) updateEarmarked(earmarked map[string]*num.Uint) { 180 a.accPL.CollateralAccounts.Earmarked = earmarked 181 } 182 183 func (a *accState) updateBalanceSnapshotTime(t time.Time) { 184 a.accPL.CollateralAccounts.NextBalanceSnapshot = t 185 } 186 187 func (a *accState) hashAssets() ([]byte, error) { 188 assets := make([]*types.Asset, 0, len(a.assetIDs)) 189 for _, id := range a.assetIDs { 190 ast := a.assets[id] 191 assets = append(assets, &ast) 192 } 193 a.assPL.CollateralAssets.Assets = assets 194 pl := types.Payload{ 195 Data: &a.assPL, 196 } 197 data, err := proto.Marshal(pl.IntoProto()) 198 if err != nil { 199 return nil, err 200 } 201 a.serialisedAssets = data 202 return data, nil 203 } 204 205 func (a *accState) hashAccounts() ([]byte, error) { 206 // the account slice is already set, sorted and all 207 pl := types.Payload{ 208 Data: &a.accPL, 209 } 210 data, err := proto.Marshal(pl.IntoProto()) 211 if err != nil { 212 return nil, err 213 } 214 a.serialisedAccounts = data 215 return data, nil 216 } 217 218 func (a *accState) serialiseK(serialFunc func() ([]byte, error), dataField *[]byte) ([]byte, error) { 219 data, err := serialFunc() 220 if err != nil { 221 return nil, err 222 } 223 *dataField = data 224 return data, nil 225 } 226 227 // get the serialised form and hash of the given key. 228 func (a *accState) getState(k string) ([]byte, error) { 229 switch k { 230 case a.accountsKey: 231 return a.serialiseK(a.hashAccounts, &a.serialisedAccounts) 232 case a.assetsKey: 233 return a.serialiseK(a.hashAssets, &a.serialisedAssets) 234 default: 235 return nil, types.ErrSnapshotKeyDoesNotExist 236 } 237 }