mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-27 11:41:39 +00:00
pretty print ConsensusState
This commit is contained in:
@ -114,6 +114,14 @@ func (b *Block) StringWithIndent(indent string) string {
|
|||||||
indent, b.hash)
|
indent, b.hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Block) Description() string {
|
||||||
|
if b == nil {
|
||||||
|
return "nil-Block"
|
||||||
|
} else {
|
||||||
|
return fmt.Sprintf("Block#%X", b.Hash())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
type Header struct {
|
type Header struct {
|
||||||
|
@ -5,10 +5,9 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/syndtr/goleveldb/leveldb"
|
|
||||||
"github.com/syndtr/goleveldb/leveldb/opt"
|
|
||||||
. "github.com/tendermint/tendermint/binary"
|
. "github.com/tendermint/tendermint/binary"
|
||||||
. "github.com/tendermint/tendermint/common"
|
. "github.com/tendermint/tendermint/common"
|
||||||
|
db_ "github.com/tendermint/tendermint/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -21,26 +20,23 @@ type BlockStoreJSON struct {
|
|||||||
Height uint32
|
Height uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bsj BlockStoreJSON) Save(db *leveldb.DB) {
|
func (bsj BlockStoreJSON) Save(db db_.DB) {
|
||||||
bytes, err := json.Marshal(bsj)
|
bytes, err := json.Marshal(bsj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Panicf("Could not marshal state bytes: %v", err)
|
Panicf("Could not marshal state bytes: %v", err)
|
||||||
}
|
}
|
||||||
db.Put(blockStoreKey, bytes, nil)
|
db.Set(blockStoreKey, bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadBlockStoreJSON(db *leveldb.DB) BlockStoreJSON {
|
func LoadBlockStoreJSON(db db_.DB) BlockStoreJSON {
|
||||||
bytes, err := db.Get(blockStoreKey, nil)
|
bytes := db.Get(blockStoreKey)
|
||||||
if err != nil {
|
|
||||||
Panicf("Could not load BlockStoreJSON from db: %v", err)
|
|
||||||
}
|
|
||||||
if bytes == nil {
|
if bytes == nil {
|
||||||
return BlockStoreJSON{
|
return BlockStoreJSON{
|
||||||
Height: 0,
|
Height: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bsj := BlockStoreJSON{}
|
bsj := BlockStoreJSON{}
|
||||||
err = json.Unmarshal(bytes, &bsj)
|
err := json.Unmarshal(bytes, &bsj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Panicf("Could not unmarshal bytes: %X", bytes)
|
Panicf("Could not unmarshal bytes: %X", bytes)
|
||||||
}
|
}
|
||||||
@ -54,10 +50,10 @@ Simple low level store for blocks, which is actually stored as separte parts (wi
|
|||||||
*/
|
*/
|
||||||
type BlockStore struct {
|
type BlockStore struct {
|
||||||
height uint32
|
height uint32
|
||||||
db *leveldb.DB
|
db db_.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBlockStore(db *leveldb.DB) *BlockStore {
|
func NewBlockStore(db db_.DB) *BlockStore {
|
||||||
bsjson := LoadBlockStoreJSON(db)
|
bsjson := LoadBlockStoreJSON(db)
|
||||||
return &BlockStore{
|
return &BlockStore{
|
||||||
height: bsjson.Height,
|
height: bsjson.Height,
|
||||||
@ -71,29 +67,30 @@ func (bs *BlockStore) Height() uint32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (bs *BlockStore) LoadBlock(height uint32) *Block {
|
func (bs *BlockStore) LoadBlock(height uint32) *Block {
|
||||||
blockBytes, err := bs.db.Get(calcBlockKey(height), nil)
|
blockBytes := bs.db.Get(calcBlockKey(height))
|
||||||
if err != nil {
|
|
||||||
Panicf("Could not load block: %v", err)
|
|
||||||
}
|
|
||||||
if blockBytes == nil {
|
if blockBytes == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var n int64
|
var n int64
|
||||||
return ReadBlock(bytes.NewReader(blockBytes), &n, &err)
|
var err error
|
||||||
|
block := ReadBlock(bytes.NewReader(blockBytes), &n, &err)
|
||||||
|
if err != nil {
|
||||||
|
Panicf("Error reading block: %v", err)
|
||||||
|
}
|
||||||
|
return block
|
||||||
}
|
}
|
||||||
|
|
||||||
// Writes are synchronous and atomic.
|
// Writes are synchronous and atomic.
|
||||||
func (bs *BlockStore) SaveBlock(block *Block) error {
|
func (bs *BlockStore) SaveBlock(block *Block) {
|
||||||
height := block.Height
|
height := block.Height
|
||||||
if height != bs.height+1 {
|
if height != bs.height+1 {
|
||||||
return Errorf("BlockStore can only save contiguous blocks. Wanted %v, got %v", bs.height+1, height)
|
Panicf("BlockStore can only save contiguous blocks. Wanted %v, got %v", bs.height+1, height)
|
||||||
}
|
}
|
||||||
// Save block
|
// Save block
|
||||||
blockBytes := BinaryBytes(block)
|
blockBytes := BinaryBytes(block)
|
||||||
err := bs.db.Put(calcBlockKey(height), blockBytes, &opt.WriteOptions{Sync: true})
|
bs.db.Set(calcBlockKey(height), blockBytes)
|
||||||
// Save new BlockStoreJSON descriptor
|
// Save new BlockStoreJSON descriptor
|
||||||
BlockStoreJSON{Height: height}.Save(bs.db)
|
BlockStoreJSON{Height: height}.Save(bs.db)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
@ -144,6 +144,13 @@ func (ps *PartSet) RootHash() []byte {
|
|||||||
return ps.rootHash
|
return ps.rootHash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ps *PartSet) Count() uint16 {
|
||||||
|
if ps == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return ps.count
|
||||||
|
}
|
||||||
|
|
||||||
func (ps *PartSet) Total() uint16 {
|
func (ps *PartSet) Total() uint16 {
|
||||||
if ps == nil {
|
if ps == nil {
|
||||||
return 0
|
return 0
|
||||||
@ -197,3 +204,11 @@ func (ps *PartSet) GetReader() io.Reader {
|
|||||||
}
|
}
|
||||||
return bytes.NewReader(buf)
|
return bytes.NewReader(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ps *PartSet) Description() string {
|
||||||
|
if ps == nil {
|
||||||
|
return "nil-PartSet"
|
||||||
|
} else {
|
||||||
|
return fmt.Sprintf("(%v of %v)", ps.Count(), ps.Total())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package consensus
|
package consensus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
. "github.com/tendermint/tendermint/binary"
|
. "github.com/tendermint/tendermint/binary"
|
||||||
@ -102,3 +103,15 @@ func (pol *POL) Verify(vset *state.ValidatorSet) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pol *POL) Description() string {
|
||||||
|
if pol == nil {
|
||||||
|
return "nil-POL"
|
||||||
|
} else {
|
||||||
|
blockHash := pol.BlockHash
|
||||||
|
if blockHash != nil {
|
||||||
|
blockHash = blockHash[:6]
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("POL{H:%v R:%v BH:%X}", pol.Height, pol.Round, blockHash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -17,10 +17,10 @@ type PrivValidator struct {
|
|||||||
func (pv *PrivValidator) Sign(o Signable) {
|
func (pv *PrivValidator) Sign(o Signable) {
|
||||||
switch o.(type) {
|
switch o.(type) {
|
||||||
case *Proposal:
|
case *Proposal:
|
||||||
//TODO: prevent double signing.
|
//TODO: prevent double signing && test.
|
||||||
pv.PrivAccount.Sign(o.(*Proposal))
|
pv.PrivAccount.Sign(o.(*Proposal))
|
||||||
case *Vote:
|
case *Vote:
|
||||||
//TODO: prevent double signing.
|
//TODO: prevent double signing && test.
|
||||||
pv.PrivAccount.Sign(o.(*Vote))
|
pv.PrivAccount.Sign(o.(*Vote))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package consensus
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -33,7 +34,6 @@ type RoundState struct {
|
|||||||
Step uint8
|
Step uint8
|
||||||
StartTime time.Time
|
StartTime time.Time
|
||||||
Validators *state.ValidatorSet
|
Validators *state.ValidatorSet
|
||||||
Proposer *state.Validator
|
|
||||||
Proposal *Proposal
|
Proposal *Proposal
|
||||||
ProposalBlock *Block
|
ProposalBlock *Block
|
||||||
ProposalBlockPartSet *PartSet
|
ProposalBlockPartSet *PartSet
|
||||||
@ -47,6 +47,38 @@ type RoundState struct {
|
|||||||
PrivValidator *PrivValidator
|
PrivValidator *PrivValidator
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rs *RoundState) String() string {
|
||||||
|
return rs.StringWithIndent("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs *RoundState) StringWithIndent(indent string) string {
|
||||||
|
return fmt.Sprintf(`RoundState{
|
||||||
|
%s H:%v R:%v S:%v
|
||||||
|
%s StartTime: %v
|
||||||
|
%s Validators: %v
|
||||||
|
%s Proposal: %v
|
||||||
|
%s ProposalBlock: %v %v
|
||||||
|
%s ProposalPOL: %v %v
|
||||||
|
%s LockedBlock: %v
|
||||||
|
%s LockedPOL: %v
|
||||||
|
%s Votes: %v
|
||||||
|
%s Precommits: %v
|
||||||
|
%s Commits: %v
|
||||||
|
%s}`,
|
||||||
|
indent, rs.Height, rs.Round, rs.Step,
|
||||||
|
indent, rs.StartTime,
|
||||||
|
indent, rs.Validators.StringWithIndent(indent+" "),
|
||||||
|
indent, rs.Proposal,
|
||||||
|
indent, rs.ProposalBlockPartSet.Description(), rs.ProposalBlock.Description(),
|
||||||
|
indent, rs.ProposalPOLPartSet.Description(), rs.ProposalPOL.Description(),
|
||||||
|
indent, rs.LockedBlock.Description(),
|
||||||
|
indent, rs.LockedPOL.Description(),
|
||||||
|
indent, rs.Votes.StringWithIndent(indent+" "),
|
||||||
|
indent, rs.Precommits.StringWithIndent(indent+" "),
|
||||||
|
indent, rs.Commits.StringWithIndent(indent+" "),
|
||||||
|
indent)
|
||||||
|
}
|
||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
|
|
||||||
// Tracks consensus state across block heights and rounds.
|
// Tracks consensus state across block heights and rounds.
|
||||||
@ -92,7 +124,6 @@ func (cs *ConsensusState) updateToState(state *state.State) {
|
|||||||
cs.Step = RoundStepStart
|
cs.Step = RoundStepStart
|
||||||
cs.StartTime = state.CommitTime.Add(newBlockWaitDuration)
|
cs.StartTime = state.CommitTime.Add(newBlockWaitDuration)
|
||||||
cs.Validators = validators
|
cs.Validators = validators
|
||||||
cs.Proposer = validators.Proposer()
|
|
||||||
cs.Proposal = nil
|
cs.Proposal = nil
|
||||||
cs.ProposalBlock = nil
|
cs.ProposalBlock = nil
|
||||||
cs.ProposalBlockPartSet = nil
|
cs.ProposalBlockPartSet = nil
|
||||||
@ -135,7 +166,6 @@ func (cs *ConsensusState) setupRound(round uint16) {
|
|||||||
cs.Round = round
|
cs.Round = round
|
||||||
cs.Step = RoundStepStart
|
cs.Step = RoundStepStart
|
||||||
cs.Validators = validators
|
cs.Validators = validators
|
||||||
cs.Proposer = validators.Proposer()
|
|
||||||
cs.Proposal = nil
|
cs.Proposal = nil
|
||||||
cs.ProposalBlock = nil
|
cs.ProposalBlock = nil
|
||||||
cs.ProposalBlockPartSet = nil
|
cs.ProposalBlockPartSet = nil
|
||||||
@ -178,7 +208,7 @@ func (cs *ConsensusState) SetProposal(proposal *Proposal) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify signature
|
// Verify signature
|
||||||
if !cs.Proposer.Verify(proposal) {
|
if !cs.Validators.Proposer().Verify(proposal) {
|
||||||
return ErrInvalidProposalSignature
|
return ErrInvalidProposalSignature
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,7 +222,7 @@ func (cs *ConsensusState) MakeProposal() {
|
|||||||
cs.mtx.Lock()
|
cs.mtx.Lock()
|
||||||
defer cs.mtx.Unlock()
|
defer cs.mtx.Unlock()
|
||||||
|
|
||||||
if cs.PrivValidator == nil || cs.Proposer.Id != cs.PrivValidator.Id {
|
if cs.PrivValidator == nil || cs.Validators.Proposer().Id != cs.PrivValidator.Id {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -382,10 +412,7 @@ func (cs *ConsensusState) Commit(height uint32, round uint16) *Block {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save to blockStore
|
// Save to blockStore
|
||||||
err := cs.blockStore.SaveBlock(block)
|
cs.blockStore.SaveBlock(block)
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// What was staged becomes committed.
|
// What was staged becomes committed.
|
||||||
state := cs.stagedState
|
state := cs.stagedState
|
||||||
|
60
consensus/state_test.go
Normal file
60
consensus/state_test.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package consensus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
. "github.com/tendermint/tendermint/blocks"
|
||||||
|
. "github.com/tendermint/tendermint/common"
|
||||||
|
db_ "github.com/tendermint/tendermint/db"
|
||||||
|
"github.com/tendermint/tendermint/mempool"
|
||||||
|
"github.com/tendermint/tendermint/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
func randAccountDetail(id uint64, status byte) (*state.AccountDetail, *state.PrivAccount) {
|
||||||
|
privAccount := state.GenPrivAccount()
|
||||||
|
privAccount.Id = id
|
||||||
|
account := privAccount.Account
|
||||||
|
return &state.AccountDetail{
|
||||||
|
Account: account,
|
||||||
|
Sequence: RandUInt(),
|
||||||
|
Balance: RandUInt64() + 1000, // At least 1000.
|
||||||
|
Status: status,
|
||||||
|
}, privAccount
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first numValidators accounts are validators.
|
||||||
|
func randGenesisState(numAccounts int, numValidators int) (*state.State, []*state.PrivAccount) {
|
||||||
|
db := db_.NewMemDB()
|
||||||
|
accountDetails := make([]*state.AccountDetail, numAccounts)
|
||||||
|
privAccounts := make([]*state.PrivAccount, numAccounts)
|
||||||
|
for i := 0; i < numAccounts; i++ {
|
||||||
|
if i < numValidators {
|
||||||
|
accountDetails[i], privAccounts[i] =
|
||||||
|
randAccountDetail(uint64(i), state.AccountStatusBonded)
|
||||||
|
} else {
|
||||||
|
accountDetails[i], privAccounts[i] =
|
||||||
|
randAccountDetail(uint64(i), state.AccountStatusNominal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s0 := state.GenesisState(db, time.Now(), accountDetails)
|
||||||
|
s0.Save(time.Now())
|
||||||
|
return s0, privAccounts
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeConsensusState() (*ConsensusState, []*state.PrivAccount) {
|
||||||
|
state, privAccounts := randGenesisState(20, 10)
|
||||||
|
blockStore := NewBlockStore(db_.NewMemDB())
|
||||||
|
mempool := mempool.NewMempool(nil, state)
|
||||||
|
cs := NewConsensusState(state, blockStore, mempool)
|
||||||
|
return cs, privAccounts
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnit(t *testing.T) {
|
||||||
|
cs, privAccounts := makeConsensusState()
|
||||||
|
rs := cs.GetRoundState()
|
||||||
|
t.Log(rs)
|
||||||
|
if false {
|
||||||
|
t.Log(privAccounts)
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,8 @@ import (
|
|||||||
"github.com/tendermint/tendermint/state"
|
"github.com/tendermint/tendermint/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Common test methods
|
||||||
|
|
||||||
func makeValidator(id uint64, votingPower uint64) (*state.Validator, *state.PrivAccount) {
|
func makeValidator(id uint64, votingPower uint64) (*state.Validator, *state.PrivAccount) {
|
||||||
privAccount := state.GenPrivAccount()
|
privAccount := state.GenPrivAccount()
|
||||||
privAccount.Id = id
|
privAccount.Id = id
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
. "github.com/tendermint/tendermint/binary"
|
. "github.com/tendermint/tendermint/binary"
|
||||||
@ -73,6 +74,10 @@ func (v *Validator) CompareAccum(other *Validator) *Validator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Validator) String() string {
|
||||||
|
return fmt.Sprintf("Validator{%v VP:%v A:%v}", v.Account, v.VotingPower, v.Accum)
|
||||||
|
}
|
||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
|
|
||||||
var ValidatorCodec = validatorCodec{}
|
var ValidatorCodec = validatorCodec{}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
. "github.com/tendermint/tendermint/binary"
|
. "github.com/tendermint/tendermint/binary"
|
||||||
"github.com/tendermint/tendermint/merkle"
|
"github.com/tendermint/tendermint/merkle"
|
||||||
@ -124,3 +126,21 @@ func (vset *ValidatorSet) Iterate(fn func(val *Validator) bool) {
|
|||||||
return fn(val_.(*Validator))
|
return fn(val_.(*Validator))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (vset *ValidatorSet) StringWithIndent(indent string) string {
|
||||||
|
valStrings := []string{}
|
||||||
|
vset.Iterate(func(val *Validator) bool {
|
||||||
|
valStrings = append(valStrings, val.String())
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
return fmt.Sprintf(`ValidatorSet{
|
||||||
|
%s Proposer: %v
|
||||||
|
%s Validators:
|
||||||
|
%s %v
|
||||||
|
%s}`,
|
||||||
|
indent, vset.proposer.String(),
|
||||||
|
indent,
|
||||||
|
indent, strings.Join(valStrings, "\n"+indent+" "),
|
||||||
|
indent)
|
||||||
|
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user