code.vegaprotocol.io/vega@v0.79.0/core/collateral/checkpoint.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 "strings" 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 checkpoint "code.vegaprotocol.io/vega/protos/vega/checkpoint/v1" 29 ) 30 31 const ( 32 separator = "___" 33 vestingAccountPrefix = "vesting" 34 ) 35 36 func (e *Engine) Name() types.CheckpointName { 37 return types.CollateralCheckpoint 38 } 39 40 func (e *Engine) Checkpoint() ([]byte, error) { 41 msg := &checkpoint.Collateral{ 42 Balances: e.getCheckpointBalances(), 43 } 44 ret, err := proto.Marshal(msg) 45 if err != nil { 46 return nil, err 47 } 48 return ret, nil 49 } 50 51 var partyOverrideAlias = map[string]string{ 52 systemOwner + types.AccountTypeNetworkTreasury.String(): systemOwner, 53 } 54 55 var partyOverrides = map[string]types.AccountType{ 56 systemOwner: types.AccountTypeNetworkTreasury, 57 systemOwner + types.AccountTypeGlobalInsurance.String(): types.AccountTypeGlobalInsurance, 58 systemOwner + types.AccountTypeGlobalReward.String(): types.AccountTypeGlobalReward, 59 systemOwner + types.AccountTypeMakerReceivedFeeReward.String(): types.AccountTypeMakerReceivedFeeReward, 60 systemOwner + types.AccountTypeMakerPaidFeeReward.String(): types.AccountTypeMakerPaidFeeReward, 61 systemOwner + types.AccountTypeLPFeeReward.String(): types.AccountTypeLPFeeReward, 62 systemOwner + types.AccountTypeAverageNotionalReward.String(): types.AccountTypeAverageNotionalReward, 63 systemOwner + types.AccountTypeRelativeReturnReward.String(): types.AccountTypeRelativeReturnReward, 64 systemOwner + types.AccountTypeReturnVolatilityReward.String(): types.AccountTypeReturnVolatilityReward, 65 systemOwner + types.AccountTypeValidatorRankingReward.String(): types.AccountTypeValidatorRankingReward, 66 systemOwner + types.AccountTypeMarketProposerReward.String(): types.AccountTypeMarketProposerReward, 67 systemOwner + types.AccountTypeFeesInfrastructure.String(): types.AccountTypeFeesInfrastructure, 68 systemOwner + types.AccountTypePendingTransfers.String(): types.AccountTypePendingTransfers, 69 systemOwner + types.AccountTypeRealisedReturnReward.String(): types.AccountTypeRealisedReturnReward, 70 systemOwner + types.AccountTypeEligibleEntitiesReward.String(): types.AccountTypeEligibleEntitiesReward, 71 } 72 73 var tradingRewardAccountTypes = map[types.AccountType]struct{}{ 74 types.AccountTypeMakerReceivedFeeReward: {}, 75 types.AccountTypeMakerPaidFeeReward: {}, 76 types.AccountTypeLPFeeReward: {}, 77 types.AccountTypeMarketProposerReward: {}, 78 types.AccountTypeAverageNotionalReward: {}, 79 types.AccountTypeRelativeReturnReward: {}, 80 types.AccountTypeReturnVolatilityReward: {}, 81 types.AccountTypeValidatorRankingReward: {}, 82 types.AccountTypeRealisedReturnReward: {}, 83 types.AccountTypeEligibleEntitiesReward: {}, 84 } 85 86 func (e *Engine) Load(ctx context.Context, data []byte) error { 87 msg := checkpoint.Collateral{} 88 if err := proto.Unmarshal(data, &msg); err != nil { 89 return err 90 } 91 92 ledgerMovements := []*types.LedgerMovement{} 93 assets := map[string]struct{}{} 94 95 for _, balance := range msg.Balances { 96 ub, _ := num.UintFromString(balance.Balance, 10) 97 isVesting := strings.HasPrefix(balance.Party, vestingAccountPrefix) 98 if isVesting { 99 balance.Party = strings.TrimPrefix(balance.Party, vestingAccountPrefix) 100 } 101 partyComponents := strings.Split(balance.Party, separator) 102 owner := partyComponents[0] 103 market := noMarket 104 if len(partyComponents) > 1 { 105 market = partyComponents[1] 106 } 107 108 if alias, aliasExists := partyOverrideAlias[owner]; aliasExists { 109 owner = alias 110 } 111 112 // for backward compatibility check both - after this is already out checkpoints will always have the type for global accounts 113 if tp, ok := partyOverrides[owner]; ok { 114 accID := e.accountID(market, systemOwner, balance.Asset, tp) 115 if _, ok := tradingRewardAccountTypes[tp]; ok { 116 e.GetOrCreateRewardAccount(ctx, balance.Asset, market, tp) 117 } 118 _, err := e.GetAccountByID(accID) 119 if err != nil { 120 return err 121 } 122 lm, err := e.RestoreCheckpointBalance( 123 ctx, market, systemOwner, balance.Asset, tp, ub.Clone()) 124 if err != nil { 125 return err 126 } 127 assets[balance.Asset] = struct{}{} 128 ledgerMovements = append(ledgerMovements, lm) 129 continue 130 } 131 var ( 132 lm *types.LedgerMovement 133 err error 134 ) 135 136 if isVesting { 137 accID := e.accountID(market, balance.Party, balance.Asset, types.AccountTypeVestingRewards) 138 if _, err := e.GetAccountByID(accID); err != nil { 139 _ = e.GetOrCreatePartyVestingRewardAccount(ctx, balance.Party, balance.Asset) 140 } 141 lm, err = e.RestoreCheckpointBalance( 142 ctx, noMarket, balance.Party, balance.Asset, types.AccountTypeVestingRewards, ub.Clone()) 143 if err != nil { 144 return err 145 } 146 147 e.addToVesting(balance.Party, balance.Asset, ub.Clone()) 148 } else { 149 accID := e.accountID(market, balance.Party, balance.Asset, types.AccountTypeGeneral) 150 if _, err := e.GetAccountByID(accID); err != nil { 151 _, _ = e.CreatePartyGeneralAccount(ctx, balance.Party, balance.Asset) 152 } 153 lm, err = e.RestoreCheckpointBalance( 154 ctx, noMarket, balance.Party, balance.Asset, types.AccountTypeGeneral, ub.Clone()) 155 if err != nil { 156 return err 157 } 158 } 159 ledgerMovements = append(ledgerMovements, lm) 160 } 161 162 if len(ledgerMovements) > 0 { 163 e.broker.Send(events.NewLedgerMovements(ctx, ledgerMovements)) 164 } 165 return nil 166 } 167 168 // get all balances for checkpoint. 169 func (e *Engine) getCheckpointBalances() []*checkpoint.AssetBalance { 170 // party -> asset -> balance 171 balances := make(map[string]map[string]*num.Uint, len(e.accs)) 172 for _, acc := range e.accs { 173 if acc.Balance.IsZero() { 174 continue 175 } 176 switch acc.Type { 177 // vesting rewards needs to be stored separately 178 // so that vesting can be started again 179 case types.AccountTypeVestingRewards: 180 owner := vestingAccountPrefix + acc.Owner 181 182 assets, ok := balances[owner] 183 if !ok { 184 assets = map[string]*num.Uint{} 185 balances[owner] = assets 186 } 187 balance, ok := assets[acc.Asset] 188 if !ok { 189 balance = num.UintZero() 190 assets[acc.Asset] = balance 191 } 192 balance.AddSum(acc.Balance) 193 194 case types.AccountTypeMargin, types.AccountTypeOrderMargin, types.AccountTypeGeneral, types.AccountTypeHolding, types.AccountTypeBond, types.AccountTypeFeesLiquidity, 195 types.AccountTypeInsurance, types.AccountTypeGlobalReward, types.AccountTypeLiquidityFeesBonusDistribution, types.AccountTypeLPLiquidityFees, 196 types.AccountTypeLPFeeReward, types.AccountTypeMakerReceivedFeeReward, types.AccountTypeMakerPaidFeeReward, 197 types.AccountTypeMarketProposerReward, types.AccountTypeFeesInfrastructure, types.AccountTypePendingTransfers, 198 types.AccountTypeNetworkTreasury, types.AccountTypeGlobalInsurance, types.AccountTypeVestedRewards, 199 types.AccountTypeAverageNotionalReward, types.AccountTypeRelativeReturnReward, types.AccountTypeRealisedReturnReward, 200 types.AccountTypeReturnVolatilityReward, types.AccountTypeValidatorRankingReward, types.AccountTypeEligibleEntitiesReward: 201 owner := acc.Owner 202 // NB: market insurance accounts funds will flow implicitly using this logic into the network treasury for the asset 203 // similarly LP Fee bonus distribution bonus account would fall over into the network treasury of the asset. 204 if owner == systemOwner { 205 for k, v := range partyOverrides { 206 if acc.Type == v { 207 owner = k 208 } 209 } 210 if acc.Type == types.AccountTypeInsurance { 211 // let the market insurnace fall into the global insurance account 212 owner = systemOwner + types.AccountTypeGlobalInsurance.String() 213 } 214 } 215 216 // NB: for market based reward accounts we don't want to move the funds to the network treasury but rather keep them 217 if acc.Type == types.AccountTypeLPFeeReward || 218 acc.Type == types.AccountTypeMakerReceivedFeeReward || 219 acc.Type == types.AccountTypeMakerPaidFeeReward || 220 acc.Type == types.AccountTypeMarketProposerReward || 221 acc.Type == types.AccountTypeAverageNotionalReward || 222 acc.Type == types.AccountTypeRelativeReturnReward || 223 acc.Type == types.AccountTypeReturnVolatilityReward || 224 acc.Type == types.AccountTypeValidatorRankingReward || 225 acc.Type == types.AccountTypeRealisedReturnReward || 226 acc.Type == types.AccountTypeEligibleEntitiesReward { 227 owner += separator + acc.MarketID 228 } 229 230 assets, ok := balances[owner] 231 if !ok { 232 assets = map[string]*num.Uint{} 233 balances[owner] = assets 234 } 235 balance, ok := assets[acc.Asset] 236 if !ok { 237 balance = num.UintZero() 238 assets[acc.Asset] = balance 239 } 240 balance.AddSum(acc.Balance) 241 case types.AccountTypeSettlement: 242 if !acc.Balance.IsZero() { 243 e.log.Panic("Settlement balance is not zero", 244 logging.String("market-id", acc.MarketID)) 245 } 246 } 247 } 248 249 out := make([]*checkpoint.AssetBalance, 0, len(balances)) 250 for owner, assets := range balances { 251 for asset, balance := range assets { 252 out = append(out, &checkpoint.AssetBalance{ 253 Party: owner, 254 Asset: asset, 255 Balance: balance.String(), 256 }) 257 } 258 } 259 260 sort.Slice(out, func(i, j int) bool { 261 switch strings.Compare(out[i].Party, out[j].Party) { 262 case -1: 263 return true 264 case 1: 265 return false 266 } 267 return out[i].Asset < out[j].Asset 268 }) 269 return out 270 }