2018-06-20 17:35:30 -07:00
package types
import (
"bytes"
"fmt"
2018-09-19 19:34:08 +02:00
"github.com/tendermint/go-amino"
2018-07-01 01:40:03 -04:00
2018-06-20 17:35:30 -07:00
"github.com/tendermint/tendermint/crypto"
2018-07-01 01:40:03 -04:00
"github.com/tendermint/tendermint/crypto/merkle"
2018-06-20 17:35:30 -07:00
)
2018-08-08 16:03:58 +04:00
const (
// MaxEvidenceBytes is a maximum size of any evidence (including amino overhead).
max-bytes PR follow-up (#2318)
* ReapMaxTxs: return all txs if max is negative
this mirrors ReapMaxBytes behavior
See https://github.com/tendermint/tendermint/pull/2184#discussion_r214439950
* increase MaxAminoOverheadForBlock
tested with:
```
func TestMaxAminoOverheadForBlock(t *testing.T) {
maxChainID := ""
for i := 0; i < MaxChainIDLen; i++ {
maxChainID += "𠜎"
}
h := Header{
ChainID: maxChainID,
Height: 10,
Time: time.Now().UTC(),
NumTxs: 100,
TotalTxs: 200,
LastBlockID: makeBlockID(make([]byte, 20), 300, make([]byte, 20)),
LastCommitHash: tmhash.Sum([]byte("last_commit_hash")),
DataHash: tmhash.Sum([]byte("data_hash")),
ValidatorsHash: tmhash.Sum([]byte("validators_hash")),
NextValidatorsHash: tmhash.Sum([]byte("next_validators_hash")),
ConsensusHash: tmhash.Sum([]byte("consensus_hash")),
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")),
}
b := Block{
Header: h,
Data: Data{Txs: makeTxs(10000, 100)},
Evidence: EvidenceData{},
LastCommit: &Commit{},
}
bz, err := cdc.MarshalBinary(b)
require.NoError(t, err)
assert.Equal(t, MaxHeaderBytes+MaxAminoOverheadForBlock-2, len(bz)-1000000-20000-1)
}
```
* fix MaxYYY constants calculation
by using math.MaxInt64
See https://github.com/tendermint/tendermint/pull/2184#discussion_r214444244
* pass mempool filter as an option
See https://github.com/tendermint/tendermint/pull/2184#discussion_r214445869
* fixes after Dev's comments
2018-09-04 11:46:34 +04:00
MaxEvidenceBytes = 440
2018-08-08 16:03:58 +04:00
)
2018-06-20 17:35:30 -07:00
// ErrEvidenceInvalid wraps a piece of evidence and the error denoting how or why it is invalid.
type ErrEvidenceInvalid struct {
Evidence Evidence
ErrorValue error
}
func NewEvidenceInvalidErr ( ev Evidence , err error ) * ErrEvidenceInvalid {
return & ErrEvidenceInvalid { ev , err }
}
// Error returns a string representation of the error.
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 {
Height ( ) int64 // height of the equivocation
Address ( ) [ ] byte // address of the equivocating validator
Hash ( ) [ ] byte // hash of the evidence
Verify ( chainID string , pubKey crypto . PubKey ) error // verify the evidence
Equal ( Evidence ) bool // check equality of evidence
String ( ) string
}
func RegisterEvidences ( cdc * amino . Codec ) {
cdc . RegisterInterface ( ( * Evidence ) ( nil ) , nil )
cdc . RegisterConcrete ( & DuplicateVoteEvidence { } , "tendermint/DuplicateVoteEvidence" , nil )
2018-07-26 18:53:19 -04:00
// mocks
2018-07-27 00:41:15 +02:00
cdc . RegisterConcrete ( MockGoodEvidence { } , "tendermint/MockGoodEvidence" , nil )
cdc . RegisterConcrete ( MockBadEvidence { } , "tendermint/MockBadEvidence" , nil )
2018-06-20 17:35:30 -07:00
}
2018-09-12 23:44:43 +04:00
// MaxEvidenceBytesPerBlock returns the maximum evidence size per block.
func MaxEvidenceBytesPerBlock ( blockMaxBytes int ) int {
return blockMaxBytes / 10
}
2018-06-20 17:35:30 -07:00
//-------------------------------------------
// DuplicateVoteEvidence contains evidence a validator signed two conflicting votes.
type DuplicateVoteEvidence struct {
PubKey crypto . PubKey
2018-09-19 19:34:08 +02:00
// TODO(ismail): this probably need to be `SignedVoteReply`s
VoteA * SignVoteReply
VoteB * SignVoteReply
2018-06-20 17:35:30 -07:00
}
// String returns a string representation of the evidence.
func ( dve * DuplicateVoteEvidence ) String ( ) string {
return fmt . Sprintf ( "VoteA: %v; VoteB: %v" , dve . VoteA , dve . VoteB )
}
// Height returns the height this evidence refers to.
func ( dve * DuplicateVoteEvidence ) Height ( ) int64 {
2018-09-19 19:34:08 +02:00
return dve . VoteA . Vote . Height
2018-06-20 17:35:30 -07:00
}
// 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 aminoHasher ( dve ) . Hash ( )
}
// 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 , pubKey crypto . PubKey ) error {
// H/R/S must be the same
2018-09-19 19:34:08 +02:00
if dve . VoteA . Vote . Height != dve . VoteB . Vote . Height ||
dve . VoteA . Vote . Round != dve . VoteB . Vote . Round ||
dve . VoteA . Vote . Type != dve . VoteB . Vote . Type {
2018-06-20 17:35:30 -07:00
return fmt . Errorf ( "DuplicateVoteEvidence Error: H/R/S does not match. Got %v and %v" , dve . VoteA , dve . VoteB )
}
// Address must be the same
2018-09-19 19:34:08 +02:00
if ! bytes . Equal ( dve . VoteA . Vote . ValidatorAddress , dve . VoteB . Vote . ValidatorAddress ) {
return fmt . Errorf ( "DuplicateVoteEvidence Error: Validator addresses do not match. Got %X and %X" , dve . VoteA . Vote . ValidatorAddress , dve . VoteB . Vote . ValidatorAddress )
2018-06-20 17:35:30 -07:00
}
// Index must be the same
2018-09-19 19:34:08 +02:00
if dve . VoteA . Vote . ValidatorIndex != dve . VoteB . Vote . ValidatorIndex {
return fmt . Errorf ( "DuplicateVoteEvidence Error: Validator indices do not match. Got %d and %d" , dve . VoteA . Vote . ValidatorIndex , dve . VoteB . Vote . ValidatorIndex )
2018-06-20 17:35:30 -07:00
}
// BlockIDs must be different
2018-09-19 19:34:08 +02:00
if dve . VoteA . Vote . BlockID . Equals ( dve . VoteB . Vote . BlockID ) {
return fmt . Errorf ( "DuplicateVoteEvidence Error: BlockIDs are the same (%v) - not a real duplicate vote" , dve . VoteA . Vote . BlockID )
2018-06-20 17:35:30 -07:00
}
// pubkey must match address (this should already be true, sanity check)
2018-09-19 19:34:08 +02:00
addr := dve . VoteA . Vote . ValidatorAddress
2018-06-20 17:35:30 -07:00
if ! bytes . Equal ( pubKey . Address ( ) , addr ) {
return fmt . Errorf ( "DuplicateVoteEvidence FAILED SANITY CHECK - address (%X) doesn't match pubkey (%v - %X)" ,
addr , pubKey , pubKey . Address ( ) )
}
// Signatures must be valid
2018-09-19 19:34:08 +02:00
if ! pubKey . VerifyBytes ( dve . VoteA . Vote . SignBytes ( ) , dve . VoteA . Signature ) {
2018-06-20 17:35:30 -07:00
return fmt . Errorf ( "DuplicateVoteEvidence Error verifying VoteA: %v" , ErrVoteInvalidSignature )
}
2018-09-19 19:34:08 +02:00
if ! pubKey . VerifyBytes ( dve . VoteB . Vote . SignBytes ( ) , dve . VoteB . Signature ) {
2018-06-20 17:35:30 -07:00
return fmt . Errorf ( "DuplicateVoteEvidence Error verifying VoteB: %v" , ErrVoteInvalidSignature )
}
return nil
}
// Equal checks if two pieces of evidence are equal.
func ( dve * DuplicateVoteEvidence ) Equal ( ev Evidence ) bool {
if _ , ok := ev . ( * DuplicateVoteEvidence ) ; ! ok {
return false
}
// just check their hashes
dveHash := aminoHasher ( dve ) . Hash ( )
evHash := aminoHasher ( ev ) . Hash ( )
return bytes . Equal ( dveHash , evHash )
}
//-----------------------------------------------------------------
// UNSTABLE
type MockGoodEvidence struct {
Height_ int64
Address_ [ ] byte
}
// UNSTABLE
func NewMockGoodEvidence ( height int64 , idx int , address [ ] byte ) MockGoodEvidence {
return MockGoodEvidence { height , address }
}
func ( e MockGoodEvidence ) Height ( ) int64 { return e . Height_ }
func ( e MockGoodEvidence ) Address ( ) [ ] byte { return e . Address_ }
func ( e MockGoodEvidence ) Hash ( ) [ ] 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 ) Equal ( ev Evidence ) bool {
e2 := ev . ( MockGoodEvidence )
return e . Height_ == e2 . Height_ &&
bytes . Equal ( e . Address_ , e2 . Address_ )
}
func ( e MockGoodEvidence ) String ( ) string {
return fmt . Sprintf ( "GoodEvidence: %d/%s" , e . Height_ , e . Address_ )
}
// UNSTABLE
type MockBadEvidence struct {
MockGoodEvidence
}
func ( e MockBadEvidence ) Verify ( chainID string , pubKey crypto . PubKey ) error {
return fmt . Errorf ( "MockBadEvidence" )
}
func ( e MockBadEvidence ) Equal ( ev Evidence ) bool {
e2 := ev . ( MockBadEvidence )
return e . Height_ == e2 . Height_ &&
bytes . Equal ( e . Address_ , e2 . Address_ )
}
func ( e MockBadEvidence ) String ( ) string {
return fmt . Sprintf ( "BadEvidence: %d/%s" , e . Height_ , e . Address_ )
}
//-------------------------------------------
// EvidenceList is a list of Evidence. Evidences is not a word.
type EvidenceList [ ] Evidence
// Hash returns the simple merkle root hash of the EvidenceList.
func ( evl EvidenceList ) Hash ( ) [ ] byte {
// Recursive impl.
2018-07-01 01:40:03 -04:00
// Copied from crypto/merkle to avoid allocations
2018-06-20 17:35:30 -07:00
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 )
}
}
func ( evl EvidenceList ) String ( ) string {
s := ""
for _ , e := range evl {
s += fmt . Sprintf ( "%s\t\t" , e )
}
return s
}
// Has returns true if the evidence is in the EvidenceList.
func ( evl EvidenceList ) Has ( evidence Evidence ) bool {
for _ , ev := range evl {
if ev . Equal ( evidence ) {
return true
}
}
return false
}