github.com/lino-network/lino@v0.6.11/x/bandwidth/manager/manager.go (about) 1 package manager 2 3 import ( 4 sdk "github.com/cosmos/cosmos-sdk/types" 5 "github.com/cosmos/cosmos-sdk/x/auth" 6 7 "github.com/lino-network/lino/param" 8 linotypes "github.com/lino-network/lino/types" 9 account "github.com/lino-network/lino/x/account" 10 "github.com/lino-network/lino/x/bandwidth/model" 11 "github.com/lino-network/lino/x/bandwidth/types" 12 developer "github.com/lino-network/lino/x/developer" 13 global "github.com/lino-network/lino/x/global" 14 vote "github.com/lino-network/lino/x/vote" 15 ) 16 17 var BandwidthManagerTestMode bool = false 18 19 // BandwidthManager - bandwidth manager 20 type BandwidthManager struct { 21 storage model.BandwidthStorage 22 paramHolder param.ParamKeeper 23 // deps 24 gm global.GlobalKeeper 25 vm vote.VoteKeeper 26 dm developer.DeveloperKeeper 27 am account.AccountKeeper 28 } 29 30 func NewBandwidthManager(key sdk.StoreKey, holder param.ParamKeeper, gm global.GlobalKeeper, vm vote.VoteKeeper, dm developer.DeveloperKeeper, am account.AccountKeeper) *BandwidthManager { 31 return &BandwidthManager{ 32 storage: model.NewBandwidthStorage(key), 33 paramHolder: holder, 34 gm: gm, 35 vm: vm, 36 dm: dm, 37 am: am, 38 } 39 } 40 41 // InitGenesis - initialize KV Store 42 func (bm BandwidthManager) InitGenesis(ctx sdk.Context) error { 43 bandwidthInfo := &model.BandwidthInfo{ 44 GeneralMsgEMA: sdk.NewDec(0), 45 AppMsgEMA: sdk.NewDec(0), 46 MaxMPS: sdk.NewDec(0), 47 } 48 49 if err := bm.storage.SetBandwidthInfo(ctx, bandwidthInfo); err != nil { 50 return err 51 } 52 53 blockInfo := &model.BlockInfo{ 54 TotalMsgSignedByApp: 0, 55 TotalMsgSignedByUser: 0, 56 CurMsgFee: linotypes.NewCoinFromInt64(int64(0)), 57 CurU: sdk.NewDec(1), 58 } 59 60 if err := bm.storage.SetBlockInfo(ctx, blockInfo); err != nil { 61 return err 62 } 63 return nil 64 } 65 66 func (bm BandwidthManager) PrecheckAndConsumeBandwidthCredit(ctx sdk.Context, accKey linotypes.AccountKey) sdk.Error { 67 appBandwidthInfo, err := bm.storage.GetAppBandwidthInfo(ctx, accKey) 68 if err != nil { 69 return err 70 } 71 72 blockInfo, err := bm.storage.GetBlockInfo(ctx) 73 if err != nil { 74 return err 75 } 76 if appBandwidthInfo.CurBandwidthCredit.LT(blockInfo.CurU) { 77 return types.ErrAppBandwidthNotEnough() 78 } 79 80 // consume u at first 81 appBandwidthInfo.CurBandwidthCredit = appBandwidthInfo.CurBandwidthCredit.Sub(blockInfo.CurU) 82 if err := bm.storage.SetAppBandwidthInfo(ctx, accKey, appBandwidthInfo); err != nil { 83 return err 84 } 85 return nil 86 } 87 88 func (bm BandwidthManager) IsUserMsgFeeEnough(ctx sdk.Context, fee auth.StdFee) bool { 89 if !fee.Amount.IsValid() { 90 return false 91 } 92 providedFee := linotypes.NewCoinFromInt64(fee.Amount.AmountOf(linotypes.LinoCoinDenom).Int64()) 93 info, err := bm.storage.GetBlockInfo(ctx) 94 if err != nil { 95 return false 96 } 97 return providedFee.IsGTE(info.CurMsgFee) 98 } 99 100 func (bm BandwidthManager) AddMsgSignedByApp(ctx sdk.Context, accKey linotypes.AccountKey, num int64) sdk.Error { 101 info, err := bm.storage.GetBlockInfo(ctx) 102 if err != nil { 103 return err 104 } 105 info.TotalMsgSignedByApp += num 106 107 if err := bm.storage.SetBlockInfo(ctx, info); err != nil { 108 return err 109 } 110 111 appBandwidthInfo, err := bm.storage.GetAppBandwidthInfo(ctx, accKey) 112 if err != nil { 113 return err 114 } 115 appBandwidthInfo.MessagesInCurBlock += 1 116 if err := bm.storage.SetAppBandwidthInfo(ctx, accKey, appBandwidthInfo); err != nil { 117 return err 118 } 119 return nil 120 } 121 122 func (bm BandwidthManager) AddMsgSignedByUser(ctx sdk.Context, num int64) sdk.Error { 123 info, err := bm.storage.GetBlockInfo(ctx) 124 if err != nil { 125 return err 126 } 127 info.TotalMsgSignedByUser += num 128 129 if err := bm.storage.SetBlockInfo(ctx, info); err != nil { 130 return err 131 } 132 return nil 133 } 134 135 func (bm BandwidthManager) ClearBlockInfo(ctx sdk.Context) sdk.Error { 136 info, err := bm.storage.GetBlockInfo(ctx) 137 if err != nil { 138 return err 139 } 140 info.TotalMsgSignedByUser = 0 141 info.TotalMsgSignedByApp = 0 142 143 if err := bm.storage.SetBlockInfo(ctx, info); err != nil { 144 return err 145 } 146 return nil 147 } 148 149 // calcuate the new EMA at the end of each block 150 func (bm BandwidthManager) UpdateMaxMPSAndEMA(ctx sdk.Context) sdk.Error { 151 lastBlockTime := bm.gm.GetLastBlockTime(ctx) 152 if lastBlockTime >= ctx.BlockHeader().Time.Unix() { 153 return nil 154 } 155 156 bandwidthInfo, err := bm.storage.GetBandwidthInfo(ctx) 157 if err != nil { 158 return err 159 } 160 161 blockInfo, err := bm.storage.GetBlockInfo(ctx) 162 if err != nil { 163 return err 164 } 165 166 params, err := bm.paramHolder.GetBandwidthParam(ctx) 167 if err != nil { 168 return err 169 } 170 171 pastTime := ctx.BlockHeader().Time.Unix() - lastBlockTime 172 173 // EMA_general = EMA_general_prev * (1 - k_general) + generalMPS * k_general 174 generalMPS := linotypes.NewDecFromRat(blockInfo.TotalMsgSignedByUser, pastTime) 175 bandwidthInfo.GeneralMsgEMA = bm.calculateEMA(bandwidthInfo.GeneralMsgEMA, params.GeneralMsgEMAFactor, generalMPS) 176 177 // EMA_app = EMA_app_prev * (1 - k_app) + appMPS * k_app 178 appMPS := linotypes.NewDecFromRat(blockInfo.TotalMsgSignedByApp, pastTime) 179 bandwidthInfo.AppMsgEMA = bm.calculateEMA(bandwidthInfo.AppMsgEMA, params.AppMsgEMAFactor, appMPS) 180 181 // MaxMPS = max( (totalMsgSignedByUser + totalMsgSignedByApp)/(curBlockTime - lastBlockTime), MaxMPS) 182 totalMPS := linotypes.NewDecFromRat(blockInfo.TotalMsgSignedByUser+blockInfo.TotalMsgSignedByApp, pastTime) 183 if totalMPS.GT(bandwidthInfo.MaxMPS) { 184 bandwidthInfo.MaxMPS = totalMPS 185 } 186 187 if err := bm.storage.SetBandwidthInfo(ctx, bandwidthInfo); err != nil { 188 return err 189 } 190 191 return nil 192 } 193 194 // calcuate the current msg fee based on last block info at the beginning of each block 195 func (bm BandwidthManager) CalculateCurMsgFee(ctx sdk.Context) sdk.Error { 196 params, err := bm.paramHolder.GetBandwidthParam(ctx) 197 if err != nil { 198 return err 199 } 200 201 bandwidthInfo, err := bm.storage.GetBandwidthInfo(ctx) 202 if err != nil { 203 return err 204 } 205 206 blockInfo, err := bm.storage.GetBlockInfo(ctx) 207 if err != nil { 208 return err 209 } 210 211 generalMsgQuota, err := bm.getGeneralMsgQuota(ctx) 212 if err != nil { 213 return err 214 } 215 216 expResult := bm.approximateExp(bandwidthInfo.GeneralMsgEMA.Sub(generalMsgQuota).Quo(generalMsgQuota).Mul(params.MsgFeeFactorA)) 217 msgFeeLino := expResult.Mul(params.MsgFeeFactorB) 218 blockInfo.CurMsgFee = linotypes.NewCoinFromInt64(msgFeeLino.Mul(sdk.NewDec(linotypes.Decimals)).RoundInt64()) 219 220 if err := bm.storage.SetBlockInfo(ctx, blockInfo); err != nil { 221 return err 222 } 223 return nil 224 } 225 226 // calcuate the current vacancy coeef u based on last block info at the beginning of each block 227 func (bm BandwidthManager) CalculateCurU(ctx sdk.Context) sdk.Error { 228 bandwidthInfo, err := bm.storage.GetBandwidthInfo(ctx) 229 if err != nil { 230 return err 231 } 232 233 params, err := bm.paramHolder.GetBandwidthParam(ctx) 234 if err != nil { 235 return err 236 } 237 238 appMsgQuota, err := bm.getAppMsgQuota(ctx) 239 if err != nil { 240 return err 241 } 242 243 blockInfo, err := bm.storage.GetBlockInfo(ctx) 244 if err != nil { 245 return err 246 } 247 248 delta := bandwidthInfo.AppMsgEMA.Sub(appMsgQuota) 249 blockInfo.CurU = bm.approximateExp(delta.Quo(appMsgQuota).Mul(params.AppVacancyFactor)) 250 if err := bm.storage.SetBlockInfo(ctx, blockInfo); err != nil { 251 return err 252 } 253 return nil 254 } 255 256 func (bm BandwidthManager) DecayMaxMPS(ctx sdk.Context) sdk.Error { 257 bandwidthInfo, err := bm.storage.GetBandwidthInfo(ctx) 258 if err != nil { 259 return err 260 } 261 262 params, err := bm.paramHolder.GetBandwidthParam(ctx) 263 if err != nil { 264 return err 265 } 266 267 bandwidthInfo.MaxMPS = bandwidthInfo.MaxMPS.Mul(params.MaxMPSDecayRate) 268 if err := bm.storage.SetBandwidthInfo(ctx, bandwidthInfo); err != nil { 269 return err 270 } 271 return nil 272 } 273 274 func (bm BandwidthManager) RefillAppBandwidthCredit(ctx sdk.Context, accKey linotypes.AccountKey) sdk.Error { 275 info, err := bm.storage.GetAppBandwidthInfo(ctx, accKey) 276 if err != nil { 277 return err 278 } 279 280 curTime := ctx.BlockHeader().Time.Unix() 281 if info.LastRefilledAt >= curTime { 282 return nil 283 } 284 285 if info.CurBandwidthCredit.GTE(info.MaxBandwidthCredit) { 286 return nil 287 } 288 289 pastSeconds := curTime - info.LastRefilledAt 290 // assume refill rate is equal to expectedMPS 291 newCredit := info.ExpectedMPS.Mul(sdk.NewDec(pastSeconds)).Add(info.CurBandwidthCredit) 292 if newCredit.GTE(info.MaxBandwidthCredit) { 293 info.CurBandwidthCredit = info.MaxBandwidthCredit 294 } else { 295 info.CurBandwidthCredit = newCredit 296 } 297 info.LastRefilledAt = curTime 298 299 if err := bm.storage.SetAppBandwidthInfo(ctx, accKey, info); err != nil { 300 return err 301 } 302 return nil 303 } 304 305 func (bm BandwidthManager) GetPunishmentCoeff(ctx sdk.Context, accKey linotypes.AccountKey) (sdk.Dec, sdk.Error) { 306 lastBlockTime := bm.gm.GetLastBlockTime(ctx) 307 pastTime := ctx.BlockHeader().Time.Unix() - lastBlockTime 308 if pastTime <= 0 { 309 return sdk.NewDec(1), nil 310 } 311 appInfo, err := bm.storage.GetAppBandwidthInfo(ctx, accKey) 312 if err != nil { 313 return sdk.NewDec(1), err 314 } 315 if !appInfo.ExpectedMPS.IsPositive() { 316 return sdk.NewDec(1), types.ErrInvalidExpectedMPS() 317 } 318 319 curMPS := linotypes.NewDecFromRat(appInfo.MessagesInCurBlock, pastTime) 320 delta := curMPS.Sub(appInfo.ExpectedMPS) 321 if delta.IsNegative() { 322 delta = sdk.NewDec(0) 323 } 324 325 params, err := bm.paramHolder.GetBandwidthParam(ctx) 326 if err != nil { 327 return sdk.NewDec(1), err 328 } 329 return bm.approximateExp(delta.Quo(appInfo.ExpectedMPS).Mul(params.AppPunishmentFactor)), nil 330 } 331 332 func (bm BandwidthManager) ConsumeBandwidthCredit(ctx sdk.Context, u sdk.Dec, p sdk.Dec, accKey linotypes.AccountKey) sdk.Error { 333 info, err := bm.storage.GetAppBandwidthInfo(ctx, accKey) 334 if err != nil { 335 return err 336 } 337 numMsgs := sdk.NewDec(info.MessagesInCurBlock) 338 // add back pre-check consumed bandwidth credit 339 info.CurBandwidthCredit = info.CurBandwidthCredit.Add(numMsgs.Mul(u)) 340 // consume credit, currently allow the credit be negative 341 costPerMsg := bm.GetBandwidthCostPerMsg(ctx, u, p) 342 info.CurBandwidthCredit = info.CurBandwidthCredit.Sub(numMsgs.Mul(costPerMsg)) 343 info.MessagesInCurBlock = 0 344 if err := bm.storage.SetAppBandwidthInfo(ctx, accKey, info); err != nil { 345 return err 346 } 347 return nil 348 } 349 350 func (bm BandwidthManager) ReCalculateAppBandwidthInfo(ctx sdk.Context) sdk.Error { 351 totalAppStakeCoin := linotypes.NewCoinFromInt64(0) 352 // calculate all app total stake 353 for _, app := range bm.dm.GetLiveDevelopers(ctx) { 354 appStakeCoin, err := bm.vm.GetLinoStake(ctx, app.Username) 355 if err != nil { 356 return err 357 } 358 totalAppStakeCoin = totalAppStakeCoin.Plus(appStakeCoin) 359 } 360 361 if !totalAppStakeCoin.IsPositive() { 362 return nil 363 } 364 365 // calculate all app MPS quota 366 appMsgQuota, err := bm.getAppMsgQuota(ctx) 367 if err != nil { 368 return err 369 } 370 371 // calculate each app's share and expected MPS 372 params, err := bm.paramHolder.GetBandwidthParam(ctx) 373 if err != nil { 374 return err 375 } 376 377 for _, app := range bm.dm.GetLiveDevelopers(ctx) { 378 appStakeCoin, err := bm.vm.GetLinoStake(ctx, app.Username) 379 if err != nil { 380 return err 381 } 382 appStakePct := appStakeCoin.ToDec().Quo(totalAppStakeCoin.ToDec()) 383 expectedMPS := appMsgQuota.Mul(appStakePct) 384 maxBandwidthCredit := expectedMPS.Mul(params.AppBandwidthPoolSize) 385 386 // first time app, refill it's current pool to the full 387 if !bm.storage.DoesAppBandwidthInfoExist(ctx, app.Username) { 388 newAppInfo := model.AppBandwidthInfo{ 389 Username: app.Username, 390 MaxBandwidthCredit: maxBandwidthCredit, 391 CurBandwidthCredit: maxBandwidthCredit, 392 MessagesInCurBlock: 0, 393 ExpectedMPS: expectedMPS, 394 LastRefilledAt: ctx.BlockHeader().Time.Unix(), 395 } 396 397 if err := bm.storage.SetAppBandwidthInfo(ctx, app.Username, &newAppInfo); err != nil { 398 return err 399 } 400 401 } else { 402 // refill the bandwidth credit with old expectedMPS (refill rate) 403 if err := bm.RefillAppBandwidthCredit(ctx, app.Username); err != nil { 404 return err 405 } 406 // update it's max and expected MPS 407 info, err := bm.storage.GetAppBandwidthInfo(ctx, app.Username) 408 if err != nil { 409 return err 410 } 411 info.ExpectedMPS = expectedMPS 412 info.MaxBandwidthCredit = maxBandwidthCredit 413 414 if err := bm.storage.SetAppBandwidthInfo(ctx, app.Username, info); err != nil { 415 return err 416 } 417 } 418 } 419 return nil 420 } 421 422 func (bm BandwidthManager) CheckBandwidth(ctx sdk.Context, addr sdk.AccAddress, fee auth.StdFee) sdk.Error { 423 bank, err := bm.am.GetBankByAddress(ctx, addr) 424 if err != nil { 425 return err 426 } 427 if bank.Username != "" { 428 appName, err := bm.dm.GetAffiliatingApp(ctx, bank.Username) 429 if err == nil { 430 // refill bandwidth for apps with messages in current block 431 if err := bm.RefillAppBandwidthCredit(ctx, appName); err != nil { 432 return err 433 } 434 435 // app bandwidth model 436 if err := bm.PrecheckAndConsumeBandwidthCredit(ctx, appName); err != nil { 437 return err 438 } 439 440 // add app message stats 441 if err := bm.AddMsgSignedByApp(ctx, appName, 1); err != nil { 442 return err 443 } 444 return nil 445 } 446 } 447 448 // msg fee for general message 449 if !bm.IsUserMsgFeeEnough(ctx, fee) { 450 return types.ErrUserMsgFeeNotEnough() 451 } 452 453 // minus message fee 454 info, err := bm.storage.GetBlockInfo(ctx) 455 if err != nil { 456 return err 457 } 458 459 // minus msg fee 460 if !BandwidthManagerTestMode { 461 err := bm.am.MoveToPool(ctx, linotypes.InflationValidatorPool, 462 linotypes.NewAccOrAddrFromAddr(addr), info.CurMsgFee) 463 if err != nil { 464 return err 465 } 466 } 467 // add general message stats 468 if err := bm.AddMsgSignedByUser(ctx, 1); err != nil { 469 return err 470 } 471 472 return nil 473 } 474 475 func (bm BandwidthManager) BeginBlocker(ctx sdk.Context) sdk.Error { 476 // calculate the new general msg fee for the current block 477 if err := bm.CalculateCurMsgFee(ctx); err != nil { 478 return err 479 } 480 481 // calculate the new vacancy coeff 482 if err := bm.CalculateCurU(ctx); err != nil { 483 return err 484 } 485 486 // clear stats for block info 487 if err := bm.ClearBlockInfo(ctx); err != nil { 488 return err 489 } 490 return nil 491 } 492 493 func (bm BandwidthManager) EndBlocker(ctx sdk.Context) sdk.Error { 494 // update maxMPS and EMA for different msgs and store cur block info 495 if err := bm.UpdateMaxMPSAndEMA(ctx); err != nil { 496 return err 497 } 498 499 blockInfo, err := bm.storage.GetBlockInfo(ctx) 500 if err != nil { 501 return err 502 } 503 504 // get all app bandwidth info 505 allInfo, err := bm.GetAllAppInfo(ctx) 506 if err != nil { 507 return err 508 } 509 510 for _, info := range allInfo { 511 if info.MessagesInCurBlock == 0 { 512 continue 513 } 514 // calculate cost and consume bandwidth credit 515 p, err := bm.GetPunishmentCoeff(ctx, info.Username) 516 if err != nil { 517 return err 518 } 519 if err := bm.ConsumeBandwidthCredit(ctx, blockInfo.CurU, p, info.Username); err != nil { 520 return err 521 } 522 523 } 524 return nil 525 } 526 527 func (bm BandwidthManager) GetBandwidthCostPerMsg(ctx sdk.Context, u sdk.Dec, p sdk.Dec) sdk.Dec { 528 return u.Mul(p) 529 } 530 531 func (bm BandwidthManager) GetAllAppInfo(ctx sdk.Context) ([]*model.AppBandwidthInfo, sdk.Error) { 532 return bm.storage.GetAllAppBandwidthInfo(ctx) 533 } 534 535 func (bm BandwidthManager) calculateEMA(prevEMA sdk.Dec, k sdk.Dec, curMPS sdk.Dec) sdk.Dec { 536 pre := prevEMA.Mul(sdk.NewDec(1).Sub(k)) 537 cur := curMPS.Mul(k) 538 return pre.Add(cur) 539 } 540 541 func (bm BandwidthManager) approximateExp(x sdk.Dec) sdk.Dec { 542 prev := x 543 x = sdk.NewDec(1).Add(x.Abs().Quo(sdk.NewDec(1024))) 544 x = x.Mul(x) 545 x = x.Mul(x) 546 x = x.Mul(x) 547 x = x.Mul(x) 548 x = x.Mul(x) 549 x = x.Mul(x) 550 x = x.Mul(x) 551 x = x.Mul(x) 552 x = x.Mul(x) 553 x = x.Mul(x) 554 555 if prev.LT(sdk.NewDec(0)) { 556 return sdk.NewDec(1).Quo(x) 557 } 558 return x 559 } 560 561 func (bm BandwidthManager) getAppMsgQuota(ctx sdk.Context) (sdk.Dec, sdk.Error) { 562 bandwidthInfo, err := bm.storage.GetBandwidthInfo(ctx) 563 if err != nil { 564 return sdk.NewDec(1), err 565 } 566 567 params, err := bm.paramHolder.GetBandwidthParam(ctx) 568 if err != nil { 569 return sdk.NewDec(1), err 570 } 571 572 var curMaxMPS sdk.Dec 573 if params.ExpectedMaxMPS.GT(bandwidthInfo.MaxMPS) { 574 curMaxMPS = params.ExpectedMaxMPS 575 } else { 576 curMaxMPS = bandwidthInfo.MaxMPS 577 } 578 579 appMsgQuota := params.AppMsgQuotaRatio.Mul(curMaxMPS) 580 if !appMsgQuota.IsPositive() { 581 return sdk.NewDec(1), types.ErrInvalidMsgQuota() 582 } 583 return appMsgQuota, nil 584 } 585 586 func (bm BandwidthManager) getGeneralMsgQuota(ctx sdk.Context) (sdk.Dec, sdk.Error) { 587 bandwidthInfo, err := bm.storage.GetBandwidthInfo(ctx) 588 if err != nil { 589 return sdk.NewDec(1), err 590 } 591 592 params, err := bm.paramHolder.GetBandwidthParam(ctx) 593 if err != nil { 594 return sdk.NewDec(1), err 595 } 596 597 var curMaxMPS sdk.Dec 598 if params.ExpectedMaxMPS.GT(bandwidthInfo.MaxMPS) { 599 curMaxMPS = params.ExpectedMaxMPS 600 } else { 601 curMaxMPS = bandwidthInfo.MaxMPS 602 } 603 604 generalMsgQuota := params.GeneralMsgQuotaRatio.Mul(curMaxMPS) 605 if !generalMsgQuota.IsPositive() { 606 return sdk.NewDec(1), types.ErrInvalidMsgQuota() 607 } 608 return generalMsgQuota, nil 609 } 610 611 // getter 612 func (bm BandwidthManager) GetBandwidthInfo(ctx sdk.Context) (*model.BandwidthInfo, sdk.Error) { 613 return bm.storage.GetBandwidthInfo(ctx) 614 } 615 616 func (bm BandwidthManager) GetBlockInfo(ctx sdk.Context) (*model.BlockInfo, sdk.Error) { 617 return bm.storage.GetBlockInfo(ctx) 618 } 619 620 func (bm BandwidthManager) GetAppBandwidthInfo(ctx sdk.Context, accKey linotypes.AccountKey) (*model.AppBandwidthInfo, sdk.Error) { 621 return bm.storage.GetAppBandwidthInfo(ctx, accKey) 622 }