From f69e54e750d03161e4da51965d03c6785f05a5db Mon Sep 17 00:00:00 2001 From: Woosang Son Date: Fri, 27 Mar 2020 08:23:33 +0900 Subject: [PATCH 1/9] feat: store proof, round on block --- blockchain/v0/reactor_test.go | 8 ++++--- blockchain/v1/reactor_test.go | 8 ++++--- consensus/replay_test.go | 4 +++- consensus/state.go | 17 +++++++++++---- node/node_test.go | 14 ++++++++++--- privval/signer_requestHandler.go | 3 +-- state/execution.go | 6 +++++- state/execution_test.go | 22 +++++++++++-------- state/helpers_test.go | 21 ++++++++++++++++--- state/state.go | 36 ++++++++++++++++++++++++++++++++ state/state_test.go | 16 ++++++++++++++ state/validation.go | 15 +++++++++++++ state/validation_test.go | 14 +++++++++---- store/store_test.go | 2 +- types/block.go | 16 ++++++++++++++ types/block_test.go | 4 +++- types/errors.go | 13 ++++++++++++ types/protobuf_test.go | 2 +- 18 files changed, 185 insertions(+), 36 deletions(-) diff --git a/blockchain/v0/reactor_test.go b/blockchain/v0/reactor_test.go index 08ff10a93..602576cbb 100644 --- a/blockchain/v0/reactor_test.go +++ b/blockchain/v0/reactor_test.go @@ -105,7 +105,7 @@ func newBlockchainReactor( lastBlockMeta.BlockID, []types.CommitSig{vote.CommitSig()}) } - thisBlock := makeBlock(blockHeight, state, lastCommit) + thisBlock := makeBlock(privVals[0], blockHeight, state, lastCommit) thisParts := thisBlock.MakePartSet(types.BlockPartSizeBytes) blockID := types.BlockID{Hash: thisBlock.Hash(), PartsHeader: thisParts.Header()} @@ -345,8 +345,10 @@ func makeTxs(height int64) (txs []types.Tx) { return txs } -func makeBlock(height int64, state sm.State, lastCommit *types.Commit) *types.Block { - block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, state.Validators.GetProposer().Address) +func makeBlock(privVal types.PrivValidator, height int64, state sm.State, lastCommit *types.Commit) *types.Block { + message, _ := state.MakeHashMessage(0) + proof, _ := privVal.GenerateVRFProof(message) + block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, state.Validators.GetProposer().Address, 0, proof) return block } diff --git a/blockchain/v1/reactor_test.go b/blockchain/v1/reactor_test.go index 2add11df1..724f39773 100644 --- a/blockchain/v1/reactor_test.go +++ b/blockchain/v1/reactor_test.go @@ -118,7 +118,7 @@ func newBlockchainReactor( lastCommit = types.NewCommit(vote.Height, vote.Round, lastBlockMeta.BlockID, []types.CommitSig{vote.CommitSig()}) } - thisBlock := makeBlock(blockHeight, state, lastCommit) + thisBlock := makeBlock(privVals[0], blockHeight, state, lastCommit) thisParts := thisBlock.MakePartSet(types.BlockPartSizeBytes) blockID := types.BlockID{Hash: thisBlock.Hash(), PartsHeader: thisParts.Header()} @@ -419,8 +419,10 @@ func makeTxs(height int64) (txs []types.Tx) { return txs } -func makeBlock(height int64, state sm.State, lastCommit *types.Commit) *types.Block { - block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, state.Validators.GetProposer().Address) +func makeBlock(privVal types.PrivValidator, height int64, state sm.State, lastCommit *types.Commit) *types.Block { + message, _ := state.MakeHashMessage(0) + proof, _ := privVal.GenerateVRFProof(message) + block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, state.Validators.GetProposer().Address, 0, proof) return block } diff --git a/consensus/replay_test.go b/consensus/replay_test.go index 530948a18..f8ec790b1 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -897,7 +897,9 @@ func makeBlock(state sm.State, lastBlock *types.Block, lastBlockMeta *types.Bloc lastBlockMeta.BlockID, []types.CommitSig{vote.CommitSig()}) } - return state.MakeBlock(height, []types.Tx{}, lastCommit, nil, state.Validators.GetProposer().Address) + message, _ := state.MakeHashMessage(0) + proof, _ := privVal.GenerateVRFProof(message) + return state.MakeBlock(height, []types.Tx{}, lastCommit, nil, state.Validators.GetProposer().Address, 0, proof) } type badApp struct { diff --git a/consensus/state.go b/consensus/state.go index 5055fdd68..8dc05306f 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -9,7 +9,6 @@ import ( "time" "github.com/pkg/errors" - "github.com/tendermint/tendermint/libs/fail" "github.com/tendermint/tendermint/libs/log" tmos "github.com/tendermint/tendermint/libs/os" @@ -899,7 +898,6 @@ func (cs *State) enterPropose(height int64, round int) { return } logger.Info(fmt.Sprintf("enterPropose(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) - defer func() { // Done enterPropose: cs.updateRoundStep(round, cstypes.RoundStepPropose) @@ -1026,7 +1024,19 @@ func (cs *State) createProposalBlock() (block *types.Block, blockParts *types.Pa } proposerAddr := cs.privValidator.GetPubKey().Address() - return cs.blockExec.CreateProposalBlock(cs.Height, cs.state, commit, proposerAddr) + + message, err := cs.state.MakeHashMessage(cs.Round) + if err != nil { + cs.Logger.Error("enterPropose: Cannot generate vrf message: %s", err.Error()) + return + } + + proof, err := cs.privValidator.GenerateVRFProof(message) + if err != nil { + cs.Logger.Error("enterPropose: Cannot generate vrf proof: %s", err.Error()) + return + } + return cs.blockExec.CreateProposalBlock(cs.Height, cs.state, commit, proposerAddr, cs.Round, proof) } // Enter: `timeoutPropose` after entering Propose. @@ -1448,7 +1458,6 @@ func (cs *State) finalizeCommit(height int64) { // NewHeightStep! cs.updateToState(stateCopy) - fail.Fail() // XXX // cs.StartTime is already set. diff --git a/node/node_test.go b/node/node_test.go index ec8510d58..7b571bc2a 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -228,7 +228,7 @@ func TestCreateProposalBlock(t *testing.T) { logger := log.TestingLogger() var height int64 = 1 - state, stateDB := state(1, height) + state, stateDB, privVal := state(1, height) maxBytes := 16384 state.ConsensusParams.Block.MaxBytes = int64(maxBytes) proposerAddr, _ := state.Validators.GetByIndex(0) @@ -280,10 +280,14 @@ func TestCreateProposalBlock(t *testing.T) { ) commit := types.NewCommit(height-1, 0, types.BlockID{}, nil) + message, _ := state.MakeHashMessage(0) + proof, _ := privVal.GenerateVRFProof(message) block, _ := blockExec.CreateProposalBlock( height, state, commit, proposerAddr, + 0, + proof, ) err = blockExec.ValidateBlock(state, block) @@ -323,11 +327,15 @@ func TestNodeNewNodeCustomReactors(t *testing.T) { assert.Equal(t, customBlockchainReactor, n.Switch().Reactor("BLOCKCHAIN")) } -func state(nVals int, height int64) (sm.State, dbm.DB) { +func state(nVals int, height int64) (sm.State, dbm.DB, types.PrivValidator) { vals := make([]types.GenesisValidator, nVals) + var privVal types.PrivValidator for i := 0; i < nVals; i++ { secret := []byte(fmt.Sprintf("test%d", i)) pk := ed25519.GenPrivKeyFromSecret(secret) + if privVal == nil { + privVal = types.NewMockPVWithParams(pk, false, false) + } vals[i] = types.GenesisValidator{ Address: pk.PubKey().Address(), PubKey: pk.PubKey(), @@ -350,5 +358,5 @@ func state(nVals int, height int64) (sm.State, dbm.DB) { s.LastValidators = s.Validators.Copy() sm.SaveState(stateDB, s) } - return s, stateDB + return s, stateDB, privVal } diff --git a/privval/signer_requestHandler.go b/privval/signer_requestHandler.go index 823d93338..b6cf4c01e 100644 --- a/privval/signer_requestHandler.go +++ b/privval/signer_requestHandler.go @@ -38,8 +38,7 @@ func DefaultValidationRequestHandler( } case *VRFProofRequest: - message := r.Message - proof, err := privVal.GenerateVRFProof(message) + proof, err := privVal.GenerateVRFProof(r.Message) if err != nil { res = &VRFProofResponse{nil, &RemoteSignerError{0, err.Error()}} } else { diff --git a/state/execution.go b/state/execution.go index 4025b6e7b..92c425421 100644 --- a/state/execution.go +++ b/state/execution.go @@ -5,6 +5,7 @@ import ( "time" abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/vrf" "github.com/tendermint/tendermint/libs/fail" "github.com/tendermint/tendermint/libs/log" mempl "github.com/tendermint/tendermint/mempool" @@ -92,6 +93,8 @@ func (blockExec *BlockExecutor) CreateProposalBlock( height int64, state State, commit *types.Commit, proposerAddr []byte, + round int, + proof vrf.Proof, ) (*types.Block, *types.PartSet) { maxBytes := state.ConsensusParams.Block.MaxBytes @@ -105,7 +108,7 @@ func (blockExec *BlockExecutor) CreateProposalBlock( maxDataBytes := types.MaxDataBytes(maxBytes, state.Validators.Size(), len(evidence)) txs := blockExec.mempool.ReapMaxBytesMaxGas(maxDataBytes, maxGas) - return state.MakeBlock(height, txs, commit, evidence, proposerAddr) + return state.MakeBlock(height, txs, commit, evidence, proposerAddr, round, proof) } // ValidateBlock validates the given block against the given state. @@ -430,6 +433,7 @@ func updateState( LastBlockHeight: header.Height, LastBlockID: blockID, LastBlockTime: header.Time, + LastProof: header.Proof, NextValidators: nValSet, Validators: state.NextValidators.Copy(), LastValidators: state.Validators.Copy(), diff --git a/state/execution_test.go b/state/execution_test.go index 78f4d6ca4..41c9dbea7 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -32,12 +32,12 @@ func TestApplyBlock(t *testing.T) { require.Nil(t, err) defer proxyApp.Stop() - state, stateDB, _ := makeState(1, 1) + state, stateDB, privVals := makeState(1, 1) blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mock.Mempool{}, sm.MockEvidencePool{}) - block := makeBlock(state, 1) + block := makeBlockWithPrivVal(state, privVals[state.Validators.Proposer.Address.String()], 1) blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} //nolint:ineffassign @@ -56,7 +56,7 @@ func TestBeginBlockValidators(t *testing.T) { require.Nil(t, err) defer proxyApp.Stop() - state, stateDB, _ := makeState(2, 2) + state, stateDB, privVals := makeState(2, 2) prevHash := state.LastBlockID.Hash prevParts := types.PartSetHeader{} @@ -88,8 +88,11 @@ func TestBeginBlockValidators(t *testing.T) { for _, tc := range testCases { lastCommit := types.NewCommit(1, 0, prevBlockID, tc.lastCommitSigs) + message, _ := state.MakeHashMessage(0) + proof, _ := privVals[state.Validators.GetProposer().Address.String()].GenerateVRFProof(message) + // block for height 2 - block, _ := state.MakeBlock(2, makeTxs(2), lastCommit, nil, state.Validators.GetProposer().Address) + block, _ := state.MakeBlock(2, makeTxs(2), lastCommit, nil, state.Validators.GetProposer().Address, 0, proof) _, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateDB) require.Nil(t, err, tc.desc) @@ -118,7 +121,7 @@ func TestBeginBlockByzantineValidators(t *testing.T) { require.Nil(t, err) defer proxyApp.Stop() - state, stateDB, _ := makeState(2, 12) + state, stateDB, privVals := makeState(2, 12) prevHash := state.LastBlockID.Hash prevParts := types.PartSetHeader{} @@ -156,8 +159,9 @@ func TestBeginBlockByzantineValidators(t *testing.T) { commitSigs := []types.CommitSig{commitSig0, commitSig1} lastCommit := types.NewCommit(9, 0, prevBlockID, commitSigs) for _, tc := range testCases { - - block, _ := state.MakeBlock(10, makeTxs(2), lastCommit, nil, state.Validators.GetProposer().Address) + message, _ := state.MakeHashMessage(0) + proof, _ := privVals[state.Validators.GetProposer().Address.String()].GenerateVRFProof(message) + block, _ := state.MakeBlock(10, makeTxs(2), lastCommit, nil, state.Validators.GetProposer().Address, 0, proof) block.Time = now block.Evidence.Evidence = tc.evidence _, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateDB) @@ -324,7 +328,7 @@ func TestEndBlockValidatorUpdates(t *testing.T) { require.Nil(t, err) defer proxyApp.Stop() - state, stateDB, _ := makeState(1, 1) + state, stateDB, privVals := makeState(1, 1) blockExec := sm.NewBlockExecutor( stateDB, @@ -347,7 +351,7 @@ func TestEndBlockValidatorUpdates(t *testing.T) { ) require.NoError(t, err) - block := makeBlock(state, 1) + block := makeBlockWithPrivVal(state, privVals[state.Validators.Proposer.Address.String()], 1) blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} pubkey := ed25519.GenPrivKey().PubKey() diff --git a/state/helpers_test.go b/state/helpers_test.go index fec965016..6034efe41 100644 --- a/state/helpers_test.go +++ b/state/helpers_test.go @@ -45,7 +45,7 @@ func makeAndCommitGoodBlock( privVals map[string]types.PrivValidator, evidence []types.Evidence) (sm.State, types.BlockID, *types.Commit, error) { // A good block passes - state, blockID, err := makeAndApplyGoodBlock(state, height, lastCommit, proposerAddr, blockExec, evidence) + state, blockID, err := makeAndApplyGoodBlock(state, privVals[state.Validators.Proposer.Address.String()], height, lastCommit, proposerAddr, blockExec, evidence) if err != nil { return state, types.BlockID{}, nil, err } @@ -58,9 +58,11 @@ func makeAndCommitGoodBlock( return state, blockID, commit, nil } -func makeAndApplyGoodBlock(state sm.State, height int64, lastCommit *types.Commit, proposerAddr []byte, +func makeAndApplyGoodBlock(state sm.State, privVal types.PrivValidator, height int64, lastCommit *types.Commit, proposerAddr []byte, blockExec *sm.BlockExecutor, evidence []types.Evidence) (sm.State, types.BlockID, error) { - block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, evidence, proposerAddr) + message, _ := state.MakeHashMessage(0) + proof, _ := privVal.GenerateVRFProof(message) + block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, evidence, proposerAddr, 0, proof) if err := blockExec.ValidateBlock(state, block); err != nil { return state, types.BlockID{}, err } @@ -99,6 +101,11 @@ func makeTxs(height int64) (txs []types.Tx) { return txs } +func makePrivVal() types.PrivValidator { + pk := ed25519.GenPrivKeyFromSecret([]byte("test private validator")) + return types.NewMockPVWithParams(pk, false, false) +} + func makeState(nVals, height int) (sm.State, dbm.DB, map[string]types.PrivValidator) { vals := make([]types.GenesisValidator, nVals) privVals := make(map[string]types.PrivValidator, nVals) @@ -132,12 +139,20 @@ func makeState(nVals, height int) (sm.State, dbm.DB, map[string]types.PrivValida } func makeBlock(state sm.State, height int64) *types.Block { + return makeBlockWithPrivVal(state, makePrivVal(), height) +} + +func makeBlockWithPrivVal(state sm.State, privVal types.PrivValidator, height int64) *types.Block { + message, _ := state.MakeHashMessage(0) + proof, _ := privVal.GenerateVRFProof(message) block, _ := state.MakeBlock( height, makeTxs(state.LastBlockHeight), new(types.Commit), nil, state.Validators.GetProposer().Address, + 0, + proof, ) return block } diff --git a/state/state.go b/state/state.go index e0612576a..24005f69d 100644 --- a/state/state.go +++ b/state/state.go @@ -2,10 +2,13 @@ package state import ( "bytes" + "encoding/binary" "fmt" "io/ioutil" "time" + "github.com/tendermint/tendermint/crypto/tmhash" + "github.com/tendermint/tendermint/crypto/vrf" "github.com/tendermint/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" "github.com/tendermint/tendermint/version" @@ -59,6 +62,9 @@ type State struct { LastBlockID types.BlockID LastBlockTime time.Time + // vrf proof + LastProof vrf.Proof + // LastValidators is used to validate block.LastCommit. // Validators are persisted to the database separately every time they change, // so we can query for historical validator sets. @@ -82,6 +88,27 @@ type State struct { AppHash []byte } +func (state State) MakeHashMessage(round int) ([]byte, error) { + var seed []byte + + if len(state.LastProof) == 0 { + // TODO: This code is temporary. When genesis seed is prepared, use that code. + seed = []byte("LINE Blockchain VRF Algorithm's first seed") + } else { + output, err := vrf.ProofToHash(state.LastProof) + if err != nil { + return nil, err + } + seed = output + } + b := make([]byte, 16) + binary.PutVarint(b, state.LastBlockHeight) + binary.PutVarint(b[8:], int64(round)) + hash := tmhash.New() + hash.Write(seed) + return hash.Sum(b), nil +} + // Copy makes a copy of the State for mutating. func (state State) Copy() State { return State{ @@ -92,6 +119,8 @@ func (state State) Copy() State { LastBlockID: state.LastBlockID, LastBlockTime: state.LastBlockTime, + LastProof: state.LastProof, + NextValidators: state.NextValidators.Copy(), Validators: state.Validators.Copy(), LastValidators: state.LastValidators.Copy(), @@ -134,6 +163,8 @@ func (state State) MakeBlock( commit *types.Commit, evidence []types.Evidence, proposerAddress []byte, + round int, + proof vrf.Proof, ) (*types.Block, *types.PartSet) { // Build base block with block data. @@ -154,6 +185,8 @@ func (state State) MakeBlock( state.Validators.Hash(), state.NextValidators.Hash(), state.ConsensusParams.Hash(), state.AppHash, state.LastResultsHash, proposerAddress, + round, + proof, ) return block, block.MakePartSet(types.BlockPartSizeBytes) @@ -238,6 +271,9 @@ func MakeGenesisState(genDoc *types.GenesisDoc) (State, error) { LastBlockID: types.BlockID{}, LastBlockTime: genDoc.GenesisTime, + // genesis block has no last proof + LastProof: nil, + NextValidators: nextValidatorSet, Validators: validatorSet, LastValidators: types.NewValidatorSet(nil), diff --git a/state/state_test.go b/state/state_test.go index b015a4e17..25f916ee2 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -1017,3 +1017,19 @@ func TestApplyUpdates(t *testing.T) { assert.Equal(t, tc.expected, res, "case %d", i) } } + +func TestState_MakeHashMessage(t *testing.T) { + _, _, state := setupTestCase(t) + message1, err := state.MakeHashMessage(0) + require.NoError(t, err) + message2, err := state.MakeHashMessage(1) + require.NoError(t, err) + require.False(t, bytes.Equal(message1, message2)) + + privVal := makePrivVal() + state.LastProof, _ = privVal.GenerateVRFProof(message1) + message3, err := state.MakeHashMessage(0) + require.NoError(t, err) + require.False(t, bytes.Equal(message1, message3)) + require.False(t, bytes.Equal(message2, message3)) +} diff --git a/state/validation.go b/state/validation.go index 6c306e2df..7683ae042 100644 --- a/state/validation.go +++ b/state/validation.go @@ -6,6 +6,8 @@ import ( "fmt" "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/tendermint/tendermint/crypto/vrf" "github.com/tendermint/tendermint/types" dbm "github.com/tendermint/tm-db" ) @@ -149,6 +151,19 @@ func validateBlock(evidencePool EvidencePool, stateDB dbm.DB, state State, block ) } + // TODO: verify right proposer using ElectProposer + + // Validate vrf proof + message, err := state.MakeHashMessage(block.Round) + if err != nil { + return types.NewErrInvalidProof(err.Error()) + } + _, val := state.Validators.GetByAddress(block.ProposerAddress) + verified, err := vrf.Verify(val.PubKey.(ed25519.PubKeyEd25519), block.Proof, message) + if !verified { + return types.NewErrInvalidProof(fmt.Sprintf("proof: %v, prevProof: %v, height=%d, round=%d, addr: %v", block.Proof, state.LastProof, state.LastBlockHeight, block.Round, block.ProposerAddress)) + } + return nil } diff --git a/state/validation_test.go b/state/validation_test.go index 9346a3bdd..4c0100797 100644 --- a/state/validation_test.go +++ b/state/validation_test.go @@ -72,7 +72,9 @@ func TestValidateBlockHeader(t *testing.T) { Invalid blocks don't pass */ for _, tc := range testCases { - block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, proposerAddr) + message, _ := state.MakeHashMessage(0) + proof, _ := privVals[proposerAddr.String()].GenerateVRFProof(message) + block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, proposerAddr, 0, proof) tc.malleateBlock(block) err := blockExec.ValidateBlock(state, block) require.Error(t, err, tc.name) @@ -125,7 +127,9 @@ func TestValidateBlockCommit(t *testing.T) { state.LastBlockID, []types.CommitSig{wrongHeightVote.CommitSig()}, ) - block, _ := state.MakeBlock(height, makeTxs(height), wrongHeightCommit, nil, proposerAddr) + message, _ := state.MakeHashMessage(0) + proof, _ := privVals[proposerAddr.String()].GenerateVRFProof(message) + block, _ := state.MakeBlock(height, makeTxs(height), wrongHeightCommit, nil, proposerAddr, 0, proof) err = blockExec.ValidateBlock(state, block) _, isErrInvalidCommitHeight := err.(types.ErrInvalidCommitHeight) require.True(t, isErrInvalidCommitHeight, "expected ErrInvalidCommitHeight at height %d but got: %v", height, err) @@ -133,7 +137,7 @@ func TestValidateBlockCommit(t *testing.T) { /* #2589: test len(block.LastCommit.Signatures) == state.LastValidators.Size() */ - block, _ = state.MakeBlock(height, makeTxs(height), wrongSigsCommit, nil, proposerAddr) + block, _ = state.MakeBlock(height, makeTxs(height), wrongSigsCommit, nil, proposerAddr, 0, proof) err = blockExec.ValidateBlock(state, block) _, isErrInvalidCommitSignatures := err.(types.ErrInvalidCommitSignatures) require.True(t, isErrInvalidCommitSignatures, @@ -214,7 +218,9 @@ func TestValidateBlockEvidence(t *testing.T) { for i := int64(0); i <= maxNumEvidence; i++ { evidence = append(evidence, goodEvidence) } - block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, evidence, proposerAddr) + message, _ := state.MakeHashMessage(0) + proof, _ := privVals[proposerAddr.String()].GenerateVRFProof(message) + block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, evidence, proposerAddr, 0, proof) err := blockExec.ValidateBlock(state, block) _, ok := err.(*types.ErrEvidenceOverflow) require.True(t, ok, "expected error to be of type ErrEvidenceOverflow at height %d", height) diff --git a/store/store_test.go b/store/store_test.go index 7fedf8606..41503c3a5 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -46,7 +46,7 @@ func makeTxs(height int64) (txs []types.Tx) { } func makeBlock(height int64, state sm.State, lastCommit *types.Commit) *types.Block { - block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, state.Validators.GetProposer().Address) + block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, state.Validators.GetProposer().Address, 0, nil) return block } diff --git a/types/block.go b/types/block.go index 206ea4414..7b69abb59 100644 --- a/types/block.go +++ b/types/block.go @@ -8,6 +8,7 @@ import ( "time" "github.com/pkg/errors" + "github.com/tendermint/tendermint/crypto/vrf" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/merkle" @@ -345,6 +346,10 @@ type Header struct { // consensus info EvidenceHash tmbytes.HexBytes `json:"evidence_hash"` // evidence included in the block ProposerAddress Address `json:"proposer_address"` // original proposer of the block + + // vrf info + Round int `json:"round"` + Proof vrf.Proof `json:"proof"` } // Populate the Header with state-derived data. @@ -355,6 +360,8 @@ func (h *Header) Populate( valHash, nextValHash []byte, consensusHash, appHash, lastResultsHash []byte, proposerAddress Address, + round int, + proof vrf.Proof, ) { h.Version = version h.ChainID = chainID @@ -366,6 +373,8 @@ func (h *Header) Populate( h.AppHash = appHash h.LastResultsHash = lastResultsHash h.ProposerAddress = proposerAddress + h.Round = round + h.Proof = proof } // Hash returns the hash of the header. @@ -393,6 +402,9 @@ func (h *Header) Hash() tmbytes.HexBytes { cdcEncode(h.LastResultsHash), cdcEncode(h.EvidenceHash), cdcEncode(h.ProposerAddress), + // include round and vrf proof in block hash + cdcEncode(h.Round), + cdcEncode(h.Proof), }) } @@ -416,6 +428,8 @@ func (h *Header) StringIndented(indent string) string { %s Results: %v %s Evidence: %v %s Proposer: %v +%s Round: %v +%s Proof: %v %s}#%v`, indent, h.Version, indent, h.ChainID, @@ -431,6 +445,8 @@ func (h *Header) StringIndented(indent string) string { indent, h.LastResultsHash, indent, h.EvidenceHash, indent, h.ProposerAddress, + indent, h.Round, + indent, h.Proof, indent, h.Hash()) } diff --git a/types/block_test.go b/types/block_test.go index 5f2f2e8bc..cbb2e9f47 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -269,7 +269,9 @@ func TestHeaderHash(t *testing.T) { LastResultsHash: tmhash.Sum([]byte("last_results_hash")), EvidenceHash: tmhash.Sum([]byte("evidence_hash")), ProposerAddress: crypto.AddressHash([]byte("proposer_address")), - }, hexBytesFromString("ABDC78921B18A47EE6BEF5E31637BADB0F3E587E3C0F4DB2D1E93E9FF0533862")}, + Round: 1, + Proof: tmhash.Sum([]byte("proof")), + }, hexBytesFromString("A607E71253D996B2D75CC98AEC7FE6363598F6ED37A501B427DBD3A7781FBE15")}, {"nil header yields nil", nil, nil}, {"nil ValidatorsHash yields nil", &Header{ Version: version.Consensus{Block: 1, App: 2}, diff --git a/types/errors.go b/types/errors.go index cbc4e60c3..4dbc6fa4e 100644 --- a/types/errors.go +++ b/types/errors.go @@ -22,6 +22,11 @@ type ( ErrUnsupportedKey struct { Expected string } + + // VRF verification failure + ErrInvalidProof struct { + ErrorMessage string + } ) func NewErrInvalidCommitHeight(expected, actual int64) ErrInvalidCommitHeight { @@ -55,3 +60,11 @@ func NewErrUnsupportedKey(expected string) ErrUnsupportedKey { func (e ErrUnsupportedKey) Error() string { return fmt.Sprintf("the private key is not a %s", e.Expected) } + +func NewErrInvalidProof(message string) ErrInvalidProof { + return ErrInvalidProof{ErrorMessage: message} +} + +func (e ErrInvalidProof) Error() string { + return fmt.Sprintf("Proof verification failed: %s", e.ErrorMessage) +} diff --git a/types/protobuf_test.go b/types/protobuf_test.go index 7d5434be6..b688716b5 100644 --- a/types/protobuf_test.go +++ b/types/protobuf_test.go @@ -97,7 +97,7 @@ func TestABCIHeader(t *testing.T) { protocolVersion, "chainID", timestamp, lastBlockID, []byte("valHash"), []byte("nextValHash"), []byte("consHash"), []byte("appHash"), []byte("lastResultsHash"), - []byte("proposerAddress"), + []byte("proposerAddress"), 0, []byte("lastProof"), ) cdc := amino.NewCodec() From 03554fe3367919f92e763272bd8fce81a948c901 Mon Sep 17 00:00:00 2001 From: Woosang Son Date: Fri, 27 Mar 2020 08:56:59 +0900 Subject: [PATCH 2/9] docs: changelog_pending --- CHANGELOG_PENDING.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index be210ae52..258d4d681 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -15,7 +15,12 @@ program](https://hackerone.com/tendermint). - Go API +- Blockchain Protocol + + - [state] [\#7](https://github.com/line/tendermint/issues/7) Add round, proof in block + ### FEATURES: +- [types] [\#40](https://github.com/line/tendermint/issues/40) Add vrf interface and add a function generating vrf proof to PrivValidator ### IMPROVEMENTS: From 0e68d598c611f5ca4db735e0232b4267f129496c Mon Sep 17 00:00:00 2001 From: Woosang Son Date: Fri, 27 Mar 2020 10:23:04 +0900 Subject: [PATCH 3/9] chore: print proof to hex --- types/block.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/types/block.go b/types/block.go index 7b69abb59..4e9bb5eb8 100644 --- a/types/block.go +++ b/types/block.go @@ -2,6 +2,7 @@ package types import ( "bytes" + "encoding/hex" "fmt" "strings" "sync" @@ -446,7 +447,7 @@ func (h *Header) StringIndented(indent string) string { indent, h.EvidenceHash, indent, h.ProposerAddress, indent, h.Round, - indent, h.Proof, + indent, strings.ToUpper(hex.EncodeToString(h.Proof)), indent, h.Hash()) } From 32f7b1c4b71aee7f1ebab219d7cf960468b45e0a Mon Sep 17 00:00:00 2001 From: Woosang Son Date: Fri, 27 Mar 2020 11:06:50 +0900 Subject: [PATCH 4/9] chore: for pretty printing of block to hex --- state/execution.go | 2 +- state/validation.go | 2 +- types/block.go | 9 ++++----- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/state/execution.go b/state/execution.go index 92c425421..2a19cd902 100644 --- a/state/execution.go +++ b/state/execution.go @@ -433,7 +433,7 @@ func updateState( LastBlockHeight: header.Height, LastBlockID: blockID, LastBlockTime: header.Time, - LastProof: header.Proof, + LastProof: header.Proof.Bytes(), NextValidators: nValSet, Validators: state.NextValidators.Copy(), LastValidators: state.Validators.Copy(), diff --git a/state/validation.go b/state/validation.go index 7683ae042..aff0f27ff 100644 --- a/state/validation.go +++ b/state/validation.go @@ -159,7 +159,7 @@ func validateBlock(evidencePool EvidencePool, stateDB dbm.DB, state State, block return types.NewErrInvalidProof(err.Error()) } _, val := state.Validators.GetByAddress(block.ProposerAddress) - verified, err := vrf.Verify(val.PubKey.(ed25519.PubKeyEd25519), block.Proof, message) + verified, err := vrf.Verify(val.PubKey.(ed25519.PubKeyEd25519), block.Proof.Bytes(), message) if !verified { return types.NewErrInvalidProof(fmt.Sprintf("proof: %v, prevProof: %v, height=%d, round=%d, addr: %v", block.Proof, state.LastProof, state.LastBlockHeight, block.Round, block.ProposerAddress)) } diff --git a/types/block.go b/types/block.go index 4e9bb5eb8..857955a28 100644 --- a/types/block.go +++ b/types/block.go @@ -2,7 +2,6 @@ package types import ( "bytes" - "encoding/hex" "fmt" "strings" "sync" @@ -349,8 +348,8 @@ type Header struct { ProposerAddress Address `json:"proposer_address"` // original proposer of the block // vrf info - Round int `json:"round"` - Proof vrf.Proof `json:"proof"` + Round int `json:"round"` + Proof tmbytes.HexBytes `json:"proof"` } // Populate the Header with state-derived data. @@ -375,7 +374,7 @@ func (h *Header) Populate( h.LastResultsHash = lastResultsHash h.ProposerAddress = proposerAddress h.Round = round - h.Proof = proof + h.Proof = tmbytes.HexBytes(proof) } // Hash returns the hash of the header. @@ -447,7 +446,7 @@ func (h *Header) StringIndented(indent string) string { indent, h.EvidenceHash, indent, h.ProposerAddress, indent, h.Round, - indent, strings.ToUpper(hex.EncodeToString(h.Proof)), + indent, h.Proof, indent, h.Hash()) } From 06c1db2f60531417c167206bcce3348f615000d5 Mon Sep 17 00:00:00 2001 From: Woosang Son Date: Fri, 27 Mar 2020 15:27:25 +0900 Subject: [PATCH 5/9] fix: race bug --- consensus/byzantine_test.go | 4 ++-- consensus/common_test.go | 2 +- consensus/replay_test.go | 8 ++++---- consensus/state.go | 9 ++++----- consensus/state_test.go | 2 +- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/consensus/byzantine_test.go b/consensus/byzantine_test.go index 65bd88f67..8a7f3d542 100644 --- a/consensus/byzantine_test.go +++ b/consensus/byzantine_test.go @@ -176,7 +176,7 @@ func byzantineDecideProposalFunc(t *testing.T, height int64, round int, cs *Stat // Avoid sending on internalMsgQueue and running consensus state. // Create a new proposal block from state/txs from the mempool. - block1, blockParts1 := cs.createProposalBlock() + block1, blockParts1 := cs.createProposalBlock(round) polRound, propBlockID := cs.ValidRound, types.BlockID{Hash: block1.Hash(), PartsHeader: blockParts1.Header()} proposal1 := types.NewProposal(height, round, polRound, propBlockID) if err := cs.privValidator.SignProposal(cs.state.ChainID, proposal1); err != nil { @@ -184,7 +184,7 @@ func byzantineDecideProposalFunc(t *testing.T, height int64, round int, cs *Stat } // Create a new proposal block from state/txs from the mempool. - block2, blockParts2 := cs.createProposalBlock() + block2, blockParts2 := cs.createProposalBlock(round) polRound, propBlockID = cs.ValidRound, types.BlockID{Hash: block2.Hash(), PartsHeader: blockParts2.Header()} proposal2 := types.NewProposal(height, round, polRound, propBlockID) if err := cs.privValidator.SignProposal(cs.state.ChainID, proposal2); err != nil { diff --git a/consensus/common_test.go b/consensus/common_test.go index c6cd18193..fdde30b44 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -163,7 +163,7 @@ func decideProposal( round int, ) (proposal *types.Proposal, block *types.Block) { cs1.mtx.Lock() - block, blockParts := cs1.createProposalBlock() + block, blockParts := cs1.createProposalBlock(round) validRound := cs1.ValidRound chainID := cs1.state.ChainID cs1.mtx.Unlock() diff --git a/consensus/replay_test.go b/consensus/replay_test.go index f8ec790b1..f665f36e7 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -349,7 +349,7 @@ func TestSimulateValidatorsChange(t *testing.T) { newValidatorTx1 := kvstore.MakeValSetChangeTx(valPubKey1ABCI, testMinPower) err := assertMempool(css[0].txNotifier).CheckTx(newValidatorTx1, nil, mempl.TxInfo{}) assert.Nil(t, err) - propBlock, _ := css[0].createProposalBlock() //changeProposer(t, cs1, vs2) + propBlock, _ := css[0].createProposalBlock(0) //changeProposer(t, cs1, vs2) propBlockParts := propBlock.MakePartSet(partSize) blockID := types.BlockID{Hash: propBlock.Hash(), PartsHeader: propBlockParts.Header()} proposal := types.NewProposal(vss[1].Height, round, -1, blockID) @@ -374,7 +374,7 @@ func TestSimulateValidatorsChange(t *testing.T) { updateValidatorTx1 := kvstore.MakeValSetChangeTx(updatePubKey1ABCI, 25) err = assertMempool(css[0].txNotifier).CheckTx(updateValidatorTx1, nil, mempl.TxInfo{}) assert.Nil(t, err) - propBlock, _ = css[0].createProposalBlock() //changeProposer(t, cs1, vs2) + propBlock, _ = css[0].createProposalBlock(0) //changeProposer(t, cs1, vs2) propBlockParts = propBlock.MakePartSet(partSize) blockID = types.BlockID{Hash: propBlock.Hash(), PartsHeader: propBlockParts.Header()} proposal = types.NewProposal(vss[2].Height, round, -1, blockID) @@ -404,7 +404,7 @@ func TestSimulateValidatorsChange(t *testing.T) { newValidatorTx3 := kvstore.MakeValSetChangeTx(newVal3ABCI, testMinPower) err = assertMempool(css[0].txNotifier).CheckTx(newValidatorTx3, nil, mempl.TxInfo{}) assert.Nil(t, err) - propBlock, _ = css[0].createProposalBlock() //changeProposer(t, cs1, vs2) + propBlock, _ = css[0].createProposalBlock(0) //changeProposer(t, cs1, vs2) propBlockParts = propBlock.MakePartSet(partSize) blockID = types.BlockID{Hash: propBlock.Hash(), PartsHeader: propBlockParts.Header()} newVss := make([]*validatorStub, nVals+1) @@ -462,7 +462,7 @@ func TestSimulateValidatorsChange(t *testing.T) { removeValidatorTx3 := kvstore.MakeValSetChangeTx(newVal3ABCI, 0) err = assertMempool(css[0].txNotifier).CheckTx(removeValidatorTx3, nil, mempl.TxInfo{}) assert.Nil(t, err) - propBlock, _ = css[0].createProposalBlock() //changeProposer(t, cs1, vs2) + propBlock, _ = css[0].createProposalBlock(0) //changeProposer(t, cs1, vs2) propBlockParts = propBlock.MakePartSet(partSize) blockID = types.BlockID{Hash: propBlock.Hash(), PartsHeader: propBlockParts.Header()} newVss = make([]*validatorStub, nVals+3) diff --git a/consensus/state.go b/consensus/state.go index 8dc05306f..8f92f7e9c 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -958,7 +958,7 @@ func (cs *State) defaultDecideProposal(height int64, round int) { block, blockParts = cs.ValidBlock, cs.ValidBlockParts } else { // Create a new proposal block from state/txs from the mempool. - block, blockParts = cs.createProposalBlock() + block, blockParts = cs.createProposalBlock(round) if block == nil { // on error return } @@ -1007,7 +1007,7 @@ func (cs *State) isProposalComplete() bool { // is returned for convenience so we can log the proposal block. // Returns nil block upon error. // NOTE: keep it side-effect free for clarity. -func (cs *State) createProposalBlock() (block *types.Block, blockParts *types.PartSet) { +func (cs *State) createProposalBlock(round int) (block *types.Block, blockParts *types.PartSet) { var commit *types.Commit switch { case cs.Height == 1: @@ -1024,8 +1024,7 @@ func (cs *State) createProposalBlock() (block *types.Block, blockParts *types.Pa } proposerAddr := cs.privValidator.GetPubKey().Address() - - message, err := cs.state.MakeHashMessage(cs.Round) + message, err := cs.state.MakeHashMessage(round) if err != nil { cs.Logger.Error("enterPropose: Cannot generate vrf message: %s", err.Error()) return @@ -1036,7 +1035,7 @@ func (cs *State) createProposalBlock() (block *types.Block, blockParts *types.Pa cs.Logger.Error("enterPropose: Cannot generate vrf proof: %s", err.Error()) return } - return cs.blockExec.CreateProposalBlock(cs.Height, cs.state, commit, proposerAddr, cs.Round, proof) + return cs.blockExec.CreateProposalBlock(cs.Height, cs.state, commit, proposerAddr, round, proof) } // Enter: `timeoutPropose` after entering Propose. diff --git a/consensus/state_test.go b/consensus/state_test.go index e8df64981..6111f7e66 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -181,7 +181,7 @@ func TestStateBadProposal(t *testing.T) { proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) voteCh := subscribe(cs1.eventBus, types.EventQueryVote) - propBlock, _ := cs1.createProposalBlock() //changeProposer(t, cs1, vs2) + propBlock, _ := cs1.createProposalBlock(round) //changeProposer(t, cs1, vs2) // make the second validator the proposer by incrementing round round++ From 50ae22e1b96f5ba282b2ba99df239d7977b7e47b Mon Sep 17 00:00:00 2001 From: Woosang Son Date: Fri, 27 Mar 2020 15:40:38 +0900 Subject: [PATCH 6/9] fix: error handling --- state/validation.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/state/validation.go b/state/validation.go index aff0f27ff..eb5f88b6d 100644 --- a/state/validation.go +++ b/state/validation.go @@ -160,7 +160,9 @@ func validateBlock(evidencePool EvidencePool, stateDB dbm.DB, state State, block } _, val := state.Validators.GetByAddress(block.ProposerAddress) verified, err := vrf.Verify(val.PubKey.(ed25519.PubKeyEd25519), block.Proof.Bytes(), message) - if !verified { + if err != nil { + return types.NewErrInvalidProof(fmt.Sprintf("verification failed: %s; proof: %v, prevProof: %v, height=%d, round=%d, addr: %v", err.Error(), block.Proof, state.LastProof, state.LastBlockHeight, block.Round, block.ProposerAddress)) + } else if !verified { return types.NewErrInvalidProof(fmt.Sprintf("proof: %v, prevProof: %v, height=%d, round=%d, addr: %v", block.Proof, state.LastProof, state.LastBlockHeight, block.Round, block.ProposerAddress)) } From 4f7173edb009891ff544db340f91c360f1e76996 Mon Sep 17 00:00:00 2001 From: Woosang Son Date: Sat, 28 Mar 2020 11:05:59 +0900 Subject: [PATCH 7/9] fix: apply comment --- state/state.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/state/state.go b/state/state.go index 24005f69d..3dd03120a 100644 --- a/state/state.go +++ b/state/state.go @@ -102,8 +102,8 @@ func (state State) MakeHashMessage(round int) ([]byte, error) { seed = output } b := make([]byte, 16) - binary.PutVarint(b, state.LastBlockHeight) - binary.PutVarint(b[8:], int64(round)) + binary.LittleEndian.PutUint64(b, uint64(state.LastBlockHeight)) + binary.LittleEndian.PutUint64(b[8:], uint64(round)) hash := tmhash.New() hash.Write(seed) return hash.Sum(b), nil From 06f124b8ae3305edc4e720785064f11fa765069e Mon Sep 17 00:00:00 2001 From: Woosang Son Date: Mon, 30 Mar 2020 16:55:04 +0900 Subject: [PATCH 8/9] fix: round validation --- consensus/state.go | 16 ++++++++-------- node/node_test.go | 2 +- state/execution.go | 8 +++++--- state/helpers_test.go | 2 +- state/validation.go | 9 +++++++-- state/validation_test.go | 10 +++++----- types/errors.go | 14 ++++++++++++++ 7 files changed, 41 insertions(+), 20 deletions(-) diff --git a/consensus/state.go b/consensus/state.go index 8f92f7e9c..dbcca0475 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -1087,7 +1087,7 @@ func (cs *State) defaultDoPrevote(height int64, round int) { } // Validate proposal block - err := cs.blockExec.ValidateBlock(cs.state, cs.ProposalBlock) + err := cs.blockExec.ValidateBlock(cs.state, round, cs.ProposalBlock) if err != nil { // ProposalBlock is invalid, prevote nil. logger.Error("enterPrevote: ProposalBlock is invalid", "err", err) @@ -1212,7 +1212,7 @@ func (cs *State) enterPrecommit(height int64, round int) { if cs.ProposalBlock.HashesTo(blockID.Hash) { logger.Info("enterPrecommit: +2/3 prevoted proposal block. Locking", "hash", blockID.Hash) // Validate the block. - if err := cs.blockExec.ValidateBlock(cs.state, cs.ProposalBlock); err != nil { + if err := cs.blockExec.ValidateBlock(cs.state, round, cs.ProposalBlock); err != nil { panic(fmt.Sprintf("enterPrecommit: +2/3 prevoted for an invalid block: %v", err)) } cs.LockedRound = round @@ -1291,7 +1291,7 @@ func (cs *State) enterCommit(height int64, commitRound int) { cs.newStep() // Maybe finalize immediately. - cs.tryFinalizeCommit(height) + cs.tryFinalizeCommit(height, commitRound) }() blockID, ok := cs.Votes.Precommits(commitRound).TwoThirdsMajority() @@ -1331,7 +1331,7 @@ func (cs *State) enterCommit(height int64, commitRound int) { } // If we have the block AND +2/3 commits for it, finalize. -func (cs *State) tryFinalizeCommit(height int64) { +func (cs *State) tryFinalizeCommit(height int64, round int) { logger := cs.Logger.With("height", height) if cs.Height != height { @@ -1356,11 +1356,11 @@ func (cs *State) tryFinalizeCommit(height int64) { } // go - cs.finalizeCommit(height) + cs.finalizeCommit(height, round) } // Increment height and goto cstypes.RoundStepNewHeight -func (cs *State) finalizeCommit(height int64) { +func (cs *State) finalizeCommit(height int64, round int) { if cs.Height != height || cs.Step != cstypes.RoundStepCommit { cs.Logger.Debug(fmt.Sprintf( "finalizeCommit(%v): Invalid args. Current step: %v/%v/%v", @@ -1383,7 +1383,7 @@ func (cs *State) finalizeCommit(height int64) { if !block.HashesTo(blockID.Hash) { panic(fmt.Sprintf("Cannot finalizeCommit, ProposalBlock does not hash to commit hash")) } - if err := cs.blockExec.ValidateBlock(cs.state, block); err != nil { + if err := cs.blockExec.ValidateBlock(cs.state, round, block); err != nil { panic(fmt.Sprintf("+2/3 committed an invalid block: %v", err)) } @@ -1638,7 +1638,7 @@ func (cs *State) addProposalBlockPart(msg *BlockPartMessage, peerID p2p.ID) (add } } else if cs.Step == cstypes.RoundStepCommit { // If we're waiting on the proposal block... - cs.tryFinalizeCommit(height) + cs.tryFinalizeCommit(height, cs.CommitRound) } return added, nil } diff --git a/node/node_test.go b/node/node_test.go index 7b571bc2a..e4d00a054 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -290,7 +290,7 @@ func TestCreateProposalBlock(t *testing.T) { proof, ) - err = blockExec.ValidateBlock(state, block) + err = blockExec.ValidateBlock(state, 0, block) assert.NoError(t, err) } diff --git a/state/execution.go b/state/execution.go index 2a19cd902..5da00bcf6 100644 --- a/state/execution.go +++ b/state/execution.go @@ -115,8 +115,8 @@ func (blockExec *BlockExecutor) CreateProposalBlock( // If the block is invalid, it returns an error. // Validation does not mutate state, but does require historical information from the stateDB, // ie. to verify evidence from a validator at an old height. -func (blockExec *BlockExecutor) ValidateBlock(state State, block *types.Block) error { - return validateBlock(blockExec.evpool, blockExec.db, state, block) +func (blockExec *BlockExecutor) ValidateBlock(state State, round int, block *types.Block) error { + return validateBlock(blockExec.evpool, blockExec.db, state, round, block) } // ApplyBlock validates the block against the state, executes it against the app, @@ -126,7 +126,9 @@ func (blockExec *BlockExecutor) ValidateBlock(state State, block *types.Block) e // It takes a blockID to avoid recomputing the parts hash. func (blockExec *BlockExecutor) ApplyBlock(state State, blockID types.BlockID, block *types.Block) (State, error) { - if err := blockExec.ValidateBlock(state, block); err != nil { + // When doing ApplyBlock, we don't need to check whether the block.Round is same to current round, + // so we just put block.Round for the current round parameter + if err := blockExec.ValidateBlock(state, block.Round, block); err != nil { return state, ErrInvalidBlock(err) } diff --git a/state/helpers_test.go b/state/helpers_test.go index 6034efe41..49ef52f41 100644 --- a/state/helpers_test.go +++ b/state/helpers_test.go @@ -63,7 +63,7 @@ func makeAndApplyGoodBlock(state sm.State, privVal types.PrivValidator, height i message, _ := state.MakeHashMessage(0) proof, _ := privVal.GenerateVRFProof(message) block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, evidence, proposerAddr, 0, proof) - if err := blockExec.ValidateBlock(state, block); err != nil { + if err := blockExec.ValidateBlock(state, 0, block); err != nil { return state, types.BlockID{}, err } blockID := types.BlockID{Hash: block.Hash(), diff --git a/state/validation.go b/state/validation.go index eb5f88b6d..f82e752aa 100644 --- a/state/validation.go +++ b/state/validation.go @@ -15,7 +15,7 @@ import ( //----------------------------------------------------- // Validate block -func validateBlock(evidencePool EvidencePool, stateDB dbm.DB, state State, block *types.Block) error { +func validateBlock(evidencePool EvidencePool, stateDB dbm.DB, state State, round int, block *types.Block) error { // Validate internal consistency. if err := block.ValidateBasic(); err != nil { return err @@ -153,7 +153,12 @@ func validateBlock(evidencePool EvidencePool, stateDB dbm.DB, state State, block // TODO: verify right proposer using ElectProposer - // Validate vrf proof + // validate round + if round != block.Round { + return types.NewErrInvalidRound(round, block.Round) + } + + // validate vrf proof message, err := state.MakeHashMessage(block.Round) if err != nil { return types.NewErrInvalidProof(err.Error()) diff --git a/state/validation_test.go b/state/validation_test.go index 4c0100797..dc9cd6d45 100644 --- a/state/validation_test.go +++ b/state/validation_test.go @@ -76,7 +76,7 @@ func TestValidateBlockHeader(t *testing.T) { proof, _ := privVals[proposerAddr.String()].GenerateVRFProof(message) block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, proposerAddr, 0, proof) tc.malleateBlock(block) - err := blockExec.ValidateBlock(state, block) + err := blockExec.ValidateBlock(state, 0, block) require.Error(t, err, tc.name) } @@ -130,7 +130,7 @@ func TestValidateBlockCommit(t *testing.T) { message, _ := state.MakeHashMessage(0) proof, _ := privVals[proposerAddr.String()].GenerateVRFProof(message) block, _ := state.MakeBlock(height, makeTxs(height), wrongHeightCommit, nil, proposerAddr, 0, proof) - err = blockExec.ValidateBlock(state, block) + err = blockExec.ValidateBlock(state, 0, block) _, isErrInvalidCommitHeight := err.(types.ErrInvalidCommitHeight) require.True(t, isErrInvalidCommitHeight, "expected ErrInvalidCommitHeight at height %d but got: %v", height, err) @@ -138,7 +138,7 @@ func TestValidateBlockCommit(t *testing.T) { #2589: test len(block.LastCommit.Signatures) == state.LastValidators.Size() */ block, _ = state.MakeBlock(height, makeTxs(height), wrongSigsCommit, nil, proposerAddr, 0, proof) - err = blockExec.ValidateBlock(state, block) + err = blockExec.ValidateBlock(state, 0, block) _, isErrInvalidCommitSignatures := err.(types.ErrInvalidCommitSignatures) require.True(t, isErrInvalidCommitSignatures, "expected ErrInvalidCommitSignatures at height %d, but got: %v", @@ -221,7 +221,7 @@ func TestValidateBlockEvidence(t *testing.T) { message, _ := state.MakeHashMessage(0) proof, _ := privVals[proposerAddr.String()].GenerateVRFProof(message) block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, evidence, proposerAddr, 0, proof) - err := blockExec.ValidateBlock(state, block) + err := blockExec.ValidateBlock(state, 0, block) _, ok := err.(*types.ErrEvidenceOverflow) require.True(t, ok, "expected error to be of type ErrEvidenceOverflow at height %d", height) } @@ -263,7 +263,7 @@ func TestValidateFailBlockOnCommittedEvidence(t *testing.T) { alreadyCommittedEvidence := types.NewMockEvidence(height, time.Now(), 0, addr) block.Evidence.Evidence = []types.Evidence{alreadyCommittedEvidence} block.EvidenceHash = block.Evidence.Hash() - err := blockExec.ValidateBlock(state, block) + err := blockExec.ValidateBlock(state, 0, block) require.Error(t, err) require.IsType(t, err, &types.ErrEvidenceInvalid{}) diff --git a/types/errors.go b/types/errors.go index 4dbc6fa4e..079b781e7 100644 --- a/types/errors.go +++ b/types/errors.go @@ -27,6 +27,12 @@ type ( ErrInvalidProof struct { ErrorMessage string } + + // invalid round + ErrInvalidRound struct { + ConsensusRound int + BlockRound int + } ) func NewErrInvalidCommitHeight(expected, actual int64) ErrInvalidCommitHeight { @@ -68,3 +74,11 @@ func NewErrInvalidProof(message string) ErrInvalidProof { func (e ErrInvalidProof) Error() string { return fmt.Sprintf("Proof verification failed: %s", e.ErrorMessage) } + +func NewErrInvalidRound(consensusRound, blockRound int) ErrInvalidRound { + return ErrInvalidRound{ConsensusRound: consensusRound, BlockRound: blockRound} +} + +func (e ErrInvalidRound) Error() string { + return fmt.Sprintf("Block round(%d) is mismatched to consensus round(%d)", e.BlockRound, e.ConsensusRound) +} \ No newline at end of file From 7fa8319a13f48002a21b9fef789c9a667327ee1e Mon Sep 17 00:00:00 2001 From: Woosang Son Date: Tue, 31 Mar 2020 10:03:48 +0900 Subject: [PATCH 9/9] fix: remove redundant parameter --- consensus/state.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/consensus/state.go b/consensus/state.go index dbcca0475..d631af6d1 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -1291,7 +1291,7 @@ func (cs *State) enterCommit(height int64, commitRound int) { cs.newStep() // Maybe finalize immediately. - cs.tryFinalizeCommit(height, commitRound) + cs.tryFinalizeCommit(height) }() blockID, ok := cs.Votes.Precommits(commitRound).TwoThirdsMajority() @@ -1331,7 +1331,7 @@ func (cs *State) enterCommit(height int64, commitRound int) { } // If we have the block AND +2/3 commits for it, finalize. -func (cs *State) tryFinalizeCommit(height int64, round int) { +func (cs *State) tryFinalizeCommit(height int64) { logger := cs.Logger.With("height", height) if cs.Height != height { @@ -1356,11 +1356,11 @@ func (cs *State) tryFinalizeCommit(height int64, round int) { } // go - cs.finalizeCommit(height, round) + cs.finalizeCommit(height) } // Increment height and goto cstypes.RoundStepNewHeight -func (cs *State) finalizeCommit(height int64, round int) { +func (cs *State) finalizeCommit(height int64) { if cs.Height != height || cs.Step != cstypes.RoundStepCommit { cs.Logger.Debug(fmt.Sprintf( "finalizeCommit(%v): Invalid args. Current step: %v/%v/%v", @@ -1383,7 +1383,7 @@ func (cs *State) finalizeCommit(height int64, round int) { if !block.HashesTo(blockID.Hash) { panic(fmt.Sprintf("Cannot finalizeCommit, ProposalBlock does not hash to commit hash")) } - if err := cs.blockExec.ValidateBlock(cs.state, round, block); err != nil { + if err := cs.blockExec.ValidateBlock(cs.state, cs.CommitRound, block); err != nil { panic(fmt.Sprintf("+2/3 committed an invalid block: %v", err)) } @@ -1638,7 +1638,7 @@ func (cs *State) addProposalBlockPart(msg *BlockPartMessage, peerID p2p.ID) (add } } else if cs.Step == cstypes.RoundStepCommit { // If we're waiting on the proposal block... - cs.tryFinalizeCommit(height, cs.CommitRound) + cs.tryFinalizeCommit(height) } return added, nil }