//go:build integration package bor import ( "context" "math/big" "testing" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" "github.com/stretchr/testify/assert" ) var ( key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") addrr = crypto.PubkeyToAddress(key1.PublicKey) stack, _ = node.New(&node.DefaultConfig) backend, _ = eth.New(stack, ðconfig.Defaults) db = backend.ChainDb() hash1 = common.BytesToHash([]byte("topic1")) hash2 = common.BytesToHash([]byte("topic2")) hash3 = common.BytesToHash([]byte("topic3")) hash4 = common.BytesToHash([]byte("topic4")) hash5 = common.BytesToHash([]byte("topic5")) ) func duplicateInArray(arr []common.Hash) bool { visited := make(map[common.Hash]bool, 0) for i := 0; i < len(arr); i++ { if visited[arr[i]] == true { return true } else { visited[arr[i]] = true } } return false } func areDifferentHashes(receipts []map[string]interface{}) bool { addresses := []common.Hash{} for i := 0; i < len(receipts); i++ { addresses = append(addresses, receipts[i]["transactionHash"].(common.Hash)) if duplicateInArray(addresses) { return false } } return true } // Test for GetTransactionReceiptsByBlock func testGetTransactionReceiptsByBlock(t *testing.T, publicBlockchainAPI *ethapi.PublicBlockChainAPI) { // check 1 : zero transactions receiptsOut, err := publicBlockchainAPI.GetTransactionReceiptsByBlock(context.Background(), rpc.BlockNumberOrHashWithNumber(1)) if err != nil { t.Error(err) } assert.Equal(t, 0, len(receiptsOut)) // check 2 : one transactions ( normal ) receiptsOut, err = publicBlockchainAPI.GetTransactionReceiptsByBlock(context.Background(), rpc.BlockNumberOrHashWithNumber(2)) if err != nil { t.Error(err) } assert.Equal(t, 1, len(receiptsOut)) assert.True(t, areDifferentHashes(receiptsOut)) // check 3 : two transactions ( both normal ) receiptsOut, err = publicBlockchainAPI.GetTransactionReceiptsByBlock(context.Background(), rpc.BlockNumberOrHashWithNumber(3)) if err != nil { t.Error(err) } assert.Equal(t, 2, len(receiptsOut)) assert.True(t, areDifferentHashes(receiptsOut)) // check 4 : two transactions ( one normal + one state-sync) receiptsOut, err = publicBlockchainAPI.GetTransactionReceiptsByBlock(context.Background(), rpc.BlockNumberOrHashWithNumber(4)) if err != nil { t.Error(err) } assert.Equal(t, 2, len(receiptsOut)) assert.True(t, areDifferentHashes(receiptsOut)) } // Test for GetTransactionByBlockNumberAndIndex func testGetTransactionByBlockNumberAndIndex(t *testing.T, publicTransactionPoolAPI *ethapi.PublicTransactionPoolAPI) { // check 1 : False ( no transaction ) tx := publicTransactionPoolAPI.GetTransactionByBlockNumberAndIndex(context.Background(), rpc.BlockNumber(1), 0) assert.Nil(t, tx) // check 2 : Normal Transaction tx = publicTransactionPoolAPI.GetTransactionByBlockNumberAndIndex(context.Background(), rpc.BlockNumber(2), 0) assert.Equal(t, common.HexToAddress("0x24"), *tx.To) // check 3 : Normal Transaction tx = publicTransactionPoolAPI.GetTransactionByBlockNumberAndIndex(context.Background(), rpc.BlockNumber(3), 0) assert.Equal(t, common.HexToAddress("0x992"), *tx.To) // check 4 : Normal Transaction tx = publicTransactionPoolAPI.GetTransactionByBlockNumberAndIndex(context.Background(), rpc.BlockNumber(3), 1) assert.Equal(t, common.HexToAddress("0x993"), *tx.To) // check 5 : Normal Transaction tx = publicTransactionPoolAPI.GetTransactionByBlockNumberAndIndex(context.Background(), rpc.BlockNumber(4), 0) assert.Equal(t, common.HexToAddress("0x1000"), *tx.To) // check 5 : Normal Transaction tx = publicTransactionPoolAPI.GetTransactionByBlockNumberAndIndex(context.Background(), rpc.BlockNumber(4), 1) assert.Equal(t, common.HexToAddress("0x0"), *tx.To) } // This Testcase tests functions for RPC API calls. // NOTE : Changes to this function might affect the child testcases. func TestAPIs(t *testing.T) { defer func() { if err := stack.Close(); err != nil { t.Error(err) } }() genesis := core.GenesisBlockForTesting(db, addrr, big.NewInt(1000000)) testBorConfig := params.TestChainConfig.Bor chain, receipts := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 6, func(i int, gen *core.BlockGen) { switch i { case 1: // 1 normal transaction on block 2 receipt := types.NewReceipt(nil, false, 0) receipt.Logs = []*types.Log{ { Address: addrr, Topics: []common.Hash{hash1}, }, } gen.AddUncheckedReceipt(receipt) gen.AddUncheckedTx(types.NewTransaction(24, common.HexToAddress("0x24"), big.NewInt(24), 24, gen.BaseFee(), nil)) case 2: // 2 normal transactions on block 3 receipt := types.NewReceipt(nil, false, 0) receipt.Logs = []*types.Log{ { Address: addrr, Topics: []common.Hash{hash2}, }, } gen.AddUncheckedReceipt(receipt) gen.AddUncheckedTx(types.NewTransaction(992, common.HexToAddress("0x992"), big.NewInt(992), 992, gen.BaseFee(), nil)) receipt2 := types.NewReceipt(nil, false, 0) receipt2.Logs = []*types.Log{ { Address: addrr, Topics: []common.Hash{hash3}, }, } gen.AddUncheckedReceipt(receipt2) gen.AddUncheckedTx(types.NewTransaction(993, common.HexToAddress("0x993"), big.NewInt(993), 993, gen.BaseFee(), nil)) case 3: // 1 normal transaction, 1 state-sync transaction on block 4 receipt := types.NewReceipt(nil, false, 0) receipt.Logs = []*types.Log{ { Address: addrr, Topics: []common.Hash{hash4}, }, } gen.AddUncheckedReceipt(receipt) gen.AddUncheckedTx(types.NewTransaction(1000, common.HexToAddress("0x1000"), big.NewInt(1000), 1000, gen.BaseFee(), nil)) // state-sync transaction receipt2 := types.NewReceipt(nil, false, 0) receipt2.Logs = []*types.Log{ { Address: addrr, Topics: []common.Hash{hash5}, }, } gen.AddUncheckedReceipt(receipt2) // not adding unchecked tx as it will be added as a state-sync tx later } }) for i, block := range chain { // write the block to database rawdb.WriteBlock(db, block) rawdb.WriteCanonicalHash(db, block.Hash(), block.NumberU64()) rawdb.WriteHeadBlockHash(db, block.Hash()) blockBatch := db.NewBatch() if i%int(testBorConfig.CalculateSprint(block.NumberU64())-1) != 0 { // if it is not sprint start write all the transactions as normal transactions. rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), receipts[i]) } else { // check for blocks with receipts. Since in state-sync block, we have 1 normal txn and 1 state-sync txn. if len(receipts[i]) > 0 { // We write receipts for the normal transaction. rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), receipts[i][:1]) // write the state-sync receipts to database => receipts[i][1:] => receipts[i][1] // State sync logs don't have tx index, tx hash and other necessary fields, DeriveFieldsForBorLogs will fill those fields for websocket subscriptions // DeriveFieldsForBorLogs argurments: // 1. State-sync logs // 2. Block Hash // 3. Block Number // 4. Transactions in the block(except state-sync) i.e. 1 in our case // 5. AllLogs -(minus) StateSyncLogs ; since we only have state-sync tx, it will be 1 types.DeriveFieldsForBorLogs(receipts[i][1].Logs, block.Hash(), block.NumberU64(), uint(1), uint(1)) rawdb.WriteBorReceipt(blockBatch, block.Hash(), block.NumberU64(), &types.ReceiptForStorage{ Status: types.ReceiptStatusSuccessful, // make receipt status successful Logs: receipts[i][1].Logs, }) rawdb.WriteBorTxLookupEntry(blockBatch, block.Hash(), block.NumberU64()) } } if err := blockBatch.Write(); err != nil { t.Error("Failed to write block into disk", "err", err) } } // Testing GetTransactionReceiptsByBlock publicBlockchainAPI := backend.PublicBlockChainAPI() testGetTransactionReceiptsByBlock(t, publicBlockchainAPI) // Testing GetTransactionByBlockNumberAndIndex nonceLock := new(ethapi.AddrLocker) publicTransactionPoolAPI := ethapi.NewPublicTransactionPoolAPI(backend.APIBackend, nonceLock) testGetTransactionByBlockNumberAndIndex(t, publicTransactionPoolAPI) }