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 }