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