mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-13 16:22:53 +00:00
Compare commits
2 Commits
tmp
...
callum/nuk
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
508b7f9758 | ||
|
|
e5ffe132ae |
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
"github.com/tendermint/tendermint/version"
|
||||
)
|
||||
|
||||
@@ -32,14 +32,14 @@ func RandomHash() []byte {
|
||||
return crypto.CRandBytes(tmhash.Size)
|
||||
}
|
||||
|
||||
func MakeBlockID() types.BlockID {
|
||||
func MakeBlockID() meta.BlockID {
|
||||
return MakeBlockIDWithHash(RandomHash())
|
||||
}
|
||||
|
||||
func MakeBlockIDWithHash(hash []byte) types.BlockID {
|
||||
return types.BlockID{
|
||||
func MakeBlockIDWithHash(hash []byte) meta.BlockID {
|
||||
return meta.BlockID{
|
||||
Hash: hash,
|
||||
PartSetHeader: types.PartSetHeader{
|
||||
PartSetHeader: meta.PartSetHeader{
|
||||
Total: 100,
|
||||
Hash: RandomHash(),
|
||||
},
|
||||
@@ -48,7 +48,7 @@ func MakeBlockIDWithHash(hash []byte) types.BlockID {
|
||||
|
||||
// MakeHeader fills the rest of the contents of the header such that it passes
|
||||
// validate basic
|
||||
func MakeHeader(h *types.Header) (*types.Header, error) {
|
||||
func MakeHeader(h *meta.Header) (*meta.Header, error) {
|
||||
if h.Version.Block == 0 {
|
||||
h.Version.Block = version.BlockProtocol
|
||||
}
|
||||
@@ -92,8 +92,8 @@ func MakeHeader(h *types.Header) (*types.Header, error) {
|
||||
return h, h.ValidateBasic()
|
||||
}
|
||||
|
||||
func MakeRandomHeader() *types.Header {
|
||||
h, err := MakeHeader(&types.Header{})
|
||||
func MakeRandomHeader() *meta.Header {
|
||||
h, err := MakeHeader(&meta.Header{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -5,12 +5,24 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/pkg/consensus"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
func MakeCommit(blockID types.BlockID, height int64, round int32,
|
||||
voteSet *types.VoteSet, validators []types.PrivValidator, now time.Time) (*types.Commit, error) {
|
||||
func MakeRandomCommit(time time.Time) *meta.Commit {
|
||||
lastID := MakeBlockID()
|
||||
h := int64(3)
|
||||
voteSet, _, vals := RandVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1)
|
||||
commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals, time)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return commit
|
||||
}
|
||||
|
||||
func MakeCommit(blockID meta.BlockID, height int64, round int32,
|
||||
voteSet *consensus.VoteSet, validators []consensus.PrivValidator, now time.Time) (*meta.Commit, error) {
|
||||
|
||||
// all sign
|
||||
for i := 0; i < len(validators); i++ {
|
||||
@@ -18,7 +30,7 @@ func MakeCommit(blockID types.BlockID, height int64, round int32,
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't get pubkey: %w", err)
|
||||
}
|
||||
vote := &types.Vote{
|
||||
vote := &consensus.Vote{
|
||||
ValidatorAddress: pubKey.Address(),
|
||||
ValidatorIndex: int32(i),
|
||||
Height: height,
|
||||
@@ -28,7 +40,7 @@ func MakeCommit(blockID types.BlockID, height int64, round int32,
|
||||
Timestamp: now,
|
||||
}
|
||||
|
||||
_, err = signAddVote(validators[i], vote, voteSet)
|
||||
_, err = SignAddVote(validators[i], vote, voteSet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -37,7 +49,7 @@ func MakeCommit(blockID types.BlockID, height int64, round int32,
|
||||
return voteSet.MakeCommit(), nil
|
||||
}
|
||||
|
||||
func signAddVote(privVal types.PrivValidator, vote *types.Vote, voteSet *types.VoteSet) (signed bool, err error) {
|
||||
func SignAddVote(privVal consensus.PrivValidator, vote *consensus.Vote, voteSet *consensus.VoteSet) (signed bool, err error) {
|
||||
v := vote.ToProto()
|
||||
err = privVal.SignVote(context.Background(), voteSet.ChainID(), v)
|
||||
if err != nil {
|
||||
|
||||
@@ -5,10 +5,10 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/tendermint/tendermint/types"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
)
|
||||
|
||||
func TestMakeHeader(t *testing.T) {
|
||||
_, err := MakeHeader(&types.Header{})
|
||||
_, err := MakeHeader(&meta.Header{})
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -5,28 +5,28 @@ import (
|
||||
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
tmtime "github.com/tendermint/tendermint/libs/time"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
"github.com/tendermint/tendermint/pkg/consensus"
|
||||
)
|
||||
|
||||
func RandGenesisDoc(
|
||||
config *cfg.Config,
|
||||
numValidators int,
|
||||
randPower bool,
|
||||
minPower int64) (*types.GenesisDoc, []types.PrivValidator) {
|
||||
minPower int64) (*consensus.GenesisDoc, []consensus.PrivValidator) {
|
||||
|
||||
validators := make([]types.GenesisValidator, numValidators)
|
||||
privValidators := make([]types.PrivValidator, numValidators)
|
||||
validators := make([]consensus.GenesisValidator, numValidators)
|
||||
privValidators := make([]consensus.PrivValidator, numValidators)
|
||||
for i := 0; i < numValidators; i++ {
|
||||
val, privVal := RandValidator(randPower, minPower)
|
||||
validators[i] = types.GenesisValidator{
|
||||
validators[i] = consensus.GenesisValidator{
|
||||
PubKey: val.PubKey,
|
||||
Power: val.VotingPower,
|
||||
}
|
||||
privValidators[i] = privVal
|
||||
}
|
||||
sort.Sort(types.PrivValidatorsByAddress(privValidators))
|
||||
sort.Sort(consensus.PrivValidatorsByAddress(privValidators))
|
||||
|
||||
return &types.GenesisDoc{
|
||||
return &consensus.GenesisDoc{
|
||||
GenesisTime: tmtime.Now(),
|
||||
InitialHeight: 1,
|
||||
ChainID: config.ChainID(),
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
package factory
|
||||
|
||||
import "github.com/tendermint/tendermint/types"
|
||||
import "github.com/tendermint/tendermint/pkg/mempool"
|
||||
|
||||
// MakeTxs is a helper function to generate mock transactions by given the block height
|
||||
// and the transaction numbers.
|
||||
func MakeTxs(height int64, num int) (txs []types.Tx) {
|
||||
func MakeTxs(height int64, num int) (txs []mempool.Tx) {
|
||||
for i := 0; i < num; i++ {
|
||||
txs = append(txs, types.Tx([]byte{byte(height), byte(i)}))
|
||||
txs = append(txs, mempool.Tx([]byte{byte(height), byte(i)}))
|
||||
}
|
||||
return txs
|
||||
}
|
||||
|
||||
func MakeTenTxs(height int64) (txs []types.Tx) {
|
||||
func MakeTenTxs(height int64) (txs []mempool.Tx) {
|
||||
return MakeTxs(height, 10)
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@ import (
|
||||
"math/rand"
|
||||
"sort"
|
||||
|
||||
"github.com/tendermint/tendermint/types"
|
||||
"github.com/tendermint/tendermint/pkg/consensus"
|
||||
)
|
||||
|
||||
func RandValidator(randPower bool, minPower int64) (*types.Validator, types.PrivValidator) {
|
||||
privVal := types.NewMockPV()
|
||||
func RandValidator(randPower bool, minPower int64) (*consensus.Validator, consensus.PrivValidator) {
|
||||
privVal := consensus.NewMockPV()
|
||||
votePower := minPower
|
||||
if randPower {
|
||||
// nolint:gosec // G404: Use of weak random number generator
|
||||
@@ -20,14 +20,14 @@ func RandValidator(randPower bool, minPower int64) (*types.Validator, types.Priv
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("could not retrieve pubkey %w", err))
|
||||
}
|
||||
val := types.NewValidator(pubKey, votePower)
|
||||
val := consensus.NewValidator(pubKey, votePower)
|
||||
return val, privVal
|
||||
}
|
||||
|
||||
func RandValidatorSet(numValidators int, votingPower int64) (*types.ValidatorSet, []types.PrivValidator) {
|
||||
func RandValidatorSet(numValidators int, votingPower int64) (*consensus.ValidatorSet, []consensus.PrivValidator) {
|
||||
var (
|
||||
valz = make([]*types.Validator, numValidators)
|
||||
privValidators = make([]types.PrivValidator, numValidators)
|
||||
valz = make([]*consensus.Validator, numValidators)
|
||||
privValidators = make([]consensus.PrivValidator, numValidators)
|
||||
)
|
||||
|
||||
for i := 0; i < numValidators; i++ {
|
||||
@@ -36,7 +36,7 @@ func RandValidatorSet(numValidators int, votingPower int64) (*types.ValidatorSet
|
||||
privValidators[i] = privValidator
|
||||
}
|
||||
|
||||
sort.Sort(types.PrivValidatorsByAddress(privValidators))
|
||||
sort.Sort(consensus.PrivValidatorsByAddress(privValidators))
|
||||
|
||||
return types.NewValidatorSet(valz), privValidators
|
||||
return consensus.NewValidatorSet(valz), privValidators
|
||||
}
|
||||
|
||||
@@ -2,27 +2,32 @@ package factory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
"github.com/tendermint/tendermint/pkg/consensus"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
func MakeVote(
|
||||
val types.PrivValidator,
|
||||
val consensus.PrivValidator,
|
||||
chainID string,
|
||||
valIndex int32,
|
||||
height int64,
|
||||
round int32,
|
||||
step int,
|
||||
blockID types.BlockID,
|
||||
blockID meta.BlockID,
|
||||
time time.Time,
|
||||
) (*types.Vote, error) {
|
||||
) (*consensus.Vote, error) {
|
||||
pubKey, err := val.GetPubKey(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v := &types.Vote{
|
||||
v := &consensus.Vote{
|
||||
ValidatorAddress: pubKey.Address(),
|
||||
ValidatorIndex: valIndex,
|
||||
Height: height,
|
||||
@@ -40,3 +45,71 @@ func MakeVote(
|
||||
v.Signature = vpb.Signature
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func RandVoteSet(
|
||||
height int64,
|
||||
round int32,
|
||||
signedMsgType tmproto.SignedMsgType,
|
||||
numValidators int,
|
||||
votingPower int64,
|
||||
) (*consensus.VoteSet, *consensus.ValidatorSet, []consensus.PrivValidator) {
|
||||
valSet, privValidators := RandValidatorPrivValSet(numValidators, votingPower)
|
||||
return consensus.NewVoteSet("test_chain_id", height, round, signedMsgType, valSet), valSet, privValidators
|
||||
}
|
||||
|
||||
func RandValidatorPrivValSet(numValidators int, votingPower int64) (*consensus.ValidatorSet, []consensus.PrivValidator) {
|
||||
var (
|
||||
valz = make([]*consensus.Validator, numValidators)
|
||||
privValidators = make([]consensus.PrivValidator, numValidators)
|
||||
)
|
||||
|
||||
for i := 0; i < numValidators; i++ {
|
||||
val, privValidator := RandValidator(false, votingPower)
|
||||
valz[i] = val
|
||||
privValidators[i] = privValidator
|
||||
}
|
||||
|
||||
sort.Sort(consensus.PrivValidatorsByAddress(privValidators))
|
||||
|
||||
return consensus.NewValidatorSet(valz), privValidators
|
||||
}
|
||||
|
||||
func DeterministicVoteSet(
|
||||
height int64,
|
||||
round int32,
|
||||
signedMsgType tmproto.SignedMsgType,
|
||||
votingPower int64,
|
||||
) (*consensus.VoteSet, *consensus.ValidatorSet, []consensus.PrivValidator) {
|
||||
valSet, privValidators := DeterministicValidatorSet()
|
||||
return consensus.NewVoteSet("test_chain_id", height, round, signedMsgType, valSet), valSet, privValidators
|
||||
}
|
||||
|
||||
func DeterministicValidatorSet() (*consensus.ValidatorSet, []consensus.PrivValidator) {
|
||||
var (
|
||||
valz = make([]*consensus.Validator, 10)
|
||||
privValidators = make([]consensus.PrivValidator, 10)
|
||||
)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
// val, privValidator := DeterministicValidator(ed25519.PrivKey([]byte(deterministicKeys[i])))
|
||||
val, privValidator := DeterministicValidator(ed25519.GenPrivKeyFromSecret([]byte(fmt.Sprintf("key: %x", i))))
|
||||
valz[i] = val
|
||||
privValidators[i] = privValidator
|
||||
}
|
||||
|
||||
sort.Sort(consensus.PrivValidatorsByAddress(privValidators))
|
||||
|
||||
return consensus.NewValidatorSet(valz), privValidators
|
||||
}
|
||||
|
||||
func DeterministicValidator(key crypto.PrivKey) (*consensus.Validator, consensus.PrivValidator) {
|
||||
privVal := consensus.NewMockPV()
|
||||
privVal.PrivKey = key
|
||||
var votePower int64 = 50
|
||||
pubKey, err := privVal.GetPubKey(context.TODO())
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("could not retrieve pubkey %w", err))
|
||||
}
|
||||
val := consensus.NewValidator(pubKey, votePower)
|
||||
return val, privVal
|
||||
}
|
||||
|
||||
@@ -9,6 +9,78 @@ var ErrOverflowInt32 = errors.New("int32 overflow")
|
||||
var ErrOverflowUint8 = errors.New("uint8 overflow")
|
||||
var ErrOverflowInt8 = errors.New("int8 overflow")
|
||||
|
||||
// SafeAdd adds two int64 numbers. If there is an overflow,
|
||||
// the function will return -1, true
|
||||
func SafeAdd(a, b int64) (int64, bool) {
|
||||
if b > 0 && a > math.MaxInt64-b {
|
||||
return -1, true
|
||||
} else if b < 0 && a < math.MinInt64-b {
|
||||
return -1, true
|
||||
}
|
||||
return a + b, false
|
||||
}
|
||||
|
||||
// SafeSub subtracts two int64 numbers. If there is an overflow,
|
||||
// the function will return -1, true
|
||||
func SafeSub(a, b int64) (int64, bool) {
|
||||
if b > 0 && a < math.MinInt64+b {
|
||||
return -1, true
|
||||
} else if b < 0 && a > math.MaxInt64+b {
|
||||
return -1, true
|
||||
}
|
||||
return a - b, false
|
||||
}
|
||||
|
||||
// SafeAddClip performs SafeAdd, however if there is an overflow,
|
||||
// it will return the maxInt64
|
||||
func SafeAddClip(a, b int64) int64 {
|
||||
c, overflow := SafeAdd(a, b)
|
||||
if overflow {
|
||||
if b < 0 {
|
||||
return math.MinInt64
|
||||
}
|
||||
return math.MaxInt64
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// SafeSubClip performs SafeSub but will clip the result to either
|
||||
// the min or max int64 number
|
||||
func SafeSubClip(a, b int64) int64 {
|
||||
c, overflow := SafeSub(a, b)
|
||||
if overflow {
|
||||
if b > 0 {
|
||||
return math.MinInt64
|
||||
}
|
||||
return math.MaxInt64
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// SafeMul multiplies two int64 numbers. It returns
|
||||
// true if the resultant overflows.
|
||||
func SafeMul(a, b int64) (int64, bool) {
|
||||
if a == 0 || b == 0 {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
absOfB := b
|
||||
if b < 0 {
|
||||
absOfB = -b
|
||||
}
|
||||
|
||||
absOfA := a
|
||||
if a < 0 {
|
||||
absOfA = -a
|
||||
}
|
||||
|
||||
if absOfA > math.MaxInt64/absOfB {
|
||||
return 0, true
|
||||
}
|
||||
|
||||
return a * b, false
|
||||
}
|
||||
|
||||
// SafeAddInt32 adds two int32 integers
|
||||
// If there is an overflow this will panic
|
||||
func SafeAddInt32(a, b int32) int32 {
|
||||
|
||||
60
libs/math/safemath_test.go
Normal file
60
libs/math/safemath_test.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package math_test
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
"testing/quick"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
tmmath "github.com/tendermint/tendermint/libs/math"
|
||||
)
|
||||
|
||||
func TestSafeAdd(t *testing.T) {
|
||||
f := func(a, b int64) bool {
|
||||
c, overflow := tmmath.SafeAdd(a, b)
|
||||
return overflow || (!overflow && c == a+b)
|
||||
}
|
||||
if err := quick.Check(f, nil); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSafeAddClip(t *testing.T) {
|
||||
assert.EqualValues(t, math.MaxInt64, tmmath.SafeAddClip(math.MaxInt64, 10))
|
||||
assert.EqualValues(t, math.MaxInt64, tmmath.SafeAddClip(math.MaxInt64, math.MaxInt64))
|
||||
assert.EqualValues(t, math.MinInt64, tmmath.SafeAddClip(math.MinInt64, -10))
|
||||
}
|
||||
|
||||
func TestSafeSubClip(t *testing.T) {
|
||||
assert.EqualValues(t, math.MinInt64, tmmath.SafeSubClip(math.MinInt64, 10))
|
||||
assert.EqualValues(t, 0, tmmath.SafeSubClip(math.MinInt64, math.MinInt64))
|
||||
assert.EqualValues(t, math.MinInt64, tmmath.SafeSubClip(math.MinInt64, math.MaxInt64))
|
||||
assert.EqualValues(t, math.MaxInt64, tmmath.SafeSubClip(math.MaxInt64, -10))
|
||||
}
|
||||
|
||||
func TestSafeMul(t *testing.T) {
|
||||
testCases := []struct {
|
||||
a int64
|
||||
b int64
|
||||
c int64
|
||||
overflow bool
|
||||
}{
|
||||
0: {0, 0, 0, false},
|
||||
1: {1, 0, 0, false},
|
||||
2: {2, 3, 6, false},
|
||||
3: {2, -3, -6, false},
|
||||
4: {-2, -3, 6, false},
|
||||
5: {-2, 3, -6, false},
|
||||
6: {math.MaxInt64, 1, math.MaxInt64, false},
|
||||
7: {math.MaxInt64 / 2, 2, math.MaxInt64 - 1, false},
|
||||
8: {math.MaxInt64 / 2, 3, 0, true},
|
||||
9: {math.MaxInt64, 2, 0, true},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
c, overflow := tmmath.SafeMul(tc.a, tc.b)
|
||||
assert.Equal(t, tc.c, c, "#%d", i)
|
||||
assert.Equal(t, tc.overflow, overflow, "#%d", i)
|
||||
}
|
||||
}
|
||||
491
pkg/block/block.go
Normal file
491
pkg/block/block.go
Normal file
@@ -0,0 +1,491 @@
|
||||
package block
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
|
||||
tmsync "github.com/tendermint/tendermint/internal/libs/sync"
|
||||
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
||||
tmmath "github.com/tendermint/tendermint/libs/math"
|
||||
"github.com/tendermint/tendermint/pkg/evidence"
|
||||
"github.com/tendermint/tendermint/pkg/mempool"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
"github.com/tendermint/tendermint/version"
|
||||
)
|
||||
|
||||
// Block defines the atomic unit of a Tendermint blockchain.
|
||||
type Block struct {
|
||||
mtx tmsync.Mutex
|
||||
|
||||
meta.Header `json:"header"`
|
||||
Data `json:"data"`
|
||||
Evidence EvidenceData `json:"evidence"`
|
||||
LastCommit *meta.Commit `json:"last_commit"`
|
||||
}
|
||||
|
||||
// ValidateBasic performs basic validation that doesn't involve state data.
|
||||
// It checks the internal consistency of the block.
|
||||
// Further validation is done using state#ValidateBlock.
|
||||
func (b *Block) ValidateBasic() error {
|
||||
if b == nil {
|
||||
return errors.New("nil block")
|
||||
}
|
||||
|
||||
b.mtx.Lock()
|
||||
defer b.mtx.Unlock()
|
||||
|
||||
if err := b.Header.ValidateBasic(); err != nil {
|
||||
return fmt.Errorf("invalid header: %w", err)
|
||||
}
|
||||
|
||||
// Validate the last commit and its hash.
|
||||
if b.LastCommit == nil {
|
||||
return errors.New("nil LastCommit")
|
||||
}
|
||||
if err := b.LastCommit.ValidateBasic(); err != nil {
|
||||
return fmt.Errorf("wrong LastCommit: %v", err)
|
||||
}
|
||||
|
||||
if w, g := b.LastCommit.Hash(), b.LastCommitHash; !bytes.Equal(w, g) {
|
||||
return fmt.Errorf("wrong Header.LastCommitHash. Expected %X, got %X", w, g)
|
||||
}
|
||||
|
||||
// NOTE: b.Data.Txs may be nil, but b.Data.Hash() still works fine.
|
||||
if w, g := b.Data.Hash(), b.DataHash; !bytes.Equal(w, g) {
|
||||
return fmt.Errorf("wrong Header.DataHash. Expected %X, got %X", w, g)
|
||||
}
|
||||
|
||||
// NOTE: b.Evidence.Evidence may be nil, but we're just looping.
|
||||
for i, ev := range b.Evidence.Evidence {
|
||||
if err := ev.ValidateBasic(); err != nil {
|
||||
return fmt.Errorf("invalid evidence (#%d): %v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
if w, g := b.Evidence.Hash(), b.EvidenceHash; !bytes.Equal(w, g) {
|
||||
return fmt.Errorf("wrong Header.EvidenceHash. Expected %X, got %X", w, g)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// fillHeader fills in any remaining header fields that are a function of the block data
|
||||
func (b *Block) fillHeader() {
|
||||
if b.LastCommitHash == nil {
|
||||
b.LastCommitHash = b.LastCommit.Hash()
|
||||
}
|
||||
if b.DataHash == nil {
|
||||
b.DataHash = b.Data.Hash()
|
||||
}
|
||||
if b.EvidenceHash == nil {
|
||||
b.EvidenceHash = b.Evidence.Hash()
|
||||
}
|
||||
}
|
||||
|
||||
// Hash computes and returns the block hash.
|
||||
// If the block is incomplete, block hash is nil for safety.
|
||||
func (b *Block) Hash() tmbytes.HexBytes {
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
b.mtx.Lock()
|
||||
defer b.mtx.Unlock()
|
||||
|
||||
if b.LastCommit == nil {
|
||||
return nil
|
||||
}
|
||||
b.fillHeader()
|
||||
return b.Header.Hash()
|
||||
}
|
||||
|
||||
// MakePartSet returns a PartSet containing parts of a serialized block.
|
||||
// This is the form in which the block is gossipped to peers.
|
||||
// CONTRACT: partSize is greater than zero.
|
||||
func (b *Block) MakePartSet(partSize uint32) *meta.PartSet {
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
b.mtx.Lock()
|
||||
defer b.mtx.Unlock()
|
||||
|
||||
pbb, err := b.ToProto()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
bz, err := proto.Marshal(pbb)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return meta.NewPartSetFromData(bz, partSize)
|
||||
}
|
||||
|
||||
// HashesTo is a convenience function that checks if a block hashes to the given argument.
|
||||
// Returns false if the block is nil or the hash is empty.
|
||||
func (b *Block) HashesTo(hash []byte) bool {
|
||||
if len(hash) == 0 {
|
||||
return false
|
||||
}
|
||||
if b == nil {
|
||||
return false
|
||||
}
|
||||
return bytes.Equal(b.Hash(), hash)
|
||||
}
|
||||
|
||||
// Size returns size of the block in bytes.
|
||||
func (b *Block) Size() int {
|
||||
pbb, err := b.ToProto()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return pbb.Size()
|
||||
}
|
||||
|
||||
// String returns a string representation of the block
|
||||
//
|
||||
// See StringIndented.
|
||||
func (b *Block) String() string {
|
||||
return b.StringIndented("")
|
||||
}
|
||||
|
||||
// StringIndented returns an indented String.
|
||||
//
|
||||
// Header
|
||||
// Data
|
||||
// Evidence
|
||||
// LastCommit
|
||||
// Hash
|
||||
func (b *Block) StringIndented(indent string) string {
|
||||
if b == nil {
|
||||
return "nil-Block"
|
||||
}
|
||||
return fmt.Sprintf(`Block{
|
||||
%s %v
|
||||
%s %v
|
||||
%s %v
|
||||
%s %v
|
||||
%s}#%v`,
|
||||
indent, b.Header.StringIndented(indent+" "),
|
||||
indent, b.Data.StringIndented(indent+" "),
|
||||
indent, b.Evidence.StringIndented(indent+" "),
|
||||
indent, b.LastCommit.StringIndented(indent+" "),
|
||||
indent, b.Hash())
|
||||
}
|
||||
|
||||
// StringShort returns a shortened string representation of the block.
|
||||
func (b *Block) StringShort() string {
|
||||
if b == nil {
|
||||
return "nil-Block"
|
||||
}
|
||||
return fmt.Sprintf("Block#%X", b.Hash())
|
||||
}
|
||||
|
||||
// ToProto converts Block to protobuf
|
||||
func (b *Block) ToProto() (*tmproto.Block, error) {
|
||||
if b == nil {
|
||||
return nil, errors.New("nil Block")
|
||||
}
|
||||
|
||||
pb := new(tmproto.Block)
|
||||
|
||||
pb.Header = *b.Header.ToProto()
|
||||
pb.LastCommit = b.LastCommit.ToProto()
|
||||
pb.Data = b.Data.ToProto()
|
||||
|
||||
protoEvidence, err := b.Evidence.ToProto()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pb.Evidence = *protoEvidence
|
||||
|
||||
return pb, nil
|
||||
}
|
||||
|
||||
// FromProto sets a protobuf Block to the given pointer.
|
||||
// It returns an error if the block is invalid.
|
||||
func BlockFromProto(bp *tmproto.Block) (*Block, error) {
|
||||
if bp == nil {
|
||||
return nil, errors.New("nil block")
|
||||
}
|
||||
|
||||
b := new(Block)
|
||||
h, err := meta.HeaderFromProto(&bp.Header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.Header = h
|
||||
data, err := DataFromProto(&bp.Data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.Data = data
|
||||
if err := b.Evidence.FromProto(&bp.Evidence); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if bp.LastCommit != nil {
|
||||
lc, err := meta.CommitFromProto(bp.LastCommit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.LastCommit = lc
|
||||
}
|
||||
|
||||
return b, b.ValidateBasic()
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// MaxDataBytes returns the maximum size of block's data.
|
||||
//
|
||||
// XXX: Panics on negative result.
|
||||
func MaxDataBytes(maxBytes, evidenceBytes int64, valsCount int) int64 {
|
||||
maxDataBytes := maxBytes -
|
||||
meta.MaxOverheadForBlock -
|
||||
meta.MaxHeaderBytes -
|
||||
meta.MaxCommitBytes(valsCount) -
|
||||
evidenceBytes
|
||||
|
||||
if maxDataBytes < 0 {
|
||||
panic(fmt.Sprintf(
|
||||
"Negative MaxDataBytes. Block.MaxBytes=%d is too small to accommodate header&lastCommit&evidence=%d",
|
||||
maxBytes,
|
||||
-(maxDataBytes - maxBytes),
|
||||
))
|
||||
}
|
||||
|
||||
return maxDataBytes
|
||||
}
|
||||
|
||||
// MaxDataBytesNoEvidence returns the maximum size of block's data when
|
||||
// evidence count is unknown. MaxEvidencePerBlock will be used for the size
|
||||
// of evidence.
|
||||
//
|
||||
// XXX: Panics on negative result.
|
||||
func MaxDataBytesNoEvidence(maxBytes int64, valsCount int) int64 {
|
||||
maxDataBytes := maxBytes -
|
||||
meta.MaxOverheadForBlock -
|
||||
meta.MaxHeaderBytes -
|
||||
meta.MaxCommitBytes(valsCount)
|
||||
|
||||
if maxDataBytes < 0 {
|
||||
panic(fmt.Sprintf(
|
||||
"Negative MaxDataBytesUnknownEvidence. Block.MaxBytes=%d is too small to accommodate header&lastCommit&evidence=%d",
|
||||
maxBytes,
|
||||
-(maxDataBytes - maxBytes),
|
||||
))
|
||||
}
|
||||
|
||||
return maxDataBytes
|
||||
}
|
||||
|
||||
// MakeBlock returns a new block with an empty header, except what can be
|
||||
// computed from itself.
|
||||
// It populates the same set of fields validated by ValidateBasic.
|
||||
func MakeBlock(height int64, txs []mempool.Tx, lastCommit *meta.Commit, evidence []evidence.Evidence) *Block {
|
||||
block := &Block{
|
||||
Header: meta.Header{
|
||||
Version: version.Consensus{Block: version.BlockProtocol, App: 0},
|
||||
Height: height,
|
||||
},
|
||||
Data: Data{
|
||||
Txs: txs,
|
||||
},
|
||||
Evidence: EvidenceData{Evidence: evidence},
|
||||
LastCommit: lastCommit,
|
||||
}
|
||||
block.fillHeader()
|
||||
return block
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Data contains the set of transactions included in the block
|
||||
type Data struct {
|
||||
|
||||
// Txs that will be applied by state @ block.Height+1.
|
||||
// NOTE: not all txs here are valid. We're just agreeing on the order first.
|
||||
// This means that block.AppHash does not include these txs.
|
||||
Txs mempool.Txs `json:"txs"`
|
||||
|
||||
// Volatile
|
||||
hash tmbytes.HexBytes
|
||||
}
|
||||
|
||||
// Hash returns the hash of the data
|
||||
func (data *Data) Hash() tmbytes.HexBytes {
|
||||
if data == nil {
|
||||
return (mempool.Txs{}).Hash()
|
||||
}
|
||||
if data.hash == nil {
|
||||
data.hash = data.Txs.Hash() // NOTE: leaves of merkle tree are TxIDs
|
||||
}
|
||||
return data.hash
|
||||
}
|
||||
|
||||
// StringIndented returns an indented string representation of the transactions.
|
||||
func (data *Data) StringIndented(indent string) string {
|
||||
if data == nil {
|
||||
return "nil-Data"
|
||||
}
|
||||
txStrings := make([]string, tmmath.MinInt(len(data.Txs), 21))
|
||||
for i, tx := range data.Txs {
|
||||
if i == 20 {
|
||||
txStrings[i] = fmt.Sprintf("... (%v total)", len(data.Txs))
|
||||
break
|
||||
}
|
||||
txStrings[i] = fmt.Sprintf("%X (%d bytes)", tx.Hash(), len(tx))
|
||||
}
|
||||
return fmt.Sprintf(`Data{
|
||||
%s %v
|
||||
%s}#%v`,
|
||||
indent, strings.Join(txStrings, "\n"+indent+" "),
|
||||
indent, data.hash)
|
||||
}
|
||||
|
||||
// ToProto converts Data to protobuf
|
||||
func (data *Data) ToProto() tmproto.Data {
|
||||
tp := new(tmproto.Data)
|
||||
|
||||
if len(data.Txs) > 0 {
|
||||
txBzs := make([][]byte, len(data.Txs))
|
||||
for i := range data.Txs {
|
||||
txBzs[i] = data.Txs[i]
|
||||
}
|
||||
tp.Txs = txBzs
|
||||
}
|
||||
|
||||
return *tp
|
||||
}
|
||||
|
||||
// ClearCache removes the saved hash. This is predominantly used for testing.
|
||||
func (data *Data) ClearCache() {
|
||||
data.hash = nil
|
||||
}
|
||||
|
||||
// DataFromProto takes a protobuf representation of Data &
|
||||
// returns the native type.
|
||||
func DataFromProto(dp *tmproto.Data) (Data, error) {
|
||||
if dp == nil {
|
||||
return Data{}, errors.New("nil data")
|
||||
}
|
||||
data := new(Data)
|
||||
|
||||
if len(dp.Txs) > 0 {
|
||||
txBzs := make(mempool.Txs, len(dp.Txs))
|
||||
for i := range dp.Txs {
|
||||
txBzs[i] = mempool.Tx(dp.Txs[i])
|
||||
}
|
||||
data.Txs = txBzs
|
||||
} else {
|
||||
data.Txs = mempool.Txs{}
|
||||
}
|
||||
|
||||
return *data, nil
|
||||
}
|
||||
|
||||
// ComputeProtoSizeForTxs wraps the transactions in tmproto.Data{} and calculates the size.
|
||||
// https://developers.google.com/protocol-buffers/docs/encoding
|
||||
func ComputeProtoSizeForTxs(txs []mempool.Tx) int64 {
|
||||
data := Data{Txs: txs}
|
||||
pdData := data.ToProto()
|
||||
return int64(pdData.Size())
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// EvidenceData contains any evidence of malicious wrong-doing by validators
|
||||
type EvidenceData struct {
|
||||
Evidence evidence.EvidenceList `json:"evidence"`
|
||||
|
||||
// Volatile. Used as cache
|
||||
hash tmbytes.HexBytes
|
||||
byteSize int64
|
||||
}
|
||||
|
||||
// Hash returns the hash of the data.
|
||||
func (data *EvidenceData) Hash() tmbytes.HexBytes {
|
||||
if data.hash == nil {
|
||||
data.hash = data.Evidence.Hash()
|
||||
}
|
||||
return data.hash
|
||||
}
|
||||
|
||||
// ByteSize returns the total byte size of all the evidence
|
||||
func (data *EvidenceData) ByteSize() int64 {
|
||||
if data.byteSize == 0 && len(data.Evidence) != 0 {
|
||||
pb, err := data.ToProto()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
data.byteSize = int64(pb.Size())
|
||||
}
|
||||
return data.byteSize
|
||||
}
|
||||
|
||||
// StringIndented returns a string representation of the evidence.
|
||||
func (data *EvidenceData) StringIndented(indent string) string {
|
||||
if data == nil {
|
||||
return "nil-Evidence"
|
||||
}
|
||||
evStrings := make([]string, tmmath.MinInt(len(data.Evidence), 21))
|
||||
for i, ev := range data.Evidence {
|
||||
if i == 20 {
|
||||
evStrings[i] = fmt.Sprintf("... (%v total)", len(data.Evidence))
|
||||
break
|
||||
}
|
||||
evStrings[i] = fmt.Sprintf("Evidence:%v", ev)
|
||||
}
|
||||
return fmt.Sprintf(`EvidenceData{
|
||||
%s %v
|
||||
%s}#%v`,
|
||||
indent, strings.Join(evStrings, "\n"+indent+" "),
|
||||
indent, data.hash)
|
||||
}
|
||||
|
||||
// ToProto converts EvidenceData to protobuf
|
||||
func (data *EvidenceData) ToProto() (*tmproto.EvidenceList, error) {
|
||||
if data == nil {
|
||||
return nil, errors.New("nil evidence data")
|
||||
}
|
||||
|
||||
evi := new(tmproto.EvidenceList)
|
||||
eviBzs := make([]tmproto.Evidence, len(data.Evidence))
|
||||
for i := range data.Evidence {
|
||||
protoEvi, err := evidence.EvidenceToProto(data.Evidence[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
eviBzs[i] = *protoEvi
|
||||
}
|
||||
evi.Evidence = eviBzs
|
||||
|
||||
return evi, nil
|
||||
}
|
||||
|
||||
// FromProto sets a protobuf EvidenceData to the given pointer.
|
||||
func (data *EvidenceData) FromProto(eviData *tmproto.EvidenceList) error {
|
||||
if eviData == nil {
|
||||
return errors.New("nil evidenceData")
|
||||
}
|
||||
|
||||
eviBzs := make(evidence.EvidenceList, len(eviData.Evidence))
|
||||
for i := range eviData.Evidence {
|
||||
evi, err := evidence.EvidenceFromProto(&eviData.Evidence[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
eviBzs[i] = evi
|
||||
}
|
||||
data.Evidence = eviBzs
|
||||
data.byteSize = int64(eviData.Size())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
@@ -1,25 +1,26 @@
|
||||
package types
|
||||
package block
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
|
||||
// BlockMeta contains meta information.
|
||||
type BlockMeta struct {
|
||||
BlockID BlockID `json:"block_id"`
|
||||
BlockSize int `json:"block_size"`
|
||||
Header Header `json:"header"`
|
||||
NumTxs int `json:"num_txs"`
|
||||
BlockID meta.BlockID `json:"block_id"`
|
||||
BlockSize int `json:"block_size"`
|
||||
Header meta.Header `json:"header"`
|
||||
NumTxs int `json:"num_txs"`
|
||||
}
|
||||
|
||||
// NewBlockMeta returns a new BlockMeta.
|
||||
func NewBlockMeta(block *Block, blockParts *PartSet) *BlockMeta {
|
||||
func NewBlockMeta(block *Block, blockParts *meta.PartSet) *BlockMeta {
|
||||
return &BlockMeta{
|
||||
BlockID: BlockID{block.Hash(), blockParts.Header()},
|
||||
BlockID: meta.BlockID{block.Hash(), blockParts.Header()},
|
||||
BlockSize: block.Size(),
|
||||
Header: block.Header,
|
||||
NumTxs: len(block.Data.Txs),
|
||||
@@ -47,12 +48,12 @@ func BlockMetaFromProto(pb *tmproto.BlockMeta) (*BlockMeta, error) {
|
||||
|
||||
bm := new(BlockMeta)
|
||||
|
||||
bi, err := BlockIDFromProto(&pb.BlockID)
|
||||
bi, err := meta.BlockIDFromProto(&pb.BlockID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
h, err := HeaderFromProto(&pb.Header)
|
||||
h, err := meta.HeaderFromProto(&pb.Header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package block_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -6,23 +6,26 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
test "github.com/tendermint/tendermint/internal/test/factory"
|
||||
tmrand "github.com/tendermint/tendermint/libs/rand"
|
||||
"github.com/tendermint/tendermint/pkg/block"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
)
|
||||
|
||||
func TestBlockMeta_ToProto(t *testing.T) {
|
||||
h := MakeRandHeader()
|
||||
bi := BlockID{Hash: h.Hash(), PartSetHeader: PartSetHeader{Total: 123, Hash: tmrand.Bytes(tmhash.Size)}}
|
||||
h := test.MakeRandomHeader()
|
||||
bi := meta.BlockID{Hash: h.Hash(), PartSetHeader: meta.PartSetHeader{Total: 123, Hash: tmrand.Bytes(tmhash.Size)}}
|
||||
|
||||
bm := &BlockMeta{
|
||||
bm := &block.BlockMeta{
|
||||
BlockID: bi,
|
||||
BlockSize: 200,
|
||||
Header: h,
|
||||
Header: *h,
|
||||
NumTxs: 0,
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
testName string
|
||||
bm *BlockMeta
|
||||
bm *block.BlockMeta
|
||||
expErr bool
|
||||
}{
|
||||
{"success", bm, false},
|
||||
@@ -34,7 +37,7 @@ func TestBlockMeta_ToProto(t *testing.T) {
|
||||
t.Run(tt.testName, func(t *testing.T) {
|
||||
pb := tt.bm.ToProto()
|
||||
|
||||
bm, err := BlockMetaFromProto(pb)
|
||||
bm, err := block.BlockMetaFromProto(pb)
|
||||
|
||||
if !tt.expErr {
|
||||
require.NoError(t, err, tt.testName)
|
||||
@@ -47,37 +50,37 @@ func TestBlockMeta_ToProto(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBlockMeta_ValidateBasic(t *testing.T) {
|
||||
h := MakeRandHeader()
|
||||
bi := BlockID{Hash: h.Hash(), PartSetHeader: PartSetHeader{Total: 123, Hash: tmrand.Bytes(tmhash.Size)}}
|
||||
bi2 := BlockID{Hash: tmrand.Bytes(tmhash.Size),
|
||||
PartSetHeader: PartSetHeader{Total: 123, Hash: tmrand.Bytes(tmhash.Size)}}
|
||||
bi3 := BlockID{Hash: []byte("incorrect hash"),
|
||||
PartSetHeader: PartSetHeader{Total: 123, Hash: []byte("incorrect hash")}}
|
||||
h := test.MakeRandomHeader()
|
||||
bi := meta.BlockID{Hash: h.Hash(), PartSetHeader: meta.PartSetHeader{Total: 123, Hash: tmrand.Bytes(tmhash.Size)}}
|
||||
bi2 := meta.BlockID{Hash: tmrand.Bytes(tmhash.Size),
|
||||
PartSetHeader: meta.PartSetHeader{Total: 123, Hash: tmrand.Bytes(tmhash.Size)}}
|
||||
bi3 := meta.BlockID{Hash: []byte("incorrect hash"),
|
||||
PartSetHeader: meta.PartSetHeader{Total: 123, Hash: []byte("incorrect hash")}}
|
||||
|
||||
bm := &BlockMeta{
|
||||
bm := &block.BlockMeta{
|
||||
BlockID: bi,
|
||||
BlockSize: 200,
|
||||
Header: h,
|
||||
Header: *h,
|
||||
NumTxs: 0,
|
||||
}
|
||||
|
||||
bm2 := &BlockMeta{
|
||||
bm2 := &block.BlockMeta{
|
||||
BlockID: bi2,
|
||||
BlockSize: 200,
|
||||
Header: h,
|
||||
Header: *h,
|
||||
NumTxs: 0,
|
||||
}
|
||||
|
||||
bm3 := &BlockMeta{
|
||||
bm3 := &block.BlockMeta{
|
||||
BlockID: bi3,
|
||||
BlockSize: 200,
|
||||
Header: h,
|
||||
Header: *h,
|
||||
NumTxs: 0,
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
bm *BlockMeta
|
||||
bm *block.BlockMeta
|
||||
wantErr bool
|
||||
}{
|
||||
{"success", bm, false},
|
||||
364
pkg/block/block_test.go
Normal file
364
pkg/block/block_test.go
Normal file
@@ -0,0 +1,364 @@
|
||||
package block_test
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"math"
|
||||
mrand "math/rand"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
test "github.com/tendermint/tendermint/internal/test/factory"
|
||||
"github.com/tendermint/tendermint/libs/bytes"
|
||||
tmrand "github.com/tendermint/tendermint/libs/rand"
|
||||
"github.com/tendermint/tendermint/pkg/block"
|
||||
"github.com/tendermint/tendermint/pkg/evidence"
|
||||
"github.com/tendermint/tendermint/pkg/mempool"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
code := m.Run()
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
func TestBlockAddEvidence(t *testing.T) {
|
||||
txs := []mempool.Tx{mempool.Tx("foo"), mempool.Tx("bar")}
|
||||
lastID := test.MakeBlockID()
|
||||
h := int64(3)
|
||||
|
||||
voteSet, _, vals := test.RandVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1)
|
||||
commit, err := test.MakeCommit(lastID, h-1, 1, voteSet, vals, time.Now())
|
||||
require.NoError(t, err)
|
||||
|
||||
ev := evidence.NewMockDuplicateVoteEvidenceWithValidator(h, time.Now(), vals[0], "block-test-chain")
|
||||
evList := []evidence.Evidence{ev}
|
||||
|
||||
b := block.MakeBlock(h, txs, commit, evList)
|
||||
require.NotNil(t, b)
|
||||
require.Equal(t, 1, len(b.Evidence.Evidence))
|
||||
require.NotNil(t, b.EvidenceHash)
|
||||
}
|
||||
|
||||
func TestBlockValidateBasic(t *testing.T) {
|
||||
require.Error(t, (*block.Block)(nil).ValidateBasic())
|
||||
|
||||
txs := []mempool.Tx{mempool.Tx("foo"), mempool.Tx("bar")}
|
||||
lastID := test.MakeBlockID()
|
||||
h := int64(3)
|
||||
|
||||
voteSet, valSet, vals := test.RandVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1)
|
||||
commit, err := test.MakeCommit(lastID, h-1, 1, voteSet, vals, time.Now())
|
||||
require.NoError(t, err)
|
||||
|
||||
ev := evidence.NewMockDuplicateVoteEvidenceWithValidator(h, time.Now(), vals[0], "block-test-chain")
|
||||
evList := []evidence.Evidence{ev}
|
||||
|
||||
testCases := []struct {
|
||||
testName string
|
||||
malleateBlock func(*block.Block)
|
||||
expErr bool
|
||||
}{
|
||||
{"Make Block", func(blk *block.Block) {}, false},
|
||||
{"Make Block w/ proposer Addr", func(blk *block.Block) { blk.ProposerAddress = valSet.GetProposer().Address }, false},
|
||||
{"Negative Height", func(blk *block.Block) { blk.Height = -1 }, true},
|
||||
{"Remove 1/2 the commits", func(blk *block.Block) {
|
||||
blk.LastCommit.Signatures = commit.Signatures[:commit.Size()/2]
|
||||
blk.LastCommit.ClearCache() // clear hash or change wont be noticed
|
||||
}, true},
|
||||
{"Remove LastCommitHash", func(blk *block.Block) { blk.LastCommitHash = []byte("something else") }, true},
|
||||
{"Tampered Data", func(blk *block.Block) {
|
||||
blk.Data.Txs[0] = mempool.Tx("something else")
|
||||
blk.Data.ClearCache() // clear hash or change wont be noticed
|
||||
}, true},
|
||||
{"Tampered DataHash", func(blk *block.Block) {
|
||||
blk.DataHash = tmrand.Bytes(len(blk.DataHash))
|
||||
}, true},
|
||||
{"Tampered EvidenceHash", func(blk *block.Block) {
|
||||
blk.EvidenceHash = tmrand.Bytes(len(blk.EvidenceHash))
|
||||
}, true},
|
||||
{"Incorrect block protocol version", func(blk *block.Block) {
|
||||
blk.Version.Block = 1
|
||||
}, true},
|
||||
{"Missing LastCommit", func(blk *block.Block) {
|
||||
blk.LastCommit = nil
|
||||
}, true},
|
||||
{"Invalid LastCommit", func(blk *block.Block) {
|
||||
blk.LastCommit = meta.NewCommit(-1, 0, test.MakeBlockID(), nil)
|
||||
}, true},
|
||||
{"Invalid Evidence", func(blk *block.Block) {
|
||||
emptyEv := &evidence.DuplicateVoteEvidence{}
|
||||
blk.Evidence = block.EvidenceData{Evidence: []evidence.Evidence{emptyEv}}
|
||||
}, true},
|
||||
}
|
||||
for i, tc := range testCases {
|
||||
tc := tc
|
||||
i := i
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
block := block.MakeBlock(h, txs, commit, evList)
|
||||
block.ProposerAddress = valSet.GetProposer().Address
|
||||
tc.malleateBlock(block)
|
||||
err = block.ValidateBasic()
|
||||
t.Log(err)
|
||||
assert.Equal(t, tc.expErr, err != nil, "#%d: %v", i, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockHash(t *testing.T) {
|
||||
assert.Nil(t, (*block.Block)(nil).Hash())
|
||||
assert.Nil(t, block.MakeBlock(int64(3), []mempool.Tx{mempool.Tx("Hello World")}, nil, nil).Hash())
|
||||
}
|
||||
|
||||
func TestBlockMakePartSet(t *testing.T) {
|
||||
assert.Nil(t, (*block.Block)(nil).MakePartSet(2))
|
||||
|
||||
partSet := block.MakeBlock(int64(3), []mempool.Tx{mempool.Tx("Hello World")}, nil, nil).MakePartSet(1024)
|
||||
assert.NotNil(t, partSet)
|
||||
assert.EqualValues(t, 1, partSet.Total())
|
||||
}
|
||||
|
||||
func TestBlockMakePartSetWithEvidence(t *testing.T) {
|
||||
assert.Nil(t, (*block.Block)(nil).MakePartSet(2))
|
||||
|
||||
lastID := test.MakeBlockID()
|
||||
h := int64(3)
|
||||
|
||||
voteSet, _, vals := test.RandVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1)
|
||||
commit, err := test.MakeCommit(lastID, h-1, 1, voteSet, vals, time.Now())
|
||||
require.NoError(t, err)
|
||||
|
||||
ev := evidence.NewMockDuplicateVoteEvidenceWithValidator(h, time.Now(), vals[0], "block-test-chain")
|
||||
evList := []evidence.Evidence{ev}
|
||||
|
||||
partSet := block.MakeBlock(h, []mempool.Tx{mempool.Tx("Hello World")}, commit, evList).MakePartSet(512)
|
||||
assert.NotNil(t, partSet)
|
||||
assert.EqualValues(t, 4, partSet.Total())
|
||||
}
|
||||
|
||||
func TestBlockHashesTo(t *testing.T) {
|
||||
assert.False(t, (*block.Block)(nil).HashesTo(nil))
|
||||
|
||||
lastID := test.MakeBlockID()
|
||||
h := int64(3)
|
||||
voteSet, valSet, vals := test.RandVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1)
|
||||
commit, err := test.MakeCommit(lastID, h-1, 1, voteSet, vals, time.Now())
|
||||
require.NoError(t, err)
|
||||
|
||||
ev := evidence.NewMockDuplicateVoteEvidenceWithValidator(h, time.Now(), vals[0], "block-test-chain")
|
||||
evList := []evidence.Evidence{ev}
|
||||
|
||||
block := block.MakeBlock(h, []mempool.Tx{mempool.Tx("Hello World")}, commit, evList)
|
||||
block.ValidatorsHash = valSet.Hash()
|
||||
assert.False(t, block.HashesTo([]byte{}))
|
||||
assert.False(t, block.HashesTo([]byte("something else")))
|
||||
assert.True(t, block.HashesTo(block.Hash()))
|
||||
}
|
||||
|
||||
func TestBlockSize(t *testing.T) {
|
||||
size := block.MakeBlock(int64(3), []mempool.Tx{mempool.Tx("Hello World")}, nil, nil).Size()
|
||||
if size <= 0 {
|
||||
t.Fatal("Size of the block is zero or negative")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockString(t *testing.T) {
|
||||
assert.Equal(t, "nil-Block", (*block.Block)(nil).String())
|
||||
assert.Equal(t, "nil-Block", (*block.Block)(nil).StringIndented(""))
|
||||
assert.Equal(t, "nil-Block", (*block.Block)(nil).StringShort())
|
||||
|
||||
block := block.MakeBlock(int64(3), []mempool.Tx{mempool.Tx("Hello World")}, nil, nil)
|
||||
assert.NotEqual(t, "nil-Block", block.String())
|
||||
assert.NotEqual(t, "nil-Block", block.StringIndented(""))
|
||||
assert.NotEqual(t, "nil-Block", block.StringShort())
|
||||
}
|
||||
|
||||
func hexBytesFromString(s string) bytes.HexBytes {
|
||||
b, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bytes.HexBytes(b)
|
||||
}
|
||||
|
||||
func TestBlockMaxDataBytes(t *testing.T) {
|
||||
testCases := []struct {
|
||||
maxBytes int64
|
||||
valsCount int
|
||||
evidenceBytes int64
|
||||
panics bool
|
||||
result int64
|
||||
}{
|
||||
0: {-10, 1, 0, true, 0},
|
||||
1: {10, 1, 0, true, 0},
|
||||
2: {841, 1, 0, true, 0},
|
||||
3: {842, 1, 0, false, 0},
|
||||
4: {843, 1, 0, false, 1},
|
||||
5: {954, 2, 0, false, 1},
|
||||
6: {1053, 2, 100, false, 0},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
tc := tc
|
||||
if tc.panics {
|
||||
assert.Panics(t, func() {
|
||||
block.MaxDataBytes(tc.maxBytes, tc.evidenceBytes, tc.valsCount)
|
||||
}, "#%v", i)
|
||||
} else {
|
||||
assert.Equal(t,
|
||||
tc.result,
|
||||
block.MaxDataBytes(tc.maxBytes, tc.evidenceBytes, tc.valsCount),
|
||||
"#%v", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockMaxDataBytesNoEvidence(t *testing.T) {
|
||||
testCases := []struct {
|
||||
maxBytes int64
|
||||
valsCount int
|
||||
panics bool
|
||||
result int64
|
||||
}{
|
||||
0: {-10, 1, true, 0},
|
||||
1: {10, 1, true, 0},
|
||||
2: {841, 1, true, 0},
|
||||
3: {842, 1, false, 0},
|
||||
4: {843, 1, false, 1},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
tc := tc
|
||||
if tc.panics {
|
||||
assert.Panics(t, func() {
|
||||
block.MaxDataBytesNoEvidence(tc.maxBytes, tc.valsCount)
|
||||
}, "#%v", i)
|
||||
} else {
|
||||
assert.Equal(t,
|
||||
tc.result,
|
||||
block.MaxDataBytesNoEvidence(tc.maxBytes, tc.valsCount),
|
||||
"#%v", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockProtoBuf(t *testing.T) {
|
||||
h := mrand.Int63()
|
||||
c1 := test.MakeRandomCommit(time.Now())
|
||||
b1 := block.MakeBlock(h, []mempool.Tx{mempool.Tx([]byte{1})}, &meta.Commit{Signatures: []meta.CommitSig{}}, []evidence.Evidence{})
|
||||
b1.ProposerAddress = tmrand.Bytes(crypto.AddressSize)
|
||||
|
||||
b2 := block.MakeBlock(h, []mempool.Tx{mempool.Tx([]byte{1})}, c1, []evidence.Evidence{})
|
||||
b2.ProposerAddress = tmrand.Bytes(crypto.AddressSize)
|
||||
evidenceTime := time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
evi := evidence.NewMockDuplicateVoteEvidence(h, evidenceTime, "block-test-chain")
|
||||
b2.Evidence = block.EvidenceData{Evidence: evidence.EvidenceList{evi}}
|
||||
b2.EvidenceHash = b2.Evidence.Hash()
|
||||
|
||||
b3 := block.MakeBlock(h, []mempool.Tx{}, c1, []evidence.Evidence{})
|
||||
b3.ProposerAddress = tmrand.Bytes(crypto.AddressSize)
|
||||
testCases := []struct {
|
||||
msg string
|
||||
b1 *block.Block
|
||||
expPass bool
|
||||
expPass2 bool
|
||||
}{
|
||||
{"nil block", nil, false, false},
|
||||
{"b1", b1, true, true},
|
||||
{"b2", b2, true, true},
|
||||
{"b3", b3, true, true},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
pb, err := tc.b1.ToProto()
|
||||
if tc.expPass {
|
||||
require.NoError(t, err, tc.msg)
|
||||
} else {
|
||||
require.Error(t, err, tc.msg)
|
||||
}
|
||||
|
||||
block, err := block.BlockFromProto(pb)
|
||||
if tc.expPass2 {
|
||||
require.NoError(t, err, tc.msg)
|
||||
require.EqualValues(t, tc.b1.Header, block.Header, tc.msg)
|
||||
require.EqualValues(t, tc.b1.Data, block.Data, tc.msg)
|
||||
require.EqualValues(t, tc.b1.Evidence.Evidence, block.Evidence.Evidence, tc.msg)
|
||||
require.EqualValues(t, *tc.b1.LastCommit, *block.LastCommit, tc.msg)
|
||||
} else {
|
||||
require.Error(t, err, tc.msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDataProtoBuf(t *testing.T) {
|
||||
data := &block.Data{Txs: mempool.Txs{mempool.Tx([]byte{1}), mempool.Tx([]byte{2}), mempool.Tx([]byte{3})}}
|
||||
data2 := &block.Data{Txs: mempool.Txs{}}
|
||||
testCases := []struct {
|
||||
msg string
|
||||
data1 *block.Data
|
||||
expPass bool
|
||||
}{
|
||||
{"success", data, true},
|
||||
{"success data2", data2, true},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
protoData := tc.data1.ToProto()
|
||||
d, err := block.DataFromProto(&protoData)
|
||||
if tc.expPass {
|
||||
require.NoError(t, err, tc.msg)
|
||||
require.EqualValues(t, tc.data1, &d, tc.msg)
|
||||
} else {
|
||||
require.Error(t, err, tc.msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestEvidenceDataProtoBuf ensures parity in converting to and from proto.
|
||||
func TestEvidenceDataProtoBuf(t *testing.T) {
|
||||
const chainID = "mychain"
|
||||
ev := evidence.NewMockDuplicateVoteEvidence(math.MaxInt64, time.Now(), chainID)
|
||||
data := &block.EvidenceData{Evidence: evidence.EvidenceList{ev}}
|
||||
_ = data.ByteSize()
|
||||
testCases := []struct {
|
||||
msg string
|
||||
data1 *block.EvidenceData
|
||||
expPass1 bool
|
||||
expPass2 bool
|
||||
}{
|
||||
{"success", data, true, true},
|
||||
{"empty evidenceData", &block.EvidenceData{Evidence: evidence.EvidenceList{}}, true, true},
|
||||
{"fail nil Data", nil, false, false},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
protoData, err := tc.data1.ToProto()
|
||||
if tc.expPass1 {
|
||||
require.NoError(t, err, tc.msg)
|
||||
} else {
|
||||
require.Error(t, err, tc.msg)
|
||||
}
|
||||
|
||||
eviD := new(block.EvidenceData)
|
||||
err = eviD.FromProto(protoData)
|
||||
if tc.expPass2 {
|
||||
require.NoError(t, err, tc.msg)
|
||||
require.Equal(t, tc.data1, eviD, tc.msg)
|
||||
} else {
|
||||
require.Error(t, err, tc.msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This follows RFC-6962, i.e. `echo -n '' | sha256sum`
|
||||
var emptyBytes = []byte{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8,
|
||||
0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b,
|
||||
0x78, 0x52, 0xb8, 0x55}
|
||||
|
||||
func TestNilDataHashDoesntCrash(t *testing.T) {
|
||||
assert.Equal(t, emptyBytes, []byte((*block.Data)(nil).Hash()))
|
||||
assert.Equal(t, emptyBytes, []byte(new(block.Data).Hash()))
|
||||
}
|
||||
32
pkg/consensus/canonical.go
Normal file
32
pkg/consensus/canonical.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
|
||||
// CanonicalizeVote transforms the given Proposal to a CanonicalProposal.
|
||||
func CanonicalizeProposal(chainID string, proposal *tmproto.Proposal) tmproto.CanonicalProposal {
|
||||
return tmproto.CanonicalProposal{
|
||||
Type: tmproto.ProposalType,
|
||||
Height: proposal.Height, // encoded as sfixed64
|
||||
Round: int64(proposal.Round), // encoded as sfixed64
|
||||
POLRound: int64(proposal.PolRound),
|
||||
BlockID: meta.CanonicalizeBlockID(proposal.BlockID),
|
||||
Timestamp: proposal.Timestamp,
|
||||
ChainID: chainID,
|
||||
}
|
||||
}
|
||||
|
||||
// CanonicalizeVote transforms the given Vote to a CanonicalVote, which does
|
||||
// not contain ValidatorIndex and ValidatorAddress fields.
|
||||
func CanonicalizeVote(chainID string, vote *tmproto.Vote) tmproto.CanonicalVote {
|
||||
return tmproto.CanonicalVote{
|
||||
Type: vote.Type,
|
||||
Height: vote.Height, // encoded as sfixed64
|
||||
Round: int64(vote.Round), // encoded as sfixed64
|
||||
BlockID: meta.CanonicalizeBlockID(vote.BlockID),
|
||||
Timestamp: vote.Timestamp,
|
||||
ChainID: chainID,
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package consensus
|
||||
|
||||
import "fmt"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -12,11 +12,7 @@ import (
|
||||
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
tmtime "github.com/tendermint/tendermint/libs/time"
|
||||
)
|
||||
|
||||
const (
|
||||
// MaxChainIDLen is a maximum length of the chain ID.
|
||||
MaxChainIDLen = 50
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
)
|
||||
|
||||
//------------------------------------------------------------
|
||||
@@ -70,8 +66,8 @@ func (genDoc *GenesisDoc) ValidateAndComplete() error {
|
||||
if genDoc.ChainID == "" {
|
||||
return errors.New("genesis doc must include non-empty chain_id")
|
||||
}
|
||||
if len(genDoc.ChainID) > MaxChainIDLen {
|
||||
return fmt.Errorf("chain_id in genesis doc is too long (max: %d)", MaxChainIDLen)
|
||||
if len(genDoc.ChainID) > meta.MaxChainIDLen {
|
||||
return fmt.Errorf("chain_id in genesis doc is too long (max: %d)", meta.MaxChainIDLen)
|
||||
}
|
||||
if genDoc.InitialHeight < 0 {
|
||||
return fmt.Errorf("initial_height cannot be negative (got %v)", genDoc.InitialHeight)
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package consensus_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
tmtime "github.com/tendermint/tendermint/libs/time"
|
||||
"github.com/tendermint/tendermint/pkg/consensus"
|
||||
)
|
||||
|
||||
func TestGenesisBad(t *testing.T) {
|
||||
@@ -52,7 +53,7 @@ func TestGenesisBad(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
_, err := GenesisDocFromJSON(testCase)
|
||||
_, err := consensus.GenesisDocFromJSON(testCase)
|
||||
assert.Error(t, err, "expected error for empty genDoc json")
|
||||
}
|
||||
}
|
||||
@@ -74,20 +75,20 @@ func TestGenesisGood(t *testing.T) {
|
||||
"app_state":{"account_owner": "Bob"}
|
||||
}`,
|
||||
)
|
||||
_, err := GenesisDocFromJSON(genDocBytes)
|
||||
_, err := consensus.GenesisDocFromJSON(genDocBytes)
|
||||
assert.NoError(t, err, "expected no error for good genDoc json")
|
||||
|
||||
pubkey := ed25519.GenPrivKey().PubKey()
|
||||
// create a base gendoc from struct
|
||||
baseGenDoc := &GenesisDoc{
|
||||
baseGenDoc := &consensus.GenesisDoc{
|
||||
ChainID: "abc",
|
||||
Validators: []GenesisValidator{{pubkey.Address(), pubkey, 10, "myval"}},
|
||||
Validators: []consensus.GenesisValidator{{pubkey.Address(), pubkey, 10, "myval"}},
|
||||
}
|
||||
genDocBytes, err = tmjson.Marshal(baseGenDoc)
|
||||
assert.NoError(t, err, "error marshaling genDoc")
|
||||
|
||||
// test base gendoc and check consensus params were filled
|
||||
genDoc, err := GenesisDocFromJSON(genDocBytes)
|
||||
genDoc, err := consensus.GenesisDocFromJSON(genDocBytes)
|
||||
assert.NoError(t, err, "expected no error for valid genDoc json")
|
||||
assert.NotNil(t, genDoc.ConsensusParams, "expected consensus params to be filled in")
|
||||
|
||||
@@ -97,14 +98,14 @@ func TestGenesisGood(t *testing.T) {
|
||||
// create json with consensus params filled
|
||||
genDocBytes, err = tmjson.Marshal(genDoc)
|
||||
assert.NoError(t, err, "error marshaling genDoc")
|
||||
genDoc, err = GenesisDocFromJSON(genDocBytes)
|
||||
genDoc, err = consensus.GenesisDocFromJSON(genDocBytes)
|
||||
assert.NoError(t, err, "expected no error for valid genDoc json")
|
||||
|
||||
// test with invalid consensus params
|
||||
genDoc.ConsensusParams.Block.MaxBytes = 0
|
||||
genDocBytes, err = tmjson.Marshal(genDoc)
|
||||
assert.NoError(t, err, "error marshaling genDoc")
|
||||
_, err = GenesisDocFromJSON(genDocBytes)
|
||||
_, err = consensus.GenesisDocFromJSON(genDocBytes)
|
||||
assert.Error(t, err, "expected error for genDoc json with block size of 0")
|
||||
|
||||
// Genesis doc from raw json
|
||||
@@ -116,7 +117,7 @@ func TestGenesisGood(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tc := range missingValidatorsTestCases {
|
||||
_, err := GenesisDocFromJSON(tc)
|
||||
_, err := consensus.GenesisDocFromJSON(tc)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
@@ -141,7 +142,7 @@ func TestGenesisSaveAs(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// load
|
||||
genDoc2, err := GenesisDocFromFile(tmpfile.Name())
|
||||
genDoc2, err := consensus.GenesisDocFromFile(tmpfile.Name())
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(t, genDoc2, genDoc)
|
||||
assert.Equal(t, genDoc2.Validators, genDoc.Validators)
|
||||
@@ -152,14 +153,14 @@ func TestGenesisValidatorHash(t *testing.T) {
|
||||
assert.NotEmpty(t, genDoc.ValidatorHash())
|
||||
}
|
||||
|
||||
func randomGenesisDoc() *GenesisDoc {
|
||||
func randomGenesisDoc() *consensus.GenesisDoc {
|
||||
pubkey := ed25519.GenPrivKey().PubKey()
|
||||
return &GenesisDoc{
|
||||
return &consensus.GenesisDoc{
|
||||
GenesisTime: tmtime.Now(),
|
||||
ChainID: "abc",
|
||||
InitialHeight: 1000,
|
||||
Validators: []GenesisValidator{{pubkey.Address(), pubkey, 10, "myval"}},
|
||||
ConsensusParams: DefaultConsensusParams(),
|
||||
Validators: []consensus.GenesisValidator{{pubkey.Address(), pubkey, 10, "myval"}},
|
||||
ConsensusParams: consensus.DefaultConsensusParams(),
|
||||
AppHash: []byte{1, 2, 3},
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package consensus
|
||||
|
||||
// UNSTABLE
|
||||
var (
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -10,19 +10,11 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto/sr25519"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
tmstrings "github.com/tendermint/tendermint/libs/strings"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
|
||||
const (
|
||||
// MaxBlockSizeBytes is the maximum permitted size of the blocks.
|
||||
MaxBlockSizeBytes = 104857600 // 100MB
|
||||
|
||||
// BlockPartSizeBytes is the size of one block part.
|
||||
BlockPartSizeBytes uint32 = 65536 // 64kB
|
||||
|
||||
// MaxBlockPartsCount is the maximum number of block parts.
|
||||
MaxBlockPartsCount = (MaxBlockSizeBytes / BlockPartSizeBytes) + 1
|
||||
|
||||
ABCIPubKeyTypeEd25519 = ed25519.KeyType
|
||||
ABCIPubKeyTypeSecp256k1 = secp256k1.KeyType
|
||||
ABCIPubKeyTypeSr25519 = sr25519.KeyType
|
||||
@@ -129,16 +121,16 @@ func (val *ValidatorParams) IsValidPubkeyType(pubkeyType string) bool {
|
||||
// allowed limits, and returns an error if they are not.
|
||||
func (params ConsensusParams) ValidateConsensusParams() error {
|
||||
if params.Block.MaxBytes <= 0 {
|
||||
return fmt.Errorf("block.MaxBytes must be greater than 0. Got %d",
|
||||
return fmt.Errorf("meta.MaxBytes must be greater than 0. Got %d",
|
||||
params.Block.MaxBytes)
|
||||
}
|
||||
if params.Block.MaxBytes > MaxBlockSizeBytes {
|
||||
return fmt.Errorf("block.MaxBytes is too big. %d > %d",
|
||||
params.Block.MaxBytes, MaxBlockSizeBytes)
|
||||
if params.Block.MaxBytes > meta.MaxBlockSizeBytes {
|
||||
return fmt.Errorf("meta.MaxBytes is too big. %d > %d",
|
||||
params.Block.MaxBytes, meta.MaxBlockSizeBytes)
|
||||
}
|
||||
|
||||
if params.Block.MaxGas < -1 {
|
||||
return fmt.Errorf("block.MaxGas must be greater or equal to -1. Got %d",
|
||||
return fmt.Errorf("meta.MaxGas must be greater or equal to -1. Got %d",
|
||||
params.Block.MaxGas)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/tendermint/tendermint/internal/libs/protoio"
|
||||
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
||||
tmtime "github.com/tendermint/tendermint/libs/time"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -24,17 +25,17 @@ var (
|
||||
// If POLRound >= 0, then BlockID corresponds to the block that is locked in POLRound.
|
||||
type Proposal struct {
|
||||
Type tmproto.SignedMsgType
|
||||
Height int64 `json:"height"`
|
||||
Round int32 `json:"round"` // there can not be greater than 2_147_483_647 rounds
|
||||
POLRound int32 `json:"pol_round"` // -1 if null.
|
||||
BlockID BlockID `json:"block_id"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Signature []byte `json:"signature"`
|
||||
Height int64 `json:"height"`
|
||||
Round int32 `json:"round"` // there can not be greater than 2_147_483_647 rounds
|
||||
POLRound int32 `json:"pol_round"` // -1 if null.
|
||||
BlockID meta.BlockID `json:"block_id"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Signature []byte `json:"signature"`
|
||||
}
|
||||
|
||||
// NewProposal returns a new Proposal.
|
||||
// If there is no POLRound, polRound should be -1.
|
||||
func NewProposal(height int64, round int32, polRound int32, blockID BlockID) *Proposal {
|
||||
func NewProposal(height int64, round int32, polRound int32, blockID meta.BlockID) *Proposal {
|
||||
return &Proposal{
|
||||
Type: tmproto.ProposalType,
|
||||
Height: height,
|
||||
@@ -73,8 +74,8 @@ func (p *Proposal) ValidateBasic() error {
|
||||
return errors.New("signature is missing")
|
||||
}
|
||||
|
||||
if len(p.Signature) > MaxSignatureSize {
|
||||
return fmt.Errorf("signature is too big (max: %d)", MaxSignatureSize)
|
||||
if len(p.Signature) > meta.MaxSignatureSize {
|
||||
return fmt.Errorf("signature is too big (max: %d)", meta.MaxSignatureSize)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -96,7 +97,7 @@ func (p *Proposal) String() string {
|
||||
p.BlockID,
|
||||
p.POLRound,
|
||||
tmbytes.Fingerprint(p.Signature),
|
||||
CanonicalTime(p.Timestamp))
|
||||
meta.CanonicalTime(p.Timestamp))
|
||||
}
|
||||
|
||||
// ProposalSignBytes returns the proto-encoding of the canonicalized Proposal,
|
||||
@@ -144,7 +145,7 @@ func ProposalFromProto(pp *tmproto.Proposal) (*Proposal, error) {
|
||||
|
||||
p := new(Proposal)
|
||||
|
||||
blockID, err := BlockIDFromProto(&pp.BlockID)
|
||||
blockID, err := meta.BlockIDFromProto(&pp.BlockID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package consensus_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -13,24 +13,26 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
"github.com/tendermint/tendermint/internal/libs/protoio"
|
||||
tmrand "github.com/tendermint/tendermint/libs/rand"
|
||||
"github.com/tendermint/tendermint/pkg/consensus"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
|
||||
var (
|
||||
testProposal *Proposal
|
||||
testProposal *consensus.Proposal
|
||||
pbp *tmproto.Proposal
|
||||
)
|
||||
|
||||
func init() {
|
||||
var stamp, err = time.Parse(TimeFormat, "2018-02-11T07:09:22.765Z")
|
||||
var stamp, err = time.Parse(meta.TimeFormat, "2018-02-11T07:09:22.765Z")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
testProposal = &Proposal{
|
||||
testProposal = &consensus.Proposal{
|
||||
Height: 12345,
|
||||
Round: 23456,
|
||||
BlockID: BlockID{Hash: []byte("--June_15_2020_amino_was_removed"),
|
||||
PartSetHeader: PartSetHeader{Total: 111, Hash: []byte("--June_15_2020_amino_was_removed")}},
|
||||
BlockID: meta.BlockID{Hash: []byte("--June_15_2020_amino_was_removed"),
|
||||
PartSetHeader: meta.PartSetHeader{Total: 111, Hash: []byte("--June_15_2020_amino_was_removed")}},
|
||||
POLRound: -1,
|
||||
Timestamp: stamp,
|
||||
}
|
||||
@@ -39,8 +41,8 @@ func init() {
|
||||
|
||||
func TestProposalSignable(t *testing.T) {
|
||||
chainID := "test_chain_id"
|
||||
signBytes := ProposalSignBytes(chainID, pbp)
|
||||
pb := CanonicalizeProposal(chainID, pbp)
|
||||
signBytes := consensus.ProposalSignBytes(chainID, pbp)
|
||||
pb := consensus.CanonicalizeProposal(chainID, pbp)
|
||||
|
||||
expected, err := protoio.MarshalDelimited(&pb)
|
||||
require.NoError(t, err)
|
||||
@@ -56,15 +58,15 @@ func TestProposalString(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProposalVerifySignature(t *testing.T) {
|
||||
privVal := NewMockPV()
|
||||
privVal := consensus.NewMockPV()
|
||||
pubKey, err := privVal.GetPubKey(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
||||
prop := NewProposal(
|
||||
prop := consensus.NewProposal(
|
||||
4, 2, 2,
|
||||
BlockID{tmrand.Bytes(tmhash.Size), PartSetHeader{777, tmrand.Bytes(tmhash.Size)}})
|
||||
meta.BlockID{tmrand.Bytes(tmhash.Size), meta.PartSetHeader{777, tmrand.Bytes(tmhash.Size)}})
|
||||
p := prop.ToProto()
|
||||
signBytes := ProposalSignBytes("test_chain_id", p)
|
||||
signBytes := consensus.ProposalSignBytes("test_chain_id", p)
|
||||
|
||||
// sign it
|
||||
err = privVal.SignProposal(context.Background(), "test_chain_id", p)
|
||||
@@ -85,11 +87,11 @@ func TestProposalVerifySignature(t *testing.T) {
|
||||
err = proto.Unmarshal(bs, newProp)
|
||||
require.NoError(t, err)
|
||||
|
||||
np, err := ProposalFromProto(newProp)
|
||||
np, err := consensus.ProposalFromProto(newProp)
|
||||
require.NoError(t, err)
|
||||
|
||||
// verify the transmitted proposal
|
||||
newSignBytes := ProposalSignBytes("test_chain_id", pb)
|
||||
newSignBytes := consensus.ProposalSignBytes("test_chain_id", pb)
|
||||
require.Equal(t, string(signBytes), string(newSignBytes))
|
||||
valid = pubKey.VerifySignature(newSignBytes, np.Signature)
|
||||
require.True(t, valid)
|
||||
@@ -97,12 +99,12 @@ func TestProposalVerifySignature(t *testing.T) {
|
||||
|
||||
func BenchmarkProposalWriteSignBytes(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ProposalSignBytes("test_chain_id", pbp)
|
||||
consensus.ProposalSignBytes("test_chain_id", pbp)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkProposalSign(b *testing.B) {
|
||||
privVal := NewMockPV()
|
||||
privVal := consensus.NewMockPV()
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := privVal.SignProposal(context.Background(), "test_chain_id", pbp)
|
||||
if err != nil {
|
||||
@@ -112,46 +114,45 @@ func BenchmarkProposalSign(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkProposalVerifySignature(b *testing.B) {
|
||||
privVal := NewMockPV()
|
||||
privVal := consensus.NewMockPV()
|
||||
err := privVal.SignProposal(context.Background(), "test_chain_id", pbp)
|
||||
require.NoError(b, err)
|
||||
pubKey, err := privVal.GetPubKey(context.Background())
|
||||
require.NoError(b, err)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
pubKey.VerifySignature(ProposalSignBytes("test_chain_id", pbp), testProposal.Signature)
|
||||
pubKey.VerifySignature(consensus.ProposalSignBytes("test_chain_id", pbp), testProposal.Signature)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProposalValidateBasic(t *testing.T) {
|
||||
|
||||
privVal := NewMockPV()
|
||||
privVal := consensus.NewMockPV()
|
||||
testCases := []struct {
|
||||
testName string
|
||||
malleateProposal func(*Proposal)
|
||||
malleateProposal func(*consensus.Proposal)
|
||||
expectErr bool
|
||||
}{
|
||||
{"Good Proposal", func(p *Proposal) {}, false},
|
||||
{"Invalid Type", func(p *Proposal) { p.Type = tmproto.PrecommitType }, true},
|
||||
{"Invalid Height", func(p *Proposal) { p.Height = -1 }, true},
|
||||
{"Invalid Round", func(p *Proposal) { p.Round = -1 }, true},
|
||||
{"Invalid POLRound", func(p *Proposal) { p.POLRound = -2 }, true},
|
||||
{"Invalid BlockId", func(p *Proposal) {
|
||||
p.BlockID = BlockID{[]byte{1, 2, 3}, PartSetHeader{111, []byte("blockparts")}}
|
||||
{"Good Proposal", func(p *consensus.Proposal) {}, false},
|
||||
{"Invalid Type", func(p *consensus.Proposal) { p.Type = tmproto.PrecommitType }, true},
|
||||
{"Invalid Height", func(p *consensus.Proposal) { p.Height = -1 }, true},
|
||||
{"Invalid Round", func(p *consensus.Proposal) { p.Round = -1 }, true},
|
||||
{"Invalid POLRound", func(p *consensus.Proposal) { p.POLRound = -2 }, true},
|
||||
{"Invalid BlockId", func(p *consensus.Proposal) {
|
||||
p.BlockID = meta.BlockID{[]byte{1, 2, 3}, meta.PartSetHeader{111, []byte("blockparts")}}
|
||||
}, true},
|
||||
{"Invalid Signature", func(p *Proposal) {
|
||||
{"Invalid Signature", func(p *consensus.Proposal) {
|
||||
p.Signature = make([]byte, 0)
|
||||
}, true},
|
||||
{"Too big Signature", func(p *Proposal) {
|
||||
p.Signature = make([]byte, MaxSignatureSize+1)
|
||||
{"Too big Signature", func(p *consensus.Proposal) {
|
||||
p.Signature = make([]byte, meta.MaxSignatureSize+1)
|
||||
}, true},
|
||||
}
|
||||
blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash")))
|
||||
blockID := meta.BlockID{tmhash.Sum([]byte("blockhash")), meta.PartSetHeader{math.MaxInt32, tmhash.Sum([]byte("partshash"))}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
prop := NewProposal(
|
||||
prop := consensus.NewProposal(
|
||||
4, 2, 2,
|
||||
blockID)
|
||||
p := prop.ToProto()
|
||||
@@ -165,24 +166,24 @@ func TestProposalValidateBasic(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProposalProtoBuf(t *testing.T) {
|
||||
proposal := NewProposal(1, 2, 3, makeBlockID([]byte("hash"), 2, []byte("part_set_hash")))
|
||||
proposal := consensus.NewProposal(1, 2, 3, meta.BlockID{[]byte("hash"), meta.PartSetHeader{2, []byte("part_set_hash")}})
|
||||
proposal.Signature = []byte("sig")
|
||||
proposal2 := NewProposal(1, 2, 3, BlockID{})
|
||||
proposal2 := consensus.NewProposal(1, 2, 3, meta.BlockID{})
|
||||
|
||||
testCases := []struct {
|
||||
msg string
|
||||
p1 *Proposal
|
||||
p1 *consensus.Proposal
|
||||
expPass bool
|
||||
}{
|
||||
{"success", proposal, true},
|
||||
{"success", proposal2, false}, // blcokID cannot be empty
|
||||
{"empty proposal failure validatebasic", &Proposal{}, false},
|
||||
{"empty proposal failure validatebasic", &consensus.Proposal{}, false},
|
||||
{"nil proposal", nil, false},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
protoProposal := tc.p1.ToProto()
|
||||
|
||||
p, err := ProposalFromProto(protoProposal)
|
||||
p, err := consensus.ProposalFromProto(protoProposal)
|
||||
if tc.expPass {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.p1, p, tc.msg)
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package consensus
|
||||
|
||||
import (
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package consensus_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -10,11 +10,12 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
cryptoenc "github.com/tendermint/tendermint/crypto/encoding"
|
||||
"github.com/tendermint/tendermint/pkg/consensus"
|
||||
)
|
||||
|
||||
func TestABCIPubKey(t *testing.T) {
|
||||
pkEd := ed25519.GenPrivKey().PubKey()
|
||||
err := testABCIPubKey(t, pkEd, ABCIPubKeyTypeEd25519)
|
||||
err := testABCIPubKey(t, pkEd, consensus.ABCIPubKeyTypeEd25519)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -31,23 +32,23 @@ func TestABCIValidators(t *testing.T) {
|
||||
pkEd := ed25519.GenPrivKey().PubKey()
|
||||
|
||||
// correct validator
|
||||
tmValExpected := NewValidator(pkEd, 10)
|
||||
tmValExpected := consensus.NewValidator(pkEd, 10)
|
||||
|
||||
tmVal := NewValidator(pkEd, 10)
|
||||
tmVal := consensus.NewValidator(pkEd, 10)
|
||||
|
||||
abciVal := TM2PB.ValidatorUpdate(tmVal)
|
||||
tmVals, err := PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{abciVal})
|
||||
abciVal := consensus.TM2PB.ValidatorUpdate(tmVal)
|
||||
tmVals, err := consensus.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{abciVal})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, tmValExpected, tmVals[0])
|
||||
|
||||
abciVals := TM2PB.ValidatorUpdates(NewValidatorSet(tmVals))
|
||||
abciVals := consensus.TM2PB.ValidatorUpdates(consensus.NewValidatorSet(tmVals))
|
||||
assert.Equal(t, []abci.ValidatorUpdate{abciVal}, abciVals)
|
||||
|
||||
// val with address
|
||||
tmVal.Address = pkEd.Address()
|
||||
|
||||
abciVal = TM2PB.ValidatorUpdate(tmVal)
|
||||
tmVals, err = PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{abciVal})
|
||||
abciVal = consensus.TM2PB.ValidatorUpdate(tmVal)
|
||||
tmVals, err = consensus.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{abciVal})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, tmValExpected, tmVals[0])
|
||||
}
|
||||
@@ -55,7 +56,7 @@ func TestABCIValidators(t *testing.T) {
|
||||
func TestABCIValidatorWithoutPubKey(t *testing.T) {
|
||||
pkEd := ed25519.GenPrivKey().PubKey()
|
||||
|
||||
abciVal := TM2PB.Validator(NewValidator(pkEd, 10))
|
||||
abciVal := consensus.TM2PB.Validator(consensus.NewValidator(pkEd, 10))
|
||||
|
||||
// pubkey must be nil
|
||||
tmValExpected := abci.Validator{
|
||||
@@ -1,17 +1,17 @@
|
||||
package types
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/batch"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
tmmath "github.com/tendermint/tendermint/libs/math"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
)
|
||||
|
||||
const batchVerifyThreshold = 2
|
||||
|
||||
func shouldBatchVerify(vals *ValidatorSet, commit *Commit) bool {
|
||||
func shouldBatchVerify(vals *ValidatorSet, commit *meta.Commit) bool {
|
||||
return len(commit.Signatures) >= batchVerifyThreshold && batch.SupportsBatchVerifier(vals.GetProposer().PubKey)
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ func shouldBatchVerify(vals *ValidatorSet, commit *Commit) bool {
|
||||
// application that depends on the LastCommitInfo sent in BeginBlock, which
|
||||
// includes which validators signed. For instance, Gaia incentivizes proposers
|
||||
// with a bonus for including more than +2/3 of the signatures.
|
||||
func VerifyCommit(chainID string, vals *ValidatorSet, blockID BlockID,
|
||||
height int64, commit *Commit) error {
|
||||
func VerifyCommit(chainID string, vals *ValidatorSet, blockID meta.BlockID,
|
||||
height int64, commit *meta.Commit) error {
|
||||
// run a basic validation of the arguments
|
||||
if err := verifyBasicValsAndCommit(vals, commit, height, blockID); err != nil {
|
||||
return err
|
||||
@@ -34,10 +34,10 @@ func VerifyCommit(chainID string, vals *ValidatorSet, blockID BlockID,
|
||||
votingPowerNeeded := vals.TotalVotingPower() * 2 / 3
|
||||
|
||||
// ignore all absent signatures
|
||||
ignore := func(c CommitSig) bool { return c.Absent() }
|
||||
ignore := func(c meta.CommitSig) bool { return c.Absent() }
|
||||
|
||||
// only count the signatures that are for the block
|
||||
count := func(c CommitSig) bool { return c.ForBlock() }
|
||||
count := func(c meta.CommitSig) bool { return c.ForBlock() }
|
||||
|
||||
// attempt to batch verify
|
||||
if shouldBatchVerify(vals, commit) {
|
||||
@@ -56,8 +56,8 @@ func VerifyCommit(chainID string, vals *ValidatorSet, blockID BlockID,
|
||||
//
|
||||
// This method is primarily used by the light client and does not check all the
|
||||
// signatures.
|
||||
func VerifyCommitLight(chainID string, vals *ValidatorSet, blockID BlockID,
|
||||
height int64, commit *Commit) error {
|
||||
func VerifyCommitLight(chainID string, vals *ValidatorSet, blockID meta.BlockID,
|
||||
height int64, commit *meta.Commit) error {
|
||||
// run a basic validation of the arguments
|
||||
if err := verifyBasicValsAndCommit(vals, commit, height, blockID); err != nil {
|
||||
return err
|
||||
@@ -67,10 +67,10 @@ func VerifyCommitLight(chainID string, vals *ValidatorSet, blockID BlockID,
|
||||
votingPowerNeeded := vals.TotalVotingPower() * 2 / 3
|
||||
|
||||
// ignore all commit signatures that are not for the block
|
||||
ignore := func(c CommitSig) bool { return !c.ForBlock() }
|
||||
ignore := func(c meta.CommitSig) bool { return !c.ForBlock() }
|
||||
|
||||
// count all the remaining signatures
|
||||
count := func(c CommitSig) bool { return true }
|
||||
count := func(c meta.CommitSig) bool { return true }
|
||||
|
||||
// attempt to batch verify
|
||||
if shouldBatchVerify(vals, commit) {
|
||||
@@ -91,7 +91,7 @@ func VerifyCommitLight(chainID string, vals *ValidatorSet, blockID BlockID,
|
||||
//
|
||||
// This method is primarily used by the light client and does not check all the
|
||||
// signatures.
|
||||
func VerifyCommitLightTrusting(chainID string, vals *ValidatorSet, commit *Commit, trustLevel tmmath.Fraction) error {
|
||||
func VerifyCommitLightTrusting(chainID string, vals *ValidatorSet, commit *meta.Commit, trustLevel tmmath.Fraction) error {
|
||||
// sanity checks
|
||||
if vals == nil {
|
||||
return errors.New("nil validator set")
|
||||
@@ -104,17 +104,17 @@ func VerifyCommitLightTrusting(chainID string, vals *ValidatorSet, commit *Commi
|
||||
}
|
||||
|
||||
// safely calculate voting power needed.
|
||||
totalVotingPowerMulByNumerator, overflow := safeMul(vals.TotalVotingPower(), int64(trustLevel.Numerator))
|
||||
totalVotingPowerMulByNumerator, overflow := tmmath.SafeMul(vals.TotalVotingPower(), int64(trustLevel.Numerator))
|
||||
if overflow {
|
||||
return errors.New("int64 overflow while calculating voting power needed. please provide smaller trustLevel numerator")
|
||||
}
|
||||
votingPowerNeeded := totalVotingPowerMulByNumerator / int64(trustLevel.Denominator)
|
||||
|
||||
// ignore all commit signatures that are not for the block
|
||||
ignore := func(c CommitSig) bool { return !c.ForBlock() }
|
||||
ignore := func(c meta.CommitSig) bool { return !c.ForBlock() }
|
||||
|
||||
// count all the remaining signatures
|
||||
count := func(c CommitSig) bool { return true }
|
||||
count := func(c meta.CommitSig) bool { return true }
|
||||
|
||||
// attempt to batch verify commit. As the validator set doesn't necessarily
|
||||
// correspond with the validator set that signed the block we need to look
|
||||
@@ -129,18 +129,6 @@ func VerifyCommitLightTrusting(chainID string, vals *ValidatorSet, commit *Commi
|
||||
ignore, count, false, false)
|
||||
}
|
||||
|
||||
// ValidateHash returns an error if the hash is not empty, but its
|
||||
// size != tmhash.Size.
|
||||
func ValidateHash(h []byte) error {
|
||||
if len(h) > 0 && len(h) != tmhash.Size {
|
||||
return fmt.Errorf("expected size to be %d bytes, got %d bytes",
|
||||
tmhash.Size,
|
||||
len(h),
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Batch verification
|
||||
|
||||
// verifyCommitBatch batch verifies commits. This routine is equivalent
|
||||
@@ -152,10 +140,10 @@ func ValidateHash(h []byte) error {
|
||||
func verifyCommitBatch(
|
||||
chainID string,
|
||||
vals *ValidatorSet,
|
||||
commit *Commit,
|
||||
commit *meta.Commit,
|
||||
votingPowerNeeded int64,
|
||||
ignoreSig func(CommitSig) bool,
|
||||
countSig func(CommitSig) bool,
|
||||
ignoreSig func(meta.CommitSig) bool,
|
||||
countSig func(meta.CommitSig) bool,
|
||||
countAllSignatures bool,
|
||||
lookUpByIndex bool,
|
||||
) error {
|
||||
@@ -203,7 +191,7 @@ func verifyCommitBatch(
|
||||
}
|
||||
|
||||
// Validate signature.
|
||||
voteSignBytes := commit.VoteSignBytes(chainID, int32(idx))
|
||||
voteSignBytes := VoteSignBytesFromCommit(commit, chainID, int32(idx))
|
||||
|
||||
// add the key, sig and message to the verifier
|
||||
if err := bv.Add(val.PubKey, voteSignBytes, commitSig.Signature); err != nil {
|
||||
@@ -265,10 +253,10 @@ func verifyCommitBatch(
|
||||
func verifyCommitSingle(
|
||||
chainID string,
|
||||
vals *ValidatorSet,
|
||||
commit *Commit,
|
||||
commit *meta.Commit,
|
||||
votingPowerNeeded int64,
|
||||
ignoreSig func(CommitSig) bool,
|
||||
countSig func(CommitSig) bool,
|
||||
ignoreSig func(meta.CommitSig) bool,
|
||||
countSig func(meta.CommitSig) bool,
|
||||
countAllSignatures bool,
|
||||
lookUpByIndex bool,
|
||||
) error {
|
||||
@@ -306,7 +294,7 @@ func verifyCommitSingle(
|
||||
seenVals[valIdx] = idx
|
||||
}
|
||||
|
||||
voteSignBytes = commit.VoteSignBytes(chainID, int32(idx))
|
||||
voteSignBytes = VoteSignBytesFromCommit(commit, chainID, int32(idx))
|
||||
|
||||
if !val.PubKey.VerifySignature(voteSignBytes, commitSig.Signature) {
|
||||
return fmt.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature)
|
||||
@@ -331,7 +319,7 @@ func verifyCommitSingle(
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyBasicValsAndCommit(vals *ValidatorSet, commit *Commit, height int64, blockID BlockID) error {
|
||||
func verifyBasicValsAndCommit(vals *ValidatorSet, commit *meta.Commit, height int64, blockID meta.BlockID) error {
|
||||
if vals == nil {
|
||||
return errors.New("nil validator set")
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package consensus_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -8,7 +8,10 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
test "github.com/tendermint/tendermint/internal/test/factory"
|
||||
tmmath "github.com/tendermint/tendermint/libs/math"
|
||||
"github.com/tendermint/tendermint/pkg/consensus"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -19,7 +22,7 @@ func TestValidatorSet_VerifyCommit_All(t *testing.T) {
|
||||
round = int32(0)
|
||||
height = int64(100)
|
||||
|
||||
blockID = makeBlockID([]byte("blockhash"), 1000, []byte("partshash"))
|
||||
blockID = test.MakeBlockIDWithHash([]byte("blockhash"))
|
||||
chainID = "Lalande21185"
|
||||
trustLevel = tmmath.Fraction{Numerator: 2, Denominator: 3}
|
||||
)
|
||||
@@ -29,7 +32,7 @@ func TestValidatorSet_VerifyCommit_All(t *testing.T) {
|
||||
// vote chainID
|
||||
chainID string
|
||||
// vote blockID
|
||||
blockID BlockID
|
||||
blockID meta.BlockID
|
||||
valSize int
|
||||
|
||||
// height of the commit
|
||||
@@ -46,7 +49,7 @@ func TestValidatorSet_VerifyCommit_All(t *testing.T) {
|
||||
{"good (single verification)", chainID, blockID, 1, height, 1, 0, 0, false},
|
||||
|
||||
{"wrong signature (#0)", "EpsilonEridani", blockID, 2, height, 2, 0, 0, true},
|
||||
{"wrong block ID", chainID, makeBlockIDRandom(), 2, height, 2, 0, 0, true},
|
||||
{"wrong block ID", chainID, test.MakeBlockID(), 2, height, 2, 0, 0, true},
|
||||
{"wrong height", chainID, blockID, 1, height - 1, 1, 0, 0, true},
|
||||
|
||||
{"wrong set size: 4 vs 3", chainID, blockID, 4, height, 3, 0, 0, true},
|
||||
@@ -60,20 +63,20 @@ func TestValidatorSet_VerifyCommit_All(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
_, valSet, vals := randVoteSet(tc.height, round, tmproto.PrecommitType, tc.valSize, 10)
|
||||
_, valSet, vals := test.RandVoteSet(tc.height, round, tmproto.PrecommitType, tc.valSize, 10)
|
||||
totalVotes := tc.blockVotes + tc.absentVotes + tc.nilVotes
|
||||
sigs := make([]CommitSig, totalVotes)
|
||||
sigs := make([]meta.CommitSig, totalVotes)
|
||||
vi := 0
|
||||
// add absent sigs first
|
||||
for i := 0; i < tc.absentVotes; i++ {
|
||||
sigs[vi] = NewCommitSigAbsent()
|
||||
sigs[vi] = meta.NewCommitSigAbsent()
|
||||
vi++
|
||||
}
|
||||
for i := 0; i < tc.blockVotes+tc.nilVotes; i++ {
|
||||
|
||||
pubKey, err := vals[vi%len(vals)].GetPubKey(context.Background())
|
||||
require.NoError(t, err)
|
||||
vote := &Vote{
|
||||
vote := &consensus.Vote{
|
||||
ValidatorAddress: pubKey.Address(),
|
||||
ValidatorIndex: int32(vi),
|
||||
Height: tc.height,
|
||||
@@ -83,7 +86,7 @@ func TestValidatorSet_VerifyCommit_All(t *testing.T) {
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
if i >= tc.blockVotes {
|
||||
vote.BlockID = BlockID{}
|
||||
vote.BlockID = meta.BlockID{}
|
||||
}
|
||||
|
||||
v := vote.ToProto()
|
||||
@@ -95,7 +98,7 @@ func TestValidatorSet_VerifyCommit_All(t *testing.T) {
|
||||
|
||||
vi++
|
||||
}
|
||||
commit := NewCommit(tc.height, round, tc.blockID, sigs)
|
||||
commit := meta.NewCommit(tc.height, round, tc.blockID, sigs)
|
||||
|
||||
err := valSet.VerifyCommit(chainID, blockID, height, commit)
|
||||
if tc.expErr {
|
||||
@@ -135,11 +138,11 @@ func TestValidatorSet_VerifyCommit_CheckAllSignatures(t *testing.T) {
|
||||
var (
|
||||
chainID = "test_chain_id"
|
||||
h = int64(3)
|
||||
blockID = makeBlockIDRandom()
|
||||
blockID = test.MakeBlockID()
|
||||
)
|
||||
|
||||
voteSet, valSet, vals := randVoteSet(h, 0, tmproto.PrecommitType, 4, 10)
|
||||
commit, err := makeCommit(blockID, h, 0, voteSet, vals, time.Now())
|
||||
voteSet, valSet, vals := test.RandVoteSet(h, 0, tmproto.PrecommitType, 4, 10)
|
||||
commit, err := test.MakeCommit(blockID, h, 0, voteSet, vals, time.Now())
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, valSet.VerifyCommit(chainID, blockID, h, commit))
|
||||
|
||||
@@ -161,11 +164,11 @@ func TestValidatorSet_VerifyCommitLight_ReturnsAsSoonAsMajorityOfVotingPowerSign
|
||||
var (
|
||||
chainID = "test_chain_id"
|
||||
h = int64(3)
|
||||
blockID = makeBlockIDRandom()
|
||||
blockID = test.MakeBlockID()
|
||||
)
|
||||
|
||||
voteSet, valSet, vals := randVoteSet(h, 0, tmproto.PrecommitType, 4, 10)
|
||||
commit, err := makeCommit(blockID, h, 0, voteSet, vals, time.Now())
|
||||
voteSet, valSet, vals := test.RandVoteSet(h, 0, tmproto.PrecommitType, 4, 10)
|
||||
commit, err := test.MakeCommit(blockID, h, 0, voteSet, vals, time.Now())
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, valSet.VerifyCommit(chainID, blockID, h, commit))
|
||||
|
||||
@@ -185,11 +188,11 @@ func TestValidatorSet_VerifyCommitLightTrusting_ReturnsAsSoonAsTrustLevelOfVotin
|
||||
var (
|
||||
chainID = "test_chain_id"
|
||||
h = int64(3)
|
||||
blockID = makeBlockIDRandom()
|
||||
blockID = test.MakeBlockID()
|
||||
)
|
||||
|
||||
voteSet, valSet, vals := randVoteSet(h, 0, tmproto.PrecommitType, 4, 10)
|
||||
commit, err := makeCommit(blockID, h, 0, voteSet, vals, time.Now())
|
||||
voteSet, valSet, vals := test.RandVoteSet(h, 0, tmproto.PrecommitType, 4, 10)
|
||||
commit, err := test.MakeCommit(blockID, h, 0, voteSet, vals, time.Now())
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, valSet.VerifyCommit(chainID, blockID, h, commit))
|
||||
|
||||
@@ -207,15 +210,15 @@ func TestValidatorSet_VerifyCommitLightTrusting_ReturnsAsSoonAsTrustLevelOfVotin
|
||||
|
||||
func TestValidatorSet_VerifyCommitLightTrusting(t *testing.T) {
|
||||
var (
|
||||
blockID = makeBlockIDRandom()
|
||||
voteSet, originalValset, vals = randVoteSet(1, 1, tmproto.PrecommitType, 6, 1)
|
||||
commit, err = makeCommit(blockID, 1, 1, voteSet, vals, time.Now())
|
||||
newValSet, _ = randValidatorPrivValSet(2, 1)
|
||||
blockID = test.MakeBlockID()
|
||||
voteSet, originalValset, vals = test.RandVoteSet(1, 1, tmproto.PrecommitType, 6, 1)
|
||||
commit, err = test.MakeCommit(blockID, 1, 1, voteSet, vals, time.Now())
|
||||
newValSet, _ = test.RandValidatorPrivValSet(2, 1)
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
valSet *ValidatorSet
|
||||
valSet *consensus.ValidatorSet
|
||||
err bool
|
||||
}{
|
||||
// good
|
||||
@@ -230,7 +233,7 @@ func TestValidatorSet_VerifyCommitLightTrusting(t *testing.T) {
|
||||
},
|
||||
// good - first two are different but the rest of the same -> >1/3
|
||||
2: {
|
||||
valSet: NewValidatorSet(append(newValSet.Validators, originalValset.Validators...)),
|
||||
valSet: consensus.NewValidatorSet(append(newValSet.Validators, originalValset.Validators...)),
|
||||
err: false,
|
||||
},
|
||||
}
|
||||
@@ -248,9 +251,9 @@ func TestValidatorSet_VerifyCommitLightTrusting(t *testing.T) {
|
||||
|
||||
func TestValidatorSet_VerifyCommitLightTrustingErrorsOnOverflow(t *testing.T) {
|
||||
var (
|
||||
blockID = makeBlockIDRandom()
|
||||
voteSet, valSet, vals = randVoteSet(1, 1, tmproto.PrecommitType, 1, MaxTotalVotingPower)
|
||||
commit, err = makeCommit(blockID, 1, 1, voteSet, vals, time.Now())
|
||||
blockID = test.MakeBlockID()
|
||||
voteSet, valSet, vals = test.RandVoteSet(1, 1, tmproto.PrecommitType, 1, consensus.MaxTotalVotingPower)
|
||||
commit, err = test.MakeCommit(blockID, 1, 1, voteSet, vals, time.Now())
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/merkle"
|
||||
tmmath "github.com/tendermint/tendermint/libs/math"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -166,13 +167,13 @@ func (vals *ValidatorSet) RescalePriorities(diffMax int64) {
|
||||
func (vals *ValidatorSet) incrementProposerPriority() *Validator {
|
||||
for _, val := range vals.Validators {
|
||||
// Check for overflow for sum.
|
||||
newPrio := safeAddClip(val.ProposerPriority, val.VotingPower)
|
||||
newPrio := tmmath.SafeAddClip(val.ProposerPriority, val.VotingPower)
|
||||
val.ProposerPriority = newPrio
|
||||
}
|
||||
// Decrement the validator with most ProposerPriority.
|
||||
mostest := vals.getValWithMostPriority()
|
||||
// Mind the underflow.
|
||||
mostest.ProposerPriority = safeSubClip(mostest.ProposerPriority, vals.TotalVotingPower())
|
||||
mostest.ProposerPriority = tmmath.SafeSubClip(mostest.ProposerPriority, vals.TotalVotingPower())
|
||||
|
||||
return mostest
|
||||
}
|
||||
@@ -229,7 +230,7 @@ func (vals *ValidatorSet) shiftByAvgProposerPriority() {
|
||||
}
|
||||
avgProposerPriority := vals.computeAvgProposerPriority()
|
||||
for _, val := range vals.Validators {
|
||||
val.ProposerPriority = safeSubClip(val.ProposerPriority, avgProposerPriority)
|
||||
val.ProposerPriority = tmmath.SafeSubClip(val.ProposerPriority, avgProposerPriority)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,7 +300,7 @@ func (vals *ValidatorSet) updateTotalVotingPower() {
|
||||
sum := int64(0)
|
||||
for _, val := range vals.Validators {
|
||||
// mind overflow
|
||||
sum = safeAddClip(sum, val.VotingPower)
|
||||
sum = tmmath.SafeAddClip(sum, val.VotingPower)
|
||||
if sum > MaxTotalVotingPower {
|
||||
panic(fmt.Sprintf(
|
||||
"Total voting power should be guarded to not exceed %v; got: %v",
|
||||
@@ -654,22 +655,22 @@ func (vals *ValidatorSet) UpdateWithChangeSet(changes []*Validator) error {
|
||||
|
||||
// VerifyCommit verifies +2/3 of the set had signed the given commit and all
|
||||
// other signatures are valid
|
||||
func (vals *ValidatorSet) VerifyCommit(chainID string, blockID BlockID,
|
||||
height int64, commit *Commit) error {
|
||||
func (vals *ValidatorSet) VerifyCommit(chainID string, blockID meta.BlockID,
|
||||
height int64, commit *meta.Commit) error {
|
||||
return VerifyCommit(chainID, vals, blockID, height, commit)
|
||||
}
|
||||
|
||||
// LIGHT CLIENT VERIFICATION METHODS
|
||||
|
||||
// VerifyCommitLight verifies +2/3 of the set had signed the given commit.
|
||||
func (vals *ValidatorSet) VerifyCommitLight(chainID string, blockID BlockID,
|
||||
height int64, commit *Commit) error {
|
||||
func (vals *ValidatorSet) VerifyCommitLight(chainID string, blockID meta.BlockID,
|
||||
height int64, commit *meta.Commit) error {
|
||||
return VerifyCommitLight(chainID, vals, blockID, height, commit)
|
||||
}
|
||||
|
||||
// VerifyCommitLightTrusting verifies that trustLevel of the validator set signed
|
||||
// this commit.
|
||||
func (vals *ValidatorSet) VerifyCommitLightTrusting(chainID string, commit *Commit, trustLevel tmmath.Fraction) error {
|
||||
func (vals *ValidatorSet) VerifyCommitLightTrusting(chainID string, commit *meta.Commit, trustLevel tmmath.Fraction) error {
|
||||
return VerifyCommitLightTrusting(chainID, vals, commit, trustLevel)
|
||||
}
|
||||
|
||||
@@ -865,69 +866,3 @@ func ValidatorSetFromExistingValidators(valz []*Validator) (*ValidatorSet, error
|
||||
sort.Sort(ValidatorsByVotingPower(vals.Validators))
|
||||
return vals, nil
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
// safe addition/subtraction/multiplication
|
||||
|
||||
func safeAdd(a, b int64) (int64, bool) {
|
||||
if b > 0 && a > math.MaxInt64-b {
|
||||
return -1, true
|
||||
} else if b < 0 && a < math.MinInt64-b {
|
||||
return -1, true
|
||||
}
|
||||
return a + b, false
|
||||
}
|
||||
|
||||
func safeSub(a, b int64) (int64, bool) {
|
||||
if b > 0 && a < math.MinInt64+b {
|
||||
return -1, true
|
||||
} else if b < 0 && a > math.MaxInt64+b {
|
||||
return -1, true
|
||||
}
|
||||
return a - b, false
|
||||
}
|
||||
|
||||
func safeAddClip(a, b int64) int64 {
|
||||
c, overflow := safeAdd(a, b)
|
||||
if overflow {
|
||||
if b < 0 {
|
||||
return math.MinInt64
|
||||
}
|
||||
return math.MaxInt64
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func safeSubClip(a, b int64) int64 {
|
||||
c, overflow := safeSub(a, b)
|
||||
if overflow {
|
||||
if b > 0 {
|
||||
return math.MinInt64
|
||||
}
|
||||
return math.MaxInt64
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func safeMul(a, b int64) (int64, bool) {
|
||||
if a == 0 || b == 0 {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
absOfB := b
|
||||
if b < 0 {
|
||||
absOfB = -b
|
||||
}
|
||||
|
||||
absOfA := a
|
||||
if a < 0 {
|
||||
absOfA = -a
|
||||
}
|
||||
|
||||
if absOfA > math.MaxInt64/absOfB {
|
||||
return 0, true
|
||||
}
|
||||
|
||||
return a * b, false
|
||||
}
|
||||
159
pkg/consensus/validator_set_benchmark_test.go
Normal file
159
pkg/consensus/validator_set_benchmark_test.go
Normal file
@@ -0,0 +1,159 @@
|
||||
package consensus_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
test "github.com/tendermint/tendermint/internal/test/factory"
|
||||
tmmath "github.com/tendermint/tendermint/libs/math"
|
||||
"github.com/tendermint/tendermint/pkg/consensus"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
|
||||
//-------------------------------------
|
||||
// Benchmark tests
|
||||
//
|
||||
func BenchmarkUpdates(b *testing.B) {
|
||||
const (
|
||||
n = 100
|
||||
m = 2000
|
||||
)
|
||||
// Init with n validators
|
||||
vs := make([]*consensus.Validator, n)
|
||||
for j := 0; j < n; j++ {
|
||||
vs[j] = consensus.NewValidator(ed25519.GenPrivKey().PubKey(), 100)
|
||||
}
|
||||
valSet := consensus.NewValidatorSet(vs)
|
||||
|
||||
// Make m new validators
|
||||
newValList := make([]*consensus.Validator, m)
|
||||
for j := 0; j < m; j++ {
|
||||
newValList[j] = consensus.NewValidator(ed25519.GenPrivKey().PubKey(), 1000)
|
||||
}
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Add m validators to valSetCopy
|
||||
valSetCopy := valSet.Copy()
|
||||
assert.NoError(b, valSetCopy.UpdateWithChangeSet(newValList))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkValidatorSet_VerifyCommit_Ed25519(b *testing.B) {
|
||||
for _, n := range []int{1, 8, 64, 1024} {
|
||||
n := n
|
||||
var (
|
||||
chainID = "test_chain_id"
|
||||
h = int64(3)
|
||||
blockID = test.MakeBlockID()
|
||||
)
|
||||
b.Run(fmt.Sprintf("valset size %d", n), func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
// generate n validators
|
||||
voteSet, valSet, vals := test.RandVoteSet(h, 0, tmproto.PrecommitType, n, int64(n*5))
|
||||
// create a commit with n validators
|
||||
commit, err := test.MakeCommit(blockID, h, 0, voteSet, vals, time.Now())
|
||||
require.NoError(b, err)
|
||||
|
||||
for i := 0; i < b.N/n; i++ {
|
||||
err = valSet.VerifyCommit(chainID, blockID, h, commit)
|
||||
assert.NoError(b, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkValidatorSet_VerifyCommitLight_Ed25519(b *testing.B) {
|
||||
for _, n := range []int{1, 8, 64, 1024} {
|
||||
n := n
|
||||
var (
|
||||
chainID = "test_chain_id"
|
||||
h = int64(3)
|
||||
blockID = test.MakeBlockID()
|
||||
)
|
||||
b.Run(fmt.Sprintf("valset size %d", n), func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
// generate n validators
|
||||
voteSet, valSet, vals := test.RandVoteSet(h, 0, tmproto.PrecommitType, n, int64(n*5))
|
||||
// create a commit with n validators
|
||||
commit, err := test.MakeCommit(blockID, h, 0, voteSet, vals, time.Now())
|
||||
require.NoError(b, err)
|
||||
|
||||
for i := 0; i < b.N/n; i++ {
|
||||
err = valSet.VerifyCommitLight(chainID, blockID, h, commit)
|
||||
assert.NoError(b, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkValidatorSet_VerifyCommitLightTrusting_Ed25519(b *testing.B) {
|
||||
for _, n := range []int{1, 8, 64, 1024} {
|
||||
n := n
|
||||
var (
|
||||
chainID = "test_chain_id"
|
||||
h = int64(3)
|
||||
blockID = test.MakeBlockID()
|
||||
)
|
||||
b.Run(fmt.Sprintf("valset size %d", n), func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
// generate n validators
|
||||
voteSet, valSet, vals := test.RandVoteSet(h, 0, tmproto.PrecommitType, n, int64(n*5))
|
||||
// create a commit with n validators
|
||||
commit, err := test.MakeCommit(blockID, h, 0, voteSet, vals, time.Now())
|
||||
require.NoError(b, err)
|
||||
|
||||
for i := 0; i < b.N/n; i++ {
|
||||
err = valSet.VerifyCommitLightTrusting(chainID, commit, tmmath.Fraction{Numerator: 1, Denominator: 3})
|
||||
assert.NoError(b, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatorSetProtoBuf(t *testing.T) {
|
||||
valset, _ := test.RandValidatorPrivValSet(10, 100)
|
||||
valset2, _ := test.RandValidatorPrivValSet(10, 100)
|
||||
valset2.Validators[0] = &consensus.Validator{}
|
||||
|
||||
valset3, _ := test.RandValidatorPrivValSet(10, 100)
|
||||
valset3.Proposer = nil
|
||||
|
||||
valset4, _ := test.RandValidatorPrivValSet(10, 100)
|
||||
valset4.Proposer = &consensus.Validator{}
|
||||
|
||||
testCases := []struct {
|
||||
msg string
|
||||
v1 *consensus.ValidatorSet
|
||||
expPass1 bool
|
||||
expPass2 bool
|
||||
}{
|
||||
{"success", valset, true, true},
|
||||
{"fail valSet2, pubkey empty", valset2, false, false},
|
||||
{"fail nil Proposer", valset3, false, false},
|
||||
{"fail empty Proposer", valset4, false, false},
|
||||
{"fail empty valSet", &consensus.ValidatorSet{}, true, false},
|
||||
{"false nil", nil, true, false},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
protoValSet, err := tc.v1.ToProto()
|
||||
if tc.expPass1 {
|
||||
require.NoError(t, err, tc.msg)
|
||||
} else {
|
||||
require.Error(t, err, tc.msg)
|
||||
}
|
||||
|
||||
valSet, err := consensus.ValidatorSetFromProto(protoValSet)
|
||||
if tc.expPass2 {
|
||||
require.NoError(t, err, tc.msg)
|
||||
require.EqualValues(t, tc.v1, valSet, tc.msg)
|
||||
} else {
|
||||
require.Error(t, err, tc.msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -9,14 +9,12 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
"testing/quick"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
|
||||
tmmath "github.com/tendermint/tendermint/libs/math"
|
||||
tmrand "github.com/tendermint/tendermint/libs/rand"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
@@ -654,29 +652,6 @@ func TestAveragingInIncrementProposerPriorityWithVotingPower(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSafeAdd(t *testing.T) {
|
||||
f := func(a, b int64) bool {
|
||||
c, overflow := safeAdd(a, b)
|
||||
return overflow || (!overflow && c == a+b)
|
||||
}
|
||||
if err := quick.Check(f, nil); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSafeAddClip(t *testing.T) {
|
||||
assert.EqualValues(t, math.MaxInt64, safeAddClip(math.MaxInt64, 10))
|
||||
assert.EqualValues(t, math.MaxInt64, safeAddClip(math.MaxInt64, math.MaxInt64))
|
||||
assert.EqualValues(t, math.MinInt64, safeAddClip(math.MinInt64, -10))
|
||||
}
|
||||
|
||||
func TestSafeSubClip(t *testing.T) {
|
||||
assert.EqualValues(t, math.MinInt64, safeSubClip(math.MinInt64, 10))
|
||||
assert.EqualValues(t, 0, safeSubClip(math.MinInt64, math.MinInt64))
|
||||
assert.EqualValues(t, math.MinInt64, safeSubClip(math.MinInt64, math.MaxInt64))
|
||||
assert.EqualValues(t, math.MaxInt64, safeSubClip(math.MaxInt64, -10))
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
func TestEmptySet(t *testing.T) {
|
||||
@@ -771,7 +746,7 @@ func valSetTotalProposerPriority(valSet *ValidatorSet) int64 {
|
||||
sum := int64(0)
|
||||
for _, val := range valSet.Validators {
|
||||
// mind overflow
|
||||
sum = safeAddClip(sum, val.ProposerPriority)
|
||||
sum = tmmath.SafeAddClip(sum, val.ProposerPriority)
|
||||
}
|
||||
return sum
|
||||
}
|
||||
@@ -1382,74 +1357,6 @@ func TestValSetUpdateOverflowRelated(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSafeMul(t *testing.T) {
|
||||
testCases := []struct {
|
||||
a int64
|
||||
b int64
|
||||
c int64
|
||||
overflow bool
|
||||
}{
|
||||
0: {0, 0, 0, false},
|
||||
1: {1, 0, 0, false},
|
||||
2: {2, 3, 6, false},
|
||||
3: {2, -3, -6, false},
|
||||
4: {-2, -3, 6, false},
|
||||
5: {-2, 3, -6, false},
|
||||
6: {math.MaxInt64, 1, math.MaxInt64, false},
|
||||
7: {math.MaxInt64 / 2, 2, math.MaxInt64 - 1, false},
|
||||
8: {math.MaxInt64 / 2, 3, 0, true},
|
||||
9: {math.MaxInt64, 2, 0, true},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
c, overflow := safeMul(tc.a, tc.b)
|
||||
assert.Equal(t, tc.c, c, "#%d", i)
|
||||
assert.Equal(t, tc.overflow, overflow, "#%d", i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatorSetProtoBuf(t *testing.T) {
|
||||
valset, _ := randValidatorPrivValSet(10, 100)
|
||||
valset2, _ := randValidatorPrivValSet(10, 100)
|
||||
valset2.Validators[0] = &Validator{}
|
||||
|
||||
valset3, _ := randValidatorPrivValSet(10, 100)
|
||||
valset3.Proposer = nil
|
||||
|
||||
valset4, _ := randValidatorPrivValSet(10, 100)
|
||||
valset4.Proposer = &Validator{}
|
||||
|
||||
testCases := []struct {
|
||||
msg string
|
||||
v1 *ValidatorSet
|
||||
expPass1 bool
|
||||
expPass2 bool
|
||||
}{
|
||||
{"success", valset, true, true},
|
||||
{"fail valSet2, pubkey empty", valset2, false, false},
|
||||
{"fail nil Proposer", valset3, false, false},
|
||||
{"fail empty Proposer", valset4, false, false},
|
||||
{"fail empty valSet", &ValidatorSet{}, true, false},
|
||||
{"false nil", nil, true, false},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
protoValSet, err := tc.v1.ToProto()
|
||||
if tc.expPass1 {
|
||||
require.NoError(t, err, tc.msg)
|
||||
} else {
|
||||
require.Error(t, err, tc.msg)
|
||||
}
|
||||
|
||||
valSet, err := ValidatorSetFromProto(protoValSet)
|
||||
if tc.expPass2 {
|
||||
require.NoError(t, err, tc.msg)
|
||||
require.EqualValues(t, tc.v1, valSet, tc.msg)
|
||||
} else {
|
||||
require.Error(t, err, tc.msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------
|
||||
// Sort validators by priority and address
|
||||
type validatorsByPriority []*Validator
|
||||
@@ -1490,129 +1397,3 @@ func (tvals testValsByVotingPower) Less(i, j int) bool {
|
||||
func (tvals testValsByVotingPower) Swap(i, j int) {
|
||||
tvals[i], tvals[j] = tvals[j], tvals[i]
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
// Benchmark tests
|
||||
//
|
||||
func BenchmarkUpdates(b *testing.B) {
|
||||
const (
|
||||
n = 100
|
||||
m = 2000
|
||||
)
|
||||
// Init with n validators
|
||||
vs := make([]*Validator, n)
|
||||
for j := 0; j < n; j++ {
|
||||
vs[j] = newValidator([]byte(fmt.Sprintf("v%d", j)), 100)
|
||||
}
|
||||
valSet := NewValidatorSet(vs)
|
||||
l := len(valSet.Validators)
|
||||
|
||||
// Make m new validators
|
||||
newValList := make([]*Validator, m)
|
||||
for j := 0; j < m; j++ {
|
||||
newValList[j] = newValidator([]byte(fmt.Sprintf("v%d", j+l)), 1000)
|
||||
}
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Add m validators to valSetCopy
|
||||
valSetCopy := valSet.Copy()
|
||||
assert.NoError(b, valSetCopy.UpdateWithChangeSet(newValList))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkValidatorSet_VerifyCommit_Ed25519(b *testing.B) {
|
||||
for _, n := range []int{1, 8, 64, 1024} {
|
||||
n := n
|
||||
var (
|
||||
chainID = "test_chain_id"
|
||||
h = int64(3)
|
||||
blockID = makeBlockIDRandom()
|
||||
)
|
||||
b.Run(fmt.Sprintf("valset size %d", n), func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
// generate n validators
|
||||
voteSet, valSet, vals := randVoteSet(h, 0, tmproto.PrecommitType, n, int64(n*5))
|
||||
// create a commit with n validators
|
||||
commit, err := makeCommit(blockID, h, 0, voteSet, vals, time.Now())
|
||||
require.NoError(b, err)
|
||||
|
||||
for i := 0; i < b.N/n; i++ {
|
||||
err = valSet.VerifyCommit(chainID, blockID, h, commit)
|
||||
assert.NoError(b, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkValidatorSet_VerifyCommitLight_Ed25519(b *testing.B) {
|
||||
for _, n := range []int{1, 8, 64, 1024} {
|
||||
n := n
|
||||
var (
|
||||
chainID = "test_chain_id"
|
||||
h = int64(3)
|
||||
blockID = makeBlockIDRandom()
|
||||
)
|
||||
b.Run(fmt.Sprintf("valset size %d", n), func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
// generate n validators
|
||||
voteSet, valSet, vals := randVoteSet(h, 0, tmproto.PrecommitType, n, int64(n*5))
|
||||
// create a commit with n validators
|
||||
commit, err := makeCommit(blockID, h, 0, voteSet, vals, time.Now())
|
||||
require.NoError(b, err)
|
||||
|
||||
for i := 0; i < b.N/n; i++ {
|
||||
err = valSet.VerifyCommitLight(chainID, blockID, h, commit)
|
||||
assert.NoError(b, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkValidatorSet_VerifyCommitLightTrusting_Ed25519(b *testing.B) {
|
||||
for _, n := range []int{1, 8, 64, 1024} {
|
||||
n := n
|
||||
var (
|
||||
chainID = "test_chain_id"
|
||||
h = int64(3)
|
||||
blockID = makeBlockIDRandom()
|
||||
)
|
||||
b.Run(fmt.Sprintf("valset size %d", n), func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
// generate n validators
|
||||
voteSet, valSet, vals := randVoteSet(h, 0, tmproto.PrecommitType, n, int64(n*5))
|
||||
// create a commit with n validators
|
||||
commit, err := makeCommit(blockID, h, 0, voteSet, vals, time.Now())
|
||||
require.NoError(b, err)
|
||||
|
||||
for i := 0; i < b.N/n; i++ {
|
||||
err = valSet.VerifyCommitLightTrusting(chainID, commit, tmmath.Fraction{Numerator: 1, Denominator: 3})
|
||||
assert.NoError(b, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Testing Utils
|
||||
|
||||
// deterministicValidatorSet returns a deterministic validator set (size: +numValidators+),
|
||||
// where each validator has a power of 50
|
||||
//
|
||||
// EXPOSED FOR TESTING.
|
||||
func deterministicValidatorSet() (*ValidatorSet, []PrivValidator) {
|
||||
var (
|
||||
valz = make([]*Validator, 10)
|
||||
privValidators = make([]PrivValidator, 10)
|
||||
)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
// val, privValidator := DeterministicValidator(ed25519.PrivKey([]byte(deterministicKeys[i])))
|
||||
val, privValidator := deterministicValidator(ed25519.GenPrivKeyFromSecret([]byte(fmt.Sprintf("key: %x", i))))
|
||||
valz[i] = val
|
||||
privValidators[i] = privValidator
|
||||
}
|
||||
|
||||
sort.Sort(PrivValidatorsByAddress(privValidators))
|
||||
|
||||
return NewValidatorSet(valz), privValidators
|
||||
}
|
||||
@@ -1,25 +1,26 @@
|
||||
package types
|
||||
package consensus_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
|
||||
test "github.com/tendermint/tendermint/internal/test/factory"
|
||||
"github.com/tendermint/tendermint/pkg/consensus"
|
||||
)
|
||||
|
||||
func TestValidatorProtoBuf(t *testing.T) {
|
||||
val, _ := randValidator(true, 100)
|
||||
val, _ := test.RandValidator(true, 100)
|
||||
testCases := []struct {
|
||||
msg string
|
||||
v1 *Validator
|
||||
v1 *consensus.Validator
|
||||
expPass1 bool
|
||||
expPass2 bool
|
||||
}{
|
||||
{"success validator", val, true, true},
|
||||
{"failure empty", &Validator{}, false, false},
|
||||
{"failure empty", &consensus.Validator{}, false, false},
|
||||
{"failure nil", nil, false, false},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
@@ -31,7 +32,7 @@ func TestValidatorProtoBuf(t *testing.T) {
|
||||
require.Error(t, err, tc.msg)
|
||||
}
|
||||
|
||||
val, err := ValidatorFromProto(protoVal)
|
||||
val, err := consensus.ValidatorFromProto(protoVal)
|
||||
if tc.expPass2 {
|
||||
require.NoError(t, err, tc.msg)
|
||||
require.Equal(t, tc.v1, val, tc.msg)
|
||||
@@ -42,15 +43,15 @@ func TestValidatorProtoBuf(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidatorValidateBasic(t *testing.T) {
|
||||
priv := NewMockPV()
|
||||
priv := consensus.NewMockPV()
|
||||
pubKey, _ := priv.GetPubKey(context.Background())
|
||||
testCases := []struct {
|
||||
val *Validator
|
||||
val *consensus.Validator
|
||||
err bool
|
||||
msg string
|
||||
}{
|
||||
{
|
||||
val: NewValidator(pubKey, 1),
|
||||
val: consensus.NewValidator(pubKey, 1),
|
||||
err: false,
|
||||
msg: "",
|
||||
},
|
||||
@@ -60,19 +61,19 @@ func TestValidatorValidateBasic(t *testing.T) {
|
||||
msg: "nil validator",
|
||||
},
|
||||
{
|
||||
val: &Validator{
|
||||
val: &consensus.Validator{
|
||||
PubKey: nil,
|
||||
},
|
||||
err: true,
|
||||
msg: "validator does not have a public key",
|
||||
},
|
||||
{
|
||||
val: NewValidator(pubKey, -1),
|
||||
val: consensus.NewValidator(pubKey, -1),
|
||||
err: true,
|
||||
msg: "validator has negative voting power",
|
||||
},
|
||||
{
|
||||
val: &Validator{
|
||||
val: &consensus.Validator{
|
||||
PubKey: pubKey,
|
||||
Address: nil,
|
||||
},
|
||||
@@ -80,7 +81,7 @@ func TestValidatorValidateBasic(t *testing.T) {
|
||||
msg: "validator address is the wrong size: ",
|
||||
},
|
||||
{
|
||||
val: &Validator{
|
||||
val: &consensus.Validator{
|
||||
PubKey: pubKey,
|
||||
Address: []byte{'a'},
|
||||
},
|
||||
@@ -100,19 +101,3 @@ func TestValidatorValidateBasic(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Testing util functions
|
||||
|
||||
// deterministicValidator returns a deterministic validator, useful for testing.
|
||||
// UNSTABLE
|
||||
func deterministicValidator(key crypto.PrivKey) (*Validator, PrivValidator) {
|
||||
privVal := NewMockPV()
|
||||
privVal.PrivKey = key
|
||||
var votePower int64 = 50
|
||||
pubKey, err := privVal.GetPubKey(context.TODO())
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("could not retrieve pubkey %w", err))
|
||||
}
|
||||
val := NewValidator(pubKey, votePower)
|
||||
return val, privVal
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/internal/libs/protoio"
|
||||
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -51,7 +52,7 @@ type Vote struct {
|
||||
Type tmproto.SignedMsgType `json:"type"`
|
||||
Height int64 `json:"height"`
|
||||
Round int32 `json:"round"` // assume there will not be greater than 2_147_483_647 rounds
|
||||
BlockID BlockID `json:"block_id"` // zero if vote is nil.
|
||||
BlockID meta.BlockID `json:"block_id"` // zero if vote is nil.
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
ValidatorAddress Address `json:"validator_address"`
|
||||
ValidatorIndex int32 `json:"validator_index"`
|
||||
@@ -59,22 +60,22 @@ type Vote struct {
|
||||
}
|
||||
|
||||
// CommitSig converts the Vote to a CommitSig.
|
||||
func (vote *Vote) CommitSig() CommitSig {
|
||||
func (vote *Vote) CommitSig() meta.CommitSig {
|
||||
if vote == nil {
|
||||
return NewCommitSigAbsent()
|
||||
return meta.NewCommitSigAbsent()
|
||||
}
|
||||
|
||||
var blockIDFlag BlockIDFlag
|
||||
var blockIDFlag meta.BlockIDFlag
|
||||
switch {
|
||||
case vote.BlockID.IsComplete():
|
||||
blockIDFlag = BlockIDFlagCommit
|
||||
blockIDFlag = meta.BlockIDFlagCommit
|
||||
case vote.BlockID.IsZero():
|
||||
blockIDFlag = BlockIDFlagNil
|
||||
blockIDFlag = meta.BlockIDFlagNil
|
||||
default:
|
||||
panic(fmt.Sprintf("Invalid vote %v - expected BlockID to be either empty or complete", vote))
|
||||
}
|
||||
|
||||
return CommitSig{
|
||||
return meta.CommitSig{
|
||||
BlockIDFlag: blockIDFlag,
|
||||
ValidatorAddress: vote.ValidatorAddress,
|
||||
Timestamp: vote.Timestamp,
|
||||
@@ -82,6 +83,23 @@ func (vote *Vote) CommitSig() CommitSig {
|
||||
}
|
||||
}
|
||||
|
||||
// GetVote converts the CommitSig for the given valIdx to a Vote.
|
||||
// Returns nil if the precommit at valIdx is nil.
|
||||
// Panics if valIdx >= commit.Size().
|
||||
func GetVoteFromCommit(commit *meta.Commit, valIdx int32) *Vote {
|
||||
commitSig := commit.Signatures[valIdx]
|
||||
return &Vote{
|
||||
Type: tmproto.PrecommitType,
|
||||
Height: commit.Height,
|
||||
Round: commit.Round,
|
||||
BlockID: commitSig.BlockID(commit.BlockID),
|
||||
Timestamp: commitSig.Timestamp,
|
||||
ValidatorAddress: commitSig.ValidatorAddress,
|
||||
ValidatorIndex: valIdx,
|
||||
Signature: commitSig.Signature,
|
||||
}
|
||||
}
|
||||
|
||||
// VoteSignBytes returns the proto-encoding of the canonicalized Vote, for
|
||||
// signing. Panics is the marshaling fails.
|
||||
//
|
||||
@@ -105,6 +123,20 @@ func (vote *Vote) Copy() *Vote {
|
||||
return &voteCopy
|
||||
}
|
||||
|
||||
// VoteSignBytes returns the bytes of the Vote corresponding to valIdx for
|
||||
// signing.
|
||||
//
|
||||
// The only unique part is the Timestamp - all other fields signed over are
|
||||
// otherwise the same for all validators.
|
||||
//
|
||||
// Panics if valIdx >= commit.Size().
|
||||
//
|
||||
// See VoteSignBytes
|
||||
func VoteSignBytesFromCommit(commit *meta.Commit, chainID string, valIdx int32) []byte {
|
||||
v := GetVoteFromCommit(commit, valIdx).ToProto()
|
||||
return VoteSignBytes(chainID, v)
|
||||
}
|
||||
|
||||
// String returns a string representation of Vote.
|
||||
//
|
||||
// 1. validator index
|
||||
@@ -140,7 +172,7 @@ func (vote *Vote) String() string {
|
||||
typeString,
|
||||
tmbytes.Fingerprint(vote.BlockID.Hash),
|
||||
tmbytes.Fingerprint(vote.Signature),
|
||||
CanonicalTime(vote.Timestamp),
|
||||
meta.CanonicalTime(vote.Timestamp),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -194,8 +226,8 @@ func (vote *Vote) ValidateBasic() error {
|
||||
return errors.New("signature is missing")
|
||||
}
|
||||
|
||||
if len(vote.Signature) > MaxSignatureSize {
|
||||
return fmt.Errorf("signature is too big (max: %d)", MaxSignatureSize)
|
||||
if len(vote.Signature) > meta.MaxSignatureSize {
|
||||
return fmt.Errorf("signature is too big (max: %d)", meta.MaxSignatureSize)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -227,7 +259,7 @@ func VoteFromProto(pv *tmproto.Vote) (*Vote, error) {
|
||||
return nil, errors.New("nil vote")
|
||||
}
|
||||
|
||||
blockID, err := BlockIDFromProto(&pv.BlockID)
|
||||
blockID, err := meta.BlockIDFromProto(&pv.BlockID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -244,3 +276,13 @@ func VoteFromProto(pv *tmproto.Vote) (*Vote, error) {
|
||||
|
||||
return vote, vote.ValidateBasic()
|
||||
}
|
||||
|
||||
// IsVoteTypeValid returns true if t is a valid vote type.
|
||||
func IsVoteTypeValid(t tmproto.SignedMsgType) bool {
|
||||
switch t {
|
||||
case tmproto.PrevoteType, tmproto.PrecommitType:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
tmsync "github.com/tendermint/tendermint/internal/libs/sync"
|
||||
"github.com/tendermint/tendermint/libs/bits"
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -41,10 +42,10 @@ type P2PID string
|
||||
the first vote seen, but when a 2/3 majority is found, votes for that get
|
||||
priority and are copied over from `.votesByBlock`.
|
||||
|
||||
`.votesByBlock` keeps track of a list of votes for a particular block. There
|
||||
`.votesByBlock` keeps track of a list of votes for a particular meta. There
|
||||
are two ways a &blockVotes{} gets created in `.votesByBlock`.
|
||||
1. the first vote seen by a validator was for the particular block.
|
||||
2. a peer claims to have seen 2/3 majority for the particular block.
|
||||
1. the first vote seen by a validator was for the particular meta.
|
||||
2. a peer claims to have seen 2/3 majority for the particular meta.
|
||||
|
||||
Since the first vote from a validator will always get added in `.votesByBlock`
|
||||
, all votes in `.votes` will have a corresponding entry in `.votesByBlock`.
|
||||
@@ -69,9 +70,9 @@ type VoteSet struct {
|
||||
votesBitArray *bits.BitArray
|
||||
votes []*Vote // Primary votes to share
|
||||
sum int64 // Sum of voting power for seen votes, discounting conflicts
|
||||
maj23 *BlockID // First 2/3 majority seen
|
||||
maj23 *meta.BlockID // First 2/3 majority seen
|
||||
votesByBlock map[string]*blockVotes // string(blockHash|blockParts) -> blockVotes
|
||||
peerMaj23s map[P2PID]BlockID // Maj23 for each peer
|
||||
peerMaj23s map[P2PID]meta.BlockID // Maj23 for each peer
|
||||
}
|
||||
|
||||
// Constructs a new VoteSet struct used to accumulate votes for given height/round.
|
||||
@@ -91,10 +92,27 @@ func NewVoteSet(chainID string, height int64, round int32,
|
||||
sum: 0,
|
||||
maj23: nil,
|
||||
votesByBlock: make(map[string]*blockVotes, valSet.Size()),
|
||||
peerMaj23s: make(map[P2PID]BlockID),
|
||||
peerMaj23s: make(map[P2PID]meta.BlockID),
|
||||
}
|
||||
}
|
||||
|
||||
// CommitToVoteSet constructs a VoteSet from the Commit and validator set.
|
||||
// Panics if signatures from the commit can't be added to the voteset.
|
||||
// Inverse of VoteSet.MakeCommit().
|
||||
func VoteSetFromCommit(chainID string, commit *meta.Commit, vals *ValidatorSet) *VoteSet {
|
||||
voteSet := NewVoteSet(chainID, commit.Height, commit.Round, tmproto.PrecommitType, vals)
|
||||
for idx, commitSig := range commit.Signatures {
|
||||
if commitSig.Absent() {
|
||||
continue // OK, some precommits can be missing.
|
||||
}
|
||||
added, err := voteSet.AddVote(GetVoteFromCommit(commit, int32(idx)))
|
||||
if !added || err != nil {
|
||||
panic(fmt.Sprintf("Failed to reconstruct LastCommit: %v", err))
|
||||
}
|
||||
}
|
||||
return voteSet
|
||||
}
|
||||
|
||||
func (voteSet *VoteSet) ChainID() string {
|
||||
return voteSet.chainID
|
||||
}
|
||||
@@ -306,7 +324,7 @@ func (voteSet *VoteSet) addVerifiedVote(
|
||||
// this can cause memory issues.
|
||||
// TODO: implement ability to remove peers too
|
||||
// NOTE: VoteSet must not be nil
|
||||
func (voteSet *VoteSet) SetPeerMaj23(peerID P2PID, blockID BlockID) error {
|
||||
func (voteSet *VoteSet) SetPeerMaj23(peerID P2PID, blockID meta.BlockID) error {
|
||||
if voteSet == nil {
|
||||
panic("SetPeerMaj23() on nil VoteSet")
|
||||
}
|
||||
@@ -351,7 +369,7 @@ func (voteSet *VoteSet) BitArray() *bits.BitArray {
|
||||
return voteSet.votesBitArray.Copy()
|
||||
}
|
||||
|
||||
func (voteSet *VoteSet) BitArrayByBlockID(blockID BlockID) *bits.BitArray {
|
||||
func (voteSet *VoteSet) BitArrayByBlockID(blockID meta.BlockID) *bits.BitArray {
|
||||
if voteSet == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -427,16 +445,16 @@ func (voteSet *VoteSet) HasAll() bool {
|
||||
|
||||
// If there was a +2/3 majority for blockID, return blockID and true.
|
||||
// Else, return the empty BlockID{} and false.
|
||||
func (voteSet *VoteSet) TwoThirdsMajority() (blockID BlockID, ok bool) {
|
||||
func (voteSet *VoteSet) TwoThirdsMajority() (blockID meta.BlockID, ok bool) {
|
||||
if voteSet == nil {
|
||||
return BlockID{}, false
|
||||
return meta.BlockID{}, false
|
||||
}
|
||||
voteSet.mtx.Lock()
|
||||
defer voteSet.mtx.Unlock()
|
||||
if voteSet.maj23 != nil {
|
||||
return *voteSet.maj23, true
|
||||
}
|
||||
return BlockID{}, false
|
||||
return meta.BlockID{}, false
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
@@ -503,9 +521,9 @@ func (voteSet *VoteSet) MarshalJSON() ([]byte, error) {
|
||||
// NOTE: insufficient for unmarshaling from (compressed votes)
|
||||
// TODO: make the peerMaj23s nicer to read (eg just the block hash)
|
||||
type VoteSetJSON struct {
|
||||
Votes []string `json:"votes"`
|
||||
VotesBitArray string `json:"votes_bit_array"`
|
||||
PeerMaj23s map[P2PID]BlockID `json:"peer_maj_23s"`
|
||||
Votes []string `json:"votes"`
|
||||
VotesBitArray string `json:"votes_bit_array"`
|
||||
PeerMaj23s map[P2PID]meta.BlockID `json:"peer_maj_23s"`
|
||||
}
|
||||
|
||||
// Return the bit-array of votes including
|
||||
@@ -589,8 +607,8 @@ func (voteSet *VoteSet) sumTotalFrac() (int64, int64, float64) {
|
||||
// for the block, which has 2/3+ majority, and nil.
|
||||
//
|
||||
// Panics if the vote type is not PrecommitType or if there's no +2/3 votes for
|
||||
// a single block.
|
||||
func (voteSet *VoteSet) MakeCommit() *Commit {
|
||||
// a single meta.
|
||||
func (voteSet *VoteSet) MakeCommit() *meta.Commit {
|
||||
if voteSet.signedMsgType != tmproto.PrecommitType {
|
||||
panic("Cannot MakeCommit() unless VoteSet.Type is PrecommitType")
|
||||
}
|
||||
@@ -603,17 +621,17 @@ func (voteSet *VoteSet) MakeCommit() *Commit {
|
||||
}
|
||||
|
||||
// For every validator, get the precommit
|
||||
commitSigs := make([]CommitSig, len(voteSet.votes))
|
||||
commitSigs := make([]meta.CommitSig, len(voteSet.votes))
|
||||
for i, v := range voteSet.votes {
|
||||
commitSig := v.CommitSig()
|
||||
// if block ID exists but doesn't match, exclude sig
|
||||
if commitSig.ForBlock() && !v.BlockID.Equals(*voteSet.maj23) {
|
||||
commitSig = NewCommitSigAbsent()
|
||||
commitSig = meta.NewCommitSigAbsent()
|
||||
}
|
||||
commitSigs[i] = commitSig
|
||||
}
|
||||
|
||||
return NewCommit(voteSet.GetHeight(), voteSet.GetRound(), *voteSet.maj23, commitSigs)
|
||||
return meta.NewCommit(voteSet.GetHeight(), voteSet.GetRound(), *voteSet.maj23, commitSigs)
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
@@ -1,23 +1,26 @@
|
||||
package types
|
||||
package consensus_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
test "github.com/tendermint/tendermint/internal/test/factory"
|
||||
tmrand "github.com/tendermint/tendermint/libs/rand"
|
||||
tmtime "github.com/tendermint/tendermint/libs/time"
|
||||
"github.com/tendermint/tendermint/pkg/consensus"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
|
||||
func TestVoteSet_AddVote_Good(t *testing.T) {
|
||||
height, round := int64(1), int32(0)
|
||||
voteSet, _, privValidators := randVoteSet(height, round, tmproto.PrevoteType, 10, 1)
|
||||
voteSet, _, privValidators := test.RandVoteSet(height, round, tmproto.PrevoteType, 10, 1)
|
||||
val0 := privValidators[0]
|
||||
|
||||
val0p, err := val0.GetPubKey(context.Background())
|
||||
@@ -29,16 +32,16 @@ func TestVoteSet_AddVote_Good(t *testing.T) {
|
||||
blockID, ok := voteSet.TwoThirdsMajority()
|
||||
assert.False(t, ok || !blockID.IsZero(), "there should be no 2/3 majority")
|
||||
|
||||
vote := &Vote{
|
||||
vote := &consensus.Vote{
|
||||
ValidatorAddress: val0Addr,
|
||||
ValidatorIndex: 0, // since privValidators are in order
|
||||
Height: height,
|
||||
Round: round,
|
||||
Type: tmproto.PrevoteType,
|
||||
Timestamp: tmtime.Now(),
|
||||
BlockID: BlockID{nil, PartSetHeader{}},
|
||||
BlockID: meta.BlockID{nil, meta.PartSetHeader{}},
|
||||
}
|
||||
_, err = signAddVote(val0, vote, voteSet)
|
||||
_, err = test.SignAddVote(val0, vote, voteSet)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.NotNil(t, voteSet.GetByAddress(val0Addr))
|
||||
@@ -49,16 +52,16 @@ func TestVoteSet_AddVote_Good(t *testing.T) {
|
||||
|
||||
func TestVoteSet_AddVote_Bad(t *testing.T) {
|
||||
height, round := int64(1), int32(0)
|
||||
voteSet, _, privValidators := randVoteSet(height, round, tmproto.PrevoteType, 10, 1)
|
||||
voteSet, _, privValidators := test.RandVoteSet(height, round, tmproto.PrevoteType, 10, 1)
|
||||
|
||||
voteProto := &Vote{
|
||||
voteProto := &consensus.Vote{
|
||||
ValidatorAddress: nil,
|
||||
ValidatorIndex: -1,
|
||||
Height: height,
|
||||
Round: round,
|
||||
Timestamp: tmtime.Now(),
|
||||
Type: tmproto.PrevoteType,
|
||||
BlockID: BlockID{nil, PartSetHeader{}},
|
||||
BlockID: meta.BlockID{nil, meta.PartSetHeader{}},
|
||||
}
|
||||
|
||||
// val0 votes for nil.
|
||||
@@ -67,19 +70,19 @@ func TestVoteSet_AddVote_Bad(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
addr := pubKey.Address()
|
||||
vote := withValidator(voteProto, addr, 0)
|
||||
added, err := signAddVote(privValidators[0], vote, voteSet)
|
||||
added, err := test.SignAddVote(privValidators[0], vote, voteSet)
|
||||
if !added || err != nil {
|
||||
t.Errorf("expected VoteSet.Add to succeed")
|
||||
}
|
||||
}
|
||||
|
||||
// val0 votes again for some block.
|
||||
// val0 votes again for some meta.
|
||||
{
|
||||
pubKey, err := privValidators[0].GetPubKey(context.Background())
|
||||
require.NoError(t, err)
|
||||
addr := pubKey.Address()
|
||||
vote := withValidator(voteProto, addr, 0)
|
||||
added, err := signAddVote(privValidators[0], withBlockHash(vote, tmrand.Bytes(32)), voteSet)
|
||||
added, err := test.SignAddVote(privValidators[0], withBlockHash(vote, tmrand.Bytes(32)), voteSet)
|
||||
if added || err == nil {
|
||||
t.Errorf("expected VoteSet.Add to fail, conflicting vote.")
|
||||
}
|
||||
@@ -91,7 +94,7 @@ func TestVoteSet_AddVote_Bad(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
addr := pubKey.Address()
|
||||
vote := withValidator(voteProto, addr, 1)
|
||||
added, err := signAddVote(privValidators[1], withHeight(vote, height+1), voteSet)
|
||||
added, err := test.SignAddVote(privValidators[1], withHeight(vote, height+1), voteSet)
|
||||
if added || err == nil {
|
||||
t.Errorf("expected VoteSet.Add to fail, wrong height")
|
||||
}
|
||||
@@ -103,7 +106,7 @@ func TestVoteSet_AddVote_Bad(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
addr := pubKey.Address()
|
||||
vote := withValidator(voteProto, addr, 2)
|
||||
added, err := signAddVote(privValidators[2], withRound(vote, round+1), voteSet)
|
||||
added, err := test.SignAddVote(privValidators[2], withRound(vote, round+1), voteSet)
|
||||
if added || err == nil {
|
||||
t.Errorf("expected VoteSet.Add to fail, wrong round")
|
||||
}
|
||||
@@ -115,7 +118,7 @@ func TestVoteSet_AddVote_Bad(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
addr := pubKey.Address()
|
||||
vote := withValidator(voteProto, addr, 3)
|
||||
added, err := signAddVote(privValidators[3], withType(vote, byte(tmproto.PrecommitType)), voteSet)
|
||||
added, err := test.SignAddVote(privValidators[3], withType(vote, byte(tmproto.PrecommitType)), voteSet)
|
||||
if added || err == nil {
|
||||
t.Errorf("expected VoteSet.Add to fail, wrong type")
|
||||
}
|
||||
@@ -124,16 +127,16 @@ func TestVoteSet_AddVote_Bad(t *testing.T) {
|
||||
|
||||
func TestVoteSet_2_3Majority(t *testing.T) {
|
||||
height, round := int64(1), int32(0)
|
||||
voteSet, _, privValidators := randVoteSet(height, round, tmproto.PrevoteType, 10, 1)
|
||||
voteSet, _, privValidators := test.RandVoteSet(height, round, tmproto.PrevoteType, 10, 1)
|
||||
|
||||
voteProto := &Vote{
|
||||
voteProto := &consensus.Vote{
|
||||
ValidatorAddress: nil, // NOTE: must fill in
|
||||
ValidatorIndex: -1, // NOTE: must fill in
|
||||
Height: height,
|
||||
Round: round,
|
||||
Type: tmproto.PrevoteType,
|
||||
Timestamp: tmtime.Now(),
|
||||
BlockID: BlockID{nil, PartSetHeader{}},
|
||||
BlockID: meta.BlockID{nil, meta.PartSetHeader{}},
|
||||
}
|
||||
// 6 out of 10 voted for nil.
|
||||
for i := int32(0); i < 6; i++ {
|
||||
@@ -141,7 +144,7 @@ func TestVoteSet_2_3Majority(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
addr := pubKey.Address()
|
||||
vote := withValidator(voteProto, addr, i)
|
||||
_, err = signAddVote(privValidators[i], vote, voteSet)
|
||||
_, err = test.SignAddVote(privValidators[i], vote, voteSet)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
blockID, ok := voteSet.TwoThirdsMajority()
|
||||
@@ -153,7 +156,7 @@ func TestVoteSet_2_3Majority(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
addr := pubKey.Address()
|
||||
vote := withValidator(voteProto, addr, 6)
|
||||
_, err = signAddVote(privValidators[6], withBlockHash(vote, tmrand.Bytes(32)), voteSet)
|
||||
_, err = test.SignAddVote(privValidators[6], withBlockHash(vote, tmrand.Bytes(32)), voteSet)
|
||||
require.NoError(t, err)
|
||||
blockID, ok = voteSet.TwoThirdsMajority()
|
||||
assert.False(t, ok || !blockID.IsZero(), "there should be no 2/3 majority")
|
||||
@@ -165,7 +168,7 @@ func TestVoteSet_2_3Majority(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
addr := pubKey.Address()
|
||||
vote := withValidator(voteProto, addr, 7)
|
||||
_, err = signAddVote(privValidators[7], vote, voteSet)
|
||||
_, err = test.SignAddVote(privValidators[7], vote, voteSet)
|
||||
require.NoError(t, err)
|
||||
blockID, ok = voteSet.TwoThirdsMajority()
|
||||
assert.True(t, ok || blockID.IsZero(), "there should be 2/3 majority for nil")
|
||||
@@ -174,20 +177,20 @@ func TestVoteSet_2_3Majority(t *testing.T) {
|
||||
|
||||
func TestVoteSet_2_3MajorityRedux(t *testing.T) {
|
||||
height, round := int64(1), int32(0)
|
||||
voteSet, _, privValidators := randVoteSet(height, round, tmproto.PrevoteType, 100, 1)
|
||||
voteSet, _, privValidators := test.RandVoteSet(height, round, tmproto.PrevoteType, 100, 1)
|
||||
|
||||
blockHash := crypto.CRandBytes(32)
|
||||
blockPartsTotal := uint32(123)
|
||||
blockPartSetHeader := PartSetHeader{blockPartsTotal, crypto.CRandBytes(32)}
|
||||
blockPartSetHeader := meta.PartSetHeader{blockPartsTotal, crypto.CRandBytes(32)}
|
||||
|
||||
voteProto := &Vote{
|
||||
voteProto := &consensus.Vote{
|
||||
ValidatorAddress: nil, // NOTE: must fill in
|
||||
ValidatorIndex: -1, // NOTE: must fill in
|
||||
Height: height,
|
||||
Round: round,
|
||||
Timestamp: tmtime.Now(),
|
||||
Type: tmproto.PrevoteType,
|
||||
BlockID: BlockID{blockHash, blockPartSetHeader},
|
||||
BlockID: meta.BlockID{blockHash, blockPartSetHeader},
|
||||
}
|
||||
|
||||
// 66 out of 100 voted for nil.
|
||||
@@ -196,7 +199,7 @@ func TestVoteSet_2_3MajorityRedux(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
addr := pubKey.Address()
|
||||
vote := withValidator(voteProto, addr, i)
|
||||
_, err = signAddVote(privValidators[i], vote, voteSet)
|
||||
_, err = test.SignAddVote(privValidators[i], vote, voteSet)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
blockID, ok := voteSet.TwoThirdsMajority()
|
||||
@@ -209,7 +212,7 @@ func TestVoteSet_2_3MajorityRedux(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
adrr := pubKey.Address()
|
||||
vote := withValidator(voteProto, adrr, 66)
|
||||
_, err = signAddVote(privValidators[66], withBlockHash(vote, nil), voteSet)
|
||||
_, err = test.SignAddVote(privValidators[66], withBlockHash(vote, nil), voteSet)
|
||||
require.NoError(t, err)
|
||||
blockID, ok = voteSet.TwoThirdsMajority()
|
||||
assert.False(t, ok || !blockID.IsZero(),
|
||||
@@ -222,8 +225,8 @@ func TestVoteSet_2_3MajorityRedux(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
addr := pubKey.Address()
|
||||
vote := withValidator(voteProto, addr, 67)
|
||||
blockPartsHeader := PartSetHeader{blockPartsTotal, crypto.CRandBytes(32)}
|
||||
_, err = signAddVote(privValidators[67], withBlockPartSetHeader(vote, blockPartsHeader), voteSet)
|
||||
blockPartsHeader := meta.PartSetHeader{blockPartsTotal, crypto.CRandBytes(32)}
|
||||
_, err = test.SignAddVote(privValidators[67], withBlockPartSetHeader(vote, blockPartsHeader), voteSet)
|
||||
require.NoError(t, err)
|
||||
blockID, ok = voteSet.TwoThirdsMajority()
|
||||
assert.False(t, ok || !blockID.IsZero(),
|
||||
@@ -236,8 +239,8 @@ func TestVoteSet_2_3MajorityRedux(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
addr := pubKey.Address()
|
||||
vote := withValidator(voteProto, addr, 68)
|
||||
blockPartsHeader := PartSetHeader{blockPartsTotal + 1, blockPartSetHeader.Hash}
|
||||
_, err = signAddVote(privValidators[68], withBlockPartSetHeader(vote, blockPartsHeader), voteSet)
|
||||
blockPartsHeader := meta.PartSetHeader{blockPartsTotal + 1, blockPartSetHeader.Hash}
|
||||
_, err = test.SignAddVote(privValidators[68], withBlockPartSetHeader(vote, blockPartsHeader), voteSet)
|
||||
require.NoError(t, err)
|
||||
blockID, ok = voteSet.TwoThirdsMajority()
|
||||
assert.False(t, ok || !blockID.IsZero(),
|
||||
@@ -250,7 +253,7 @@ func TestVoteSet_2_3MajorityRedux(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
addr := pubKey.Address()
|
||||
vote := withValidator(voteProto, addr, 69)
|
||||
_, err = signAddVote(privValidators[69], withBlockHash(vote, tmrand.Bytes(32)), voteSet)
|
||||
_, err = test.SignAddVote(privValidators[69], withBlockHash(vote, tmrand.Bytes(32)), voteSet)
|
||||
require.NoError(t, err)
|
||||
blockID, ok = voteSet.TwoThirdsMajority()
|
||||
assert.False(t, ok || !blockID.IsZero(),
|
||||
@@ -263,28 +266,28 @@ func TestVoteSet_2_3MajorityRedux(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
addr := pubKey.Address()
|
||||
vote := withValidator(voteProto, addr, 70)
|
||||
_, err = signAddVote(privValidators[70], vote, voteSet)
|
||||
_, err = test.SignAddVote(privValidators[70], vote, voteSet)
|
||||
require.NoError(t, err)
|
||||
blockID, ok = voteSet.TwoThirdsMajority()
|
||||
assert.True(t, ok && blockID.Equals(BlockID{blockHash, blockPartSetHeader}),
|
||||
assert.True(t, ok && blockID.Equals(meta.BlockID{blockHash, blockPartSetHeader}),
|
||||
"there should be 2/3 majority")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVoteSet_Conflicts(t *testing.T) {
|
||||
height, round := int64(1), int32(0)
|
||||
voteSet, _, privValidators := randVoteSet(height, round, tmproto.PrevoteType, 4, 1)
|
||||
voteSet, _, privValidators := test.RandVoteSet(height, round, tmproto.PrevoteType, 4, 1)
|
||||
blockHash1 := tmrand.Bytes(32)
|
||||
blockHash2 := tmrand.Bytes(32)
|
||||
|
||||
voteProto := &Vote{
|
||||
voteProto := &consensus.Vote{
|
||||
ValidatorAddress: nil,
|
||||
ValidatorIndex: -1,
|
||||
Height: height,
|
||||
Round: round,
|
||||
Timestamp: tmtime.Now(),
|
||||
Type: tmproto.PrevoteType,
|
||||
BlockID: BlockID{nil, PartSetHeader{}},
|
||||
BlockID: meta.BlockID{nil, meta.PartSetHeader{}},
|
||||
}
|
||||
|
||||
val0, err := privValidators[0].GetPubKey(context.Background())
|
||||
@@ -294,7 +297,7 @@ func TestVoteSet_Conflicts(t *testing.T) {
|
||||
// val0 votes for nil.
|
||||
{
|
||||
vote := withValidator(voteProto, val0Addr, 0)
|
||||
added, err := signAddVote(privValidators[0], vote, voteSet)
|
||||
added, err := test.SignAddVote(privValidators[0], vote, voteSet)
|
||||
if !added || err != nil {
|
||||
t.Errorf("expected VoteSet.Add to succeed")
|
||||
}
|
||||
@@ -303,31 +306,31 @@ func TestVoteSet_Conflicts(t *testing.T) {
|
||||
// val0 votes again for blockHash1.
|
||||
{
|
||||
vote := withValidator(voteProto, val0Addr, 0)
|
||||
added, err := signAddVote(privValidators[0], withBlockHash(vote, blockHash1), voteSet)
|
||||
added, err := test.SignAddVote(privValidators[0], withBlockHash(vote, blockHash1), voteSet)
|
||||
assert.False(t, added, "conflicting vote")
|
||||
assert.Error(t, err, "conflicting vote")
|
||||
}
|
||||
|
||||
// start tracking blockHash1
|
||||
err = voteSet.SetPeerMaj23("peerA", BlockID{blockHash1, PartSetHeader{}})
|
||||
err = voteSet.SetPeerMaj23("peerA", meta.BlockID{blockHash1, meta.PartSetHeader{}})
|
||||
require.NoError(t, err)
|
||||
|
||||
// val0 votes again for blockHash1.
|
||||
{
|
||||
vote := withValidator(voteProto, val0Addr, 0)
|
||||
added, err := signAddVote(privValidators[0], withBlockHash(vote, blockHash1), voteSet)
|
||||
added, err := test.SignAddVote(privValidators[0], withBlockHash(vote, blockHash1), voteSet)
|
||||
assert.True(t, added, "called SetPeerMaj23()")
|
||||
assert.Error(t, err, "conflicting vote")
|
||||
}
|
||||
|
||||
// attempt tracking blockHash2, should fail because already set for peerA.
|
||||
err = voteSet.SetPeerMaj23("peerA", BlockID{blockHash2, PartSetHeader{}})
|
||||
err = voteSet.SetPeerMaj23("peerA", meta.BlockID{blockHash2, meta.PartSetHeader{}})
|
||||
require.Error(t, err)
|
||||
|
||||
// val0 votes again for blockHash1.
|
||||
{
|
||||
vote := withValidator(voteProto, val0Addr, 0)
|
||||
added, err := signAddVote(privValidators[0], withBlockHash(vote, blockHash2), voteSet)
|
||||
added, err := test.SignAddVote(privValidators[0], withBlockHash(vote, blockHash2), voteSet)
|
||||
assert.False(t, added, "duplicate SetPeerMaj23() from peerA")
|
||||
assert.Error(t, err, "conflicting vote")
|
||||
}
|
||||
@@ -338,7 +341,7 @@ func TestVoteSet_Conflicts(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
addr := pv.Address()
|
||||
vote := withValidator(voteProto, addr, 1)
|
||||
added, err := signAddVote(privValidators[1], withBlockHash(vote, blockHash1), voteSet)
|
||||
added, err := test.SignAddVote(privValidators[1], withBlockHash(vote, blockHash1), voteSet)
|
||||
if !added || err != nil {
|
||||
t.Errorf("expected VoteSet.Add to succeed")
|
||||
}
|
||||
@@ -358,7 +361,7 @@ func TestVoteSet_Conflicts(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
addr := pv.Address()
|
||||
vote := withValidator(voteProto, addr, 2)
|
||||
added, err := signAddVote(privValidators[2], withBlockHash(vote, blockHash2), voteSet)
|
||||
added, err := test.SignAddVote(privValidators[2], withBlockHash(vote, blockHash2), voteSet)
|
||||
if !added || err != nil {
|
||||
t.Errorf("expected VoteSet.Add to succeed")
|
||||
}
|
||||
@@ -373,7 +376,7 @@ func TestVoteSet_Conflicts(t *testing.T) {
|
||||
}
|
||||
|
||||
// now attempt tracking blockHash1
|
||||
err = voteSet.SetPeerMaj23("peerB", BlockID{blockHash1, PartSetHeader{}})
|
||||
err = voteSet.SetPeerMaj23("peerB", meta.BlockID{blockHash1, meta.PartSetHeader{}})
|
||||
require.NoError(t, err)
|
||||
|
||||
// val2 votes for blockHash1.
|
||||
@@ -382,7 +385,7 @@ func TestVoteSet_Conflicts(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
addr := pv.Address()
|
||||
vote := withValidator(voteProto, addr, 2)
|
||||
added, err := signAddVote(privValidators[2], withBlockHash(vote, blockHash1), voteSet)
|
||||
added, err := test.SignAddVote(privValidators[2], withBlockHash(vote, blockHash1), voteSet)
|
||||
assert.True(t, added)
|
||||
assert.Error(t, err, "conflicting vote")
|
||||
}
|
||||
@@ -402,26 +405,26 @@ func TestVoteSet_Conflicts(t *testing.T) {
|
||||
|
||||
func TestVoteSet_MakeCommit(t *testing.T) {
|
||||
height, round := int64(1), int32(0)
|
||||
voteSet, _, privValidators := randVoteSet(height, round, tmproto.PrecommitType, 10, 1)
|
||||
blockHash, blockPartSetHeader := crypto.CRandBytes(32), PartSetHeader{123, crypto.CRandBytes(32)}
|
||||
voteSet, _, privValidators := test.RandVoteSet(height, round, tmproto.PrecommitType, 10, 1)
|
||||
blockHash, blockPartSetHeader := crypto.CRandBytes(32), meta.PartSetHeader{123, crypto.CRandBytes(32)}
|
||||
|
||||
voteProto := &Vote{
|
||||
voteProto := &consensus.Vote{
|
||||
ValidatorAddress: nil,
|
||||
ValidatorIndex: -1,
|
||||
Height: height,
|
||||
Round: round,
|
||||
Timestamp: tmtime.Now(),
|
||||
Type: tmproto.PrecommitType,
|
||||
BlockID: BlockID{blockHash, blockPartSetHeader},
|
||||
BlockID: meta.BlockID{blockHash, blockPartSetHeader},
|
||||
}
|
||||
|
||||
// 6 out of 10 voted for some block.
|
||||
// 6 out of 10 voted for some meta.
|
||||
for i := int32(0); i < 6; i++ {
|
||||
pv, err := privValidators[i].GetPubKey(context.Background())
|
||||
assert.NoError(t, err)
|
||||
addr := pv.Address()
|
||||
vote := withValidator(voteProto, addr, i)
|
||||
_, err = signAddVote(privValidators[i], vote, voteSet)
|
||||
_, err = test.SignAddVote(privValidators[i], vote, voteSet)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -430,16 +433,16 @@ func TestVoteSet_MakeCommit(t *testing.T) {
|
||||
// MakeCommit should fail.
|
||||
assert.Panics(t, func() { voteSet.MakeCommit() }, "Doesn't have +2/3 majority")
|
||||
|
||||
// 7th voted for some other block.
|
||||
// 7th voted for some other meta.
|
||||
{
|
||||
pv, err := privValidators[6].GetPubKey(context.Background())
|
||||
assert.NoError(t, err)
|
||||
addr := pv.Address()
|
||||
vote := withValidator(voteProto, addr, 6)
|
||||
vote = withBlockHash(vote, tmrand.Bytes(32))
|
||||
vote = withBlockPartSetHeader(vote, PartSetHeader{123, tmrand.Bytes(32)})
|
||||
vote = withBlockPartSetHeader(vote, meta.PartSetHeader{123, tmrand.Bytes(32)})
|
||||
|
||||
_, err = signAddVote(privValidators[6], vote, voteSet)
|
||||
_, err = test.SignAddVote(privValidators[6], vote, voteSet)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -449,7 +452,7 @@ func TestVoteSet_MakeCommit(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
addr := pv.Address()
|
||||
vote := withValidator(voteProto, addr, 7)
|
||||
_, err = signAddVote(privValidators[7], vote, voteSet)
|
||||
_, err = test.SignAddVote(privValidators[7], vote, voteSet)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -459,9 +462,9 @@ func TestVoteSet_MakeCommit(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
addr := pv.Address()
|
||||
vote := withValidator(voteProto, addr, 8)
|
||||
vote.BlockID = BlockID{}
|
||||
vote.BlockID = meta.BlockID{}
|
||||
|
||||
_, err = signAddVote(privValidators[8], vote, voteSet)
|
||||
_, err = test.SignAddVote(privValidators[8], vote, voteSet)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -476,47 +479,91 @@ func TestVoteSet_MakeCommit(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: privValidators are in order
|
||||
func randVoteSet(
|
||||
height int64,
|
||||
round int32,
|
||||
signedMsgType tmproto.SignedMsgType,
|
||||
numValidators int,
|
||||
votingPower int64,
|
||||
) (*VoteSet, *ValidatorSet, []PrivValidator) {
|
||||
valSet, privValidators := randValidatorPrivValSet(numValidators, votingPower)
|
||||
return NewVoteSet("test_chain_id", height, round, signedMsgType, valSet), valSet, privValidators
|
||||
func TestCommitToVoteSet(t *testing.T) {
|
||||
lastID := test.MakeBlockID()
|
||||
h := int64(3)
|
||||
|
||||
voteSet, valSet, vals := test.RandVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1)
|
||||
commit, err := test.MakeCommit(lastID, h-1, 1, voteSet, vals, time.Now())
|
||||
assert.NoError(t, err)
|
||||
|
||||
chainID := voteSet.ChainID()
|
||||
voteSet2 := consensus.VoteSetFromCommit(chainID, commit, valSet)
|
||||
|
||||
for i := int32(0); int(i) < len(vals); i++ {
|
||||
vote1 := voteSet.GetByIndex(i)
|
||||
vote2 := voteSet2.GetByIndex(i)
|
||||
vote3 := consensus.GetVoteFromCommit(commit, i)
|
||||
|
||||
vote1bz, err := vote1.ToProto().Marshal()
|
||||
require.NoError(t, err)
|
||||
vote2bz, err := vote2.ToProto().Marshal()
|
||||
require.NoError(t, err)
|
||||
vote3bz, err := vote3.ToProto().Marshal()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, vote1bz, vote2bz)
|
||||
assert.Equal(t, vote1bz, vote3bz)
|
||||
}
|
||||
}
|
||||
|
||||
func deterministicVoteSet(
|
||||
height int64,
|
||||
round int32,
|
||||
signedMsgType tmproto.SignedMsgType,
|
||||
votingPower int64,
|
||||
) (*VoteSet, *ValidatorSet, []PrivValidator) {
|
||||
valSet, privValidators := deterministicValidatorSet()
|
||||
return NewVoteSet("test_chain_id", height, round, signedMsgType, valSet), valSet, privValidators
|
||||
}
|
||||
func TestCommitToVoteSetWithVotesForNilBlock(t *testing.T) {
|
||||
blockID := test.MakeBlockIDWithHash([]byte("blockhash"))
|
||||
|
||||
func randValidatorPrivValSet(numValidators int, votingPower int64) (*ValidatorSet, []PrivValidator) {
|
||||
var (
|
||||
valz = make([]*Validator, numValidators)
|
||||
privValidators = make([]PrivValidator, numValidators)
|
||||
const (
|
||||
height = int64(3)
|
||||
round = 0
|
||||
)
|
||||
|
||||
for i := 0; i < numValidators; i++ {
|
||||
val, privValidator := randValidator(false, votingPower)
|
||||
valz[i] = val
|
||||
privValidators[i] = privValidator
|
||||
type commitVoteTest struct {
|
||||
blockIDs []meta.BlockID
|
||||
numVotes []int // must sum to numValidators
|
||||
numValidators int
|
||||
valid bool
|
||||
}
|
||||
|
||||
sort.Sort(PrivValidatorsByAddress(privValidators))
|
||||
testCases := []commitVoteTest{
|
||||
{[]meta.BlockID{blockID, {}}, []int{67, 33}, 100, true},
|
||||
}
|
||||
|
||||
return NewValidatorSet(valz), privValidators
|
||||
for _, tc := range testCases {
|
||||
voteSet, valSet, vals := test.RandVoteSet(height-1, round, tmproto.PrecommitType, tc.numValidators, 1)
|
||||
|
||||
vi := int32(0)
|
||||
for n := range tc.blockIDs {
|
||||
for i := 0; i < tc.numVotes[n]; i++ {
|
||||
pubKey, err := vals[vi].GetPubKey(context.Background())
|
||||
require.NoError(t, err)
|
||||
vote := &consensus.Vote{
|
||||
ValidatorAddress: pubKey.Address(),
|
||||
ValidatorIndex: vi,
|
||||
Height: height - 1,
|
||||
Round: round,
|
||||
Type: tmproto.PrecommitType,
|
||||
BlockID: tc.blockIDs[n],
|
||||
Timestamp: tmtime.Now(),
|
||||
}
|
||||
|
||||
added, err := test.SignAddVote(vals[vi], vote, voteSet)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, added)
|
||||
|
||||
vi++
|
||||
}
|
||||
}
|
||||
|
||||
if tc.valid {
|
||||
commit := voteSet.MakeCommit() // panics without > 2/3 valid votes
|
||||
assert.NotNil(t, commit)
|
||||
err := valSet.VerifyCommit(voteSet.ChainID(), blockID, height-1, commit)
|
||||
assert.Nil(t, err)
|
||||
} else {
|
||||
assert.Panics(t, func() { voteSet.MakeCommit() })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience: Return new vote with different validator address/index
|
||||
func withValidator(vote *Vote, addr []byte, idx int32) *Vote {
|
||||
func withValidator(vote *consensus.Vote, addr []byte, idx int32) *consensus.Vote {
|
||||
vote = vote.Copy()
|
||||
vote.ValidatorAddress = addr
|
||||
vote.ValidatorIndex = idx
|
||||
@@ -524,35 +571,35 @@ func withValidator(vote *Vote, addr []byte, idx int32) *Vote {
|
||||
}
|
||||
|
||||
// Convenience: Return new vote with different height
|
||||
func withHeight(vote *Vote, height int64) *Vote {
|
||||
func withHeight(vote *consensus.Vote, height int64) *consensus.Vote {
|
||||
vote = vote.Copy()
|
||||
vote.Height = height
|
||||
return vote
|
||||
}
|
||||
|
||||
// Convenience: Return new vote with different round
|
||||
func withRound(vote *Vote, round int32) *Vote {
|
||||
func withRound(vote *consensus.Vote, round int32) *consensus.Vote {
|
||||
vote = vote.Copy()
|
||||
vote.Round = round
|
||||
return vote
|
||||
}
|
||||
|
||||
// Convenience: Return new vote with different type
|
||||
func withType(vote *Vote, signedMsgType byte) *Vote {
|
||||
func withType(vote *consensus.Vote, signedMsgType byte) *consensus.Vote {
|
||||
vote = vote.Copy()
|
||||
vote.Type = tmproto.SignedMsgType(signedMsgType)
|
||||
return vote
|
||||
}
|
||||
|
||||
// Convenience: Return new vote with different blockHash
|
||||
func withBlockHash(vote *Vote, blockHash []byte) *Vote {
|
||||
func withBlockHash(vote *consensus.Vote, blockHash []byte) *consensus.Vote {
|
||||
vote = vote.Copy()
|
||||
vote.BlockID.Hash = blockHash
|
||||
return vote
|
||||
}
|
||||
|
||||
// Convenience: Return new vote with different blockParts
|
||||
func withBlockPartSetHeader(vote *Vote, blockPartsHeader PartSetHeader) *Vote {
|
||||
func withBlockPartSetHeader(vote *consensus.Vote, blockPartsHeader meta.PartSetHeader) *consensus.Vote {
|
||||
vote = vote.Copy()
|
||||
vote.BlockID.PartSetHeader = blockPartsHeader
|
||||
return vote
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
"github.com/tendermint/tendermint/internal/libs/protoio"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -25,7 +26,7 @@ func examplePrecommit() *Vote {
|
||||
}
|
||||
|
||||
func exampleVote(t byte) *Vote {
|
||||
var stamp, err = time.Parse(TimeFormat, "2017-12-25T03:00:01.234Z")
|
||||
var stamp, err = time.Parse(meta.TimeFormat, "2017-12-25T03:00:01.234Z")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -35,9 +36,9 @@ func exampleVote(t byte) *Vote {
|
||||
Height: 12345,
|
||||
Round: 2,
|
||||
Timestamp: stamp,
|
||||
BlockID: BlockID{
|
||||
BlockID: meta.BlockID{
|
||||
Hash: tmhash.Sum([]byte("blockID_hash")),
|
||||
PartSetHeader: PartSetHeader{
|
||||
PartSetHeader: meta.PartSetHeader{
|
||||
Total: 1000000,
|
||||
Hash: tmhash.Sum([]byte("blockID_part_set_header_hash")),
|
||||
},
|
||||
@@ -244,12 +245,12 @@ func TestVoteValidateBasic(t *testing.T) {
|
||||
{"Negative Height", func(v *Vote) { v.Height = -1 }, true},
|
||||
{"Negative Round", func(v *Vote) { v.Round = -1 }, true},
|
||||
{"Invalid BlockID", func(v *Vote) {
|
||||
v.BlockID = BlockID{[]byte{1, 2, 3}, PartSetHeader{111, []byte("blockparts")}}
|
||||
v.BlockID = meta.BlockID{[]byte{1, 2, 3}, meta.PartSetHeader{111, []byte("blockparts")}}
|
||||
}, true},
|
||||
{"Invalid Address", func(v *Vote) { v.ValidatorAddress = make([]byte, 1) }, true},
|
||||
{"Invalid ValidatorIndex", func(v *Vote) { v.ValidatorIndex = -1 }, true},
|
||||
{"Invalid Signature", func(v *Vote) { v.Signature = nil }, true},
|
||||
{"Too big Signature", func(v *Vote) { v.Signature = make([]byte, MaxSignatureSize+1) }, true},
|
||||
{"Too big Signature", func(v *Vote) { v.Signature = make([]byte, meta.MaxSignatureSize+1) }, true},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package events
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
tmpubsub "github.com/tendermint/tendermint/libs/pubsub"
|
||||
"github.com/tendermint/tendermint/libs/service"
|
||||
"github.com/tendermint/tendermint/pkg/mempool"
|
||||
)
|
||||
|
||||
const defaultCapacity = 0
|
||||
@@ -178,7 +179,7 @@ func (b *EventBus) PublishEventTx(data EventDataTx) error {
|
||||
Attributes: []types.EventAttribute{
|
||||
{
|
||||
Key: tokens[1],
|
||||
Value: fmt.Sprintf("%X", Tx(data.Tx).Hash()),
|
||||
Value: fmt.Sprintf("%X", mempool.Tx(data.Tx).Hash()),
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package events_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -13,10 +13,15 @@ import (
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
tmpubsub "github.com/tendermint/tendermint/libs/pubsub"
|
||||
tmquery "github.com/tendermint/tendermint/libs/pubsub/query"
|
||||
"github.com/tendermint/tendermint/pkg/block"
|
||||
"github.com/tendermint/tendermint/pkg/events"
|
||||
"github.com/tendermint/tendermint/pkg/evidence"
|
||||
"github.com/tendermint/tendermint/pkg/mempool"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
)
|
||||
|
||||
func TestEventBusPublishEventTx(t *testing.T) {
|
||||
eventBus := NewEventBus()
|
||||
eventBus := events.NewEventBus()
|
||||
err := eventBus.Start()
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
@@ -25,7 +30,7 @@ func TestEventBusPublishEventTx(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
tx := Tx("foo")
|
||||
tx := mempool.Tx("foo")
|
||||
result := abci.ResponseDeliverTx{
|
||||
Data: []byte("bar"),
|
||||
Events: []abci.Event{
|
||||
@@ -41,7 +46,7 @@ func TestEventBusPublishEventTx(t *testing.T) {
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
msg := <-txsSub.Out()
|
||||
edt := msg.Data().(EventDataTx)
|
||||
edt := msg.Data().(events.EventDataTx)
|
||||
assert.Equal(t, int64(1), edt.Height)
|
||||
assert.Equal(t, uint32(0), edt.Index)
|
||||
assert.EqualValues(t, tx, edt.Tx)
|
||||
@@ -49,7 +54,7 @@ func TestEventBusPublishEventTx(t *testing.T) {
|
||||
close(done)
|
||||
}()
|
||||
|
||||
err = eventBus.PublishEventTx(EventDataTx{abci.TxResult{
|
||||
err = eventBus.PublishEventTx(events.EventDataTx{abci.TxResult{
|
||||
Height: 1,
|
||||
Index: 0,
|
||||
Tx: tx,
|
||||
@@ -65,7 +70,7 @@ func TestEventBusPublishEventTx(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEventBusPublishEventNewBlock(t *testing.T) {
|
||||
eventBus := NewEventBus()
|
||||
eventBus := events.NewEventBus()
|
||||
err := eventBus.Start()
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
@@ -74,8 +79,8 @@ func TestEventBusPublishEventNewBlock(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
block := MakeBlock(0, []Tx{}, nil, []Evidence{})
|
||||
blockID := BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(BlockPartSizeBytes).Header()}
|
||||
block := block.MakeBlock(0, []mempool.Tx{}, nil, []evidence.Evidence{})
|
||||
blockID := meta.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(meta.BlockPartSizeBytes).Header()}
|
||||
resultBeginBlock := abci.ResponseBeginBlock{
|
||||
Events: []abci.Event{
|
||||
{Type: "testType", Attributes: []abci.EventAttribute{{Key: "baz", Value: "1"}}},
|
||||
@@ -95,7 +100,7 @@ func TestEventBusPublishEventNewBlock(t *testing.T) {
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
msg := <-blocksSub.Out()
|
||||
edt := msg.Data().(EventDataNewBlock)
|
||||
edt := msg.Data().(events.EventDataNewBlock)
|
||||
assert.Equal(t, block, edt.Block)
|
||||
assert.Equal(t, blockID, edt.BlockID)
|
||||
assert.Equal(t, resultBeginBlock, edt.ResultBeginBlock)
|
||||
@@ -103,7 +108,7 @@ func TestEventBusPublishEventNewBlock(t *testing.T) {
|
||||
close(done)
|
||||
}()
|
||||
|
||||
err = eventBus.PublishEventNewBlock(EventDataNewBlock{
|
||||
err = eventBus.PublishEventNewBlock(events.EventDataNewBlock{
|
||||
Block: block,
|
||||
BlockID: blockID,
|
||||
ResultBeginBlock: resultBeginBlock,
|
||||
@@ -119,7 +124,7 @@ func TestEventBusPublishEventNewBlock(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEventBusPublishEventTxDuplicateKeys(t *testing.T) {
|
||||
eventBus := NewEventBus()
|
||||
eventBus := events.NewEventBus()
|
||||
err := eventBus.Start()
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
@@ -128,7 +133,7 @@ func TestEventBusPublishEventTxDuplicateKeys(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
tx := Tx("foo")
|
||||
tx := mempool.Tx("foo")
|
||||
result := abci.ResponseDeliverTx{
|
||||
Data: []byte("bar"),
|
||||
Events: []abci.Event{
|
||||
@@ -194,7 +199,7 @@ func TestEventBusPublishEventTxDuplicateKeys(t *testing.T) {
|
||||
go func() {
|
||||
select {
|
||||
case msg := <-sub.Out():
|
||||
data := msg.Data().(EventDataTx)
|
||||
data := msg.Data().(events.EventDataTx)
|
||||
assert.Equal(t, int64(1), data.Height)
|
||||
assert.Equal(t, uint32(0), data.Index)
|
||||
assert.EqualValues(t, tx, data.Tx)
|
||||
@@ -205,7 +210,7 @@ func TestEventBusPublishEventTxDuplicateKeys(t *testing.T) {
|
||||
}
|
||||
}()
|
||||
|
||||
err = eventBus.PublishEventTx(EventDataTx{abci.TxResult{
|
||||
err = eventBus.PublishEventTx(events.EventDataTx{abci.TxResult{
|
||||
Height: 1,
|
||||
Index: 0,
|
||||
Tx: tx,
|
||||
@@ -227,7 +232,7 @@ func TestEventBusPublishEventTxDuplicateKeys(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEventBusPublishEventNewBlockHeader(t *testing.T) {
|
||||
eventBus := NewEventBus()
|
||||
eventBus := events.NewEventBus()
|
||||
err := eventBus.Start()
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
@@ -236,7 +241,7 @@ func TestEventBusPublishEventNewBlockHeader(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
block := MakeBlock(0, []Tx{}, nil, []Evidence{})
|
||||
block := block.MakeBlock(0, []mempool.Tx{}, nil, []evidence.Evidence{})
|
||||
resultBeginBlock := abci.ResponseBeginBlock{
|
||||
Events: []abci.Event{
|
||||
{Type: "testType", Attributes: []abci.EventAttribute{{Key: "baz", Value: "1"}}},
|
||||
@@ -256,14 +261,14 @@ func TestEventBusPublishEventNewBlockHeader(t *testing.T) {
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
msg := <-headersSub.Out()
|
||||
edt := msg.Data().(EventDataNewBlockHeader)
|
||||
edt := msg.Data().(events.EventDataNewBlockHeader)
|
||||
assert.Equal(t, block.Header, edt.Header)
|
||||
assert.Equal(t, resultBeginBlock, edt.ResultBeginBlock)
|
||||
assert.Equal(t, resultEndBlock, edt.ResultEndBlock)
|
||||
close(done)
|
||||
}()
|
||||
|
||||
err = eventBus.PublishEventNewBlockHeader(EventDataNewBlockHeader{
|
||||
err = eventBus.PublishEventNewBlockHeader(events.EventDataNewBlockHeader{
|
||||
Header: block.Header,
|
||||
ResultBeginBlock: resultBeginBlock,
|
||||
ResultEndBlock: resultEndBlock,
|
||||
@@ -278,7 +283,7 @@ func TestEventBusPublishEventNewBlockHeader(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEventBusPublishEventNewEvidence(t *testing.T) {
|
||||
eventBus := NewEventBus()
|
||||
eventBus := events.NewEventBus()
|
||||
err := eventBus.Start()
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
@@ -287,7 +292,7 @@ func TestEventBusPublishEventNewEvidence(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
ev := NewMockDuplicateVoteEvidence(1, time.Now(), "test-chain-id")
|
||||
ev := evidence.NewMockDuplicateVoteEvidence(1, time.Now(), "test-chain-id")
|
||||
|
||||
query := "tm.event='NewEvidence'"
|
||||
evSub, err := eventBus.Subscribe(context.Background(), "test", tmquery.MustParse(query))
|
||||
@@ -296,13 +301,13 @@ func TestEventBusPublishEventNewEvidence(t *testing.T) {
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
msg := <-evSub.Out()
|
||||
edt := msg.Data().(EventDataNewEvidence)
|
||||
edt := msg.Data().(events.EventDataNewEvidence)
|
||||
assert.Equal(t, ev, edt.Evidence)
|
||||
assert.Equal(t, int64(4), edt.Height)
|
||||
close(done)
|
||||
}()
|
||||
|
||||
err = eventBus.PublishEventNewEvidence(EventDataNewEvidence{
|
||||
err = eventBus.PublishEventNewEvidence(events.EventDataNewEvidence{
|
||||
Evidence: ev,
|
||||
Height: 4,
|
||||
})
|
||||
@@ -316,7 +321,7 @@ func TestEventBusPublishEventNewEvidence(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEventBusPublish(t *testing.T) {
|
||||
eventBus := NewEventBus()
|
||||
eventBus := events.NewEventBus()
|
||||
err := eventBus.Start()
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
@@ -342,37 +347,37 @@ func TestEventBusPublish(t *testing.T) {
|
||||
}
|
||||
}()
|
||||
|
||||
err = eventBus.Publish(EventNewBlockHeaderValue, EventDataNewBlockHeader{})
|
||||
err = eventBus.Publish(events.EventNewBlockHeaderValue, events.EventDataNewBlockHeader{})
|
||||
require.NoError(t, err)
|
||||
err = eventBus.PublishEventNewBlock(EventDataNewBlock{})
|
||||
err = eventBus.PublishEventNewBlock(events.EventDataNewBlock{})
|
||||
require.NoError(t, err)
|
||||
err = eventBus.PublishEventNewBlockHeader(EventDataNewBlockHeader{})
|
||||
err = eventBus.PublishEventNewBlockHeader(events.EventDataNewBlockHeader{})
|
||||
require.NoError(t, err)
|
||||
err = eventBus.PublishEventVote(EventDataVote{})
|
||||
err = eventBus.PublishEventVote(events.EventDataVote{})
|
||||
require.NoError(t, err)
|
||||
err = eventBus.PublishEventNewRoundStep(EventDataRoundState{})
|
||||
err = eventBus.PublishEventNewRoundStep(events.EventDataRoundState{})
|
||||
require.NoError(t, err)
|
||||
err = eventBus.PublishEventTimeoutPropose(EventDataRoundState{})
|
||||
err = eventBus.PublishEventTimeoutPropose(events.EventDataRoundState{})
|
||||
require.NoError(t, err)
|
||||
err = eventBus.PublishEventTimeoutWait(EventDataRoundState{})
|
||||
err = eventBus.PublishEventTimeoutWait(events.EventDataRoundState{})
|
||||
require.NoError(t, err)
|
||||
err = eventBus.PublishEventNewRound(EventDataNewRound{})
|
||||
err = eventBus.PublishEventNewRound(events.EventDataNewRound{})
|
||||
require.NoError(t, err)
|
||||
err = eventBus.PublishEventCompleteProposal(EventDataCompleteProposal{})
|
||||
err = eventBus.PublishEventCompleteProposal(events.EventDataCompleteProposal{})
|
||||
require.NoError(t, err)
|
||||
err = eventBus.PublishEventPolka(EventDataRoundState{})
|
||||
err = eventBus.PublishEventPolka(events.EventDataRoundState{})
|
||||
require.NoError(t, err)
|
||||
err = eventBus.PublishEventUnlock(EventDataRoundState{})
|
||||
err = eventBus.PublishEventUnlock(events.EventDataRoundState{})
|
||||
require.NoError(t, err)
|
||||
err = eventBus.PublishEventRelock(EventDataRoundState{})
|
||||
err = eventBus.PublishEventRelock(events.EventDataRoundState{})
|
||||
require.NoError(t, err)
|
||||
err = eventBus.PublishEventLock(EventDataRoundState{})
|
||||
err = eventBus.PublishEventLock(events.EventDataRoundState{})
|
||||
require.NoError(t, err)
|
||||
err = eventBus.PublishEventValidatorSetUpdates(EventDataValidatorSetUpdates{})
|
||||
err = eventBus.PublishEventValidatorSetUpdates(events.EventDataValidatorSetUpdates{})
|
||||
require.NoError(t, err)
|
||||
err = eventBus.PublishEventBlockSyncStatus(EventDataBlockSyncStatus{})
|
||||
err = eventBus.PublishEventBlockSyncStatus(events.EventDataBlockSyncStatus{})
|
||||
require.NoError(t, err)
|
||||
err = eventBus.PublishEventStateSyncStatus(EventDataStateSyncStatus{})
|
||||
err = eventBus.PublishEventStateSyncStatus(events.EventDataStateSyncStatus{})
|
||||
require.NoError(t, err)
|
||||
|
||||
select {
|
||||
@@ -418,7 +423,7 @@ func benchmarkEventBus(numClients int, randQueries bool, randEvents bool, b *tes
|
||||
// for random* functions
|
||||
mrand.Seed(time.Now().Unix())
|
||||
|
||||
eventBus := NewEventBusWithBufferCapacity(0) // set buffer capacity to 0 so we are not testing cache
|
||||
eventBus := events.NewEventBusWithBufferCapacity(0) // set buffer capacity to 0 so we are not testing cache
|
||||
err := eventBus.Start()
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
@@ -430,7 +435,7 @@ func benchmarkEventBus(numClients int, randQueries bool, randEvents bool, b *tes
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
q := EventQueryNewBlock
|
||||
q := events.EventQueryNewBlock
|
||||
|
||||
for i := 0; i < numClients; i++ {
|
||||
if randQueries {
|
||||
@@ -451,7 +456,7 @@ func benchmarkEventBus(numClients int, randQueries bool, randEvents bool, b *tes
|
||||
}()
|
||||
}
|
||||
|
||||
eventValue := EventNewBlockValue
|
||||
eventValue := events.EventNewBlockValue
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
@@ -460,50 +465,50 @@ func benchmarkEventBus(numClients int, randQueries bool, randEvents bool, b *tes
|
||||
eventValue = randEventValue()
|
||||
}
|
||||
|
||||
err := eventBus.Publish(eventValue, EventDataString("Gamora"))
|
||||
err := eventBus.Publish(eventValue, events.EventDataString("Gamora"))
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var events = []string{
|
||||
EventNewBlockValue,
|
||||
EventNewBlockHeaderValue,
|
||||
EventNewRoundValue,
|
||||
EventNewRoundStepValue,
|
||||
EventTimeoutProposeValue,
|
||||
EventCompleteProposalValue,
|
||||
EventPolkaValue,
|
||||
EventUnlockValue,
|
||||
EventLockValue,
|
||||
EventRelockValue,
|
||||
EventTimeoutWaitValue,
|
||||
EventVoteValue,
|
||||
EventBlockSyncStatusValue,
|
||||
EventStateSyncStatusValue,
|
||||
var allEvents = []string{
|
||||
events.EventNewBlockValue,
|
||||
events.EventNewBlockHeaderValue,
|
||||
events.EventNewRoundValue,
|
||||
events.EventNewRoundStepValue,
|
||||
events.EventTimeoutProposeValue,
|
||||
events.EventCompleteProposalValue,
|
||||
events.EventPolkaValue,
|
||||
events.EventUnlockValue,
|
||||
events.EventLockValue,
|
||||
events.EventRelockValue,
|
||||
events.EventTimeoutWaitValue,
|
||||
events.EventVoteValue,
|
||||
events.EventBlockSyncStatusValue,
|
||||
events.EventStateSyncStatusValue,
|
||||
}
|
||||
|
||||
func randEventValue() string {
|
||||
|
||||
return events[mrand.Intn(len(events))]
|
||||
return allEvents[mrand.Intn(len(allEvents))]
|
||||
}
|
||||
|
||||
var queries = []tmpubsub.Query{
|
||||
EventQueryNewBlock,
|
||||
EventQueryNewBlockHeader,
|
||||
EventQueryNewRound,
|
||||
EventQueryNewRoundStep,
|
||||
EventQueryTimeoutPropose,
|
||||
EventQueryCompleteProposal,
|
||||
EventQueryPolka,
|
||||
EventQueryUnlock,
|
||||
EventQueryLock,
|
||||
EventQueryRelock,
|
||||
EventQueryTimeoutWait,
|
||||
EventQueryVote,
|
||||
EventQueryBlockSyncStatus,
|
||||
EventQueryStateSyncStatus,
|
||||
events.EventQueryNewBlock,
|
||||
events.EventQueryNewBlockHeader,
|
||||
events.EventQueryNewRound,
|
||||
events.EventQueryNewRoundStep,
|
||||
events.EventQueryTimeoutPropose,
|
||||
events.EventQueryCompleteProposal,
|
||||
events.EventQueryPolka,
|
||||
events.EventQueryUnlock,
|
||||
events.EventQueryLock,
|
||||
events.EventQueryRelock,
|
||||
events.EventQueryTimeoutWait,
|
||||
events.EventQueryVote,
|
||||
events.EventQueryBlockSyncStatus,
|
||||
events.EventQueryStateSyncStatus,
|
||||
}
|
||||
|
||||
func randQuery() tmpubsub.Query {
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package events
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -8,6 +8,12 @@ import (
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
tmpubsub "github.com/tendermint/tendermint/libs/pubsub"
|
||||
tmquery "github.com/tendermint/tendermint/libs/pubsub/query"
|
||||
|
||||
"github.com/tendermint/tendermint/pkg/block"
|
||||
"github.com/tendermint/tendermint/pkg/consensus"
|
||||
"github.com/tendermint/tendermint/pkg/evidence"
|
||||
"github.com/tendermint/tendermint/pkg/mempool"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
)
|
||||
|
||||
// Reserved event types (alphabetically sorted).
|
||||
@@ -112,15 +118,15 @@ func init() {
|
||||
// but some (an input to a call tx or a receive) are more exotic
|
||||
|
||||
type EventDataNewBlock struct {
|
||||
Block *Block `json:"block"`
|
||||
BlockID BlockID `json:"block_id"`
|
||||
Block *block.Block `json:"block"`
|
||||
BlockID meta.BlockID `json:"block_id"`
|
||||
|
||||
ResultBeginBlock abci.ResponseBeginBlock `json:"result_begin_block"`
|
||||
ResultEndBlock abci.ResponseEndBlock `json:"result_end_block"`
|
||||
}
|
||||
|
||||
type EventDataNewBlockHeader struct {
|
||||
Header Header `json:"header"`
|
||||
Header meta.Header `json:"header"`
|
||||
|
||||
NumTxs int64 `json:"num_txs"` // Number of txs in a block
|
||||
ResultBeginBlock abci.ResponseBeginBlock `json:"result_begin_block"`
|
||||
@@ -128,7 +134,7 @@ type EventDataNewBlockHeader struct {
|
||||
}
|
||||
|
||||
type EventDataNewEvidence struct {
|
||||
Evidence Evidence `json:"evidence"`
|
||||
Evidence evidence.Evidence `json:"evidence"`
|
||||
|
||||
Height int64 `json:"height"`
|
||||
}
|
||||
@@ -146,8 +152,8 @@ type EventDataRoundState struct {
|
||||
}
|
||||
|
||||
type ValidatorInfo struct {
|
||||
Address Address `json:"address"`
|
||||
Index int32 `json:"index"`
|
||||
Address consensus.Address `json:"address"`
|
||||
Index int32 `json:"index"`
|
||||
}
|
||||
|
||||
type EventDataNewRound struct {
|
||||
@@ -163,17 +169,17 @@ type EventDataCompleteProposal struct {
|
||||
Round int32 `json:"round"`
|
||||
Step string `json:"step"`
|
||||
|
||||
BlockID BlockID `json:"block_id"`
|
||||
BlockID meta.BlockID `json:"block_id"`
|
||||
}
|
||||
|
||||
type EventDataVote struct {
|
||||
Vote *Vote
|
||||
Vote *consensus.Vote
|
||||
}
|
||||
|
||||
type EventDataString string
|
||||
|
||||
type EventDataValidatorSetUpdates struct {
|
||||
ValidatorUpdates []*Validator `json:"validator_updates"`
|
||||
ValidatorUpdates []*consensus.Validator `json:"validator_updates"`
|
||||
}
|
||||
|
||||
// EventDataBlockSyncStatus shows the fastsync status and the
|
||||
@@ -231,7 +237,7 @@ var (
|
||||
EventQueryStateSyncStatus = QueryForEvent(EventStateSyncStatusValue)
|
||||
)
|
||||
|
||||
func EventQueryTxFor(tx Tx) tmpubsub.Query {
|
||||
func EventQueryTxFor(tx mempool.Tx) tmpubsub.Query {
|
||||
return tmquery.MustParse(fmt.Sprintf("%s='%s' AND %s='%X'", EventTypeKey, EventTxValue, TxHashKey, tx.Hash()))
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
package types
|
||||
package events
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/tendermint/tendermint/pkg/mempool"
|
||||
)
|
||||
|
||||
func TestQueryTxFor(t *testing.T) {
|
||||
tx := Tx("foo")
|
||||
tx := mempool.Tx("foo")
|
||||
assert.Equal(t,
|
||||
fmt.Sprintf("tm.event='Tx' AND tx.hash='%X'", tx.Hash()),
|
||||
EventQueryTxFor(tx).String(),
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package evidence
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -15,6 +15,9 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
tmrand "github.com/tendermint/tendermint/libs/rand"
|
||||
"github.com/tendermint/tendermint/pkg/consensus"
|
||||
"github.com/tendermint/tendermint/pkg/light"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -30,12 +33,49 @@ type Evidence interface {
|
||||
ValidateBasic() error // basic consistency check
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// EvidenceList is a list of Evidence. Evidences is not a word.
|
||||
type EvidenceList []Evidence
|
||||
|
||||
// Hash returns the simple merkle root hash of the EvidenceList.
|
||||
func (evl EvidenceList) Hash() []byte {
|
||||
// These allocations are required because Evidence is not of type Bytes, and
|
||||
// golang slices can't be typed cast. This shouldn't be a performance problem since
|
||||
// the Evidence size is capped.
|
||||
evidenceBzs := make([][]byte, len(evl))
|
||||
for i := 0; i < len(evl); i++ {
|
||||
// TODO: We should change this to the hash. Using bytes contains some unexported data that
|
||||
// may cause different hashes
|
||||
evidenceBzs[i] = evl[i].Bytes()
|
||||
}
|
||||
return merkle.HashFromByteSlices(evidenceBzs)
|
||||
}
|
||||
|
||||
func (evl EvidenceList) String() string {
|
||||
s := ""
|
||||
for _, e := range evl {
|
||||
s += fmt.Sprintf("%s\t\t", e)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Has returns true if the evidence is in the EvidenceList.
|
||||
func (evl EvidenceList) Has(evidence Evidence) bool {
|
||||
for _, ev := range evl {
|
||||
if bytes.Equal(evidence.Hash(), ev.Hash()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
// DuplicateVoteEvidence contains evidence of a single validator signing two conflicting votes.
|
||||
type DuplicateVoteEvidence struct {
|
||||
VoteA *Vote `json:"vote_a"`
|
||||
VoteB *Vote `json:"vote_b"`
|
||||
VoteA *consensus.Vote `json:"vote_a"`
|
||||
VoteB *consensus.Vote `json:"vote_b"`
|
||||
|
||||
// abci specific information
|
||||
TotalVotingPower int64
|
||||
@@ -47,8 +87,8 @@ var _ Evidence = &DuplicateVoteEvidence{}
|
||||
|
||||
// NewDuplicateVoteEvidence creates DuplicateVoteEvidence with right ordering given
|
||||
// two conflicting votes. If one of the votes is nil, evidence returned is nil as well
|
||||
func NewDuplicateVoteEvidence(vote1, vote2 *Vote, blockTime time.Time, valSet *ValidatorSet) *DuplicateVoteEvidence {
|
||||
var voteA, voteB *Vote
|
||||
func NewDuplicateVoteEvidence(vote1, vote2 *consensus.Vote, blockTime time.Time, valSet *consensus.ValidatorSet) *DuplicateVoteEvidence {
|
||||
var voteA, voteB *consensus.Vote
|
||||
if vote1 == nil || vote2 == nil || valSet == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -143,8 +183,8 @@ func (dve *DuplicateVoteEvidence) ValidateBasic() error {
|
||||
// ValidateABCI validates the ABCI component of the evidence by checking the
|
||||
// timestamp, validator power and total voting power.
|
||||
func (dve *DuplicateVoteEvidence) ValidateABCI(
|
||||
val *Validator,
|
||||
valSet *ValidatorSet,
|
||||
val *consensus.Validator,
|
||||
valSet *consensus.ValidatorSet,
|
||||
evidenceTime time.Time,
|
||||
) error {
|
||||
|
||||
@@ -169,8 +209,8 @@ func (dve *DuplicateVoteEvidence) ValidateABCI(
|
||||
// GenerateABCI populates the ABCI component of the evidence. This includes the
|
||||
// validator power, timestamp and total voting power.
|
||||
func (dve *DuplicateVoteEvidence) GenerateABCI(
|
||||
val *Validator,
|
||||
valSet *ValidatorSet,
|
||||
val *consensus.Validator,
|
||||
valSet *consensus.ValidatorSet,
|
||||
evidenceTime time.Time,
|
||||
) {
|
||||
dve.ValidatorPower = val.VotingPower
|
||||
@@ -198,12 +238,12 @@ func DuplicateVoteEvidenceFromProto(pb *tmproto.DuplicateVoteEvidence) (*Duplica
|
||||
return nil, errors.New("nil duplicate vote evidence")
|
||||
}
|
||||
|
||||
vA, err := VoteFromProto(pb.VoteA)
|
||||
vA, err := consensus.VoteFromProto(pb.VoteA)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vB, err := VoteFromProto(pb.VoteB)
|
||||
vB, err := consensus.VoteFromProto(pb.VoteB)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -230,13 +270,13 @@ func DuplicateVoteEvidenceFromProto(pb *tmproto.DuplicateVoteEvidence) (*Duplica
|
||||
// CommonHeight is used to indicate the type of attack. If the height is different to the conflicting block
|
||||
// height, then nodes will treat this as of the Lunatic form, else it is of the Equivocation form.
|
||||
type LightClientAttackEvidence struct {
|
||||
ConflictingBlock *LightBlock
|
||||
ConflictingBlock *light.LightBlock
|
||||
CommonHeight int64
|
||||
|
||||
// abci specific information
|
||||
ByzantineValidators []*Validator // validators in the validator set that misbehaved in creating the conflicting block
|
||||
TotalVotingPower int64 // total voting power of the validator set at the common height
|
||||
Timestamp time.Time // timestamp of the block at the common height
|
||||
ByzantineValidators []*consensus.Validator // validators in the validator set that misbehaved in creating the conflicting block
|
||||
TotalVotingPower int64 // total voting power of the validator set at the common height
|
||||
Timestamp time.Time // timestamp of the block at the common height
|
||||
}
|
||||
|
||||
var _ Evidence = &LightClientAttackEvidence{}
|
||||
@@ -247,7 +287,7 @@ func (l *LightClientAttackEvidence) ABCI() []abci.Evidence {
|
||||
for idx, val := range l.ByzantineValidators {
|
||||
abciEv[idx] = abci.Evidence{
|
||||
Type: abci.EvidenceType_LIGHT_CLIENT_ATTACK,
|
||||
Validator: TM2PB.Validator(val),
|
||||
Validator: consensus.TM2PB.Validator(val),
|
||||
Height: l.Height(),
|
||||
Time: l.Timestamp,
|
||||
TotalVotingPower: l.TotalVotingPower,
|
||||
@@ -272,9 +312,9 @@ func (l *LightClientAttackEvidence) Bytes() []byte {
|
||||
// GetByzantineValidators finds out what style of attack LightClientAttackEvidence was and then works out who
|
||||
// the malicious validators were and returns them. This is used both for forming the ByzantineValidators
|
||||
// field and for validating that it is correct. Validators are ordered based on validator power
|
||||
func (l *LightClientAttackEvidence) GetByzantineValidators(commonVals *ValidatorSet,
|
||||
trusted *SignedHeader) []*Validator {
|
||||
var validators []*Validator
|
||||
func (l *LightClientAttackEvidence) GetByzantineValidators(commonVals *consensus.ValidatorSet,
|
||||
trusted *light.SignedHeader) []*consensus.Validator {
|
||||
var validators []*consensus.Validator
|
||||
// First check if the header is invalid. This means that it is a lunatic attack and therefore we take the
|
||||
// validators who are in the commonVals and voted for the lunatic header
|
||||
if l.ConflictingHeaderIsInvalid(trusted.Header) {
|
||||
@@ -290,7 +330,7 @@ func (l *LightClientAttackEvidence) GetByzantineValidators(commonVals *Validator
|
||||
}
|
||||
validators = append(validators, val)
|
||||
}
|
||||
sort.Sort(ValidatorsByVotingPower(validators))
|
||||
sort.Sort(consensus.ValidatorsByVotingPower(validators))
|
||||
return validators
|
||||
} else if trusted.Commit.Round == l.ConflictingBlock.Commit.Round {
|
||||
// This is an equivocation attack as both commits are in the same round. We then find the validators
|
||||
@@ -311,7 +351,7 @@ func (l *LightClientAttackEvidence) GetByzantineValidators(commonVals *Validator
|
||||
_, val := l.ConflictingBlock.ValidatorSet.GetByAddress(sigA.ValidatorAddress)
|
||||
validators = append(validators, val)
|
||||
}
|
||||
sort.Sort(ValidatorsByVotingPower(validators))
|
||||
sort.Sort(consensus.ValidatorsByVotingPower(validators))
|
||||
return validators
|
||||
}
|
||||
// if the rounds are different then this is an amnesia attack. Unfortunately, given the nature of the attack,
|
||||
@@ -324,7 +364,7 @@ func (l *LightClientAttackEvidence) GetByzantineValidators(commonVals *Validator
|
||||
// to determine whether the conflicting header was the product of a valid state transition
|
||||
// or not. If it is then all the deterministic fields of the header should be the same.
|
||||
// If not, it is an invalid header and constitutes a lunatic attack.
|
||||
func (l *LightClientAttackEvidence) ConflictingHeaderIsInvalid(trustedHeader *Header) bool {
|
||||
func (l *LightClientAttackEvidence) ConflictingHeaderIsInvalid(trustedHeader *meta.Header) bool {
|
||||
return !bytes.Equal(trustedHeader.ValidatorsHash, l.ConflictingBlock.ValidatorsHash) ||
|
||||
!bytes.Equal(trustedHeader.NextValidatorsHash, l.ConflictingBlock.NextValidatorsHash) ||
|
||||
!bytes.Equal(trustedHeader.ConsensusHash, l.ConflictingBlock.ConsensusHash) ||
|
||||
@@ -413,8 +453,8 @@ func (l *LightClientAttackEvidence) ValidateBasic() error {
|
||||
// components are validated separately because they can be re generated if
|
||||
// invalid.
|
||||
func (l *LightClientAttackEvidence) ValidateABCI(
|
||||
commonVals *ValidatorSet,
|
||||
trustedHeader *SignedHeader,
|
||||
commonVals *consensus.ValidatorSet,
|
||||
trustedHeader *light.SignedHeader,
|
||||
evidenceTime time.Time,
|
||||
) error {
|
||||
|
||||
@@ -469,8 +509,8 @@ func (l *LightClientAttackEvidence) ValidateABCI(
|
||||
// GenerateABCI populates the ABCI component of the evidence: the timestamp,
|
||||
// total voting power and byantine validators
|
||||
func (l *LightClientAttackEvidence) GenerateABCI(
|
||||
commonVals *ValidatorSet,
|
||||
trustedHeader *SignedHeader,
|
||||
commonVals *consensus.ValidatorSet,
|
||||
trustedHeader *light.SignedHeader,
|
||||
evidenceTime time.Time,
|
||||
) {
|
||||
l.Timestamp = evidenceTime
|
||||
@@ -509,14 +549,14 @@ func LightClientAttackEvidenceFromProto(lpb *tmproto.LightClientAttackEvidence)
|
||||
return nil, errors.New("empty light client attack evidence")
|
||||
}
|
||||
|
||||
conflictingBlock, err := LightBlockFromProto(lpb.ConflictingBlock)
|
||||
conflictingBlock, err := light.LightBlockFromProto(lpb.ConflictingBlock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
byzVals := make([]*Validator, len(lpb.ByzantineValidators))
|
||||
byzVals := make([]*consensus.Validator, len(lpb.ByzantineValidators))
|
||||
for idx, valpb := range lpb.ByzantineValidators {
|
||||
val, err := ValidatorFromProto(valpb)
|
||||
val, err := consensus.ValidatorFromProto(valpb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -534,43 +574,6 @@ func LightClientAttackEvidenceFromProto(lpb *tmproto.LightClientAttackEvidence)
|
||||
return l, l.ValidateBasic()
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
|
||||
// EvidenceList is a list of Evidence. Evidences is not a word.
|
||||
type EvidenceList []Evidence
|
||||
|
||||
// Hash returns the simple merkle root hash of the EvidenceList.
|
||||
func (evl EvidenceList) Hash() []byte {
|
||||
// These allocations are required because Evidence is not of type Bytes, and
|
||||
// golang slices can't be typed cast. This shouldn't be a performance problem since
|
||||
// the Evidence size is capped.
|
||||
evidenceBzs := make([][]byte, len(evl))
|
||||
for i := 0; i < len(evl); i++ {
|
||||
// TODO: We should change this to the hash. Using bytes contains some unexported data that
|
||||
// may cause different hashes
|
||||
evidenceBzs[i] = evl[i].Bytes()
|
||||
}
|
||||
return merkle.HashFromByteSlices(evidenceBzs)
|
||||
}
|
||||
|
||||
func (evl EvidenceList) String() string {
|
||||
s := ""
|
||||
for _, e := range evl {
|
||||
s += fmt.Sprintf("%s\t\t", e)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Has returns true if the evidence is in the EvidenceList.
|
||||
func (evl EvidenceList) Has(evidence Evidence) bool {
|
||||
for _, ev := range evl {
|
||||
if bytes.Equal(evidence.Hash(), ev.Hash()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//------------------------------------------ PROTO --------------------------------------
|
||||
|
||||
// EvidenceToProto is a generalized function for encoding evidence that conforms to the
|
||||
@@ -667,15 +670,15 @@ func (err *ErrEvidenceOverflow) Error() string {
|
||||
|
||||
// assumes the round to be 0 and the validator index to be 0
|
||||
func NewMockDuplicateVoteEvidence(height int64, time time.Time, chainID string) *DuplicateVoteEvidence {
|
||||
val := NewMockPV()
|
||||
val := consensus.NewMockPV()
|
||||
return NewMockDuplicateVoteEvidenceWithValidator(height, time, val, chainID)
|
||||
}
|
||||
|
||||
// assumes voting power to be 10 and validator to be the only one in the set
|
||||
func NewMockDuplicateVoteEvidenceWithValidator(height int64, time time.Time,
|
||||
pv PrivValidator, chainID string) *DuplicateVoteEvidence {
|
||||
pv consensus.PrivValidator, chainID string) *DuplicateVoteEvidence {
|
||||
pubKey, _ := pv.GetPubKey(context.Background())
|
||||
val := NewValidator(pubKey, 10)
|
||||
val := consensus.NewValidator(pubKey, 10)
|
||||
voteA := makeMockVote(height, 0, 0, pubKey.Address(), randBlockID(), time)
|
||||
vA := voteA.ToProto()
|
||||
_ = pv.SignVote(context.Background(), chainID, vA)
|
||||
@@ -684,12 +687,12 @@ func NewMockDuplicateVoteEvidenceWithValidator(height int64, time time.Time,
|
||||
vB := voteB.ToProto()
|
||||
_ = pv.SignVote(context.Background(), chainID, vB)
|
||||
voteB.Signature = vB.Signature
|
||||
return NewDuplicateVoteEvidence(voteA, voteB, time, NewValidatorSet([]*Validator{val}))
|
||||
return NewDuplicateVoteEvidence(voteA, voteB, time, consensus.NewValidatorSet([]*consensus.Validator{val}))
|
||||
}
|
||||
|
||||
func makeMockVote(height int64, round, index int32, addr Address,
|
||||
blockID BlockID, time time.Time) *Vote {
|
||||
return &Vote{
|
||||
func makeMockVote(height int64, round, index int32, addr consensus.Address,
|
||||
blockID meta.BlockID, time time.Time) *consensus.Vote {
|
||||
return &consensus.Vote{
|
||||
Type: tmproto.SignedMsgType(2),
|
||||
Height: height,
|
||||
Round: round,
|
||||
@@ -700,10 +703,10 @@ func makeMockVote(height int64, round, index int32, addr Address,
|
||||
}
|
||||
}
|
||||
|
||||
func randBlockID() BlockID {
|
||||
return BlockID{
|
||||
func randBlockID() meta.BlockID {
|
||||
return meta.BlockID{
|
||||
Hash: tmrand.Bytes(tmhash.Size),
|
||||
PartSetHeader: PartSetHeader{
|
||||
PartSetHeader: meta.PartSetHeader{
|
||||
Total: 1,
|
||||
Hash: tmrand.Bytes(tmhash.Size),
|
||||
},
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package evidence_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -14,7 +14,12 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
test "github.com/tendermint/tendermint/internal/test/factory"
|
||||
tmrand "github.com/tendermint/tendermint/libs/rand"
|
||||
"github.com/tendermint/tendermint/pkg/consensus"
|
||||
"github.com/tendermint/tendermint/pkg/evidence"
|
||||
"github.com/tendermint/tendermint/pkg/light"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
"github.com/tendermint/tendermint/version"
|
||||
)
|
||||
@@ -23,19 +28,19 @@ var defaultVoteTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
func TestEvidenceList(t *testing.T) {
|
||||
ev := randomDuplicateVoteEvidence(t)
|
||||
evl := EvidenceList([]Evidence{ev})
|
||||
evl := evidence.EvidenceList([]evidence.Evidence{ev})
|
||||
|
||||
assert.NotNil(t, evl.Hash())
|
||||
assert.True(t, evl.Has(ev))
|
||||
assert.False(t, evl.Has(&DuplicateVoteEvidence{}))
|
||||
assert.False(t, evl.Has(&evidence.DuplicateVoteEvidence{}))
|
||||
}
|
||||
|
||||
func randomDuplicateVoteEvidence(t *testing.T) *DuplicateVoteEvidence {
|
||||
val := NewMockPV()
|
||||
blockID := makeBlockID([]byte("blockhash"), 1000, []byte("partshash"))
|
||||
blockID2 := makeBlockID([]byte("blockhash2"), 1000, []byte("partshash"))
|
||||
func randomDuplicateVoteEvidence(t *testing.T) *evidence.DuplicateVoteEvidence {
|
||||
val := consensus.NewMockPV()
|
||||
blockID := test.MakeBlockIDWithHash([]byte("blockhash"))
|
||||
blockID2 := test.MakeBlockIDWithHash([]byte("blockhash2"))
|
||||
const chainID = "mychain"
|
||||
return &DuplicateVoteEvidence{
|
||||
return &evidence.DuplicateVoteEvidence{
|
||||
VoteA: makeVote(t, val, chainID, 0, 10, 2, 1, blockID, defaultVoteTime),
|
||||
VoteB: makeVote(t, val, chainID, 0, 10, 2, 1, blockID2, defaultVoteTime.Add(1*time.Minute)),
|
||||
TotalVotingPower: 30,
|
||||
@@ -46,34 +51,34 @@ func randomDuplicateVoteEvidence(t *testing.T) *DuplicateVoteEvidence {
|
||||
|
||||
func TestDuplicateVoteEvidence(t *testing.T) {
|
||||
const height = int64(13)
|
||||
ev := NewMockDuplicateVoteEvidence(height, time.Now(), "mock-chain-id")
|
||||
ev := evidence.NewMockDuplicateVoteEvidence(height, time.Now(), "mock-chain-id")
|
||||
assert.Equal(t, ev.Hash(), tmhash.Sum(ev.Bytes()))
|
||||
assert.NotNil(t, ev.String())
|
||||
assert.Equal(t, ev.Height(), height)
|
||||
}
|
||||
|
||||
func TestDuplicateVoteEvidenceValidation(t *testing.T) {
|
||||
val := NewMockPV()
|
||||
blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash")))
|
||||
blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt32, tmhash.Sum([]byte("partshash")))
|
||||
val := consensus.NewMockPV()
|
||||
blockID := test.MakeBlockIDWithHash(tmhash.Sum([]byte("blockhash")))
|
||||
blockID2 := test.MakeBlockIDWithHash(tmhash.Sum([]byte("blockhash2")))
|
||||
const chainID = "mychain"
|
||||
|
||||
testCases := []struct {
|
||||
testName string
|
||||
malleateEvidence func(*DuplicateVoteEvidence)
|
||||
malleateEvidence func(*evidence.DuplicateVoteEvidence)
|
||||
expectErr bool
|
||||
}{
|
||||
{"Good DuplicateVoteEvidence", func(ev *DuplicateVoteEvidence) {}, false},
|
||||
{"Nil vote A", func(ev *DuplicateVoteEvidence) { ev.VoteA = nil }, true},
|
||||
{"Nil vote B", func(ev *DuplicateVoteEvidence) { ev.VoteB = nil }, true},
|
||||
{"Nil votes", func(ev *DuplicateVoteEvidence) {
|
||||
{"Good DuplicateVoteEvidence", func(ev *evidence.DuplicateVoteEvidence) {}, false},
|
||||
{"Nil vote A", func(ev *evidence.DuplicateVoteEvidence) { ev.VoteA = nil }, true},
|
||||
{"Nil vote B", func(ev *evidence.DuplicateVoteEvidence) { ev.VoteB = nil }, true},
|
||||
{"Nil votes", func(ev *evidence.DuplicateVoteEvidence) {
|
||||
ev.VoteA = nil
|
||||
ev.VoteB = nil
|
||||
}, true},
|
||||
{"Invalid vote type", func(ev *DuplicateVoteEvidence) {
|
||||
{"Invalid vote type", func(ev *evidence.DuplicateVoteEvidence) {
|
||||
ev.VoteA = makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, math.MaxInt32, 0, blockID2, defaultVoteTime)
|
||||
}, true},
|
||||
{"Invalid vote order", func(ev *DuplicateVoteEvidence) {
|
||||
{"Invalid vote order", func(ev *evidence.DuplicateVoteEvidence) {
|
||||
swap := ev.VoteA.Copy()
|
||||
ev.VoteA = ev.VoteB.Copy()
|
||||
ev.VoteB = swap
|
||||
@@ -84,8 +89,8 @@ func TestDuplicateVoteEvidenceValidation(t *testing.T) {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
vote1 := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, math.MaxInt32, 0x02, blockID, defaultVoteTime)
|
||||
vote2 := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, math.MaxInt32, 0x02, blockID2, defaultVoteTime)
|
||||
valSet := NewValidatorSet([]*Validator{val.ExtractIntoValidator(10)})
|
||||
ev := NewDuplicateVoteEvidence(vote1, vote2, defaultVoteTime, valSet)
|
||||
valSet := consensus.NewValidatorSet([]*consensus.Validator{val.ExtractIntoValidator(10)})
|
||||
ev := evidence.NewDuplicateVoteEvidence(vote1, vote2, defaultVoteTime, valSet)
|
||||
tc.malleateEvidence(ev)
|
||||
assert.Equal(t, tc.expectErr, ev.ValidateBasic() != nil, "Validate Basic had an unexpected result")
|
||||
})
|
||||
@@ -96,15 +101,15 @@ func TestLightClientAttackEvidenceBasic(t *testing.T) {
|
||||
height := int64(5)
|
||||
commonHeight := height - 1
|
||||
nValidators := 10
|
||||
voteSet, valSet, privVals := randVoteSet(height, 1, tmproto.PrecommitType, nValidators, 1)
|
||||
voteSet, valSet, privVals := test.RandVoteSet(height, 1, tmproto.PrecommitType, nValidators, 1)
|
||||
header := makeHeaderRandom()
|
||||
header.Height = height
|
||||
blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash")))
|
||||
commit, err := makeCommit(blockID, height, 1, voteSet, privVals, defaultVoteTime)
|
||||
blockID := test.MakeBlockIDWithHash(tmhash.Sum([]byte("blockhash")))
|
||||
commit, err := test.MakeCommit(blockID, height, 1, voteSet, privVals, defaultVoteTime)
|
||||
require.NoError(t, err)
|
||||
lcae := &LightClientAttackEvidence{
|
||||
ConflictingBlock: &LightBlock{
|
||||
SignedHeader: &SignedHeader{
|
||||
lcae := &evidence.LightClientAttackEvidence{
|
||||
ConflictingBlock: &light.LightBlock{
|
||||
SignedHeader: &light.SignedHeader{
|
||||
Header: header,
|
||||
Commit: commit,
|
||||
},
|
||||
@@ -123,18 +128,18 @@ func TestLightClientAttackEvidenceBasic(t *testing.T) {
|
||||
// maleate evidence to test hash uniqueness
|
||||
testCases := []struct {
|
||||
testName string
|
||||
malleateEvidence func(*LightClientAttackEvidence)
|
||||
malleateEvidence func(*evidence.LightClientAttackEvidence)
|
||||
}{
|
||||
{"Different header", func(ev *LightClientAttackEvidence) { ev.ConflictingBlock.Header = makeHeaderRandom() }},
|
||||
{"Different common height", func(ev *LightClientAttackEvidence) {
|
||||
{"Different header", func(ev *evidence.LightClientAttackEvidence) { ev.ConflictingBlock.Header = makeHeaderRandom() }},
|
||||
{"Different common height", func(ev *evidence.LightClientAttackEvidence) {
|
||||
ev.CommonHeight = height + 1
|
||||
}},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
lcae := &LightClientAttackEvidence{
|
||||
ConflictingBlock: &LightBlock{
|
||||
SignedHeader: &SignedHeader{
|
||||
lcae := &evidence.LightClientAttackEvidence{
|
||||
ConflictingBlock: &light.LightBlock{
|
||||
SignedHeader: &light.SignedHeader{
|
||||
Header: header,
|
||||
Commit: commit,
|
||||
},
|
||||
@@ -155,16 +160,16 @@ func TestLightClientAttackEvidenceValidation(t *testing.T) {
|
||||
height := int64(5)
|
||||
commonHeight := height - 1
|
||||
nValidators := 10
|
||||
voteSet, valSet, privVals := randVoteSet(height, 1, tmproto.PrecommitType, nValidators, 1)
|
||||
voteSet, valSet, privVals := test.RandVoteSet(height, 1, tmproto.PrecommitType, nValidators, 1)
|
||||
header := makeHeaderRandom()
|
||||
header.Height = height
|
||||
header.ValidatorsHash = valSet.Hash()
|
||||
blockID := makeBlockID(header.Hash(), math.MaxInt32, tmhash.Sum([]byte("partshash")))
|
||||
commit, err := makeCommit(blockID, height, 1, voteSet, privVals, time.Now())
|
||||
blockID := test.MakeBlockIDWithHash(header.Hash())
|
||||
commit, err := test.MakeCommit(blockID, height, 1, voteSet, privVals, time.Now())
|
||||
require.NoError(t, err)
|
||||
lcae := &LightClientAttackEvidence{
|
||||
ConflictingBlock: &LightBlock{
|
||||
SignedHeader: &SignedHeader{
|
||||
lcae := &evidence.LightClientAttackEvidence{
|
||||
ConflictingBlock: &light.LightBlock{
|
||||
SignedHeader: &light.SignedHeader{
|
||||
Header: header,
|
||||
Commit: commit,
|
||||
},
|
||||
@@ -179,32 +184,32 @@ func TestLightClientAttackEvidenceValidation(t *testing.T) {
|
||||
|
||||
testCases := []struct {
|
||||
testName string
|
||||
malleateEvidence func(*LightClientAttackEvidence)
|
||||
malleateEvidence func(*evidence.LightClientAttackEvidence)
|
||||
expectErr bool
|
||||
}{
|
||||
{"Good LightClientAttackEvidence", func(ev *LightClientAttackEvidence) {}, false},
|
||||
{"Negative height", func(ev *LightClientAttackEvidence) { ev.CommonHeight = -10 }, true},
|
||||
{"Height is greater than divergent block", func(ev *LightClientAttackEvidence) {
|
||||
{"Good LightClientAttackEvidence", func(ev *evidence.LightClientAttackEvidence) {}, false},
|
||||
{"Negative height", func(ev *evidence.LightClientAttackEvidence) { ev.CommonHeight = -10 }, true},
|
||||
{"Height is greater than divergent block", func(ev *evidence.LightClientAttackEvidence) {
|
||||
ev.CommonHeight = height + 1
|
||||
}, true},
|
||||
{"Height is equal to the divergent block", func(ev *LightClientAttackEvidence) {
|
||||
{"Height is equal to the divergent block", func(ev *evidence.LightClientAttackEvidence) {
|
||||
ev.CommonHeight = height
|
||||
}, false},
|
||||
{"Nil conflicting header", func(ev *LightClientAttackEvidence) { ev.ConflictingBlock.Header = nil }, true},
|
||||
{"Nil conflicting blocl", func(ev *LightClientAttackEvidence) { ev.ConflictingBlock = nil }, true},
|
||||
{"Nil validator set", func(ev *LightClientAttackEvidence) {
|
||||
ev.ConflictingBlock.ValidatorSet = &ValidatorSet{}
|
||||
{"Nil conflicting header", func(ev *evidence.LightClientAttackEvidence) { ev.ConflictingBlock.Header = nil }, true},
|
||||
{"Nil conflicting blocl", func(ev *evidence.LightClientAttackEvidence) { ev.ConflictingBlock = nil }, true},
|
||||
{"Nil validator set", func(ev *evidence.LightClientAttackEvidence) {
|
||||
ev.ConflictingBlock.ValidatorSet = &consensus.ValidatorSet{}
|
||||
}, true},
|
||||
{"Negative total voting power", func(ev *LightClientAttackEvidence) {
|
||||
{"Negative total voting power", func(ev *evidence.LightClientAttackEvidence) {
|
||||
ev.TotalVotingPower = -1
|
||||
}, true},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
lcae := &LightClientAttackEvidence{
|
||||
ConflictingBlock: &LightBlock{
|
||||
SignedHeader: &SignedHeader{
|
||||
lcae := &evidence.LightClientAttackEvidence{
|
||||
ConflictingBlock: &light.LightBlock{
|
||||
SignedHeader: &light.SignedHeader{
|
||||
Header: header,
|
||||
Commit: commit,
|
||||
},
|
||||
@@ -227,16 +232,16 @@ func TestLightClientAttackEvidenceValidation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMockEvidenceValidateBasic(t *testing.T) {
|
||||
goodEvidence := NewMockDuplicateVoteEvidence(int64(1), time.Now(), "mock-chain-id")
|
||||
goodEvidence := evidence.NewMockDuplicateVoteEvidence(int64(1), time.Now(), "mock-chain-id")
|
||||
assert.Nil(t, goodEvidence.ValidateBasic())
|
||||
}
|
||||
|
||||
func makeVote(
|
||||
t *testing.T, val PrivValidator, chainID string, valIndex int32, height int64, round int32, step int, blockID BlockID,
|
||||
time time.Time) *Vote {
|
||||
t *testing.T, val consensus.PrivValidator, chainID string, valIndex int32, height int64, round int32, step int, blockID meta.BlockID,
|
||||
time time.Time) *consensus.Vote {
|
||||
pubKey, err := val.GetPubKey(context.Background())
|
||||
require.NoError(t, err)
|
||||
v := &Vote{
|
||||
v := &consensus.Vote{
|
||||
ValidatorAddress: pubKey.Address(),
|
||||
ValidatorIndex: valIndex,
|
||||
Height: height,
|
||||
@@ -255,13 +260,13 @@ func makeVote(
|
||||
return v
|
||||
}
|
||||
|
||||
func makeHeaderRandom() *Header {
|
||||
return &Header{
|
||||
func makeHeaderRandom() *meta.Header {
|
||||
return &meta.Header{
|
||||
Version: version.Consensus{Block: version.BlockProtocol, App: 1},
|
||||
ChainID: tmrand.Str(12),
|
||||
Height: int64(mrand.Uint32() + 1),
|
||||
Time: time.Now(),
|
||||
LastBlockID: makeBlockIDRandom(),
|
||||
LastBlockID: test.MakeBlockID(),
|
||||
LastCommitHash: crypto.CRandBytes(tmhash.Size),
|
||||
DataHash: crypto.CRandBytes(tmhash.Size),
|
||||
ValidatorsHash: crypto.CRandBytes(tmhash.Size),
|
||||
@@ -276,36 +281,36 @@ func makeHeaderRandom() *Header {
|
||||
|
||||
func TestEvidenceProto(t *testing.T) {
|
||||
// -------- Votes --------
|
||||
val := NewMockPV()
|
||||
blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash")))
|
||||
blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt32, tmhash.Sum([]byte("partshash")))
|
||||
val := consensus.NewMockPV()
|
||||
blockID := test.MakeBlockIDWithHash(tmhash.Sum([]byte("blockhash")))
|
||||
blockID2 := test.MakeBlockIDWithHash(tmhash.Sum([]byte("blockhash2")))
|
||||
const chainID = "mychain"
|
||||
v := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, 1, 0x01, blockID, defaultVoteTime)
|
||||
v2 := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, 2, 0x01, blockID2, defaultVoteTime)
|
||||
|
||||
tests := []struct {
|
||||
testName string
|
||||
evidence Evidence
|
||||
evidence evidence.Evidence
|
||||
toProtoErr bool
|
||||
fromProtoErr bool
|
||||
}{
|
||||
{"nil fail", nil, true, true},
|
||||
{"DuplicateVoteEvidence empty fail", &DuplicateVoteEvidence{}, false, true},
|
||||
{"DuplicateVoteEvidence nil voteB", &DuplicateVoteEvidence{VoteA: v, VoteB: nil}, false, true},
|
||||
{"DuplicateVoteEvidence nil voteA", &DuplicateVoteEvidence{VoteA: nil, VoteB: v}, false, true},
|
||||
{"DuplicateVoteEvidence success", &DuplicateVoteEvidence{VoteA: v2, VoteB: v}, false, false},
|
||||
{"DuplicateVoteEvidence empty fail", &evidence.DuplicateVoteEvidence{}, false, true},
|
||||
{"DuplicateVoteEvidence nil voteB", &evidence.DuplicateVoteEvidence{VoteA: v, VoteB: nil}, false, true},
|
||||
{"DuplicateVoteEvidence nil voteA", &evidence.DuplicateVoteEvidence{VoteA: nil, VoteB: v}, false, true},
|
||||
{"DuplicateVoteEvidence success", &evidence.DuplicateVoteEvidence{VoteA: v2, VoteB: v}, false, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.testName, func(t *testing.T) {
|
||||
pb, err := EvidenceToProto(tt.evidence)
|
||||
pb, err := evidence.EvidenceToProto(tt.evidence)
|
||||
if tt.toProtoErr {
|
||||
assert.Error(t, err, tt.testName)
|
||||
return
|
||||
}
|
||||
assert.NoError(t, err, tt.testName)
|
||||
|
||||
evi, err := EvidenceFromProto(pb)
|
||||
evi, err := evidence.EvidenceFromProto(pb)
|
||||
if tt.fromProtoErr {
|
||||
assert.Error(t, err, tt.testName)
|
||||
return
|
||||
@@ -317,10 +322,10 @@ func TestEvidenceProto(t *testing.T) {
|
||||
|
||||
func TestEvidenceVectors(t *testing.T) {
|
||||
// Votes for duplicateEvidence
|
||||
val := NewMockPV()
|
||||
val := consensus.NewMockPV()
|
||||
val.PrivKey = ed25519.GenPrivKeyFromSecret([]byte("it's a secret")) // deterministic key
|
||||
blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash")))
|
||||
blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt32, tmhash.Sum([]byte("partshash")))
|
||||
blockID := test.MakeBlockIDWithHash(tmhash.Sum([]byte("blockhash")))
|
||||
blockID2 := test.MakeBlockIDWithHash(tmhash.Sum([]byte("blockhash2")))
|
||||
const chainID = "mychain"
|
||||
v := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, 1, 0x01, blockID, defaultVoteTime)
|
||||
v2 := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, 2, 0x01, blockID2, defaultVoteTime)
|
||||
@@ -329,13 +334,13 @@ func TestEvidenceVectors(t *testing.T) {
|
||||
height := int64(5)
|
||||
commonHeight := height - 1
|
||||
nValidators := 10
|
||||
voteSet, valSet, privVals := deterministicVoteSet(height, 1, tmproto.PrecommitType, 1)
|
||||
header := &Header{
|
||||
voteSet, valSet, privVals := test.DeterministicVoteSet(height, 1, tmproto.PrecommitType, 1)
|
||||
header := &meta.Header{
|
||||
Version: version.Consensus{Block: 1, App: 1},
|
||||
ChainID: chainID,
|
||||
Height: height,
|
||||
Time: time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC),
|
||||
LastBlockID: BlockID{},
|
||||
LastBlockID: meta.BlockID{},
|
||||
LastCommitHash: []byte("f2564c78071e26643ae9b3e2a19fa0dc10d4d9e873aa0be808660123f11a1e78"),
|
||||
DataHash: []byte("f2564c78071e26643ae9b3e2a19fa0dc10d4d9e873aa0be808660123f11a1e78"),
|
||||
ValidatorsHash: valSet.Hash(),
|
||||
@@ -348,12 +353,12 @@ func TestEvidenceVectors(t *testing.T) {
|
||||
EvidenceHash: []byte("f2564c78071e26643ae9b3e2a19fa0dc10d4d9e873aa0be808660123f11a1e78"),
|
||||
ProposerAddress: []byte("2915b7b15f979e48ebc61774bb1d86ba3136b7eb"),
|
||||
}
|
||||
blockID3 := makeBlockID(header.Hash(), math.MaxInt32, tmhash.Sum([]byte("partshash")))
|
||||
commit, err := makeCommit(blockID3, height, 1, voteSet, privVals, defaultVoteTime)
|
||||
blockID3 := test.MakeBlockIDWithHash(header.Hash())
|
||||
commit, err := test.MakeCommit(blockID3, height, 1, voteSet, privVals, defaultVoteTime)
|
||||
require.NoError(t, err)
|
||||
lcae := &LightClientAttackEvidence{
|
||||
ConflictingBlock: &LightBlock{
|
||||
SignedHeader: &SignedHeader{
|
||||
lcae := &evidence.LightClientAttackEvidence{
|
||||
ConflictingBlock: &light.LightBlock{
|
||||
SignedHeader: &light.SignedHeader{
|
||||
Header: header,
|
||||
Commit: commit,
|
||||
},
|
||||
@@ -368,19 +373,19 @@ func TestEvidenceVectors(t *testing.T) {
|
||||
|
||||
testCases := []struct {
|
||||
testName string
|
||||
evList EvidenceList
|
||||
evList evidence.EvidenceList
|
||||
expBytes string
|
||||
}{
|
||||
{"duplicateVoteEvidence",
|
||||
EvidenceList{&DuplicateVoteEvidence{VoteA: v2, VoteB: v}},
|
||||
evidence.EvidenceList{&evidence.DuplicateVoteEvidence{VoteA: v2, VoteB: v}},
|
||||
"a9ce28d13bb31001fc3e5b7927051baf98f86abdbd64377643a304164c826923",
|
||||
},
|
||||
{"LightClientAttackEvidence",
|
||||
EvidenceList{lcae},
|
||||
evidence.EvidenceList{lcae},
|
||||
"2f8782163c3905b26e65823ababc977fe54e97b94e60c0360b1e4726b668bb8e",
|
||||
},
|
||||
{"LightClientAttackEvidence & DuplicateVoteEvidence",
|
||||
EvidenceList{&DuplicateVoteEvidence{VoteA: v2, VoteB: v}, lcae},
|
||||
evidence.EvidenceList{&evidence.DuplicateVoteEvidence{VoteA: v2, VoteB: v}, lcae},
|
||||
"eedb4b47d6dbc9d43f53da8aa50bb826e8d9fc7d897da777c8af6a04aa74163e",
|
||||
},
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
package types
|
||||
package light
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/tendermint/tendermint/pkg/consensus"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -12,7 +14,7 @@ import (
|
||||
// It is the basis of the light client
|
||||
type LightBlock struct {
|
||||
*SignedHeader `json:"signed_header"`
|
||||
ValidatorSet *ValidatorSet `json:"validator_set"`
|
||||
ValidatorSet *consensus.ValidatorSet `json:"validator_set"`
|
||||
}
|
||||
|
||||
// ValidateBasic checks that the data is correct and consistent
|
||||
@@ -101,7 +103,7 @@ func LightBlockFromProto(pb *tmproto.LightBlock) (*LightBlock, error) {
|
||||
}
|
||||
|
||||
if pb.ValidatorSet != nil {
|
||||
vals, err := ValidatorSetFromProto(pb.ValidatorSet)
|
||||
vals, err := consensus.ValidatorSetFromProto(pb.ValidatorSet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -115,9 +117,9 @@ func LightBlockFromProto(pb *tmproto.LightBlock) (*LightBlock, error) {
|
||||
|
||||
// SignedHeader is a header along with the commits that prove it.
|
||||
type SignedHeader struct {
|
||||
*Header `json:"header"`
|
||||
*meta.Header `json:"header"`
|
||||
|
||||
Commit *Commit `json:"commit"`
|
||||
Commit *meta.Commit `json:"commit"`
|
||||
}
|
||||
|
||||
// ValidateBasic does basic consistency checks and makes sure the header
|
||||
@@ -202,7 +204,7 @@ func SignedHeaderFromProto(shp *tmproto.SignedHeader) (*SignedHeader, error) {
|
||||
sh := new(SignedHeader)
|
||||
|
||||
if shp.Header != nil {
|
||||
h, err := HeaderFromProto(shp.Header)
|
||||
h, err := meta.HeaderFromProto(shp.Header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -210,7 +212,7 @@ func SignedHeaderFromProto(shp *tmproto.SignedHeader) (*SignedHeader, error) {
|
||||
}
|
||||
|
||||
if shp.Commit != nil {
|
||||
c, err := CommitFromProto(shp.Commit)
|
||||
c, err := meta.CommitFromProto(shp.Commit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package light_test
|
||||
|
||||
import (
|
||||
"math"
|
||||
@@ -6,43 +6,48 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
test "github.com/tendermint/tendermint/internal/test/factory"
|
||||
"github.com/tendermint/tendermint/pkg/consensus"
|
||||
"github.com/tendermint/tendermint/pkg/light"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
"github.com/tendermint/tendermint/version"
|
||||
)
|
||||
|
||||
func TestLightBlockValidateBasic(t *testing.T) {
|
||||
header := MakeRandHeader()
|
||||
commit := randCommit(time.Now())
|
||||
vals, _ := randValidatorPrivValSet(5, 1)
|
||||
header := test.MakeRandomHeader()
|
||||
commit := test.MakeRandomCommit(time.Now())
|
||||
vals, _ := test.RandValidatorPrivValSet(5, 1)
|
||||
header.Height = commit.Height
|
||||
header.LastBlockID = commit.BlockID
|
||||
header.ValidatorsHash = vals.Hash()
|
||||
header.Version.Block = version.BlockProtocol
|
||||
vals2, _ := randValidatorPrivValSet(3, 1)
|
||||
vals2, _ := test.RandValidatorPrivValSet(3, 1)
|
||||
vals3 := vals.Copy()
|
||||
vals3.Proposer = &Validator{}
|
||||
vals3.Proposer = &consensus.Validator{}
|
||||
commit.BlockID.Hash = header.Hash()
|
||||
|
||||
sh := &SignedHeader{
|
||||
Header: &header,
|
||||
sh := &light.SignedHeader{
|
||||
Header: header,
|
||||
Commit: commit,
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
sh *SignedHeader
|
||||
vals *ValidatorSet
|
||||
sh *light.SignedHeader
|
||||
vals *consensus.ValidatorSet
|
||||
expectErr bool
|
||||
}{
|
||||
{"valid light block", sh, vals, false},
|
||||
{"hashes don't match", sh, vals2, true},
|
||||
{"invalid validator set", sh, vals3, true},
|
||||
{"invalid signed header", &SignedHeader{Header: &header, Commit: randCommit(time.Now())}, vals, true},
|
||||
{"invalid signed header", &light.SignedHeader{Header: header, Commit: test.MakeRandomCommit(time.Now())}, vals, true},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
lightBlock := LightBlock{
|
||||
lightBlock := light.LightBlock{
|
||||
SignedHeader: tc.sh,
|
||||
ValidatorSet: tc.vals,
|
||||
}
|
||||
@@ -57,37 +62,37 @@ func TestLightBlockValidateBasic(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLightBlockProtobuf(t *testing.T) {
|
||||
header := MakeRandHeader()
|
||||
commit := randCommit(time.Now())
|
||||
vals, _ := randValidatorPrivValSet(5, 1)
|
||||
header := test.MakeRandomHeader()
|
||||
commit := test.MakeRandomCommit(time.Now())
|
||||
vals, _ := test.RandValidatorPrivValSet(5, 1)
|
||||
header.Height = commit.Height
|
||||
header.LastBlockID = commit.BlockID
|
||||
header.Version.Block = version.BlockProtocol
|
||||
header.ValidatorsHash = vals.Hash()
|
||||
vals3 := vals.Copy()
|
||||
vals3.Proposer = &Validator{}
|
||||
vals3.Proposer = &consensus.Validator{}
|
||||
commit.BlockID.Hash = header.Hash()
|
||||
|
||||
sh := &SignedHeader{
|
||||
Header: &header,
|
||||
sh := &light.SignedHeader{
|
||||
Header: header,
|
||||
Commit: commit,
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
sh *SignedHeader
|
||||
vals *ValidatorSet
|
||||
sh *light.SignedHeader
|
||||
vals *consensus.ValidatorSet
|
||||
toProtoErr bool
|
||||
toBlockErr bool
|
||||
}{
|
||||
{"valid light block", sh, vals, false, false},
|
||||
{"empty signed header", &SignedHeader{}, vals, false, false},
|
||||
{"empty validator set", sh, &ValidatorSet{}, false, true},
|
||||
{"empty light block", &SignedHeader{}, &ValidatorSet{}, false, true},
|
||||
{"empty signed header", &light.SignedHeader{}, vals, false, false},
|
||||
{"empty validator set", sh, &consensus.ValidatorSet{}, false, true},
|
||||
{"empty light block", &light.SignedHeader{}, &consensus.ValidatorSet{}, false, true},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
lightBlock := &LightBlock{
|
||||
lightBlock := &light.LightBlock{
|
||||
SignedHeader: tc.sh,
|
||||
ValidatorSet: tc.vals,
|
||||
}
|
||||
@@ -98,7 +103,7 @@ func TestLightBlockProtobuf(t *testing.T) {
|
||||
assert.NoError(t, err, tc.name)
|
||||
}
|
||||
|
||||
lb, err := LightBlockFromProto(lbp)
|
||||
lb, err := light.LightBlockFromProto(lbp)
|
||||
if tc.toBlockErr {
|
||||
assert.Error(t, err, tc.name)
|
||||
} else {
|
||||
@@ -110,10 +115,10 @@ func TestLightBlockProtobuf(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSignedHeaderValidateBasic(t *testing.T) {
|
||||
commit := randCommit(time.Now())
|
||||
commit := test.MakeRandomCommit(time.Now())
|
||||
chainID := "𠜎"
|
||||
timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC)
|
||||
h := Header{
|
||||
h := meta.Header{
|
||||
Version: version.Consensus{Block: version.BlockProtocol, App: math.MaxInt64},
|
||||
ChainID: chainID,
|
||||
Height: commit.Height,
|
||||
@@ -130,14 +135,14 @@ func TestSignedHeaderValidateBasic(t *testing.T) {
|
||||
ProposerAddress: crypto.AddressHash([]byte("proposer_address")),
|
||||
}
|
||||
|
||||
validSignedHeader := SignedHeader{Header: &h, Commit: commit}
|
||||
validSignedHeader := light.SignedHeader{Header: &h, Commit: commit}
|
||||
validSignedHeader.Commit.BlockID.Hash = validSignedHeader.Hash()
|
||||
invalidSignedHeader := SignedHeader{}
|
||||
invalidSignedHeader := light.SignedHeader{}
|
||||
|
||||
testCases := []struct {
|
||||
testName string
|
||||
shHeader *Header
|
||||
shCommit *Commit
|
||||
shHeader *meta.Header
|
||||
shCommit *meta.Commit
|
||||
expectErr bool
|
||||
}{
|
||||
{"Valid Signed Header", validSignedHeader.Header, validSignedHeader.Commit, false},
|
||||
@@ -148,7 +153,7 @@ func TestSignedHeaderValidateBasic(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
sh := SignedHeader{
|
||||
sh := light.SignedHeader{
|
||||
Header: tc.shHeader,
|
||||
Commit: tc.shCommit,
|
||||
}
|
||||
@@ -163,3 +168,32 @@ func TestSignedHeaderValidateBasic(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignedHeaderProtoBuf(t *testing.T) {
|
||||
commit := test.MakeRandomCommit(time.Now())
|
||||
h := test.MakeRandomHeader()
|
||||
|
||||
sh := light.SignedHeader{Header: h, Commit: commit}
|
||||
|
||||
testCases := []struct {
|
||||
msg string
|
||||
sh1 *light.SignedHeader
|
||||
expPass bool
|
||||
}{
|
||||
{"empty SignedHeader 2", &light.SignedHeader{}, true},
|
||||
{"success", &sh, true},
|
||||
{"failure nil", nil, false},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
protoSignedHeader := tc.sh1.ToProto()
|
||||
|
||||
sh, err := light.SignedHeaderFromProto(protoSignedHeader)
|
||||
|
||||
if tc.expPass {
|
||||
require.NoError(t, err, tc.msg)
|
||||
require.Equal(t, tc.sh1, sh, tc.msg)
|
||||
} else {
|
||||
require.Error(t, err, tc.msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package mempool
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -137,11 +137,3 @@ func TxProofFromProto(pb tmproto.TxProof) (TxProof, error) {
|
||||
|
||||
return pbtp, nil
|
||||
}
|
||||
|
||||
// ComputeProtoSizeForTxs wraps the transactions in tmproto.Data{} and calculates the size.
|
||||
// https://developers.google.com/protocol-buffers/docs/encoding
|
||||
func ComputeProtoSizeForTxs(txs []Tx) int64 {
|
||||
data := Data{Txs: txs}
|
||||
pdData := data.ToProto()
|
||||
return int64(pdData.Size())
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package mempool
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package meta
|
||||
|
||||
import (
|
||||
"time"
|
||||
@@ -38,32 +38,6 @@ func CanonicalizePartSetHeader(psh tmproto.PartSetHeader) tmproto.CanonicalPartS
|
||||
return tmproto.CanonicalPartSetHeader(psh)
|
||||
}
|
||||
|
||||
// CanonicalizeVote transforms the given Proposal to a CanonicalProposal.
|
||||
func CanonicalizeProposal(chainID string, proposal *tmproto.Proposal) tmproto.CanonicalProposal {
|
||||
return tmproto.CanonicalProposal{
|
||||
Type: tmproto.ProposalType,
|
||||
Height: proposal.Height, // encoded as sfixed64
|
||||
Round: int64(proposal.Round), // encoded as sfixed64
|
||||
POLRound: int64(proposal.PolRound),
|
||||
BlockID: CanonicalizeBlockID(proposal.BlockID),
|
||||
Timestamp: proposal.Timestamp,
|
||||
ChainID: chainID,
|
||||
}
|
||||
}
|
||||
|
||||
// CanonicalizeVote transforms the given Vote to a CanonicalVote, which does
|
||||
// not contain ValidatorIndex and ValidatorAddress fields.
|
||||
func CanonicalizeVote(chainID string, vote *tmproto.Vote) tmproto.CanonicalVote {
|
||||
return tmproto.CanonicalVote{
|
||||
Type: vote.Type,
|
||||
Height: vote.Height, // encoded as sfixed64
|
||||
Round: int64(vote.Round), // encoded as sfixed64
|
||||
BlockID: CanonicalizeBlockID(vote.BlockID),
|
||||
Timestamp: vote.Timestamp,
|
||||
ChainID: chainID,
|
||||
}
|
||||
}
|
||||
|
||||
// CanonicalTime can be used to stringify time in a canonical way.
|
||||
func CanonicalTime(t time.Time) string {
|
||||
// Note that sending time over amino resets it to
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package meta_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
tmrand "github.com/tendermint/tendermint/libs/rand"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -31,7 +32,7 @@ func TestCanonicalizeBlockID(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := CanonicalizeBlockID(tt.args); !reflect.DeepEqual(got, tt.want) {
|
||||
if got := meta.CanonicalizeBlockID(tt.args); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("CanonicalizeBlockID() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
379
pkg/meta/commit.go
Normal file
379
pkg/meta/commit.go
Normal file
@@ -0,0 +1,379 @@
|
||||
package meta
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/merkle"
|
||||
"github.com/tendermint/tendermint/libs/bits"
|
||||
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
||||
tmmath "github.com/tendermint/tendermint/libs/math"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
|
||||
var (
|
||||
// MaxSignatureSize is a maximum allowed signature size for the Proposal
|
||||
// and Vote.
|
||||
// XXX: secp256k1 does not have Size nor MaxSize defined.
|
||||
MaxSignatureSize = tmmath.MaxInt(ed25519.SignatureSize, 64)
|
||||
)
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
const (
|
||||
// Max size of commit without any commitSigs -> 82 for BlockID, 8 for Height, 4 for Round.
|
||||
MaxCommitOverheadBytes int64 = 94
|
||||
// Commit sig size is made up of 64 bytes for the signature, 20 bytes for the address,
|
||||
// 1 byte for the flag and 14 bytes for the timestamp
|
||||
MaxCommitSigBytes int64 = 109
|
||||
)
|
||||
|
||||
// CommitSig is a part of the Vote included in a Commit.
|
||||
type CommitSig struct {
|
||||
BlockIDFlag BlockIDFlag `json:"block_id_flag"`
|
||||
ValidatorAddress crypto.Address `json:"validator_address"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Signature []byte `json:"signature"`
|
||||
}
|
||||
|
||||
// NewCommitSigForBlock returns new CommitSig with BlockIDFlagCommit.
|
||||
func NewCommitSigForBlock(signature []byte, valAddr crypto.Address, ts time.Time) CommitSig {
|
||||
return CommitSig{
|
||||
BlockIDFlag: BlockIDFlagCommit,
|
||||
ValidatorAddress: valAddr,
|
||||
Timestamp: ts,
|
||||
Signature: signature,
|
||||
}
|
||||
}
|
||||
|
||||
func MaxCommitBytes(valCount int) int64 {
|
||||
// From the repeated commit sig field
|
||||
var protoEncodingOverhead int64 = 2
|
||||
return MaxCommitOverheadBytes + ((MaxCommitSigBytes + protoEncodingOverhead) * int64(valCount))
|
||||
}
|
||||
|
||||
// NewCommitSigAbsent returns new CommitSig with BlockIDFlagAbsent. Other
|
||||
// fields are all empty.
|
||||
func NewCommitSigAbsent() CommitSig {
|
||||
return CommitSig{
|
||||
BlockIDFlag: BlockIDFlagAbsent,
|
||||
}
|
||||
}
|
||||
|
||||
// ForBlock returns true if CommitSig is for the block.
|
||||
func (cs CommitSig) ForBlock() bool {
|
||||
return cs.BlockIDFlag == BlockIDFlagCommit
|
||||
}
|
||||
|
||||
// Absent returns true if CommitSig is absent.
|
||||
func (cs CommitSig) Absent() bool {
|
||||
return cs.BlockIDFlag == BlockIDFlagAbsent
|
||||
}
|
||||
|
||||
// CommitSig returns a string representation of CommitSig.
|
||||
//
|
||||
// 1. first 6 bytes of signature
|
||||
// 2. first 6 bytes of validator address
|
||||
// 3. block ID flag
|
||||
// 4. timestamp
|
||||
func (cs CommitSig) String() string {
|
||||
return fmt.Sprintf("CommitSig{%X by %X on %v @ %s}",
|
||||
tmbytes.Fingerprint(cs.Signature),
|
||||
tmbytes.Fingerprint(cs.ValidatorAddress),
|
||||
cs.BlockIDFlag,
|
||||
CanonicalTime(cs.Timestamp))
|
||||
}
|
||||
|
||||
// BlockID returns the Commit's BlockID if CommitSig indicates signing,
|
||||
// otherwise - empty BlockID.
|
||||
func (cs CommitSig) BlockID(commitBlockID BlockID) BlockID {
|
||||
var blockID BlockID
|
||||
switch cs.BlockIDFlag {
|
||||
case BlockIDFlagAbsent:
|
||||
blockID = BlockID{}
|
||||
case BlockIDFlagCommit:
|
||||
blockID = commitBlockID
|
||||
case BlockIDFlagNil:
|
||||
blockID = BlockID{}
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown BlockIDFlag: %v", cs.BlockIDFlag))
|
||||
}
|
||||
return blockID
|
||||
}
|
||||
|
||||
// ValidateBasic performs basic validation.
|
||||
func (cs CommitSig) ValidateBasic() error {
|
||||
switch cs.BlockIDFlag {
|
||||
case BlockIDFlagAbsent:
|
||||
case BlockIDFlagCommit:
|
||||
case BlockIDFlagNil:
|
||||
default:
|
||||
return fmt.Errorf("unknown BlockIDFlag: %v", cs.BlockIDFlag)
|
||||
}
|
||||
|
||||
switch cs.BlockIDFlag {
|
||||
case BlockIDFlagAbsent:
|
||||
if len(cs.ValidatorAddress) != 0 {
|
||||
return errors.New("validator address is present")
|
||||
}
|
||||
if !cs.Timestamp.IsZero() {
|
||||
return errors.New("time is present")
|
||||
}
|
||||
if len(cs.Signature) != 0 {
|
||||
return errors.New("signature is present")
|
||||
}
|
||||
default:
|
||||
if len(cs.ValidatorAddress) != crypto.AddressSize {
|
||||
return fmt.Errorf("expected ValidatorAddress size to be %d bytes, got %d bytes",
|
||||
crypto.AddressSize,
|
||||
len(cs.ValidatorAddress),
|
||||
)
|
||||
}
|
||||
// NOTE: Timestamp validation is subtle and handled elsewhere.
|
||||
if len(cs.Signature) == 0 {
|
||||
return errors.New("signature is missing")
|
||||
}
|
||||
if len(cs.Signature) > MaxSignatureSize {
|
||||
return fmt.Errorf("signature is too big (max: %d)", MaxSignatureSize)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToProto converts CommitSig to protobuf
|
||||
func (cs *CommitSig) ToProto() *tmproto.CommitSig {
|
||||
if cs == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &tmproto.CommitSig{
|
||||
BlockIdFlag: tmproto.BlockIDFlag(cs.BlockIDFlag),
|
||||
ValidatorAddress: cs.ValidatorAddress,
|
||||
Timestamp: cs.Timestamp,
|
||||
Signature: cs.Signature,
|
||||
}
|
||||
}
|
||||
|
||||
// FromProto sets a protobuf CommitSig to the given pointer.
|
||||
// It returns an error if the CommitSig is invalid.
|
||||
func (cs *CommitSig) FromProto(csp tmproto.CommitSig) error {
|
||||
|
||||
cs.BlockIDFlag = BlockIDFlag(csp.BlockIdFlag)
|
||||
cs.ValidatorAddress = csp.ValidatorAddress
|
||||
cs.Timestamp = csp.Timestamp
|
||||
cs.Signature = csp.Signature
|
||||
|
||||
return cs.ValidateBasic()
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
// Commit contains the evidence that a block was committed by a set of validators.
|
||||
// NOTE: Commit is empty for height 1, but never nil.
|
||||
type Commit struct {
|
||||
// NOTE: The signatures are in order of address to preserve the bonded
|
||||
// ValidatorSet order.
|
||||
// Any peer with a block can gossip signatures by index with a peer without
|
||||
// recalculating the active ValidatorSet.
|
||||
Height int64 `json:"height"`
|
||||
Round int32 `json:"round"`
|
||||
BlockID BlockID `json:"block_id"`
|
||||
Signatures []CommitSig `json:"signatures"`
|
||||
|
||||
// Memoized in first call to corresponding method.
|
||||
// NOTE: can't memoize in constructor because constructor isn't used for
|
||||
// unmarshaling.
|
||||
hash tmbytes.HexBytes
|
||||
bitArray *bits.BitArray
|
||||
}
|
||||
|
||||
// NewCommit returns a new Commit.
|
||||
func NewCommit(height int64, round int32, blockID BlockID, commitSigs []CommitSig) *Commit {
|
||||
return &Commit{
|
||||
Height: height,
|
||||
Round: round,
|
||||
BlockID: blockID,
|
||||
Signatures: commitSigs,
|
||||
}
|
||||
}
|
||||
|
||||
// Type returns the vote type of the commit, which is always VoteTypePrecommit
|
||||
// Implements VoteSetReader.
|
||||
func (commit *Commit) Type() byte {
|
||||
return byte(tmproto.PrecommitType)
|
||||
}
|
||||
|
||||
// GetHeight returns height of the commit.
|
||||
// Implements VoteSetReader.
|
||||
func (commit *Commit) GetHeight() int64 {
|
||||
return commit.Height
|
||||
}
|
||||
|
||||
// GetRound returns height of the commit.
|
||||
// Implements VoteSetReader.
|
||||
func (commit *Commit) GetRound() int32 {
|
||||
return commit.Round
|
||||
}
|
||||
|
||||
// Size returns the number of signatures in the commit.
|
||||
// Implements VoteSetReader.
|
||||
func (commit *Commit) Size() int {
|
||||
if commit == nil {
|
||||
return 0
|
||||
}
|
||||
return len(commit.Signatures)
|
||||
}
|
||||
|
||||
// ClearCache removes the saved hash. This is predominantly used for testing.
|
||||
func (commit *Commit) ClearCache() {
|
||||
commit.hash = nil
|
||||
commit.bitArray = nil
|
||||
}
|
||||
|
||||
// BitArray returns a BitArray of which validators voted for BlockID or nil in this commit.
|
||||
// Implements VoteSetReader.
|
||||
func (commit *Commit) BitArray() *bits.BitArray {
|
||||
if commit.bitArray == nil {
|
||||
commit.bitArray = bits.NewBitArray(len(commit.Signatures))
|
||||
for i, commitSig := range commit.Signatures {
|
||||
// TODO: need to check the BlockID otherwise we could be counting conflicts,
|
||||
// not just the one with +2/3 !
|
||||
commit.bitArray.SetIndex(i, !commitSig.Absent())
|
||||
}
|
||||
}
|
||||
return commit.bitArray
|
||||
}
|
||||
|
||||
// IsCommit returns true if there is at least one signature.
|
||||
// Implements VoteSetReader.
|
||||
func (commit *Commit) IsCommit() bool {
|
||||
return len(commit.Signatures) != 0
|
||||
}
|
||||
|
||||
// ValidateBasic performs basic validation that doesn't involve state data.
|
||||
// Does not actually check the cryptographic signatures.
|
||||
func (commit *Commit) ValidateBasic() error {
|
||||
if commit.Height < 0 {
|
||||
return errors.New("negative Height")
|
||||
}
|
||||
if commit.Round < 0 {
|
||||
return errors.New("negative Round")
|
||||
}
|
||||
|
||||
if commit.Height >= 1 {
|
||||
if commit.BlockID.IsZero() {
|
||||
return errors.New("commit cannot be for nil block")
|
||||
}
|
||||
|
||||
if len(commit.Signatures) == 0 {
|
||||
return errors.New("no signatures in commit")
|
||||
}
|
||||
for i, commitSig := range commit.Signatures {
|
||||
if err := commitSig.ValidateBasic(); err != nil {
|
||||
return fmt.Errorf("wrong CommitSig #%d: %v", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Hash returns the hash of the commit
|
||||
func (commit *Commit) Hash() tmbytes.HexBytes {
|
||||
if commit == nil {
|
||||
return nil
|
||||
}
|
||||
if commit.hash == nil {
|
||||
bs := make([][]byte, len(commit.Signatures))
|
||||
for i, commitSig := range commit.Signatures {
|
||||
pbcs := commitSig.ToProto()
|
||||
bz, err := pbcs.Marshal()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
bs[i] = bz
|
||||
}
|
||||
commit.hash = merkle.HashFromByteSlices(bs)
|
||||
}
|
||||
return commit.hash
|
||||
}
|
||||
|
||||
// StringIndented returns a string representation of the commit.
|
||||
func (commit *Commit) StringIndented(indent string) string {
|
||||
if commit == nil {
|
||||
return "nil-Commit"
|
||||
}
|
||||
commitSigStrings := make([]string, len(commit.Signatures))
|
||||
for i, commitSig := range commit.Signatures {
|
||||
commitSigStrings[i] = commitSig.String()
|
||||
}
|
||||
return fmt.Sprintf(`Commit{
|
||||
%s Height: %d
|
||||
%s Round: %d
|
||||
%s BlockID: %v
|
||||
%s Signatures:
|
||||
%s %v
|
||||
%s}#%v`,
|
||||
indent, commit.Height,
|
||||
indent, commit.Round,
|
||||
indent, commit.BlockID,
|
||||
indent,
|
||||
indent, strings.Join(commitSigStrings, "\n"+indent+" "),
|
||||
indent, commit.hash)
|
||||
}
|
||||
|
||||
// ToProto converts Commit to protobuf
|
||||
func (commit *Commit) ToProto() *tmproto.Commit {
|
||||
if commit == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
c := new(tmproto.Commit)
|
||||
sigs := make([]tmproto.CommitSig, len(commit.Signatures))
|
||||
for i := range commit.Signatures {
|
||||
sigs[i] = *commit.Signatures[i].ToProto()
|
||||
}
|
||||
c.Signatures = sigs
|
||||
|
||||
c.Height = commit.Height
|
||||
c.Round = commit.Round
|
||||
c.BlockID = commit.BlockID.ToProto()
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// FromProto sets a protobuf Commit to the given pointer.
|
||||
// It returns an error if the commit is invalid.
|
||||
func CommitFromProto(cp *tmproto.Commit) (*Commit, error) {
|
||||
if cp == nil {
|
||||
return nil, errors.New("nil Commit")
|
||||
}
|
||||
|
||||
var (
|
||||
commit = new(Commit)
|
||||
)
|
||||
|
||||
bi, err := BlockIDFromProto(&cp.BlockID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sigs := make([]CommitSig, len(cp.Signatures))
|
||||
for i := range cp.Signatures {
|
||||
if err := sigs[i].FromProto(cp.Signatures[i]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
commit.Signatures = sigs
|
||||
|
||||
commit.Height = cp.Height
|
||||
commit.Round = cp.Round
|
||||
commit.BlockID = *bi
|
||||
|
||||
return commit, commit.ValidateBasic()
|
||||
}
|
||||
284
pkg/meta/commit_test.go
Normal file
284
pkg/meta/commit_test.go
Normal file
@@ -0,0 +1,284 @@
|
||||
package meta_test
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
test "github.com/tendermint/tendermint/internal/test/factory"
|
||||
"github.com/tendermint/tendermint/libs/bits"
|
||||
"github.com/tendermint/tendermint/pkg/consensus"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
|
||||
func TestCommit(t *testing.T) {
|
||||
lastID := test.MakeBlockID()
|
||||
h := int64(3)
|
||||
voteSet, _, vals := test.RandVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1)
|
||||
commit, err := test.MakeCommit(lastID, h-1, 1, voteSet, vals, time.Now())
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, h-1, commit.Height)
|
||||
assert.EqualValues(t, 1, commit.Round)
|
||||
assert.Equal(t, tmproto.PrecommitType, tmproto.SignedMsgType(commit.Type()))
|
||||
if commit.Size() <= 0 {
|
||||
t.Fatalf("commit %v has a zero or negative size: %d", commit, commit.Size())
|
||||
}
|
||||
|
||||
require.NotNil(t, commit.BitArray())
|
||||
assert.Equal(t, bits.NewBitArray(10).Size(), commit.BitArray().Size())
|
||||
|
||||
assert.Equal(t, voteSet.GetByIndex(0), consensus.GetVoteFromCommit(commit, 0))
|
||||
assert.True(t, commit.IsCommit())
|
||||
}
|
||||
|
||||
func TestCommitValidateBasic(t *testing.T) {
|
||||
testCases := []struct {
|
||||
testName string
|
||||
malleateCommit func(*meta.Commit)
|
||||
expectErr bool
|
||||
}{
|
||||
{"Random Commit", func(com *meta.Commit) {}, false},
|
||||
{"Incorrect signature", func(com *meta.Commit) { com.Signatures[0].Signature = []byte{0} }, false},
|
||||
{"Incorrect height", func(com *meta.Commit) { com.Height = int64(-100) }, true},
|
||||
{"Incorrect round", func(com *meta.Commit) { com.Round = -100 }, true},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
com := test.MakeRandomCommit(time.Now())
|
||||
tc.malleateCommit(com)
|
||||
assert.Equal(t, tc.expectErr, com.ValidateBasic() != nil, "Validate Basic had an unexpected result")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommit_ValidateBasic(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
commit *meta.Commit
|
||||
expectErr bool
|
||||
errString string
|
||||
}{
|
||||
{
|
||||
"invalid height",
|
||||
&meta.Commit{Height: -1},
|
||||
true, "negative Height",
|
||||
},
|
||||
{
|
||||
"invalid round",
|
||||
&meta.Commit{Height: 1, Round: -1},
|
||||
true, "negative Round",
|
||||
},
|
||||
{
|
||||
"invalid block ID",
|
||||
&meta.Commit{
|
||||
Height: 1,
|
||||
Round: 1,
|
||||
BlockID: meta.BlockID{},
|
||||
},
|
||||
true, "commit cannot be for nil block",
|
||||
},
|
||||
{
|
||||
"no signatures",
|
||||
&meta.Commit{
|
||||
Height: 1,
|
||||
Round: 1,
|
||||
BlockID: meta.BlockID{
|
||||
Hash: make([]byte, tmhash.Size),
|
||||
PartSetHeader: meta.PartSetHeader{
|
||||
Hash: make([]byte, tmhash.Size),
|
||||
},
|
||||
},
|
||||
},
|
||||
true, "no signatures in commit",
|
||||
},
|
||||
{
|
||||
"invalid signature",
|
||||
&meta.Commit{
|
||||
Height: 1,
|
||||
Round: 1,
|
||||
BlockID: meta.BlockID{
|
||||
Hash: make([]byte, tmhash.Size),
|
||||
PartSetHeader: meta.PartSetHeader{
|
||||
Hash: make([]byte, tmhash.Size),
|
||||
},
|
||||
},
|
||||
Signatures: []meta.CommitSig{
|
||||
{
|
||||
BlockIDFlag: meta.BlockIDFlagCommit,
|
||||
ValidatorAddress: make([]byte, crypto.AddressSize),
|
||||
Signature: make([]byte, meta.MaxSignatureSize+1),
|
||||
},
|
||||
},
|
||||
},
|
||||
true, "wrong CommitSig",
|
||||
},
|
||||
{
|
||||
"valid commit",
|
||||
&meta.Commit{
|
||||
Height: 1,
|
||||
Round: 1,
|
||||
BlockID: meta.BlockID{
|
||||
Hash: make([]byte, tmhash.Size),
|
||||
PartSetHeader: meta.PartSetHeader{
|
||||
Hash: make([]byte, tmhash.Size),
|
||||
},
|
||||
},
|
||||
Signatures: []meta.CommitSig{
|
||||
{
|
||||
BlockIDFlag: meta.BlockIDFlagCommit,
|
||||
ValidatorAddress: make([]byte, crypto.AddressSize),
|
||||
Signature: make([]byte, meta.MaxSignatureSize),
|
||||
},
|
||||
},
|
||||
},
|
||||
false, "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := tc.commit.ValidateBasic()
|
||||
if tc.expectErr {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), tc.errString)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxCommitBytes(t *testing.T) {
|
||||
// time is varint encoded so need to pick the max.
|
||||
// year int, month Month, day, hour, min, sec, nsec int, loc *Location
|
||||
timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC)
|
||||
|
||||
cs := meta.CommitSig{
|
||||
BlockIDFlag: meta.BlockIDFlagNil,
|
||||
ValidatorAddress: crypto.AddressHash([]byte("validator_address")),
|
||||
Timestamp: timestamp,
|
||||
Signature: crypto.CRandBytes(meta.MaxSignatureSize),
|
||||
}
|
||||
|
||||
pbSig := cs.ToProto()
|
||||
// test that a single commit sig doesn't exceed max commit sig bytes
|
||||
assert.EqualValues(t, meta.MaxCommitSigBytes, pbSig.Size())
|
||||
|
||||
// check size with a single commit
|
||||
commit := &meta.Commit{
|
||||
Height: math.MaxInt64,
|
||||
Round: math.MaxInt32,
|
||||
BlockID: meta.BlockID{
|
||||
Hash: tmhash.Sum([]byte("blockID_hash")),
|
||||
PartSetHeader: meta.PartSetHeader{
|
||||
Total: math.MaxInt32,
|
||||
Hash: tmhash.Sum([]byte("blockID_part_set_header_hash")),
|
||||
},
|
||||
},
|
||||
Signatures: []meta.CommitSig{cs},
|
||||
}
|
||||
|
||||
pb := commit.ToProto()
|
||||
|
||||
assert.EqualValues(t, meta.MaxCommitBytes(1), int64(pb.Size()))
|
||||
|
||||
// check the upper bound of the commit size
|
||||
for i := 1; i < consensus.MaxVotesCount; i++ {
|
||||
commit.Signatures = append(commit.Signatures, cs)
|
||||
}
|
||||
|
||||
pb = commit.ToProto()
|
||||
|
||||
assert.EqualValues(t, meta.MaxCommitBytes(consensus.MaxVotesCount), int64(pb.Size()))
|
||||
}
|
||||
|
||||
func TestCommitSig_ValidateBasic(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
cs meta.CommitSig
|
||||
expectErr bool
|
||||
errString string
|
||||
}{
|
||||
{
|
||||
"invalid ID flag",
|
||||
meta.CommitSig{BlockIDFlag: meta.BlockIDFlag(0xFF)},
|
||||
true, "unknown BlockIDFlag",
|
||||
},
|
||||
{
|
||||
"BlockIDFlagAbsent validator address present",
|
||||
meta.CommitSig{BlockIDFlag: meta.BlockIDFlagAbsent, ValidatorAddress: crypto.Address("testaddr")},
|
||||
true, "validator address is present",
|
||||
},
|
||||
{
|
||||
"BlockIDFlagAbsent timestamp present",
|
||||
meta.CommitSig{BlockIDFlag: meta.BlockIDFlagAbsent, Timestamp: time.Now().UTC()},
|
||||
true, "time is present",
|
||||
},
|
||||
{
|
||||
"BlockIDFlagAbsent signatures present",
|
||||
meta.CommitSig{BlockIDFlag: meta.BlockIDFlagAbsent, Signature: []byte{0xAA}},
|
||||
true, "signature is present",
|
||||
},
|
||||
{
|
||||
"BlockIDFlagAbsent valid BlockIDFlagAbsent",
|
||||
meta.CommitSig{BlockIDFlag: meta.BlockIDFlagAbsent},
|
||||
false, "",
|
||||
},
|
||||
{
|
||||
"non-BlockIDFlagAbsent invalid validator address",
|
||||
meta.CommitSig{BlockIDFlag: meta.BlockIDFlagCommit, ValidatorAddress: make([]byte, 1)},
|
||||
true, "expected ValidatorAddress size",
|
||||
},
|
||||
{
|
||||
"non-BlockIDFlagAbsent invalid signature (zero)",
|
||||
meta.CommitSig{
|
||||
BlockIDFlag: meta.BlockIDFlagCommit,
|
||||
ValidatorAddress: make([]byte, crypto.AddressSize),
|
||||
Signature: make([]byte, 0),
|
||||
},
|
||||
true, "signature is missing",
|
||||
},
|
||||
{
|
||||
"non-BlockIDFlagAbsent invalid signature (too large)",
|
||||
meta.CommitSig{
|
||||
BlockIDFlag: meta.BlockIDFlagCommit,
|
||||
ValidatorAddress: make([]byte, crypto.AddressSize),
|
||||
Signature: make([]byte, meta.MaxSignatureSize+1),
|
||||
},
|
||||
true, "signature is too big",
|
||||
},
|
||||
{
|
||||
"non-BlockIDFlagAbsent valid",
|
||||
meta.CommitSig{
|
||||
BlockIDFlag: meta.BlockIDFlagCommit,
|
||||
ValidatorAddress: make([]byte, crypto.AddressSize),
|
||||
Signature: make([]byte, meta.MaxSignatureSize),
|
||||
},
|
||||
false, "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := tc.cs.ValidateBasic()
|
||||
if tc.expectErr {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), tc.errString)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
293
pkg/meta/header.go
Normal file
293
pkg/meta/header.go
Normal file
@@ -0,0 +1,293 @@
|
||||
package meta
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
gogotypes "github.com/gogo/protobuf/types"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/merkle"
|
||||
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
"github.com/tendermint/tendermint/version"
|
||||
)
|
||||
|
||||
const (
|
||||
// MaxHeaderBytes is a maximum header size.
|
||||
// NOTE: Because app hash can be of arbitrary size, the header is therefore not
|
||||
// capped in size and thus this number should be seen as a soft max
|
||||
MaxHeaderBytes int64 = 626
|
||||
|
||||
// MaxOverheadForBlock - maximum overhead to encode a block (up to
|
||||
// MaxBlockSizeBytes in size) not including it's parts except Data.
|
||||
// This means it also excludes the overhead for individual transactions.
|
||||
//
|
||||
// Uvarint length of MaxBlockSizeBytes: 4 bytes
|
||||
// 2 fields (2 embedded): 2 bytes
|
||||
// Uvarint length of Data.Txs: 4 bytes
|
||||
// Data.Txs field: 1 byte
|
||||
MaxOverheadForBlock int64 = 11
|
||||
|
||||
// MaxChainIDLen is a maximum length of the chain ID.
|
||||
MaxChainIDLen = 50
|
||||
|
||||
// MaxBlockSizeBytes is the maximum permitted size of the blocks.
|
||||
MaxBlockSizeBytes = 104857600 // 100MB
|
||||
)
|
||||
|
||||
// Header defines the structure of a Tendermint block header.
|
||||
// NOTE: changes to the Header should be duplicated in:
|
||||
// - header.Hash()
|
||||
// - abci.Header
|
||||
// - https://github.com/tendermint/spec/blob/master/spec/blockchain/blockchain.md
|
||||
type Header struct {
|
||||
// basic block info
|
||||
Version version.Consensus `json:"version"`
|
||||
ChainID string `json:"chain_id"`
|
||||
Height int64 `json:"height"`
|
||||
Time time.Time `json:"time"`
|
||||
|
||||
// prev block info
|
||||
LastBlockID BlockID `json:"last_block_id"`
|
||||
|
||||
// hashes of block data
|
||||
LastCommitHash tmbytes.HexBytes `json:"last_commit_hash"` // commit from validators from the last block
|
||||
DataHash tmbytes.HexBytes `json:"data_hash"` // transactions
|
||||
|
||||
// hashes from the app output from the prev block
|
||||
ValidatorsHash tmbytes.HexBytes `json:"validators_hash"` // validators for the current block
|
||||
NextValidatorsHash tmbytes.HexBytes `json:"next_validators_hash"` // validators for the next block
|
||||
ConsensusHash tmbytes.HexBytes `json:"consensus_hash"` // consensus params for current block
|
||||
AppHash tmbytes.HexBytes `json:"app_hash"` // state after txs from the previous block
|
||||
// root hash of all results from the txs from the previous block
|
||||
// see `deterministicResponseDeliverTx` to understand which parts of a tx is hashed into here
|
||||
LastResultsHash tmbytes.HexBytes `json:"last_results_hash"`
|
||||
|
||||
// consensus info
|
||||
EvidenceHash tmbytes.HexBytes `json:"evidence_hash"` // evidence included in the block
|
||||
ProposerAddress crypto.Address `json:"proposer_address"` // original proposer of the block
|
||||
}
|
||||
|
||||
// Populate the Header with state-derived data.
|
||||
// Call this after MakeBlock to complete the Header.
|
||||
func (h *Header) Populate(
|
||||
version version.Consensus, chainID string,
|
||||
timestamp time.Time, lastBlockID BlockID,
|
||||
valHash, nextValHash []byte,
|
||||
consensusHash, appHash, lastResultsHash []byte,
|
||||
proposerAddress crypto.Address,
|
||||
) {
|
||||
h.Version = version
|
||||
h.ChainID = chainID
|
||||
h.Time = timestamp
|
||||
h.LastBlockID = lastBlockID
|
||||
h.ValidatorsHash = valHash
|
||||
h.NextValidatorsHash = nextValHash
|
||||
h.ConsensusHash = consensusHash
|
||||
h.AppHash = appHash
|
||||
h.LastResultsHash = lastResultsHash
|
||||
h.ProposerAddress = proposerAddress
|
||||
}
|
||||
|
||||
// ValidateBasic performs stateless validation on a Header returning an error
|
||||
// if any validation fails.
|
||||
//
|
||||
// NOTE: Timestamp validation is subtle and handled elsewhere.
|
||||
func (h Header) ValidateBasic() error {
|
||||
if h.Version.Block != version.BlockProtocol {
|
||||
return fmt.Errorf("block protocol is incorrect: got: %d, want: %d ", h.Version.Block, version.BlockProtocol)
|
||||
}
|
||||
if len(h.ChainID) > MaxChainIDLen {
|
||||
return fmt.Errorf("chainID is too long; got: %d, max: %d", len(h.ChainID), MaxChainIDLen)
|
||||
}
|
||||
|
||||
if h.Height < 0 {
|
||||
return errors.New("negative Height")
|
||||
} else if h.Height == 0 {
|
||||
return errors.New("zero Height")
|
||||
}
|
||||
|
||||
if err := h.LastBlockID.ValidateBasic(); err != nil {
|
||||
return fmt.Errorf("wrong LastBlockID: %w", err)
|
||||
}
|
||||
|
||||
if err := ValidateHash(h.LastCommitHash); err != nil {
|
||||
return fmt.Errorf("wrong LastCommitHash: %v", err)
|
||||
}
|
||||
|
||||
if err := ValidateHash(h.DataHash); err != nil {
|
||||
return fmt.Errorf("wrong DataHash: %v", err)
|
||||
}
|
||||
|
||||
if err := ValidateHash(h.EvidenceHash); err != nil {
|
||||
return fmt.Errorf("wrong EvidenceHash: %v", err)
|
||||
}
|
||||
|
||||
if len(h.ProposerAddress) != crypto.AddressSize {
|
||||
return fmt.Errorf(
|
||||
"invalid ProposerAddress length; got: %d, expected: %d",
|
||||
len(h.ProposerAddress), crypto.AddressSize,
|
||||
)
|
||||
}
|
||||
|
||||
// Basic validation of hashes related to application data.
|
||||
// Will validate fully against state in state#ValidateBlock.
|
||||
if err := ValidateHash(h.ValidatorsHash); err != nil {
|
||||
return fmt.Errorf("wrong ValidatorsHash: %v", err)
|
||||
}
|
||||
if err := ValidateHash(h.NextValidatorsHash); err != nil {
|
||||
return fmt.Errorf("wrong NextValidatorsHash: %v", err)
|
||||
}
|
||||
if err := ValidateHash(h.ConsensusHash); err != nil {
|
||||
return fmt.Errorf("wrong ConsensusHash: %v", err)
|
||||
}
|
||||
// NOTE: AppHash is arbitrary length
|
||||
if err := ValidateHash(h.LastResultsHash); err != nil {
|
||||
return fmt.Errorf("wrong LastResultsHash: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Hash returns the hash of the header.
|
||||
// It computes a Merkle tree from the header fields
|
||||
// ordered as they appear in the Header.
|
||||
// Returns nil if ValidatorHash is missing,
|
||||
// since a Header is not valid unless there is
|
||||
// a ValidatorsHash (corresponding to the validator set).
|
||||
func (h *Header) Hash() tmbytes.HexBytes {
|
||||
if h == nil || len(h.ValidatorsHash) == 0 {
|
||||
return nil
|
||||
}
|
||||
hpb := h.Version.ToProto()
|
||||
hbz, err := hpb.Marshal()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
pbt, err := gogotypes.StdTimeMarshal(h.Time)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
pbbi := h.LastBlockID.ToProto()
|
||||
bzbi, err := pbbi.Marshal()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return merkle.HashFromByteSlices([][]byte{
|
||||
hbz,
|
||||
CdcEncode(h.ChainID),
|
||||
CdcEncode(h.Height),
|
||||
pbt,
|
||||
bzbi,
|
||||
CdcEncode(h.LastCommitHash),
|
||||
CdcEncode(h.DataHash),
|
||||
CdcEncode(h.ValidatorsHash),
|
||||
CdcEncode(h.NextValidatorsHash),
|
||||
CdcEncode(h.ConsensusHash),
|
||||
CdcEncode(h.AppHash),
|
||||
CdcEncode(h.LastResultsHash),
|
||||
CdcEncode(h.EvidenceHash),
|
||||
CdcEncode(h.ProposerAddress),
|
||||
})
|
||||
}
|
||||
|
||||
// StringIndented returns an indented string representation of the header.
|
||||
func (h *Header) StringIndented(indent string) string {
|
||||
if h == nil {
|
||||
return "nil-Header"
|
||||
}
|
||||
return fmt.Sprintf(`Header{
|
||||
%s Version: %v
|
||||
%s ChainID: %v
|
||||
%s Height: %v
|
||||
%s Time: %v
|
||||
%s LastBlockID: %v
|
||||
%s LastCommit: %v
|
||||
%s Data: %v
|
||||
%s Validators: %v
|
||||
%s NextValidators: %v
|
||||
%s App: %v
|
||||
%s Consensus: %v
|
||||
%s Results: %v
|
||||
%s Evidence: %v
|
||||
%s Proposer: %v
|
||||
%s}#%v`,
|
||||
indent, h.Version,
|
||||
indent, h.ChainID,
|
||||
indent, h.Height,
|
||||
indent, h.Time,
|
||||
indent, h.LastBlockID,
|
||||
indent, h.LastCommitHash,
|
||||
indent, h.DataHash,
|
||||
indent, h.ValidatorsHash,
|
||||
indent, h.NextValidatorsHash,
|
||||
indent, h.AppHash,
|
||||
indent, h.ConsensusHash,
|
||||
indent, h.LastResultsHash,
|
||||
indent, h.EvidenceHash,
|
||||
indent, h.ProposerAddress,
|
||||
indent, h.Hash())
|
||||
}
|
||||
|
||||
// ToProto converts Header to protobuf
|
||||
func (h *Header) ToProto() *tmproto.Header {
|
||||
if h == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &tmproto.Header{
|
||||
Version: h.Version.ToProto(),
|
||||
ChainID: h.ChainID,
|
||||
Height: h.Height,
|
||||
Time: h.Time,
|
||||
LastBlockId: h.LastBlockID.ToProto(),
|
||||
ValidatorsHash: h.ValidatorsHash,
|
||||
NextValidatorsHash: h.NextValidatorsHash,
|
||||
ConsensusHash: h.ConsensusHash,
|
||||
AppHash: h.AppHash,
|
||||
DataHash: h.DataHash,
|
||||
EvidenceHash: h.EvidenceHash,
|
||||
LastResultsHash: h.LastResultsHash,
|
||||
LastCommitHash: h.LastCommitHash,
|
||||
ProposerAddress: h.ProposerAddress,
|
||||
}
|
||||
}
|
||||
|
||||
// FromProto sets a protobuf Header to the given pointer.
|
||||
// It returns an error if the header is invalid.
|
||||
func HeaderFromProto(ph *tmproto.Header) (Header, error) {
|
||||
if ph == nil {
|
||||
return Header{}, errors.New("nil Header")
|
||||
}
|
||||
|
||||
h := new(Header)
|
||||
|
||||
bi, err := BlockIDFromProto(&ph.LastBlockId)
|
||||
if err != nil {
|
||||
return Header{}, err
|
||||
}
|
||||
|
||||
h.Version = version.Consensus{Block: ph.Version.Block, App: ph.Version.App}
|
||||
h.ChainID = ph.ChainID
|
||||
h.Height = ph.Height
|
||||
h.Time = ph.Time
|
||||
h.Height = ph.Height
|
||||
h.LastBlockID = *bi
|
||||
h.ValidatorsHash = ph.ValidatorsHash
|
||||
h.NextValidatorsHash = ph.NextValidatorsHash
|
||||
h.ConsensusHash = ph.ConsensusHash
|
||||
h.AppHash = ph.AppHash
|
||||
h.DataHash = ph.DataHash
|
||||
h.EvidenceHash = ph.EvidenceHash
|
||||
h.LastResultsHash = ph.LastResultsHash
|
||||
h.LastCommitHash = ph.LastCommitHash
|
||||
h.ProposerAddress = ph.ProposerAddress
|
||||
|
||||
return *h, h.ValidateBasic()
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
496
pkg/meta/header_test.go
Normal file
496
pkg/meta/header_test.go
Normal file
@@ -0,0 +1,496 @@
|
||||
package meta_test
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
gogotypes "github.com/gogo/protobuf/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/merkle"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
test "github.com/tendermint/tendermint/internal/test/factory"
|
||||
"github.com/tendermint/tendermint/libs/bytes"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
tmversion "github.com/tendermint/tendermint/proto/tendermint/version"
|
||||
"github.com/tendermint/tendermint/version"
|
||||
)
|
||||
|
||||
var nilBytes []byte
|
||||
|
||||
func TestNilHeaderHashDoesntCrash(t *testing.T) {
|
||||
assert.Equal(t, nilBytes, []byte((*meta.Header)(nil).Hash()))
|
||||
assert.Equal(t, nilBytes, []byte((new(meta.Header)).Hash()))
|
||||
}
|
||||
|
||||
func TestHeaderHash(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
header *meta.Header
|
||||
expectHash bytes.HexBytes
|
||||
}{
|
||||
{"Generates expected hash", &meta.Header{
|
||||
Version: version.Consensus{Block: 1, App: 2},
|
||||
ChainID: "chainId",
|
||||
Height: 3,
|
||||
Time: time.Date(2019, 10, 13, 16, 14, 44, 0, time.UTC),
|
||||
LastBlockID: test.MakeBlockIDWithHash(make([]byte, tmhash.Size)),
|
||||
LastCommitHash: tmhash.Sum([]byte("last_commit_hash")),
|
||||
DataHash: tmhash.Sum([]byte("data_hash")),
|
||||
ValidatorsHash: tmhash.Sum([]byte("validators_hash")),
|
||||
NextValidatorsHash: tmhash.Sum([]byte("next_validators_hash")),
|
||||
ConsensusHash: tmhash.Sum([]byte("consensus_hash")),
|
||||
AppHash: tmhash.Sum([]byte("app_hash")),
|
||||
LastResultsHash: tmhash.Sum([]byte("last_results_hash")),
|
||||
EvidenceHash: tmhash.Sum([]byte("evidence_hash")),
|
||||
ProposerAddress: crypto.AddressHash([]byte("proposer_address")),
|
||||
}, hexBytesFromString("F740121F553B5418C3EFBD343C2DBFE9E007BB67B0D020A0741374BAB65242A4")},
|
||||
{"nil header yields nil", nil, nil},
|
||||
{"nil ValidatorsHash yields nil", &meta.Header{
|
||||
Version: version.Consensus{Block: 1, App: 2},
|
||||
ChainID: "chainId",
|
||||
Height: 3,
|
||||
Time: time.Date(2019, 10, 13, 16, 14, 44, 0, time.UTC),
|
||||
LastBlockID: test.MakeBlockIDWithHash(make([]byte, tmhash.Size)),
|
||||
LastCommitHash: tmhash.Sum([]byte("last_commit_hash")),
|
||||
DataHash: tmhash.Sum([]byte("data_hash")),
|
||||
ValidatorsHash: nil,
|
||||
NextValidatorsHash: tmhash.Sum([]byte("next_validators_hash")),
|
||||
ConsensusHash: tmhash.Sum([]byte("consensus_hash")),
|
||||
AppHash: tmhash.Sum([]byte("app_hash")),
|
||||
LastResultsHash: tmhash.Sum([]byte("last_results_hash")),
|
||||
EvidenceHash: tmhash.Sum([]byte("evidence_hash")),
|
||||
ProposerAddress: crypto.AddressHash([]byte("proposer_address")),
|
||||
}, nil},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
assert.Equal(t, tc.expectHash, tc.header.Hash())
|
||||
|
||||
// We also make sure that all fields are hashed in struct order, and that all
|
||||
// fields in the test struct are non-zero.
|
||||
if tc.header != nil && tc.expectHash != nil {
|
||||
byteSlices := [][]byte{}
|
||||
|
||||
s := reflect.ValueOf(*tc.header)
|
||||
for i := 0; i < s.NumField(); i++ {
|
||||
f := s.Field(i)
|
||||
|
||||
assert.False(t, f.IsZero(), "Found zero-valued field %v",
|
||||
s.Type().Field(i).Name)
|
||||
|
||||
switch f := f.Interface().(type) {
|
||||
case int64, bytes.HexBytes, string:
|
||||
byteSlices = append(byteSlices, meta.CdcEncode(f))
|
||||
case time.Time:
|
||||
bz, err := gogotypes.StdTimeMarshal(f)
|
||||
require.NoError(t, err)
|
||||
byteSlices = append(byteSlices, bz)
|
||||
case version.Consensus:
|
||||
pbc := tmversion.Consensus{
|
||||
Block: f.Block,
|
||||
App: f.App,
|
||||
}
|
||||
bz, err := pbc.Marshal()
|
||||
require.NoError(t, err)
|
||||
byteSlices = append(byteSlices, bz)
|
||||
case meta.BlockID:
|
||||
pbbi := f.ToProto()
|
||||
bz, err := pbbi.Marshal()
|
||||
require.NoError(t, err)
|
||||
byteSlices = append(byteSlices, bz)
|
||||
default:
|
||||
t.Errorf("unknown type %T", f)
|
||||
}
|
||||
}
|
||||
assert.Equal(t,
|
||||
bytes.HexBytes(merkle.HashFromByteSlices(byteSlices)), tc.header.Hash())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxHeaderBytes(t *testing.T) {
|
||||
// Construct a UTF-8 string of MaxChainIDLen length using the supplementary
|
||||
// characters.
|
||||
// Each supplementary character takes 4 bytes.
|
||||
// http://www.i18nguy.com/unicode/supplementary-test.html
|
||||
maxChainID := ""
|
||||
for i := 0; i < meta.MaxChainIDLen; i++ {
|
||||
maxChainID += "𠜎"
|
||||
}
|
||||
|
||||
// time is varint encoded so need to pick the max.
|
||||
// year int, month Month, day, hour, min, sec, nsec int, loc *Location
|
||||
timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC)
|
||||
|
||||
h := meta.Header{
|
||||
Version: version.Consensus{Block: math.MaxInt64, App: math.MaxInt64},
|
||||
ChainID: maxChainID,
|
||||
Height: math.MaxInt64,
|
||||
Time: timestamp,
|
||||
LastBlockID: meta.BlockID{make([]byte, tmhash.Size), meta.PartSetHeader{math.MaxInt32, make([]byte, tmhash.Size)}},
|
||||
LastCommitHash: tmhash.Sum([]byte("last_commit_hash")),
|
||||
DataHash: tmhash.Sum([]byte("data_hash")),
|
||||
ValidatorsHash: tmhash.Sum([]byte("validators_hash")),
|
||||
NextValidatorsHash: tmhash.Sum([]byte("next_validators_hash")),
|
||||
ConsensusHash: tmhash.Sum([]byte("consensus_hash")),
|
||||
AppHash: tmhash.Sum([]byte("app_hash")),
|
||||
LastResultsHash: tmhash.Sum([]byte("last_results_hash")),
|
||||
EvidenceHash: tmhash.Sum([]byte("evidence_hash")),
|
||||
ProposerAddress: crypto.AddressHash([]byte("proposer_address")),
|
||||
}
|
||||
|
||||
bz, err := h.ToProto().Marshal()
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.EqualValues(t, meta.MaxHeaderBytes, int64(len(bz)))
|
||||
}
|
||||
|
||||
func randCommit(now time.Time) *meta.Commit {
|
||||
lastID := test.MakeBlockID()
|
||||
h := int64(3)
|
||||
voteSet, _, vals := test.RandVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1)
|
||||
commit, err := test.MakeCommit(lastID, h-1, 1, voteSet, vals, now)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return commit
|
||||
}
|
||||
|
||||
func hexBytesFromString(s string) bytes.HexBytes {
|
||||
b, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bytes.HexBytes(b)
|
||||
}
|
||||
|
||||
func TestHeader_ValidateBasic(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
header meta.Header
|
||||
expectErr bool
|
||||
errString string
|
||||
}{
|
||||
{
|
||||
"invalid version block",
|
||||
meta.Header{Version: version.Consensus{Block: version.BlockProtocol + 1}},
|
||||
true, "block protocol is incorrect",
|
||||
},
|
||||
{
|
||||
"invalid chain ID length",
|
||||
meta.Header{
|
||||
Version: version.Consensus{Block: version.BlockProtocol},
|
||||
ChainID: string(make([]byte, meta.MaxChainIDLen+1)),
|
||||
},
|
||||
true, "chainID is too long",
|
||||
},
|
||||
{
|
||||
"invalid height (negative)",
|
||||
meta.Header{
|
||||
Version: version.Consensus{Block: version.BlockProtocol},
|
||||
ChainID: string(make([]byte, meta.MaxChainIDLen)),
|
||||
Height: -1,
|
||||
},
|
||||
true, "negative Height",
|
||||
},
|
||||
{
|
||||
"invalid height (zero)",
|
||||
meta.Header{
|
||||
Version: version.Consensus{Block: version.BlockProtocol},
|
||||
ChainID: string(make([]byte, meta.MaxChainIDLen)),
|
||||
Height: 0,
|
||||
},
|
||||
true, "zero Height",
|
||||
},
|
||||
{
|
||||
"invalid block ID hash",
|
||||
meta.Header{
|
||||
Version: version.Consensus{Block: version.BlockProtocol},
|
||||
ChainID: string(make([]byte, meta.MaxChainIDLen)),
|
||||
Height: 1,
|
||||
LastBlockID: meta.BlockID{
|
||||
Hash: make([]byte, tmhash.Size+1),
|
||||
},
|
||||
},
|
||||
true, "wrong Hash",
|
||||
},
|
||||
{
|
||||
"invalid block ID parts header hash",
|
||||
meta.Header{
|
||||
Version: version.Consensus{Block: version.BlockProtocol},
|
||||
ChainID: string(make([]byte, meta.MaxChainIDLen)),
|
||||
Height: 1,
|
||||
LastBlockID: meta.BlockID{
|
||||
Hash: make([]byte, tmhash.Size),
|
||||
PartSetHeader: meta.PartSetHeader{
|
||||
Hash: make([]byte, tmhash.Size+1),
|
||||
},
|
||||
},
|
||||
},
|
||||
true, "wrong PartSetHeader",
|
||||
},
|
||||
{
|
||||
"invalid last commit hash",
|
||||
meta.Header{
|
||||
Version: version.Consensus{Block: version.BlockProtocol},
|
||||
ChainID: string(make([]byte, meta.MaxChainIDLen)),
|
||||
Height: 1,
|
||||
LastBlockID: meta.BlockID{
|
||||
Hash: make([]byte, tmhash.Size),
|
||||
PartSetHeader: meta.PartSetHeader{
|
||||
Hash: make([]byte, tmhash.Size),
|
||||
},
|
||||
},
|
||||
LastCommitHash: make([]byte, tmhash.Size+1),
|
||||
},
|
||||
true, "wrong LastCommitHash",
|
||||
},
|
||||
{
|
||||
"invalid data hash",
|
||||
meta.Header{
|
||||
Version: version.Consensus{Block: version.BlockProtocol},
|
||||
ChainID: string(make([]byte, meta.MaxChainIDLen)),
|
||||
Height: 1,
|
||||
LastBlockID: meta.BlockID{
|
||||
Hash: make([]byte, tmhash.Size),
|
||||
PartSetHeader: meta.PartSetHeader{
|
||||
Hash: make([]byte, tmhash.Size),
|
||||
},
|
||||
},
|
||||
LastCommitHash: make([]byte, tmhash.Size),
|
||||
DataHash: make([]byte, tmhash.Size+1),
|
||||
},
|
||||
true, "wrong DataHash",
|
||||
},
|
||||
{
|
||||
"invalid evidence hash",
|
||||
meta.Header{
|
||||
Version: version.Consensus{Block: version.BlockProtocol},
|
||||
ChainID: string(make([]byte, meta.MaxChainIDLen)),
|
||||
Height: 1,
|
||||
LastBlockID: meta.BlockID{
|
||||
Hash: make([]byte, tmhash.Size),
|
||||
PartSetHeader: meta.PartSetHeader{
|
||||
Hash: make([]byte, tmhash.Size),
|
||||
},
|
||||
},
|
||||
LastCommitHash: make([]byte, tmhash.Size),
|
||||
DataHash: make([]byte, tmhash.Size),
|
||||
EvidenceHash: make([]byte, tmhash.Size+1),
|
||||
},
|
||||
true, "wrong EvidenceHash",
|
||||
},
|
||||
{
|
||||
"invalid proposer address",
|
||||
meta.Header{
|
||||
Version: version.Consensus{Block: version.BlockProtocol},
|
||||
ChainID: string(make([]byte, meta.MaxChainIDLen)),
|
||||
Height: 1,
|
||||
LastBlockID: meta.BlockID{
|
||||
Hash: make([]byte, tmhash.Size),
|
||||
PartSetHeader: meta.PartSetHeader{
|
||||
Hash: make([]byte, tmhash.Size),
|
||||
},
|
||||
},
|
||||
LastCommitHash: make([]byte, tmhash.Size),
|
||||
DataHash: make([]byte, tmhash.Size),
|
||||
EvidenceHash: make([]byte, tmhash.Size),
|
||||
ProposerAddress: make([]byte, crypto.AddressSize+1),
|
||||
},
|
||||
true, "invalid ProposerAddress length",
|
||||
},
|
||||
{
|
||||
"invalid validator hash",
|
||||
meta.Header{
|
||||
Version: version.Consensus{Block: version.BlockProtocol},
|
||||
ChainID: string(make([]byte, meta.MaxChainIDLen)),
|
||||
Height: 1,
|
||||
LastBlockID: meta.BlockID{
|
||||
Hash: make([]byte, tmhash.Size),
|
||||
PartSetHeader: meta.PartSetHeader{
|
||||
Hash: make([]byte, tmhash.Size),
|
||||
},
|
||||
},
|
||||
LastCommitHash: make([]byte, tmhash.Size),
|
||||
DataHash: make([]byte, tmhash.Size),
|
||||
EvidenceHash: make([]byte, tmhash.Size),
|
||||
ProposerAddress: make([]byte, crypto.AddressSize),
|
||||
ValidatorsHash: make([]byte, tmhash.Size+1),
|
||||
},
|
||||
true, "wrong ValidatorsHash",
|
||||
},
|
||||
{
|
||||
"invalid next validator hash",
|
||||
meta.Header{
|
||||
Version: version.Consensus{Block: version.BlockProtocol},
|
||||
ChainID: string(make([]byte, meta.MaxChainIDLen)),
|
||||
Height: 1,
|
||||
LastBlockID: meta.BlockID{
|
||||
Hash: make([]byte, tmhash.Size),
|
||||
PartSetHeader: meta.PartSetHeader{
|
||||
Hash: make([]byte, tmhash.Size),
|
||||
},
|
||||
},
|
||||
LastCommitHash: make([]byte, tmhash.Size),
|
||||
DataHash: make([]byte, tmhash.Size),
|
||||
EvidenceHash: make([]byte, tmhash.Size),
|
||||
ProposerAddress: make([]byte, crypto.AddressSize),
|
||||
ValidatorsHash: make([]byte, tmhash.Size),
|
||||
NextValidatorsHash: make([]byte, tmhash.Size+1),
|
||||
},
|
||||
true, "wrong NextValidatorsHash",
|
||||
},
|
||||
{
|
||||
"invalid consensus hash",
|
||||
meta.Header{
|
||||
Version: version.Consensus{Block: version.BlockProtocol},
|
||||
ChainID: string(make([]byte, meta.MaxChainIDLen)),
|
||||
Height: 1,
|
||||
LastBlockID: meta.BlockID{
|
||||
Hash: make([]byte, tmhash.Size),
|
||||
PartSetHeader: meta.PartSetHeader{
|
||||
Hash: make([]byte, tmhash.Size),
|
||||
},
|
||||
},
|
||||
LastCommitHash: make([]byte, tmhash.Size),
|
||||
DataHash: make([]byte, tmhash.Size),
|
||||
EvidenceHash: make([]byte, tmhash.Size),
|
||||
ProposerAddress: make([]byte, crypto.AddressSize),
|
||||
ValidatorsHash: make([]byte, tmhash.Size),
|
||||
NextValidatorsHash: make([]byte, tmhash.Size),
|
||||
ConsensusHash: make([]byte, tmhash.Size+1),
|
||||
},
|
||||
true, "wrong ConsensusHash",
|
||||
},
|
||||
{
|
||||
"invalid last results hash",
|
||||
meta.Header{
|
||||
Version: version.Consensus{Block: version.BlockProtocol},
|
||||
ChainID: string(make([]byte, meta.MaxChainIDLen)),
|
||||
Height: 1,
|
||||
LastBlockID: meta.BlockID{
|
||||
Hash: make([]byte, tmhash.Size),
|
||||
PartSetHeader: meta.PartSetHeader{
|
||||
Hash: make([]byte, tmhash.Size),
|
||||
},
|
||||
},
|
||||
LastCommitHash: make([]byte, tmhash.Size),
|
||||
DataHash: make([]byte, tmhash.Size),
|
||||
EvidenceHash: make([]byte, tmhash.Size),
|
||||
ProposerAddress: make([]byte, crypto.AddressSize),
|
||||
ValidatorsHash: make([]byte, tmhash.Size),
|
||||
NextValidatorsHash: make([]byte, tmhash.Size),
|
||||
ConsensusHash: make([]byte, tmhash.Size),
|
||||
LastResultsHash: make([]byte, tmhash.Size+1),
|
||||
},
|
||||
true, "wrong LastResultsHash",
|
||||
},
|
||||
{
|
||||
"valid header",
|
||||
meta.Header{
|
||||
Version: version.Consensus{Block: version.BlockProtocol},
|
||||
ChainID: string(make([]byte, meta.MaxChainIDLen)),
|
||||
Height: 1,
|
||||
LastBlockID: meta.BlockID{
|
||||
Hash: make([]byte, tmhash.Size),
|
||||
PartSetHeader: meta.PartSetHeader{
|
||||
Hash: make([]byte, tmhash.Size),
|
||||
},
|
||||
},
|
||||
LastCommitHash: make([]byte, tmhash.Size),
|
||||
DataHash: make([]byte, tmhash.Size),
|
||||
EvidenceHash: make([]byte, tmhash.Size),
|
||||
ProposerAddress: make([]byte, crypto.AddressSize),
|
||||
ValidatorsHash: make([]byte, tmhash.Size),
|
||||
NextValidatorsHash: make([]byte, tmhash.Size),
|
||||
ConsensusHash: make([]byte, tmhash.Size),
|
||||
LastResultsHash: make([]byte, tmhash.Size),
|
||||
},
|
||||
false, "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := tc.header.ValidateBasic()
|
||||
if tc.expectErr {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), tc.errString)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeaderProto(t *testing.T) {
|
||||
h1 := test.MakeRandomHeader()
|
||||
tc := []struct {
|
||||
msg string
|
||||
h1 *meta.Header
|
||||
expPass bool
|
||||
}{
|
||||
{"success", h1, true},
|
||||
{"failure empty Header", &meta.Header{}, false},
|
||||
}
|
||||
|
||||
for _, tt := range tc {
|
||||
tt := tt
|
||||
t.Run(tt.msg, func(t *testing.T) {
|
||||
pb := tt.h1.ToProto()
|
||||
h, err := meta.HeaderFromProto(pb)
|
||||
if tt.expPass {
|
||||
require.NoError(t, err, tt.msg)
|
||||
require.Equal(t, tt.h1, &h, tt.msg)
|
||||
} else {
|
||||
require.Error(t, err, tt.msg)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeaderHashVector(t *testing.T) {
|
||||
chainID := "test"
|
||||
h := meta.Header{
|
||||
Version: version.Consensus{Block: 1, App: 1},
|
||||
ChainID: chainID,
|
||||
Height: 50,
|
||||
Time: time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC),
|
||||
LastBlockID: meta.BlockID{},
|
||||
LastCommitHash: []byte("f2564c78071e26643ae9b3e2a19fa0dc10d4d9e873aa0be808660123f11a1e78"),
|
||||
DataHash: []byte("f2564c78071e26643ae9b3e2a19fa0dc10d4d9e873aa0be808660123f11a1e78"),
|
||||
ValidatorsHash: []byte("f2564c78071e26643ae9b3e2a19fa0dc10d4d9e873aa0be808660123f11a1e78"),
|
||||
NextValidatorsHash: []byte("f2564c78071e26643ae9b3e2a19fa0dc10d4d9e873aa0be808660123f11a1e78"),
|
||||
ConsensusHash: []byte("f2564c78071e26643ae9b3e2a19fa0dc10d4d9e873aa0be808660123f11a1e78"),
|
||||
AppHash: []byte("f2564c78071e26643ae9b3e2a19fa0dc10d4d9e873aa0be808660123f11a1e78"),
|
||||
|
||||
LastResultsHash: []byte("f2564c78071e26643ae9b3e2a19fa0dc10d4d9e873aa0be808660123f11a1e78"),
|
||||
|
||||
EvidenceHash: []byte("f2564c78071e26643ae9b3e2a19fa0dc10d4d9e873aa0be808660123f11a1e78"),
|
||||
ProposerAddress: []byte("2915b7b15f979e48ebc61774bb1d86ba3136b7eb"),
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
header meta.Header
|
||||
expBytes string
|
||||
}{
|
||||
{header: h, expBytes: "87b6117ac7f827d656f178a3d6d30b24b205db2b6a3a053bae8baf4618570bfc"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
hash := tc.header.Hash()
|
||||
require.Equal(t, tc.expBytes, hex.EncodeToString(hash))
|
||||
}
|
||||
}
|
||||
124
pkg/meta/id.go
Normal file
124
pkg/meta/id.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package meta
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
|
||||
// BlockID
|
||||
type BlockID struct {
|
||||
Hash tmbytes.HexBytes `json:"hash"`
|
||||
PartSetHeader PartSetHeader `json:"parts"`
|
||||
}
|
||||
|
||||
// BlockIDFlag indicates which BlockID the signature is for.
|
||||
type BlockIDFlag byte
|
||||
|
||||
const (
|
||||
// BlockIDFlagAbsent - no vote was received from a validator.
|
||||
BlockIDFlagAbsent BlockIDFlag = iota + 1
|
||||
// BlockIDFlagCommit - voted for the Commit.BlockID.
|
||||
BlockIDFlagCommit
|
||||
// BlockIDFlagNil - voted for nil.
|
||||
BlockIDFlagNil
|
||||
)
|
||||
|
||||
// Equals returns true if the BlockID matches the given BlockID
|
||||
func (blockID BlockID) Equals(other BlockID) bool {
|
||||
return bytes.Equal(blockID.Hash, other.Hash) &&
|
||||
blockID.PartSetHeader.Equals(other.PartSetHeader)
|
||||
}
|
||||
|
||||
// Key returns a machine-readable string representation of the BlockID
|
||||
func (blockID BlockID) Key() string {
|
||||
pbph := blockID.PartSetHeader.ToProto()
|
||||
bz, err := pbph.Marshal()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return fmt.Sprint(string(blockID.Hash), string(bz))
|
||||
}
|
||||
|
||||
// ValidateBasic performs basic validation.
|
||||
func (blockID BlockID) ValidateBasic() error {
|
||||
// Hash can be empty in case of POLBlockID in Proposal.
|
||||
if err := ValidateHash(blockID.Hash); err != nil {
|
||||
return fmt.Errorf("wrong Hash: %w", err)
|
||||
}
|
||||
if err := blockID.PartSetHeader.ValidateBasic(); err != nil {
|
||||
return fmt.Errorf("wrong PartSetHeader: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsZero returns true if this is the BlockID of a nil block.
|
||||
func (blockID BlockID) IsZero() bool {
|
||||
return len(blockID.Hash) == 0 &&
|
||||
blockID.PartSetHeader.IsZero()
|
||||
}
|
||||
|
||||
// IsComplete returns true if this is a valid BlockID of a non-nil block.
|
||||
func (blockID BlockID) IsComplete() bool {
|
||||
return len(blockID.Hash) == tmhash.Size &&
|
||||
blockID.PartSetHeader.Total > 0 &&
|
||||
len(blockID.PartSetHeader.Hash) == tmhash.Size
|
||||
}
|
||||
|
||||
// String returns a human readable string representation of the BlockID.
|
||||
//
|
||||
// 1. hash
|
||||
// 2. part set header
|
||||
//
|
||||
// See PartSetHeader#String
|
||||
func (blockID BlockID) String() string {
|
||||
return fmt.Sprintf(`%v:%v`, blockID.Hash, blockID.PartSetHeader)
|
||||
}
|
||||
|
||||
// ToProto converts BlockID to protobuf
|
||||
func (blockID *BlockID) ToProto() tmproto.BlockID {
|
||||
if blockID == nil {
|
||||
return tmproto.BlockID{}
|
||||
}
|
||||
|
||||
return tmproto.BlockID{
|
||||
Hash: blockID.Hash,
|
||||
PartSetHeader: blockID.PartSetHeader.ToProto(),
|
||||
}
|
||||
}
|
||||
|
||||
// FromProto sets a protobuf BlockID to the given pointer.
|
||||
// It returns an error if the block id is invalid.
|
||||
func BlockIDFromProto(bID *tmproto.BlockID) (*BlockID, error) {
|
||||
if bID == nil {
|
||||
return nil, errors.New("nil BlockID")
|
||||
}
|
||||
|
||||
blockID := new(BlockID)
|
||||
ph, err := PartSetHeaderFromProto(&bID.PartSetHeader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blockID.PartSetHeader = *ph
|
||||
blockID.Hash = bID.Hash
|
||||
|
||||
return blockID, blockID.ValidateBasic()
|
||||
}
|
||||
|
||||
// ValidateHash returns an error if the hash is not empty, but its
|
||||
// size != tmhash.Size.
|
||||
func ValidateHash(h []byte) error {
|
||||
if len(h) > 0 && len(h) != tmhash.Size {
|
||||
return fmt.Errorf("expected size to be %d bytes, got %d bytes",
|
||||
tmhash.Size,
|
||||
len(h),
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
91
pkg/meta/id_test.go
Normal file
91
pkg/meta/id_test.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package meta_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
test "github.com/tendermint/tendermint/internal/test/factory"
|
||||
"github.com/tendermint/tendermint/libs/bytes"
|
||||
"github.com/tendermint/tendermint/pkg/meta"
|
||||
)
|
||||
|
||||
func TestBlockIDEquals(t *testing.T) {
|
||||
var (
|
||||
blockID = meta.BlockID{[]byte("hash"), meta.PartSetHeader{2, []byte("part_set_hash")}}
|
||||
blockIDDuplicate = meta.BlockID{[]byte("hash"), meta.PartSetHeader{2, []byte("part_set_hash")}}
|
||||
blockIDDifferent = meta.BlockID{[]byte("different_hash"), meta.PartSetHeader{2, []byte("part_set_hash")}}
|
||||
blockIDEmpty = meta.BlockID{}
|
||||
)
|
||||
|
||||
assert.True(t, blockID.Equals(blockIDDuplicate))
|
||||
assert.False(t, blockID.Equals(blockIDDifferent))
|
||||
assert.False(t, blockID.Equals(blockIDEmpty))
|
||||
assert.True(t, blockIDEmpty.Equals(blockIDEmpty))
|
||||
assert.False(t, blockIDEmpty.Equals(blockIDDifferent))
|
||||
}
|
||||
|
||||
func TestBlockIDValidateBasic(t *testing.T) {
|
||||
validBlockID := meta.BlockID{
|
||||
Hash: bytes.HexBytes{},
|
||||
PartSetHeader: meta.PartSetHeader{
|
||||
Total: 1,
|
||||
Hash: bytes.HexBytes{},
|
||||
},
|
||||
}
|
||||
|
||||
invalidBlockID := meta.BlockID{
|
||||
Hash: []byte{0},
|
||||
PartSetHeader: meta.PartSetHeader{
|
||||
Total: 1,
|
||||
Hash: []byte{0},
|
||||
},
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
testName string
|
||||
blockIDHash bytes.HexBytes
|
||||
blockIDPartSetHeader meta.PartSetHeader
|
||||
expectErr bool
|
||||
}{
|
||||
{"Valid BlockID", validBlockID.Hash, validBlockID.PartSetHeader, false},
|
||||
{"Invalid BlockID", invalidBlockID.Hash, validBlockID.PartSetHeader, true},
|
||||
{"Invalid BlockID", validBlockID.Hash, invalidBlockID.PartSetHeader, true},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
blockID := meta.BlockID{
|
||||
Hash: tc.blockIDHash,
|
||||
PartSetHeader: tc.blockIDPartSetHeader,
|
||||
}
|
||||
assert.Equal(t, tc.expectErr, blockID.ValidateBasic() != nil, "Validate Basic had an unexpected result")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockIDProtoBuf(t *testing.T) {
|
||||
blockID := test.MakeBlockIDWithHash([]byte("hash"))
|
||||
testCases := []struct {
|
||||
msg string
|
||||
bid1 *meta.BlockID
|
||||
expPass bool
|
||||
}{
|
||||
{"success", &blockID, true},
|
||||
{"success empty", &meta.BlockID{}, true},
|
||||
{"failure BlockID nil", nil, false},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
protoBlockID := tc.bid1.ToProto()
|
||||
|
||||
bi, err := meta.BlockIDFromProto(&protoBlockID)
|
||||
if tc.expPass {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.bid1, bi, tc.msg)
|
||||
} else {
|
||||
require.NotEqual(t, tc.bid1, bi, tc.msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package meta
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -20,6 +20,14 @@ var (
|
||||
ErrPartSetInvalidProof = errors.New("error part set invalid proof")
|
||||
)
|
||||
|
||||
const (
|
||||
// BlockPartSizeBytes is the size of one block part.
|
||||
BlockPartSizeBytes uint32 = 65536 // 64kB
|
||||
|
||||
// MaxBlockPartsCount is the maximum number of block parts.
|
||||
MaxBlockPartsCount = (MaxBlockSizeBytes / BlockPartSizeBytes) + 1
|
||||
)
|
||||
|
||||
type Part struct {
|
||||
Index uint32 `json:"index"`
|
||||
Bytes tmbytes.HexBytes `json:"bytes"`
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package meta
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
75
pkg/meta/utils.go
Normal file
75
pkg/meta/utils.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package meta
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
gogotypes "github.com/gogo/protobuf/types"
|
||||
|
||||
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
||||
)
|
||||
|
||||
// Go lacks a simple and safe way to see if something is a typed nil.
|
||||
// See:
|
||||
// - https://dave.cheney.net/2017/08/09/typed-nils-in-go-2
|
||||
// - https://groups.google.com/forum/#!topic/golang-nuts/wnH302gBa4I/discussion
|
||||
// - https://github.com/golang/go/issues/21538
|
||||
func isTypedNil(o interface{}) bool {
|
||||
rv := reflect.ValueOf(o)
|
||||
switch rv.Kind() {
|
||||
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice:
|
||||
return rv.IsNil()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if it has zero length.
|
||||
func isEmpty(o interface{}) bool {
|
||||
rv := reflect.ValueOf(o)
|
||||
switch rv.Kind() {
|
||||
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
|
||||
return rv.Len() == 0
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// CdcEncode returns nil if the input is nil, otherwise returns
|
||||
// proto.Marshal(<type>Value{Value: item})
|
||||
func CdcEncode(item interface{}) []byte {
|
||||
if item != nil && !isTypedNil(item) && !isEmpty(item) {
|
||||
switch item := item.(type) {
|
||||
case string:
|
||||
i := gogotypes.StringValue{
|
||||
Value: item,
|
||||
}
|
||||
bz, err := i.Marshal()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return bz
|
||||
case int64:
|
||||
i := gogotypes.Int64Value{
|
||||
Value: item,
|
||||
}
|
||||
bz, err := i.Marshal()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return bz
|
||||
case tmbytes.HexBytes:
|
||||
i := gogotypes.BytesValue{
|
||||
Value: item,
|
||||
}
|
||||
bz, err := i.Marshal()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return bz
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package p2p
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -2,7 +2,7 @@
|
||||
// Originally Copyright (c) 2013-2014 Conformal Systems LLC.
|
||||
// https://github.com/conformal/btcd/blob/master/LICENSE
|
||||
|
||||
package types
|
||||
package p2p
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package p2p
|
||||
|
||||
import (
|
||||
"net"
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package p2p
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package p2p
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package p2p
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package p2p
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
@@ -1,4 +1,4 @@
|
||||
package types_test
|
||||
package p2p_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
@@ -8,16 +8,16 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
tmrand "github.com/tendermint/tendermint/libs/rand"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
"github.com/tendermint/tendermint/pkg/p2p"
|
||||
)
|
||||
|
||||
func TestLoadOrGenNodeKey(t *testing.T) {
|
||||
filePath := filepath.Join(os.TempDir(), tmrand.Str(12)+"_peer_id.json")
|
||||
|
||||
nodeKey, err := types.LoadOrGenNodeKey(filePath)
|
||||
nodeKey, err := p2p.LoadOrGenNodeKey(filePath)
|
||||
require.Nil(t, err)
|
||||
|
||||
nodeKey2, err := types.LoadOrGenNodeKey(filePath)
|
||||
nodeKey2, err := p2p.LoadOrGenNodeKey(filePath)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, nodeKey, nodeKey2)
|
||||
}
|
||||
@@ -25,13 +25,13 @@ func TestLoadOrGenNodeKey(t *testing.T) {
|
||||
func TestLoadNodeKey(t *testing.T) {
|
||||
filePath := filepath.Join(os.TempDir(), tmrand.Str(12)+"_peer_id.json")
|
||||
|
||||
_, err := types.LoadNodeKey(filePath)
|
||||
_, err := p2p.LoadNodeKey(filePath)
|
||||
require.True(t, os.IsNotExist(err))
|
||||
|
||||
_, err = types.LoadOrGenNodeKey(filePath)
|
||||
_, err = p2p.LoadOrGenNodeKey(filePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
nodeKey, err := types.LoadNodeKey(filePath)
|
||||
nodeKey, err := p2p.LoadNodeKey(filePath)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, nodeKey)
|
||||
}
|
||||
@@ -40,7 +40,7 @@ func TestNodeKeySaveAs(t *testing.T) {
|
||||
filePath := filepath.Join(os.TempDir(), tmrand.Str(12)+"_peer_id.json")
|
||||
require.NoFileExists(t, filePath)
|
||||
|
||||
nodeKey := types.GenNodeKey()
|
||||
nodeKey := p2p.GenNodeKey()
|
||||
require.NoError(t, nodeKey.SaveAs(filePath))
|
||||
require.FileExists(t, filePath)
|
||||
}
|
||||
1259
types/block.go
1259
types/block.go
File diff suppressed because it is too large
Load Diff
1349
types/block_test.go
1349
types/block_test.go
File diff suppressed because it is too large
Load Diff
@@ -1,47 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
gogotypes "github.com/gogo/protobuf/types"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/bytes"
|
||||
)
|
||||
|
||||
// cdcEncode returns nil if the input is nil, otherwise returns
|
||||
// proto.Marshal(<type>Value{Value: item})
|
||||
func cdcEncode(item interface{}) []byte {
|
||||
if item != nil && !isTypedNil(item) && !isEmpty(item) {
|
||||
switch item := item.(type) {
|
||||
case string:
|
||||
i := gogotypes.StringValue{
|
||||
Value: item,
|
||||
}
|
||||
bz, err := i.Marshal()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return bz
|
||||
case int64:
|
||||
i := gogotypes.Int64Value{
|
||||
Value: item,
|
||||
}
|
||||
bz, err := i.Marshal()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return bz
|
||||
case bytes.HexBytes:
|
||||
i := gogotypes.BytesValue{
|
||||
Value: item,
|
||||
}
|
||||
bz, err := i.Marshal()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return bz
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
tmmath "github.com/tendermint/tendermint/libs/math"
|
||||
)
|
||||
|
||||
var (
|
||||
// MaxSignatureSize is a maximum allowed signature size for the Proposal
|
||||
// and Vote.
|
||||
// XXX: secp256k1 does not have Size nor MaxSize defined.
|
||||
MaxSignatureSize = tmmath.MaxInt(ed25519.SignatureSize, 64)
|
||||
)
|
||||
|
||||
// Signable is an interface for all signable things.
|
||||
// It typically removes signatures before serializing.
|
||||
// SignBytes returns the bytes to be signed
|
||||
// NOTE: chainIDs are part of the SignBytes but not
|
||||
// necessarily the object themselves.
|
||||
// NOTE: Expected to panic if there is an error marshaling.
|
||||
type Signable interface {
|
||||
SignBytes(chainID string) []byte
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package types
|
||||
|
||||
import tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
|
||||
// IsVoteTypeValid returns true if t is a valid vote type.
|
||||
func IsVoteTypeValid(t tmproto.SignedMsgType) bool {
|
||||
switch t {
|
||||
case tmproto.PrevoteType, tmproto.PrecommitType:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
|
||||
func makeCommit(blockID BlockID, height int64, round int32,
|
||||
voteSet *VoteSet, validators []PrivValidator, now time.Time) (*Commit, error) {
|
||||
|
||||
// all sign
|
||||
for i := 0; i < len(validators); i++ {
|
||||
pubKey, err := validators[i].GetPubKey(context.Background())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't get pubkey: %w", err)
|
||||
}
|
||||
vote := &Vote{
|
||||
ValidatorAddress: pubKey.Address(),
|
||||
ValidatorIndex: int32(i),
|
||||
Height: height,
|
||||
Round: round,
|
||||
Type: tmproto.PrecommitType,
|
||||
BlockID: blockID,
|
||||
Timestamp: now,
|
||||
}
|
||||
|
||||
_, err = signAddVote(validators[i], vote, voteSet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return voteSet.MakeCommit(), nil
|
||||
}
|
||||
|
||||
func signAddVote(privVal PrivValidator, vote *Vote, voteSet *VoteSet) (signed bool, err error) {
|
||||
v := vote.ToProto()
|
||||
err = privVal.SignVote(context.Background(), voteSet.ChainID(), v)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
vote.Signature = v.Signature
|
||||
return voteSet.AddVote(vote)
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package types
|
||||
|
||||
import "reflect"
|
||||
|
||||
// Go lacks a simple and safe way to see if something is a typed nil.
|
||||
// See:
|
||||
// - https://dave.cheney.net/2017/08/09/typed-nils-in-go-2
|
||||
// - https://groups.google.com/forum/#!topic/golang-nuts/wnH302gBa4I/discussion
|
||||
// - https://github.com/golang/go/issues/21538
|
||||
func isTypedNil(o interface{}) bool {
|
||||
rv := reflect.ValueOf(o)
|
||||
switch rv.Kind() {
|
||||
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice:
|
||||
return rv.IsNil()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if it has zero length.
|
||||
func isEmpty(o interface{}) bool {
|
||||
rv := reflect.ValueOf(o)
|
||||
switch rv.Kind() {
|
||||
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
|
||||
return rv.Len() == 0
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user