From 9ea504030ec317466c1ef7d87f7d52b39dc8bbe4 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Sat, 18 Oct 2014 01:42:33 -0700 Subject: [PATCH] pretty print ConsensusState --- blocks/block.go | 8 +++++ blocks/store.go | 39 +++++++++++------------- consensus/part_set.go | 15 ++++++++++ consensus/pol.go | 13 ++++++++ consensus/priv_validator.go | 4 +-- consensus/state.go | 45 ++++++++++++++++++++++------ consensus/state_test.go | 60 +++++++++++++++++++++++++++++++++++++ consensus/test.go | 2 ++ state/validator.go | 5 ++++ state/validator_set.go | 20 +++++++++++++ 10 files changed, 179 insertions(+), 32 deletions(-) create mode 100644 consensus/state_test.go diff --git a/blocks/block.go b/blocks/block.go index 15518d22f..216ba124c 100644 --- a/blocks/block.go +++ b/blocks/block.go @@ -114,6 +114,14 @@ func (b *Block) StringWithIndent(indent string) string { 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 { diff --git a/blocks/store.go b/blocks/store.go index c56267f6c..f0c2f2848 100644 --- a/blocks/store.go +++ b/blocks/store.go @@ -5,10 +5,9 @@ import ( "encoding/binary" "encoding/json" - "github.com/syndtr/goleveldb/leveldb" - "github.com/syndtr/goleveldb/leveldb/opt" . "github.com/tendermint/tendermint/binary" . "github.com/tendermint/tendermint/common" + db_ "github.com/tendermint/tendermint/db" ) var ( @@ -21,26 +20,23 @@ type BlockStoreJSON struct { Height uint32 } -func (bsj BlockStoreJSON) Save(db *leveldb.DB) { +func (bsj BlockStoreJSON) Save(db db_.DB) { bytes, err := json.Marshal(bsj) if err != nil { Panicf("Could not marshal state bytes: %v", err) } - db.Put(blockStoreKey, bytes, nil) + db.Set(blockStoreKey, bytes) } -func LoadBlockStoreJSON(db *leveldb.DB) BlockStoreJSON { - bytes, err := db.Get(blockStoreKey, nil) - if err != nil { - Panicf("Could not load BlockStoreJSON from db: %v", err) - } +func LoadBlockStoreJSON(db db_.DB) BlockStoreJSON { + bytes := db.Get(blockStoreKey) if bytes == nil { return BlockStoreJSON{ Height: 0, } } bsj := BlockStoreJSON{} - err = json.Unmarshal(bytes, &bsj) + err := json.Unmarshal(bytes, &bsj) if err != nil { 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 { height uint32 - db *leveldb.DB + db db_.DB } -func NewBlockStore(db *leveldb.DB) *BlockStore { +func NewBlockStore(db db_.DB) *BlockStore { bsjson := LoadBlockStoreJSON(db) return &BlockStore{ height: bsjson.Height, @@ -71,29 +67,30 @@ func (bs *BlockStore) Height() uint32 { } func (bs *BlockStore) LoadBlock(height uint32) *Block { - blockBytes, err := bs.db.Get(calcBlockKey(height), nil) - if err != nil { - Panicf("Could not load block: %v", err) - } + blockBytes := bs.db.Get(calcBlockKey(height)) if blockBytes == nil { return nil } 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. -func (bs *BlockStore) SaveBlock(block *Block) error { +func (bs *BlockStore) SaveBlock(block *Block) { height := block.Height 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 blockBytes := BinaryBytes(block) - err := bs.db.Put(calcBlockKey(height), blockBytes, &opt.WriteOptions{Sync: true}) + bs.db.Set(calcBlockKey(height), blockBytes) // Save new BlockStoreJSON descriptor BlockStoreJSON{Height: height}.Save(bs.db) - return err } //----------------------------------------------------------------------------- diff --git a/consensus/part_set.go b/consensus/part_set.go index 036ea8a3f..9b10e02b1 100644 --- a/consensus/part_set.go +++ b/consensus/part_set.go @@ -144,6 +144,13 @@ func (ps *PartSet) RootHash() []byte { return ps.rootHash } +func (ps *PartSet) Count() uint16 { + if ps == nil { + return 0 + } + return ps.count +} + func (ps *PartSet) Total() uint16 { if ps == nil { return 0 @@ -197,3 +204,11 @@ func (ps *PartSet) GetReader() io.Reader { } 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()) + } +} diff --git a/consensus/pol.go b/consensus/pol.go index eca67b646..efd82b65e 100644 --- a/consensus/pol.go +++ b/consensus/pol.go @@ -1,6 +1,7 @@ package consensus import ( + "fmt" "io" . "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) + } +} diff --git a/consensus/priv_validator.go b/consensus/priv_validator.go index c997e4b10..aeb7e7787 100644 --- a/consensus/priv_validator.go +++ b/consensus/priv_validator.go @@ -17,10 +17,10 @@ type PrivValidator struct { func (pv *PrivValidator) Sign(o Signable) { switch o.(type) { case *Proposal: - //TODO: prevent double signing. + //TODO: prevent double signing && test. pv.PrivAccount.Sign(o.(*Proposal)) case *Vote: - //TODO: prevent double signing. + //TODO: prevent double signing && test. pv.PrivAccount.Sign(o.(*Vote)) } } diff --git a/consensus/state.go b/consensus/state.go index a275d1845..ccb9ab19e 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -2,6 +2,7 @@ package consensus import ( "errors" + "fmt" "sync" "time" @@ -33,7 +34,6 @@ type RoundState struct { Step uint8 StartTime time.Time Validators *state.ValidatorSet - Proposer *state.Validator Proposal *Proposal ProposalBlock *Block ProposalBlockPartSet *PartSet @@ -47,6 +47,38 @@ type RoundState struct { 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. @@ -92,7 +124,6 @@ func (cs *ConsensusState) updateToState(state *state.State) { cs.Step = RoundStepStart cs.StartTime = state.CommitTime.Add(newBlockWaitDuration) cs.Validators = validators - cs.Proposer = validators.Proposer() cs.Proposal = nil cs.ProposalBlock = nil cs.ProposalBlockPartSet = nil @@ -135,7 +166,6 @@ func (cs *ConsensusState) setupRound(round uint16) { cs.Round = round cs.Step = RoundStepStart cs.Validators = validators - cs.Proposer = validators.Proposer() cs.Proposal = nil cs.ProposalBlock = nil cs.ProposalBlockPartSet = nil @@ -178,7 +208,7 @@ func (cs *ConsensusState) SetProposal(proposal *Proposal) error { } // Verify signature - if !cs.Proposer.Verify(proposal) { + if !cs.Validators.Proposer().Verify(proposal) { return ErrInvalidProposalSignature } @@ -192,7 +222,7 @@ func (cs *ConsensusState) MakeProposal() { cs.mtx.Lock() 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 } @@ -382,10 +412,7 @@ func (cs *ConsensusState) Commit(height uint32, round uint16) *Block { } // Save to blockStore - err := cs.blockStore.SaveBlock(block) - if err != nil { - return nil - } + cs.blockStore.SaveBlock(block) // What was staged becomes committed. state := cs.stagedState diff --git a/consensus/state_test.go b/consensus/state_test.go new file mode 100644 index 000000000..15c034368 --- /dev/null +++ b/consensus/state_test.go @@ -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) + } +} diff --git a/consensus/test.go b/consensus/test.go index 147282d73..c614996c6 100644 --- a/consensus/test.go +++ b/consensus/test.go @@ -5,6 +5,8 @@ import ( "github.com/tendermint/tendermint/state" ) +// Common test methods + func makeValidator(id uint64, votingPower uint64) (*state.Validator, *state.PrivAccount) { privAccount := state.GenPrivAccount() privAccount.Id = id diff --git a/state/validator.go b/state/validator.go index 8505d46f1..999060d2d 100644 --- a/state/validator.go +++ b/state/validator.go @@ -1,6 +1,7 @@ package state import ( + "fmt" "io" . "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{} diff --git a/state/validator_set.go b/state/validator_set.go index 9b5de2439..ef7dac98b 100644 --- a/state/validator_set.go +++ b/state/validator_set.go @@ -1,7 +1,9 @@ package state import ( + "fmt" "io" + "strings" . "github.com/tendermint/tendermint/binary" "github.com/tendermint/tendermint/merkle" @@ -124,3 +126,21 @@ func (vset *ValidatorSet) Iterate(fn func(val *Validator) bool) { 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) + +}