mirror of
https://github.com/tendermint/tendermint.git
synced 2026-02-10 05:50:19 +00:00
366 lines
11 KiB
Go
366 lines
11 KiB
Go
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/metadata"
|
|
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 = metadata.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})}, &metadata.Commit{Signatures: []metadata.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()))
|
|
}
|