code.vegaprotocol.io/vega@v0.79.0/core/banking/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 banking 17 18 import ( 19 "context" 20 "fmt" 21 "sort" 22 "sync/atomic" 23 "time" 24 25 "code.vegaprotocol.io/vega/core/assets" 26 "code.vegaprotocol.io/vega/core/events" 27 "code.vegaprotocol.io/vega/core/types" 28 "code.vegaprotocol.io/vega/libs/proto" 29 "code.vegaprotocol.io/vega/logging" 30 "code.vegaprotocol.io/vega/protos/vega" 31 checkpoint "code.vegaprotocol.io/vega/protos/vega/checkpoint/v1" 32 eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1" 33 34 "github.com/emirpasic/gods/sets/treeset" 35 "go.uber.org/zap" 36 ) 37 38 func (e *Engine) Name() types.CheckpointName { 39 return types.BankingCheckpoint 40 } 41 42 func (e *Engine) Checkpoint() ([]byte, error) { 43 msg := &checkpoint.Banking{ 44 TransfersAtTime: e.getScheduledTransfers(), 45 RecurringTransfers: e.getRecurringTransfers(), 46 PrimaryBridgeState: e.getPrimaryBridgeState(), 47 LastSeenPrimaryEthBlock: e.lastSeenPrimaryEthBlock, 48 GovernanceTransfersAtTime: e.getScheduledGovernanceTransfers(), 49 RecurringGovernanceTransfers: e.getRecurringGovernanceTransfers(), 50 SecondaryBridgeState: e.getSecondaryBridgeState(), 51 LastSeenSecondaryEthBlock: e.lastSeenSecondaryEthBlock, 52 } 53 54 msg.SeenRefs = make([]string, 0, e.seenAssetActions.Size()) 55 iter := e.seenAssetActions.Iterator() 56 for iter.Next() { 57 msg.SeenRefs = append(msg.SeenRefs, iter.Value().(string)) 58 } 59 60 msg.AssetActions = make([]*checkpoint.AssetAction, 0, len(e.assetActions)) 61 for _, aa := range e.getAssetActions() { 62 msg.AssetActions = append(msg.AssetActions, aa.IntoProto()) 63 } 64 65 ret, err := proto.Marshal(msg) 66 if err != nil { 67 return nil, err 68 } 69 return ret, nil 70 } 71 72 func (e *Engine) Load(ctx context.Context, data []byte) error { 73 b := checkpoint.Banking{} 74 if err := proto.Unmarshal(data, &b); err != nil { 75 return err 76 } 77 78 evts, err := e.loadScheduledTransfers(ctx, b.TransfersAtTime) 79 if err != nil { 80 return err 81 } 82 83 evts = append(evts, e.loadRecurringTransfers(ctx, b.RecurringTransfers)...) 84 85 evts = append(evts, e.loadScheduledGovernanceTransfers(ctx, b.GovernanceTransfersAtTime)...) 86 evts = append(evts, e.loadRecurringGovernanceTransfers(ctx, b.RecurringGovernanceTransfers)...) 87 88 e.loadPrimaryBridgeState(b.PrimaryBridgeState) 89 e.loadSecondaryBridgeState(b.SecondaryBridgeState) 90 91 e.seenAssetActions = treeset.NewWithStringComparator() 92 for _, v := range b.SeenRefs { 93 e.seenAssetActions.Add(v) 94 } 95 96 e.lastSeenPrimaryEthBlock = b.LastSeenPrimaryEthBlock 97 if e.lastSeenPrimaryEthBlock != 0 { 98 e.log.Info("restoring primary collateral bridge starting block", logging.Uint64("block", e.lastSeenPrimaryEthBlock)) 99 e.ethEventSource.UpdateContractBlock(e.bridgeAddresses[e.primaryEthChainID], e.primaryEthChainID, e.lastSeenPrimaryEthBlock) 100 } 101 102 e.lastSeenSecondaryEthBlock = b.LastSeenSecondaryEthBlock 103 if e.lastSeenSecondaryEthBlock != 0 { 104 e.log.Info("restoring secondary collateral bridge starting block", logging.Uint64("block", e.lastSeenSecondaryEthBlock)) 105 e.ethEventSource.UpdateContractBlock(e.bridgeAddresses[e.secondaryEthChainID], e.secondaryEthChainID, e.lastSeenSecondaryEthBlock) 106 } 107 108 aa := make([]*types.AssetAction, 0, len(b.AssetActions)) 109 for _, a := range b.AssetActions { 110 aa = append(aa, types.AssetActionFromProto(a)) 111 } 112 if err := e.loadAssetActions(aa); err != nil { 113 return fmt.Errorf("could not load asset actions: %w", err) 114 } 115 for _, aa := range e.assetActions { 116 e.witness.StartCheck(aa, e.onCheckDone, e.timeService.GetTimeNow().Add(defaultValidationDuration)) 117 } 118 119 if len(evts) > 0 { 120 e.broker.SendBatch(evts) 121 } 122 123 return nil 124 } 125 126 func (e *Engine) loadAssetActions(aa []*types.AssetAction) error { 127 for _, v := range aa { 128 var ( 129 err error 130 asset *assets.Asset 131 bridgeStopped *types.ERC20EventBridgeStopped 132 bridgeResumed *types.ERC20EventBridgeResumed 133 ) 134 135 // only others action than bridge stop and resume 136 // have an actual asset associated 137 if !v.BridgeResume && !v.BridgeStopped { 138 asset, err = e.assets.Get(v.Asset) 139 if err != nil { 140 e.log.Panic("trying to restore an assetAction with no asset", logging.String("asset", v.Asset)) 141 } 142 } 143 144 if v.BridgeStopped { 145 bridgeStopped = &types.ERC20EventBridgeStopped{BridgeStopped: true} 146 } 147 148 if v.BridgeResume { 149 bridgeResumed = &types.ERC20EventBridgeResumed{BridgeResumed: true} 150 } 151 152 // This handle old asset actions that are not associated to a chain ID 153 // yet, that are likely asset actions that should be associated to the 154 // primary bridge. 155 var bridgeView ERC20BridgeView 156 if v.ChainID == "" { 157 bridgeView = e.primaryBridgeView 158 } else { 159 bridgeView, err = e.bridgeViewForChainID(v.ChainID) 160 if err != nil { 161 return err 162 } 163 } 164 165 state := &atomic.Uint32{} 166 state.Store(v.State) 167 aa := &assetAction{ 168 id: v.ID, 169 state: state, 170 blockHeight: v.BlockNumber, 171 asset: asset, 172 logIndex: v.TxIndex, 173 txHash: v.Hash, 174 chainID: v.ChainID, 175 builtinD: v.BuiltinD, 176 erc20AL: v.Erc20AL, 177 erc20D: v.Erc20D, 178 erc20AssetLimitsUpdated: v.ERC20AssetLimitsUpdated, 179 erc20BridgeStopped: bridgeStopped, 180 erc20BridgeResumed: bridgeResumed, 181 // this is needed every time now 182 bridgeView: bridgeView, 183 } 184 185 if len(aa.getRef().Hash) == 0 { 186 // if we're here it means that the IntoProto code has not done its job properly for a particular asset action type 187 e.log.Panic("asset action has not been serialised correct and is empty", logging.String("txHash", aa.txHash)) 188 } 189 190 e.assetActions[v.ID] = aa 191 // store the deposit in the deposits 192 if v.BuiltinD != nil { 193 e.deposits[v.ID] = e.newDeposit(v.ID, v.BuiltinD.PartyID, v.BuiltinD.VegaAssetID, v.BuiltinD.Amount, v.Hash) 194 } else if v.Erc20D != nil { 195 e.deposits[v.ID] = e.newDeposit(v.ID, v.Erc20D.TargetPartyID, v.Erc20D.VegaAssetID, v.Erc20D.Amount, v.Hash) 196 } 197 } 198 return nil 199 } 200 201 func (e *Engine) loadPrimaryBridgeState(state *checkpoint.BridgeState) { 202 // this would eventually be nil if we restore from a checkpoint 203 // which have been produce from an old version of the core. 204 // we set it to active by default in the case 205 if state == nil { 206 e.primaryBridgeState = &bridgeState{ 207 active: true, 208 } 209 return 210 } 211 212 e.primaryBridgeState = &bridgeState{ 213 active: state.Active, 214 block: state.BlockHeight, 215 logIndex: state.LogIndex, 216 } 217 } 218 219 func (e *Engine) loadSecondaryBridgeState(state *checkpoint.BridgeState) { 220 // this would eventually be nil if we restore from a checkpoint 221 // which have been produce from an old version of the core. 222 // we set it to active by default in the case 223 if state == nil { 224 e.secondaryBridgeState = &bridgeState{ 225 active: true, 226 } 227 return 228 } 229 230 e.secondaryBridgeState = &bridgeState{ 231 active: state.Active, 232 block: state.BlockHeight, 233 logIndex: state.LogIndex, 234 } 235 } 236 237 func (e *Engine) loadScheduledGovernanceTransfers(ctx context.Context, r []*checkpoint.ScheduledGovernanceTransferAtTime) []events.Event { 238 evts := []events.Event{} 239 for _, v := range r { 240 transfers := make([]*types.GovernanceTransfer, 0, len(v.Transfers)) 241 for _, g := range v.Transfers { 242 transfer := types.GovernanceTransferFromProto(g) 243 transfers = append(transfers, transfer) 244 evts = append(evts, events.NewGovTransferFundsEvent(ctx, transfer, transfer.Config.MaxAmount, e.getGovGameID(transfer))) 245 } 246 e.scheduledGovernanceTransfers[v.DeliverOn] = transfers 247 } 248 return evts 249 } 250 251 func (e *Engine) loadScheduledTransfers( 252 ctx context.Context, r []*checkpoint.ScheduledTransferAtTime, 253 ) ([]events.Event, error) { 254 evts := []events.Event{} 255 for _, v := range r { 256 transfers := make([]scheduledTransfer, 0, len(v.Transfers)) 257 for _, v := range v.Transfers { 258 transfer, err := scheduledTransferFromProto(v) 259 if err != nil { 260 return nil, err 261 } 262 evts = append(evts, events.NewOneOffTransferFundsEvent(ctx, transfer.oneoff)) 263 transfers = append(transfers, transfer) 264 } 265 e.scheduledTransfers[v.DeliverOn] = transfers 266 } 267 268 return evts, nil 269 } 270 271 func (e *Engine) loadRecurringTransfers( 272 ctx context.Context, r *checkpoint.RecurringTransfers, 273 ) []events.Event { 274 evts := []events.Event{} 275 e.nextMetricUpdate = time.Unix(0, r.NextMetricUpdate) 276 for _, v := range r.RecurringTransfers { 277 transfer := types.RecurringTransferFromEvent(v) 278 e.recurringTransfers = append(e.recurringTransfers, transfer) 279 e.recurringTransfersMap[transfer.ID] = transfer 280 // reload the dispatch strategy to the hash cache 281 if transfer.DispatchStrategy != nil { 282 // reset defaults for new dispatch strategy params: 283 if transfer.DispatchStrategy.EntityScope == vega.EntityScope_ENTITY_SCOPE_UNSPECIFIED { 284 e.applyMigrationDefaults(transfer.DispatchStrategy) 285 } 286 e.registerDispatchStrategy(transfer.DispatchStrategy) 287 } 288 evts = append(evts, events.NewRecurringTransferFundsEvent(ctx, transfer, e.getGameID(transfer))) 289 } 290 return evts 291 } 292 293 func (e *Engine) applyMigrationDefaults(ds *vega.DispatchStrategy) { 294 ds.EntityScope = vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS 295 ds.LockPeriod = 0 296 ds.WindowLength = 1 297 ds.DistributionStrategy = vega.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA 298 } 299 300 func (e *Engine) loadRecurringGovernanceTransfers(ctx context.Context, transfers []*checkpoint.GovernanceTransfer) []events.Event { 301 evts := []events.Event{} 302 for _, v := range transfers { 303 transfer := types.GovernanceTransferFromProto(v) 304 e.recurringGovernanceTransfers = append(e.recurringGovernanceTransfers, transfer) 305 e.recurringGovernanceTransfersMap[transfer.ID] = transfer 306 if transfer.Config.RecurringTransferConfig.DispatchStrategy != nil { 307 e.registerDispatchStrategy(transfer.Config.RecurringTransferConfig.DispatchStrategy) 308 } 309 evts = append(evts, events.NewGovTransferFundsEvent(ctx, transfer, transfer.Config.MaxAmount, e.getGovGameID(transfer))) 310 } 311 return evts 312 } 313 314 func (e *Engine) getPrimaryBridgeState() *checkpoint.BridgeState { 315 return &checkpoint.BridgeState{ 316 Active: e.primaryBridgeState.active, 317 BlockHeight: e.primaryBridgeState.block, 318 LogIndex: e.primaryBridgeState.logIndex, 319 } 320 } 321 322 func (e *Engine) getSecondaryBridgeState() *checkpoint.BridgeState { 323 return &checkpoint.BridgeState{ 324 Active: e.secondaryBridgeState.active, 325 BlockHeight: e.secondaryBridgeState.block, 326 LogIndex: e.secondaryBridgeState.logIndex, 327 } 328 } 329 330 func (e *Engine) getRecurringTransfers() *checkpoint.RecurringTransfers { 331 out := &checkpoint.RecurringTransfers{ 332 RecurringTransfers: make([]*eventspb.Transfer, 0, len(e.recurringTransfers)), 333 } 334 335 for _, v := range e.recurringTransfers { 336 out.RecurringTransfers = append(out.RecurringTransfers, v.IntoEvent(nil, e.getGameID(v))) 337 } 338 out.NextMetricUpdate = e.nextMetricUpdate.UnixNano() 339 return out 340 } 341 342 func (e *Engine) getRecurringGovernanceTransfers() []*checkpoint.GovernanceTransfer { 343 out := make([]*checkpoint.GovernanceTransfer, 0, len(e.recurringGovernanceTransfers)) 344 for _, v := range e.recurringGovernanceTransfers { 345 out = append(out, v.IntoProto()) 346 } 347 return out 348 } 349 350 func (e *Engine) getScheduledTransfers() []*checkpoint.ScheduledTransferAtTime { 351 out := make([]*checkpoint.ScheduledTransferAtTime, 0, len(e.scheduledTransfers)) 352 353 for k, v := range e.scheduledTransfers { 354 transfers := make([]*checkpoint.ScheduledTransfer, 0, len(v)) 355 for _, v := range v { 356 transfers = append(transfers, v.ToProto()) 357 } 358 out = append(out, &checkpoint.ScheduledTransferAtTime{DeliverOn: k, Transfers: transfers}) 359 } 360 361 sort.SliceStable(out, func(i, j int) bool { return out[i].DeliverOn < out[j].DeliverOn }) 362 363 return out 364 } 365 366 func (e *Engine) getScheduledGovernanceTransfers() []*checkpoint.ScheduledGovernanceTransferAtTime { 367 out := make([]*checkpoint.ScheduledGovernanceTransferAtTime, 0, len(e.scheduledGovernanceTransfers)) 368 369 for k, v := range e.scheduledGovernanceTransfers { 370 transfers := make([]*checkpoint.GovernanceTransfer, 0, len(v)) 371 for _, v := range v { 372 transfers = append(transfers, v.IntoProto()) 373 } 374 out = append(out, &checkpoint.ScheduledGovernanceTransferAtTime{DeliverOn: k, Transfers: transfers}) 375 } 376 sort.SliceStable(out, func(i, j int) bool { return out[i].DeliverOn < out[j].DeliverOn }) 377 return out 378 } 379 380 func (e *Engine) getAssetActions() []*types.AssetAction { 381 aa := make([]*types.AssetAction, 0, len(e.assetActions)) 382 for _, v := range e.assetActions { 383 // this is optional as bridge action don't have one 384 var assetID string 385 if v.asset != nil { 386 assetID = v.asset.ToAssetType().ID 387 } 388 389 var bridgeStopped bool 390 if v.erc20BridgeStopped != nil { 391 bridgeStopped = true 392 } 393 394 var bridgeResumed bool 395 if v.erc20BridgeResumed != nil { 396 bridgeResumed = true 397 } 398 399 action := types.AssetAction{ 400 ID: v.id, 401 State: v.state.Load(), 402 BlockNumber: v.blockHeight, 403 Asset: assetID, 404 TxIndex: v.logIndex, 405 Hash: v.txHash, 406 ChainID: v.chainID, 407 BuiltinD: v.builtinD, 408 Erc20AL: v.erc20AL, 409 Erc20D: v.erc20D, 410 ERC20AssetLimitsUpdated: v.erc20AssetLimitsUpdated, 411 BridgeStopped: bridgeStopped, 412 BridgeResume: bridgeResumed, 413 } 414 415 e.log.Info("getAssetActions", 416 zap.Any("action", fmt.Sprintf("%+v", action)), 417 zap.String("ref", v.getRef().Hash), 418 ) 419 420 aa = append(aa, &action) 421 } 422 423 sort.SliceStable(aa, func(i, j int) bool { return aa[i].ID < aa[j].ID }) 424 return aa 425 }