github.com/vipernet-xyz/tm@v0.34.24/privval/file_test.go (about)

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