github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/gossip/c_llr_callbacks_test.go (about) 1 package gossip 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "math/big" 8 "math/rand" 9 "reflect" 10 "sync" 11 "testing" 12 "time" 13 14 "github.com/status-im/keycard-go/hexutils" 15 "github.com/stretchr/testify/require" 16 "github.com/stretchr/testify/suite" 17 "github.com/unicornultrafoundation/go-helios/hash" 18 "github.com/unicornultrafoundation/go-helios/native/idx" 19 "github.com/unicornultrafoundation/go-helios/native/pos" 20 "github.com/unicornultrafoundation/go-helios/u2udb" 21 "github.com/unicornultrafoundation/go-u2u/common" 22 "github.com/unicornultrafoundation/go-u2u/core/types" 23 "github.com/unicornultrafoundation/go-u2u/rlp" 24 "github.com/unicornultrafoundation/go-u2u/rpc" 25 26 "github.com/unicornultrafoundation/go-u2u/eventcheck" 27 "github.com/unicornultrafoundation/go-u2u/eventcheck/epochcheck" 28 "github.com/unicornultrafoundation/go-u2u/evmcore" 29 "github.com/unicornultrafoundation/go-u2u/gossip/contract/ballot" 30 "github.com/unicornultrafoundation/go-u2u/gossip/filters" 31 "github.com/unicornultrafoundation/go-u2u/native" 32 "github.com/unicornultrafoundation/go-u2u/native/ibr" 33 "github.com/unicornultrafoundation/go-u2u/native/ier" 34 ) 35 36 type IntegrationTestSuite struct { 37 suite.Suite 38 39 startEpoch, lastEpoch idx.Epoch 40 generator, processor *testEnv 41 epochToEvsMap map[idx.Epoch][]*native.LlrSignedEpochVote 42 bvs []*native.LlrSignedBlockVotes 43 blockIndices []idx.Block 44 } 45 46 func (s *IntegrationTestSuite) SetupTest() { 47 const ( 48 rounds = 20 49 validatorsNum = 10 50 startEpoch = 1 51 ) 52 53 //creating generator and processor 54 generator := newTestEnv(startEpoch, validatorsNum) 55 processor := newTestEnv(startEpoch, validatorsNum) 56 57 proposals := [][32]byte{ 58 ballotOption("Option 1"), 59 ballotOption("Option 2"), 60 ballotOption("Option 3"), 61 } 62 63 for n := uint64(0); n < rounds; n++ { 64 txs := make([]*types.Transaction, validatorsNum) 65 for i := idx.Validator(0); i < validatorsNum; i++ { 66 _, tx, cBallot, err := ballot.DeployBallot(generator.Pay(idx.ValidatorID(i+1)), generator, proposals) 67 s.Require().NoError(err) 68 s.Require().NotNil(cBallot) 69 s.Require().NotNil(tx) 70 txs[i] = tx 71 } 72 tm := sameEpoch 73 if n%10 == 0 { 74 tm = nextEpoch 75 } 76 77 rr, err := generator.ApplyTxs(tm, txs...) 78 s.Require().NoError(err) 79 for _, r := range rr { 80 s.Require().Len(r.Logs, 3) 81 for _, l := range r.Logs { 82 s.Require().NotNil(l) 83 } 84 } 85 } 86 87 s.startEpoch = startEpoch 88 s.generator = generator 89 s.processor = processor 90 91 s.epochToEvsMap = fetchEvs(s.generator) 92 bvs, blockIndices := fetchBvsBlockIdxs(s.generator) 93 s.bvs = bvs 94 s.blockIndices = blockIndices 95 s.lastEpoch = generator.store.GetEpoch() 96 } 97 98 func (s *IntegrationTestSuite) TearDownSuite() { 99 s.T().Log("tearing down test suite") 100 s.generator.Close() 101 s.processor.Close() 102 } 103 104 // fetchEvs fetches LlrSignedEpochVotes from generator 105 func fetchEvs(generator *testEnv) map[idx.Epoch][]*native.LlrSignedEpochVote { 106 m := make(map[idx.Epoch][]*native.LlrSignedEpochVote) 107 it := generator.store.table.LlrEpochVotes.NewIterator(nil, nil) 108 defer it.Release() 109 for it.Next() { 110 ev := &native.LlrSignedEpochVote{} 111 if err := rlp.DecodeBytes(it.Value(), ev); err != nil { 112 generator.store.Log.Crit("Failed to decode epoch vote", "err", err) 113 } 114 115 if ev != nil { 116 m[ev.Val.Epoch] = append(m[ev.Val.Epoch], ev) 117 } 118 } 119 return m 120 } 121 122 // fetchBvsBlockIdxs fetches block indices of blocks that have min 4 LLR votes. 123 func fetchBvsBlockIdxs(generator *testEnv) ([]*native.LlrSignedBlockVotes, []idx.Block) { 124 var bvs []*native.LlrSignedBlockVotes 125 blockIdxCountMap := make(map[idx.Block]uint64) 126 127 // fetching blockIndices with at least minVoteCount 128 fetchBlockIdxs := func(blockIdxCountMap map[idx.Block]uint64) (blockIndices []idx.Block) { 129 const minVoteCount = 4 130 for blockIdx, count := range blockIdxCountMap { 131 if count >= minVoteCount { 132 blockIndices = append(blockIndices, blockIdx) 133 } 134 } 135 return 136 } 137 138 // compute how any votes have been given for a particular block idx 139 fillblockIdxCountMap := func(bv *native.LlrSignedBlockVotes) { 140 start, end := bv.Val.Start, bv.Val.Start+idx.Block(len(bv.Val.Votes))-1 141 142 for b := start; start != 0 && b <= end; b++ { 143 blockIdxCountMap[b] += 1 144 } 145 } 146 147 it := generator.store.table.LlrBlockVotes.NewIterator(nil, nil) 148 defer it.Release() 149 for it.Next() { 150 bv := &native.LlrSignedBlockVotes{} 151 if err := rlp.DecodeBytes(it.Value(), bv); err != nil { 152 generator.store.Log.Crit("Failed to decode block vote while running ", "err", err) 153 } 154 155 if bv != nil { 156 fillblockIdxCountMap(bv) 157 bvs = append(bvs, bv) 158 } 159 } 160 161 return bvs, fetchBlockIdxs(blockIdxCountMap) 162 } 163 164 // txByBlockSubsetOf iterates over procMap keys and checks equality of transaction hashes of genenerator and processor 165 func txByBlockSubsetOf(t *testing.T, procMap, genMap map[idx.Block]types.Transactions) { 166 // assert len(procBlockToTxsMap.keys()) <= len(genBlockToTxsMap.keys()") 167 require.LessOrEqual(t, len(reflect.ValueOf(procMap).MapKeys()), len(reflect.ValueOf(genMap).MapKeys()), "The number of keys does not match") 168 for b, txs := range procMap { 169 genTxs, ok := genMap[b] 170 require.True(t, ok) 171 require.Equal(t, len(txs), len(genTxs)) 172 for i, tx := range txs { 173 require.Equal(t, tx.Hash().Hex(), genTxs[i].Hash().Hex()) 174 } 175 } 176 } 177 178 // checkLogsEquality checks equality of logs slice field byf ield 179 func checkLogsEquality(t *testing.T, genLogs, procLogs []*types.Log) { 180 require.Equal(t, len(genLogs), len(procLogs)) 181 for i, procLog := range procLogs { 182 // compare all fields 183 require.Equal(t, procLog.Address.Hex(), genLogs[i].Address.Hex()) 184 require.Equal(t, procLog.BlockHash.Hex(), genLogs[i].BlockHash.Hex()) 185 require.Equal(t, procLog.BlockNumber, genLogs[i].BlockNumber) 186 require.Equal(t, hexutils.BytesToHex(procLog.Data), hexutils.BytesToHex(genLogs[i].Data)) 187 require.Equal(t, procLog.Index, genLogs[i].Index) 188 require.Equal(t, procLog.Removed, genLogs[i].Removed) 189 190 for j, topic := range procLog.Topics { 191 require.Equal(t, topic.Hex(), genLogs[i].Topics[j].Hex()) 192 } 193 194 require.Equal(t, procLog.TxHash.Hex(), genLogs[i].TxHash.Hex()) 195 require.Equal(t, procLog.TxIndex, genLogs[i].TxIndex) 196 } 197 } 198 199 type testParams struct { 200 t *testing.T 201 genEvmBlock *evmcore.EvmBlock 202 procEvmBlock *evmcore.EvmBlock 203 genReceipts types.Receipts 204 procReceipts types.Receipts 205 } 206 207 func newTestParams(t *testing.T, genEvmBlock, procEvmBlock *evmcore.EvmBlock, genReceipts, procReceipts types.Receipts) testParams { 208 return testParams{t, genEvmBlock, procEvmBlock, genReceipts, procReceipts} 209 } 210 211 func (p testParams) compareEvmBlocks() { 212 // comparing all fields of genEvmBlock and procEvmBlock 213 require.Equal(p.t, p.genEvmBlock.Number, p.procEvmBlock.Number) 214 require.Equal(p.t, p.genEvmBlock.Hash, p.procEvmBlock.Hash) 215 require.Equal(p.t, p.genEvmBlock.ParentHash, p.procEvmBlock.ParentHash) 216 require.Equal(p.t, p.genEvmBlock.Root, p.procEvmBlock.Root) 217 require.Equal(p.t, p.genEvmBlock.TxHash, p.procEvmBlock.TxHash) 218 require.Equal(p.t, p.genEvmBlock.Time, p.procEvmBlock.Time) 219 require.Equal(p.t, p.genEvmBlock.GasLimit, p.procEvmBlock.GasLimit) 220 require.Equal(p.t, p.genEvmBlock.GasUsed, p.procEvmBlock.GasUsed) 221 require.Equal(p.t, p.genEvmBlock.BaseFee, p.procEvmBlock.BaseFee) 222 } 223 224 func (p testParams) compareReceipts() { 225 require.Equal(p.t, len(p.genReceipts), len(p.procReceipts)) 226 // compare every field except logs, I compare them separately 227 for i, initRec := range p.genReceipts { 228 require.Equal(p.t, initRec.Type, p.procReceipts[i].Type) 229 require.Equal(p.t, hexutils.BytesToHex(initRec.PostState), hexutils.BytesToHex(p.procReceipts[i].PostState)) 230 require.Equal(p.t, initRec.Status, p.procReceipts[i].Status) 231 require.Equal(p.t, initRec.CumulativeGasUsed, p.procReceipts[i].CumulativeGasUsed) 232 require.Equal(p.t, hexutils.BytesToHex(initRec.Bloom.Bytes()), hexutils.BytesToHex(p.procReceipts[i].Bloom.Bytes())) 233 require.Equal(p.t, initRec.TxHash.Hex(), p.procReceipts[i].TxHash.Hex()) 234 require.Equal(p.t, initRec.ContractAddress.Hex(), p.procReceipts[i].ContractAddress.Hex()) 235 require.Equal(p.t, initRec.GasUsed, p.procReceipts[i].GasUsed) 236 require.Equal(p.t, initRec.BlockHash.String(), p.procReceipts[i].BlockHash.String()) 237 require.Equal(p.t, initRec.BlockNumber, p.procReceipts[i].BlockNumber) 238 require.Equal(p.t, initRec.TransactionIndex, p.procReceipts[i].TransactionIndex) 239 } 240 } 241 242 func (p testParams) compareLogs(initLogs2D, procLogs2D [][]*types.Log) { 243 require.Equal(p.t, len(initLogs2D), len(procLogs2D)) 244 for i, initLogs := range initLogs2D { 245 checkLogsEquality(p.t, initLogs, procLogs2D[i]) 246 } 247 } 248 249 func (p testParams) serializeAndCompare(val1, val2 interface{}) { 250 // serialize val1 and val2 251 buf1, err := rlp.EncodeToBytes(val1) 252 require.NotNil(p.t, buf1) 253 require.NoError(p.t, err) 254 buf2, err := rlp.EncodeToBytes(val2) 255 require.NotNil(p.t, buf2) 256 require.NoError(p.t, err) 257 258 // compare serialized representation of val1 and val2 259 require.Equal(p.t, hexutils.BytesToHex(buf1), hexutils.BytesToHex(buf2)) 260 } 261 262 func (p testParams) compareTransactions(initiator, processor *testEnv) { 263 ctx := context.Background() 264 require.Equal(p.t, len(p.genEvmBlock.Transactions), len(p.procEvmBlock.Transactions)) 265 for i, tx := range p.genEvmBlock.Transactions { 266 txHash := tx.Hash() 267 initTx, _, _, err := initiator.EthAPI.GetTransaction(ctx, txHash) 268 require.NoError(p.t, err) 269 270 procTx, _, _, err := processor.EthAPI.GetTransaction(ctx, txHash) 271 require.NoError(p.t, err) 272 273 require.Equal(p.t, txHash.Hex(), p.procEvmBlock.Transactions[i].Hash().Hex()) 274 require.Equal(p.t, txHash.Hex(), initTx.Hash().Hex()) 275 require.Equal(p.t, txHash.Hex(), procTx.Hash().Hex()) 276 } 277 } 278 279 func fetchTxsbyBlock(env *testEnv) map[idx.Block]types.Transactions { 280 m := make(map[idx.Block]types.Transactions) 281 it := env.store.table.Blocks.NewIterator(nil, nil) 282 defer it.Release() 283 for it.Next() { 284 block := &native.Block{} 285 if err := rlp.DecodeBytes(it.Value(), block); err != nil { 286 env.store.Log.Crit("Failed to decode block", "err", err) 287 } 288 289 if block != nil { 290 n := idx.BytesToBlock(it.Key()) 291 txs := env.store.GetBlockTxs(n, block) 292 m[n] = txs 293 } 294 } 295 return m 296 } 297 298 type repeater struct { 299 generator *testEnv 300 processor *testEnv 301 bvs []*native.LlrSignedBlockVotes 302 blockIndices []idx.Block 303 epochToEvsMap map[idx.Epoch][]*native.LlrSignedEpochVote 304 t *testing.T 305 } 306 307 func newRepeater(s *IntegrationTestSuite) repeater { 308 return repeater{ 309 generator: s.generator, 310 processor: s.processor, 311 bvs: s.bvs, 312 blockIndices: s.blockIndices, 313 epochToEvsMap: s.epochToEvsMap, 314 t: s.T(), 315 } 316 } 317 318 // processBlockVotesRecords processes block votes. Moreover, it processes block records for every block index that has minimum 4 LLr Votes. 319 // If ProcessFullBlockRecord returns an error, omit it in fullRepeater scenario, but not in testRepeater scenario. 320 func (r repeater) processBlockVotesRecords(isTestRepeater bool) { 321 for _, bv := range r.bvs { 322 r.processor.ProcessBlockVotes(*bv) 323 } 324 325 for _, blockIdx := range r.blockIndices { 326 if br := r.generator.store.GetFullBlockRecord(blockIdx); br != nil { 327 ibr := ibr.LlrIdxFullBlockRecord{LlrFullBlockRecord: *br, Idx: blockIdx} 328 err := r.processor.ProcessFullBlockRecord(ibr) 329 if err == nil { 330 continue 331 } 332 333 // do not ingore this error in testRepeater 334 if isTestRepeater { 335 require.NoError(r.t, err) 336 } else { 337 // omit this error in fullRepeater 338 require.EqualError(r.t, err, eventcheck.ErrAlreadyProcessedBR.Error()) 339 } 340 341 } else { 342 r.generator.Log.Crit("Empty full block record popped up") 343 } 344 } 345 } 346 347 // processEpochVotesRecords processes each epoch vote. Additionally, it processes epoch block records in range [startEpoch+1; lastEpoch] 348 func (r repeater) processEpochVotesRecords(startEpoch, lastEpoch idx.Epoch) { 349 // invoke repeater.ProcessEpochVote and ProcessFullEpochRecord for epoch in range [2; lastepoch] 350 for e := idx.Epoch(startEpoch + 1); e <= lastEpoch; e++ { 351 epochVotes, ok := r.epochToEvsMap[e] 352 if !ok { 353 r.processor.store.Log.Crit("Failed to fetch epoch votes for a given epoch") 354 } 355 356 for _, v := range epochVotes { 357 require.NoError(r.t, r.processor.ProcessEpochVote(*v)) 358 } 359 360 if er := r.generator.store.GetFullEpochRecord(e); er != nil { 361 ier := ier.LlrIdxFullEpochRecord{LlrFullEpochRecord: *er, Idx: e} 362 require.NoError(r.t, r.processor.ProcessFullEpochRecord(ier)) 363 } 364 } 365 } 366 367 // compareERHashes compares epoch recors hashes. Moreover, it checks equality of hashes of epoch and block states. 368 func (r repeater) compareERHashes(startEpoch, lastEpoch idx.Epoch) { 369 for e := startEpoch; e <= lastEpoch; e++ { 370 371 genBs, genEs := r.generator.store.GetHistoryBlockEpochState(e) 372 repBs, repEs := r.processor.store.GetHistoryBlockEpochState(e) 373 require.Equal(r.t, genBs.Hash().Hex(), repBs.Hash().Hex()) 374 require.Equal(r.t, genEs.Hash().Hex(), repEs.Hash().Hex()) 375 376 genEr := r.generator.store.GetFullEpochRecord(e) 377 repEr := r.processor.store.GetFullEpochRecord(e) 378 require.Equal(r.t, genEr.Hash().Hex(), repEr.Hash().Hex()) 379 } 380 } 381 382 // compareParams checks equality of different parameters such as BlockByHash, BlockByNumber, Receipts, Logs 383 func (r repeater) compareParams() { 384 ctx := context.Background() 385 386 // compare blockbyNumber 387 for _, blockIdx := range r.blockIndices { 388 389 // comparing EvmBlock by calling BlockByHash 390 genEvmBlock, err := r.generator.EthAPI.BlockByNumber(ctx, rpc.BlockNumber(blockIdx)) 391 require.NotNil(r.t, genEvmBlock) 392 require.NoError(r.t, err) 393 394 procEvmBlock, err := r.processor.EthAPI.BlockByNumber(ctx, rpc.BlockNumber(blockIdx)) 395 require.NotNil(r.t, procEvmBlock) 396 require.NoError(r.t, err) 397 398 // compare Receipts 399 genReceipts := r.generator.store.evm.GetReceipts(blockIdx, r.generator.EthAPI.signer, genEvmBlock.Hash, genEvmBlock.Transactions) 400 require.NotNil(r.t, genReceipts) 401 procReceipts := r.processor.store.evm.GetReceipts(blockIdx, r.processor.EthAPI.signer, procEvmBlock.Hash, procEvmBlock.Transactions) 402 require.NotNil(r.t, procReceipts) 403 404 testParams := newTestParams(r.t, genEvmBlock, procEvmBlock, genReceipts, procReceipts) 405 testParams.compareEvmBlocks() 406 407 r.t.Log("comparing receipts") 408 testParams.compareReceipts() 409 410 // comparing evmBlock by calling BlockByHash 411 genEvmBlock, err = r.generator.EthAPI.BlockByHash(ctx, genEvmBlock.Hash) 412 require.NotNil(r.t, genEvmBlock) 413 require.NoError(r.t, err) 414 procEvmBlock, err = r.processor.EthAPI.BlockByHash(ctx, procEvmBlock.Hash) 415 require.NotNil(r.t, procEvmBlock) 416 require.NoError(r.t, err) 417 418 testParams = newTestParams(r.t, genEvmBlock, procEvmBlock, genReceipts, procReceipts) 419 testParams.compareEvmBlocks() 420 421 // compare Logs 422 genLogs, err := r.generator.EthAPI.GetLogs(ctx, genEvmBlock.Hash) 423 require.NoError(r.t, err) 424 425 procLogs, err := r.processor.EthAPI.GetLogs(ctx, genEvmBlock.Hash) 426 require.NoError(r.t, err) 427 428 r.t.Log("comparing logs") 429 testParams.serializeAndCompare(genLogs, procLogs) 430 testParams.compareLogs(genLogs, procLogs) 431 432 // compare ReceiptForStorage 433 genBR := r.generator.store.GetFullBlockRecord(blockIdx) 434 procBR := r.processor.store.GetFullBlockRecord(blockIdx) 435 testParams.serializeAndCompare(genBR.Receipts, procBR.Receipts) 436 437 // compare BR hashes 438 require.Equal(r.t, genBR.Hash().Hex(), procBR.Hash().Hex()) 439 440 // compare transactions 441 testParams.compareTransactions(r.generator, r.processor) 442 } 443 } 444 445 // compareLogsByFilterCriteria introduces testing logic for GetLogs function for generator and processor 446 func (r repeater) compareLogsByFilterCriteria() { 447 var crit filters.FilterCriteria 448 449 blockIdxLogsMap := func() map[idx.Block][]*types.Log { 450 ctx := context.Background() 451 m := make(map[idx.Block][]*types.Log, len(r.blockIndices)) 452 453 for _, blockIdx := range r.blockIndices { 454 block, err := r.generator.EthAPI.BlockByNumber(ctx, rpc.BlockNumber(blockIdx)) 455 require.NotNil(r.t, block) 456 require.NoError(r.t, err) 457 receipts := r.generator.store.evm.GetReceipts(blockIdx, r.generator.EthAPI.signer, block.Hash, block.Transactions) 458 for _, r := range receipts { 459 // we add only non empty logs 460 if len(r.Logs) > 0 { 461 m[blockIdx] = append(m[blockIdx], r.Logs...) 462 } 463 } 464 465 } 466 return m 467 }() 468 469 findLastNonEmptyLogs := func() (idx.Block, []*types.Log, error) { 470 for i := len(r.blockIndices) - 1; i >= 0; i-- { 471 logs, ok := blockIdxLogsMap[r.blockIndices[i]] 472 if !ok { 473 continue 474 } 475 if len(logs) > 0 { 476 return r.blockIndices[i], logs, nil 477 } 478 } 479 480 return 0, nil, errors.New("all blocks have no logs") 481 } 482 483 lastBlockNumber, lastLogs, err := findLastNonEmptyLogs() 484 require.NoError(r.t, err) 485 require.NotNil(r.t, lastLogs) 486 487 defaultCrit := filters.FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(int64(lastBlockNumber/2 + 1))} 488 489 ctx := context.Background() 490 491 config := filters.DefaultConfig() 492 config.UnindexedLogsBlockRangeLimit = idx.Block(1000) 493 genApi := filters.NewPublicFilterAPI(r.generator.EthAPI, config) 494 require.NotNil(r.t, genApi) 495 496 procApi := filters.NewPublicFilterAPI(r.processor.EthAPI, config) 497 require.NotNil(r.t, procApi) 498 499 defaultLogs, err := genApi.GetLogs(ctx, defaultCrit) 500 require.NoError(r.t, err) 501 require.NotNil(r.t, defaultLogs) 502 require.NotEqual(r.t, defaultLogs, []*types.Log{}) 503 504 findFirstNonEmptyLogs := func() (idx.Block, []*types.Log, error) { 505 for _, blockIdx := range r.blockIndices { 506 logs, ok := blockIdxLogsMap[blockIdx] 507 if !ok { 508 continue 509 } 510 if len(logs) > 0 { 511 return blockIdx, logs, nil 512 } 513 } 514 515 return 0, nil, errors.New("all blocks have no logs") 516 } 517 518 fetchFirstAddrFromLogs := func(logs []*types.Log) (common.Address, error) { 519 for i := range logs { 520 if logs[i] != nil && logs[i].Address != (common.Address{}) { 521 return logs[i].Address, nil 522 } 523 } 524 525 return common.Address{}, errors.New("no address can be found in logs") 526 } 527 528 fetchFirstTopicFromLogs := func(logs []*types.Log) (common.Hash, error) { 529 for i := range logs { 530 if logs[i] != nil && logs[i].Topics[0] != (common.Hash{}) { 531 return logs[i].Topics[0], nil 532 } 533 } 534 return common.Hash{}, errors.New("no topic can be found in logs") 535 } 536 537 fetchRandomTopicFromLogs := func(logs []*types.Log) common.Hash { 538 rand.Seed(time.Now().Unix()) 539 l := rand.Int() % len(logs) // pick log at random 540 541 rand.Seed(time.Now().Unix()) 542 t := rand.Int() % len(logs[l].Topics) // pick topic at random 543 544 return logs[l].Topics[t] 545 } 546 547 fetchRandomAddrFromLogs := func(logs []*types.Log) common.Address { 548 rand.Seed(time.Now().Unix()) 549 l := rand.Int() % len(logs) // pick log at random 550 551 return logs[l].Address 552 } 553 554 blockNumber, logs, err := findFirstNonEmptyLogs() 555 require.NoError(r.t, err) 556 require.NotNil(r.t, logs) 557 558 firstAddr, err := fetchFirstAddrFromLogs(logs) 559 require.NoError(r.t, err) 560 561 firstTopic, err := fetchFirstTopicFromLogs(logs) 562 require.NoError(r.t, err) 563 564 lastAddr, err := fetchFirstAddrFromLogs(lastLogs) 565 require.NoError(r.t, err) 566 567 lastTopic, err := fetchFirstTopicFromLogs(lastLogs) 568 require.NoError(r.t, err) 569 570 testCases := []struct { 571 name string 572 pretest func() 573 success bool 574 }{ 575 {"single valid address", 576 func() { 577 crit = filters.FilterCriteria{ 578 FromBlock: big.NewInt(int64(blockNumber)), 579 ToBlock: big.NewInt(int64(blockNumber)), 580 Addresses: []common.Address{firstAddr}, 581 } 582 }, 583 true, 584 }, 585 {"single invalid address", 586 func() { 587 invalidAddr := common.BytesToAddress([]byte("invalid address")) 588 crit = filters.FilterCriteria{ 589 FromBlock: big.NewInt(int64(blockNumber)), 590 ToBlock: big.NewInt(int64(blockNumber)), 591 Addresses: []common.Address{invalidAddr}, 592 } 593 }, 594 false, 595 }, 596 {"invalid block range", 597 func() { 598 crit = filters.FilterCriteria{ 599 FromBlock: big.NewInt(int64(blockNumber) + 1), 600 ToBlock: big.NewInt(int64(blockNumber) + 2), 601 Addresses: []common.Address{firstAddr}, 602 } 603 }, 604 false, 605 }, 606 {"block range 1-1000", 607 func() { 608 crit = defaultCrit 609 }, 610 true, 611 }, 612 {"block range 1-1000 and first topic", 613 func() { 614 crit = defaultCrit 615 crit.Topics = [][]common.Hash{{firstTopic}} 616 }, 617 true, 618 }, 619 {"block range 1-1000 and random topic", 620 func() { 621 randomTopic := fetchRandomTopicFromLogs(defaultLogs) 622 crit = defaultCrit 623 crit.Topics = [][]common.Hash{{randomTopic}} 624 }, 625 true, 626 }, 627 {"block range 1-1000 and first address", 628 func() { 629 crit = defaultCrit 630 crit.Addresses = []common.Address{firstAddr} 631 }, 632 true, 633 }, 634 {"block range 1-1000 and random address", 635 func() { 636 randomAddress := fetchRandomAddrFromLogs(defaultLogs) 637 crit = defaultCrit 638 crit.Addresses = []common.Address{randomAddress} 639 }, 640 true, 641 }, 642 {"block range 1 to lastBlockNumber", 643 func() { 644 crit = filters.FilterCriteria{ 645 FromBlock: big.NewInt(int64(1)), 646 ToBlock: big.NewInt(int64(lastBlockNumber)), 647 } 648 }, 649 true, 650 }, 651 {"block range 1 to lastBlockNumber and last topic", 652 func() { 653 crit = filters.FilterCriteria{ 654 FromBlock: big.NewInt(int64(1)), 655 ToBlock: big.NewInt(int64(lastBlockNumber)), 656 Topics: [][]common.Hash{{lastTopic}}, 657 } 658 }, 659 true, 660 }, 661 {"block range 1 to lastBlockNumber, last address", 662 func() { 663 crit = filters.FilterCriteria{ 664 FromBlock: big.NewInt(int64(1)), 665 ToBlock: big.NewInt(int64(lastBlockNumber)), 666 Addresses: []common.Address{lastAddr}, 667 } 668 }, 669 true, 670 }, 671 672 {"block range is nil and last address", 673 func() { 674 crit = filters.FilterCriteria{ 675 Addresses: []common.Address{lastAddr}, 676 } 677 }, 678 true, 679 }, 680 {"block range is nil and invalid address", 681 func() { 682 invalidAddr := common.BytesToAddress([]byte("invalid addr")) 683 crit = filters.FilterCriteria{ 684 Addresses: []common.Address{invalidAddr}, 685 } 686 }, 687 false, 688 }, 689 } 690 691 for _, tc := range testCases { 692 tc := tc 693 r.t.Run(tc.name, func(t *testing.T) { 694 tc.pretest() 695 genLogs, genErr := genApi.GetLogs(ctx, crit) 696 procLogs, procErr := procApi.GetLogs(ctx, crit) 697 if tc.success { 698 require.NoError(t, procErr) 699 require.NoError(t, genErr) 700 checkLogsEquality(t, genLogs, procLogs) 701 } else { 702 require.Equal(t, genLogs, []*types.Log{}) 703 require.Equal(t, procLogs, []*types.Log{}) 704 } 705 }) 706 } 707 708 // iterative tests with random address and random topic 709 itTestCases := []struct { 710 name string 711 rounds int 712 pretest func() 713 }{ 714 {"block range 1-1000 and random topic", 715 100, 716 func() { 717 randomTopic := fetchRandomTopicFromLogs(defaultLogs) 718 crit = defaultCrit 719 crit.Topics = [][]common.Hash{{randomTopic}} 720 }, 721 }, 722 {"block range 1-1000 and random address", 723 100, 724 func() { 725 randomAddress := fetchRandomAddrFromLogs(defaultLogs) 726 crit = defaultCrit 727 crit.Addresses = []common.Address{randomAddress} 728 }, 729 }, 730 } 731 732 for _, tc := range itTestCases { 733 tc := tc 734 r.t.Run(tc.name, func(t *testing.T) { 735 for i := 0; i < tc.rounds; i++ { 736 tc.pretest() 737 genLogs, genErr := genApi.GetLogs(ctx, crit) 738 procLogs, procErr := procApi.GetLogs(ctx, crit) 739 require.NoError(t, procErr) 740 require.NoError(t, genErr) 741 checkLogsEquality(t, genLogs, procLogs) 742 } 743 }) 744 } 745 } 746 747 // use command `go test -timeout 120s -run ^TestIntegrationTestSuite$ -testify.m TestRepeater` to run test scenario 748 func (s *IntegrationTestSuite) TestRepeater() { 749 repeater := newRepeater(s) 750 repeater.processEpochVotesRecords(s.startEpoch, s.lastEpoch) 751 repeater.processBlockVotesRecords(true) 752 753 s.Require().NoError(s.generator.store.Commit()) 754 s.Require().NoError(s.processor.store.Commit()) 755 756 // Compare transaction hashes 757 s.T().Log("Checking procBlockToTxsMap <= genBlockToTxsMap") 758 genBlockToTxsMap := fetchTxsbyBlock(s.generator) 759 procBlockToTxsMap := fetchTxsbyBlock(s.processor) 760 txByBlockSubsetOf(s.T(), procBlockToTxsMap, genBlockToTxsMap) 761 762 // compare ER hashes 763 repeater.compareERHashes(s.startEpoch+1, s.lastEpoch) 764 // compare different parameters such as Logs,Receipts, Blockhash etc 765 repeater.compareParams() 766 767 // compare Logs by different criteria 768 repeater.compareLogsByFilterCriteria() 769 } 770 771 // use command `go test -timeout 120s -run ^TestIntegrationTestSuite$ -testify.m TestFullRepeater` to run test scenario 772 func (s *IntegrationTestSuite) TestFullRepeater() { 773 774 fullRepeater := newRepeater(s) 775 776 wg := new(sync.WaitGroup) 777 wg.Add(2) 778 go func() { 779 defer wg.Done() 780 // process LLR epochVotes in fullRepeater 781 fullRepeater.processEpochVotesRecords(s.startEpoch, s.lastEpoch) 782 783 // process LLR block votes and BRs in fullReapeter 784 fullRepeater.processBlockVotesRecords(false) 785 786 }() 787 788 go func() { 789 defer wg.Done() 790 events := func() (events []*native.EventPayload) { 791 it := s.generator.store.table.Events.NewIterator(nil, nil) 792 defer it.Release() 793 for it.Next() { 794 e := &native.EventPayload{} 795 if err := rlp.DecodeBytes(it.Value(), e); err != nil { 796 s.generator.store.Log.Crit("Failed to decode event", "err", err) 797 } 798 if e != nil { 799 events = append(events, e) 800 } 801 } 802 return 803 }() 804 805 for _, e := range events { 806 s.processor.engineMu.Lock() 807 s.Require().NoError(s.processor.processEvent(e)) 808 s.processor.engineMu.Unlock() 809 } 810 }() 811 812 wg.Wait() 813 814 // Comparing the store states 815 fetchTable := func(table u2udb.Store) map[string]string { 816 var m = make(map[string]string) 817 it := table.NewIterator(nil, nil) 818 defer it.Release() 819 for it.Next() { 820 key, value := it.Key(), it.Value() 821 m[string(key)] = string(value) 822 } 823 return m 824 } 825 826 s.Require().NoError(s.generator.store.Commit()) 827 s.Require().NoError(s.processor.store.Commit()) 828 829 // Comparing generator and fullRepeater states 830 831 // 1.Comparing Tx hashes 832 s.T().Log("Checking genBlockToTxsMap <= procRepBlockToTxsMap") 833 genBlockToTxsMap := fetchTxsbyBlock(s.generator) 834 procBlockToTxsMap := fetchTxsbyBlock(s.processor) 835 txByBlockSubsetOf(s.T(), procBlockToTxsMap, genBlockToTxsMap) 836 837 // 2.Compare BlockByNumber,BlockByhash, GetReceipts, GetLogs 838 fullRepeater.compareParams() 839 840 // 2. Comparing mainDb of generator and fullRepeater 841 genKVDB, _ := s.generator.store.dbs.OpenDB("gossip") 842 fullRepKVDB, _ := s.processor.store.dbs.OpenDB("gossip") 843 genKVMap := fetchTable(genKVDB) 844 fullRepKVMap := fetchTable(fullRepKVDB) 845 846 subsetOf := func(aa, bb map[string]string) { 847 for _k, _v := range aa { 848 k, v := []byte(_k), []byte(_v) 849 if k[0] == 0 || k[0] == 'x' || k[0] == 'X' || k[0] == 'b' || k[0] == 'S' { 850 continue 851 } 852 s.Require().Equal(hexutils.BytesToHex(v), hexutils.BytesToHex([]byte(bb[_k]))) 853 } 854 } 855 856 checkEqual := func(aa, bb map[string]string) { 857 subsetOf(aa, bb) 858 subsetOf(bb, aa) 859 } 860 861 s.T().Log("Checking genKVs == fullKVs") 862 checkEqual(genKVMap, fullRepKVMap) 863 864 genKVMapAfterIndexLogsDB, _ := s.generator.store.dbs.OpenDB("gossip") 865 fullRepKVMapAfterIndexLogsDB, _ := s.processor.store.dbs.OpenDB("gossip") 866 genKVMapAfterIndexLogs := fetchTable(genKVMapAfterIndexLogsDB) 867 fullRepKVMapAfterIndexLogs := fetchTable(fullRepKVMapAfterIndexLogsDB) 868 869 // comparing the states 870 checkEqual(genKVMap, genKVMapAfterIndexLogs) 871 checkEqual(fullRepKVMap, fullRepKVMapAfterIndexLogs) 872 checkEqual(genKVMapAfterIndexLogs, fullRepKVMapAfterIndexLogs) 873 874 fullRepeater.compareLogsByFilterCriteria() 875 } 876 877 func TestLlrIntegrationTestSuite(t *testing.T) { 878 t.Skip() // skip until fixed 879 suite.Run(t, new(IntegrationTestSuite)) 880 } 881 882 func TestBlockAndEpochRecords(t *testing.T) { 883 t.Skip() // skip until fixed 884 const ( 885 validatorsNum = 10 886 startEpoch = 1 887 ) 888 // setup testEnv 889 env := newTestEnv(startEpoch, validatorsNum) 890 891 // 1.create epoch record er1 manually 892 er1 := ier.LlrIdxFullEpochRecord{Idx: idx.Epoch(startEpoch) + 1} 893 er1Hash := er1.Hash() 894 // 3. process ER1, the error will be popped up. 895 require.EqualError(t, env.ProcessFullEpochRecord(er1), eventcheck.ErrUndecidedER.Error()) 896 897 // 2.create block record manually 898 br1 := ibr.LlrIdxFullBlockRecord{Idx: idx.Block(2)} 899 br1Hash := br1.Hash() 900 //3. process BR1, the error will popped up 901 require.EqualError(t, env.ProcessFullBlockRecord(br1), eventcheck.ErrUndecidedBR.Error()) 902 903 // 4.create less than 1/3W+1 epoch votes, ER1 still should not be processed 904 for i := 1; i < 4; i++ { 905 e := fakeEvent(0, 0, true, 2, i, er1Hash) 906 ev := native.AsSignedEpochVote(e) 907 require.NoError(t, env.ProcessEpochVote(ev)) 908 } 909 require.EqualError(t, env.ProcessFullEpochRecord(er1), eventcheck.ErrUndecidedER.Error()) 910 911 // 5. add one more epoch vote,so 4 = 1/3W+1. Hence, ER1 has to be processed. 912 fmt.Println("adding 4th epoch vote") 913 e := fakeEvent(0, 0, true, 2, 4, er1Hash) 914 ev := native.AsSignedEpochVote(e) 915 require.NoError(t, env.ProcessEpochVote(ev)) 916 require.NoError(t, env.ProcessFullEpochRecord(er1)) 917 918 // 6.create epoch record er2 of same epoch as er1, but with another name. 919 er2 := ier.LlrIdxFullEpochRecord{Idx: idx.Epoch(startEpoch + 1)} 920 // 7.Get an error that the er has been already processed. 921 require.EqualError(t, env.ProcessFullEpochRecord(er2), eventcheck.ErrAlreadyProcessedER.Error()) 922 923 //8. try to process Br1 with one vote with the same epoch as er1. it will(*Validators).GetWeightByIdx(...) 924 e = fakeEvent(1, er1.Idx, false, 0, 0, br1Hash) 925 bv := native.AsSignedBlockVotes(e) 926 require.EqualError(t, env.ProcessBlockVotes(bv), errValidatorNotExist.Error()) //cause there are no validators 927 require.EqualError(t, env.ProcessFullBlockRecord(br1), eventcheck.ErrUndecidedBR.Error()) 928 929 //9,10. process er1 and er2. it should yield an ErrAlreadyProcessedER error 930 require.EqualError(t, env.ProcessFullEpochRecord(er1), eventcheck.ErrAlreadyProcessedER.Error()) 931 require.EqualError(t, env.ProcessFullEpochRecord(er2), eventcheck.ErrAlreadyProcessedER.Error()) 932 933 //11 add votes < 1/3W+1 for Br1. Record still should not be processed. 934 fmt.Println("adding 3 votes for br1") 935 for i := 5; i < 8; i++ { 936 e := fakeEvent(1, 0, false, 0, i, br1Hash) 937 bv := native.AsSignedBlockVotes(e) 938 require.NoError(t, env.ProcessBlockVotes(bv)) 939 } 940 require.EqualError(t, env.ProcessFullBlockRecord(br1), eventcheck.ErrUndecidedBR.Error()) 941 942 //12 add one vote for br1, then we have 1/3W+1 votes 943 fmt.Println("adding 4th block vote to make up to match 1/3W+1") 944 e = fakeEvent(1, 0, true, 2, 8, br1Hash) 945 bv = native.AsSignedBlockVotes(e) 946 require.NoError(t, env.ProcessBlockVotes(bv)) 947 948 // 13. create one more record of the same block, but different. 949 br2 := ibr.LlrIdxFullBlockRecord{LlrFullBlockRecord: ibr.LlrFullBlockRecord{GasUsed: 100505}, Idx: idx.Block(2)} 950 951 // 14. process br2. It should output an error, that block record hash is mismatched. 952 require.EqualError(t, env.ProcessFullBlockRecord(br2), errors.New("block record hash mismatch").Error()) 953 954 // 15 process br1 955 require.NoError(t, env.ProcessFullBlockRecord(br1)) 956 957 //16 process br1 and br2, they should yield an error that they have been already processed 958 require.EqualError(t, env.ProcessFullBlockRecord(br1), eventcheck.ErrAlreadyProcessedBR.Error()) 959 require.EqualError(t, env.ProcessFullBlockRecord(br2), eventcheck.ErrAlreadyProcessedBR.Error()) 960 } 961 962 // can not import it from native package ((( 963 964 func fakeEvent(bvsNum int, bvEpoch idx.Epoch, ersNum bool, evEpoch idx.Epoch, valID int, recordHash hash.Hash) *native.EventPayload { 965 random := &native.MutableEventPayload{} 966 r := rand.New(rand.NewSource(int64(0))) 967 random.SetVersion(1) 968 random.SetEpoch(2) 969 random.SetNetForkID(0) 970 random.SetLamport(idx.Lamport(rand.Intn(100) + 900)) 971 random.SetExtra([]byte{byte(r.Uint32())}) 972 random.SetSeq(idx.Event(r.Uint32() >> 8)) 973 random.SetCreator(idx.ValidatorID(valID)) 974 random.SetFrame(idx.Frame(r.Uint32() >> 16)) 975 random.SetCreationTime(native.Timestamp(r.Uint64())) 976 random.SetMedianTime(native.Timestamp(r.Uint64())) 977 random.SetGasPowerUsed(r.Uint64()) 978 random.SetGasPowerLeft(native.GasPowerLeft{[2]uint64{r.Uint64(), r.Uint64()}}) 979 980 bvs := native.LlrBlockVotes{} 981 if bvsNum > 0 { 982 bvs.Start = 2 983 switch { 984 case bvEpoch > 0: 985 bvs.Epoch = bvEpoch 986 random.SetEpoch(bvEpoch) 987 default: 988 bvs.Epoch = 1 989 random.SetEpoch(1) 990 } 991 } 992 993 for i := 0; i < bvsNum; i++ { 994 bvs.Votes = append(bvs.Votes, recordHash) 995 } 996 997 ev := native.LlrEpochVote{} 998 if ersNum { 999 ev.Epoch = evEpoch 1000 ev.Vote = recordHash 1001 } 1002 1003 random.SetEpochVote(ev) 1004 random.SetBlockVotes(bvs) 1005 random.SetPayloadHash(native.CalcPayloadHash(random)) 1006 1007 parent := native.MutableEventPayload{} 1008 parent.SetVersion(1) 1009 parent.SetLamport(random.Lamport() - 500) 1010 parent.SetEpoch(random.Epoch()) 1011 random.SetParents(hash.Events{parent.Build().ID()}) 1012 1013 return random.Build() 1014 } 1015 1016 func randBig(r *rand.Rand) *big.Int { 1017 b := make([]byte, r.Intn(8)) 1018 _, _ = r.Read(b) 1019 if len(b) == 0 { 1020 b = []byte{0} 1021 } 1022 return new(big.Int).SetBytes(b) 1023 } 1024 1025 func randBytes(r *rand.Rand, size int) []byte { 1026 b := make([]byte, size) 1027 r.Read(b) 1028 return b 1029 } 1030 1031 func randAddrPtr(r *rand.Rand) *common.Address { 1032 addr := randAddr(r) 1033 return &addr 1034 } 1035 1036 func randAddr(r *rand.Rand) common.Address { 1037 addr := common.Address{} 1038 r.Read(addr[:]) 1039 return addr 1040 } 1041 1042 func randAccessList(r *rand.Rand, maxAddrs, maxKeys int) types.AccessList { 1043 accessList := make(types.AccessList, r.Intn(maxAddrs)) 1044 for i := range accessList { 1045 accessList[i].Address = randAddr(r) 1046 accessList[i].StorageKeys = make([]common.Hash, r.Intn(maxKeys)) 1047 for j := range accessList[i].StorageKeys { 1048 r.Read(accessList[i].StorageKeys[j][:]) 1049 } 1050 } 1051 return accessList 1052 } 1053 1054 func TestEpochRecordWithDiffValidators(t *testing.T) { 1055 const ( 1056 validatorsNum = 10 1057 startEpoch = 2 1058 ) 1059 require := require.New(t) 1060 // setup testEnv 1061 env := newTestEnv(startEpoch, validatorsNum) 1062 1063 // Стартвые валидаторы имеют равномерные веса, стартовая эпоха - 2 1064 bs, es := env.store.GetHistoryBlockEpochState(startEpoch) 1065 1066 // get new validators with different votes 1067 newVals := func() *pos.Validators { 1068 builder := pos.NewBuilder() 1069 defaultWeight := pos.Weight(111022302) 1070 for i := idx.ValidatorID(1); i <= 10; i++ { 1071 w := defaultWeight 1072 if i%2 == 0 { 1073 w -= 10021567 1074 } else { 1075 w += 10021567 1076 } 1077 builder.Set(i, w) 1078 } 1079 return builder.Build() 1080 }() 1081 1082 // save new validators to state of epoch 2 1083 esCopy := es.Copy() 1084 esCopy.Validators = newVals 1085 1086 // process ER of 3rd epoch 1087 er := ier.LlrIdxFullEpochRecord{ 1088 LlrFullEpochRecord: ier.LlrFullEpochRecord{*bs, esCopy}, 1089 Idx: idx.Epoch(startEpoch + 1), 1090 } 1091 erHash := er.Hash() 1092 1093 for i := 1; i <= 4; i++ { 1094 e := fakeEvent(0, 0, true, startEpoch+1, i, erHash) 1095 ev := native.AsSignedEpochVote(e) 1096 // process validators with equal weights 1097 require.NoError(env.ProcessEpochVote(ev)) 1098 } 1099 1100 require.NoError(env.ProcessFullEpochRecord(er)) 1101 1102 // process ER of 4th epoch with validators with different weights 1103 1104 // get bs and es of 3rd apoch 1105 bs, es = env.store.GetHistoryBlockEpochState(startEpoch + 1) 1106 1107 // put es and bs of 3rd apoch at LlrIdxFullEpochRecord of epoch 4 1108 er = ier.LlrIdxFullEpochRecord{ 1109 LlrFullEpochRecord: ier.LlrFullEpochRecord{*bs, *es}, 1110 Idx: idx.Epoch(startEpoch + 2)} 1111 erHash = er.Hash() 1112 1113 // confirm with votes of different weights 1114 for i := 1; i <= 5; i++ { 1115 e := fakeEvent(0, 0, true, startEpoch+2, i, erHash) 1116 ev := native.AsSignedEpochVote(e) 1117 require.NoError(env.ProcessEpochVote(ev)) 1118 } 1119 1120 // process ER of epoch 4 1121 require.NoError(env.ProcessFullEpochRecord(er)) 1122 1123 // process ER for epoch 5 and yield an error cause the total weight of validators is less than threshold 1/3W+1 1124 bs, es = env.store.GetHistoryBlockEpochState(startEpoch + 2) 1125 1126 // yield validators with unequal weights to process in epoch 6 1127 newVals, partialWeight := func() (*pos.Validators, pos.Weight) { 1128 builder := pos.NewBuilder() 1129 w := pos.Weight(1000) 1130 1131 // set 7 validators with weight 1000 1132 var partialWeight pos.Weight 1133 for i := idx.ValidatorID(1); i <= 7; i++ { 1134 partialWeight += w 1135 builder.Set(i, w) 1136 } 1137 1138 w = pos.Weight(1000000) 1139 //set 8th, 9th and 10th validatora with weight 1000000 1140 for i := idx.ValidatorID(8); i <= 10; i++ { 1141 builder.Set(i, w) 1142 } 1143 1144 return builder.Build(), partialWeight 1145 }() 1146 1147 // save new validators to state 1148 esCopy = es.Copy() 1149 esCopy.Validators = newVals 1150 1151 er = ier.LlrIdxFullEpochRecord{ 1152 LlrFullEpochRecord: ier.LlrFullEpochRecord{*bs, esCopy}, 1153 Idx: idx.Epoch(startEpoch + 3), 1154 } 1155 erHash = er.Hash() 1156 1157 for i := 1; i <= 10; i++ { 1158 e := fakeEvent(0, 0, true, startEpoch+3, i, erHash) 1159 ev := native.AsSignedEpochVote(e) 1160 require.NoError(env.ProcessEpochVote(ev)) 1161 } 1162 1163 require.NoError(env.ProcessFullEpochRecord(er)) 1164 1165 // process ER with epoch 6 1166 bs, es = env.store.GetHistoryBlockEpochState(startEpoch + 3) 1167 1168 er = ier.LlrIdxFullEpochRecord{ 1169 LlrFullEpochRecord: ier.LlrFullEpochRecord{*bs, *es}, 1170 Idx: idx.Epoch(startEpoch + 4), 1171 } 1172 erHash = er.Hash() 1173 1174 for i := 1; i <= 7; i++ { 1175 e := fakeEvent(0, 0, true, startEpoch+4, i, erHash) 1176 ev := native.AsSignedEpochVote(e) 1177 require.NoError(env.ProcessEpochVote(ev)) 1178 } 1179 1180 // process ER for epoch 6 1181 // threshold weight is 1002334 = 1/3W+1 1182 // 7 validators with total weight 7000 is less than threshold weight 1183 // so 7 votes are not enough 1184 totalWeight := newVals.TotalWeight() 1185 thresholdWeight := pos.Weight(totalWeight/3 + 1) 1186 require.Less(partialWeight, thresholdWeight) 1187 require.EqualError(env.ProcessFullEpochRecord(er), eventcheck.ErrUndecidedER.Error()) 1188 } 1189 1190 func TestProcessEpochVotesWonErNil(t *testing.T) { 1191 1192 const ( 1193 validatorsNum = 10 1194 startEpoch = 2 1195 ) 1196 1197 require := require.New(t) 1198 1199 // setup testEnv 1200 env := newTestEnv(startEpoch, validatorsNum) 1201 1202 newVals, partialWeight := func() (*pos.Validators, pos.Weight) { 1203 builder := pos.NewBuilder() 1204 w := pos.Weight(1000) 1205 1206 // set 5 validators with weight 1000 1207 var partialWeight pos.Weight 1208 for i := idx.ValidatorID(1); i < 5; i++ { 1209 partialWeight += w 1210 builder.Set(i, w) 1211 } 1212 1213 w = pos.Weight(10000) 1214 //set 8th, 9th and 10th validatora with weight 10000 1215 for i := idx.ValidatorID(5); i <= 10; i++ { 1216 builder.Set(i, w) 1217 if i == idx.ValidatorID(9) || i == idx.ValidatorID(10) { 1218 continue 1219 } 1220 partialWeight += w 1221 } 1222 1223 return builder.Build(), partialWeight 1224 }() 1225 1226 bs, es := env.store.GetHistoryBlockEpochState(startEpoch) 1227 1228 esCopy := es.Copy() 1229 esCopy.Validators = newVals 1230 1231 er := ier.LlrIdxFullEpochRecord{ 1232 LlrFullEpochRecord: ier.LlrFullEpochRecord{*bs, esCopy}, 1233 Idx: idx.Epoch(startEpoch + 1), 1234 } 1235 erHash := er.Hash() 1236 1237 // process validators with equal weights 1238 for i := 1; i <= 4; i++ { 1239 e := fakeEvent(0, 0, true, startEpoch+1, i, erHash) 1240 ev := native.AsSignedEpochVote(e) 1241 require.NoError(env.ProcessEpochVote(ev)) 1242 } 1243 1244 require.NoError(env.ProcessFullEpochRecord(er)) 1245 1246 bs, es = env.store.GetHistoryBlockEpochState(startEpoch + 1) 1247 1248 er = ier.LlrIdxFullEpochRecord{ 1249 LlrFullEpochRecord: ier.LlrFullEpochRecord{*bs, *es}, 1250 Idx: idx.Epoch(startEpoch + 2), 1251 } 1252 erHash = er.Hash() 1253 1254 // process validators with inequal weighs 1255 // total weights of all validators 1256 for i := 1; i <= 8; i++ { 1257 e := fakeEvent(0, 0, true, startEpoch+2, i, erHash) 1258 ev := native.AsSignedEpochVote(e) 1259 require.NoError(env.ProcessEpochVote(ev)) 1260 } 1261 1262 res := env.store.GetLlrEpochResult(startEpoch + 2) 1263 require.NotNil(res) 1264 require.NotNil(erHash.Hex(), res.Hex()) 1265 1266 llrs := env.store.GetLlrState() 1267 actualLowestEpochToDecide := llrs.LowestEpochToDecide 1268 expectedLowestEpochToDecide := idx.Epoch(actualizeLowestIndex(uint64(llrs.LowestEpochToDecide), uint64(startEpoch+2), 1269 func(u uint64) bool { 1270 return env.store.GetLlrEpochResult(idx.Epoch(u)) != nil 1271 })) 1272 require.Equal(actualLowestEpochToDecide, expectedLowestEpochToDecide) 1273 1274 totalWeight := newVals.TotalWeight() 1275 thresholdWeight := pos.Weight(totalWeight/3 + 1) 1276 require.GreaterOrEqual(partialWeight, thresholdWeight) 1277 1278 require.NoError(env.ProcessFullEpochRecord(er)) 1279 } 1280 1281 func TestProcessEpochVotesWonErNotNilDoubleSign(t *testing.T) { 1282 1283 const ( 1284 validatorsNum = 10 1285 startEpoch = 2 1286 ) 1287 1288 require := require.New(t) 1289 1290 // setup testEnv 1291 env := newTestEnv(startEpoch, validatorsNum) 1292 1293 newVals := func() *pos.Validators { 1294 builder := pos.NewBuilder() 1295 w := pos.Weight(1000) 1296 1297 //thresholdweight totalWeight(8200)/3 +1 = 2734 1298 // set 8 validators with weight 1000 1299 1300 for i := idx.ValidatorID(1); i <= 8; i++ { 1301 //partialWeight += w 1302 builder.Set(i, w) 1303 } 1304 1305 w = pos.Weight(100) 1306 //set 9th and 10th validatora with weight 100 1307 for i := idx.ValidatorID(9); i <= 10; i++ { 1308 builder.Set(i, w) 1309 } 1310 1311 return builder.Build() 1312 }() 1313 1314 bs, es := env.store.GetHistoryBlockEpochState(startEpoch) 1315 1316 esCopy := es.Copy() 1317 esCopy.Validators = newVals 1318 1319 er := ier.LlrIdxFullEpochRecord{ 1320 LlrFullEpochRecord: ier.LlrFullEpochRecord{*bs, esCopy}, 1321 Idx: idx.Epoch(startEpoch + 1), 1322 } 1323 erHash := er.Hash() 1324 1325 // process validators with equal weights 1326 for i := 1; i <= 4; i++ { 1327 e := fakeEvent(0, 0, true, startEpoch+1, i, erHash) 1328 ev := native.AsSignedEpochVote(e) 1329 require.NoError(env.ProcessEpochVote(ev)) 1330 } 1331 1332 require.NoError(env.ProcessFullEpochRecord(er)) 1333 1334 bs, es = env.store.GetHistoryBlockEpochState(startEpoch + 1) 1335 1336 er = ier.LlrIdxFullEpochRecord{ 1337 LlrFullEpochRecord: ier.LlrFullEpochRecord{*bs, *es}, 1338 Idx: idx.Epoch(startEpoch + 2), 1339 } 1340 1341 require.Equal(er.Hash().Hex(), erHash.Hex()) 1342 1343 // process validators with inequal weighs 1344 for i := 1; i <= 3; i++ { 1345 e := fakeEvent(0, 0, true, startEpoch+2, i, erHash) 1346 ev := native.AsSignedEpochVote(e) 1347 require.NoError(env.ProcessEpochVote(ev)) 1348 1349 // output error if an event has been previously processed 1350 require.EqualError(env.ProcessEpochVote(ev), eventcheck.ErrAlreadyProcessedEV.Error()) 1351 } 1352 1353 res1 := env.store.GetLlrEpochResult(startEpoch + 2) 1354 require.Equal(res1.Hex(), erHash.Hex()) 1355 1356 // Unvoted validators submit their votes for invalid hash record to cause doublesign 1357 // the weight of these validators is 3000 that is >= w/3+1 1358 invalidHash := hash.HexToHash("0x12") 1359 for i := 5; i <= 7; i++ { 1360 e := fakeEvent(0, 0, true, startEpoch+2, i, invalidHash) 1361 ev := native.AsSignedEpochVote(e) 1362 require.NoError(env.ProcessEpochVote(ev)) 1363 } 1364 1365 // checking double sign 1366 wonEr := env.store.GetLlrEpochResult(startEpoch + 2) // erHash 1367 require.NotNil(wonEr) // wonEr != nil 1368 require.Equal(wonEr.Hex(), erHash.Hex()) 1369 require.NotEqual(wonEr.Hex(), invalidHash.Hex()) // *wonEr != ev 1370 1371 // processing an event with incorrect epoch will result in eventcheck.ErrUnknownEpochEV error 1372 i := 9 1373 invalidEpoch := idx.Epoch(1) 1374 e := fakeEvent(0, 0, true, invalidEpoch, i, erHash) 1375 require.EqualError(env.ProcessEpochVote(native.AsSignedEpochVote(e)), eventcheck.ErrUnknownEpochEV.Error()) 1376 1377 // processing an event with incorrect epoch will result in eventcheck.ErrUnknownEpochEV error 1378 i = 10 1379 invalidEpoch = idx.Epoch(20) 1380 e = fakeEvent(0, 0, true, invalidEpoch, i, erHash) 1381 require.EqualError(env.ProcessEpochVote(native.AsSignedEpochVote(e)), eventcheck.ErrUnknownEpochEV.Error()) 1382 1383 require.NoError(env.ProcessFullEpochRecord(er)) 1384 1385 // check that unvoted validators with less weight than 1/3w+1 can not damage LLR 1386 1387 bs, es = env.store.GetHistoryBlockEpochState(startEpoch + 2) 1388 1389 er = ier.LlrIdxFullEpochRecord{ 1390 LlrFullEpochRecord: ier.LlrFullEpochRecord{*bs, *es}, 1391 Idx: idx.Epoch(startEpoch + 3), 1392 } 1393 1394 // total weight = 2000, threshold 1/3w+1 is 2733 1395 for i := 1; i < 3; i++ { 1396 e := fakeEvent(0, 0, true, startEpoch+3, i, invalidHash) 1397 ev := native.AsSignedEpochVote(e) 1398 require.NoError(env.ProcessEpochVote(ev)) 1399 } 1400 1401 wonEr = env.store.GetLlrEpochResult(startEpoch + 3) 1402 require.Nil(wonEr) 1403 1404 require.EqualError(env.ProcessFullEpochRecord(er), eventcheck.ErrUndecidedER.Error()) 1405 } 1406 1407 func TestProcessBlockVotesDoubleSign(t *testing.T) { 1408 const ( 1409 validatorsNum = 10 1410 startEpoch = 1 1411 ) 1412 1413 require := require.New(t) 1414 1415 // setup testEnv 1416 env := newTestEnv(startEpoch, validatorsNum) 1417 1418 br1 := ibr.LlrIdxFullBlockRecord{Idx: idx.Block(2)} 1419 br1Hash := br1.Hash() 1420 1421 // adding 4 votes for br1 it excceeds 1/3W+1 since all weights are equal 1422 for i := 1; i <= 4; i++ { 1423 e := fakeEvent(1, 0, false, 0, i, br1Hash) 1424 bv := native.AsSignedBlockVotes(e) 1425 require.NoError(env.ProcessBlockVotes(bv)) 1426 require.EqualError(env.ProcessBlockVotes(bv), eventcheck.ErrAlreadyProcessedBVs.Error()) 1427 } 1428 1429 wonBr := env.store.GetLlrBlockResult(idx.Block(2)) 1430 require.NotNil(wonBr) 1431 require.Equal(wonBr.Hex(), br1Hash.Hex()) 1432 1433 // compare llrs.LowestBlockToDecide 1434 llrs := env.store.GetLlrState() 1435 actualLowestBlockToDecide := llrs.LowestBlockToDecide 1436 expectedLowestBlockToDecide := idx.Block(actualizeLowestIndex(uint64(llrs.LowestBlockToDecide), uint64(2), func(u uint64) bool { 1437 return env.store.GetLlrBlockResult(idx.Block(u)) != nil 1438 })) 1439 require.Equal(actualLowestBlockToDecide, expectedLowestBlockToDecide) 1440 1441 // doublesign scenario 1442 invalidHash := hash.HexToHash("0x12") 1443 for i := 1; i <= 4; i++ { 1444 e := fakeEvent(1, 0, false, 0, i, invalidHash) 1445 bv := native.AsSignedBlockVotes(e) 1446 require.NoError(env.ProcessBlockVotes(bv)) 1447 } 1448 1449 wonBr = env.store.GetLlrBlockResult(idx.Block(2)) //br1Hash 1450 require.NotNil(wonBr) 1451 require.NotEqual(wonBr.Hex(), invalidHash.Hex()) // *wonBr != bv 1452 } 1453 1454 /* 1455 1456 Blockvotes test cases 1457 1) hash of block record incorrect, block N does not belong epoch E, 1458 block vote for correct/incorrect hash 1459 validators from enother epoch 1460 2) hash of block record correct, block N does not belong epoch E, block vote for correct/incorrect hash validators >= 1/3W ,validators <=1/3W -> error anticipated 1461 3) hash of block record correct, block N belongs epoch E, block vote for correct/incorrect hash validators >= 1/3W ,validators <=1/3W 1462 1463 1464 Переменные Значения 1465 hash of block record correct | incorrect 1466 block N belongs to epoch E | does not belong to epoch E 1467 validators have total weight <= 1/3W | have total weights >= 1/3W | vote for correct/incorrect hash record | vote for correct/ incorrect epoch | have equal weights or not | vote for block N or for incorrect block 1468 1469 vote for a block 1470 1471 1472 type BlockCtx struct { 1473 Idx idx.Block 1474 Time native.Timestamp 1475 Atropos hash.Event 1476 } 1477 1478 highestBlock.Idx = blockIdx 1479 highestBlock.Atropos = block.Atropos 1480 highestBlock.Time = block.Time 1481 blockproc.BlockState{ 1482 LastBlock: highestBlock, 1483 1484 } 1485 1486 TODO test with not random validators 1487 1488 */ 1489 1490 func TestBlockVotesTests(t *testing.T) { 1491 const ( 1492 validatorsNum = 10 1493 startEpoch = 1 1494 ) 1495 1496 require := require.New(t) 1497 1498 // setup testEnv 1499 env := newTestEnv(startEpoch, validatorsNum) 1500 1501 bs, es := env.store.GetHistoryBlockEpochState(startEpoch) 1502 1503 newVals := func() *pos.Validators { 1504 builder := pos.NewBuilder() 1505 w := pos.Weight(1000) 1506 1507 //thresholdweight totalWeight(8200)/3 +1 = 2734 1508 // set 8 validators with weight 1000 1509 1510 for i := idx.ValidatorID(1); i <= 8; i++ { 1511 //partialWeight += w 1512 builder.Set(i, w) 1513 } 1514 1515 w = pos.Weight(100) 1516 //set 9th and 10th validatora with weight 100 1517 for i := idx.ValidatorID(9); i <= 10; i++ { 1518 builder.Set(i, w) 1519 } 1520 1521 return builder.Build() 1522 }() 1523 1524 // save new validators to state of epoch 2 1525 esCopy := es.Copy() 1526 esCopy.Validators = newVals 1527 1528 // process ER of 3rd epoch 1529 er := ier.LlrIdxFullEpochRecord{ 1530 LlrFullEpochRecord: ier.LlrFullEpochRecord{*bs, esCopy}, 1531 Idx: idx.Epoch(startEpoch + 1), 1532 } 1533 erHash := er.Hash() 1534 1535 incorrectValEvent := fakeEvent(0, 0, true, startEpoch+1, validatorsNum+1, erHash) 1536 ev := native.AsSignedEpochVote(incorrectValEvent) 1537 require.NoError(env.checkers.Basiccheck.ValidateEV(ev)) 1538 require.EqualError(env.checkers.Heavycheck.ValidateEV(ev), epochcheck.ErrAuth.Error()) 1539 require.EqualError(env.ProcessEpochVote(ev), errValidatorNotExist.Error()) 1540 1541 for i := 1; i <= 4; i++ { 1542 e := fakeEvent(0, 0, true, startEpoch+1, i, erHash) 1543 ev := native.AsSignedEpochVote(e) 1544 // process validators with equal weights 1545 require.NoError(env.checkers.Basiccheck.ValidateEV(ev)) 1546 // TODO debug it require.NoError(env.checkers.Heavycheck.ValidateEV(ev)) 1547 require.NoError(env.ProcessEpochVote(ev)) 1548 } 1549 1550 require.NoError(env.ProcessFullEpochRecord(er)) 1551 1552 bs, es = env.store.GetHistoryBlockEpochState(startEpoch + 1) 1553 1554 er = ier.LlrIdxFullEpochRecord{ 1555 LlrFullEpochRecord: ier.LlrFullEpochRecord{*bs, *es}, 1556 Idx: idx.Epoch(startEpoch + 2)} 1557 erHash = er.Hash() 1558 1559 br := ibr.LlrIdxFullBlockRecord{Idx: idx.Block(2)} 1560 brHash := br.Hash() 1561 1562 //we can votr for validators with the different weights 1563 incorrectBVEpoch := idx.Epoch(3) 1564 e := fakeEvent(1, incorrectBVEpoch, false, 0, 1, brHash) 1565 bvs := native.AsSignedBlockVotes(e) 1566 require.NoError(env.checkers.Basiccheck.ValidateBVs(bvs)) 1567 // require.NoError(env.checkers.Heavycheck.ValidateBVs(bvs)) debug it event has whone signature 1568 require.EqualError(env.ProcessBlockVotes(bvs), eventcheck.ErrUnknownEpochBVs.Error()) 1569 1570 // ьфлу туц е 1571 1572 //require.EqualError(env.ProcessBlockVotes(bvs), eventcheck.ErrAlreadyProcessedBVs.Error()) 1573 1574 /* 1575 for i := 5; i < 8; i++ { 1576 e := fakeEvent(20, 0, false, 0, i, brHash) 1577 bvs := native.AsSignedBlockVotes(e) 1578 require.NoError(env.checkers.Basiccheck.ValidateBVs(bvs)) 1579 require.NoError(env.checkers.Heavycheck.ValidateBVs(bvs)) 1580 require.NoError(env.ProcessBlockVotes(bvs)) 1581 } 1582 */ 1583 } 1584 1585 func fakeEventWithLamport(bv native.LlrBlockVotes, ev native.LlrEpochVote, lamport idx.Lamport) *native.EventPayload { 1586 me := &native.MutableEventPayload{} 1587 me.SetVersion(1) 1588 me.SetEpoch(2) 1589 me.SetNetForkID(0) 1590 me.SetLamport(lamport) 1591 me.SetCreator(idx.ValidatorID(1)) 1592 me.SetBlockVotes(bv) 1593 me.SetEpochVote(ev) 1594 me.SetPayloadHash(native.CalcPayloadHash(me)) 1595 return me.Build() 1596 } 1597 1598 func TestProcessBlockVotesOneValidatorMultipleBvs(t *testing.T) { 1599 t.Skip() // skip until fixed 1600 const ( 1601 validatorsNum = 10 1602 startEpoch = 2 1603 ) 1604 1605 br := ibr.LlrIdxFullBlockRecord{Idx: idx.Block(2)} 1606 brHash := br.Hash() 1607 1608 require := require.New(t) 1609 1610 getBv := func(start idx.Block, epoch idx.Epoch, vote hash.Hash, bvNum int) native.LlrBlockVotes { 1611 bv := native.LlrBlockVotes{ 1612 Start: start, 1613 Epoch: epoch, 1614 } 1615 1616 for i := 0; i < bvNum; i++ { 1617 bv.Votes = append(bv.Votes, vote) 1618 } 1619 1620 return bv 1621 } 1622 1623 testCases := []struct { 1624 name string 1625 pretest func(*testEnv) 1626 }{ 1627 { 1628 "bv with different Start", 1629 func(env *testEnv) { 1630 EventWithBvCorrectStart := fakeEventWithLamport(getBv(2, 2, brHash, 1), native.LlrEpochVote{}, idx.Lamport(rand.Intn(100))) 1631 require.NoError(env.ProcessBlockVotes(native.AsSignedBlockVotes(EventWithBvCorrectStart))) 1632 1633 randLamport := idx.Lamport(rand.Intn(100)) 1634 for i := 0; i < 9; i++ { 1635 bv := getBv(idx.Block(i+1), 2, brHash, 1) 1636 e := fakeEventWithLamport(bv, native.LlrEpochVote{}, randLamport) 1637 require.NoError(env.ProcessBlockVotes(native.AsSignedBlockVotes(e))) 1638 } 1639 }, 1640 }, 1641 { 1642 "bv with different votes", 1643 func(env *testEnv) { 1644 randLamport := idx.Lamport(rand.Intn(100)) 1645 for i := 0; i < 10; i++ { 1646 bv := getBv(2, 2, brHash, i) 1647 e := fakeEventWithLamport(bv, native.LlrEpochVote{}, randLamport) 1648 require.NoError(env.ProcessBlockVotes(native.AsSignedBlockVotes(e))) 1649 } 1650 }, 1651 }, 1652 { 1653 "bv with different Lamport", 1654 func(env *testEnv) { 1655 bv := getBv(2, 2, brHash, 1) 1656 for i := 0; i < 9; i++ { 1657 randLamport := idx.Lamport(rand.Intn(1000)) 1658 e := fakeEventWithLamport(bv, native.LlrEpochVote{}, randLamport) 1659 require.NoError(env.ProcessBlockVotes(native.AsSignedBlockVotes(e))) 1660 } 1661 }, 1662 }, 1663 } 1664 1665 for _, tc := range testCases { 1666 tc := tc 1667 t.Run(tc.name, func(t *testing.T) { 1668 env := newTestEnv(startEpoch, validatorsNum) 1669 tc.pretest(env) 1670 require.EqualError(env.ProcessFullBlockRecord(br), eventcheck.ErrUndecidedBR.Error()) 1671 }) 1672 } 1673 } 1674 1675 func TestProcessEpochVotesOneValidatorMultipleEvsDiffLamport(t *testing.T) { 1676 const ( 1677 validatorsNum = 5 1678 startEpoch = 2 1679 ) 1680 1681 require := require.New(t) 1682 1683 getEv := func(epoch idx.Epoch, vote hash.Hash) native.LlrEpochVote { 1684 return native.LlrEpochVote{ 1685 Epoch: epoch, 1686 Vote: vote, 1687 } 1688 } 1689 1690 env := newTestEnv(startEpoch, validatorsNum) 1691 1692 er := ier.LlrIdxFullEpochRecord{Idx: idx.Epoch(startEpoch) + 1} 1693 erHash := er.Hash() 1694 1695 for i := 0; i < validatorsNum; i++ { 1696 randLamport := idx.Lamport(rand.Intn(1000)) 1697 e := fakeEventWithLamport(native.LlrBlockVotes{}, getEv(startEpoch+1, erHash), randLamport) 1698 require.NoError(env.ProcessEpochVote(native.AsSignedEpochVote(e))) 1699 } 1700 1701 require.EqualError(env.ProcessFullEpochRecord(er), eventcheck.ErrUndecidedER.Error()) 1702 }