Skip to content

Commit c680b2b

Browse files
authored
Merge pull request #324 from arkeonetwork/fix-potential-chain-halt-due-to-non-determinism-and-local-time-usage
fix: potential chain halt due to non-determinism and local time usage
2 parents 7869f4f + 015ea8d commit c680b2b

8 files changed

+117
-37
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ Contains all the PRs that improved the code without changing the behaviors.
4646
### Fixed
4747
- validator rewards payout
4848
49+
### Fixed
50+
- fix non deterministic map iteration to sorted iteration
51+
4952
# v1.0.5-Prerelease
5053
### Added
5154
- Arkeo testnet validator addresses to airdrop

x/arkeo/configs/config.go

+84-26
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import (
44
"encoding/json"
55
"fmt"
66
"regexp"
7+
"sort"
78
"strconv"
89
"strings"
9-
"time"
1010
)
1111

1212
var (
@@ -20,7 +20,7 @@ var (
2020
stringOverrides = map[ConfigName]string{}
2121
)
2222

23-
var BlockTime = 5 * time.Second
23+
// var BlockTime = 5 * time.Second
2424

2525
// ConfigVals implement ConfigValues interface
2626
type ConfigVals struct {
@@ -71,23 +71,40 @@ func (cv *ConfigVals) GetStringValue(name ConfigName) string {
7171
}
7272

7373
func (cv *ConfigVals) String() string {
74+
// get all the keys
75+
int64Keys := make([]ConfigName, 0, len(cv.int64values))
76+
for k := range cv.int64values {
77+
int64Keys = append(int64Keys, k)
78+
}
79+
sort.Slice(int64Keys, func(i, j int) bool {
80+
return int64Keys[i].String() < int64Keys[j].String()
81+
})
82+
83+
boolKeys := make([]ConfigName, 0, len(cv.boolValues))
84+
for k := range cv.boolValues {
85+
boolKeys = append(boolKeys, k)
86+
}
87+
sort.Slice(boolKeys, func(i, j int) bool {
88+
return boolKeys[i].String() < boolKeys[j].String()
89+
})
90+
7491
sb := strings.Builder{}
75-
// analyze-ignore(map-iteration)
76-
for k, v := range cv.int64values {
92+
for _, k := range int64Keys {
7793
if overrideValue, ok := int64Overrides[k]; ok {
7894
sb.WriteString(fmt.Sprintf("%s:%d\n", k, overrideValue))
7995
continue
8096
}
81-
sb.WriteString(fmt.Sprintf("%s:%d\n", k, v))
97+
sb.WriteString(fmt.Sprintf("%s:%d\n", k, cv.int64values[k]))
8298
}
83-
// analyze-ignore(map-iteration)
84-
for k, v := range cv.boolValues {
99+
100+
for _, k := range boolKeys {
85101
if overrideValue, ok := boolOverrides[k]; ok {
86102
sb.WriteString(fmt.Sprintf("%s:%v\n", k, overrideValue))
87103
continue
88104
}
89-
sb.WriteString(fmt.Sprintf("%s:%v\n", k, v))
105+
sb.WriteString(fmt.Sprintf("%s:%v\n", k, cv.boolValues[k]))
90106
}
107+
91108
return sb.String()
92109
}
93110

@@ -101,29 +118,70 @@ func (cv ConfigVals) MarshalJSON() ([]byte, error) {
101118
result.Int64Values = make(map[string]int64)
102119
result.BoolValues = make(map[string]bool)
103120
result.StringValues = make(map[string]string)
104-
// analyze-ignore(map-iteration)
105-
for k, v := range cv.int64values {
106-
result.Int64Values[k.String()] = v
121+
122+
// get and sort all keys including overrides
123+
int64Keys := make([]ConfigName, 0, len(cv.int64values)+len(int64Overrides))
124+
for k := range cv.int64values {
125+
int64Keys = append(int64Keys, k)
126+
}
127+
for k := range int64Overrides {
128+
if _, exists := cv.int64values[k]; !exists {
129+
int64Keys = append(int64Keys, k)
130+
}
131+
}
132+
sort.Slice(int64Keys, func(i, j int) bool {
133+
return int64Keys[i].String() < int64Keys[j].String()
134+
})
135+
136+
// Same for bool and string keys
137+
boolKeys := make([]ConfigName, 0, len(cv.boolValues)+len(boolOverrides))
138+
for k := range cv.boolValues {
139+
boolKeys = append(boolKeys, k)
140+
}
141+
for k := range boolOverrides {
142+
if _, exists := cv.boolValues[k]; !exists {
143+
boolKeys = append(boolKeys, k)
144+
}
107145
}
108-
// analyze-ignore(map-iteration)
109-
for k, v := range int64Overrides {
110-
result.Int64Values[k.String()] = v
146+
sort.Slice(boolKeys, func(i, j int) bool {
147+
return boolKeys[i].String() < boolKeys[j].String()
148+
})
149+
150+
stringKeys := make([]ConfigName, 0, len(cv.stringValues)+len(stringOverrides))
151+
for k := range cv.stringValues {
152+
stringKeys = append(stringKeys, k)
111153
}
112-
// analyze-ignore(map-iteration)
113-
for k, v := range cv.boolValues {
114-
result.BoolValues[k.String()] = v
154+
for k := range stringOverrides {
155+
if _, exists := cv.stringValues[k]; !exists {
156+
stringKeys = append(stringKeys, k)
157+
}
115158
}
116-
// analyze-ignore(map-iteration)
117-
for k, v := range boolOverrides {
118-
result.BoolValues[k.String()] = v
159+
sort.Slice(stringKeys, func(i, j int) bool {
160+
return stringKeys[i].String() < stringKeys[j].String()
161+
})
162+
163+
for _, k := range int64Keys {
164+
if override, ok := int64Overrides[k]; ok {
165+
result.Int64Values[k.String()] = override
166+
} else {
167+
result.Int64Values[k.String()] = cv.int64values[k]
168+
}
119169
}
120-
// analyze-ignore(map-iteration)
121-
for k, v := range cv.stringValues {
122-
result.StringValues[k.String()] = v
170+
171+
for _, k := range boolKeys {
172+
if override, ok := boolOverrides[k]; ok {
173+
result.BoolValues[k.String()] = override
174+
} else {
175+
result.BoolValues[k.String()] = cv.boolValues[k]
176+
}
123177
}
124-
// analyze-ignore(map-iteration)
125-
for k, v := range stringOverrides {
126-
result.StringValues[k.String()] = v
178+
179+
for _, k := range stringKeys {
180+
if override, ok := stringOverrides[k]; ok {
181+
result.StringValues[k.String()] = override
182+
} else {
183+
result.StringValues[k.String()] = cv.stringValues[k]
184+
}
127185
}
128186

129187
return json.MarshalIndent(result, "", " ")

x/arkeo/genesis_test.go

-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
)
1616

1717
func TestGenesis(t *testing.T) {
18-
1918
ctx, k := keepertest.ArkeoKeeper(t)
2019

2120
genesisState := types.GenesisState{

x/arkeo/keeper/msg_server_close_contract.go

-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ func (k msgServer) CloseContractHandle(ctx cosmos.Context, msg *types.MsgCloseCo
9090
}
9191

9292
if contract.IsPayAsYouGo() {
93-
9493
k.RemoveContractExpirationSet(ctx, contract.Expiration())
9594
// add a new expiration return deposit to user
9695
newHeight := ctx.BlockHeight() + contract.SettlementDuration

x/arkeo/keeper/msg_server_open_contract.go

-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,6 @@ func (k msgServer) OpenContractValidate(ctx cosmos.Context, msg *types.MsgOpenCo
126126
}
127127

128128
func (k msgServer) OpenContractHandle(ctx cosmos.Context, msg *types.MsgOpenContract) error {
129-
130129
// set back client as delegate if delegate is empty
131130
if msg.Delegate == "" {
132131
msg.Delegate = msg.Client

x/claim/keeper/claim.go

+25-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package keeper
22

33
import (
44
"context"
5+
"sort"
56
"strings"
67

78
sdkerror "cosmossdk.io/errors"
@@ -96,13 +97,24 @@ func (k Keeper) GetClaimRecords(ctx sdk.Context, chain types.Chain) ([]types.Cla
9697

9798
func (k Keeper) GetAllClaimRecords(ctx sdk.Context) ([]types.ClaimRecord, error) {
9899
claimRecords := []types.ClaimRecord{}
100+
101+
chains := make([]types.Chain, 0, len(types.Chain_name))
99102
for chain := range types.Chain_name {
100-
records, err := k.GetClaimRecords(ctx, types.Chain(chain))
103+
chains = append(chains, types.Chain(chain))
104+
}
105+
106+
sort.Slice(chains, func(i, j int) bool {
107+
return int32(chains[i]) < int32(chains[j])
108+
})
109+
110+
for _, chain := range chains {
111+
records, err := k.GetClaimRecords(ctx, chain)
101112
if err != nil {
102113
return nil, err
103114
}
104115
claimRecords = append(claimRecords, records...)
105116
}
117+
106118
return claimRecords, nil
107119
}
108120

@@ -135,9 +147,19 @@ func (k Keeper) GetUserTotalClaimable(ctx sdk.Context, addr string, chain types.
135147
return sdk.Coin{}, nil
136148
}
137149

138-
totalClaimable := sdk.NewCoin(claimRecord.AmountClaim.Denom, cosmos.ZeroInt())
150+
actions := make([]types.Action, 0, len(types.Action_name))
139151
for action := range types.Action_name {
140-
claimableForAction, err := k.GetClaimableAmountForAction(ctx, addr, types.Action(action), chain)
152+
actions = append(actions, types.Action(action))
153+
}
154+
155+
sort.Slice(actions, func(i, j int) bool {
156+
return int32(actions[i]) < int32(actions[j])
157+
})
158+
159+
totalClaimable := sdk.NewCoin(claimRecord.AmountClaim.Denom, cosmos.ZeroInt())
160+
161+
for _, action := range actions {
162+
claimableForAction, err := k.GetClaimableAmountForAction(ctx, addr, action, chain)
141163
if err != nil {
142164
return sdk.Coin{}, err
143165
}

x/claim/keeper/msg_server_claim_thorchain_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ func TestClaimThorchainTestnetAddress(t *testing.T) {
1717
config := sdk.GetConfig()
1818
config.SetBech32PrefixForAccount("tarkeo", "tarkeopub")
1919

20-
arkeoServerAddress, err := sdk.AccAddressFromBech32("tarkeo1z02ke8639m47g9dfrheegr2u9zecegt5qvtj00")
20+
arkeoServerAddress, err := sdk.AccAddressFromBech32("tarkeo1zsafqx0qk6rp2vvs97n9udylquj7mfkt8mfypq")
2121
require.NoError(t, err)
2222

2323
fromAddr := utils.GetRandomArkeoAddress()
@@ -92,7 +92,7 @@ func TestClaimThorchainMainnetAddress(t *testing.T) {
9292
config := sdk.GetConfig()
9393
config.SetBech32PrefixForAccount("arkeo", "arkeopub")
9494

95-
arkeoServerAddress, err := sdk.AccAddressFromBech32("arkeo1z02ke8639m47g9dfrheegr2u9zecegt50fjg7v")
95+
arkeoServerAddress, err := sdk.AccAddressFromBech32("arkeo1zsafqx0qk6rp2vvs97n9udylquj7mfktg7s7sr")
9696
require.NoError(t, err)
9797

9898
fromAddr := utils.GetRandomArkeoAddress()
@@ -167,7 +167,7 @@ func TestClaimThorchainFailureCases(t *testing.T) {
167167
config := sdk.GetConfig()
168168
config.SetBech32PrefixForAccount("arkeo", "arkeopub")
169169

170-
arkeoServerAddress, err := sdk.AccAddressFromBech32("arkeo1z02ke8639m47g9dfrheegr2u9zecegt50fjg7v")
170+
arkeoServerAddress, err := sdk.AccAddressFromBech32("arkeo1zsafqx0qk6rp2vvs97n9udylquj7mfktg7s7sr")
171171
require.NoError(t, err)
172172

173173
fromAddr := utils.GetRandomArkeoAddress()

x/claim/types/params.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ var (
2424

2525
var (
2626
KeyAirdropStartTime = []byte("AirdropStartTime")
27-
DeafultAirdropStartTime time.Time = time.Now().UTC()
27+
DefaultAirdropStartTime time.Time = time.Date(2025, 5, 1, 0, 0, 0, 0, time.UTC)
2828
)
2929

3030
var _ paramtypes.ParamSet = (*Params)(nil)
@@ -50,7 +50,7 @@ func DefaultParams() Params {
5050
ClaimDenom: DefaultClaimDenom,
5151
DurationUntilDecay: DefaultDurationUntilDecay,
5252
DurationOfDecay: DefaultDurationOfDecay,
53-
AirdropStartTime: DeafultAirdropStartTime,
53+
AirdropStartTime: DefaultAirdropStartTime,
5454
}
5555
}
5656

0 commit comments

Comments
 (0)