From 2b6db268cfb1cacfe8fb70347f68a8622c87fb50 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 12 Sep 2017 15:20:19 -0400 Subject: [PATCH] genesis json tests and mv ConsensusParams to types --- blockchain/reactor.go | 4 +- config/consensus.go | 27 -------------- consensus/replay_test.go | 8 ++-- consensus/state.go | 4 +- consensus/state_test.go | 14 +++---- state/state.go | 28 ++++++++------ types/genesis.go | 14 +++---- types/genesis_test.go | 79 ++++++++++++++++++++++++++++++++++++++++ types/params.go | 32 ++++++++++++++++ 9 files changed, 149 insertions(+), 61 deletions(-) delete mode 100644 config/consensus.go create mode 100644 types/genesis_test.go create mode 100644 types/params.go diff --git a/blockchain/reactor.go b/blockchain/reactor.go index fa3c70799..fccfcdf8f 100644 --- a/blockchain/reactor.go +++ b/blockchain/reactor.go @@ -165,7 +165,7 @@ func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) // maxMsgSize returns the maximum allowable size of a // message on the blockchain reactor. func (bcR *BlockchainReactor) maxMsgSize() int { - return bcR.state.GenesisDoc.ConsensusParams.MaxBlockSizeBytes + 2 + return bcR.state.Params().MaxBlockSizeBytes + 2 } // Handle messages from the poolReactor telling the reactor what to do. @@ -226,7 +226,7 @@ FOR_LOOP: // We need both to sync the first block. break SYNC_LOOP } - firstParts := first.MakePartSet(bcR.state.GenesisDoc.ConsensusParams.BlockPartSizeBytes) + firstParts := first.MakePartSet(bcR.state.Params().BlockPartSizeBytes) firstPartsHeader := firstParts.Header() // Finally, verify the first block using the second's commit // NOTE: we can probably make this more efficient, but note that calling diff --git a/config/consensus.go b/config/consensus.go deleted file mode 100644 index 5b10911d8..000000000 --- a/config/consensus.go +++ /dev/null @@ -1,27 +0,0 @@ -package config - -import ( - "fmt" -) - -type ConsensusParams struct { - MaxBlockSizeBytes int `json:"max_block_size_bytes"` - BlockPartSizeBytes int `json:"block_part_size_bytes"` -} - -func DefaultConsensusParams() *ConsensusParams { - return &ConsensusParams{ - MaxBlockSizeBytes: 22020096, // 21MB - BlockPartSizeBytes: 65536, // 64kB, - } -} - -func (params *ConsensusParams) Validate() error { - if params.MaxBlockSizeBytes <= 0 { - return fmt.Errorf("MaxBlockSizeBytes must be greater than 0. Got %d", params.MaxBlockSizeBytes) - } - if params.BlockPartSizeBytes <= 0 { - return fmt.Errorf("BlockPartSizeBytes must be greater than 0. Got %d", params.BlockPartSizeBytes) - } - return nil -} diff --git a/consensus/replay_test.go b/consensus/replay_test.go index 179e8696c..3fa885176 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -381,7 +381,7 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) { } func applyBlock(st *sm.State, blk *types.Block, proxyApp proxy.AppConns) { - testPartSize := st.GenesisDoc.ConsensusParams.BlockPartSizeBytes + testPartSize := st.Params().BlockPartSizeBytes err := st.ApplyBlock(nil, proxyApp.Consensus(), blk, blk.MakePartSet(testPartSize).Header(), mempool) if err != nil { panic(err) @@ -561,7 +561,7 @@ func stateAndStore(config *cfg.Config, pubKey crypto.PubKey) (*sm.State, *mockBl state := sm.MakeGenesisStateFromFile(stateDB, config.GenesisFile()) state.SetLogger(log.TestingLogger().With("module", "state")) - store := NewMockBlockStore(config, state.GenesisDoc.ConsensusParams) + store := NewMockBlockStore(config, state.Params()) return state, store } @@ -570,13 +570,13 @@ func stateAndStore(config *cfg.Config, pubKey crypto.PubKey) (*sm.State, *mockBl type mockBlockStore struct { config *cfg.Config - params *cfg.ConsensusParams + params *types.ConsensusParams chain []*types.Block commits []*types.Commit } // TODO: NewBlockStore(db.NewMemDB) ... -func NewMockBlockStore(config *cfg.Config, params *cfg.ConsensusParams) *mockBlockStore { +func NewMockBlockStore(config *cfg.Config, params *types.ConsensusParams) *mockBlockStore { return &mockBlockStore{config, params, nil, nil} } diff --git a/consensus/state.go b/consensus/state.go index 8a19a052d..1a5b26e62 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -984,7 +984,7 @@ func (cs *ConsensusState) createProposalBlock() (block *types.Block, blockParts return types.MakeBlock(cs.Height, cs.state.ChainID, txs, commit, cs.state.LastBlockID, cs.state.Validators.Hash(), - cs.state.AppHash, cs.state.GenesisDoc.ConsensusParams.BlockPartSizeBytes) + cs.state.AppHash, cs.state.Params().BlockPartSizeBytes) } // Enter: `timeoutPropose` after entering Propose. @@ -1419,7 +1419,7 @@ func (cs *ConsensusState) addProposalBlockPart(height int, part *types.Part, ver var n int var err error cs.ProposalBlock = wire.ReadBinary(&types.Block{}, cs.ProposalBlockParts.GetReader(), - cs.state.GenesisDoc.ConsensusParams.MaxBlockSizeBytes, &n, &err).(*types.Block) + cs.state.Params().MaxBlockSizeBytes, &n, &err).(*types.Block) // NOTE: it's possible to receive complete proposal blocks for future rounds without having the proposal cs.Logger.Info("Received complete proposal block", "height", cs.ProposalBlock.Height, "hash", cs.ProposalBlock.Hash()) if cs.Step == RoundStepPropose && cs.isProposalComplete() { diff --git a/consensus/state_test.go b/consensus/state_test.go index 6f804fcb4..9ae052033 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -180,7 +180,7 @@ func TestBadProposal(t *testing.T) { height, round := cs1.Height, cs1.Round vs2 := vss[1] - partSize := cs1.state.GenesisDoc.ConsensusParams.BlockPartSizeBytes + partSize := cs1.state.Params().BlockPartSizeBytes proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1) voteCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringVote(), 1) @@ -327,7 +327,7 @@ func TestLockNoPOL(t *testing.T) { vs2 := vss[1] height := cs1.Height - partSize := cs1.state.GenesisDoc.ConsensusParams.BlockPartSizeBytes + partSize := cs1.state.Params().BlockPartSizeBytes timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1) timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1) @@ -493,7 +493,7 @@ func TestLockPOLRelock(t *testing.T) { cs1, vss := randConsensusState(4) vs2, vs3, vs4 := vss[1], vss[2], vss[3] - partSize := cs1.state.GenesisDoc.ConsensusParams.BlockPartSizeBytes + partSize := cs1.state.Params().BlockPartSizeBytes timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1) timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1) @@ -608,7 +608,7 @@ func TestLockPOLUnlock(t *testing.T) { cs1, vss := randConsensusState(4) vs2, vs3, vs4 := vss[1], vss[2], vss[3] - partSize := cs1.state.GenesisDoc.ConsensusParams.BlockPartSizeBytes + partSize := cs1.state.Params().BlockPartSizeBytes proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1) timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1) @@ -703,7 +703,7 @@ func TestLockPOLSafety1(t *testing.T) { cs1, vss := randConsensusState(4) vs2, vs3, vs4 := vss[1], vss[2], vss[3] - partSize := cs1.state.GenesisDoc.ConsensusParams.BlockPartSizeBytes + partSize := cs1.state.Params().BlockPartSizeBytes proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1) timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1) @@ -824,7 +824,7 @@ func TestLockPOLSafety2(t *testing.T) { cs1, vss := randConsensusState(4) vs2, vs3, vs4 := vss[1], vss[2], vss[3] - partSize := cs1.state.GenesisDoc.ConsensusParams.BlockPartSizeBytes + partSize := cs1.state.Params().BlockPartSizeBytes proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1) timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1) @@ -999,7 +999,7 @@ func TestHalt1(t *testing.T) { cs1, vss := randConsensusState(4) vs2, vs3, vs4 := vss[1], vss[2], vss[3] - partSize := cs1.state.GenesisDoc.ConsensusParams.BlockPartSizeBytes + partSize := cs1.state.Params().BlockPartSizeBytes proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1) timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1) diff --git a/state/state.go b/state/state.go index 4e0fa75b5..0ea15acaf 100644 --- a/state/state.go +++ b/state/state.go @@ -65,6 +65,19 @@ type State struct { logger log.Logger } +// GetState loads the most recent state from the database, +// or creates a new one from the given genesisFile and persists the result +// to the database. +func GetState(stateDB dbm.DB, genesisFile string) *State { + state := LoadState(stateDB) + if state == nil { + state = MakeGenesisStateFromFile(stateDB, genesisFile) + state.Save() + } + + return state +} + // LoadState loads the State from the database. func LoadState(db dbm.DB) *State { return loadState(db, stateKey) @@ -248,17 +261,10 @@ func (s *State) GetValidators() (*types.ValidatorSet, *types.ValidatorSet) { return s.LastValidators, s.Validators } -// GetState loads the most recent state from the database, -// or creates a new one from the given genesisFile and persists the result -// to the database. -func GetState(stateDB dbm.DB, genesisFile string) *State { - state := LoadState(stateDB) - if state == nil { - state = MakeGenesisStateFromFile(stateDB, genesisFile) - state.Save() - } - - return state +// Params returns the consensus parameters used for +// validating blocks +func (s *State) Params() *types.ConsensusParams { + return s.GenesisDoc.ConsensusParams } //------------------------------------------------------------------------ diff --git a/types/genesis.go b/types/genesis.go index 79bf63c8b..822a4e166 100644 --- a/types/genesis.go +++ b/types/genesis.go @@ -10,8 +10,6 @@ import ( "github.com/tendermint/go-crypto" "github.com/tendermint/go-wire/data" cmn "github.com/tendermint/tmlibs/common" - - cfg "github.com/tendermint/tendermint/config" ) //------------------------------------------------------------ @@ -26,11 +24,11 @@ type GenesisValidator struct { // GenesisDoc defines the initial conditions for a tendermint blockchain, in particular its validator set. type GenesisDoc struct { - GenesisTime time.Time `json:"genesis_time"` - ChainID string `json:"chain_id"` - ConsensusParams *cfg.ConsensusParams `json:"consensus_params"` - Validators []GenesisValidator `json:"validators"` - AppHash data.Bytes `json:"app_hash"` + GenesisTime time.Time `json:"genesis_time"` + ChainID string `json:"chain_id"` + ConsensusParams *ConsensusParams `json:"consensus_params"` + Validators []GenesisValidator `json:"validators"` + AppHash data.Bytes `json:"app_hash"` } // SaveAs is a utility method for saving GenensisDoc as a JSON file. @@ -61,7 +59,7 @@ func (genDoc *GenesisDoc) ValidateAndComplete() error { } if genDoc.ConsensusParams == nil { - genDoc.ConsensusParams = cfg.DefaultConsensusParams() + genDoc.ConsensusParams = DefaultConsensusParams() } else { if err := genDoc.ConsensusParams.Validate(); err != nil { return err diff --git a/types/genesis_test.go b/types/genesis_test.go new file mode 100644 index 000000000..9743a9610 --- /dev/null +++ b/types/genesis_test.go @@ -0,0 +1,79 @@ +package types + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + crypto "github.com/tendermint/go-crypto" +) + +func TestGenesis(t *testing.T) { + + // test some bad ones from raw json + testCases := [][]byte{ + []byte{}, // empty + []byte{1, 1, 1, 1, 1}, // junk + []byte(`{}`), // empty + []byte(`{"chain_id": "mychain"}`), // missing validators + []byte(`{"validators": [{"data":abcd}`), // missing validators + []byte(`{"validators":[{"pub_key": + {"type":"ed25519","data":"961EAB8752E51A03618502F55C2B6E09C38C65635C64CCF3173ED452CF86C957"}, + "amount":10,"name":""}]}`), // missing chain_id + } + + for _, testCase := range testCases { + _, err := GenesisDocFromJSON(testCase) + assert.NotNil(t, err, "expected error for empty genDoc json") + } + + // test a good one by raw json + genDocBytes := []byte(`{"genesis_time":"0001-01-01T00:00:00Z","chain_id":"test-chain-QDKdJr","consensus_params":null,"validators":[{"pub_key":{"type":"ed25519","data":"961EAB8752E51A03618502F55C2B6E09C38C65635C64CCF3173ED452CF86C957"},"amount":10,"name":""}],"app_hash":""}`) + _, err := GenesisDocFromJSON(genDocBytes) + assert.Nil(t, err, "expected no error for good genDoc json") + + // create a base gendoc from struct + baseGenDoc := &GenesisDoc{ + ChainID: "abc", + Validators: []GenesisValidator{{crypto.GenPrivKeyEd25519().PubKey(), 10, "myval"}}, + } + genDocBytes, err = json.Marshal(baseGenDoc) + assert.Nil(t, err, "error marshalling genDoc") + + // test base gendoc and check consensus params were filled + genDoc, err := GenesisDocFromJSON(genDocBytes) + assert.Nil(t, err, "expected no error for valid genDoc json") + assert.NotNil(t, genDoc.ConsensusParams, "expected consensus params to be filled in") + + // create json with consensus params filled + genDocBytes, err = json.Marshal(genDoc) + assert.Nil(t, err, "error marshalling genDoc") + genDoc, err = GenesisDocFromJSON(genDocBytes) + assert.Nil(t, err, "expected no error for valid genDoc json") + + // test with invalid consensus params + genDoc.ConsensusParams.MaxBlockSizeBytes = 0 + genDocBytes, err = json.Marshal(genDoc) + assert.Nil(t, err, "error marshalling genDoc") + genDoc, err = GenesisDocFromJSON(genDocBytes) + assert.NotNil(t, err, "expected error for genDoc json with block size of 0") +} + +func TestConsensusParams(t *testing.T) { + testCases := []struct { + params *ConsensusParams + valid bool + }{ + {&ConsensusParams{1, 1}, true}, + {&ConsensusParams{1, 0}, false}, + {&ConsensusParams{0, 1}, false}, + {&ConsensusParams{0, 0}, false}, + } + for _, testCase := range testCases { + if testCase.valid { + assert.Nil(t, testCase.params.Validate(), "expected no error for valid params") + } else { + assert.NotNil(t, testCase.params.Validate(), "expected error for non valid params") + } + } +} diff --git a/types/params.go b/types/params.go new file mode 100644 index 000000000..cff9ae3d3 --- /dev/null +++ b/types/params.go @@ -0,0 +1,32 @@ +package types + +import ( + "github.com/pkg/errors" +) + +// ConsensusParams contains consensus critical parameters +// that determine the validity of blocks. +type ConsensusParams struct { + MaxBlockSizeBytes int `json:"max_block_size_bytes"` + BlockPartSizeBytes int `json:"block_part_size_bytes"` +} + +// DefaultConsensusParams returns a default ConsensusParams. +func DefaultConsensusParams() *ConsensusParams { + return &ConsensusParams{ + MaxBlockSizeBytes: 22020096, // 21MB + BlockPartSizeBytes: 65536, // 64kB, + } +} + +// Validate validates the ConsensusParams to ensure all values +// are within their allowed limits, and returns an error if they are not. +func (params *ConsensusParams) Validate() error { + if params.MaxBlockSizeBytes <= 0 { + return errors.Errorf("MaxBlockSizeBytes must be greater than 0. Got %d", params.MaxBlockSizeBytes) + } + if params.BlockPartSizeBytes <= 0 { + return errors.Errorf("BlockPartSizeBytes must be greater than 0. Got %d", params.BlockPartSizeBytes) + } + return nil +}