From 249ae380683080498213be0167721c21ad46246f Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Sat, 17 Jan 2015 01:56:55 -0800 Subject: [PATCH] test bondTx --- consensus/state.go | 6 +-- consensus/test.go | 41 +++++---------- state/state.go | 51 ++++++++++-------- state/state_test.go | 125 +++++++++++++++++++++++++++++++++++--------- state/test.go | 15 ++++-- 5 files changed, 158 insertions(+), 80 deletions(-) diff --git a/consensus/state.go b/consensus/state.go index 748c05cc3..b7307407e 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -635,9 +635,7 @@ func (cs *ConsensusState) RunActionPropose(height uint, round uint) { } // Set the blk.Header.StateHash. - // TODO: we could cache the resulting state to cs.stagedState. - // TODO: This is confusing, not clear that we're mutating block. - cs.state.Copy().AppendBlock(block, blk.PartSetHeader{}, false) + cs.state.SetBlockStateHash(block) blockParts = blk.NewPartSetFromData(binary.BinaryBytes(block)) pol = cs.LockedPOL // If exists, is a PoUnlock. @@ -1019,7 +1017,7 @@ func (cs *ConsensusState) stageBlock(block *blk.Block, blockParts *blk.PartSet) // Commit block onto the copied state. // NOTE: Basic validation is done in state.AppendBlock(). - err := stateCopy.AppendBlock(block, blockParts.Header(), true) + err := stateCopy.AppendBlock(block, blockParts.Header()) if err != nil { return err } else { diff --git a/consensus/test.go b/consensus/test.go index fa93e1f63..2ccdeb3b7 100644 --- a/consensus/test.go +++ b/consensus/test.go @@ -9,34 +9,6 @@ import ( sm "github.com/tendermint/tendermint/state" ) -// Common test methods - -func makeValidator(valInfo *sm.ValidatorInfo) *sm.Validator { - return &sm.Validator{ - Address: valInfo.Address, - PubKey: valInfo.PubKey, - BondHeight: 0, - UnbondHeight: 0, - LastCommitHeight: 0, - VotingPower: valInfo.FirstBondAmount, - Accum: 0, - } -} - -func randVoteSet(height uint, round uint, type_ byte, numValidators int, votingPower uint64) (*VoteSet, *sm.ValidatorSet, []*sm.PrivValidator) { - vals := make([]*sm.Validator, numValidators) - privValidators := make([]*sm.PrivValidator, numValidators) - for i := 0; i < numValidators; i++ { - valInfo, privValidator := sm.RandValidator(false, votingPower) - val := makeValidator(valInfo) - vals[i] = val - privValidators[i] = privValidator - } - valSet := sm.NewValidatorSet(vals) - sort.Sort(sm.PrivValidatorsByAddress(privValidators)) - return NewVoteSet(height, round, type_, valSet), valSet, privValidators -} - func randConsensusState() (*ConsensusState, []*sm.PrivValidator) { state, _, privValidators := sm.RandGenesisState(20, false, 1000, 10, false, 1000) blockStore := blk.NewBlockStore(dbm.NewMemDB()) @@ -45,3 +17,16 @@ func randConsensusState() (*ConsensusState, []*sm.PrivValidator) { cs := NewConsensusState(state, blockStore, mempoolReactor) return cs, privValidators } + +func randVoteSet(height uint, round uint, type_ byte, numValidators int, votingPower uint64) (*VoteSet, *sm.ValidatorSet, []*sm.PrivValidator) { + vals := make([]*sm.Validator, numValidators) + privValidators := make([]*sm.PrivValidator, numValidators) + for i := 0; i < numValidators; i++ { + _, val, privValidator := sm.RandValidator(false, votingPower) + vals[i] = val + privValidators[i] = privValidator + } + valSet := sm.NewValidatorSet(vals) + sort.Sort(sm.PrivValidatorsByAddress(privValidators)) + return NewVoteSet(height, round, type_, valSet), valSet, privValidators +} diff --git a/state/state.go b/state/state.go index fa9cef871..49881c548 100644 --- a/state/state.go +++ b/state/state.go @@ -482,12 +482,37 @@ func (s *State) destroyValidator(val *Validator) { } -// "checkStateHash": If false, instead of checking the resulting -// state.Hash() against blk.StateHash, it *sets* the blk.StateHash. -// (used for constructing a new proposal) // NOTE: If an error occurs during block execution, state will be left // at an invalid state. Copy the state before calling AppendBlock! -func (s *State) AppendBlock(block *blk.Block, blockPartsHeader blk.PartSetHeader, checkStateHash bool) error { +func (s *State) AppendBlock(block *blk.Block, blockPartsHeader blk.PartSetHeader) error { + err := s.appendBlock(block, blockPartsHeader) + if err != nil { + return err + } + // State.Hash should match block.StateHash + stateHash := s.Hash() + if !bytes.Equal(stateHash, block.StateHash) { + return Errorf("Invalid state hash. Expected %X, got %X", + stateHash, block.StateHash) + } + return nil +} + +func (s *State) SetBlockStateHash(block *blk.Block) error { + sCopy := s.Copy() + err := sCopy.appendBlock(block, blk.PartSetHeader{}) + if err != nil { + return err + } + // Set block.StateHash + block.StateHash = sCopy.Hash() + return nil +} + +// Appends the block, does not check block.StateHash +// NOTE: If an error occurs during block execution, state will be left +// at an invalid state. Copy the state before calling appendBlock! +func (s *State) appendBlock(block *blk.Block, blockPartsHeader blk.PartSetHeader) error { // Basic block validation. err := block.ValidateBasic(s.LastBlockHeight, s.LastBlockHash, s.LastBlockParts, s.LastBlockTime) if err != nil { @@ -603,22 +628,6 @@ func (s *State) AppendBlock(block *blk.Block, blockPartsHeader blk.PartSetHeader // Increment validator AccumPowers s.BondedValidators.IncrementAccum(1) - // Check or set blk.StateHash - stateHash := s.Hash() - if checkStateHash { - // State hash should match - if !bytes.Equal(stateHash, block.StateHash) { - return Errorf("Invalid state hash. Got %X, block says %X", - stateHash, block.StateHash) - } - } else { - // Set the state hash. - if block.StateHash != nil { - panic("Cannot overwrite block.StateHash") - } - block.StateHash = stateHash - } - s.LastBlockHeight = block.Height s.LastBlockHash = block.Hash() s.LastBlockParts = blockPartsHeader @@ -674,7 +683,7 @@ func (s *State) SetValidatorInfo(valInfo *ValidatorInfo) (updated bool) { } // Returns a hash that represents the state data, -// excluding LastBlock* +// excluding Last* func (s *State) Hash() []byte { hashables := []merkle.Hashable{ s.BondedValidators, diff --git a/state/state_test.go b/state/state_test.go index c0622d196..bb2859b98 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -52,33 +52,28 @@ func TestCopyState(t *testing.T) { } } -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. +func makeBlock(t *testing.T, state *State, commits []blk.Commit, txs []blk.Tx) *blk.Block { block := &blk.Block{ Header: &blk.Header{ Network: config.App.GetString("Network"), - Height: 1, - Time: s0.LastBlockTime.Add(time.Minute), + Height: state.LastBlockHeight + 1, + Time: state.LastBlockTime.Add(time.Minute), Fees: 0, - NumTxs: 0, - LastBlockHash: s0.LastBlockHash, - LastBlockParts: s0.LastBlockParts, + NumTxs: uint(len(txs)), + LastBlockHash: state.LastBlockHash, + LastBlockParts: state.LastBlockParts, StateHash: nil, }, - Validation: &blk.Validation{}, + Validation: &blk.Validation{ + Commits: commits, + }, Data: &blk.Data{ - Txs: []blk.Tx{}, + Txs: txs, }, } - blockParts := blk.NewPartSetFromData(binary.BinaryBytes(block)) - // The last argument to AppendBlock() is `false`, - // which sets Block.Header.StateHash. - err := s0.Copy().AppendBlock(block, blockParts.Header(), false) + // Fill in block StateHash + err := state.SetBlockStateHash(block) if err != nil { t.Error("Error appending initial block:", err) } @@ -86,9 +81,20 @@ func TestGenesisSaveLoad(t *testing.T) { t.Error("Expected StateHash but got nothing.") } + return block +} + +func TestGenesisSaveLoad(t *testing.T) { + + // Generate a state, save & load it. + s0, _, _ := RandGenesisState(10, true, 1000, 5, true, 1000) + + // Make complete block and blockParts + block := makeBlock(t, s0, nil, nil) + blockParts := blk.NewPartSetFromData(binary.BinaryBytes(block)) + // Now append the block to s0. - // This time we also check the StateHash (as computed above). - err = s0.AppendBlock(block, blockParts.Header(), true) + err := s0.AppendBlock(block, blockParts.Header()) if err != nil { t.Error("Error appending initial block:", err) } @@ -143,11 +149,6 @@ func TestGenesisSaveLoad(t *testing.T) { } } -/* TODO -func TestSendTxStateSave(t *testing.T) { -} -*/ - func TestTxSequence(t *testing.T) { state, privAccounts, _ := RandGenesisState(3, true, 1000, 1, true, 1000) @@ -308,3 +309,79 @@ func TestTxs(t *testing.T) { // TODO UnbondTx. } + +func TestAddValidator(t *testing.T) { + + // Generate a state, save & load it. + s0, privAccounts, privValidators := RandGenesisState(10, false, 1000, 1, false, 1000) + + // The first privAccount will become a validator + acc0 := privAccounts[0] + bondTx := &blk.BondTx{ + PubKey: acc0.PubKey.(account.PubKeyEd25519), + Inputs: []*blk.TxInput{ + &blk.TxInput{ + Address: acc0.Address, + Amount: 1000, + Sequence: 1, + PubKey: acc0.PubKey, + }, + }, + UnbondTo: []*blk.TxOutput{ + &blk.TxOutput{ + Address: acc0.Address, + Amount: 1000, + }, + }, + } + bondTx.Inputs[0].Signature = acc0.Sign(bondTx) + + // Make complete block and blockParts + block0 := makeBlock(t, s0, nil, []blk.Tx{bondTx}) + block0Parts := blk.NewPartSetFromData(binary.BinaryBytes(block0)) + + // Sanity check + if s0.BondedValidators.Size() != 1 { + t.Error("Expected there to be 1 validators before bondTx") + } + + // Now append the block to s0. + err := s0.AppendBlock(block0, block0Parts.Header()) + if err != nil { + t.Error("Error appending initial block:", err) + } + + // Must save before further modification + s0.Save() + + // Test new validator set + if s0.BondedValidators.Size() != 2 { + t.Error("Expected there to be 2 validators after bondTx") + } + + // The validation for the next block should only require 1 signature + // (the new validator wasn't active for block0) + commit0 := &blk.Vote{ + Height: 1, + Round: 0, + Type: blk.VoteTypeCommit, + BlockHash: block0.Hash(), + BlockParts: block0Parts.Header(), + } + privValidators[0].SignVote(commit0) + + block1 := makeBlock(t, s0, + []blk.Commit{ + blk.Commit{ + Address: privValidators[0].Address, + Round: 0, + Signature: commit0.Signature, + }, + }, nil, + ) + block1Parts := blk.NewPartSetFromData(binary.BinaryBytes(block1)) + err = s0.AppendBlock(block1, block1Parts.Header()) + if err != nil { + t.Error("Error appending secondary block:", err) + } +} diff --git a/state/test.go b/state/test.go index 5ca772ac9..05b079ec0 100644 --- a/state/test.go +++ b/state/test.go @@ -36,7 +36,7 @@ func RandAccount(randBalance bool, minBalance uint64) (*account.Account, *accoun return acc, privAccount } -func RandValidator(randBonded bool, minBonded uint64) (*ValidatorInfo, *PrivValidator) { +func RandValidator(randBonded bool, minBonded uint64) (*ValidatorInfo, *Validator, *PrivValidator) { privVal := GenPrivValidator() _, privVal.filename = Tempfile("priv_validator_") bonded := minBonded @@ -53,7 +53,16 @@ func RandValidator(randBonded bool, minBonded uint64) (*ValidatorInfo, *PrivVali FirstBondHeight: 0, FirstBondAmount: bonded, } - return valInfo, privVal + val := &Validator{ + Address: valInfo.Address, + PubKey: valInfo.PubKey, + BondHeight: 0, + UnbondHeight: 0, + LastCommitHeight: 0, + VotingPower: valInfo.FirstBondAmount, + Accum: 0, + } + return valInfo, val, privVal } func RandGenesisState(numAccounts int, randBalance bool, minBalance uint64, numValidators int, randBonded bool, minBonded uint64) (*State, []*account.PrivAccount, []*PrivValidator) { @@ -71,7 +80,7 @@ func RandGenesisState(numAccounts int, randBalance bool, minBalance uint64, numV validators := make([]GenesisValidator, numValidators) privValidators := make([]*PrivValidator, numValidators) for i := 0; i < numValidators; i++ { - valInfo, privVal := RandValidator(randBonded, minBonded) + valInfo, _, privVal := RandValidator(randBonded, minBonded) validators[i] = GenesisValidator{ PubKey: valInfo.PubKey, Amount: valInfo.FirstBondAmount,