Unverified Commit 7ea03fb2 authored by vipwzw's avatar vipwzw Committed by GitHub

Merge pull request #674 from caopingcp/issue615_tendermint

tendermint save local state
parents a413a97a 152ba70e
......@@ -83,13 +83,13 @@ powLimitBits = "0x1f2fffff"
genesis="14KEKbYtKKQm4wMthSK9J4La4nAiidGozt"
genesisBlockTime=1514533394
timeoutTxAvail=1000
timeoutPropose=3000
timeoutPropose=1000
timeoutProposeDelta=500
timeoutPrevote=1000
timeoutPrevoteDelta=500
timeoutPrecommit=1000
timeoutPrecommitDelta=500
timeoutCommit=1000
timeoutCommit=500
skipTimeoutCommit=false
createEmptyBlocks=false
createEmptyBlocksInterval=0
......
......@@ -8,7 +8,6 @@ import (
"bytes"
"errors"
"fmt"
"reflect"
"runtime/debug"
"sync"
"sync/atomic"
......@@ -66,8 +65,6 @@ type ConsensusState struct {
// TODO: encapsulate all of this in one "BlockManager"
blockExec *BlockExecutor
evpool ttypes.EvidencePool
// internal state
mtx sync.Mutex
ttypes.RoundState
......@@ -89,8 +86,7 @@ type ConsensusState struct {
broadcastChannel chan<- MsgInfo
ourID ID
started uint32 // atomic
stopped uint32 // atomic
status uint32 // 0-stop, 1-start
Quit chan struct{}
txsAvailable chan int64
......@@ -99,19 +95,19 @@ type ConsensusState struct {
}
// NewConsensusState returns a new ConsensusState.
func NewConsensusState(client *Client, state State, blockExec *BlockExecutor, evpool ttypes.EvidencePool) *ConsensusState {
func NewConsensusState(client *Client, state State, blockExec *BlockExecutor) *ConsensusState {
cs := &ConsensusState{
client: client,
blockExec: blockExec,
peerMsgQueue: make(chan MsgInfo, msgQueueSize),
internalMsgQueue: make(chan MsgInfo, msgQueueSize),
timeoutTicker: NewTimeoutTicker(),
evpool: evpool,
Quit: make(chan struct{}),
txsAvailable: make(chan int64, 1),
begCons: time.Time{},
}
atomic.CompareAndSwapUint32(&cs.status, 0, 0)
// set function defaults (may be overwritten before calling Start)
cs.decideProposal = cs.defaultDecideProposal
cs.doPrevote = cs.defaultDoPrevote
......@@ -137,15 +133,10 @@ func (cs *ConsensusState) SetBroadcastChannel(broadcastChannel chan<- MsgInfo) {
// IsRunning method
func (cs *ConsensusState) IsRunning() bool {
return atomic.LoadUint32(&cs.started) == 1 && atomic.LoadUint32(&cs.stopped) == 0
return atomic.LoadUint32(&cs.status) == 1
}
//----------------------------------------
// String returns a string.
func (cs *ConsensusState) String() string {
// better not to access shared variables
return fmt.Sprintf("ConsensusState") //(H:%v R:%v S:%v", cs.Height, cs.Round, cs.Step)
}
// GetState returns a copy of the chain state.
func (cs *ConsensusState) GetState() State {
......@@ -189,18 +180,15 @@ func (cs *ConsensusState) SetTimeoutTicker(timeoutTicker TimeoutTicker) {
func (cs *ConsensusState) LoadCommit(height int64) *tmtypes.TendermintCommit {
cs.mtx.Lock()
defer cs.mtx.Unlock()
if height == cs.client.GetCurrentHeight() {
return cs.client.LoadSeenCommit(height)
if height == cs.client.csStore.LoadStateHeight() {
return cs.client.csStore.LoadSeenCommit(height)
}
return cs.client.LoadBlockCommit(height + 1)
}
// Start It start first time starts the timeout checkTxsAvailable routine and receive routines.
func (cs *ConsensusState) Start() {
if atomic.CompareAndSwapUint32(&cs.started, 0, 1) {
if atomic.LoadUint32(&cs.stopped) == 1 {
tendermintlog.Error("ConsensusState already stoped")
}
if atomic.CompareAndSwapUint32(&cs.status, 0, 1) {
cs.timeoutTicker.Start()
go cs.checkTxsAvailable()
......@@ -215,6 +203,7 @@ func (cs *ConsensusState) Start() {
// Stop timer and receive routine
func (cs *ConsensusState) Stop() {
atomic.CompareAndSwapUint32(&cs.status, 1, 0)
cs.timeoutTicker.Stop()
cs.Quit <- struct{}{}
}
......@@ -262,7 +251,7 @@ func (cs *ConsensusState) reconstructLastCommit(state State) {
if state.LastBlockHeight == 0 {
return
}
seenCommit := cs.client.LoadSeenCommit(state.LastBlockHeight)
seenCommit := cs.client.csStore.LoadSeenCommit(state.LastBlockHeight)
seenCommitC := ttypes.Commit{TendermintCommit: seenCommit}
lastPrecommits := ttypes.NewVoteSet(state.ChainID, state.LastBlockHeight, seenCommitC.Round(), ttypes.VoteTypePrecommit, state.LastValidators)
for _, item := range seenCommit.Precommits {
......@@ -272,11 +261,11 @@ func (cs *ConsensusState) reconstructLastCommit(state State) {
precommit := &ttypes.Vote{Vote: item}
added, err := lastPrecommits.AddVote(precommit)
if !added || err != nil {
panic(fmt.Sprintf("Panicked on a Crisis: %v", fmt.Sprintf("Failed to reconstruct LastCommit: %v", err)))
panic(fmt.Sprintf("Failed to reconstruct LastCommit: %v", err))
}
}
if !lastPrecommits.HasTwoThirdsMajority() {
panic(fmt.Sprintf("Panicked on a Sanity Check: %v", "Failed to reconstruct LastCommit: Does not have +2/3 maj"))
panic("Failed to reconstruct LastCommit: Does not have +2/3 maj")
}
cs.LastCommit = lastPrecommits
}
......@@ -285,14 +274,12 @@ func (cs *ConsensusState) reconstructLastCommit(state State) {
// The round becomes 0 and cs.Step becomes ttypes.RoundStepNewHeight.
func (cs *ConsensusState) updateToState(state State) {
if cs.CommitRound > -1 && 0 < cs.Height && cs.Height != state.LastBlockHeight {
panic(fmt.Sprintf("Panicked on a Sanity Check: %v", fmt.Sprintf("updateToState() expected state height of %v but found %v",
cs.Height, state.LastBlockHeight)))
panic(fmt.Sprintf("updateToState expected state height of %v but found %v", cs.Height, state.LastBlockHeight))
}
if !cs.state.IsEmpty() && cs.state.LastBlockHeight+1 != cs.Height {
// This might happen when someone else is mutating cs.state.
// Someone forgot to pass in state.Copy() somewhere?!
panic(fmt.Sprintf("Panicked on a Sanity Check: %v", fmt.Sprintf("Inconsistent cs.state.LastBlockHeight+1 %v vs cs.Height %v",
cs.state.LastBlockHeight+1, cs.Height)))
panic(fmt.Sprintf("Inconsistent cs.state.LastBlockHeight+1 %v vs cs.Height %v", cs.state.LastBlockHeight+1, cs.Height))
}
// If state isn't further out than cs.state, just ignore.
......@@ -308,7 +295,7 @@ func (cs *ConsensusState) updateToState(state State) {
lastPrecommits := (*ttypes.VoteSet)(nil)
if cs.CommitRound > -1 && cs.Votes != nil {
if !cs.Votes.Precommits(cs.CommitRound).HasTwoThirdsMajority() {
panic(fmt.Sprintf("Panicked on a Sanity Check: %v", "updateToState(state) called but last Precommit round didn't have +2/3"))
panic("updateToState called but last Precommit round didn't have +2/3")
}
lastPrecommits = cs.Votes.Precommits(cs.CommitRound)
}
......@@ -333,8 +320,10 @@ func (cs *ConsensusState) updateToState(state State) {
cs.Proposal = nil
cs.ProposalBlock = nil
cs.ProposalBlockHash = nil
cs.LockedRound = 0
cs.LockedRound = -1
cs.LockedBlock = nil
cs.ValidRound = -1
cs.ValidBlock = nil
cs.Votes = ttypes.NewHeightVoteSet(state.ChainID, height, validators)
cs.CommitRound = -1
cs.LastCommit = lastPrecommits
......@@ -428,10 +417,11 @@ func (cs *ConsensusState) handleMsg(mi MsgInfo) {
case *tmtypes.Vote:
// attempt to add the vote and dupeout the validator if its a duplicate signature
// if the vote gives us a 2/3-any or 2/3-one, we transition
err := cs.tryAddVote(msg, peerID, peerIP)
if err == ErrAddingVote {
// TODO: punish peer
}
err = cs.tryAddVote(msg, peerID, peerIP)
//if err == ErrAddingVote {
// TODO: punish peer
//}
// NOTE: the vote is broadcast to peers by the reactor listening
// for vote events
......@@ -443,12 +433,12 @@ func (cs *ConsensusState) handleMsg(mi MsgInfo) {
tendermintlog.Error("Unknown msg type", msg.String(), "peerid", peerID, "peerip", peerIP)
}
if err != nil {
tendermintlog.Error("Error with msg", "type", reflect.TypeOf(msg), "peerid", peerID, "peerip", peerIP, "err", err, "msg", msg)
//tendermintlog.Error("Error with msg", "type", reflect.TypeOf(msg), "peerid", peerID, "peerip", peerIP, "err", err, "msg", msg)
}
}
func (cs *ConsensusState) handleTimeout(ti timeoutInfo, rs ttypes.RoundState) {
tendermintlog.Debug("Received tock", "timeout", ti.Duration, "height", ti.Height, "round", ti.Round, "step", ti.Step)
tendermintlog.Debug("Received tock", "timeout", ti.String())
// timeouts must be for current height, round, step
if ti.Height != rs.Height || ti.Round < rs.Round || (ti.Round == rs.Round && ti.Step < rs.Step) {
......@@ -472,6 +462,7 @@ func (cs *ConsensusState) handleTimeout(ti timeoutInfo, rs ttypes.RoundState) {
case ttypes.RoundStepPrevoteWait:
cs.enterPrecommit(ti.Height, ti.Round)
case ttypes.RoundStepPrecommitWait:
cs.enterPrecommit(ti.Height, ti.Round)
cs.enterNewRound(ti.Height, ti.Round+1)
default:
panic(fmt.Sprintf("Invalid timeout step: %v", ti.Step))
......@@ -543,9 +534,9 @@ func (cs *ConsensusState) enterNewRound(height int64, round int) {
if cs.Round < round {
validators = validators.Copy()
validators.IncrementAccum(round - cs.Round)
tendermintlog.Debug("enterNewRound validator changed", "csr", cs.Round, "round", round)
tendermintlog.Debug("enterNewRound validator changed", "cs.Round", cs.Round, "round", round)
}
tendermintlog.Debug("enterNewRound proposer ", "proposer", validators.Proposer, "validators", validators)
tendermintlog.Debug("enterNewRound proposer", "proposer", validators.Proposer, "validators", validators)
// Setup new round
// we don't fire newStep for this step,
// but we fire an event, so update the round step first
......@@ -615,7 +606,7 @@ func (cs *ConsensusState) proposalHeartbeat(height int64, round int) {
// Enter (!CreateEmptyBlocks) : after enterNewRound(height,round), once txs are in the mempool
func (cs *ConsensusState) enterPropose(height int64, round int) {
if cs.Height != height || round < cs.Round || (cs.Round == round && ttypes.RoundStepPropose <= cs.Step) {
tendermintlog.Info(fmt.Sprintf("enterPropose(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
tendermintlog.Debug(fmt.Sprintf("enterPropose(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
return
}
tendermintlog.Info(fmt.Sprintf("enterPropose(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
......@@ -647,16 +638,22 @@ func (cs *ConsensusState) enterPropose(height int64, round int) {
// if not a validator, we're done
if !cs.Validators.HasAddress(cs.privValidator.GetAddress()) {
tendermintlog.Debug("This node is not a validator", "addr", cs.privValidator.GetAddress(), "vals", cs.Validators)
tendermintlog.Debug("This node is not a validator",
"privValidator", fmt.Sprintf("%X", ttypes.Fingerprint(cs.privValidator.GetAddress())),
"Validators", cs.Validators.String())
return
}
tendermintlog.Debug("This node is a validator")
if cs.isProposer() {
tendermintlog.Info("enterPropose: Our turn to propose", "proposer", cs.Validators.GetProposer().Address, "privValidator", cs.privValidator)
tendermintlog.Info("enterPropose: Our turn to propose",
"proposer", fmt.Sprintf("%X", ttypes.Fingerprint(cs.Validators.GetProposer().Address)),
"privValidator", fmt.Sprintf("%X", ttypes.Fingerprint(cs.privValidator.GetAddress())))
cs.decideProposal(height, round)
} else {
tendermintlog.Info("enterPropose: Not our turn to propose", "proposer", cs.Validators.GetProposer().Address, "privValidator", cs.privValidator)
tendermintlog.Info("enterPropose: Not our turn to propose",
"proposer", fmt.Sprintf("%X", ttypes.Fingerprint(cs.Validators.GetProposer().Address)),
"privValidator", fmt.Sprintf("%X", ttypes.Fingerprint(cs.privValidator.GetAddress())))
}
}
......@@ -669,9 +666,9 @@ func (cs *ConsensusState) defaultDecideProposal(height int64, round int) {
var block *ttypes.TendermintBlock
// Decide on block
if cs.LockedBlock != nil {
// If we're locked onto a block, just choose that.
block = cs.LockedBlock
if cs.ValidBlock != nil {
// If there is valid block, choose that.
block = cs.ValidBlock
} else {
// Create a new proposal block from state/txs from the mempool.
block = cs.createProposalBlock()
......@@ -681,8 +678,8 @@ func (cs *ConsensusState) defaultDecideProposal(height int64, round int) {
}
// Make proposal
polRound, polBlockID := cs.Votes.POLInfo()
proposal := ttypes.NewProposal(height, round, block.Hash(), polRound, polBlockID.BlockID)
propBlockID := tmtypes.BlockID{Hash: block.Hash()}
proposal := ttypes.NewProposal(height, round, block.Hash(), cs.ValidRound, propBlockID)
if err := cs.privValidator.SignProposal(cs.state.ChainID, proposal); err == nil {
// send proposal and block on internal msg queue
cs.sendInternalMessage(MsgInfo{ttypes.ProposalID, &proposal.Proposal, cs.ourID, ""})
......@@ -723,7 +720,7 @@ func (cs *ConsensusState) createProposalBlock() (block *ttypes.TendermintBlock)
} else {
// This shouldn't happen.
tendermintlog.Error("enterPropose: Cannot propose anything: No commit for the previous block.")
return
return nil
}
// Mempool validated transactions
......@@ -734,18 +731,37 @@ func (cs *ConsensusState) createProposalBlock() (block *ttypes.TendermintBlock)
if pblock.Height != cs.Height {
tendermintlog.Error("pblock.Height is not equal to cs.Height")
return
return nil
}
block = cs.state.MakeBlock(cs.Height, int64(cs.Round), pblock.Txs, commit)
tendermintlog.Info("createProposalBlock block", "txs-len", len(block.Txs))
block.ProposerAddr = cs.privValidator.GetAddress()
evidence := cs.evpool.PendingEvidence()
block.AddEvidence(evidence)
proposerAddr := cs.privValidator.GetAddress()
block = cs.state.MakeBlock(cs.Height, int64(cs.Round), pblock, commit, proposerAddr)
baseTx := cs.createBaseTx(block.TendermintBlock)
block.Data.Txs[0] = baseTx
block.Data.TxHash = merkle.CalcMerkleRoot(block.Data.Txs)
pblockNew := cs.client.PreExecBlock(block.Data, false)
if pblockNew == nil {
tendermintlog.Error("createProposalBlock PreExecBlock fail")
return nil
}
block.Data = pblockNew
return block
}
func (cs *ConsensusState) createBaseTx(block *tmtypes.TendermintBlock) (tx *types.Transaction) {
var state *tmtypes.State
if cs.Height == 1 {
state = &tmtypes.State{}
} else {
state = cs.client.csStore.LoadStateFromStore()
if state == nil {
panic("createBaseTx LoadStateFromStore fail")
}
}
tx = CreateBlockInfoTx(cs.client.pubKey, state, block)
return tx
}
// Enter: `timeoutPropose` after entering Propose.
// Enter: proposal block and POL is ready.
// Enter: any +2/3 prevotes for future round.
......@@ -763,14 +779,6 @@ func (cs *ConsensusState) enterPrevote(height int64, round int) {
cs.newStep()
}()
// fire event for how we got here
if cs.isProposalComplete() {
//cs.eventBus.PublishEventCompleteProposal(cs.RoundStateEvent())
} else {
// we received +2/3 prevotes for a future round
// TODO: catchup event?
}
tendermintlog.Info(fmt.Sprintf("enterPrevote(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step), "cost", types.Since(cs.begCons))
// Sign and broadcast vote as necessary
......@@ -804,6 +812,20 @@ func (cs *ConsensusState) defaultDoPrevote(height int64, round int) {
return
}
// PreExec proposal block
blockCopy := *cs.ProposalBlock.Data
blockNew := cs.client.PreExecBlock(&blockCopy, true)
if blockNew == nil {
tendermintlog.Error("enterPrevote: PreExec ProposalBlock fail")
cs.signAddVote(ttypes.VoteTypePrevote, nil)
return
}
if !bytes.Equal(blockNew.Hash(), cs.ProposalBlock.Data.Hash()) {
tendermintlog.Error("enterPrevote: PreExec ProposalBlock has change")
cs.signAddVote(ttypes.VoteTypePrevote, nil)
return
}
// Prevote cs.ProposalBlock
// NOTE: the proposal signature is validated when it is received,
// and the proposal block parts are validated as they are received (against the merkle hash in the proposal)
......@@ -818,7 +840,7 @@ func (cs *ConsensusState) enterPrevoteWait(height int64, round int) {
return
}
if !cs.Votes.Prevotes(round).HasTwoThirdsAny() {
panic(fmt.Sprintf("Panicked on a Sanity Check: %v", fmt.Sprintf("enterPrevoteWait(%v/%v), but Prevotes does not have any +2/3 votes", height, round)))
panic(fmt.Sprintf("enterPrevoteWait(%v/%v), but Prevotes does not have any +2/3 votes", height, round))
}
tendermintlog.Info(fmt.Sprintf("enterPrevoteWait(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
......@@ -868,7 +890,7 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) {
// the latest POLRound should be this round
polRound, _ := cs.Votes.POLInfo()
if polRound < round {
panic(fmt.Sprintf("Panicked on a Sanity Check: %v", fmt.Sprintf("This POLRound should be %v but got %v", round, polRound)))
panic(fmt.Sprintf("This POLRound should be %v but got %v", round, polRound))
}
// +2/3 prevoted nil. Unlock and precommit nil.
......@@ -877,7 +899,7 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) {
tendermintlog.Info("enterPrecommit: +2/3 prevoted for nil.")
} else {
tendermintlog.Info("enterPrecommit: +2/3 prevoted for nil. Unlocking")
cs.LockedRound = 0
cs.LockedRound = -1
cs.LockedBlock = nil
}
cs.signAddVote(ttypes.VoteTypePrecommit, nil)
......@@ -899,7 +921,7 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) {
tendermintlog.Info("enterPrecommit: +2/3 prevoted proposal block. Locking", "hash", fmt.Sprintf("%X", blockID.Hash))
// Validate the block.
if err := cs.blockExec.ValidateBlock(cs.state, cs.ProposalBlock); err != nil {
panic(fmt.Sprintf("Panicked on a Consensus Failure: %v", fmt.Sprintf("enterPrecommit: +2/3 prevoted for an invalid block: %v", err)))
panic(fmt.Sprintf("enterPrecommit: +2/3 prevoted for an invalid block: %v", err))
}
cs.LockedRound = round
cs.LockedBlock = cs.ProposalBlock
......@@ -911,7 +933,7 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) {
// Fetch that block, unlock, and precommit nil.
// The +2/3 prevotes for this round is the POL for our unlock.
// TODO: In the future save the POL prevotes for justification.
cs.LockedRound = 0
cs.LockedRound = -1
cs.LockedBlock = nil
if !bytes.Equal(cs.ProposalBlockHash, blockID.Hash) {
cs.ProposalBlock = nil
......@@ -927,7 +949,7 @@ func (cs *ConsensusState) enterPrecommitWait(height int64, round int) {
return
}
if !cs.Votes.Precommits(round).HasTwoThirdsAny() {
panic(fmt.Sprintf("Panicked on a Sanity Check: %v", fmt.Sprintf("enterPrecommitWait(%v/%v), but Precommits does not have any +2/3 votes", height, round)))
panic(fmt.Sprintf("enterPrecommitWait(%v/%v), but Precommits does not have any +2/3 votes", height, round))
}
tendermintlog.Info(fmt.Sprintf("enterPrecommitWait(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
......@@ -964,7 +986,7 @@ func (cs *ConsensusState) enterCommit(height int64, commitRound int) {
blockID, ok := cs.Votes.Precommits(commitRound).TwoThirdsMajority()
if !ok {
panic(fmt.Sprintf("Panicked on a Sanity Check: %v", "RunActionCommit() expects +2/3 precommits"))
panic("RunActionCommit() expects +2/3 precommits")
}
// The Locked* fields no longer matter.
......@@ -985,16 +1007,25 @@ func (cs *ConsensusState) enterCommit(height int64, commitRound int) {
// Set up ProposalBlockHash and keep waiting.
cs.ProposalBlock = nil
cs.ProposalBlockHash = blockID.Hash
} else {
// We just need to keep waiting.
validBlockMsg := &tmtypes.ValidBlockMsg{
Height: cs.Height,
Round: int32(cs.Round),
Blockhash: cs.ProposalBlockHash,
IsCommit: false,
}
cs.broadcastChannel <- MsgInfo{TypeID: ttypes.ValidBlockID, Msg: validBlockMsg, PeerID: cs.ourID, PeerIP: ""}
}
//else {
// We just need to keep waiting.
//}
}
}
// If we have the block AND +2/3 commits for it, finalize.
func (cs *ConsensusState) tryFinalizeCommit(height int64) {
if cs.Height != height {
panic(fmt.Sprintf("Panicked on a Sanity Check: %v", fmt.Sprintf("tryFinalizeCommit() cs.Height: %v vs height: %v", cs.Height, height)))
panic(fmt.Sprintf("tryFinalizeCommit() cs.Height: %v vs height: %v", cs.Height, height))
}
blockID, ok := cs.Votes.Precommits(cs.CommitRound).TwoThirdsMajority()
......@@ -1027,14 +1058,14 @@ func (cs *ConsensusState) finalizeCommit(height int64) {
block := cs.ProposalBlock
if !ok {
panic(fmt.Sprintf("Panicked on a Sanity Check: %v", fmt.Sprintf("Cannot finalizeCommit, commit does not have two thirds majority")))
panic(fmt.Sprintf("Cannot finalizeCommit, commit does not have two thirds majority"))
}
if !block.HashesTo(blockID.Hash) {
panic(fmt.Sprintf("Panicked on a Sanity Check: %v", fmt.Sprintf("Cannot finalizeCommit, ProposalBlock does not hash to commit hash")))
panic(fmt.Sprintf("Cannot finalizeCommit, ProposalBlock does not hash to commit hash"))
}
if err := cs.blockExec.ValidateBlock(cs.state, block); err != nil {
panic(fmt.Sprintf("Panicked on a Sanity Check: %v", fmt.Sprintf("+2/3 committed an invalid block: %v", err)))
panic(fmt.Sprintf("+2/3 committed an invalid block: %v", err))
}
stateCopy := cs.state.Copy()
......@@ -1043,55 +1074,21 @@ func (cs *ConsensusState) finalizeCommit(height int64) {
var err error
stateCopy, err = cs.blockExec.ApplyBlock(stateCopy, ttypes.BlockID{BlockID: tmtypes.BlockID{Hash: block.Hash()}}, block)
if err != nil {
tendermintlog.Error("Error on ApplyBlock", "err", err)
cs.enterNewRound(cs.Height, cs.CommitRound+1)
return
panic(fmt.Sprintf("finalizeCommit ApplyBlock fail: %v", err))
}
newState := SaveState(stateCopy)
tendermintlog.Info(fmt.Sprintf("Save consensus state. Current: %v/%v/%v", cs.Height, cs.CommitRound, cs.Step), "cost", types.Since(cs.begCons))
// original proposer commit block
if bytes.Equal(cs.privValidator.GetAddress(), block.TendermintBlock.ProposerAddr) {
newProposal := cs.Proposal
tendermintlog.Debug("finalizeCommit proposal block txs hash", "height", block.Header.Height, "tx-hash", fmt.Sprintf("%X", merkle.CalcMerkleRoot(block.Txs)))
commitBlock := &types.Block{}
commitBlock.Height = block.Header.Height
commitBlock.Txs = make([]*types.Transaction, 1, len(block.Txs)+1)
commitBlock.Txs = append(commitBlock.Txs, block.Txs...)
lastCommit := block.LastCommit
precommits := cs.Votes.Precommits(cs.CommitRound)
seenCommit := precommits.MakeCommit()
tx0 := CreateBlockInfoTx(cs.client.pubKey, lastCommit, seenCommit, newState, newProposal, cs.ProposalBlock.TendermintBlock)
commitBlock.Txs[0] = tx0
cs.mtx.Unlock()
err = cs.client.CommitBlock(commitBlock)
cs.mtx.Lock()
if err != nil {
cs.LockedRound = 0
cs.LockedBlock = nil
tendermintlog.Info(fmt.Sprintf("Proposer continue consensus. Current: %v/%v/%v", cs.Height, cs.Round, cs.Step),
"CommitRound", cs.CommitRound, "cost", types.Since(cs.begCons))
cs.enterNewRound(cs.Height, cs.CommitRound+1)
return
}
// commit block
commitBlock := cs.ProposalBlock.Data
err = cs.client.CommitBlock(commitBlock)
if err != nil {
panic(fmt.Sprintf("finalizeCommit CommitBlock fail: %v", err))
}
if bytes.Equal(cs.privValidator.GetAddress(), block.TendermintBlock.Header.ProposerAddr) {
tendermintlog.Info(fmt.Sprintf("Proposer reach consensus. Current: %v/%v/%v", cs.Height, cs.Round, cs.Step), "CommitRound", cs.CommitRound,
"tx-len", len(commitBlock.Txs), "cost", types.Since(cs.begCons), "proposer-addr", fmt.Sprintf("%X", ttypes.Fingerprint(block.TendermintBlock.ProposerAddr)))
"tx-len", len(commitBlock.Txs), "cost", types.Since(cs.begCons), "proposer-addr", fmt.Sprintf("%X", ttypes.Fingerprint(block.TendermintBlock.Header.ProposerAddr)))
} else {
cs.mtx.Unlock()
reachCons := cs.client.CheckCommit(block.Header.Height)
cs.mtx.Lock()
if !reachCons {
cs.LockedRound = 0
cs.LockedBlock = nil
tendermintlog.Info(fmt.Sprintf("Not-Proposer continue consensus, will catchup. Current: %v/%v/%v", cs.Height, cs.Round, cs.Step),
"CommitRound", cs.CommitRound, "cost", types.Since(cs.begCons))
cs.enterNewRound(cs.Height, cs.CommitRound+1)
return
}
tendermintlog.Info(fmt.Sprintf("Not-Proposer reach consensus. Current: %v/%v/%v", cs.Height, cs.Round, cs.Step), "CommitRound", cs.CommitRound,
"tx-len", block.Header.NumTxs+1, "cost", types.Since(cs.begCons), "proposer-addr", fmt.Sprintf("%X", ttypes.Fingerprint(block.TendermintBlock.ProposerAddr)))
"tx-len", len(commitBlock.Txs), "cost", types.Since(cs.begCons), "proposer-addr", fmt.Sprintf("%X", ttypes.Fingerprint(block.TendermintBlock.Header.ProposerAddr)))
}
//check whether need update validator nodes
......@@ -1111,7 +1108,18 @@ func (cs *ConsensusState) finalizeCommit(height int64) {
stateCopy.Validators = nextValSet
}
}
tendermintlog.Debug("finalizeCommit validators of statecopy", "validators", stateCopy.Validators)
tendermintlog.Debug("finalizeCommit validators of statecopy", "validators", stateCopy.Validators.String())
// save local state and seen commit
precommits := cs.Votes.Precommits(cs.CommitRound)
seenCommit := precommits.MakeCommit()
newState := SaveState(stateCopy)
err = cs.client.csStore.SaveConsensusState(height, newState, seenCommit)
if err != nil {
panic(fmt.Sprintf("finalizeCommit SaveSeenCommit fail: %v", err))
}
tendermintlog.Info(fmt.Sprintf("Save consensus state. Current: %v/%v/%v", cs.Height, cs.CommitRound, cs.Step), "cost", types.Since(cs.begCons))
// NewHeightStep!
cs.updateToState(stateCopy)
......@@ -1150,15 +1158,9 @@ func (cs *ConsensusState) defaultSetProposal(proposal *tmtypes.Proposal) error {
cs.begCons = time.Now()
}
// We don't care about the proposal if we're already in ttypes.RoundStepCommit.
if ttypes.RoundStepCommit <= cs.Step {
tendermintlog.Error("defaultSetProposal: already in RoundStepCommit")
return nil
}
// Verify POLRound, which must be -1 or between 0 and proposal.Round exclusive.
if proposal.POLRound != -1 &&
(proposal.POLRound < 0 || proposal.Round <= proposal.POLRound) {
// Verify POLRound, which must be -1 or in range [0, proposal.Round).
if proposal.POLRound != -1 ||
(proposal.POLRound >= 0 && proposal.Round >= proposal.POLRound) {
return ErrInvalidProposalPOLRound
}
......@@ -1221,9 +1223,29 @@ func (cs *ConsensusState) addProposalBlock(proposalBlock *tmtypes.TendermintBloc
tendermintlog.Info(fmt.Sprintf("Consensus set proposal block. Current: %v/%v/%v", cs.Height, cs.Round, cs.Step),
"ProposalBlockHash", fmt.Sprintf("%X", cs.ProposalBlockHash), "cost", types.Since(cs.begCons))
if cs.Step <= ttypes.RoundStepPropose {
// Update Valid* if we can.
prevotes := cs.Votes.Prevotes(cs.Round)
blockID, hasTwoThirds := prevotes.TwoThirdsMajority()
if hasTwoThirds && len(blockID.Hash) == 0 && (cs.ValidRound < cs.Round) {
if cs.ProposalBlock.HashesTo(blockID.Hash) {
tendermintlog.Info("Updating valid block to new proposal block",
"valid-round", cs.Round, "valid-block-hash", cs.ProposalBlock.Hash())
cs.ValidRound = cs.Round
cs.ValidBlock = cs.ProposalBlock
}
// TODO: In case there is +2/3 majority in Prevotes set for some
// block and cs.ProposalBlock contains different block, either
// proposer is faulty or voting power of faulty processes is more
// than 1/3. We should trigger in the future accountability
// procedure at this point.
}
if cs.Step <= ttypes.RoundStepPropose && cs.isProposalComplete() {
// Move onto the next step
cs.enterPrevote(cs.Height, cs.Round)
if hasTwoThirds { // this is optimisation as this will be triggered when prevote is added
cs.enterPrecommit(cs.Height, cs.Round)
}
} else if cs.Step == ttypes.RoundStepCommit {
// If we're waiting on the proposal block...
cs.tryFinalizeCommit(cs.Height)
......@@ -1241,13 +1263,11 @@ func (cs *ConsensusState) tryAddVote(voteRaw *tmtypes.Vote, peerID string, peerI
// If it's otherwise invalid, punish peer.
if err == ErrVoteHeightMismatch {
return err
} else if voteErr, ok := err.(*ttypes.ErrVoteConflictingVotes); ok {
} else if err == ttypes.ErrVoteConflict {
if bytes.Equal(vote.ValidatorAddress, cs.privValidator.GetAddress()) {
tendermintlog.Error("Found conflicting vote from ourselves. Did you unsafe_reset a validator?", "height", vote.Height, "round", vote.Round, "type", vote.Type)
return err
}
err = cs.evpool.AddEvidence(voteErr.DuplicateVoteEvidence)
return err
} else {
// Probably an invalid signature / Bad peer.
// Seems this can also err sometimes with "Unexpected step" - perhaps not from a bad peer ?
......@@ -1264,104 +1284,161 @@ func (cs *ConsensusState) addVote(vote *ttypes.Vote, peerID string, peerIP strin
tendermintlog.Debug(fmt.Sprintf("Consensus receive vote. Current: %v/%v/%v", cs.Height, cs.Round, cs.Step),
"vote", fmt.Sprintf("{%v:%X %v/%02d/%v}", vote.ValidatorIndex, ttypes.Fingerprint(vote.ValidatorAddress), vote.Height, vote.Round, vote.Type), "peerip", peerIP)
// A prevote/precommit for this height?
if vote.Height == cs.Height {
if cs.begCons.IsZero() {
cs.begCons = time.Now()
// A precommit for the previous height
// These come in while we wait timeoutCommit
if vote.Height+1 == cs.Height {
if !(cs.Step == ttypes.RoundStepNewHeight && vote.Type == uint32(ttypes.VoteTypePrecommit)) {
// TODO: give the reason ..
// fmt.Errorf("tryAddVote: Wrong height, not a LastCommit straggler commit.")
return added, ErrVoteHeightMismatch
}
added, err = cs.LastCommit.AddVote(vote)
if !added {
return added, err
}
tendermintlog.Info(fmt.Sprintf("Added to lastPrecommits: %v", cs.LastCommit.StringShort()))
hasVoteMsg := &tmtypes.HasVoteMsg{
Height: vote.Height,
Round: vote.Round,
Type: int32(vote.Type),
Index: vote.ValidatorIndex,
}
cs.broadcastChannel <- MsgInfo{TypeID: ttypes.HasVoteID, Msg: hasVoteMsg, PeerID: cs.ourID, PeerIP: ""}
height := cs.Height
added, err = cs.Votes.AddVote(vote, peerID)
if added {
//cs.broadcastChannel <- MsgInfo{TypeID: ttypes.VoteID, Msg: vote.Vote, PeerID: cs.ourID, PeerIP: ""}
hasVoteMsg := &tmtypes.HasVoteMsg{
Height: vote.Height,
Round: vote.Round,
Type: int32(vote.Type),
Index: vote.ValidatorIndex,
// if we can skip timeoutCommit and have all the votes now,
if skipTimeoutCommit && cs.LastCommit.HasAll() {
// go straight to new round (skip timeout commit)
// cs.scheduleTimeout(time.Duration(0), cs.Height, 0, cstypes.RoundStepNewHeight)
cs.enterNewRound(cs.Height, 0)
}
return
}
// Height mismatch is ignored.
// Not necessarily a bad peer, but not favourable behaviour.
if vote.Height != cs.Height {
err = ErrVoteHeightMismatch
tendermintlog.Info("Vote ignored and not added", "voteHeight", vote.Height, "csHeight", cs.Height, "peerID", peerID)
return
}
if cs.begCons.IsZero() {
cs.begCons = time.Now()
}
height := cs.Height
added, err = cs.Votes.AddVote(vote, peerID)
if !added {
// Either duplicate, or error upon cs.Votes.AddByIndex()
return
}
//cs.broadcastChannel <- MsgInfo{TypeID: ttypes.VoteID, Msg: vote.Vote, PeerID: cs.ourID, PeerIP: ""}
hasVoteMsg := &tmtypes.HasVoteMsg{
Height: vote.Height,
Round: vote.Round,
Type: int32(vote.Type),
Index: vote.ValidatorIndex,
}
cs.broadcastChannel <- MsgInfo{TypeID: ttypes.HasVoteID, Msg: hasVoteMsg, PeerID: cs.ourID, PeerIP: ""}
switch vote.Type {
case uint32(ttypes.VoteTypePrevote):
prevotes := cs.Votes.Prevotes(int(vote.Round))
tendermintlog.Info("Added to prevote", "vote", vote, "prevotes", prevotes.StringShort())
// If +2/3 prevotes for a block or nil for *any* round:
if blockID, ok := prevotes.TwoThirdsMajority(); ok {
// There was a polka!
// If we're locked but this is a recent polka, unlock.
// If it matches our ProposalBlock, update the ValidBlock
// Unlock if `cs.LockedRound < vote.Round <= cs.Round`
// NOTE: If vote.Round > cs.Round, we'll deal with it when we get to vote.Round
if (cs.LockedBlock != nil) &&
(cs.LockedRound < int(vote.Round)) &&
(int(vote.Round) <= cs.Round) &&
!cs.LockedBlock.HashesTo(blockID.Hash) {
tendermintlog.Info("Unlocking because of POL.", "lockedRound", cs.LockedRound, "POLRound", vote.Round)
cs.LockedRound = -1
cs.LockedBlock = nil
}
cs.broadcastChannel <- MsgInfo{TypeID: ttypes.HasVoteID, Msg: hasVoteMsg, PeerID: cs.ourID, PeerIP: ""}
switch vote.Type {
case uint32(ttypes.VoteTypePrevote):
prevotes := cs.Votes.Prevotes(int(vote.Round))
tendermintlog.Info("Added to prevote", "vote", vote, "prevotes", prevotes.StringShort())
// If +2/3 prevotes for a block or nil for *any* round:
if blockID, ok := prevotes.TwoThirdsMajority(); ok {
// There was a polka!
// If we're locked but this is a recent polka, unlock.
// If it matches our ProposalBlock, update the ValidBlock
// Unlock if `cs.LockedRound < vote.Round <= cs.Round`
// NOTE: If vote.Round > cs.Round, we'll deal with it when we get to vote.Round
if (cs.LockedBlock != nil) &&
(cs.LockedRound < int(vote.Round)) &&
(int(vote.Round) <= cs.Round) &&
!cs.LockedBlock.HashesTo(blockID.Hash) {
tendermintlog.Info("Unlocking because of POL.", "lockedRound", cs.LockedRound, "POLRound", vote.Round)
cs.LockedRound = 0
cs.LockedBlock = nil
}
}
// If +2/3 prevotes for *anything* for this or future round:
if cs.Round <= int(vote.Round) && prevotes.HasTwoThirdsAny() {
// Round-skip over to PrevoteWait or goto Precommit.
cs.enterNewRound(height, int(vote.Round)) // if the vote is ahead of us
if prevotes.HasTwoThirdsMajority() {
cs.enterPrecommit(height, int(vote.Round))
} else {
cs.enterPrevote(height, int(vote.Round)) // if the vote is ahead of us
cs.enterPrevoteWait(height, int(vote.Round))
}
} else if cs.Proposal != nil && 0 <= cs.Proposal.POLRound && cs.Proposal.POLRound == vote.Round {
// If the proposal is now complete, enter prevote of cs.Round.
if cs.isProposalComplete() {
cs.enterPrevote(height, cs.Round)
}
// Update Valid* if we can.
// NOTE: our proposal block may be nil or not what received a polka..
if len(blockID.Hash) != 0 && (cs.ValidRound < int(vote.Round)) && (int(vote.Round) == cs.Round) {
if cs.ProposalBlock.HashesTo(blockID.Hash) {
tendermintlog.Info("Updating ValidBlock because of POL.", "validRound", cs.ValidRound, "POLRound", vote.Round)
cs.ValidRound = int(vote.Round)
cs.ValidBlock = cs.ProposalBlock
} else {
tendermintlog.Info(
"Valid block we don't know about. Set ProposalBlock=nil",
"proposal", fmt.Sprintf("%X", cs.ProposalBlock.Hash()), "blockId", fmt.Sprintf("%X", blockID.Hash))
// We're getting the wrong block.
cs.ProposalBlock = nil
}
cs.ProposalBlockHash = blockID.Hash
case uint32(ttypes.VoteTypePrecommit):
precommits := cs.Votes.Precommits(int(vote.Round))
tendermintlog.Info("Added to precommit", "vote", vote, "precommits", precommits.StringShort())
blockID, ok := precommits.TwoThirdsMajority()
if ok {
if len(blockID.Hash) == 0 {
cs.enterNewRound(height, int(vote.Round)+1)
} else {
cs.enterNewRound(height, int(vote.Round))
cs.enterPrecommit(height, int(vote.Round))
cs.enterCommit(height, int(vote.Round))
if skipTimeoutCommit && precommits.HasAll() {
// if we have all the votes now,
// go straight to new round (skip timeout commit)
cs.enterNewRound(cs.Height, 0)
}
}
} else if cs.Round <= int(vote.Round) && precommits.HasTwoThirdsAny() {
cs.enterNewRound(height, int(vote.Round))
cs.enterPrecommit(height, int(vote.Round))
cs.enterPrecommitWait(height, int(vote.Round))
validBlockMsg := &tmtypes.ValidBlockMsg{
Height: vote.Height,
Round: vote.Round,
Blockhash: cs.ProposalBlockHash,
IsCommit: false,
}
cs.broadcastChannel <- MsgInfo{TypeID: ttypes.ValidBlockID, Msg: validBlockMsg, PeerID: cs.ourID, PeerIP: ""}
}
}
default:
panic(fmt.Sprintf("Panicked on a Sanity Check: %v", fmt.Sprintf("Unexpected vote type %X", vote.Type))) // Should not happen.
// If +2/3 prevotes for *anything* for this or future round:
switch {
case cs.Round < int(vote.Round) && prevotes.HasTwoThirdsAny():
// Round-skip if there is any 2/3+ of votes ahead of us
cs.enterNewRound(height, int(vote.Round))
case cs.Round == int(vote.Round) && ttypes.RoundStepPrevote <= cs.Step: // current round
blockID, ok := prevotes.TwoThirdsMajority()
if ok && (cs.isProposalComplete() || len(blockID.Hash) == 0) {
cs.enterPrecommit(height, int(vote.Round))
} else if prevotes.HasTwoThirdsAny() {
cs.enterPrevoteWait(height, int(vote.Round))
}
case cs.Proposal != nil && 0 <= cs.Proposal.POLRound && cs.Proposal.POLRound == vote.Round:
// If the proposal is now complete, enter prevote of cs.Round.
if cs.isProposalComplete() {
cs.enterPrevote(height, cs.Round)
}
}
// Either duplicate, or error upon cs.Votes.AddByIndex()
// return
} else {
err = ErrVoteHeightMismatch
case uint32(ttypes.VoteTypePrecommit):
precommits := cs.Votes.Precommits(int(vote.Round))
tendermintlog.Info("Added to precommit", "vote", vote, "precommits", precommits.StringShort())
blockID, ok := precommits.TwoThirdsMajority()
if ok {
// Executed as TwoThirdsMajority could be from a higher round
cs.enterNewRound(height, int(vote.Round))
cs.enterPrecommit(height, int(vote.Round))
if len(blockID.Hash) != 0 {
cs.enterCommit(height, int(vote.Round))
if skipTimeoutCommit && precommits.HasAll() {
cs.enterNewRound(cs.Height, 0)
}
} else {
cs.enterPrecommitWait(height, int(vote.Round))
}
} else if cs.Round <= int(vote.Round) && precommits.HasTwoThirdsAny() {
cs.enterNewRound(height, int(vote.Round))
cs.enterPrecommitWait(height, int(vote.Round))
}
default:
panic(fmt.Sprintf("Unexpected vote type %X", vote.Type)) // Should not happen.
}
// Height mismatch, bad peer?
tendermintlog.Debug("Vote ignored and not added", "voteType", vote.Type, "voteHeight", vote.Height, "csHeight", cs.Height, "err", err)
return
return added, err
}
func (cs *ConsensusState) signVote(voteType byte, hash []byte) (*ttypes.Vote, error) {
......
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package tendermint
import (
"encoding/json"
"fmt"
"reflect"
"sync"
dbm "github.com/33cn/chain33/common/db"
ttypes "github.com/33cn/plugin/plugin/consensus/tendermint/types"
tmtypes "github.com/33cn/plugin/plugin/dapp/valnode/types"
"github.com/pkg/errors"
)
/*
Requirements:
- Valid new evidence must be persisted immediately and never forgotten
- Uncommitted evidence must be continuously broadcast
- Uncommitted evidence has a partial order, the evidence's priority
Impl:
- First commit atomically in outqueue, pending, lookup.
- Once broadcast, remove from outqueue. No need to sync
- Once committed, atomically remove from pending and update lookup.
- TODO: If we crash after committed but before removing/updating,
we'll be stuck broadcasting evidence we never know we committed.
so either share the state db and atomically MarkCommitted
with ApplyBlock, or check all outqueue/pending on Start to see if its committed
Schema for indexing evidence (note you need both height and hash to find a piece of evidence):
"evidence-lookup"/<evidence-height>/<evidence-hash> -> EvidenceInfo
"evidence-outqueue"/<priority>/<evidence-height>/<evidence-hash> -> EvidenceInfo
"evidence-pending"/<evidence-height>/<evidence-hash> -> EvidenceInfo
*/
type envelope struct {
Kind string `json:"type"`
Data *json.RawMessage `json:"data"`
}
// EvidenceInfo struct
type EvidenceInfo struct {
Committed bool `json:"committed"`
Priority int64 `json:"priority"`
Evidence envelope `json:"evidence"`
}
const (
baseKeyLookup = "evidence-lookup" // all evidence
baseKeyOutqueue = "evidence-outqueue" // not-yet broadcast
baseKeyPending = "evidence-pending" // broadcast but not committed
)
func keyLookup(evidence ttypes.Evidence) []byte {
return keyLookupFromHeightAndHash(evidence.Height(), evidence.Hash())
}
// big endian padded hex
func bE(h int64) string {
return fmt.Sprintf("%0.16X", h)
}
func keyLookupFromHeightAndHash(height int64, hash []byte) []byte {
return _key("%s/%s/%X", baseKeyLookup, bE(height), hash)
}
func keyOutqueue(evidence ttypes.Evidence, priority int64) []byte {
return _key("%s/%s/%s/%X", baseKeyOutqueue, bE(priority), bE(evidence.Height()), evidence.Hash())
}
func keyPending(evidence ttypes.Evidence) []byte {
return _key("%s/%s/%X", baseKeyPending, bE(evidence.Height()), evidence.Hash())
}
func _key(s string, o ...interface{}) []byte {
return []byte(fmt.Sprintf(s, o...))
}
// EvidenceStore is a store of all the evidence we've seen, including
// evidence that has been committed, evidence that has been verified but not broadcast,
// and evidence that has been broadcast but not yet committed.
type EvidenceStore struct {
db dbm.DB
}
// NewEvidenceStore method
func NewEvidenceStore(db dbm.DB) *EvidenceStore {
if len(ttypes.EvidenceType2Type) == 0 {
ttypes.EvidenceType2Type = map[string]reflect.Type{
ttypes.DuplicateVote: reflect.TypeOf(tmtypes.DuplicateVoteEvidence{}),
}
}
if len(ttypes.EvidenceType2Obj) == 0 {
ttypes.EvidenceType2Obj = map[string]ttypes.Evidence{
ttypes.DuplicateVote: &ttypes.DuplicateVoteEvidence{},
}
}
return &EvidenceStore{
db: db,
}
}
// PriorityEvidence returns the evidence from the outqueue, sorted by highest priority.
func (store *EvidenceStore) PriorityEvidence() (evidence []ttypes.Evidence) {
// reverse the order so highest priority is first
l := store.ListEvidence(baseKeyOutqueue)
l2 := make([]ttypes.Evidence, len(l))
for i := range l {
l2[i] = l[len(l)-1-i]
}
return l2
}
// PendingEvidence returns all known uncommitted evidence.
func (store *EvidenceStore) PendingEvidence() (evidence []ttypes.Evidence) {
return store.ListEvidence(baseKeyPending)
}
// ListEvidence lists the evidence for the given prefix key.
// It is wrapped by PriorityEvidence and PendingEvidence for convenience.
func (store *EvidenceStore) ListEvidence(prefixKey string) (evidence []ttypes.Evidence) {
iter := store.db.Iterator([]byte(prefixKey), nil, false)
for iter.Next() {
val := iter.Value()
evi, err := store.EvidenceFromInfoBytes(val)
if err != nil {
fmt.Printf("ListEvidence evidence info unmarshal failed:%v", err)
} else {
evidence = append(evidence, evi)
}
}
return evidence
}
// GetEvidence fetches the evidence with the given height and hash.
func (store *EvidenceStore) GetEvidence(height int64, hash []byte) *EvidenceInfo {
key := keyLookupFromHeightAndHash(height, hash)
val, e := store.db.Get(key)
if e != nil {
fmt.Printf(fmt.Sprintf(`GetEvidence: db get key %v failed:%v\n`, key, e))
}
if len(val) == 0 {
return nil
}
var ei EvidenceInfo
err := json.Unmarshal(val, &ei)
if err != nil {
fmt.Printf(fmt.Sprintf(`GetEvidence: unmarshal failed:%v\n`, err))
}
return &ei
}
// AddNewEvidence adds the given evidence to the database.
// It returns false if the evidence is already stored.
func (store *EvidenceStore) AddNewEvidence(evidence ttypes.Evidence, priority int64) bool {
// check if we already have seen it
ei := store.GetEvidence(evidence.Height(), evidence.Hash())
if ei != nil && len(ei.Evidence.Kind) == 0 {
return false
}
eiBytes, err := EvidenceToInfoBytes(evidence, priority)
if err != nil {
fmt.Printf("AddNewEvidence failed:%v\n", err)
return false
}
// add it to the store
key := keyOutqueue(evidence, priority)
if err = store.db.Set(key, eiBytes); err != nil {
fmt.Printf("AddNewEvidence Set failed:%v\n", err)
return false
}
key = keyPending(evidence)
if err = store.db.Set(key, eiBytes); err != nil {
fmt.Printf("AddNewEvidence Set failed:%v\n", err)
return false
}
key = keyLookup(evidence)
if err = store.db.SetSync(key, eiBytes); err != nil {
fmt.Printf("AddNewEvidence SetSync failed:%v\n", err)
return false
}
return true
}
// MarkEvidenceAsBroadcasted removes evidence from Outqueue.
func (store *EvidenceStore) MarkEvidenceAsBroadcasted(evidence ttypes.Evidence) {
ei := store.getEvidenceInfo(evidence)
key := keyOutqueue(evidence, ei.Priority)
if err := store.db.Delete(key); err != nil {
fmt.Printf("MarkEvidenceAsBroadcasted Delete failed:%v", err)
}
}
// MarkEvidenceAsCommitted removes evidence from pending and outqueue and sets the state to committed.
func (store *EvidenceStore) MarkEvidenceAsCommitted(evidence ttypes.Evidence) {
// if its committed, its been broadcast
store.MarkEvidenceAsBroadcasted(evidence)
pendingKey := keyPending(evidence)
if err := store.db.Delete(pendingKey); err != nil {
fmt.Printf("MarkEvidenceAsCommitted Delete failed:%v", err)
}
ei := store.getEvidenceInfo(evidence)
ei.Committed = true
lookupKey := keyLookup(evidence)
eiBytes, err := json.Marshal(ei)
if err != nil {
fmt.Printf("MarkEvidenceAsCommitted marshal failed:%v", err)
}
if err = store.db.SetSync(lookupKey, eiBytes); err != nil {
fmt.Printf("MarkEvidenceAsCommitted SetSync failed:%v", err)
}
}
//---------------------------------------------------
// utils
func (store *EvidenceStore) getEvidenceInfo(evidence ttypes.Evidence) EvidenceInfo {
key := keyLookup(evidence)
var ei EvidenceInfo
b, e := store.db.Get(key)
if e != nil {
fmt.Printf(fmt.Sprintf(`getEvidenceInfo: db get key %v failed:%v\n`, key, e))
}
err := json.Unmarshal(b, &ei)
if err != nil {
fmt.Printf(fmt.Sprintf(`getEvidenceInfo: Unmarshal failed:%v\n`, err))
}
return ei
}
// EvidenceToInfoBytes method
func EvidenceToInfoBytes(evidence ttypes.Evidence, priority int64) ([]byte, error) {
evi, err := json.Marshal(evidence)
if err != nil {
return nil, errors.Errorf("EvidenceToBytes marshal evidence failed:%v\n", err)
}
msg := json.RawMessage(evi)
env := envelope{
Kind: evidence.TypeName(),
Data: &msg,
}
ei := EvidenceInfo{
Committed: false,
Priority: priority,
Evidence: env,
}
eiBytes, err := json.Marshal(ei)
if err != nil {
return nil, errors.Errorf("EvidenceToBytes marshal evidence info failed:%v\n", err)
}
return eiBytes, nil
}
// EvidenceFromInfoBytes method
func (store *EvidenceStore) EvidenceFromInfoBytes(data []byte) (ttypes.Evidence, error) {
vote2 := EvidenceInfo{}
err := json.Unmarshal(data, &vote2)
if err != nil {
return nil, errors.Errorf("BytesToEvidence Unmarshal evidence info failed:%v\n", err)
}
if v, ok := ttypes.EvidenceType2Type[vote2.Evidence.Kind]; ok {
tmp := v.(ttypes.Evidence).Copy()
err = json.Unmarshal(*vote2.Evidence.Data, &tmp)
if err != nil {
return nil, errors.Errorf("BytesToEvidence Unmarshal evidence failed:%v\n", err)
}
return tmp, nil
}
return nil, errors.Errorf("BytesToEvidence not find evidence kind:%v\n", vote2.Evidence.Kind)
}
// EvidencePool maintains a pool of valid evidence
// in an EvidenceStore.
type EvidencePool struct {
evidenceStore *EvidenceStore
// needed to load validators to verify evidence
stateDB *CSStateDB
// latest state
mtx sync.Mutex
state State
// never close
evidenceChan chan ttypes.Evidence
}
// NewEvidencePool method
func NewEvidencePool(stateDB *CSStateDB, state State, evidenceStore *EvidenceStore) *EvidencePool {
evpool := &EvidencePool{
stateDB: stateDB,
state: state,
evidenceStore: evidenceStore,
evidenceChan: make(chan ttypes.Evidence),
}
return evpool
}
// EvidenceChan returns an unbuffered channel on which new evidence can be received.
func (evpool *EvidencePool) EvidenceChan() <-chan ttypes.Evidence {
return evpool.evidenceChan
}
// PriorityEvidence returns the priority evidence.
func (evpool *EvidencePool) PriorityEvidence() []ttypes.Evidence {
return evpool.evidenceStore.PriorityEvidence()
}
// PendingEvidence returns all uncommitted evidence.
func (evpool *EvidencePool) PendingEvidence() []ttypes.Evidence {
return evpool.evidenceStore.PendingEvidence()
}
// State returns the current state of the evpool.
func (evpool *EvidencePool) State() State {
evpool.mtx.Lock()
defer evpool.mtx.Unlock()
return evpool.state
}
// Update loads the latest
func (evpool *EvidencePool) Update(block *ttypes.TendermintBlock) {
evpool.mtx.Lock()
defer evpool.mtx.Unlock()
state := evpool.stateDB.LoadState()
if state.LastBlockHeight != block.Header.Height {
panic(fmt.Sprintf("EvidencePool.Update: loaded state with height %d when block.Height=%d", state.LastBlockHeight, block.Header.Height))
}
evpool.state = state
// NOTE: shouldn't need the mutex
evpool.MarkEvidenceAsCommitted(block.Evidence.Evidence)
}
// AddEvidence checks the evidence is valid and adds it to the pool.
// Blocks on the EvidenceChan.
func (evpool *EvidencePool) AddEvidence(evidence ttypes.Evidence) error {
// TODO: check if we already have evidence for this
// validator at this height so we dont get spammed
if err := VerifyEvidence(evpool.stateDB, evpool.State(), evidence); err != nil {
return err
}
// fetch the validator and return its voting power as its priority
// TODO: something better ?
valset, err := evpool.stateDB.LoadValidators(evidence.Height())
if err != nil {
return err
}
_, val := valset.GetByAddress(evidence.Address())
priority := val.VotingPower
added := evpool.evidenceStore.AddNewEvidence(evidence, priority)
if !added {
// evidence already known, just ignore
return nil
}
tendermintlog.Info("Verified new evidence of byzantine behaviour", "evidence", evidence)
// never closes. always safe to send on
evpool.evidenceChan <- evidence
return nil
}
// MarkEvidenceAsCommitted marks all the evidence as committed.
func (evpool *EvidencePool) MarkEvidenceAsCommitted(evidence []*tmtypes.EvidenceEnvelope) {
for _, ev := range evidence {
tmp := ttypes.EvidenceEnvelope2Evidence(ev)
if tmp != nil {
evpool.evidenceStore.MarkEvidenceAsCommitted(tmp)
}
}
}
// VerifyEvidence verifies the evidence fully by checking it is internally
// consistent and sufficiently recent.
func VerifyEvidence(stateDB *CSStateDB, s State, evidence ttypes.Evidence) error {
height := s.LastBlockHeight
evidenceAge := height - evidence.Height()
maxAge := s.ConsensusParams.EvidenceParams.MaxAge
if evidenceAge > maxAge {
return fmt.Errorf("Evidence from height %d is too old. Min height is %d",
evidence.Height(), height-maxAge)
}
if err := evidence.Verify(s.ChainID); err != nil {
return err
}
valset, err := stateDB.LoadValidators(evidence.Height())
if err != nil {
// TODO: if err is just that we cant find it cuz we pruned, ignore.
// TODO: if its actually bad evidence, punish peer
return err
}
// The address must have been an active validator at the height
ev := evidence
height, addr, idx := ev.Height(), ev.Address(), ev.Index()
valIdx, val := valset.GetByAddress(addr)
if val == nil {
return fmt.Errorf("Address %X was not a validator at height %d", addr, height)
} else if idx != valIdx {
return fmt.Errorf("Address %X was validator %d at height %d, not %d", addr, valIdx, height, idx)
}
return nil
}
......@@ -21,16 +21,14 @@ import (
// BlockExecutor provides the context and accessories for properly executing a block.
type BlockExecutor struct {
// save state, validators, consensus params, abci responses here
db *CSStateDB
evpool ttypes.EvidencePool
db *CSStateDB
}
// NewBlockExecutor returns a new BlockExecutor with a NopEventBus.
// Call SetEventBus to provide one.
func NewBlockExecutor(db *CSStateDB, evpool ttypes.EvidencePool) *BlockExecutor {
func NewBlockExecutor(db *CSStateDB) *BlockExecutor {
return &BlockExecutor{
db: db,
evpool: evpool,
db: db,
}
}
......@@ -59,12 +57,6 @@ func (blockExec *BlockExecutor) ApplyBlock(s State, blockID ttypes.BlockID, bloc
}
blockExec.db.SaveState(s)
// Update evpool now that state is saved
// TODO: handle the crash/recover scenario
// ie. (may need to call Update for last block)
blockExec.evpool.Update(block)
return s, nil
}
......@@ -191,7 +183,10 @@ func changeInVotingPowerMoreOrEqualToOneThird(currentSet *ttypes.ValidatorSet, u
}
func validateBlock(stateDB *CSStateDB, s State, b *ttypes.TendermintBlock) error {
newTxs := b.Header.NumTxs
// Validate internal consistency.
if err := b.ValidateBasic(); err != nil {
return err
}
// validate basic info
if b.Header.ChainID != s.ChainID {
......@@ -206,6 +201,7 @@ func validateBlock(stateDB *CSStateDB, s State, b *ttypes.TendermintBlock) error
return fmt.Errorf("Wrong Block.Header.LastBlockID. Expected %v, got %v", s.LastBlockID, b.Header.LastBlockID)
}
newTxs := b.Header.NumTxs
if b.Header.TotalTxs != s.LastBlockTotalTx+newTxs {
return fmt.Errorf("Wrong Block.Header.TotalTxs. Expected %v, got %v", s.LastBlockTotalTx+newTxs, b.Header.TotalTxs)
}
......@@ -242,13 +238,5 @@ func validateBlock(stateDB *CSStateDB, s State, b *ttypes.TendermintBlock) error
}
}
for _, ev := range b.Evidence.Evidence {
evidence := ttypes.EvidenceEnvelope2Evidence(ev)
if evidence != nil {
if err := VerifyEvidence(stateDB, s, evidence); err != nil {
return ttypes.NewEvidenceInvalidErr(evidence, err)
}
}
}
return nil
}
......@@ -17,8 +17,6 @@ import (
"github.com/33cn/chain33/common/crypto"
ttypes "github.com/33cn/plugin/plugin/consensus/tendermint/types"
tmtypes "github.com/33cn/plugin/plugin/dapp/valnode/types"
"github.com/golang/protobuf/proto"
)
const (
......@@ -44,8 +42,6 @@ const (
minReadBufferSize = 1024
minWriteBufferSize = 65536
broadcastEvidenceIntervalS = 60 // broadcast uncommitted evidence this often
)
// Parallel method
......@@ -132,7 +128,6 @@ type Node struct {
lAddr string
state *ConsensusState
evpool *EvidencePool
broadcastChannel chan MsgInfo
started uint32 // atomic
stopped uint32 // atomic
......@@ -140,7 +135,7 @@ type Node struct {
}
// NewNode method
func NewNode(seeds []string, protocol string, lAddr string, privKey crypto.PrivKey, network string, version string, state *ConsensusState, evpool *EvidencePool) *Node {
func NewNode(seeds []string, protocol string, lAddr string, privKey crypto.PrivKey, network string, version string, state *ConsensusState) *Node {
address := GenAddressByPubKey(privKey.PubKey())
node := &Node{
......@@ -158,7 +153,6 @@ func NewNode(seeds []string, protocol string, lAddr string, privKey crypto.PrivK
reconnecting: NewMutexMap(),
broadcastChannel: make(chan MsgInfo, maxSendQueueSize),
state: state,
evpool: evpool,
localIPs: make(map[string]net.IP),
}
......@@ -219,7 +213,6 @@ func (node *Node) Start() {
go node.StartConsensusRoutine()
go node.BroadcastRoutine()
go node.evidenceBroadcastRoutine()
}
}
......@@ -234,7 +227,7 @@ func (node *Node) DialPeerWithAddress(addr string) error {
func (node *Node) addOutboundPeerWithConfig(addr string) error {
tendermintlog.Info("Dialing peer", "address", addr)
peerConn, err := newOutboundPeerConn(addr, node.privKey, node.StopPeerForError, node.state, node.evpool)
peerConn, err := newOutboundPeerConn(addr, node.privKey, node.StopPeerForError, node.state)
if err != nil {
go node.reconnectToPeer(addr)
return err
......@@ -307,66 +300,6 @@ func (node *Node) StartConsensusRoutine() {
}
}
func (node *Node) evidenceBroadcastRoutine() {
ticker := time.NewTicker(time.Second * broadcastEvidenceIntervalS)
for {
select {
case evidence := <-node.evpool.EvidenceChan():
// broadcast some new evidence
data, err := proto.Marshal(evidence.Child())
if err != nil {
msg := MsgInfo{TypeID: ttypes.EvidenceListID,
Msg: &tmtypes.EvidenceData{
Evidence: []*tmtypes.EvidenceEnvelope{
{
TypeName: evidence.TypeName(),
Data: data,
},
},
},
PeerID: node.ID, PeerIP: node.IP,
}
node.Broadcast(msg)
// TODO: Broadcast runs asynchronously, so this should wait on the successChan
// in another routine before marking to be proper.
node.evpool.evidenceStore.MarkEvidenceAsBroadcasted(evidence)
}
case <-ticker.C:
// broadcast all pending evidence
var eData tmtypes.EvidenceData
evidence := node.evpool.PendingEvidence()
for _, item := range evidence {
ev := item.Child()
if ev != nil {
data, err := proto.Marshal(ev)
if err != nil {
panic("AddEvidence marshal failed")
}
env := &tmtypes.EvidenceEnvelope{
TypeName: item.TypeName(),
Data: data,
}
eData.Evidence = append(eData.Evidence, env)
}
}
msg := MsgInfo{TypeID: ttypes.EvidenceListID,
Msg: &eData,
PeerID: node.ID,
PeerIP: node.IP,
}
node.Broadcast(msg)
case _, ok := <-node.quit:
if !ok {
node.quit = nil
tendermintlog.Info("evidenceBroadcastRoutine quit")
return
}
}
}
}
// BroadcastRoutine receive to broadcast
func (node *Node) BroadcastRoutine() {
for {
......@@ -413,7 +346,7 @@ func (node *Node) StopPeerForError(peer Peer, reason interface{}) {
}
func (node *Node) addInboundPeer(conn net.Conn) error {
peerConn, err := newInboundPeerConn(conn, node.privKey, node.StopPeerForError, node.state, node.evpool)
peerConn, err := newInboundPeerConn(conn, node.privKey, node.StopPeerForError, node.state)
if err != nil {
if er := conn.Close(); er != nil {
tendermintlog.Error("addInboundPeer close conn failed", "er", er)
......@@ -481,11 +414,6 @@ func (node *Node) addPeer(pc *peerConn) error {
return fmt.Errorf("get remote ip failed:%v", rErr)
}
// Filter peer against ID white list
//if err := node.FilterConnByID(peerID); err != nil {
//return err
//}
// Check version, chain id
if err := node.CompatibleWith(peerNodeInfo); err != nil {
return err
......@@ -507,7 +435,6 @@ func (node *Node) addPeer(pc *peerConn) error {
if err := node.peerSet.Add(pc); err != nil {
return err
}
//node.metrics.Peers.Add(float64(1))
tendermintlog.Info("Added peer", "peer", pc.ip)
return nil
......@@ -710,13 +637,13 @@ func dial(addr string) (net.Conn, error) {
return conn, nil
}
func newOutboundPeerConn(addr string, ourNodePrivKey crypto.PrivKey, onPeerError func(Peer, interface{}), state *ConsensusState, evpool *EvidencePool) (*peerConn, error) {
func newOutboundPeerConn(addr string, ourNodePrivKey crypto.PrivKey, onPeerError func(Peer, interface{}), state *ConsensusState) (*peerConn, error) {
conn, err := dial(addr)
if err != nil {
return &peerConn{}, fmt.Errorf("Error creating peer:%v", err)
}
pc, err := newPeerConn(conn, true, true, ourNodePrivKey, onPeerError, state, evpool)
pc, err := newPeerConn(conn, true, true, ourNodePrivKey, onPeerError, state)
if err != nil {
if cerr := conn.Close(); cerr != nil {
return &peerConn{}, fmt.Errorf("newPeerConn failed:%v, connection close failed:%v", err, cerr)
......@@ -732,12 +659,11 @@ func newInboundPeerConn(
ourNodePrivKey crypto.PrivKey,
onPeerError func(Peer, interface{}),
state *ConsensusState,
evpool *EvidencePool,
) (*peerConn, error) {
// TODO: issue PoW challenge
return newPeerConn(conn, false, false, ourNodePrivKey, onPeerError, state, evpool)
return newPeerConn(conn, false, false, ourNodePrivKey, onPeerError, state)
}
func newPeerConn(
......@@ -746,7 +672,6 @@ func newPeerConn(
ourNodePrivKey crypto.PrivKey,
onPeerError func(Peer, interface{}),
state *ConsensusState,
evpool *EvidencePool,
) (pc *peerConn, err error) {
conn := rawConn
......@@ -769,6 +694,5 @@ func newPeerConn(
conn: conn,
onPeerError: onPeerError,
myState: state,
myevpool: evpool,
}, nil
}
package tendermint
import (
"encoding/hex"
"fmt"
"sync"
"testing"
"github.com/33cn/chain33/common/crypto"
"github.com/33cn/chain33/types"
"github.com/stretchr/testify/assert"
)
var (
secureConnCrypto crypto.Crypto
sum = 0
mutx sync.Mutex
privKey = "B3DC4C0725884EBB7264B92F1D8D37584A64ADE1799D997EC64B4FE3973E08DE220ACBE680DF2473A0CB48987A00FCC1812F106A7390BE6B8E2D31122C992A19"
expectAddress = "02A13174B92727C4902DB099E51A3339F48BD45E"
)
func init() {
cr2, err := crypto.New(types.GetSignName("", types.ED25519))
if err != nil {
fmt.Println("crypto.New failed for types.ED25519")
return
}
secureConnCrypto = cr2
}
func TestParallel(t *testing.T) {
Parallel(
func() {
mutx.Lock()
sum++
mutx.Unlock()
},
func() {
mutx.Lock()
sum += 2
mutx.Unlock()
},
func() {
mutx.Lock()
sum += 3
mutx.Unlock()
},
)
fmt.Println("TestParallel ok")
assert.Equal(t, 6, sum)
}
func TestGenAddressByPubKey(t *testing.T) {
tmp, err := hex.DecodeString(privKey)
assert.Nil(t, err)
priv, err := secureConnCrypto.PrivKeyFromBytes(tmp)
assert.Nil(t, err)
addr := GenAddressByPubKey(priv.PubKey())
strAddr := fmt.Sprintf("%X", addr)
assert.Equal(t, expectAddress, strAddr)
fmt.Println("TestGenAddressByPubKey ok")
}
func TestIP2IPPort(t *testing.T) {
testMap := NewMutexMap()
assert.Equal(t, false, testMap.Has("1.1.1.1"))
testMap.Set("1.1.1.1", "1.1.1.1:80")
assert.Equal(t, true, testMap.Has("1.1.1.1"))
testMap.Set("1.1.1.2", "1.1.1.2:80")
assert.Equal(t, true, testMap.Has("1.1.1.2"))
testMap.Delete("1.1.1.1")
assert.Equal(t, false, testMap.Has("1.1.1.1"))
fmt.Println("TestIP2IPPort ok")
}
func TestPeerSet(t *testing.T) {
testSet := NewPeerSet()
assert.Equal(t, false, testSet.Has("1"))
peer1 := &peerConn{id: "1", ip: []byte("1.1.1.1")}
testSet.Add(peer1)
assert.Equal(t, true, testSet.Has("1"))
assert.Equal(t, true, testSet.HasIP([]byte("1.1.1.1")))
err := testSet.Add(peer1)
assert.NotNil(t, err)
peer2 := &peerConn{id: "2", ip: []byte("1.1.1.2")}
testSet.Add(peer2)
assert.Equal(t, true, testSet.Has("2"))
assert.Equal(t, 2, testSet.Size())
testSet.Remove(peer1)
assert.Equal(t, 1, testSet.Size())
assert.Equal(t, false, testSet.Has("1"))
assert.Equal(t, false, testSet.HasIP([]byte("1.1.1.1")))
fmt.Println("TestPeerSet ok")
}
......@@ -6,6 +6,7 @@ package tendermint
import (
"bufio"
"bytes"
"encoding/binary"
"encoding/hex"
"encoding/json"
......@@ -93,8 +94,7 @@ type peerConn struct {
onPeerError func(Peer, interface{})
myState *ConsensusState
myevpool *EvidencePool
myState *ConsensusState
state *PeerConnState
updateStateQueue chan MsgInfo
......@@ -374,6 +374,21 @@ func (pc *peerConn) TrySend(msg MsgInfo) bool {
}
}
// PickSendVote picks a vote and sends it to the peer.
// Returns true if vote was sent.
func (pc *peerConn) PickSendVote(votes ttypes.VoteSetReader) bool {
if vote, ok := pc.state.PickVoteToSend(votes); ok {
msg := MsgInfo{TypeID: ttypes.VoteID, Msg: vote.Vote, PeerID: pc.id, PeerIP: pc.ip.String()}
tendermintlog.Debug("Sending vote message", "vote", msg)
if pc.Send(msg) {
pc.state.SetHasVote(vote)
return true
}
return false
}
return false
}
func (pc *peerConn) IsRunning() bool {
return atomic.LoadUint32(&pc.started) == 1 && atomic.LoadUint32(&pc.stopped) == 0
}
......@@ -545,7 +560,8 @@ FOR_LOOP:
tendermintlog.Error("peerConn recvRoutine Unmarshal data failed", "err", err)
continue
}
if pc.transferChannel != nil && (pkt.TypeID == ttypes.ProposalID || pkt.TypeID == ttypes.VoteID || pkt.TypeID == ttypes.ProposalBlockID) {
if pc.transferChannel != nil && (pkt.TypeID == ttypes.ProposalID || pkt.TypeID == ttypes.VoteID ||
pkt.TypeID == ttypes.ProposalBlockID) {
pc.transferChannel <- MsgInfo{pkt.TypeID, realMsg.(proto.Message), pc.ID(), pc.ip.String()}
if pkt.TypeID == ttypes.ProposalID {
proposal := realMsg.(*tmtypes.Proposal)
......@@ -562,19 +578,6 @@ FOR_LOOP:
}
} else if pkt.TypeID == ttypes.ProposalHeartbeatID {
pc.heartbeatQueue <- realMsg.(*tmtypes.Heartbeat)
} else if pkt.TypeID == ttypes.EvidenceListID {
go func() {
for _, ev := range realMsg.(*tmtypes.EvidenceData).Evidence {
evidence := ttypes.EvidenceEnvelope2Evidence(ev)
if evidence != nil {
err := pc.myevpool.AddEvidence(evidence.(ttypes.Evidence))
if err != nil {
tendermintlog.Error("Evidence is not valid", "evidence", ev, "err", err)
// TODO: punish peer
}
}
}
}()
} else {
pc.updateStateQueue <- MsgInfo{pkt.TypeID, realMsg.(proto.Message), pc.ID(), pc.ip.String()}
}
......@@ -601,12 +604,15 @@ FOR_LOOP:
typeID := msg.TypeID
if typeID == ttypes.NewRoundStepID {
pc.state.ApplyNewRoundStepMessage(msg.Msg.(*tmtypes.NewRoundStepMsg))
} else if typeID == ttypes.ValidBlockID {
pc.state.ApplyValidBlockMessage(msg.Msg.(*tmtypes.ValidBlockMsg))
} else if typeID == ttypes.CommitStepID {
pc.state.ApplyCommitStepMessage(msg.Msg.(*tmtypes.CommitStepMsg))
} else if typeID == ttypes.HasVoteID {
pc.state.ApplyHasVoteMessage(msg.Msg.(*tmtypes.HasVoteMsg))
} else if typeID == ttypes.VoteSetMaj23ID {
tmp := msg.Msg.(*tmtypes.VoteSetMaj23Msg)
tendermintlog.Debug("updateStateRoutine", "VoteSetMaj23Msg", tmp)
pc.myState.SetPeerMaj23(tmp.Height, int(tmp.Round), byte(tmp.Type), pc.id, tmp.BlockID)
var myVotes *ttypes.BitArray
switch byte(tmp.Type) {
......@@ -692,16 +698,21 @@ OUTER_LOOP:
// If the peer is on a previous height, help catch up.
if (0 < prs.Height) && (prs.Height < rs.Height) {
if prs.Height+1 == rs.Height && prs.Round == rs.LastCommit.Round() && prs.Step == ttypes.RoundStepCommit && prs.ProposalBlock {
tendermintlog.Debug("Peer is waiting for finalizeCommit finish", "peerip", pc.ip.String(),
"state", fmt.Sprintf("%v/%v/%v", prs.Height, prs.Round, prs.Step))
time.Sleep(10 * pc.myState.PeerGossipSleep())
if prs.ProposalBlockHash == nil || prs.ProposalBlock {
time.Sleep(pc.myState.PeerGossipSleep())
continue OUTER_LOOP
}
tendermintlog.Info("help catch up", "peerip", pc.ip.String(), "selfHeight", rs.Height, "peerHeight", prs.Height)
proposalBlock := pc.myState.client.LoadProposalBlock(prs.Height)
newBlock := &ttypes.TendermintBlock{TendermintBlock: proposalBlock}
if proposalBlock == nil {
tendermintlog.Error("Failed to load propsal block", "selfHeight", rs.Height, "blockstoreHeight", pc.myState.client.GetCurrentHeight())
tendermintlog.Error("Fail to load propsal block", "selfHeight", rs.Height,
"blockstoreHeight", pc.myState.client.GetCurrentHeight())
time.Sleep(pc.myState.PeerGossipSleep())
continue OUTER_LOOP
} else if !bytes.Equal(newBlock.Hash(), prs.ProposalBlockHash) {
tendermintlog.Error("Peer ProposalBlockHash mismatch", "ProposalBlockHash", fmt.Sprintf("%X", prs.ProposalBlockHash),
"newBlockHash", fmt.Sprintf("%X", newBlock.Hash()))
time.Sleep(pc.myState.PeerGossipSleep())
continue OUTER_LOOP
}
......@@ -709,9 +720,8 @@ OUTER_LOOP:
tendermintlog.Info("Sending block for catchup", "peerip", pc.ip.String(), "block(H/R)",
fmt.Sprintf("%v/%v", proposalBlock.Header.Height, proposalBlock.Header.Round))
if pc.Send(msg) {
//prs.SetHasProposalBlock(rs.ProposalBlock)
prs.SetHasProposalBlock(newBlock)
}
time.Sleep(10 * pc.myState.PeerGossipSleep())
continue OUTER_LOOP
}
......@@ -754,14 +764,16 @@ OUTER_LOOP:
}
// Send proposal block
if rs.ProposalBlock != nil && !prs.ProposalBlock {
msg := MsgInfo{TypeID: ttypes.ProposalBlockID, Msg: rs.ProposalBlock.TendermintBlock, PeerID: pc.id, PeerIP: pc.ip.String()}
tendermintlog.Debug(fmt.Sprintf("Sending proposal block. Self state: %v/%v/%v", rs.Height, rs.Round, rs.Step),
"peerip", pc.ip.String(), "block-height", rs.ProposalBlock.Header.Height, "block-round", rs.ProposalBlock.Header.Round)
if pc.Send(msg) {
prs.SetHasProposalBlock(rs.ProposalBlock)
if rs.Proposal != nil && prs.ProposalBlockHash != nil && bytes.Equal(rs.Proposal.Blockhash, prs.ProposalBlockHash) {
if rs.ProposalBlock != nil && !prs.ProposalBlock {
msg := MsgInfo{TypeID: ttypes.ProposalBlockID, Msg: rs.ProposalBlock.TendermintBlock, PeerID: pc.id, PeerIP: pc.ip.String()}
tendermintlog.Debug(fmt.Sprintf("Sending proposal block. Self state: %v/%v/%v", rs.Height, rs.Round, rs.Step),
"peerip", pc.ip.String(), "block-height", rs.ProposalBlock.Header.Height, "block-round", rs.ProposalBlock.Header.Round)
if pc.Send(msg) {
prs.SetHasProposalBlock(rs.ProposalBlock)
}
continue OUTER_LOOP
}
continue OUTER_LOOP
}
// Nothing to do. Sleep.
......@@ -785,9 +797,6 @@ OUTER_LOOP:
rs := pc.myState.GetRoundState()
prs := pc.state
//tendermintlog.Debug("gossipVotesRoutine", "rs(H/R/S)", fmt.Sprintf("%v/%v/%v", rs.Height, rs.Round, rs.Step.String()),
// "prs(H/R/S)", fmt.Sprintf("%v/%v/%v", prs.Height, prs.Round, prs.Step.String()),
// "precommits", rs.Votes.Precommits(prs.Round).BitArray().String(), "peerip", pc.ip.String())
switch sleeping {
case 1: // First sleep
......@@ -806,11 +815,8 @@ OUTER_LOOP:
// Special catchup logic.
// If peer is lagging by height 1, send LastCommit.
if prs.Height != 0 && rs.Height == prs.Height+1 {
if vote, ok := pc.state.PickVoteToSend(rs.LastCommit); ok {
msg := MsgInfo{TypeID: ttypes.VoteID, Msg: vote.Vote, PeerID: pc.id, PeerIP: pc.ip.String()}
pc.Send(msg)
tendermintlog.Debug("Picked rs.LastCommit to send", "peerip", pc.ip.String(), "height", prs.Height, "vote.Height", vote.Height)
} else {
if pc.PickSendVote(rs.LastCommit) {
tendermintlog.Debug("Picked rs.LastCommit to send", "peerip", pc.ip.String(), "height", prs.Height)
continue OUTER_LOOP
}
}
......@@ -820,14 +826,13 @@ OUTER_LOOP:
if prs.Height != 0 && rs.Height >= prs.Height+2 {
// Load the block commit for prs.Height,
// which contains precommit signatures for prs.Height.
commit := pc.myState.client.LoadSeenCommit(prs.Height)
commit := pc.myState.client.LoadBlockCommit(prs.Height + 1)
commitObj := &ttypes.Commit{TendermintCommit: commit}
if vote, ok := pc.state.PickVoteToSend(commitObj); ok {
msg := MsgInfo{TypeID: ttypes.VoteID, Msg: vote.Vote, PeerID: pc.id, PeerIP: pc.ip.String()}
pc.Send(msg)
tendermintlog.Info("Picked Catchup commit to send", "BitArray", commitObj.BitArray().String(), "valIndex", vote.ValidatorIndex,
"peerip", pc.ip.String(), "height", prs.Height, "vote.Height", vote.Height)
} else {
if pc.PickSendVote(commitObj) {
tendermintlog.Info("Picked Catchup commit to send",
"commit(H/R)", fmt.Sprintf("%v/%v", commitObj.Height(), commitObj.Round()),
"BitArray", commitObj.BitArray().String(),
"peerip", pc.ip.String(), "height", prs.Height)
continue OUTER_LOOP
}
}
......@@ -851,47 +856,53 @@ OUTER_LOOP:
func (pc *peerConn) gossipVotesForHeight(rs *ttypes.RoundState, prs *ttypes.PeerRoundState) bool {
// If there are lastCommits to send...
if prs.Step == ttypes.RoundStepNewHeight {
if vote, ok := pc.state.PickVoteToSend(rs.LastCommit); ok {
msg := MsgInfo{TypeID: ttypes.VoteID, Msg: vote.Vote, PeerID: pc.id, PeerIP: pc.ip.String()}
pc.Send(msg)
tendermintlog.Debug("Picked rs.LastCommit to send", "peerip", pc.ip.String(), "peer(H/R)", fmt.Sprintf("%v/%v", prs.Height, prs.Round))
if pc.PickSendVote(rs.LastCommit) {
tendermintlog.Debug("Picked rs.LastCommit to send", "peerip", pc.ip.String(),
"peer(H/R)", fmt.Sprintf("%v/%v", prs.Height, prs.Round))
return true
}
}
// If there are POL prevotes to send...
if prs.Step <= ttypes.RoundStepPropose && prs.Round != -1 && prs.Round <= rs.Round && prs.ProposalPOLRound != -1 {
if polPrevotes := rs.Votes.Prevotes(prs.ProposalPOLRound); polPrevotes != nil {
if pc.PickSendVote(polPrevotes) {
tendermintlog.Debug("Picked rs.Prevotes(prs.ProposalPOLRound) to send",
"peerip", pc.ip.String(), "peer(H/R)", fmt.Sprintf("%v/%v", prs.Height, prs.Round),
"POLRound", prs.ProposalPOLRound)
return true
}
}
}
// If there are prevotes to send...
if prs.Step <= ttypes.RoundStepPrevoteWait && prs.Round != -1 && prs.Round <= rs.Round {
if vote, ok := pc.state.PickVoteToSend(rs.Votes.Prevotes(prs.Round)); ok {
msg := MsgInfo{TypeID: ttypes.VoteID, Msg: vote.Vote, PeerID: pc.id, PeerIP: pc.ip.String()}
pc.Send(msg)
tendermintlog.Debug("Picked rs.Prevotes(prs.Round) to send", "peerip", pc.ip.String(), "peer(H/R)", fmt.Sprintf("%v/%v", prs.Height, prs.Round))
if pc.PickSendVote(rs.Votes.Prevotes(prs.Round)) {
tendermintlog.Debug("Picked rs.Prevotes(prs.Round) to send",
"peerip", pc.ip.String(), "peer(H/R)", fmt.Sprintf("%v/%v", prs.Height, prs.Round))
return true
}
}
// If there are precommits to send...
if prs.Step <= ttypes.RoundStepPrecommitWait && prs.Round != -1 && prs.Round <= rs.Round {
if vote, ok := pc.state.PickVoteToSend(rs.Votes.Precommits(prs.Round)); ok {
msg := MsgInfo{TypeID: ttypes.VoteID, Msg: vote.Vote, PeerID: pc.id, PeerIP: pc.ip.String()}
pc.Send(msg)
tendermintlog.Debug("Picked rs.Precommits(prs.Round) to send", "peerip", pc.ip.String(), "peer(H/R)", fmt.Sprintf("%v/%v", prs.Height, prs.Round))
if pc.PickSendVote(rs.Votes.Precommits(prs.Round)) {
tendermintlog.Debug("Picked rs.Precommits(prs.Round) to send",
"peerip", pc.ip.String(), "peer(H/R)", fmt.Sprintf("%v/%v", prs.Height, prs.Round))
return true
}
}
// If there are prevotes to send...Needed because of validBlock mechanism
if prs.Round != -1 && prs.Round <= rs.Round {
if vote, ok := pc.state.PickVoteToSend(rs.Votes.Prevotes(prs.Round)); ok {
msg := MsgInfo{TypeID: ttypes.VoteID, Msg: vote.Vote, PeerID: pc.id, PeerIP: pc.ip.String()}
pc.Send(msg)
tendermintlog.Debug("Picked rs.Prevotes(prs.Round) to send", "peerip", pc.ip.String(), "peer(H/R)", fmt.Sprintf("%v/%v", prs.Height, prs.Round))
if pc.PickSendVote(rs.Votes.Prevotes(prs.Round)) {
tendermintlog.Debug("Picked rs.Prevotes(prs.Round) to send",
"peerip", pc.ip.String(), "peer(H/R)", fmt.Sprintf("%v/%v", prs.Height, prs.Round))
return true
}
}
// If there are POLPrevotes to send...
if prs.ProposalPOLRound != -1 {
if polPrevotes := rs.Votes.Prevotes(prs.ProposalPOLRound); polPrevotes != nil {
if vote, ok := pc.state.PickVoteToSend(polPrevotes); ok {
msg := MsgInfo{TypeID: ttypes.VoteID, Msg: vote.Vote, PeerID: pc.id, PeerIP: pc.ip.String()}
pc.Send(msg)
tendermintlog.Debug("Picked rs.Prevotes(prs.ProposalPOLRound) to send", "peerip", pc.ip.String(), "round", prs.ProposalPOLRound)
if pc.PickSendVote(polPrevotes) {
tendermintlog.Debug("Picked rs.Prevotes(prs.ProposalPOLRound) to send",
"peerip", pc.ip.String(), "round", prs.ProposalPOLRound)
return true
}
}
......@@ -972,7 +983,7 @@ OUTER_LOOP:
// Maybe send Height/CatchupCommitRound/CatchupCommit.
{
prs := pc.state.GetRoundState()
if prs.CatchupCommitRound != -1 && 0 < prs.Height && prs.Height <= pc.myState.client.GetCurrentHeight() {
if prs.CatchupCommitRound != -1 && 0 < prs.Height && prs.Height <= pc.myState.client.csStore.LoadStateHeight() {
commit := pc.myState.LoadCommit(prs.Height)
commitTmp := ttypes.Commit{TendermintCommit: commit}
msg := MsgInfo{TypeID: ttypes.VoteSetMaj23ID, Msg: &tmtypes.VoteSetMaj23Msg{
......@@ -1036,10 +1047,12 @@ func (ps *PeerConnState) SetHasProposal(proposal *tmtypes.Proposal) {
if ps.Proposal {
return
}
tendermintlog.Debug("Peer set proposal", "peerip", ps.ip.String(), "peer-state", fmt.Sprintf("%v/%v/%v", ps.Height, ps.Round, ps.Step),
"proposal(H/R)", fmt.Sprintf("%v/%v", proposal.Height, proposal.Round))
tendermintlog.Debug("Peer set proposal", "peerip", ps.ip.String(),
"peer-state", fmt.Sprintf("%v/%v/%v", ps.Height, ps.Round, ps.Step),
"proposal(H/R/Hash)", fmt.Sprintf("%v/%v/%X", proposal.Height, proposal.Round, proposal.Blockhash))
ps.Proposal = true
ps.ProposalBlockHash = proposal.Blockhash
ps.ProposalPOLRound = int(proposal.POLRound)
ps.ProposalPOL = nil // Nil until ttypes.ProposalPOLMessage received.
}
......@@ -1055,7 +1068,8 @@ func (ps *PeerConnState) SetHasProposalBlock(block *ttypes.TendermintBlock) {
if ps.ProposalBlock {
return
}
tendermintlog.Debug("Peer set proposal block", "peerip", ps.ip.String(), "peer-state", fmt.Sprintf("%v/%v/%v", ps.Height, ps.Round, ps.Step),
tendermintlog.Debug("Peer set proposal block", "peerip", ps.ip.String(),
"peer-state", fmt.Sprintf("%v/%v/%v", ps.Height, ps.Round, ps.Step),
"block(H/R)", fmt.Sprintf("%v/%v", block.Header.Height, block.Header.Round))
ps.ProposalBlock = true
}
......@@ -1088,7 +1102,6 @@ func (ps *PeerConnState) PickVoteToSend(votes ttypes.VoteSetReader) (vote *ttype
tendermintlog.Debug("PickVoteToSend", "peer(H/R)", fmt.Sprintf("%v/%v", ps.Height, ps.Round),
"vote(H/R)", fmt.Sprintf("%v/%v", height, round), "type", voteType, "selfVotes", votes.BitArray().String(),
"peerVotes", psVotes.String(), "peerip", ps.ip.String())
ps.setHasVote(height, round, voteType, index)
return votes.GetByIndex(index), true
}
return nil, false
......@@ -1211,7 +1224,8 @@ func (ps *PeerConnState) setHasVote(height int64, round int, voteType byte, inde
if psVotes != nil {
psVotes.SetIndex(index, true)
}
tendermintlog.Debug("setHasVote after", "height", height, "index", index, "type", voteType, "peerVotes", psVotes.String(), "peerip", ps.ip.String())
tendermintlog.Debug("setHasVote after", "height", height, "index", index, "type", voteType,
"peerVotes", psVotes.String(), "peerip", ps.ip.String())
}
// ApplyNewRoundStepMessage updates the peer state for the new round.
......@@ -1238,13 +1252,16 @@ func (ps *PeerConnState) ApplyNewRoundStepMessage(msg *tmtypes.NewRoundStepMsg)
ps.Step = ttypes.RoundStepType(msg.Step)
ps.StartTime = startTime
tendermintlog.Debug("ApplyNewRoundStepMessage", "peerip", ps.ip.String(), "peer(H/R)", fmt.Sprintf("%v/%v", psHeight, psRound),
tendermintlog.Debug("ApplyNewRoundStepMessage", "peerip", ps.ip.String(),
"peer(H/R)", fmt.Sprintf("%v/%v", psHeight, psRound),
"msg(H/R/S)", fmt.Sprintf("%v/%v/%v", msg.Height, msg.Round, ps.Step))
if psHeight != msg.Height || psRound != int(msg.Round) {
tendermintlog.Debug("Reset Proposal, Prevotes, Precommits", "peerip", ps.ip.String(), "peer(H/R)", fmt.Sprintf("%v/%v", psHeight, psRound))
tendermintlog.Debug("Reset Proposal, Prevotes, Precommits", "peerip", ps.ip.String(),
"peer(H/R)", fmt.Sprintf("%v/%v", psHeight, psRound))
ps.Proposal = false
ps.ProposalBlock = false
ps.ProposalBlockHash = nil
ps.ProposalPOLRound = -1
ps.ProposalPOL = nil
// We'll update the BitArray capacity later.
......@@ -1256,11 +1273,13 @@ func (ps *PeerConnState) ApplyNewRoundStepMessage(msg *tmtypes.NewRoundStepMsg)
// Preserve psCatchupCommit!
// NOTE: We prefer to use prs.Precommits if
// pr.Round matches pr.CatchupCommitRound.
tendermintlog.Debug("Reset Precommits to CatchupCommit", "peerip", ps.ip.String(), "peer(H/R)", fmt.Sprintf("%v/%v", psHeight, psRound))
tendermintlog.Debug("Reset Precommits to CatchupCommit", "peerip", ps.ip.String(),
"peer(H/R)", fmt.Sprintf("%v/%v", psHeight, psRound))
ps.Precommits = psCatchupCommit
}
if psHeight != msg.Height {
tendermintlog.Debug("Reset LastCommit, CatchupCommit", "peerip", ps.ip.String(), "peer(H/R)", fmt.Sprintf("%v/%v", psHeight, psRound))
tendermintlog.Debug("Reset LastCommit, CatchupCommit", "peerip", ps.ip.String(),
"peer(H/R)", fmt.Sprintf("%v/%v", psHeight, psRound))
// Shift Precommits to LastCommit.
if psHeight+1 == msg.Height && psRound == int(msg.LastCommitRound) {
ps.LastCommitRound = int(msg.LastCommitRound)
......@@ -1283,7 +1302,24 @@ func (ps *PeerConnState) ApplyCommitStepMessage(msg *tmtypes.CommitStepMsg) {
if ps.Height != msg.Height {
return
}
}
// ApplyValidBlockMessage updates the peer state for the new valid block.
func (ps *PeerConnState) ApplyValidBlockMessage(msg *tmtypes.ValidBlockMsg) {
ps.mtx.Lock()
defer ps.mtx.Unlock()
if ps.Height != msg.Height {
return
}
if ps.Round != int(msg.Round) && !msg.IsCommit {
return
}
tendermintlog.Debug("ApplyValidBlockMessage", "peerip", ps.ip.String(),
"peer(H/R)", fmt.Sprintf("%v/%v", ps.Height, ps.Round),
"blockhash", fmt.Sprintf("%X", msg.Blockhash))
ps.ProposalBlockHash = msg.Blockhash
}
// ApplyProposalPOLMessage updates the peer state for the new proposal POL.
......@@ -1312,7 +1348,8 @@ func (ps *PeerConnState) ApplyHasVoteMessage(msg *tmtypes.HasVoteMsg) {
return
}
tendermintlog.Debug("ApplyHasVoteMessage", "msg(H/R)", fmt.Sprintf("%v/%v", msg.Height, msg.Round), "peerip", ps.ip.String())
tendermintlog.Debug("ApplyHasVoteMessage", "msg(H/R)", fmt.Sprintf("%v/%v", msg.Height, msg.Round),
"peerip", ps.ip.String())
ps.setHasVote(msg.Height, int(msg.Round), byte(msg.Type), int(msg.Index))
}
......
......@@ -133,7 +133,6 @@ func (sc *SecretConnection) Write(data []byte) (n int, err error) {
// encrypt the frame
var sealedFrame = make([]byte, sealedFrameSize)
secretbox.Seal(sealedFrame[:0], frame, sc.sendNonce, sc.shrSecret)
// fmt.Printf("secretbox.Seal(sealed:%X,sendNonce:%X,shrSecret:%X\n", sealedFrame, sc.sendNonce, sc.shrSecret)
incr2Nonce(sc.sendNonce)
// end encryption
......@@ -162,7 +161,6 @@ func (sc *SecretConnection) Read(data []byte) (n int, err error) {
// decrypt the frame
var frame = make([]byte, totalFrameSize)
// fmt.Printf("secretbox.Open(sealed:%X,recvNonce:%X,shrSecret:%X\n", sealedFrame, sc.recvNonce, sc.shrSecret)
_, ok := secretbox.Open(frame[:0], sealedFrame, sc.recvNonce, sc.shrSecret)
if !ok {
return n, errors.New("Failed to decrypt SecretConnection")
......@@ -298,9 +296,7 @@ func shareAuthSignature(sc io.ReadWriter, pubKey crypto.PubKey, signature crypto
if err2 != nil {
return
}
//n := int(0) // not used.
//recvMsg = wire.ReadBinary(authSigMessage{}, bytes.NewBuffer(readBuffer), authSigMsgSize, &n, &err2).(authSigMessage)
//secret.Info("shareAuthSignature", "readBuffer", readBuffer)
recvMsg.Key, err2 = types.ConsensusCrypto.PubKeyFromBytes(readBuffer[:32])
if err2 != nil {
return
......
......@@ -118,9 +118,9 @@ func (s State) GetValidators() (last *ttypes.ValidatorSet, current *ttypes.Valid
// Create a block from the latest state
// MakeBlock builds a block with the given txs and commit from the current state.
func (s State) MakeBlock(height int64, round int64, Txs []*types.Transaction, commit *tmtypes.TendermintCommit) *ttypes.TendermintBlock {
func (s State) MakeBlock(height int64, round int64, pblock *types.Block, commit *tmtypes.TendermintCommit, proposerAddr []byte) *ttypes.TendermintBlock {
// build base block
block := ttypes.MakeBlock(height, round, Txs, commit)
block := ttypes.MakeBlock(height, round, pblock, commit)
// fill header with state data
block.Header.ChainID = s.ChainID
......@@ -130,6 +130,7 @@ func (s State) MakeBlock(height int64, round int64, Txs []*types.Transaction, co
block.Header.AppHash = s.AppHash
block.Header.ConsensusHash = s.ConsensusParams.Hash()
block.Header.LastResultsHash = s.LastResultsHash
block.Header.ProposerAddr = proposerAddr
return block
}
......@@ -226,6 +227,7 @@ func LoadState(state *tmtypes.State) State {
ChainID: state.GetChainID(),
LastBlockHeight: state.GetLastBlockHeight(),
LastBlockTotalTx: state.GetLastBlockTotalTx(),
LastBlockID: ttypes.BlockID{BlockID: *state.LastBlockID},
LastBlockTime: state.LastBlockTime,
Validators: nil,
LastValidators: nil,
......@@ -307,7 +309,7 @@ func (csdb *CSStateDB) LoadValidators(height int64) (*ttypes.ValidatorSet, error
if height == 0 {
return nil, nil
}
if csdb.state.LastBlockHeight+1 == height {
if csdb.state.LastBlockHeight == height {
return csdb.state.Validators, nil
}
......@@ -374,6 +376,7 @@ func SaveState(state State) *tmtypes.State {
ChainID: state.ChainID,
LastBlockHeight: state.LastBlockHeight,
LastBlockTotalTx: state.LastBlockTotalTx,
LastBlockID: &state.LastBlockID.BlockID,
LastBlockTime: state.LastBlockTime,
Validators: &tmtypes.ValidatorSet{Validators: make([]*tmtypes.Validator, 0), Proposer: &tmtypes.Validator{}},
LastValidators: &tmtypes.ValidatorSet{Validators: make([]*tmtypes.Validator, 0), Proposer: &tmtypes.Validator{}},
......@@ -458,17 +461,14 @@ func LoadProposer(source *tmtypes.Validator) (*ttypes.Validator, error) {
}
// CreateBlockInfoTx make blockInfo to the first transaction of the block and execer is valnode
func CreateBlockInfoTx(pubkey string, lastCommit *tmtypes.TendermintCommit, seenCommit *tmtypes.TendermintCommit, state *tmtypes.State, proposal *tmtypes.Proposal, block *tmtypes.TendermintBlock) *types.Transaction {
blockNoTxs := *block
blockNoTxs.Txs = make([]*types.Transaction, 0)
func CreateBlockInfoTx(pubkey string, state *tmtypes.State, block *tmtypes.TendermintBlock) *types.Transaction {
blockSave := *block
blockSave.Data = nil
blockInfo := &tmtypes.TendermintBlockInfo{
SeenCommit: seenCommit,
LastCommit: lastCommit,
State: state,
Proposal: proposal,
Block: &blockNoTxs,
State: state,
Block: &blockSave,
}
tendermintlog.Debug("CreateBlockInfoTx", "validators", blockInfo.State.Validators.Validators, "block", block, "block-notxs", blockNoTxs)
tendermintlog.Debug("CreateBlockInfoTx", "blockInfo", blockInfo)
nput := &tmtypes.ValNodeAction_BlockInfo{BlockInfo: blockInfo}
action := &tmtypes.ValNodeAction{Value: nput, Ty: tmtypes.ValNodeActionBlockInfo}
......
package tendermint
import (
"fmt"
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/types"
tmtypes "github.com/33cn/plugin/plugin/dapp/valnode/types"
"github.com/gogo/protobuf/proto"
)
var (
stateKey = []byte("stateKey")
)
type ConsensusStore struct {
db dbm.DB
}
// NewConsensusStore returns a new ConsensusStore with the given DB
func NewConsensusStore() *ConsensusStore {
db := DefaultDBProvider("state")
db.SetCacheSize(100)
return &ConsensusStore{
db: db,
}
}
// LoadStateFromStore
func (cs *ConsensusStore) LoadStateFromStore() *tmtypes.State {
buf, err := cs.db.Get(stateKey)
if err != nil {
tendermintlog.Error("LoadStateFromStore", "err", err)
return nil
}
state := &tmtypes.State{}
err = types.Decode(buf, state)
if err != nil {
panic(err)
}
return state
}
// LoadStateHeight
func (cs *ConsensusStore) LoadStateHeight() int64 {
state := cs.LoadStateFromStore()
if state == nil {
return int64(0)
}
return state.LastBlockHeight
}
// LoadSeenCommit by height
func (cs *ConsensusStore) LoadSeenCommit(height int64) *tmtypes.TendermintCommit {
buf, err := cs.db.Get(calcSeenCommitKey(height))
if err != nil {
tendermintlog.Error("LoadSeenCommit", "err", err)
return nil
}
commit := &tmtypes.TendermintCommit{}
err = types.Decode(buf, commit)
if err != nil {
panic(err)
}
return commit
}
// SaveConsensusState save state and seenCommit
func (cs *ConsensusStore) SaveConsensusState(height int64, state *tmtypes.State, sc proto.Message) error {
seenCommitBytes := types.Encode(sc)
stateBytes := types.Encode(state)
batch := cs.db.NewBatch(true)
batch.Set(calcSeenCommitKey(height), seenCommitBytes)
batch.Set(stateKey, stateBytes)
err := batch.Write()
if err != nil {
tendermintlog.Error("SaveConsensusState batch.Write", "err", err)
return err
}
return nil
}
func calcSeenCommitKey(height int64) []byte {
return []byte(fmt.Sprintf("SC:%v", height))
}
......@@ -5,7 +5,9 @@
package tendermint
import (
"bytes"
"fmt"
"os"
"time"
"github.com/33cn/chain33/common/crypto"
......@@ -38,6 +40,7 @@ var (
timeoutCommit int32 = 1000
skipTimeoutCommit = false
createEmptyBlocks = false
fastSync = false
createEmptyBlocksInterval int32 // second
validatorNodes = []string{"127.0.0.1:46656"}
peerGossipSleepDuration int32 = 100
......@@ -59,6 +62,7 @@ type Client struct {
privKey crypto.PrivKey // local node's p2p key
pubKey string
csState *ConsensusState
csStore *ConsensusStore // save consensus state
evidenceDB dbm.DB
crypto crypto.Crypto
node *Node
......@@ -81,6 +85,7 @@ type subConfig struct {
CreateEmptyBlocks bool `json:"createEmptyBlocks"`
CreateEmptyBlocksInterval int32 `json:"createEmptyBlocksInterval"`
ValidatorNodes []string `json:"validatorNodes"`
FastSync bool `json:"fastSync"`
}
func (client *Client) applyConfig(sub []byte) {
......@@ -126,12 +131,13 @@ func (client *Client) applyConfig(sub []byte) {
if len(subcfg.ValidatorNodes) > 0 {
validatorNodes = subcfg.ValidatorNodes
}
fastSync = subcfg.FastSync
}
// DefaultDBProvider returns a database using the DBBackend and DBDir
// specified in the ctx.Config.
func DefaultDBProvider(ID string) (dbm.DB, error) {
return dbm.NewDB(ID, "leveldb", "./datadir", 0), nil
func DefaultDBProvider(name string) dbm.DB {
return dbm.NewDB(name, "leveldb", fmt.Sprintf("datadir%stendermint", string(os.PathSeparator)), 0)
}
// New ...
......@@ -140,18 +146,14 @@ func New(cfg *types.Consensus, sub []byte) queue.Module {
//init rand
ttypes.Init()
genDoc, err := ttypes.GenesisDocFromFile("./genesis.json")
genDoc, err := ttypes.GenesisDocFromFile("genesis.json")
if err != nil {
tendermintlog.Error("NewTendermintClient", "msg", "GenesisDocFromFile failded", "error", err)
return nil
}
// Make Evidence Reactor
evidenceDB, err := DefaultDBProvider("CSevidence")
if err != nil {
tendermintlog.Error("NewTendermintClient", "msg", "DefaultDBProvider evidenceDB failded", "error", err)
return nil
}
evidenceDB := DefaultDBProvider("evidence")
cr, err := crypto.New(types.GetSignName("", types.ED25519))
if err != nil {
......@@ -167,7 +169,7 @@ func New(cfg *types.Consensus, sub []byte) queue.Module {
return nil
}
privValidator := ttypes.LoadOrGenPrivValidatorFS("./priv_validator.json")
privValidator := ttypes.LoadOrGenPrivValidatorFS("priv_validator.json")
if privValidator == nil {
tendermintlog.Error("NewTendermintClient create priv_validator file failed")
return nil
......@@ -183,6 +185,7 @@ func New(cfg *types.Consensus, sub []byte) queue.Module {
privValidator: privValidator,
privKey: priv,
pubKey: pubkey,
csStore: NewConsensusStore(),
evidenceDB: evidenceDB,
crypto: cr,
txsAvailable: make(chan int64, 1),
......@@ -223,16 +226,13 @@ func (client *Client) SetQueueClient(q queue.Client) {
go client.StartConsensus()
}
// DebugCatchup define whether catch up now
const DebugCatchup = false
// StartConsensus a routine that make the consensus start
func (client *Client) StartConsensus() {
//进入共识前先同步到最大高度
hint := time.NewTicker(5 * time.Second)
beg := time.Now()
OuterLoop:
for !DebugCatchup {
for fastSync {
select {
case <-hint.C:
tendermintlog.Info("Still catching up max height......", "Height", client.GetCurrentHeight(), "cost", time.Since(beg))
......@@ -246,60 +246,46 @@ OuterLoop:
}
hint.Stop()
curHeight := client.GetCurrentHeight()
blockInfo, err := client.QueryBlockInfoByHeight(curHeight)
if curHeight != 0 && err != nil {
tendermintlog.Error("StartConsensus GetBlockInfo failed", "error", err)
panic(fmt.Sprintf("StartConsensus GetBlockInfo failed:%v", err))
}
// load state
var state State
if blockInfo == nil {
if curHeight != 0 {
tendermintlog.Error("StartConsensus", "msg", "block height is not 0 but blockinfo is nil")
panic(fmt.Sprintf("StartConsensus block height is %v but block info is nil", curHeight))
}
statetmp, err := MakeGenesisState(client.genesisDoc)
if client.GetCurrentHeight() == 0 {
genState, err := MakeGenesisState(client.genesisDoc)
if err != nil {
tendermintlog.Error("StartConsensus", "msg", "MakeGenesisState failded", "error", err)
return
panic(fmt.Sprintf("StartConsensus MakeGenesisState fail:%v", err))
}
state = statetmp.Copy()
state = genState.Copy()
} else if client.GetCurrentHeight() <= client.csStore.LoadStateHeight() {
stoState := client.csStore.LoadStateFromStore()
if stoState == nil {
panic("StartConsensus LoadStateFromStore fail")
}
state = LoadState(stoState)
tendermintlog.Info("Load state from store")
} else {
tendermintlog.Debug("StartConsensus", "blockinfo", blockInfo)
csState := blockInfo.GetState()
if csState == nil {
tendermintlog.Error("StartConsensus", "msg", "blockInfo.GetState is nil")
return
height := client.GetCurrentHeight()
blkState := client.LoadBlockState(height)
if blkState == nil {
panic("StartConsensus LoadBlockState fail")
}
state = LoadState(csState)
if seenCommit := blockInfo.SeenCommit; seenCommit != nil {
state.LastBlockID = ttypes.BlockID{
BlockID: tmtypes.BlockID{
Hash: seenCommit.BlockID.GetHash(),
},
}
state = LoadState(blkState)
tendermintlog.Info("Load state from block")
//save initial state in store
blkCommit := client.LoadBlockCommit(height)
if blkCommit == nil {
panic("StartConsensus LoadBlockCommit fail")
}
}
tendermintlog.Debug("Load state finish", "state", state)
valNodes, err := client.QueryValidatorsByHeight(curHeight)
if err == nil && valNodes != nil {
if len(valNodes.Nodes) > 0 {
tendermintlog.Info("StartConsensus validators update", "update-valnodes", valNodes)
prevValSet := state.LastValidators.Copy()
nextValSet := prevValSet.Copy()
err := updateValidators(nextValSet, valNodes.Nodes)
if err != nil {
tendermintlog.Error("Error changing validator set", "error", err)
}
// change results from this height but only applies to the next height
state.LastHeightValidatorsChanged = curHeight + 1
nextValSet.IncrementAccum(1)
state.Validators = nextValSet
err := client.csStore.SaveConsensusState(height-1, blkState, blkCommit)
if err != nil {
panic(fmt.Sprintf("StartConsensus SaveConsensusState fail: %v", err))
}
tendermintlog.Info("Save state from block")
}
tendermintlog.Info("StartConsensus", "validators", state.Validators)
tendermintlog.Debug("Load state finish", "state", state)
// start
tendermintlog.Info("StartConsensus",
"privValidator", fmt.Sprintf("%X", ttypes.Fingerprint(client.privValidator.GetAddress())),
"Validators", state.Validators.String())
// Log whether this node is a validator or an observer
if state.Validators.HasAddress(client.privValidator.GetAddress()) {
tendermintlog.Info("This node is a validator")
......@@ -309,15 +295,11 @@ OuterLoop:
stateDB := NewStateDB(client, state)
//make evidenceReactor
evidenceStore := NewEvidenceStore(client.evidenceDB)
evidencePool := NewEvidencePool(stateDB, state, evidenceStore)
// make block executor for consensus and blockchain reactors to execute blocks
blockExec := NewBlockExecutor(stateDB, evidencePool)
blockExec := NewBlockExecutor(stateDB)
// Make ConsensusReactor
csState := NewConsensusState(client, state, blockExec, evidencePool)
csState := NewConsensusState(client, state, blockExec)
// reset height, round, state begin at newheigt,0,0
client.privValidator.ResetLastHeight(state.LastBlockHeight)
csState.SetPrivValidator(client.privValidator)
......@@ -326,7 +308,7 @@ OuterLoop:
// Create & add listener
protocol, listeningAddress := "tcp", "0.0.0.0:46656"
node := NewNode(validatorNodes, protocol, listeningAddress, client.privKey, state.ChainID, tendermintVersion, csState, evidencePool)
node := NewNode(validatorNodes, protocol, listeningAddress, client.privKey, state.ChainID, tendermintVersion, csState)
client.node = node
node.Start()
......@@ -353,8 +335,56 @@ func (client *Client) CreateGenesisTx() (ret []*types.Transaction) {
return
}
// CheckBlock 暂不检查任何的交易
func (client *Client) getBlockInfoTx(current *types.Block) (*tmtypes.ValNodeAction, error) {
//检查第一个笔交易的execs, 以及执行状态
if len(current.Txs) == 0 {
return nil, types.ErrEmptyTx
}
baseTx := current.Txs[0]
//判断交易类型和执行情况
var valAction tmtypes.ValNodeAction
err := types.Decode(baseTx.GetPayload(), &valAction)
if err != nil {
return nil, err
}
if valAction.GetTy() != tmtypes.ValNodeActionBlockInfo {
return nil, ttypes.ErrBaseTxType
}
//判断交易执行是否OK
if valAction.GetBlockInfo() == nil {
return nil, ttypes.ErrBlockInfoTx
}
return &valAction, nil
}
// CheckBlock 检查区块
func (client *Client) CheckBlock(parent *types.Block, current *types.BlockDetail) error {
if current.Block.Difficulty != types.GetP(0).PowLimitBits {
return types.ErrBlockHeaderDifficulty
}
valAction, err := client.getBlockInfoTx(current.Block)
if err != nil {
return err
}
if parent.Height+1 != current.Block.Height {
return types.ErrBlockHeight
}
//判断exec 是否成功
if current.Receipts[0].Ty != types.ExecOk {
return ttypes.ErrBaseExecErr
}
info := valAction.GetBlockInfo()
if current.Block.Height > 1 {
lastValAction, err := client.getBlockInfoTx(parent)
if err != nil {
return err
}
lastInfo := lastValAction.GetBlockInfo()
lastProposalBlock := &ttypes.TendermintBlock{TendermintBlock: lastInfo.GetBlock()}
if !lastProposalBlock.HashesTo(info.Block.Header.LastBlockID.Hash) {
return ttypes.ErrLastBlockID
}
}
return nil
}
......@@ -377,17 +407,30 @@ func (client *Client) CreateBlock() {
if issleep {
time.Sleep(time.Second)
}
if !client.CheckTxsAvailable() {
height, err := client.getLastHeight()
if err != nil {
issleep = true
continue
}
if !client.CheckTxsAvailable(height) {
issleep = true
continue
}
issleep = false
client.txsAvailable <- client.GetCurrentHeight() + 1
client.txsAvailable <- height + 1
time.Sleep(time.Duration(timeoutTxAvail) * time.Millisecond)
}
}
func (client *Client) getLastHeight() (int64, error) {
lastBlock, err := client.RequestLastBlock()
if err != nil {
return -1, err
}
return lastBlock.Height, nil
}
// TxsAvailable check available channel
func (client *Client) TxsAvailable() <-chan int64 {
return client.txsAvailable
......@@ -399,9 +442,9 @@ func (client *Client) StopC() <-chan struct{} {
}
// CheckTxsAvailable check whether some new transactions arriving
func (client *Client) CheckTxsAvailable() bool {
func (client *Client) CheckTxsAvailable(height int64) bool {
txs := client.RequestTx(10, nil)
txs = client.CheckTxDup(txs, client.GetCurrentHeight())
txs = client.CheckTxDup(txs, height)
return len(txs) != 0
}
......@@ -416,58 +459,64 @@ func (client *Client) CheckTxDup(txs []*types.Transaction, height int64) (transa
return types.CacheToTxs(cacheTxs)
}
// BuildBlock build a new block contains some transactions
// BuildBlock build a new block
func (client *Client) BuildBlock() *types.Block {
lastHeight := client.GetCurrentHeight()
txs := client.RequestTx(int(types.GetP(lastHeight+1).MaxTxNumber)-1, nil)
newblock := &types.Block{}
newblock.Height = lastHeight + 1
client.AddTxsToBlock(newblock, txs)
return newblock
}
// CommitBlock call WriteBlock to real commit to chain
func (client *Client) CommitBlock(propBlock *types.Block) error {
newblock := *propBlock
lastBlock, err := client.RequestBlock(newblock.Height - 1)
if err != nil {
tendermintlog.Error("RequestBlock fail", "err", err)
return err
}
lastBlock := client.GetCurrentBlock()
txs := client.RequestTx(int(types.GetP(lastBlock.Height+1).MaxTxNumber)-1, nil)
// placeholder
tx0 := &types.Transaction{}
txs = append([]*types.Transaction{tx0}, txs...)
var newblock types.Block
newblock.ParentHash = lastBlock.Hash()
newblock.TxHash = merkle.CalcMerkleRoot(newblock.Txs)
newblock.BlockTime = time.Now().Unix()
newblock.Height = lastBlock.Height + 1
client.AddTxsToBlock(&newblock, txs)
//固定难度
newblock.Difficulty = types.GetP(0).PowLimitBits
//newblock.TxHash = merkle.CalcMerkleRoot(newblock.Txs)
newblock.BlockTime = types.Now().Unix()
if lastBlock.BlockTime >= newblock.BlockTime {
newblock.BlockTime = lastBlock.BlockTime + 1
}
newblock.Difficulty = types.GetP(0).PowLimitBits
return &newblock
}
err = client.WriteBlock(lastBlock.StateHash, &newblock)
if err != nil {
tendermintlog.Error(fmt.Sprintf("********************CommitBlock err:%v", err.Error()))
return err
}
tendermintlog.Info("Commit block success", "height", newblock.Height, "CurrentHeight", client.GetCurrentHeight())
if client.GetCurrentHeight() != newblock.Height {
tendermintlog.Warn("Commit block fail", "height", newblock.Height, "CurrentHeight", client.GetCurrentHeight())
// CommitBlock call WriteBlock to commit to chain
func (client *Client) CommitBlock(block *types.Block) error {
retErr := client.WriteBlock(nil, block)
if retErr != nil {
tendermintlog.Info("CommitBlock fail", "err", retErr)
if client.WaitBlock(block.Height) {
curBlock, err := client.RequestBlock(block.Height)
if err == nil {
if bytes.Equal(curBlock.Hash(), block.Hash()) {
tendermintlog.Info("already has block")
return nil
}
tendermintlog.Info("block is different", "block", block, "curBlock", curBlock)
if bytes.Equal(curBlock.Txs[0].Hash(), block.Txs[0].Hash()) {
tendermintlog.Warn("base tx is same, origin maybe same")
return nil
}
}
}
return retErr
}
return nil
}
// CheckCommit by height
func (client *Client) CheckCommit(height int64) bool {
// WaitBlock by height
func (client *Client) WaitBlock(height int64) bool {
retry := 0
var newHeight int64
for {
newHeight = client.GetCurrentHeight()
if newHeight >= height {
tendermintlog.Info("Sync block success", "height", height, "CurrentHeight", newHeight)
newHeight, err := client.getLastHeight()
if err == nil && newHeight >= height {
return true
}
retry++
time.Sleep(100 * time.Millisecond)
if retry >= 600 {
tendermintlog.Warn("Sync block fail", "height", height, "CurrentHeight", newHeight)
if retry >= 100 {
tendermintlog.Warn("Wait block fail", "height", height, "CurrentHeight", newHeight)
return false
}
}
......@@ -493,6 +542,7 @@ func (client *Client) QueryValidatorsByHeight(height int64) (*tmtypes.ValNodes,
}
msg, err = client.GetQueueClient().Wait(msg)
if err != nil {
tendermintlog.Info("QueryValidatorsByHeight result", "err", err)
return nil, err
}
return msg.GetData().(types.Message).(*tmtypes.ValNodes), nil
......@@ -523,30 +573,32 @@ func (client *Client) QueryBlockInfoByHeight(height int64) (*tmtypes.TendermintB
return msg.GetData().(types.Message).(*tmtypes.TendermintBlockInfo), nil
}
// LoadSeenCommit by height
func (client *Client) LoadSeenCommit(height int64) *tmtypes.TendermintCommit {
// LoadBlockCommit by height
func (client *Client) LoadBlockCommit(height int64) *tmtypes.TendermintCommit {
blockInfo, err := client.QueryBlockInfoByHeight(height)
if err != nil {
panic(fmt.Sprintf("LoadSeenCommit GetBlockInfo failed:%v", err))
tendermintlog.Error("LoadBlockCommit GetBlockInfo fail", "err", err)
return nil
}
if blockInfo == nil {
tendermintlog.Error("LoadSeenCommit get nil block info")
tendermintlog.Error("LoadBlockCommit get nil block info")
return nil
}
return blockInfo.GetSeenCommit()
return blockInfo.GetBlock().GetLastCommit()
}
// LoadBlockCommit by height
func (client *Client) LoadBlockCommit(height int64) *tmtypes.TendermintCommit {
// LoadBlockState by height
func (client *Client) LoadBlockState(height int64) *tmtypes.State {
blockInfo, err := client.QueryBlockInfoByHeight(height)
if err != nil {
panic(fmt.Sprintf("LoadBlockCommit GetBlockInfo failed:%v", err))
tendermintlog.Error("LoadBlockState GetBlockInfo fail", "err", err)
return nil
}
if blockInfo == nil {
tendermintlog.Error("LoadBlockCommit get nil block info")
tendermintlog.Error("LoadBlockState get nil block info")
return nil
}
return blockInfo.GetLastCommit()
return blockInfo.GetState()
}
// LoadProposalBlock by height
......@@ -567,8 +619,8 @@ func (client *Client) LoadProposalBlock(height int64) *tmtypes.TendermintBlock {
proposalBlock := blockInfo.GetBlock()
if proposalBlock != nil {
proposalBlock.Txs = append(proposalBlock.Txs, block.Txs[1:]...)
txHash := merkle.CalcMerkleRoot(proposalBlock.Txs)
proposalBlock.Data = block
txHash := merkle.CalcMerkleRoot(proposalBlock.Data.Txs)
tendermintlog.Debug("LoadProposalBlock txs hash", "height", proposalBlock.Header.Height, "tx-hash", fmt.Sprintf("%X", txHash))
}
return proposalBlock
......
......@@ -7,6 +7,7 @@ package tendermint
import (
"context"
"encoding/binary"
"encoding/hex"
"errors"
"flag"
"fmt"
......@@ -15,6 +16,8 @@ import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/33cn/chain33/blockchain"
"github.com/33cn/chain33/common/address"
"github.com/33cn/chain33/common/limits"
......@@ -27,6 +30,7 @@ import (
"github.com/33cn/chain33/store"
"github.com/33cn/chain33/types"
pty "github.com/33cn/plugin/plugin/dapp/norm/types"
ty "github.com/33cn/plugin/plugin/dapp/valnode/types"
"google.golang.org/grpc"
_ "github.com/33cn/chain33/system"
......@@ -36,7 +40,7 @@ import (
var (
random *rand.Rand
loopCount = 10
loopCount = 3
conn *grpc.ClientConn
c types.Chain33Client
)
......@@ -50,12 +54,12 @@ func init() {
log.SetLogLevel("info")
}
func TestTendermintPerf(t *testing.T) {
TendermintPerf()
TendermintPerf(t)
fmt.Println("=======start clear test data!=======")
clearTestData()
}
func TendermintPerf() {
func TendermintPerf(t *testing.T) {
q, chain, s, mem, exec, cs, p2p := initEnvTendermint()
defer chain.Close()
defer mem.Close()
......@@ -68,12 +72,18 @@ func TendermintPerf() {
for err != nil {
err = createConn()
}
time.Sleep(10 * time.Second)
time.Sleep(2 * time.Second)
for i := 0; i < loopCount; i++ {
NormPut()
time.Sleep(time.Second)
}
time.Sleep(10 * time.Second)
CheckState(t, cs.(*Client))
AddNode()
for i := 0; i < loopCount*3; i++ {
NormPut()
time.Sleep(time.Second)
}
time.Sleep(2 * time.Second)
}
func initEnvTendermint() (queue.Queue, *blockchain.BlockChain, queue.Module, queue.Module, *executor.Executor, queue.Module, queue.Module) {
......@@ -177,3 +187,38 @@ func NormPut() {
return
}
}
func AddNode() {
pubkey := "788657125A5A547B499F8B74239092EBB6466E8A205348D9EA645D510235A671"
pubkeybyte, err := hex.DecodeString(pubkey)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
nput := &ty.ValNodeAction_Node{Node: &ty.ValNode{PubKey: pubkeybyte, Power: int64(2)}}
action := &ty.ValNodeAction{Value: nput, Ty: ty.ValNodeActionUpdate}
tx := &types.Transaction{Execer: []byte("valnode"), Payload: types.Encode(action), Fee: fee}
tx.To = address.ExecAddress("valnode")
tx.Nonce = random.Int63()
tx.Sign(types.SECP256K1, getprivkey("CC38546E9E659D15E6B4893F0AB32A06D103931A8230B0BDE71459D2B27D6944"))
reply, err := c.SendTransaction(context.Background(), tx)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
if !reply.IsOk {
fmt.Fprintln(os.Stderr, errors.New(string(reply.GetMsg())))
return
}
}
func CheckState(t *testing.T, client *Client) {
msg1, err := client.Query_IsHealthy(&types.ReqNil{})
assert.Nil(t, err)
flag := msg1.(*ty.IsHealthy).IsHealthy
assert.Equal(t, true, flag)
_, err = client.Query_NodeInfo(&types.ReqNil{})
assert.Nil(t, err)
}
......@@ -17,7 +17,6 @@ import (
"github.com/33cn/chain33/common/merkle"
"github.com/33cn/chain33/types"
tmtypes "github.com/33cn/plugin/plugin/dapp/valnode/types"
"github.com/golang/protobuf/proto"
)
var (
......@@ -58,67 +57,62 @@ type TendermintBlock struct {
// MakeBlock returns a new block with an empty header, except what can be computed from itself.
// It populates the same set of fields validated by ValidateBasic
func MakeBlock(height int64, round int64, Txs []*types.Transaction, commit *tmtypes.TendermintCommit) *TendermintBlock {
block := &TendermintBlock{&tmtypes.TendermintBlock{
Header: &tmtypes.TendermintBlockHeader{
Height: height,
Round: round,
Time: time.Now().UnixNano(),
NumTxs: int64(len(Txs)),
func MakeBlock(height int64, round int64, pblock *types.Block, commit *tmtypes.TendermintCommit) *TendermintBlock {
block := &TendermintBlock{
&tmtypes.TendermintBlock{
Header: &tmtypes.TendermintBlockHeader{
Height: height,
Round: round,
Time: pblock.BlockTime,
NumTxs: int64(len(pblock.Txs)),
},
Data: pblock,
LastCommit: commit,
},
Txs: Txs,
LastCommit: commit,
Evidence: &tmtypes.EvidenceData{Evidence: make([]*tmtypes.EvidenceEnvelope, 0)},
},
}
block.FillHeader()
return block
}
// AddEvidence appends the given evidence to the block
func (b *TendermintBlock) AddEvidence(evidence []Evidence) {
for _, item := range evidence {
ev := item.Child()
if ev != nil {
data, err := proto.Marshal(ev)
if err != nil {
blocklog.Error("AddEvidence marshal failed", "error", err)
panic("AddEvidence marshal failed")
}
env := &tmtypes.EvidenceEnvelope{
TypeName: item.TypeName(),
Data: data,
}
b.Evidence.Evidence = append(b.Evidence.Evidence, env)
}
}
}
// ValidateBasic performs basic validation that doesn't involve state data.
// It checks the internal consistency of the block.
func (b *TendermintBlock) ValidateBasic() (int64, error) {
newTxs := int64(len(b.Txs))
// Further validation is done using state#ValidateBlock.
func (b *TendermintBlock) ValidateBasic() error {
if b == nil {
return errors.New("nil block")
}
if b.Header.Height < 0 {
return errors.New("Negative Header.Height")
} else if b.Header.Height == 0 {
return errors.New("Zero Header.Height")
}
newTxs := int64(len(b.Data.Txs))
if b.Header.NumTxs != newTxs {
return 0, fmt.Errorf("Wrong Block.Header.NumTxs. Expected %v, got %v", newTxs, b.Header.NumTxs)
return fmt.Errorf("Wrong Header.NumTxs. Expected %v, got %v", newTxs, b.Header.NumTxs)
}
if b.Header.TotalTxs < 0 {
return errors.New("Negative Header.TotalTxs")
}
lastCommit := Commit{
TendermintCommit: b.LastCommit,
}
if !bytes.Equal(b.Header.LastCommitHash, lastCommit.Hash()) {
return 0, fmt.Errorf("Wrong Block.Header.LastCommitHash. Expected %v, got %v", b.Header.LastCommitHash, lastCommit.Hash())
}
if b.Header.Height != 1 {
if b.Header.Height > 1 {
if b.LastCommit == nil {
return errors.New("nil LastCommit")
}
if err := lastCommit.ValidateBasic(); err != nil {
return 0, err
return err
}
}
evidence := &EvidenceData{EvidenceData: b.Evidence}
if !bytes.Equal(b.Header.EvidenceHash, evidence.Hash()) {
return 0, errors.New(Fmt("Wrong Block.Header.EvidenceHash. Expected %v, got %v", b.Header.EvidenceHash, evidence.Hash()))
if !bytes.Equal(b.Header.LastCommitHash, lastCommit.Hash()) {
return fmt.Errorf("Wrong Header.LastCommitHash. Expected %v, got %v", b.Header.LastCommitHash, lastCommit.Hash())
}
return newTxs, nil
return nil
}
// FillHeader fills in any remaining header fields that are a function of the block data
......@@ -129,10 +123,6 @@ func (b *TendermintBlock) FillHeader() {
}
b.Header.LastCommitHash = lastCommit.Hash()
}
if b.Header.EvidenceHash == nil {
evidence := &EvidenceData{EvidenceData: b.Evidence}
b.Header.EvidenceHash = evidence.Hash()
}
}
// Hash computes and returns the block hash.
......@@ -173,10 +163,8 @@ func (b *TendermintBlock) StringIndented(indent string) string {
return Fmt(`Block{
%s %v
%s %v
%s %v
%s}#%v`,
indent, header.StringIndented(indent+" "),
// indent, b.Evidence.StringIndented(indent+" "),
indent, lastCommit.StringIndented(indent+" "),
indent, b.Hash())
}
......@@ -227,7 +215,6 @@ 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,
......@@ -240,24 +227,19 @@ func (h *Header) StringIndented(indent string) string {
indent, h.AppHash,
indent, h.ConsensusHash,
indent, h.LastResultsHash,
indent, h.EvidenceHash,
indent, h.Hash())
}
// Commit struct
type Commit struct {
*tmtypes.TendermintCommit
firstPrecommit *tmtypes.Vote
hash []byte
bitArray *BitArray
firstPrecommit *tmtypes.Vote
}
// FirstPrecommit returns the first non-nil precommit in the commit
func (commit *Commit) FirstPrecommit() *tmtypes.Vote {
if len(commit.Precommits) == 0 {
return nil
}
if commit.firstPrecommit != nil {
return commit.firstPrecommit
}
......@@ -334,13 +316,14 @@ func (commit *Commit) ValidateBasic() error {
height, round := commit.Height(), commit.Round()
// validate the precommits
for _, precommit := range commit.Precommits {
// It's OK for precommits to be missing.
if precommit == nil {
for _, item := range commit.Precommits {
// may be nil if validator skipped.
if item == nil || len(item.Signature) == 0 {
continue
}
precommit := &Vote{Vote: item}
// Ensure that all votes are precommits
if byte(precommit.Type) != VoteTypePrecommit {
if precommit.Type != uint32(VoteTypePrecommit) {
return fmt.Errorf("Invalid commit vote. Expected precommit, got %v",
precommit.Type)
}
......@@ -394,113 +377,3 @@ type SignedHeader struct {
Header *Header `json:"header"`
Commit *Commit `json:"commit"`
}
// EvidenceEnvelope ...
type EvidenceEnvelope struct {
*tmtypes.EvidenceEnvelope
}
// EvidenceEnvelopeList contains any evidence of malicious wrong-doing by validators
type EvidenceEnvelopeList []EvidenceEnvelope
// Hash ...
func (env EvidenceEnvelope) Hash() []byte {
penv := env.EvidenceEnvelope
evidence := EvidenceEnvelope2Evidence(penv)
if evidence != nil {
return evidence.Hash()
}
return nil
}
func (env EvidenceEnvelope) String() string {
penv := env.EvidenceEnvelope
evidence := EvidenceEnvelope2Evidence(penv)
if evidence != nil {
return evidence.String()
}
return ""
}
// Hash returns the simple merkle root hash of the EvidenceList.
func (evl EvidenceEnvelopeList) Hash() []byte {
// Recursive impl.
// Copied from tmlibs/merkle to avoid allocations
switch len(evl) {
case 0:
return nil
case 1:
return evl[0].Hash()
default:
left := evl[:(len(evl)+1)/2].Hash()
right := evl[(len(evl)+1)/2:].Hash()
cache := make([]byte, len(left)+len(right))
return merkle.GetHashFromTwoHash(cache, left, right)
}
}
func (evl EvidenceEnvelopeList) String() string {
s := ""
for _, e := range evl {
s += Fmt("%s\t\t", e)
}
return s
}
// Has returns true if the evidence is in the EvidenceList.
func (evl EvidenceEnvelopeList) Has(evidence Evidence) bool {
for _, ev := range evl {
penv := ev.EvidenceEnvelope
tmp := EvidenceEnvelope2Evidence(penv)
if tmp != nil {
if tmp.Equal(evidence) {
return true
}
}
}
return false
}
// EvidenceData ...
type EvidenceData struct {
*tmtypes.EvidenceData
hash []byte
}
// Hash returns the hash of the data.
func (data *EvidenceData) Hash() []byte {
if data.hash == nil {
if data.EvidenceData == nil {
return nil
}
var evidence EvidenceEnvelopeList
for _, item := range data.Evidence {
elem := EvidenceEnvelope{
EvidenceEnvelope: item,
}
evidence = append(evidence, elem)
}
data.hash = evidence.Hash()
}
return data.hash
}
// StringIndented returns a string representation of the evidence.
func (data *EvidenceData) StringIndented(indent string) string {
if data == nil {
return "nil-Evidence"
}
evStrings := make([]string, MinInt(len(data.Evidence), 21))
for i, ev := range data.Evidence {
if i == 20 {
evStrings[i] = Fmt("... (%v total)", len(data.Evidence))
break
}
evStrings[i] = Fmt("Evidence:%v", ev)
}
return Fmt(`Data{
%s %v
%s}#%v`,
indent, strings.Join(evStrings, "\n"+indent+" "),
indent, data.hash)
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package types
import "errors"
var (
// ErrBaseTxType error type
ErrBaseTxType = errors.New("ErrBaseTxType")
// ErrBlockInfoTx error type
ErrBlockInfoTx = errors.New("ErrBlockInfoTx")
// ErrBaseExecErr error type
ErrBaseExecErr = errors.New("ErrBaseExecErr")
// ErrLastBlockID error type
ErrLastBlockID = errors.New("ErrLastBlockID")
)
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package types
import (
"bytes"
"encoding/json"
"fmt"
"reflect"
"github.com/33cn/chain33/common/crypto"
"github.com/33cn/chain33/common/merkle"
tmtypes "github.com/33cn/plugin/plugin/dapp/valnode/types"
"github.com/golang/protobuf/proto"
)
// ErrEvidenceInvalid wraps a piece of evidence and the error denoting how or why it is invalid.
type ErrEvidenceInvalid struct {
Evidence Evidence
ErrorValue error
}
// NewEvidenceInvalidErr ...
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("Invalid evidence: %v. Evidence: %v", err.ErrorValue, err.Evidence)
}
//-------------------------------------------
const (
DuplicateVote = "DuplicateVote"
MockGood = "MockGood"
MockBad = "MockBad"
)
// EvidenceType map define
var (
EvidenceType2Type map[string]reflect.Type
EvidenceType2Obj map[string]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
Index() int // index of the validator in the validator set
Hash() []byte // hash of the evidence
Verify(chainID string) error // verify the evidence
Equal(Evidence) bool // check equality of evidence
String() string
Copy() Evidence
TypeName() string
SetChild(child proto.Message)
Child() proto.Message
}
//-------------------------------------------
// 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.
// Copied from tmlibs/merkle to avoid allocations
switch len(evl) {
case 0:
return nil
case 1:
return evl[0].Hash()
default:
left := evl[:(len(evl)+1)/2].Hash()
right := evl[(len(evl)+1)/2:].Hash()
cache := make([]byte, len(left)+len(right))
return merkle.GetHashFromTwoHash(cache, left, right)
}
}
func (evl EvidenceList) String() string {
s := ""
for _, e := range evl {
s += Fmt("%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
}
//-------------------------------------------
// DuplicateVoteEvidence contains evidence a validator signed two conflicting votes.
type DuplicateVoteEvidence struct {
*tmtypes.DuplicateVoteEvidence
}
// String returns a string representation of the evidence.
func (dve *DuplicateVoteEvidence) String() string {
return Fmt("VoteA: %v; VoteB: %v", dve.VoteA, dve.VoteB)
}
// Height returns the height this evidence refers to.
func (dve *DuplicateVoteEvidence) Height() int64 {
return dve.VoteA.Height
}
// Address returns the address of the validator.
func (dve *DuplicateVoteEvidence) Address() []byte {
pubkey, err := PubKeyFromString(dve.PubKey)
if err != nil {
return nil
}
return GenAddressByPubKey(pubkey)
}
// Index returns the index of the validator.
func (dve *DuplicateVoteEvidence) Index() int {
return int(dve.VoteA.ValidatorIndex)
}
// Hash returns the hash of the evidence.
func (dve *DuplicateVoteEvidence) Hash() []byte {
return 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 {
// 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 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)
}
// XXX: Should we enforce index is the same ?
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)
}
blockIDA := BlockID{
*dve.VoteA.BlockID,
}
blockIDB := BlockID{
*dve.VoteB.BlockID,
}
// BlockIDs must be different
if blockIDA.Equals(blockIDB) {
return fmt.Errorf("DuplicateVoteEvidence Error: BlockIDs are the same (%v) - not a real duplicate vote", dve.VoteA.BlockID)
}
// Signatures must be valid
pubkey, err := PubKeyFromString(dve.PubKey)
if err != nil {
return fmt.Errorf("DuplicateVoteEvidence Error: pubkey[%v] to PubKey failed:%v", dve.PubKey, err)
}
sigA, err := ConsensusCrypto.SignatureFromBytes(dve.VoteA.Signature)
if err != nil {
return fmt.Errorf("DuplicateVoteEvidence Error: SIGA[%v] to signature failed:%v", dve.VoteA.Signature, err)
}
sigB, err := ConsensusCrypto.SignatureFromBytes(dve.VoteB.Signature)
if err != nil {
return fmt.Errorf("DuplicateVoteEvidence Error: SIGB[%v] to signature failed:%v", dve.VoteB.Signature, err)
}
vote := &Vote{
dve.VoteA,
}
if !pubkey.VerifyBytes(SignBytes(chainID, vote), sigA) {
return fmt.Errorf("DuplicateVoteEvidence Error verifying VoteA: %v", ErrVoteInvalidSignature)
}
vote = &Vote{
dve.VoteB,
}
if !pubkey.VerifyBytes(SignBytes(chainID, vote), sigB) {
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
}
if dve == nil {
return false
}
// just check their hashes
return bytes.Equal(SimpleHashFromBinary(dve), SimpleHashFromBinary(ev.(*DuplicateVoteEvidence)))
}
// TypeName ...
func (dve *DuplicateVoteEvidence) TypeName() string {
return DuplicateVote
}
// Copy ...
func (dve *DuplicateVoteEvidence) Copy() Evidence {
return &DuplicateVoteEvidence{}
}
// SetChild ...
func (dve *DuplicateVoteEvidence) SetChild(child proto.Message) {
dve.DuplicateVoteEvidence = child.(*tmtypes.DuplicateVoteEvidence)
}
// Child ...
func (dve *DuplicateVoteEvidence) Child() proto.Message {
return dve.DuplicateVoteEvidence
}
// SimpleHashFromBinary ...
func SimpleHashFromBinary(item *DuplicateVoteEvidence) []byte {
bytes, e := json.Marshal(item)
if e != nil {
//commonlog.Error("SimpleHashFromBinary marshal failed", "type", item, "error", e)
panic(Fmt("SimpleHashFromBinary marshal failed, err:%v", e))
}
return crypto.Ripemd160(bytes)
}
// EvidenceEnvelope2Evidence ...
func EvidenceEnvelope2Evidence(envelope *tmtypes.EvidenceEnvelope) Evidence {
if v, ok := EvidenceType2Type[envelope.TypeName]; ok {
realMsg2 := reflect.New(v).Interface()
err := proto.Unmarshal(envelope.Data, realMsg2.(proto.Message))
if err != nil {
panic(Fmt("Evidence is not valid", "evidenceType", envelope.TypeName, "err", err))
}
if evidence, ok2 := EvidenceType2Obj[envelope.TypeName]; ok2 {
evidence = evidence.Copy()
evidence.SetChild(realMsg2.(proto.Message))
return evidence.(Evidence)
}
}
return nil
}
// MockGoodEvidence UNSTABLE
type MockGoodEvidence struct {
MGHeight int64
MGAddress []byte
MGIndex int
}
// NewMockGoodEvidence UNSTABLE
func NewMockGoodEvidence(height int64, index int, address []byte) MockGoodEvidence {
return MockGoodEvidence{height, address, index}
}
// Height ...
func (e MockGoodEvidence) Height() int64 { return e.MGHeight }
// Address ...
func (e MockGoodEvidence) Address() []byte { return e.MGAddress }
// Index ...
func (e MockGoodEvidence) Index() int { return e.MGIndex }
// Hash ...
func (e MockGoodEvidence) Hash() []byte {
return []byte(Fmt("%d-%d", e.MGHeight, e.MGIndex))
}
// Verify ...
func (e MockGoodEvidence) Verify(chainID string) error { return nil }
// Equal ...
func (e MockGoodEvidence) Equal(ev Evidence) bool {
e2 := ev.(MockGoodEvidence)
return e.MGHeight == e2.MGHeight &&
bytes.Equal(e.MGAddress, e2.MGAddress) &&
e.MGIndex == e2.MGIndex
}
func (e MockGoodEvidence) String() string {
return Fmt("GoodEvidence: %d/%s/%d", e.MGHeight, e.MGAddress, e.MGIndex)
}
// TypeName ...
func (e MockGoodEvidence) TypeName() string {
return MockGood
}
// Copy ...
func (e MockGoodEvidence) Copy() Evidence {
return &MockGoodEvidence{}
}
// SetChild ...
func (e MockGoodEvidence) SetChild(proto.Message) {}
// Child ...
func (e MockGoodEvidence) Child() proto.Message {
return nil
}
// MockBadEvidence UNSTABLE
type MockBadEvidence struct {
MockGoodEvidence
}
// Verify ...
func (e MockBadEvidence) Verify(chainID string) error { return fmt.Errorf("MockBadEvidence") }
// Equal ...
func (e MockBadEvidence) Equal(ev Evidence) bool {
e2 := ev.(MockBadEvidence)
return e.MGHeight == e2.MGHeight &&
bytes.Equal(e.MGAddress, e2.MGAddress) &&
e.MGIndex == e2.MGIndex
}
func (e MockBadEvidence) String() string {
return Fmt("BadEvidence: %d/%s/%d", e.MGHeight, e.MGAddress, e.MGIndex)
}
// TypeName ...
func (e MockBadEvidence) TypeName() string {
return MockBad
}
// Copy ...
func (e MockBadEvidence) Copy() Evidence {
return &MockBadEvidence{}
}
// SetChild ...
func (e MockBadEvidence) SetChild(proto.Message) {}
// Child ...
func (e MockBadEvidence) Child() proto.Message {
return nil
}
//------------------------------------------------------
// evidence pool
// EvidencePool defines the EvidencePool interface used by the ConsensusState.
// UNSTABLE
type EvidencePool interface {
PendingEvidence() []Evidence
AddEvidence(Evidence) error
Update(*TendermintBlock)
}
// MockEvidencePool is an empty implementation of a Mempool, useful for testing.
// UNSTABLE
type MockEvidencePool struct {
}
// PendingEvidence ...
func (m MockEvidencePool) PendingEvidence() []Evidence { return nil }
// AddEvidence ...
func (m MockEvidencePool) AddEvidence(Evidence) error { return nil }
// Update ...
func (m MockEvidencePool) Update(*TendermintBlock) {}
......@@ -100,9 +100,8 @@ func (hvs *HeightVoteSet) SetRound(round int) {
func (hvs *HeightVoteSet) addRound(round int) {
if _, ok := hvs.roundVoteSets[round]; ok {
panic(Fmt("Panicked on a Sanity Check: %v", "addRound() for an existing round"))
panic("addRound() for an existing round")
}
// log.Debug("addRound(round)", "round", round)
prevotes := NewVoteSet(hvs.chainID, hvs.height, round, VoteTypePrevote, hvs.valSet)
precommits := NewVoteSet(hvs.chainID, hvs.height, round, VoteTypePrecommit, hvs.valSet)
hvs.roundVoteSets[round] = RoundVoteSet{
......@@ -179,7 +178,7 @@ func (hvs *HeightVoteSet) getVoteSet(round int, voteType byte) *VoteSet {
case VoteTypePrecommit:
return rvs.Precommits
default:
panic(Fmt("Panicked on a Sanity Check: %v", Fmt("Unexpected vote type %X", voteType)))
panic(Fmt("Unexpected vote type %X", voteType))
}
}
......
......@@ -440,7 +440,7 @@ func (pv *PrivValidatorImp) SignHeartbeat(chainID string, heartbeat *Heartbeat)
// String returns a string representation of the PrivValidatorImp.
func (pv *PrivValidatorImp) String() string {
return Fmt("PrivValidator{%v LH:%v, LR:%v, LS:%v}", pv.GetAddress(), pv.LastHeight, pv.LastRound, pv.LastStep)
return Fmt("PrivValidator{%X LH:%v, LR:%v, LS:%v}", pv.GetAddress(), pv.LastHeight, pv.LastRound, pv.LastStep)
}
// GetLastHeight ...
......
......@@ -32,7 +32,6 @@ const (
RoundStepCommit = RoundStepType(0x08) // Entered commit state machine
// NOTE: RoundStepNewHeight acts as RoundStepCommitWait.
EvidenceListID = byte(0x01)
NewRoundStepID = byte(0x02)
CommitStepID = byte(0x03)
ProposalID = byte(0x04)
......@@ -43,6 +42,7 @@ const (
VoteSetBitsID = byte(0x09)
ProposalHeartbeatID = byte(0x0a)
ProposalBlockID = byte(0x0b)
ValidBlockID = byte(0x0c)
PacketTypePing = byte(0xff)
PacketTypePong = byte(0xfe)
......@@ -51,7 +51,6 @@ const (
// InitMessageMap ...
func InitMessageMap() {
MsgMap = map[byte]reflect.Type{
EvidenceListID: reflect.TypeOf(tmtypes.EvidenceData{}),
NewRoundStepID: reflect.TypeOf(tmtypes.NewRoundStepMsg{}),
CommitStepID: reflect.TypeOf(tmtypes.CommitStepMsg{}),
ProposalID: reflect.TypeOf(tmtypes.Proposal{}),
......@@ -62,6 +61,7 @@ func InitMessageMap() {
VoteSetBitsID: reflect.TypeOf(tmtypes.VoteSetBitsMsg{}),
ProposalHeartbeatID: reflect.TypeOf(tmtypes.Heartbeat{}),
ProposalBlockID: reflect.TypeOf(tmtypes.TendermintBlock{}),
ValidBlockID: reflect.TypeOf(tmtypes.ValidBlockMsg{}),
}
}
......@@ -108,6 +108,8 @@ type RoundState struct {
ProposalBlock *TendermintBlock
LockedRound int
LockedBlock *TendermintBlock
ValidRound int // Last known round with POL for non-nil valid block.
ValidBlock *TendermintBlock // Last known block of POL mentioned above.
Votes *HeightVoteSet
CommitRound int
LastCommit *VoteSet // Last precommits at Height-1
......@@ -141,6 +143,8 @@ func (rs *RoundState) StringIndented(indent string) string {
%s ProposalBlock: %v
%s LockedRound: %v
%s LockedBlock: %v
%s ValidRound: %v
%s ValidBlock: %v
%s Votes: %v
%s LastCommit: %v
%s LastValidators:%v
......@@ -153,6 +157,8 @@ func (rs *RoundState) StringIndented(indent string) string {
indent, rs.ProposalBlock.StringShort(),
indent, rs.LockedRound,
indent, rs.LockedBlock.StringShort(),
indent, rs.ValidRound,
indent, rs.ValidBlock.StringShort(),
indent, rs.Votes.StringIndented(indent+" "),
indent, rs.LastCommit.StringShort(),
indent, rs.LastValidators.StringIndented(indent+" "),
......@@ -173,14 +179,15 @@ type PeerRoundState struct {
StartTime time.Time // Estimated start of round 0 at this height
Proposal bool // True if peer has proposal for this round
ProposalBlock bool // True if peer has proposal block for this round
ProposalPOLRound int // Proposal's POL round. -1 if none.
ProposalPOL *BitArray // nil until ProposalPOLMessage received.
Prevotes *BitArray // All votes peer has for this round
Precommits *BitArray // All precommits peer has for this round
LastCommitRound int // Round of commit for last height. -1 if none.
LastCommit *BitArray // All commit precommits of commit for last height.
CatchupCommitRound int // Round that we have commit for. Not necessarily unique. -1 if none.
CatchupCommit *BitArray // All commit precommits peer has for this height & CatchupCommitRound
ProposalBlockHash []byte
ProposalPOLRound int // Proposal's POL round. -1 if none.
ProposalPOL *BitArray // nil until ProposalPOLMessage received.
Prevotes *BitArray // All votes peer has for this round
Precommits *BitArray // All precommits peer has for this round
LastCommitRound int // Round of commit for last height. -1 if none.
LastCommit *BitArray // All commit precommits of commit for last height.
CatchupCommitRound int // Round that we have commit for. Not necessarily unique. -1 if none.
CatchupCommit *BitArray // All commit precommits peer has for this height & CatchupCommitRound
}
// String returns a string representation of the PeerRoundState
......@@ -194,6 +201,7 @@ func (prs PeerRoundState) StringIndented(indent string) string {
%s %v/%v/%v @%v
%s Proposal %v
%s ProposalBlock %v
%s ProposalBlockHash %X
%s POL %v (round %v)
%s Prevotes %v
%s Precommits %v
......@@ -203,6 +211,7 @@ func (prs PeerRoundState) StringIndented(indent string) string {
indent, prs.Height, prs.Round, prs.Step, prs.StartTime,
indent, prs.Proposal,
indent, prs.ProposalBlock,
indent, prs.ProposalBlock,
indent, prs.ProposalPOL, prs.ProposalPOLRound,
indent, prs.Prevotes,
indent, prs.Precommits,
......
......@@ -25,6 +25,7 @@ var (
ErrVoteInvalidSignature = errors.New("Invalid signature")
ErrVoteInvalidBlockHash = errors.New("Invalid block hash")
ErrVoteNonDeterministicSignature = errors.New("Non-deterministic signature")
ErrVoteConflict = errors.New("Conflicting vote")
ErrVoteNil = errors.New("Nil vote")
votelog = log15.New("module", "tendermint-vote")
)
......@@ -70,7 +71,7 @@ func NewProposal(height int64, round int, blockhash []byte, polRound int, polBlo
// String returns a string representation of the Proposal.
func (p *Proposal) String() string {
return fmt.Sprintf("Proposal{%v/%v (%v,%v) %X %v @ %s}",
return fmt.Sprintf("Proposal{%v/%v (%v, %X) %X %X @ %s}",
p.Height, p.Round, p.POLRound, p.POLBlockID,
p.Blockhash, p.Signature, CanonicalTime(time.Unix(0, p.Timestamp)))
}
......@@ -119,34 +120,6 @@ func (heartbeat *Heartbeat) WriteSignBytes(chainID string, w io.Writer, n *int,
*err = writeErr
}
// ErrVoteConflictingVotes ...
type ErrVoteConflictingVotes struct {
*DuplicateVoteEvidence
}
func (err *ErrVoteConflictingVotes) Error() string {
pubkey, error := PubKeyFromString(err.PubKey)
if error != nil {
return fmt.Sprintf("Conflicting votes from validator PubKey:%v,error:%v", err.PubKey, error)
}
addr := GenAddressByPubKey(pubkey)
return fmt.Sprintf("Conflicting votes from validator %v", addr)
}
// NewConflictingVoteError ...
func NewConflictingVoteError(val *Validator, voteA, voteB *tmtypes.Vote) *ErrVoteConflictingVotes {
keyString := fmt.Sprintf("%X", val.PubKey)
return &ErrVoteConflictingVotes{
&DuplicateVoteEvidence{
&tmtypes.DuplicateVoteEvidence{
PubKey: keyString,
VoteA: voteA,
VoteB: voteB,
},
},
}
}
// Types of votes
// TODO Make a new type "VoteType"
const (
......@@ -211,7 +184,7 @@ func (vote *Vote) String() string {
PanicSanity("Unknown vote type")
}
return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %v @ %s}",
return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %X @ %s}",
vote.ValidatorIndex, Fingerprint(vote.ValidatorAddress),
vote.Height, vote.Round, vote.Type, typeString,
Fingerprint(vote.BlockID.Hash), vote.Signature,
......@@ -240,7 +213,6 @@ func (vote *Vote) Verify(chainID string, pubKey crypto.PubKey) error {
// Hash ...
func (vote *Vote) Hash() []byte {
if vote == nil {
//votelog.Error("vote hash is nil")
return nil
}
bytes, err := json.Marshal(vote)
......
......@@ -70,7 +70,7 @@ func (v *Validator) String() string {
if v == nil {
return "nil-Validator"
}
return Fmt("Validator{%v %v VP:%v A:%v}",
return Fmt("Validator{ADDR:%X PUB:%X VP:%v A:%v}",
v.Address,
v.PubKey,
v.VotingPower,
......
......@@ -200,7 +200,7 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) {
// Add vote and get conflicting vote if any
added, conflicting := voteSet.addVerifiedVote(vote, blockKey, val.VotingPower)
if conflicting != nil {
return added, NewConflictingVoteError(val, conflicting.Vote, vote.Vote)
return added, errors.Wrapf(ErrVoteConflict, "Conflicting vote: %v; New vote: %v", conflicting, vote)
}
if !added {
PanicSanity("Expected to add non-conflicting vote")
......
syntax = "proto3";
import "transaction.proto";
import "blockchain.proto";
package types;
......@@ -30,11 +30,9 @@ message TendermintCommit {
}
message TendermintBlockInfo {
TendermintCommit SeenCommit = 1;
TendermintCommit LastCommit = 2;
State State = 3;
Proposal Proposal = 4;
TendermintBlock block = 5;
State State = 2;
Proposal Proposal = 3;
TendermintBlock block = 4;
}
message BlockSize {
......@@ -89,20 +87,6 @@ message State {
bytes AppHash = 12;
}
message DuplicateVoteEvidence {
string pubKey = 1;
Vote voteA = 2;
Vote voteB = 3;
}
message EvidenceEnvelope {
string typeName = 1;
bytes data = 2;
}
message EvidenceData {
repeated EvidenceEnvelope evidence = 1;
}
message TendermintBlockHeader {
string chainID = 1;
......@@ -117,15 +101,13 @@ message TendermintBlockHeader {
bytes consensusHash = 10;
bytes appHash = 11;
bytes lastResultsHash = 12;
bytes evidenceHash = 13;
bytes proposerAddr = 13;
}
message TendermintBlock {
TendermintBlockHeader header = 1;
repeated Transaction txs = 2;
EvidenceData evidence = 3;
TendermintCommit lastCommit = 4;
bytes proposerAddr = 5;
TendermintBlockHeader header = 1;
Block data = 2;
TendermintCommit lastCommit = 4;
}
message Proposal {
......@@ -146,6 +128,13 @@ message NewRoundStepMsg {
int32 lastCommitRound = 5;
}
message ValidBlockMsg {
int64 height = 1;
int32 round = 2;
bytes blockhash = 3;
bool isCommit = 4;
}
message CommitStepMsg {
int64 height = 1;
}
......
......@@ -35,7 +35,7 @@ func (m *BlockID) Reset() { *m = BlockID{} }
func (m *BlockID) String() string { return proto.CompactTextString(m) }
func (*BlockID) ProtoMessage() {}
func (*BlockID) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{0}
return fileDescriptor_tendermint_df861948ed10449a, []int{0}
}
func (m *BlockID) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_BlockID.Unmarshal(m, b)
......@@ -74,7 +74,7 @@ func (m *TendermintBitArray) Reset() { *m = TendermintBitArray{} }
func (m *TendermintBitArray) String() string { return proto.CompactTextString(m) }
func (*TendermintBitArray) ProtoMessage() {}
func (*TendermintBitArray) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{1}
return fileDescriptor_tendermint_df861948ed10449a, []int{1}
}
func (m *TendermintBitArray) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_TendermintBitArray.Unmarshal(m, b)
......@@ -126,7 +126,7 @@ func (m *Vote) Reset() { *m = Vote{} }
func (m *Vote) String() string { return proto.CompactTextString(m) }
func (*Vote) ProtoMessage() {}
func (*Vote) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{2}
return fileDescriptor_tendermint_df861948ed10449a, []int{2}
}
func (m *Vote) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Vote.Unmarshal(m, b)
......@@ -214,7 +214,7 @@ func (m *TendermintCommit) Reset() { *m = TendermintCommit{} }
func (m *TendermintCommit) String() string { return proto.CompactTextString(m) }
func (*TendermintCommit) ProtoMessage() {}
func (*TendermintCommit) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{3}
return fileDescriptor_tendermint_df861948ed10449a, []int{3}
}
func (m *TendermintCommit) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_TendermintCommit.Unmarshal(m, b)
......@@ -249,21 +249,19 @@ func (m *TendermintCommit) GetPrecommits() []*Vote {
}
type TendermintBlockInfo struct {
SeenCommit *TendermintCommit `protobuf:"bytes,1,opt,name=SeenCommit,proto3" json:"SeenCommit,omitempty"`
LastCommit *TendermintCommit `protobuf:"bytes,2,opt,name=LastCommit,proto3" json:"LastCommit,omitempty"`
State *State `protobuf:"bytes,3,opt,name=State,proto3" json:"State,omitempty"`
Proposal *Proposal `protobuf:"bytes,4,opt,name=Proposal,proto3" json:"Proposal,omitempty"`
Block *TendermintBlock `protobuf:"bytes,5,opt,name=block,proto3" json:"block,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
State *State `protobuf:"bytes,2,opt,name=State,proto3" json:"State,omitempty"`
Proposal *Proposal `protobuf:"bytes,3,opt,name=Proposal,proto3" json:"Proposal,omitempty"`
Block *TendermintBlock `protobuf:"bytes,4,opt,name=block,proto3" json:"block,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *TendermintBlockInfo) Reset() { *m = TendermintBlockInfo{} }
func (m *TendermintBlockInfo) String() string { return proto.CompactTextString(m) }
func (*TendermintBlockInfo) ProtoMessage() {}
func (*TendermintBlockInfo) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{4}
return fileDescriptor_tendermint_df861948ed10449a, []int{4}
}
func (m *TendermintBlockInfo) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_TendermintBlockInfo.Unmarshal(m, b)
......@@ -283,20 +281,6 @@ func (m *TendermintBlockInfo) XXX_DiscardUnknown() {
var xxx_messageInfo_TendermintBlockInfo proto.InternalMessageInfo
func (m *TendermintBlockInfo) GetSeenCommit() *TendermintCommit {
if m != nil {
return m.SeenCommit
}
return nil
}
func (m *TendermintBlockInfo) GetLastCommit() *TendermintCommit {
if m != nil {
return m.LastCommit
}
return nil
}
func (m *TendermintBlockInfo) GetState() *State {
if m != nil {
return m.State
......@@ -331,7 +315,7 @@ func (m *BlockSize) Reset() { *m = BlockSize{} }
func (m *BlockSize) String() string { return proto.CompactTextString(m) }
func (*BlockSize) ProtoMessage() {}
func (*BlockSize) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{5}
return fileDescriptor_tendermint_df861948ed10449a, []int{5}
}
func (m *BlockSize) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_BlockSize.Unmarshal(m, b)
......@@ -384,7 +368,7 @@ func (m *TxSize) Reset() { *m = TxSize{} }
func (m *TxSize) String() string { return proto.CompactTextString(m) }
func (*TxSize) ProtoMessage() {}
func (*TxSize) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{6}
return fileDescriptor_tendermint_df861948ed10449a, []int{6}
}
func (m *TxSize) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_TxSize.Unmarshal(m, b)
......@@ -429,7 +413,7 @@ func (m *BlockGossip) Reset() { *m = BlockGossip{} }
func (m *BlockGossip) String() string { return proto.CompactTextString(m) }
func (*BlockGossip) ProtoMessage() {}
func (*BlockGossip) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{7}
return fileDescriptor_tendermint_df861948ed10449a, []int{7}
}
func (m *BlockGossip) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_BlockGossip.Unmarshal(m, b)
......@@ -467,7 +451,7 @@ func (m *EvidenceParams) Reset() { *m = EvidenceParams{} }
func (m *EvidenceParams) String() string { return proto.CompactTextString(m) }
func (*EvidenceParams) ProtoMessage() {}
func (*EvidenceParams) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{8}
return fileDescriptor_tendermint_df861948ed10449a, []int{8}
}
func (m *EvidenceParams) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_EvidenceParams.Unmarshal(m, b)
......@@ -508,7 +492,7 @@ func (m *ConsensusParams) Reset() { *m = ConsensusParams{} }
func (m *ConsensusParams) String() string { return proto.CompactTextString(m) }
func (*ConsensusParams) ProtoMessage() {}
func (*ConsensusParams) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{9}
return fileDescriptor_tendermint_df861948ed10449a, []int{9}
}
func (m *ConsensusParams) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ConsensusParams.Unmarshal(m, b)
......@@ -570,7 +554,7 @@ func (m *Validator) Reset() { *m = Validator{} }
func (m *Validator) String() string { return proto.CompactTextString(m) }
func (*Validator) ProtoMessage() {}
func (*Validator) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{10}
return fileDescriptor_tendermint_df861948ed10449a, []int{10}
}
func (m *Validator) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Validator.Unmarshal(m, b)
......@@ -630,7 +614,7 @@ func (m *ValidatorSet) Reset() { *m = ValidatorSet{} }
func (m *ValidatorSet) String() string { return proto.CompactTextString(m) }
func (*ValidatorSet) ProtoMessage() {}
func (*ValidatorSet) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{11}
return fileDescriptor_tendermint_df861948ed10449a, []int{11}
}
func (m *ValidatorSet) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ValidatorSet.Unmarshal(m, b)
......@@ -686,7 +670,7 @@ func (m *State) Reset() { *m = State{} }
func (m *State) String() string { return proto.CompactTextString(m) }
func (*State) ProtoMessage() {}
func (*State) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{12}
return fileDescriptor_tendermint_df861948ed10449a, []int{12}
}
func (m *State) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_State.Unmarshal(m, b)
......@@ -790,144 +774,6 @@ func (m *State) GetAppHash() []byte {
return nil
}
type DuplicateVoteEvidence struct {
PubKey string `protobuf:"bytes,1,opt,name=pubKey,proto3" json:"pubKey,omitempty"`
VoteA *Vote `protobuf:"bytes,2,opt,name=voteA,proto3" json:"voteA,omitempty"`
VoteB *Vote `protobuf:"bytes,3,opt,name=voteB,proto3" json:"voteB,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DuplicateVoteEvidence) Reset() { *m = DuplicateVoteEvidence{} }
func (m *DuplicateVoteEvidence) String() string { return proto.CompactTextString(m) }
func (*DuplicateVoteEvidence) ProtoMessage() {}
func (*DuplicateVoteEvidence) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{13}
}
func (m *DuplicateVoteEvidence) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DuplicateVoteEvidence.Unmarshal(m, b)
}
func (m *DuplicateVoteEvidence) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_DuplicateVoteEvidence.Marshal(b, m, deterministic)
}
func (dst *DuplicateVoteEvidence) XXX_Merge(src proto.Message) {
xxx_messageInfo_DuplicateVoteEvidence.Merge(dst, src)
}
func (m *DuplicateVoteEvidence) XXX_Size() int {
return xxx_messageInfo_DuplicateVoteEvidence.Size(m)
}
func (m *DuplicateVoteEvidence) XXX_DiscardUnknown() {
xxx_messageInfo_DuplicateVoteEvidence.DiscardUnknown(m)
}
var xxx_messageInfo_DuplicateVoteEvidence proto.InternalMessageInfo
func (m *DuplicateVoteEvidence) GetPubKey() string {
if m != nil {
return m.PubKey
}
return ""
}
func (m *DuplicateVoteEvidence) GetVoteA() *Vote {
if m != nil {
return m.VoteA
}
return nil
}
func (m *DuplicateVoteEvidence) GetVoteB() *Vote {
if m != nil {
return m.VoteB
}
return nil
}
type EvidenceEnvelope struct {
TypeName string `protobuf:"bytes,1,opt,name=typeName,proto3" json:"typeName,omitempty"`
Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *EvidenceEnvelope) Reset() { *m = EvidenceEnvelope{} }
func (m *EvidenceEnvelope) String() string { return proto.CompactTextString(m) }
func (*EvidenceEnvelope) ProtoMessage() {}
func (*EvidenceEnvelope) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{14}
}
func (m *EvidenceEnvelope) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_EvidenceEnvelope.Unmarshal(m, b)
}
func (m *EvidenceEnvelope) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_EvidenceEnvelope.Marshal(b, m, deterministic)
}
func (dst *EvidenceEnvelope) XXX_Merge(src proto.Message) {
xxx_messageInfo_EvidenceEnvelope.Merge(dst, src)
}
func (m *EvidenceEnvelope) XXX_Size() int {
return xxx_messageInfo_EvidenceEnvelope.Size(m)
}
func (m *EvidenceEnvelope) XXX_DiscardUnknown() {
xxx_messageInfo_EvidenceEnvelope.DiscardUnknown(m)
}
var xxx_messageInfo_EvidenceEnvelope proto.InternalMessageInfo
func (m *EvidenceEnvelope) GetTypeName() string {
if m != nil {
return m.TypeName
}
return ""
}
func (m *EvidenceEnvelope) GetData() []byte {
if m != nil {
return m.Data
}
return nil
}
type EvidenceData struct {
Evidence []*EvidenceEnvelope `protobuf:"bytes,1,rep,name=evidence,proto3" json:"evidence,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *EvidenceData) Reset() { *m = EvidenceData{} }
func (m *EvidenceData) String() string { return proto.CompactTextString(m) }
func (*EvidenceData) ProtoMessage() {}
func (*EvidenceData) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{15}
}
func (m *EvidenceData) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_EvidenceData.Unmarshal(m, b)
}
func (m *EvidenceData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_EvidenceData.Marshal(b, m, deterministic)
}
func (dst *EvidenceData) XXX_Merge(src proto.Message) {
xxx_messageInfo_EvidenceData.Merge(dst, src)
}
func (m *EvidenceData) XXX_Size() int {
return xxx_messageInfo_EvidenceData.Size(m)
}
func (m *EvidenceData) XXX_DiscardUnknown() {
xxx_messageInfo_EvidenceData.DiscardUnknown(m)
}
var xxx_messageInfo_EvidenceData proto.InternalMessageInfo
func (m *EvidenceData) GetEvidence() []*EvidenceEnvelope {
if m != nil {
return m.Evidence
}
return nil
}
type TendermintBlockHeader struct {
ChainID string `protobuf:"bytes,1,opt,name=chainID,proto3" json:"chainID,omitempty"`
Height int64 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"`
......@@ -941,7 +787,7 @@ type TendermintBlockHeader struct {
ConsensusHash []byte `protobuf:"bytes,10,opt,name=consensusHash,proto3" json:"consensusHash,omitempty"`
AppHash []byte `protobuf:"bytes,11,opt,name=appHash,proto3" json:"appHash,omitempty"`
LastResultsHash []byte `protobuf:"bytes,12,opt,name=lastResultsHash,proto3" json:"lastResultsHash,omitempty"`
EvidenceHash []byte `protobuf:"bytes,13,opt,name=evidenceHash,proto3" json:"evidenceHash,omitempty"`
ProposerAddr []byte `protobuf:"bytes,13,opt,name=proposerAddr,proto3" json:"proposerAddr,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
......@@ -951,7 +797,7 @@ func (m *TendermintBlockHeader) Reset() { *m = TendermintBlockHeader{} }
func (m *TendermintBlockHeader) String() string { return proto.CompactTextString(m) }
func (*TendermintBlockHeader) ProtoMessage() {}
func (*TendermintBlockHeader) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{16}
return fileDescriptor_tendermint_df861948ed10449a, []int{13}
}
func (m *TendermintBlockHeader) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_TendermintBlockHeader.Unmarshal(m, b)
......@@ -1055,19 +901,17 @@ func (m *TendermintBlockHeader) GetLastResultsHash() []byte {
return nil
}
func (m *TendermintBlockHeader) GetEvidenceHash() []byte {
func (m *TendermintBlockHeader) GetProposerAddr() []byte {
if m != nil {
return m.EvidenceHash
return m.ProposerAddr
}
return nil
}
type TendermintBlock struct {
Header *TendermintBlockHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
Txs []*types.Transaction `protobuf:"bytes,2,rep,name=txs,proto3" json:"txs,omitempty"`
Evidence *EvidenceData `protobuf:"bytes,3,opt,name=evidence,proto3" json:"evidence,omitempty"`
Data *types.Block `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
LastCommit *TendermintCommit `protobuf:"bytes,4,opt,name=lastCommit,proto3" json:"lastCommit,omitempty"`
ProposerAddr []byte `protobuf:"bytes,5,opt,name=proposerAddr,proto3" json:"proposerAddr,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
......@@ -1077,7 +921,7 @@ func (m *TendermintBlock) Reset() { *m = TendermintBlock{} }
func (m *TendermintBlock) String() string { return proto.CompactTextString(m) }
func (*TendermintBlock) ProtoMessage() {}
func (*TendermintBlock) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{17}
return fileDescriptor_tendermint_df861948ed10449a, []int{14}
}
func (m *TendermintBlock) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_TendermintBlock.Unmarshal(m, b)
......@@ -1104,16 +948,9 @@ func (m *TendermintBlock) GetHeader() *TendermintBlockHeader {
return nil
}
func (m *TendermintBlock) GetTxs() []*types.Transaction {
func (m *TendermintBlock) GetData() *types.Block {
if m != nil {
return m.Txs
}
return nil
}
func (m *TendermintBlock) GetEvidence() *EvidenceData {
if m != nil {
return m.Evidence
return m.Data
}
return nil
}
......@@ -1125,13 +962,6 @@ func (m *TendermintBlock) GetLastCommit() *TendermintCommit {
return nil
}
func (m *TendermintBlock) GetProposerAddr() []byte {
if m != nil {
return m.ProposerAddr
}
return nil
}
type Proposal struct {
Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"`
Round int32 `protobuf:"varint,2,opt,name=round,proto3" json:"round,omitempty"`
......@@ -1149,7 +979,7 @@ func (m *Proposal) Reset() { *m = Proposal{} }
func (m *Proposal) String() string { return proto.CompactTextString(m) }
func (*Proposal) ProtoMessage() {}
func (*Proposal) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{18}
return fileDescriptor_tendermint_df861948ed10449a, []int{15}
}
func (m *Proposal) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Proposal.Unmarshal(m, b)
......@@ -1233,7 +1063,7 @@ func (m *NewRoundStepMsg) Reset() { *m = NewRoundStepMsg{} }
func (m *NewRoundStepMsg) String() string { return proto.CompactTextString(m) }
func (*NewRoundStepMsg) ProtoMessage() {}
func (*NewRoundStepMsg) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{19}
return fileDescriptor_tendermint_df861948ed10449a, []int{16}
}
func (m *NewRoundStepMsg) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NewRoundStepMsg.Unmarshal(m, b)
......@@ -1288,6 +1118,68 @@ func (m *NewRoundStepMsg) GetLastCommitRound() int32 {
return 0
}
type ValidBlockMsg struct {
Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"`
Round int32 `protobuf:"varint,2,opt,name=round,proto3" json:"round,omitempty"`
Blockhash []byte `protobuf:"bytes,3,opt,name=blockhash,proto3" json:"blockhash,omitempty"`
IsCommit bool `protobuf:"varint,4,opt,name=isCommit,proto3" json:"isCommit,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ValidBlockMsg) Reset() { *m = ValidBlockMsg{} }
func (m *ValidBlockMsg) String() string { return proto.CompactTextString(m) }
func (*ValidBlockMsg) ProtoMessage() {}
func (*ValidBlockMsg) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_df861948ed10449a, []int{17}
}
func (m *ValidBlockMsg) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ValidBlockMsg.Unmarshal(m, b)
}
func (m *ValidBlockMsg) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ValidBlockMsg.Marshal(b, m, deterministic)
}
func (dst *ValidBlockMsg) XXX_Merge(src proto.Message) {
xxx_messageInfo_ValidBlockMsg.Merge(dst, src)
}
func (m *ValidBlockMsg) XXX_Size() int {
return xxx_messageInfo_ValidBlockMsg.Size(m)
}
func (m *ValidBlockMsg) XXX_DiscardUnknown() {
xxx_messageInfo_ValidBlockMsg.DiscardUnknown(m)
}
var xxx_messageInfo_ValidBlockMsg proto.InternalMessageInfo
func (m *ValidBlockMsg) GetHeight() int64 {
if m != nil {
return m.Height
}
return 0
}
func (m *ValidBlockMsg) GetRound() int32 {
if m != nil {
return m.Round
}
return 0
}
func (m *ValidBlockMsg) GetBlockhash() []byte {
if m != nil {
return m.Blockhash
}
return nil
}
func (m *ValidBlockMsg) GetIsCommit() bool {
if m != nil {
return m.IsCommit
}
return false
}
type CommitStepMsg struct {
Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
......@@ -1299,7 +1191,7 @@ func (m *CommitStepMsg) Reset() { *m = CommitStepMsg{} }
func (m *CommitStepMsg) String() string { return proto.CompactTextString(m) }
func (*CommitStepMsg) ProtoMessage() {}
func (*CommitStepMsg) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{20}
return fileDescriptor_tendermint_df861948ed10449a, []int{18}
}
func (m *CommitStepMsg) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CommitStepMsg.Unmarshal(m, b)
......@@ -1339,7 +1231,7 @@ func (m *ProposalPOLMsg) Reset() { *m = ProposalPOLMsg{} }
func (m *ProposalPOLMsg) String() string { return proto.CompactTextString(m) }
func (*ProposalPOLMsg) ProtoMessage() {}
func (*ProposalPOLMsg) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{21}
return fileDescriptor_tendermint_df861948ed10449a, []int{19}
}
func (m *ProposalPOLMsg) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ProposalPOLMsg.Unmarshal(m, b)
......@@ -1394,7 +1286,7 @@ func (m *HasVoteMsg) Reset() { *m = HasVoteMsg{} }
func (m *HasVoteMsg) String() string { return proto.CompactTextString(m) }
func (*HasVoteMsg) ProtoMessage() {}
func (*HasVoteMsg) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{22}
return fileDescriptor_tendermint_df861948ed10449a, []int{20}
}
func (m *HasVoteMsg) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_HasVoteMsg.Unmarshal(m, b)
......@@ -1456,7 +1348,7 @@ func (m *VoteSetMaj23Msg) Reset() { *m = VoteSetMaj23Msg{} }
func (m *VoteSetMaj23Msg) String() string { return proto.CompactTextString(m) }
func (*VoteSetMaj23Msg) ProtoMessage() {}
func (*VoteSetMaj23Msg) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{23}
return fileDescriptor_tendermint_df861948ed10449a, []int{21}
}
func (m *VoteSetMaj23Msg) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_VoteSetMaj23Msg.Unmarshal(m, b)
......@@ -1519,7 +1411,7 @@ func (m *VoteSetBitsMsg) Reset() { *m = VoteSetBitsMsg{} }
func (m *VoteSetBitsMsg) String() string { return proto.CompactTextString(m) }
func (*VoteSetBitsMsg) ProtoMessage() {}
func (*VoteSetBitsMsg) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{24}
return fileDescriptor_tendermint_df861948ed10449a, []int{22}
}
func (m *VoteSetBitsMsg) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_VoteSetBitsMsg.Unmarshal(m, b)
......@@ -1590,7 +1482,7 @@ func (m *Heartbeat) Reset() { *m = Heartbeat{} }
func (m *Heartbeat) String() string { return proto.CompactTextString(m) }
func (*Heartbeat) ProtoMessage() {}
func (*Heartbeat) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{25}
return fileDescriptor_tendermint_df861948ed10449a, []int{23}
}
func (m *Heartbeat) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Heartbeat.Unmarshal(m, b)
......@@ -1663,7 +1555,7 @@ func (m *IsHealthy) Reset() { *m = IsHealthy{} }
func (m *IsHealthy) String() string { return proto.CompactTextString(m) }
func (*IsHealthy) ProtoMessage() {}
func (*IsHealthy) Descriptor() ([]byte, []int) {
return fileDescriptor_tendermint_73641ebf19da43cb, []int{26}
return fileDescriptor_tendermint_df861948ed10449a, []int{24}
}
func (m *IsHealthy) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_IsHealthy.Unmarshal(m, b)
......@@ -1704,13 +1596,11 @@ func init() {
proto.RegisterType((*Validator)(nil), "types.Validator")
proto.RegisterType((*ValidatorSet)(nil), "types.ValidatorSet")
proto.RegisterType((*State)(nil), "types.State")
proto.RegisterType((*DuplicateVoteEvidence)(nil), "types.DuplicateVoteEvidence")
proto.RegisterType((*EvidenceEnvelope)(nil), "types.EvidenceEnvelope")
proto.RegisterType((*EvidenceData)(nil), "types.EvidenceData")
proto.RegisterType((*TendermintBlockHeader)(nil), "types.TendermintBlockHeader")
proto.RegisterType((*TendermintBlock)(nil), "types.TendermintBlock")
proto.RegisterType((*Proposal)(nil), "types.Proposal")
proto.RegisterType((*NewRoundStepMsg)(nil), "types.NewRoundStepMsg")
proto.RegisterType((*ValidBlockMsg)(nil), "types.ValidBlockMsg")
proto.RegisterType((*CommitStepMsg)(nil), "types.CommitStepMsg")
proto.RegisterType((*ProposalPOLMsg)(nil), "types.ProposalPOLMsg")
proto.RegisterType((*HasVoteMsg)(nil), "types.HasVoteMsg")
......@@ -1720,100 +1610,92 @@ func init() {
proto.RegisterType((*IsHealthy)(nil), "types.IsHealthy")
}
func init() { proto.RegisterFile("tendermint.proto", fileDescriptor_tendermint_73641ebf19da43cb) }
var fileDescriptor_tendermint_73641ebf19da43cb = []byte{
// 1472 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0x4f, 0x73, 0x1b, 0xc5,
0x12, 0xaf, 0xb5, 0x2c, 0xd9, 0x6a, 0xc9, 0x7f, 0xde, 0xe4, 0x39, 0xd1, 0xcb, 0xf3, 0xab, 0xd2,
0x9b, 0x0a, 0x20, 0x92, 0x94, 0x93, 0xb2, 0x53, 0xc5, 0x21, 0x84, 0x8a, 0x65, 0xa7, 0x62, 0x83,
0x9d, 0xa8, 0x46, 0xaa, 0x70, 0x1e, 0x4b, 0x83, 0xb4, 0x20, 0xed, 0x2e, 0x3b, 0x23, 0x45, 0xa6,
0x8a, 0x0b, 0x07, 0xee, 0x7c, 0x10, 0x4e, 0x1c, 0xf8, 0x08, 0x7c, 0x02, 0x8e, 0xf0, 0x45, 0xb8,
0x50, 0xd3, 0x33, 0xb3, 0xff, 0xa4, 0x28, 0x40, 0x51, 0xdc, 0xd4, 0x3d, 0xbf, 0x99, 0xde, 0xee,
0xfe, 0x75, 0x4f, 0x8f, 0x60, 0x57, 0x89, 0x60, 0x20, 0xe2, 0x89, 0x1f, 0xa8, 0x83, 0x28, 0x0e,
0x55, 0x48, 0xca, 0xea, 0x3a, 0x12, 0xf2, 0xf6, 0xbf, 0x54, 0xcc, 0x03, 0xc9, 0xfb, 0xca, 0x0f,
0x03, 0xb3, 0x42, 0xff, 0x07, 0x1b, 0xed, 0x71, 0xd8, 0xff, 0xe2, 0xfc, 0x94, 0x10, 0x58, 0x3f,
0xe3, 0x72, 0xd4, 0xf0, 0x9a, 0x5e, 0xab, 0xce, 0xf0, 0x37, 0xfd, 0x08, 0x48, 0x2f, 0x39, 0xac,
0xed, 0xab, 0xe3, 0x38, 0xe6, 0xd7, 0x1a, 0xd9, 0xf6, 0x95, 0x44, 0x64, 0x99, 0xe1, 0x6f, 0xf2,
0x6f, 0x28, 0x3f, 0x1b, 0x8b, 0x89, 0x6c, 0xac, 0x35, 0x4b, 0xad, 0x75, 0x66, 0x04, 0xfa, 0xcd,
0x1a, 0xac, 0xbf, 0x0a, 0x95, 0x20, 0x77, 0x61, 0xf7, 0x15, 0x1f, 0xfb, 0x03, 0xae, 0xc2, 0xf8,
0x78, 0x30, 0x88, 0x85, 0x94, 0xd6, 0xd0, 0x82, 0x9e, 0xbc, 0x0b, 0xdb, 0x89, 0xee, 0x3c, 0x18,
0x88, 0x79, 0x63, 0x0d, 0x0d, 0x15, 0xb4, 0xe4, 0x26, 0x54, 0xce, 0x84, 0x3f, 0x1c, 0xa9, 0x46,
0xa9, 0xe9, 0xb5, 0x4a, 0xcc, 0x4a, 0xfa, 0x53, 0x58, 0x38, 0x0d, 0x06, 0x8d, 0x75, 0xdc, 0x66,
0x04, 0xb2, 0x0f, 0xd5, 0x9e, 0x3f, 0x11, 0x52, 0xf1, 0x49, 0xd4, 0x28, 0xe3, 0x86, 0x54, 0xa1,
0x5d, 0xea, 0x5d, 0x47, 0xa2, 0x51, 0x69, 0x7a, 0xad, 0x2d, 0x86, 0xbf, 0x49, 0x2b, 0x89, 0x4d,
0x63, 0xa3, 0xe9, 0xb5, 0x6a, 0x87, 0xdb, 0x07, 0x18, 0xc7, 0x03, 0xab, 0x65, 0x49, 0xe8, 0xf6,
0xa1, 0xda, 0xf5, 0x87, 0x01, 0x57, 0xd3, 0x58, 0x34, 0x36, 0xd1, 0xad, 0x54, 0x41, 0x7d, 0xd8,
0x4d, 0x83, 0x78, 0x12, 0x4e, 0x26, 0xbe, 0xca, 0x9e, 0xed, 0xad, 0x3e, 0xfb, 0x1e, 0x40, 0x27,
0x16, 0x7d, 0xdc, 0x66, 0xa2, 0x5b, 0x3b, 0xac, 0x59, 0xb0, 0x0e, 0x2d, 0xcb, 0x2c, 0xd3, 0x6f,
0xd7, 0xe0, 0x46, 0x26, 0x61, 0x78, 0x44, 0xf0, 0x59, 0x48, 0x3e, 0x00, 0xe8, 0x0a, 0x11, 0x18,
0xe3, 0xd6, 0xe2, 0x2d, 0x7b, 0x48, 0xf1, 0xdb, 0x58, 0x06, 0xaa, 0x37, 0x5e, 0x70, 0x69, 0x57,
0x30, 0x0f, 0xab, 0x36, 0xa6, 0x50, 0x42, 0xa1, 0xdc, 0x55, 0x5c, 0x09, 0xcc, 0x4d, 0xed, 0xb0,
0x6e, 0xf7, 0xa0, 0x8e, 0x99, 0x25, 0x72, 0x0f, 0x36, 0x3b, 0x71, 0x18, 0x85, 0x92, 0x8f, 0x31,
0x57, 0xb5, 0xc3, 0x1d, 0x0b, 0x73, 0x6a, 0x96, 0x00, 0xc8, 0x7d, 0x28, 0x5f, 0x69, 0x7f, 0x30,
0x77, 0xb5, 0xc3, 0x9b, 0x0b, 0x1f, 0x81, 0xde, 0x32, 0x03, 0xa2, 0x9f, 0x42, 0x15, 0xe5, 0xae,
0xff, 0x95, 0x20, 0xb7, 0x61, 0xf3, 0x92, 0xcf, 0xdb, 0xd7, 0x4a, 0x38, 0xce, 0x26, 0xb2, 0x26,
0xd1, 0x25, 0x9f, 0xf7, 0xe6, 0xd2, 0x92, 0xcc, 0x4a, 0x56, 0xff, 0x9c, 0x4b, 0x47, 0x2e, 0x23,
0xd1, 0x0f, 0xa1, 0xd2, 0x9b, 0xff, 0xc1, 0x53, 0xf5, 0xee, 0xb5, 0xdc, 0xee, 0x27, 0x50, 0xc3,
0xcf, 0x7a, 0x1e, 0x4a, 0xe9, 0x47, 0xe4, 0x00, 0x08, 0x8a, 0x1d, 0x1e, 0x2b, 0x7d, 0x66, 0xf6,
0xb0, 0x25, 0x2b, 0xb4, 0x05, 0xdb, 0xcf, 0x66, 0xfe, 0x40, 0x04, 0x7d, 0xd1, 0xe1, 0x31, 0x9f,
0x38, 0x43, 0xc7, 0x43, 0x81, 0xbb, 0x8c, 0xa1, 0xe3, 0xa1, 0xa0, 0xbf, 0x78, 0xb0, 0x73, 0x12,
0x06, 0x52, 0x04, 0x72, 0x2a, 0x2d, 0xf6, 0x20, 0x13, 0x13, 0xcb, 0x81, 0xdd, 0x2c, 0xeb, 0xb4,
0x9e, 0x65, 0xc2, 0xf6, 0x8e, 0x73, 0xd5, 0xe6, 0x7d, 0xcb, 0x85, 0x1c, 0x95, 0xcc, 0xc5, 0xe1,
0x51, 0xce, 0x27, 0x9b, 0x6f, 0x92, 0x3d, 0xd8, 0xac, 0xb0, 0x9c, 0xeb, 0x4f, 0x8a, 0xae, 0x58,
0x06, 0xec, 0xd9, 0x8d, 0xf9, 0x45, 0x56, 0x00, 0xd3, 0x29, 0x54, 0x93, 0x6e, 0x40, 0x1a, 0xb0,
0x91, 0xef, 0x29, 0x4e, 0xd4, 0xe1, 0xe9, 0x4c, 0xaf, 0x3e, 0x11, 0xd7, 0xe8, 0x42, 0x9d, 0x59,
0x89, 0x34, 0xa1, 0xf6, 0x2a, 0x54, 0x7e, 0x30, 0xec, 0x84, 0xaf, 0x45, 0x6c, 0x53, 0x9c, 0x55,
0xe9, 0x26, 0x72, 0xdc, 0xef, 0x4f, 0x27, 0xf8, 0x59, 0x25, 0x66, 0x04, 0x1a, 0x40, 0x3d, 0x31,
0xdb, 0x15, 0x8a, 0x3c, 0x04, 0x48, 0x64, 0x6d, 0xbc, 0x94, 0x89, 0x69, 0xb2, 0xc0, 0x32, 0x18,
0x72, 0xdf, 0x71, 0x5e, 0xc4, 0x36, 0xac, 0x8b, 0xf8, 0x04, 0x41, 0x7f, 0x5e, 0xb7, 0x65, 0xa4,
0x7d, 0x3c, 0x19, 0x71, 0x3f, 0xb0, 0x0d, 0xa3, 0xca, 0x9c, 0x48, 0x5a, 0xb0, 0xa3, 0xeb, 0x0e,
0x83, 0x6b, 0xfb, 0xa1, 0x21, 0x5d, 0x51, 0xad, 0x9b, 0x70, 0xa2, 0xea, 0x85, 0x8a, 0x8f, 0x7b,
0x73, 0xeb, 0xfa, 0x82, 0x9e, 0x3c, 0x84, 0x5a, 0xa2, 0x3b, 0x3f, 0xb5, 0xc9, 0x29, 0x36, 0xa9,
0x2c, 0x84, 0xdc, 0x81, 0xad, 0xf4, 0x14, 0x7f, 0x22, 0x6c, 0x93, 0xcd, 0x2b, 0xc9, 0x51, 0x2e,
0x62, 0x15, 0x3c, 0xf6, 0x46, 0x31, 0x02, 0x5d, 0xa1, 0x72, 0x41, 0x7b, 0x0c, 0xdb, 0xfa, 0x94,
0xcc, 0xc6, 0x8d, 0x37, 0x6f, 0x2c, 0x40, 0xc9, 0x53, 0xf8, 0xaf, 0xd6, 0x98, 0x18, 0xa4, 0xfa,
0x93, 0x11, 0x0f, 0x86, 0x62, 0x80, 0xed, 0xba, 0xc4, 0x56, 0x41, 0xc8, 0xd3, 0x85, 0x5a, 0x6a,
0x54, 0x73, 0x4d, 0xa8, 0xb0, 0xca, 0x16, 0x4a, 0xef, 0x63, 0x68, 0xa6, 0x06, 0x0a, 0x8b, 0xee,
0x43, 0x00, 0x3f, 0xe4, 0xad, 0x38, 0x97, 0x6f, 0x26, 0xe4, 0x74, 0xac, 0x24, 0x5e, 0xd9, 0x35,
0x24, 0x77, 0x51, 0x8d, 0x75, 0x11, 0x45, 0x88, 0xa8, 0xdb, 0xba, 0x30, 0x22, 0x9d, 0xc2, 0xde,
0xe9, 0x34, 0x1a, 0xfb, 0x7d, 0xae, 0x84, 0xbe, 0x44, 0x5c, 0x75, 0xe9, 0x82, 0x89, 0x4c, 0xc1,
0x18, 0x96, 0x59, 0x89, 0xfc, 0x1f, 0xca, 0xb3, 0x50, 0x89, 0x63, 0xcb, 0xd9, 0xdc, 0x05, 0x64,
0x56, 0x1c, 0xa4, 0x6d, 0x3b, 0xc0, 0x22, 0xa4, 0x4d, 0xdb, 0xb0, 0xeb, 0x2c, 0x3d, 0x0b, 0x66,
0x62, 0x1c, 0x46, 0xd8, 0x46, 0x35, 0xf0, 0x05, 0x9f, 0x08, 0x6b, 0x33, 0x91, 0xf5, 0xad, 0x3c,
0xe0, 0x8a, 0xdb, 0xe2, 0xc5, 0xdf, 0xf4, 0x04, 0xea, 0xee, 0x8c, 0x53, 0xae, 0x38, 0x39, 0x82,
0x4d, 0x61, 0x65, 0x5b, 0x80, 0xb7, 0x0a, 0x2d, 0xc4, 0x99, 0x62, 0x09, 0x90, 0xfe, 0x58, 0x82,
0xbd, 0xc2, 0xcd, 0x71, 0x26, 0xf8, 0x40, 0x60, 0x2f, 0xe9, 0xe7, 0xeb, 0xcc, 0x8a, 0x3a, 0x34,
0xa3, 0x6c, 0x79, 0x59, 0x49, 0x77, 0x8a, 0x18, 0xc7, 0x0d, 0x53, 0x4a, 0x46, 0xd0, 0x9f, 0xae,
0x74, 0x11, 0x98, 0xf6, 0x81, 0xbf, 0xf5, 0x09, 0xc1, 0x74, 0xa2, 0xef, 0x1a, 0x53, 0x1a, 0x56,
0xd2, 0xb5, 0x36, 0xce, 0xd4, 0x5a, 0x65, 0x79, 0xad, 0x65, 0x20, 0x18, 0x34, 0x53, 0xa8, 0xa6,
0x14, 0x4a, 0x2c, 0x91, 0xf5, 0xf8, 0x34, 0x4e, 0xee, 0x61, 0x4c, 0xbe, 0x99, 0x48, 0x0a, 0x5a,
0x8d, 0x9b, 0x25, 0x54, 0x47, 0x5c, 0xd5, 0xe0, 0xf2, 0x5a, 0x5d, 0xd7, 0x7d, 0xc7, 0x44, 0x84,
0x01, 0xc2, 0xf2, 0x4a, 0x1d, 0x37, 0x6e, 0xb9, 0x66, 0xd8, 0xe8, 0x44, 0xcd, 0xd7, 0x71, 0x81,
0xaf, 0x86, 0x8d, 0x45, 0x35, 0xa1, 0x50, 0x77, 0x19, 0x42, 0xd8, 0x16, 0xc2, 0x72, 0x3a, 0xfa,
0x9b, 0x07, 0x3b, 0x85, 0xcc, 0x91, 0x47, 0x3a, 0x33, 0x3a, 0x7b, 0xf6, 0x56, 0xdb, 0x5f, 0x3e,
0x1b, 0x98, 0x0c, 0x33, 0x8b, 0x25, 0x77, 0xa0, 0xa4, 0xe6, 0x6e, 0xa2, 0x72, 0xf7, 0x55, 0x2f,
0x9d, 0x90, 0x99, 0x5e, 0x26, 0x0f, 0x32, 0xf4, 0x2a, 0xe5, 0x9a, 0x4e, 0x96, 0x85, 0x29, 0xb5,
0xf4, 0xc4, 0x94, 0x06, 0xda, 0xf6, 0xcd, 0x37, 0x4f, 0x4c, 0xe3, 0xec, 0xc4, 0x54, 0x8f, 0x6c,
0xdf, 0xd7, 0xd7, 0x17, 0x72, 0xa4, 0xce, 0x72, 0x3a, 0xfa, 0xab, 0x97, 0x8e, 0x4c, 0x19, 0x42,
0x7a, 0xcb, 0x09, 0x69, 0x26, 0x1a, 0x4b, 0xc8, 0x7d, 0xa8, 0xaa, 0x64, 0xfe, 0x35, 0x54, 0x4d,
0x15, 0x9a, 0x50, 0x9d, 0x97, 0x17, 0xd9, 0xb1, 0x39, 0x91, 0xc9, 0x01, 0x40, 0xe7, 0xe5, 0x85,
0x63, 0x67, 0x79, 0x29, 0x3b, 0x33, 0x08, 0x6d, 0x49, 0x26, 0xd3, 0x70, 0xc5, 0x4c, 0xc3, 0x89,
0x42, 0xaf, 0xe2, 0x88, 0x36, 0xd2, 0x19, 0xde, 0x30, 0xab, 0x89, 0x82, 0xfe, 0xe0, 0xc1, 0xce,
0x0b, 0xf1, 0x1a, 0x0d, 0x77, 0x95, 0x88, 0x2e, 0xe5, 0xf0, 0x4f, 0xfa, 0x49, 0x60, 0x5d, 0x2a,
0x61, 0x5c, 0x2c, 0x33, 0xfc, 0x4d, 0x1e, 0xc1, 0x9e, 0x14, 0xfd, 0x30, 0x18, 0xc8, 0xae, 0x1f,
0xf4, 0x45, 0x57, 0xf1, 0x58, 0xf5, 0x5c, 0x75, 0x96, 0xd9, 0xf2, 0x45, 0x47, 0x5c, 0x9b, 0x2a,
0xb4, 0x54, 0x46, 0x7c, 0x51, 0x4d, 0xdf, 0x83, 0x2d, 0x23, 0xbe, 0xe5, 0x93, 0xe9, 0x77, 0x1e,
0x6c, 0xbb, 0xfc, 0x75, 0x5e, 0x5e, 0xac, 0xf2, 0xee, 0x2e, 0xec, 0x46, 0x29, 0x92, 0x65, 0x1c,
0x5d, 0xd0, 0x93, 0xc7, 0x50, 0xcb, 0xe8, 0x2c, 0x4f, 0xff, 0xb3, 0x58, 0x05, 0xf6, 0x01, 0xc7,
0xb2, 0x68, 0x3a, 0x00, 0x38, 0xe3, 0x52, 0xb7, 0xe9, 0xbf, 0x14, 0x6c, 0x6d, 0xc4, 0x05, 0x5b,
0xff, 0xd6, 0x48, 0x1f, 0x5f, 0x6d, 0xf6, 0xf9, 0x85, 0x02, 0xfd, 0x1a, 0x76, 0xb4, 0x89, 0xae,
0x50, 0x97, 0xfc, 0xf3, 0xc3, 0xa3, 0xbf, 0xc7, 0x54, 0x0b, 0x36, 0xae, 0x56, 0x0e, 0x28, 0x6e,
0x99, 0x7e, 0xef, 0xc1, 0xb6, 0xb5, 0xaf, 0x9f, 0xab, 0xff, 0xb0, 0x79, 0xf2, 0xc0, 0xdc, 0x8d,
0xd2, 0x56, 0xcf, 0x8a, 0xd4, 0x18, 0x1c, 0xfd, 0xc9, 0x83, 0xea, 0x99, 0xe0, 0xb1, 0xba, 0x12,
0x1c, 0xb9, 0x30, 0x7b, 0xc3, 0xeb, 0x79, 0xb6, 0xe4, 0xf5, 0x3c, 0x5b, 0xfa, 0x7a, 0x9e, 0x2d,
0xbc, 0x9e, 0x47, 0xb9, 0xd7, 0x73, 0xd1, 0xfd, 0xf5, 0xac, 0xfb, 0xb7, 0x61, 0x53, 0x8a, 0x2f,
0xa7, 0xd8, 0x06, 0x4d, 0x11, 0x24, 0xf2, 0xea, 0x7a, 0xa7, 0xef, 0x43, 0xf5, 0x5c, 0x9e, 0x09,
0x3e, 0x56, 0xa3, 0x6b, 0x0d, 0xf5, 0x9d, 0x80, 0x1e, 0x6c, 0xb2, 0x54, 0x71, 0x55, 0xc1, 0xff,
0x24, 0x8e, 0x7e, 0x0f, 0x00, 0x00, 0xff, 0xff, 0x8e, 0x20, 0x41, 0x55, 0xc1, 0x10, 0x00, 0x00,
func init() { proto.RegisterFile("tendermint.proto", fileDescriptor_tendermint_df861948ed10449a) }
var fileDescriptor_tendermint_df861948ed10449a = []byte{
// 1339 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0xdd, 0x6e, 0x1b, 0x45,
0x14, 0xd6, 0xc6, 0x3f, 0x89, 0x8f, 0xf3, 0xa7, 0x29, 0x2d, 0xa6, 0x04, 0xc9, 0x1a, 0xf1, 0x63,
0xda, 0x2a, 0x54, 0x69, 0x25, 0x2e, 0x4a, 0x51, 0x93, 0xb4, 0x6a, 0x02, 0x09, 0xb5, 0xc6, 0x56,
0xb9, 0x9e, 0xd8, 0x83, 0xbd, 0x60, 0xef, 0x9a, 0x9d, 0xb1, 0x1b, 0x23, 0x71, 0xc3, 0x1b, 0x20,
0xf1, 0x04, 0xdc, 0x73, 0xc5, 0x05, 0x8f, 0xc0, 0x13, 0x70, 0x09, 0xcf, 0x82, 0xce, 0x99, 0xd9,
0xf5, 0xec, 0xda, 0x4d, 0x29, 0x42, 0xdc, 0xed, 0xf9, 0xce, 0x37, 0x73, 0xf6, 0xfc, 0xce, 0x0c,
0xec, 0x1a, 0x15, 0xf5, 0x55, 0x32, 0x0e, 0x23, 0xb3, 0x3f, 0x49, 0x62, 0x13, 0xb3, 0x8a, 0x99,
0x4f, 0x94, 0xbe, 0xb9, 0x7b, 0x31, 0x8a, 0x7b, 0xdf, 0xf4, 0x86, 0x32, 0x8c, 0xac, 0x82, 0xbf,
0x03, 0xeb, 0x47, 0x88, 0x9d, 0x3e, 0x66, 0x0c, 0xca, 0x27, 0x52, 0x0f, 0x1b, 0x41, 0x33, 0x68,
0x6d, 0x0a, 0xfa, 0xe6, 0x9f, 0x02, 0xeb, 0x66, 0x7b, 0x1d, 0x85, 0xe6, 0x30, 0x49, 0xe4, 0x1c,
0x99, 0x47, 0xa1, 0xd1, 0xc4, 0xac, 0x08, 0xfa, 0x66, 0x6f, 0x40, 0xe5, 0xc9, 0x48, 0x8d, 0x75,
0x63, 0xad, 0x59, 0x6a, 0x95, 0x85, 0x15, 0xf8, 0x0f, 0x6b, 0x50, 0x7e, 0x1e, 0x1b, 0xc5, 0x6e,
0xc1, 0xee, 0x73, 0x39, 0x0a, 0xfb, 0xd2, 0xc4, 0xc9, 0x61, 0xbf, 0x9f, 0x28, 0xad, 0x9d, 0xa1,
0x25, 0x9c, 0xbd, 0x0f, 0xdb, 0x19, 0x76, 0x1a, 0xf5, 0xd5, 0x65, 0x63, 0x8d, 0x0c, 0x15, 0x50,
0x76, 0x03, 0xaa, 0x27, 0x2a, 0x1c, 0x0c, 0x4d, 0xa3, 0xd4, 0x0c, 0x5a, 0x25, 0xe1, 0x24, 0xfc,
0x15, 0x11, 0x4f, 0xa3, 0x7e, 0xa3, 0x4c, 0xcb, 0xac, 0xc0, 0xf6, 0xa0, 0xd6, 0x0d, 0xc7, 0x4a,
0x1b, 0x39, 0x9e, 0x34, 0x2a, 0xb4, 0x60, 0x01, 0xa0, 0x4b, 0xdd, 0xf9, 0x44, 0x35, 0xaa, 0xcd,
0xa0, 0xb5, 0x25, 0xe8, 0x9b, 0xb5, 0xb2, 0xd8, 0x34, 0xd6, 0x9b, 0x41, 0xab, 0x7e, 0xb0, 0xbd,
0x4f, 0x61, 0xdc, 0x77, 0xa8, 0xc8, 0x42, 0xb7, 0x07, 0xb5, 0x4e, 0x38, 0x88, 0xa4, 0x99, 0x26,
0xaa, 0xb1, 0x41, 0x6e, 0x2d, 0x00, 0x1e, 0xc2, 0xee, 0x22, 0x88, 0xc7, 0xf1, 0x78, 0x1c, 0x1a,
0x7f, 0xef, 0xe0, 0xea, 0xbd, 0x6f, 0x03, 0xb4, 0x13, 0xd5, 0xa3, 0x65, 0x36, 0xba, 0xf5, 0x83,
0xba, 0x23, 0x63, 0x68, 0x85, 0xa7, 0xe6, 0x3f, 0x05, 0x70, 0xcd, 0x4b, 0x18, 0x6d, 0x11, 0x7d,
0x15, 0x33, 0x0e, 0x95, 0x8e, 0x91, 0x46, 0x51, 0x24, 0xeb, 0x07, 0x9b, 0x6e, 0x3d, 0x61, 0xc2,
0xaa, 0xd8, 0x6d, 0xd8, 0x68, 0x27, 0xf1, 0x24, 0xd6, 0x72, 0x44, 0x01, 0xad, 0x1f, 0xec, 0x38,
0x5a, 0x0a, 0x8b, 0x8c, 0xc0, 0xee, 0x40, 0x85, 0x6a, 0x89, 0x62, 0x5c, 0x3f, 0xb8, 0xe1, 0x98,
0x05, 0xdb, 0xc2, 0x92, 0xf8, 0x97, 0x50, 0x23, 0xb9, 0x13, 0x7e, 0xa7, 0xd8, 0x4d, 0xd8, 0x38,
0x97, 0x97, 0x47, 0x73, 0xa3, 0xd2, 0x0a, 0xca, 0x64, 0x4c, 0xe9, 0xb9, 0xbc, 0xec, 0x5e, 0x6a,
0x97, 0x72, 0x27, 0x39, 0xfc, 0xa9, 0xd4, 0x69, 0xaa, 0xad, 0xc4, 0x3f, 0x81, 0x6a, 0xf7, 0xf2,
0x1f, 0xee, 0x8a, 0xab, 0xd7, 0x72, 0xab, 0x1f, 0x42, 0x9d, 0x7e, 0xeb, 0x69, 0xac, 0x75, 0x38,
0x61, 0xfb, 0xc0, 0x48, 0x6c, 0xcb, 0xc4, 0xe0, 0x9e, 0xfe, 0x66, 0x2b, 0x34, 0xbc, 0x05, 0xdb,
0x4f, 0x66, 0x61, 0x5f, 0x45, 0x3d, 0xd5, 0x96, 0x89, 0x1c, 0xa7, 0x86, 0x0e, 0x07, 0x8a, 0x56,
0x59, 0x43, 0x87, 0x03, 0xc5, 0xff, 0x0c, 0x60, 0xe7, 0x38, 0x8e, 0xb4, 0x8a, 0xf4, 0x54, 0x3b,
0xee, 0xbe, 0x17, 0x13, 0x57, 0x03, 0xbb, 0x7e, 0x0d, 0x20, 0x2e, 0xbc, 0xb0, 0xbd, 0x97, 0xba,
0xea, 0x72, 0xb8, 0x95, 0x86, 0x9c, 0x40, 0x91, 0xc6, 0xe1, 0x7e, 0xce, 0x27, 0x97, 0x48, 0xe6,
0x6f, 0x6c, 0x35, 0x22, 0xe7, 0xfa, 0xc3, 0xa2, 0x2b, 0x2e, 0xaf, 0xd7, 0xdd, 0xc2, 0xbc, 0x52,
0x14, 0xc8, 0x7c, 0x0a, 0xb5, 0xac, 0x37, 0x59, 0x03, 0xd6, 0xf3, 0x1d, 0x9e, 0x8a, 0x18, 0x9e,
0xf6, 0xf4, 0xe2, 0x73, 0x35, 0x27, 0x17, 0x36, 0x85, 0x93, 0x58, 0x13, 0xea, 0xcf, 0x63, 0x13,
0x46, 0x83, 0x76, 0xfc, 0x42, 0x25, 0x2e, 0xc5, 0x3e, 0x84, 0x2d, 0x7d, 0xd8, 0xeb, 0x4d, 0xc7,
0xf4, 0x5b, 0x25, 0x61, 0x05, 0x1e, 0xc1, 0x66, 0x66, 0xb6, 0xa3, 0x0c, 0xbb, 0x0b, 0x90, 0xc9,
0x68, 0xbc, 0xe4, 0xc5, 0x34, 0x53, 0x08, 0x8f, 0xc3, 0xee, 0xa4, 0x35, 0xaf, 0x12, 0x17, 0xd6,
0x65, 0x7e, 0xc6, 0xe0, 0x7f, 0x94, 0x5d, 0x1b, 0xa1, 0x8f, 0xc7, 0x38, 0x45, 0x5d, 0xfb, 0xd6,
0x44, 0x2a, 0xb2, 0x16, 0xec, 0x9c, 0x49, 0x6d, 0xcb, 0xdf, 0x4d, 0x27, 0x5b, 0x74, 0x45, 0x18,
0x47, 0x62, 0x06, 0x75, 0x63, 0x23, 0x47, 0xdd, 0x4b, 0xe7, 0xfa, 0x12, 0xce, 0xee, 0x42, 0x3d,
0xc3, 0x4e, 0x1f, 0xbb, 0xe4, 0x14, 0x47, 0x86, 0x4f, 0x61, 0xef, 0xc2, 0xd6, 0x62, 0x97, 0x70,
0xac, 0xdc, 0xc8, 0xcb, 0x83, 0xec, 0x5e, 0x2e, 0x62, 0x55, 0xda, 0xf6, 0x5a, 0x31, 0x02, 0x1d,
0x65, 0x72, 0x41, 0x7b, 0x00, 0xdb, 0xb8, 0x8b, 0xb7, 0x70, 0xfd, 0xe5, 0x0b, 0x0b, 0x54, 0xf6,
0x08, 0xde, 0x46, 0xc4, 0xc6, 0x60, 0x81, 0x1f, 0x0f, 0x65, 0x34, 0x50, 0x7d, 0x1a, 0x9e, 0x25,
0x71, 0x15, 0x85, 0x3d, 0x5a, 0xea, 0xa5, 0x46, 0x2d, 0x37, 0x84, 0x0a, 0x5a, 0xb1, 0xd4, 0x7a,
0x9f, 0x41, 0x73, 0x61, 0xa0, 0xa0, 0x4c, 0x7f, 0x04, 0xe8, 0x47, 0x5e, 0xc9, 0x4b, 0xf3, 0x2d,
0x94, 0x9e, 0x8e, 0x8c, 0xa6, 0x03, 0xb4, 0x4e, 0xc5, 0x5d, 0x84, 0xa9, 0x2f, 0x26, 0x13, 0x62,
0x6c, 0xba, 0xbe, 0xb0, 0x22, 0xff, 0xad, 0x04, 0xd7, 0x0b, 0x93, 0xf3, 0x44, 0xc9, 0xbe, 0xa2,
0x5e, 0xea, 0xe5, 0xeb, 0xcc, 0x89, 0xd8, 0x4b, 0x43, 0xbf, 0xbc, 0x9c, 0x84, 0x9d, 0x92, 0xd0,
0xe1, 0x67, 0x4b, 0xc9, 0x0a, 0x78, 0xbc, 0x19, 0x2c, 0x02, 0xdb, 0x3e, 0xf4, 0x8d, 0x3b, 0x44,
0xd3, 0x31, 0xce, 0x5a, 0x5b, 0x1a, 0x4e, 0xc2, 0x5a, 0x1b, 0x79, 0xb5, 0x56, 0x5d, 0x5d, 0x6b,
0x1e, 0x05, 0x67, 0xaf, 0xb1, 0x85, 0x6a, 0x4b, 0xa1, 0x24, 0x32, 0x19, 0x0f, 0x73, 0xa4, 0xda,
0x63, 0x8f, 0x9c, 0xb7, 0xe7, 0x63, 0x01, 0x45, 0xde, 0x2c, 0x4b, 0x35, 0xf1, 0x6a, 0x96, 0x97,
0x47, 0xb1, 0xae, 0x7b, 0x69, 0x26, 0x88, 0x06, 0x44, 0xcb, 0x83, 0x18, 0x37, 0xe9, 0x62, 0x6d,
0xb3, 0x91, 0x8a, 0x98, 0xaf, 0x51, 0x21, 0x5f, 0x36, 0x1b, 0x45, 0x98, 0x71, 0xd8, 0x9c, 0xb8,
0xce, 0xc7, 0x01, 0xd6, 0xd8, 0x22, 0x5a, 0x0e, 0xe3, 0x3f, 0x07, 0xb0, 0x53, 0xc8, 0x1c, 0xbb,
0x8f, 0x99, 0xc1, 0xec, 0xb9, 0xa9, 0xbe, 0xb7, 0xfa, 0x6c, 0xb4, 0x19, 0x16, 0x8e, 0xcb, 0x9a,
0x50, 0xee, 0x4b, 0x23, 0x0b, 0x07, 0xb4, 0x3d, 0x45, 0x49, 0xc3, 0x3e, 0x06, 0x58, 0xc4, 0xcc,
0x8d, 0x80, 0x37, 0x97, 0xf6, 0xb6, 0x6a, 0xe1, 0x51, 0xf9, 0x5f, 0xc1, 0xe2, 0x64, 0xf7, 0xea,
0x26, 0x58, 0x5d, 0x37, 0xf6, 0xe0, 0x75, 0x75, 0xb3, 0x07, 0x35, 0x93, 0x5d, 0x9a, 0x6c, 0x45,
0x2d, 0x00, 0xcc, 0x7b, 0xfb, 0xd9, 0x99, 0x7f, 0xd7, 0xca, 0x64, 0xb6, 0x0f, 0xd0, 0x7e, 0x76,
0x96, 0x16, 0x51, 0x65, 0x65, 0x11, 0x79, 0x0c, 0xb4, 0xa4, 0xb3, 0x2b, 0x54, 0xd5, 0x5e, 0xa1,
0x32, 0x00, 0xb5, 0x74, 0x93, 0x18, 0x62, 0xbe, 0xd6, 0xad, 0x36, 0x03, 0xf8, 0xaf, 0x01, 0xec,
0x7c, 0xa1, 0x5e, 0x90, 0xe1, 0x8e, 0x51, 0x93, 0x73, 0x3d, 0x78, 0x4d, 0x3f, 0x19, 0x94, 0xb5,
0x51, 0xd6, 0xc5, 0x8a, 0xa0, 0x6f, 0x76, 0x1f, 0xae, 0x6b, 0xd5, 0x8b, 0xa3, 0xbe, 0xee, 0x84,
0x51, 0x4f, 0x75, 0x8c, 0x4c, 0x4c, 0x37, 0x6d, 0xa2, 0x8a, 0x58, 0xad, 0x4c, 0xeb, 0xcb, 0xa5,
0x81, 0x2c, 0x55, 0x88, 0x5f, 0x84, 0xf9, 0x0b, 0xd8, 0xa2, 0xe1, 0x46, 0x11, 0x78, 0xfd, 0x5f,
0xce, 0x85, 0xa4, 0x54, 0x08, 0x09, 0xa6, 0x26, 0xd4, 0x5e, 0xa9, 0x6c, 0x88, 0x4c, 0xe6, 0x1f,
0xc0, 0x96, 0xfd, 0x7a, 0x45, 0xac, 0xf8, 0x8f, 0x01, 0x6c, 0xa7, 0x85, 0xd3, 0x7e, 0x76, 0x76,
0xd5, 0x3f, 0xde, 0x82, 0xdd, 0xc9, 0x82, 0x29, 0xbc, 0xdf, 0x5d, 0xc2, 0xd9, 0x03, 0xa8, 0x7b,
0x98, 0xbb, 0xa2, 0xbc, 0xb5, 0xdc, 0x25, 0xee, 0xb9, 0x21, 0x7c, 0x36, 0xef, 0x03, 0x9c, 0x48,
0x8d, 0x17, 0xdf, 0x7f, 0x95, 0x65, 0x34, 0x92, 0x66, 0x19, 0xbf, 0x91, 0x19, 0xd2, 0x1b, 0xc3,
0x3d, 0x16, 0x48, 0xe0, 0xdf, 0xc3, 0x0e, 0x9a, 0xe8, 0x28, 0x73, 0x2e, 0xbf, 0x3e, 0xb8, 0xf7,
0xdf, 0x98, 0x6a, 0xc1, 0xfa, 0xc5, 0x95, 0x07, 0x78, 0xaa, 0xe6, 0xbf, 0x04, 0xb0, 0xed, 0xec,
0xe3, 0xe3, 0xea, 0x7f, 0x36, 0xcf, 0x3e, 0x82, 0xca, 0x2c, 0xc6, 0xbb, 0x6f, 0xe5, 0x55, 0xa9,
0xb1, 0x3c, 0xfe, 0x7b, 0x00, 0xb5, 0x13, 0x25, 0x13, 0x73, 0xa1, 0x24, 0xd5, 0xc2, 0xec, 0x25,
0x6f, 0xbd, 0xd9, 0x8a, 0xb7, 0xde, 0x6c, 0xe5, 0x5b, 0x6f, 0xb6, 0xf4, 0xd6, 0x1b, 0xe6, 0xde,
0x7a, 0x45, 0xf7, 0xcb, 0xbe, 0xfb, 0x37, 0x61, 0x43, 0xab, 0x6f, 0xa7, 0x78, 0x43, 0x75, 0xdd,
0x97, 0xc9, 0x57, 0x0f, 0x1a, 0xfe, 0x21, 0xd4, 0x4e, 0xf5, 0x89, 0x92, 0x23, 0x33, 0x9c, 0x23,
0x35, 0x4c, 0x05, 0xf2, 0x60, 0x43, 0x2c, 0x80, 0x8b, 0x2a, 0xbd, 0xa0, 0xef, 0xfd, 0x1d, 0x00,
0x00, 0xff, 0xff, 0xe7, 0xeb, 0xd6, 0x7a, 0x6e, 0x0f, 0x00, 0x00,
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment