github.com/Finschia/ostracon@v1.1.5/privval/file_test.go (about)

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