原文标题:《Kava-3 主网错误修复报告》
原文作者:卡瓦实验室
摘要:目前 Kava 主网上有一个较小的 bug,会影响 BEP3 资产供应的调整,这意味着我们暂时无法上调 Kava CDP 内的 BNB 抵押额度(目前已触顶 40,000 BNB)。不过请放心,该 bug 不会影响用户资金的安全性。
kava-3 网络使用参数来管理治理链链的状态。通过治理改变参数,Kava 可以管理主链的运行方式,并在有时效性的紧急情况下用委员会治理来快速应对。我们发现在其中一个参数——BEP3 的资产供应限制上存在一个较小的 bug,导致参数值的变化无法实现,这意味着当前的供应替代 40,000BNB 无法更新。
如何修复 bug?
修复实际上只需要更新几行代码,设置当供应限制的参数值更新时,强制执行应用的状态也相应更新。但只能是的,这对系统而言是一个大的变化,所以我们不能仅仅依靠发布一个新的 v0.8 版本来修复这个问题。
修复将如何实现?
初步网络更新
我们已经编写了一个软件版本,修复了这个小 bug。在 bug 修复中,我们指定了代码执行的时间。验证者需要在指定的时间之前更新自己的中断,确保网络顺利更新,不会出现服务中断的情况。任何遗留在上一版本软件上的上游都会在代码生效时被软分叉掉。这基本上也是以太坊核心开发者在「网络升级」时的操作。
软分叉
指区块链网络系统版本或协议升级后,旧的例程并不会被代码发生改变,并继续接受由新中断创造的区块,新老堆栈始终还是在同一条链上工作。
技术代码附录
接下来我们从技术角度来分析一下 Kava 代码中的这个 bug。这个问题代码在 Kava 的 bep3 模块中。在该模块的参数中,支持的抵押资产被指定如下:
类型 AssetParam struct {
Denom string`json:「denom」yaml:「denom」`//资产名称
CoinID int`json:「coin_id」yaml:「coin_id」`//国际公认的硬币 ID
限制 sdk.Int`json ::「limit」yaml:「limit」`//资产供应限制
有效 bool`json:「active」yaml:「active」`//表示资产是处于活动状态还是已暂停
}
Github 链接:https://github.com/Kava-Labs/kava/blob/v0.8.1/x/bep3/types/params.go
每个抵押资产都规定了一个限制–即在网络限制进一步的交换交换之前,该资产可以抵押的最大数量。当主网启动时,以下代码为每个资产设置了初始配额:
//初始化
_,资产的受支持资产:=范围 gs.Params.SupportedAssets {
zeroCoin:= sdk.NewCoin(asset.Denom,sdk.NewInt(0))
供给:= 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 更新资产的当前有效供应
func(k Keeper)SetAssetSupply(ctx sdk.Context,提供类型.AssetSupply,名词 [] 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 递增资产的传入供应
函数(k Keeper)IncrementIncomingAssetSupply(ctx sdk.Context,coin sdk.Coin)error {
supply,found:= k.GetAssetSupply(ctx,[] byte(coin.Denom))
如果找到了 {
return sdkerrors.Wrap(types.ErrAssetNotSupported,coin.Denom)
}
//(当前+传入+金额)的结果必须在资产的
// limit
totalSupply 以下:= supply.CurrentSupply.Add(supply.IncomingSupply)(
如果 supply.SupplyLimit).IsLT(totalSupply.Add(coin)){
return sdkerrors.Wrapf(
types.ErrExceedsSupplyLimit,
「增加%s,资产供应%s,限制%s」,
硬币,totalSupply,供应。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 都将过期的原子交换过期,
//从长期存储中删除封闭的交换(默认存储时间为 1 周),并更新
//通过治理可能已更改的资产供应限制。
func BeginBlocker(ctx sdk.Context,k Keeper){
k.UpdateExpiredAtomicSwaps(ctx)
k.DeleteClosedAtomicSwapsFromLongtermStorage(ctx)
k.UpdateAssetSupplies(ctx)
}
UpdateAssetSupply 函数可以调节参数和数据库里的数值之间的差异,设置数据库与参数相匹配。
// UpdateAssetSupplies 更新应用到的资产上限从参数到资产用品
FUNC(K 守护)UpdateAssetSupplies(CTX sdk.Context)错误 {
= k.GetParams(CTX):PARAMS
为_,supportedAsset:=范围 params.SupportedAssets {
资产,找到:= k.GetAssetSupply(
ctx,[] byte(supportedAsset.Denom),
)
如果!found {
继续
} 如果 asset.SupplyLimit.Amount!= supportedAsset.Limit {
asset.SupplyLimit = sdk.NewCoin(
supportedAsset.Denom, supportAsset.Limit ,
)
k.SetAssetSupply(ctx,asset,[] byte(supportedAsset.Denom))
}
}
return nil
}
为了将其作为异步网络分叉来实现,我们在函数中添加一个激活时间参数,该参数指定 UpdateAssetSupplies 应该何时运行。例如,我们指定仅在 ctx.BlockTime()。After(activationTime)时才运行 UpdateAssetSupply,注意,ctx.BlockTime()是验证器集同意的区块链时间的值,不受时钟漂移的影响。