Kava 3 Chain Advisory
TLDR: There is a minor bug that affects changing the BEP3 asset supply, which means that the current value of 40000 BNB cannot be currently updated. No user funds are at risk due to this minor bug.
The kava-3 blockchain uses parameters to govern the state of the blockchain. By changing parameters via governance, Kava can manage how the chain functions and respond to changing conditions in a time-sensitive manner. One of those parameters, the asset supply limit for BEP3 assets, has a minor bug which causes changes in the parameter value not to take effect, which means that the current supply limit, 40000BNB, cannot be updated.
How can it be fixed?
The fix required is a few lines of code which enforce that when the parameter value for supply limit is updated, the state of the application is updated at that time. Importantly, this is a breaking change so we can’t just release a new version of v0.8 to fix this.
How the fix will be implemented
Asynchronous network update
A version of the software has been written that fixes the minor bug. In the bug fix, a time is specified when that code will take effect. Validators update their nodes before the specified time and the network will update smoothly without service interruption. Any nodes left on the previous version of the software will be forked off at that time. This is essentially the process used by the Ethereum core developers during what they call ‘network upgrades’.
Technical addendum
This section gives a breakdown of where the minor bug is in the kava code. The code in question is in the bep3
module on kava. In the Params
for the module, the assets which are supported are specified as follows:
type AssetParam struct {
Denom string `json:"denom" yaml:"denom"` // name of the asset
CoinID int `json:"coin_id" yaml:"coin_id"` // internationally recognized coin ID
Limit sdk.Int `json:"limit" yaml:"limit"` // asset supply limit
Active bool `json:"active" yaml:"active"` // denotes if asset is active or paused
}
Each asset specifies a limit — which is the number of units of that asset that can exist before further incoming swaps are disallowed. When the chain starts, the following code sets the initial limit for each asset:
// Initialize supported assets
for _, asset := range gs.Params.SupportedAssets {
zeroCoin := sdk.NewCoin(asset.Denom, sdk.NewInt(0))
supply := NewAssetSupply(asset.Denom, zeroCoin, zeroCoin,
zeroCoin, sdk.NewCoin(asset.Denom, asset.Limit))
keeper.SetAssetSupply(ctx, supply, []byte(asset.Denom))
}
The function SetAssetSupply
sets the limit for each asset at the prefix 0x02
in the application database.
// SetAssetSupply updates an asset's current active supplyfunc (k Keeper) SetAssetSupply(ctx sdk.Context, supply types.AssetSupply, denom []byte) {
store := prefix.NewStore(
ctx.KVStore(k.key),
types.AssetSupplyKeyPrefix, //0x02
)
bz := k.cdc.MustMarshalBinaryLengthPrefixed(supply)
store.Set(denom, bz)
}
When the parameter value for AssetParam.Limit
is updated, there is no corresponding function that updates the value in the store (by calling SetAssetSupply
). Thus, updating the parameters does not propagate to the application state.
When a new swap is created, the function IncrementIncomingAssetSupply
is called, which checks that the swap is not over the asset limit stored at prefix 0x02
. Because this value has not been updated, the swap fails this check:
// IncrementIncomingAssetSupply increments an asset's incoming supply
func (k Keeper) IncrementIncomingAssetSupply(ctx sdk.Context, coin sdk.Coin) error {
supply, found := k.GetAssetSupply(ctx, []byte(coin.Denom))
if !found {
return sdkerrors.Wrap(types.ErrAssetNotSupported, coin.Denom)
}
// Result of (current + incoming + amount) must be under asset's
//limit
totalSupply := supply.CurrentSupply.Add(supply.IncomingSupply)
if supply.SupplyLimit.IsLT(totalSupply.Add(coin)) {
return sdkerrors.Wrapf(
types.ErrExceedsSupplyLimit,
"increase %s, asset supply %s, limit %s",
coin, totalSupply, supply.SupplyLimit,
)
}...
The fix is to add a function to the bep3
begin blocker that updates the value of AssetSupply
when the value is updated in the parameters:
// BeginBlocker on every block expires outdated atomic swaps, removes closed
// swap from long term storage (default storage time of 1 week), and updates
// asset supply limits that may have changed via governance.
func BeginBlocker(ctx sdk.Context, k Keeper) {
k.UpdateExpiredAtomicSwaps(ctx)
k.DeleteClosedAtomicSwapsFromLongtermStorage(ctx)
k.UpdateAssetSupplies(ctx)
}
The UpdateAssetSupply
function reconciles differences between the parameters and what’s in the store, setting the store to match the parameters.
// UpdateAssetSupplies applies updates to the asset limit from parameters to the asset supplies
func (k Keeper) UpdateAssetSupplies(ctx sdk.Context) error {
params := k.GetParams(ctx)
for _, supportedAsset := range params.SupportedAssets {
asset, found := k.GetAssetSupply(
ctx, []byte(supportedAsset.Denom),
)
if !found {
continue
} if asset.SupplyLimit.Amount != supportedAsset.Limit {
asset.SupplyLimit = sdk.NewCoin(
supportedAsset.Denom, supportedAsset.Limit,
)
k.SetAssetSupply(ctx, asset, []byte(supportedAsset.Denom))
}
}
return nil
}
To implement this as an asynchronous network fork, we add an activationTime
argument to the function which specifies when UpdateAssetSupplies
should run. For example, we specify that UpdateAssetSupply
only run if ctx.BlockTime().After(activationTime)
and set activation time to be one week after the bugfix release is published. Note that ctx.BlockTime()
is the value of the block time agreed by the validator set, and is not subject to wall clock drift.
Stay in touch!
- Follow us on Twitter: https://twitter.com/kava_labs
- Join the conversation on Telegram: https://t.me/kavalabs
- Checkout our code: https://github.com/kava-labs
Disclaimer: This content is provided for informational purposes only, and should not be relied upon as legal, business, investment, or tax advice. You should consult your own advisers as to those matters. References to any securities or digital assets are for illustrative purposes only, and do not constitute an investment recommendation or offer to provide investment advisory services. Furthermore, this content is not directed at nor intended for use by any investors or prospective investors, and may not under any circumstances be relied upon when making investment decisions.