Skip to content

Commit

Permalink
TMHASH is 32 bytes. Closes #1990 (#2732)
Browse files Browse the repository at this point in the history
* tmhash is fully 32 bytes. closes #1990

* AddressSize

* fix tests

* fix max sizes
  • Loading branch information
ebuchman committed Oct 31, 2018
1 parent 1660e30 commit a22c962
Show file tree
Hide file tree
Showing 16 changed files with 96 additions and 51 deletions.
21 changes: 15 additions & 6 deletions crypto/crypto.go
Original file line number Diff line number Diff line change
@@ -1,28 +1,37 @@
package crypto

import (
"github.com/tendermint/tendermint/crypto/tmhash"
cmn "github.com/tendermint/tendermint/libs/common"
)

type PrivKey interface {
Bytes() []byte
Sign(msg []byte) ([]byte, error)
PubKey() PubKey
Equals(PrivKey) bool
}
const (
AddressSize = tmhash.TruncatedSize
)

// An address is a []byte, but hex-encoded even in JSON.
// []byte leaves us the option to change the address length.
// Use an alias so Unmarshal methods (with ptr receivers) are available too.
type Address = cmn.HexBytes

func AddressHash(bz []byte) Address {
return Address(tmhash.SumTruncated(bz))
}

type PubKey interface {
Address() Address
Bytes() []byte
VerifyBytes(msg []byte, sig []byte) bool
Equals(PubKey) bool
}

type PrivKey interface {
Bytes() []byte
Sign(msg []byte) ([]byte, error)
PubKey() PubKey
Equals(PrivKey) bool
}

