Merge pull request #954 from tendermint/668-send-absent-validators
Send absent validators
This commit is contained in:
commit
3eee69de2d
|
@ -324,7 +324,7 @@ func (h *Handshaker) replayBlocks(proxyApp proxy.AppConns, appBlockHeight, store
|
||||||
for i := appBlockHeight + 1; i <= finalBlock; i++ {
|
for i := appBlockHeight + 1; i <= finalBlock; i++ {
|
||||||
h.logger.Info("Applying block", "height", i)
|
h.logger.Info("Applying block", "height", i)
|
||||||
block := h.store.LoadBlock(i)
|
block := h.store.LoadBlock(i)
|
||||||
appHash, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, h.logger)
|
appHash, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, h.logger, h.state.LastValidators)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ func (s *State) ValExecBlock(txEventPublisher types.TxEventPublisher, proxyAppCo
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute the block txs
|
// Execute the block txs
|
||||||
abciResponses, err := execBlockOnProxyApp(txEventPublisher, proxyAppConn, block, s.logger)
|
abciResponses, err := execBlockOnProxyApp(txEventPublisher, proxyAppConn, block, s.logger, s.LastValidators)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// There was some error in proxyApp
|
// There was some error in proxyApp
|
||||||
// TODO Report error and wait for proxyApp to be available.
|
// TODO Report error and wait for proxyApp to be available.
|
||||||
|
@ -39,7 +39,7 @@ func (s *State) ValExecBlock(txEventPublisher types.TxEventPublisher, proxyAppCo
|
||||||
// Executes block's transactions on proxyAppConn.
|
// Executes block's transactions on proxyAppConn.
|
||||||
// Returns a list of transaction results and updates to the validator set
|
// Returns a list of transaction results and updates to the validator set
|
||||||
// TODO: Generate a bitmap or otherwise store tx validity in state.
|
// TODO: Generate a bitmap or otherwise store tx validity in state.
|
||||||
func execBlockOnProxyApp(txEventPublisher types.TxEventPublisher, proxyAppConn proxy.AppConnConsensus, block *types.Block, logger log.Logger) (*ABCIResponses, error) {
|
func execBlockOnProxyApp(txEventPublisher types.TxEventPublisher, proxyAppConn proxy.AppConnConsensus, block *types.Block, logger log.Logger, lastValidators *types.ValidatorSet) (*ABCIResponses, error) {
|
||||||
var validTxs, invalidTxs = 0, 0
|
var validTxs, invalidTxs = 0, 0
|
||||||
|
|
||||||
txIndex := 0
|
txIndex := 0
|
||||||
|
@ -76,12 +76,20 @@ func execBlockOnProxyApp(txEventPublisher types.TxEventPublisher, proxyAppConn p
|
||||||
}
|
}
|
||||||
proxyAppConn.SetResponseCallback(proxyCb)
|
proxyAppConn.SetResponseCallback(proxyCb)
|
||||||
|
|
||||||
|
// determine which validators did not sign last block
|
||||||
|
absentVals := make([]int32, 0)
|
||||||
|
for valI, vote := range block.LastCommit.Precommits {
|
||||||
|
if vote == nil {
|
||||||
|
absentVals = append(absentVals, int32(valI))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Begin block
|
// Begin block
|
||||||
_, err := proxyAppConn.BeginBlockSync(abci.RequestBeginBlock{
|
_, err := proxyAppConn.BeginBlockSync(abci.RequestBeginBlock{
|
||||||
block.Hash(),
|
Hash: block.Hash(),
|
||||||
types.TM2PB.Header(block.Header),
|
Header: types.TM2PB.Header(block.Header),
|
||||||
nil,
|
AbsentValidators: absentVals,
|
||||||
nil,
|
ByzantineValidators: nil,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Error in proxyAppConn.BeginBlock", "err", err)
|
logger.Error("Error in proxyAppConn.BeginBlock", "err", err)
|
||||||
|
@ -275,8 +283,8 @@ func (s *State) CommitStateUpdateMempool(proxyAppConn proxy.AppConnConsensus, bl
|
||||||
|
|
||||||
// ExecCommitBlock executes and commits a block on the proxyApp without validating or mutating the state.
|
// ExecCommitBlock executes and commits a block on the proxyApp without validating or mutating the state.
|
||||||
// It returns the application root hash (result of abci.Commit).
|
// It returns the application root hash (result of abci.Commit).
|
||||||
func ExecCommitBlock(appConnConsensus proxy.AppConnConsensus, block *types.Block, logger log.Logger) ([]byte, error) {
|
func ExecCommitBlock(appConnConsensus proxy.AppConnConsensus, block *types.Block, logger log.Logger, lastValidators *types.ValidatorSet) ([]byte, error) {
|
||||||
_, err := execBlockOnProxyApp(types.NopEventBus{}, appConnConsensus, block, logger)
|
_, err := execBlockOnProxyApp(types.NopEventBus{}, appConnConsensus, block, logger, lastValidators)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Error executing block on proxy app", "height", block.Height, "err", err)
|
logger.Error("Error executing block on proxy app", "height", block.Height, "err", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -2,10 +2,13 @@ package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/tendermint/abci/example/dummy"
|
"github.com/tendermint/abci/example/dummy"
|
||||||
|
abci "github.com/tendermint/abci/types"
|
||||||
crypto "github.com/tendermint/go-crypto"
|
crypto "github.com/tendermint/go-crypto"
|
||||||
"github.com/tendermint/tendermint/proxy"
|
"github.com/tendermint/tendermint/proxy"
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
|
@ -30,16 +33,64 @@ func TestApplyBlock(t *testing.T) {
|
||||||
state := state()
|
state := state()
|
||||||
state.SetLogger(log.TestingLogger())
|
state.SetLogger(log.TestingLogger())
|
||||||
|
|
||||||
// make block
|
|
||||||
block := makeBlock(1, state)
|
block := makeBlock(1, state)
|
||||||
|
|
||||||
err = state.ApplyBlock(types.NopEventBus{}, proxyApp.Consensus(), block, block.MakePartSet(testPartSize).Header(), types.MockMempool{})
|
err = state.ApplyBlock(types.NopEventBus{}, proxyApp.Consensus(), block, block.MakePartSet(testPartSize).Header(), types.MockMempool{})
|
||||||
|
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
// TODO check state and mempool
|
// TODO check state and mempool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestBeginBlockAbsentValidators ensures we send absent validators list.
|
||||||
|
func TestBeginBlockAbsentValidators(t *testing.T) {
|
||||||
|
app := &testApp{}
|
||||||
|
cc := proxy.NewLocalClientCreator(app)
|
||||||
|
proxyApp := proxy.NewAppConns(cc, nil)
|
||||||
|
err := proxyApp.Start()
|
||||||
|
require.Nil(t, err)
|
||||||
|
defer proxyApp.Stop()
|
||||||
|
|
||||||
|
state := state()
|
||||||
|
state.SetLogger(log.TestingLogger())
|
||||||
|
|
||||||
|
// there were 2 validators
|
||||||
|
val1PrivKey := crypto.GenPrivKeyEd25519()
|
||||||
|
val2PrivKey := crypto.GenPrivKeyEd25519()
|
||||||
|
lastValidators := types.NewValidatorSet([]*types.Validator{
|
||||||
|
types.NewValidator(val1PrivKey.PubKey(), 10),
|
||||||
|
types.NewValidator(val2PrivKey.PubKey(), 5),
|
||||||
|
})
|
||||||
|
|
||||||
|
prevHash := state.LastBlockID.Hash
|
||||||
|
prevParts := types.PartSetHeader{}
|
||||||
|
prevBlockID := types.BlockID{prevHash, prevParts}
|
||||||
|
|
||||||
|
now := time.Now().UTC()
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
lastCommitPrecommits []*types.Vote
|
||||||
|
expectedAbsentValidators []int32
|
||||||
|
}{
|
||||||
|
{"none absent", []*types.Vote{{ValidatorIndex: 0, Timestamp: now}, {ValidatorIndex: 1, Timestamp: now}}, []int32{}},
|
||||||
|
{"one absent", []*types.Vote{{ValidatorIndex: 0, Timestamp: now}, nil}, []int32{1}},
|
||||||
|
{"multiple absent", []*types.Vote{nil, nil}, []int32{0, 1}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
lastCommit := &types.Commit{BlockID: prevBlockID, Precommits: tc.lastCommitPrecommits}
|
||||||
|
|
||||||
|
valHash := state.Validators.Hash()
|
||||||
|
block, _ := types.MakeBlock(2, chainID, makeTxs(2), state.LastBlockTotalTx, lastCommit,
|
||||||
|
prevBlockID, valHash, state.AppHash, testPartSize)
|
||||||
|
|
||||||
|
_, err = ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), lastValidators)
|
||||||
|
require.Nil(t, err, tc.desc)
|
||||||
|
|
||||||
|
// -> app must receive an index of the absent validator
|
||||||
|
assert.Equal(t, tc.expectedAbsentValidators, app.AbsentValidators, tc.desc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
// make some bogus txs
|
// make some bogus txs
|
||||||
|
@ -72,3 +123,42 @@ func makeBlock(height int64, state *State) *types.Block {
|
||||||
state.AppHash, testPartSize)
|
state.AppHash, testPartSize)
|
||||||
return block
|
return block
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
var _ abci.Application = (*testApp)(nil)
|
||||||
|
|
||||||
|
type testApp struct {
|
||||||
|
abci.BaseApplication
|
||||||
|
|
||||||
|
AbsentValidators []int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDummyApplication() *testApp {
|
||||||
|
return &testApp{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *testApp) Info(req abci.RequestInfo) (resInfo abci.ResponseInfo) {
|
||||||
|
return abci.ResponseInfo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *testApp) BeginBlock(req abci.RequestBeginBlock) abci.ResponseBeginBlock {
|
||||||
|
app.AbsentValidators = req.AbsentValidators
|
||||||
|
return abci.ResponseBeginBlock{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *testApp) DeliverTx(tx []byte) abci.ResponseDeliverTx {
|
||||||
|
return abci.ResponseDeliverTx{Tags: []*abci.KVPair{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *testApp) CheckTx(tx []byte) abci.ResponseCheckTx {
|
||||||
|
return abci.ResponseCheckTx{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *testApp) Commit() abci.ResponseCommit {
|
||||||
|
return abci.ResponseCommit{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *testApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue