From 70eb75dca7347a4d24cba9d3a1d9d5f2655779b6 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Tue, 23 Dec 2014 23:20:49 -0800 Subject: [PATCH] Refactor consensus/vote_set_test.go --- cmd/daemon.go | 2 +- consensus/vote_set_test.go | 154 +++++++++++++++++++------------------ mempool/mempool.go | 4 +- state/genesis.go | 6 +- state/state.go | 1 + state/state_test.go | 18 ++++- state/test.go | 2 +- state/validator.go | 2 +- state/validator_set.go | 2 +- 9 files changed, 103 insertions(+), 88 deletions(-) diff --git a/cmd/daemon.go b/cmd/daemon.go index f587ceb70..689406754 100644 --- a/cmd/daemon.go +++ b/cmd/daemon.go @@ -33,7 +33,7 @@ func NewNode() *Node { stateDB := db_.NewMemDB() // TODO configurable db. state := state_.LoadState(stateDB) if state == nil { - state = state_.GenesisStateFromFile(stateDB, config.GenesisFile()) + state = state_.MakeGenesisStateFromFile(stateDB, config.GenesisFile()) state.Save() } diff --git a/consensus/vote_set_test.go b/consensus/vote_set_test.go index 4653b1e84..96e54d202 100644 --- a/consensus/vote_set_test.go +++ b/consensus/vote_set_test.go @@ -6,12 +6,54 @@ import ( . "github.com/tendermint/tendermint/block" . "github.com/tendermint/tendermint/common" . "github.com/tendermint/tendermint/common/test" + "github.com/tendermint/tendermint/state" "testing" ) // NOTE: see consensus/test.go for common test methods. +// Convenience: Return new vote with different height +func withHeight(vote *Vote, height uint) *Vote { + vote = vote.Copy() + vote.Height = height + return vote +} + +// Convenience: Return new vote with different round +func withRound(vote *Vote, round uint) *Vote { + vote = vote.Copy() + vote.Round = round + return vote +} + +// Convenience: Return new vote with different type +func withType(vote *Vote, type_ byte) *Vote { + vote = vote.Copy() + vote.Type = type_ + return vote +} + +// Convenience: Return new vote with different blockHash +func withBlockHash(vote *Vote, blockHash []byte) *Vote { + vote = vote.Copy() + vote.BlockHash = blockHash + return vote +} + +// Convenience: Return new vote with different blockParts +func withBlockParts(vote *Vote, blockParts PartSetHeader) *Vote { + vote = vote.Copy() + vote.BlockParts = blockParts + return vote +} + +func signAddVote(privVal *state.PrivValidator, vote *Vote, voteSet *VoteSet) (bool, error) { + vote.Signature = privVal.SignVoteUnsafe(vote) + added, _, err := voteSet.Add(privVal.Address, vote) + return added, err +} + func TestAddVote(t *testing.T) { height, round := uint(1), uint(0) voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrevote, 10, 1) @@ -31,8 +73,7 @@ func TestAddVote(t *testing.T) { } vote := &Vote{Height: height, Round: round, Type: VoteTypePrevote, BlockHash: nil} - vote.Signature = val0.SignVoteUnsafe(vote) - voteSet.Add(val0.Address, vote) + signAddVote(val0, vote, voteSet) if voteSet.GetByAddress(val0.Address) == nil { t.Errorf("Expected GetByAddress(val0.Address) to be present") @@ -50,12 +91,11 @@ func Test2_3Majority(t *testing.T) { height, round := uint(1), uint(0) voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrevote, 10, 1) + vote := &Vote{Height: height, Round: round, Type: VoteTypePrevote, BlockHash: nil} + // 6 out of 10 voted for nil. - voteProto := &Vote{Height: height, Round: round, Type: VoteTypePrevote, BlockHash: nil} for i := 0; i < 6; i++ { - vote := voteProto.Copy() - vote.Signature = privValidators[i].SignVoteUnsafe(vote) - voteSet.Add(privValidators[i].Address, vote) + signAddVote(privValidators[i], vote, voteSet) } hash, header, ok := voteSet.TwoThirdsMajority() if hash != nil || !header.IsZero() || ok { @@ -64,10 +104,7 @@ func Test2_3Majority(t *testing.T) { // 7th validator voted for some blockhash { - vote := voteProto.Copy() - vote.BlockHash = CRandBytes(32) - vote.Signature = privValidators[6].SignVoteUnsafe(vote) - voteSet.Add(privValidators[6].Address, vote) + signAddVote(privValidators[6], withBlockHash(vote, RandBytes(32)), voteSet) hash, header, ok = voteSet.TwoThirdsMajority() if hash != nil || !header.IsZero() || ok { t.Errorf("There should be no 2/3 majority") @@ -76,10 +113,7 @@ func Test2_3Majority(t *testing.T) { // 8th validator voted for nil. { - vote := voteProto.Copy() - vote.BlockHash = nil - vote.Signature = privValidators[7].SignVoteUnsafe(vote) - voteSet.Add(privValidators[7].Address, vote) + signAddVote(privValidators[7], vote, voteSet) hash, header, ok = voteSet.TwoThirdsMajority() if hash != nil || !header.IsZero() || !ok { t.Errorf("There should be 2/3 majority for nil") @@ -95,12 +129,11 @@ func Test2_3MajorityRedux(t *testing.T) { blockPartsTotal := uint(123) blockParts := PartSetHeader{blockPartsTotal, CRandBytes(32)} + vote := &Vote{Height: height, Round: round, Type: VoteTypePrevote, BlockHash: blockHash, BlockParts: blockParts} + // 66 out of 100 voted for nil. - voteProto := &Vote{Height: height, Round: round, Type: VoteTypePrevote, BlockHash: blockHash, BlockParts: blockParts} for i := 0; i < 66; i++ { - vote := voteProto.Copy() - vote.Signature = privValidators[i].SignVoteUnsafe(vote) - voteSet.Add(privValidators[i].Address, vote) + signAddVote(privValidators[i], vote, voteSet) } hash, header, ok := voteSet.TwoThirdsMajority() if hash != nil || !header.IsZero() || ok { @@ -109,9 +142,7 @@ func Test2_3MajorityRedux(t *testing.T) { // 67th validator voted for nil { - vote := &Vote{Height: height, Round: round, Type: VoteTypePrevote, BlockHash: nil, BlockParts: PartSetHeader{}} - vote.Signature = privValidators[66].SignVoteUnsafe(vote) - voteSet.Add(privValidators[66].Address, vote) + signAddVote(privValidators[66], withBlockHash(vote, nil), voteSet) hash, header, ok = voteSet.TwoThirdsMajority() if hash != nil || !header.IsZero() || ok { t.Errorf("There should be no 2/3 majority: last vote added was nil") @@ -120,9 +151,8 @@ func Test2_3MajorityRedux(t *testing.T) { // 68th validator voted for a different BlockParts PartSetHeader { - vote := &Vote{Height: height, Round: round, Type: VoteTypePrevote, BlockHash: blockHash, BlockParts: PartSetHeader{blockPartsTotal, CRandBytes(32)}} - vote.Signature = privValidators[67].SignVoteUnsafe(vote) - voteSet.Add(privValidators[67].Address, vote) + blockParts := PartSetHeader{blockPartsTotal, CRandBytes(32)} + signAddVote(privValidators[67], withBlockParts(vote, blockParts), voteSet) hash, header, ok = voteSet.TwoThirdsMajority() if hash != nil || !header.IsZero() || ok { t.Errorf("There should be no 2/3 majority: last vote added had different PartSetHeader Hash") @@ -131,9 +161,8 @@ func Test2_3MajorityRedux(t *testing.T) { // 69th validator voted for different BlockParts Total { - vote := &Vote{Height: height, Round: round, Type: VoteTypePrevote, BlockHash: blockHash, BlockParts: PartSetHeader{blockPartsTotal + 1, blockParts.Hash}} - vote.Signature = privValidators[68].SignVoteUnsafe(vote) - voteSet.Add(privValidators[68].Address, vote) + blockParts := PartSetHeader{blockPartsTotal + 1, blockParts.Hash} + signAddVote(privValidators[68], withBlockParts(vote, blockParts), voteSet) hash, header, ok = voteSet.TwoThirdsMajority() if hash != nil || !header.IsZero() || ok { t.Errorf("There should be no 2/3 majority: last vote added had different PartSetHeader Total") @@ -142,9 +171,7 @@ func Test2_3MajorityRedux(t *testing.T) { // 70th validator voted for different BlockHash { - vote := &Vote{Height: height, Round: round, Type: VoteTypePrevote, BlockHash: CRandBytes(32), BlockParts: blockParts} - vote.Signature = privValidators[69].SignVoteUnsafe(vote) - voteSet.Add(privValidators[69].Address, vote) + signAddVote(privValidators[69], withBlockHash(vote, RandBytes(32)), voteSet) hash, header, ok = voteSet.TwoThirdsMajority() if hash != nil || !header.IsZero() || ok { t.Errorf("There should be no 2/3 majority: last vote added had different BlockHash") @@ -153,9 +180,7 @@ func Test2_3MajorityRedux(t *testing.T) { // 71st validator voted for the right BlockHash & BlockParts { - vote := voteProto.Copy() - vote.Signature = privValidators[70].SignVoteUnsafe(vote) - voteSet.Add(privValidators[70].Address, vote) + signAddVote(privValidators[70], vote, voteSet) hash, header, ok = voteSet.TwoThirdsMajority() if !bytes.Equal(hash, blockHash) || !header.Equals(blockParts) || !ok { t.Errorf("There should be 2/3 majority") @@ -169,40 +194,31 @@ func TestBadVotes(t *testing.T) { // val0 votes for nil. vote := &Vote{Height: height, Round: round, Type: VoteTypePrevote, BlockHash: nil} - vote.Signature = privValidators[0].SignVoteUnsafe(vote) - added, _, err := voteSet.Add(privValidators[0].Address, vote) + added, err := signAddVote(privValidators[0], vote, voteSet) if !added || err != nil { t.Errorf("Expected Add() to succeed") } // val0 votes again for some block. - vote = &Vote{Height: height, Round: round, Type: VoteTypePrevote, BlockHash: CRandBytes(32)} - vote.Signature = privValidators[0].SignVoteUnsafe(vote) - added, _, err = voteSet.Add(privValidators[0].Address, vote) + added, err = signAddVote(privValidators[0], withBlockHash(vote, RandBytes(32)), voteSet) if added || err == nil { t.Errorf("Expected Add() to fail, dupeout.") } // val1 votes on another height - vote = &Vote{Height: height + 1, Round: round, Type: VoteTypePrevote, BlockHash: nil} - vote.Signature = privValidators[1].SignVoteUnsafe(vote) - added, _, err = voteSet.Add(privValidators[1].Address, vote) + added, err = signAddVote(privValidators[1], withHeight(vote, height+1), voteSet) if added { t.Errorf("Expected Add() to fail, wrong height") } // val2 votes on another round - vote = &Vote{Height: height, Round: round + 1, Type: VoteTypePrevote, BlockHash: nil} - vote.Signature = privValidators[2].SignVoteUnsafe(vote) - added, _, err = voteSet.Add(privValidators[2].Address, vote) + added, err = signAddVote(privValidators[2], withRound(vote, round+1), voteSet) if added { t.Errorf("Expected Add() to fail, wrong round") } // val3 votes of another type. - vote = &Vote{Height: height, Round: round, Type: VoteTypePrecommit, BlockHash: nil} - vote.Signature = privValidators[3].SignVoteUnsafe(vote) - added, _, err = voteSet.Add(privValidators[3].Address, vote) + added, err = signAddVote(privValidators[3], withType(vote, VoteTypePrecommit), voteSet) if added { t.Errorf("Expected Add() to fail, wrong type") } @@ -213,11 +229,9 @@ func TestAddCommitsToPrevoteVotes(t *testing.T) { voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrevote, 10, 1) // val0, val1, val2, val3, val4, val5 vote for nil. - voteProto := &Vote{Height: height, Round: round, Type: VoteTypePrevote, BlockHash: nil} + vote := &Vote{Height: height, Round: round, Type: VoteTypePrevote, BlockHash: nil} for i := 0; i < 6; i++ { - vote := voteProto.Copy() - vote.Signature = privValidators[i].SignVoteUnsafe(vote) - voteSet.Add(privValidators[i].Address, vote) + signAddVote(privValidators[i], vote, voteSet) } hash, header, ok := voteSet.TwoThirdsMajority() if hash != nil || !header.IsZero() || ok { @@ -225,41 +239,36 @@ func TestAddCommitsToPrevoteVotes(t *testing.T) { } // Attempt to add a commit from val6 at a previous height - vote := &Vote{Height: height - 1, Round: round, Type: VoteTypeCommit, BlockHash: nil} - vote.Signature = privValidators[6].SignVoteUnsafe(vote) - added, _, _ := voteSet.Add(privValidators[6].Address, vote) + vote = &Vote{Height: height - 1, Round: round, Type: VoteTypeCommit, BlockHash: nil} + added, _ := signAddVote(privValidators[6], vote, voteSet) if added { t.Errorf("Expected Add() to fail, wrong height.") } // Attempt to add a commit from val6 at a later round vote = &Vote{Height: height, Round: round + 1, Type: VoteTypeCommit, BlockHash: nil} - vote.Signature = privValidators[6].SignVoteUnsafe(vote) - added, _, _ = voteSet.Add(privValidators[6].Address, vote) + added, _ = signAddVote(privValidators[6], vote, voteSet) if added { t.Errorf("Expected Add() to fail, cannot add future round vote.") } // Attempt to add a commit from val6 for currrent height/round. vote = &Vote{Height: height, Round: round, Type: VoteTypeCommit, BlockHash: nil} - vote.Signature = privValidators[6].SignVoteUnsafe(vote) - added, _, err := voteSet.Add(privValidators[6].Address, vote) + added, err := signAddVote(privValidators[6], vote, voteSet) if added || err == nil { t.Errorf("Expected Add() to fail, only prior round commits can be added.") } // Add commit from val6 at a previous round vote = &Vote{Height: height, Round: round - 1, Type: VoteTypeCommit, BlockHash: nil} - vote.Signature = privValidators[6].SignVoteUnsafe(vote) - added, _, err = voteSet.Add(privValidators[6].Address, vote) + added, err = signAddVote(privValidators[6], vote, voteSet) if !added || err != nil { t.Errorf("Expected Add() to succeed, commit for prior rounds are relevant.") } // Also add commit from val7 for previous round. vote = &Vote{Height: height, Round: round - 2, Type: VoteTypeCommit, BlockHash: nil} - vote.Signature = privValidators[7].SignVoteUnsafe(vote) - added, _, err = voteSet.Add(privValidators[7].Address, vote) + added, err = signAddVote(privValidators[7], vote, voteSet) if !added || err != nil { t.Errorf("Expected Add() to succeed. err: %v", err) } @@ -277,13 +286,12 @@ func TestMakeValidation(t *testing.T) { voteSet, _, privValidators := randVoteSet(height, round, VoteTypeCommit, 10, 1) blockHash, blockParts := CRandBytes(32), PartSetHeader{123, CRandBytes(32)} - // 6 out of 10 voted for some block. - voteProto := &Vote{Height: height, Round: round, Type: VoteTypeCommit, + vote := &Vote{Height: height, Round: round, Type: VoteTypeCommit, BlockHash: blockHash, BlockParts: blockParts} + + // 6 out of 10 voted for some block. for i := 0; i < 6; i++ { - vote := voteProto.Copy() - vote.Signature = privValidators[i].SignVoteUnsafe(vote) - voteSet.Add(privValidators[i].Address, vote) + signAddVote(privValidators[i], vote, voteSet) } // MakeValidation should fail. @@ -291,18 +299,14 @@ func TestMakeValidation(t *testing.T) { // 7th voted for some other block. { - vote := &Vote{Height: height, Round: round, Type: VoteTypeCommit, - BlockHash: CRandBytes(32), - BlockParts: PartSetHeader{123, CRandBytes(32)}} - vote.Signature = privValidators[6].SignVoteUnsafe(vote) - voteSet.Add(privValidators[6].Address, vote) + vote := withBlockHash(vote, RandBytes(32)) + vote = withBlockParts(vote, PartSetHeader{123, RandBytes(32)}) + signAddVote(privValidators[6], vote, voteSet) } // The 8th voted like everyone else. { - vote := voteProto.Copy() - vote.Signature = privValidators[7].SignVoteUnsafe(vote) - voteSet.Add(privValidators[7].Address, vote) + signAddVote(privValidators[7], vote, voteSet) } validation := voteSet.MakeValidation() diff --git a/mempool/mempool.go b/mempool/mempool.go index ea095df74..3c7abb4d9 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -2,8 +2,8 @@ Mempool receives new transactions and applies them to the latest committed state. If the transaction is acceptable, then it broadcasts the tx to peers. -When this node happens to be the next proposer, it simply takes the recently -modified state (and the associated transactions) and use that as the proposal. +When this node happens to be the next proposer, it simply uses the recently +modified state (and the associated transactions) to construct a proposal. */ package mempool diff --git a/state/genesis.go b/state/genesis.go index 16ecd6a00..56b3b6f44 100644 --- a/state/genesis.go +++ b/state/genesis.go @@ -27,16 +27,16 @@ func GenesisDocFromJSON(jsonBlob []byte) (genState *GenesisDoc) { return } -func GenesisStateFromFile(db db_.DB, genDocFile string) *State { +func MakeGenesisStateFromFile(db db_.DB, genDocFile string) *State { jsonBlob, err := ioutil.ReadFile(genDocFile) if err != nil { Panicf("Couldn't read GenesisDoc file: %v", err) } genDoc := GenesisDocFromJSON(jsonBlob) - return GenesisState(db, genDoc) + return MakeGenesisState(db, genDoc) } -func GenesisState(db db_.DB, genDoc *GenesisDoc) *State { +func MakeGenesisState(db db_.DB, genDoc *GenesisDoc) *State { if len(genDoc.Validators) == 0 { panic("Must have some validators") } diff --git a/state/state.go b/state/state.go index e0bc8e5ff..5406a31b2 100644 --- a/state/state.go +++ b/state/state.go @@ -629,6 +629,7 @@ func (s *State) Hash() []byte { s.BondedValidators, s.UnbondingValidators, s.accounts, + s.validatorInfos, } return merkle.HashFromHashables(hashables) } diff --git a/state/state_test.go b/state/state_test.go index c2c7ee2f5..fb1e2ae55 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -12,7 +12,7 @@ import ( ) func TestCopyState(t *testing.T) { - // Generate a state + // Generate a random state s0, privAccounts, _ := RandGenesisState(10, true, 1000, 5, true, 1000) s0Hash := s0.Hash() if len(s0Hash) == 0 { @@ -29,19 +29,23 @@ func TestCopyState(t *testing.T) { acc0Address := privAccounts[0].PubKey.Address() acc := s0.GetAccount(acc0Address) acc.Balance += 1 + // The account balance shouldn't have changed yet. if s0.GetAccount(acc0Address).Balance == acc.Balance { t.Error("Account balance changed unexpectedly") } + // Setting, however, should change the balance. s0.SetAccount(acc) if s0.GetAccount(acc0Address).Balance != acc.Balance { t.Error("Account balance wasn't set") } - // How that the state changed, the hash should change too. + + // Now that the state changed, the hash should change too. if bytes.Equal(s0Hash, s0.Hash()) { t.Error("Expected state hash to have changed") } + // The s0Copy shouldn't have changed though. if !bytes.Equal(s0Hash, s0Copy.Hash()) { t.Error("Expected state copy hash to have not changed") @@ -52,6 +56,7 @@ func TestGenesisSaveLoad(t *testing.T) { // Generate a state, save & load it. s0, _, _ := RandGenesisState(10, true, 1000, 5, true, 1000) + // Mutate the state to append one empty block. block := &Block{ Header: &Header{ @@ -70,7 +75,8 @@ func TestGenesisSaveLoad(t *testing.T) { }, } blockParts := NewPartSetFromData(BinaryBytes(block)) - // The second argument to AppendBlock() is false, + + // The last argument to AppendBlock() is `false`, // which sets Block.Header.StateHash. err := s0.Copy().AppendBlock(block, blockParts.Header(), false) if err != nil { @@ -79,6 +85,7 @@ func TestGenesisSaveLoad(t *testing.T) { if len(block.Header.StateHash) == 0 { t.Error("Expected StateHash but got nothing.") } + // Now append the block to s0. // This time we also check the StateHash (as computed above). err = s0.AppendBlock(block, blockParts.Header(), true) @@ -108,6 +115,7 @@ func TestGenesisSaveLoad(t *testing.T) { if !bytes.Equal(s0.LastBlockHash, s1.LastBlockHash) { t.Error("LastBlockHash mismatch") } + // Compare state merkle trees if s0.BondedValidators.Size() != s1.BondedValidators.Size() { t.Error("BondedValidators Size mismatch") @@ -130,6 +138,9 @@ func TestGenesisSaveLoad(t *testing.T) { if !bytes.Equal(s0.accounts.Hash(), s1.accounts.Hash()) { t.Error("Accounts mismatch") } + if !bytes.Equal(s0.validatorInfos.Hash(), s1.validatorInfos.Hash()) { + t.Error("Accounts mismatch") + } } func TestTxSequence(t *testing.T) { @@ -285,6 +296,5 @@ func TestTxs(t *testing.T) { } // TODO UnbondTx. - // TODO NameTx. } diff --git a/state/test.go b/state/test.go index dd2b8f49c..affef40df 100644 --- a/state/test.go +++ b/state/test.go @@ -70,7 +70,7 @@ func RandGenesisState(numAccounts int, randBalance bool, minBalance uint64, numV privValidators[i] = privVal } sort.Sort(PrivValidatorsByAddress(privValidators)) - s0 := GenesisState(db, &GenesisDoc{ + s0 := MakeGenesisState(db, &GenesisDoc{ GenesisTime: time.Now(), Accounts: accounts, Validators: validators, diff --git a/state/validator.go b/state/validator.go index ef0e61631..689e834b5 100644 --- a/state/validator.go +++ b/state/validator.go @@ -10,7 +10,7 @@ import ( . "github.com/tendermint/tendermint/block" ) -// Persistent static data for each Validator +// Persistent (mostly) static data for each Validator type ValidatorInfo struct { Address []byte PubKey PubKeyEd25519 diff --git a/state/validator_set.go b/state/validator_set.go index ad8b2dedb..9db6f8bcc 100644 --- a/state/validator_set.go +++ b/state/validator_set.go @@ -12,7 +12,7 @@ import ( // ValidatorSet represent a set of *Validator at a given height. // The validators can be fetched by address or index. -// The index is in order of .Address, so the index are the same +// The index is in order of .Address, so the indices are fixed // for all rounds of a given blockchain height. // On the other hand, the .AccumPower of each validator and // the designated .Proposer() of a set changes every round,