Skip to content

Commit 0068ddf

Browse files
authored
Blockchain tests support: Fix tx receipt rlp encoding (#680)
- Encode `cumulative_gas_used` instead of `gas_used` - Support tx receipt format for pre Byzantium
2 parents 4b2db8b + 70f083f commit 0068ddf

File tree

4 files changed

+78
-7
lines changed

4 files changed

+78
-7
lines changed

test/state/state.cpp

+21-5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// SPDX-License-Identifier: Apache-2.0
44

55
#include "state.hpp"
6+
#include "../utils/stdx/utility.hpp"
67
#include "errors.hpp"
78
#include "host.hpp"
89
#include "rlp.hpp"
@@ -212,7 +213,8 @@ std::variant<TransactionReceipt, std::error_code> transition(State& state, const
212213
std::erase_if(state.get_accounts(),
213214
[](const std::pair<const address, Account>& p) noexcept { return p.second.destructed; });
214215

215-
auto receipt = TransactionReceipt{tx.type, result.status_code, gas_used, host.take_logs(), {}};
216+
// Cumulative gas used is unknown in this scope.
217+
TransactionReceipt receipt{tx.type, result.status_code, gas_used, {}, host.take_logs(), {}, {}};
216218

217219
// Cannot put it into constructor call because logs are std::moved from host instance.
218220
receipt.logs_bloom_filter = compute_bloom_filter(receipt.logs);
@@ -273,10 +275,24 @@ std::variant<TransactionReceipt, std::error_code> transition(State& state, const
273275

274276
[[nodiscard]] bytes rlp_encode(const TransactionReceipt& receipt)
275277
{
276-
const auto prefix = receipt.type == Transaction::Type::eip1559 ? bytes{0x02} : bytes{};
277-
return prefix + rlp::encode_tuple(receipt.status == EVMC_SUCCESS,
278-
static_cast<uint64_t>(receipt.gas_used),
279-
bytes_view(receipt.logs_bloom_filter), receipt.logs);
278+
if (receipt.post_state.has_value())
279+
{
280+
assert(receipt.type == Transaction::Type::legacy);
281+
282+
return rlp::encode_tuple(receipt.post_state.value(),
283+
static_cast<uint64_t>(receipt.cumulative_gas_used),
284+
bytes_view(receipt.logs_bloom_filter), receipt.logs);
285+
}
286+
else
287+
{
288+
const auto prefix = receipt.type == Transaction::Type::legacy ?
289+
bytes{} :
290+
bytes{stdx::to_underlying(receipt.type)};
291+
292+
return prefix + rlp::encode_tuple(receipt.status == EVMC_SUCCESS,
293+
static_cast<uint64_t>(receipt.cumulative_gas_used),
294+
bytes_view(receipt.logs_bloom_filter), receipt.logs);
295+
}
280296
}
281297

282298
} // namespace evmone::state

test/state/state.hpp

+17
Original file line numberDiff line numberDiff line change
@@ -134,13 +134,30 @@ struct Log
134134
std::vector<hash256> topics;
135135
};
136136

137+
/// Transaction Receipt
138+
///
139+
/// This struct is used in two contexts:
140+
/// 1. As the formally specified, RLP-encode transaction receipt included in the Ethereum blocks.
141+
/// 2. As the internal representation of the transaction execution result.
142+
/// These both roles share most, but not all the information. There are some fields that cannot be
143+
/// assigned in the single transaction execution context. There are also fields that are not a part
144+
/// of the RLP-encoded transaction receipts.
145+
/// TODO: Consider splitting the struct into two based on the duality explained above.
137146
struct TransactionReceipt
138147
{
139148
Transaction::Type type = Transaction::Type::legacy;
140149
evmc_status_code status = EVMC_INTERNAL_ERROR;
150+
151+
/// Amount of gas used by this transaction.
141152
int64_t gas_used = 0;
153+
154+
/// Amount of gas used by this and previous transactions in the block.
155+
int64_t cumulative_gas_used = 0;
142156
std::vector<Log> logs;
143157
BloomFilter logs_bloom_filter;
158+
159+
/// Root hash of the state after this transaction. Used only in old pre-Byzantium transactions.
160+
std::optional<bytes32> post_state;
144161
};
145162

146163
/// Finalize state after applying a "block" of transactions.

test/t8n/t8n.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ int main(int argc, const char* argv[])
145145
j_receipt["transactionHash"] = hex0x(computed_tx_hash);
146146
j_receipt["gasUsed"] = hex0x(static_cast<uint64_t>(receipt.gas_used));
147147
cumulative_gas_used += receipt.gas_used;
148+
receipt.cumulative_gas_used = cumulative_gas_used;
149+
if (rev < EVMC_BYZANTIUM)
150+
receipt.post_state = state::mpt_hash(state.get_accounts());
148151
j_receipt["cumulativeGasUsed"] = hex0x(cumulative_gas_used);
149152

150153
j_receipt["blockHash"] = hex0x(bytes32{});

test/unittests/state_mpt_hash_test.cpp

+37-2
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ TEST(state_mpt_hash, legacy_and_eip1559_receipt_three_logs_no_logs)
188188
TransactionReceipt receipt0{};
189189
receipt0.type = evmone::state::Transaction::Type::legacy;
190190
receipt0.status = EVMC_SUCCESS;
191-
receipt0.gas_used = 0x24522;
191+
receipt0.cumulative_gas_used = 0x24522;
192192

193193
Log l0;
194194
l0.addr = 0x84bf5c35c54a994c72ff9d8b4cca8f5034153a2c_address;
@@ -238,9 +238,44 @@ TEST(state_mpt_hash, legacy_and_eip1559_receipt_three_logs_no_logs)
238238
TransactionReceipt receipt1{};
239239
receipt1.type = evmone::state::Transaction::Type::eip1559;
240240
receipt1.status = EVMC_SUCCESS;
241-
receipt1.gas_used = 0x2cd9b;
241+
receipt1.cumulative_gas_used = 0x2cd9b;
242242
receipt1.logs_bloom_filter = compute_bloom_filter(receipt1.logs);
243243

244244
EXPECT_EQ(mpt_hash(std::array{receipt0, receipt1}),
245245
0x7199a3a86010634dc205a1cdd6ec609f70b954167583cb3acb6a2e3057916016_bytes32);
246246
}
247+
248+
TEST(state_mpt_hash, pre_byzantium_receipt)
249+
{
250+
// Block taken from Ethereum mainnet
251+
// https://etherscan.io/txs?block=4276370
252+
253+
using namespace evmone::state;
254+
255+
TransactionReceipt receipt0{
256+
.type = Transaction::Type::legacy,
257+
.cumulative_gas_used = 0x8323,
258+
.logs = {},
259+
.logs_bloom_filter = compute_bloom_filter(receipt0.logs),
260+
.post_state = 0x4a8f9db452b100f9ec85830785b2d1744c3e727561c334c4f18022daa113290a_bytes32,
261+
};
262+
263+
TransactionReceipt receipt1{
264+
.type = Transaction::Type::legacy,
265+
.cumulative_gas_used = 0x10646,
266+
.logs = {},
267+
.logs_bloom_filter = compute_bloom_filter(receipt1.logs),
268+
.post_state = 0xb14ab7c32b3e126591731850976a15e2359c1f3628f1b0ff37776c210b9cadb8_bytes32,
269+
};
270+
271+
TransactionReceipt receipt2{
272+
.type = Transaction::Type::legacy,
273+
.cumulative_gas_used = 0x1584e,
274+
.logs = {},
275+
.logs_bloom_filter = compute_bloom_filter(receipt2.logs),
276+
.post_state = 0x7bda915deb201cae321d31028d322877e8fb98264db3ffcbfec7ea7b9b2106b1_bytes32,
277+
};
278+
279+
EXPECT_EQ(mpt_hash(std::array{receipt0, receipt1, receipt2}),
280+
0x8a4fa43a95939b06ad13ce8cd08e026ae6e79ea3c5fc80c732d252e2769ce778_bytes32);
281+
}

0 commit comments

Comments
 (0)