Browse Source

Beginning of complete merkle proofs

pull/130/head
Jae Kwon 9 years ago
parent
commit
b96fd8a031
7 changed files with 145 additions and 41 deletions
  1. +1
    -0
      consensus/state.go
  2. +1
    -1
      merkle/iavl_node.go
  3. +54
    -1
      merkle/simple_tree.go
  4. +36
    -0
      merkle/simple_tree_test.go
  5. +7
    -8
      state/state.go
  6. +1
    -0
      state/state_test.go
  7. +45
    -31
      types/block.go

+ 1
- 0
consensus/state.go View File

@ -640,6 +640,7 @@ func (cs *ConsensusState) createProposalBlock() (block *types.Block, blockParts
Txs: txs,
},
}
block.FillHeader()
// Set the block.Header.StateHash.
err := cs.state.ComputeBlockStateHash(block)


+ 1
- 1
merkle/iavl_node.go View File

@ -5,8 +5,8 @@ import (
"github.com/tendermint/tendermint/Godeps/_workspace/src/code.google.com/p/go.crypto/ripemd160"
"io"
"github.com/tendermint/tendermint/wire"
. "github.com/tendermint/tendermint/common"
"github.com/tendermint/tendermint/wire"
)
// Node


+ 54
- 1
merkle/simple_tree.go View File

@ -27,11 +27,12 @@ package merkle
import (
"bytes"
"fmt"
"sort"
"github.com/tendermint/tendermint/Godeps/_workspace/src/code.google.com/p/go.crypto/ripemd160"
"github.com/tendermint/tendermint/wire"
. "github.com/tendermint/tendermint/common"
"github.com/tendermint/tendermint/wire"
)
func SimpleHashFromTwoHashes(left []byte, right []byte) []byte {
@ -89,6 +90,58 @@ func SimpleHashFromHashables(items []Hashable) []byte {
return SimpleHashFromHashes(hashes)
}
// Convenience for SimpleHashFromHashes.
func SimpleHashFromMap(m map[string]interface{}) []byte {
kpPairsH := MakeSortedKVPairs(m)
return SimpleHashFromHashables(kpPairsH)
}
//--------------------------------------------------------------------------------
/* Convenience struct for key-value pairs.
A list of KVPairs is hashed via `SimpleHashFromHashables`.
NOTE: Each `Value` is encoded for hashing without extra type information,
so the user is presumed to be aware of the Value types.
*/
type KVPair struct {
Key string
Value interface{}
}
func (kv KVPair) Hash() []byte {
hasher, n, err := ripemd160.New(), new(int64), new(error)
wire.WriteString(kv.Key, hasher, n, err)
if kvH, ok := kv.Value.(Hashable); ok {
wire.WriteByteSlice(kvH.Hash(), hasher, n, err)
} else {
wire.WriteBinary(kv.Value, hasher, n, err)
}
if *err != nil {
PanicSanity(*err)
}
return hasher.Sum(nil)
}
type KVPairs []KVPair
func (kvps KVPairs) Len() int { return len(kvps) }
func (kvps KVPairs) Less(i, j int) bool { return kvps[i].Key < kvps[j].Key }
func (kvps KVPairs) Swap(i, j int) { kvps[i], kvps[j] = kvps[j], kvps[i] }
func (kvps KVPairs) Sort() { sort.Sort(kvps) }
func MakeSortedKVPairs(m map[string]interface{}) []Hashable {
kvPairs := []KVPair{}
for k, v := range m {
kvPairs = append(kvPairs, KVPair{k, v})
}
KVPairs(kvPairs).Sort()
kvPairsH := []Hashable{}
for _, kvp := range kvPairs {
kvPairsH = append(kvPairsH, kvp)
}
return kvPairsH
}
//--------------------------------------------------------------------------------
type SimpleProof struct {


+ 36
- 0
merkle/simple_tree_test.go View File

@ -1,9 +1,12 @@
package merkle
import (
"bytes"
. "github.com/tendermint/tendermint/common"
. "github.com/tendermint/tendermint/common/test"
"fmt"
"testing"
)
@ -81,3 +84,36 @@ func TestSimpleProof(t *testing.T) {
}
}
}
func TestKVPairs(t *testing.T) {
// NOTE: in alphabetical order for convenience.
m := map[string]interface{}{}
m["bytez"] = []byte("hizz") // 0
m["light"] = "shadow" // 1
m["one"] = 1 // 2
m["one_u64"] = uint64(1) // 3
m["struct"] = struct { // 4
A int
B int
}{0, 1}
kvPairsH := MakeSortedKVPairs(m)
// rootHash := SimpleHashFromHashables(kvPairsH)
proofs := SimpleProofsFromHashables(kvPairsH)
// Some manual tests
if !bytes.Equal(proofs[1].LeafHash, KVPair{"light", "shadow"}.Hash()) {
t.Errorf("\"light\": proof failed")
fmt.Printf("%v\n%X", proofs[0], KVPair{"light", "shadow"}.Hash())
}
if !bytes.Equal(proofs[2].LeafHash, KVPair{"one", 1}.Hash()) {
t.Errorf("\"one\": proof failed")
}
if !bytes.Equal(proofs[4].LeafHash, KVPair{"struct", struct {
A int
B int
}{0, 1}}.Hash()) {
t.Errorf("\"struct\": proof failed")
}
}

+ 7
- 8
state/state.go View File

