mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-28 07:36:44 +00:00
Merge pull request #477 from maticnetwork/shivam/POS-695
Fix getTransactionReceiptsByBlock and TestGetTransactionReceiptsByBlock
This commit is contained in:
commit
65875c629d
4 changed files with 184 additions and 133 deletions
|
|
@ -382,6 +382,10 @@ func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) {
|
|||
s.blockchain.ResetWithGenesisBlock(gb)
|
||||
}
|
||||
|
||||
func (s *Ethereum) PublicBlockChainAPI() *ethapi.PublicBlockChainAPI {
|
||||
return s.handler.ethAPI
|
||||
}
|
||||
|
||||
func (s *Ethereum) Etherbase() (eb common.Address, err error) {
|
||||
s.lock.RLock()
|
||||
etherbase := s.etherbase
|
||||
|
|
|
|||
|
|
@ -670,6 +670,7 @@ func (s *PublicBlockChainAPI) GetTransactionReceiptsByBlock(ctx context.Context,
|
|||
"contractAddress": nil,
|
||||
"logs": receipt.Logs,
|
||||
"logsBloom": receipt.Bloom,
|
||||
"type": hexutil.Uint(tx.Type()),
|
||||
}
|
||||
|
||||
// Assign receipt status or post state.
|
||||
|
|
@ -681,9 +682,11 @@ func (s *PublicBlockChainAPI) GetTransactionReceiptsByBlock(ctx context.Context,
|
|||
if receipt.Logs == nil {
|
||||
fields["logs"] = [][]*types.Log{}
|
||||
}
|
||||
if borReceipt != nil {
|
||||
|
||||
if borReceipt != nil && idx == len(receipts)-1 {
|
||||
fields["transactionHash"] = txHash
|
||||
}
|
||||
|
||||
// If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
|
||||
if receipt.ContractAddress != (common.Address{}) {
|
||||
fields["contractAddress"] = receipt.ContractAddress
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
//go:build integration
|
||||
|
||||
package bor
|
||||
|
||||
import (
|
||||
|
|
@ -7,162 +5,208 @@ import (
|
|||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/bor"
|
||||
"github.com/ethereum/go-ethereum/consensus/bor/clerk"
|
||||
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/checkpoint"
|
||||
"github.com/ethereum/go-ethereum/consensus/bor/valset"
|
||||
"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/internal/ethapi"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/tests/bor/mocks"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetTransactionReceiptsByBlock(t *testing.T) {
|
||||
init := buildEthereumInstance(t, rawdb.NewMemoryDatabase())
|
||||
chain := init.ethereum.BlockChain()
|
||||
engine := init.ethereum.Engine()
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
_bor := engine.(*bor.Bor)
|
||||
defer _bor.Close()
|
||||
|
||||
// Mock /bor/span/1
|
||||
res, _ := loadSpanFromFile(t)
|
||||
|
||||
h := mocks.NewMockIHeimdallClient(ctrl)
|
||||
|
||||
h.EXPECT().Span(gomock.Any(), uint64(1)).Return(&res.Result, nil).MinTimes(1)
|
||||
h.EXPECT().Close().MinTimes(1)
|
||||
h.EXPECT().FetchCheckpoint(gomock.Any(), int64(-1)).Return(&checkpoint.Checkpoint{
|
||||
Proposer: res.Result.SelectedProducers[0].Address,
|
||||
StartBlock: big.NewInt(0),
|
||||
EndBlock: big.NewInt(int64(spanSize)),
|
||||
}, nil).AnyTimes()
|
||||
|
||||
// Mock State Sync events
|
||||
// at # sprintSize, events are fetched for [fromID, (block-sprint).Time)
|
||||
fromID := uint64(1)
|
||||
to := int64(chain.GetHeaderByNumber(0).Time)
|
||||
sample := getSampleEventRecord(t)
|
||||
|
||||
// First query will be from [id=1, (block-sprint).Time]
|
||||
eventRecords := []*clerk.EventRecordWithTime{
|
||||
buildStateEvent(sample, 1, 1),
|
||||
buildStateEvent(sample, 2, 2),
|
||||
buildStateEvent(sample, 3, 3),
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
h.EXPECT().StateSyncEvents(gomock.Any(), fromID, to).Return(eventRecords, nil).MinTimes(1)
|
||||
_bor.SetHeimdallClient(h)
|
||||
return false
|
||||
}
|
||||
|
||||
// Insert blocks for 0th sprint
|
||||
db := init.ethereum.ChainDb()
|
||||
block := init.genesis.ToBlock(db)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
signer := types.LatestSigner(init.genesis.Config)
|
||||
toAddress := common.HexToAddress("0x000000000000000000000000000000000000aaaa")
|
||||
return true
|
||||
}
|
||||
|
||||
currentValidators := []*valset.Validator{valset.NewValidator(addr, 10)}
|
||||
txHashes := map[int]common.Hash{} // blockNumber -> txHash
|
||||
func TestGetTransactionReceiptsByBlock(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
err error
|
||||
nonce uint64
|
||||
tx *types.Transaction
|
||||
txs []*types.Transaction
|
||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
addr = 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"))
|
||||
)
|
||||
|
||||
for i := uint64(1); i <= sprintSize; i++ {
|
||||
if IsSpanEnd(i) {
|
||||
currentValidators = []*valset.Validator{valset.NewValidator(addr, 10)}
|
||||
defer func() {
|
||||
if err := stack.Close(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}()
|
||||
|
||||
genesis := core.GenesisBlockForTesting(db, addr, big.NewInt(1000000))
|
||||
sprint := params.TestChainConfig.Bor.Sprint
|
||||
|
||||
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: addr,
|
||||
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: addr,
|
||||
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: addr,
|
||||
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: addr,
|
||||
Topics: []common.Hash{hash4},
|
||||
},
|
||||
}
|
||||
gen.AddUncheckedReceipt(receipt)
|
||||
gen.AddUncheckedTx(types.NewTransaction(1000, common.HexToAddress("0x0"), big.NewInt(1000), 1000, gen.BaseFee(), nil))
|
||||
|
||||
// state-sync transaction
|
||||
receipt2 := types.NewReceipt(nil, false, 0)
|
||||
receipt2.Logs = []*types.Log{
|
||||
{
|
||||
Address: addr,
|
||||
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(sprint-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 i%3 == 0 {
|
||||
txdata := &types.LegacyTx{
|
||||
Nonce: nonce,
|
||||
To: &toAddress,
|
||||
Gas: 30000,
|
||||
GasPrice: newGwei(5),
|
||||
}
|
||||
|
||||
nonce++
|
||||
|
||||
tx = types.NewTx(txdata)
|
||||
tx, err = types.SignTx(tx, signer, key)
|
||||
require.Nil(t, err, "an incorrect transaction or signer")
|
||||
|
||||
txs = []*types.Transaction{tx}
|
||||
} else {
|
||||
txs = nil
|
||||
}
|
||||
|
||||
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor, txs, currentValidators)
|
||||
insertNewBlock(t, chain, block)
|
||||
|
||||
if len(txs) != 0 {
|
||||
txHashes[int(block.Number().Uint64())] = tx.Hash()
|
||||
if err := blockBatch.Write(); err != nil {
|
||||
t.Error("Failed to write block into disk", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// state 6 was not written
|
||||
//
|
||||
fromID = uint64(4)
|
||||
to = int64(chain.GetHeaderByNumber(sprintSize).Time)
|
||||
publicBlockchainAPI := backend.PublicBlockChainAPI()
|
||||
|
||||
eventRecords = []*clerk.EventRecordWithTime{
|
||||
buildStateEvent(sample, 4, 4),
|
||||
buildStateEvent(sample, 5, 5),
|
||||
}
|
||||
h.EXPECT().StateSyncEvents(gomock.Any(), fromID, to).Return(eventRecords, nil).MinTimes(1)
|
||||
|
||||
for i := sprintSize + 1; i <= spanSize; i++ {
|
||||
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor, nil, currentValidators)
|
||||
insertNewBlock(t, chain, block)
|
||||
// check 1 : zero transactions
|
||||
receiptsOut, err := publicBlockchainAPI.GetTransactionReceiptsByBlock(context.Background(), rpc.BlockNumberOrHashWithNumber(1))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
ethAPI := ethapi.NewPublicBlockChainAPI(init.ethereum.APIBackend)
|
||||
txPoolAPI := ethapi.NewPublicTransactionPoolAPI(init.ethereum.APIBackend, nil)
|
||||
assert.Equal(t, 0, len(receiptsOut))
|
||||
|
||||
for n := 0; n < int(spanSize)+1; n++ {
|
||||
rpcNumber := rpc.BlockNumberOrHashWithNumber(rpc.BlockNumber(n))
|
||||
|
||||
txs, err := ethAPI.GetTransactionReceiptsByBlock(context.Background(), rpcNumber)
|
||||
require.Nil(t, err)
|
||||
|
||||
tx := txPoolAPI.GetTransactionByBlockNumberAndIndex(context.Background(), rpc.BlockNumber(n), 0)
|
||||
|
||||
blockMap, err := ethAPI.GetBlockByNumber(context.Background(), rpc.BlockNumber(n), true)
|
||||
require.Nil(t, err)
|
||||
|
||||
expectedTxHash, ok := txHashes[n]
|
||||
// FIXME: add `IsSprintStart(uint64(n)) || IsSpanStart(uint64(n))` after adding a full state receiver contract
|
||||
if ok {
|
||||
require.Len(t, txs, 1)
|
||||
|
||||
require.NotNil(t, tx, "not nil receipt expected")
|
||||
|
||||
require.Equal(t, expectedTxHash, tx.Hash, "got different from expected receipt")
|
||||
|
||||
blockTxs, ok := blockMap["transactions"].([]interface{})
|
||||
require.Len(t, blockTxs, 1)
|
||||
|
||||
blockTx, ok := blockTxs[0].(*ethapi.RPCTransaction)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, expectedTxHash, blockTx.Hash)
|
||||
} else {
|
||||
require.Len(t, txs, 0)
|
||||
|
||||
require.Nil(t, tx, "nil receipt expected")
|
||||
|
||||
blockTxs, _ := blockMap["transactions"].([]interface{})
|
||||
require.Len(t, blockTxs, 0)
|
||||
}
|
||||
// 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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ func TestBorFilters(t *testing.T) {
|
|||
// since all the transactions are state-sync, we will not include them as normal receipts
|
||||
rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), []*types.Receipt{})
|
||||
|
||||
// check for blocks with receipts. Since the only receipt is state-sync, we can chack the length of receipts
|
||||
// check for blocks with receipts. Since the only receipt is state-sync, we can check the length of receipts
|
||||
if len(receipts[i]) > 0 {
|
||||
// write the state-sync receipts to database
|
||||
// State sync logs don't have tx index, tx hash and other necessary fields, DeriveFieldsForBorLogs will fill those fields for websocket subscriptions
|
||||
|
|
|
|||
Loading…
Reference in a new issue