Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blockchain tests support: Fix tx receipt rlp encoding #680

Merged
merged 2 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"
chfast marked this conversation as resolved.
Show resolved Hide resolved
#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.
chfast marked this conversation as resolved.
Show resolved Hide resolved
chfast marked this conversation as resolved.
Show resolved Hide resolved
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(),
chfast marked this conversation as resolved.
Show resolved Hide resolved
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
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add documentation of TransactionReceipt.

/// 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Transaction::Type type = Transaction::Type::legacy;
evmc_status_code status = EVMC_INTERNAL_ERROR;

/// Amount of gas used by this transaction.
chfast marked this conversation as resolved.
Show resolved Hide resolved
int64_t gas_used = 0;

/// Amount of gas used by this and previous transactions in the block.
chfast marked this conversation as resolved.
Show resolved Hide resolved
int64_t cumulative_gas_used = 0;
chfast marked this conversation as resolved.
Show resolved Hide resolved
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;
chfast marked this conversation as resolved.
Show resolved Hide resolved
};

/// 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;
chfast marked this conversation as resolved.
Show resolved Hide resolved
if (rev < EVMC_BYZANTIUM)
chfast marked this conversation as resolved.
Show resolved Hide resolved
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);
}