github.com/badrootd/nibiru-cometbft@v0.37.5-0.20240307173500-2a75559eee9b/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/badrootd/nibiru-cometbft/crypto/ed25519"
    14  	"github.com/badrootd/nibiru-cometbft/crypto/tmhash"
    15  	cmtjson "github.com/badrootd/nibiru-cometbft/libs/json"
    16  	cmtrand "github.com/badrootd/nibiru-cometbft/libs/rand"
    17  	cmtproto "github.com/badrootd/nibiru-cometbft/proto/tendermint/types"
    18  	"github.com/badrootd/nibiru-cometbft/types"
    19  	cmttime "github.com/badrootd/nibiru-cometbft/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 := cmtproto.PrevoteType
    57  	randBytes := cmtrand.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 := cmtjson.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 := cmtjson.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 := cmtjson.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 := cmtjson.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 := cmtrand.Bytes(tmhash.Size)
   169  	randbytes2 := cmtrand.Bytes(tmhash.Size)
   170  
   171  	block1 := types.BlockID{Hash: randbytes,
   172  		PartSetHeader: types.PartSetHeader{Total: 5, Hash: randbytes}}
   173  	block2 := types.BlockID{Hash: randbytes2,
   174  		PartSetHeader: types.PartSetHeader{Total: 10, Hash: randbytes2}}
   175  
   176  	height, round := int64(10), int32(1)
   177  	voteType := cmtproto.PrevoteType
   178  
   179  	// sign a vote for first time
   180  	vote := newVote(privVal.Key.Address, 0, height, round, voteType, block1)
   181  	v := vote.ToProto()
   182  	err = privVal.SignVote("mychainid", v)
   183  	assert.NoError(err, "expected no error signing vote")
   184  
   185  	// try to sign the same vote again; should be fine
   186  	err = privVal.SignVote("mychainid", v)
   187  	assert.NoError(err, "expected no error on signing same vote")
   188  
   189  	// now try some bad votes
   190  	cases := []*types.Vote{
   191  		newVote(privVal.Key.Address, 0, height, round-1, voteType, block1),   // round regression
   192  		newVote(privVal.Key.Address, 0, height-1, round, voteType, block1),   // height regression
   193  		newVote(privVal.Key.Address, 0, height-2, round+4, voteType, block1), // height regression and different round
   194  		newVote(privVal.Key.Address, 0, height, round, voteType, block2),     // different block
   195  	}
   196  
   197  	for _, c := range cases {
   198  		cpb := c.ToProto()
   199  		err = privVal.SignVote("mychainid", cpb)
   200  		assert.Error(err, "expected error on signing conflicting vote")
   201  	}
   202  
   203  	// try signing a vote with a different time stamp
   204  	sig := vote.Signature
   205  	vote.Timestamp = vote.Timestamp.Add(time.Duration(1000))
   206  	err = privVal.SignVote("mychainid", v)
   207  	assert.NoError(err)
   208  	assert.Equal(sig, vote.Signature)
   209  }
   210  
   211  func TestSignProposal(t *testing.T) {
   212  	assert := assert.New(t)
   213  
   214  	tempKeyFile, err := os.CreateTemp("", "priv_validator_key_")
   215  	require.Nil(t, err)
   216  	tempStateFile, err := os.CreateTemp("", "priv_validator_state_")
   217  	require.Nil(t, err)
   218  
   219  	privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name())
   220  
   221  	randbytes := cmtrand.Bytes(tmhash.Size)
   222  	randbytes2 := cmtrand.Bytes(tmhash.Size)
   223  
   224  	block1 := types.BlockID{Hash: randbytes,
   225  		PartSetHeader: types.PartSetHeader{Total: 5, Hash: randbytes}}
   226  	block2 := types.BlockID{Hash: randbytes2,
   227  		PartSetHeader: types.PartSetHeader{Total: 10, Hash: randbytes2}}
   228  	height, round := int64(10), int32(1)
   229  
   230  	// sign a proposal for first time
   231  	proposal := newProposal(height, round, block1)
   232  	pbp := proposal.ToProto()
   233  	err = privVal.SignProposal("mychainid", pbp)
   234  	assert.NoError(err, "expected no error signing proposal")
   235  
   236  	// try to sign the same proposal again; should be fine
   237  	err = privVal.SignProposal("mychainid", pbp)
   238  	assert.NoError(err, "expected no error on signing same proposal")
   239  
   240  	// now try some bad Proposals
   241  	cases := []*types.Proposal{
   242  		newProposal(height, round-1, block1),   // round regression
   243  		newProposal(height-1, round, block1),   // height regression
   244  		newProposal(height-2, round+4, block1), // height regression and different round
   245  		newProposal(height, round, block2),     // different block
   246  	}
   247  
   248  	for _, c := range cases {
   249  		err = privVal.SignProposal("mychainid", c.ToProto())
   250  		assert.Error(err, "expected error on signing conflicting proposal")
   251  	}
   252  
   253  	// try signing a proposal with a different time stamp
   254  	sig := proposal.Signature
   255  	proposal.Timestamp = proposal.Timestamp.Add(time.Duration(1000))
   256  	err = privVal.SignProposal("mychainid", pbp)
   257  	assert.NoError(err)
   258  	assert.Equal(sig, proposal.Signature)
   259  }
   260  
   261  func TestDifferByTimestamp(t *testing.T) {
   262  	tempKeyFile, err := os.CreateTemp("", "priv_validator_key_")
   263  	require.Nil(t, err)
   264  	tempStateFile, err := os.CreateTemp("", "priv_validator_state_")
   265  	require.Nil(t, err)
   266  
   267  	privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name())
   268  	randbytes := cmtrand.Bytes(tmhash.Size)
   269  	block1 := types.BlockID{Hash: randbytes, PartSetHeader: types.PartSetHeader{Total: 5, Hash: randbytes}}
   270  	height, round := int64(10), int32(1)
   271  	chainID := "mychainid"
   272  
   273  	// test proposal
   274  	{
   275  		proposal := newProposal(height, round, block1)
   276  		pb := proposal.ToProto()
   277  		err := privVal.SignProposal(chainID, pb)
   278  		assert.NoError(t, err, "expected no error signing proposal")
   279  		signBytes := types.ProposalSignBytes(chainID, pb)
   280  
   281  		sig := proposal.Signature
   282  		timeStamp := proposal.Timestamp
   283  
   284  		// manipulate the timestamp. should get changed back
   285  		pb.Timestamp = pb.Timestamp.Add(time.Millisecond)
   286  		var emptySig []byte
   287  		proposal.Signature = emptySig
   288  		err = privVal.SignProposal("mychainid", pb)
   289  		assert.NoError(t, err, "expected no error on signing same proposal")
   290  
   291  		assert.Equal(t, timeStamp, pb.Timestamp)
   292  		assert.Equal(t, signBytes, types.ProposalSignBytes(chainID, pb))
   293  		assert.Equal(t, sig, proposal.Signature)
   294  	}
   295  
   296  	// test vote
   297  	{
   298  		voteType := cmtproto.PrevoteType
   299  		blockID := types.BlockID{Hash: randbytes, PartSetHeader: types.PartSetHeader{}}
   300  		vote := newVote(privVal.Key.Address, 0, height, round, voteType, blockID)
   301  		v := vote.ToProto()
   302  		err := privVal.SignVote("mychainid", v)
   303  		assert.NoError(t, err, "expected no error signing vote")
   304  
   305  		signBytes := types.VoteSignBytes(chainID, v)
   306  		sig := v.Signature
   307  		timeStamp := vote.Timestamp
   308  
   309  		// manipulate the timestamp. should get changed back
   310  		v.Timestamp = v.Timestamp.Add(time.Millisecond)
   311  		var emptySig []byte
   312  		v.Signature = emptySig
   313  		err = privVal.SignVote("mychainid", v)
   314  		assert.NoError(t, err, "expected no error on signing same vote")
   315  
   316  		assert.Equal(t, timeStamp, v.Timestamp)
   317  		assert.Equal(t, signBytes, types.VoteSignBytes(chainID, v))
   318  		assert.Equal(t, sig, v.Signature)
   319  	}
   320  }
   321  
   322  func newVote(addr types.Address, idx int32, height int64, round int32,
   323  	typ cmtproto.SignedMsgType, blockID types.BlockID) *types.Vote {
   324  	return &types.Vote{
   325  		ValidatorAddress: addr,
   326  		ValidatorIndex:   idx,
   327  		Height:           height,
   328  		Round:            round,
   329  		Type:             typ,
   330  		Timestamp:        cmttime.Now(),
   331  		BlockID:          blockID,
   332  	}
   333  }
   334  
   335  func newProposal(height int64, round int32, blockID types.BlockID) *types.Proposal {
   336  	return &types.Proposal{
   337  		Height:    height,
   338  		Round:     round,
   339  		BlockID:   blockID,
   340  		Timestamp: cmttime.Now(),
   341  	}
   342  }