From 77e45756f20f07f390f853ba8e1a9e11a94f1e9a Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 28 Aug 2017 20:01:34 -0400 Subject: [PATCH] types: Evidences for merkle hashing; Evidence.String() --- state/execution.go | 2 +- types/block.go | 26 +++++++++++++++++++------- types/evidence.go | 38 +++++++++++++++++++++++++++++++++++++- 3 files changed, 57 insertions(+), 9 deletions(-) diff --git a/state/execution.go b/state/execution.go index 7e84ce2db..adc6c1394 100644 --- a/state/execution.go +++ b/state/execution.go @@ -309,7 +309,7 @@ func (s *State) validateBlock(b *types.Block) error { } } - for _, ev := range block.Evidence.Evidence { + for _, ev := range block.Evidence.Evidences { if err := ev.Verify(s.ChainID); err != nil { return types.NewEvidenceInvalidErr(ev, err) } diff --git a/types/block.go b/types/block.go index 8d733d502..fa59c8420 100644 --- a/types/block.go +++ b/types/block.go @@ -43,7 +43,7 @@ func MakeBlock(height int64, txs []Tx, commit *Commit) *Block { // AddEvidence appends the given evidence to the block func (b *Block) AddEvidence(evidence []Evidence) { - b.Evidence.Evidence = append(b.Evidence.Evidence, evidence...) + b.Evidence.Evidences = append(b.Evidence.Evidences, evidence...) } // ValidateBasic performs basic validation that doesn't involve state data. @@ -438,26 +438,38 @@ func (data *Data) StringIndented(indent string) string { // EvidenceData contains any evidence of malicious wrong-doing by validators type EvidenceData struct { - Evidence []Evidence `json:"evidence"` + Evidences Evidences `json:"evidence"` // Volatile hash data.Bytes } -// Hash returns the hash of the data +// Hash returns the hash of the data. func (data *EvidenceData) Hash() data.Bytes { if data.hash == nil { - // TODO + data.hash = data.Evidences.Hash() } return data.hash } -// StringIndented returns a string representation of the transactions +// StringIndented returns a string representation of the evidence. func (data *EvidenceData) StringIndented(indent string) string { if data == nil { - return "nil-Data" + return "nil-Evidence" } - // TODO + evStrings := make([]string, cmn.MinInt(len(data.Evidences), 21)) + for i, ev := range data.Evidences { + if i == 20 { + evStrings[i] = fmt.Sprintf("... (%v total)", len(data.Evidences)) + break + } + evStrings[i] = fmt.Sprintf("Evidence:%v", ev) + } + return fmt.Sprintf(`Data{ +%s %v +%s}#%v`, + indent, strings.Join(evStrings, "\n"+indent+" "), + indent, data.hash) return "" } diff --git a/types/evidence.go b/types/evidence.go index b61edcc2e..e248eeb88 100644 --- a/types/evidence.go +++ b/types/evidence.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/tendermint/go-crypto" + "github.com/tendermint/tmlibs/merkle" ) // ErrEvidenceInvalid wraps a piece of evidence and the error denoting how or why it is invalid. @@ -22,10 +23,34 @@ func (err *ErrEvidenceInvalid) Error() string { return fmt.Sprintf("Invalid evidence: %v. Evidence: %v", err.ErrorValue, err.Evidence) } +//------------------------------------------- + // Evidence represents any provable malicious activity by a validator type Evidence interface { - Verify(chainID string) error Address() []byte + Hash() []byte + Verify(chainID string) error + + String() string +} + +//------------------------------------------- + +type Evidences []Evidence + +func (evs Evidences) Hash() []byte { + // Recursive impl. + // Copied from tmlibs/merkle to avoid allocations + switch len(evs) { + case 0: + return nil + case 1: + return evs[0].Hash() + default: + left := Evidences(evs[:(len(evs)+1)/2]).Hash() + right := Evidences(evs[(len(evs)+1)/2:]).Hash() + return merkle.SimpleHashFromTwoHashes(left, right) + } } //------------------------------------------- @@ -37,11 +62,22 @@ type DuplicateVoteEvidence struct { VoteB *Vote } +// String returns a string representation of the evidence. +func (dve *DuplicateVoteEvidence) String() string { + return fmt.Sprintf("VoteA: %v; VoteB: %v", dve.VoteA, dve.VoteB) + +} + // Address returns the address of the validator. func (dve *DuplicateVoteEvidence) Address() []byte { return dve.PubKey.Address() } +// Hash returns the hash of the evidence. +func (dve *DuplicateVoteEvidence) Hash() []byte { + return merkle.SimpleHashFromBinary(dve) +} + // Verify returns an error if the two votes aren't conflicting. // To be conflicting, they must be from the same validator, for the same H/R/S, but for different blocks. func (dve *DuplicateVoteEvidence) Verify(chainID string) error {