github.com/line/ostracon@v1.0.10-0.20230328032236-7f20145f065d/privval/file_test.go (about)

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