Skip to content

Commit

Permalink
test: add tests, fix related bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
leonz789 committed Aug 29, 2024
1 parent 359d0f2 commit d2e117d
Show file tree
Hide file tree
Showing 4 changed files with 575 additions and 29 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ require (
github.com/holiman/uint256 v1.2.3 // indirect
github.com/huandu/skiplist v1.2.0 // indirect
github.com/huin/goupnp v1.3.0 // indirect
github.com/imroc/biu v0.0.0-20170329141542-0376ce6761c0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1147,6 +1147,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ=
github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8=
github.com/imroc/biu v0.0.0-20170329141542-0376ce6761c0 h1:pkyNAS9IQiZgseFrdhZC4cloBo2k2O2Son/k+3NquwY=
github.com/imroc/biu v0.0.0-20170329141542-0376ce6761c0/go.mod h1:wscexmyH+oDXfQr1q8PAZUXfKnxCUcNm62D/M5Ec8Lw=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
Expand Down
205 changes: 176 additions & 29 deletions x/oracle/keeper/native_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,39 @@ import (

var stakerList types.StakerList

func (k Keeper) GetStakerInfo(ctx sdk.Context, assetID, stakerAddr string) types.StakerInfo {
store := ctx.KVStore(k.storeKey)
stakerInfo := types.StakerInfo{}
value := store.Get(types.NativeTokenStakerKey(assetID, stakerAddr))
if value == nil {
return stakerInfo
}
k.cdc.MustUnmarshal(value, &stakerInfo)
return stakerInfo
}

func (k Keeper) GetStakerDelegations(ctx sdk.Context, assetID, stakerAddr string) types.StakerDelegationInfo {
store := ctx.KVStore(k.storeKey)
value := store.Get(types.NativeTokenStakerDelegationKey(assetID, stakerAddr))
stakerDelegation := types.StakerDelegationInfo{}
if value == nil {
return stakerDelegation
}
k.cdc.MustUnmarshal(value, &stakerDelegation)
return stakerDelegation
}

func (k Keeper) GetOperatorInfo(ctx sdk.Context, assetID, operator string) types.OperatorInfo {
store := ctx.KVStore(k.storeKey)
value := store.Get(types.NativeTokenOperatorKey(assetID, operator))
operatorInfo := types.OperatorInfo{}
if value == nil {
return operatorInfo
}
k.cdc.MustUnmarshal(value, &operatorInfo)
return operatorInfo
}

// TODO, NOTE: price changes will effect reward/slash calculation, every time one staker's price changed, it's reward/slash amount(LST) should be cleaned or recalculated immediately
// TODO: validatorIndex
// amount: represents for originalToken
Expand All @@ -37,9 +70,44 @@ func (k Keeper) UpdateNativeTokenByDepositOrWithdraw(ctx sdk.Context, assetID, s
// calculate amount of virtual LST from nativetoken with price
amountInt := convertAmountOriginalIntToAmountFloat(amount, stakerInfo.PriceList[latestIndex].Price).RoundInt()
stakerInfo.TotalDeposit = stakerInfo.TotalDeposit.Add(amountInt)

keyStakerList := types.NativeTokenStakerListKey(assetID)
valueStakerList := store.Get(keyStakerList)
stakerList := &types.StakerList{}
if valueStakerList != nil {
k.cdc.MustUnmarshal(valueStakerList, stakerList)
}
exists := false
for idx, stakerExists := range stakerList.StakerAddrs {
if stakerExists == stakerAddr {
if !stakerInfo.TotalDeposit.IsPositive() {
stakerList.StakerAddrs = append(stakerList.StakerAddrs[:idx], stakerList.StakerAddrs[idx+1:]...)
valueStakerList = k.cdc.MustMarshal(stakerList)
store.Set(keyStakerList, valueStakerList)
}
exists = true
stakerInfo.StakerIndex = int64(idx)
break
}
}

// update totalDeposit of staker, and price won't change on either deposit or withdraw
bz := k.cdc.MustMarshal(stakerInfo)
store.Set(key, bz)
if !stakerInfo.TotalDeposit.IsPositive() {
store.Delete(key)
} else {
bz := k.cdc.MustMarshal(stakerInfo)
store.Set(key, bz)
}

if !exists {
if !stakerInfo.TotalDeposit.IsPositive() {
// this should not happened, if a staker execute the 'withdraw' action, he must have already been in the stakerList
return amountInt
}
stakerList.StakerAddrs = append(stakerList.StakerAddrs, stakerAddr)
valueStakerList = k.cdc.MustMarshal(stakerList)
store.Set(keyStakerList, valueStakerList)
}
return amountInt
}

Expand All @@ -49,15 +117,16 @@ func (k Keeper) UpdateNativeTokenByDelegation(ctx sdk.Context, assetID, operator
store := ctx.KVStore(k.storeKey)
keyOperator := types.NativeTokenOperatorKey(assetID, operatorAddr)
operatorInfo := &types.OperatorInfo{}
value := store.Get(keyOperator)
if value == nil {
valueOperator := store.Get(keyOperator)
if valueOperator == nil {
operatorInfo = types.NewOperatorInfo(operatorAddr)
} else {
k.cdc.MustUnmarshal(value, operatorInfo)
k.cdc.MustUnmarshal(valueOperator, operatorInfo)
}
stakerInfo := &types.StakerInfo{}
keyStaker := types.NativeTokenStakerKey(assetID, stakerAddr)
if value = store.Get(keyStaker); value == nil {
value := store.Get(keyStaker)
if value == nil {
panic("staker must exist before delegation")
}
k.cdc.MustUnmarshal(value, stakerInfo)
Expand All @@ -69,15 +138,29 @@ func (k Keeper) UpdateNativeTokenByDelegation(ctx sdk.Context, assetID, operator
operatorAmountFloat = operatorAmountFloat.Add(amountFloat)

// update operator's price for native token base on new delegation
operatorInfo.PriceList = append(operatorInfo.PriceList, &types.PriceInfo{
Price: operatorAmountOriginalFloat.Quo(operatorAmountFloat),
Block: uint64(ctx.BlockHeight()),
})

if valueOperator == nil {
// undelegation should not happen on nil operatorInfo, this is delegate case
operatorInfo.PriceList = []*types.PriceInfo{
{
Price: operatorAmountOriginalFloat.Quo(operatorAmountFloat),
Block: uint64(ctx.BlockHeight()),
},
}
} else if operatorAmountFloat.IsPositive() {
// if amount <=0 thies operatorInfo will be rmoved, no need to append any price
operatorInfo.PriceList = append(operatorInfo.PriceList, &types.PriceInfo{
Price: operatorAmountOriginalFloat.Quo(operatorAmountFloat),
Block: uint64(ctx.BlockHeight()),
})
}
// update operator's total amount for native token, for this 'amount' we don't disginguish different tokens from different stakers. That difference reflects in 'operator price'
operatorInfo.TotalAmount = operatorAmountFloat.RoundInt()
bz := k.cdc.MustMarshal(operatorInfo)
store.Set(keyOperator, bz)
if operatorInfo.TotalAmount.IsPositive() {
bz := k.cdc.MustMarshal(operatorInfo)
store.Set(keyOperator, bz)
} else {
store.Delete(keyOperator)
}
amountInt := amountFloat.RoundInt()
// update staker's related operator list
keyDelegation := types.NativeTokenStakerDelegationKey(assetID, stakerAddr)
Expand All @@ -94,7 +177,7 @@ func (k Keeper) UpdateNativeTokenByDelegation(ctx sdk.Context, assetID, operator
for idx, delegationInfo := range stakerDelegation.Delegations {
if delegationInfo.OperatorAddr == operatorAddr {
if delegationInfo.Amount = delegationInfo.Amount.Add(amountInt); !delegationInfo.Amount.IsPositive() {
stakerDelegation.Delegations = append(stakerDelegation.Delegations[0:idx], stakerDelegation.Delegations[idx:]...)
stakerDelegation.Delegations = append(stakerDelegation.Delegations[:idx], stakerDelegation.Delegations[idx+1:]...)
}
value = k.cdc.MustMarshal(stakerDelegation)
store.Set(keyDelegation, value)
Expand Down Expand Up @@ -150,6 +233,9 @@ func (k Keeper) GetStakerList(ctx sdk.Context, assetID string) types.StakerList
}

func (k Keeper) UpdateNativeTokenByBalanceChange(ctx sdk.Context, assetID string, rawData []byte, roundID uint64) error {
if len(rawData) < 32 {
return errors.New("length of indicate maps for stakers shoule be exactly 32 bytes")
}
sl := k.getStakerList(ctx, assetID)
if len(sl.StakerAddrs) == 0 {
return errors.New("staker list is empty")
Expand Down Expand Up @@ -221,38 +307,54 @@ func (k Keeper) getStakerList(ctx sdk.Context, assetID string) types.StakerList
func parseBalanceChange(rawData []byte, sl types.StakerList) (map[string]int, error) {
indexs := rawData[:32]
changes := rawData[32:]
// lenChanges := len(changes)
index := -1
byteIndex := -1
bitOffset := 5
byteIndex := 0
bitOffset := 0
lengthBits := 5
stakerChanges := make(map[string]int)
for _, b := range indexs {
for i := 7; i >= 0; i-- {
// staker's index start from 1
index++
if (b>>i)&1 == 1 {
// effect balance f stakerAddr[index] has changed
lenValue := int(changes[byteIndex] >> 4)
lenValue := changes[byteIndex] << bitOffset
bitsLeft := 8 - bitOffset
lenValue >>= (8 - lengthBits)
if bitsLeft < lengthBits {
byteIndex++
lenValue |= changes[byteIndex] >> (8 - lengthBits + bitsLeft)
bitOffset = lengthBits - bitsLeft
} else {
if bitOffset += lengthBits; bitOffset == 8 {
bitOffset = 0
}
if bitsLeft == lengthBits {
byteIndex++
}
}

symbol := lenValue & 1
lenValue >>= 1
if lenValue <= 0 {
return stakerChanges, errors.New("length of change value must be at least 1 bit")
}
symbol := (changes[byteIndex] >> 3) & 1

bitsExtracted := 0
stakerChange := 0
for j := 0; j < lenValue; j++ {
byteIndex++
byteValue := changes[byteIndex] << bitOffset
// byteValue <<= bitOffset
for bitsExtracted < int(lenValue) { //0<8, offset:

Check failure on line 343 in x/oracle/keeper/native_token.go

View workflow job for this annotation

GitHub Actions / Run golangci-lint

commentFormatting: put a space between `//` and comment text (gocritic)
bitsLeft := 8 - bitOffset
if bitsExtracted+bitsLeft > lenValue {
bitsLeft = lenValue - bitsExtracted
bitOffset = bitsLeft
byteValue := changes[byteIndex] << bitOffset
if (int(lenValue) - bitsExtracted) < bitsLeft {
bitsLeft = int(lenValue) - bitsExtracted
bitOffset += bitsLeft
} else {
byteIndex++
bitOffset = 0
}
byteValue = (byteValue >> (8 - bitsLeft)) & ((1 << bitsLeft) - 1)
byteValue >>= (8 - bitsLeft)
stakerChange = (stakerChange << bitsLeft) | int(byteValue)
bitsExtracted += bitsLeft
}
stakerChange++
if symbol == 1 {
stakerChange *= -1
}
Expand All @@ -263,6 +365,51 @@ func parseBalanceChange(rawData []byte, sl types.StakerList) (map[string]int, er
return stakerChanges, nil
}

// func parseBalanceChange(rawData []byte, sl types.StakerList) (map[string]int, error) {
// indexs := rawData[:32]
// changes := rawData[32:]
// // lenChanges := len(changes)
// index := -1
// byteIndex := -1
// bitOffset := 5
// stakerChanges := make(map[string]int)
// for _, b := range indexs {
// for i := 7; i >= 0; i-- {
// // staker's index start from 1
// index++
// if (b>>i)&1 == 1 {
// // effect balance f stakerAddr[index] has changed
// lenValue := int(changes[byteIndex] >> 4)
// if lenValue <= 0 {
// return stakerChanges, errors.New("length of change value must be at least 1 bit")
// }
// symbol := (changes[byteIndex] >> 3) & 1
// bitsExtracted := 0
// stakerChange := 0
// for j := 0; j < lenValue; j++ {
// byteIndex++
// byteValue := changes[byteIndex] << bitOffset
// // byteValue <<= bitOffset
// bitsLeft := 8 - bitOffset
// if bitsExtracted+bitsLeft > lenValue {
// bitsLeft = lenValue - bitsExtracted
// bitOffset = bitsLeft
// } else {
// bitOffset = 0
// }
// byteValue = (byteValue >> (8 - bitsLeft)) & ((1 << bitsLeft) - 1)
// stakerChange = (stakerChange << bitsLeft) | int(byteValue)
// }
// if symbol == 1 {
// stakerChange *= -1
// }
// stakerChanges[sl.StakerAddrs[index]] = stakerChange
// }
// }
// }
// return stakerChanges, nil
// }

func getLatestOperatorPriceFloat(operatorInfo *types.OperatorInfo) sdkmath.LegacyDec {
latestIndex := len(operatorInfo.PriceList) - 1
return operatorInfo.PriceList[latestIndex].Price
Expand Down
Loading

0 comments on commit d2e117d

Please sign in to comment.