rpc: /broadcast_evidence (#3481)

* implement broadcast_duplicate_vote endpoint

* fix test_cover

* address comments

* address comments

* Update abci/example/kvstore/persistent_kvstore.go

Co-Authored-By: mossid <torecursedivine@gmail.com>

* Update rpc/client/main_test.go

Co-Authored-By: mossid <torecursedivine@gmail.com>

* address comments in progress

* reformat the code

* make linter happy

* make tests pass

* replace BroadcastDuplicateVote with BroadcastEvidence

* fix test

* fix endpoint name

* improve doc

* fix TestBroadcastEvidenceDuplicateVote

* Update rpc/core/evidence.go

Co-Authored-By: Thane Thomson <connect@thanethomson.com>

* add changelog entry

* fix TestBroadcastEvidenceDuplicateVote
This commit is contained in:
Anton Kaliaev
2019-07-22 12:15:29 +04:00
committed by GitHub
parent 657832a95a
commit c6daa48368
13 changed files with 302 additions and 29 deletions

View File

@ -1,7 +1,9 @@
package client_test
import (
"bytes"
"fmt"
"math/rand"
"net/http"
"strings"
"sync"
@ -12,7 +14,10 @@ import (
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/tmhash"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/privval"
"github.com/tendermint/tendermint/rpc/client"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
rpctest "github.com/tendermint/tendermint/rpc/test"
@ -446,6 +451,145 @@ func TestTxSearch(t *testing.T) {
}
}
func deepcpVote(vote *types.Vote) (res *types.Vote) {
res = &types.Vote{
ValidatorAddress: make([]byte, len(vote.ValidatorAddress)),
ValidatorIndex: vote.ValidatorIndex,
Height: vote.Height,
Round: vote.Round,
Type: vote.Type,
BlockID: types.BlockID{
Hash: make([]byte, len(vote.BlockID.Hash)),
PartsHeader: vote.BlockID.PartsHeader,
},
Signature: make([]byte, len(vote.Signature)),
}
copy(res.ValidatorAddress, vote.ValidatorAddress)
copy(res.BlockID.Hash, vote.BlockID.Hash)
copy(res.Signature, vote.Signature)
return
}
func newEvidence(t *testing.T, val *privval.FilePV, vote *types.Vote, vote2 *types.Vote, chainID string) types.DuplicateVoteEvidence {
var err error
vote2_ := deepcpVote(vote2)
vote2_.Signature, err = val.Key.PrivKey.Sign(vote2_.SignBytes(chainID))
require.NoError(t, err)
return types.DuplicateVoteEvidence{
PubKey: val.Key.PubKey,
VoteA: vote,
VoteB: vote2_,
}
}
func makeEvidences(t *testing.T, val *privval.FilePV, chainID string) (ev types.DuplicateVoteEvidence, fakes []types.DuplicateVoteEvidence) {
vote := &types.Vote{
ValidatorAddress: val.Key.Address,
ValidatorIndex: 0,
Height: 1,
Round: 0,
Type: types.PrevoteType,
BlockID: types.BlockID{
Hash: tmhash.Sum([]byte("blockhash")),
PartsHeader: types.PartSetHeader{
Total: 1000,
Hash: tmhash.Sum([]byte("partset")),
},
},
}
var err error
vote.Signature, err = val.Key.PrivKey.Sign(vote.SignBytes(chainID))
require.NoError(t, err)
vote2 := deepcpVote(vote)
vote2.BlockID.Hash = tmhash.Sum([]byte("blockhash2"))
ev = newEvidence(t, val, vote, vote2, chainID)
fakes = make([]types.DuplicateVoteEvidence, 42)
// different address
vote2 = deepcpVote(vote)
for i := 0; i < 10; i++ {
rand.Read(vote2.ValidatorAddress) // nolint: gosec
fakes[i] = newEvidence(t, val, vote, vote2, chainID)
}
// different index
vote2 = deepcpVote(vote)
for i := 10; i < 20; i++ {
vote2.ValidatorIndex = rand.Int()%100 + 1 // nolint: gosec
fakes[i] = newEvidence(t, val, vote, vote2, chainID)
}
// different height
vote2 = deepcpVote(vote)
for i := 20; i < 30; i++ {
vote2.Height = rand.Int63()%1000 + 100 // nolint: gosec
fakes[i] = newEvidence(t, val, vote, vote2, chainID)
}
// different round
vote2 = deepcpVote(vote)
for i := 30; i < 40; i++ {
vote2.Round = rand.Int()%10 + 1 // nolint: gosec
fakes[i] = newEvidence(t, val, vote, vote2, chainID)
}
// different type
vote2 = deepcpVote(vote)
vote2.Type = types.PrecommitType
fakes[40] = newEvidence(t, val, vote, vote2, chainID)
// exactly same vote
vote2 = deepcpVote(vote)
fakes[41] = newEvidence(t, val, vote, vote2, chainID)
return
}
func TestBroadcastEvidenceDuplicateVote(t *testing.T) {
config := rpctest.GetConfig()
chainID := config.ChainID()
pvKeyFile := config.PrivValidatorKeyFile()
pvKeyStateFile := config.PrivValidatorStateFile()
pv := privval.LoadOrGenFilePV(pvKeyFile, pvKeyStateFile)
ev, fakes := makeEvidences(t, pv, chainID)
t.Logf("evidence %v", ev)
for i, c := range GetClients() {
t.Logf("client %d", i)
result, err := c.BroadcastEvidence(&types.DuplicateVoteEvidence{PubKey: ev.PubKey, VoteA: ev.VoteA, VoteB: ev.VoteB})
require.Nil(t, err)
require.Equal(t, ev.Hash(), result.Hash, "Invalid response, result %+v", result)
status, err := c.Status()
require.NoError(t, err)
client.WaitForHeight(c, status.SyncInfo.LatestBlockHeight+2, nil)
ed25519pub := ev.PubKey.(ed25519.PubKeyEd25519)
rawpub := ed25519pub[:]
result2, err := c.ABCIQuery("/val", rawpub)
require.Nil(t, err, "Error querying evidence, err %v", err)
qres := result2.Response
require.True(t, qres.IsOK(), "Response not OK")
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, value %v", string(qres.Value))
require.Equal(t, int64(9), v.Power, "Stored Power not equal with expected, value %v", string(qres.Value))
for _, fake := range fakes {
_, err := c.BroadcastEvidence(&types.DuplicateVoteEvidence{
PubKey: fake.PubKey,
VoteA: fake.VoteA,
VoteB: fake.VoteB})
require.Error(t, err, "Broadcasting fake evidence succeed: %s", fake.String())
}
}
}
func TestBatchedJSONRPCCalls(t *testing.T) {
c := getHTTPClient()
testBatchedJSONRPCCalls(t, c)