摘要:目前 Kava 主网上有一个较小的 bug,会影响 BEP3 资产供应的调整,这意味着我们暂时无法上调 Kava CDP 内的 BNB 抵押额度(目前已触顶 40,000 BNB)。不过请放心,该 bug 不会影响用户资金的安全性。
我们已将 Kava 网络设计为一个结状结构
kava-3 网络使用参数来治理区块链的状态。通过治理改变参数,Kava 可以管理主链的运行方式,并在有时效性的紧急情况下用委员会治理来快速做出应对。我们发现在其中一个参数——BEP3 的资产供应限额上存在一个较小的 bug,导致参数值的变化无法生效,这意味着当前的供应限额 40,000BNB 无法更新。
如何修复 bug?
修复其实只需要更新几行代码,设置当供应限制的参数值更新时,强制执行应用的状态也相应更新。但值得注意的是,这对系统而言是一个大的变化,所以我们不能仅仅依靠发布一个新的 v0.8 版本来修复这个问题。
修复将如何实现?
异步网络更新
我们已经编写了一个软件版本,修复了这个小 bug。在 bug 修复中,我们指定了代码生效的时间。验证者需要在指定的时间之前更新自己的节点,确保网络顺利更新,不会出现服务中断的情况。任何遗留在上一版本软件上的节点都会在代码生效时被软分叉掉。这基本上也是以太坊核心开发者在“网络升级”时的操作。
软分叉
指区块链网络系统版本或协议升级后,旧的节点并不会意识到代码发生改变,并继续接受由新节点创造的区块,新老节点始终还是在同一条链上工作。
技术代码附录
接下来我们从技术角度来分析一下 Kava 代码中的这个 bug。这个问题代码在 Kava 的bep3 模块中。在该模块的参数中,支持的抵押资产被指定如下:
type AssetParam struct {Denom string `json:\”denom\” yaml:\”denom\”` // name of the assetCoinID int `json:\”coin_id\” yaml:\”coin_id\”` // internationally recognized coin IDLimit sdk.Int `json:\”limit\” yaml:\”limit\”` // asset supply limitActive bool `json:\”active\” yaml:\”active\”` // denotes if asset is active or paused}
?? Github 链接:https://github.com/Kava-Labs/kava/blob/v0.8.1/x/bep3/types/params.go
每个抵押资产都规定了一个限额——即在网络限制进一步的传入交换之前,该资产可以抵押的最大数量。当主网启动时,以下代码为每个资产设置了初始限额:
// Initialize supported assetsfor _, 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))}
?? Github 链接:https://github.com/Kava-Labs/kava/blob/v0.8.1/x/bep3/genesis.go#L29
SetAssetSupply函数为应用数据库中前缀0x02处的每个资产设置了限制:
// SetAssetSupply updates an asset\’s current active supply
func (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)}
?? Github 链接:https://github.com/Kava-Labs/kava/blob/869189054d68d6ec3e6446156ea0a91eb45af09c/x/bep3/keeper/keeper.go#L199
https://github.com/Kava-Labs/kava/blob/869189054d68d6ec3e6446156ea0a91eb45af09c/x/bep3/types/keys.go#L31
当系统更新AssetParam.Limit的参数值时,没有相应的函数更新数据库中的值(通过调用SetAssetSupply)。因此,更新参数不会传送到应用状态。
当一个新的交换被创建时,IncrementIncomingAssetSupply函数被调用,来检查交换资产量没有超过存储在前缀0x02的资产限制量。目前因为这个值没有被更新,所以交换没有通过这个检查。
// IncrementIncomingAssetSupply increments an asset\’s incoming supplyfunc (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//limittotalSupply := 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, )}
…
?? Github 链接:https://github.com/Kava-Labs/kava/blob/869189054d68d6ec3e6446156ea0a91eb45af09c/x/bep3/keeper/swap.go#L70
https://github.com/Kava-Labs/kava/blob/869189054d68d6ec3e6446156ea0a91eb45af09c/x/bep3/keeper/asset.go#L46
我们的修复方法是在bep3 begin blocker中添加一个函数,当参数中的 AssetSupply 值更新时,该函数会更新 AssetSupply 的值。
// 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)}
UpdateAssetSupply 函数可以调节参数和数据库里的数值之间的差异,设置数据库与参数相匹配。
// UpdateAssetSupplies applies updates to the asset limit from parameters to the asset suppliesfunc (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}
为了将其作为异步网络分叉来实现,我们在函数中添加一个activationTime参数,该参数指定UpdateAssetSupplies应该何时运行。例如,我们指定只有在ctx.BlockTime().After(activationTime)时才运行UpdateAssetSupply,并将激活时间设置为 bugfix 发布后一周。注意,ctx.BlockTime()是验证器集同意的区块时间的值,不受时钟漂移的影响。