Skip to content

Commit

Permalink
Merge branch 'release/v0.48.x' into mergify/bp/release/v0.48.x/pr-1316
Browse files Browse the repository at this point in the history
  • Loading branch information
0Tech committed Mar 29, 2024
2 parents 9a1ac63 + a14fa3e commit 4a511d7
Show file tree
Hide file tree
Showing 36 changed files with 354 additions and 148 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,21 @@ Ref: https://keepachangelog.com/en/1.0.0/
### Features

### Improvements
* (types) [\#1314](https://github.com/Finschia/finschia-sdk/pull/1314) replace IsEqual with Equal

### Bug Fixes
* (x/auth) [#1281](https://github.com/Finschia/finschia-sdk/pull/1281) `ModuleAccount.Validate` now reports a nil `.BaseAccount` instead of panicking. (backport #1274)
* (x/foundation) [\#1283](https://github.com/Finschia/finschia-sdk/pull/1283) add init logic of foundation module accounts to InitGenesis in order to eliminate potential panic (backport #1277)
* (x/collection) [\#1282](https://github.com/Finschia/finschia-sdk/pull/1282) eliminates potential risk for Insufficient Sanity Check of tokenID in Genesis (backport #1276)
* (x/collection) [\#1290](https://github.com/Finschia/finschia-sdk/pull/1290) export x/collection params into genesis (backport #1268)
* (x/foundation) [\#1295](https://github.com/Finschia/finschia-sdk/pull/1295) add missing error handling for migration
* (sec) [\#1302](https://github.com/Finschia/finschia-sdk/pull/1302) remove map iteration non-determinism with keys + sorting
* (client) [\#1303](https://github.com/Finschia/finschia-sdk/pull/1303) fix possible overflow in BuildUnsignedTx
* (types) [\#1299](https://github.com/Finschia/finschia-sdk/pull/1299) add missing nil checks
* (x/staking) [\#1301](https://github.com/Finschia/finschia-sdk/pull/1301) Use bytes instead of string comparison in delete validator queue (backport cosmos/cosmos-sdk#12303)
* (client/keys) [#1312](https://github.com/Finschia/finschia-sdk/pull/1312) ignore error when key not found in `keys delete`
* (store) [\#1310](https://github.com/Finschia/finschia-sdk/pull/1310) fix app-hash mismatch if upgrade migration commit is interrupted(backport cosmos/cosmos-sdk#13530)
* (types) [\#1313](https://github.com/Finschia/finschia-sdk/pull/1313) fix correctly coalesce coins even with repeated denominations(backport cosmos/cosmos-sdk#13265)
* (x/crypto) [\#1316](https://github.com/Finschia/finschia-sdk/pull/1316) error if incorrect ledger public key (backport cosmos/cosmos-sdk#14460, cosmos/cosmos-sdk#19691)

### Removed
Expand Down
7 changes: 6 additions & 1 deletion baseapp/baseapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"reflect"
"sort"
"strings"
"sync"

Expand All @@ -16,6 +17,7 @@ import (
"github.com/Finschia/ostracon/crypto/tmhash"
"github.com/Finschia/ostracon/libs/log"
dbm "github.com/tendermint/tm-db"
"golang.org/x/exp/maps"

"github.com/Finschia/finschia-sdk/codec/types"
"github.com/Finschia/finschia-sdk/server/config"
Expand Down Expand Up @@ -272,7 +274,10 @@ func (app *BaseApp) MountKVStores(keys map[string]*sdk.KVStoreKey) {
// MountMemoryStores mounts all in-memory KVStores with the BaseApp's internal
// commit multi-store.
func (app *BaseApp) MountMemoryStores(keys map[string]*sdk.MemoryStoreKey) {
for _, memKey := range keys {
skeys := maps.Keys(keys)
sort.Strings(skeys)
for _, key := range skeys {
memKey := keys[key]
app.MountStore(memKey, sdk.StoreTypeMemory)
}
}
Expand Down
3 changes: 2 additions & 1 deletion client/keys/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ private keys stored in a ledger device cannot be deleted with the CLI.
for _, name := range args {
info, err := clientCtx.Keyring.Key(name)
if err != nil {
return err
cmd.PrintErrf("key %s not found\n", name)
continue
}

// confirm deletion, unless -y is passed
Expand Down
3 changes: 1 addition & 2 deletions client/keys/delete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ func Test_runDeleteCmd(t *testing.T) {
ctx := context.WithValue(context.Background(), client.ClientContextKey, &clientCtx)

err = cmd.ExecuteContext(ctx)
require.Error(t, err)
require.EqualError(t, err, "blah.info: key not found")
require.NoError(t, err)

// User confirmation missing
cmd.SetArgs([]string{
Expand Down
5 changes: 4 additions & 1 deletion client/tx/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package tx
import (
"errors"
"fmt"
"math/big"
"os"

"github.com/spf13/pflag"
Expand Down Expand Up @@ -212,7 +213,9 @@ func (f Factory) BuildUnsignedTx(msgs ...sdk.Msg) (client.TxBuilder, error) {
return nil, errors.New("cannot provide both fees and gas prices")
}

glDec := sdk.NewDec(int64(f.gas))
// f.gas is a uint64 and we should convert to LegacyDec
// without the risk of under/overflow via uint64->int64.
glDec := sdk.NewDecFromBigInt(new(big.Int).SetUint64(f.gas))

// Derive the fees based on the provided gas prices, where
// fee = ceil(gasPrice * gasLimit).
Expand Down
2 changes: 1 addition & 1 deletion simapp/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ func TestAddr(addr string, bech string) (sdk.AccAddress, error) {
// CheckBalance checks the balance of an account.
func CheckBalance(t *testing.T, app *SimApp, addr sdk.AccAddress, balances sdk.Coins) {
ctxCheck := app.BaseApp.NewContext(true, tmproto.Header{})
require.True(t, balances.IsEqual(app.BankKeeper.GetAllBalances(ctxCheck, addr)))
require.True(t, balances.Equal(app.BankKeeper.GetAllBalances(ctxCheck, addr)))
}

// SignCheckDeliver checks a generated signed transaction and simulates a
Expand Down
35 changes: 30 additions & 5 deletions store/rootmulti/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@ const (

const iavlDisablefastNodeDefault = true

// keysFromStoreKeyMap returns a slice of keys for the provided map lexically sorted by StoreKey.Name()
func keysFromStoreKeyMap[V any](m map[types.StoreKey]V) []types.StoreKey {
keys := make([]types.StoreKey, 0, len(m))
for key := range m {
keys = append(keys, key)
}
sort.Slice(keys, func(i, j int) bool {
ki, kj := keys[i], keys[j]
return ki.Name() < kj.Name()
})
return keys
}

// Store is composed of many CommitStores. Name contrasts with
// cacheMultiStore which is used for branching other MultiStores. It implements
// the CommitMultiStore interface.
Expand Down Expand Up @@ -691,8 +704,9 @@ func (rs *Store) Snapshot(height uint64, protoWriter protoio.Writer) error {
*iavl.Store
name string
}
stores := []namedStore{}
for key := range rs.stores {
stores := make([]namedStore, 0)
keys := keysFromStoreKeyMap(rs.stores)
for _, key := range keys {
switch store := rs.GetCommitKVStore(key).(type) {
case *iavl.Store:
stores = append(stores, namedStore{name: key.Name(), Store: store})
Expand Down Expand Up @@ -900,9 +914,12 @@ func (rs *Store) loadCommitStoreFromParams(key types.StoreKey, id types.CommitID
}

func (rs *Store) buildCommitInfo(version int64) *types.CommitInfo {
keys := keysFromStoreKeyMap(rs.stores)
storeInfos := []types.StoreInfo{}
for key, store := range rs.stores {
if store.GetStoreType() == types.StoreTypeTransient {
for _, key := range keys {
store := rs.stores[key]
storeType := store.GetStoreType()
if storeType == types.StoreTypeTransient || storeType == types.StoreTypeMemory {
continue
}
storeInfos = append(storeInfos, types.StoreInfo{
Expand Down Expand Up @@ -968,8 +985,16 @@ func commitStores(version int64, storeMap map[types.StoreKey]types.CommitKVStore
storeInfos := make([]types.StoreInfo, 0, len(storeMap))

for key, store := range storeMap {
commitID := store.Commit()
last := store.LastCommitID()

// If a commit event execution is interrupted, a new iavl store's version will be larger than the rootmulti's metadata, when the block is replayed, we should avoid committing that iavl store again.
var commitID types.CommitID
if last.Version >= version {
last.Version = version
commitID = last
} else {
commitID = store.Commit()
}
if store.GetStoreType() == types.StoreTypeTransient {
continue
}
Expand Down
75 changes: 75 additions & 0 deletions store/rootmulti/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -918,3 +918,78 @@ func TestSetIAVLDIsableFastNode(t *testing.T) {
multi.SetIAVLDisableFastNode(false)
require.Equal(t, multi.iavlDisableFastNode, false)
}

type commitKVStoreStub struct {
types.CommitKVStore
Committed int
}

func (stub *commitKVStoreStub) Commit() types.CommitID {
commitID := stub.CommitKVStore.Commit()
stub.Committed += 1
return commitID
}

func prepareStoreMap() map[types.StoreKey]types.CommitKVStore {
var db dbm.DB = dbm.NewMemDB()
store := NewStore(db, log.NewNopLogger())
store.MountStoreWithDB(types.NewKVStoreKey("iavl1"), types.StoreTypeIAVL, nil)
store.MountStoreWithDB(types.NewKVStoreKey("iavl2"), types.StoreTypeIAVL, nil)
store.MountStoreWithDB(types.NewTransientStoreKey("trans1"), types.StoreTypeTransient, nil)
err := store.LoadLatestVersion()
if err != nil {
panic(err)
}
return map[types.StoreKey]types.CommitKVStore{
testStoreKey1: &commitKVStoreStub{
CommitKVStore: store.GetStoreByName("iavl1").(types.CommitKVStore),
},
testStoreKey2: &commitKVStoreStub{
CommitKVStore: store.GetStoreByName("iavl2").(types.CommitKVStore),
},
testStoreKey3: &commitKVStoreStub{
CommitKVStore: store.GetStoreByName("trans1").(types.CommitKVStore),
},
}
}

func TestCommitStores(t *testing.T) {
testCases := []struct {
name string
committed int
exptectCommit int
}{
{
"when upgrade not get interrupted",
0,
1,
},
{
"when upgrade get interrupted once",
1,
0,
},
{
"when upgrade get interrupted twice",
2,
0,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
storeMap := prepareStoreMap()
store := storeMap[testStoreKey1].(*commitKVStoreStub)
for i := tc.committed; i > 0; i-- {
store.Commit()
}
store.Committed = 0
var version int64 = 1
res := commitStores(version, storeMap)
for _, s := range res.StoreInfos {
require.Equal(t, version, s.CommitId.Version)
}
require.Equal(t, version, res.Version)
require.Equal(t, tc.exptectCommit, store.Committed)
})
}
}
79 changes: 27 additions & 52 deletions types/coin.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package types

import (
"encoding/json"
"errors"
"fmt"
"regexp"
"sort"
Expand Down Expand Up @@ -44,6 +45,10 @@ func (coin Coin) Validate() error {
return err
}

if coin.Amount.IsNil() {
return errors.New("amount is nil")
}

if coin.Amount.IsNegative() {
return fmt.Errorf("negative coin amount: %v", coin.Amount)
}
Expand Down Expand Up @@ -82,12 +87,9 @@ func (coin Coin) IsLT(other Coin) bool {
}

// IsEqual returns true if the two sets of Coins have the same value
// Deprecated: Use Coin.Equal instead.
func (coin Coin) IsEqual(other Coin) bool {
if coin.Denom != other.Denom {
panic(fmt.Sprintf("invalid coin denominations; %s, %s", coin.Denom, other.Denom))
}

return coin.Amount.Equal(other.Amount)
return coin.Equal(other)
}

// Add adds amounts of two coins with same denom. If the coins differ in denom then
Expand Down Expand Up @@ -290,7 +292,7 @@ func (coins Coins) Add(coinsB ...Coin) Coins {
// denomination and addition only occurs when the denominations match, otherwise
// the coin is simply added to the sum assuming it's not zero.
// The function panics if `coins` or `coinsB` are not sorted (ascending).
func (coins Coins) safeAdd(coinsB Coins) Coins {
func (coins Coins) safeAdd(coinsB Coins) (coalesced Coins) {
// probably the best way will be to make Coins and interface and hide the structure
// definition (type alias)
if !coins.isSorted() {
Expand All @@ -300,51 +302,24 @@ func (coins Coins) safeAdd(coinsB Coins) Coins {
panic("Wrong argument: coins must be sorted")
}

sum := ([]Coin)(nil)
indexA, indexB := 0, 0
lenA, lenB := len(coins), len(coinsB)

for {
if indexA == lenA {
if indexB == lenB {
// return nil coins if both sets are empty
return sum
}

// return set B (excluding zero coins) if set A is empty
return append(sum, removeZeroCoins(coinsB[indexB:])...)
} else if indexB == lenB {
// return set A (excluding zero coins) if set B is empty
return append(sum, removeZeroCoins(coins[indexA:])...)
uniqCoins := make(map[string]Coins, len(coins)+len(coinsB))
// Traverse all the coins for each of the coins and coinsB.
for _, cL := range []Coins{coins, coinsB} {
for _, c := range cL {
uniqCoins[c.Denom] = append(uniqCoins[c.Denom], c)
}
}

coinA, coinB := coins[indexA], coinsB[indexB]

switch strings.Compare(coinA.Denom, coinB.Denom) {
case -1: // coin A denom < coin B denom
if !coinA.IsZero() {
sum = append(sum, coinA)
}

indexA++

case 0: // coin A denom == coin B denom
res := coinA.Add(coinB)
if !res.IsZero() {
sum = append(sum, res)
}

indexA++
indexB++

case 1: // coin A denom > coin B denom
if !coinB.IsZero() {
sum = append(sum, coinB)
}

indexB++
for denom, cL := range uniqCoins {
comboCoin := Coin{Denom: denom, Amount: NewInt(0)}
for _, c := range cL {
comboCoin = comboCoin.Add(c)
}
if !comboCoin.IsZero() {
coalesced = append(coalesced, comboCoin)
}
}
return coalesced.Sort()
}

// DenomsSubsetOf returns true if receiver's denom set
Expand Down Expand Up @@ -399,7 +374,7 @@ func (coins Coins) SafeSub(coinsB Coins) (Coins, bool) {
// a.IsAllLTE(a.Max(b))
// b.IsAllLTE(a.Max(b))
// a.IsAllLTE(c) && b.IsAllLTE(c) == a.Max(b).IsAllLTE(c)
// a.Add(b...).IsEqual(a.Min(b).Add(a.Max(b)...))
// a.Add(b...).Equal(a.Min(b).Add(a.Max(b)...))
//
// E.g.
// {1A, 3B, 2C}.Max({4A, 2B, 2C} == {4A, 3B, 2C})
Expand Down Expand Up @@ -445,7 +420,7 @@ func (coins Coins) Max(coinsB Coins) Coins {
// a.Min(b).IsAllLTE(a)
// a.Min(b).IsAllLTE(b)
// c.IsAllLTE(a) && c.IsAllLTE(b) == c.IsAllLTE(a.Min(b))
// a.Add(b...).IsEqual(a.Min(b).Add(a.Max(b)...))
// a.Add(b...).Equal(a.Min(b).Add(a.Max(b)...))
//
// E.g.
// {1A, 3B, 2C}.Min({4A, 2B, 2C} == {1A, 2B, 2C})
Expand Down Expand Up @@ -588,8 +563,8 @@ func (coins Coins) IsZero() bool {
return true
}

// IsEqual returns true if the two sets of Coins have the same value
func (coins Coins) IsEqual(coinsB Coins) bool {
// Equal returns true if the two sets of Coins have the same value
func (coins Coins) Equal(coinsB Coins) bool {
if len(coins) != len(coinsB) {
return false
}
Expand All @@ -598,7 +573,7 @@ func (coins Coins) IsEqual(coinsB Coins) bool {
coinsB = coinsB.Sort()

for i := 0; i < len(coins); i++ {
if !coins[i].IsEqual(coinsB[i]) {
if !coins[i].Equal(coinsB[i]) {
return false
}
}
Expand Down
Loading

0 comments on commit 4a511d7

Please sign in to comment.