diff --git a/abci/example/kvstore/kvstore.go b/abci/example/kvstore/kvstore.go index bf7b5edf..9523bf74 100644 --- a/abci/example/kvstore/kvstore.go +++ b/abci/example/kvstore/kvstore.go @@ -10,7 +10,6 @@ import ( "github.com/tendermint/tendermint/abci/types" cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" - tmtypes "github.com/tendermint/tendermint/types" ) var ( @@ -69,23 +68,6 @@ func (app *KVStoreApplication) Info(req types.RequestInfo) (resInfo types.Respon return types.ResponseInfo{Data: fmt.Sprintf("{\"size\":%v}", app.state.Size)} } -// handle evidence -func (app *KVStoreApplication) BeginBlock(req types.RequestBeginBlock) types.ResponseBeginBlock { - for _, evidence := range req.ByzantineValidators { - switch evidence.Type { - case tmtypes.ABCIEvidenceTypeDuplicateVote: - key := prefixKey(evidence.Validator.Address) - // Do nothing, just store it in the state to check from outside - app.state.db.Set(key, []byte{}) - app.state.Size += 1 - } - } - - return types.ResponseBeginBlock{ - Tags: []cmn.KVPair{{Key: []byte("height"), Value: []byte(fmt.Sprintf("%d", req.Header.Height))}}, - } -} - // tx is either "key=value" or just arbitrary bytes func (app *KVStoreApplication) DeliverTx(tx []byte) types.ResponseDeliverTx { var key, value []byte diff --git a/abci/example/kvstore/persistent_kvstore.go b/abci/example/kvstore/persistent_kvstore.go index f969eebf..920572a1 100644 --- a/abci/example/kvstore/persistent_kvstore.go +++ b/abci/example/kvstore/persistent_kvstore.go @@ -9,8 +9,10 @@ import ( "github.com/tendermint/tendermint/abci/example/code" "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/ed25519" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + tmtypes "github.com/tendermint/tendermint/types" ) const ( @@ -27,6 +29,8 @@ type PersistentKVStoreApplication struct { // validator set ValUpdates []types.ValidatorUpdate + relation map[string]types.PubKey // address to pubkey + logger log.Logger } @@ -40,8 +44,9 @@ func NewPersistentKVStoreApplication(dbDir string) *PersistentKVStoreApplication state := loadState(db) return &PersistentKVStoreApplication{ - app: &KVStoreApplication{state: state}, - logger: log.NewNopLogger(), + app: &KVStoreApplication{state: state}, + relation: make(map[string]types.PubKey), + logger: log.NewNopLogger(), } } @@ -83,8 +88,18 @@ func (app *PersistentKVStoreApplication) Commit() types.ResponseCommit { return app.app.Commit() } -func (app *PersistentKVStoreApplication) Query(reqQuery types.RequestQuery) types.ResponseQuery { - return app.app.Query(reqQuery) +func (app *PersistentKVStoreApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) { + switch reqQuery.Path { + case "/val": + key := []byte("val:" + string(reqQuery.Data)) + value := app.app.state.db.Get(key) + + resQuery.Key = reqQuery.Data + resQuery.Value = value + return + default: + return app.app.Query(reqQuery) + } } // Save the validators in the merkle tree @@ -102,6 +117,17 @@ func (app *PersistentKVStoreApplication) InitChain(req types.RequestInitChain) t func (app *PersistentKVStoreApplication) BeginBlock(req types.RequestBeginBlock) types.ResponseBeginBlock { // reset valset changes app.ValUpdates = make([]types.ValidatorUpdate, 0) + + for _, ev := range req.ByzantineValidators { + switch ev.Type { + case tmtypes.ABCIEvidenceTypeDuplicateVote: + // decrease voting power by 1 + app.updateValidator(types.ValidatorUpdate{ + PubKey: app.relation[string(ev.Validator.Address)], + Power: ev.TotalVotingPower - 1, + }) + } + } return types.ResponseBeginBlock{} } @@ -173,6 +199,10 @@ func (app *PersistentKVStoreApplication) execValidatorTx(tx []byte) types.Respon // add, update, or remove a validator func (app *PersistentKVStoreApplication) updateValidator(v types.ValidatorUpdate) types.ResponseDeliverTx { key := []byte("val:" + string(v.PubKey.Data)) + + pubkey := ed25519.PubKeyEd25519{} + copy(pubkey[:], v.PubKey.Data) + if v.Power == 0 { // remove validator if !app.app.state.db.Has(key) { @@ -181,6 +211,9 @@ func (app *PersistentKVStoreApplication) updateValidator(v types.ValidatorUpdate Log: fmt.Sprintf("Cannot remove non-existent validator %X", key)} } app.app.state.db.Delete(key) + + delete(app.relation, string(pubkey.Address())) + } else { // add or update validator value := bytes.NewBuffer(make([]byte, 0)) @@ -190,6 +223,8 @@ func (app *PersistentKVStoreApplication) updateValidator(v types.ValidatorUpdate Log: fmt.Sprintf("Error encoding validator: %v", err)} } app.app.state.db.Set(key, value.Bytes()) + + app.relation[string(pubkey.Address())] = v.PubKey } // we only update the changes array if we successfully updated the tree diff --git a/rpc/client/main_test.go b/rpc/client/main_test.go index 1e911bbe..0553f70e 100644 --- a/rpc/client/main_test.go +++ b/rpc/client/main_test.go @@ -1,6 +1,8 @@ package client_test import ( + "fmt" + "io/ioutil" "os" "testing" @@ -13,7 +15,12 @@ var node *nm.Node func TestMain(m *testing.M) { // start a tendermint node (and kvstore) in the background to test against - app := kvstore.NewKVStoreApplication() + dir, err := ioutil.TempDir("/tmp", "abci-rpc-client-test") + if err != nil { + fmt.Println(err) + os.Exit(1) + } + app := kvstore.NewPersistentKVStoreApplication(dir) node = rpctest.StartTendermint(app) code := m.Run() diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index 438201d1..bfc25d44 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -1,6 +1,7 @@ package client_test import ( + "bytes" "fmt" "math/rand" "strings" @@ -11,6 +12,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/rpc/client" rpctest "github.com/tendermint/tendermint/rpc/test" @@ -427,7 +429,7 @@ func makeEvidences(t *testing.T, val *privval.FilePV, chainID string) (ev types. ev = newEvidence(t, val, vote, vote2, chainID) - fakes = make([]types.DuplicateVoteEvidence, 41) + fakes = make([]types.DuplicateVoteEvidence, 42) // different address vote2 = deepcpVote(vote) @@ -457,6 +459,9 @@ func makeEvidences(t *testing.T, val *privval.FilePV, chainID string) (ev types. vote2 = deepcpVote(vote) vote2.Type = types.VoteTypePrecommit fakes[40] = newEvidence(t, val, vote, vote2, chainID) + // exactly same vote + vote2 = deepcpVote(vote) + fakes[41] = newEvidence(t, val, vote, vote2, chainID) return } @@ -479,12 +484,19 @@ func TestBroadcastDuplicateVote(t *testing.T) { require.Equal(t, ev.Hash(), result.Hash, "Invalid response, evidence %v, result %+v", ev.String(), result) - result2, err := c.ABCIQuery("/key", ev.PubKey.Address()) + ed25519pub := ev.PubKey.(ed25519.PubKeyEd25519) + rawpub := ed25519pub[:] + result2, err := c.ABCIQuery("/val", rawpub) require.Nil(t, err, "Error querying evidence, evidence %v", ev.String()) qres := result2.Response require.True(t, qres.IsOK(), "Response not OK, evidence %v", ev.String()) - require.EqualValues(t, []byte{}, qres.Value, "Value not equal with expected, evidence %v, value %v", ev.String(), string(qres.Value)) + var v abci.ValidatorUpdate + err = abci.ReadMessage(bytes.NewReader(qres.Value), &v) + require.NoError(t, err, "Error reading query result, value %v", qres.Value) + + require.EqualValues(t, rawpub, v.PubKey.Data, "Stored PubKey not equal with expected, evidence %v, value %v", ev.String(), string(qres.Value)) + require.Equal(t, int64(9), v.Power, "Stored Power not equal with expected, evidence %v, value %v", ev.String(), string(qres.Value)) for _, fake := range fakes { _, err := c.BroadcastDuplicateVote(fake.PubKey, *fake.VoteA, *fake.VoteB) diff --git a/rpc/core/evidence.go b/rpc/core/evidence.go index 29e87939..fd257cce 100644 --- a/rpc/core/evidence.go +++ b/rpc/core/evidence.go @@ -19,12 +19,15 @@ func BroadcastDuplicateVote(pubkey crypto.PubKey, vote1 types.Vote, vote2 types. chainID := p2pTransport.NodeInfo().Network ev := &types.DuplicateVoteEvidence{pubkey, &vote1, &vote2} if err := vote1.Verify(chainID, pubkey); err != nil { - return nil, fmt.Errorf("Error broadcasting evidence: %v", err) + return nil, fmt.Errorf("Error broadcasting evidence, invalid vote1: %v", err) + } + if err := vote2.Verify(chainID, pubkey); err != nil { + return nil, fmt.Errorf("Error broadcasting evidence, invalid vote2: %v", err) } err := evidencePool.AddEvidence(ev) if err != nil { - return nil, fmt.Errorf("Error broadcasting evidence: %v", err) + return nil, fmt.Errorf("Error broadcasting evidence, adding evidence: %v", err) } return &ctypes.ResultBroadcastDuplicateVote{ev.Hash()}, nil }