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 }