@ -123,14 +123,13 @@ func (s *State) Copy() *State {
// Returns a hash that represents the state data, excluding Last*
func (s *State) Hash() []byte {
hashables := []merkle.Hashable{
s.BondedValidators,
s.UnbondingValidators,
s.accounts,
s.validatorInfos,
s.nameReg,
}
return merkle.SimpleHashFromHashables(hashables)
return merkle.SimpleHashFromMap(map[string]interface{}{
"BondedValidators": s.BondedValidators,
"UnbondingValidators": s.UnbondingValidators,
"Accounts": s.accounts,
"ValidatorInfos": s.validatorInfos,
"NameRegistry": s.nameReg,
})
}
// Mutates the block in place and updates it with new state hash.


+ 1
- 0
state/state_test.go View File

@ -91,6 +91,7 @@ func makeBlock(t *testing.T, state *State, validation *types.Validation, txs []t
Txs: txs,
},
}
block.FillHeader()
// Fill in block StateHash
err := state.ComputeBlockStateHash(block)


+ 45
- 31
types/block.go View File

@ -8,9 +8,9 @@ import (
"time"
acm "github.com/tendermint/tendermint/account"
"github.com/tendermint/tendermint/wire"
. "github.com/tendermint/tendermint/common"
"github.com/tendermint/tendermint/merkle"
"github.com/tendermint/tendermint/wire"
)
type Block struct {
@ -28,6 +28,14 @@ func (b *Block) ValidateBasic(chainID string, lastBlockHeight int, lastBlockHash
if b.Height != lastBlockHeight+1 {
return errors.New(Fmt("Wrong Block.Header.Height. Expected %v, got %v", lastBlockHeight+1, b.Height))
}
/* TODO: Determine bounds for Time
See blockchain/reactor "stopSyncingDurationMinutes"
if !b.Time.After(lastBlockTime) {
return errors.New("Invalid Block.Header.Time")
}
*/
// TODO: validate Fees
if b.NumTxs != len(b.Data.Txs) {
return errors.New(Fmt("Wrong Block.Header.NumTxs. Expected %v, got %v", len(b.Data.Txs), b.NumTxs))
}
@ -37,22 +45,26 @@ func (b *Block) ValidateBasic(chainID string, lastBlockHeight int, lastBlockHash
if !b.LastBlockParts.Equals(lastBlockParts) {
return errors.New(Fmt("Wrong Block.Header.LastBlockParts. Expected %v, got %v", lastBlockParts, b.LastBlockParts))
}
/* TODO: Determine bounds
See blockchain/reactor "stopSyncingDurationMinutes"
if !b.Time.After(lastBlockTime) {
return errors.New("Invalid Block.Header.Time")
}
*/
if !bytes.Equal(b.LastValidationHash, b.LastValidation.Hash()) {
return errors.New(Fmt("Wrong Block.Header.LastValidationHash. Expected %X, got %X", b.LastValidationHash, b.LastValidation.Hash()))
}
if b.Header.Height != 1 {
if err := b.LastValidation.ValidateBasic(); err != nil {
return err
}
}
// XXX more validation
if !bytes.Equal(b.DataHash, b.Data.Hash()) {
return errors.New(Fmt("Wrong Block.Header.DataHash. Expected %X, got %X", b.DataHash, b.Data.Hash()))
}
// NOTE: the StateHash is validated later.
return nil
}
func (b *Block) FillHeader() {
b.LastValidationHash = b.LastValidation.Hash()
b.DataHash = b.Data.Hash()
}
// Computes and returns the block hash.
// If the block is incomplete (e.g. missing Header.StateHash)
// then the hash is nil, to prevent the usage of that hash.
@ -60,18 +72,8 @@ func (b *Block) Hash() []byte {
if b.Header == nil || b.Data == nil || b.LastValidation == nil {
return nil
}
hashHeader := b.Header.Hash()
hashData := b.Data.Hash()
hashLastValidation := b.LastValidation.Hash()
// If hashHeader is nil, required fields are missing.
if len(hashHeader) == 0 {
return nil
}
// Merkle hash from subhashes.
hashes := [][]byte{hashHeader, hashData, hashLastValidation}
return merkle.SimpleHashFromHashes(hashes)
b.FillHeader()
return b.Header.Hash()
}
func (b *Block) MakePartSet() *PartSet {
@ -121,14 +123,16 @@ func (b *Block) StringShort() string {
//-----------------------------------------------------------------------------
type Header struct {
ChainID string `json:"chain_id"`
Height int `json:"height"`
Time time.Time `json:"time"`
Fees int64 `json:"fees"`
NumTxs int `json:"num_txs"`
LastBlockHash []byte `json:"last_block_hash"`
LastBlockParts PartSetHeader `json:"last_block_parts"`
StateHash []byte `json:"state_hash"`
ChainID string `json:"chain_id"`
Height int `json:"height"`
Time time.Time `json:"time"`
Fees int64 `json:"fees"`
NumTxs int `json:"num_txs"`
LastBlockHash []byte `json:"last_block_hash"`
LastBlockParts PartSetHeader `json:"last_block_parts"`
LastValidationHash []byte `json:"last_validation_hash"`
DataHash []byte `json:"data_hash"`
StateHash []byte `json:"state_hash"`
}
// NOTE: hash is nil if required fields are missing.
@ -136,8 +140,18 @@ func (h *Header) Hash() []byte {
if len(h.StateHash) == 0 {
return nil
}
return wire.BinaryRipemd160(h)
return merkle.SimpleHashFromMap(map[string]interface{}{
"ChainID": h.ChainID,
"Height": h.Height,
"Time": h.Time,
"Fees": h.Fees,
"NumTxs": h.NumTxs,
"LastBlock": h.LastBlockHash,
"LastBlockParts": h.LastBlockParts,
"LastValidation": h.LastValidationHash,
"Data": h.DataHash,
"State": h.StateHash,
})
}
func (h *Header) StringIndented(indent string) string {


Loading…
Cancel
Save