Slow ABCI methods

ABCI methods (like EndBlocker) are not constrained by gas. Therefore, it is essential to ensure that they always will finish in a reasonable time. Otherwise, the chain will halt.

Example

Below you can find part of a tokens lending application. Before a block is executed, the BeginBlocker method charges an interest for each borrower.

func BeginBlocker(ctx sdk.Context, k keeper.Keeper) {
    updatePrices(ctx, k)
    accrueInterest(ctx, k)
}

func accrueInterest(ctx sdk.Context, k keeper.Keeper) {
    for _, pool := range k.GetLendingPools() {
        poolAssets := k.GetPoolAssets(ctx, pool.Id)
        for userId, _ := range k.GetAllUsers() {
            for _, asset := range poolAssets {
                for _, loan := range k.GetUserLoans(ctx, pool, asset, userId) {
                    if err := k.AccrueInterest(ctx, loan); err != nil {
                        k.PunishUser(ctx, userId)
                    }
                }
            }
        }
    }
}

The accrueInterest contains multiple nested for loops and is obviously too complex to be efficient. Mischievous users can take a lot of small loans to slow down computation to a point where the chain is not able to keep up with blocks production and halts.

Mitigations

External examples