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