You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

586 lines
18 KiB

7 years ago
  1. package evidence
  2. import (
  3. "os"
  4. "testing"
  5. "time"
  6. "github.com/gogo/protobuf/proto"
  7. "github.com/stretchr/testify/assert"
  8. "github.com/stretchr/testify/require"
  9. dbm "github.com/tendermint/tm-db"
  10. "github.com/tendermint/tendermint/crypto/tmhash"
  11. "github.com/tendermint/tendermint/libs/bytes"
  12. "github.com/tendermint/tendermint/libs/log"
  13. tmrand "github.com/tendermint/tendermint/libs/rand"
  14. tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
  15. sm "github.com/tendermint/tendermint/state"
  16. "github.com/tendermint/tendermint/store"
  17. "github.com/tendermint/tendermint/types"
  18. tmtime "github.com/tendermint/tendermint/types/time"
  19. )
  20. func TestMain(m *testing.M) {
  21. code := m.Run()
  22. os.Exit(code)
  23. }
  24. const evidenceChainID = "test_chain"
  25. func TestEvidencePool(t *testing.T) {
  26. var (
  27. val = types.NewMockPV()
  28. valAddr = val.PrivKey.PubKey().Address()
  29. height = int64(52)
  30. stateDB = initializeValidatorState(val, height)
  31. evidenceDB = dbm.NewMemDB()
  32. blockStoreDB = dbm.NewMemDB()
  33. blockStore = initializeBlockStore(blockStoreDB, sm.LoadState(stateDB), valAddr)
  34. evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
  35. goodEvidence = types.NewMockDuplicateVoteEvidenceWithValidator(height, evidenceTime, val, evidenceChainID)
  36. badEvidence = types.NewMockDuplicateVoteEvidenceWithValidator(1, evidenceTime, val, evidenceChainID)
  37. )
  38. pool, err := NewPool(stateDB, evidenceDB, blockStore)
  39. require.NoError(t, err)
  40. // bad evidence
  41. err = pool.AddEvidence(badEvidence)
  42. if assert.Error(t, err) {
  43. assert.Contains(t, err.Error(), "is too old; min height is 32 and evidence can not be older than")
  44. }
  45. assert.False(t, pool.IsPending(badEvidence))
  46. assert.True(t, pool.IsEvidenceExpired(badEvidence))
  47. // good evidence
  48. evAdded := make(chan struct{})
  49. go func() {
  50. <-pool.EvidenceWaitChan()
  51. close(evAdded)
  52. }()
  53. err = pool.AddEvidence(goodEvidence)
  54. require.NoError(t, err)
  55. select {
  56. case <-evAdded:
  57. case <-time.After(5 * time.Second):
  58. t.Fatal("evidence was not added to list after 5s")
  59. }
  60. assert.Equal(t, 1, pool.evidenceList.Len())
  61. // if we send it again, it shouldnt add and return an error
  62. err = pool.AddEvidence(goodEvidence)
  63. assert.NoError(t, err)
  64. assert.Equal(t, 1, pool.evidenceList.Len())
  65. }
  66. func TestProposingAndCommittingEvidence(t *testing.T) {
  67. var (
  68. val = types.NewMockPV()
  69. height = int64(1)
  70. stateDB = initializeValidatorState(val, height)
  71. evidenceDB = dbm.NewMemDB()
  72. blockStoreDB = dbm.NewMemDB()
  73. blockStore = initializeBlockStore(blockStoreDB, sm.LoadState(stateDB), val.PrivKey.PubKey().Address())
  74. evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
  75. )
  76. pool, err := NewPool(stateDB, evidenceDB, blockStore)
  77. require.NoError(t, err)
  78. // evidence not seen yet:
  79. evidence := types.NewMockDuplicateVoteEvidenceWithValidator(height, evidenceTime, val, evidenceChainID)
  80. assert.False(t, pool.IsCommitted(evidence))
  81. // evidence seen but not yet committed:
  82. assert.NoError(t, pool.AddEvidence(evidence))
  83. assert.False(t, pool.IsCommitted(evidence))
  84. // test evidence is proposed
  85. proposedEvidence := pool.AllPendingEvidence()
  86. assert.Equal(t, proposedEvidence[0], evidence)
  87. // evidence seen and committed:
  88. pool.MarkEvidenceAsCommitted(height, proposedEvidence)
  89. assert.True(t, pool.IsCommitted(evidence))
  90. assert.False(t, pool.IsPending(evidence))
  91. assert.Equal(t, 0, pool.evidenceList.Len())
  92. // evidence should
  93. }
  94. func TestAddEvidence(t *testing.T) {
  95. var (
  96. val = types.NewMockPV()
  97. valAddr = val.PrivKey.PubKey().Address()
  98. height = int64(30)
  99. stateDB = initializeValidatorState(val, height)
  100. evidenceDB = dbm.NewMemDB()
  101. blockStoreDB = dbm.NewMemDB()
  102. blockStore = initializeBlockStore(blockStoreDB, sm.LoadState(stateDB), valAddr)
  103. evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
  104. )
  105. pool, err := NewPool(stateDB, evidenceDB, blockStore)
  106. require.NoError(t, err)
  107. testCases := []struct {
  108. evHeight int64
  109. evTime time.Time
  110. expErr bool
  111. evDescription string
  112. }{
  113. {height, time.Now(), false, "valid evidence"},
  114. {height, evidenceTime, false, "valid evidence (despite old time)"},
  115. {int64(1), time.Now(), false, "valid evidence (despite old height)"},
  116. {int64(1), evidenceTime, true,
  117. "evidence from height 1 (created at: 2019-01-01 00:00:00 +0000 UTC) is too old"},
  118. }
  119. for _, tc := range testCases {
  120. tc := tc
  121. t.Run(tc.evDescription, func(t *testing.T) {
  122. ev := types.NewMockDuplicateVoteEvidence(tc.evHeight, tc.evTime, evidenceChainID)
  123. err := pool.AddEvidence(ev)
  124. if tc.expErr {
  125. assert.Error(t, err)
  126. t.Log(err)
  127. }
  128. })
  129. }
  130. }
  131. func TestEvidencePoolUpdate(t *testing.T) {
  132. var (
  133. val = types.NewMockPV()
  134. valAddr = val.PrivKey.PubKey().Address()
  135. height = int64(21)
  136. stateDB = initializeValidatorState(val, height)
  137. evidenceDB = dbm.NewMemDB()
  138. blockStoreDB = dbm.NewMemDB()
  139. state = sm.LoadState(stateDB)
  140. blockStore = initializeBlockStore(blockStoreDB, state, valAddr)
  141. )
  142. pool, err := NewPool(stateDB, evidenceDB, blockStore)
  143. require.NoError(t, err)
  144. // create new block (no need to save it to blockStore)
  145. evidence := types.NewMockDuplicateVoteEvidence(height, time.Now(), evidenceChainID)
  146. lastCommit := makeCommit(height, valAddr)
  147. block := types.MakeBlock(height+1, []types.Tx{}, lastCommit, []types.Evidence{evidence})
  148. // update state (partially)
  149. state.LastBlockHeight = height + 1
  150. pool.Update(block, state)
  151. // a) Update marks evidence as committed
  152. assert.True(t, pool.IsCommitted(evidence))
  153. // b) Update updates valToLastHeight map
  154. assert.Equal(t, height+1, pool.ValidatorLastHeight(valAddr))
  155. }
  156. func TestEvidencePoolNewPool(t *testing.T) {
  157. var (
  158. val = types.NewMockPV()
  159. valAddr = val.PrivKey.PubKey().Address()
  160. height = int64(1)
  161. stateDB = initializeValidatorState(val, height)
  162. evidenceDB = dbm.NewMemDB()
  163. blockStoreDB = dbm.NewMemDB()
  164. state = sm.LoadState(stateDB)
  165. blockStore = initializeBlockStore(blockStoreDB, state, valAddr)
  166. )
  167. pool, err := NewPool(stateDB, evidenceDB, blockStore)
  168. require.NoError(t, err)
  169. assert.Equal(t, height, pool.ValidatorLastHeight(valAddr))
  170. assert.EqualValues(t, 0, pool.ValidatorLastHeight([]byte("non-existent-validator")))
  171. }
  172. func TestAddingAndPruningPOLC(t *testing.T) {
  173. var (
  174. val = types.NewMockPV()
  175. valAddr = val.PrivKey.PubKey().Address()
  176. stateDB = initializeValidatorState(val, 1)
  177. evidenceDB = dbm.NewMemDB()
  178. blockStoreDB = dbm.NewMemDB()
  179. state = sm.LoadState(stateDB)
  180. blockStore = initializeBlockStore(blockStoreDB, state, valAddr)
  181. height = state.ConsensusParams.Evidence.MaxAgeNumBlocks * 2
  182. evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
  183. firstBlockID = types.BlockID{
  184. Hash: tmrand.Bytes(tmhash.Size),
  185. PartSetHeader: types.PartSetHeader{
  186. Total: 1,
  187. Hash: tmrand.Bytes(tmhash.Size),
  188. },
  189. }
  190. )
  191. voteA := makeVote(1, 1, 0, val.PrivKey.PubKey().Address(), firstBlockID, evidenceTime)
  192. vA := voteA.ToProto()
  193. err := val.SignVote(evidenceChainID, vA)
  194. require.NoError(t, err)
  195. voteA.Signature = vA.Signature
  196. pubKey, _ := types.NewMockPV().GetPubKey()
  197. polc := &types.ProofOfLockChange{
  198. Votes: []*types.Vote{voteA},
  199. PubKey: pubKey,
  200. }
  201. pool, err := NewPool(stateDB, evidenceDB, blockStore)
  202. require.NoError(t, err)
  203. err = pool.AddPOLC(polc)
  204. assert.NoError(t, err)
  205. // should be able to retrieve polc
  206. newPolc, err := pool.RetrievePOLC(1, 1)
  207. assert.NoError(t, err)
  208. assert.True(t, polc.Equal(newPolc))
  209. // should not be able to retrieve because it doesn't exist
  210. emptyPolc, err := pool.RetrievePOLC(2, 1)
  211. assert.NoError(t, err)
  212. assert.Nil(t, emptyPolc)
  213. lastCommit := makeCommit(height-1, valAddr)
  214. block := types.MakeBlock(height, []types.Tx{}, lastCommit, []types.Evidence{})
  215. // update state (partially)
  216. state.LastBlockHeight = height
  217. pool.state.LastBlockHeight = height
  218. // update should prune the polc
  219. pool.Update(block, state)
  220. emptyPolc, err = pool.RetrievePOLC(1, 1)
  221. assert.NoError(t, err)
  222. assert.Nil(t, emptyPolc)
  223. }
  224. func TestRecoverPendingEvidence(t *testing.T) {
  225. var (
  226. val = types.NewMockPV()
  227. valAddr = val.PrivKey.PubKey().Address()
  228. height = int64(30)
  229. stateDB = initializeValidatorState(val, height)
  230. evidenceDB = dbm.NewMemDB()
  231. blockStoreDB = dbm.NewMemDB()
  232. state = sm.LoadState(stateDB)
  233. blockStore = initializeBlockStore(blockStoreDB, state, valAddr)
  234. evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
  235. goodEvidence = types.NewMockDuplicateVoteEvidenceWithValidator(height, time.Now(), val, evidenceChainID)
  236. expiredEvidence = types.NewMockDuplicateVoteEvidenceWithValidator(int64(1), evidenceTime, val, evidenceChainID)
  237. )
  238. // load good evidence
  239. goodKey := keyPending(goodEvidence)
  240. evi, err := types.EvidenceToProto(goodEvidence)
  241. require.NoError(t, err)
  242. goodEvidenceBytes, err := proto.Marshal(evi)
  243. require.NoError(t, err)
  244. _ = evidenceDB.Set(goodKey, goodEvidenceBytes)
  245. // load expired evidence
  246. expiredKey := keyPending(expiredEvidence)
  247. eevi, err := types.EvidenceToProto(expiredEvidence)
  248. require.NoError(t, err)
  249. expiredEvidenceBytes, err := proto.Marshal(eevi)
  250. require.NoError(t, err)
  251. _ = evidenceDB.Set(expiredKey, expiredEvidenceBytes)
  252. pool, err := NewPool(stateDB, evidenceDB, blockStore)
  253. require.NoError(t, err)
  254. assert.Equal(t, 1, pool.evidenceList.Len())
  255. assert.True(t, pool.IsPending(goodEvidence))
  256. }
  257. // Comprehensive set of test cases relating to the adding, upgrading and overall
  258. // processing of PotentialAmnesiaEvidence and AmnesiaEvidence
  259. func TestAddingPotentialAmnesiaEvidence(t *testing.T) {
  260. var (
  261. val = types.NewMockPV()
  262. val2 = types.NewMockPV()
  263. pubKey = val.PrivKey.PubKey()
  264. pubKey2 = val2.PrivKey.PubKey()
  265. valSet = &types.ValidatorSet{
  266. Validators: []*types.Validator{
  267. val.ExtractIntoValidator(1),
  268. val2.ExtractIntoValidator(3),
  269. },
  270. Proposer: val.ExtractIntoValidator(1),
  271. }
  272. height = int64(30)
  273. stateDB = initializeStateFromValidatorSet(valSet, height)
  274. evidenceDB = dbm.NewMemDB()
  275. blockStoreDB = dbm.NewMemDB()
  276. state = sm.LoadState(stateDB)
  277. blockStore = initializeBlockStore(blockStoreDB, state, pubKey.Address())
  278. //evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
  279. firstBlockID = types.BlockID{
  280. Hash: []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
  281. PartSetHeader: types.PartSetHeader{
  282. Total: 1,
  283. Hash: []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
  284. },
  285. }
  286. secondBlockID = types.BlockID{
  287. Hash: []byte("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
  288. PartSetHeader: types.PartSetHeader{
  289. Total: 1,
  290. Hash: []byte("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
  291. },
  292. }
  293. evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
  294. )
  295. // TEST SETUP
  296. pool, err := NewPool(stateDB, evidenceDB, blockStore)
  297. require.NoError(t, err)
  298. pool.SetLogger(log.TestingLogger())
  299. voteA := makeVote(height, 0, 0, pubKey.Address(), firstBlockID, evidenceTime)
  300. vA := voteA.ToProto()
  301. err = val.SignVote(evidenceChainID, vA)
  302. voteA.Signature = vA.Signature
  303. require.NoError(t, err)
  304. voteB := makeVote(height, 1, 0, pubKey.Address(), secondBlockID, evidenceTime.Add(3*time.Second))
  305. vB := voteB.ToProto()
  306. err = val.SignVote(evidenceChainID, vB)
  307. voteB.Signature = vB.Signature
  308. require.NoError(t, err)
  309. voteC := makeVote(height, 2, 0, pubKey.Address(), firstBlockID, evidenceTime.Add(2*time.Second))
  310. vC := voteC.ToProto()
  311. err = val.SignVote(evidenceChainID, vC)
  312. voteC.Signature = vC.Signature
  313. require.NoError(t, err)
  314. ev := &types.PotentialAmnesiaEvidence{
  315. VoteA: voteA,
  316. VoteB: voteB,
  317. }
  318. polc := &types.ProofOfLockChange{
  319. Votes: []*types.Vote{voteB},
  320. PubKey: pubKey2,
  321. }
  322. err = pool.AddPOLC(polc)
  323. require.NoError(t, err)
  324. polc, err = pool.RetrievePOLC(height, 1)
  325. require.NoError(t, err)
  326. require.NotEmpty(t, polc)
  327. secondValVote := makeVote(height, 1, 0, pubKey2.Address(), secondBlockID, evidenceTime.Add(1*time.Second))
  328. vv2 := secondValVote.ToProto()
  329. err = val2.SignVote(evidenceChainID, vv2)
  330. require.NoError(t, err)
  331. secondValVote.Signature = vv2.Signature
  332. validPolc := &types.ProofOfLockChange{
  333. Votes: []*types.Vote{secondValVote},
  334. PubKey: pubKey,
  335. }
  336. // CASE A
  337. pool.logger.Info("CASE A")
  338. // we expect the evidence pool to find the polc but log an error as the polc is not valid -> vote was
  339. // not from a validator in this set. However, an error isn't thrown because the evidence pool
  340. // should still be able to save the regular potential amnesia evidence.
  341. err = pool.AddEvidence(ev)
  342. assert.NoError(t, err)
  343. // evidence requires trial period until it is available -> we expect no evidence to be returned
  344. assert.Equal(t, 0, len(pool.PendingEvidence(1)))
  345. assert.True(t, pool.IsOnTrial(ev))
  346. nextHeight := pool.nextEvidenceTrialEndedHeight
  347. assert.Greater(t, nextHeight, int64(0))
  348. // CASE B
  349. pool.logger.Info("CASE B")
  350. // evidence is not ready to be upgraded so we return the height we expect the evidence to be.
  351. nextHeight = pool.upgradePotentialAmnesiaEvidence()
  352. assert.Equal(t, height+pool.state.ConsensusParams.Evidence.ProofTrialPeriod, nextHeight)
  353. // CASE C
  354. pool.logger.Info("CASE C")
  355. // now evidence is ready to be upgraded to amnesia evidence -> we expect -1 to be the next height as their is
  356. // no more pending potential amnesia evidence left
  357. lastCommit := makeCommit(height+1, pubKey.Address())
  358. block := types.MakeBlock(height+2, []types.Tx{}, lastCommit, []types.Evidence{})
  359. state.LastBlockHeight = height + 2
  360. pool.Update(block, state)
  361. assert.Equal(t, int64(-1), pool.nextEvidenceTrialEndedHeight)
  362. assert.Equal(t, 1, len(pool.PendingEvidence(1)))
  363. // CASE D
  364. pool.logger.Info("CASE D")
  365. // evidence of voting back in the past which is instantly punishable -> amnesia evidence is made directly
  366. ev2 := &types.PotentialAmnesiaEvidence{
  367. VoteA: voteC,
  368. VoteB: voteB,
  369. }
  370. err = pool.AddEvidence(ev2)
  371. assert.NoError(t, err)
  372. expectedAe := &types.AmnesiaEvidence{
  373. PotentialAmnesiaEvidence: ev2,
  374. Polc: types.NewEmptyPOLC(),
  375. }
  376. assert.True(t, pool.IsPending(expectedAe))
  377. assert.Equal(t, 2, len(pool.AllPendingEvidence()))
  378. // CASE E
  379. pool.logger.Info("CASE E")
  380. // test for receiving amnesia evidence
  381. ae := types.NewAmnesiaEvidence(ev, types.NewEmptyPOLC())
  382. // we need to run the trial period ourselves so amnesia evidence should not be added, instead
  383. // we should extract out the potential amnesia evidence and trying to add that before realising
  384. // that we already have it -> no error
  385. err = pool.AddEvidence(ae)
  386. assert.NoError(t, err)
  387. assert.Equal(t, 2, len(pool.AllPendingEvidence()))
  388. voteD := makeVote(height, 2, 0, pubKey.Address(), firstBlockID, evidenceTime.Add(4*time.Second))
  389. vD := voteD.ToProto()
  390. err = val.SignVote(evidenceChainID, vD)
  391. require.NoError(t, err)
  392. voteD.Signature = vD.Signature
  393. // CASE F
  394. pool.logger.Info("CASE F")
  395. // a new amnesia evidence is seen. It has an empty polc so we should extract the potential amnesia evidence
  396. // and start our own trial
  397. newPe := &types.PotentialAmnesiaEvidence{
  398. VoteA: voteB,
  399. VoteB: voteD,
  400. }
  401. newAe := &types.AmnesiaEvidence{
  402. PotentialAmnesiaEvidence: newPe,
  403. Polc: types.NewEmptyPOLC(),
  404. }
  405. err = pool.AddEvidence(newAe)
  406. assert.NoError(t, err)
  407. assert.Equal(t, 2, len(pool.AllPendingEvidence()))
  408. assert.True(t, pool.IsOnTrial(newPe))
  409. // CASE G
  410. pool.logger.Info("CASE G")
  411. // Finally, we receive an amnesia evidence containing a valid polc for an earlier potential amnesia evidence
  412. // that we have already upgraded to. We should ad this new amnesia evidence in replace of the prior
  413. // amnesia evidence with an empty polc that we have
  414. aeWithPolc := &types.AmnesiaEvidence{
  415. PotentialAmnesiaEvidence: ev,
  416. Polc: validPolc,
  417. }
  418. err = pool.AddEvidence(aeWithPolc)
  419. assert.NoError(t, err)
  420. assert.True(t, pool.IsPending(aeWithPolc))
  421. assert.Equal(t, 2, len(pool.AllPendingEvidence()))
  422. t.Log(pool.AllPendingEvidence())
  423. }
  424. func initializeStateFromValidatorSet(valSet *types.ValidatorSet, height int64) dbm.DB {
  425. stateDB := dbm.NewMemDB()
  426. state := sm.State{
  427. ChainID: evidenceChainID,
  428. LastBlockHeight: height,
  429. LastBlockTime: tmtime.Now(),
  430. Validators: valSet,
  431. NextValidators: valSet.CopyIncrementProposerPriority(1),
  432. LastValidators: valSet,
  433. LastHeightValidatorsChanged: 1,
  434. ConsensusParams: tmproto.ConsensusParams{
  435. Block: tmproto.BlockParams{
  436. MaxBytes: 22020096,
  437. MaxGas: -1,
  438. },
  439. Evidence: tmproto.EvidenceParams{
  440. MaxAgeNumBlocks: 20,
  441. MaxAgeDuration: 48 * time.Hour,
  442. MaxNum: 50,
  443. ProofTrialPeriod: 1,
  444. },
  445. },
  446. }
  447. // save all states up to height
  448. for i := int64(0); i <= height; i++ {
  449. state.LastBlockHeight = i
  450. sm.SaveState(stateDB, state)
  451. }
  452. return stateDB
  453. }
  454. func initializeValidatorState(privVal types.PrivValidator, height int64) dbm.DB {
  455. pubKey, _ := privVal.GetPubKey()
  456. validator := &types.Validator{Address: pubKey.Address(), VotingPower: 0, PubKey: pubKey}
  457. // create validator set and state
  458. valSet := &types.ValidatorSet{
  459. Validators: []*types.Validator{validator},
  460. Proposer: validator,
  461. }
  462. return initializeStateFromValidatorSet(valSet, height)
  463. }
  464. // initializeBlockStore creates a block storage and populates it w/ a dummy
  465. // block at +height+.
  466. func initializeBlockStore(db dbm.DB, state sm.State, valAddr []byte) *store.BlockStore {
  467. blockStore := store.NewBlockStore(db)
  468. for i := int64(1); i <= state.LastBlockHeight; i++ {
  469. lastCommit := makeCommit(i-1, valAddr)
  470. block, _ := state.MakeBlock(i, []types.Tx{}, lastCommit, nil,
  471. state.Validators.GetProposer().Address)
  472. const parts = 1
  473. partSet := block.MakePartSet(parts)
  474. seenCommit := makeCommit(i, valAddr)
  475. blockStore.SaveBlock(block, partSet, seenCommit)
  476. }
  477. return blockStore
  478. }
  479. func makeCommit(height int64, valAddr []byte) *types.Commit {
  480. commitSigs := []types.CommitSig{{
  481. BlockIDFlag: types.BlockIDFlagCommit,
  482. ValidatorAddress: valAddr,
  483. Timestamp: time.Now(),
  484. Signature: []byte("Signature"),
  485. }}
  486. return types.NewCommit(height, 0, types.BlockID{}, commitSigs)
  487. }
  488. func makeVote(height int64, round, index int32, addr bytes.HexBytes,
  489. blockID types.BlockID, time time.Time) *types.Vote {
  490. return &types.Vote{
  491. Type: tmproto.SignedMsgType(2),
  492. Height: height,
  493. Round: round,
  494. BlockID: blockID,
  495. Timestamp: time,
  496. ValidatorAddress: addr,
  497. ValidatorIndex: index,
  498. }
  499. }