github.com/evdatsion/aphelion-dpos-bft@v0.32.1/types/block_test.go (about)

     1  package types
     2  
     3  import (
     4  	// it is ok to use math/rand here: we do not need a cryptographically secure random
     5  	// number generator here and we can run the tests a bit faster
     6  	"crypto/rand"
     7  	"math"
     8  	"os"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  
    15  	"github.com/evdatsion/aphelion-dpos-bft/crypto"
    16  	"github.com/evdatsion/aphelion-dpos-bft/crypto/tmhash"
    17  	cmn "github.com/evdatsion/aphelion-dpos-bft/libs/common"
    18  	"github.com/evdatsion/aphelion-dpos-bft/version"
    19  )
    20  
    21  func TestMain(m *testing.M) {
    22  	RegisterMockEvidences(cdc)
    23  
    24  	code := m.Run()
    25  	os.Exit(code)
    26  }
    27  
    28  func TestBlockAddEvidence(t *testing.T) {
    29  	txs := []Tx{Tx("foo"), Tx("bar")}
    30  	lastID := makeBlockIDRandom()
    31  	h := int64(3)
    32  
    33  	voteSet, valSet, vals := randVoteSet(h-1, 1, PrecommitType, 10, 1)
    34  	commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals)
    35  	require.NoError(t, err)
    36  
    37  	ev := NewMockGoodEvidence(h, 0, valSet.Validators[0].Address)
    38  	evList := []Evidence{ev}
    39  
    40  	block := MakeBlock(h, txs, commit, evList)
    41  	require.NotNil(t, block)
    42  	require.Equal(t, 1, len(block.Evidence.Evidence))
    43  	require.NotNil(t, block.EvidenceHash)
    44  }
    45  
    46  func TestBlockValidateBasic(t *testing.T) {
    47  	require.Error(t, (*Block)(nil).ValidateBasic())
    48  
    49  	txs := []Tx{Tx("foo"), Tx("bar")}
    50  	lastID := makeBlockIDRandom()
    51  	h := int64(3)
    52  
    53  	voteSet, valSet, vals := randVoteSet(h-1, 1, PrecommitType, 10, 1)
    54  	commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals)
    55  	require.NoError(t, err)
    56  
    57  	ev := NewMockGoodEvidence(h, 0, valSet.Validators[0].Address)
    58  	evList := []Evidence{ev}
    59  
    60  	testCases := []struct {
    61  		testName      string
    62  		malleateBlock func(*Block)
    63  		expErr        bool
    64  	}{
    65  		{"Make Block", func(blk *Block) {}, false},
    66  		{"Make Block w/ proposer Addr", func(blk *Block) { blk.ProposerAddress = valSet.GetProposer().Address }, false},
    67  		{"Negative Height", func(blk *Block) { blk.Height = -1 }, true},
    68  		{"Increase NumTxs", func(blk *Block) { blk.NumTxs++ }, true},
    69  		{"Remove 1/2 the commits", func(blk *Block) {
    70  			blk.LastCommit.Precommits = commit.Precommits[:commit.Size()/2]
    71  			blk.LastCommit.hash = nil // clear hash or change wont be noticed
    72  		}, true},
    73  		{"Remove LastCommitHash", func(blk *Block) { blk.LastCommitHash = []byte("something else") }, true},
    74  		{"Tampered Data", func(blk *Block) {
    75  			blk.Data.Txs[0] = Tx("something else")
    76  			blk.Data.hash = nil // clear hash or change wont be noticed
    77  		}, true},
    78  		{"Tampered DataHash", func(blk *Block) {
    79  			blk.DataHash = cmn.RandBytes(len(blk.DataHash))
    80  		}, true},
    81  		{"Tampered EvidenceHash", func(blk *Block) {
    82  			blk.EvidenceHash = []byte("something else")
    83  		}, true},
    84  	}
    85  	for i, tc := range testCases {
    86  		t.Run(tc.testName, func(t *testing.T) {
    87  			block := MakeBlock(h, txs, commit, evList)
    88  			block.ProposerAddress = valSet.GetProposer().Address
    89  			tc.malleateBlock(block)
    90  			err = block.ValidateBasic()
    91  			assert.Equal(t, tc.expErr, err != nil, "#%d: %v", i, err)
    92  		})
    93  	}
    94  }
    95  
    96  func TestBlockHash(t *testing.T) {
    97  	assert.Nil(t, (*Block)(nil).Hash())
    98  	assert.Nil(t, MakeBlock(int64(3), []Tx{Tx("Hello World")}, nil, nil).Hash())
    99  }
   100  
   101  func TestBlockMakePartSet(t *testing.T) {
   102  	assert.Nil(t, (*Block)(nil).MakePartSet(2))
   103  
   104  	partSet := MakeBlock(int64(3), []Tx{Tx("Hello World")}, nil, nil).MakePartSet(1024)
   105  	assert.NotNil(t, partSet)
   106  	assert.Equal(t, 1, partSet.Total())
   107  }
   108  
   109  func TestBlockMakePartSetWithEvidence(t *testing.T) {
   110  	assert.Nil(t, (*Block)(nil).MakePartSet(2))
   111  
   112  	lastID := makeBlockIDRandom()
   113  	h := int64(3)
   114  
   115  	voteSet, valSet, vals := randVoteSet(h-1, 1, PrecommitType, 10, 1)
   116  	commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals)
   117  	require.NoError(t, err)
   118  
   119  	ev := NewMockGoodEvidence(h, 0, valSet.Validators[0].Address)
   120  	evList := []Evidence{ev}
   121  
   122  	partSet := MakeBlock(h, []Tx{Tx("Hello World")}, commit, evList).MakePartSet(1024)
   123  	assert.NotNil(t, partSet)
   124  	assert.Equal(t, 3, partSet.Total())
   125  }
   126  
   127  func TestBlockHashesTo(t *testing.T) {
   128  	assert.False(t, (*Block)(nil).HashesTo(nil))
   129  
   130  	lastID := makeBlockIDRandom()
   131  	h := int64(3)
   132  	voteSet, valSet, vals := randVoteSet(h-1, 1, PrecommitType, 10, 1)
   133  	commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals)
   134  	require.NoError(t, err)
   135  
   136  	ev := NewMockGoodEvidence(h, 0, valSet.Validators[0].Address)
   137  	evList := []Evidence{ev}
   138  
   139  	block := MakeBlock(h, []Tx{Tx("Hello World")}, commit, evList)
   140  	block.ValidatorsHash = valSet.Hash()
   141  	assert.False(t, block.HashesTo([]byte{}))
   142  	assert.False(t, block.HashesTo([]byte("something else")))
   143  	assert.True(t, block.HashesTo(block.Hash()))
   144  }
   145  
   146  func TestBlockSize(t *testing.T) {
   147  	size := MakeBlock(int64(3), []Tx{Tx("Hello World")}, nil, nil).Size()
   148  	if size <= 0 {
   149  		t.Fatal("Size of the block is zero or negative")
   150  	}
   151  }
   152  
   153  func TestBlockString(t *testing.T) {
   154  	assert.Equal(t, "nil-Block", (*Block)(nil).String())
   155  	assert.Equal(t, "nil-Block", (*Block)(nil).StringIndented(""))
   156  	assert.Equal(t, "nil-Block", (*Block)(nil).StringShort())
   157  
   158  	block := MakeBlock(int64(3), []Tx{Tx("Hello World")}, nil, nil)
   159  	assert.NotEqual(t, "nil-Block", block.String())
   160  	assert.NotEqual(t, "nil-Block", block.StringIndented(""))
   161  	assert.NotEqual(t, "nil-Block", block.StringShort())
   162  }
   163  
   164  func makeBlockIDRandom() BlockID {
   165  	blockHash := make([]byte, tmhash.Size)
   166  	partSetHash := make([]byte, tmhash.Size)
   167  	rand.Read(blockHash)   //nolint: gosec
   168  	rand.Read(partSetHash) //nolint: gosec
   169  	blockPartsHeader := PartSetHeader{123, partSetHash}
   170  	return BlockID{blockHash, blockPartsHeader}
   171  }
   172  
   173  func makeBlockID(hash []byte, partSetSize int, partSetHash []byte) BlockID {
   174  	return BlockID{
   175  		Hash: hash,
   176  		PartsHeader: PartSetHeader{
   177  			Total: partSetSize,
   178  			Hash:  partSetHash,
   179  		},
   180  	}
   181  
   182  }
   183  
   184  var nilBytes []byte
   185  
   186  func TestNilHeaderHashDoesntCrash(t *testing.T) {
   187  	assert.Equal(t, []byte((*Header)(nil).Hash()), nilBytes)
   188  	assert.Equal(t, []byte((new(Header)).Hash()), nilBytes)
   189  }
   190  
   191  func TestNilDataHashDoesntCrash(t *testing.T) {
   192  	assert.Equal(t, []byte((*Data)(nil).Hash()), nilBytes)
   193  	assert.Equal(t, []byte(new(Data).Hash()), nilBytes)
   194  }
   195  
   196  func TestCommit(t *testing.T) {
   197  	lastID := makeBlockIDRandom()
   198  	h := int64(3)
   199  	voteSet, _, vals := randVoteSet(h-1, 1, PrecommitType, 10, 1)
   200  	commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals)
   201  	require.NoError(t, err)
   202  
   203  	assert.Equal(t, h-1, commit.Height())
   204  	assert.Equal(t, 1, commit.Round())
   205  	assert.Equal(t, PrecommitType, SignedMsgType(commit.Type()))
   206  	if commit.Size() <= 0 {
   207  		t.Fatalf("commit %v has a zero or negative size: %d", commit, commit.Size())
   208  	}
   209  
   210  	require.NotNil(t, commit.BitArray())
   211  	assert.Equal(t, cmn.NewBitArray(10).Size(), commit.BitArray().Size())
   212  
   213  	assert.Equal(t, voteSet.GetByIndex(0), commit.GetByIndex(0))
   214  	assert.True(t, commit.IsCommit())
   215  }
   216  
   217  func TestCommitValidateBasic(t *testing.T) {
   218  	testCases := []struct {
   219  		testName       string
   220  		malleateCommit func(*Commit)
   221  		expectErr      bool
   222  	}{
   223  		{"Random Commit", func(com *Commit) {}, false},
   224  		{"Nil precommit", func(com *Commit) { com.Precommits[0] = nil }, false},
   225  		{"Incorrect signature", func(com *Commit) { com.Precommits[0].Signature = []byte{0} }, false},
   226  		{"Incorrect type", func(com *Commit) { com.Precommits[0].Type = PrevoteType }, true},
   227  		{"Incorrect height", func(com *Commit) { com.Precommits[0].Height = int64(100) }, true},
   228  		{"Incorrect round", func(com *Commit) { com.Precommits[0].Round = 100 }, true},
   229  	}
   230  	for _, tc := range testCases {
   231  		t.Run(tc.testName, func(t *testing.T) {
   232  			com := randCommit()
   233  			tc.malleateCommit(com)
   234  			assert.Equal(t, tc.expectErr, com.ValidateBasic() != nil, "Validate Basic had an unexpected result")
   235  		})
   236  	}
   237  }
   238  
   239  func TestMaxHeaderBytes(t *testing.T) {
   240  	// Construct a UTF-8 string of MaxChainIDLen length using the supplementary
   241  	// characters.
   242  	// Each supplementary character takes 4 bytes.
   243  	// http://www.i18nguy.com/unicode/supplementary-test.html
   244  	maxChainID := ""
   245  	for i := 0; i < MaxChainIDLen; i++ {
   246  		maxChainID += "𠜎"
   247  	}
   248  
   249  	// time is varint encoded so need to pick the max.
   250  	// year int, month Month, day, hour, min, sec, nsec int, loc *Location
   251  	timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC)
   252  
   253  	h := Header{
   254  		Version:            version.Consensus{Block: math.MaxInt64, App: math.MaxInt64},
   255  		ChainID:            maxChainID,
   256  		Height:             math.MaxInt64,
   257  		Time:               timestamp,
   258  		NumTxs:             math.MaxInt64,
   259  		TotalTxs:           math.MaxInt64,
   260  		LastBlockID:        makeBlockID(make([]byte, tmhash.Size), math.MaxInt64, make([]byte, tmhash.Size)),
   261  		LastCommitHash:     tmhash.Sum([]byte("last_commit_hash")),
   262  		DataHash:           tmhash.Sum([]byte("data_hash")),
   263  		ValidatorsHash:     tmhash.Sum([]byte("validators_hash")),
   264  		NextValidatorsHash: tmhash.Sum([]byte("next_validators_hash")),
   265  		ConsensusHash:      tmhash.Sum([]byte("consensus_hash")),
   266  		AppHash:            tmhash.Sum([]byte("app_hash")),
   267  		LastResultsHash:    tmhash.Sum([]byte("last_results_hash")),
   268  		EvidenceHash:       tmhash.Sum([]byte("evidence_hash")),
   269  		ProposerAddress:    crypto.AddressHash([]byte("proposer_address")),
   270  	}
   271  
   272  	bz, err := cdc.MarshalBinaryLengthPrefixed(h)
   273  	require.NoError(t, err)
   274  
   275  	assert.EqualValues(t, MaxHeaderBytes, len(bz))
   276  }
   277  
   278  func randCommit() *Commit {
   279  	lastID := makeBlockIDRandom()
   280  	h := int64(3)
   281  	voteSet, _, vals := randVoteSet(h-1, 1, PrecommitType, 10, 1)
   282  	commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals)
   283  	if err != nil {
   284  		panic(err)
   285  	}
   286  	return commit
   287  }
   288  
   289  func TestBlockMaxDataBytes(t *testing.T) {
   290  	testCases := []struct {
   291  		maxBytes      int64
   292  		valsCount     int
   293  		evidenceCount int
   294  		panics        bool
   295  		result        int64
   296  	}{
   297  		0: {-10, 1, 0, true, 0},
   298  		1: {10, 1, 0, true, 0},
   299  		2: {886, 1, 0, true, 0},
   300  		3: {887, 1, 0, false, 0},
   301  		4: {888, 1, 0, false, 1},
   302  	}
   303  
   304  	for i, tc := range testCases {
   305  		if tc.panics {
   306  			assert.Panics(t, func() {
   307  				MaxDataBytes(tc.maxBytes, tc.valsCount, tc.evidenceCount)
   308  			}, "#%v", i)
   309  		} else {
   310  			assert.Equal(t,
   311  				tc.result,
   312  				MaxDataBytes(tc.maxBytes, tc.valsCount, tc.evidenceCount),
   313  				"#%v", i)
   314  		}
   315  	}
   316  }
   317  
   318  func TestBlockMaxDataBytesUnknownEvidence(t *testing.T) {
   319  	testCases := []struct {
   320  		maxBytes  int64
   321  		valsCount int
   322  		panics    bool
   323  		result    int64
   324  	}{
   325  		0: {-10, 1, true, 0},
   326  		1: {10, 1, true, 0},
   327  		2: {984, 1, true, 0},
   328  		3: {985, 1, false, 0},
   329  		4: {986, 1, false, 1},
   330  	}
   331  
   332  	for i, tc := range testCases {
   333  		if tc.panics {
   334  			assert.Panics(t, func() {
   335  				MaxDataBytesUnknownEvidence(tc.maxBytes, tc.valsCount)
   336  			}, "#%v", i)
   337  		} else {
   338  			assert.Equal(t,
   339  				tc.result,
   340  				MaxDataBytesUnknownEvidence(tc.maxBytes, tc.valsCount),
   341  				"#%v", i)
   342  		}
   343  	}
   344  }
   345  
   346  func TestCommitToVoteSet(t *testing.T) {
   347  	lastID := makeBlockIDRandom()
   348  	h := int64(3)
   349  
   350  	voteSet, valSet, vals := randVoteSet(h-1, 1, PrecommitType, 10, 1)
   351  	commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals)
   352  	assert.NoError(t, err)
   353  
   354  	chainID := voteSet.ChainID()
   355  	voteSet2 := CommitToVoteSet(chainID, commit, valSet)
   356  
   357  	for i := 0; i < len(vals); i++ {
   358  		vote1 := voteSet.GetByIndex(i)
   359  		vote2 := voteSet2.GetByIndex(i)
   360  		vote3 := commit.GetVote(i)
   361  
   362  		vote1bz := cdc.MustMarshalBinaryBare(vote1)
   363  		vote2bz := cdc.MustMarshalBinaryBare(vote2)
   364  		vote3bz := cdc.MustMarshalBinaryBare(vote3)
   365  		assert.Equal(t, vote1bz, vote2bz)
   366  		assert.Equal(t, vote1bz, vote3bz)
   367  	}
   368  }