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