diff --git a/cmd/test/consensus.cpp b/cmd/test/consensus.cpp index f5f5b80e04..675aa4a2bf 100644 --- a/cmd/test/consensus.cpp +++ b/cmd/test/consensus.cpp @@ -599,7 +599,8 @@ RunResults transaction_test(const nlohmann::json& j) { txn.from.reset(); if (ValidationResult err{ - pre_validate_transaction(txn, rev, config.chain_id, /*base_fee_per_gas=*/std::nullopt)}; + pre_validate_transaction(txn, rev, config.chain_id, /*base_fee_per_gas=*/std::nullopt, + /*data_gas_price=*/std::nullopt)}; err != ValidationResult::kOk) { if (should_be_valid) { std::cout << "Validation error " << magic_enum::enum_name(err) << std::endl; @@ -609,18 +610,6 @@ RunResults transaction_test(const nlohmann::json& j) { } } - const intx::uint512 max_gas_cost{intx::umul(intx::uint256{txn.gas_limit}, txn.max_fee_per_gas)}; - // A corollary check of the following assertion from EIP-1559: - // signer.balance >= transaction.gas_limit * transaction.max_fee_per_gas - if (intx::count_significant_bytes(max_gas_cost) > 32) { - if (should_be_valid) { - std::cout << "gas_limit * max_fee_per_gas overflow\n"; - return Status::kFailed; - } else { - continue; - } - } - txn.recover_sender(); if (should_be_valid && !txn.from.has_value()) { std::cout << "Failed to recover sender" << std::endl; diff --git a/silkworm/core/chain/protocol_param.hpp b/silkworm/core/chain/protocol_param.hpp index 3b51a0109f..4b9d9367d7 100644 --- a/silkworm/core/chain/protocol_param.hpp +++ b/silkworm/core/chain/protocol_param.hpp @@ -61,6 +61,14 @@ namespace param { inline constexpr uint64_t kBaseFeeMaxChangeDenominator{8}; inline constexpr uint64_t kElasticityMultiplier{2}; + // EIP-4844: Shard Blob Transactions + inline constexpr uint8_t kBlobCommitmentVersionKzg{1}; + inline constexpr uint64_t kMaxDataGasPerBlock{1u << 19}; + inline constexpr uint64_t kTargetDataGasPerBlock{1u << 18}; + inline constexpr uint64_t kDataGasPerBlob{1u << 17}; + inline constexpr uint64_t kMinDataGasPrice{1}; + inline constexpr uint64_t kDataGasPriceUpdateFraction{2225652}; + } // namespace param } // namespace silkworm diff --git a/silkworm/core/consensus/base/engine.cpp b/silkworm/core/consensus/base/engine.cpp index 3e1b08e805..7b2b7587fc 100644 --- a/silkworm/core/consensus/base/engine.cpp +++ b/silkworm/core/consensus/base/engine.cpp @@ -26,6 +26,7 @@ namespace silkworm::consensus { ValidationResult EngineBase::pre_validate_block_body(const Block& block, const BlockState& state) { const BlockHeader& header{block.header}; + const evmc_revision rev{chain_config_.revision(header.number, header.timestamp)}; const evmc::bytes32 txn_root{compute_transaction_root(block)}; if (txn_root != header.transactions_root) { @@ -36,7 +37,6 @@ ValidationResult EngineBase::pre_validate_block_body(const Block& block, const B return err; } - const evmc_revision rev{chain_config_.revision(header.number, header.timestamp)}; if (rev < EVMC_SHANGHAI && block.withdrawals) { return ValidationResult::kUnexpectedWithdrawals; } @@ -53,6 +53,23 @@ ValidationResult EngineBase::pre_validate_block_body(const Block& block, const B } } + size_t total_blobs{0}; + for (const Transaction& tx : block.transactions) { + total_blobs += tx.blob_versioned_hashes.size(); + } + if (total_blobs > param::kMaxDataGasPerBlock / param::kDataGasPerBlob) { + return ValidationResult::kTooManyBlobs; + } + + const std::optional parent{get_parent_header(state, header)}; + if (!parent) { + return ValidationResult::kUnknownParent; + } + + if (header.excess_data_gas != calc_excess_data_gas(*parent, total_blobs, rev)) { + return ValidationResult::kWrongExcessDataGas; + } + if (block.ommers.empty()) { return header.ommers_hash == kEmptyListHash ? ValidationResult::kOk : ValidationResult::kWrongOmmersHash; } else if (prohibit_ommers_) { @@ -71,9 +88,11 @@ ValidationResult EngineBase::pre_validate_transactions(const Block& block) { const BlockHeader& header{block.header}; const evmc_revision rev{chain_config_.revision(header.number, header.timestamp)}; + const std::optional data_gas_price{header.data_gas_price()}; for (const Transaction& txn : block.transactions) { - if (ValidationResult err{pre_validate_transaction(txn, rev, chain_config_.chain_id, header.base_fee_per_gas)}; - err != ValidationResult::kOk) { + ValidationResult err{pre_validate_transaction(txn, rev, chain_config_.chain_id, + header.base_fee_per_gas, data_gas_price)}; + if (err != ValidationResult::kOk) { return err; } } @@ -176,11 +195,12 @@ ValidationResult EngineBase::validate_block_header(const BlockHeader& header, co } } - if (header.base_fee_per_gas != expected_base_fee_per_gas(header, parent.value())) { + const evmc_revision rev{chain_config_.revision(header.number, header.timestamp)}; + + if (header.base_fee_per_gas != expected_base_fee_per_gas(*parent, rev)) { return ValidationResult::kWrongBaseFee; } - const evmc_revision rev{chain_config_.revision(header.number, header.timestamp)}; if (rev < EVMC_SHANGHAI && header.withdrawals_root) { return ValidationResult::kUnexpectedWithdrawals; } @@ -227,20 +247,17 @@ bool EngineBase::is_kin(const BlockHeader& branch_header, const BlockHeader& mai evmc::address EngineBase::get_beneficiary(const BlockHeader& header) { return header.beneficiary; } -std::optional EngineBase::expected_base_fee_per_gas(const BlockHeader& header, - const BlockHeader& parent) { - if (chain_config_.revision(header.number, header.timestamp) < EVMC_LONDON) { +std::optional expected_base_fee_per_gas(const BlockHeader& parent, const evmc_revision rev) { + if (rev < EVMC_LONDON) { return std::nullopt; } - if (header.number == chain_config_.london_block) { + if (!parent.base_fee_per_gas) { return param::kInitialBaseFee; } const uint64_t parent_gas_target{parent.gas_limit / param::kElasticityMultiplier}; - - assert(parent.base_fee_per_gas.has_value()); - const intx::uint256 parent_base_fee_per_gas{parent.base_fee_per_gas.value()}; + const intx::uint256& parent_base_fee_per_gas{*parent.base_fee_per_gas}; if (parent.gas_used == parent_gas_target) { return parent_base_fee_per_gas; @@ -266,14 +283,31 @@ std::optional EngineBase::expected_base_fee_per_gas(const BlockHe } } -evmc::bytes32 EngineBase::compute_transaction_root(const BlockBody& body) { +std::optional calc_excess_data_gas(const BlockHeader& parent, + std::size_t num_blobs, + const evmc_revision rev) { + if (rev < EVMC_CANCUN) { + return std::nullopt; + } + + const uint64_t consumed_data_gas{num_blobs * param::kDataGasPerBlob}; + const intx::uint256 parent_excess_data_gas{parent.excess_data_gas.value_or(0)}; + + if (parent_excess_data_gas + consumed_data_gas < param::kTargetDataGasPerBlock) { + return 0; + } else { + return parent_excess_data_gas + consumed_data_gas - param::kTargetDataGasPerBlock; + } +} + +evmc::bytes32 compute_transaction_root(const BlockBody& body) { static constexpr auto kEncoder = [](Bytes& to, const Transaction& txn) { rlp::encode(to, txn, /*for_signing=*/false, /*wrap_eip2718_into_string=*/false); }; return trie::root_hash(body.transactions, kEncoder); } -evmc::bytes32 EngineBase::compute_ommers_hash(const BlockBody& body) { +evmc::bytes32 compute_ommers_hash(const BlockBody& body) { if (body.ommers.empty()) { return kEmptyListHash; } diff --git a/silkworm/core/consensus/base/engine.hpp b/silkworm/core/consensus/base/engine.hpp index 0cd302e0e5..8d1cf5356a 100644 --- a/silkworm/core/consensus/base/engine.hpp +++ b/silkworm/core/consensus/base/engine.hpp @@ -58,18 +58,9 @@ class EngineBase : public IEngine { //! \brief Validates the difficulty of the header virtual ValidationResult validate_difficulty(const BlockHeader& header, const BlockHeader& parent) = 0; - //! \brief See https://eips.ethereum.org/EIPS/eip-1559 - std::optional expected_base_fee_per_gas(const BlockHeader& header, const BlockHeader& parent); - //! \brief Returns parent header (if any) of provided header static std::optional get_parent_header(const BlockState& state, const BlockHeader& header); - //! \brief Calculate the transaction root of a block body - static evmc::bytes32 compute_transaction_root(const BlockBody& body); - - //! \brief Calculate the hash of ommers of a block body - static evmc::bytes32 compute_ommers_hash(const BlockBody& body); - protected: const ChainConfig& chain_config_; bool prohibit_ommers_{false}; @@ -80,4 +71,17 @@ class EngineBase : public IEngine { std::vector& old_ommers); }; +//! \see EIP-1559: Fee market change for ETH 1.0 chain +std::optional expected_base_fee_per_gas(const BlockHeader& parent, const evmc_revision); + +//! \see EIP-4844: Shard Blob Transactions +std::optional calc_excess_data_gas(const BlockHeader& parent, std::size_t num_blobs, + const evmc_revision); + +//! \brief Calculate the transaction root of a block body +evmc::bytes32 compute_transaction_root(const BlockBody& body); + +//! \brief Calculate the hash of ommers of a block body +evmc::bytes32 compute_ommers_hash(const BlockBody& body); + } // namespace silkworm::consensus diff --git a/silkworm/core/consensus/engine.cpp b/silkworm/core/consensus/engine.cpp index fcdd344b71..c6608b985c 100644 --- a/silkworm/core/consensus/engine.cpp +++ b/silkworm/core/consensus/engine.cpp @@ -28,23 +28,27 @@ namespace silkworm::consensus { +bool transaction_type_is_supported(Transaction::Type type, evmc_revision rev) { + static constexpr evmc_revision kMinRevisionByType[]{ + EVMC_FRONTIER, // kLegacy + EVMC_BERLIN, // kEip2930 + EVMC_LONDON, // kEip1559 + EVMC_CANCUN, // kEip4844 + }; + const auto n{static_cast(type)}; + return n < std::size(kMinRevisionByType) && rev >= kMinRevisionByType[n]; +} + ValidationResult pre_validate_transaction(const Transaction& txn, const evmc_revision rev, const uint64_t chain_id, - const std::optional& base_fee_per_gas) { + const std::optional& base_fee_per_gas, + const std::optional& data_gas_price) { if (txn.chain_id.has_value()) { if (rev < EVMC_SPURIOUS_DRAGON || txn.chain_id.value() != chain_id) { return ValidationResult::kWrongChainId; } } - if (txn.type == Transaction::Type::kEip2930) { - if (rev < EVMC_BERLIN) { - return ValidationResult::kUnsupportedTransactionType; - } - } else if (txn.type == Transaction::Type::kEip1559) { - if (rev < EVMC_LONDON) { - return ValidationResult::kUnsupportedTransactionType; - } - } else if (txn.type != Transaction::Type::kLegacy) { + if (!transaction_type_is_supported(txn.type, rev)) { return ValidationResult::kUnsupportedTransactionType; } @@ -69,6 +73,10 @@ ValidationResult pre_validate_transaction(const Transaction& txn, const evmc_rev return ValidationResult::kIntrinsicGas; } + if (intx::count_significant_bytes(txn.maximum_gas_cost()) > 32) { + return ValidationResult::kInsufficientFunds; + } + // EIP-2681: Limit account nonce to 2^64-1 if (txn.nonce >= UINT64_MAX) { return ValidationResult::kNonceTooHigh; @@ -80,6 +88,26 @@ ValidationResult pre_validate_transaction(const Transaction& txn, const evmc_rev return ValidationResult::kMaxInitCodeSizeExceeded; } + // EIP-4844: Shard Blob Transactions + if (txn.type == Transaction::Type::kEip4844) { + if (txn.blob_versioned_hashes.empty()) { + return ValidationResult::kNoBlobs; + } + for (const Hash& h : txn.blob_versioned_hashes) { + if (h.bytes[0] != param::kBlobCommitmentVersionKzg) { + return ValidationResult::kWrongBlobCommitmentVersion; + } + } + SILKWORM_ASSERT(txn.max_fee_per_data_gas); + SILKWORM_ASSERT(data_gas_price); + if (txn.max_fee_per_data_gas < data_gas_price) { + return ValidationResult::kMaxFeePerDataGasTooLow; + } + // TODO(yperbasis): There is an equal amount of versioned hashes, kzg commitments and blobs. + // The KZG commitments hash to the versioned hashes, i.e. kzg_to_versioned_hash(kzg[i]) == versioned_hash[i] + // The KZG commitments match the blob contents. + } + return ValidationResult::kOk; } diff --git a/silkworm/core/consensus/engine.hpp b/silkworm/core/consensus/engine.hpp index 7837a42530..49eae21ac9 100644 --- a/silkworm/core/consensus/engine.hpp +++ b/silkworm/core/consensus/engine.hpp @@ -71,12 +71,14 @@ class IEngine { virtual evmc::address get_beneficiary(const BlockHeader& header) = 0; }; +bool transaction_type_is_supported(Transaction::Type, evmc_revision); + //! \brief Performs validation of a transaction that can be done prior to sender recovery and block execution. -//! \return Any of kIntrinsicGas, kInvalidSignature, kWrongChainId, kUnsupportedTransactionType, or kOk. //! \remarks Should sender of transaction not yet recovered a check on signature's validity is performed //! \remarks These function is agnostic to whole block validity ValidationResult pre_validate_transaction(const Transaction& txn, evmc_revision revision, uint64_t chain_id, - const std::optional& base_fee_per_gas); + const std::optional& base_fee_per_gas, + const std::optional& data_gas_price); //! \brief Creates an instance of proper Consensus Engine on behalf of chain configuration std::unique_ptr engine_factory(const ChainConfig& chain_config); diff --git a/silkworm/core/consensus/engine_test.cpp b/silkworm/core/consensus/engine_test.cpp index f0f1febf72..e06b2dcf19 100644 --- a/silkworm/core/consensus/engine_test.cpp +++ b/silkworm/core/consensus/engine_test.cpp @@ -48,65 +48,67 @@ TEST_CASE("Consensus Engine Seal") { TEST_CASE("Validate transaction types") { const std::optional base_fee_per_gas{std::nullopt}; + const std::optional data_gas_price{std::nullopt}; Transaction txn; txn.type = Transaction::Type::kLegacy; - CHECK(pre_validate_transaction(txn, EVMC_ISTANBUL, 1, base_fee_per_gas) != + CHECK(pre_validate_transaction(txn, EVMC_ISTANBUL, 1, base_fee_per_gas, data_gas_price) != ValidationResult::kUnsupportedTransactionType); - CHECK(pre_validate_transaction(txn, EVMC_BERLIN, 1, base_fee_per_gas) != + CHECK(pre_validate_transaction(txn, EVMC_BERLIN, 1, base_fee_per_gas, data_gas_price) != ValidationResult::kUnsupportedTransactionType); - CHECK(pre_validate_transaction(txn, EVMC_LONDON, 1, base_fee_per_gas) != + CHECK(pre_validate_transaction(txn, EVMC_LONDON, 1, base_fee_per_gas, data_gas_price) != ValidationResult::kUnsupportedTransactionType); txn.type = static_cast(0x03); // unsupported transaction type - CHECK(pre_validate_transaction(txn, EVMC_ISTANBUL, 1, base_fee_per_gas) == + CHECK(pre_validate_transaction(txn, EVMC_ISTANBUL, 1, base_fee_per_gas, data_gas_price) == ValidationResult::kUnsupportedTransactionType); - CHECK(pre_validate_transaction(txn, EVMC_BERLIN, 1, base_fee_per_gas) == + CHECK(pre_validate_transaction(txn, EVMC_BERLIN, 1, base_fee_per_gas, data_gas_price) == ValidationResult::kUnsupportedTransactionType); - CHECK(pre_validate_transaction(txn, EVMC_LONDON, 1, base_fee_per_gas) == + CHECK(pre_validate_transaction(txn, EVMC_LONDON, 1, base_fee_per_gas, data_gas_price) == ValidationResult::kUnsupportedTransactionType); txn.type = Transaction::Type::kEip2930; - CHECK(pre_validate_transaction(txn, EVMC_ISTANBUL, 1, base_fee_per_gas) == + CHECK(pre_validate_transaction(txn, EVMC_ISTANBUL, 1, base_fee_per_gas, data_gas_price) == ValidationResult::kUnsupportedTransactionType); - CHECK(pre_validate_transaction(txn, EVMC_BERLIN, 1, base_fee_per_gas) != + CHECK(pre_validate_transaction(txn, EVMC_BERLIN, 1, base_fee_per_gas, data_gas_price) != ValidationResult::kUnsupportedTransactionType); - CHECK(pre_validate_transaction(txn, EVMC_LONDON, 1, base_fee_per_gas) != + CHECK(pre_validate_transaction(txn, EVMC_LONDON, 1, base_fee_per_gas, data_gas_price) != ValidationResult::kUnsupportedTransactionType); txn.type = Transaction::Type::kEip1559; - CHECK(pre_validate_transaction(txn, EVMC_ISTANBUL, 1, base_fee_per_gas) == + CHECK(pre_validate_transaction(txn, EVMC_ISTANBUL, 1, base_fee_per_gas, data_gas_price) == ValidationResult::kUnsupportedTransactionType); - CHECK(pre_validate_transaction(txn, EVMC_BERLIN, 1, base_fee_per_gas) == + CHECK(pre_validate_transaction(txn, EVMC_BERLIN, 1, base_fee_per_gas, data_gas_price) == ValidationResult::kUnsupportedTransactionType); - CHECK(pre_validate_transaction(txn, EVMC_LONDON, 1, base_fee_per_gas) != + CHECK(pre_validate_transaction(txn, EVMC_LONDON, 1, base_fee_per_gas, data_gas_price) != ValidationResult::kUnsupportedTransactionType); } TEST_CASE("Validate max_fee_per_gas") { const std::optional base_fee_per_gas{1'000'000'000}; + const std::optional data_gas_price{std::nullopt}; Transaction txn; txn.type = Transaction::Type::kEip1559; txn.max_priority_fee_per_gas = 500'000'000; txn.max_fee_per_gas = 700'000'000; - CHECK(pre_validate_transaction(txn, EVMC_LONDON, 1, base_fee_per_gas) == + CHECK(pre_validate_transaction(txn, EVMC_LONDON, 1, base_fee_per_gas, data_gas_price) == ValidationResult::kMaxFeeLessThanBase); txn.max_priority_fee_per_gas = 3'000'000'000; txn.max_fee_per_gas = 2'000'000'000; - CHECK(pre_validate_transaction(txn, EVMC_LONDON, 1, base_fee_per_gas) == + CHECK(pre_validate_transaction(txn, EVMC_LONDON, 1, base_fee_per_gas, data_gas_price) == ValidationResult::kMaxPriorityFeeGreaterThanMax); txn.max_priority_fee_per_gas = 2'000'000'000; txn.max_fee_per_gas = 2'000'000'000; - CHECK(pre_validate_transaction(txn, EVMC_LONDON, 1, base_fee_per_gas) != + CHECK(pre_validate_transaction(txn, EVMC_LONDON, 1, base_fee_per_gas, data_gas_price) != ValidationResult::kMaxPriorityFeeGreaterThanMax); txn.max_priority_fee_per_gas = 1'000'000'000; txn.max_fee_per_gas = 2'000'000'000; - CHECK(pre_validate_transaction(txn, EVMC_LONDON, 1, base_fee_per_gas) != + CHECK(pre_validate_transaction(txn, EVMC_LONDON, 1, base_fee_per_gas, data_gas_price) != ValidationResult::kMaxPriorityFeeGreaterThanMax); } diff --git a/silkworm/core/consensus/pos/engine_test.cpp b/silkworm/core/consensus/pos/engine_test.cpp index fb214247e2..74134e3fe4 100644 --- a/silkworm/core/consensus/pos/engine_test.cpp +++ b/silkworm/core/consensus/pos/engine_test.cpp @@ -47,7 +47,7 @@ TEST_CASE("Proof-of-Stake consensus engine") { EthashEngine ethash_engine{kMainnetConfig}; ProofOfStakeEngine pos_engine{kMainnetConfig}; - header.base_fee_per_gas = pos_engine.expected_base_fee_per_gas(header, parent.header); + header.base_fee_per_gas = expected_base_fee_per_gas(parent.header, EVMC_LONDON); InMemoryState state; state.insert_block(parent, header.parent_hash); diff --git a/silkworm/core/consensus/validation.hpp b/silkworm/core/consensus/validation.hpp index b373b5e4dc..005b81a23a 100644 --- a/silkworm/core/consensus/validation.hpp +++ b/silkworm/core/consensus/validation.hpp @@ -80,6 +80,13 @@ enum class [[nodiscard]] ValidationResult{ kMissingWithdrawals, kUnexpectedWithdrawals, kWrongWithdrawalsRoot, + + // EIP-4844: Shard Blob Transactions + kWrongExcessDataGas, + kNoBlobs, + kTooManyBlobs, + kWrongBlobCommitmentVersion, + kMaxFeePerDataGasTooLow, // max_fee_per_data_gas < data_gas_price }; } // namespace silkworm diff --git a/silkworm/core/crypto/kzg.cpp b/silkworm/core/crypto/kzg.cpp index ec7082c496..ced590724d 100644 --- a/silkworm/core/crypto/kzg.cpp +++ b/silkworm/core/crypto/kzg.cpp @@ -18,6 +18,7 @@ #include +#include #include // Based on https://github.com/ethereum/c-kzg-4844/blob/main/src/c_kzg_4844.c @@ -61,7 +62,7 @@ static const G2 kKzgSetupG2_1{ Hash kzg_to_versioned_hash(ByteView kzg) { Hash hash; silkworm_sha256(hash.bytes, kzg.data(), kzg.length(), /*use_cpu_extensions=*/true); - hash.bytes[0] = 0x1; + hash.bytes[0] = param::kBlobCommitmentVersionKzg; return hash; } diff --git a/silkworm/core/execution/processor.cpp b/silkworm/core/execution/processor.cpp index 373b2310ea..3c3167390e 100644 --- a/silkworm/core/execution/processor.cpp +++ b/silkworm/core/execution/processor.cpp @@ -45,10 +45,8 @@ ValidationResult ExecutionProcessor::validate_transaction(const Transaction& txn return ValidationResult::kWrongNonce; } - // https://github.com/ethereum/EIPs/pull/3594 - const intx::uint512 max_gas_cost{intx::umul(intx::uint256{txn.gas_limit}, txn.max_fee_per_gas)}; - // See YP, Eq (57) in Section 6.2 "Execution" - const intx::uint512 v0{max_gas_cost + txn.value}; + // See YP, Eq (61) in Section 6.2 "Execution" + const intx::uint512 v0{txn.maximum_gas_cost() + txn.value}; if (state_.get_balance(*txn.from) < v0) { return ValidationResult::kInsufficientFunds; } @@ -71,10 +69,10 @@ void ExecutionProcessor::execute_transaction(const Transaction& txn, Receipt& re state_.clear_journal_and_substate(); - assert(txn.from.has_value()); + assert(txn.from); state_.access_account(*txn.from); - if (txn.to.has_value()) { + if (txn.to) { state_.access_account(*txn.to); // EVM itself increments the nonce for contract creation state_.set_nonce(*txn.from, txn.nonce + 1); @@ -93,10 +91,15 @@ void ExecutionProcessor::execute_transaction(const Transaction& txn, Receipt& re state_.access_account(evm_.beneficiary); } + // EIP-1559 normal gas cost const intx::uint256 base_fee_per_gas{evm_.block().header.base_fee_per_gas.value_or(0)}; const intx::uint256 effective_gas_price{txn.effective_gas_price(base_fee_per_gas)}; state_.subtract_from_balance(*txn.from, txn.gas_limit * effective_gas_price); + // EIP-4844 data gas cost (calc_data_fee) + const intx::uint256 data_gas_price{evm_.block().header.data_gas_price().value_or(0)}; + state_.subtract_from_balance(*txn.from, txn.total_data_gas() * data_gas_price); + const intx::uint128 g0{intrinsic_gas(txn, rev)}; assert(g0 <= UINT64_MAX); // true due to the precondition (transaction must be valid) diff --git a/silkworm/core/types/block.cpp b/silkworm/core/types/block.cpp index dea16e0147..d7293bd31c 100644 --- a/silkworm/core/types/block.cpp +++ b/silkworm/core/types/block.cpp @@ -16,6 +16,7 @@ #include "block.hpp" +#include #include #include @@ -35,6 +36,31 @@ ethash::hash256 BlockHeader::boundary() const { return intx::be::store(result); } +// Approximates factor*e^(numerator/denominator) using Taylor expansion. +// See https://eips.ethereum.org/EIPS/eip-4844#helpers +static intx::uint256 fake_exponential(const intx::uint256& factor, + const intx::uint256& numerator, + const intx::uint256& denominator) { + intx::uint256 output{0}; + intx::uint256 numerator_accum{factor * denominator}; + for (unsigned i{1}; numerator_accum > 0; ++i) { + output += numerator_accum; + numerator_accum = (numerator_accum * numerator) / (denominator * i); + } + return output / denominator; +} + +std::optional BlockHeader::data_gas_price() const { + if (!excess_data_gas) { + return std::nullopt; + } + + return fake_exponential( + param::kMinDataGasPrice, + *excess_data_gas, + param::kDataGasPriceUpdateFraction); +} + //! \brief Recover transaction senders for each block. void Block::recover_senders() { for (Transaction& txn : transactions) { @@ -74,6 +100,10 @@ namespace rlp { if (header.withdrawals_root) { rlp_head.payload_length += kHashLength + 1; } + if (header.excess_data_gas) { + rlp_head.payload_length += length(*header.excess_data_gas); + } + return rlp_head; } @@ -112,6 +142,9 @@ namespace rlp { if (header.withdrawals_root) { encode(to, *header.withdrawals_root); } + if (header.excess_data_gas) { + encode(to, *header.excess_data_gas); + } } template <> @@ -189,6 +222,15 @@ namespace rlp { to.withdrawals_root = withdrawals_root; } + to.excess_data_gas = std::nullopt; + if (from.length() > leftover) { + intx::uint256 excess_data_gas; + if (DecodingResult res{decode(from, excess_data_gas)}; !res) { + return res; + } + to.excess_data_gas = excess_data_gas; + } + if (from.length() != leftover) { return tl::unexpected{DecodingError::kListLengthMismatch}; } diff --git a/silkworm/core/types/block.hpp b/silkworm/core/types/block.hpp index fcd58e82ea..68b214033d 100644 --- a/silkworm/core/types/block.hpp +++ b/silkworm/core/types/block.hpp @@ -70,12 +70,16 @@ struct BlockHeader { std::optional base_fee_per_gas{std::nullopt}; // EIP-1559 std::optional withdrawals_root{std::nullopt}; // EIP-4895 + std::optional excess_data_gas{std::nullopt}; // EIP-4844 [[nodiscard]] evmc::bytes32 hash(bool for_sealing = false, bool exclude_extra_data_sig = false) const; - //! \brief Calculates header's boundary. This is described by Equation(50) by the yellow paper. + //! \brief Calculates header's boundary. This is described by Equation(50) by the Yellow Paper. //! \return A hash of 256 bits with big endian byte order - [[nodiscard, maybe_unused]] ethash::hash256 boundary() const; + [[nodiscard]] ethash::hash256 boundary() const; + + //! \see https://eips.ethereum.org/EIPS/eip-4844#gas-accounting + [[nodiscard]] std::optional data_gas_price() const; friend bool operator==(const BlockHeader&, const BlockHeader&) = default; diff --git a/silkworm/core/types/transaction.cpp b/silkworm/core/types/transaction.cpp index 16a95694c5..5461c8ad52 100644 --- a/silkworm/core/types/transaction.cpp +++ b/silkworm/core/types/transaction.cpp @@ -16,10 +16,9 @@ #include "transaction.hpp" -#include - #include +#include #include #include #include @@ -33,7 +32,8 @@ bool operator==(const Transaction& a, const Transaction& b) { return a.type == b.type && a.nonce == b.nonce && a.max_priority_fee_per_gas == b.max_priority_fee_per_gas && a.max_fee_per_gas == b.max_fee_per_gas && a.gas_limit == b.gas_limit && a.to == b.to && a.value == b.value && a.data == b.data && a.odd_y_parity == b.odd_y_parity && a.chain_id == b.chain_id && a.r == b.r && - a.s == b.s && a.access_list == b.access_list; + a.s == b.s && a.access_list == b.access_list && a.max_fee_per_data_gas == b.max_fee_per_data_gas && + a.blob_versioned_hashes == b.blob_versioned_hashes; } // https://eips.ethereum.org/EIPS/eip-155 @@ -111,7 +111,7 @@ namespace rlp { h.payload_length += length(txn.data); if (txn.type != Transaction::Type::kLegacy) { - assert(txn.type == Transaction::Type::kEip2930 || txn.type == Transaction::Type::kEip1559); + SILKWORM_ASSERT(txn.type == Transaction::Type::kEip2930 || txn.type == Transaction::Type::kEip1559); h.payload_length += length(txn.access_list); } @@ -167,7 +167,7 @@ namespace rlp { } static void eip2718_encode(Bytes& to, const Transaction& txn, bool for_signing, bool wrap_into_array) { - assert(txn.type == Transaction::Type::kEip2930 || txn.type == Transaction::Type::kEip1559); + SILKWORM_ASSERT(txn.type == Transaction::Type::kEip2930 || txn.type == Transaction::Type::kEip1559); Header rlp_head{rlp_header(txn, for_signing)}; @@ -440,8 +440,18 @@ void Transaction::recover_sender() { } } +intx::uint512 Transaction::maximum_gas_cost() const { + // See https://github.com/ethereum/EIPs/pull/3594 + intx::uint512 max_gas_cost{intx::umul(intx::uint256{gas_limit}, max_fee_per_gas)}; + // and https://eips.ethereum.org/EIPS/eip-4844#gas-accounting + if (max_fee_per_data_gas) { + max_gas_cost += intx::umul(intx::uint256{total_data_gas()}, *max_fee_per_data_gas); + } + return max_gas_cost; +} + intx::uint256 Transaction::priority_fee_per_gas(const intx::uint256& base_fee_per_gas) const { - assert(max_fee_per_gas >= base_fee_per_gas); + SILKWORM_ASSERT(max_fee_per_gas >= base_fee_per_gas); return std::min(max_priority_fee_per_gas, max_fee_per_gas - base_fee_per_gas); } @@ -449,4 +459,8 @@ intx::uint256 Transaction::effective_gas_price(const intx::uint256& base_fee_per return priority_fee_per_gas(base_fee_per_gas) + base_fee_per_gas; } +uint64_t Transaction::total_data_gas() const { + return param::kDataGasPerBlob * blob_versioned_hashes.size(); +} + } // namespace silkworm diff --git a/silkworm/core/types/transaction.hpp b/silkworm/core/types/transaction.hpp index 48e9a3299e..f476db1943 100644 --- a/silkworm/core/types/transaction.hpp +++ b/silkworm/core/types/transaction.hpp @@ -23,6 +23,7 @@ #include #include +#include namespace silkworm { @@ -41,12 +42,13 @@ struct Transaction { kLegacy = 0, kEip2930 = 1, kEip1559 = 2, + kEip4844 = 3, }; Type type{Type::kLegacy}; uint64_t nonce{0}; - intx::uint256 max_priority_fee_per_gas{0}; + intx::uint256 max_priority_fee_per_gas{0}; // EIP-1559 intx::uint256 max_fee_per_gas{0}; uint64_t gas_limit{0}; std::optional to{std::nullopt}; @@ -59,7 +61,12 @@ struct Transaction { std::vector access_list{}; // EIP-2930 - std::optional from{std::nullopt}; // sender recovered from the signature + // EIP-4844: Shard Blob Transactions + std::optional max_fee_per_data_gas{std::nullopt}; + std::vector blob_versioned_hashes{}; + + // sender recovered from the signature + std::optional from{std::nullopt}; [[nodiscard]] intx::uint256 v() const; // EIP-155 @@ -67,14 +74,19 @@ struct Transaction { [[nodiscard]] bool set_v(const intx::uint256& v); //! \brief Populates the from field with recovered sender. - //! See Yellow Paper, Appendix F "Signing Transactions", + //! \see Yellow Paper, Appendix F "Signing Transactions", //! https://eips.ethereum.org/EIPS/eip-2 and //! https://eips.ethereum.org/EIPS/eip-155. //! If recovery fails the from field is set to null. void recover_sender(); + //! \brief Maximum possible cost of normal and data (EIP-4844) gas + [[nodiscard]] intx::uint512 maximum_gas_cost() const; + [[nodiscard]] intx::uint256 priority_fee_per_gas(const intx::uint256& base_fee_per_gas) const; // EIP-1559 [[nodiscard]] intx::uint256 effective_gas_price(const intx::uint256& base_fee_per_gas) const; // EIP-1559 + + [[nodiscard]] uint64_t total_data_gas() const; // EIP-4844 }; bool operator==(const Transaction& a, const Transaction& b); diff --git a/silkworm/node/stagedsync/remote_client.cpp b/silkworm/node/stagedsync/remote_client.cpp index 7feb4cf85f..1ae152a0a7 100644 --- a/silkworm/node/stagedsync/remote_client.cpp +++ b/silkworm/node/stagedsync/remote_client.cpp @@ -52,6 +52,9 @@ static void serialize_header(const BlockHeader& bh, ::execution::Header* header) if (bh.withdrawals_root) { header->set_allocated_withdrawal_hash(rpc::H256_from_bytes32(*bh.withdrawals_root).release()); } + if (bh.excess_data_gas) { + header->set_allocated_excess_data_gas(rpc::H256_from_uint256(*bh.excess_data_gas).release()); + } } static void deserialize_header(const ::execution::Header& received_header, BlockHeader& header) { @@ -77,6 +80,9 @@ static void deserialize_header(const ::execution::Header& received_header, Block if (received_header.has_withdrawal_hash()) { header.withdrawals_root = rpc::bytes32_from_H256(received_header.withdrawal_hash()); } + if (received_header.has_excess_data_gas()) { + header.excess_data_gas = rpc::uint256_from_H256(received_header.excess_data_gas()); + } } static void match_or_throw(const Hash& block_hash, const types::H256& received_hash) { diff --git a/silkworm/node/stagedsync/stages/stage_senders.cpp b/silkworm/node/stagedsync/stages/stage_senders.cpp index 74f3b05cb7..0807528b9e 100644 --- a/silkworm/node/stagedsync/stages/stage_senders.cpp +++ b/silkworm/node/stagedsync/stages/stage_senders.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -453,28 +454,13 @@ Stage::Result Senders::add_to_batch(BlockNum block_num, std::vector const evmc_revision rev{node_settings_->chain_config->revision(block_num, /*block_time=*/0)}; const bool has_homestead{rev >= EVMC_HOMESTEAD}; const bool has_spurious_dragon{rev >= EVMC_SPURIOUS_DRAGON}; - const bool has_berlin{rev >= EVMC_BERLIN}; - const bool has_london{rev >= EVMC_LONDON}; uint32_t tx_id{0}; for (const auto& transaction : transactions) { - switch (transaction.type) { - case Transaction::Type::kLegacy: - break; - case Transaction::Type::kEip2930: - if (!has_berlin) { - log::Error(log_prefix_) << "Transaction type " << magic_enum::enum_name(transaction.type) - << " for transaction #" << tx_id << " in block #" << block_num << " before Berlin"; - return Stage::Result::kInvalidTransaction; - } - break; - case Transaction::Type::kEip1559: - if (!has_london) { - log::Error(log_prefix_) << "Transaction type " << magic_enum::enum_name(transaction.type) - << " for transaction #" << tx_id << " in block #" << block_num << " before London"; - return Stage::Result::kInvalidTransaction; - } - break; + if (!consensus::transaction_type_is_supported(transaction.type, rev)) { + log::Error(log_prefix_) << "Transaction type " << magic_enum::enum_name(transaction.type) + << " for transaction #" << tx_id << " in block #" << block_num << " before it's supported"; + return Stage::Result::kInvalidTransaction; } if (!is_valid_signature(transaction.r, transaction.s, has_homestead)) { diff --git a/silkworm/silkrpc/common/util_test.cpp b/silkworm/silkrpc/common/util_test.cpp index 133bfe5c87..aa715aae99 100644 --- a/silkworm/silkrpc/common/util_test.cpp +++ b/silkworm/silkrpc/common/util_test.cpp @@ -133,19 +133,18 @@ TEST_CASE("check_tx_fee_less_cap returns false", "[silkrpc][common][util]") { } TEST_CASE("is_replay_protected(tx legacy) returns true", "[silkrpc][common][util]") { - const silkworm::Transaction txn{ - silkworm::Transaction::Type::kEip2930, - 0, // nonce - 50'000 * kGiga, // max_priority_fee_per_gas - 50'000 * kGiga, // max_fee_per_gas - 21'000, // gas_limit - 0x5df9b87991262f6ba471f09758cde1c0fc1de734_address, // to - 31337, // value - {}, // data - true, // odd_y_parity - std::nullopt, // chain_id - intx::from_string("0x88ff6cf0fefd94db46111149ae4bfc179e9b94721fffd821d38d16464b3f71d0"), // r - intx::from_string("0x45e0aff800961cfce805daef7016b9b675c137a6a41a548f7b60a3484c06a33a"), // s + const Transaction txn{ + .type = Transaction::Type::kEip2930, + .nonce = 0, + .max_priority_fee_per_gas = 50'000 * kGiga, + .max_fee_per_gas = 50'000 * kGiga, + .gas_limit = 21'000, + .to = 0x5df9b87991262f6ba471f09758cde1c0fc1de734_address, + .value = 31337, + .odd_y_parity = true, + .chain_id = std::nullopt, + .r = intx::from_string("0x88ff6cf0fefd94db46111149ae4bfc179e9b94721fffd821d38d16464b3f71d0"), + .s = intx::from_string("0x45e0aff800961cfce805daef7016b9b675c137a6a41a548f7b60a3484c06a33a"), }; auto check = is_replay_protected(txn); @@ -153,39 +152,38 @@ TEST_CASE("is_replay_protected(tx legacy) returns true", "[silkrpc][common][util } TEST_CASE("is_replay_protected returns true", "[silkrpc][common][util]") { - silkworm::Transaction txn{ - silkworm::Transaction::Type::kLegacy, // type - 0, - 20000000000, - 20000000000, - uint64_t{0}, - 0x0715a7794a1dc8e42615f059dd6e406a6594651a_address, - intx::uint256{8}, - *silkworm::from_hex("001122aabbcc"), - false, - intx::uint256{9}, - intx::uint256{18}, - intx::uint256{36}, - std::vector{}, - 0x007fb8417eb9ad4d958b050fc3720d5b46a2c053_address}; + Transaction txn{ + .type = Transaction::Type::kLegacy, + .nonce = 0, + .max_priority_fee_per_gas = 20000000000, + .max_fee_per_gas = 20000000000, + .gas_limit = 0, + .to = 0x0715a7794a1dc8e42615f059dd6e406a6594651a_address, + .value = 8, + .data = *from_hex("001122aabbcc"), + .odd_y_parity = false, + .chain_id = 9, + .r = 18, + .s = 36, + .from = 0x007fb8417eb9ad4d958b050fc3720d5b46a2c053_address, + }; auto check = is_replay_protected(txn); CHECK(check == true); } TEST_CASE("is_replay_protected returns false", "[silkrpc][common][util]") { - const silkworm::Transaction txn{ - silkworm::Transaction::Type::kLegacy, // type - 0, // nonce - 50'000 * kGiga, // max_priority_fee_per_gas - 50'000 * kGiga, // max_fee_per_gas - 21'000, // gas_limit - 0x5df9b87991262f6ba471f09758cde1c0fc1de734_address, // to - 31337, // value - {}, // data - true, // odd_y_parity - std::nullopt, // chain_id - intx::from_string("0x88ff6cf0fefd94db46111149ae4bfc179e9b94721fffd821d38d16464b3f71d0"), // r - intx::from_string("0x45e0aff800961cfce805daef7016b9b675c137a6a41a548f7b60a3484c06a33a"), // s + const Transaction txn{ + .type = Transaction::Type::kLegacy, + .nonce = 0, + .max_priority_fee_per_gas = 50'000 * kGiga, + .max_fee_per_gas = 50'000 * kGiga, + .gas_limit = 21'000, + .to = 0x5df9b87991262f6ba471f09758cde1c0fc1de734_address, + .value = 31337, + .odd_y_parity = true, + .chain_id = std::nullopt, + .r = intx::from_string("0x88ff6cf0fefd94db46111149ae4bfc179e9b94721fffd821d38d16464b3f71d0"), + .s = intx::from_string("0x45e0aff800961cfce805daef7016b9b675c137a6a41a548f7b60a3484c06a33a"), }; auto check = is_replay_protected(txn); diff --git a/silkworm/silkrpc/json/types_test.cpp b/silkworm/silkrpc/json/types_test.cpp index 332abe16e9..841be75b28 100644 --- a/silkworm/silkrpc/json/types_test.cpp +++ b/silkworm/silkrpc/json/types_test.cpp @@ -799,20 +799,18 @@ TEST_CASE("serialize legacy transaction (type=0)", "[silkrpc][to_json]") { silkworm::rpc::Transaction txn2{ { - silkworm::Transaction::Type::kLegacy, // type - 0, // nonce - 50'000 * kGiga, // max_priority_fee_per_gas - 50'000 * kGiga, // max_fee_per_gas - 21'000, // gas_limit - 0x5df9b87991262f6ba471f09758cde1c0fc1de734_address, // to - 31337, // value - {}, // data - true, // odd_y_parity - std::nullopt, // chain_id - intx::from_string("0x88ff6cf0fefd94db46111149ae4bfc179e9b94721fffd821d38d16464b3f71d0"), // r - intx::from_string("0x45e0aff800961cfce805daef7016b9b675c137a6a41a548f7b60a3484c06a33a"), // s - std::vector{}, // access_list - 0x007fb8417eb9ad4d958b050fc3720d5b46a2c053_address, // from + .type = Transaction::Type::kLegacy, + .nonce = 0, + .max_priority_fee_per_gas = 50'000 * kGiga, + .max_fee_per_gas = 50'000 * kGiga, + .gas_limit = 21'000, + .to = 0x5df9b87991262f6ba471f09758cde1c0fc1de734_address, + .value = 31337, + .odd_y_parity = true, + .chain_id = std::nullopt, + .r = intx::from_string("0x88ff6cf0fefd94db46111149ae4bfc179e9b94721fffd821d38d16464b3f71d0"), + .s = intx::from_string("0x45e0aff800961cfce805daef7016b9b675c137a6a41a548f7b60a3484c06a33a"), + .from = 0x007fb8417eb9ad4d958b050fc3720d5b46a2c053_address, }, 0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd_bytes32, // block_hash 46147, // block_number @@ -839,20 +837,18 @@ TEST_CASE("serialize legacy transaction (type=0)", "[silkrpc][to_json]") { })"_json); silkworm::rpc::Transaction txn3{ { - silkworm::Transaction::Type::kLegacy, // type - 0, // nonce - 50'000 * kGiga, // max_priority_fee_per_gas - 50'000 * kGiga, // max_fee_per_gas - 21'000, // gas_limit - 0x5df9b87991262f6ba471f09758cde1c0fc1de734_address, // to - 31337, // value - {}, // data - true, // odd_y_parity - std::nullopt, // chain_id - intx::from_string("0x88ff6cf0fefd94db46111149ae4bfc179e9b94721fffd821d38d16464b3f71d0"), // r - intx::from_string("0x45e0aff800961cfce805daef7016b9b675c137a6a41a548f7b60a3484c06a33a"), // s - std::vector{}, // access_list - 0x007fb8417eb9ad4d958b050fc3720d5b46a2c053_address, // from + .type = Transaction::Type::kLegacy, + .nonce = 0, + .max_priority_fee_per_gas = 50'000 * kGiga, + .max_fee_per_gas = 50'000 * kGiga, + .gas_limit = 21'000, + .to = 0x5df9b87991262f6ba471f09758cde1c0fc1de734_address, + .value = 31337, + .odd_y_parity = true, + .chain_id = std::nullopt, + .r = intx::from_string("0x88ff6cf0fefd94db46111149ae4bfc179e9b94721fffd821d38d16464b3f71d0"), + .s = intx::from_string("0x45e0aff800961cfce805daef7016b9b675c137a6a41a548f7b60a3484c06a33a"), + .from = 0x007fb8417eb9ad4d958b050fc3720d5b46a2c053_address, }, 0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd_bytes32, // block_hash 46147, // block_number @@ -882,20 +878,20 @@ TEST_CASE("serialize legacy transaction (type=0)", "[silkrpc][to_json]") { TEST_CASE("serialize EIP-2930 transaction (type=1)", "[silkrpc][to_json]") { silkworm::Transaction txn1{ - silkworm::Transaction::Type::kEip2930, - 0, - 20000000000, - 20000000000, - uint64_t{0}, - 0x0715a7794a1dc8e42615f059dd6e406a6594651a_address, - intx::uint256{0}, - *silkworm::from_hex("001122aabbcc"), - false, - intx::uint256{1}, - intx::uint256{18}, - intx::uint256{36}, - std::vector{}, - 0x007fb8417eb9ad4d958b050fc3720d5b46a2c053_address}; + .type = Transaction::Type::kEip2930, + .nonce = 0, + .max_priority_fee_per_gas = 20000000000, + .max_fee_per_gas = 20000000000, + .gas_limit = 0, + .to = 0x0715a7794a1dc8e42615f059dd6e406a6594651a_address, + .value = 0, + .data = *from_hex("001122aabbcc"), + .odd_y_parity = false, + .chain_id = 1, + .r = 18, + .s = 36, + .from = 0x007fb8417eb9ad4d958b050fc3720d5b46a2c053_address, + }; nlohmann::json j1 = txn1; CHECK(j1 == R"({ "nonce":"0x0", @@ -924,20 +920,20 @@ TEST_CASE("serialize EIP-2930 transaction (type=1)", "[silkrpc][to_json]") { silkworm::rpc::Transaction txn2{ { - silkworm::Transaction::Type::kEip2930, - 0, - 20000000000, - 30000000000, - uint64_t{0}, - 0x0715a7794a1dc8e42615f059dd6e406a6594651a_address, - intx::uint256{0}, - *silkworm::from_hex("001122aabbcc"), - false, - intx::uint256{1}, - intx::uint256{18}, - intx::uint256{36}, - access_list, - 0x007fb8417eb9ad4d958b050fc3720d5b46a2c053_address, + .type = Transaction::Type::kEip2930, + .nonce = 0, + .max_priority_fee_per_gas = 20000000000, + .max_fee_per_gas = 30000000000, + .gas_limit = 0, + .to = 0x0715a7794a1dc8e42615f059dd6e406a6594651a_address, + .value = 0, + .data = *from_hex("001122aabbcc"), + .odd_y_parity = false, + .chain_id = 1, + .r = 18, + .s = 36, + .access_list = access_list, + .from = 0x007fb8417eb9ad4d958b050fc3720d5b46a2c053_address, }, 0x374f3a049e006f36f6cf91b02a3b0ee16c858af2f75858733eb0e927b5b7126c_bytes32, 123123, @@ -979,20 +975,19 @@ TEST_CASE("serialize EIP-2930 transaction (type=1)", "[silkrpc][to_json]") { TEST_CASE("serialize EIP-1559 transaction (type=2)", "[silkrpc][to_json]") { silkworm::Transaction txn1{ - silkworm::Transaction::Type::kEip1559, // type - 0, // nonce - 50'000 * kGiga, // max_priority_fee_per_gas - 50'000 * kGiga, // max_fee_per_gas - 21'000, // gas_limit - 0x5df9b87991262f6ba471f09758cde1c0fc1de734_address, // to - 31337, // value - *silkworm::from_hex("001122aabbcc"), // data - true, // odd_y_parity - intx::uint256{1}, // chainId - intx::from_string("0x88ff6cf0fefd94db46111149ae4bfc179e9b94721fffd821d38d16464b3f71d0"), // r - intx::from_string("0x45e0aff800961cfce805daef7016b9b675c137a6a41a548f7b60a3484c06a33a"), // s - std::vector{}, - 0x007fb8417eb9ad4d958b050fc3720d5b46a2c053_address // + .type = Transaction::Type::kEip1559, + .nonce = 0, + .max_priority_fee_per_gas = 50'000 * kGiga, + .max_fee_per_gas = 50'000 * kGiga, + .gas_limit = 21'000, + .to = 0x5df9b87991262f6ba471f09758cde1c0fc1de734_address, + .value = 31337, + .data = *from_hex("001122aabbcc"), + .odd_y_parity = true, + .chain_id = intx::uint256{1}, + .r = intx::from_string("0x88ff6cf0fefd94db46111149ae4bfc179e9b94721fffd821d38d16464b3f71d0"), + .s = intx::from_string("0x45e0aff800961cfce805daef7016b9b675c137a6a41a548f7b60a3484c06a33a"), + .from = 0x007fb8417eb9ad4d958b050fc3720d5b46a2c053_address, }; nlohmann::json j1 = txn1; CHECK(j1 == R"({ diff --git a/silkworm/silkrpc/types/transaction_test.cpp b/silkworm/silkrpc/types/transaction_test.cpp index d86669d993..a2d6b38c41 100644 --- a/silkworm/silkrpc/types/transaction_test.cpp +++ b/silkworm/silkrpc/types/transaction_test.cpp @@ -52,20 +52,19 @@ TEST_CASE("print type-2 transaction", "[silkrpc][types][transaction]") { // https://etherscan.io/tx/0x4b408a48f927f03a63502fb63f7d42c5c4783737ebe8d084cef157575d40f344 Transaction txn{ { - silkworm::Transaction::Type::kEip1559, // type - 371, // nonce - 1 * kGiga, // max_priority_fee_per_gas - 217'914'097'876, // max_fee_per_gas - 613'991, // gas_limit - 0x14efa0d4b0f9850ba1787edc730324962446d7cc_address, // to - 210'000'000 * kGiga, // value - *silkworm::from_hex("0x6ecd23060000000000000000000000000000000000000000000000000000000000000005"), // data - true, // odd_y_parity - intx::from_string("0x1"), // chain_id - intx::from_string("0x88ff6cf0fefd94db46111149ae4bfc179e9b94721fffd821d38d16464b3f71d0"), // r - intx::from_string("0x45e0aff800961cfce805daef7016b9b675c137a6a41a548f7b60a3484c06a33a"), // s - std::vector{}, // access list - 0x7ad75fdb6244111753822140dad3337f5535f718_address, // from + .type = Transaction::Type::kEip1559, + .nonce = 371, + .max_priority_fee_per_gas = 1 * kGiga, + .max_fee_per_gas = 217'914'097'876, + .gas_limit = 613'991, + .to = 0x14efa0d4b0f9850ba1787edc730324962446d7cc_address, + .value = 210'000'000 * kGiga, + .data = *from_hex("0x6ecd23060000000000000000000000000000000000000000000000000000000000000005"), + .odd_y_parity = true, + .chain_id = 1, + .r = intx::from_string("0x88ff6cf0fefd94db46111149ae4bfc179e9b94721fffd821d38d16464b3f71d0"), + .s = intx::from_string("0x45e0aff800961cfce805daef7016b9b675c137a6a41a548f7b60a3484c06a33a"), + .from = 0x7ad75fdb6244111753822140dad3337f5535f718_address, }, 0x007fe79ccdd5365f46c34336b8a15b36e05c249a0c62596878236a38034edc21_bytes32, // block hash 13116571, // block number @@ -78,20 +77,19 @@ TEST_CASE("print type-2 transaction", "[silkrpc][types][transaction]") { TEST_CASE("print type-2 silkworm::transaction", "[silkrpc][types][silkworm::transaction]") { // https://etherscan.io/tx/0x4b408a48f927f03a63502fb63f7d42c5c4783737ebe8d084cef157575d40f344 silkworm::Transaction txn{ - silkworm::Transaction::Type::kEip1559, // type - 371, // nonce - 1 * kGiga, // max_priority_fee_per_gas - 217'914'097'876, // max_fee_per_gas - 613'991, // gas_limit - 0x14efa0d4b0f9850ba1787edc730324962446d7cc_address, // to - 210'000'000 * kGiga, // value - *silkworm::from_hex("0x6ecd23060000000000000000000000000000000000000000000000000000000000000005"), // data - true, // odd_y_parity - intx::from_string("0x1"), // chain_id - intx::from_string("0x88ff6cf0fefd94db46111149ae4bfc179e9b94721fffd821d38d16464b3f71d0"), // r - intx::from_string("0x45e0aff800961cfce805daef7016b9b675c137a6a41a548f7b60a3484c06a33a"), // s - std::vector{}, // access list - 0x7ad75fdb6244111753822140dad3337f5535f718_address, // from + .type = Transaction::Type::kEip1559, + .nonce = 371, + .max_priority_fee_per_gas = 1 * kGiga, + .max_fee_per_gas = 217'914'097'876, + .gas_limit = 613'991, + .to = 0x14efa0d4b0f9850ba1787edc730324962446d7cc_address, + .value = 210'000'000 * kGiga, + .data = *from_hex("0x6ecd23060000000000000000000000000000000000000000000000000000000000000005"), + .odd_y_parity = true, + .chain_id = 1, + .r = intx::from_string("0x88ff6cf0fefd94db46111149ae4bfc179e9b94721fffd821d38d16464b3f71d0"), + .s = intx::from_string("0x45e0aff800961cfce805daef7016b9b675c137a6a41a548f7b60a3484c06a33a"), + .from = 0x7ad75fdb6244111753822140dad3337f5535f718_address, }; CHECK_NOTHROW(null_stream() << txn); } @@ -100,20 +98,18 @@ TEST_CASE("create legacy transaction", "[silkrpc][types][transaction]") { // https://etherscan.io/tx/0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060 Transaction txn{ { - silkworm::Transaction::Type::kLegacy, // type - 0, // nonce - 50'000 * kGiga, // max_priority_fee_per_gas - 50'000 * kGiga, // max_fee_per_gas - 21'000, // gas_limit - 0x5df9b87991262f6ba471f09758cde1c0fc1de734_address, // to - 31337, // value - {}, // data - true, // odd_y_parity - std::nullopt, // chain_id - intx::from_string("0x88ff6cf0fefd94db46111149ae4bfc179e9b94721fffd821d38d16464b3f71d0"), // r - intx::from_string("0x45e0aff800961cfce805daef7016b9b675c137a6a41a548f7b60a3484c06a33a"), // s - std::vector{}, // access list - 0xa1e4380a3b1f749673e270229993ee55f35663b4_address, // from + .type = Transaction::Type::kLegacy, + .nonce = 0, + .max_priority_fee_per_gas = 50'000 * kGiga, + .max_fee_per_gas = 50'000 * kGiga, + .gas_limit = 21'000, + .to = 0x5df9b87991262f6ba471f09758cde1c0fc1de734_address, + .value = 31337, + .odd_y_parity = true, + .chain_id = std::nullopt, + .r = intx::from_string("0x88ff6cf0fefd94db46111149ae4bfc179e9b94721fffd821d38d16464b3f71d0"), + .s = intx::from_string("0x45e0aff800961cfce805daef7016b9b675c137a6a41a548f7b60a3484c06a33a"), + .from = 0xa1e4380a3b1f749673e270229993ee55f35663b4_address, }, 0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd_bytes32, // block hash 46147, // block number @@ -127,20 +123,18 @@ TEST_CASE("create legacy transaction", "[silkrpc][types][transaction]") { TEST_CASE("create legacy silkworm::transaction", "[silkrpc][types][silkworm::transaction]") { // https://etherscan.io/tx/0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060 silkworm::Transaction txn{ - silkworm::Transaction::Type::kLegacy, // type - 0, // nonce - 50'000 * kGiga, // max_priority_fee_per_gas - 50'000 * kGiga, // max_fee_per_gas - 21'000, // gas_limit - 0x5df9b87991262f6ba471f09758cde1c0fc1de734_address, // to - 31337, // value - {}, // data - true, // odd_y_parity - std::nullopt, // chain_id - intx::from_string("0x88ff6cf0fefd94db46111149ae4bfc179e9b94721fffd821d38d16464b3f71d0"), // r - intx::from_string("0x45e0aff800961cfce805daef7016b9b675c137a6a41a548f7b60a3484c06a33a"), // s - std::vector{}, // access list - 0xa1e4380a3b1f749673e270229993ee55f35663b4_address, // from + .type = Transaction::Type::kLegacy, + .nonce = 0, + .max_priority_fee_per_gas = 50'000 * kGiga, + .max_fee_per_gas = 50'000 * kGiga, + .gas_limit = 21'000, + .to = 0x5df9b87991262f6ba471f09758cde1c0fc1de734_address, + .value = 31337, + .odd_y_parity = true, + .chain_id = std::nullopt, + .r = intx::from_string("0x88ff6cf0fefd94db46111149ae4bfc179e9b94721fffd821d38d16464b3f71d0"), + .s = intx::from_string("0x45e0aff800961cfce805daef7016b9b675c137a6a41a548f7b60a3484c06a33a"), + .from = 0xa1e4380a3b1f749673e270229993ee55f35663b4_address, }; CHECK_NOTHROW(null_stream() << txn); diff --git a/silkworm/sync/internals/body_sequence.cpp b/silkworm/sync/internals/body_sequence.cpp index e18bd8124a..4e10cf5afb 100644 --- a/silkworm/sync/internals/body_sequence.cpp +++ b/silkworm/sync/internals/body_sequence.cpp @@ -71,8 +71,8 @@ Penalty BodySequence::accept_requested_bodies(BlockBodiesPacket66& packet, const auto matching_requests = body_requests_.find_by_request_id(packet.requestId); for (auto& body : packet.request) { - Hash oh = consensus::EngineBase::compute_ommers_hash(body); - Hash tr = consensus::EngineBase::compute_transaction_root(body); + Hash oh = consensus::compute_ommers_hash(body); + Hash tr = consensus::compute_transaction_root(body); auto exact_request = body_requests_.end(); // = no request @@ -283,10 +283,12 @@ void BodySequence::request_nack(const GetBlockBodiesPacket66& packet) { } bool BodySequence::is_valid_body(const BlockHeader& header, const BlockBody& body) { - if (header.ommers_hash != consensus::EngineBase::compute_ommers_hash(body)) + if (header.ommers_hash != consensus::compute_ommers_hash(body)) { return false; - if (header.transactions_root != consensus::EngineBase::compute_transaction_root(body)) + } + if (header.transactions_root != consensus::compute_transaction_root(body)) { return false; + } return true; } diff --git a/silkworm/sync/sync_engine_pos.cpp b/silkworm/sync/sync_engine_pos.cpp index f26ee94390..46f84522b3 100644 --- a/silkworm/sync/sync_engine_pos.cpp +++ b/silkworm/sync/sync_engine_pos.cpp @@ -117,7 +117,7 @@ Block PoSSync::make_execution_block(const ExecutionPayload& payload) { } block.transactions.push_back(tx); } - header.transactions_root = consensus::EngineBase::compute_transaction_root(block); + header.transactions_root = consensus::compute_transaction_root(block); // as per EIP-3675 header.ommers_hash = kEmptyListHash; // = Keccak256(RLP([]))