type Symmetric interface {
Keygen() []byte
Encrypt(plaintext []byte, secret []byte) (ciphertext []byte)
Expand Down
2 changes: 1 addition & 1 deletion crypto/ed25519/ed25519.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ type PubKeyEd25519 [PubKeyEd25519Size]byte

// Address is the SHA256-20 of the raw pubkey bytes.
func (pubKey PubKeyEd25519) Address() crypto.Address {
return crypto.Address(tmhash.Sum(pubKey[:]))
return crypto.Address(tmhash.SumTruncated(pubKey[:]))
}

// Bytes marshals the PubKey using amino encoding.
Expand Down
12 changes: 6 additions & 6 deletions crypto/merkle/simple_map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"}, "fa9bc106ffd932d919bee935ceb6cf2b3dd72d8f"},
{[]string{"key1"}, []string{"value2"}, "e00e7dcfe54e9fafef5111e813a587f01ba9c3e8"},
{[]string{"key1"}, []string{"value1"}, "321d150de16dceb51c72981b432b115045383259b1a550adf8dc80f927508967"},
{[]string{"key1"}, []string{"value2"}, "2a9e4baf321eac99f6eecc3406603c14bc5e85bb7b80483cbfc75b3382d24a2f"},
// swap order with 2 keys
{[]string{"key1", "key2"}, []string{"value1", "value2"}, "eff12d1c703a1022ab509287c0f196130123d786"},
{[]string{"key2", "key1"}, []string{"value2", "value1"}, "eff12d1c703a1022ab509287c0f196130123d786"},
{[]string{"key1", "key2"}, []string{"value1", "value2"}, "c4d8913ab543ba26aa970646d4c99a150fd641298e3367cf68ca45fb45a49881"},
{[]string{"key2", "key1"}, []string{"value2", "value1"}, "c4d8913ab543ba26aa970646d4c99a150fd641298e3367cf68ca45fb45a49881"},
// swap order with 3 keys
{[]string{"key1", "key2", "key3"}, []string{"value1", "value2", "value3"}, "b2c62a277c08dbd2ad73ca53cd1d6bfdf5830d26"},
{[]string{"key1", "key3", "key2"}, []string{"value1", "value3", "value2"}, "b2c62a277c08dbd2ad73ca53cd1d6bfdf5830d26"},
{[]string{"key1", "key2", "key3"}, []string{"value1", "value2", "value3"}, "b23cef00eda5af4548a213a43793f2752d8d9013b3f2b64bc0523a4791196268"},
{[]string{"key1", "key3", "key2"}, []string{"value1", "value3", "value2"}, "b23cef00eda5af4548a213a43793f2752d8d9013b3f2b64bc0523a4791196268"},
}
for i, tc := range tests {
db := newSimpleMap()
Expand Down
33 changes: 25 additions & 8 deletions crypto/tmhash/hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,27 @@ import (
)

const (
Size = 20
Size = sha256.Size
BlockSize = sha256.BlockSize
)

// New returns a new hash.Hash.
func New() hash.Hash {
return sha256.New()
}

// Sum returns the SHA256 of the bz.
func Sum(bz []byte) []byte {
h := sha256.Sum256(bz)
return h[:]
}

//-------------------------------------------------------------

const (
TruncatedSize = 20
)

type sha256trunc struct {
sha256 hash.Hash
}
Expand All @@ -19,30 +36,30 @@ func (h sha256trunc) Write(p []byte) (n int, err error) {
}
func (h sha256trunc) Sum(b []byte) []byte {
shasum := h.sha256.Sum(b)
return shasum[:Size]
return shasum[:TruncatedSize]
}

func (h sha256trunc) Reset() {
h.sha256.Reset()
}

func (h sha256trunc) Size() int {
return Size
return TruncatedSize
}

func (h sha256trunc) BlockSize() int {
return h.sha256.BlockSize()
}

// New returns a new hash.Hash.
func New() hash.Hash {
// NewTruncated returns a new hash.Hash.
func NewTruncated() hash.Hash {
return sha256trunc{
sha256: sha256.New(),
}
}

// Sum returns the first 20 bytes of SHA256 of the bz.
func Sum(bz []byte) []byte {
// SumTruncated returns the first 20 bytes of SHA256 of the bz.
func SumTruncated(bz []byte) []byte {
hash := sha256.Sum256(bz)
return hash[:Size]
return hash[:TruncatedSize]
}
23 changes: 21 additions & 2 deletions crypto/tmhash/hash_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,29 @@ func TestHash(t *testing.T) {
hasher.Write(testVector)
bz := hasher.Sum(nil)

bz2 := tmhash.Sum(testVector)

hasher = sha256.New()
hasher.Write(testVector)
bz3 := hasher.Sum(nil)

assert.Equal(t, bz, bz2)
assert.Equal(t, bz, bz3)
}

func TestHashTruncated(t *testing.T) {
testVector := []byte("abc")
hasher := tmhash.NewTruncated()
hasher.Write(testVector)
bz := hasher.Sum(nil)

bz2 := tmhash.SumTruncated(testVector)

hasher = sha256.New()
hasher.Write(testVector)
bz2 := hasher.Sum(nil)
bz2 = bz2[:20]
bz3 := hasher.Sum(nil)
bz3 = bz3[:tmhash.TruncatedSize]

assert.Equal(t, bz, bz2)
assert.Equal(t, bz, bz3)
}
2 changes: 1 addition & 1 deletion docs/spec/blockchain/blockchain.md
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ next validator sets Merkle root.
### ConsensusParamsHash
```go
block.ConsensusParamsHash == tmhash(amino(state.ConsensusParams))
block.ConsensusParamsHash == TMHASH(amino(state.ConsensusParams))
```
Hash of the amino-encoded consensus parameters.
Expand Down
7 changes: 3 additions & 4 deletions docs/spec/blockchain/encoding.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,13 +176,12 @@ greater, for example:
h0 h1 h3 h4 h0 h1 h2 h3 h4 h5
```

Tendermint always uses the `TMHASH` hash function, which is the first 20-bytes
of the SHA256:
Tendermint always uses the `TMHASH` hash function, which is equivalent to
SHA256:

```
func TMHASH(bz []byte) []byte {
shasum := SHA256(bz)
return shasum[:20]
return SHA256(bz)
}
```

Expand Down
4 changes: 2 additions & 2 deletions docs/spec/blockchain/state.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ type Validator struct {
}
```

When hashing the Validator struct, the pubkey is not hashed,
because the address is already the hash of the pubkey.
When hashing the Validator struct, the address is not included,
because it is redundant with the pubkey.

The `state.Validators`, `state.LastValidators`, and `state.NextValidators`, must always by sorted by validator address,
so that there is a canonical order for computing the SimpleMerkleRoot.
Expand Down
3 changes: 1 addition & 2 deletions p2p/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (

crypto "github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/tmhash"
cmn "github.com/tendermint/tendermint/libs/common"
)

Expand All @@ -17,7 +16,7 @@ type ID string

// IDByteLength is the length of a crypto.Address. Currently only 20.
// TODO: support other length addresses ?
const IDByteLength = tmhash.Size
const IDByteLength = crypto.AddressSize

//------------------------------------------------------------------------------
// Persistent peer ID
Expand Down
4 changes: 2 additions & 2 deletions state/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"errors"
"fmt"

"github.com/tendermint/tendermint/crypto/tmhash"
"github.com/tendermint/tendermint/crypto"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/types"
)
Expand Down Expand Up @@ -158,7 +158,7 @@ func validateBlock(stateDB dbm.DB, state State, block *types.Block) error {
// NOTE: We can't actually verify it's the right proposer because we dont
// know what round the block was first proposed. So just check that it's
// a legit address and a known validator.
if len(block.ProposerAddress) != tmhash.Size ||
if len(block.ProposerAddress) != crypto.AddressSize ||
!state.Validators.HasAddress(block.ProposerAddress) {
return fmt.Errorf(
"Block.Header.ProposerAddress, %X, is not a validator",
Expand Down
2 changes: 1 addition & 1 deletion types/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (

const (
// MaxHeaderBytes is a maximum header size (including amino overhead).
MaxHeaderBytes int64 = 533
MaxHeaderBytes int64 = 653

// MaxAminoOverheadForBlock - maximum amino overhead to encode a block (up to
// MaxBlockSizeBytes in size) not including it's parts except Data.
Expand Down
17 changes: 9 additions & 8 deletions types/block_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/tmhash"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/version"
Expand Down Expand Up @@ -116,7 +117,7 @@ func TestBlockMakePartSetWithEvidence(t *testing.T) {

partSet := MakeBlock(h, []Tx{Tx("Hello World")}, commit, evList).MakePartSet(1024)
assert.NotNil(t, partSet)
assert.Equal(t, 2, partSet.Total())
assert.Equal(t, 3, partSet.Total())
}

func TestBlockHashesTo(t *testing.T) {
Expand Down Expand Up @@ -262,7 +263,7 @@ func TestMaxHeaderBytes(t *testing.T) {
AppHash: tmhash.Sum([]byte("app_hash")),
LastResultsHash: tmhash.Sum([]byte("last_results_hash")),
EvidenceHash: tmhash.Sum([]byte("evidence_hash")),
ProposerAddress: tmhash.Sum([]byte("proposer_address")),
ProposerAddress: crypto.AddressHash([]byte("proposer_address")),
}

bz, err := cdc.MarshalBinaryLengthPrefixed(h)
Expand Down Expand Up @@ -292,9 +293,9 @@ func TestBlockMaxDataBytes(t *testing.T) {
}{
0: {-10, 1, 0, true, 0},
1: {10, 1, 0, true, 0},
2: {742, 1, 0, true, 0},
3: {743, 1, 0, false, 0},
4: {744, 1, 0, false, 1},
2: {886, 1, 0, true, 0},
3: {887, 1, 0, false, 0},
4: {888, 1, 0, false, 1},
}

for i, tc := range testCases {
Expand All @@ -320,9 +321,9 @@ func TestBlockMaxDataBytesUnknownEvidence(t *testing.T) {
}{
0: {-10, 1, true, 0},
1: {10, 1, true, 0},
2: {824, 1, true, 0},
3: {825, 1, false, 0},
4: {826, 1, false, 1},
2: {984, 1, true, 0},
3: {985, 1, false, 0},
4: {986, 1, false, 1},
}

for i, tc := range testCases {
Expand Down
2 changes: 1 addition & 1 deletion types/evidence.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (

const (
// MaxEvidenceBytes is a maximum size of any evidence (including amino overhead).
MaxEvidenceBytes int64 = 436
MaxEvidenceBytes int64 = 484
)

// ErrEvidenceInvalid wraps a piece of evidence and the error denoting how or why it is invalid.
Expand Down
8 changes: 4 additions & 4 deletions types/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,15 @@ func (v *Validator) Hash() []byte {
}

// Bytes computes the unique encoding of a validator with a given voting power.
// These are the bytes that gets hashed in consensus. It excludes pubkey
// as its redundant with the address. This also excludes accum which changes
// These are the bytes that gets hashed in consensus. It excludes address
// as its redundant with the pubkey. This also excludes accum which changes
// every round.
func (v *Validator) Bytes() []byte {
return cdcEncode((struct {
Address Address
PubKey crypto.PubKey
VotingPower int64
}{
v.Address,
v.PubKey,
v.VotingPower,
}))
}
Expand Down
2 changes: 1 addition & 1 deletion types/vote.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

const (
// MaxVoteBytes is a maximum vote size (including amino overhead).
MaxVoteBytes int64 = 199
MaxVoteBytes int64 = 223
)

var (
Expand Down
5 changes: 3 additions & 2 deletions types/vote_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/tmhash"
)
Expand Down Expand Up @@ -37,7 +38,7 @@ func exampleVote(t byte) *Vote {
Hash: tmhash.Sum([]byte("blockID_part_set_header_hash")),
},
},
ValidatorAddress: tmhash.Sum([]byte("validator_address")),
ValidatorAddress: crypto.AddressHash([]byte("validator_address")),
ValidatorIndex: 56789,
}
}
Expand Down Expand Up @@ -211,7 +212,7 @@ func TestMaxVoteBytes(t *testing.T) {
timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC)

vote := &Vote{
ValidatorAddress: tmhash.Sum([]byte("validator_address")),
ValidatorAddress: crypto.AddressHash([]byte("validator_address")),
ValidatorIndex: math.MaxInt64,
Height: math.MaxInt64,
Round: math.MaxInt64,
Expand Down

0 comments on commit a22c962

Please sign in to comment.