Skip to content

Commit

Permalink
Blockchain tests support: Fix tx receipt rlp encoding (#680)
Browse files Browse the repository at this point in the history
- Encode `cumulative_gas_used` instead of `gas_used`
- Support tx receipt format for pre Byzantium
  • Loading branch information
rodiazet authored Aug 22, 2023
2 parents 4b2db8b + 70f083f commit 0068ddf
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 7 deletions.
26 changes: 21 additions & 5 deletions test/state/state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// SPDX-License-Identifier: Apache-2.0

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

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

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

[[nodiscard]] bytes rlp_encode(const TransactionReceipt& receipt)
{
const auto prefix = receipt.type == Transaction::Type::eip1559 ? bytes{0x02} : bytes{};
return prefix + rlp::encode_tuple(receipt.status == EVMC_SUCCESS,
static_cast<uint64_t>(receipt.gas_used),
bytes_view(receipt.logs_bloom_filter), receipt.logs);
if (receipt.post_state.has_value())
{
assert(receipt.type == Transaction::Type::legacy);

return rlp::encode_tuple(receipt.post_state.value(),
static_cast<uint64_t>(receipt.cumulative_gas_used),
bytes_view(receipt.logs_bloom_filter), receipt.logs);
}
else
{
const auto prefix = receipt.type == Transaction::Type::legacy ?
bytes{} :
bytes{stdx::to_underlying(receipt.type)};

return prefix + rlp::encode_tuple(receipt.status == EVMC_SUCCESS,
static_cast<uint64_t>(receipt.cumulative_gas_used),
bytes_view(receipt.logs_bloom_filter), receipt.logs);
}
}

} // namespace evmone::state
17 changes: 17 additions & 0 deletions test/state/state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,13 +134,30 @@ struct Log
std::vector<hash256> topics;
};

/// Transaction Receipt
///
/// This struct is used in two contexts:
/// 1. As the formally specified, RLP-encode transaction receipt included in the Ethereum blocks.
/// 2. As the internal representation of the transaction execution result.
/// These both roles share most, but not all the information. There are some fields that cannot be
/// assigned in the single transaction execution context. There are also fields that are not a part
/// of the RLP-encoded transaction receipts.
/// TODO: Consider splitting the struct into two based on the duality explained above.
struct TransactionReceipt
{
Transaction::Type type = Transaction::Type::legacy;
evmc_status_code status = EVMC_INTERNAL_ERROR;

/// Amount of gas used by this transaction.
int64_t gas_used = 0;

/// Amount of gas used by this and previous transactions in the block.
int64_t cumulative_gas_used = 0;
std::vector<Log> logs;
BloomFilter logs_bloom_filter;

/// Root hash of the state after this transaction. Used only in old pre-Byzantium transactions.
std::optional<bytes32> post_state;
};

/// Finalize state after applying a "block" of transactions.
Expand Down
3 changes: 3 additions & 0 deletions test/t8n/t8n.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ int main(int argc, const char* argv[])
j_receipt["transactionHash"] = hex0x(computed_tx_hash);
j_receipt["gasUsed"] = hex0x(static_cast<uint64_t>(receipt.gas_used));
cumulative_gas_used += receipt.gas_used;
receipt.cumulative_gas_used = cumulative_gas_used;
if (rev < EVMC_BYZANTIUM)
receipt.post_state = state::mpt_hash(state.get_accounts());
j_receipt["cumulativeGasUsed"] = hex0x(cumulative_gas_used);

j_receipt["blockHash"] = hex0x(bytes32{});
Expand Down
39 changes: 37 additions & 2 deletions test/unittests/state_mpt_hash_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ TEST(state_mpt_hash, legacy_and_eip1559_receipt_three_logs_no_logs)
TransactionReceipt receipt0{};
receipt0.type = evmone::state::Transaction::Type::legacy;
receipt0.status = EVMC_SUCCESS;
receipt0.gas_used = 0x24522;
receipt0.cumulative_gas_used = 0x24522;

Log l0;
l0.addr = 0x84bf5c35c54a994c72ff9d8b4cca8f5034153a2c_address;
Expand Down Expand Up @@ -238,9 +238,44 @@ TEST(state_mpt_hash, legacy_and_eip1559_receipt_three_logs_no_logs)
TransactionReceipt receipt1{};
receipt1.type = evmone::state::Transaction::Type::eip1559;
receipt1.status = EVMC_SUCCESS;
receipt1.gas_used = 0x2cd9b;
receipt1.cumulative_gas_used = 0x2cd9b;
receipt1.logs_bloom_filter = compute_bloom_filter(receipt1.logs);

EXPECT_EQ(mpt_hash(std::array{receipt0, receipt1}),
0x7199a3a86010634dc205a1cdd6ec609f70b954167583cb3acb6a2e3057916016_bytes32);
}

TEST(state_mpt_hash, pre_byzantium_receipt)
{
// Block taken from Ethereum mainnet
// https://etherscan.io/txs?block=4276370

using namespace evmone::state;

TransactionReceipt receipt0{
.type = Transaction::Type::legacy,
.cumulative_gas_used = 0x8323,
.logs = {},
.logs_bloom_filter = compute_bloom_filter(receipt0.logs),
.post_state = 0x4a8f9db452b100f9ec85830785b2d1744c3e727561c334c4f18022daa113290a_bytes32,
};

TransactionReceipt receipt1{
.type = Transaction::Type::legacy,
.cumulative_gas_used = 0x10646,
.logs = {},
.logs_bloom_filter = compute_bloom_filter(receipt1.logs),
.post_state = 0xb14ab7c32b3e126591731850976a15e2359c1f3628f1b0ff37776c210b9cadb8_bytes32,
};

TransactionReceipt receipt2{
.type = Transaction::Type::legacy,
.cumulative_gas_used = 0x1584e,
.logs = {},
.logs_bloom_filter = compute_bloom_filter(receipt2.logs),
.post_state = 0x7bda915deb201cae321d31028d322877e8fb98264db3ffcbfec7ea7b9b2106b1_bytes32,
};

EXPECT_EQ(mpt_hash(std::array{receipt0, receipt1, receipt2}),
0x8a4fa43a95939b06ad13ce8cd08e026ae6e79ea3c5fc80c732d252e2769ce778_bytes32);
}

0 comments on commit 0068ddf

Please sign in to comment.