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 }