From ec53ce359bb8f011e4dbb715da098bea08c32ded Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Sun, 13 Jan 2019 17:02:38 -0600 Subject: [PATCH] Simple merkle rfc compatibility (#2713) * Begin simple merkle compatibility PR * Fix query_test * Use trillian test vectors * Change the split point per RFC 6962 * update spec * refactor innerhash to match spec * Update changelog * Address @liamsi's comments * Write the comment requested by @liamsi --- CHANGELOG_PENDING.md | 1 + blockchain/store_test.go | 2 +- crypto/merkle/hash.go | 21 +++++++ crypto/merkle/proof_simple_value.go | 8 +-- crypto/merkle/rfc6962_test.go | 97 +++++++++++++++++++++++++++++ crypto/merkle/simple_map_test.go | 12 ++-- crypto/merkle/simple_proof.go | 19 +++--- crypto/merkle/simple_tree.go | 38 +++++------ crypto/merkle/simple_tree_test.go | 36 ++++++++--- docs/spec/blockchain/encoding.md | 70 +++++++++------------ lite/proxy/query_test.go | 6 +- types/part_set.go | 13 +--- types/results_test.go | 2 +- types/tx.go | 13 ++-- types/tx_test.go | 7 +-- 15 files changed, 233 insertions(+), 112 deletions(-) create mode 100644 crypto/merkle/hash.go create mode 100644 crypto/merkle/rfc6962_test.go diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 83ec4d8fd..8012832f2 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -14,6 +14,7 @@ Special thanks to external contributors on this release: * Go API * Blockchain Protocol + * [merkle] \#2713 Merkle trees now match the RFC 6962 specification * P2P Protocol diff --git a/blockchain/store_test.go b/blockchain/store_test.go index a52039fa4..8059072e1 100644 --- a/blockchain/store_test.go +++ b/blockchain/store_test.go @@ -311,7 +311,7 @@ func TestLoadBlockPart(t *testing.T) { gotPart, _, panicErr := doFn(loadPart) require.Nil(t, panicErr, "an existent and proper block should not panic") require.Nil(t, res, "a properly saved block should return a proper block") - require.Equal(t, gotPart.(*types.Part).Hash(), part1.Hash(), + require.Equal(t, gotPart.(*types.Part), part1, "expecting successful retrieval of previously saved block") } diff --git a/crypto/merkle/hash.go b/crypto/merkle/hash.go new file mode 100644 index 000000000..4e24046ac --- /dev/null +++ b/crypto/merkle/hash.go @@ -0,0 +1,21 @@ +package merkle + +import ( + "github.com/tendermint/tendermint/crypto/tmhash" +) + +// TODO: make these have a large predefined capacity +var ( + leafPrefix = []byte{0} + innerPrefix = []byte{1} +) + +// returns tmhash(0x00 || leaf) +func leafHash(leaf []byte) []byte { + return tmhash.Sum(append(leafPrefix, leaf...)) +} + +// returns tmhash(0x01 || left || right) +func innerHash(left []byte, right []byte) []byte { + return tmhash.Sum(append(innerPrefix, append(left, right...)...)) +} diff --git a/crypto/merkle/proof_simple_value.go b/crypto/merkle/proof_simple_value.go index 904b6e5ec..247921ad5 100644 --- a/crypto/merkle/proof_simple_value.go +++ b/crypto/merkle/proof_simple_value.go @@ -71,11 +71,11 @@ func (op SimpleValueOp) Run(args [][]byte) ([][]byte, error) { hasher.Write(value) // does not error vhash := hasher.Sum(nil) + bz := new(bytes.Buffer) // Wrap to hash the KVPair. - hasher = tmhash.New() - encodeByteSlice(hasher, []byte(op.key)) // does not error - encodeByteSlice(hasher, []byte(vhash)) // does not error - kvhash := hasher.Sum(nil) + encodeByteSlice(bz, []byte(op.key)) // does not error + encodeByteSlice(bz, []byte(vhash)) // does not error + kvhash := leafHash(bz.Bytes()) if !bytes.Equal(kvhash, op.Proof.LeafHash) { return nil, cmn.NewError("leaf hash mismatch: want %X got %X", op.Proof.LeafHash, kvhash) diff --git a/crypto/merkle/rfc6962_test.go b/crypto/merkle/rfc6962_test.go new file mode 100644 index 000000000..b6413b479 --- /dev/null +++ b/crypto/merkle/rfc6962_test.go @@ -0,0 +1,97 @@ +package merkle + +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// These tests were taken from https://github.com/google/trillian/blob/master/merkle/rfc6962/rfc6962_test.go, +// and consequently fall under the above license. +import ( + "bytes" + "encoding/hex" + "testing" + + "github.com/tendermint/tendermint/crypto/tmhash" +) + +func TestRFC6962Hasher(t *testing.T) { + _, leafHashTrail := trailsFromByteSlices([][]byte{[]byte("L123456")}) + leafHash := leafHashTrail.Hash + _, leafHashTrail = trailsFromByteSlices([][]byte{[]byte{}}) + emptyLeafHash := leafHashTrail.Hash + for _, tc := range []struct { + desc string + got []byte + want string + }{ + // Since creating a merkle tree of no leaves is unsupported here, we skip + // the corresponding trillian test vector. + + // Check that the empty hash is not the same as the hash of an empty leaf. + // echo -n 00 | xxd -r -p | sha256sum + { + desc: "RFC6962 Empty Leaf", + want: "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d"[:tmhash.Size*2], + got: emptyLeafHash, + }, + // echo -n 004C313233343536 | xxd -r -p | sha256sum + { + desc: "RFC6962 Leaf", + want: "395aa064aa4c29f7010acfe3f25db9485bbd4b91897b6ad7ad547639252b4d56"[:tmhash.Size*2], + got: leafHash, + }, + // echo -n 014E3132334E343536 | xxd -r -p | sha256sum + { + desc: "RFC6962 Node", + want: "aa217fe888e47007fa15edab33c2b492a722cb106c64667fc2b044444de66bbb"[:tmhash.Size*2], + got: innerHash([]byte("N123"), []byte("N456")), + }, + } { + t.Run(tc.desc, func(t *testing.T) { + wantBytes, err := hex.DecodeString(tc.want) + if err != nil { + t.Fatalf("hex.DecodeString(%x): %v", tc.want, err) + } + if got, want := tc.got, wantBytes; !bytes.Equal(got, want) { + t.Errorf("got %x, want %x", got, want) + } + }) + } +} + +func TestRFC6962HasherCollisions(t *testing.T) { + // Check that different leaves have different hashes. + leaf1, leaf2 := []byte("Hello"), []byte("World") + _, leafHashTrail := trailsFromByteSlices([][]byte{leaf1}) + hash1 := leafHashTrail.Hash + _, leafHashTrail = trailsFromByteSlices([][]byte{leaf2}) + hash2 := leafHashTrail.Hash + if bytes.Equal(hash1, hash2) { + t.Errorf("Leaf hashes should differ, but both are %x", hash1) + } + // Compute an intermediate subtree hash. + _, subHash1Trail := trailsFromByteSlices([][]byte{hash1, hash2}) + subHash1 := subHash1Trail.Hash + // Check that this is not the same as a leaf hash of their concatenation. + preimage := append(hash1, hash2...) + _, forgedHashTrail := trailsFromByteSlices([][]byte{preimage}) + forgedHash := forgedHashTrail.Hash + if bytes.Equal(subHash1, forgedHash) { + t.Errorf("Hasher is not second-preimage resistant") + } + // Swap the order of nodes and check that the hash is different. + _, subHash2Trail := trailsFromByteSlices([][]byte{hash2, hash1}) + subHash2 := subHash2Trail.Hash + if bytes.Equal(subHash1, subHash2) { + t.Errorf("Subtree hash does not depend on the order of leaves") + } +} diff --git a/crypto/merkle/simple_map_test.go b/crypto/merkle/simple_map_test.go index 7abde119d..366d9f390 100644 --- a/crypto/merkle/simple_map_test.go +++ b/crypto/merkle/simple_map_test.go @@ -13,14 +13,14 @@ func TestSimpleMap(t *testing.T) { values []string // each string gets converted to []byte in test want string }{ - {[]string{"key1"}, []string{"value1"}, "321d150de16dceb51c72981b432b115045383259b1a550adf8dc80f927508967"}, - {[]string{"key1"}, []string{"value2"}, "2a9e4baf321eac99f6eecc3406603c14bc5e85bb7b80483cbfc75b3382d24a2f"}, + {[]string{"key1"}, []string{"value1"}, "a44d3cc7daba1a4600b00a2434b30f8b970652169810d6dfa9fb1793a2189324"}, + {[]string{"key1"}, []string{"value2"}, "0638e99b3445caec9d95c05e1a3fc1487b4ddec6a952ff337080360b0dcc078c"}, // swap order with 2 keys - {[]string{"key1", "key2"}, []string{"value1", "value2"}, "c4d8913ab543ba26aa970646d4c99a150fd641298e3367cf68ca45fb45a49881"}, - {[]string{"key2", "key1"}, []string{"value2", "value1"}, "c4d8913ab543ba26aa970646d4c99a150fd641298e3367cf68ca45fb45a49881"}, + {[]string{"key1", "key2"}, []string{"value1", "value2"}, "8fd19b19e7bb3f2b3ee0574027d8a5a4cec370464ea2db2fbfa5c7d35bb0cff3"}, + {[]string{"key2", "key1"}, []string{"value2", "value1"}, "8fd19b19e7bb3f2b3ee0574027d8a5a4cec370464ea2db2fbfa5c7d35bb0cff3"}, // swap order with 3 keys - {[]string{"key1", "key2", "key3"}, []string{"value1", "value2", "value3"}, "b23cef00eda5af4548a213a43793f2752d8d9013b3f2b64bc0523a4791196268"}, - {[]string{"key1", "key3", "key2"}, []string{"value1", "value3", "value2"}, "b23cef00eda5af4548a213a43793f2752d8d9013b3f2b64bc0523a4791196268"}, + {[]string{"key1", "key2", "key3"}, []string{"value1", "value2", "value3"}, "1dd674ec6782a0d586a903c9c63326a41cbe56b3bba33ed6ff5b527af6efb3dc"}, + {[]string{"key1", "key3", "key2"}, []string{"value1", "value3", "value2"}, "1dd674ec6782a0d586a903c9c63326a41cbe56b3bba33ed6ff5b527af6efb3dc"}, } for i, tc := range tests { db := newSimpleMap() diff --git a/crypto/merkle/simple_proof.go b/crypto/merkle/simple_proof.go index fd6d07b88..f01dcdca1 100644 --- a/crypto/merkle/simple_proof.go +++ b/crypto/merkle/simple_proof.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" - "github.com/tendermint/tendermint/crypto/tmhash" cmn "github.com/tendermint/tendermint/libs/common" ) @@ -67,7 +66,8 @@ func SimpleProofsFromMap(m map[string][]byte) (rootHash []byte, proofs map[strin // Verify that the SimpleProof proves the root hash. // Check sp.Index/sp.Total manually if needed -func (sp *SimpleProof) Verify(rootHash []byte, leafHash []byte) error { +func (sp *SimpleProof) Verify(rootHash []byte, leaf []byte) error { + leafHash := leafHash(leaf) if sp.Total < 0 { return errors.New("Proof total must be positive") } @@ -128,19 +128,19 @@ func computeHashFromAunts(index int, total int, leafHash []byte, innerHashes [][ if len(innerHashes) == 0 { return nil } - numLeft := (total + 1) / 2 + numLeft := getSplitPoint(total) if index < numLeft { leftHash := computeHashFromAunts(index, numLeft, leafHash, innerHashes[:len(innerHashes)-1]) if leftHash == nil { return nil } - return simpleHashFromTwoHashes(leftHash, innerHashes[len(innerHashes)-1]) + return innerHash(leftHash, innerHashes[len(innerHashes)-1]) } rightHash := computeHashFromAunts(index-numLeft, total-numLeft, leafHash, innerHashes[:len(innerHashes)-1]) if rightHash == nil { return nil } - return simpleHashFromTwoHashes(innerHashes[len(innerHashes)-1], rightHash) + return innerHash(innerHashes[len(innerHashes)-1], rightHash) } } @@ -182,12 +182,13 @@ func trailsFromByteSlices(items [][]byte) (trails []*SimpleProofNode, root *Simp case 0: return nil, nil case 1: - trail := &SimpleProofNode{tmhash.Sum(items[0]), nil, nil, nil} + trail := &SimpleProofNode{leafHash(items[0]), nil, nil, nil} return []*SimpleProofNode{trail}, trail default: - lefts, leftRoot := trailsFromByteSlices(items[:(len(items)+1)/2]) - rights, rightRoot := trailsFromByteSlices(items[(len(items)+1)/2:]) - rootHash := simpleHashFromTwoHashes(leftRoot.Hash, rightRoot.Hash) + k := getSplitPoint(len(items)) + lefts, leftRoot := trailsFromByteSlices(items[:k]) + rights, rightRoot := trailsFromByteSlices(items[k:]) + rootHash := innerHash(leftRoot.Hash, rightRoot.Hash) root := &SimpleProofNode{rootHash, nil, nil, nil} leftRoot.Parent = root leftRoot.Right = rightRoot diff --git a/crypto/merkle/simple_tree.go b/crypto/merkle/simple_tree.go index 7aacb0889..e150c0d31 100644 --- a/crypto/merkle/simple_tree.go +++ b/crypto/merkle/simple_tree.go @@ -1,23 +1,9 @@ package merkle import ( - "github.com/tendermint/tendermint/crypto/tmhash" + "math/bits" ) -// simpleHashFromTwoHashes is the basic operation of the Merkle tree: Hash(left | right). -func simpleHashFromTwoHashes(left, right []byte) []byte { - var hasher = tmhash.New() - err := encodeByteSlice(hasher, left) - if err != nil { - panic(err) - } - err = encodeByteSlice(hasher, right) - if err != nil { - panic(err) - } - return hasher.Sum(nil) -} - // SimpleHashFromByteSlices computes a Merkle tree where the leaves are the byte slice, // in the provided order. func SimpleHashFromByteSlices(items [][]byte) []byte { @@ -25,11 +11,12 @@ func SimpleHashFromByteSlices(items [][]byte) []byte { case 0: return nil case 1: - return tmhash.Sum(items[0]) + return leafHash(items[0]) default: - left := SimpleHashFromByteSlices(items[:(len(items)+1)/2]) - right := SimpleHashFromByteSlices(items[(len(items)+1)/2:]) - return simpleHashFromTwoHashes(left, right) + k := getSplitPoint(len(items)) + left := SimpleHashFromByteSlices(items[:k]) + right := SimpleHashFromByteSlices(items[k:]) + return innerHash(left, right) } } @@ -44,3 +31,16 @@ func SimpleHashFromMap(m map[string][]byte) []byte { } return sm.Hash() } + +func getSplitPoint(length int) int { + if length < 1 { + panic("Trying to split a tree with size < 1") + } + uLength := uint(length) + bitlen := bits.Len(uLength) + k := 1 << uint(bitlen-1) + if k == length { + k >>= 1 + } + return k +} diff --git a/crypto/merkle/simple_tree_test.go b/crypto/merkle/simple_tree_test.go index 32edc652e..9abe321c3 100644 --- a/crypto/merkle/simple_tree_test.go +++ b/crypto/merkle/simple_tree_test.go @@ -34,7 +34,6 @@ func TestSimpleProof(t *testing.T) { // For each item, check the trail. for i, item := range items { - itemHash := tmhash.Sum(item) proof := proofs[i] // Check total/index @@ -43,30 +42,53 @@ func TestSimpleProof(t *testing.T) { require.Equal(t, proof.Total, total, "Unmatched totals: %d vs %d", proof.Total, total) // Verify success - err := proof.Verify(rootHash, itemHash) - require.NoError(t, err, "Verificatior failed: %v.", err) + err := proof.Verify(rootHash, item) + require.NoError(t, err, "Verification failed: %v.", err) // Trail too long should make it fail origAunts := proof.Aunts proof.Aunts = append(proof.Aunts, cmn.RandBytes(32)) - err = proof.Verify(rootHash, itemHash) + err = proof.Verify(rootHash, item) require.Error(t, err, "Expected verification to fail for wrong trail length") proof.Aunts = origAunts // Trail too short should make it fail proof.Aunts = proof.Aunts[0 : len(proof.Aunts)-1] - err = proof.Verify(rootHash, itemHash) + err = proof.Verify(rootHash, item) require.Error(t, err, "Expected verification to fail for wrong trail length") proof.Aunts = origAunts // Mutating the itemHash should make it fail. - err = proof.Verify(rootHash, MutateByteSlice(itemHash)) + err = proof.Verify(rootHash, MutateByteSlice(item)) require.Error(t, err, "Expected verification to fail for mutated leaf hash") // Mutating the rootHash should make it fail. - err = proof.Verify(MutateByteSlice(rootHash), itemHash) + err = proof.Verify(MutateByteSlice(rootHash), item) require.Error(t, err, "Expected verification to fail for mutated root hash") } } + +func Test_getSplitPoint(t *testing.T) { + tests := []struct { + length int + want int + }{ + {1, 0}, + {2, 1}, + {3, 2}, + {4, 2}, + {5, 4}, + {10, 8}, + {20, 16}, + {100, 64}, + {255, 128}, + {256, 128}, + {257, 256}, + } + for _, tt := range tests { + got := getSplitPoint(tt.length) + require.Equal(t, tt.want, got, "getSplitPoint(%d) = %v, want %v", tt.length, got, tt.want) + } +} diff --git a/docs/spec/blockchain/encoding.md b/docs/spec/blockchain/encoding.md index 689ebbd6a..aefe1e7f7 100644 --- a/docs/spec/blockchain/encoding.md +++ b/docs/spec/blockchain/encoding.md @@ -144,12 +144,17 @@ func MakeParts(obj interface{}, partSize int) []Part For an overview of Merkle trees, see [wikipedia](https://en.wikipedia.org/wiki/Merkle_tree) -A Simple Tree is a simple compact binary tree for a static list of items. Simple Merkle trees are used in numerous places in Tendermint to compute a cryptographic digest of a data structure. In a Simple Tree, the transactions and validation signatures of a block are hashed using this simple merkle tree logic. +We use the RFC 6962 specification of a merkle tree, instantiated with sha256 as the hash function. +Merkle trees are used throughout Tendermint to compute a cryptographic digest of a data structure. +The differences between RFC 6962 and the simplest form a merkle tree are that: -If the number of items is not a power of two, the tree will not be full -and some leaf nodes will be at different levels. Simple Tree tries to -keep both sides of the tree the same size, but the left side may be one -greater, for example: +1) leaf nodes and inner nodes have different hashes. + This is to prevent a proof to an inner node, claiming that it is the hash of the leaf. + The leaf nodes are `SHA256(0x00 || leaf_data)`, and inner nodes are `SHA256(0x01 || left_hash || right_hash)`. + +2) When the number of items isn't a power of two, the left half of the tree is as big as it could be. + (The smallest power of two less than the number of items) This allows new leaves to be added with less + recomputation. For example: ``` Simple Tree with 6 items Simple Tree with 7 items @@ -163,48 +168,31 @@ greater, for example: / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ - * h2 * h5 * * * h6 - / \ / \ / \ / \ / \ -h0 h1 h3 h4 h0 h1 h2 h3 h4 h5 -``` - -Tendermint always uses the `TMHASH` hash function, which is equivalent to -SHA256: - -``` -func TMHASH(bz []byte) []byte { - return SHA256(bz) -} + * * h4 h5 * * * h6 + / \ / \ / \ / \ / \ +h0 h1 h2 h3 h0 h1 h2 h3 h4 h5 ``` ### Simple Merkle Root -The function `SimpleMerkleRoot` is a simple recursive function defined as follows: +The function `MerkleRoot` is a simple recursive function defined as follows: ```go -func SimpleMerkleRoot(hashes [][]byte) []byte{ - switch len(hashes) { - case 0: - return nil - case 1: - return hashes[0] - default: - left := SimpleMerkleRoot(hashes[:(len(hashes)+1)/2]) - right := SimpleMerkleRoot(hashes[(len(hashes)+1)/2:]) - return SimpleConcatHash(left, right) - } -} - -func SimpleConcatHash(left, right []byte) []byte{ - left = encodeByteSlice(left) - right = encodeByteSlice(right) - return TMHASH(append(left, right)) +func MerkleRootFromLeafs(leafs [][]byte) []byte{ + switch len(items) { + case 0: + return nil + case 1: + return leafHash(leafs[0]) // SHA256(0x00 || leafs[0]) + default: + k := getSplitPoint(len(items)) // largest power of two smaller than items + left := MerkleRootFromLeafs(items[:k]) + right := MerkleRootFromLeafs(items[k:]) + return innerHash(left, right) // SHA256(0x01 || left || right) + } } ``` -Note that the leaves are Amino encoded as byte-arrays (ie. simple Uvarint length -prefix) before being concatenated together and hashed. - Note: we will abuse notion and invoke `SimpleMerkleRoot` with arguments of type `struct` or type `[]struct`. For `struct` arguments, we compute a `[][]byte` containing the hash of each field in the struct, in the same order the fields appear in the struct. @@ -214,7 +202,7 @@ For `[]struct` arguments, we compute a `[][]byte` by hashing the individual `str Proof that a leaf is in a Merkle tree consists of a simple structure: -``` +```golang type SimpleProof struct { Aunts [][]byte } @@ -222,7 +210,7 @@ type SimpleProof struct { Which is verified using the following: -``` +```golang func (proof SimpleProof) Verify(index, total int, leafHash, rootHash []byte) bool { computedHash := computeHashFromAunts(index, total, leafHash, proof.Aunts) return computedHash == rootHash @@ -238,7 +226,7 @@ func computeHashFromAunts(index, total int, leafHash []byte, innerHashes [][]byt assert(len(innerHashes) > 0) - numLeft := (total + 1) / 2 + numLeft := getSplitPoint(total) // largest power of 2 less than total if index < numLeft { leftHash := computeHashFromAunts(index, numLeft, leafHash, innerHashes[:len(innerHashes)-1]) assert(leftHash != nil) diff --git a/lite/proxy/query_test.go b/lite/proxy/query_test.go index 0e30d7558..d8d45df3b 100644 --- a/lite/proxy/query_test.go +++ b/lite/proxy/query_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/abci/example/kvstore" + "github.com/tendermint/tendermint/crypto/merkle" "github.com/tendermint/tendermint/lite" certclient "github.com/tendermint/tendermint/lite/client" nm "github.com/tendermint/tendermint/node" @@ -143,12 +144,13 @@ func TestTxProofs(t *testing.T) { require.NotNil(err) require.Contains(err.Error(), "not found") - // Now let's check with the real tx hash. + // Now let's check with the real tx root hash. key = types.Tx(tx).Hash() res, err = cl.Tx(key, true) require.NoError(err, "%#v", err) require.NotNil(res) - err = res.Proof.Validate(key) + keyHash := merkle.SimpleHashFromByteSlices([][]byte{key}) + err = res.Proof.Validate(keyHash) assert.NoError(err, "%#v", err) commit, err := GetCertifiedCommit(br.Height, cl, cert) diff --git a/types/part_set.go b/types/part_set.go index 7e1aa3c35..3c1c8b299 100644 --- a/types/part_set.go +++ b/types/part_set.go @@ -9,7 +9,6 @@ import ( "github.com/pkg/errors" "github.com/tendermint/tendermint/crypto/merkle" - "github.com/tendermint/tendermint/crypto/tmhash" cmn "github.com/tendermint/tendermint/libs/common" ) @@ -27,16 +26,6 @@ type Part struct { hash []byte } -func (part *Part) Hash() []byte { - if part.hash != nil { - return part.hash - } - hasher := tmhash.New() - hasher.Write(part.Bytes) // nolint: errcheck, gas - part.hash = hasher.Sum(nil) - return part.hash -} - // ValidateBasic performs basic validation. func (part *Part) ValidateBasic() error { if part.Index < 0 { @@ -217,7 +206,7 @@ func (ps *PartSet) AddPart(part *Part) (bool, error) { } // Check hash proof - if part.Proof.Verify(ps.Hash(), part.Hash()) != nil { + if part.Proof.Verify(ps.Hash(), part.Bytes) != nil { return false, ErrPartSetInvalidProof } diff --git a/types/results_test.go b/types/results_test.go index 4e57e5804..def042d50 100644 --- a/types/results_test.go +++ b/types/results_test.go @@ -38,7 +38,7 @@ func TestABCIResults(t *testing.T) { for i, res := range results { proof := results.ProveResult(i) - valid := proof.Verify(root, res.Hash()) + valid := proof.Verify(root, res.Bytes()) assert.NoError(t, valid, "%d", i) } } diff --git a/types/tx.go b/types/tx.go index 41be77946..87d387a02 100644 --- a/types/tx.go +++ b/types/tx.go @@ -31,13 +31,14 @@ func (tx Tx) String() string { // Txs is a slice of Tx. type Txs []Tx -// Hash returns the simple Merkle root hash of the transactions. +// Hash returns the Merkle root hash of the transaction hashes. +// i.e. the leaves of the tree are the hashes of the txs. func (txs Txs) Hash() []byte { // These allocations will be removed once Txs is switched to [][]byte, // ref #2603. This is because golang does not allow type casting slices without unsafe txBzs := make([][]byte, len(txs)) for i := 0; i < len(txs); i++ { - txBzs[i] = txs[i] + txBzs[i] = txs[i].Hash() } return merkle.SimpleHashFromByteSlices(txBzs) } @@ -69,7 +70,7 @@ func (txs Txs) Proof(i int) TxProof { l := len(txs) bzs := make([][]byte, l) for i := 0; i < l; i++ { - bzs[i] = txs[i] + bzs[i] = txs[i].Hash() } root, proofs := merkle.SimpleProofsFromByteSlices(bzs) @@ -87,8 +88,8 @@ type TxProof struct { Proof merkle.SimpleProof } -// LeadHash returns the hash of the this proof refers to. -func (tp TxProof) LeafHash() []byte { +// Leaf returns the hash(tx), which is the leaf in the merkle tree which this proof refers to. +func (tp TxProof) Leaf() []byte { return tp.Data.Hash() } @@ -104,7 +105,7 @@ func (tp TxProof) Validate(dataHash []byte) error { if tp.Proof.Total <= 0 { return errors.New("Proof total must be positive") } - valid := tp.Proof.Verify(tp.RootHash, tp.LeafHash()) + valid := tp.Proof.Verify(tp.RootHash, tp.Leaf()) if valid != nil { return errors.New("Proof is not internally consistent") } diff --git a/types/tx_test.go b/types/tx_test.go index 5cdadce52..511f4c3a8 100644 --- a/types/tx_test.go +++ b/types/tx_test.go @@ -66,14 +66,13 @@ func TestValidTxProof(t *testing.T) { root := txs.Hash() // make sure valid proof for every tx for i := range txs { - leaf := txs[i] - leafHash := leaf.Hash() + tx := []byte(txs[i]) proof := txs.Proof(i) assert.Equal(t, i, proof.Proof.Index, "%d: %d", h, i) assert.Equal(t, len(txs), proof.Proof.Total, "%d: %d", h, i) assert.EqualValues(t, root, proof.RootHash, "%d: %d", h, i) - assert.EqualValues(t, leaf, proof.Data, "%d: %d", h, i) - assert.EqualValues(t, leafHash, proof.LeafHash(), "%d: %d", h, i) + assert.EqualValues(t, tx, proof.Data, "%d: %d", h, i) + assert.EqualValues(t, txs[i].Hash(), proof.Leaf(), "%d: %d", h, i) assert.Nil(t, proof.Validate(root), "%d: %d", h, i) assert.NotNil(t, proof.Validate([]byte("foobar")), "%d: %d", h, i)