diff --git a/CHANGELOG.md b/CHANGELOG.md index 09a8900a8..8cfa659d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 0.15.0-rc2 + +### Breaking Changes + +- [\#304](https://github.com/cosmos/iavl/pull/304) Empty trees now return hashes rather than `nil` + from e.g. `Hash()`, `WorkingHash()`, and `SaveVersion()`, for conformance with RFC-6962. + ## 0.15.0-rc1 (July 30, 2020) The IAVL project has moved from https://github.com/tendermint/iavl to diff --git a/basic_test.go b/basic_test.go index de7d09fd6..efb8d1f88 100644 --- a/basic_test.go +++ b/basic_test.go @@ -3,6 +3,7 @@ package iavl import ( "bytes" + "encoding/hex" mrand "math/rand" "sort" "testing" @@ -429,7 +430,7 @@ func TestTreeProof(t *testing.T) { db := db.NewMemDB() tree, err := NewMutableTree(db, 100) require.NoError(t, err) - assert.Equal(t, tree.Hash(), []byte(nil)) + assert.Equal(t, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", hex.EncodeToString(tree.Hash())) // should get false for proof with nil root value, proof, err := tree.GetWithProof([]byte("foo")) diff --git a/immutable_tree.go b/immutable_tree.go index a8be53b01..d9f16e9a1 100644 --- a/immutable_tree.go +++ b/immutable_tree.go @@ -127,18 +127,12 @@ func (t *ImmutableTree) Has(key []byte) bool { // Hash returns the root hash. func (t *ImmutableTree) Hash() []byte { - if t.root == nil { - return nil - } hash, _ := t.root.hashWithCount() return hash } // hashWithCount returns the root hash and hash count. func (t *ImmutableTree) hashWithCount() ([]byte, int64) { - if t.root == nil { - return nil, 0 - } return t.root.hashWithCount() } diff --git a/mutable_tree.go b/mutable_tree.go index 346683d6a..eb210dce0 100644 --- a/mutable_tree.go +++ b/mutable_tree.go @@ -74,10 +74,7 @@ func (tree *MutableTree) AvailableVersions() []int { // Hash returns the hash of the latest saved version of the tree, as returned // by SaveVersion. If no versions have been saved, Hash returns nil. func (tree *MutableTree) Hash() []byte { - if tree.version > 0 { - return tree.lastSaved.Hash() - } - return nil + return tree.lastSaved.Hash() } // WorkingHash returns the hash of the current working tree. diff --git a/node.go b/node.go index c399353a4..bec49eeb8 100644 --- a/node.go +++ b/node.go @@ -218,7 +218,12 @@ func (node *Node) _hash() []byte { // Hash the node and its descendants recursively. This usually mutates all // descendant nodes. Returns the node hash and number of nodes hashed. +// If the tree is empty (i.e. the node is nil), returns the hash of an empty input, +// to conform with RFC-6962. func (node *Node) hashWithCount() ([]byte, int64) { + if node == nil { + return sha256.New().Sum(nil), 0 + } if node.hash != nil { return node.hash, 0 } diff --git a/tree_test.go b/tree_test.go index fd123fef0..c875de13d 100644 --- a/tree_test.go +++ b/tree_test.go @@ -301,24 +301,24 @@ func TestVersionedEmptyTree(t *testing.T) { require.NoError(err) hash, v, err := tree.SaveVersion() - require.Nil(hash) - require.EqualValues(1, v) require.NoError(err) + require.Equal("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", hex.EncodeToString(hash)) + require.EqualValues(1, v) hash, v, err = tree.SaveVersion() - require.Nil(hash) - require.EqualValues(2, v) require.NoError(err) + require.Equal("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", hex.EncodeToString(hash)) + require.EqualValues(2, v) hash, v, err = tree.SaveVersion() - require.Nil(hash) - require.EqualValues(3, v) require.NoError(err) + require.Equal("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", hex.EncodeToString(hash)) + require.EqualValues(3, v) hash, v, err = tree.SaveVersion() - require.Nil(hash) - require.EqualValues(4, v) require.NoError(err) + require.Equal("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", hex.EncodeToString(hash)) + require.EqualValues(4, v) require.EqualValues(4, tree.Version()) @@ -1183,20 +1183,22 @@ func TestVersionedTreeHash(t *testing.T) { tree, err := getTestTree(0) require.NoError(err) - require.Nil(tree.Hash()) + require.Equal("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", hex.EncodeToString(tree.Hash())) tree.Set([]byte("I"), []byte("D")) - require.Nil(tree.Hash()) + require.Equal("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", hex.EncodeToString(tree.Hash())) - hash1, _, _ := tree.SaveVersion() + hash1, _, err := tree.SaveVersion() + require.NoError(err) tree.Set([]byte("I"), []byte("F")) require.EqualValues(hash1, tree.Hash()) - hash2, _, _ := tree.SaveVersion() + hash2, _, err := tree.SaveVersion() + require.NoError(err) val, proof, err := tree.GetVersionedWithProof([]byte("I"), 2) require.NoError(err) - require.EqualValues(val, []byte("F")) + require.EqualValues([]byte("F"), val) require.NoError(proof.Verify(hash2)) require.NoError(proof.VerifyItem([]byte("I"), val)) }