track evidence, include in block
This commit is contained in:
parent
f80f6445a6
commit
7928659f70
|
@ -863,7 +863,9 @@ func (cs *ConsensusState) createProposalBlock() (block *types.Block, blockParts
|
|||
|
||||
// Mempool validated transactions
|
||||
txs := cs.mempool.Reap(cs.config.MaxBlockSizeTxs)
|
||||
return cs.state.MakeBlock(cs.Height, txs, commit)
|
||||
block, parts := cs.state.MakeBlock(cs.Height, txs, commit)
|
||||
block.AddEvidence(cs.Evidence)
|
||||
return block, parts
|
||||
}
|
||||
|
||||
// Enter: `timeoutPropose` after entering Propose.
|
||||
|
@ -1231,6 +1233,10 @@ func (cs *ConsensusState) finalizeCommit(height int64) {
|
|||
|
||||
fail.Fail() // XXX
|
||||
|
||||
// TODO: remove included evidence
|
||||
// and persist remaining evidence
|
||||
// ... is this the right spot? need to ensure we never lose evidence
|
||||
|
||||
// NewHeightStep!
|
||||
cs.updateToState(stateCopy)
|
||||
|
||||
|
@ -1327,15 +1333,14 @@ func (cs *ConsensusState) tryAddVote(vote *types.Vote, peerKey string) error {
|
|||
// If it's otherwise invalid, punish peer.
|
||||
if err == ErrVoteHeightMismatch {
|
||||
return err
|
||||
} else if _, ok := err.(*types.ErrVoteConflictingVotes); ok {
|
||||
} else if voteErr, ok := err.(*types.ErrVoteConflictingVotes); ok {
|
||||
if bytes.Equal(vote.ValidatorAddress, cs.privValidator.GetAddress()) {
|
||||
cs.Logger.Error("Found conflicting vote from ourselves. Did you unsafe_reset a validator?", "height", vote.Height, "round", vote.Round, "type", vote.Type)
|
||||
return err
|
||||
}
|
||||
cs.Logger.Error("Found conflicting vote. Publish evidence (TODO)", "height", vote.Height, "round", vote.Round, "type", vote.Type, "valAddr", vote.ValidatorAddress, "valIndex", vote.ValidatorIndex)
|
||||
|
||||
// TODO: track evidence for inclusion in a block
|
||||
cs.Logger.Error("Found conflicting vote. Recording evidence in the RoundState", "height", vote.Height, "round", vote.Round, "type", vote.Type, "valAddr", vote.ValidatorAddress, "valIndex", vote.ValidatorIndex)
|
||||
|
||||
cs.Evidence = append(cs.Evidence, &types.DuplicateVoteEvidence{voteErr.VoteA, voteErr.VoteB})
|
||||
return err
|
||||
} else {
|
||||
// Probably an invalid signature / Bad peer.
|
||||
|
|
|
@ -309,6 +309,8 @@ func (s *State) validateBlock(b *types.Block) error {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Validate evidence
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,8 @@ import (
|
|||
type Block struct {
|
||||
*Header `json:"header"`
|
||||
*Data `json:"data"`
|
||||
LastCommit *Commit `json:"last_commit"`
|
||||
Evidence EvidenceData `json:"evidence"`
|
||||
LastCommit *Commit `json:"last_commit"`
|
||||
}
|
||||
|
||||
// MakeBlock returns a new block with an empty header, except what can be computed from itself.
|
||||
|
@ -40,6 +41,11 @@ func MakeBlock(height int64, txs []Tx, commit *Commit) *Block {
|
|||
return block
|
||||
}
|
||||
|
||||
// AddEvidence appends the given evidence to the block
|
||||
func (b *Block) AddEvidence(evidence []Evidence) {
|
||||
b.Evidence.Evidence = append(b.Evidence.Evidence, evidence...)
|
||||
}
|
||||
|
||||
// ValidateBasic performs basic validation that doesn't involve state data.
|
||||
// It checks the internal consistency of the block.
|
||||
func (b *Block) ValidateBasic() error {
|
||||
|
@ -58,6 +64,9 @@ func (b *Block) ValidateBasic() error {
|
|||
if !bytes.Equal(b.DataHash, b.Data.Hash()) {
|
||||
return fmt.Errorf("Wrong Block.Header.DataHash. Expected %v, got %v", b.DataHash, b.Data.Hash())
|
||||
}
|
||||
if !bytes.Equal(b.EvidenceHash, b.Evidence.Hash()) {
|
||||
return errors.New(cmn.Fmt("Wrong Block.Header.EvidenceHash. Expected %v, got %v", b.EvidenceHash, b.Evidence.Hash()))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -69,6 +78,9 @@ func (b *Block) FillHeader() {
|
|||
if b.DataHash == nil {
|
||||
b.DataHash = b.Data.Hash()
|
||||
}
|
||||
if b.EvidenceHash == nil {
|
||||
b.EvidenceHash = b.Evidence.Hash()
|
||||
}
|
||||
}
|
||||
|
||||
// Hash computes and returns the block hash.
|
||||
|
@ -114,9 +126,11 @@ func (b *Block) StringIndented(indent string) string {
|
|||
%s %v
|
||||
%s %v
|
||||
%s %v
|
||||
%s %v
|
||||
%s}#%v`,
|
||||
indent, b.Header.StringIndented(indent+" "),
|
||||
indent, b.Data.StringIndented(indent+" "),
|
||||
indent, b.Evidence.StringIndented(indent+" "),
|
||||
indent, b.LastCommit.StringIndented(indent+" "),
|
||||
indent, b.Hash())
|
||||
}
|
||||
|
@ -134,6 +148,7 @@ func (b *Block) StringShort() string {
|
|||
|
||||
// Header defines the structure of a Tendermint block header
|
||||
// TODO: limit header size
|
||||
// NOTE: changes to the Header should be duplicated in the abci Header
|
||||
type Header struct {
|
||||
// basic block info
|
||||
ChainID string `json:"chain_id"`
|
||||
|
@ -154,6 +169,9 @@ type Header struct {
|
|||
ConsensusHash data.Bytes `json:"consensus_hash"` // consensus params for current block
|
||||
AppHash data.Bytes `json:"app_hash"` // state after txs from the previous block
|
||||
LastResultsHash data.Bytes `json:"last_results_hash"` // root hash of all results from the txs from the previous block
|
||||
|
||||
// consensus info
|
||||
EvidenceHash data.Bytes `json:"evidence_hash"` // evidence included in the block
|
||||
}
|
||||
|
||||
// Hash returns the hash of the header.
|
||||
|
@ -175,6 +193,7 @@ func (h *Header) Hash() data.Bytes {
|
|||
"App": h.AppHash,
|
||||
"Consensus": h.ConsensusHash,
|
||||
"Results": h.LastResultsHash,
|
||||
"Evidence": h.EvidenceHash,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -196,6 +215,7 @@ func (h *Header) StringIndented(indent string) string {
|
|||
%s App: %v
|
||||
%s Conensus: %v
|
||||
%s Results: %v
|
||||
%s Evidence: %v
|
||||
%s}#%v`,
|
||||
indent, h.ChainID,
|
||||
indent, h.Height,
|
||||
|
@ -209,6 +229,7 @@ func (h *Header) StringIndented(indent string) string {
|
|||
indent, h.AppHash,
|
||||
indent, h.ConsensusHash,
|
||||
indent, h.LastResultsHash,
|
||||
indent, h.EvidenceHash,
|
||||
indent, h.Hash())
|
||||
}
|
||||
|
||||
|
@ -413,6 +434,33 @@ func (data *Data) StringIndented(indent string) string {
|
|||
indent, data.hash)
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// EvidenceData contains any evidence of malicious wrong-doing by validators
|
||||
type EvidenceData struct {
|
||||
Evidence []Evidence `json:"evidence"`
|
||||
|
||||
// Volatile
|
||||
hash data.Bytes
|
||||
}
|
||||
|
||||
// Hash returns the hash of the data
|
||||
func (data *EvidenceData) Hash() data.Bytes {
|
||||
if data.hash == nil {
|
||||
// TODO
|
||||
}
|
||||
return data.hash
|
||||
}
|
||||
|
||||
// StringIndented returns a string representation of the transactions
|
||||
func (data *EvidenceData) StringIndented(indent string) string {
|
||||
if data == nil {
|
||||
return "nil-Data"
|
||||
}
|
||||
// TODO
|
||||
return ""
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
// BlockID defines the unique ID of a block as its Hash and its PartSetHeader
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Evidence represents any provable malicious activity by a validator
|
||||
type Evidence interface {
|
||||
Verify() error
|
||||
Address() []byte
|
||||
}
|
||||
|
||||
//-------------------------------------------
|
||||
|
||||
type DuplicateVoteEvidence struct {
|
||||
VoteA *Vote
|
||||
VoteB *Vote
|
||||
}
|
||||
|
||||
// Address returns the address of the validator
|
||||
func (dve *DuplicateVoteEvidence) Address() []byte {
|
||||
return dve.VoteA.ValidatorAddress
|
||||
}
|
||||
|
||||
// Verify returns an error if the two votes aren't from the same validator, for the same H/R/S, but for different blocks
|
||||
func (dve *DuplicateVoteEvidence) Verify() error {
|
||||
// H/R/S must be the same
|
||||
if dve.VoteA.Height != dve.VoteB.Height ||
|
||||
dve.VoteA.Round != dve.VoteB.Round ||
|
||||
dve.VoteA.Type != dve.VoteB.Type {
|
||||
return fmt.Errorf("DuplicateVoteEvidence Error: H/R/S does not match. Got %v and %v", dve.VoteA, dve.VoteB)
|
||||
}
|
||||
|
||||
// Address and Index must be the same
|
||||
if !bytes.Equal(dve.VoteA.ValidatorAddress, dve.VoteB.ValidatorAddress) {
|
||||
return fmt.Errorf("DuplicateVoteEvidence Error: Validator addresses do not match. Got %X and %X", dve.VoteA.ValidatorAddress, dve.VoteB.ValidatorAddress)
|
||||
}
|
||||
if dve.VoteA.ValidatorIndex != dve.VoteB.ValidatorIndex {
|
||||
return fmt.Errorf("DuplicateVoteEvidence Error: Validator indices do not match. Got %d and %d", dve.VoteA.ValidatorIndex, dve.VoteB.ValidatorIndex)
|
||||
}
|
||||
|
||||
// BlockIDs must be different
|
||||
if dve.VoteA.BlockID.Equals(dve.VoteB.BlockID) {
|
||||
return fmt.Errorf("DuplicateVoteEvidence Error: BlockIDs are the same (%v) - not a real duplicate vote!", dve.VoteA.BlockID)
|
||||
}
|
||||
|
||||
// Signatures must be valid
|
||||
// TODO
|
||||
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue