Skip to content

Commit

Permalink
Merge pull request #30 from m-Peter/eth-get-transaction-receipt-endpoint
Browse files Browse the repository at this point in the history
Implement the `eth_getTransactionReceipt` JSON-RPC endpoint
  • Loading branch information
m-Peter authored Jan 26, 2024
2 parents 81219d9 + cb4a159 commit 3e5f8ab
Show file tree
Hide file tree
Showing 7 changed files with 271 additions and 31 deletions.
73 changes: 59 additions & 14 deletions api/api.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package api

import (
"bytes"
"context"
"encoding/hex"
"fmt"
Expand All @@ -12,6 +13,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
"github.com/onflow/flow-evm-gateway/storage"
)
Expand Down Expand Up @@ -303,22 +305,65 @@ func (s *BlockChainAPI) GetTransactionReceipt(
// defined interfaces for the storage & indexer services, so that
// we can proceed with some tests, and get rid of these mock data.
receipt := map[string]interface{}{}
txIndex := uint64(64)
blockHash := common.HexToHash("0x1d59ff54b1eb26b013ce3cb5fc9dab3705b415a67127a003c3e61eb445bb8df2")
receipt["blockHash"] = blockHash
receipt["blockNumber"] = (*hexutil.Big)(big.NewInt(6139707))
receipt["contractAddress"] = nil

txReceipt, err := s.Store.GetTransactionByHash(ctx, hash)
if err != nil {
return receipt, err
}

receipt["blockNumber"] = (*hexutil.Big)(big.NewInt(int64(txReceipt.BlockHeight)))
receipt["transactionHash"] = common.HexToHash(txReceipt.TxHash)

if txReceipt.Failed {
receipt["status"] = hexutil.Uint64(0)
} else {
receipt["status"] = hexutil.Uint64(1)
}

receipt["type"] = hexutil.Uint64(txReceipt.TxType)
receipt["gasUsed"] = hexutil.Uint64(txReceipt.GasConsumed)
receipt["contractAddress"] = common.HexToAddress(txReceipt.DeployedContractAddress)

logs := []*types.Log{}
decodedLogs, err := hex.DecodeString(txReceipt.Logs)
if err != nil {
return receipt, err
}
err = rlp.Decode(bytes.NewReader(decodedLogs), &logs)
if err != nil {
return receipt, err
}
receipt["logs"] = logs
receipt["logsBloom"] = hexutil.Bytes(types.LogsBloom(logs))

decodedTx, err := hex.DecodeString(txReceipt.Transaction)
if err != nil {
return receipt, err
}
tx := &types.Transaction{}
encodedLen := uint(len(txReceipt.Transaction))
err = tx.DecodeRLP(
rlp.NewStream(
bytes.NewReader(decodedTx),
uint64(encodedLen),
),
)
if err != nil {
return receipt, err
}
from, err := types.Sender(types.LatestSignerForChainID(tx.ChainId()), tx)
if err != nil {
return receipt, err
}
receipt["from"] = from
receipt["to"] = tx.To()

txIndex := uint64(0)
receipt["transactionIndex"] = (*hexutil.Uint64)(&txIndex)
receipt["blockHash"] = common.HexToHash("0x1d59ff54b1eb26b013ce3cb5fc9dab3705b415a67127a003c3e61eb445bb8df2")
receipt["cumulativeGasUsed"] = hexutil.Uint64(50000)
receipt["effectiveGasPrice"] = (*hexutil.Big)(big.NewInt(20000000000))
receipt["from"] = common.HexToAddress("0xa7d9ddbe1f17865597fbd27ec712455208b6b76d")
receipt["gasUsed"] = hexutil.Uint64(40000)
receipt["logs"] = []*types.Log{}
receipt["logsBloom"] = "0x88df016429689c079f3b2f6ad39fa052532c56795b733da78a91ebe6a713944b"
receipt["status"] = hexutil.Uint64(1)
receipt["to"] = common.HexToAddress("0xf02c1c8e6114b1dbe8937a39260b5b0a374432bb")
receipt["transactionHash"] = common.HexToHash("0x88df016429689c079f3b2f6ad39fa052532c56795b733da78a91ebe6a713944b")
receipt["transactionIndex"] = (*hexutil.Uint64)(&txIndex)
receipt["type"] = hexutil.Uint64(2)

return receipt, nil
}

Expand Down
60 changes: 45 additions & 15 deletions api/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,29 +327,59 @@ func TestBlockChainAPI(t *testing.T) {
})

t.Run("GetTransactionReceipt", func(t *testing.T) {
event := transactionExecutedEvent(
3,
"0xb47d74ea64221eb941490bdc0c9a404dacd0a8573379a45c992ac60ee3e83c3c",
"b88c02f88982029a01808083124f809499466ed2e37b892a2ee3e9cd55a98b68f5735db280a4c6888fa10000000000000000000000000000000000000000000000000000000000000006c001a0f84168f821b427dc158c4d8083bdc4b43e178cf0977a2c5eefbcbedcc4e351b0a066a747a38c6c266b9dc2136523cef04395918de37773db63d574aabde59c12eb",
false,
2,
22514,
"0000000000000000000000000000000000000000",
"000000000000000000000000000000000000000000000000000000000000002a",
"f85af8589499466ed2e37b892a2ee3e9cd55a98b68f5735db2e1a024abdb5865df5079dcc5ac590ff6f01d5c16edbc5fab4e195d9febd1114503daa0000000000000000000000000000000000000000000000000000000000000002a",
)

store := blockchainAPI.Store
store.StoreTransaction(context.Background(), event)

receipt, err := blockchainAPI.GetTransactionReceipt(
context.Background(),
common.Hash{0, 1, 2},
common.HexToHash("0xb47d74ea64221eb941490bdc0c9a404dacd0a8573379a45c992ac60ee3e83c3c"),
)
require.NoError(t, err)

expectedReceipt := map[string]interface{}{}
txIndex := uint64(64)
blockHash := common.HexToHash("0x1d59ff54b1eb26b013ce3cb5fc9dab3705b415a67127a003c3e61eb445bb8df2")
expectedReceipt["blockHash"] = blockHash
expectedReceipt["blockNumber"] = (*hexutil.Big)(big.NewInt(6139707))
expectedReceipt["contractAddress"] = nil
expectedReceipt["cumulativeGasUsed"] = hexutil.Uint64(50000)
expectedReceipt["effectiveGasPrice"] = (*hexutil.Big)(big.NewInt(20000000000))
expectedReceipt["from"] = common.HexToAddress("0xa7d9ddbe1f17865597fbd27ec712455208b6b76d")
expectedReceipt["gasUsed"] = hexutil.Uint64(40000)
expectedReceipt["logs"] = []*types.Log{}
expectedReceipt["logsBloom"] = "0x88df016429689c079f3b2f6ad39fa052532c56795b733da78a91ebe6a713944b"
expectedReceipt["blockNumber"] = (*hexutil.Big)(big.NewInt(3))
expectedReceipt["transactionHash"] = common.HexToHash("0xb47d74ea64221eb941490bdc0c9a404dacd0a8573379a45c992ac60ee3e83c3c")
expectedReceipt["status"] = hexutil.Uint64(1)
expectedReceipt["to"] = common.HexToAddress("0xf02c1c8e6114b1dbe8937a39260b5b0a374432bb")
expectedReceipt["transactionHash"] = common.HexToHash("0x88df016429689c079f3b2f6ad39fa052532c56795b733da78a91ebe6a713944b")
expectedReceipt["transactionIndex"] = (*hexutil.Uint64)(&txIndex)
expectedReceipt["type"] = hexutil.Uint64(2)
expectedReceipt["gasUsed"] = hexutil.Uint64(22514)
expectedReceipt["contractAddress"] = common.HexToAddress("0x0000000000000000000000000000000000000000")
expectedReceipt["from"] = common.HexToAddress("0x658Bdf435d810C91414eC09147DAA6DB62406379")
to := common.HexToAddress("0x99466ED2E37B892A2Ee3E9CD55a98b68f5735db2")
expectedReceipt["to"] = &to

txIndex := uint64(0)
expectedReceipt["transactionIndex"] = (*hexutil.Uint64)(&txIndex)
expectedReceipt["blockHash"] = common.HexToHash("0x1d59ff54b1eb26b013ce3cb5fc9dab3705b415a67127a003c3e61eb445bb8df2")
expectedReceipt["cumulativeGasUsed"] = hexutil.Uint64(50000)
expectedReceipt["effectiveGasPrice"] = (*hexutil.Big)(big.NewInt(20000000000))

data, err := hex.DecodeString("000000000000000000000000000000000000000000000000000000000000002a")
require.NoError(t, err)
log := &types.Log{
Index: 0,
BlockNumber: 0,
BlockHash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"),
TxHash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"),
TxIndex: 0,
Address: common.HexToAddress("0x99466ed2e37b892a2ee3e9cd55a98b68f5735db2"),
Data: data,
Topics: []common.Hash{common.HexToHash("0x24abdb5865df5079dcc5ac590ff6f01d5c16edbc5fab4e195d9febd1114503da")},
}
logs := []*types.Log{log}
expectedReceipt["logs"] = logs
expectedReceipt["logsBloom"] = hexutil.Bytes(types.LogsBloom(logs))

assert.Equal(t, expectedReceipt, receipt)
})
Expand Down
1 change: 0 additions & 1 deletion api/fixtures/eth_json_rpc_requests.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
{"jsonrpc":"2.0","id":1,"method":"eth_getTransactionByHash","params":["0x88df016429689c079f3b2f6ad39fa052532c56795b733da78a91ebe6a713944b"]}
{"jsonrpc":"2.0","id":1,"method":"eth_getTransactionByBlockHashAndIndex","params":["0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b", "0x0"]}
{"jsonrpc":"2.0","id":1,"method":"eth_getTransactionByBlockNumberAndIndex","params":["0x29c", "0x0"]}
{"jsonrpc":"2.0","id":1,"method":"eth_getTransactionReceipt","params":["0x85d995eba9763907fdf35cd2034144dd9d53ce32cbec21349d4b12823c6860c5"]}
{"jsonrpc":"2.0","id":1,"method":"eth_coinbase"}
{"jsonrpc":"2.0","id":1,"method":"eth_getBlockByHash","params":["0xdc0818cf78f21a8e70579cb46a43643f78291264dda342ae31049421c82d21ae",false]}
{"jsonrpc":"2.0","id":1,"method":"eth_getBlockTransactionCountByHash","params":["0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"]}
Expand Down
1 change: 0 additions & 1 deletion api/fixtures/eth_json_rpc_responses.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
{"jsonrpc":"2.0","id":1,"result":{"blockHash":"0x1d59ff54b1eb26b013ce3cb5fc9dab3705b415a67127a003c3e61eb445bb8df2","blockNumber":"0x5daf3b","from":"0xa7d9ddbe1f17865597fbd27ec712455208b6b76d","gas":"0xc350","gasPrice":"0x4a817c800","hash":"0x88df016429689c079f3b2f6ad39fa052532c56795b733da78a91ebe6a713944b","input":"0x3078363836353663366336663231","nonce":"0x15","to":"0xf02c1c8e6114b1dbe8937a39260b5b0a374432bb","transactionIndex":"0x40","value":"0xf3dbb76162000","type":"0x0","v":"0x25","r":"0x96","s":"0xfa"}}
{"jsonrpc":"2.0","id":1,"result":{"blockHash":"0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b","blockNumber":"0x5daf3b","from":"0xa7d9ddbe1f17865597fbd27ec712455208b6b76d","gas":"0xc350","gasPrice":"0x4a817c800","hash":"0x88df016429689c079f3b2f6ad39fa052532c56795b733da78a91ebe6a713944b","input":"0x3078363836353663366336663231","nonce":"0x15","to":"0xf02c1c8e6114b1dbe8937a39260b5b0a374432bb","transactionIndex":"0x40","value":"0xf3dbb76162000","type":"0x0","v":"0x25","r":"0x96","s":"0xfa"}}
{"jsonrpc":"2.0","id":1,"result":{"blockHash":"0x1d59ff54b1eb26b013ce3cb5fc9dab3705b415a67127a003c3e61eb445bb8df2","blockNumber":"0x5daf3b","from":"0xa7d9ddbe1f17865597fbd27ec712455208b6b76d","gas":"0xc350","gasPrice":"0x4a817c800","hash":"0x88df016429689c079f3b2f6ad39fa052532c56795b733da78a91ebe6a713944b","input":"0x3078363836353663366336663231","nonce":"0x15","to":"0xf02c1c8e6114b1dbe8937a39260b5b0a374432bb","transactionIndex":"0x40","value":"0xf3dbb76162000","type":"0x0","v":"0x25","r":"0x96","s":"0xfa"}}
{"jsonrpc":"2.0","id":1,"result":{"blockHash":"0x1d59ff54b1eb26b013ce3cb5fc9dab3705b415a67127a003c3e61eb445bb8df2","blockNumber":"0x5daf3b","contractAddress":null,"cumulativeGasUsed":"0xc350","effectiveGasPrice":"0x4a817c800","from":"0xa7d9ddbe1f17865597fbd27ec712455208b6b76d","gasUsed":"0x9c40","logs":[],"logsBloom":"0x88df016429689c079f3b2f6ad39fa052532c56795b733da78a91ebe6a713944b","status":"0x1","to":"0xf02c1c8e6114b1dbe8937a39260b5b0a374432bb","transactionHash":"0x88df016429689c079f3b2f6ad39fa052532c56795b733da78a91ebe6a713944b","transactionIndex":"0x40","type":"0x2"}}
{"jsonrpc":"2.0","id":1,"result":"0xf02c1c8e6114b1dbe8937a39260b5b0a374432bb"}
{"jsonrpc":"2.0","id":1,"result":{"difficulty":"0x4ea3f27bc","extraData":"0x476574682f4c5649562f76312e302e302f6c696e75782f676f312e342e32","gasLimit":"0x1388","gasUsed":"0x0","hash":"0xdc0818cf78f21a8e70579cb46a43643f78291264dda342ae31049421c82d21ae","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0xbb7b8287f3f0a933474a79eae42cbca977791171","mixHash":"0x4fffe9ae21f1c9e15207b1f472d5bbdd68c9595d461666602f2be20daf5e7843","nonce":"0x689056015818adbe","number":"0x1b4","parentHash":"0xe99e022112df268087ea7eafaf4790497fd21dbeeb6bd7a1721df161a6657a54","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x220","stateRoot":"0xddc8b0234c2e0cad087c8b389aa7ef01f7d79b2570bccb77ce48648aa61c904d","timestamp":"0x55ba467c","totalDifficulty":"0x78ed983323d","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}
{"jsonrpc":"2.0","id":1,"result":"0x188aa"}
Expand Down
30 changes: 30 additions & 0 deletions api/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,36 @@ func TestServerJSONRPCOveHTTPHandler(t *testing.T) {

assert.Equal(t, expectedResponse, strings.TrimSuffix(string(content), "\n"))
})

t.Run("eth_getTransactionReceipt", func(t *testing.T) {
request := `{"jsonrpc":"2.0","id":1,"method":"eth_getTransactionReceipt","params":["0xb47d74ea64221eb941490bdc0c9a404dacd0a8573379a45c992ac60ee3e83c3c"]}`
expectedResponse := `{"jsonrpc":"2.0","id":1,"result":{"blockHash":"0x1d59ff54b1eb26b013ce3cb5fc9dab3705b415a67127a003c3e61eb445bb8df2","blockNumber":"0x3","contractAddress":"0x0000000000000000000000000000000000000000","cumulativeGasUsed":"0xc350","effectiveGasPrice":"0x4a817c800","from":"0x658bdf435d810c91414ec09147daa6db62406379","gasUsed":"0x57f2","logs":[{"address":"0x99466ed2e37b892a2ee3e9cd55a98b68f5735db2","topics":["0x24abdb5865df5079dcc5ac590ff6f01d5c16edbc5fab4e195d9febd1114503da"],"data":"0x000000000000000000000000000000000000000000000000000000000000002a","blockNumber":"0x0","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","logIndex":"0x0","removed":false}],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000020000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000","status":"0x1","to":"0x99466ed2e37b892a2ee3e9cd55a98b68f5735db2","transactionHash":"0xb47d74ea64221eb941490bdc0c9a404dacd0a8573379a45c992ac60ee3e83c3c","transactionIndex":"0x0","type":"0x2"}}`

event := transactionExecutedEvent(
3,
"0xb47d74ea64221eb941490bdc0c9a404dacd0a8573379a45c992ac60ee3e83c3c",
"b88c02f88982029a01808083124f809499466ed2e37b892a2ee3e9cd55a98b68f5735db280a4c6888fa10000000000000000000000000000000000000000000000000000000000000006c001a0f84168f821b427dc158c4d8083bdc4b43e178cf0977a2c5eefbcbedcc4e351b0a066a747a38c6c266b9dc2136523cef04395918de37773db63d574aabde59c12eb",
false,
2,
22514,
"0000000000000000000000000000000000000000",
"000000000000000000000000000000000000000000000000000000000000002a",
"f85af8589499466ed2e37b892a2ee3e9cd55a98b68f5735db2e1a024abdb5865df5079dcc5ac590ff6f01d5c16edbc5fab4e195d9febd1114503daa0000000000000000000000000000000000000000000000000000000000000002a",
)

err := store.StoreTransaction(context.Background(), event)
require.NoError(t, err)

resp := rpcRequest(url, request, "origin", "test.com")
defer resp.Body.Close()

content, err := io.ReadAll(resp.Body)
if err != nil {
panic(err)
}

assert.Equal(t, expectedResponse, strings.TrimSuffix(string(content), "\n"))
})
}

func TestServerJSONRPCOveWebSocketHandler(t *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ func runIndexer(ctx context.Context, store *storage.Store, logger zerolog.Logger
for _, event := range response.Events {
logger.Info().Msgf(" %s", event.Value)
if event.Type == "flow.evm.TransactionExecuted" {
store.StoreTransaction(ctx, event.Value)
store.UpdateAccountNonce(ctx, event.Value)
store.StoreLog(ctx, event.Value)
}
Expand Down
Loading

0 comments on commit 3e5f8ab

Please sign in to comment.