Skip to content
This repository was archived by the owner on Jan 9, 2024. It is now read-only.

Commit 06efdab

Browse files
Sixtysixtercanepat
andauthored
Implementation of eth_getTransactionReceipt API (partial) (#95)
Co-authored-by: canepat <16927169+canepat@users.noreply.github.com>
1 parent 2273296 commit 06efdab

File tree

11 files changed

+225
-39
lines changed

11 files changed

+225
-39
lines changed

docs/API.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ The following table shows the current [JSON RPC API](https://eth.wiki/json-rpc/A
3030
| eth_getTransactionByHash | Yes | partially implemented |
3131
| eth_getTransactionByBlockHashAndIndex | Yes | |
3232
| eth_getTransactionByBlockNumberAndIndex | Yes | |
33-
| eth_getTransactionReceipt | - | not yet implemented |
33+
| eth_getTransactionReceipt | Yes | partially implemented |
3434
| | | |
3535
| eth_estimateGas | - | not yet implemented |
3636
| eth_getBalance | Yes | |

silkrpc/commands/eth_api.cpp

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "eth_api.hpp"
1818

1919
#include <algorithm>
20+
#include <cstring>
2021
#include <exception>
2122
#include <iostream>
2223
#include <string>
@@ -581,12 +582,45 @@ asio::awaitable<void> EthereumRpcApi::handle_eth_get_transaction_by_block_number
581582

582583
// https://eth.wiki/json-rpc/API#eth_gettransactionreceipt
583584
asio::awaitable<void> EthereumRpcApi::handle_eth_get_transaction_receipt(const nlohmann::json& request, nlohmann::json& reply) {
585+
auto params = request["params"];
586+
if (params.size() != 1) {
587+
auto error_msg = "invalid eth_getTransactionReceipt params: " + params.dump();
588+
SILKRPC_ERROR << error_msg << "\n";
589+
reply = make_json_error(request["id"], 100, error_msg);
590+
co_return;
591+
}
592+
auto transaction_hash = params[0].get<evmc::bytes32>();
593+
SILKRPC_DEBUG << "transaction_hash: " << transaction_hash << "\n";
584594
auto tx = co_await database_->begin();
585595

586596
try {
587597
ethdb::TransactionDatabase tx_database{*tx};
598+
reply = make_json_content(request["id"], nullptr);
599+
const auto block_with_hash = co_await core::rawdb::read_block_by_transaction_hash(tx_database, transaction_hash);
600+
auto receipts = co_await core::get_receipts(tx_database, block_with_hash.hash, block_with_hash.block.header.number);
601+
auto transactions = block_with_hash.block.transactions;
602+
if (receipts.size() != transactions.size()) {
603+
throw std::invalid_argument{"Unexpected size for receipts in handle_eth_get_transaction_receipt"};
604+
}
588605

589-
reply = make_json_content(request["id"], to_quantity(0));
606+
size_t tx_index = -1;
607+
for (size_t idx{0}; idx < transactions.size(); idx++) {
608+
auto ethash_hash{hash_of_transaction(transactions[idx])};
609+
610+
SILKRPC_TRACE << "tx " << idx << ") hash: " << silkworm::to_bytes32(ethash_hash.bytes) << "\n";
611+
if (std::memcmp(transaction_hash.bytes, ethash_hash.bytes, silkworm::kHashLength) == 0) {
612+
tx_index = idx;
613+
break;
614+
}
615+
}
616+
617+
if (tx_index == -1) {
618+
throw std::invalid_argument{"Unexpected transaction index in handle_eth_get_transaction_receipt"};
619+
}
620+
reply = make_json_content(request["id"], receipts[tx_index]);
621+
} catch (const std::invalid_argument& iv) {
622+
SILKRPC_DEBUG << "invalid_argument: " << iv.what() << " processing request: " << request.dump() << "\n";
623+
reply = make_json_content(request["id"], {});
590624
} catch (const std::exception& e) {
591625
SILKRPC_ERROR << "exception: " << e.what() << " processing request: " << request.dump() << "\n";
592626
reply = make_json_error(request["id"], 100, e.what());

silkrpc/commands/parity_api.cpp

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
#include <string>
2020

2121
#include <silkworm/common/util.hpp>
22-
#include <silkworm/types/bloom.cpp> // NOLINT(build/include) m3_2048 not exported
2322

2423
#include <silkrpc/common/constants.hpp>
2524
#include <silkrpc/common/log.hpp>
@@ -34,19 +33,6 @@
3433

3534
namespace silkrpc::commands {
3635

37-
silkworm::Bloom bloom_from_logs(const Logs& logs) {
38-
SILKRPC_TRACE << "bloom_from_logs #logs: " << logs.size() << "\n";
39-
silkworm::Bloom bloom{};
40-
for (auto const& log : logs) {
41-
silkworm::m3_2048(bloom, silkworm::full_view(log.address));
42-
for (const auto& topic : log.topics) {
43-
silkworm::m3_2048(bloom, silkworm::full_view(topic));
44-
}
45-
}
46-
SILKRPC_TRACE << "bloom_from_logs bloom: " << silkworm::to_hex(silkworm::full_view(bloom)) << "\n";
47-
return bloom;
48-
}
49-
5036
// https://eth.wiki/json-rpc/API#parity_getblockreceipts
5137
asio::awaitable<void> ParityRpcApi::handle_parity_get_block_receipts(const nlohmann::json& request, nlohmann::json& reply) {
5238
auto params = request["params"];
@@ -70,15 +56,6 @@ asio::awaitable<void> ParityRpcApi::handle_parity_get_block_receipts(const nlohm
7056
auto receipts{co_await core::get_receipts(tx_database, block_hash, block_number)};
7157
SILKRPC_INFO << "#receipts: " << receipts.size() << "\n";
7258

73-
for (auto& receipt : receipts) {
74-
auto tx = block_with_hash.block.transactions[receipt.tx_index];
75-
tx.recover_sender();
76-
receipt.from = tx.from;
77-
receipt.to = tx.to;
78-
receipt.type = tx.type;
79-
receipt.bloom = bloom_from_logs(receipt.logs);
80-
}
81-
8259
reply = make_json_content(request["id"], receipts);
8360
} catch (const std::invalid_argument& iv) {
8461
SILKRPC_DEBUG << "invalid_argument: " << iv.what() << " processing request: " << request.dump() << "\n";

silkrpc/core/rawdb/chain.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,18 @@ asio::awaitable<silkworm::BlockWithHash> read_block_by_number(const DatabaseRead
133133
co_return co_await read_block(reader, block_hash, block_number);
134134
}
135135

136+
asio::awaitable<silkworm::BlockWithHash> read_block_by_transaction_hash(const DatabaseReader& reader, const evmc::bytes32& transaction_hash) {
137+
const silkworm::ByteView tx_hash{transaction_hash.bytes, silkworm::kHashLength};
138+
139+
auto bytes = co_await reader.get_one(silkworm::db::table::kTxLookup.name, tx_hash);
140+
if (bytes.empty()) {
141+
throw std::invalid_argument{"empty block number value in read_block_by_transaction_hash"};
142+
}
143+
auto block_number = std::stoul(silkworm::to_hex(bytes), 0, 16);
144+
SILKRPC_TRACE << "Block number " << block_number << " for transaction hash " << transaction_hash << "\n";
145+
co_return co_await core::rawdb::read_block_by_number(reader, block_number);
146+
}
147+
136148
asio::awaitable<silkworm::BlockWithHash> read_block(const DatabaseReader& reader, const evmc::bytes32& block_hash, uint64_t block_number) {
137149
auto header = co_await read_header(reader, block_hash, block_number);
138150
SILKRPC_INFO << "header: number=" << header.number << "\n";
@@ -255,6 +267,7 @@ asio::awaitable<Receipts> read_raw_receipts(const DatabaseReader& reader, const
255267
Walker walker = [&](const silkworm::Bytes& k, const silkworm::Bytes& v) {
256268
auto tx_id = boost::endian::load_big_u32(&k[sizeof(uint64_t)]);
257269
cbor_decode(v, receipts[tx_id].logs);
270+
receipts[tx_id].bloom = bloom_from_logs(receipts[tx_id].logs);
258271
SILKRPC_DEBUG << "#receipts[" << tx_id << "].logs: " << receipts[tx_id].logs.size() << "\n";
259272
return true;
260273
};
@@ -295,9 +308,12 @@ asio::awaitable<Receipts> read_receipts(const DatabaseReader& reader, const evmc
295308
if (i == 0) {
296309
receipts[i].gas_used = receipts[i].cumulative_gas_used;
297310
} else {
298-
receipts[i].gas_used = receipts[i].cumulative_gas_used - receipts[i-1].cumulative_gas_used;;
311+
receipts[i].gas_used = receipts[i].cumulative_gas_used - receipts[i-1].cumulative_gas_used;
299312
}
300313

314+
receipts[i].from = senders[i];
315+
receipts[i].to = transactions[i].to;
316+
301317
// The derived fields of receipt are taken from block and transaction
302318
for (size_t j{0}; j < receipts[i].logs.size(); j++) {
303319
receipts[i].logs[j].block_number = block_number;

silkrpc/core/rawdb/chain.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ asio::awaitable<silkworm::BlockWithHash> read_block_by_hash(const DatabaseReader
5151

5252
asio::awaitable<silkworm::BlockWithHash> read_block_by_number(const DatabaseReader& reader, uint64_t block_number);
5353

54+
asio::awaitable<silkworm::BlockWithHash> read_block_by_transaction_hash(const DatabaseReader& reader, const evmc::bytes32& transaction_hash);
55+
5456
asio::awaitable<silkworm::BlockWithHash> read_block(const DatabaseReader& reader, const evmc::bytes32& block_hash, uint64_t block_number);
5557

5658
asio::awaitable<silkworm::BlockHeader> read_header_by_hash(const DatabaseReader& reader, const evmc::bytes32& block_hash);

silkrpc/core/rawdb/chain_test.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,15 @@ TEST_CASE("decode empty receipt list", "[silkrpc::core][cbor_decode]") {
3434

3535
TEST_CASE("decode receipt list", "[silkrpc::core][cbor_decode]") {
3636
std::vector<Receipt> receipts{};
37-
auto bytes = silkworm::from_hex("8283f6001a0032f05d83f6011a00beadd0").value();
37+
auto bytes = silkworm::from_hex("838400f601196d398400f6011a00371b0b8400f6011a003947f4").value();
3838
cbor_decode(bytes, receipts);
39-
CHECK(receipts.size() == 2);
40-
CHECK(receipts[0].success == false);
41-
CHECK(receipts[0].cumulative_gas_used == 0x32f05d);
39+
CHECK(receipts.size() == 3);
40+
CHECK(receipts[0].success == true);
41+
CHECK(receipts[0].cumulative_gas_used == 0x6d39);
4242
CHECK(receipts[1].success == true);
43-
CHECK(receipts[1].cumulative_gas_used == 0xbeadd0);
43+
CHECK(receipts[1].cumulative_gas_used == 0x371b0b);
44+
CHECK(receipts[2].success == true);
45+
CHECK(receipts[2].cumulative_gas_used == 0x3947f4);
4446
}
4547

4648
TEST_CASE("decode empty log list", "[silkrpc::core][cbor_decode]") {

silkrpc/json/types.cpp

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -305,20 +305,27 @@ void to_json(nlohmann::json& json, const Receipt& receipt) {
305305
void from_json(const nlohmann::json& json, Receipt& receipt) {
306306
SILKRPC_TRACE << "from_json<Receipt> json: " << json.dump() << "\n";
307307
if (json.is_array()) {
308-
if (json.size() < 3) {
308+
if (json.size() < 4) {
309309
throw std::system_error{std::make_error_code(std::errc::invalid_argument), "Receipt CBOR: missing entries"};
310310
}
311-
if (!json[0].is_null()) {
312-
throw std::system_error{std::make_error_code(std::errc::invalid_argument), "Receipt CBOR: null expected in [0]"};
311+
if (!json[0].is_number()) {
312+
throw std::system_error{std::make_error_code(std::errc::invalid_argument), "Receipt CBOR: number expected in [0]"};
313313
}
314-
if (!json[1].is_number()) {
315-
throw std::system_error{std::make_error_code(std::errc::invalid_argument), "Receipt CBOR: number expected in [1]"};
314+
receipt.type = json[0];
315+
316+
if (!json[1].is_null()) {
317+
throw std::system_error{std::make_error_code(std::errc::invalid_argument), "Receipt CBOR: null expected in [1]"};
316318
}
317-
receipt.success = json[1] == 1u;
319+
318320
if (!json[2].is_number()) {
319321
throw std::system_error{std::make_error_code(std::errc::invalid_argument), "Receipt CBOR: number expected in [2]"};
320322
}
321-
receipt.cumulative_gas_used = json[2];
323+
receipt.success = json[2] == 1u;
324+
325+
if (!json[3].is_number()) {
326+
throw std::system_error{std::make_error_code(std::errc::invalid_argument), "Receipt CBOR: number expected in [3]"};
327+
}
328+
receipt.cumulative_gas_used = json[3];
322329
} else {
323330
receipt.success = json.at("success").get<bool>();
324331
receipt.cumulative_gas_used = json.at("cumulative_gas_used").get<uint64_t>();

silkrpc/types/receipt.cpp

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
Copyright 2020 The Silkrpc Authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
#include "receipt.hpp"
18+
19+
#include <iomanip>
20+
21+
#include <silkrpc/common/log.hpp>
22+
#include <silkrpc/common/util.hpp>
23+
#include <silkworm/types/bloom.cpp> // NOLINT(build/include) m3_2048 not exported
24+
25+
namespace silkrpc {
26+
27+
std::ostream& operator<<(std::ostream& out, const Receipt& r) {
28+
out << " block_hash: " << r.block_hash;
29+
out << " block_number: " << r.block_number;
30+
out << " contract_address: " << r.contract_address;
31+
out << " cumulative_gas_used: " << r.cumulative_gas_used;
32+
if (r.from) {
33+
out << " from: " << silkworm::to_hex(*r.from);
34+
} else {
35+
out << " from: null";
36+
}
37+
out << " gas_used: " << r.gas_used;
38+
out << " #logs: " << r.logs.size();
39+
auto bloom_view = silkworm::full_view(r.bloom);
40+
out << " bloom: " << silkworm::to_hex(bloom_view);
41+
out << " success: " << r.success;
42+
if (r.to) {
43+
out << " to: " << silkworm::to_hex(*r.to);
44+
} else {
45+
out << " to: null";
46+
}
47+
out << " tx_hash: " << r.tx_hash;
48+
out << " tx_index: " << r.tx_index;
49+
if (r.type) {
50+
out << " type: 0x" << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(*r.type);
51+
} else {
52+
out << " type: null";
53+
}
54+
55+
return out;
56+
}
57+
58+
silkworm::Bloom bloom_from_logs(const Logs& logs) {
59+
SILKRPC_TRACE << "bloom_from_logs #logs: " << logs.size() << "\n";
60+
silkworm::Bloom bloom{};
61+
for (auto const& log : logs) {
62+
silkworm::m3_2048(bloom, silkworm::full_view(log.address));
63+
for (const auto& topic : log.topics) {
64+
silkworm::m3_2048(bloom, silkworm::full_view(topic));
65+
}
66+
}
67+
SILKRPC_TRACE << "bloom_from_logs bloom: " << silkworm::to_hex(silkworm::full_view(bloom)) << "\n";
68+
return bloom;
69+
}
70+
71+
} // namespace silkrpc

silkrpc/types/receipt.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ struct Receipt {
3434
/* raw fields */
3535
bool success{false};
3636
uint64_t cumulative_gas_used{0};
37-
silkworm::Bloom bloom;
37+
silkworm::Bloom bloom{};
3838
Logs logs;
3939

4040
/* derived fields */
@@ -49,6 +49,10 @@ struct Receipt {
4949
std::optional<uint8_t> type{std::nullopt}; // EIP-2718
5050
};
5151

52+
std::ostream& operator<<(std::ostream& out, const Receipt& r);
53+
54+
silkworm::Bloom bloom_from_logs(const Logs& logs);
55+
5256
typedef std::vector<Receipt> Receipts;
5357

5458
} // namespace silkrpc

silkrpc/types/transaction.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,41 @@
2323
namespace silkrpc {
2424

2525
std::ostream& operator<<(std::ostream& out, const Transaction& t) {
26+
out << " #access_list: " << t.access_list.size();
2627
out << " block_hash: " << t.block_hash;
2728
out << " block_number: " << t.block_number;
29+
if (t.chain_id) {
30+
out << " chain_id: " << silkworm::to_hex(silkworm::rlp::big_endian(*t.chain_id));
31+
} else {
32+
out << " chain_id: null";
33+
}
34+
out << " data: " << silkworm::to_hex(t.data);
35+
if (t.from) {
36+
out << " from: " << silkworm::to_hex(*t.from);
37+
} else {
38+
out << " from: null";
39+
}
40+
out << " nonce: " << t.nonce;
41+
out << " gas_price: " << silkworm::to_hex(silkworm::rlp::big_endian(t.gas_price));
42+
out << " gas_limit: " << t.gas_limit;
43+
out << " odd_y_parity: " << t.odd_y_parity;
44+
45+
out << " r: " << silkworm::to_hex(silkworm::rlp::big_endian(t.r));
46+
out << " s: " << silkworm::to_hex(silkworm::rlp::big_endian(t.s));
47+
48+
if (t.to) {
49+
out << " to: " << silkworm::to_hex(*t.to);
50+
} else {
51+
out << " to: null";
52+
}
2853
out << " transaction_index: " << t.transaction_index;
54+
if (t.type) {
55+
out << " type: 0x" << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(*t.type);
56+
} else {
57+
out << " type: null";
58+
}
59+
out << " value: " << silkworm::to_hex(silkworm::rlp::big_endian(t.value));
60+
2961
return out;
3062
}
3163

0 commit comments

Comments
 (0)