github.com/lazyledger/lazyledger-core@v0.35.0-dev.0.20210613111200-4c651f053571/privval/file_test.go (about)

     1  package privval
     2  
     3  import (
     4  	"encoding/base64"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/require"
    13  
    14  	"github.com/lazyledger/lazyledger-core/crypto/ed25519"
    15  	"github.com/lazyledger/lazyledger-core/crypto/tmhash"
    16  	tmjson "github.com/lazyledger/lazyledger-core/libs/json"
    17  	tmrand "github.com/lazyledger/lazyledger-core/libs/rand"
    18  	tmproto "github.com/lazyledger/lazyledger-core/proto/tendermint/types"
    19  	"github.com/lazyledger/lazyledger-core/types"
    20  	tmtime "github.com/lazyledger/lazyledger-core/types/time"
    21  )
    22  
    23  func TestGenLoadValidator(t *testing.T) {
    24  	assert := assert.New(t)
    25  
    26  	tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_")
    27  	require.Nil(t, err)
    28  	tempStateFile, err := ioutil.TempFile("", "priv_validator_state_")
    29  	require.Nil(t, err)
    30  
    31  	privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), "")
    32  	require.NoError(t, err)
    33  
    34  	height := int64(100)
    35  	privVal.LastSignState.Height = height
    36  	privVal.Save()
    37  	addr := privVal.GetAddress()
    38  
    39  	privVal = LoadFilePV(tempKeyFile.Name(), tempStateFile.Name())
    40  	assert.Equal(addr, privVal.GetAddress(), "expected privval addr to be the same")
    41  	assert.Equal(height, privVal.LastSignState.Height, "expected privval.LastHeight to have been saved")
    42  }
    43  
    44  func TestResetValidator(t *testing.T) {
    45  	tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_")
    46  	require.Nil(t, err)
    47  	tempStateFile, err := ioutil.TempFile("", "priv_validator_state_")
    48  	require.Nil(t, err)
    49  
    50  	privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), "")
    51  	require.NoError(t, err)
    52  	emptyState := FilePVLastSignState{filePath: tempStateFile.Name()}
    53  
    54  	// new priv val has empty state
    55  	assert.Equal(t, privVal.LastSignState, emptyState)
    56  
    57  	// test vote
    58  	height, round := int64(10), int32(1)
    59  	voteType := tmproto.PrevoteType
    60  	randBytes := tmrand.Bytes(tmhash.Size)
    61  	blockID := types.BlockID{Hash: randBytes, PartSetHeader: types.PartSetHeader{}}
    62  	vote := newVote(privVal.Key.Address, 0, height, round, voteType, blockID)
    63  	err = privVal.SignVote("mychainid", vote.ToProto())
    64  	assert.NoError(t, err, "expected no error signing vote")
    65  
    66  	// priv val after signing is not same as empty
    67  	assert.NotEqual(t, privVal.LastSignState, emptyState)
    68  
    69  	// priv val after AcceptNewConnection is same as empty
    70  	privVal.Reset()
    71  	assert.Equal(t, privVal.LastSignState, emptyState)
    72  }
    73  
    74  func TestLoadOrGenValidator(t *testing.T) {
    75  	assert := assert.New(t)
    76  
    77  	tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_")
    78  	require.Nil(t, err)
    79  	tempStateFile, err := ioutil.TempFile("", "priv_validator_state_")
    80  	require.Nil(t, err)
    81  
    82  	tempKeyFilePath := tempKeyFile.Name()
    83  	if err := os.Remove(tempKeyFilePath); err != nil {
    84  		t.Error(err)
    85  	}
    86  	tempStateFilePath := tempStateFile.Name()
    87  	if err := os.Remove(tempStateFilePath); err != nil {
    88  		t.Error(err)
    89  	}
    90  
    91  	privVal, err := LoadOrGenFilePV(tempKeyFilePath, tempStateFilePath)
    92  	require.NoError(t, err)
    93  	addr := privVal.GetAddress()
    94  	privVal, err = LoadOrGenFilePV(tempKeyFilePath, tempStateFilePath)
    95  	require.NoError(t, err)
    96  	assert.Equal(addr, privVal.GetAddress(), "expected privval addr to be the same")
    97  }
    98  
    99  func TestUnmarshalValidatorState(t *testing.T) {
   100  	assert, require := assert.New(t), require.New(t)
   101  
   102  	// create some fixed values
   103  	serialized := `{
   104  		"height": "1",
   105  		"round": 1,
   106  		"step": 1
   107  	}`
   108  
   109  	val := FilePVLastSignState{}
   110  	err := tmjson.Unmarshal([]byte(serialized), &val)
   111  	require.Nil(err, "%+v", err)
   112  
   113  	// make sure the values match
   114  	assert.EqualValues(val.Height, 1)
   115  	assert.EqualValues(val.Round, 1)
   116  	assert.EqualValues(val.Step, 1)
   117  
   118  	// export it and make sure it is the same
   119  	out, err := tmjson.Marshal(val)
   120  	require.Nil(err, "%+v", err)
   121  	assert.JSONEq(serialized, string(out))
   122  }
   123  
   124  func TestUnmarshalValidatorKey(t *testing.T) {
   125  	assert, require := assert.New(t), require.New(t)
   126  
   127  	// create some fixed values
   128  	privKey := ed25519.GenPrivKey()
   129  	pubKey := privKey.PubKey()
   130  	addr := pubKey.Address()
   131  	pubBytes := pubKey.Bytes()
   132  	privBytes := privKey.Bytes()
   133  	pubB64 := base64.StdEncoding.EncodeToString(pubBytes)
   134  	privB64 := base64.StdEncoding.EncodeToString(privBytes)
   135  
   136  	serialized := fmt.Sprintf(`{
   137    "address": "%s",
   138    "pub_key": {
   139      "type": "tendermint/PubKeyEd25519",
   140      "value": "%s"
   141    },
   142    "priv_key": {
   143      "type": "tendermint/PrivKeyEd25519",
   144      "value": "%s"
   145    }
   146  }`, addr, pubB64, privB64)
   147  
   148  	val := FilePVKey{}
   149  	err := tmjson.Unmarshal([]byte(serialized), &val)
   150  	require.Nil(err, "%+v", err)
   151  
   152  	// make sure the values match
   153  	assert.EqualValues(addr, val.Address)
   154  	assert.EqualValues(pubKey, val.PubKey)
   155  	assert.EqualValues(privKey, val.PrivKey)
   156  
   157  	// export it and make sure it is the same
   158  	out, err := tmjson.Marshal(val)
   159  	require.Nil(err, "%+v", err)
   160  	assert.JSONEq(serialized, string(out))
   161  }
   162  
   163  func TestSignVote(t *testing.T) {
   164  	assert := assert.New(t)
   165  
   166  	tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_")
   167  	require.Nil(t, err)
   168  	tempStateFile, err := ioutil.TempFile("", "priv_validator_state_")
   169  	require.Nil(t, err)
   170  
   171  	privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), "")
   172  	require.NoError(t, err)
   173  
   174  	randbytes := tmrand.Bytes(tmhash.Size)
   175  	randbytes2 := tmrand.Bytes(tmhash.Size)
   176  
   177  	block1 := types.BlockID{Hash: randbytes,
   178  		PartSetHeader: types.PartSetHeader{Total: 5, Hash: randbytes}}
   179  	block2 := types.BlockID{Hash: randbytes2,
   180  		PartSetHeader: types.PartSetHeader{Total: 10, Hash: randbytes2}}
   181  
   182  	height, round := int64(10), int32(1)
   183  	voteType := tmproto.PrevoteType
   184  
   185  	// sign a vote for first time
   186  	vote := newVote(privVal.Key.Address, 0, height, round, voteType, block1)
   187  	v := vote.ToProto()
   188  	err = privVal.SignVote("mychainid", v)
   189  	assert.NoError(err, "expected no error signing vote")
   190  
   191  	// try to sign the same vote again; should be fine
   192  	err = privVal.SignVote("mychainid", v)
   193  	assert.NoError(err, "expected no error on signing same vote")
   194  
   195  	// now try some bad votes
   196  	cases := []*types.Vote{
   197  		newVote(privVal.Key.Address, 0, height, round-1, voteType, block1),   // round regression
   198  		newVote(privVal.Key.Address, 0, height-1, round, voteType, block1),   // height regression
   199  		newVote(privVal.Key.Address, 0, height-2, round+4, voteType, block1), // height regression and different round
   200  		newVote(privVal.Key.Address, 0, height, round, voteType, block2),     // different block
   201  	}
   202  
   203  	for _, c := range cases {
   204  		cpb := c.ToProto()
   205  		err = privVal.SignVote("mychainid", cpb)
   206  		assert.Error(err, "expected error on signing conflicting vote")
   207  	}
   208  
   209  	// try signing a vote with a different time stamp
   210  	sig := vote.Signature
   211  	vote.Timestamp = vote.Timestamp.Add(time.Duration(1000))
   212  	err = privVal.SignVote("mychainid", v)
   213  	assert.NoError(err)
   214  	assert.Equal(sig, vote.Signature)
   215  }
   216  
   217  func TestSignProposal(t *testing.T) {
   218  	assert := assert.New(t)
   219  
   220  	tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_")
   221  	require.Nil(t, err)
   222  	tempStateFile, err := ioutil.TempFile("", "priv_validator_state_")
   223  	require.Nil(t, err)
   224  
   225  	privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), "")
   226  	require.NoError(t, err)
   227  
   228  	randbytes := tmrand.Bytes(tmhash.Size)
   229  	randbytes2 := tmrand.Bytes(tmhash.Size)
   230  
   231  	block1 := types.BlockID{Hash: randbytes,
   232  		PartSetHeader: types.PartSetHeader{Total: 5, Hash: randbytes}}
   233  	block2 := types.BlockID{Hash: randbytes2,
   234  		PartSetHeader: types.PartSetHeader{Total: 10, Hash: randbytes2}}
   235  	height, round := int64(10), int32(1)
   236  
   237  	// sign a proposal for first time
   238  	proposal := newProposal(height, round, block1)
   239  	pbp, err := proposal.ToProto()
   240  	require.NoError(t, err)
   241  	err = privVal.SignProposal("mychainid", pbp)
   242  	assert.NoError(err, "expected no error signing proposal")
   243  
   244  	// try to sign the same proposal again; should be fine
   245  	err = privVal.SignProposal("mychainid", pbp)
   246  	assert.NoError(err, "expected no error on signing same proposal")
   247  
   248  	// now try some bad Proposals
   249  	cases := []*types.Proposal{
   250  		newProposal(height, round-1, block1),   // round regression
   251  		newProposal(height-1, round, block1),   // height regression
   252  		newProposal(height-2, round+4, block1), // height regression and different round
   253  		newProposal(height, round, block2),     // different block
   254  	}
   255  
   256  	for _, c := range cases {
   257  		p, err := c.ToProto()
   258  		require.NoError(t, err)
   259  		err = privVal.SignProposal("mychainid", p)
   260  		assert.Error(err, "expected error on signing conflicting proposal")
   261  	}
   262  
   263  	// try signing a proposal with a different time stamp
   264  	sig := proposal.Signature
   265  	proposal.Timestamp = proposal.Timestamp.Add(time.Duration(1000))
   266  	err = privVal.SignProposal("mychainid", pbp)
   267  	assert.NoError(err)
   268  	assert.Equal(sig, proposal.Signature)
   269  }
   270  
   271  func TestDifferByTimestamp(t *testing.T) {
   272  	tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_")
   273  	require.Nil(t, err)
   274  	tempStateFile, err := ioutil.TempFile("", "priv_validator_state_")
   275  	require.Nil(t, err)
   276  
   277  	privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), "")
   278  	require.NoError(t, err)
   279  	randbytes := tmrand.Bytes(tmhash.Size)
   280  	block1 := types.BlockID{Hash: randbytes, PartSetHeader: types.PartSetHeader{Total: 5, Hash: randbytes}}
   281  	height, round := int64(10), int32(1)
   282  	chainID := "mychainid"
   283  
   284  	// test proposal
   285  	{
   286  		proposal := newProposal(height, round, block1)
   287  		pb, err := proposal.ToProto()
   288  		require.NoError(t, err)
   289  		err = privVal.SignProposal(chainID, pb)
   290  		assert.NoError(t, err, "expected no error signing proposal")
   291  		signBytes := types.ProposalSignBytes(chainID, pb)
   292  
   293  		sig := proposal.Signature
   294  		timeStamp := proposal.Timestamp
   295  
   296  		// manipulate the timestamp. should get changed back
   297  		pb.Timestamp = pb.Timestamp.Add(time.Millisecond)
   298  		var emptySig []byte
   299  		proposal.Signature = emptySig
   300  		err = privVal.SignProposal("mychainid", pb)
   301  		assert.NoError(t, err, "expected no error on signing same proposal")
   302  
   303  		assert.Equal(t, timeStamp, pb.Timestamp)
   304  		assert.Equal(t, signBytes, types.ProposalSignBytes(chainID, pb))
   305  		assert.Equal(t, sig, proposal.Signature)
   306  	}
   307  
   308  	// test vote
   309  	{
   310  		voteType := tmproto.PrevoteType
   311  		blockID := types.BlockID{Hash: randbytes, PartSetHeader: types.PartSetHeader{}}
   312  		vote := newVote(privVal.Key.Address, 0, height, round, voteType, blockID)
   313  		v := vote.ToProto()
   314  		err := privVal.SignVote("mychainid", v)
   315  		assert.NoError(t, err, "expected no error signing vote")
   316  
   317  		signBytes := types.VoteSignBytes(chainID, v)
   318  		sig := v.Signature
   319  		timeStamp := vote.Timestamp
   320  
   321  		// manipulate the timestamp. should get changed back
   322  		v.Timestamp = v.Timestamp.Add(time.Millisecond)
   323  		var emptySig []byte
   324  		v.Signature = emptySig
   325  		err = privVal.SignVote("mychainid", v)
   326  		assert.NoError(t, err, "expected no error on signing same vote")
   327  
   328  		assert.Equal(t, timeStamp, v.Timestamp)
   329  		assert.Equal(t, signBytes, types.VoteSignBytes(chainID, v))
   330  		assert.Equal(t, sig, v.Signature)
   331  	}
   332  }
   333  
   334  func newVote(addr types.Address, idx int32, height int64, round int32,
   335  	typ tmproto.SignedMsgType, blockID types.BlockID) *types.Vote {
   336  	return &types.Vote{
   337  		ValidatorAddress: addr,
   338  		ValidatorIndex:   idx,
   339  		Height:           height,
   340  		Round:            round,
   341  		Type:             typ,
   342  		Timestamp:        tmtime.Now(),
   343  		BlockID:          blockID,
   344  	}
   345  }
   346  
   347  func newProposal(height int64, round int32, blockID types.BlockID) *types.Proposal {
   348  	return &types.Proposal{
   349  		Height:    height,
   350  		Round:     round,
   351  		BlockID:   blockID,
   352  		Timestamp: tmtime.Now(),
   353  		DAHeader:  &types.DataAvailabilityHeader{},
   354  	}
   355  }