Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem: app hash mismatch occurs when upgrade to iavl v1.2.0 #1618

Merged
merged 17 commits into from
Oct 5, 2024
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## UNRELEASED

### State Machine Breaking

* (memiavl)[#1618](https://github.com/crypto-org-chain/cronos/pull/1618) memiavl change initial version logic to be
compatible with iavl 1.2.0.

### Improvements

* [#1592](https://github.com/crypto-org-chain/cronos/pull/1592) Change the default parallelism of the block-stm to minimum between GOMAXPROCS and NumCPU
Expand Down
2 changes: 0 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,6 @@ replace (
replace (
// Use cosmos keyring
github.com/99designs/keyring => github.com/cosmos/keyring v1.2.0
// FIXME: address the replacement when bump v1.2.0 to that triggers app hash mismatch in upgrade test
github.com/cosmos/iavl => github.com/cosmos/iavl v1.1.2
// dgrijalva/jwt-go is deprecated and doesn't receive security updates.
// TODO: remove it: https://github.com/cosmos/cosmos-sdk/issues/13134
github.com/dgrijalva/jwt-go => github.com/golang-jwt/jwt/v4 v4.4.2
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -393,8 +393,8 @@ github.com/cosmos/gogogateway v1.2.0/go.mod h1:iQpLkGWxYcnCdz5iAdLcRBSw3h7NXeOkZ
github.com/cosmos/gogoproto v1.4.2/go.mod h1:cLxOsn1ljAHSV527CHOtaIP91kK6cCrZETRBrkzItWU=
github.com/cosmos/gogoproto v1.7.0 h1:79USr0oyXAbxg3rspGh/m4SWNyoz/GLaAh0QlCe2fro=
github.com/cosmos/gogoproto v1.7.0/go.mod h1:yWChEv5IUEYURQasfyBW5ffkMHR/90hiHgbNgrtp4j0=
github.com/cosmos/iavl v1.1.2 h1:zL9FK7C4L/P4IF1Dm5fIwz0WXCnn7Bp1M2FxH0ayM7Y=
github.com/cosmos/iavl v1.1.2/go.mod h1:jLeUvm6bGT1YutCaL2fIar/8vGUE8cPZvh/gXEWDaDM=
github.com/cosmos/iavl v1.2.0 h1:kVxTmjTh4k0Dh1VNL046v6BXqKziqMDzxo93oh3kOfM=
github.com/cosmos/iavl v1.2.0/go.mod h1:HidWWLVAtODJqFD6Hbne2Y0q3SdxByJepHUOeoH4LiI=
github.com/cosmos/ibc-go/modules/apps/callbacks v0.0.0-20240913130017-6b2554360c0e h1:jMqihcJRBdpRrKGOMS1bDyyoo2JoQxv4QmMCwK3HSvI=
github.com/cosmos/ibc-go/modules/apps/callbacks v0.0.0-20240913130017-6b2554360c0e/go.mod h1:akR14gsU5YD5S1G5I6lOI7z51OjR1vJko06Rs/3/Ym0=
github.com/cosmos/ibc-go/modules/capability v1.0.1 h1:ibwhrpJ3SftEEZRxCRkH0fQZ9svjthrX2+oXdZvzgGI=
Expand Down
5 changes: 2 additions & 3 deletions gomod2nix.toml
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,8 @@ schema = 3
version = "v1.7.0"
hash = "sha256-ZkEUImxBBo8Q/6c7tVR0rybpLbtlplzvgfLl5xvtV00="
[mod."github.com/cosmos/iavl"]
version = "v1.1.2"
hash = "sha256-fhh5fN1BMDxbF4PobERMQdIb9vIrxaSl0tRXas0WKmc="
replaced = "github.com/cosmos/iavl"
version = "v1.2.0"
hash = "sha256-NYSt6LOGyspP6eZXo9e5+2MFwyrWxD/rp2dRTtlWg2E="
yihuang marked this conversation as resolved.
Show resolved Hide resolved
[mod."github.com/cosmos/ibc-go/modules/apps/callbacks"]
version = "v0.0.0-20240913130017-6b2554360c0e"
hash = "sha256-mL+g8WB+fLUlTKMnrdn8rTgw2gqXsHlky3/BTsyfek0="
Expand Down
3 changes: 1 addition & 2 deletions integration_tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,9 @@ def wait_for_block(cli, height, timeout=240):
print(f"get sync status failed: {e}", file=sys.stderr)
else:
current_height = int(get_sync_info(status)["latest_block_height"])
print("debug current height", current_height)
print("current block height", current_height)
if current_height >= height:
break
print("current block height", current_height)
time.sleep(0.5)
else:
raise TimeoutError(f"wait for block {height} timeout")
Expand Down
2 changes: 1 addition & 1 deletion memiavl/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ require (
github.com/alitto/pond v1.8.3
github.com/cosmos/cosmos-db v1.0.2
github.com/cosmos/gogoproto v1.4.11
github.com/cosmos/iavl v1.1.2
github.com/cosmos/iavl v1.2.0
github.com/cosmos/ics23/go v0.10.0
github.com/ledgerwatch/erigon-lib v0.0.0-20230210071639-db0e7ed11263
github.com/stretchr/testify v1.8.4
Expand Down
4 changes: 2 additions & 2 deletions memiavl/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ github.com/cosmos/cosmos-db v1.0.2 h1:hwMjozuY1OlJs/uh6vddqnk9j7VamLv+0DBlbEXbAK
github.com/cosmos/cosmos-db v1.0.2/go.mod h1:Z8IXcFJ9PqKK6BIsVOB3QXtkKoqUOp1vRvPT39kOXEA=
github.com/cosmos/gogoproto v1.4.11 h1:LZcMHrx4FjUgrqQSWeaGC1v/TeuVFqSLa43CC6aWR2g=
github.com/cosmos/gogoproto v1.4.11/go.mod h1:/g39Mh8m17X8Q/GDEs5zYTSNaNnInBSohtaxzQnYq1Y=
github.com/cosmos/iavl v1.1.2 h1:zL9FK7C4L/P4IF1Dm5fIwz0WXCnn7Bp1M2FxH0ayM7Y=
github.com/cosmos/iavl v1.1.2/go.mod h1:jLeUvm6bGT1YutCaL2fIar/8vGUE8cPZvh/gXEWDaDM=
github.com/cosmos/iavl v1.2.0 h1:kVxTmjTh4k0Dh1VNL046v6BXqKziqMDzxo93oh3kOfM=
github.com/cosmos/iavl v1.2.0/go.mod h1:HidWWLVAtODJqFD6Hbne2Y0q3SdxByJepHUOeoH4LiI=
github.com/cosmos/ics23/go v0.10.0 h1:iXqLLgp2Lp+EdpIuwXTYIQU+AiHj9mOC2X9ab++bZDM=
github.com/cosmos/ics23/go v0.10.0/go.mod h1:ZfJSmng/TBNTBkFemHHHj5YY7VAU/MBU980F4VU1NG0=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
Expand Down
4 changes: 2 additions & 2 deletions memiavl/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@
// doImport a stream of `ExportNode`s into a new snapshot.
func doImport(dir string, version int64, nodes <-chan *ExportNode) (returnErr error) {
if version > int64(math.MaxUint32) {
return errors.New("version overflows uint32")
return fmt.Errorf("version overflows uint32: %d", version)

Check warning on line 134 in memiavl/import.go

View check run for this annotation

Codecov / codecov/patch

memiavl/import.go#L134

Added line #L134 was not covered by tests
}

return writeSnapshot(context.Background(), dir, uint32(version), func(w *snapshotWriter) (uint32, error) {
Expand Down Expand Up @@ -167,7 +167,7 @@

func (i *importer) Add(n *ExportNode) error {
if n.Version > int64(math.MaxUint32) {
return errors.New("version overflows uint32")
return fmt.Errorf("version overflows uint32: %d", n.Version)

Check warning on line 170 in memiavl/import.go

View check run for this annotation

Codecov / codecov/patch

memiavl/import.go#L170

Added line #L170 was not covered by tests
}

if n.Height == 0 {
Expand Down
6 changes: 4 additions & 2 deletions memiavl/multitree.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,10 @@ func (t *MultiTree) SetInitialVersion(initialVersion int64) error {

func (t *MultiTree) setInitialVersion(initialVersion int64) {
t.initialVersion = uint32(initialVersion)
for _, entry := range t.trees {
entry.Tree.initialVersion = t.initialVersion
if t.initialVersion > 1 {
for _, entry := range t.trees {
entry.Tree.version = t.initialVersion - 1
}
yihuang marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down
38 changes: 16 additions & 22 deletions memiavl/tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import (
"bytes"
"crypto/sha256"
"errors"
"fmt"
"math"

Expand All @@ -21,16 +20,14 @@

// verify change sets by replay them to rebuild iavl tree and verify the root hashes
type Tree struct {
version uint32
version, cowVersion uint32
yihuang marked this conversation as resolved.
Show resolved Hide resolved
// root node of empty tree is represented as `nil`
root Node
snapshot *Snapshot

// simple lru cache provided by iavl library
cache cache.Cache

initialVersion, cowVersion uint32

// when true, the get and iterator methods could return a slice pointing to mmaped blob files.
zeroCopy bool
}
Expand All @@ -44,14 +41,13 @@
}

// NewEmptyTree creates an empty tree at an arbitrary version.
func NewEmptyTree(version uint64, initialVersion uint32, cacheSize int) *Tree {
func NewEmptyTree(version uint64, cacheSize int) *Tree {
yihuang marked this conversation as resolved.
Show resolved Hide resolved
if version >= math.MaxUint32 {
panic("version overflows uint32")
}

return &Tree{
version: uint32(version),
initialVersion: initialVersion,
version: uint32(version),
// no need to copy if the tree is not backed by snapshot
zeroCopy: true,
cache: NewCache(cacheSize),
Expand All @@ -60,13 +56,16 @@

// New creates an empty tree at genesis version
func New(cacheSize int) *Tree {
return NewEmptyTree(0, 0, cacheSize)
return NewEmptyTree(0, cacheSize)
}

// New creates a empty tree with initial-version,
// it happens when a new store created at the middle of the chain.
func NewWithInitialVersion(initialVersion uint32, cacheSize int) *Tree {
return NewEmptyTree(0, initialVersion, cacheSize)
if initialVersion <= 1 {
return New(cacheSize)
}
return NewEmptyTree(uint64(initialVersion-1), cacheSize)
}

// NewFromSnapshot mmap the blob files and create the root node.
Expand Down Expand Up @@ -94,10 +93,14 @@
}

func (t *Tree) SetInitialVersion(initialVersion int64) error {
if initialVersion >= math.MaxUint32 {
switch {
case initialVersion >= math.MaxUint32:

Check warning on line 97 in memiavl/tree.go

View check run for this annotation

Codecov / codecov/patch

memiavl/tree.go#L96-L97

Added lines #L96 - L97 were not covered by tests
yihuang marked this conversation as resolved.
Show resolved Hide resolved
return fmt.Errorf("version overflows uint32: %d", initialVersion)
case initialVersion < 1:
t.version = 0
default:
t.version = uint32(initialVersion - 1)

Check warning on line 102 in memiavl/tree.go

View check run for this annotation

Codecov / codecov/patch

memiavl/tree.go#L99-L102

Added lines #L99 - L102 were not covered by tests
}
t.initialVersion = uint32(initialVersion)
return nil
}

Expand Down Expand Up @@ -146,15 +149,15 @@
// SaveVersion increases the version number and optionally updates the hashes
func (t *Tree) SaveVersion(updateHash bool) ([]byte, int64, error) {
if t.version >= uint32(math.MaxUint32) {
return nil, 0, errors.New("version overflows uint32")
return nil, 0, fmt.Errorf("version overflows uint32: %d", t.version)

Check warning on line 152 in memiavl/tree.go

View check run for this annotation

Codecov / codecov/patch

memiavl/tree.go#L152

Added line #L152 was not covered by tests
yihuang marked this conversation as resolved.
Show resolved Hide resolved
}

var hash []byte
if updateHash {
hash = t.RootHash()
}

t.version = nextVersionU32(t.version, t.initialVersion)
t.version = t.version + 1
return hash, int64(t.version), nil
}

Expand Down Expand Up @@ -284,12 +287,3 @@
t.root = nil
return err
}

// nextVersionU32 is compatible with existing golang iavl implementation.
// see: https://github.com/cosmos/iavl/pull/660
func nextVersionU32(v uint32, initialVersion uint32) uint32 {
if v == 0 && initialVersion > 1 {
return initialVersion
}
return v + 1
}
40 changes: 40 additions & 0 deletions memiavl/tree_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package memiavl

import (
"bytes"
"fmt"
"strconv"
"testing"
Expand All @@ -16,6 +17,9 @@ var (
ChangeSets []ChangeSet
RefHashes [][]byte
ExpectItems [][]pair

IAVLInitialVersion = 100
RefHashesInitialVersion [][]byte
)

func mockKVPairs(kvPairs ...string) []*KVPair {
Expand Down Expand Up @@ -62,14 +66,34 @@ func init() {
// generate ref hashes with ref impl
d := wrapper.NewDBWrapper(db.NewMemDB())
refTree := iavl.NewMutableTree(d, 0, true, log.NewNopLogger())
refTreeInitialVersion := iavl.NewMutableTree(d, 0, true, log.NewNopLogger(), iavl.InitialVersionOption(uint64(IAVLInitialVersion)))
for _, changes := range ChangeSets {
{
if err := applyChangeSetRef(refTreeInitialVersion, changes); err != nil {
panic(err)
}
workingHash := refTreeInitialVersion.WorkingHash()
refHash, _, err := refTreeInitialVersion.SaveVersion()
if err != nil {
panic(err)
}
if !bytes.Equal(workingHash, refHash) {
panic(fmt.Sprintf("working hash %X != ref hash %X", workingHash, refHash))
}
RefHashesInitialVersion = append(RefHashesInitialVersion, refHash)
}

yihuang marked this conversation as resolved.
Show resolved Hide resolved
if err := applyChangeSetRef(refTree, changes); err != nil {
panic(err)
}
workingHash := refTree.WorkingHash()
refHash, _, err := refTree.SaveVersion()
if err != nil {
panic(err)
}
if !bytes.Equal(workingHash, refHash) {
panic(fmt.Sprintf("working hash %X != ref hash %X", workingHash, refHash))
}
RefHashes = append(RefHashes, refHash)
}

Expand Down Expand Up @@ -154,10 +178,26 @@ func TestRootHashes(t *testing.T) {

for i, changes := range ChangeSets {
tree.ApplyChangeSet(changes)
workingHash := tree.RootHash()
hash, v, err := tree.SaveVersion(true)
require.NoError(t, err)
require.Equal(t, i+1, int(v))
require.Equal(t, RefHashes[i], hash)
require.Equal(t, hash, workingHash)
}
}

func TestRootHashesInitialVersion(t *testing.T) {
tree := NewWithInitialVersion(uint32(IAVLInitialVersion), 0)

for i, changes := range ChangeSets {
tree.ApplyChangeSet(changes)
workingHash := tree.RootHash()
hash, v, err := tree.SaveVersion(true)
require.NoError(t, err)
require.Equal(t, IAVLInitialVersion+i, int(v))
require.Equal(t, RefHashesInitialVersion[i], hash)
require.Equal(t, hash, workingHash)
yihuang marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down
Loading