Compare commits

...

2 Commits

Author SHA1 Message Date
Callum Waters
508b7f9758 split up packages 2021-08-09 13:30:27 +02:00
Aleksandr Bezobchuk
e5ffe132ae pkg/block: move from types/ 2021-08-06 18:01:11 -04:00
72 changed files with 3874 additions and 3807 deletions

View File

@@ -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)
}

View File

@@ -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 {

View File

@@ -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)
}

View File

@@ -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(),

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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 {

View 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
View 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
}
//--------------------------------------------------------------------------------

View File

@@ -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
}

View File

@@ -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
View 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()))
}

View 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,
}
}

View File

@@ -1,4 +1,4 @@
package types
package consensus
import "fmt"

View File

@@ -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)

View File

@@ -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},
}
}

View File

@@ -1,4 +1,4 @@
package types
package consensus
// UNSTABLE
var (

View File

@@ -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)
}

View File

@@ -1,4 +1,4 @@
package types
package consensus
import (
"bytes"

View File

@@ -1,4 +1,4 @@
package types
package consensus
import (
"bytes"

View File

@@ -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
}

View File

@@ -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)

View File

@@ -1,4 +1,4 @@
package types
package consensus
import (
abci "github.com/tendermint/tendermint/abci/types"

View File

@@ -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{

View File

@@ -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")
}

View File

@@ -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)

View File

@@ -1,4 +1,4 @@
package types
package consensus
import (
"bytes"

View File

@@ -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
}

View 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)
}
}
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}
}

View File

@@ -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)
}
//--------------------------------------------------------------------------------

View File

@@ -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

View File

@@ -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

View File

@@ -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()),
},
},
})

View File

@@ -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 {

View File

@@ -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()))
}

View File

@@ -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(),

View File

@@ -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),
},

View File

@@ -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",
},
}

View File

@@ -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
}

View File

@@ -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)
}
}
}

View File

@@ -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())
}

View File

@@ -1,4 +1,4 @@
package types
package mempool
import (
"bytes"

View File

@@ -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

View File

@@ -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
View 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
View 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
View 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
View 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
View 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
View 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)
}
}
}

View File

@@ -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"`

View File

@@ -1,4 +1,4 @@
package types
package meta
import (
"io/ioutil"

75
pkg/meta/utils.go Normal file
View 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
}

View File

@@ -1,4 +1,4 @@
package types
package p2p
import (
"fmt"

View File

@@ -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"

View File

@@ -1,4 +1,4 @@
package types
package p2p
import (
"net"

View File

@@ -1,4 +1,4 @@
package types
package p2p
import (
"encoding/hex"

View File

@@ -1,4 +1,4 @@
package types
package p2p
import (
"errors"

View File

@@ -1,4 +1,4 @@
package types
package p2p
import (
"fmt"

View File

@@ -1,4 +1,4 @@
package types
package p2p
import (
"io/ioutil"

View File

@@ -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)
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}
}

View File

@@ -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)
}

View File

@@ -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
}
}