Browse Source

Make txs and evidencelist use merkle.SimpleHashFromBytes to create hash (#2635)

This is a performance regression, but will also spare the types directory
from knowing about RFC 6962, which is a more correct abstraction. For txs
this performance hit will be fixed soon with #2603. For evidence, the
performance impact is negligible due to it being capped at a small number.
pull/2645/head
Dev Ojha 6 years ago
committed by Ethan Buchman
parent
commit
124d0db1e0
6 changed files with 33 additions and 30 deletions
  1. +1
    -0
      CHANGELOG_PENDING.md
  2. +3
    -3
      crypto/merkle/simple_proof.go
  3. +3
    -3
      crypto/merkle/simple_tree.go
  4. +0
    -2
      types/block.go
  5. +20
    -11
      types/evidence.go
  6. +6
    -11
      types/tx.go

+ 1
- 0
CHANGELOG_PENDING.md View File

@ -24,6 +24,7 @@ BREAKING CHANGES:
* [types] \#2298 Remove `Index` and `Total` fields from `TxProof`. * [types] \#2298 Remove `Index` and `Total` fields from `TxProof`.
* [crypto/merkle & lite] \#2298 Various changes to accomodate General Merkle trees * [crypto/merkle & lite] \#2298 Various changes to accomodate General Merkle trees
* [crypto/merkle] \#2595 Remove all Hasher objects in favor of byte slices * [crypto/merkle] \#2595 Remove all Hasher objects in favor of byte slices
* [crypto/merkle] \#2635 merkle.SimpleHashFromTwoHashes is no longer exported
* [types] \#2598 `VoteTypeXxx` are now * [types] \#2598 `VoteTypeXxx` are now
* Blockchain Protocol * Blockchain Protocol


+ 3
- 3
crypto/merkle/simple_proof.go View File

@ -134,13 +134,13 @@ func computeHashFromAunts(index int, total int, leafHash []byte, innerHashes [][
if leftHash == nil { if leftHash == nil {
return nil return nil
} }
return SimpleHashFromTwoHashes(leftHash, innerHashes[len(innerHashes)-1])
return simpleHashFromTwoHashes(leftHash, innerHashes[len(innerHashes)-1])
} }
rightHash := computeHashFromAunts(index-numLeft, total-numLeft, leafHash, innerHashes[:len(innerHashes)-1]) rightHash := computeHashFromAunts(index-numLeft, total-numLeft, leafHash, innerHashes[:len(innerHashes)-1])
if rightHash == nil { if rightHash == nil {
return nil return nil
} }
return SimpleHashFromTwoHashes(innerHashes[len(innerHashes)-1], rightHash)
return simpleHashFromTwoHashes(innerHashes[len(innerHashes)-1], rightHash)
} }
} }
@ -187,7 +187,7 @@ func trailsFromByteSlices(items [][]byte) (trails []*SimpleProofNode, root *Simp
default: default:
lefts, leftRoot := trailsFromByteSlices(items[:(len(items)+1)/2]) lefts, leftRoot := trailsFromByteSlices(items[:(len(items)+1)/2])
rights, rightRoot := trailsFromByteSlices(items[(len(items)+1)/2:]) rights, rightRoot := trailsFromByteSlices(items[(len(items)+1)/2:])
rootHash := SimpleHashFromTwoHashes(leftRoot.Hash, rightRoot.Hash)
rootHash := simpleHashFromTwoHashes(leftRoot.Hash, rightRoot.Hash)
root := &SimpleProofNode{rootHash, nil, nil, nil} root := &SimpleProofNode{rootHash, nil, nil, nil}
leftRoot.Parent = root leftRoot.Parent = root
leftRoot.Right = rightRoot leftRoot.Right = rightRoot


+ 3
- 3
crypto/merkle/simple_tree.go View File

@ -4,8 +4,8 @@ import (
"github.com/tendermint/tendermint/crypto/tmhash" "github.com/tendermint/tendermint/crypto/tmhash"
) )
// SimpleHashFromTwoHashes is the basic operation of the Merkle tree: Hash(left | right).
func SimpleHashFromTwoHashes(left, right []byte) []byte {
// simpleHashFromTwoHashes is the basic operation of the Merkle tree: Hash(left | right).
func simpleHashFromTwoHashes(left, right []byte) []byte {
var hasher = tmhash.New() var hasher = tmhash.New()
err := encodeByteSlice(hasher, left) err := encodeByteSlice(hasher, left)
if err != nil { if err != nil {
@ -29,7 +29,7 @@ func SimpleHashFromByteSlices(items [][]byte) []byte {
default: default:
left := SimpleHashFromByteSlices(items[:(len(items)+1)/2]) left := SimpleHashFromByteSlices(items[:(len(items)+1)/2])
right := SimpleHashFromByteSlices(items[(len(items)+1)/2:]) right := SimpleHashFromByteSlices(items[(len(items)+1)/2:])
return SimpleHashFromTwoHashes(left, right)
return simpleHashFromTwoHashes(left, right)
} }
} }


+ 0
- 2
types/block.go View File

@ -576,7 +576,6 @@ func (sh SignedHeader) StringIndented(indent string) string {
indent, sh.Header.StringIndented(indent+" "), indent, sh.Header.StringIndented(indent+" "),
indent, sh.Commit.StringIndented(indent+" "), indent, sh.Commit.StringIndented(indent+" "),
indent) indent)
return ""
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -660,7 +659,6 @@ func (data *EvidenceData) StringIndented(indent string) string {
%s}#%v`, %s}#%v`,
indent, strings.Join(evStrings, "\n"+indent+" "), indent, strings.Join(evStrings, "\n"+indent+" "),
indent, data.hash) indent, data.hash)
return ""
} }
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------


+ 20
- 11
types/evidence.go View File

@ -55,6 +55,7 @@ func (err *ErrEvidenceOverflow) Error() string {
type Evidence interface { type Evidence interface {
Height() int64 // height of the equivocation Height() int64 // height of the equivocation
Address() []byte // address of the equivocating validator Address() []byte // address of the equivocating validator
Bytes() []byte // bytes which compromise the evidence
Hash() []byte // hash of the evidence Hash() []byte // hash of the evidence
Verify(chainID string, pubKey crypto.PubKey) error // verify the evidence Verify(chainID string, pubKey crypto.PubKey) error // verify the evidence
Equal(Evidence) bool // check equality of evidence Equal(Evidence) bool // check equality of evidence
@ -88,6 +89,8 @@ type DuplicateVoteEvidence struct {
VoteB *Vote VoteB *Vote
} }
var _ Evidence = &DuplicateVoteEvidence{}
// String returns a string representation of the evidence. // String returns a string representation of the evidence.
func (dve *DuplicateVoteEvidence) String() string { func (dve *DuplicateVoteEvidence) String() string {
return fmt.Sprintf("VoteA: %v; VoteB: %v", dve.VoteA, dve.VoteB) return fmt.Sprintf("VoteA: %v; VoteB: %v", dve.VoteA, dve.VoteB)
@ -104,6 +107,11 @@ func (dve *DuplicateVoteEvidence) Address() []byte {
return dve.PubKey.Address() return dve.PubKey.Address()
} }
// Hash returns the hash of the evidence.
func (dve *DuplicateVoteEvidence) Bytes() []byte {
return cdcEncode(dve)
}
// Hash returns the hash of the evidence. // Hash returns the hash of the evidence.
func (dve *DuplicateVoteEvidence) Hash() []byte { func (dve *DuplicateVoteEvidence) Hash() []byte {
return tmhash.Sum(cdcEncode(dve)) return tmhash.Sum(cdcEncode(dve))
@ -172,6 +180,8 @@ type MockGoodEvidence struct {
Address_ []byte Address_ []byte
} }
var _ Evidence = &MockGoodEvidence{}
// UNSTABLE // UNSTABLE
func NewMockGoodEvidence(height int64, idx int, address []byte) MockGoodEvidence { func NewMockGoodEvidence(height int64, idx int, address []byte) MockGoodEvidence {
return MockGoodEvidence{height, address} return MockGoodEvidence{height, address}
@ -182,6 +192,9 @@ func (e MockGoodEvidence) Address() []byte { return e.Address_ }
func (e MockGoodEvidence) Hash() []byte { func (e MockGoodEvidence) Hash() []byte {
return []byte(fmt.Sprintf("%d-%x", e.Height_, e.Address_)) return []byte(fmt.Sprintf("%d-%x", e.Height_, e.Address_))
} }
func (e MockGoodEvidence) Bytes() []byte {
return []byte(fmt.Sprintf("%d-%x", e.Height_, e.Address_))
}
func (e MockGoodEvidence) Verify(chainID string, pubKey crypto.PubKey) error { return nil } func (e MockGoodEvidence) Verify(chainID string, pubKey crypto.PubKey) error { return nil }
func (e MockGoodEvidence) Equal(ev Evidence) bool { func (e MockGoodEvidence) Equal(ev Evidence) bool {
e2 := ev.(MockGoodEvidence) e2 := ev.(MockGoodEvidence)
@ -216,18 +229,14 @@ type EvidenceList []Evidence
// Hash returns the simple merkle root hash of the EvidenceList. // Hash returns the simple merkle root hash of the EvidenceList.
func (evl EvidenceList) Hash() []byte { func (evl EvidenceList) Hash() []byte {
// Recursive impl.
// Copied from crypto/merkle to avoid allocations
switch len(evl) {
case 0:
return nil
case 1:
return evl[0].Hash()
default:
left := EvidenceList(evl[:(len(evl)+1)/2]).Hash()
right := EvidenceList(evl[(len(evl)+1)/2:]).Hash()
return merkle.SimpleHashFromTwoHashes(left, right)
// These allocations are required because Evidence is not of type Bytes, and
// golang slices can't be typed cast. This shouldn't be a performance problem since
// the Evidence size is capped.
evidenceBzs := make([][]byte, len(evl))
for i := 0; i < len(evl); i++ {
evidenceBzs[i] = evl[i].Bytes()
} }
return merkle.SimpleHashFromByteSlices(evidenceBzs)
} }
func (evl EvidenceList) String() string { func (evl EvidenceList) String() string {


+ 6
- 11
types/tx.go View File

@ -31,18 +31,13 @@ type Txs []Tx
// Hash returns the simple Merkle root hash of the transactions. // Hash returns the simple Merkle root hash of the transactions.
func (txs Txs) Hash() []byte { func (txs Txs) Hash() []byte {
// Recursive impl.
// Copied from tendermint/crypto/merkle to avoid allocations
switch len(txs) {
case 0:
return nil
case 1:
return txs[0].Hash()
default:
left := Txs(txs[:(len(txs)+1)/2]).Hash()
right := Txs(txs[(len(txs)+1)/2:]).Hash()
return merkle.SimpleHashFromTwoHashes(left, right)
// 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]
} }
return merkle.SimpleHashFromByteSlices(txBzs)
} }
// Index returns the index of this transaction in the list, or -1 if not found // Index returns the index of this transaction in the list, or -1 if not found


Loading…
Cancel
Save