From 51fdcde96ca54562a2b37f638d22d972bea9b792 Mon Sep 17 00:00:00 2001 From: Leonardo Amalaral Date: Mon, 28 Oct 2024 20:05:46 -0300 Subject: [PATCH 01/37] view of address --- src/bytes/cast.h | 29 +++++ src/bytes/range.h | 3 +- src/contract/contract.cpp | 2 +- src/contract/contractfactory.h | 2 +- src/contract/contracthost.cpp | 14 +-- src/contract/contracthost.h | 21 ++-- src/core/state.cpp | 10 +- src/net/http/jsonrpc/methods.cpp | 7 +- src/utils/CMakeLists.txt | 1 + src/utils/address.cpp | 35 ++++++ src/utils/address.h | 183 +++++++++++++++++++++++++++++++ src/utils/bytes.h | 10 ++ src/utils/bytesinterface.h | 165 ++++++++++++++++++++++++++++ src/utils/hex.h | 3 +- src/utils/strings.cpp | 49 --------- src/utils/strings.h | 58 +--------- src/utils/tx.cpp | 7 +- src/utils/utils.h | 3 +- src/utils/view.h | 24 ++++ tests/core/state.cpp | 4 +- tests/sdktestsuite.hpp | 14 +-- tests/utils/strings.cpp | 2 +- 22 files changed, 495 insertions(+), 151 deletions(-) create mode 100644 src/bytes/cast.h create mode 100644 src/utils/address.cpp create mode 100644 src/utils/address.h create mode 100644 src/utils/bytes.h create mode 100644 src/utils/bytesinterface.h create mode 100644 src/utils/view.h diff --git a/src/bytes/cast.h b/src/bytes/cast.h new file mode 100644 index 00000000..44a0b485 --- /dev/null +++ b/src/bytes/cast.h @@ -0,0 +1,29 @@ +#ifndef BDK_BYTES_CAST_H +#define BDK_BYTES_CAST_H + +#include "range.h" + +namespace bytes { + +/** + * Casts a range of bytes from one type to another. + * + * @param src the input bytes to be cast + * @param dest the destiny bytes container + * @return the destiny bytes container + * @throws invalid argument exception on size incompatibility + */ +template +constexpr R cast(const Range auto& src, R dest = R()) { + if (std::ranges::size(src) != std::ranges::size(dest)) { + throw std::invalid_argument("incompatible sizes for casting"); + } + + std::ranges::copy(src, std::ranges::begin(dest)); + + return dest; +} + +} // namespace bytes + +#endif // BDK_BYTES_CAST_H diff --git a/src/bytes/range.h b/src/bytes/range.h index 6ca2b7fb..14f135b2 100644 --- a/src/bytes/range.h +++ b/src/bytes/range.h @@ -3,8 +3,7 @@ #include #include - -using Byte = std::uint8_t; +#include "utils/bytes.h" namespace bytes { /** diff --git a/src/contract/contract.cpp b/src/contract/contract.cpp index cf44b8e6..d074a155 100644 --- a/src/contract/contract.cpp +++ b/src/contract/contract.cpp @@ -17,7 +17,7 @@ Address BaseContract::getOrigin() const { if (this->host_ == nullptr) { throw DynamicException("Contracts going haywire! trying to get origin without a host!"); } - return this->host_->get_tx_context().tx_origin; + return Address(this->host_->get_tx_context().tx_origin); } uint64_t BaseContract::getNonce(const Address& address) const { diff --git a/src/contract/contractfactory.h b/src/contract/contractfactory.h index 6416bb14..6199d088 100644 --- a/src/contract/contractfactory.h +++ b/src/contract/contractfactory.h @@ -128,7 +128,7 @@ namespace ContractFactory { // The constructor can set SafeVariable values from the constructor. // We need to take account of that and set the variables accordingly. auto contract = createContractWithTuple( - callInfo.sender, derivedAddress, chainId, decodedData + Address(callInfo.sender), derivedAddress, chainId, decodedData ); host->registerNewCPPContract(derivedAddress, contract.get()); contracts.insert(std::make_pair(derivedAddress, std::move(contract))); diff --git a/src/contract/contracthost.cpp b/src/contract/contracthost.cpp index c5f69a17..656d8319 100644 --- a/src/contract/contracthost.cpp +++ b/src/contract/contracthost.cpp @@ -131,7 +131,7 @@ evmc::Result ContractHost::createEVMContract(const evmc_message& msg, assert (kind == evmc_call_kind::EVMC_CREATE || kind == evmc_call_kind::EVMC_CREATE2); // Create a new contract auto createMsg = msg; - createMsg.recipient = contractAddress.toEvmcAddress(); + createMsg.recipient = bytes::cast(contractAddress); createMsg.kind = kind; createMsg.input_data = nullptr; createMsg.input_size = 0; @@ -434,7 +434,7 @@ void ContractHost::simulate(const evmc_message& msg, const ContractType& type) { } bool ContractHost::account_exists(const evmc::address& addr) const noexcept { - return accounts_.find(addr) != accounts_.end(); + return accounts_.find(Address(addr)) != accounts_.end(); } evmc::bytes32 ContractHost::get_storage(const evmc::address& addr, @@ -473,7 +473,7 @@ evmc_storage_status ContractHost::set_storage(const evmc::address& addr, evmc::uint256be ContractHost::get_balance(const evmc::address& addr) const noexcept { try { - auto it = accounts_.find(addr); + auto it = accounts_.find(Address(addr)); if (it != accounts_.end()) { return Utils::uint256ToEvmcUint256(it->second->balance); } @@ -486,7 +486,7 @@ evmc::uint256be ContractHost::get_balance(const evmc::address& addr) const noexc size_t ContractHost::get_code_size(const evmc::address& addr) const noexcept { try { - auto it = accounts_.find(addr); + auto it = accounts_.find(Address(addr)); if (it != accounts_.end()) { return it->second->code.size(); } @@ -499,7 +499,7 @@ size_t ContractHost::get_code_size(const evmc::address& addr) const noexcept { evmc::bytes32 ContractHost::get_code_hash(const evmc::address& addr) const noexcept { try { - auto it = accounts_.find(addr); + auto it = accounts_.find(Address(addr)); if (it != accounts_.end()) { return it->second->codeHash.toEvmcBytes32(); } @@ -515,7 +515,7 @@ size_t ContractHost::copy_code(const evmc::address& addr, uint8_t* buffer_data, size_t buffer_size) const noexcept { try { - const auto it = this->accounts_.find(addr); + const auto it = this->accounts_.find(Address(addr)); if (it != this->accounts_.end()) { const auto& code = it->second->code; if (code_offset < code.size()) { @@ -727,7 +727,7 @@ void ContractHost::emit_log(const evmc::address& addr, this->txIndex_, this->blockHash_, this->currentTxContext_.block_number, - addr, + Address(addr), Bytes(data, data + data_size), topics_, (topics_count == 0) diff --git a/src/contract/contracthost.h b/src/contract/contracthost.h index 9ae8d170..fafc91be 100644 --- a/src/contract/contracthost.h +++ b/src/contract/contracthost.h @@ -16,7 +16,8 @@ #include "contractmanager.h" #include "../core/dump.h" #include "calltracer.h" -#include "../bytes/join.h" +#include "bytes/join.h" +#include "bytes/cast.h" // TODO: EVMC Static Mode Handling @@ -252,8 +253,8 @@ class ContractHost : public evmc::Host { msg.flags = EVMC_STATIC; msg.depth = 1; msg.gas = this->leftoverGas_; - msg.recipient = targetAddr.toEvmcAddress(); - msg.sender = caller->getContractAddress().toEvmcAddress(); + msg.recipient = bytes::cast(targetAddr); + msg.sender = bytes::cast(caller->getContractAddress()); auto functionName = ContractReflectionInterface::getFunctionName(func); if (functionName.empty()) { throw DynamicException("ContractHost::callContractViewFunction: EVM contract function name is empty (contract not registered?)"); @@ -268,7 +269,7 @@ class ContractHost : public evmc::Host { msg.input_size = fullData.size(); msg.value = {}; msg.create2_salt = {}; - msg.code_address = targetAddr.toEvmcAddress(); + msg.code_address = bytes::cast(targetAddr); /// TODO: OMG this is so ugly, we need to fix this. /// A **CONST_CAST** is needed because we can't explicity tell the evmc_execute to do a view call. /// Regardless of that, we set flag = 1 to indicate that this is a view/STATIC call. @@ -390,8 +391,8 @@ class ContractHost : public evmc::Host { msg.flags = 0; msg.depth = 1; msg.gas = this->leftoverGas_; - msg.recipient = targetAddr.toEvmcAddress(); - msg.sender = caller->getContractAddress().toEvmcAddress(); + msg.recipient = bytes::cast(targetAddr); + msg.sender = bytes::cast(caller->getContractAddress()); auto functionName = ContractReflectionInterface::getFunctionName(func); if (functionName.empty()) { throw DynamicException("ContractHost::callContractFunction: EVM contract function name is empty (contract not registered?)"); @@ -406,7 +407,7 @@ class ContractHost : public evmc::Host { msg.input_size = fullData.size(); msg.value = Utils::uint256ToEvmcUint256(value); msg.create2_salt = {}; - msg.code_address = targetAddr.toEvmcAddress(); + msg.code_address = bytes::cast(targetAddr); evmc::Result result (evmc_execute(this->vm_, &this->get_interface(), this->to_context(), evmc_revision::EVMC_LATEST_STABLE_REVISION, &msg, recipientAcc.code.data(), recipientAcc.code.size())); this->leftoverGas_ = result.gas_left; @@ -475,13 +476,13 @@ class ContractHost : public evmc::Host { callInfo.flags = 0; callInfo.depth = 1; callInfo.gas = this->leftoverGas_; - callInfo.recipient = to.toEvmcAddress(); - callInfo.sender = from.toEvmcAddress(); + callInfo.recipient = bytes::cast(to); + callInfo.sender = bytes::cast(from); callInfo.input_data = fullData.data(); callInfo.input_size = fullData.size(); callInfo.value = {}; callInfo.create2_salt = {}; - callInfo.code_address = to.toEvmcAddress(); + callInfo.code_address = bytes::cast(to); // Get the ContractManager from the this->accounts_ map ContractManager* contractManager = dynamic_cast(this->contracts_.at(to).get()); this->setContractVars(contractManager, from, 0); diff --git a/src/core/state.cpp b/src/core/state.cpp index 0b5a07e5..e563c7c9 100644 --- a/src/core/state.cpp +++ b/src/core/state.cpp @@ -208,8 +208,8 @@ void State::processTransaction( try { evmc_tx_context txContext; txContext.tx_gas_price = Utils::uint256ToEvmcUint256(tx.getMaxFeePerGas()); - txContext.tx_origin = tx.getFrom().toEvmcAddress(); - txContext.block_coinbase = ContractGlobals::getCoinbase().toEvmcAddress(); + txContext.tx_origin = bytes::cast(tx.getFrom()); + txContext.block_coinbase = bytes::cast(ContractGlobals::getCoinbase()); txContext.block_number = ContractGlobals::getBlockHeight(); txContext.block_timestamp = ContractGlobals::getBlockTimestamp(); txContext.block_gas_limit = 10000000; @@ -436,7 +436,7 @@ Bytes State::ethCall(const evmc_message& callInfo) { // As the contract host will modify (reverting in the end) the state. std::unique_lock lock(this->stateMutex_); const auto recipient(callInfo.recipient); - const auto& accIt = this->accounts_.find(recipient); + const auto& accIt = this->accounts_.find(Address(recipient)); if (accIt == this->accounts_.end()) { return {}; } @@ -446,7 +446,7 @@ Bytes State::ethCall(const evmc_message& callInfo) { evmc_tx_context txContext; txContext.tx_gas_price = {}; txContext.tx_origin = callInfo.sender; - txContext.block_coinbase = ContractGlobals::getCoinbase().toEvmcAddress(); + txContext.block_coinbase = bytes::cast(ContractGlobals::getCoinbase()); txContext.block_number = static_cast(ContractGlobals::getBlockHeight()); txContext.block_timestamp = static_cast(ContractGlobals::getBlockTimestamp()); txContext.block_gas_limit = 10000000; @@ -479,7 +479,7 @@ Bytes State::ethCall(const evmc_message& callInfo) { int64_t State::estimateGas(const evmc_message& callInfo) { std::unique_lock lock(this->stateMutex_); - const Address to = callInfo.recipient; + const Address to(callInfo.recipient); // ContractHost simulate already do all necessary checks // We just need to execute and get the leftOverGas ContractType type = ContractType::NOT_A_CONTRACT; diff --git a/src/net/http/jsonrpc/methods.cpp b/src/net/http/jsonrpc/methods.cpp index ab1876dc..edf6be07 100644 --- a/src/net/http/jsonrpc/methods.cpp +++ b/src/net/http/jsonrpc/methods.cpp @@ -4,6 +4,7 @@ #include "variadicparser.h" #include "../../../core/storage.h" #include "../../../core/state.h" +#include "bytes/cast.h" #include @@ -106,14 +107,14 @@ static std::pair parseEvmcMessage(const json& request, cons throw Error(-32601, "Only latest block is supported"); msg.sender = parseIfExists
(txJson, "from") - .transform([] (const Address& addr) { return addr.toEvmcAddress(); }) + .transform([] (const Address& addr) { return bytes::cast(addr); }) .value_or(evmc::address{}); if (recipientRequired) - msg.recipient = parse
(txJson.at("to")).toEvmcAddress(); + msg.recipient = bytes::cast(parse
(txJson.at("to"))); else msg.recipient = parseIfExists
(txJson, "to") - .transform([] (const Address& addr) { return addr.toEvmcAddress(); }) + .transform([] (const Address& addr) { return bytes::cast(addr); }) .value_or(evmc::address{}); msg.gas = parseIfExists(txJson, "gas").value_or(10000000); diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index defa65b4..cc65f02a 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -18,6 +18,7 @@ set(UTILS_HEADERS set(UTILS_SOURCES ${CMAKE_SOURCE_DIR}/src/utils/db.cpp + ${CMAKE_SOURCE_DIR}/src/utils/address.cpp ${CMAKE_SOURCE_DIR}/src/utils/utils.cpp ${CMAKE_SOURCE_DIR}/src/utils/strings.cpp ${CMAKE_SOURCE_DIR}/src/utils/hex.cpp diff --git a/src/utils/address.cpp b/src/utils/address.cpp new file mode 100644 index 00000000..4f9a2306 --- /dev/null +++ b/src/utils/address.cpp @@ -0,0 +1,35 @@ +#include "address.h" +#include "utils.h" + +Hex Address::checksum(View
address) { + // Hash requires lowercase address without "0x" + std::string str = Hex::fromBytes(address, false).get(); + Hex hash = Utils::sha3(Utils::create_view_span(str)).hex(); + for (int i = 0; i < str.length(); i++) { + if (!std::isdigit(str[i])) { // Only check letters (A-F) + // If character hash is 8-F then make it uppercase + int nibble = std::stoi(hash.substr(i, 1), nullptr, 16); + str[i] = (nibble >= 8) ? std::toupper(str[i]) : std::tolower(str[i]); + } + } + str.insert(0, "0x"); + return Hex(str, true); +} + +bool Address::isValid(const std::string_view add, bool inBytes) { + if (inBytes) return (add.size() == 20); + if (add[0] == '0' && (add[1] == 'x' || add[1] == 'X')) { + return (add.size() == 42 && + add.substr(2).find_first_not_of("0123456789abcdefABCDEF") == std::string::npos + ); + } else { + return (add.size() == 40 && + add.find_first_not_of("0123456789abcdefABCDEF") == std::string::npos + ); + } +} + +bool Address::isChksum(const std::string_view add) { + Address myAdd(add, false); + return (add == std::string_view(Address::checksum(myAdd))); +} diff --git a/src/utils/address.h b/src/utils/address.h new file mode 100644 index 00000000..50ea905a --- /dev/null +++ b/src/utils/address.h @@ -0,0 +1,183 @@ +#ifndef BDK_UTILS_ADDRESS_H +#define BDK_UTILS_ADDRESS_H + +#include "bytes/range.h" +#include "bytesinterface.h" +#include "evmc/evmc.hpp" +#include "view.h" +#include "zpp_bits.h" + +inline constexpr size_t ADDRESS_SIZE = 20; + +/// Abstraction for a single 20-byte address (e.g. "1234567890abcdef...") +class Address : public BytesInterface { +public: + + /** + * Constructs address with zeroes + */ + constexpr Address() : data_() {} + + /** + * Constructs an address from a given input. + * The input should construct a BytesInterface. + * + * Implicit construction is enabled when input + * meet the Initializer requirements. + */ + template + explicit (not bytes::Initializer) + constexpr Address(I&& input) : BytesInterface(std::forward(input)) {} + + /** + * Constructs an address from a initializer list + */ + constexpr Address(std::initializer_list initList) { + if (initList.size() != ADDRESS_SIZE) { + throw std::invalid_argument("20 bytes are required to initialize an address object"); + } + + std::ranges::copy(initList, data_.begin()); + } + + /** + * Copy constructor. + * @param add The address itself. + * @param inBytes If `true`, treats the input as a raw bytes string. + * @throw DynamicException if address has wrong size or is invalid. + */ + Address(const std::string_view add, bool inBytes); + + /** + * Returns the hexadecimal checksum of the given address representation. + * Per [EIP-55](https://eips.ethereum.org/EIPS/eip-55). + * @param address the address representation + * @return the checksum hexadecimal + */ + static Hex checksum(View
address); + + /** + * Check if a given address string is valid. + * If the address has both upper *and* lowercase letters, will also check the checksum. + * @param add The address to be checked. + * @param inBytes If `true`, treats the input as a raw bytes string. + * @return `true` if the address is valid, `false` otherwise. + */ + static bool isValid(const std::string_view add, bool inBytes); + + /** + * Check if an address string is checksummed, as per [EIP-55](https://eips.ethereum.org/EIPS/eip-55). + * Uses `toChksum()` internally. Does not alter the original string. + * @param add The string to check. + * @return `true` if the address is checksummed, `false` otherwise. + */ + static bool isChksum(const std::string_view add); + + /** + * Returns the beginning iterator of the address. + * @return the beginning iterator of the address. + */ + constexpr auto begin() { return data_.begin(); } + + /** + * Returns the beginning constant iterator of the address. + * @return the beginning constant iterator of the address. + */ + constexpr auto begin() const { return data_.begin(); } + +private: + friend zpp::bits::access; + using serialize = zpp::bits::members<1>; + + std::array data_; +}; + + +/** + * Base class template for identifying address representation types. + * Other address types must specialize. + */ +template +struct IsAddressType : std::is_same {}; + +/** + * Specialization for evmc::address + */ +template<> +struct IsAddressType : std::true_type {}; + +/** + * Specialization for evmc_address + */ +template<> +struct IsAddressType : std::true_type {}; + +/** + * View of a address representation type. + */ +template<> +class View
: public BytesInterface, ADDRESS_SIZE> { +public: + + /** + * Constructs a address view from the given input. + * Implicit construction is allowed only for address representation types. + * e.g. Address, evmc_address, and evmc::address + */ + template + explicit(not IsAddressType>::value) + constexpr View(R&& address) : data_(std::forward(address)) {} + + /** + * Returns the beginning constant iterator of the range + * @return the beginning constant iterator of the range + */ + constexpr auto begin() const { return data_.begin(); } + +private: + std::span data_; +}; + +/** + * This allows the standard algorithms to treat evmc_address as a range of contiguous bytes. + * @param addr the constant address representation + * @return the beginning constant iterator of the address + */ +constexpr const Byte* begin(const evmc_address& addr) { return addr.bytes; } + +/** + * This allows the standard algorithms to treat evmc_address as a range of contiguous bytes. + * @param addr the address representation + * @return the beginning iterator of the address + */ +constexpr Byte* begin(evmc_address& addr) { return addr.bytes; } + + +/** + * This allows the standard algorithms to treat evmc_address as a range of contiguous bytes. + * @param addr the address representation + * @return the sentinel constant iterator of the address + */ +constexpr const Byte* end(const evmc_address& addr) { return addr.bytes + ADDRESS_SIZE; } + +/** + * This allows the standard algorithms to treat evmc_address as a range of contiguous bytes. + * @param addr the address representation + * @return the sentinel iterator of the address + */ +constexpr Byte* end(evmc_address& addr) { return addr.bytes + ADDRESS_SIZE; } + +/** + * This allows the standard algorithms to treat evmc::address as a range of contiguous bytes. + * @param addr the constant address representation + * @return the beginning constant iterator of the address + */ +constexpr const Byte* begin(const evmc::address& addr) { return addr.bytes; } + +constexpr Byte* begin(evmc::address& addr) { return addr.bytes; } + +constexpr const Byte* end(const evmc::address& addr) { return addr.bytes + ADDRESS_SIZE; } + +constexpr Byte* end(evmc::address& addr) { return addr.bytes + ADDRESS_SIZE; } + +#endif // BDK_UTILS_ADDRESS_H diff --git a/src/utils/bytes.h b/src/utils/bytes.h new file mode 100644 index 00000000..d7126eae --- /dev/null +++ b/src/utils/bytes.h @@ -0,0 +1,10 @@ +#ifndef BDK_UTILS_BYTES_H +#define BDK_UTILS_BYTES_H + +#include + +using Byte = uint8_t; + +using Bytes = std::vector; + +#endif // BDK_UTILS_BYTES_H diff --git a/src/utils/bytesinterface.h b/src/utils/bytesinterface.h new file mode 100644 index 00000000..dd6dab1b --- /dev/null +++ b/src/utils/bytesinterface.h @@ -0,0 +1,165 @@ +#ifndef BDK_UTILS_BYTESINTERFACE_H +#define BDK_UTILS_BYTESINTERFACE_H + +#include +#include +#include + +#include "bytes/initializer.h" +#include "hex.h" + +/** + * CRTP helper class template for defining a range of bytes. + * + * Derived classes of static extent must only implement begin(). + * Derived classes of dynamic extent must also implement end(). + * + * This class will generate all common functions of bytes range, + * such as size(), data(), operator[], and comparison operators. + * + * This class is heavily based on std::ranges::view_interface. + */ +template +class BytesInterface { +public: + constexpr BytesInterface() = default; + + /** + * Helper constructor from bytes initializer. + */ + explicit constexpr BytesInterface(const bytes::Initializer auto& initializer) { + initializer.to(self()); + } + + /** + * Construction by copying another bytes range. + */ + explicit constexpr BytesInterface(const bytes::Range auto& data) + requires (N != std::dynamic_extent) { + if (const size_t size = std::ranges::size(data); size != N) + throw DynamicException("Given bytes range of size " + std::to_string(size) + + " is not suitable for initializing a FixedBytes<" + std::to_string(N) + ">"); + + std::ranges::copy(data, self().begin()); + } + + /** + * Default equality operator for ranges of bytes. + */ + friend constexpr bool operator==(const T& lhs, const T& rhs) { + return std::ranges::equal(lhs, rhs); + } + + + /** + * Default "space-ship" operator for ranges of bytes. This will generate + * operator<, operator<=, operator>, and operator>=. + */ + friend constexpr std::strong_ordering operator<=>(const T& lhs, const T& rhs) { + assert(std::ranges::size(lhs) == std::ranges::size(rhs)); + + const int r = std::memcmp(std::ranges::data(lhs), std::ranges::data(rhs), std::ranges::size(lhs)); + + if (r < 0) { + return std::strong_ordering::less; + } else if (r > 0) { + return std::strong_ordering::greater; + } else { + return std::strong_ordering::equivalent; + } + } + + /** + * @return constant beginning iterator of the range. + */ + constexpr auto cbegin() const { return self().begin(); } + + /** + * @return sentinel iterator of the range. + */ + constexpr auto end() requires (N != std::dynamic_extent) { return self().begin() + N; } + + /** + * @return sentinel iterator of the range. + */ + constexpr auto end() const requires (N != std::dynamic_extent) { return self().begin() + N; } + + /** + * @return sentinel constant iterator of the range. + */ + constexpr auto cend() const { return end(); } + + /** + * @return size of the range. + */ + constexpr auto size() const { return std::distance(self().begin(), self().end()); } + + /** + * @return pointer to the beginning of the range. + */ + constexpr auto data() { return std::to_address(self().begin()); } + + /** + * @return pointer to the beginning of the range. + */ + constexpr auto data() const { return std::to_address(self().begin()); } + + /** + * @return (usually) a reference to the element at given index. + */ + constexpr decltype(auto) operator[](size_t i) { return *(self().begin() + i); } + + /** + * @return (usually) a reference to the element at given index. + */ + constexpr decltype(auto) operator[](size_t i) const { return *(self().begin() + i); } + + /** + * @return false if all elements are 0, true otherwise. + */ + explicit constexpr operator bool() const { + return std::ranges::any_of(self(), [] (Byte b) { return b != 0; }); + } + + /** + * @return hexadecimal representation of the range + */ + Hex hex(bool strict = false) const { return Hex::fromBytes(self(), strict); } + + /** + * @param pos the starting position of the view + * @param len the length of the view + * @return a view from the given position and with the given length + * @throw out of range exception if given position or length are invalid + */ + constexpr bytes::View view(size_t pos, size_t len) const { + const size_t real_len = std::min(len, N - pos); + + if (pos + real_len > size()) { + throw std::out_of_range("len greater than size"); + } + + return bytes::View(self().begin() + pos, self().begin() + pos + real_len); + } + + constexpr bytes::View view(size_t pos) const { return view(pos, size()); } + + constexpr bytes::View view() const { return view(0); } + + /** + * Transforms the range to Bytes + */ + Bytes asBytes() const { + Bytes res; + res.resize(size()); + std::ranges::copy(self(), res.begin()); + return res; + } + + +private: + constexpr T& self() { return static_cast(*this); } + constexpr const T& self() const { return static_cast(*this); } +}; + +#endif // BDK_UTILS_BYTESINTERFACE_H diff --git a/src/utils/hex.h b/src/utils/hex.h index 3ed30d41..05d93869 100644 --- a/src/utils/hex.h +++ b/src/utils/hex.h @@ -20,9 +20,8 @@ See the LICENSE.txt file in the project root for more information. #include "dynamicexception.h" #include "bytes/view.h" +#include "bytes.h" -using Byte = uint8_t; -using Bytes = std::vector; template using BytesArr = std::array; diff --git a/src/utils/strings.cpp b/src/utils/strings.cpp index badc2a02..5d6f8f1b 100644 --- a/src/utils/strings.cpp +++ b/src/utils/strings.cpp @@ -45,55 +45,6 @@ Address::Address(const std::string_view add, bool inBytes) { } } -Address::Address(const evmc::address& data) { - // Copy the data from the evmc::address struct to this->data_ - std::copy(data.bytes, data.bytes + 20, this->begin()); -} - -evmc::address Address::toEvmcAddress() const { - evmc::address addr; - std::ranges::copy(*this, addr.bytes); - return addr; -} - -Address::Address(const evmc_address &data) { - // Same as evmc::address - std::copy(data.bytes, data.bytes + 20, this->begin()); -} - -Hex Address::toChksum() const { - // Hash requires lowercase address without "0x" - std::string str = Hex::fromBytes(*this, false).get(); - Hex hash = Utils::sha3(Utils::create_view_span(str)).hex(); - for (int i = 0; i < str.length(); i++) { - if (!std::isdigit(str[i])) { // Only check letters (A-F) - // If character hash is 8-F then make it uppercase - int nibble = std::stoi(hash.substr(i, 1), nullptr, 16); - str[i] = (nibble >= 8) ? std::toupper(str[i]) : std::tolower(str[i]); - } - } - str.insert(0, "0x"); - return Hex(str, true); -} - -bool Address::isValid(const std::string_view add, bool inBytes) { - if (inBytes) return (add.size() == 20); - if (add[0] == '0' && (add[1] == 'x' || add[1] == 'X')) { - return (add.size() == 42 && - add.substr(2).find_first_not_of("0123456789abcdefABCDEF") == std::string::npos - ); - } else { - return (add.size() == 40 && - add.find_first_not_of("0123456789abcdefABCDEF") == std::string::npos - ); - } -} - -bool Address::isChksum(const std::string_view add) { - Address myAdd(add, false); - return (add == std::string_view(myAdd.toChksum())); -} - StorageKey::StorageKey(const evmc::address& addr, const evmc::bytes32& slot) { // Copy the data from the evmc::address struct to this->data_ std::copy_n(addr.bytes, 20, this->begin()); diff --git a/src/utils/strings.h b/src/utils/strings.h index d7c3dcef..efaf7c0b 100644 --- a/src/utils/strings.h +++ b/src/utils/strings.h @@ -19,6 +19,8 @@ See the LICENSE.txt file in the project root for more information. #include "bytes/initializer.h" #include "zpp_bits.h" +#include "address.h" + // TODO: It is possible to implement **fast** operators for some types, // such as Address, Functor and Hash. Taking advantage that memory located within // the array are contiguous, we can cast the data to a pointer of native types @@ -181,62 +183,6 @@ class Signature : public FixedBytes<65> { uint8_t v() const; ///< Get the recovery ID (1 byte) of the signature. }; -/// Abstraction for a single 20-byte address (e.g. "1234567890abcdef..."). Inherits `FixedBytes<20>`. -class Address : public FixedBytes<20> { - public: - using FixedBytes<20>::FixedBytes; - using FixedBytes<20>::operator<; - using FixedBytes<20>::operator<=; - using FixedBytes<20>::operator>; - using FixedBytes<20>::operator>=; - using FixedBytes<20>::operator=; - - /** - * Constructor using a reference to evmc::address - * @param data The evmc::address pointer to convert into an address. - */ - Address(const evmc::address& data); - - /** - * Constructor using a reference to evmc_address - * @param data The evmc_address pointer to convert into an address. - */ - Address(const evmc_address& data); - - /** - * Copy constructor. - * @param add The address itself. - * @param inBytes If `true`, treats the input as a raw bytes string. - * @throw DynamicException if address has wrong size or is invalid. - */ - Address(const std::string_view add, bool inBytes); - - evmc::address toEvmcAddress() const; ///< Convert the address string back to an evmc::address. - - /** - * Convert the address to checksum format, as per [EIP-55](https://eips.ethereum.org/EIPS/eip-55). - * @return A copy of the checksummed address as a Hex object. - */ - Hex toChksum() const; - - /** - * Check if a given address string is valid. - * If the address has both upper *and* lowercase letters, will also check the checksum. - * @param add The address to be checked. - * @param inBytes If `true`, treats the input as a raw bytes string. - * @return `true` if the address is valid, `false` otherwise. - */ - static bool isValid(const std::string_view add, bool inBytes); - - /** - * Check if an address string is checksummed, as per [EIP-55](https://eips.ethereum.org/EIPS/eip-55). - * Uses `toChksum()` internally. Does not alter the original string. - * @param add The string to check. - * @return `true` if the address is checksummed, `false` otherwise. - */ - static bool isChksum(const std::string_view add); -}; - /// Abstraction of a EVM Storage key (20-bytes address + 32 bytes slot key). Inherits `FixedBytes<52>`. class StorageKey : public FixedBytes<52> { public: diff --git a/src/utils/tx.cpp b/src/utils/tx.cpp index a8bb0b90..c13f8067 100644 --- a/src/utils/tx.cpp +++ b/src/utils/tx.cpp @@ -6,6 +6,7 @@ See the LICENSE.txt file in the project root for more information. */ #include "tx.h" +#include "bytes/cast.h" TxBlock::TxBlock(const bytes::View bytes, const uint64_t&) { uint64_t index = 0; @@ -478,13 +479,13 @@ evmc_message TxBlock::txToMessage() const { msg.flags = 0; msg.depth = 1; msg.gas = static_cast(this->gasLimit_); - msg.recipient = this->to_.toEvmcAddress(); - msg.sender = this->from_.toEvmcAddress(); + msg.recipient = bytes::cast(this->to_); + msg.sender = bytes::cast(this->from_); msg.input_data = (this->data_.empty()) ? nullptr : this->data_.data(); msg.input_size = this->data_.size(); msg.value = Utils::uint256ToEvmcUint256(this->value_); msg.create2_salt = {}; - msg.code_address = this->to_.toEvmcAddress(); + msg.code_address = bytes::cast(this->to_); return msg; } diff --git a/src/utils/utils.h b/src/utils/utils.h index 6e268fd3..5edc2fba 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -33,6 +33,7 @@ See the LICENSE.txt file in the project root for more information. #include "strings.h" #include "logger.h" +#include "bytes.h" #include "../libs/zpp_bits.h" #include "../libs/json.hpp" @@ -58,8 +59,6 @@ using json = nlohmann::ordered_json; /// Typedef for bigint. using bigint = boost::multiprecision::number>; -using Byte = uint8_t; ///< Typedef for Byte. -using Bytes = std::vector; ///< Typedef for Bytes. template using BytesArr = std::array; ///< Typedef for BytesArr. // Base case for the recursive helper - now using requires for an empty body function diff --git a/src/utils/view.h b/src/utils/view.h new file mode 100644 index 00000000..f0f7a2e8 --- /dev/null +++ b/src/utils/view.h @@ -0,0 +1,24 @@ +#ifndef BDK_UTILS_VIEW_H +#define BDK_UTILS_VIEW_H + +#include +#include +#include "bytes.h" + +/** + * Base class template for defining a view of a type. + * Specific view types must specialize it. + */ +template +struct View; + +/** + * Most generic purpose view specialization. + * Behaves just like a span of constant bytes. + */ +template<> +struct View : std::span { + using std::span::span; +}; + +#endif // BDK_UTILS_VIEW_H diff --git a/tests/core/state.cpp b/tests/core/state.cpp index 676bb434..b2e90053 100644 --- a/tests/core/state.cpp +++ b/tests/core/state.cpp @@ -53,13 +53,13 @@ std::pair buildCallInfo(const Address& addressToCall, const callFlags = 0; callDepth = 1; callGas = 100000000; - callRecipient = addressToCall.toEvmcAddress(); + callRecipient = bytes::cast(addressToCall); callSender = {}; callInputData = messageBytes.data(); callInputSize = messageBytes.size(); callValue = {}; callCreate2Salt = {}; - callCodeAddress = addressToCall.toEvmcAddress(); + callCodeAddress = bytes::cast(addressToCall); return callInfo; } diff --git a/tests/sdktestsuite.hpp b/tests/sdktestsuite.hpp index 8ef39714..abb2d24e 100644 --- a/tests/sdktestsuite.hpp +++ b/tests/sdktestsuite.hpp @@ -334,8 +334,8 @@ class SDKTestSuite { callFlags = 0; callDepth = 1; callGas = 1000000000; - callRecipient = to.toEvmcAddress(); - callSender = from.address.toEvmcAddress(); + callRecipient = bytes::cast(to); + callSender = bytes::cast(from.address); callInputData = data.data(); callInputSize = data.size(); callValue = Utils::uint256ToEvmcUint256(value); @@ -781,13 +781,13 @@ class SDKTestSuite { callFlags = 0; callDepth = 1; callGas = 10000000; - callRecipient = contractAddress.toEvmcAddress(); - callSender = this->getChainOwnerAccount().address.toEvmcAddress(); + callRecipient = bytes::cast(contractAddress); + callSender = bytes::cast(this->getChainOwnerAccount().address); callInputData = fullData.data(); callInputSize = fullData.size(); callValue = Utils::uint256ToEvmcUint256(0); callCreate2Salt = {}; - callCodeAddress = contractAddress.toEvmcAddress(); + callCodeAddress = bytes::cast(contractAddress); return std::get<0>(ABI::Decoder::decodeData(this->state_.ethCall(callData))); } @@ -829,8 +829,8 @@ class SDKTestSuite { callFlags = 0; callDepth = 1; callGas = 10000000; - callRecipient = contractAddress.toEvmcAddress(); - callSender = this->getChainOwnerAccount().address.toEvmcAddress(); + callRecipient = bytes::cast(contractAddress); + callSender = bytes::cast(this->getChainOwnerAccount().address); callInputData = fullData.data(); callInputSize = fullData.size(); callValue = Utils::uint256ToEvmcUint256(0); diff --git a/tests/utils/strings.cpp b/tests/utils/strings.cpp index b980881b..0292c118 100644 --- a/tests/utils/strings.cpp +++ b/tests/utils/strings.cpp @@ -195,7 +195,7 @@ namespace TAddress { SECTION("Address toChksum") { Address inputAddress(std::string("0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359"), false); - std::string inputChecksum = inputAddress.toChksum(); + std::string inputChecksum = Address::checksum(inputAddress); Address outputAddress(inputChecksum, false); Address expectedOutputAddress(std::string("0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359"), false); REQUIRE(outputAddress == expectedOutputAddress); From 7c462b63d7366484ace388497db2f1d39a8954c5 Mon Sep 17 00:00:00 2001 From: Leonardo Amaral Date: Tue, 29 Oct 2024 15:24:30 -0300 Subject: [PATCH 02/37] view of hash --- src/bytes/join.h | 79 +++++++++-------- src/bytes/random.h | 41 +++++++++ src/contract/contracthost.cpp | 6 +- src/core/consensus.cpp | 3 +- src/core/state.cpp | 7 +- src/utils/CMakeLists.txt | 1 + src/utils/hash.cpp | 12 +++ src/utils/hash.h | 107 +++++++++++++++++++++++ src/utils/randomgen.cpp | 2 +- src/utils/randomgen.h | 2 +- src/utils/safehash.h | 4 + src/utils/strings.cpp | 20 ----- src/utils/strings.h | 36 +------- tests/benchmark/erc20.cpp | 4 +- tests/benchmark/erc721.cpp | 4 +- tests/benchmark/snailtracer.cpp | 4 +- tests/benchmark/snailtraceroptimized.cpp | 4 +- tests/benchmark/uniswapv2.cpp | 2 +- tests/blockchainwrapper.hpp | 3 +- tests/contract/createcontract.cpp | 2 +- tests/contract/pebble.cpp | 2 +- tests/core/rdpos.cpp | 4 +- tests/core/storage.cpp | 9 +- tests/sdktestsuite.hpp | 3 +- tests/utils/block.cpp | 23 ++--- tests/utils/db.cpp | 5 +- tests/utils/merkle.cpp | 9 +- tests/utils/randomgen.cpp | 11 +-- tests/utils/strings.cpp | 7 +- tests/utils/tx_throw.cpp | 5 +- 30 files changed, 274 insertions(+), 147 deletions(-) create mode 100644 src/bytes/random.h create mode 100644 src/utils/hash.cpp create mode 100644 src/utils/hash.h diff --git a/src/bytes/join.h b/src/bytes/join.h index cd5ed6ac..a6c7d37f 100644 --- a/src/bytes/join.h +++ b/src/bytes/join.h @@ -8,43 +8,48 @@ #include "utils/dynamicexception.h" namespace bytes { - namespace detail { - std::size_t joinedSize(const SizedInitializer auto& arg) { - return arg.size(); - } - - std::size_t joinedSize(const DataRange auto& arg) { - return std::ranges::size(arg); - } - - std::size_t joinedSize(const auto& arg, const auto&... args) { - return joinedSize(arg) + joinedSize(args...); - } - - Byte* joinImpl(Byte *dest, const SizedInitializer auto& init) { - init.to(dest); - return dest + init.size(); - } - - Byte* joinImpl(Byte *dest, const DataRange auto& range) { - std::memcpy(dest, std::ranges::data(range), std::ranges::size(range)); - return dest + std::ranges::size(range); - } - - Byte* joinImpl(Byte *dest, const auto& arg, const auto&... args) { - return joinImpl(joinImpl(dest, arg), args...); - } - } // namespace detail - - template SizedInitializer auto join(Ts&&... args) { - const size_t size = detail::joinedSize(args...); - - auto func = [args_ = std::tuple(std::forward(args)...)] (Byte *dest) { - std::apply(detail::joinImpl, std::tuple_cat(std::make_tuple(dest), std::tuple(args_))); - }; - - return makeInitializer(size, std::move(func)); - } + +namespace detail { + +std::size_t joinedSize(const SizedInitializer auto& arg) { + return arg.size(); +} + +std::size_t joinedSize(const DataRange auto& arg) { + return std::ranges::size(arg); +} + +std::size_t joinedSize(const auto& arg, const auto&... args) { + return joinedSize(arg) + joinedSize(args...); +} + +Byte* joinImpl(Byte *dest, const SizedInitializer auto& init) { + init.to(dest); + return dest + init.size(); +} + +Byte* joinImpl(Byte *dest, const DataRange auto& range) { + std::memcpy(dest, std::ranges::data(range), std::ranges::size(range)); + return dest + std::ranges::size(range); +} + +Byte* joinImpl(Byte *dest, const auto& arg, const auto&... args) { + return joinImpl(joinImpl(dest, arg), args...); +} + +} // namespace detail + +template +SizedInitializer auto join(Ts&&... args) { + const size_t size = detail::joinedSize(args...); + + auto func = [args_ = std::tuple(std::forward(args)...)] (Byte *dest) { + std::apply(detail::joinImpl, std::tuple_cat(std::make_tuple(dest), std::tuple(args_))); + }; + + return makeInitializer(size, std::move(func)); +} + } // namespace bytes #endif // BYTES_JOIN_H diff --git a/src/bytes/random.h b/src/bytes/random.h new file mode 100644 index 00000000..2fde403e --- /dev/null +++ b/src/bytes/random.h @@ -0,0 +1,41 @@ +#ifndef BDK_BYTES_RANDOM_H +#define BDK_BYTES_RANDOM_H + +#include "range.h" +#include "initializer.h" + +#include + +namespace bytes { + +/** + * Creates an initializer of random bytes. + * The number of generated bytes exactly + * matches the size of the target bytes range. + * + * For example: + * Hash hash = bytes::random(); // generates 32 random bytes + * Address addr = bytes::random(); // generates 20 random bytes + * + * @return a random bytes initializer + */ +constexpr Initializer auto random() { + return makeInitializer([] (Span span) { + ::RAND_bytes(span.data(), span.size()); + }); +} + +/** + * Creates an random bytes initializer of the given size. + * + * @return a sized initializer of random bytes + */ +constexpr SizedInitializer auto random(size_t size) { + return makeInitializer(size, [size] (Byte* ptr) { + ::RAND_bytes(ptr, size); + }); +} + +} // namespace bytes + +#endif // BDK_BYTES_RANDOM_H diff --git a/src/contract/contracthost.cpp b/src/contract/contracthost.cpp index 656d8319..9fce1a08 100644 --- a/src/contract/contracthost.cpp +++ b/src/contract/contracthost.cpp @@ -443,7 +443,7 @@ evmc::bytes32 ContractHost::get_storage(const evmc::address& addr, try { auto it = vmStorage_.find(storageKey); if (it != vmStorage_.end()) - return it->second.toEvmcBytes32(); + return bytes::cast(it->second); } catch (const std::exception& e) { this->evmcThrows_.emplace_back(e.what()); this->evmcThrow_ = true; @@ -501,7 +501,7 @@ evmc::bytes32 ContractHost::get_code_hash(const evmc::address& addr) const noexc try { auto it = accounts_.find(Address(addr)); if (it != accounts_.end()) { - return it->second->codeHash.toEvmcBytes32(); + return bytes::cast(it->second->codeHash); } } catch (const std::exception& e) { this->evmcThrows_.emplace_back(e.what()); @@ -757,7 +757,7 @@ evmc::bytes32 ContractHost::get_transient_storage(const evmc::address &addr, try { auto it = transientStorage_.find(storageKey); if (it != transientStorage_.end()) { - return it->second.toEvmcBytes32(); + return bytes::cast(it->second); } } catch (const std::exception& e) { this->evmcThrows_.emplace_back(e.what()); diff --git a/src/core/consensus.cpp b/src/core/consensus.cpp index d343af89..2645ff60 100644 --- a/src/core/consensus.cpp +++ b/src/core/consensus.cpp @@ -7,6 +7,7 @@ See the LICENSE.txt file in the project root for more information. #include "consensus.h" #include "blockchain.h" +#include "bytes/random.h" void Consensus::validatorLoop() { LOGINFO("Starting validator loop."); @@ -177,7 +178,7 @@ void Consensus::doValidatorBlock() { } void Consensus::doValidatorTx(const uint64_t& nHeight, const Validator& me) { - Hash randomness = Hash::random(); + Hash randomness = bytes::random(); Hash randomHash = Utils::sha3(randomness); LOGDEBUG("Creating random Hash transaction"); Bytes randomHashBytes = Hex::toBytes("0xcfffe746"); diff --git a/src/core/state.cpp b/src/core/state.cpp index e563c7c9..34a890c1 100644 --- a/src/core/state.cpp +++ b/src/core/state.cpp @@ -8,6 +8,7 @@ See the LICENSE.txt file in the project root for more information. #include "state.h" #include #include "../contract/contracthost.h" +#include "bytes/random.h" State::State( const DB& db, @@ -219,7 +220,7 @@ void State::processTransaction( txContext.blob_base_fee = {}; txContext.blob_hashes = nullptr; txContext.blob_hashes_count = 0; - Hash randomSeed(Utils::uint256ToBytes((randomnessHash.toUint256() + txIndex))); + Hash randomSeed(Utils::uint256ToBytes((static_cast(randomnessHash) + txIndex))); ContractHost host( this->vm_, this->dumpManager_, @@ -457,7 +458,7 @@ Bytes State::ethCall(const evmc_message& callInfo) { txContext.blob_hashes = nullptr; txContext.blob_hashes_count = 0; // As we are simulating, the randomSeed can be anything - Hash randomSeed = Hash::random(); + Hash randomSeed = bytes::random(); return ContractHost( this->vm_, this->dumpManager_, @@ -488,7 +489,7 @@ int64_t State::estimateGas(const evmc_message& callInfo) { } int64_t leftOverGas = callInfo.gas; - Hash randomSeed = Hash::random(); + Hash randomSeed = bytes::random(); ContractHost( this->vm_, this->dumpManager_, diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index cc65f02a..52330730 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -19,6 +19,7 @@ set(UTILS_HEADERS set(UTILS_SOURCES ${CMAKE_SOURCE_DIR}/src/utils/db.cpp ${CMAKE_SOURCE_DIR}/src/utils/address.cpp + ${CMAKE_SOURCE_DIR}/src/utils/hash.cpp ${CMAKE_SOURCE_DIR}/src/utils/utils.cpp ${CMAKE_SOURCE_DIR}/src/utils/strings.cpp ${CMAKE_SOURCE_DIR}/src/utils/hex.cpp diff --git a/src/utils/hash.cpp b/src/utils/hash.cpp new file mode 100644 index 00000000..1f6db3b3 --- /dev/null +++ b/src/utils/hash.cpp @@ -0,0 +1,12 @@ +#include "hash.h" +#include "utils.h" + +Hash::Hash(const uint256_t& value) : Hash(Utils::uint256ToBytes(value)) {} + +Hash::operator uint256_t() const { + return Utils::bytesToUint256(*this); +} + +View::operator uint256_t() const { + return Utils::bytesToUint256(*this); +} \ No newline at end of file diff --git a/src/utils/hash.h b/src/utils/hash.h new file mode 100644 index 00000000..4c190cac --- /dev/null +++ b/src/utils/hash.h @@ -0,0 +1,107 @@ +#ifndef BDK_UTILS_HASH_H +#define BDK_UTILS_HASH_H + +#include "bytes/range.h" +#include "bytesinterface.h" +#include "evmc/evmc.hpp" +#include "view.h" +#include "zpp_bits.h" + +inline constexpr size_t HASH_SIZE = 32; + +class Hash : public BytesInterface { +public: + + constexpr Hash() : data_() {} + + template + requires std::constructible_from, I&&> + explicit (not bytes::Initializer) + constexpr Hash(I&& input) : BytesInterface(std::forward(input)) {} + + explicit Hash(const uint256_t& value); + + explicit operator uint256_t() const; + + constexpr auto begin() { return data_.begin(); } + + constexpr auto begin() const { return data_.begin(); } + +private: + friend zpp::bits::access; + using serialize = zpp::bits::members<1>; + + std::array data_; +}; + +template +struct is_hash_representation : std::is_same {}; + +template<> +struct is_hash_representation : std::true_type {}; + +template<> +struct is_hash_representation : std::true_type {}; + +template +inline constexpr bool is_hash_representation_v = is_hash_representation::value; + +template<> +class View : public BytesInterface, HASH_SIZE> { +public: + + template + explicit(not is_hash_representation_v>) + constexpr View(R&& input) : data_(std::forward(input)) {} + + constexpr auto begin() const { return data_.begin(); } + + explicit operator uint256_t() const; + +private: + std::span data_; +}; + +/** + * This allows the standard algorithms to treat evmc_bytes32 as a range of contiguous bytes. + * @param hash the constant address representation + * @return the beginning constant iterator of the address + */ +constexpr const Byte* begin(const evmc_bytes32& hash) { return hash.bytes; } + +/** + * This allows the standard algorithms to treat evmc_bytes32 as a range of contiguous bytes. + * @param hash the address representation + * @return the beginning iterator of the address + */ +constexpr Byte* begin(evmc_bytes32& hash) { return hash.bytes; } + + +/** + * This allows the standard algorithms to treat evmc_bytes32 as a range of contiguous bytes. + * @param hash the address representation + * @return the sentinel constant iterator of the address + */ +constexpr const Byte* end(const evmc_bytes32& hash) { return hash.bytes + HASH_SIZE; } + +/** + * This allows the standard algorithms to treat evmc_bytes32 as a range of contiguous bytes. + * @param hash the address representation + * @return the sentinel iterator of the address + */ +constexpr Byte* end(evmc_bytes32& hash) { return hash.bytes + HASH_SIZE; } + +/** + * This allows the standard algorithms to treat evmc::bytes32 as a range of contiguous bytes. + * @param hash the constant address representation + * @return the beginning constant iterator of the address + */ +constexpr const Byte* begin(const evmc::bytes32& hash) { return hash.bytes; } + +constexpr Byte* begin(evmc::bytes32& hash) { return hash.bytes; } + +constexpr const Byte* end(const evmc::bytes32& hash) { return hash.bytes + HASH_SIZE; } + +constexpr Byte* end(evmc::bytes32& hash) { return hash.bytes + HASH_SIZE; } + +#endif // BDK_UTILS_HASH_H diff --git a/src/utils/randomgen.cpp b/src/utils/randomgen.cpp index 7234bbc1..231f54b4 100644 --- a/src/utils/randomgen.cpp +++ b/src/utils/randomgen.cpp @@ -10,7 +10,7 @@ See the LICENSE.txt file in the project root for more information. uint256_t RandomGen::operator()() { std::lock_guard lock(this->seedLock_); this->seed_ = Utils::sha3(this->seed_); - uint256_t ret = this->seed_.toUint256(); + uint256_t ret = static_cast(this->seed_); return ret; } diff --git a/src/utils/randomgen.h b/src/utils/randomgen.h index f345355f..5563f380 100644 --- a/src/utils/randomgen.h +++ b/src/utils/randomgen.h @@ -58,7 +58,7 @@ class RandomGen { for (uint64_t i = 0; i < v.size(); ++i) { this->seed_ = Utils::sha3(this->seed_); // Print seed as hex here if you want to debug it - uint64_t n = uint64_t(i + this->seed_.toUint256() % (v.size() - i)); + uint64_t n = uint64_t(i + static_cast(this->seed_) % (v.size() - i)); std::swap(v[n], v[i]); } } diff --git a/src/utils/safehash.h b/src/utils/safehash.h index a07ca48a..9199e3c6 100644 --- a/src/utils/safehash.h +++ b/src/utils/safehash.h @@ -29,6 +29,10 @@ struct SafeHash { return wyhash(std::bit_cast(&i), sizeof(i), 0, _wyp); } + size_t operator()(const uint256_t& i) const { + return (*this)(Hash(i)); + } + size_t operator()(const std::string& str) const { return wyhash(str.c_str(), str.size(), 0, _wyp); } diff --git a/src/utils/strings.cpp b/src/utils/strings.cpp index 5d6f8f1b..1dcf3e29 100644 --- a/src/utils/strings.cpp +++ b/src/utils/strings.cpp @@ -8,26 +8,6 @@ See the LICENSE.txt file in the project root for more information. #include "strings.h" #include "utils.h" -Hash::Hash(const uint256_t& data) : FixedBytes<32>(Utils::uint256ToBytes(data)) {}; - -Hash::Hash(const std::string_view sv) { - if (sv.size() != 32) throw std::invalid_argument("Hash must be 32 bytes long."); - std::ranges::copy(sv, this->begin()); -} - -Hash::Hash(const evmc::bytes32& data) { - // Copy the data from the evmc::bytes32 struct to this->data_ - std::copy(data.bytes, data.bytes + 32, this->begin()); -} - -uint256_t Hash::toUint256() const { return Utils::bytesToUint256(*this); } - -evmc::bytes32 Hash::toEvmcBytes32() const { - evmc::bytes32 bytes; - std::memcpy(bytes.bytes, this->data(), 32); - return bytes; -} - uint256_t Signature::r() const { return Utils::bytesToUint256(this->view(0, 32)); } uint256_t Signature::s() const { return Utils::bytesToUint256(this->view(32, 32)); } diff --git a/src/utils/strings.h b/src/utils/strings.h index efaf7c0b..ea267305 100644 --- a/src/utils/strings.h +++ b/src/utils/strings.h @@ -20,6 +20,7 @@ See the LICENSE.txt file in the project root for more information. #include "zpp_bits.h" #include "address.h" +#include "hash.h" // TODO: It is possible to implement **fast** operators for some types, // such as Address, Functor and Hash. Taking advantage that memory located within @@ -133,41 +134,6 @@ template class FixedBytes { } }; -/// Abstraction of a 32-byte hash. Inherits `FixedBytes<32>`. -class Hash : public FixedBytes<32> { - public: - using FixedBytes<32>::FixedBytes; - using FixedBytes<32>::operator=; - using FixedBytes<32>::operator>=; - using FixedBytes<32>::operator<=; - using FixedBytes<32>::operator>; - using FixedBytes<32>::operator<; - - /** - * Constructor using uint256_t. - * @param data The unsigned 256-bit number to convert into a hash string. - */ - Hash(const uint256_t& data); - - /** - * Constructor using a reference to evmc::bytes32. - * @param data The evmc::bytes32 pointer to convert into a hash string. - */ - Hash(const evmc::bytes32& data); - - /** - * Constructor using string_view. - * @param sv The string view to convert into a hash string. - */ - Hash(const std::string_view sv); - - uint256_t toUint256() const; ///< Convert the hash string back to an unsigned 256-bit number. - evmc::bytes32 toEvmcBytes32() const; ///< Convert the hash string back to an evmc::bytes32 pointer. - - /// Generate a CRYPTOGRAPHICALLY SECURE random 32-byte/256-bit hash. - inline static Hash random() { Hash h; RAND_bytes(h.data(), 32); return h; } -}; - /// Abstraction of a functor (the first 4 bytes of a function's keccak hash). struct Functor { uint32_t value = 0; diff --git a/tests/benchmark/erc20.cpp b/tests/benchmark/erc20.cpp index bf21ba1e..3d507751 100644 --- a/tests/benchmark/erc20.cpp +++ b/tests/benchmark/erc20.cpp @@ -70,7 +70,7 @@ namespace TERC20BENCHMARK { txContext.blob_hashes_count = 0; auto callInfo = transferTx.txToMessage(); - Hash randomnessHash = Hash::random(); + Hash randomnessHash = bytes::random(); int64_t leftOverGas = std::numeric_limits::max(); uint64_t iterations = 2500000; @@ -132,7 +132,7 @@ namespace TERC20BENCHMARK { txContext.blob_hashes_count = 0; auto callInfo = transferTx.txToMessage(); - Hash randomnessHash = Hash::random(); + Hash randomnessHash = bytes::random(); int64_t leftOverGas = std::numeric_limits::max(); uint64_t iterations = 250000; diff --git a/tests/benchmark/erc721.cpp b/tests/benchmark/erc721.cpp index 3b5c21ab..9cfa01c9 100644 --- a/tests/benchmark/erc721.cpp +++ b/tests/benchmark/erc721.cpp @@ -74,7 +74,7 @@ namespace TERC721BENCHMARK { txContext.blob_hashes_count = 0; auto callInfo = transferTx.txToMessage(); - Hash randomnessHash = Hash::random(); + Hash randomnessHash = bytes::random(); int64_t leftOverGas = std::numeric_limits::max(); uint64_t iterations = 1000000; @@ -125,7 +125,7 @@ namespace TERC721BENCHMARK { txContext.blob_hashes_count = 0; auto callInfo = transferTx.txToMessage(); - Hash randomnessHash = Hash::random(); + Hash randomnessHash = bytes::random(); int64_t leftOverGas = std::numeric_limits::max(); uint64_t iterations = 100000; diff --git a/tests/benchmark/snailtracer.cpp b/tests/benchmark/snailtracer.cpp index 3d76cd67..7c0c6076 100644 --- a/tests/benchmark/snailtracer.cpp +++ b/tests/benchmark/snailtracer.cpp @@ -53,7 +53,7 @@ namespace TSNAILTRACERBENCHMARK { txContext.blob_hashes_count = 0; auto callInfo = benchmarkTx.txToMessage(); - Hash randomnessHash = Hash::random(); + Hash randomnessHash = bytes::random(); int64_t leftOverGas = std::numeric_limits::max(); auto start = std::chrono::high_resolution_clock::now(); @@ -98,7 +98,7 @@ namespace TSNAILTRACERBENCHMARK { txContext.blob_hashes_count = 0; auto callInfo = benchmarkTx.txToMessage(); - Hash randomnessHash = Hash::random(); + Hash randomnessHash = bytes::random(); int64_t leftOverGas = std::numeric_limits::max(); auto start = std::chrono::high_resolution_clock::now(); diff --git a/tests/benchmark/snailtraceroptimized.cpp b/tests/benchmark/snailtraceroptimized.cpp index c2abc662..d00ca372 100644 --- a/tests/benchmark/snailtraceroptimized.cpp +++ b/tests/benchmark/snailtraceroptimized.cpp @@ -53,7 +53,7 @@ namespace TSNAILTRACEROPTIMIZEDBENCHMARK { txContext.blob_hashes_count = 0; auto callInfo = benchmarkTx.txToMessage(); - Hash randomnessHash = Hash::random(); + Hash randomnessHash = bytes::random(); int64_t leftOverGas = std::numeric_limits::max(); auto start = std::chrono::high_resolution_clock::now(); @@ -98,7 +98,7 @@ namespace TSNAILTRACEROPTIMIZEDBENCHMARK { txContext.blob_hashes_count = 0; auto callInfo = benchmarkTx.txToMessage(); - Hash randomnessHash = Hash::random(); + Hash randomnessHash = bytes::random(); int64_t leftOverGas = std::numeric_limits::max(); auto start = std::chrono::high_resolution_clock::now(); diff --git a/tests/benchmark/uniswapv2.cpp b/tests/benchmark/uniswapv2.cpp index 0a2f0eb6..8ed4ce48 100644 --- a/tests/benchmark/uniswapv2.cpp +++ b/tests/benchmark/uniswapv2.cpp @@ -83,7 +83,7 @@ namespace TDEXV2 { txContext.blob_hashes_count = 0; auto callInfo = tx.txToMessage(); - Hash randomnessHash = Hash::random(); + Hash randomnessHash = bytes::random(); int64_t leftOverGas = std::numeric_limits::max(); uint64_t iterations = 250000; diff --git a/tests/blockchainwrapper.hpp b/tests/blockchainwrapper.hpp index c69ba79b..52064248 100644 --- a/tests/blockchainwrapper.hpp +++ b/tests/blockchainwrapper.hpp @@ -16,6 +16,7 @@ See the LICENSE.txt file in the project root for more information. #include "../src/utils/db.h" #include "../src/core/blockchain.h" #include "../src/utils/utils.h" +#include "bytes/random.h" /** * Simple wrapper struct for management of all blockchain related objects. @@ -174,7 +175,7 @@ inline FinalizedBlock createValidBlock(const std::vector& validatorPrivKey std::vector randomHashTxs; std::vector randomTxs; - std::vector randomSeeds(orderedPrivKeys.size(), Hash::random()); + std::vector randomSeeds(orderedPrivKeys.size(), bytes::random()); for (uint64_t i = 0; i < orderedPrivKeys.size(); ++i) { Address validatorAddress = Secp256k1::toAddress(Secp256k1::toUPub(orderedPrivKeys[i])); Bytes hashTxData = Hex::toBytes("0xcfffe746"); diff --git a/tests/contract/createcontract.cpp b/tests/contract/createcontract.cpp index 8d6ca641..42050f76 100644 --- a/tests/contract/createcontract.cpp +++ b/tests/contract/createcontract.cpp @@ -88,7 +88,7 @@ namespace TContractRandomness { SECTION("EVM Create Another EVM Contract Test") { auto sdk = SDKTestSuite::createNewEnvironment("ContractCreateContract"); auto createContractAddress = sdk.deployBytecode(contractCreateAnotherContractBytecode); - auto salt = Hash::random(); + Hash salt = bytes::random(); REQUIRE(sdk.getState().getEvmContracts().size() == 1); sdk.callFunction(createContractAddress, &SolCreateContract::deployWithNew, uint256_t(100)); Address newContractAddress = Address(); diff --git a/tests/contract/pebble.cpp b/tests/contract/pebble.cpp index ee6309c4..0970df0a 100644 --- a/tests/contract/pebble.cpp +++ b/tests/contract/pebble.cpp @@ -52,7 +52,7 @@ namespace TPEBBLE { // Derive the same randomness as the one generated to create the rarity // then check against the rarity inside the event. auto latestBlock = sdk.getStorage().latest(); - auto latestRandomness = latestBlock->getBlockRandomness().toUint256(); + auto latestRandomness = static_cast(latestBlock->getBlockRandomness()); auto expectedRarity = sdk.callViewFunction(pebbleAddr, &Pebble::determineRarity, latestRandomness); REQUIRE(std::get<2>(event) == expectedRarity); REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::totalSupply) == uint256_t(1)); diff --git a/tests/core/rdpos.cpp b/tests/core/rdpos.cpp index 7fbf73f4..640b0f45 100644 --- a/tests/core/rdpos.cpp +++ b/tests/core/rdpos.cpp @@ -188,7 +188,7 @@ namespace TRdPoS { uint64_t newBlocknHeight = blockchainWrapper1.storage.latest()->getNHeight() + 1; std::vector txValidators; - std::vector randomSeeds(orderedPrivKeys.size(), Hash::random()); + std::vector randomSeeds(orderedPrivKeys.size(), bytes::random()); for (uint64_t i = 0; i < orderedPrivKeys.size(); ++i) { Address validatorAddress = Secp256k1::toAddress(Secp256k1::toUPub(orderedPrivKeys[i])); Bytes hashTxData = Hex::toBytes("0xcfffe746"); @@ -439,7 +439,7 @@ namespace TRdPoS { // Create a block with 8 TxValidator transactions, 2 for each validator, in order (randomHash and random) uint64_t newBlocknHeight = blockchainWrapper1.storage.latest()->getNHeight() + 1; std::vector txValidators; - std::vector randomSeeds(orderedPrivKeys.size(), Hash::random()); + std::vector randomSeeds(orderedPrivKeys.size(), bytes::random()); for (uint64_t i = 0; i < orderedPrivKeys.size(); ++i) { Address validatorAddress = Secp256k1::toAddress(Secp256k1::toUPub(orderedPrivKeys[i])); Bytes hashTxData = Hex::toBytes("0xcfffe746"); diff --git a/tests/core/storage.cpp b/tests/core/storage.cpp index db5faded..fefb363f 100644 --- a/tests/core/storage.cpp +++ b/tests/core/storage.cpp @@ -10,6 +10,7 @@ See the LICENSE.txt file in the project root for more information. #include "../../src/utils/db.h" #include "../../src/utils/options.h" #include "../blockchainwrapper.hpp" +#include "bytes/random.h" #include #include @@ -35,7 +36,7 @@ TestBlockchainWrapper initialize(const std::vector& validatorPrivKeys, // Random transaction TxBlock createRandomTx(const uint64_t& requiredChainId) { - PrivKey txPrivKey = PrivKey::random(); + PrivKey txPrivKey = bytes::random(); Address from = Secp256k1::toAddress(Secp256k1::toUPub(txPrivKey)); Address to(Utils::randBytes(20)); Bytes data = Utils::randBytes(32); @@ -55,10 +56,10 @@ std::pair, Bytes> createRandomTxValidatorList(uint64_t Bytes randomnessStr; ret.first.reserve(N); - std::vector seeds(N, Hash::random()); + std::vector seeds(N, bytes::random()); for (const auto& seed : seeds) { Utils::appendBytes(ret.second, seed); - PrivKey txValidatorPrivKey = PrivKey::random(); + PrivKey txValidatorPrivKey = bytes::random(); Address validatorAddress = Secp256k1::toAddress(Secp256k1::toUPub(txValidatorPrivKey)); Bytes hashTxData = Hex::toBytes("0xcfffe746"); Utils::appendBytes(hashTxData, Utils::sha3(seed)); @@ -84,7 +85,7 @@ std::pair, Bytes> createRandomTxValidatorList(uint64_t } FinalizedBlock createRandomBlock(uint64_t txCount, uint64_t validatorCount, uint64_t nHeight, Hash prevHash, const uint64_t& requiredChainId) { - PrivKey blockValidatorPrivKey = PrivKey::random(); + PrivKey blockValidatorPrivKey = bytes::random(); uint64_t timestamp = 230915972837111; // Timestamp doesn't really matter. std::vector txs; diff --git a/tests/sdktestsuite.hpp b/tests/sdktestsuite.hpp index abb2d24e..7872c3b5 100644 --- a/tests/sdktestsuite.hpp +++ b/tests/sdktestsuite.hpp @@ -19,6 +19,7 @@ See the LICENSE.txt file in the project root for more information. #include "../src/utils/utils.h" #include "contract/contracthost.h" #include "statetest.hpp" +#include "bytes/random.h" /// Wrapper struct for accounts used within the SDKTestSuite. @@ -251,7 +252,7 @@ class SDKTestSuite { std::vector randomHashTxs; std::vector randomTxs; - std::vector randomSeeds(orderedPrivKeys.size(), Hash::random()); + std::vector randomSeeds(orderedPrivKeys.size(), bytes::random()); for (uint64_t i = 0; i < orderedPrivKeys.size(); ++i) { Address validatorAddress = Secp256k1::toAddress(Secp256k1::toUPub(orderedPrivKeys[i])); Bytes hashTxData = Hex::toBytes("0xcfffe746"); diff --git a/tests/utils/block.cpp b/tests/utils/block.cpp index c9a3e841..902434a0 100644 --- a/tests/utils/block.cpp +++ b/tests/utils/block.cpp @@ -9,6 +9,7 @@ See the LICENSE.txt file in the project root for more information. #include "../../src/utils/utils.h" #include "../../src/utils/tx.h" #include "../../src/utils/finalizedblock.h" +#include "bytes/random.h" using Catch::Matchers::Equals; @@ -79,7 +80,7 @@ namespace TBlock { for (uint64_t i = 0; i < 64; i++) txs.emplace_back(tx); // Create and append 8 - std::vector randomSeeds(8, Hash::random()); + std::vector randomSeeds(8, bytes::random()); Bytes randomSeed; // Concatenated random seed of block. for (const auto &seed : randomSeeds) randomSeed.insert(randomSeed.end(), seed.begin(), seed.end()); @@ -140,15 +141,15 @@ namespace TBlock { SECTION("Block with 500 dynamically created transactions and 64 dynamically created validator transactions") { // There is 16 TxValidator transactions, but only 8 of them are used for block randomness. - PrivKey blockValidatorPrivKey = PrivKey::random(); - Hash nPrevBlockHash = Hash::random(); + PrivKey blockValidatorPrivKey = bytes::random(); + Hash nPrevBlockHash = bytes::random(); uint64_t timestamp = 64545214244; uint64_t nHeight = 6414363551; std::vector txs; for (uint64_t i = 0; i < 500; ++i) { - PrivKey txPrivKey = PrivKey::random(); + PrivKey txPrivKey = bytes::random(); Address from = Secp256k1::toAddress(Secp256k1::toUPub(txPrivKey)); Address to(Utils::randBytes(20)); Bytes data = Utils::randBytes(32); @@ -172,7 +173,7 @@ namespace TBlock { } // Create and append 32 randomSeeds - std::vector randomSeeds(32, Hash::random()); + std::vector randomSeeds(32, bytes::random()); Bytes randomSeed; // Concatenated random seed of block. for (const auto &seed : randomSeeds) randomSeed.insert(randomSeed.end(), seed.begin(), seed.end()); @@ -180,7 +181,7 @@ namespace TBlock { // Create 64 TxValidator transactions, half for each type. for (const auto &seed : randomSeeds) { - PrivKey txValidatorPrivKey = PrivKey::random(); + PrivKey txValidatorPrivKey = bytes::random(); Address validatorAddress = Secp256k1::toAddress(Secp256k1::toUPub(txValidatorPrivKey)); Bytes hashTxData = Hex::toBytes("0xcfffe746"); Utils::appendBytes(hashTxData, Utils::sha3(seed)); @@ -231,8 +232,8 @@ namespace TBlock { SECTION("Block with 40000 dynamically created transactions and 256 dynamically created validator transactions") { // There is 16 TxValidator transactions, but only 8 of them are used for block randomness. - PrivKey blockValidatorPrivKey = PrivKey::random(); - Hash nPrevBlockHash = Hash::random(); + PrivKey blockValidatorPrivKey = bytes::random(); + Hash nPrevBlockHash = bytes::random(); uint64_t timestamp = 230915972837112; uint64_t nHeight = 239178513; @@ -249,7 +250,7 @@ namespace TBlock { uint64_t coreJobs = nJobPerCore[i]; futures.push_back(std::async(std::launch::async, [&txs, &txLock, coreJobs, i] { for (uint64_t j = 0; j < coreJobs; ++j) { - PrivKey txPrivKey = PrivKey::random(); + PrivKey txPrivKey = bytes::random(); Address from = Secp256k1::toAddress(Secp256k1::toUPub(txPrivKey)); Address to(Utils::randBytes(20)); Bytes data = Utils::randBytes(32); @@ -281,7 +282,7 @@ namespace TBlock { // Create and append 32 randomSeeds - std::vector randomSeeds(128, Hash::random()); + std::vector randomSeeds(128, bytes::random()); Bytes randomSeed; // Concatenated random seed of block. for (const auto &seed : randomSeeds) Utils::appendBytes(randomSeed, seed); @@ -289,7 +290,7 @@ namespace TBlock { // Create 64 TxValidator transactions, half for each type. for (const auto &seed : randomSeeds) { - PrivKey txValidatorPrivKey = PrivKey::random(); + PrivKey txValidatorPrivKey = bytes::random(); Address validatorAddress = Secp256k1::toAddress(Secp256k1::toUPub(txValidatorPrivKey)); Bytes hashTxData = Hex::toBytes("0xcfffe746"); Utils::appendBytes(hashTxData, Utils::sha3(seed)); diff --git a/tests/utils/db.cpp b/tests/utils/db.cpp index 43a5d540..0bd67684 100644 --- a/tests/utils/db.cpp +++ b/tests/utils/db.cpp @@ -8,6 +8,7 @@ See the LICENSE.txt file in the project root for more information. #include "../../src/libs/catch2/catch_amalgamated.hpp" #include "../../src/utils/db.h" #include "../../src/utils/strings.h" +#include "bytes/random.h" #include #include @@ -61,7 +62,7 @@ namespace TDB { DBBatch batchD; std::vector keys; for (int i = 0; i < 32; i++) { - batchP.push_back(Hash::random().asBytes(), Hash::random().asBytes(), pfx); + batchP.push_back(Utils::makeBytes(bytes::random(32)), Utils::makeBytes(bytes::random(32)), pfx); batchD.delete_key(batchP.getPuts()[i].key, pfx); } @@ -88,7 +89,7 @@ namespace TDB { // Update DBBatch newPutB; for (int i = 0; i < 32; i++) { - newPutB.push_back(batchP.getPuts()[i].key, Hash::random().asBytes(), pfx); + newPutB.push_back(batchP.getPuts()[i].key, Utils::makeBytes(bytes::random(32)), pfx); } REQUIRE(db.putBatch(newPutB)); /// No need to pass prefix as entry.key already contains it diff --git a/tests/utils/merkle.cpp b/tests/utils/merkle.cpp index 155f5524..7c198198 100644 --- a/tests/utils/merkle.cpp +++ b/tests/utils/merkle.cpp @@ -10,6 +10,7 @@ See the LICENSE.txt file in the project root for more information. #include "../../src/utils/merkle.h" #include "../../src/utils/tx.h" #include "../../src/libs/catch2/catch_amalgamated.hpp" +#include "bytes/random.h" using Catch::Matchers::Equals; @@ -38,10 +39,10 @@ namespace TMerkle { SECTION("Random Merkle Tree") { std::vector hashedLeafs { - Hash::random(), Hash::random(), Hash::random(), Hash::random(), - Hash::random(), Hash::random(), Hash::random(), Hash::random(), - Hash::random(), Hash::random(), Hash::random(), Hash::random(), - Hash::random(), Hash::random(), Hash::random() + bytes::random(), bytes::random(), bytes::random(), bytes::random(), + bytes::random(), bytes::random(), bytes::random(), bytes::random(), + bytes::random(), bytes::random(), bytes::random(), bytes::random(), + bytes::random(), bytes::random(), bytes::random() }; Merkle tree(hashedLeafs); diff --git a/tests/utils/randomgen.cpp b/tests/utils/randomgen.cpp index 958ee272..9662252b 100644 --- a/tests/utils/randomgen.cpp +++ b/tests/utils/randomgen.cpp @@ -9,6 +9,7 @@ See the LICENSE.txt file in the project root for more information. #include "../../src/utils/randomgen.h" #include "../../src/libs/catch2/catch_amalgamated.hpp" +#include "bytes/random.h" using Catch::Matchers::Equals; @@ -29,13 +30,13 @@ namespace TRandomGen { } SECTION("RandomGen getSeed") { - Hash seed(std::string("\xa6\x2a\x86\x47\x2e\x5c\x22\x4a\xa0\xa7\x84\xec\xca\xf7\x94\xab\xb6\x03\x02\xe2\x07\x3d\x52\xae\x0d\x09\x5a\xc5\xd1\x6f\x03\xa6")); + Hash seed(bytes::view("\xa6\x2a\x86\x47\x2e\x5c\x22\x4a\xa0\xa7\x84\xec\xca\xf7\x94\xab\xb6\x03\x02\xe2\x07\x3d\x52\xae\x0d\x09\x5a\xc5\xd1\x6f\x03\xa6")); RandomGen generator(seed); REQUIRE(generator.getSeed() == seed); auto newSeed = generator(); - REQUIRE(generator.getSeed().toUint256() == newSeed); + REQUIRE(static_cast(generator.getSeed()) == newSeed); newSeed = generator(); - REQUIRE(generator.getSeed().toUint256() == newSeed); + REQUIRE(static_cast(generator.getSeed()) == newSeed); } SECTION("RandomGen Min/Max") { @@ -50,7 +51,7 @@ namespace TRandomGen { "Ninth String", "Tenth String" }; - Hash seed(std::string("\xa4\xdd\x40\x26\x1f\xba\xbe\x97\x7a\xb6\xff\x77\xa7\xea\x9f\x76\xcd\x3b\x28\x6a\xa6\x62\x90\xb0\xd6\x2b\xdf\x43\x03\xf4\x38\x2b")); + Hash seed(bytes::view("\xa4\xdd\x40\x26\x1f\xba\xbe\x97\x7a\xb6\xff\x77\xa7\xea\x9f\x76\xcd\x3b\x28\x6a\xa6\x62\x90\xb0\xd6\x2b\xdf\x43\x03\xf4\x38\x2b")); RandomGen generator(seed); generator.shuffle(vector); @@ -67,7 +68,7 @@ namespace TRandomGen { } SECTION("RandomGen randomness") { - Hash seed = Hash::random(); + Hash seed = bytes::random(); RandomGen generator(seed); std::vector randoms; diff --git a/tests/utils/strings.cpp b/tests/utils/strings.cpp index 0292c118..fb1d0c50 100644 --- a/tests/utils/strings.cpp +++ b/tests/utils/strings.cpp @@ -8,6 +8,7 @@ See the LICENSE.txt file in the project root for more information. #include "../../src/libs/catch2/catch_amalgamated.hpp" #include "../../src/utils/strings.h" #include "bytes/view.h" +#include "bytes/random.h" using Catch::Matchers::Equals; @@ -153,12 +154,12 @@ namespace THash { SECTION("Hash toUint256") { uint256_t i = uint256_t("70518832285973061936518038480459635341011381946952877582230426678885538674712"); Hash hash(i); - REQUIRE(hash.toUint256() == i); + REQUIRE(static_cast(hash) == i); } SECTION("Hash random()") { - Hash hash1 = Hash::random(); - Hash hash2 = Hash::random(); + Hash hash1 = bytes::random(); + Hash hash2 = bytes::random(); REQUIRE(hash1 != hash2); } } diff --git a/tests/utils/tx_throw.cpp b/tests/utils/tx_throw.cpp index 07e2af91..c1376db5 100644 --- a/tests/utils/tx_throw.cpp +++ b/tests/utils/tx_throw.cpp @@ -8,6 +8,7 @@ See the LICENSE.txt file in the project root for more information. #include "../../src/libs/catch2/catch_amalgamated.hpp" #include "../../src/utils/utils.h" #include "../../src/utils/tx.h" +#include "bytes/random.h" using Catch::Matchers::Equals; @@ -184,7 +185,7 @@ namespace TTX { SECTION("Tx invalid PrivKey (doesn't match from)") { bool catched = false; try { - PrivKey privKey(Hash::random()); + PrivKey privKey(bytes::random()); TxBlock tx( Address(Hex::toBytes("0x1234567890123456789012345678901234567890")), Address(Hex::toBytes("0x1234567890123456789012345678901234567890")), @@ -294,7 +295,7 @@ namespace TTX { SECTION("Tx invalid PrivKey (doesn't match from)") { bool catched = false; try { - PrivKey privKey(Hash::random()); + PrivKey privKey(bytes::random()); TxValidator tx( Address(Hex::toBytes("0x1234567890123456789012345678901234567890")), {}, 8080, 0, privKey From 384d1ec30f79a22a12b55e73d46adf8082ac963a Mon Sep 17 00:00:00 2001 From: Leonardo Amalaral Date: Wed, 30 Oct 2024 12:00:18 -0300 Subject: [PATCH 03/37] view of signature --- src/utils/signature.h | 63 +++++++++++++++++++++++++++++++++++++++++++ src/utils/strings.cpp | 6 ----- src/utils/strings.h | 10 +------ 3 files changed, 64 insertions(+), 15 deletions(-) create mode 100644 src/utils/signature.h diff --git a/src/utils/signature.h b/src/utils/signature.h new file mode 100644 index 00000000..548246e6 --- /dev/null +++ b/src/utils/signature.h @@ -0,0 +1,63 @@ +#ifndef BDK_UTILS_SIGNATURE_H +#define BDK_UTILS_SIGNATURE_H + +#include "bytes/range.h" +#include "bytesinterface.h" +#include "evmc/evmc.hpp" +#include "hash.h" +#include "view.h" + +inline constexpr size_t SIGNATURE_SIZE = 65; + +template +class BaseSignature : public BytesInterface { +public: + using BytesInterface::BytesInterface; + + uint256_t r() const { + return static_cast(View(self() | std::views::take(HASH_SIZE))); + } + + uint256_t s() const { + return static_cast(View( + self() | std::views::drop(HASH_SIZE) | std::views::take(HASH_SIZE))); + } + + uint8_t v() const { + return *std::ranges::rbegin(self()); + } + +private: + constexpr const T& self() const { return static_cast(*this); } +}; + +class Signature : public BaseSignature { +public: + constexpr Signature() : data_() {} + + template + explicit (not bytes::Initializer) + constexpr Signature(I&& input) : BaseSignature(std::forward(input)) {} + + constexpr auto begin() { return data_.begin(); } + + constexpr auto begin() const { return data_.begin(); } + +private: + std::array data_; +}; + +template<> +class View : public BaseSignature> { +public: + template + explicit(not std::same_as, Signature>) + constexpr View(R&& input) : data_(std::forward(input)) {} + + constexpr auto begin() const { return data_.begin(); } + +private: + std::span data_; +}; + +#endif // BDK_UTILS_SIGNATURE_H diff --git a/src/utils/strings.cpp b/src/utils/strings.cpp index 1dcf3e29..51986c7d 100644 --- a/src/utils/strings.cpp +++ b/src/utils/strings.cpp @@ -8,12 +8,6 @@ See the LICENSE.txt file in the project root for more information. #include "strings.h" #include "utils.h" -uint256_t Signature::r() const { return Utils::bytesToUint256(this->view(0, 32)); } - -uint256_t Signature::s() const { return Utils::bytesToUint256(this->view(32, 32)); } - -uint8_t Signature::v() const { return Utils::bytesToUint8(this->view(64, 1)); } - Address::Address(const std::string_view add, bool inBytes) { if (inBytes) { if (add.size() != 20) throw std::invalid_argument("Address must be 20 bytes long."); diff --git a/src/utils/strings.h b/src/utils/strings.h index ea267305..c8c051fd 100644 --- a/src/utils/strings.h +++ b/src/utils/strings.h @@ -21,6 +21,7 @@ See the LICENSE.txt file in the project root for more information. #include "address.h" #include "hash.h" +#include "signature.h" // TODO: It is possible to implement **fast** operators for some types, // such as Address, Functor and Hash. Taking advantage that memory located within @@ -140,15 +141,6 @@ struct Functor { inline bool operator==(const Functor& other) const { return this->value == other.value; } }; -/// Abstraction of a 65-byte ECDSA signature. Inherits `FixedBytes<65>`. -class Signature : public FixedBytes<65> { - public: - using FixedBytes<65>::FixedBytes; - uint256_t r() const; ///< Get the first half (32 bytes) of the signature. - uint256_t s() const; ///< Get the second half (32 bytes) of the signature. - uint8_t v() const; ///< Get the recovery ID (1 byte) of the signature. -}; - /// Abstraction of a EVM Storage key (20-bytes address + 32 bytes slot key). Inherits `FixedBytes<52>`. class StorageKey : public FixedBytes<52> { public: From 4a50279e2cd5a495d828a8ae5f67f634bfe3c36e Mon Sep 17 00:00:00 2001 From: Leonardo Amalaral Date: Wed, 6 Nov 2024 18:23:10 -0300 Subject: [PATCH 04/37] views modifications --- src/bytes/hex.h | 52 ++++++ src/bytes/random.h | 5 +- src/bytes/range.h | 12 ++ src/bytes/view.h | 17 +- src/contract/abi.cpp | 4 +- src/contract/abi.h | 46 ++--- src/contract/calltracer.cpp | 4 +- src/contract/calltracer.h | 2 +- src/contract/contracthost.cpp | 15 +- src/contract/contracthost.h | 19 +- src/contract/event.cpp | 3 +- src/contract/templates/dexv2/dexv2factory.cpp | 2 +- src/contract/templates/erc20.cpp | 2 +- src/contract/templates/erc20wrapper.cpp | 2 +- src/contract/templates/erc721.cpp | 6 +- src/core/consensus.cpp | 4 +- src/core/rdpos.cpp | 8 +- src/core/state.cpp | 10 +- src/core/state.h | 2 +- src/core/storage.cpp | 6 +- src/core/storage.h | 2 +- src/net/p2p/encoding.cpp | 16 +- src/net/p2p/encoding.h | 14 +- src/utils/CMakeLists.txt | 1 - src/utils/address.cpp | 3 +- src/utils/address.h | 147 ++++++++------- src/utils/bytesinterface.h | 18 +- src/utils/db.h | 4 +- src/utils/finalizedblock.cpp | 4 +- src/utils/finalizedblock.h | 2 +- src/utils/fixedbytes.h | 47 +++++ src/utils/hash.h | 171 +++++++++++++----- src/utils/hex.cpp | 2 +- src/utils/hex.h | 5 +- src/utils/safehash.h | 48 ++--- src/utils/strings.cpp | 56 ------ src/utils/strings.h | 168 +---------------- src/utils/tx.cpp | 32 ++-- src/utils/tx.h | 30 +-- src/utils/utils.cpp | 92 +++++----- src/utils/utils.h | 110 +++++------ src/utils/view.h | 41 ++++- tests/utils/strings.cpp | 9 +- 43 files changed, 633 insertions(+), 610 deletions(-) create mode 100644 src/bytes/hex.h create mode 100644 src/utils/fixedbytes.h delete mode 100644 src/utils/strings.cpp diff --git a/src/bytes/hex.h b/src/bytes/hex.h new file mode 100644 index 00000000..6f898539 --- /dev/null +++ b/src/bytes/hex.h @@ -0,0 +1,52 @@ +#ifndef BDK_BYTES_HEX_H +#define BDK_BYTES_HEX_H + +#include +#include +#include "range.h" +#include "initializer.h" +#include "utils/hex.h" + +namespace bytes { + +/** + * Creates a sized initializer from a hex representation. The initializer will + * fill the container data with by converting the hexadecimal representation + * into binary data. + * @param str the hex string + * @return the sized initializer + */ +constexpr SizedInitializer auto hex(std::string_view str) { + if (str.size() >= 2 && str.starts_with("0x")) { + str = str.substr(2); + } + + if (str.size() % 2) { + throw std::invalid_argument("the length of hex string is required to be even number"); + } + + return makeInitializer(str.size() / 2, [str] (Byte* dest) { + const auto value = [] (char c) -> Byte { + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + else + throw std::invalid_argument("character '" + std::to_string(c) + "' is invalid in hex string"); + }; + + auto it = str.begin(); + + while (it != str.end()) { + const Byte l = value(*it++); + const Byte r = value(*it++); + *dest++ = (l << 4) | r; + } + }); +} + +} // namespace bytes + +#endif // BDK_BYTES_HEX_H diff --git a/src/bytes/random.h b/src/bytes/random.h index 2fde403e..25f64fd0 100644 --- a/src/bytes/random.h +++ b/src/bytes/random.h @@ -10,10 +10,9 @@ namespace bytes { /** * Creates an initializer of random bytes. - * The number of generated bytes exactly - * matches the size of the target bytes range. + * The number of generated bytes exactly matches the size of the target bytes range. * - * For example: + * Examples: * Hash hash = bytes::random(); // generates 32 random bytes * Address addr = bytes::random(); // generates 20 random bytes * diff --git a/src/bytes/range.h b/src/bytes/range.h index 14f135b2..4005a41d 100644 --- a/src/bytes/range.h +++ b/src/bytes/range.h @@ -6,6 +6,18 @@ #include "utils/bytes.h" namespace bytes { + /** + * Concept of a bytes iterator. + */ + template + concept Iterator = std::input_or_output_iterator && std::same_as, Byte>; + + /** + * Concept of a bytes contiguous bytes iterator. + */ + template + concept DataIterator = Iterator && std::contiguous_iterator; + /** * The concept of a range of bytes. */ diff --git a/src/bytes/view.h b/src/bytes/view.h index a3bfacea..1dc10102 100644 --- a/src/bytes/view.h +++ b/src/bytes/view.h @@ -4,13 +4,10 @@ #include #include "range.h" +#include "utils/view.h" namespace bytes { -/// A view over sized contiguous bytes -using View = std::span; - -/// A span (i.e. a non-owning array of bytes that allows modification) over sized contiguous bytes using Span = std::span; /** @@ -22,7 +19,7 @@ using Span = std::span; * @return a view object of the bytes */ template -constexpr View view(R&& r) { return View(std::forward(r)); } +constexpr View view(R&& r) { return View(std::forward(r)); } /** * Creates a span from the given data range. It needs to be Borrowed @@ -43,8 +40,8 @@ constexpr Span span(R&& r) { return Span(std::forward(r)); } * @param str the target string * @return a bytes view of the string bytes */ -inline View view(std::string_view str) { - return View(reinterpret_cast(str.data()), str.size()); +inline View view(std::string_view str) { + return View(reinterpret_cast(str.data()), str.size()); } /** @@ -57,12 +54,6 @@ inline Span span(std::string& str) { return Span(reinterpret_cast(str.data()), str.size()); } -/// The concept of a type that can be viewed as a sized range of contiguous bytes. -template -concept Viewable = requires(const T& a) { - { view(a) } -> std::convertible_to; -}; - } // namespace bytes #endif // BYTES_VIEW_H diff --git a/src/contract/abi.cpp b/src/contract/abi.cpp index e5a0f0af..2c7ec6d2 100644 --- a/src/contract/abi.cpp +++ b/src/contract/abi.cpp @@ -31,14 +31,14 @@ Bytes ABI::Encoder::encodeInt(const int256_t& num) { return ret; } -uint256_t ABI::Decoder::decodeUint(const bytes::View &bytes, uint64_t &index) { +uint256_t ABI::Decoder::decodeUint(const View &bytes, uint64_t &index) { if (index + 32 > bytes.size()) throw std::length_error("Data too short for uint256"); uint256_t result = Utils::bytesToUint256(bytes.subspan(index, 32)); index += 32; return result; } -int256_t ABI::Decoder::decodeInt(const bytes::View& bytes, uint64_t& index) { +int256_t ABI::Decoder::decodeInt(const View& bytes, uint64_t& index) { if (index + 32 > bytes.size()) throw std::length_error("Data too short for int256"); int256_t result = Utils::bytesToInt256(bytes.subspan(index, 32)); index += 32; diff --git a/src/contract/abi.h b/src/contract/abi.h index 06b7edbc..268705ce 100644 --- a/src/contract/abi.h +++ b/src/contract/abi.h @@ -90,7 +90,7 @@ namespace ABI { */ template constexpr bool isDynamic() { if constexpr ( - std::is_same_v || std::is_same_v || std::is_same_v || false + std::is_same_v || std::is_same_v> || std::is_same_v || false ) return true; if constexpr (isVectorV) return true; if constexpr (isTupleOfDynamicTypes::value) return true; @@ -418,7 +418,7 @@ namespace ABI { }; template <> struct TypeEncoder { static Bytes encode(const std::string& str) { - bytes::View bytes = Utils::create_view_span(str); + View bytes = Utils::create_view_span(str); int pad = 0; do { pad += 32; } while (pad < bytes.size()); Bytes len = Utils::padLeftBytes(Utils::uintToBytes(bytes.size()), 32); @@ -604,7 +604,7 @@ namespace ABI { template <> struct TypeEncoder { static Bytes encode(const std::string& str) { - bytes::View bytes = Utils::create_view_span(str); + View bytes = Utils::create_view_span(str); int pad = 0; do { pad += 32; } while (pad < bytes.size()); return Utils::padRightBytes(bytes, pad); @@ -738,7 +738,7 @@ namespace ABI { * @return The decoded data. * @throw std::length_error if data is too short for the type. */ - uint256_t decodeUint(const bytes::View& bytes, uint64_t& index); + uint256_t decodeUint(const View& bytes, uint64_t& index); /** * Decode an int256. @@ -747,12 +747,12 @@ namespace ABI { * @return The decoded data. * @throw std::length_error if data is too short for the type. */ - int256_t decodeInt(const bytes::View& bytes, uint64_t& index); + int256_t decodeInt(const View& bytes, uint64_t& index); /// @cond // General template for bytes to type decoding template struct TypeDecoder { - static T decode(const bytes::View&, const uint64_t&) { + static T decode(const View&, const uint64_t&) { static_assert(always_false, "TypeName specialization for this type is not defined"); return T(); } @@ -760,7 +760,7 @@ namespace ABI { // Specialization for default solidity types template <> struct TypeDecoder
{ - static Address decode(const bytes::View& bytes, uint64_t& index) { + static Address decode(const View& bytes, uint64_t& index) { if (index + 32 > bytes.size()) throw std::length_error("Data too short for address"); auto result = Address(bytes.subspan(index + 12, 20)); index += 32; @@ -769,7 +769,7 @@ namespace ABI { }; template <> struct TypeDecoder { - static Hash decode(const bytes::View& bytes, uint64_t& index) { + static Hash decode(const View& bytes, uint64_t& index) { if (index + 32 > bytes.size()) { throw std::length_error("Data too short for hash"); } auto result = Hash(bytes.subspan(index, 32)); index += 32; @@ -778,7 +778,7 @@ namespace ABI { }; template <> struct TypeDecoder { - static bool decode(const bytes::View& bytes, uint64_t& index) { + static bool decode(const View& bytes, uint64_t& index) { if (index + 32 > bytes.size()) throw std::length_error("Data too short for bool"); bool result = (bytes[index + 31] == 0x01); index += 32; @@ -787,7 +787,7 @@ namespace ABI { }; template <> struct TypeDecoder { - static Bytes decode(const bytes::View& bytes, uint64_t& index) { + static Bytes decode(const View& bytes, uint64_t& index) { if (index + 32 > bytes.size()) throw std::length_error("Data too short for bytes"); Bytes tmp(bytes.begin() + index, bytes.begin() + index + 32); uint64_t bytesStart = Utils::fromBigEndian(tmp); @@ -810,7 +810,7 @@ namespace ABI { }; template <> struct TypeDecoder { - static std::string decode(const bytes::View& bytes, uint64_t& index) { + static std::string decode(const View& bytes, uint64_t& index) { if (index + 32 > bytes.size()) throw std::length_error("Data too short for string 1"); std::string tmp(bytes.begin() + index, bytes.begin() + index + 32); uint64_t bytesStart = Utils::fromBigEndian(tmp); @@ -847,7 +847,7 @@ namespace ABI { std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v struct TypeDecoder { - static T decode(const bytes::View& bytes, uint64_t& index) { + static T decode(const View& bytes, uint64_t& index) { return static_cast(decodeInt(bytes, index)); } }; @@ -867,21 +867,21 @@ namespace ABI { std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v struct TypeDecoder { - static T decode(const bytes::View& bytes, uint64_t& index) { + static T decode(const View& bytes, uint64_t& index) { return static_cast(decodeUint(bytes, index)); } }; // Specialization for enum types template requires std::is_enum_v struct TypeDecoder { - static T decode(const bytes::View& bytes, uint64_t& index) { + static T decode(const View& bytes, uint64_t& index) { return static_cast(decodeUint(bytes, index)); } }; // Forward declaration of TypeDecode> so TypeDecoder> can see it. template requires isVectorV struct TypeDecoder { - static T decode(const bytes::View& bytes, uint64_t& index); + static T decode(const View& bytes, uint64_t& index); }; /** @@ -893,7 +893,7 @@ namespace ABI { * @param index The point on the encoded string to start decoding. * @param ret The tuple object to "return". Needs to be a reference and created outside the function due to recursion. */ - template void decodeTuple(const bytes::View& bytes, uint64_t& index, TupleLike& ret) { + template void decodeTuple(const View& bytes, uint64_t& index, TupleLike& ret) { if constexpr (I < std::tuple_size_v) { using SelectedType = typename std::tuple_element::type; std::get(ret) = TypeDecoder::decode(bytes, index); @@ -903,7 +903,7 @@ namespace ABI { // Specialization for std::tuple template requires isTuple::value struct TypeDecoder { - static T decode(const bytes::View& bytes, uint64_t& index) { + static T decode(const View& bytes, uint64_t& index) { T ret; if constexpr (isTupleOfDynamicTypes::value) { if (index + 32 > bytes.size()) throw std::length_error("Data too short for tuple of dynamic types"); @@ -922,7 +922,7 @@ namespace ABI { }; /// Specialization for std::vector - template requires isVectorV T TypeDecoder::decode(const bytes::View& bytes, uint64_t& index) { + template requires isVectorV T TypeDecoder::decode(const View& bytes, uint64_t& index) { using ElementType = vectorElementTypeT; std::vector retVector; @@ -951,7 +951,7 @@ namespace ABI { /// Specialization of decodeTupleHelper() for when tuple index is the last one template requires (Index == sizeof...(Args)) - void decodeTupleHelper(const bytes::View&, const uint64_t&, std::tuple&) { + void decodeTupleHelper(const View&, const uint64_t&, std::tuple&) { // End of recursion, do nothing } @@ -965,7 +965,7 @@ namespace ABI { */ template requires (Index < sizeof...(Args)) - void decodeTupleHelper(const bytes::View& encodedData, uint64_t& index, std::tuple& tuple) { + void decodeTupleHelper(const View& encodedData, uint64_t& index, std::tuple& tuple) { // TODO: Technically, we could pass std::get(tuple) as a reference to decode<>(). // But, it is worth to reduce code readability for a few nanoseconds? Need to benchmark. std::get(tuple) = TypeDecoder>>::decode(encodedData, index); @@ -980,7 +980,7 @@ namespace ABI { * @return A tuple with the decoded data, or an empty tuple if there's no arguments to decode. */ template - inline std::tuple decodeData(const bytes::View& encodedData, uint64_t index = 0) { + inline std::tuple decodeData(const View& encodedData, uint64_t index = 0) { if constexpr (sizeof...(Args) == 0) { return std::tuple<>(); } else { @@ -993,7 +993,7 @@ namespace ABI { /// Specialization for tuples without args. template struct decodeDataAsTuple { /// Decode the tuple. - static T decode(const bytes::View&) { + static T decode(const View&) { static_assert(always_false, "Can't use decodeDataAsTuple with a non-tuple type"); return T(); } @@ -1002,7 +1002,7 @@ namespace ABI { /// Specialization for tuples with args. template struct decodeDataAsTuple> { /// Decode the tuple. - static std::tuple decode(const bytes::View& encodedData) { + static std::tuple decode(const View& encodedData) { if constexpr (sizeof...(Args) == 0) { throw std::invalid_argument("Can't decode empty tuple"); } else { diff --git a/src/contract/calltracer.cpp b/src/contract/calltracer.cpp index 59c593e4..4f2cb557 100644 --- a/src/contract/calltracer.cpp +++ b/src/contract/calltracer.cpp @@ -32,7 +32,7 @@ Bytes encodeRevertReason(std::string_view reason) { )); } -std::string decodeRevertReason(bytes::View data) { +std::string decodeRevertReason(View data) { if (data.size() != 100) { throw DynamicException("Encoded revert reason is expected to have exactly 100 bytes"); } @@ -52,7 +52,7 @@ Call::Call(const evmc_message& msg) value(msg.value.bytes), gas(msg.gas), gasUsed(0), - input(Utils::makeBytes(bytes::View(msg.input_data, msg.input_size))) {} + input(Utils::makeBytes(View(msg.input_data, msg.input_size))) {} json Call::toJson() const { using enum Call::Type; diff --git a/src/contract/calltracer.h b/src/contract/calltracer.h index d55c8a8d..3017d0b3 100644 --- a/src/contract/calltracer.h +++ b/src/contract/calltracer.h @@ -12,7 +12,7 @@ namespace trace { Bytes encodeRevertReason(std::string_view reason); -std::string decodeRevertReason(bytes::View data); +std::string decodeRevertReason(View data); enum class Status { SUCCEEDED, diff --git a/src/contract/contracthost.cpp b/src/contract/contracthost.cpp index 9fce1a08..53ea64d9 100644 --- a/src/contract/contracthost.cpp +++ b/src/contract/contracthost.cpp @@ -111,7 +111,7 @@ Address ContractHost::deriveContractAddress(const uint64_t& nonce, Address ContractHost::deriveContractAddress(const Address& fromAddress, const Hash& salt, - const bytes::View& code) + const View& code) { const auto code_hash = Utils::sha3(code); Bytes buffer(1 + sizeof(fromAddress) + sizeof(salt) + sizeof(code_hash)); @@ -184,7 +184,7 @@ void ContractHost::traceCallFinished(const evmc_result& res) noexcept { } const uint64_t gasUsed = callTracer_.current().gas - res.gas_left; - Bytes output = Utils::makeBytes(bytes::View(res.output_data, res.output_size)); + Bytes output = Utils::makeBytes(View(res.output_data, res.output_size)); if (res.status_code == EVMC_SUCCESS) { callTracer_.callSucceeded(std::move(output), gasUsed); @@ -311,7 +311,7 @@ void ContractHost::execute(const evmc_message& msg, const ContractType& type) { this->accounts_[to]->code.data(), this->accounts_[to]->code.size())); - output = Utils::makeBytes(bytes::View(result.output_data, result.output_size)); + output = Utils::makeBytes(View(result.output_data, result.output_size)); this->leftoverGas_ = result.gas_left; // gas_left is not linked with leftoverGas_, we need to link it. if (result.status_code) { @@ -439,9 +439,8 @@ bool ContractHost::account_exists(const evmc::address& addr) const noexcept { evmc::bytes32 ContractHost::get_storage(const evmc::address& addr, const evmc::bytes32& key) const noexcept { - StorageKey storageKey(addr, key); try { - auto it = vmStorage_.find(storageKey); + auto it = vmStorage_.find(StorageKeyView{addr, key}); if (it != vmStorage_.end()) return bytes::cast(it->second); } catch (const std::exception& e) { @@ -755,7 +754,7 @@ evmc::bytes32 ContractHost::get_transient_storage(const evmc::address &addr, const evmc::bytes32 &key) const noexcept { StorageKey storageKey(addr, key); try { - auto it = transientStorage_.find(storageKey); + auto it = transientStorage_.find(StorageKeyView{addr, key}); if (it != transientStorage_.end()) { return bytes::cast(it->second); } @@ -769,10 +768,8 @@ evmc::bytes32 ContractHost::get_transient_storage(const evmc::address &addr, void ContractHost::set_transient_storage(const evmc::address &addr, const evmc::bytes32 &key, const evmc::bytes32 &value) noexcept { - StorageKey storageKey(addr, key); try { - Hash hashValue(value); - transientStorage_[storageKey] = hashValue; + transientStorage_.emplace(StorageKeyView{addr, key}, value); } catch (const std::exception& e) { this->evmcThrows_.emplace_back(e.what()); this->evmcThrow_ = true; diff --git a/src/contract/contracthost.h b/src/contract/contracthost.h index fafc91be..e1738db2 100644 --- a/src/contract/contracthost.h +++ b/src/contract/contracthost.h @@ -1,3 +1,4 @@ + #ifndef CONTRACT_HOST_H #define CONTRACT_HOST_H @@ -75,8 +76,8 @@ class ContractHost : public evmc::Host { const evmc_tx_context& currentTxContext_; // MUST be initialized within the constructor. boost::unordered_flat_map, SafeHash>& contracts_; boost::unordered_flat_map, SafeHash>& accounts_; - boost::unordered_flat_map& vmStorage_; - boost::unordered_flat_map transientStorage_; + boost::unordered_flat_map& vmStorage_; + boost::unordered_flat_map transientStorage_; bool mustRevert_ = true; // We always assume that we must revert until proven otherwise. mutable bool evmcThrow_ = false; // Did the EVMC throw an exception? mutable std::vector evmcThrows_; @@ -128,7 +129,7 @@ class ContractHost : public evmc::Host { Address computeNewAccountAddress(const Address& fromAddress, const uint64_t& nonce, const Hash& salt, - const bytes::View& init_code); + const View& init_code); bool isTracingCalls() const noexcept; @@ -156,7 +157,7 @@ class ContractHost : public evmc::Host { const evmc_tx_context& currentTxContext, boost::unordered_flat_map, SafeHash>& contracts, boost::unordered_flat_map, SafeHash>& accounts, - boost::unordered_flat_map& vmStorage, + boost::unordered_flat_map& vmStorage, const Hash& txHash, const uint64_t txIndex, const Hash& blockHash, @@ -187,7 +188,7 @@ class ContractHost : public evmc::Host { static Address deriveContractAddress(const Address& fromAddress, const Hash& salt, - const bytes::View& init_code); + const View& init_code); /// Executes a call void execute(const evmc_message& msg, const ContractType& type); @@ -277,12 +278,12 @@ class ContractHost : public evmc::Host { evmc_revision::EVMC_LATEST_STABLE_REVISION, &msg, recipientAcc.code.data(), recipientAcc.code.size())); this->leftoverGas_ = result.gas_left; if (result.status_code) { - auto hexResult = Hex::fromBytes(bytes::View(result.output_data, result.output_data + result.output_size)); + auto hexResult = Hex::fromBytes(View(result.output_data, result.output_data + result.output_size)); throw DynamicException("ContractHost::callContractViewFunction: EVMC call failed - Type: " + Utils::getRealTypeName() + " at address: " + targetAddr.hex().get() + " - Result: " + hexResult.get() ); } - return std::get<0>(ABI::Decoder::decodeData(bytes::View(result.output_data, result.output_data + result.output_size))); + return std::get<0>(ABI::Decoder::decodeData(View(result.output_data, result.output_data + result.output_size))); } break; case ContractType::CPP : { this->deduceGas(1000); @@ -412,7 +413,7 @@ class ContractHost : public evmc::Host { evmc_revision::EVMC_LATEST_STABLE_REVISION, &msg, recipientAcc.code.data(), recipientAcc.code.size())); this->leftoverGas_ = result.gas_left; if (result.status_code) { - auto hexResult = Hex::fromBytes(bytes::View(result.output_data, result.output_data + result.output_size)); + auto hexResult = Hex::fromBytes(View(result.output_data, result.output_data + result.output_size)); throw DynamicException("ContractHost::callContractFunction: EVMC call failed - Type: " + Utils::getRealTypeName() + " at address: " + targetAddr.hex().get() + " - Result: " + hexResult.get() ); @@ -420,7 +421,7 @@ class ContractHost : public evmc::Host { if constexpr (std::same_as) { return; } else { - return std::get<0>(ABI::Decoder::decodeData(bytes::View(result.output_data, result.output_data + result.output_size))); + return std::get<0>(ABI::Decoder::decodeData(View(result.output_data, result.output_data + result.output_size))); } } break; case ContractType::CPP : { diff --git a/src/contract/event.cpp b/src/contract/event.cpp index eec54853..ac2a6a92 100644 --- a/src/contract/event.cpp +++ b/src/contract/event.cpp @@ -6,6 +6,7 @@ See the LICENSE.txt file in the project root for more information. */ #include "event.h" +#include "bytes/hex.h" Event::Event(const std::string& jsonstr) { json obj = json::parse(jsonstr); @@ -15,7 +16,7 @@ Event::Event(const std::string& jsonstr) { this->txIndex_ = obj["txIndex"].get(); this->blockHash_ = Hash(Hex::toBytes(std::string_view(obj["blockHash"].get()).substr(2))); this->blockIndex_ = obj["blockIndex"].get(); - this->address_ = Address(obj["address"].get(), false); + this->address_ = bytes::hex(obj["address"].get()); this->data_ = obj["data"].get(); for (std::string topic : obj["topics"]) this->topics_.emplace_back(Hex::toBytes(topic)); this->anonymous_ = obj["anonymous"].get(); diff --git a/src/contract/templates/dexv2/dexv2factory.cpp b/src/contract/templates/dexv2/dexv2factory.cpp index f5cf6eb9..15f7790b 100644 --- a/src/contract/templates/dexv2/dexv2factory.cpp +++ b/src/contract/templates/dexv2/dexv2factory.cpp @@ -18,7 +18,7 @@ DEXV2Factory::DEXV2Factory(const Address &address, const DB& db this->allPairs_.push_back(Address(dbEntry.value)); } for (const auto& dbEntry : db.getBatch(this->getNewPrefix("getPair_"))) { - bytes::View valueView(dbEntry.value); + View valueView(dbEntry.value); this->getPair_[Address(dbEntry.key)][Address(valueView.subspan(0, 20))] = Address(valueView.subspan(20)); } diff --git a/src/contract/templates/erc20.cpp b/src/contract/templates/erc20.cpp index 5615242a..73883f57 100644 --- a/src/contract/templates/erc20.cpp +++ b/src/contract/templates/erc20.cpp @@ -19,7 +19,7 @@ ERC20::ERC20(const Address& address, const DB& db) this->balances_[Address(dbEntry.key)] = Utils::fromBigEndian(dbEntry.value); } for (const auto& dbEntry : db.getBatch(this->getNewPrefix("allowed_"))) { - bytes::View key(dbEntry.key); + View key(dbEntry.key); Address owner(key.subspan(0,20)); Address spender(key.subspan(20)); this->allowed_[owner][spender] = Utils::bytesToUint256(dbEntry.value); diff --git a/src/contract/templates/erc20wrapper.cpp b/src/contract/templates/erc20wrapper.cpp index c5938d87..223a06f9 100644 --- a/src/contract/templates/erc20wrapper.cpp +++ b/src/contract/templates/erc20wrapper.cpp @@ -11,7 +11,7 @@ ERC20Wrapper::ERC20Wrapper(const Address& contractAddress, const DB& db ) : DynamicContract(contractAddress, db), tokensAndBalances_(this) { for (const auto& dbEntry : db.getBatch(this->getNewPrefix("tokensAndBalances_"))) { - bytes::View valueView(dbEntry.value); + View valueView(dbEntry.value); this->tokensAndBalances_[Address(dbEntry.key)][Address(valueView.subspan(0, 20))] = Utils::fromBigEndian(valueView.subspan(20)); } diff --git a/src/contract/templates/erc721.cpp b/src/contract/templates/erc721.cpp index 8e13f42f..2eb7cae4 100644 --- a/src/contract/templates/erc721.cpp +++ b/src/contract/templates/erc721.cpp @@ -14,7 +14,7 @@ ERC721::ERC721(const Address& address, const DB& db this->name_ = Utils::bytesToString(db.get(std::string("name_"), this->getDBPrefix())); this->symbol_ = Utils::bytesToString(db.get(std::string("symbol_"), this->getDBPrefix())); for (const auto& dbEntry : db.getBatch(this->getNewPrefix("owners_"))) { - bytes::View valueView(dbEntry.value); + View valueView(dbEntry.value); this->owners_[Utils::fromBigEndian(dbEntry.key)] = Address(valueView.subspan(0, 20)); } for (const auto& dbEntry : db.getBatch(this->getNewPrefix("balances_"))) { @@ -24,7 +24,7 @@ ERC721::ERC721(const Address& address, const DB& db this->tokenApprovals_[Utils::fromBigEndian(dbEntry.key)] = Address(dbEntry.value); } for (const auto& dbEntry : db.getBatch(this->getNewPrefix("operatorAddressApprovals_"))) { - bytes::View keyView(dbEntry.key); + View keyView(dbEntry.key); Address owner(keyView.subspan(0, 20)); Address operatorAddress(keyView.subspan(20)); this->operatorAddressApprovals_[owner][operatorAddress] = dbEntry.value[0]; @@ -265,7 +265,7 @@ void ERC721::transferFrom(const Address& from, const Address& to, const uint256_ DBBatch ERC721::dump() const { DBBatch dbBatch = BaseContract::dump(); - boost::unordered_flat_map data { + boost::unordered_flat_map> data { {"name_", Utils::stringToBytes(name_.get())}, {"symbol_", Utils::stringToBytes(symbol_.get())} }; diff --git a/src/core/consensus.cpp b/src/core/consensus.cpp index 2645ff60..fa1f15e9 100644 --- a/src/core/consensus.cpp +++ b/src/core/consensus.cpp @@ -202,8 +202,8 @@ void Consensus::doValidatorTx(const uint64_t& nHeight, const Validator& me) { ); // Sanity check if tx is valid - bytes::View randomHashTxView(randomHashTx.getData()); - bytes::View randomSeedTxView(seedTx.getData()); + View randomHashTxView(randomHashTx.getData()); + View randomSeedTxView(seedTx.getData()); if (Utils::sha3(randomSeedTxView.subspan(4)) != Hash(randomHashTxView.subspan(4))) { LOGDEBUG("RandomHash transaction is not valid!!!"); return; diff --git a/src/core/rdpos.cpp b/src/core/rdpos.cpp index 7b7c49ad..c79a9dff 100644 --- a/src/core/rdpos.cpp +++ b/src/core/rdpos.cpp @@ -155,10 +155,10 @@ bool rdPoS::validateBlockTxSanityCheck(const TxValidator& hashTx, const TxValida } // Check if the randomHash transaction matches the random transaction. - bytes::View hashTxData = hashTx.getData(); - bytes::View seedTxData = seedTx.getData(); - bytes::View hash = hashTxData.subspan(4); - bytes::View random = seedTxData.subspan(4); + View hashTxData = hashTx.getData(); + View seedTxData = seedTx.getData(); + View hash = hashTxData.subspan(4); + View random = seedTxData.subspan(4); // Size sanity check, should be 32 bytes. if (hash.size() != 32) { diff --git a/src/core/state.cpp b/src/core/state.cpp index 34a890c1..c0404715 100644 --- a/src/core/state.cpp +++ b/src/core/state.cpp @@ -44,7 +44,12 @@ State::State( // Load all the EVM Storage Slot/keys from the DB for (const auto& dbEntry : db.getBatch(DBPrefix::vmStorage)) { - this->vmStorage_.emplace(StorageKey(dbEntry.key), dbEntry.value); + Address addr(dbEntry.key | std::views::take(ADDRESS_SIZE)); + Hash hash(dbEntry.key | std::views::drop(ADDRESS_SIZE)); + + this->vmStorage_.emplace( + StorageKeyView(addr, hash), + dbEntry.value); } auto latestBlock = this->storage_.latest(); @@ -137,7 +142,8 @@ DBBatch State::dump() const { } // There is also the need to dump the vmStorage_ map for (const auto& [storageKey, storageValue] : this->vmStorage_) { - stateBatch.push_back(storageKey, storageValue, DBPrefix::vmStorage); + const auto key = Utils::makeBytes(bytes::join(storageKey.first, storageKey.second)); + stateBatch.push_back(key, storageValue, DBPrefix::vmStorage); } return stateBatch; diff --git a/src/core/state.h b/src/core/state.h index 811b02f0..fe531492 100644 --- a/src/core/state.h +++ b/src/core/state.h @@ -39,7 +39,7 @@ class State : public Dumpable, public Log::LogicalLocationProvider { P2P::ManagerNormal& p2pManager_; ///< Reference to the P2P connection manager. rdPoS rdpos_; ///< rdPoS object (consensus). boost::unordered_flat_map, SafeHash> contracts_; ///< Map with information about blockchain contracts (Address -> Contract). - boost::unordered_flat_map vmStorage_; ///< Map with the storage of the EVM. + boost::unordered_flat_map vmStorage_; ///< Map with the storage of the EVM. boost::unordered_flat_map, SafeHash> accounts_; ///< Map with information about blockchain accounts (Address -> Account). boost::unordered_flat_map mempool_; ///< TxBlock mempool. diff --git a/src/core/storage.cpp b/src/core/storage.cpp index e36a1577..e7e3ade4 100644 --- a/src/core/storage.cpp +++ b/src/core/storage.cpp @@ -83,7 +83,7 @@ void Storage::initializeBlockchain() { } } -TxBlock Storage::getTxFromBlockWithIndex(bytes::View blockData, uint64_t txIndex) const { +TxBlock Storage::getTxFromBlockWithIndex(View blockData, uint64_t txIndex) const { uint64_t index = 217; // Start of block tx range // Count txs until index. uint64_t currentTx = 0; @@ -131,7 +131,7 @@ std::tuple< const Bytes txData = blocksDb_.get(tx, DBPrefix::txToBlock); if (txData.empty()) return std::make_tuple(nullptr, Hash(), 0u, 0u); - const bytes::View txDataView = txData; + const View txDataView = txData; const Hash blockHash(txDataView.subspan(0, 32)); const uint64_t blockIndex = Utils::bytesToUint32(txDataView.subspan(32, 4)); const uint64_t blockHeight = Utils::bytesToUint64(txDataView.subspan(36, 8)); @@ -149,7 +149,7 @@ std::tuple< const Bytes blockData = blocksDb_.get(blockHash, DBPrefix::blocks); if (blockData.empty()) std::make_tuple(nullptr, Hash(), 0u, 0u); - const uint64_t blockHeight = Utils::bytesToUint64(bytes::View(blockData).subspan(201, 8)); + const uint64_t blockHeight = Utils::bytesToUint64(View(blockData).subspan(201, 8)); return std::make_tuple( std::make_shared(getTxFromBlockWithIndex(blockData, blockIndex)), blockHash, blockIndex, blockHeight diff --git a/src/core/storage.h b/src/core/storage.h index fdd7e621..63395e98 100644 --- a/src/core/storage.h +++ b/src/core/storage.h @@ -37,7 +37,7 @@ class Storage : public Log::LogicalLocationProvider { void initializeBlockchain(); - TxBlock getTxFromBlockWithIndex(bytes::View blockData, uint64_t txIndex) const; + TxBlock getTxFromBlockWithIndex(View blockData, uint64_t txIndex) const; public: /** diff --git a/src/net/p2p/encoding.cpp b/src/net/p2p/encoding.cpp index 8659553d..8eecbbeb 100644 --- a/src/net/p2p/encoding.cpp +++ b/src/net/p2p/encoding.cpp @@ -14,7 +14,7 @@ namespace P2P { // These are shared between messages of various types that share the same encoding and decoding patterns. // ------------------------------------------------------------------------------------------------------------------ - boost::unordered_flat_map nodesFromMessage(bytes::View data) { + boost::unordered_flat_map nodesFromMessage(View data) { boost::unordered_flat_map nodes; size_t index = 0; while (index < data.size()) { @@ -63,7 +63,7 @@ namespace P2P { } } - NodeInfo nodeInfoFromMessage(const bytes::View& data) { + NodeInfo nodeInfoFromMessage(const View& data) { uint64_t nodeVersion = Utils::bytesToUint64(data.subspan(0, 8)); uint64_t nodeEpoch = Utils::bytesToUint64(data.subspan(8, 8)); uint64_t nodeHeight = Utils::bytesToUint64(data.subspan(16, 8)); @@ -93,7 +93,7 @@ namespace P2P { } template - std::vector txsFromMessage(const bytes::View& data, const uint64_t& requiredChainId) { + std::vector txsFromMessage(const View& data, const uint64_t& requiredChainId) { std::vector txs; size_t index = 0; while (index < data.size()) { @@ -101,7 +101,7 @@ namespace P2P { uint32_t txSize = Utils::bytesToUint32(data.subspan(index, 4)); index += 4; if (data.size() - index < txSize) { throw DynamicException("Invalid data size."); } - bytes::View txData = data.subspan(index, txSize); + View txData = data.subspan(index, txSize); index += txSize; // Assuming requiredChainId is declared elsewhere txs.emplace_back(txData, requiredChainId); @@ -127,7 +127,7 @@ namespace P2P { } } - std::vector blocksFromMessage(const bytes::View& data, const uint64_t& requiredChainId) { + std::vector blocksFromMessage(const View& data, const uint64_t& requiredChainId) { std::vector blocks; size_t index = 0; while (index < data.size()) { @@ -135,7 +135,7 @@ namespace P2P { uint64_t blockSize = Utils::bytesToUint64(data.subspan(index, 8)); index += 8; if (data.size() - index < blockSize) { throw DynamicException("Invalid data size."); } - bytes::View blockData = data.subspan(index, blockSize); + View blockData = data.subspan(index, blockSize); index += blockSize; blocks.emplace_back(FinalizedBlock::fromBytes(blockData, requiredChainId)); } @@ -160,7 +160,7 @@ namespace P2P { RequestID RequestID::random() { return RequestID(Utils::randBytes(8)); } - CommandType getCommandType(const bytes::View message) { + CommandType getCommandType(const View message) { if (message.size() != 2) { throw DynamicException("Invalid Command Type size." + std::to_string(message.size())); } uint16_t commandType = Utils::bytesToUint16(message); if (commandType > commandPrefixes.size()) { throw DynamicException("Invalid command type."); } @@ -169,7 +169,7 @@ namespace P2P { const Bytes& getCommandPrefix(const CommandType& commType) { return commandPrefixes[commType]; } - RequestType getRequestType(const bytes::View message) { + RequestType getRequestType(const View message) { if (message.size() != 1) { throw DynamicException("Invalid Request Type size. " + std::to_string(message.size())); } uint8_t requestType = Utils::bytesToUint8(message); if (requestType > typePrefixes.size()) { throw DynamicException("Invalid request type."); } diff --git a/src/net/p2p/encoding.h b/src/net/p2p/encoding.h index ec899005..2b07d34f 100644 --- a/src/net/p2p/encoding.h +++ b/src/net/p2p/encoding.h @@ -98,7 +98,7 @@ namespace P2P { * @param message The message to parse. * @return The request type. */ - RequestType getRequestType(const bytes::View message); + RequestType getRequestType(const View message); /** * Get the 1-byte prefix of a given request inside typePrefixes. @@ -112,7 +112,7 @@ namespace P2P { * @param message The message to parse. * @return The command type. */ - CommandType getCommandType(const bytes::View message); + CommandType getCommandType(const View message); /** * Get the 2-byte prefix of a given command inside commandPrefixes. @@ -587,19 +587,19 @@ namespace P2P { } /// Get the request type of the message. - RequestType type() const { return getRequestType(bytes::View(rawMessage_).subspan(0,1)); } + RequestType type() const { return getRequestType(View(rawMessage_).subspan(0,1)); } /// Get the request ID of the message. - RequestID id() const { return RequestID(bytes::View(rawMessage_).subspan(1, 8)); } + RequestID id() const { return RequestID(View(rawMessage_).subspan(1, 8)); } /// Get the command type of the message. - CommandType command() const { return getCommandType(bytes::View(rawMessage_).subspan(9,2)); } + CommandType command() const { return getCommandType(View(rawMessage_).subspan(9,2)); } /// Get the message data (without the flags and IDs). - bytes::View message() const { return bytes::View(rawMessage_).subspan(11); } + View message() const { return View(rawMessage_).subspan(11); } /// Get the whole message. - bytes::View raw() const { return this->rawMessage_; } + View raw() const { return this->rawMessage_; } /// Get the message's size. size_t size() const { return this->rawMessage_.size(); } diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index 52330730..490d5531 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -21,7 +21,6 @@ set(UTILS_SOURCES ${CMAKE_SOURCE_DIR}/src/utils/address.cpp ${CMAKE_SOURCE_DIR}/src/utils/hash.cpp ${CMAKE_SOURCE_DIR}/src/utils/utils.cpp - ${CMAKE_SOURCE_DIR}/src/utils/strings.cpp ${CMAKE_SOURCE_DIR}/src/utils/hex.cpp ${CMAKE_SOURCE_DIR}/src/utils/merkle.cpp ${CMAKE_SOURCE_DIR}/src/utils/ecdsa.cpp diff --git a/src/utils/address.cpp b/src/utils/address.cpp index 4f9a2306..b5c71f4d 100644 --- a/src/utils/address.cpp +++ b/src/utils/address.cpp @@ -1,5 +1,6 @@ #include "address.h" #include "utils.h" +#include "bytes/hex.h" Hex Address::checksum(View
address) { // Hash requires lowercase address without "0x" @@ -30,6 +31,6 @@ bool Address::isValid(const std::string_view add, bool inBytes) { } bool Address::isChksum(const std::string_view add) { - Address myAdd(add, false); + Address myAdd = bytes::hex(add); return (add == std::string_view(Address::checksum(myAdd))); } diff --git a/src/utils/address.h b/src/utils/address.h index 50ea905a..0ca06c56 100644 --- a/src/utils/address.h +++ b/src/utils/address.h @@ -7,14 +7,14 @@ #include "view.h" #include "zpp_bits.h" -inline constexpr size_t ADDRESS_SIZE = 20; +constexpr size_t ADDRESS_SIZE = 20; /// Abstraction for a single 20-byte address (e.g. "1234567890abcdef...") class Address : public BytesInterface { public: /** - * Constructs address with zeroes + * Constructs an address with all bits clear. */ constexpr Address() : data_() {} @@ -40,14 +40,6 @@ class Address : public BytesInterface { std::ranges::copy(initList, data_.begin()); } - /** - * Copy constructor. - * @param add The address itself. - * @param inBytes If `true`, treats the input as a raw bytes string. - * @throw DynamicException if address has wrong size or is invalid. - */ - Address(const std::string_view add, bool inBytes); - /** * Returns the hexadecimal checksum of the given address representation. * Per [EIP-55](https://eips.ethereum.org/EIPS/eip-55). @@ -92,92 +84,111 @@ class Address : public BytesInterface { std::array data_; }; - /** - * Base class template for identifying address representation types. - * Other address types must specialize. + * Returns the beginning constant iterator of the bytes range. + * @param addr the target bytes range + * @return the beginning constant iterator of the data */ -template -struct IsAddressType : std::is_same {}; +constexpr const Byte* begin(const evmc_address& addr) { return addr.bytes; } /** - * Specialization for evmc::address + * Returns the beginning iterator of the bytes range. + * @param addr the target bytes range + * @return the beginning iterator of the data */ -template<> -struct IsAddressType : std::true_type {}; +constexpr Byte* begin(evmc_address& addr) { return addr.bytes; } /** - * Specialization for evmc_address + * Returns the sentinel constant iterator of the bytes range. + * @param addr the target bytes range + * @return the sentinel constant iterator of the data */ -template<> -struct IsAddressType : std::true_type {}; +constexpr const Byte* end(const evmc_address& addr) { return addr.bytes + ADDRESS_SIZE; } /** - * View of a address representation type. + * Returns the sentinel iterator of the bytes range. + * @param addr the target bytes range + * @return the sentinel iterator of the data */ -template<> -class View
: public BytesInterface, ADDRESS_SIZE> { -public: - - /** - * Constructs a address view from the given input. - * Implicit construction is allowed only for address representation types. - * e.g. Address, evmc_address, and evmc::address - */ - template - explicit(not IsAddressType>::value) - constexpr View(R&& address) : data_(std::forward(address)) {} - - /** - * Returns the beginning constant iterator of the range - * @return the beginning constant iterator of the range - */ - constexpr auto begin() const { return data_.begin(); } - -private: - std::span data_; -}; +constexpr Byte* end(evmc_address& addr) { return addr.bytes + ADDRESS_SIZE; } /** - * This allows the standard algorithms to treat evmc_address as a range of contiguous bytes. - * @param addr the constant address representation - * @return the beginning constant iterator of the address + * Returns the beginning constant iterator of the bytes range. + * @param addr the target bytes range + * @return the beginning constant iterator of the data */ -constexpr const Byte* begin(const evmc_address& addr) { return addr.bytes; } +constexpr const Byte* begin(const evmc::address& addr) { return addr.bytes; } /** - * This allows the standard algorithms to treat evmc_address as a range of contiguous bytes. - * @param addr the address representation - * @return the beginning iterator of the address + * Returns the beginning iterator of the bytes range. + * @param addr the target bytes range + * @return the beginning iterator of the data */ -constexpr Byte* begin(evmc_address& addr) { return addr.bytes; } - +constexpr Byte* begin(evmc::address& addr) { return addr.bytes; } /** - * This allows the standard algorithms to treat evmc_address as a range of contiguous bytes. - * @param addr the address representation - * @return the sentinel constant iterator of the address + * Returns the sentinel constant iterator of the bytes range. + * @param addr the target bytes range + * @return the sentinel constant iterator of the data */ -constexpr const Byte* end(const evmc_address& addr) { return addr.bytes + ADDRESS_SIZE; } +constexpr const Byte* end(const evmc::address& addr) { return addr.bytes + ADDRESS_SIZE; } /** - * This allows the standard algorithms to treat evmc_address as a range of contiguous bytes. - * @param addr the address representation - * @return the sentinel iterator of the address + * Returns the sentinel iterator of the bytes range. + * @param addr the target bytes range + * @return the sentinel iterator of the data */ -constexpr Byte* end(evmc_address& addr) { return addr.bytes + ADDRESS_SIZE; } +constexpr Byte* end(evmc::address& addr) { return addr.bytes + ADDRESS_SIZE; } /** - * This allows the standard algorithms to treat evmc::address as a range of contiguous bytes. - * @param addr the constant address representation - * @return the beginning constant iterator of the address + * View of a address representation type. */ -constexpr const Byte* begin(const evmc::address& addr) { return addr.bytes; } +template<> +class View
: public BytesInterface, ADDRESS_SIZE> { +public: -constexpr Byte* begin(evmc::address& addr) { return addr.bytes; } + /** + * Constructs a address view from the given bytes ranges. + * + * @param range the contiguous and sized range of bytes to be viewed + * @throw invalid argument exception case the range size is incompatible with the view size. + */ + template + explicit constexpr View(R&& range) : data_(range) { + if (const size_t size = std::ranges::size(range); size != ADDRESS_SIZE) { + throw std::invalid_argument("address view requires exactly 20 bytes, but " + std::to_string(size) + " were given"); + } + } -constexpr const Byte* end(const evmc::address& addr) { return addr.bytes + ADDRESS_SIZE; } + /** + * Implicitly contructs an address view from a address object + * + * @param address the address object + */ + constexpr View(const Address& address) : data_(address) {} -constexpr Byte* end(evmc::address& addr) { return addr.bytes + ADDRESS_SIZE; } + /** + * Implictly constructs an address view from the evmc address type. + * + * @param address te address object + */ + constexpr View(const evmc_address& address) : data_(address) {} + + /** + * Implictly constructs an address view from the evmc address type. + * + * @param address te address object + */ + constexpr View(const evmc::address& address) : data_(address) {} + + /** + * Returns the beginning constant iterator of the range + * @return the beginning constant iterator of the range + */ + constexpr auto begin() const { return data_.begin(); } + +private: + std::span data_; +}; #endif // BDK_UTILS_ADDRESS_H diff --git a/src/utils/bytesinterface.h b/src/utils/bytesinterface.h index dd6dab1b..2fb81488 100644 --- a/src/utils/bytesinterface.h +++ b/src/utils/bytesinterface.h @@ -27,7 +27,7 @@ class BytesInterface { /** * Helper constructor from bytes initializer. */ - explicit constexpr BytesInterface(const bytes::Initializer auto& initializer) { + explicit constexpr BytesInterface(bytes::Initializer auto&& initializer) { initializer.to(self()); } @@ -92,7 +92,12 @@ class BytesInterface { /** * @return size of the range. */ - constexpr auto size() const { return std::distance(self().begin(), self().end()); } + constexpr size_t size() const { + if constexpr (N == std::dynamic_extent) + return std::distance(self().begin(), self().end()); + else + return N; + } /** * @return pointer to the beginning of the range. @@ -132,19 +137,19 @@ class BytesInterface { * @return a view from the given position and with the given length * @throw out of range exception if given position or length are invalid */ - constexpr bytes::View view(size_t pos, size_t len) const { + constexpr View view(size_t pos, size_t len) const { const size_t real_len = std::min(len, N - pos); if (pos + real_len > size()) { throw std::out_of_range("len greater than size"); } - return bytes::View(self().begin() + pos, self().begin() + pos + real_len); + return View(self().begin() + pos, self().begin() + pos + real_len); } - constexpr bytes::View view(size_t pos) const { return view(pos, size()); } + constexpr View view(size_t pos) const { return view(pos, size()); } - constexpr bytes::View view() const { return view(0); } + constexpr View view() const { return view(0); } /** * Transforms the range to Bytes @@ -156,7 +161,6 @@ class BytesInterface { return res; } - private: constexpr T& self() { return static_cast(*this); } constexpr const T& self() const { return static_cast(*this); } diff --git a/src/utils/db.h b/src/utils/db.h index a0aeb4cc..f97c647c 100644 --- a/src/utils/db.h +++ b/src/utils/db.h @@ -92,7 +92,7 @@ class DBBatch { * @param value The entry's value. * @param prefix The entry's prefix. */ - void push_back(const bytes::View key, const bytes::View value, const Bytes& prefix) { + void push_back(const View key, const View value, const Bytes& prefix) { Bytes tmp = prefix; tmp.reserve(prefix.size() + key.size()); tmp.insert(tmp.end(), key.begin(), key.end()); @@ -110,7 +110,7 @@ class DBBatch { * @param key The entry's key. * @param prefix The entry's prefix. */ - void delete_key(const bytes::View key, const Bytes& prefix) { + void delete_key(const View key, const Bytes& prefix) { Bytes tmp = prefix; tmp.reserve(prefix.size() + key.size()); tmp.insert(tmp.end(), key.begin(), key.end()); diff --git a/src/utils/finalizedblock.cpp b/src/utils/finalizedblock.cpp index f7328a71..ed3cfe95 100644 --- a/src/utils/finalizedblock.cpp +++ b/src/utils/finalizedblock.cpp @@ -8,7 +8,7 @@ See the LICENSE.txt file in the project root for more information. #include "finalizedblock.h" #include "../core/rdpos.h" -FinalizedBlock FinalizedBlock::fromBytes(const bytes::View bytes, const uint64_t& requiredChainId) { +FinalizedBlock FinalizedBlock::fromBytes(const View bytes, const uint64_t& requiredChainId) { try { // Verify minimum size for a valid block SLOGTRACE("Deserializing block..."); @@ -129,7 +129,7 @@ FinalizedBlock FinalizedBlock::fromBytes(const bytes::View bytes, const uint64_t if (expectedRandomness != blockRandomness) throw std::invalid_argument("Invalid block randomness"); /// Block header to hash is the 144 after the signature - bytes::View headerBytes = bytes.subspan(65, 144); + View headerBytes = bytes.subspan(65, 144); Hash hash = Utils::sha3(headerBytes); UPubKey validatorPubKey = Secp256k1::recover(validatorSig, hash); return { diff --git a/src/utils/finalizedblock.h b/src/utils/finalizedblock.h index 32a4b600..f40627f8 100644 --- a/src/utils/finalizedblock.h +++ b/src/utils/finalizedblock.h @@ -129,7 +129,7 @@ class FinalizedBlock { * @return A FinalizedBlock instance. * @throw std::domain_error if deserialization fails for some reason. */ - static FinalizedBlock fromBytes(const bytes::View bytes, const uint64_t& requiredChainId); + static FinalizedBlock fromBytes(const View bytes, const uint64_t& requiredChainId); /** * Serialize the entire block (including the header) to a raw bytes string. diff --git a/src/utils/fixedbytes.h b/src/utils/fixedbytes.h new file mode 100644 index 00000000..4f8a0fa3 --- /dev/null +++ b/src/utils/fixedbytes.h @@ -0,0 +1,47 @@ +#ifndef BDK_UTILS_FIXEDBYTES_H +#define BDK_UTILS_FIXEDBYTES_H + +#include "bytesinterface.h" +#include "hex.h" + +/** + * Abstraction of a fixed-size bytes container. + * `FixedBytes<10>` would have *exactly* 10 bytes, no more, no less. + * Used as a base for both aliases (e.g. PrivKey, PubKey, etc.) and classes inheriting it (e.g. Hash, Signature, etc.). + */ +template +class FixedBytes : public BytesInterface, N> { +public: + constexpr FixedBytes() : data_() {} + + constexpr FixedBytes(std::initializer_list initList) { + if (initList.size() != N) + throw DynamicException("Given initializer list of size " + std::to_string(initList.size()) + + " is not suitable for initializing a FixedBytes<" + std::to_string(N) + ">"); + + std::ranges::copy(initList, data_.begin()); + } + + constexpr FixedBytes(bytes::Initializer auto&& initializer) { initializer.to(data_); } + + explicit constexpr FixedBytes(const bytes::Range auto& input) { + if (const size_t size = std::ranges::size(input); size != N) { + throw std::invalid_argument("Given bytes range of size " + std::to_string(size) + + " is not suitable for initializing a FixedBytes<" + std::to_string(N) + ">"); + } + + std::ranges::copy(input, data_.begin()); + } + + constexpr auto begin() { return data_.begin(); } + + constexpr auto begin() const { return data_.begin(); } + +private: + BytesArr data_; + + friend zpp::bits::access; + using serialize = zpp::bits::members<1>; +}; + +#endif // BDK_UTILS_FIXEDBYTES_H diff --git a/src/utils/hash.h b/src/utils/hash.h index 4c190cac..68f3ef6a 100644 --- a/src/utils/hash.h +++ b/src/utils/hash.h @@ -7,24 +7,49 @@ #include "view.h" #include "zpp_bits.h" -inline constexpr size_t HASH_SIZE = 32; +constexpr size_t HASH_SIZE = 32; +/// Abstraction of a 32-byte hash. class Hash : public BytesInterface { public: + /** + * Initializes the hash object with all bits clear. + */ constexpr Hash() : data_() {} + /** + * Constructs a hash from the given argument. The argument + * must be able to initialize a BytesInterface. Refer to BytesInterface + * for checking constructor overloads. + * + * @param input the argument capable of constructing a BytesInterface + */ template requires std::constructible_from, I&&> explicit (not bytes::Initializer) constexpr Hash(I&& input) : BytesInterface(std::forward(input)) {} + /** + * Constructs a hash using the bits of the unsigned 256 bits integer. + * @param value the 256 bits unsigned integer value + */ explicit Hash(const uint256_t& value); + /** + * Converts the 256 bits of the hash into a unsigned integer representation. + * @return the uint256_t representation + */ explicit operator uint256_t() const; + /** + * @return the beginning iterator of the hash bytes + */ constexpr auto begin() { return data_.begin(); } + /** + * @return the beginning constant iterator of the hash bytes + */ constexpr auto begin() const { return data_.begin(); } private: @@ -34,74 +59,126 @@ class Hash : public BytesInterface { std::array data_; }; -template -struct is_hash_representation : std::is_same {}; - -template<> -struct is_hash_representation : std::true_type {}; - -template<> -struct is_hash_representation : std::true_type {}; - -template -inline constexpr bool is_hash_representation_v = is_hash_representation::value; +/** + * Returns the beginning constant iterator of the bytes range. + * @param bytes32 the target bytes range + * @return the beginning constant iterator of the data + */ +constexpr const Byte* begin(const evmc_bytes32& bytes32) { return bytes32.bytes; } -template<> -class View : public BytesInterface, HASH_SIZE> { -public: +/** + * Returns the beginning iterator of the bytes range. + * @param bytes32 the target bytes range + * @return the beginning iterator of the data + */ +constexpr Byte* begin(evmc_bytes32& bytes32) { return bytes32.bytes; } - template - explicit(not is_hash_representation_v>) - constexpr View(R&& input) : data_(std::forward(input)) {} - constexpr auto begin() const { return data_.begin(); } +/** + * Returns the sentinel constant iterator of the bytes range. + * @param bytes32 the target bytes range + * @return the sentinel constant iterator of the data + */ +constexpr const Byte* end(const evmc_bytes32& bytes32) { return bytes32.bytes + HASH_SIZE; } - explicit operator uint256_t() const; +/** + * Returns the sentinel iterator of the bytes range. + * @param bytes32 the target bytes range + * @return the sentinel iterator of the data + */ +constexpr Byte* end(evmc_bytes32& bytes32) { return bytes32.bytes + HASH_SIZE; } -private: - std::span data_; -}; /** - * This allows the standard algorithms to treat evmc_bytes32 as a range of contiguous bytes. - * @param hash the constant address representation - * @return the beginning constant iterator of the address + * Returns the beginning constant iterator of the bytes range. + * @param bytes32 the target bytes range + * @return the beginning constant iterator of the data */ -constexpr const Byte* begin(const evmc_bytes32& hash) { return hash.bytes; } +constexpr const Byte* begin(const evmc::bytes32& hash) { return hash.bytes; } /** - * This allows the standard algorithms to treat evmc_bytes32 as a range of contiguous bytes. - * @param hash the address representation - * @return the beginning iterator of the address + * Returns the beginning iterator of the bytes range. + * @param bytes32 the target bytes range + * @return the beginning iterator of the data */ -constexpr Byte* begin(evmc_bytes32& hash) { return hash.bytes; } - +constexpr Byte* begin(evmc::bytes32& bytes32) { return bytes32.bytes; } /** - * This allows the standard algorithms to treat evmc_bytes32 as a range of contiguous bytes. - * @param hash the address representation - * @return the sentinel constant iterator of the address + * Returns the sentinel constant iterator of the bytes range. + * @param bytes32 the target bytes range + * @return the sentinel constant iterator of the data */ -constexpr const Byte* end(const evmc_bytes32& hash) { return hash.bytes + HASH_SIZE; } +constexpr const Byte* end(const evmc::bytes32& bytes32) { return bytes32.bytes + HASH_SIZE; } /** - * This allows the standard algorithms to treat evmc_bytes32 as a range of contiguous bytes. - * @param hash the address representation - * @return the sentinel iterator of the address + * Returns the sentinel iterator of the bytes range. + * @param bytes32 the target bytes range + * @return the sentinel iterator of the data */ -constexpr Byte* end(evmc_bytes32& hash) { return hash.bytes + HASH_SIZE; } +constexpr Byte* end(evmc::bytes32& bytes32) { return bytes32.bytes + HASH_SIZE; } /** - * This allows the standard algorithms to treat evmc::bytes32 as a range of contiguous bytes. - * @param hash the constant address representation - * @return the beginning constant iterator of the address + * Views a type as a hash type. */ -constexpr const Byte* begin(const evmc::bytes32& hash) { return hash.bytes; } +template<> +class View : public BytesInterface, HASH_SIZE> { +public: -constexpr Byte* begin(evmc::bytes32& hash) { return hash.bytes; } + /** + * Explicitly constructs a hash view from a borrowed and contiguous + * range of exactly 32 bytes. Pretty much any type that follows + * these constraints can be seen as a hash view. + * + * @param range the target sized, contiguous, and borrowed bytes range + * @throw invalid argument exception if the input range has incorrect size + */ + template + explicit constexpr View(R&& range) : data_(range) { + if (const size_t size = std::ranges::size(range); size != HASH_SIZE) { + throw std::invalid_argument("hash view requires exactly 32 bytes, but " + std::to_string(size) + " were given"); + } + } + + /** + * Constructs a view from a const reference to a hash. + * Implicit construction is allowed. + * + * @param hash the target hash + */ + constexpr View(const Hash& hash) : data_(hash) {} + + /** + * Constructs a view from a const reference to a evmc bytes32. + * Implicit construction is allowed. + * + * @param bytes32 the target bytes + */ + constexpr View(const evmc_bytes32& bytes32) : data_(bytes32) {} + + /** + * Constructs a view from a const reference to a evmc bytes32. + * Implicit construction is allowed. + * + * @param bytes32 the target bytes + */ + constexpr View(const evmc::bytes32& bytes32) : data_(bytes32) {} + + /** + * Returns the beginning constant iterator of this range + * + * @return the range beginnig constant iterator + */ + constexpr auto begin() const { return data_.begin(); } -constexpr const Byte* end(const evmc::bytes32& hash) { return hash.bytes + HASH_SIZE; } + /** + * Casts this hash bits to a unsigned 256-bits integer + * + * @return the unsigned 256-bits integer + */ + explicit operator uint256_t() const; -constexpr Byte* end(evmc::bytes32& hash) { return hash.bytes + HASH_SIZE; } +private: + std::span data_; +}; #endif // BDK_UTILS_HASH_H diff --git a/src/utils/hex.cpp b/src/utils/hex.cpp index 1cb45f3e..df71f3f8 100644 --- a/src/utils/hex.cpp +++ b/src/utils/hex.cpp @@ -44,7 +44,7 @@ bool Hex::isValid(const std::string_view hex, bool strict) { return true; } -Hex Hex::fromBytes(const std::span bytes, bool strict) { +Hex Hex::fromBytes(View bytes, bool strict) { auto beg = bytes.begin(); auto end = bytes.end(); static const char* digits = "0123456789abcdef"; diff --git a/src/utils/hex.h b/src/utils/hex.h index 05d93869..c5a28b75 100644 --- a/src/utils/hex.h +++ b/src/utils/hex.h @@ -21,6 +21,7 @@ See the LICENSE.txt file in the project root for more information. #include "bytes/view.h" #include "bytes.h" +#include "utils/view.h" template using BytesArr = std::array; @@ -62,7 +63,7 @@ class Hex { * @param strict (optional) If `true`, includes "0x". Defaults to `false`. * @return The constructed Hex object. */ - static Hex fromBytes(const bytes::View bytes, bool strict = false); + static Hex fromBytes(const View bytes, bool strict = false); /** * Build a Hex object from a UTF-8 string ("example" = "6578616d706c65"). @@ -112,7 +113,7 @@ class Hex { inline uint256_t getUint() const { Bytes b = Hex::toBytes(this->hex_); if (b.size() > 32) throw std::length_error("Hex too big for uint conversion"); - bytes::View bV(b.data(), b.size()); + View bV(b.data(), b.size()); uint256_t ret; boost::multiprecision::import_bits(ret, bV.begin(), bV.end(), 8); return ret; diff --git a/src/utils/safehash.h b/src/utils/safehash.h index 9199e3c6..d9950672 100644 --- a/src/utils/safehash.h +++ b/src/utils/safehash.h @@ -10,6 +10,7 @@ See the LICENSE.txt file in the project root for more information. #include #include +#include #include "../bytes/join.h" #include "../libs/wyhash.h" @@ -23,6 +24,8 @@ See the LICENSE.txt file in the project root for more information. * We use the highest and fastest quality hash function available for size_t (64-bit) hashes (Wyhash) */ struct SafeHash { + using is_transparent = void; + ///@{ /** Wrapper for `splitmix()`. */ size_t operator()(const uint64_t& i) const { @@ -41,38 +44,25 @@ struct SafeHash { return wyhash(str.data(), str.size(), 0, _wyp); } - size_t operator()(const Bytes& bytes) const { - return wyhash(std::bit_cast(bytes.data()), bytes.size(), 0, _wyp); - } - - template size_t operator()(const BytesArr& bytesArr) const { - return wyhash(std::bit_cast(bytesArr.data()), bytesArr.size(), 0, _wyp); - } - - size_t operator()(const bytes::View& bytesArrView) const { - return wyhash(std::bit_cast(bytesArrView.data()), bytesArrView.size(), 0, _wyp); - } - - size_t operator()(const Address& address) const { - return wyhash(std::bit_cast(address.data()), address.size(), 0, _wyp); - } - size_t operator()(const Functor& functor) const { return functor.value; // Functor is already a hash. Just return it. } - size_t operator()(const Hash& hash) const { - return wyhash(std::bit_cast(hash.data()), hash.size(), 0, _wyp); - } - size_t operator()(const TxValidator& tx) const { return SafeHash()(tx.hash()); } template size_t operator()(const std::shared_ptr& ptr) const { return SafeHash()(*ptr->get()); } - template size_t operator()(const FixedBytes& bytes) const { - return wyhash(std::bit_cast(bytes.data()), bytes.size(), 0, _wyp); + size_t operator()(View data) const { + return wyhash(data.data(), data.size(), 0, _wyp); + } + + template + size_t operator()(const std::pair& pair) const { + size_t hash = (*this)(pair.first); + boost::hash_combine(hash, pair.second); + return hash; } template size_t operator()(const boost::unordered_flat_map& a) const { @@ -97,6 +87,18 @@ struct SafeHash { ///@} }; +struct SafeCompare { + using is_transparent = void; + + constexpr bool operator()(View lhs, View rhs) const { + return std::ranges::equal(lhs, rhs); + } + + constexpr bool operator()(const std::pair, View>& lhs, const std::pair, View>& rhs) const { + return (*this)(lhs.first, rhs.first) && (*this)(lhs.second, rhs.second); + } +}; + /** * [Fowler-Noll-Vo](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function) * hash struct used within broadcast messages. @@ -108,7 +110,7 @@ struct FNVHash { * Call operator. * @param s The string to hash. */ - size_t operator()(bytes::View s) const { + size_t operator()(View s) const { size_t result = 2166136261U; for (auto it = s.begin(); it != s.end(); it++) result = (16777619 * result) ^ (*it); return result; diff --git a/src/utils/strings.cpp b/src/utils/strings.cpp deleted file mode 100644 index 51986c7d..00000000 --- a/src/utils/strings.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright (c) [2023-2024] [AppLayer Developers] - -This software is distributed under the MIT License. -See the LICENSE.txt file in the project root for more information. -*/ - -#include "strings.h" -#include "utils.h" - -Address::Address(const std::string_view add, bool inBytes) { - if (inBytes) { - if (add.size() != 20) throw std::invalid_argument("Address must be 20 bytes long."); - std::ranges::copy(add, this->begin()); - } else { - if (!Address::isValid(add, false)) throw std::invalid_argument("Invalid Hex address."); - auto bytes = Hex::toBytes(add); - std::ranges::copy(bytes, this->begin()); - } -} - -StorageKey::StorageKey(const evmc::address& addr, const evmc::bytes32& slot) { - // Copy the data from the evmc::address struct to this->data_ - std::copy_n(addr.bytes, 20, this->begin()); - // Copy the data from the evmc::bytes32 struct to this->data_ - std::copy_n(slot.bytes, 32, this->begin() + 20); -} - -StorageKey::StorageKey(const evmc_address& addr, const evmc_bytes32& slot) { - // Copy the data from the evmc::address struct to this->data_ - std::copy_n(addr.bytes, 20, this->begin()); - // Copy the data from the evmc::bytes32 struct to this->data_ - std::copy_n(slot.bytes, 32, this->begin() + 20); -} - -StorageKey::StorageKey(const evmc_address& addr, const evmc::bytes32& slot) { - // Copy the data from the evmc::address struct to this->data_ - std::copy_n(addr.bytes, 20, this->begin()); - // Copy the data from the evmc::bytes32 struct to this->data_ - std::copy_n(slot.bytes, 32, this->begin() + 20); -} - -StorageKey::StorageKey(const evmc::address& addr, const evmc_bytes32& slot) { - // Copy the data from the evmc::address struct to this->data_ - std::copy_n(addr.bytes, 20, this->begin()); - // Copy the data from the evmc::bytes32 struct to this->data_ - std::copy_n(slot.bytes, 32, this->begin() + 20); -} - -StorageKey::StorageKey(const Address& addr, const Hash& slot) { - // Copy the data from the evmc::address struct to this->data_ - std::copy_n(addr.cbegin(), 20, this->begin()); - // Copy the data from the evmc::bytes32 struct to this->data_ - std::copy_n(slot.cbegin(), 32, this->begin() + 20); -} - diff --git a/src/utils/strings.h b/src/utils/strings.h index c8c051fd..87ce7b68 100644 --- a/src/utils/strings.h +++ b/src/utils/strings.h @@ -19,121 +19,14 @@ See the LICENSE.txt file in the project root for more information. #include "bytes/initializer.h" #include "zpp_bits.h" +#include "fixedbytes.h" #include "address.h" #include "hash.h" #include "signature.h" -// TODO: It is possible to implement **fast** operators for some types, -// such as Address, Functor and Hash. Taking advantage that memory located within -// the array are contiguous, we can cast the data to a pointer of native types -// (such as uint64_t*) and compare them faster than using a for loop. Example: -// // Fast equality operator for h256. -// template<> inline bool FixedHash<32>::operator==(FixedHash<32> const& _other) const -// { -// const uint64_t* hash1 = (const uint64_t*)data(); -// const uint64_t* hash2 = (const uint64_t*)_other.data(); -// return (hash1[0] == hash2[0]) && (hash1[1] == hash2[1]) && (hash1[2] == hash2[2]) && (hash1[3] == hash2[3]); -// } +using StorageKey = std::pair; +using StorageKeyView = std::pair, View>; -/** - * Abstraction of a fixed-size bytes container. - * `FixedBytes<10>` would have *exactly* 10 bytes, no more, no less. - * Used as a base for both aliases (e.g. PrivKey, PubKey, etc.) and classes inheriting it (e.g. Hash, Signature, etc.). - */ -template class FixedBytes { - private: - BytesArr data_; - - friend zpp::bits::access; - using serialize = zpp::bits::members<1>; - - public: - constexpr FixedBytes() : data_() {}; - - constexpr FixedBytes(std::initializer_list initList) { - if (initList.size() != N) - throw DynamicException("Given initializer list of size " + std::to_string(initList.size()) + - " is not suitable for initializing a FixedBytes<" + std::to_string(N) + ">"); - - std::ranges::copy(initList, data_.begin()); - } - - constexpr FixedBytes(const bytes::Initializer auto& initializer) { initializer.to(data_); } - - constexpr explicit FixedBytes(const bytes::Range auto& data) { - if (const size_t size = std::ranges::size(data); size != N) - throw DynamicException("Given bytes range of size " + std::to_string(size) + - " is not suitable for initializing a FixedBytes<" + std::to_string(N) + ">"); - - std::ranges::copy(data, data_.begin()); - } - - constexpr auto begin() { return data_.begin(); } - - constexpr auto begin() const { return data_.begin(); } - - constexpr auto cbegin() const { return data_.cbegin(); } - - constexpr auto end() { return data_.end(); } - - constexpr auto end() const { return data_.end(); } - - constexpr auto cend() const { return data_.cend(); } - - constexpr Byte* data() { return data_.data(); } - - constexpr const Byte* data() const { return data_.data(); } - - constexpr size_t size() const { return data_.size(); } - - constexpr Byte& operator[](size_t index) { return data_[index]; } - - constexpr const Byte& operator[](size_t index) const { return data_[index]; } - - /** - * Getter for `data_`, but returns it as a hex string. - * @param strict If `true`, returns the value with an appended "0x" prefix. - */ - inline Hex hex(bool strict = false) const { return Hex::fromBytes(this->view(), strict); } - - /** - * Getter for `data_`, but returns it as a span of the data string. - * @param pos (optional) Index to start getting chars from. Defaults to the start of the string. - * @param len (optional) Number of chars to get. Defaults to the whole string. - * @return A string view of the data, in bytes. - */ - inline bytes::View view(size_t pos = 0, size_t len = N) const { - auto real_len = std::min(len, N - pos); - if (pos + real_len > N) { throw std::out_of_range("len > N"); } - return bytes::View(this->data_.begin() + pos, this->data_.begin() + pos + real_len); - } - - /// Create a Bytes object from the internal data string. - inline Bytes asBytes() const { return Bytes(this->data_.begin(), this->data_.end()); } - - /// Equality operator. Checks if both internal strings are the same. - inline bool operator==(const FixedBytes& other) const { return (this->data_ == other.data_); } - - /// Lesser operator. Does a lexicographical check on both data strings. - inline bool operator<(const FixedBytes& other) const { return (this->data_ < other.data_); } - - /// Greater-or-equal operator. Does a lexicographical check on both data strings. - inline bool operator>=(const FixedBytes& other) const { return (this->data_ >= other.data_); } - - /// Lesser-or-equal operator. Does a lexicographical check on both data strings. - inline bool operator<=(const FixedBytes& other) const { return (this->data_ <= other.data_); } - - /// Greater operator. Does a lexicographical check on both data strings. - inline bool operator>(const FixedBytes& other) const { return (this->data_ > other.data_); } - - /** - * Operator for checking the string's "real emptyness" (all zeroes). - * @return `true` if string is an empty value, `false` otherwise. - */ - explicit operator bool() const { - return std::ranges::any_of(*this, [] (Byte b) { return b != 0; }); - } -}; /// Abstraction of a functor (the first 4 bytes of a function's keccak hash). struct Functor { @@ -141,59 +34,4 @@ struct Functor { inline bool operator==(const Functor& other) const { return this->value == other.value; } }; -/// Abstraction of a EVM Storage key (20-bytes address + 32 bytes slot key). Inherits `FixedBytes<52>`. -class StorageKey : public FixedBytes<52> { - public: - using FixedBytes<52>::operator<; - using FixedBytes<52>::operator<=; - using FixedBytes<52>::operator>; - using FixedBytes<52>::operator>=; - using FixedBytes<52>::operator=; - - /** - * Constructor using a bytes::View - * @param data The bytes::View pointer to convert into a storage key. - */ - StorageKey(const bytes::View& data) { - if (data.size() != 52) throw std::invalid_argument("Invalid StorageKey size."); - std::copy(data.begin(), data.end(), this->begin()); - } - - /** - * Constructor using a reference to evmc::address and a reference to evmc::bytes32. - * @param addr The evmc::address pointer to convert into a storage key. - * @param slot The evmc::bytes32 pointer to convert into a storage key. - */ - StorageKey(const evmc::address& addr, const evmc::bytes32& slot); - - /** - * Constructor using a reference to evmc_address and a reference to evmc_bytes32. - * @param addr The evmc_address pointer to convert into a storage key. - * @param slot The evmc::bytes32 pointer to convert into a storage key. - */ - StorageKey(const evmc_address& addr, const evmc_bytes32& slot); - - /** - * Constructor using a reference to evmc_address and a reference to evmc::bytes32. - * @param addr The evmc::address pointer to convert into a storage key. - * @param slot The evmc::bytes32 pointer to convert into a storage key. - */ - StorageKey(const evmc_address& addr, const evmc::bytes32& slot); - - /** - * Constructor using a reference to evmc::address and a reference to evmc_bytes32. - * @param addr The evmc_address pointer to convert into a storage key. - * @param slot The evmc::bytes32 pointer to convert into a storage key. - */ - StorageKey(const evmc::address& addr, const evmc_bytes32& slot); - - /** - * Constructor using a reference to Address and a reference to Hash. - * @param addr The Address pointer to convert into a storage key. - * @param slot The Hash pointer to convert into a storage key. - */ - StorageKey(const Address& addr, const Hash& slot); -}; - - #endif // STRINGS_H diff --git a/src/utils/tx.cpp b/src/utils/tx.cpp index c13f8067..072f34ce 100644 --- a/src/utils/tx.cpp +++ b/src/utils/tx.cpp @@ -8,9 +8,9 @@ See the LICENSE.txt file in the project root for more information. #include "tx.h" #include "bytes/cast.h" -TxBlock::TxBlock(const bytes::View bytes, const uint64_t&) { +TxBlock::TxBlock(const View bytes, const uint64_t&) { uint64_t index = 0; - bytes::View txData = bytes.subspan(1); + View txData = bytes.subspan(1); // Check if Tx is type 2 and if first byte is equal or higher than 0xf7, meaning it is a list if (bytes[0] != 0x02) throw DynamicException("Tx is not type 2"); @@ -80,7 +80,7 @@ TxBlock::TxBlock( this->hash_ = Utils::sha3(this->rlpSerialize(true)); // Include signature } -void TxBlock::parseChainId(bytes::View txData, uint64_t& index) { +void TxBlock::parseChainId(View txData, uint64_t& index) { // If chainId > 0, get chainId from string. // chainId can be a small string or the byte itself if ( @@ -98,7 +98,7 @@ void TxBlock::parseChainId(bytes::View txData, uint64_t& index) { } } -void TxBlock::parseNonce(bytes::View txData, uint64_t& index) { +void TxBlock::parseNonce(View txData, uint64_t& index) { // If nonce > 0, get nonce from string. // nonce can be a small string or the byte itself if ( @@ -116,7 +116,7 @@ void TxBlock::parseNonce(bytes::View txData, uint64_t& index) { } } -void TxBlock::parseMaxPriorityFeePerGas(bytes::View txData, uint64_t& index) { +void TxBlock::parseMaxPriorityFeePerGas(View txData, uint64_t& index) { // If maxPriorityFeePerGas > 0, get maxPriorityFeePerGas from string. // maxPriorityFeePerGas can be a small string or the byte itself if ( @@ -136,7 +136,7 @@ void TxBlock::parseMaxPriorityFeePerGas(bytes::View txData, uint64_t& index) { } } -void TxBlock::parseMaxFeePerGas(bytes::View txData, uint64_t& index) { +void TxBlock::parseMaxFeePerGas(View txData, uint64_t& index) { // If maxFeePerGas > 0, get nonce from string. // maxFeePerGas can be a small string or the byte itself if ( @@ -154,7 +154,7 @@ void TxBlock::parseMaxFeePerGas(bytes::View txData, uint64_t& index) { } } -void TxBlock::parseGasLimit(bytes::View txData, uint64_t& index) { +void TxBlock::parseGasLimit(View txData, uint64_t& index) { // If gasLimit > 0, get gasLimit from string. // gasLimit can be a small string or the byte itself if ( @@ -172,7 +172,7 @@ void TxBlock::parseGasLimit(bytes::View txData, uint64_t& index) { } } -void TxBlock::parseTo(bytes::View txData, uint64_t& index) { +void TxBlock::parseTo(View txData, uint64_t& index) { // Get receiver address (to) - small string. // It can either be 20 bytes or 0x80 (empty string, Address()). Anything else is invalid. uint8_t toLength = txData[index] - 0x80; @@ -188,7 +188,7 @@ void TxBlock::parseTo(bytes::View txData, uint64_t& index) { } } -void TxBlock::parseValue(bytes::View txData, uint64_t& index) { +void TxBlock::parseValue(View txData, uint64_t& index) { // Get value - small string or byte itself. if ( uint8_t valueLength = (txData[index]) >= 0x80 ? txData[index] - 0x80 : 0; @@ -205,7 +205,7 @@ void TxBlock::parseValue(bytes::View txData, uint64_t& index) { } } -void TxBlock::parseData(bytes::View txData, uint64_t& index) { +void TxBlock::parseData(View txData, uint64_t& index) { // Get data - it can be anything really, from nothing (0x80) to a big string (0xb7) if (uint8_t(txData[index]) < 0x80) { this->data_.assign(txData.begin() + index, txData.begin() + index + 1); @@ -229,13 +229,13 @@ void TxBlock::parseData(bytes::View txData, uint64_t& index) { } } -void TxBlock::parseAccessList(bytes::View txData, uint64_t& index) const { +void TxBlock::parseAccessList(View txData, uint64_t& index) const { // Get access list - ALWAYS 0xc0 (empty list) if (txData[index] != 0xc0) throw DynamicException("Access list is not empty"); index++; // Index at rlp[9] size } -void TxBlock::parseVRS(bytes::View txData, uint64_t& index) { +void TxBlock::parseVRS(View txData, uint64_t& index) { // Get v - always byte itself (1 byte) if (txData[index] == 0x80) { this->v_ = 0; @@ -489,7 +489,7 @@ evmc_message TxBlock::txToMessage() const { return msg; } -TxValidator::TxValidator(const bytes::View bytes, const uint64_t&) { +TxValidator::TxValidator(const View bytes, const uint64_t&) { uint64_t index = 0; // Check if first byte is equal or higher than 0xf7, meaning it is a list @@ -560,7 +560,7 @@ TxValidator::TxValidator( this->hash_ = Utils::sha3(this->rlpSerialize(true)); // Include signature } -void TxValidator::parseData(bytes::View bytes, uint64_t& index) { +void TxValidator::parseData(View bytes, uint64_t& index) { // Get data - it can be anything really, from nothing (0x80) to a big string (0xb7) if (uint8_t(bytes[index]) < 0x80) { this->data_.assign(bytes.begin() + index, bytes.begin() + index + 1); @@ -582,7 +582,7 @@ void TxValidator::parseData(bytes::View bytes, uint64_t& index) { } } -void TxValidator::parseNHeight(bytes::View bytes, uint64_t& index) { +void TxValidator::parseNHeight(View bytes, uint64_t& index) { // Get nHeight - can be a small string or the byte itself if ( const uint8_t nHeightLength = (bytes[index] >= 0x80) ? bytes[index] - 0x80 : 0; @@ -598,7 +598,7 @@ void TxValidator::parseNHeight(bytes::View bytes, uint64_t& index) { } } -void TxValidator::parseVRS(bytes::View bytes, uint64_t& index) { +void TxValidator::parseVRS(View bytes, uint64_t& index) { // Get v - small string or the byte itself if ( uint8_t vLength = (bytes[index] >= 0x80) ? bytes[index] - 0x80 : 0; diff --git a/src/utils/tx.h b/src/utils/tx.h index 233c78fb..c8a00386 100644 --- a/src/utils/tx.h +++ b/src/utils/tx.h @@ -44,16 +44,16 @@ class TxBlock { * @param txData The raw data to parse. * @param index The index to start parsing. */ - void parseChainId(bytes::View txData, uint64_t& index); - void parseNonce(bytes::View txData, uint64_t& index); - void parseMaxPriorityFeePerGas(bytes::View txData, uint64_t& index); - void parseMaxFeePerGas(bytes::View txData, uint64_t& index); - void parseGasLimit(bytes::View txData, uint64_t& index); - void parseTo(bytes::View txData, uint64_t& index); - void parseValue(bytes::View txData, uint64_t& index); - void parseData(bytes::View txData, uint64_t& index); - void parseAccessList(bytes::View txData, uint64_t& index) const; // We don't support access lists, therefore we don't alter the object - void parseVRS(bytes::View txData, uint64_t& index); + void parseChainId(View txData, uint64_t& index); + void parseNonce(View txData, uint64_t& index); + void parseMaxPriorityFeePerGas(View txData, uint64_t& index); + void parseMaxFeePerGas(View txData, uint64_t& index); + void parseGasLimit(View txData, uint64_t& index); + void parseTo(View txData, uint64_t& index); + void parseValue(View txData, uint64_t& index); + void parseData(View txData, uint64_t& index); + void parseAccessList(View txData, uint64_t& index) const; // We don't support access lists, therefore we don't alter the object + void parseVRS(View txData, uint64_t& index); ///@} ///@{ @@ -81,7 +81,7 @@ class TxBlock { * @param requiredChainId The chain ID of the transaction. * @throw DynamicException on any parsing failure. */ - TxBlock(const bytes::View bytes, const uint64_t& requiredChainId); + TxBlock(const View bytes, const uint64_t& requiredChainId); /** * Manual constructor. Leave fields blank ("" or 0) if they're not required. @@ -234,9 +234,9 @@ class TxValidator { * @param bytes The raw data to parse. * @param index The index to start parsing. */ - void parseData(bytes::View bytes, uint64_t& index); - void parseNHeight(bytes::View bytes, uint64_t& index); - void parseVRS(bytes::View bytes, uint64_t& index); + void parseData(View bytes, uint64_t& index); + void parseNHeight(View bytes, uint64_t& index); + void parseVRS(View bytes, uint64_t& index); ///@} ///@{ @@ -259,7 +259,7 @@ class TxValidator { * @param requiredChainId The chain ID of the transaction. * @throw DynamicException on any parsing failure. */ - TxValidator(const bytes::View bytes, const uint64_t& requiredChainId); + TxValidator(const View bytes, const uint64_t& requiredChainId); /** * Manual constructor. Leave fields blank ("" or 0) if they're not required. diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp index de8636bd..380c21ee 100644 --- a/src/utils/utils.cpp +++ b/src/utils/utils.cpp @@ -30,22 +30,22 @@ Functor Utils::getFunctor(const evmc_message& msg) { Functor ret; if (msg.input_size < 4) return ret; // Memcpy the first 4 bytes from the input data to the function signature - ret.value = Utils::bytesToUint32(bytes::View(msg.input_data, 4)); + ret.value = Utils::bytesToUint32(View(msg.input_data, 4)); return ret; } Functor Utils::makeFunctor(std::string_view functionSignature) { Functor ret; // Create the hash - Hash hash = Utils::sha3(bytes::View(reinterpret_cast(functionSignature.data()), functionSignature.size())); + Hash hash = Utils::sha3(View(reinterpret_cast(functionSignature.data()), functionSignature.size())); // Copy the first 4 bytes of the hash to the value ret.value = Utils::bytesToUint32(hash.view(0,4)); return ret; } -bytes::View Utils::getFunctionArgs(const evmc_message& msg) { - if (msg.input_size < 4) return bytes::View(); - return bytes::View(msg.input_data + 4, msg.input_size - 4); +View Utils::getFunctionArgs(const evmc_message& msg) { + if (msg.input_size < 4) return View(); + return View(msg.input_data + 4, msg.input_size - 4); } void Utils::safePrint(std::string_view str) { @@ -56,7 +56,7 @@ void Utils::safePrintTest(std::string_view str) { Log::safePrintTest(str); } -Hash Utils::sha3(const bytes::View input) { +Hash Utils::sha3(const View input) { ethash_hash256 h = ethash_keccak256(input.data(), input.size()); Hash ret; std::copy(reinterpret_cast(h.bytes), reinterpret_cast(h.bytes + 32), ret.begin()); @@ -65,7 +65,7 @@ Hash Utils::sha3(const bytes::View input) { uint256_t Utils::evmcUint256ToUint256(const evmc::uint256be& i) { // We can use the uint256ToBytes directly as it is std::span and we can create a span from an array - return Utils::bytesToUint256(bytes::View(i.bytes, 32)); + return Utils::bytesToUint256(View(i.bytes, 32)); } evmc::uint256be Utils::uint256ToEvmcUint256(const uint256_t& i) { @@ -83,13 +83,13 @@ BytesArr<32> Utils::evmcUint256ToBytes(const evmc::uint256be& i) { return ret; } -evmc::uint256be Utils::bytesToEvmcUint256(const bytes::View b) { +evmc::uint256be Utils::bytesToEvmcUint256(const View b) { evmc::uint256be ret; std::copy(b.begin(), b.end(), ret.bytes); return ret; } -Account::Account(const bytes::View& bytes) { +Account::Account(const View& bytes) { if (bytes.size() < 73) throw DynamicException(std::string(__func__) + ": Invalid bytes size"); this->balance = Utils::bytesToUint256(bytes.subspan(0,32)); this->nonce = Utils::bytesToUint64(bytes.subspan(32,8)); @@ -371,7 +371,7 @@ BytesArr<32> Utils::uint256ToBytes(const uint256_t& i) { return ret; } -uint256_t Utils::bytesToUint256(const bytes::View b) { +uint256_t Utils::bytesToUint256(const View b) { if (b.size() != 32) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 32, got " + std::to_string(b.size()) ); @@ -380,7 +380,7 @@ uint256_t Utils::bytesToUint256(const bytes::View b) { return ret; } -uint248_t Utils::bytesToUint248(const bytes::View b) { +uint248_t Utils::bytesToUint248(const View b) { if (b.size() != 31) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 31, got " + std::to_string(b.size()) ); @@ -389,7 +389,7 @@ uint248_t Utils::bytesToUint248(const bytes::View b) { return ret; } -uint240_t Utils::bytesToUint240(const bytes::View b) { +uint240_t Utils::bytesToUint240(const View b) { if (b.size() != 30) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 30, got " + std::to_string(b.size()) ); @@ -398,7 +398,7 @@ uint240_t Utils::bytesToUint240(const bytes::View b) { return ret; } -uint232_t Utils::bytesToUint232(const bytes::View b) { +uint232_t Utils::bytesToUint232(const View b) { if (b.size() != 29) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 29, got " + std::to_string(b.size()) ); @@ -407,7 +407,7 @@ uint232_t Utils::bytesToUint232(const bytes::View b) { return ret; } -uint224_t Utils::bytesToUint224(const bytes::View b) { +uint224_t Utils::bytesToUint224(const View b) { if (b.size() != 28) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 28, got " + std::to_string(b.size()) ); @@ -416,7 +416,7 @@ uint224_t Utils::bytesToUint224(const bytes::View b) { return ret; } -uint216_t Utils::bytesToUint216(const bytes::View b) { +uint216_t Utils::bytesToUint216(const View b) { if (b.size() != 27) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 27, got " + std::to_string(b.size()) ); @@ -425,7 +425,7 @@ uint216_t Utils::bytesToUint216(const bytes::View b) { return ret; } -uint208_t Utils::bytesToUint208(const bytes::View b) { +uint208_t Utils::bytesToUint208(const View b) { if (b.size() != 26) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 26, got " + std::to_string(b.size()) ); @@ -434,7 +434,7 @@ uint208_t Utils::bytesToUint208(const bytes::View b) { return ret; } -uint200_t Utils::bytesToUint200(const bytes::View b) { +uint200_t Utils::bytesToUint200(const View b) { if (b.size() != 25) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 25, got " + std::to_string(b.size()) ); @@ -443,7 +443,7 @@ uint200_t Utils::bytesToUint200(const bytes::View b) { return ret; } -uint192_t Utils::bytesToUint192(const bytes::View b) { +uint192_t Utils::bytesToUint192(const View b) { if (b.size() != 24) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 24, got " + std::to_string(b.size()) ); @@ -452,7 +452,7 @@ uint192_t Utils::bytesToUint192(const bytes::View b) { return ret; } -uint184_t Utils::bytesToUint184(const bytes::View b) { +uint184_t Utils::bytesToUint184(const View b) { if (b.size() != 23) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 23, got " + std::to_string(b.size()) ); @@ -461,7 +461,7 @@ uint184_t Utils::bytesToUint184(const bytes::View b) { return ret; } -uint176_t Utils::bytesToUint176(const bytes::View b) { +uint176_t Utils::bytesToUint176(const View b) { if (b.size() != 22) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 22, got " + std::to_string(b.size()) ); @@ -470,7 +470,7 @@ uint176_t Utils::bytesToUint176(const bytes::View b) { return ret; } -uint168_t Utils::bytesToUint168(const bytes::View b) { +uint168_t Utils::bytesToUint168(const View b) { if (b.size() != 21) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 21, got " + std::to_string(b.size()) ); @@ -479,7 +479,7 @@ uint168_t Utils::bytesToUint168(const bytes::View b) { return ret; } -uint160_t Utils::bytesToUint160(const bytes::View b) { +uint160_t Utils::bytesToUint160(const View b) { if (b.size() != 20) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 20, got " + std::to_string(b.size()) ); @@ -488,7 +488,7 @@ uint160_t Utils::bytesToUint160(const bytes::View b) { return ret; } -uint152_t Utils::bytesToUint152(const bytes::View b) { +uint152_t Utils::bytesToUint152(const View b) { if (b.size() != 19) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 19, got " + std::to_string(b.size()) ); @@ -497,7 +497,7 @@ uint152_t Utils::bytesToUint152(const bytes::View b) { return ret; } -uint144_t Utils::bytesToUint144(const bytes::View b) { +uint144_t Utils::bytesToUint144(const View b) { if (b.size() != 18) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 18, got " + std::to_string(b.size()) ); @@ -506,7 +506,7 @@ uint144_t Utils::bytesToUint144(const bytes::View b) { return ret; } -uint136_t Utils::bytesToUint136(const bytes::View b) { +uint136_t Utils::bytesToUint136(const View b) { if (b.size() != 17) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 17, got " + std::to_string(b.size()) ); @@ -515,7 +515,7 @@ uint136_t Utils::bytesToUint136(const bytes::View b) { return ret; } -uint128_t Utils::bytesToUint128(const bytes::View b) { +uint128_t Utils::bytesToUint128(const View b) { if (b.size() != 16) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 16, got " + std::to_string(b.size()) ); @@ -524,7 +524,7 @@ uint128_t Utils::bytesToUint128(const bytes::View b) { return ret; } -uint120_t Utils::bytesToUint120(const bytes::View b) { +uint120_t Utils::bytesToUint120(const View b) { if (b.size() != 15) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 15, got " + std::to_string(b.size()) ); @@ -533,7 +533,7 @@ uint120_t Utils::bytesToUint120(const bytes::View b) { return ret; } -uint112_t Utils::bytesToUint112(const bytes::View b) { +uint112_t Utils::bytesToUint112(const View b) { if (b.size() != 14) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 16, got " + std::to_string(b.size()) ); @@ -542,7 +542,7 @@ uint112_t Utils::bytesToUint112(const bytes::View b) { return ret; } -uint104_t Utils::bytesToUint104(const bytes::View b) { +uint104_t Utils::bytesToUint104(const View b) { if (b.size() != 13) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 13, got " + std::to_string(b.size()) ); @@ -551,7 +551,7 @@ uint104_t Utils::bytesToUint104(const bytes::View b) { return ret; } -uint96_t Utils::bytesToUint96(const bytes::View b) { +uint96_t Utils::bytesToUint96(const View b) { if (b.size() != 12) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 12, got " + std::to_string(b.size()) ); @@ -560,7 +560,7 @@ uint96_t Utils::bytesToUint96(const bytes::View b) { return ret; } -uint88_t Utils::bytesToUint88(const bytes::View b) { +uint88_t Utils::bytesToUint88(const View b) { if (b.size() != 11) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 11, got " + std::to_string(b.size()) ); @@ -569,7 +569,7 @@ uint88_t Utils::bytesToUint88(const bytes::View b) { return ret; } -uint80_t Utils::bytesToUint80(const bytes::View b) { +uint80_t Utils::bytesToUint80(const View b) { if (b.size() != 10) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 10, got " + std::to_string(b.size()) ); @@ -578,7 +578,7 @@ uint80_t Utils::bytesToUint80(const bytes::View b) { return ret; } -uint72_t Utils::bytesToUint72(const bytes::View b) { +uint72_t Utils::bytesToUint72(const View b) { if (b.size() != 9) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 9, got " + std::to_string(b.size()) ); @@ -587,7 +587,7 @@ uint72_t Utils::bytesToUint72(const bytes::View b) { return ret; } -uint56_t Utils::bytesToUint56(const bytes::View b) { +uint56_t Utils::bytesToUint56(const View b) { if (b.size() != 7) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 7, got " + std::to_string(b.size()) ); @@ -596,7 +596,7 @@ uint56_t Utils::bytesToUint56(const bytes::View b) { return ret; } -uint48_t Utils::bytesToUint48(const bytes::View b) { +uint48_t Utils::bytesToUint48(const View b) { if (b.size() != 6) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 6, got " + std::to_string(b.size()) ); @@ -605,7 +605,7 @@ uint48_t Utils::bytesToUint48(const bytes::View b) { return ret; } -uint40_t Utils::bytesToUint40(const bytes::View b) { +uint40_t Utils::bytesToUint40(const View b) { if (b.size() != 5) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 5, got " + std::to_string(b.size()) ); @@ -614,7 +614,7 @@ uint40_t Utils::bytesToUint40(const bytes::View b) { return ret; } -uint24_t Utils::bytesToUint24(const bytes::View b) { +uint24_t Utils::bytesToUint24(const View b) { if (b.size() != 3) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 3, got " + std::to_string(b.size()) ); @@ -632,7 +632,7 @@ BytesArr<8> Utils::uint64ToBytes(const uint64_t& i) { return ret; } -uint64_t Utils::bytesToUint64(const bytes::View b) { +uint64_t Utils::bytesToUint64(const View b) { if (b.size() != 8) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 8, got " + std::to_string(b.size()) ); @@ -653,7 +653,7 @@ BytesArr<4> Utils::uint32ToBytes(const uint32_t& i) { return ret; } -uint32_t Utils::bytesToUint32(const bytes::View b) { +uint32_t Utils::bytesToUint32(const View b) { if (b.size() != 4) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 4, got " + std::to_string(b.size()) ); @@ -674,7 +674,7 @@ BytesArr<2> Utils::uint16ToBytes(const uint16_t& i) { return ret; } -uint16_t Utils::bytesToUint16(const bytes::View b) { +uint16_t Utils::bytesToUint16(const View b) { if (b.size() != 2) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 2, got " + std::to_string(b.size()) ); @@ -692,7 +692,7 @@ BytesArr<1> Utils::uint8ToBytes(const uint8_t& i) { return ret; } -uint8_t Utils::bytesToUint8(const bytes::View b) { +uint8_t Utils::bytesToUint8(const View b) { if (b.size() != 1) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 1, got " + std::to_string(b.size()) ); @@ -701,7 +701,7 @@ uint8_t Utils::bytesToUint8(const bytes::View b) { return ret; } -int256_t Utils::bytesToInt256(const bytes::View b) { +int256_t Utils::bytesToInt256(const View b) { if (b.size() != 32) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 32, got " + std::to_string(b.size()) ); @@ -773,7 +773,7 @@ BytesArr<8> Utils::int64ToBytes(const int64_t& i) { return ret; } -int136_t Utils::bytesToInt136(const bytes::View b) { +int136_t Utils::bytesToInt136(const View b) { if (b.size() != 18) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 18, got " + std::to_string(b.size()) ); @@ -782,7 +782,7 @@ int136_t Utils::bytesToInt136(const bytes::View b) { return ret; } -int64_t Utils::bytesToInt64(const bytes::View b) { +int64_t Utils::bytesToInt64(const View b) { if (b.size() != 8) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 8, got " + std::to_string(b.size()) ); @@ -816,7 +816,7 @@ std::string Utils::padRight(std::string str, unsigned int charAmount, char sign) return (hasPrefix ? "0x" : "") + str + padded; } -Bytes Utils::padLeftBytes(const bytes::View bytes, unsigned int charAmount, uint8_t sign) { +Bytes Utils::padLeftBytes(const View bytes, unsigned int charAmount, uint8_t sign) { size_t padding = (charAmount > bytes.size()) ? (charAmount - bytes.size()) : 0; Bytes padded = (padding != 0) ? Bytes(padding, sign) : Bytes(0, 0x00); padded.reserve(bytes.size() + padded.size()); @@ -824,7 +824,7 @@ Bytes Utils::padLeftBytes(const bytes::View bytes, unsigned int charAmount, uint return padded; } -Bytes Utils::padRightBytes(const bytes::View bytes, unsigned int charAmount, uint8_t sign) { +Bytes Utils::padRightBytes(const View bytes, unsigned int charAmount, uint8_t sign) { size_t padding = (charAmount > bytes.size()) ? (charAmount - bytes.size()) : 0; Bytes padded = (padding != 0) ? Bytes(padding, sign) : Bytes(0, 0x00); Bytes ret; diff --git a/src/utils/utils.h b/src/utils/utils.h index 5edc2fba..61d1802d 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -277,7 +277,7 @@ struct Account { Account(uint256_t&& balance, uint64_t&& nonce) : balance(std::move(balance)), nonce(std::move(nonce)) {} /// Deserialize constructor. - Account(const bytes::View& bytes); + Account(const View& bytes); /** * Serialize the account. @@ -461,11 +461,11 @@ namespace Utils { Functor makeFunctor(std::string_view functionSignature); /** - * Get the bytes::View representing the function arguments of a given evmc_message. + * Get the View representing the function arguments of a given evmc_message. * @param msg The evmc_message to get the function arguments from. - * @return The bytes::View representing the function arguments. + * @return The View representing the function arguments. */ - bytes::View getFunctionArgs(const evmc_message& msg); + View getFunctionArgs(const evmc_message& msg); /** * Print a string to stdout. Does not print if in a test. @@ -484,7 +484,7 @@ namespace Utils { * @param input The string to hash. * @return The SHA3-hashed string. */ - Hash sha3(const bytes::View input); + Hash sha3(const View input); /** * Generate a random bytes string of a given size. @@ -502,7 +502,7 @@ namespace Utils { uint256_t evmcUint256ToUint256(const evmc::uint256be& i); evmc::uint256be uint256ToEvmcUint256(const uint256_t& i); BytesArr<32> evmcUint256ToBytes(const evmc::uint256be& i); - evmc::uint256be bytesToEvmcUint256(const bytes::View b); + evmc::uint256be bytesToEvmcUint256(const View b); ///@} /** @@ -565,41 +565,41 @@ namespace Utils { * @return The converted integer. * @throw DynamicException if string size is invalid. */ - uint256_t bytesToUint256(const bytes::View b); - uint248_t bytesToUint248(const bytes::View b); - uint240_t bytesToUint240(const bytes::View b); - uint232_t bytesToUint232(const bytes::View b); - uint224_t bytesToUint224(const bytes::View b); - uint216_t bytesToUint216(const bytes::View b); - uint208_t bytesToUint208(const bytes::View b); - uint200_t bytesToUint200(const bytes::View b); - uint192_t bytesToUint192(const bytes::View b); - uint184_t bytesToUint184(const bytes::View b); - uint176_t bytesToUint176(const bytes::View b); - uint168_t bytesToUint168(const bytes::View b); - uint160_t bytesToUint160(const bytes::View b); - uint152_t bytesToUint152(const bytes::View b); - uint144_t bytesToUint144(const bytes::View b); - uint136_t bytesToUint136(const bytes::View b); - uint128_t bytesToUint128(const bytes::View b); - uint120_t bytesToUint120(const bytes::View b); - uint112_t bytesToUint112(const bytes::View b); - uint104_t bytesToUint104(const bytes::View b); - uint96_t bytesToUint96(const bytes::View b); - uint88_t bytesToUint88(const bytes::View b); - uint80_t bytesToUint80(const bytes::View b); - uint72_t bytesToUint72(const bytes::View b); - uint64_t bytesToUint64(const bytes::View b); - uint56_t bytesToUint56(const bytes::View b); - uint48_t bytesToUint48(const bytes::View b); - uint40_t bytesToUint40(const bytes::View b); - uint32_t bytesToUint32(const bytes::View b); - uint24_t bytesToUint24(const bytes::View b); - uint16_t bytesToUint16(const bytes::View b); - uint8_t bytesToUint8(const bytes::View b); - int256_t bytesToInt256(const bytes::View b); - int136_t bytesToInt136(const bytes::View b); - int64_t bytesToInt64(const bytes::View b); + uint256_t bytesToUint256(const View b); + uint248_t bytesToUint248(const View b); + uint240_t bytesToUint240(const View b); + uint232_t bytesToUint232(const View b); + uint224_t bytesToUint224(const View b); + uint216_t bytesToUint216(const View b); + uint208_t bytesToUint208(const View b); + uint200_t bytesToUint200(const View b); + uint192_t bytesToUint192(const View b); + uint184_t bytesToUint184(const View b); + uint176_t bytesToUint176(const View b); + uint168_t bytesToUint168(const View b); + uint160_t bytesToUint160(const View b); + uint152_t bytesToUint152(const View b); + uint144_t bytesToUint144(const View b); + uint136_t bytesToUint136(const View b); + uint128_t bytesToUint128(const View b); + uint120_t bytesToUint120(const View b); + uint112_t bytesToUint112(const View b); + uint104_t bytesToUint104(const View b); + uint96_t bytesToUint96(const View b); + uint88_t bytesToUint88(const View b); + uint80_t bytesToUint80(const View b); + uint72_t bytesToUint72(const View b); + uint64_t bytesToUint64(const View b); + uint56_t bytesToUint56(const View b); + uint48_t bytesToUint48(const View b); + uint40_t bytesToUint40(const View b); + uint32_t bytesToUint32(const View b); + uint24_t bytesToUint24(const View b); + uint16_t bytesToUint16(const View b); + uint8_t bytesToUint8(const View b); + int256_t bytesToInt256(const View b); + int136_t bytesToInt136(const View b); + int64_t bytesToInt64(const View b); ///@} /** @@ -620,7 +620,7 @@ namespace Utils { * @param sign (optional) The character to use as padding. Defaults to '0'. * @return The padded vector. */ - Bytes padLeftBytes(const bytes::View bytes, unsigned int charAmount, uint8_t sign = 0x00); + Bytes padLeftBytes(const View bytes, unsigned int charAmount, uint8_t sign = 0x00); /** * Add padding to the right of a byte vector. @@ -632,7 +632,7 @@ namespace Utils { * @param sign (optional) The character to use as padding. Defaults to '0'. * @return The padded vector. */ - Bytes padRightBytes(const bytes::View bytes, unsigned int charAmount, uint8_t sign = 0x00); + Bytes padRightBytes(const View bytes, unsigned int charAmount, uint8_t sign = 0x00); /// Overload of padLeftBytes() that works with UTF-8 strings. std::string padLeft(std::string str, unsigned int charAmount, char sign = '\x00'); @@ -733,8 +733,8 @@ namespace Utils { * @param vec The vector to convert. * @return The converted span. */ - inline bytes::View create_view_span(const Bytes& vec) { - return bytes::View(vec.data(), vec.size()); + inline View create_view_span(const Bytes& vec) { + return View(vec.data(), vec.size()); } /** @@ -744,9 +744,9 @@ namespace Utils { * @param size The size of the subvector. * @return The converted span. */ - inline bytes::View create_view_span(const Bytes& vec, size_t start, size_t size) { + inline View create_view_span(const Bytes& vec, size_t start, size_t size) { if (start + size > vec.size()) throw DynamicException("Invalid range for span"); - return bytes::View(vec.data() + start, size); + return View(vec.data() + start, size); } /** @@ -754,8 +754,8 @@ namespace Utils { * @param arr The array to convert. * @return The converted span. */ - template inline bytes::View create_view_span(const BytesArr& arr) { - return bytes::View(arr.data(), arr.size()); + template inline View create_view_span(const BytesArr& arr) { + return View(arr.data(), arr.size()); } /** @@ -765,11 +765,11 @@ namespace Utils { * @param size The size of the subarray. * @return The converted span. */ - template inline bytes::View create_view_span( + template inline View create_view_span( const BytesArr& arr, size_t start, size_t size ) { if (start + size > arr.size()) throw DynamicException("Invalid range for span"); - return bytes::View(arr.data() + start, size); + return View(arr.data() + start, size); } /** @@ -777,8 +777,8 @@ namespace Utils { * @param str The string to convert. * @return The converted span. */ - inline bytes::View create_view_span(const std::string_view str) { - return bytes::View(reinterpret_cast(str.data()), str.size()); + inline View create_view_span(const std::string_view str) { + return View(reinterpret_cast(str.data()), str.size()); } /** @@ -788,11 +788,11 @@ namespace Utils { * @param size The size of the substring. * @return The converted span. */ - inline bytes::View create_view_span(const std::string_view str, size_t start, size_t size) { + inline View create_view_span(const std::string_view str, size_t start, size_t size) { if (start + size > str.size()) { throw DynamicException("Invalid range for span"); } - return bytes::View(reinterpret_cast(str.data()) + start, size); + return View(reinterpret_cast(str.data()) + start, size); } /** diff --git a/src/utils/view.h b/src/utils/view.h index f0f7a2e8..b7087c53 100644 --- a/src/utils/view.h +++ b/src/utils/view.h @@ -3,6 +3,7 @@ #include #include +#include "bytes/range.h" #include "bytes.h" /** @@ -18,7 +19,45 @@ struct View; */ template<> struct View : std::span { - using std::span::span; + + /** + * Constructs an empty view of size 0 + */ + constexpr View() = default; + + /** + * Constructs a view from a given data range. + * The range must also follow the constraints for initializing a span. + * + * @param range the contiguous and sized range of bytes + */ + template + constexpr View(R&& range) : span(std::forward(range)) {} + + /** + * Constructs a view from an data iterator and a size. + * + * @param it the contiguous iterator of the range beginning + * @param size the number of bytes to be viewed + */ + template + constexpr View(I it, size_t size) : span(it, size) {} + + /** + * Constructs a view from a iterator-sentinel pair. + * + * @param begin the contiguous iterator of the range beginning + * @param end the sentinel iterator of the range end + */ + template S> + constexpr View(I begin, S end) : span(begin, end) {} }; +/** + * Partial initialization for enabling all view types as borrowed. + * All view types are borrowed since they don't own the data. + */ +template +constexpr bool std::ranges::enable_borrowed_range> = true; + #endif // BDK_UTILS_VIEW_H diff --git a/tests/utils/strings.cpp b/tests/utils/strings.cpp index fb1d0c50..23531ecc 100644 --- a/tests/utils/strings.cpp +++ b/tests/utils/strings.cpp @@ -9,6 +9,7 @@ See the LICENSE.txt file in the project root for more information. #include "../../src/utils/strings.h" #include "bytes/view.h" #include "bytes/random.h" +#include "bytes/hex.h" using Catch::Matchers::Equals; @@ -188,17 +189,17 @@ namespace TAddress { TEST_CASE("Address Class", "[utils]") { SECTION("Address Copy Constructor") { Address addr1(Bytes({0x71, 0xc7, 0x65, 0x6e, 0xc7, 0xab, 0x88, 0xb0, 0x98, 0xde, 0xfb, 0x75, 0x1b, 0x74, 0x01, 0xb5, 0xf6, 0xd8, 0x97, 0x6f})); - Address addr2(std::string("\x71\xc7\x65\x6e\xc7\xab\x88\xb0\x98\xde\xfb\x75\x1b\x74\x01\xb5\xf6\xd8\x97\x6f"), true); + Address addr2(bytes::view("\x71\xc7\x65\x6e\xc7\xab\x88\xb0\x98\xde\xfb\x75\x1b\x74\x01\xb5\xf6\xd8\x97\x6f")); REQUIRE(addr1 == addr2); REQUIRE_THAT(addr1.hex(), Equals("71c7656ec7ab88b098defb751b7401b5f6d8976f")); REQUIRE(addr2 == Address({0x71, 0xc7, 0x65, 0x6e, 0xc7, 0xab, 0x88, 0xb0, 0x98, 0xde, 0xfb, 0x75, 0x1b, 0x74, 0x01, 0xb5, 0xf6, 0xd8, 0x97, 0x6f})); } SECTION("Address toChksum") { - Address inputAddress(std::string("0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359"), false); + Address inputAddress = bytes::hex("0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359"); std::string inputChecksum = Address::checksum(inputAddress); - Address outputAddress(inputChecksum, false); - Address expectedOutputAddress(std::string("0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359"), false); + Address outputAddress = bytes::hex(inputChecksum); + Address expectedOutputAddress = bytes::hex("0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359"); REQUIRE(outputAddress == expectedOutputAddress); } From 2b3419179601332c075cadab8758ae137bb50d23 Mon Sep 17 00:00:00 2001 From: Leonardo Amalaral Date: Thu, 7 Nov 2024 11:49:06 -0300 Subject: [PATCH 05/37] missing docs added --- src/utils/fixedbytes.h | 29 ++++++++++++++++- src/utils/signature.h | 72 +++++++++++++++++++++++++++++++++++------- 2 files changed, 89 insertions(+), 12 deletions(-) diff --git a/src/utils/fixedbytes.h b/src/utils/fixedbytes.h index 4f8a0fa3..57ba63c6 100644 --- a/src/utils/fixedbytes.h +++ b/src/utils/fixedbytes.h @@ -12,18 +12,39 @@ template class FixedBytes : public BytesInterface, N> { public: + + /** + * Constructs a fixed bytes container with all bits clear + */ constexpr FixedBytes() : data_() {} + /** + * Constructs a fixed bytes container from the given initializer list + * + * @param initList the initalizer list + * @throw invalid argument exception if the initializer list size does not match the container size + */ constexpr FixedBytes(std::initializer_list initList) { if (initList.size() != N) - throw DynamicException("Given initializer list of size " + std::to_string(initList.size()) + + throw std::invalid_argument("Given initializer list of size " + std::to_string(initList.size()) + " is not suitable for initializing a FixedBytes<" + std::to_string(N) + ">"); std::ranges::copy(initList, data_.begin()); } + /** + * Constructs a fixed bytes container from the given bytes initializer + * + * @param initializer the bytes initializer + */ constexpr FixedBytes(bytes::Initializer auto&& initializer) { initializer.to(data_); } + /** + * Constructs a fixed bytes container by copying the bytes from the given range. + * + * @param input the input bytes + * @throw invalid argument exception if the input size does not match the container size + */ explicit constexpr FixedBytes(const bytes::Range auto& input) { if (const size_t size = std::ranges::size(input); size != N) { throw std::invalid_argument("Given bytes range of size " + std::to_string(size) + @@ -33,8 +54,14 @@ class FixedBytes : public BytesInterface, N> { std::ranges::copy(input, data_.begin()); } + /** + * @return the beginning iterator of this container bytes + */ constexpr auto begin() { return data_.begin(); } + /** + * @return the beginning constant iterator of this container bytes + */ constexpr auto begin() const { return data_.begin(); } private: diff --git a/src/utils/signature.h b/src/utils/signature.h index 548246e6..a985c194 100644 --- a/src/utils/signature.h +++ b/src/utils/signature.h @@ -7,23 +7,37 @@ #include "hash.h" #include "view.h" -inline constexpr size_t SIGNATURE_SIZE = 65; +constexpr size_t SIGNATURE_SIZE = 65; +/** + * Base class for all signature types. Should NOT be used for polymorphism. + * It's helper class template aimed to define a common interface for all signature types. + * This class uses CRTP. + */ template -class BaseSignature : public BytesInterface { +class SignatureInterface : public BytesInterface { public: using BytesInterface::BytesInterface; - uint256_t r() const { + /** + * @return get the first half (32 bytes) of the signature. + */ + constexpr uint256_t r() const { return static_cast(View(self() | std::views::take(HASH_SIZE))); } - uint256_t s() const { + /** + * @return get the second half (32 bytes) of the signature. + */ + constexpr uint256_t s() const { return static_cast(View( self() | std::views::drop(HASH_SIZE) | std::views::take(HASH_SIZE))); } - uint8_t v() const { + /** + * @return get the recovery ID (1 byte) of the signature. + */ + constexpr uint8_t v() const { return *std::ranges::rbegin(self()); } @@ -31,16 +45,33 @@ class BaseSignature : public BytesInterface { constexpr const T& self() const { return static_cast(*this); } }; -class Signature : public BaseSignature { +/// Abstraction of a 65-byte ECDSA signature. +class Signature : public SignatureInterface { public: + /** + * Constructs a signature object with all bits clear + */ constexpr Signature() : data_() {} + /** + * Constructs a hash from the given argument. The argument + * must be able to initialize a BytesInterface. Refer to BytesInterface + * for checking constructor overloads. + * + * @param input the argument capable of constructing a BytesInterface + */ template explicit (not bytes::Initializer) - constexpr Signature(I&& input) : BaseSignature(std::forward(input)) {} + constexpr Signature(I&& input) : SignatureInterface(std::forward(input)) {} + /** + * @return the beginning iterator of the bytes range + */ constexpr auto begin() { return data_.begin(); } + /** + * @return the beginning constant iterator of the bytes range + */ constexpr auto begin() const { return data_.begin(); } private: @@ -48,12 +79,31 @@ class Signature : public BaseSignature { }; template<> -class View : public BaseSignature> { +class View : public SignatureInterface> { public: - template - explicit(not std::same_as, Signature>) - constexpr View(R&& input) : data_(std::forward(input)) {} + /** + * Constructs a signature view from the given bytes range of 65 bytes. + * + * @param range a contiguous and sized byte range of exactly 65 bytes + * @throw invalid argument exception if the input range does not have the correct size + */ + template + explicit constexpr View(R&& range) : data_(range) { + if (const size_t size = std::ranges::size(range); size != HASH_SIZE) { + throw std::invalid_argument("signature view requires exactly 65 bytes, but " + std::to_string(size) + " were given"); + } + } + + /** + * Constructs a signature view from a signature object. + * + * @param the signature object + */ + constexpr View(const Signature& signature) : data_(signature) {} + /** + * @return the beginning constant iterator of the signature bytes range + */ constexpr auto begin() const { return data_.begin(); } private: From 609758d962696540a325f4ee5f311a2e140319c2 Mon Sep 17 00:00:00 2001 From: Leonardo Amalaral Date: Sun, 29 Sep 2024 09:52:14 -0300 Subject: [PATCH 06/37] merging multiple changes, unfinished work DO NOT MERGE --- src/contract/CMakeLists.txt | 1 + src/contract/calldispatcher.h | 50 +++++++++++++++++ src/contract/contract.h | 2 +- src/contract/contracthost.cpp | 17 ++++++ src/contract/contracthost.h | 65 +++++++++++++++++++-- src/contract/contractmanager.cpp | 5 ++ src/contract/contractmanager.h | 10 ++++ src/contract/cpp/callexecutor.cpp | 34 +++++++++++ src/contract/cpp/callexecutor.h | 93 +++++++++++++++++++++++++++++++ src/contract/cpp/message.h | 35 ++++++++++++ src/contract/evm/anycallhandler.h | 27 +++++++++ src/contract/evm/callexecutor.h | 80 ++++++++++++++++++++++++++ src/contract/evm/message.h | 23 ++++++++ src/contract/gas.h | 52 +++++++++++++++++ src/contract/traits/method.h | 22 ++++++++ src/core/state.cpp | 36 +++++++----- src/utils/utils.h | 4 ++ tests/statetest.hpp | 5 +- 18 files changed, 538 insertions(+), 23 deletions(-) create mode 100644 src/contract/calldispatcher.h create mode 100644 src/contract/cpp/callexecutor.cpp create mode 100644 src/contract/cpp/callexecutor.h create mode 100644 src/contract/cpp/message.h create mode 100644 src/contract/evm/anycallhandler.h create mode 100644 src/contract/evm/callexecutor.h create mode 100644 src/contract/evm/message.h create mode 100644 src/contract/gas.h create mode 100644 src/contract/traits/method.h diff --git a/src/contract/CMakeLists.txt b/src/contract/CMakeLists.txt index 13606f33..2b3e8b92 100644 --- a/src/contract/CMakeLists.txt +++ b/src/contract/CMakeLists.txt @@ -54,6 +54,7 @@ set(CONTRACT_SOURCES ${CMAKE_SOURCE_DIR}/src/contract/dynamiccontract.cpp ${CMAKE_SOURCE_DIR}/src/contract/calltracer.cpp ${CMAKE_SOURCE_DIR}/src/contract/event.cpp + ${CMAKE_SOURCE_DIR}/src/contract/cpp/callexecutor.cpp ${CMAKE_SOURCE_DIR}/src/contract/templates/ownable.cpp ${CMAKE_SOURCE_DIR}/src/contract/templates/erc20.cpp ${CMAKE_SOURCE_DIR}/src/contract/templates/erc721.cpp diff --git a/src/contract/calldispatcher.h b/src/contract/calldispatcher.h new file mode 100644 index 00000000..2e7c61f8 --- /dev/null +++ b/src/contract/calldispatcher.h @@ -0,0 +1,50 @@ +#pragma once + +#include "../utils/utils.h" +#include "cpp/callexecutor.h" + +struct DummyHandler { + Bytes executeCall(auto, Gas&, const evm::Message&) { throw std::runtime_error("TODO"); } + + template + typename M::ReturnType executeCall(auto, Gas&, const cpp::Message&) { throw std::runtime_error("TODO"); } +}; + +class CallDispatcher { +public: + using Accounts = boost::unordered_flat_map, SafeHash>; + using TransferHandler = std::function; + + CallDispatcher(cpp::CallExecutor cppCallHandler, DummyHandler evmCallHandler, TransferHandler transferHandler, Accounts& accounts) + : cppCallHandler_(std::move(cppCallHandler)), evmCallHandler_(std::move(evmCallHandler)), transferHandler_(std::move(transferHandler)), accounts_(accounts) {} + + decltype(auto) onCall(auto kind, Gas& gas, auto&& msg) { + const auto it = accounts_.find(msg.to); + + if (it == accounts_.end()) { + throw ExecutionFailure("Account not found"); + } + + const auto& account = it->second; + + if (!account->isContract()) { + throw ExecutionFailure("Not a contract address"); + } + + if (msg.value) { + transferHandler_(msg.from, msg.to, msg.value); + } + + if (account->contractType == ContractType::CPP) { + return cppCallHandler_.executeCall(kind, gas, std::forward(msg)); + } else { + return evmCallHandler_.executeCall(kind, gas, std::forward(msg)); + } + } + +private: + Accounts& accounts_; + cpp::CallExecutor cppCallHandler_; + DummyHandler evmCallHandler_; + TransferHandler transferHandler_; +}; diff --git a/src/contract/contract.h b/src/contract/contract.h index c9ae010c..deca6556 100644 --- a/src/contract/contract.h +++ b/src/contract/contract.h @@ -48,7 +48,7 @@ class ContractGlobals { /// Class that maintains local variables for contracts. class ContractLocals : public ContractGlobals { - private: + public: // TODO: revert this to private // TODO: DONT RELY ON ContractLocals, INSTEAD, USE CONTRACTHOST TO STORE LOCALS mutable Address caller_; ///< Who sent the transaction. mutable uint256_t value_; ///< Value sent within the transaction. diff --git a/src/contract/contracthost.cpp b/src/contract/contracthost.cpp index 53ea64d9..e3c00a55 100644 --- a/src/contract/contracthost.cpp +++ b/src/contract/contracthost.cpp @@ -269,6 +269,23 @@ void ContractHost::execute(const evmc_message& msg, const ContractType& type) { const uint256_t value(Utils::evmcUint256ToUint256(msg.value)); const bool isContractCall = isCall(msg); + bool enabled = true; + + if (enabled && to != Address()) { + evm::Message emsg{ + .from{msg.sender}, + .to{msg.recipient}, + .value = Utils::evmcUint256ToUint256(msg.value), + .depth = msg.depth, + .input = bytes::View(msg.input_data, msg.input_size) + }; + + // TODO: return the result + execute(std::move(emsg)); + this->mustRevert_ = false; + return; + } + if (isContractCall) { this->traceCallStarted(msg); } diff --git a/src/contract/contracthost.h b/src/contract/contracthost.h index e1738db2..75ecf1cc 100644 --- a/src/contract/contracthost.h +++ b/src/contract/contracthost.h @@ -19,7 +19,8 @@ #include "calltracer.h" #include "bytes/join.h" #include "bytes/cast.h" - +#include "gas.h" +#include "calldispatcher.h" // TODO: EVMC Static Mode Handling // TODO: Contract creating other contracts (EVM Factories) @@ -86,10 +87,13 @@ class ContractHost : public evmc::Host { const Hash& txHash_; const uint64_t txIndex_; const Hash& blockHash_; - int64_t& leftoverGas_; /// Reference to the leftover gas from the transaction. + uint64_t + & leftoverGas_; /// Reference to the leftover gas from the transaction. /// The leftoverGas_ is a object given by the State TxAdditionalData addTxData_; trace::CallTracer callTracer_; + Gas& gas_; + CallDispatcher callHandler_; // Private as this is not available for contracts as it has safety checks void transfer(const Address& from, const Address& to, const uint256_t& value); @@ -161,7 +165,7 @@ class ContractHost : public evmc::Host { const Hash& txHash, const uint64_t txIndex, const Hash& blockHash, - int64_t& txGasLimit) : + Gas& gas) : vm_(vm), manager_(manager), storage_(storage), @@ -173,8 +177,14 @@ class ContractHost : public evmc::Host { txHash_(txHash), txIndex_(txIndex), blockHash_(blockHash), - leftoverGas_(txGasLimit), - addTxData_({.hash = txHash}) {} + leftoverGas_(gas.value_), + addTxData_({.hash = txHash}), + gas_(gas), + callHandler_(cpp::CallExecutor(*this, contracts_), DummyHandler(), [this] (const Address& from, const Address& to, const uint256_t& value) { transfer(from, to, value); }, accounts) {} + // callHandler_(cpp::CallExecutor(*this, contracts_), DummyHandler(), std::function(), accounts_){} + // callHandler_(vm_, accounts_, contracts_, nullptr) { + // callHandler_.setContractHost(this); + // } // Rule of five, no copy/move allowed. ContractHost(const ContractHost&) = delete; @@ -193,6 +203,27 @@ class ContractHost : public evmc::Host { /// Executes a call void execute(const evmc_message& msg, const ContractType& type); + decltype(auto) execute(auto&& msg) { + gas_.use(21000); + + try { + return callHandler_.onCall(kind::NORMAL, gas_, std::forward(msg)); + } catch (const std::exception& err) { + mustRevert_ = true; + throw err; + } + } + + // decltype(auto) execute(auto&& msg) { + // gas_.use(21000); + // return callHandler_.onCall(kind::Normal, gas_, std::forward(msg)); + // } + + // Address execute(Gas& gas, const CreateMessage& msg) { + // gas.use(666); // TODO: how much for a contract creation? + // return callHandler_.onCreate(gas, msg); + // } + /// Executes a eth_call RPC method (view) /// returns the result of the call Bytes ethCallView(const evmc_message& msg, const ContractType& type); @@ -232,7 +263,18 @@ class ContractHost : public evmc::Host { * @return The result of the view function. */ template - R callContractViewFunction(const BaseContract* caller, const Address& targetAddr, R(C::*func)(const Args&...) const, const Args&... args) const { + R callContractViewFunction(const BaseContract* caller, const Address& targetAddr, R(C::*func)(const Args&...) const, const Args&... args) { + + cpp::Message msg{ + .from = caller->getContractAddress(), + .to = targetAddr, + .value = 0, + .caller = caller, + .method = cpp::PackagedMethod(func, args...) + }; + + return callHandler_.onCall(kind::STATIC, gas_, std::move(msg)); + const auto recipientAccIt = this->accounts_.find(targetAddr); if (recipientAccIt == this->accounts_.end()) { throw DynamicException(std::string(__func__) + ": Contract Account does not exist - Type: " @@ -373,6 +415,17 @@ class ContractHost : public evmc::Host { const uint256_t& value, R(C::*func)(const Args&...), const Args&... args ) { + + cpp::Message msg{ + .from = caller->getContractAddress(), + .to = targetAddr, + .value = value, + .caller = caller, + .method = cpp::PackagedMethod(func, args...) + }; + + return callHandler_.onCall(kind::NORMAL, gas_, std::move(msg)); + // 1000 Gas Limit for every C++ contract call! auto& recipientAcc = *this->accounts_[targetAddr]; if (!recipientAcc.isContract()) { diff --git a/src/contract/contractmanager.cpp b/src/contract/contractmanager.cpp index 325f4085..8c831213 100644 --- a/src/contract/contractmanager.cpp +++ b/src/contract/contractmanager.cpp @@ -86,6 +86,11 @@ void ContractManager::ethCall(const evmc_message& callInfo, ContractHost* host) this->host_); } +Bytes ContractManager::evmEthCall(const evmc_message& callInfo, ContractHost* host) { + this->ethCall(callInfo, host); + return Bytes(); +} + Bytes ContractManager::ethCallView(const evmc_message& callInfo, ContractHost* host) const { PointerNullifier nullifier(this->host_); // This hash is equivalent to "function getDeployedContracts() public view returns (Contract[] memory) {}" diff --git a/src/contract/contractmanager.h b/src/contract/contractmanager.h index e8a86646..8cb7f23e 100644 --- a/src/contract/contractmanager.h +++ b/src/contract/contractmanager.h @@ -134,6 +134,16 @@ class ContractManager : public BaseContract { */ void ethCall(const evmc_message& callInfo, ContractHost* host) override; + /** + * Override the default contract function call. + * ContractManager processes things in a non-standard way + * (you cannot use SafeVariables as contract creation actively writes to DB). + * @param callInfo The call info to process. + * @return The bytes from the call + * @throw DynamicException if the call is not valid. + */ + Bytes evmEthCall(const evmc_message& callInfo, ContractHost* host) override; + /** * Override the default contract view function call. * ContractManager process things in a non-standard way diff --git a/src/contract/cpp/callexecutor.cpp b/src/contract/cpp/callexecutor.cpp new file mode 100644 index 00000000..25674645 --- /dev/null +++ b/src/contract/cpp/callexecutor.cpp @@ -0,0 +1,34 @@ +#include "callexecutor.h" +#include "../contracthost.h" + +namespace cpp { + +Bytes CallExecutor::executeCall(kind::Normal, Gas& gas, const evm::Message& msg) { + gas.use(1000); + + const auto it = contracts_.find(msg.to); + + if (it == contracts_.end()) { + throw std::runtime_error("Contract not found"); // TODO: proper error + more info + } + + auto& contract = *it->second; + + contract.caller_ = msg.from; + contract.value_ = msg.value; + + const evmc_message evmcMsg{ + .kind = EVMC_CALL, + .flags = 0, + .depth = msg.depth, + .gas = gas.value(), + .recipient = msg.to.toEvmcAddress(), + .sender = msg.from.toEvmcAddress(), + .input_data = msg.input.data(), + .input_size = msg.input.size() + }; + + return contract.evmEthCall(evmcMsg, &host_); +} + +} // namespace cpp diff --git a/src/contract/cpp/callexecutor.h b/src/contract/cpp/callexecutor.h new file mode 100644 index 00000000..338372f5 --- /dev/null +++ b/src/contract/cpp/callexecutor.h @@ -0,0 +1,93 @@ +#pragma once + +#include "message.h" +#include "../gas.h" +#include "../traits/method.h" +#include "../evm/message.h" +// #include "../dynamiccontract.h" + +struct ContractHost; + +namespace cpp { + +class NestedCallSafeGuard { +public: + NestedCallSafeGuard(const ContractLocals* contract, const Address& caller, const uint256_t& value) : + contract_(contract), caller_(contract->caller_), value_(contract->value_) { + } + + ~NestedCallSafeGuard() { + contract_->caller_ = caller_; + contract_->value_ = value_; + } +private: + const ContractLocals* contract_; + Address caller_; + uint256_t value_; +}; + +class CallExecutor { +public: + using Contracts = boost::unordered_flat_map, SafeHash>&; + + CallExecutor(ContractHost& host, Contracts& contracts) + : host_(host), contracts_(contracts) {} + + template + decltype(auto) executeCall(CallKind, Gas& gas, Message msg) { + if constexpr (std::same_as) { + throw std::runtime_error("TODO: delegate not allowed for C++ contracts"); + } + + gas.use(1000); + + NestedCallSafeGuard guard(msg.caller, msg.caller->caller_, msg.caller->value_); + + auto *contract = getContract(msg.to); + + // TODO: check nullptr + + auto binded = [&] (auto&&... args) { + if constexpr (std::same_as) { + return std::invoke(msg.method.func, *contract, std::forward(args)...); + } else { + return contract->callContractFunction(&host_, msg.method.func, std::forward(args)...); + } + }; + + return std::apply(binded, std::move(msg.method.args)); + } + + decltype(auto) executeCall(kind::Static, Gas& gas, const evm::Message& msg) { + throw std::runtime_error("TODO"); + } + + Bytes executeCall(kind::Normal, Gas& gas, const evm::Message& msg); + + decltype(auto) executeCall(kind::Delegate, Gas& gas, const evm::Message& msg) { + throw std::runtime_error("TODO"); + } + +private: + template + C* getContract(const Address& address) { + const auto it = contracts_.find(address); + + if (it == contracts_.end()) { + throw ExecutionFailure("Contract not found"); // TODO: add more error info + } + + C* ptr = dynamic_cast(it->second.get()); + + if (ptr == nullptr) { + throw ExecutionFailure("Contract is not of the requested type"); // TODO: add more error info + } + + return ptr; + } + + ContractHost& host_; + Contracts& contracts_; +}; + +} // namespace cpp diff --git a/src/contract/cpp/message.h b/src/contract/cpp/message.h new file mode 100644 index 00000000..0057d863 --- /dev/null +++ b/src/contract/cpp/message.h @@ -0,0 +1,35 @@ + +#pragma once + +#include "../traits/method.h" +#include "../contract.h" +#include "../../utils/strings.h" + +namespace cpp { + +template +struct PackagedMethod { + using ClassType = traits::Method::ClassType; + using ReturnType = traits::Method::ReturnType; + static constexpr bool IsView = traits::Method::IsView; + + explicit PackagedMethod(M func, Args&&... args) + : func(std::move(func)), args(std::forward(args)...) {} + + M func; + std::tuple args; +}; + +template +explicit PackagedMethod(M, Ts&&...) -> PackagedMethod; + +template +struct Message { + Address from; + Address to; + uint256_t value; + const BaseContract *caller; + Method method; +}; + +} // namespace cpp diff --git a/src/contract/evm/anycallhandler.h b/src/contract/evm/anycallhandler.h new file mode 100644 index 00000000..9aa3ad7c --- /dev/null +++ b/src/contract/evm/anycallhandler.h @@ -0,0 +1,27 @@ +#pragma once + +#include "../gas.h" +#include "message.h" + +namespace evm { + +class AnyCallHandler { +public: + template + AnyCallHandler(T& callHandler) + : object_(&callHandler), + func_([] (void *object, kind::Any callKind, Gas& gas, const CallMessage& msg) { + return std::visit([&] (auto callKind) { return static_cast(object)->onCall(callKind, gas, msg); }, callKind); + }) {} + + Bytes onCall(kind::Any callKind, Gas& gas, const CallMessage& msg) { + return std::invoke(func_, object_, callKind, gas, msg); + } + + +private: + void *object_; + Bytes (*func_)(void*, kind::Any, Gas&, const CallMessage&); +}; + +} // namespace evm diff --git a/src/contract/evm/callexecutor.h b/src/contract/evm/callexecutor.h new file mode 100644 index 00000000..280a06c1 --- /dev/null +++ b/src/contract/evm/callexecutor.h @@ -0,0 +1,80 @@ +#pragma once + +#include "message.h" +#include "anycallhandler.h" + +namespace evm { + +class CallExecutor : public evmc::Host { +public: + using VmStorage = boost::unordered_flat_map; + using Accounts = boost::unordered_flat_map, SafeHash>; + + CallExecutor(AnyCallHandler callHandler, evmc_vm* vm, VmStorage& vmStorage, Accounts& accounts, ContractStack& stack, const Hash& txHash, const Hash& blockHash, const evmc_tx_context& currentTxContext) + : callHandler_(std::move(callHandler)), vm_(vm), vmStorage_(vmStorage), accounts_(accounts), stack_(stack), txHash_(txHash), blockHash_(blockHash), currentTxContext_(currentTxContext) {} + + // Bytes executeCall(auto& callHandler, auto kind, Gas& gas, const auto& msg) { + // } + + // Bytes executeCall(auto&& callHandler, auto kind, Gas& gas, const CallMessage& msg, bytes::View code) { + // static constexpr auto getKind = Utils::Overloaded{ + // [] (kind::Normal) { return EVMC_CALL; }, + // [] (kind::Static) { return EVMC_CALL; }, + // [] (kind::Delegate) { return EVMC_DELEGATECALL; } + // }; + + // static constexpr auto getFlags = Utils::Overloaded{ + // [] (kind::Normal) -> std::uint32_t { return 0; }, + // [] (kind::Static) -> std::uint32_t { return EVMC_STATIC; }, + // [] (kind::Delegate) -> std::uint32_t { return 0; } + // }; + + // const evmc_message evmcMessage{ + // .kind = getKind(kind), + // .flags = getFlags(kind), + // .depth = 0, + // .gas = gas.value(), + // .recipient = msg.to.toEvmcAddress(), + // .sender = msg.from.toEvmcAddress(), + // .input_data = msg.input.data(), + // .input_size = msg.input.size(), + // .value = Utils::uint256ToEvmcUint256(msg.value), + // .create2_salt = {}, + // .code_address = {} + // }; + + // return executeCallImpl(evmcMessage, gas, code, ); + // } + + bool account_exists(const evmc::address& addr) const noexcept override final; + evmc::bytes32 get_storage(const evmc::address& addr, const evmc::bytes32& key) const noexcept override final; + evmc_storage_status set_storage(const evmc::address& addr, const evmc::bytes32& key, const evmc::bytes32& value) noexcept override final; + evmc::uint256be get_balance(const evmc::address& addr) const noexcept override final; + size_t get_code_size(const evmc::address& addr) const noexcept override final; + evmc::bytes32 get_code_hash(const evmc::address& addr) const noexcept override final; + size_t copy_code(const evmc::address& addr, size_t code_offset, uint8_t* buffer_data, size_t buffer_size) const noexcept override final; + bool selfdestruct(const evmc::address& addr, const evmc::address& beneficiary) noexcept override final; + evmc_tx_context get_tx_context() const noexcept override final; + evmc::bytes32 get_block_hash(int64_t number) const noexcept override final; + void emit_log(const evmc::address& addr, const uint8_t* data, size_t data_size, const evmc::bytes32 topics[], size_t topics_count) noexcept override final; + evmc_access_status access_account(const evmc::address& addr) noexcept override final; + evmc_access_status access_storage(const evmc::address& addr, const evmc::bytes32& key) noexcept override final; + evmc::bytes32 get_transient_storage(const evmc::address &addr, const evmc::bytes32 &key) const noexcept override final; + void set_transient_storage(const evmc::address &addr, const evmc::bytes32 &key, const evmc::bytes32 &value) noexcept override final; + evmc::Result call(const evmc_message& msg) noexcept override final; + +private: + AnyCallHandler callHandler_; + evmc_vm* vm_; + VmStorage& vmStorage_; + Accounts& accounts_; + boost::unordered_flat_map transientStorage_; + ContractStack& stack_; + uint64_t eventIndex_ = 0; + const Hash& txHash_; + const Hash& blockHash_; + const evmc_tx_context& currentTxContext_; +}; + +} // namespace evm + diff --git a/src/contract/evm/message.h b/src/contract/evm/message.h new file mode 100644 index 00000000..308f1070 --- /dev/null +++ b/src/contract/evm/message.h @@ -0,0 +1,23 @@ +#pragma once + +#include "../../utils/strings.h" + +namespace evm { + +struct Message { + Address from; + Address to; + uint256_t value; + uint32_t depth; + bytes::View input; +}; + +struct CreateMessage { + Address from; + uint256_t value; + uint32_t depth; + bytes::View code; + std::optional salt; +}; + +} // namespace evm diff --git a/src/contract/gas.h b/src/contract/gas.h new file mode 100644 index 00000000..09dd9bc7 --- /dev/null +++ b/src/contract/gas.h @@ -0,0 +1,52 @@ + +#pragma once + +struct ExecutionFailure : std::runtime_error { + explicit ExecutionFailure(std::string_view str) : std::runtime_error(str.data()) {} +}; + +struct OutOfGas : ExecutionFailure { + OutOfGas() : ExecutionFailure("out of gas") {} +}; + +struct ExecutionReverted : ExecutionFailure { + explicit ExecutionReverted(std::string_view msg) : ExecutionFailure(msg) {} + explicit ExecutionReverted() : ExecutionReverted("execution reverted") {} +}; + +namespace kind { + +struct Normal{}; +struct Static{}; +struct Delegate{}; + +using Any = std::variant; + +inline constexpr Normal NORMAL{}; +inline constexpr Static STATIC{}; +inline constexpr Delegate DELEGATE{}; + +}; + +class Gas { +public: + constexpr explicit Gas(uint64_t value) : value_(value) {} + + constexpr uint64_t value() const { return value_; } + + constexpr void use(uint64_t amount) { + if (amount > value_) { + value_ = 0; + throw OutOfGas(); + } + + value_ -= amount; + } + + constexpr void refund(uint64_t amount) { + value_ += amount; // TODO: check overflow? + } + +// private: + uint64_t value_; +}; \ No newline at end of file diff --git a/src/contract/traits/method.h b/src/contract/traits/method.h new file mode 100644 index 00000000..cb8d2e7a --- /dev/null +++ b/src/contract/traits/method.h @@ -0,0 +1,22 @@ +#pragma once + +namespace traits { + +template +struct Method {}; + +template +struct Method { + using ReturnType = R; + using ClassType = C; + static constexpr bool IsView = false; +}; + +template +struct Method { + using ReturnType = R; + using ClassType = const C; + static constexpr bool IsView = true; +}; + +} // namespace traits diff --git a/src/core/state.cpp b/src/core/state.cpp index c0404715..352fa7eb 100644 --- a/src/core/state.cpp +++ b/src/core/state.cpp @@ -197,7 +197,8 @@ void State::processTransaction( // as it calls validateNextBlock as a sanity check. Account& accountFrom = *this->accounts_[tx.getFrom()]; Account& accountTo = *this->accounts_[tx.getTo()]; - auto leftOverGas = int64_t(tx.getGasLimit()); + // auto leftOverGas = int64_t(tx.getGasLimit()); + Gas gasLeft(tx.getGasLimit().convert_to()); auto& fromNonce = accountFrom.nonce; auto& fromBalance = accountFrom.balance; if (fromBalance < (tx.getValue() + tx.getGasLimit() * tx.getMaxFeePerGas())) { @@ -239,18 +240,26 @@ void State::processTransaction( tx.hash(), txIndex, blockHash, - leftOverGas + gasLeft ); host.execute(tx.txToMessage(), accountTo.contractType); + + // if (tx.getTo() == Address()) { + // CreateMessage msg{ tx.getTo(), tx.getFrom(), tx.getValue(), 0, tx.getData() }; + // host.execute(msg); + // } else { + // host.execute(tx.txToMessage(), accountTo.contractType); + // } + } catch (std::exception& e) { LOGERRORP("Transaction: " + tx.hash().hex().get() + " failed to process, reason: " + e.what()); } - if (leftOverGas < 0) { - leftOverGas = 0; // We don't want to """refund""" gas due to negative gas - } + // if (leftOverGas < 0) { + // leftOverGas = 0; // We don't want to """refund""" gas due to negative gas + // } ++fromNonce; - auto usedGas = tx.getGasLimit() - leftOverGas; + auto usedGas = tx.getGasLimit() - gasLeft.value(); fromBalance -= (usedGas * tx.getMaxFeePerGas()); } @@ -449,7 +458,7 @@ Bytes State::ethCall(const evmc_message& callInfo) { } const auto& acc = accIt->second; if (acc->isContract()) { - int64_t leftOverGas = callInfo.gas; + Gas gasLeft(callInfo.gas); evmc_tx_context txContext; txContext.tx_gas_price = {}; txContext.tx_origin = callInfo.sender; @@ -477,7 +486,7 @@ Bytes State::ethCall(const evmc_message& callInfo) { Hash(), 0, Hash(), - leftOverGas + gasLeft ).ethCallView(callInfo, acc->contractType); } else { return {}; @@ -508,13 +517,14 @@ int64_t State::estimateGas(const evmc_message& callInfo) { Hash(), 0, Hash(), - leftOverGas + gasLeft ).simulate(callInfo, type); - auto left = callInfo.gas - leftOverGas; - if (left < 0) { - left = 0; + // return gasLeft.value(); + auto used = callInfo.gas - gasLeft.value(); + if (used < 0) { + used = 0; } - return left; + return used; } std::vector> State::getCppContracts() const { diff --git a/src/utils/utils.h b/src/utils/utils.h index 61d1802d..cfafe0c9 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -344,6 +344,10 @@ template struct EventParam { /// Namespace for utility functions. namespace Utils { + + template + struct Overloaded : Ts... { using Ts::operator()...; }; + std::string getTestDumpPath(); ///< Get the path to the test dump folder. // TODO: document those later diff --git a/tests/statetest.hpp b/tests/statetest.hpp index 71bd488f..8148a033 100644 --- a/tests/statetest.hpp +++ b/tests/statetest.hpp @@ -2,7 +2,6 @@ #include "../src/contract/contracthost.h" - // TestState class // The only purpose of this class is to be able to allow // Direct call to internal methods of State class @@ -24,7 +23,7 @@ class StateTest : public State { const Hash& randomness, const Hash& txHash, const Hash& blockHash, - int64_t& leftOverGas) { + Gas& gasLeft) { ContractHost host( this->vm_, this->dumpManager_, @@ -37,7 +36,7 @@ class StateTest : public State { txHash, 0, blockHash, - leftOverGas + gasLeft ); host.execute(callInfo, type); } From d20a7252f2cbe10b833945b378015910f76b0c42 Mon Sep 17 00:00:00 2001 From: Leonardo Amaral Date: Mon, 30 Sep 2024 09:15:41 -0300 Subject: [PATCH 07/37] initial evm executor --- src/contract/cpp/callexecutor.h | 6 +- src/contract/evm/callexecutor.cpp | 103 ++++++++++++++++++++++++++++++ src/contract/evm/callexecutor.h | 2 + 3 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 src/contract/evm/callexecutor.cpp diff --git a/src/contract/cpp/callexecutor.h b/src/contract/cpp/callexecutor.h index 338372f5..61a7b25b 100644 --- a/src/contract/cpp/callexecutor.h +++ b/src/contract/cpp/callexecutor.h @@ -45,9 +45,7 @@ class CallExecutor { auto *contract = getContract(msg.to); - // TODO: check nullptr - - auto binded = [&] (auto&&... args) { + const auto binded = [&] (auto&&... args) { if constexpr (std::same_as) { return std::invoke(msg.method.func, *contract, std::forward(args)...); } else { @@ -69,6 +67,8 @@ class CallExecutor { } private: + + // TODO: return as reference template C* getContract(const Address& address) { const auto it = contracts_.find(address); diff --git a/src/contract/evm/callexecutor.cpp b/src/contract/evm/callexecutor.cpp new file mode 100644 index 00000000..9396d2c9 --- /dev/null +++ b/src/contract/evm/callexecutor.cpp @@ -0,0 +1,103 @@ +#include "callexecutor.h" + +constexpr decltype(auto) findAnd(auto&& map, const auto& key, auto andThen, auto orElse) noexcept { + const auto it = map.find(key); + + if (it == map.end()) { + return std::invoke(orElse); + } + + return std::invoke(andThen, it->second); +} + +static evmc::bytes32 thenToBytes32(const Hash& hash) noexcept { + return hash.toEvmcBytes32(); +} + +template +static T orDefault() noexcept { + return T{}; +} + +namespace evm { + +Bytes CallExecutor::executeCall(kind::Any callKind, Gas& gas, const Message& msg) { + +} + +bool CallExecutor::account_exists(const evmc::address& addr) const noexcept override final { + return accounts_.find(addr) != accounts_.end(); +} + +evmc::bytes32 CallExecutor::get_storage(const evmc::address& addr, const evmc::bytes32& key) const noexcept override final { + return findAnd(vmStorage_, StorageKey(addr, key), thenToBytes32, orDefault); +} + +evmc_storage_status CallExecutor::set_storage(const evmc::address& addr, const evmc::bytes32& key, const evmc::bytes32& value) noexcept override final { + const StorageKey storageKey(addr, key); + auto& storageValue = vmStorage_[storageKey]; + stack_.registerStorageChange(storageKey, storageValue); + storageValue = Hash(value); + return EVMC_STORAGE_MODIFIED; +} + +evmc::uint256be CallExecutor::get_balance(const evmc::address& addr) const noexcept override final { + return findAnd(accounts_, addr, + [] (const auto& account) { return Utils::uint256ToEvmcUint256(account->balance); }, + orDefault); +} + +size_t CallExecutor::get_code_size(const evmc::address& addr) const noexcept override final { + return findAnd(accounts_, addr, + [] (const auto& account) { return account->code.size(); }, + orDefault); +} + +evmc::bytes32 CallExecutor::get_code_hash(const evmc::address& addr) const noexcept override final { + return findAnd(accounts_, addr, + [] (const auto& account) { return account->codeHash.toEvmcBytes32(); }, + orDefault); +} + +size_t CallExecutor::copy_code(const evmc::address& addr, size_t code_offset, uint8_t* buffer_data, size_t buffer_size) const noexcept override final { + return findAnd(); +} + +bool CallExecutor::selfdestruct(const evmc::address& addr, const evmc::address& beneficiary) noexcept override final { + +} + +evmc_tx_context CallExecutor::get_tx_context() const noexcept override final { + +} + +evmc::bytes32 CallExecutor::get_block_hash(int64_t number) const noexcept override final { + +} + +void CallExecutor::emit_log(const evmc::address& addr, const uint8_t* data, size_t data_size, const evmc::bytes32 topics[], size_t topics_count) noexcept override final { + +} + +evmc_access_status CallExecutor::access_account(const evmc::address& addr) noexcept override final { + +} + +evmc_access_status CallExecutor::access_storage(const evmc::address& addr, const evmc::bytes32& key) noexcept override final { + +} + +evmc::bytes32 CallExecutor::get_transient_storage(const evmc::address &addr, const evmc::bytes32 &key) const noexcept override final { + +} + +void CallExecutor::set_transient_storage(const evmc::address &addr, const evmc::bytes32 &key, const evmc::bytes32 &value) noexcept override final { + +} + +evmc::Result CallExecutor::call(const evmc_message& msg) noexcept override final { + +} + + +} // namespace evm \ No newline at end of file diff --git a/src/contract/evm/callexecutor.h b/src/contract/evm/callexecutor.h index 280a06c1..595ba900 100644 --- a/src/contract/evm/callexecutor.h +++ b/src/contract/evm/callexecutor.h @@ -13,6 +13,8 @@ class CallExecutor : public evmc::Host { CallExecutor(AnyCallHandler callHandler, evmc_vm* vm, VmStorage& vmStorage, Accounts& accounts, ContractStack& stack, const Hash& txHash, const Hash& blockHash, const evmc_tx_context& currentTxContext) : callHandler_(std::move(callHandler)), vm_(vm), vmStorage_(vmStorage), accounts_(accounts), stack_(stack), txHash_(txHash), blockHash_(blockHash), currentTxContext_(currentTxContext) {} + Bytes executeCall(kind::Any callKind, Gas& gas, const Message& msg); + // Bytes executeCall(auto& callHandler, auto kind, Gas& gas, const auto& msg) { // } From b5b88aa5bfe201cf9e812b6d74c8944b360b93f7 Mon Sep 17 00:00:00 2001 From: Leonardo Amalaral Date: Wed, 9 Oct 2024 16:43:23 -0300 Subject: [PATCH 08/37] simplecontract and evm working --- src/contract/CMakeLists.txt | 1 + src/contract/calldispatcher.h | 14 +- src/contract/contracthost.cpp | 23 ++- src/contract/contracthost.h | 57 +++++--- src/contract/cpp/callexecutor.cpp | 60 ++++++-- src/contract/cpp/callexecutor.h | 61 +++++--- src/contract/cpp/message.h | 12 +- src/contract/evm/anycallhandler.h | 9 +- src/contract/evm/callexecutor.cpp | 235 ++++++++++++++++++++++++------ src/contract/evm/callexecutor.h | 81 +++++----- src/contract/gas.h | 4 + src/core/state.cpp | 15 +- tests/contract/evm.cpp | 2 +- tests/statetest.hpp | 4 +- 14 files changed, 411 insertions(+), 167 deletions(-) diff --git a/src/contract/CMakeLists.txt b/src/contract/CMakeLists.txt index 2b3e8b92..97b58f80 100644 --- a/src/contract/CMakeLists.txt +++ b/src/contract/CMakeLists.txt @@ -55,6 +55,7 @@ set(CONTRACT_SOURCES ${CMAKE_SOURCE_DIR}/src/contract/calltracer.cpp ${CMAKE_SOURCE_DIR}/src/contract/event.cpp ${CMAKE_SOURCE_DIR}/src/contract/cpp/callexecutor.cpp + ${CMAKE_SOURCE_DIR}/src/contract/evm/callexecutor.cpp ${CMAKE_SOURCE_DIR}/src/contract/templates/ownable.cpp ${CMAKE_SOURCE_DIR}/src/contract/templates/erc20.cpp ${CMAKE_SOURCE_DIR}/src/contract/templates/erc721.cpp diff --git a/src/contract/calldispatcher.h b/src/contract/calldispatcher.h index 2e7c61f8..e45e678c 100644 --- a/src/contract/calldispatcher.h +++ b/src/contract/calldispatcher.h @@ -2,20 +2,14 @@ #include "../utils/utils.h" #include "cpp/callexecutor.h" - -struct DummyHandler { - Bytes executeCall(auto, Gas&, const evm::Message&) { throw std::runtime_error("TODO"); } - - template - typename M::ReturnType executeCall(auto, Gas&, const cpp::Message&) { throw std::runtime_error("TODO"); } -}; +#include "evm/callexecutor.h" class CallDispatcher { public: using Accounts = boost::unordered_flat_map, SafeHash>; using TransferHandler = std::function; - CallDispatcher(cpp::CallExecutor cppCallHandler, DummyHandler evmCallHandler, TransferHandler transferHandler, Accounts& accounts) + CallDispatcher(cpp::CallExecutor cppCallHandler, evm::CallExecutor evmCallHandler, TransferHandler transferHandler, Accounts& accounts) : cppCallHandler_(std::move(cppCallHandler)), evmCallHandler_(std::move(evmCallHandler)), transferHandler_(std::move(transferHandler)), accounts_(accounts) {} decltype(auto) onCall(auto kind, Gas& gas, auto&& msg) { @@ -38,13 +32,13 @@ class CallDispatcher { if (account->contractType == ContractType::CPP) { return cppCallHandler_.executeCall(kind, gas, std::forward(msg)); } else { - return evmCallHandler_.executeCall(kind, gas, std::forward(msg)); + return evmCallHandler_.executeCall(kind, gas, std::forward(msg), account->code); } } private: Accounts& accounts_; cpp::CallExecutor cppCallHandler_; - DummyHandler evmCallHandler_; + evm::CallExecutor evmCallHandler_; TransferHandler transferHandler_; }; diff --git a/src/contract/contracthost.cpp b/src/contract/contracthost.cpp index e3c00a55..f60530a0 100644 --- a/src/contract/contracthost.cpp +++ b/src/contract/contracthost.cpp @@ -280,8 +280,17 @@ void ContractHost::execute(const evmc_message& msg, const ContractType& type) { .input = bytes::View(msg.input_data, msg.input_size) }; + Gas gas(leftoverGas_); + // TODO: return the result - execute(std::move(emsg)); + try { + execute(gas, std::move(emsg)); + leftoverGas_ = gas.value(); + } catch (const std::exception& e) { + leftoverGas_ = gas.value(); + throw e; + } + this->mustRevert_ = false; return; } @@ -393,6 +402,18 @@ void ContractHost::execute(const evmc_message& msg, const ContractType& type) { } Bytes ContractHost::ethCallView(const evmc_message& msg, const ContractType& type) { + Gas gas(leftoverGas_); // TODO: or we can just set a very high value, why not? + + evm::Message evmcMsg{ + .from = Address(msg.sender), + .to = Address(msg.recipient), + .value = Utils::evmcUint256ToUint256(msg.value), + .depth = 0, + .input = bytes::View(msg.input_data, msg.input_size) + }; + + return callHandler_.onCall(kind::STATIC, gas, std::move(evmcMsg)); + Bytes ret; //const Address from(tx.sender); const Address to(msg.recipient); diff --git a/src/contract/contracthost.h b/src/contract/contracthost.h index 75ecf1cc..b53d2abc 100644 --- a/src/contract/contracthost.h +++ b/src/contract/contracthost.h @@ -87,12 +87,11 @@ class ContractHost : public evmc::Host { const Hash& txHash_; const uint64_t txIndex_; const Hash& blockHash_; - uint64_t - & leftoverGas_; /// Reference to the leftover gas from the transaction. + int64_t& leftoverGas_; /// Reference to the leftover gas from the transaction. /// The leftoverGas_ is a object given by the State TxAdditionalData addTxData_; trace::CallTracer callTracer_; - Gas& gas_; + Gas *gas_ = nullptr; CallDispatcher callHandler_; // Private as this is not available for contracts as it has safety checks @@ -165,7 +164,7 @@ class ContractHost : public evmc::Host { const Hash& txHash, const uint64_t txIndex, const Hash& blockHash, - Gas& gas) : + int64_t& txGasLimit) : vm_(vm), manager_(manager), storage_(storage), @@ -177,10 +176,10 @@ class ContractHost : public evmc::Host { txHash_(txHash), txIndex_(txIndex), blockHash_(blockHash), - leftoverGas_(gas.value_), + leftoverGas_(txGasLimit), addTxData_({.hash = txHash}), - gas_(gas), - callHandler_(cpp::CallExecutor(*this, contracts_), DummyHandler(), [this] (const Address& from, const Address& to, const uint256_t& value) { transfer(from, to, value); }, accounts) {} + stack_(), + callHandler_(cpp::CallExecutor(*this, contracts_), evm::CallExecutor(callHandler_, vm_, vmStorage, accounts, stack_, txHash, txIndex, blockHash, currentTxContext), [this] (const Address& from, const Address& to, const uint256_t& value) { transfer(from, to, value); }, accounts) {} // TODO: remove this lambda? // callHandler_(cpp::CallExecutor(*this, contracts_), DummyHandler(), std::function(), accounts_){} // callHandler_(vm_, accounts_, contracts_, nullptr) { // callHandler_.setContractHost(this); @@ -203,27 +202,17 @@ class ContractHost : public evmc::Host { /// Executes a call void execute(const evmc_message& msg, const ContractType& type); - decltype(auto) execute(auto&& msg) { - gas_.use(21000); + decltype(auto) execute(Gas& gas, auto&& msg) { + gas.use(21000); try { - return callHandler_.onCall(kind::NORMAL, gas_, std::forward(msg)); + return callHandler_.onCall(kind::NORMAL, gas, std::forward(msg)); } catch (const std::exception& err) { mustRevert_ = true; throw err; } } - // decltype(auto) execute(auto&& msg) { - // gas_.use(21000); - // return callHandler_.onCall(kind::Normal, gas_, std::forward(msg)); - // } - - // Address execute(Gas& gas, const CreateMessage& msg) { - // gas.use(666); // TODO: how much for a contract creation? - // return callHandler_.onCreate(gas, msg); - // } - /// Executes a eth_call RPC method (view) /// returns the result of the call Bytes ethCallView(const evmc_message& msg, const ContractType& type); @@ -273,7 +262,7 @@ class ContractHost : public evmc::Host { .method = cpp::PackagedMethod(func, args...) }; - return callHandler_.onCall(kind::STATIC, gas_, std::move(msg)); + return callHandler_.onCall(kind::STATIC, *gas_, std::move(msg)); const auto recipientAccIt = this->accounts_.find(targetAddr); if (recipientAccIt == this->accounts_.end()) { @@ -424,7 +413,7 @@ class ContractHost : public evmc::Host { .method = cpp::PackagedMethod(func, args...) }; - return callHandler_.onCall(kind::NORMAL, gas_, std::move(msg)); + return callHandler_.onCall(kind::NORMAL, *gas_, std::move(msg)); // 1000 Gas Limit for every C++ contract call! auto& recipientAcc = *this->accounts_[targetAddr]; @@ -654,6 +643,30 @@ class ContractHost : public evmc::Host { } /// END OF CONTRACT INTERFACING FUNCTIONS + // auto setGasContext(Gas& gas) { + // struct GasContextGuard { + // ~GasContextGuard() { + // *target = prevGas; + // } + + // Gas** target; + // Gas* prevGas; + // }; + + // const GasContextGuard guard{ .target = &gas_, .prevGas = gas_ }; + + // gas_ = &gas; + + // return guard; + // } + + void setGas(Gas& gas) { + gas_ = &gas; + } + + Gas& getGas() { + return *gas_; // TODO: check null + } }; #endif // CONTRACT_HOST_H diff --git a/src/contract/cpp/callexecutor.cpp b/src/contract/cpp/callexecutor.cpp index 25674645..88eec852 100644 --- a/src/contract/cpp/callexecutor.cpp +++ b/src/contract/cpp/callexecutor.cpp @@ -3,23 +3,34 @@ namespace cpp { -Bytes CallExecutor::executeCall(kind::Normal, Gas& gas, const evm::Message& msg) { - gas.use(1000); +GasGuard::GasGuard(ContractHost& host) + : host_(host), prevGas_(host.getGas()) {} - const auto it = contracts_.find(msg.to); +GasGuard::~GasGuard() { + host_.setGas(prevGas_); +} - if (it == contracts_.end()) { - throw std::runtime_error("Contract not found"); // TODO: proper error + more info - } +Bytes CallExecutor::executeCall(kind::Normal, Gas& gas, const evm::Message& msg) { + const evmc_message evmcMsg{ + .kind = EVMC_CALL, + .flags = 0, + .depth = msg.depth, + .gas = gas.value(), + .recipient = msg.to.toEvmcAddress(), + .sender = msg.from.toEvmcAddress(), + .input_data = msg.input.data(), + .input_size = msg.input.size() + }; - auto& contract = *it->second; + const GasGuard gasContextGuard = setGasContext(gas); - contract.caller_ = msg.from; - contract.value_ = msg.value; + return prepareCall(gas, msg).evmEthCall(evmcMsg, &host_); +} +Bytes CallExecutor::executeCall(kind::Static, Gas& gas, const evm::Message& msg) { const evmc_message evmcMsg{ .kind = EVMC_CALL, - .flags = 0, + .flags = EVMC_STATIC, .depth = msg.depth, .gas = gas.value(), .recipient = msg.to.toEvmcAddress(), @@ -28,7 +39,34 @@ Bytes CallExecutor::executeCall(kind::Normal, Gas& gas, const evm::Message& msg) .input_size = msg.input.size() }; - return contract.evmEthCall(evmcMsg, &host_); + // TODO: construct evmc_message only after call is prepared, so gas will be correct + + const GasGuard gasContextGuard = setGasContext(gas); + + return prepareCall(gas, msg).ethCallView(evmcMsg, &host_); +} + +BaseContract& CallExecutor::prepareCall(Gas& gas, const evm::Message& msg) { + gas.use(1000); + + const auto it = contracts_.find(msg.to); + + if (it == contracts_.end()) { + throw ExecutionFailure("Contract not found"); // TODO: proper error + more info + } + + auto& contract = *it->second; + + contract.caller_ = msg.from; + contract.value_ = msg.value; + + return contract; +} + +GasGuard CallExecutor::setGasContext(Gas& gas) { + GasGuard guard(host_); + host_.setGas(gas); + return guard; } } // namespace cpp diff --git a/src/contract/cpp/callexecutor.h b/src/contract/cpp/callexecutor.h index 61a7b25b..463121c2 100644 --- a/src/contract/cpp/callexecutor.h +++ b/src/contract/cpp/callexecutor.h @@ -25,6 +25,17 @@ class NestedCallSafeGuard { Address caller_; uint256_t value_; }; + +class GasGuard { +public: + explicit GasGuard(ContractHost& host); + + ~GasGuard(); + +private: + ContractHost& host_; + Gas& prevGas_; +}; class CallExecutor { public: @@ -33,44 +44,50 @@ class CallExecutor { CallExecutor(ContractHost& host, Contracts& contracts) : host_(host), contracts_(contracts) {} - template - decltype(auto) executeCall(CallKind, Gas& gas, Message msg) { - if constexpr (std::same_as) { - throw std::runtime_error("TODO: delegate not allowed for C++ contracts"); - } + template + decltype(auto) executeCall(kind::Delegate, Gas& gas, Message msg) { + throw ExecutionFailure("Delegate call not supported for C++ contracts"); + } + Bytes executeCall(kind::Delegate, Gas& gas, const evm::Message& msg) { + throw ExecutionFailure("Delegate call not supported for C++ contracts"); + } + + template + decltype(auto) executeCall(auto callKind, Gas& gas, Message msg) { gas.use(1000); NestedCallSafeGuard guard(msg.caller, msg.caller->caller_, msg.caller->value_); - auto *contract = getContract(msg.to); + auto& contract = getContract(msg.to); - const auto binded = [&] (auto&&... args) { - if constexpr (std::same_as) { - return std::invoke(msg.method.func, *contract, std::forward(args)...); - } else { - return contract->callContractFunction(&host_, msg.method.func, std::forward(args)...); + const Utils::Overloaded invokeContractFunction{ + [&] (kind::Static, auto&&... args) { + return std::invoke(msg.method.func, contract, std::forward(args)...); + }, + [&] (kind::Normal, auto&&... args) { + return contract.callContractFunction(&host_, msg.method.func, std::forward(args)...); } }; - return std::apply(binded, std::move(msg.method.args)); - } + const GasGuard gasContextGuard = setGasContext(gas); - decltype(auto) executeCall(kind::Static, Gas& gas, const evm::Message& msg) { - throw std::runtime_error("TODO"); + return std::apply([&] (auto&&... args) { + return invokeContractFunction(callKind, std::forward(args)...); + }, std::move(msg.method.args)); } - Bytes executeCall(kind::Normal, Gas& gas, const evm::Message& msg); + Bytes executeCall(kind::Static, Gas& gas, const evm::Message& msg); - decltype(auto) executeCall(kind::Delegate, Gas& gas, const evm::Message& msg) { - throw std::runtime_error("TODO"); - } + Bytes executeCall(kind::Normal, Gas& gas, const evm::Message& msg); private: + BaseContract& prepareCall(Gas& gas, const evm::Message& msg); + + GasGuard setGasContext(Gas& gas); - // TODO: return as reference template - C* getContract(const Address& address) { + C& getContract(const Address& address) { const auto it = contracts_.find(address); if (it == contracts_.end()) { @@ -83,7 +100,7 @@ class CallExecutor { throw ExecutionFailure("Contract is not of the requested type"); // TODO: add more error info } - return ptr; + return *ptr; } ContractHost& host_; diff --git a/src/contract/cpp/message.h b/src/contract/cpp/message.h index 0057d863..02f82a84 100644 --- a/src/contract/cpp/message.h +++ b/src/contract/cpp/message.h @@ -23,13 +23,21 @@ struct PackagedMethod { template explicit PackagedMethod(M, Ts&&...) -> PackagedMethod; -template +template struct Message { Address from; Address to; uint256_t value; + uint32_t depth; const BaseContract *caller; - Method method; + MethodType method; +}; + +template +struct CreateMessage { + const BaseContract *caller; + Address from; + std::tuple args; }; } // namespace cpp diff --git a/src/contract/evm/anycallhandler.h b/src/contract/evm/anycallhandler.h index 9aa3ad7c..c356a9d6 100644 --- a/src/contract/evm/anycallhandler.h +++ b/src/contract/evm/anycallhandler.h @@ -10,18 +10,17 @@ class AnyCallHandler { template AnyCallHandler(T& callHandler) : object_(&callHandler), - func_([] (void *object, kind::Any callKind, Gas& gas, const CallMessage& msg) { + func_([] (void *object, kind::Any callKind, Gas& gas, const Message& msg) { return std::visit([&] (auto callKind) { return static_cast(object)->onCall(callKind, gas, msg); }, callKind); }) {} - Bytes onCall(kind::Any callKind, Gas& gas, const CallMessage& msg) { + Bytes onCall(kind::Any callKind, Gas& gas, const Message& msg) { return std::invoke(func_, object_, callKind, gas, msg); - } - + } private: void *object_; - Bytes (*func_)(void*, kind::Any, Gas&, const CallMessage&); + Bytes (*func_)(void*, kind::Any, Gas&, const Message&); }; } // namespace evm diff --git a/src/contract/evm/callexecutor.cpp b/src/contract/evm/callexecutor.cpp index 9396d2c9..5390021a 100644 --- a/src/contract/evm/callexecutor.cpp +++ b/src/contract/evm/callexecutor.cpp @@ -1,39 +1,97 @@ #include "callexecutor.h" -constexpr decltype(auto) findAnd(auto&& map, const auto& key, auto andThen, auto orElse) noexcept { +template +using map_value_t = decltype(std::declval>().second); + +template +using map_value_reference_t = std::add_lvalue_reference_t< + std::conditional_t< + std::is_const_v>, + std::add_const_t>, + map_value_t>>; + +template +constexpr std::optional>>> get(M&& map, const auto& key) noexcept { const auto it = map.find(key); if (it == map.end()) { - return std::invoke(orElse); + return std::nullopt; } - return std::invoke(andThen, it->second); + return it->second; } -static evmc::bytes32 thenToBytes32(const Hash& hash) noexcept { - return hash.toEvmcBytes32(); +constexpr evmc_call_kind getCallKind(kind::Any callKind) noexcept { + return std::visit(Utils::Overloaded{ + [] (kind::Delegate) { return EVMC_DELEGATECALL; }, + [] (kind::Normal) { return EVMC_CALL; }, + [] (kind::Static) { return EVMC_CALL; } + }, callKind); } -template -static T orDefault() noexcept { - return T{}; +constexpr uint32_t getCallFlags(kind::Any callKind) noexcept { + return std::visit(Utils::Overloaded{ + [] (kind::Delegate) -> uint32_t { return 0; }, + [] (kind::Normal) -> uint32_t { return 0; }, + [] (kind::Static) -> uint32_t { return EVMC_STATIC; } + }, callKind); } namespace evm { -Bytes CallExecutor::executeCall(kind::Any callKind, Gas& gas, const Message& msg) { - +Bytes CallExecutor::executeCall(kind::Any callKind, Gas& gas, const Message& msg, bytes::View code) { + const evmc_message evmcMsg{ + .kind = getCallKind(callKind), + .flags = getCallFlags(callKind), + .depth = msg.depth, + .gas = gas.value(), + .recipient = msg.to.toEvmcAddress(), + .sender = msg.from.toEvmcAddress(), + .input_data = msg.input.data(), + .input_size = msg.input.size(), + .value = Utils::uint256ToEvmcUint256(msg.value), + .create2_salt = {}, + .code_address = {} + }; + + evmc::Result result(::evmc_execute(this->vm_, + &this->get_interface(), + this->to_context(), + evmc_revision::EVMC_LATEST_STABLE_REVISION, + &evmcMsg, + code.data(), + code.size())); + + gas.use(evmcMsg.gas - result.gas_left); + + bytes::View output(result.output_data, result.output_size); + + switch (result.status_code) { + case EVMC_SUCCESS: + return Utils::makeBytes(output); + + case EVMC_REVERT: + throw ExecutionReverted("TODO"); + + case EVMC_OUT_OF_GAS: + throw OutOfGas(); + + default: + throw ExecutionFailure("TODO"); + } } -bool CallExecutor::account_exists(const evmc::address& addr) const noexcept override final { +bool CallExecutor::account_exists(const evmc::address& addr) const noexcept { return accounts_.find(addr) != accounts_.end(); } -evmc::bytes32 CallExecutor::get_storage(const evmc::address& addr, const evmc::bytes32& key) const noexcept override final { - return findAnd(vmStorage_, StorageKey(addr, key), thenToBytes32, orDefault); +evmc::bytes32 CallExecutor::get_storage(const evmc::address& addr, const evmc::bytes32& key) const noexcept { + return get(vmStorage_, StorageKey(addr, key)) + .transform([] (const Hash& hash) { return hash.toEvmcBytes32(); }) + .value_or(evmc::bytes32{}); } -evmc_storage_status CallExecutor::set_storage(const evmc::address& addr, const evmc::bytes32& key, const evmc::bytes32& value) noexcept override final { +evmc_storage_status CallExecutor::set_storage(const evmc::address& addr, const evmc::bytes32& key, const evmc::bytes32& value) noexcept { const StorageKey storageKey(addr, key); auto& storageValue = vmStorage_[storageKey]; stack_.registerStorageChange(storageKey, storageValue); @@ -41,63 +99,150 @@ evmc_storage_status CallExecutor::set_storage(const evmc::address& addr, const e return EVMC_STORAGE_MODIFIED; } -evmc::uint256be CallExecutor::get_balance(const evmc::address& addr) const noexcept override final { - return findAnd(accounts_, addr, - [] (const auto& account) { return Utils::uint256ToEvmcUint256(account->balance); }, - orDefault); -} - -size_t CallExecutor::get_code_size(const evmc::address& addr) const noexcept override final { - return findAnd(accounts_, addr, - [] (const auto& account) { return account->code.size(); }, - orDefault); +evmc::uint256be CallExecutor::get_balance(const evmc::address& addr) const noexcept { + return get(accounts_, addr) + .transform([] (const auto& account) { return Utils::uint256ToEvmcUint256(account.get()->balance); }) + .value_or(evmc::uint256be{}); } -evmc::bytes32 CallExecutor::get_code_hash(const evmc::address& addr) const noexcept override final { - return findAnd(accounts_, addr, - [] (const auto& account) { return account->codeHash.toEvmcBytes32(); }, - orDefault); +size_t CallExecutor::get_code_size(const evmc::address& addr) const noexcept { + return get(accounts_, addr) + .transform([] (const auto& account) { return account.get()->code.size(); }) + .value_or(0); } -size_t CallExecutor::copy_code(const evmc::address& addr, size_t code_offset, uint8_t* buffer_data, size_t buffer_size) const noexcept override final { - return findAnd(); +evmc::bytes32 CallExecutor::get_code_hash(const evmc::address& addr) const noexcept { + return get(accounts_, addr) + .transform([] (const auto& account) { return account.get()->codeHash.toEvmcBytes32(); }) + .value_or(evmc::bytes32{}); } -bool CallExecutor::selfdestruct(const evmc::address& addr, const evmc::address& beneficiary) noexcept override final { +size_t CallExecutor::copy_code(const evmc::address& addr, size_t code_offset, uint8_t* buffer_data, size_t buffer_size) const noexcept { + const auto thenCopyCode = [&] (const auto& account) -> size_t { + bytes::View code = account.get()->code; -} + if (code_offset < code.size()) { + const size_t n = std::min(buffer_size, code.size() - code_offset); + if (n > 0) + std::copy_n(&code[code_offset], n, buffer_data); + return n; + } -evmc_tx_context CallExecutor::get_tx_context() const noexcept override final { + return 0; + }; + return get(accounts_, addr) + .transform(thenCopyCode) + .value_or(0); } -evmc::bytes32 CallExecutor::get_block_hash(int64_t number) const noexcept override final { - +bool CallExecutor::selfdestruct(const evmc::address& addr, const evmc::address& beneficiary) noexcept { + return false; } -void CallExecutor::emit_log(const evmc::address& addr, const uint8_t* data, size_t data_size, const evmc::bytes32 topics[], size_t topics_count) noexcept override final { - +evmc_tx_context CallExecutor::get_tx_context() const noexcept { + return currentTxContext_; } -evmc_access_status CallExecutor::access_account(const evmc::address& addr) noexcept override final { +evmc::bytes32 CallExecutor::get_block_hash(int64_t number) const noexcept { + return Utils::uint256ToEvmcUint256(number); +} +void CallExecutor::emit_log(const evmc::address& addr, const uint8_t* data, size_t data_size, const evmc::bytes32 topics[], size_t topics_count) noexcept { + try { + std::vector topics_; + for (uint64_t i = 0; i < topics_count; i++) { + topics_.emplace_back(topics[i]); + } + Event event("", // EVM events do not have names + this->eventIndex_, + this->txHash_, + this->txIndex_, + this->blockHash_, + this->currentTxContext_.block_number, + addr, + Bytes(data, data + data_size), + topics_, + (topics_count == 0) + ); + ++this->eventIndex_; + this->stack_.registerEvent(std::move(event)); + } catch (const std::exception& ignored) {} } -evmc_access_status CallExecutor::access_storage(const evmc::address& addr, const evmc::bytes32& key) noexcept override final { +evmc_access_status CallExecutor::access_account(const evmc::address& addr) noexcept { + return EVMC_ACCESS_WARM; +} +evmc_access_status CallExecutor::access_storage(const evmc::address& addr, const evmc::bytes32& key) noexcept { + return EVMC_ACCESS_WARM; } -evmc::bytes32 CallExecutor::get_transient_storage(const evmc::address &addr, const evmc::bytes32 &key) const noexcept override final { +evmc::bytes32 CallExecutor::get_transient_storage(const evmc::address &addr, const evmc::bytes32 &key) const noexcept { + return get(transientStorage_, StorageKey(addr, key)) + .transform([] (const Hash& hash) { return hash.toEvmcBytes32(); }) + .value_or(evmc::bytes32{}); +} +void CallExecutor::set_transient_storage(const evmc::address &addr, const evmc::bytes32 &key, const evmc::bytes32 &value) noexcept { + transientStorage_.emplace(StorageKey(addr, key), value); } -void CallExecutor::set_transient_storage(const evmc::address &addr, const evmc::bytes32 &key, const evmc::bytes32 &value) noexcept override final { +evmc::Result CallExecutor::call(const evmc_message& msg) noexcept { + Message callMsg; + CreateMessage createMsg; + evmc_status_code status; + std::variant result = Bytes(); + kind::Any callKind; -} + Gas gas(msg.gas); -evmc::Result CallExecutor::call(const evmc_message& msg) noexcept override final { + if (msg.kind == EVMC_DELEGATECALL) { + callKind = kind::DELEGATE; + } else if (msg.flags == EVMC_STATIC) { + callKind = kind::STATIC; + } else { + callKind = kind::NORMAL; + } -} + try { + switch (msg.kind) { + case EVMC_CALL: + case EVMC_DELEGATECALL: + callMsg.from = Address(msg.sender); + callMsg.to = Address(msg.recipient); + callMsg.value = Utils::evmcUint256ToUint256(msg.value); + callMsg.depth = msg.depth; + callMsg.input = bytes::View(msg.input_data, msg.input_size); + result = callHandler_.onCall(callKind, gas, callMsg); + break; + + case EVMC_CREATE2: + [[fallthrough]]; + + case EVMC_CREATE: + [[fallthrough]]; + + case EVMC_CALLCODE: + assert(false); // TODO + } + + status = EVMC_SUCCESS; + + } catch (const OutOfGas&) { + status = EVMC_OUT_OF_GAS; + } catch (const ExecutionReverted&) { + // TODO: encode error if exists + status = EVMC_REVERT; + } catch (const std::exception&) { + // TODO: encode error if exists + status = EVMC_FAILURE; + } + return std::visit(Utils::Overloaded{ + [&] (Bytes output) { return evmc::Result(status, gas.value(), 0, output.data(), output.size()); }, + [&] (const Address& createAddress) { return evmc::Result(status, gas.value(), 0, createAddress.toEvmcAddress()); }, + }, std::move(result)); +} -} // namespace evm \ No newline at end of file +} // namespace evm diff --git a/src/contract/evm/callexecutor.h b/src/contract/evm/callexecutor.h index 595ba900..71a0218a 100644 --- a/src/contract/evm/callexecutor.h +++ b/src/contract/evm/callexecutor.h @@ -2,6 +2,11 @@ #include "message.h" #include "anycallhandler.h" +#include "../gas.h" +#include "../traits/method.h" +#include "../contractstack.h" +#include "../cpp/message.h" +#include "../../utils/contractreflectioninterface.h" namespace evm { @@ -10,43 +15,43 @@ class CallExecutor : public evmc::Host { using VmStorage = boost::unordered_flat_map; using Accounts = boost::unordered_flat_map, SafeHash>; - CallExecutor(AnyCallHandler callHandler, evmc_vm* vm, VmStorage& vmStorage, Accounts& accounts, ContractStack& stack, const Hash& txHash, const Hash& blockHash, const evmc_tx_context& currentTxContext) - : callHandler_(std::move(callHandler)), vm_(vm), vmStorage_(vmStorage), accounts_(accounts), stack_(stack), txHash_(txHash), blockHash_(blockHash), currentTxContext_(currentTxContext) {} - - Bytes executeCall(kind::Any callKind, Gas& gas, const Message& msg); - - // Bytes executeCall(auto& callHandler, auto kind, Gas& gas, const auto& msg) { - // } - - // Bytes executeCall(auto&& callHandler, auto kind, Gas& gas, const CallMessage& msg, bytes::View code) { - // static constexpr auto getKind = Utils::Overloaded{ - // [] (kind::Normal) { return EVMC_CALL; }, - // [] (kind::Static) { return EVMC_CALL; }, - // [] (kind::Delegate) { return EVMC_DELEGATECALL; } - // }; - - // static constexpr auto getFlags = Utils::Overloaded{ - // [] (kind::Normal) -> std::uint32_t { return 0; }, - // [] (kind::Static) -> std::uint32_t { return EVMC_STATIC; }, - // [] (kind::Delegate) -> std::uint32_t { return 0; } - // }; - - // const evmc_message evmcMessage{ - // .kind = getKind(kind), - // .flags = getFlags(kind), - // .depth = 0, - // .gas = gas.value(), - // .recipient = msg.to.toEvmcAddress(), - // .sender = msg.from.toEvmcAddress(), - // .input_data = msg.input.data(), - // .input_size = msg.input.size(), - // .value = Utils::uint256ToEvmcUint256(msg.value), - // .create2_salt = {}, - // .code_address = {} - // }; - - // return executeCallImpl(evmcMessage, gas, code, ); - // } + CallExecutor(AnyCallHandler callHandler, evmc_vm* vm, VmStorage& vmStorage, Accounts& accounts, ContractStack& stack, const Hash& txHash, uint64_t txIndex, const Hash& blockHash, const evmc_tx_context& currentTxContext) + : callHandler_(std::move(callHandler)), vm_(vm), vmStorage_(vmStorage), accounts_(accounts), stack_(stack), txHash_(txHash), txIndex_(txIndex), blockHash_(blockHash), currentTxContext_(currentTxContext) {} + + Bytes executeCall(kind::Any callKind, Gas& gas, const Message& msg, bytes::View code); + + template + M::ReturnType executeCall(auto callKind, Gas& gas, cpp::Message msg, bytes::View code) { + const Bytes input = std::apply([&] (const Args&... args) { + const std::string functionName = ContractReflectionInterface::getFunctionName(msg.method.func); + + if (functionName.empty()) { + throw DynamicException("EVM contract function name is empty (contract not registered?)"); + } + + Bytes res = Utils::makeBytes(Utils::uint32ToBytes(ABI::FunctorEncoder::encode(functionName).value)); + + if constexpr (sizeof...(Args) > 0) { + Utils::appendBytes(res, ABI::Encoder::encodeData(args...)); + } + + return res; + }, msg.method.args); + + const Message newMsg { + .from = msg.from, + .to = msg.to, + .value = msg.value, + .depth = msg.depth, + .input = input + }; + + const Bytes output = executeCall(callKind, gas, newMsg, code); + + if constexpr (not std::same_as) { + return std::get<0>(ABI::Decoder::decodeData(output)); + } + } bool account_exists(const evmc::address& addr) const noexcept override final; evmc::bytes32 get_storage(const evmc::address& addr, const evmc::bytes32& key) const noexcept override final; @@ -74,9 +79,9 @@ class CallExecutor : public evmc::Host { ContractStack& stack_; uint64_t eventIndex_ = 0; const Hash& txHash_; + const uint64_t txIndex_; const Hash& blockHash_; const evmc_tx_context& currentTxContext_; }; } // namespace evm - diff --git a/src/contract/gas.h b/src/contract/gas.h index 09dd9bc7..a7f1dcbe 100644 --- a/src/contract/gas.h +++ b/src/contract/gas.h @@ -47,6 +47,10 @@ class Gas { value_ += amount; // TODO: check overflow? } + constexpr void set(uint64_t amount) { + value_ = amount; + } + // private: uint64_t value_; }; \ No newline at end of file diff --git a/src/core/state.cpp b/src/core/state.cpp index 352fa7eb..1f233069 100644 --- a/src/core/state.cpp +++ b/src/core/state.cpp @@ -197,8 +197,7 @@ void State::processTransaction( // as it calls validateNextBlock as a sanity check. Account& accountFrom = *this->accounts_[tx.getFrom()]; Account& accountTo = *this->accounts_[tx.getTo()]; - // auto leftOverGas = int64_t(tx.getGasLimit()); - Gas gasLeft(tx.getGasLimit().convert_to()); + auto leftOverGas = int64_t(tx.getGasLimit()); auto& fromNonce = accountFrom.nonce; auto& fromBalance = accountFrom.balance; if (fromBalance < (tx.getValue() + tx.getGasLimit() * tx.getMaxFeePerGas())) { @@ -240,7 +239,7 @@ void State::processTransaction( tx.hash(), txIndex, blockHash, - gasLeft + leftOverGas ); host.execute(tx.txToMessage(), accountTo.contractType); @@ -259,7 +258,7 @@ void State::processTransaction( // leftOverGas = 0; // We don't want to """refund""" gas due to negative gas // } ++fromNonce; - auto usedGas = tx.getGasLimit() - gasLeft.value(); + auto usedGas = tx.getGasLimit() - leftOverGas; fromBalance -= (usedGas * tx.getMaxFeePerGas()); } @@ -458,7 +457,7 @@ Bytes State::ethCall(const evmc_message& callInfo) { } const auto& acc = accIt->second; if (acc->isContract()) { - Gas gasLeft(callInfo.gas); + int64_t leftoverGas = callInfo.gas; evmc_tx_context txContext; txContext.tx_gas_price = {}; txContext.tx_origin = callInfo.sender; @@ -486,7 +485,7 @@ Bytes State::ethCall(const evmc_message& callInfo) { Hash(), 0, Hash(), - gasLeft + leftoverGas ).ethCallView(callInfo, acc->contractType); } else { return {}; @@ -517,10 +516,10 @@ int64_t State::estimateGas(const evmc_message& callInfo) { Hash(), 0, Hash(), - gasLeft + leftoverGas ).simulate(callInfo, type); // return gasLeft.value(); - auto used = callInfo.gas - gasLeft.value(); + auto used = callInfo.gas - leftoverGas; if (used < 0) { used = 0; } diff --git a/tests/contract/evm.cpp b/tests/contract/evm.cpp index a4fac915..fe5704fa 100644 --- a/tests/contract/evm.cpp +++ b/tests/contract/evm.cpp @@ -199,7 +199,7 @@ namespace TEVM { REQUIRE (sdk.callViewFunction(erc20Address, &ERC20::balanceOf, sdk.getChainOwnerAccount().address) == uint256_t("10000000000000000000000")); REQUIRE (sdk.callViewFunction(erc20Address, &ERC20::balanceOf, erc20WrapperAddress) == uint256_t("0")); - auto deposit = sdk.callFunction(erc20WrapperAddress, &SolERC20Wrapper::deposit, erc20Address, uint256_t("10000000000000000000000")); + auto deposit = sdk.callFunction(erc20WrapperAddress, &SolERC20Wrapper::deposit, erc20Address, uint256_t("10000000000000000000000")); // TODO: THIS ONE ACTUALLY REQUIRE(sdk.callViewFunction(erc20WrapperAddress, &SolERC20Wrapper::balanceOf, sdk.getChainOwnerAccount().address, erc20Address) == uint256_t("10000000000000000000000")); REQUIRE(sdk.callViewFunction(erc20WrapperAddress, &SolERC20Wrapper::contractBalance, erc20Address) == uint256_t("10000000000000000000000")); REQUIRE(sdk.callViewFunction(erc20Address, &ERC20::balanceOf, sdk.getChainOwnerAccount().address) == uint256_t("0")); diff --git a/tests/statetest.hpp b/tests/statetest.hpp index 8148a033..5fa3de25 100644 --- a/tests/statetest.hpp +++ b/tests/statetest.hpp @@ -23,7 +23,7 @@ class StateTest : public State { const Hash& randomness, const Hash& txHash, const Hash& blockHash, - Gas& gasLeft) { + int64_t& leftoverGas) { ContractHost host( this->vm_, this->dumpManager_, @@ -36,7 +36,7 @@ class StateTest : public State { txHash, 0, blockHash, - gasLeft + leftoverGas ); host.execute(callInfo, type); } From 2f4a890e213ceea704ce8eef10f51c81981dfdd0 Mon Sep 17 00:00:00 2001 From: Leonardo Amalaral Date: Mon, 11 Nov 2024 15:54:01 -0300 Subject: [PATCH 09/37] making compilable again --- src/contract/CMakeLists.txt | 2 -- src/contract/contracthost.cpp | 31 ++----------------------------- src/contract/contracthost.h | 24 +++++++++++------------- src/contract/cpp/callexecutor.cpp | 8 ++++---- src/contract/evm/callexecutor.cpp | 13 +++++++------ src/contract/evm/callexecutor.h | 4 ++-- src/contract/evm/message.h | 4 ++-- src/core/state.cpp | 2 +- 8 files changed, 29 insertions(+), 59 deletions(-) diff --git a/src/contract/CMakeLists.txt b/src/contract/CMakeLists.txt index 97b58f80..13606f33 100644 --- a/src/contract/CMakeLists.txt +++ b/src/contract/CMakeLists.txt @@ -54,8 +54,6 @@ set(CONTRACT_SOURCES ${CMAKE_SOURCE_DIR}/src/contract/dynamiccontract.cpp ${CMAKE_SOURCE_DIR}/src/contract/calltracer.cpp ${CMAKE_SOURCE_DIR}/src/contract/event.cpp - ${CMAKE_SOURCE_DIR}/src/contract/cpp/callexecutor.cpp - ${CMAKE_SOURCE_DIR}/src/contract/evm/callexecutor.cpp ${CMAKE_SOURCE_DIR}/src/contract/templates/ownable.cpp ${CMAKE_SOURCE_DIR}/src/contract/templates/erc20.cpp ${CMAKE_SOURCE_DIR}/src/contract/templates/erc721.cpp diff --git a/src/contract/contracthost.cpp b/src/contract/contracthost.cpp index f60530a0..52300b93 100644 --- a/src/contract/contracthost.cpp +++ b/src/contract/contracthost.cpp @@ -269,32 +269,6 @@ void ContractHost::execute(const evmc_message& msg, const ContractType& type) { const uint256_t value(Utils::evmcUint256ToUint256(msg.value)); const bool isContractCall = isCall(msg); - bool enabled = true; - - if (enabled && to != Address()) { - evm::Message emsg{ - .from{msg.sender}, - .to{msg.recipient}, - .value = Utils::evmcUint256ToUint256(msg.value), - .depth = msg.depth, - .input = bytes::View(msg.input_data, msg.input_size) - }; - - Gas gas(leftoverGas_); - - // TODO: return the result - try { - execute(gas, std::move(emsg)); - leftoverGas_ = gas.value(); - } catch (const std::exception& e) { - leftoverGas_ = gas.value(); - throw e; - } - - this->mustRevert_ = false; - return; - } - if (isContractCall) { this->traceCallStarted(msg); } @@ -409,10 +383,9 @@ Bytes ContractHost::ethCallView(const evmc_message& msg, const ContractType& typ .to = Address(msg.recipient), .value = Utils::evmcUint256ToUint256(msg.value), .depth = 0, - .input = bytes::View(msg.input_data, msg.input_size) + .input = View(msg.input_data, msg.input_size) }; - - return callHandler_.onCall(kind::STATIC, gas, std::move(evmcMsg)); + // return callHandler_.onCall(kind::STATIC, gas, std::move(evmcMsg)); Bytes ret; //const Address from(tx.sender); diff --git a/src/contract/contracthost.h b/src/contract/contracthost.h index b53d2abc..c46bab3b 100644 --- a/src/contract/contracthost.h +++ b/src/contract/contracthost.h @@ -92,7 +92,6 @@ class ContractHost : public evmc::Host { TxAdditionalData addTxData_; trace::CallTracer callTracer_; Gas *gas_ = nullptr; - CallDispatcher callHandler_; // Private as this is not available for contracts as it has safety checks void transfer(const Address& from, const Address& to, const uint256_t& value); @@ -178,8 +177,7 @@ class ContractHost : public evmc::Host { blockHash_(blockHash), leftoverGas_(txGasLimit), addTxData_({.hash = txHash}), - stack_(), - callHandler_(cpp::CallExecutor(*this, contracts_), evm::CallExecutor(callHandler_, vm_, vmStorage, accounts, stack_, txHash, txIndex, blockHash, currentTxContext), [this] (const Address& from, const Address& to, const uint256_t& value) { transfer(from, to, value); }, accounts) {} // TODO: remove this lambda? + stack_() {} // callHandler_(cpp::CallExecutor(*this, contracts_), DummyHandler(), std::function(), accounts_){} // callHandler_(vm_, accounts_, contracts_, nullptr) { // callHandler_.setContractHost(this); @@ -202,15 +200,15 @@ class ContractHost : public evmc::Host { /// Executes a call void execute(const evmc_message& msg, const ContractType& type); - decltype(auto) execute(Gas& gas, auto&& msg) { - gas.use(21000); + decltype(auto) execute(auto&& msg) { + // gas.use(21000); - try { - return callHandler_.onCall(kind::NORMAL, gas, std::forward(msg)); - } catch (const std::exception& err) { - mustRevert_ = true; - throw err; - } + // try { + // return callHandler_.onCall(kind::NORMAL, gas, std::forward(msg)); + // } catch (const std::exception& err) { + // mustRevert_ = true; + // throw err; + // } } /// Executes a eth_call RPC method (view) @@ -262,7 +260,7 @@ class ContractHost : public evmc::Host { .method = cpp::PackagedMethod(func, args...) }; - return callHandler_.onCall(kind::STATIC, *gas_, std::move(msg)); + // return callHandler_.onCall(kind::STATIC, *gas_, std::move(msg)); const auto recipientAccIt = this->accounts_.find(targetAddr); if (recipientAccIt == this->accounts_.end()) { @@ -413,7 +411,7 @@ class ContractHost : public evmc::Host { .method = cpp::PackagedMethod(func, args...) }; - return callHandler_.onCall(kind::NORMAL, *gas_, std::move(msg)); + // return callHandler_.onCall(kind::NORMAL, *gas_, std::move(msg)); // 1000 Gas Limit for every C++ contract call! auto& recipientAcc = *this->accounts_[targetAddr]; diff --git a/src/contract/cpp/callexecutor.cpp b/src/contract/cpp/callexecutor.cpp index 88eec852..115bca6e 100644 --- a/src/contract/cpp/callexecutor.cpp +++ b/src/contract/cpp/callexecutor.cpp @@ -16,8 +16,8 @@ Bytes CallExecutor::executeCall(kind::Normal, Gas& gas, const evm::Message& msg) .flags = 0, .depth = msg.depth, .gas = gas.value(), - .recipient = msg.to.toEvmcAddress(), - .sender = msg.from.toEvmcAddress(), + .recipient = bytes::cast(msg.to), + .sender = bytes::cast(msg.from), .input_data = msg.input.data(), .input_size = msg.input.size() }; @@ -33,8 +33,8 @@ Bytes CallExecutor::executeCall(kind::Static, Gas& gas, const evm::Message& msg) .flags = EVMC_STATIC, .depth = msg.depth, .gas = gas.value(), - .recipient = msg.to.toEvmcAddress(), - .sender = msg.from.toEvmcAddress(), + .recipient = bytes::cast(msg.to), + .sender = bytes::cast(msg.from), .input_data = msg.input.data(), .input_size = msg.input.size() }; diff --git a/src/contract/evm/callexecutor.cpp b/src/contract/evm/callexecutor.cpp index 5390021a..7b96f0e9 100644 --- a/src/contract/evm/callexecutor.cpp +++ b/src/contract/evm/callexecutor.cpp @@ -1,4 +1,5 @@ #include "callexecutor.h" +#include "bytes/cast.h" template using map_value_t = decltype(std::declval>().second); @@ -39,14 +40,14 @@ constexpr uint32_t getCallFlags(kind::Any callKind) noexcept { namespace evm { -Bytes CallExecutor::executeCall(kind::Any callKind, Gas& gas, const Message& msg, bytes::View code) { +Bytes CallExecutor::executeCall(kind::Any callKind, Gas& gas, const Message& msg, View code) { const evmc_message evmcMsg{ .kind = getCallKind(callKind), .flags = getCallFlags(callKind), .depth = msg.depth, .gas = gas.value(), - .recipient = msg.to.toEvmcAddress(), - .sender = msg.from.toEvmcAddress(), + .recipient = bytes::cast(msg.to), + .sender = bytes::cast(msg.from), .input_data = msg.input.data(), .input_size = msg.input.size(), .value = Utils::uint256ToEvmcUint256(msg.value), @@ -64,7 +65,7 @@ Bytes CallExecutor::executeCall(kind::Any callKind, Gas& gas, const Message& msg gas.use(evmcMsg.gas - result.gas_left); - bytes::View output(result.output_data, result.output_size); + View output(result.output_data, result.output_size); switch (result.status_code) { case EVMC_SUCCESS: @@ -119,7 +120,7 @@ evmc::bytes32 CallExecutor::get_code_hash(const evmc::address& addr) const noexc size_t CallExecutor::copy_code(const evmc::address& addr, size_t code_offset, uint8_t* buffer_data, size_t buffer_size) const noexcept { const auto thenCopyCode = [&] (const auto& account) -> size_t { - bytes::View code = account.get()->code; + View code = account.get()->code; if (code_offset < code.size()) { const size_t n = std::min(buffer_size, code.size() - code_offset); @@ -213,7 +214,7 @@ evmc::Result CallExecutor::call(const evmc_message& msg) noexcept { callMsg.to = Address(msg.recipient); callMsg.value = Utils::evmcUint256ToUint256(msg.value); callMsg.depth = msg.depth; - callMsg.input = bytes::View(msg.input_data, msg.input_size); + callMsg.input = View(msg.input_data, msg.input_size); result = callHandler_.onCall(callKind, gas, callMsg); break; diff --git a/src/contract/evm/callexecutor.h b/src/contract/evm/callexecutor.h index 71a0218a..84220bdd 100644 --- a/src/contract/evm/callexecutor.h +++ b/src/contract/evm/callexecutor.h @@ -18,10 +18,10 @@ class CallExecutor : public evmc::Host { CallExecutor(AnyCallHandler callHandler, evmc_vm* vm, VmStorage& vmStorage, Accounts& accounts, ContractStack& stack, const Hash& txHash, uint64_t txIndex, const Hash& blockHash, const evmc_tx_context& currentTxContext) : callHandler_(std::move(callHandler)), vm_(vm), vmStorage_(vmStorage), accounts_(accounts), stack_(stack), txHash_(txHash), txIndex_(txIndex), blockHash_(blockHash), currentTxContext_(currentTxContext) {} - Bytes executeCall(kind::Any callKind, Gas& gas, const Message& msg, bytes::View code); + Bytes executeCall(kind::Any callKind, Gas& gas, const Message& msg, View code); template - M::ReturnType executeCall(auto callKind, Gas& gas, cpp::Message msg, bytes::View code) { + M::ReturnType executeCall(auto callKind, Gas& gas, cpp::Message msg, View code) { const Bytes input = std::apply([&] (const Args&... args) { const std::string functionName = ContractReflectionInterface::getFunctionName(msg.method.func); diff --git a/src/contract/evm/message.h b/src/contract/evm/message.h index 308f1070..0f4ef824 100644 --- a/src/contract/evm/message.h +++ b/src/contract/evm/message.h @@ -9,14 +9,14 @@ struct Message { Address to; uint256_t value; uint32_t depth; - bytes::View input; + View input; }; struct CreateMessage { Address from; uint256_t value; uint32_t depth; - bytes::View code; + View code; std::optional salt; }; diff --git a/src/core/state.cpp b/src/core/state.cpp index 1f233069..e3176936 100644 --- a/src/core/state.cpp +++ b/src/core/state.cpp @@ -502,7 +502,7 @@ int64_t State::estimateGas(const evmc_message& callInfo) { type = accIt->second->contractType; } - int64_t leftOverGas = callInfo.gas; + int64_t leftoverGas = callInfo.gas; Hash randomSeed = bytes::random(); ContractHost( this->vm_, From 8b24d42f91057a47402dbdba17ef61c73dfe02f7 Mon Sep 17 00:00:00 2001 From: Leonardo Amalaral Date: Wed, 18 Dec 2024 18:34:32 -0300 Subject: [PATCH 10/37] a lot of things, a lot --- src/bytes/cast.h | 1 + src/contract/CMakeLists.txt | 3 + src/contract/abi.cpp | 29 + src/contract/abi.h | 4 + src/contract/calldispatcher.h | 44 - src/contract/contract.cpp | 4 +- src/contract/contractfactory.h | 17 +- src/contract/contracthost.cpp | 837 +----------------- src/contract/contracthost.h | 610 ++----------- src/contract/contractmanager.cpp | 4 +- src/contract/contractmanager.h | 6 +- src/contract/contractstack.h | 37 - src/contract/cpp/callexecutor.h | 2 +- src/contract/dynamiccontract.h | 16 +- src/contract/evm/callexecutor.h | 2 +- .../messages/anyencodedmessagehandler.h | 59 ++ src/contract/messages/basemessage.h | 149 ++++ src/contract/messages/common.cpp | 21 + src/contract/messages/common.h | 43 + src/contract/messages/concepts.h | 107 +++ src/contract/messages/cppcontractexecutor.h | 142 +++ src/contract/messages/encodedmessages.h | 45 + src/contract/messages/evmcontractexecutor.cpp | 359 ++++++++ src/contract/messages/evmcontractexecutor.h | 111 +++ src/contract/messages/executioncontext.cpp | 179 ++++ src/contract/messages/executioncontext.h | 201 +++++ src/contract/messages/executionreverted.h | 15 + src/contract/messages/gas.h | 33 + src/contract/messages/messagedispatcher.h | 54 ++ src/contract/messages/outofgas.h | 8 + src/contract/messages/packedmessages.h | 38 + src/contract/messages/traits.h | 78 ++ src/core/state.cpp | 158 ++-- src/core/state.h | 8 +- src/net/http/jsonrpc/methods.cpp | 63 +- src/utils/transactional.h | 150 ++++ src/utils/tx.cpp | 8 + src/utils/tx.h | 3 + src/utils/utils.h | 10 +- src/utils/view.h | 4 + tests/CMakeLists.txt | 2 + tests/contract/createcontract.cpp | 4 +- tests/contract/evm.cpp | 2 +- .../contract/messages/evmcontractexecutor.cpp | 182 ++++ tests/contract/messages/executioncontext.cpp | 345 ++++++++ tests/core/state.cpp | 40 +- tests/sdktestsuite.hpp | 103 +-- tests/statetest.hpp | 27 - 48 files changed, 2650 insertions(+), 1717 deletions(-) delete mode 100644 src/contract/calldispatcher.h create mode 100644 src/contract/messages/anyencodedmessagehandler.h create mode 100644 src/contract/messages/basemessage.h create mode 100644 src/contract/messages/common.cpp create mode 100644 src/contract/messages/common.h create mode 100644 src/contract/messages/concepts.h create mode 100644 src/contract/messages/cppcontractexecutor.h create mode 100644 src/contract/messages/encodedmessages.h create mode 100644 src/contract/messages/evmcontractexecutor.cpp create mode 100644 src/contract/messages/evmcontractexecutor.h create mode 100644 src/contract/messages/executioncontext.cpp create mode 100644 src/contract/messages/executioncontext.h create mode 100644 src/contract/messages/executionreverted.h create mode 100644 src/contract/messages/gas.h create mode 100644 src/contract/messages/messagedispatcher.h create mode 100644 src/contract/messages/outofgas.h create mode 100644 src/contract/messages/packedmessages.h create mode 100644 src/contract/messages/traits.h create mode 100644 src/utils/transactional.h create mode 100644 tests/contract/messages/evmcontractexecutor.cpp create mode 100644 tests/contract/messages/executioncontext.cpp diff --git a/src/bytes/cast.h b/src/bytes/cast.h index 44a0b485..086cf140 100644 --- a/src/bytes/cast.h +++ b/src/bytes/cast.h @@ -1,6 +1,7 @@ #ifndef BDK_BYTES_CAST_H #define BDK_BYTES_CAST_H +#include #include "range.h" namespace bytes { diff --git a/src/contract/CMakeLists.txt b/src/contract/CMakeLists.txt index 13606f33..0177e2b8 100644 --- a/src/contract/CMakeLists.txt +++ b/src/contract/CMakeLists.txt @@ -54,6 +54,9 @@ set(CONTRACT_SOURCES ${CMAKE_SOURCE_DIR}/src/contract/dynamiccontract.cpp ${CMAKE_SOURCE_DIR}/src/contract/calltracer.cpp ${CMAKE_SOURCE_DIR}/src/contract/event.cpp + ${CMAKE_SOURCE_DIR}/src/contract/messages/common.cpp + ${CMAKE_SOURCE_DIR}/src/contract/messages/executioncontext.cpp + ${CMAKE_SOURCE_DIR}/src/contract/messages/evmcontractexecutor.cpp ${CMAKE_SOURCE_DIR}/src/contract/templates/ownable.cpp ${CMAKE_SOURCE_DIR}/src/contract/templates/erc20.cpp ${CMAKE_SOURCE_DIR}/src/contract/templates/erc721.cpp diff --git a/src/contract/abi.cpp b/src/contract/abi.cpp index 2c7ec6d2..3b417539 100644 --- a/src/contract/abi.cpp +++ b/src/contract/abi.cpp @@ -45,3 +45,32 @@ int256_t ABI::Decoder::decodeInt(const View& bytes, uint64_t& index) { return result; } +Bytes ABI::Encoder::encodeError(std::string_view reason) { + FixedBytes<32> reasonEncoded{}; + + const size_t count = std::min(reason.size(), reasonEncoded.size()); + std::copy_n(reason.begin(), count, reasonEncoded.begin()); + + const uint256_t size(reason.size()); + const FixedBytes<32> sizeEncoded(Utils::uint256ToBytes(size)); + + return Utils::makeBytes(bytes::join( + Hex::toBytes("0x08c379a0"), + Hex::toBytes("0x0000000000000000000000000000000000000000000000000000000000000020"), + sizeEncoded, + reasonEncoded + )); +} + +std::string ABI::Decoder::decodeError(View data) { + if (data.size() != 100) { + throw DynamicException("Encoded revert reason is expected to have exactly 100 bytes"); + } + + const size_t size = Utils::bytesToUint256(data.subspan(36, 32)).convert_to(); + + std::string res; + res.reserve(size); + std::ranges::copy(data.subspan(68, size), std::back_inserter(res)); + return res; +} diff --git a/src/contract/abi.h b/src/contract/abi.h index 268705ce..47171d2e 100644 --- a/src/contract/abi.h +++ b/src/contract/abi.h @@ -564,6 +564,8 @@ namespace ABI { result.insert(result.end(), dynamicBytes.begin(), dynamicBytes.end()); return result; } + + Bytes encodeError(std::string_view reason); }; // namespace Encoder /** @@ -990,6 +992,8 @@ namespace ABI { } } + std::string decodeError(View data); + /// Specialization for tuples without args. template struct decodeDataAsTuple { /// Decode the tuple. diff --git a/src/contract/calldispatcher.h b/src/contract/calldispatcher.h deleted file mode 100644 index e45e678c..00000000 --- a/src/contract/calldispatcher.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "../utils/utils.h" -#include "cpp/callexecutor.h" -#include "evm/callexecutor.h" - -class CallDispatcher { -public: - using Accounts = boost::unordered_flat_map, SafeHash>; - using TransferHandler = std::function; - - CallDispatcher(cpp::CallExecutor cppCallHandler, evm::CallExecutor evmCallHandler, TransferHandler transferHandler, Accounts& accounts) - : cppCallHandler_(std::move(cppCallHandler)), evmCallHandler_(std::move(evmCallHandler)), transferHandler_(std::move(transferHandler)), accounts_(accounts) {} - - decltype(auto) onCall(auto kind, Gas& gas, auto&& msg) { - const auto it = accounts_.find(msg.to); - - if (it == accounts_.end()) { - throw ExecutionFailure("Account not found"); - } - - const auto& account = it->second; - - if (!account->isContract()) { - throw ExecutionFailure("Not a contract address"); - } - - if (msg.value) { - transferHandler_(msg.from, msg.to, msg.value); - } - - if (account->contractType == ContractType::CPP) { - return cppCallHandler_.executeCall(kind, gas, std::forward(msg)); - } else { - return evmCallHandler_.executeCall(kind, gas, std::forward(msg), account->code); - } - } - -private: - Accounts& accounts_; - cpp::CallExecutor cppCallHandler_; - evm::CallExecutor evmCallHandler_; - TransferHandler transferHandler_; -}; diff --git a/src/contract/contract.cpp b/src/contract/contract.cpp index d074a155..25f9484e 100644 --- a/src/contract/contract.cpp +++ b/src/contract/contract.cpp @@ -17,12 +17,12 @@ Address BaseContract::getOrigin() const { if (this->host_ == nullptr) { throw DynamicException("Contracts going haywire! trying to get origin without a host!"); } - return Address(this->host_->get_tx_context().tx_origin); + return Address(this->host_->context().getTxOrigin()); } uint64_t BaseContract::getNonce(const Address& address) const { if (this->host_ == nullptr) { throw DynamicException("Contracts going haywire! trying to get nonce without a host!"); } - return this->host_->getNonce(address); + return this->host_->context().getAccount(address).nonce; } \ No newline at end of file diff --git a/src/contract/contractfactory.h b/src/contract/contractfactory.h index 6199d088..a47bde70 100644 --- a/src/contract/contractfactory.h +++ b/src/contract/contractfactory.h @@ -28,7 +28,7 @@ See the LICENSE.txt file in the project root for more information. * std::function< * void(const evmc_message&, * const Address&, - * boost::unordered_flat_map, SafeHash>& contracts_, + * boost::unordered_flat_map, SafeHash, SafeCompare>& contracts_, * const uint64_t&, * ContractHost* * )>, @@ -116,7 +116,7 @@ namespace ContractFactory { */ template void createNewContract(const evmc_message& callInfo, const Address& derivedAddress, - boost::unordered_flat_map, SafeHash>& contracts, + boost::unordered_flat_map, SafeHash, SafeCompare>& contracts, const uint64_t& chainId, ContractHost* host) { using ConstructorArguments = typename TContract::ConstructorArguments; @@ -130,8 +130,7 @@ namespace ContractFactory { auto contract = createContractWithTuple( Address(callInfo.sender), derivedAddress, chainId, decodedData ); - host->registerNewCPPContract(derivedAddress, contract.get()); - contracts.insert(std::make_pair(derivedAddress, std::move(contract))); + host->context().addContract(derivedAddress, std::move(contract)); } /** @@ -143,12 +142,12 @@ namespace ContractFactory { void addContractFuncs(const std::function< void(const evmc_message&, const Address&, - boost::unordered_flat_map, SafeHash>& contracts_, + boost::unordered_flat_map, SafeHash, SafeCompare>& contracts_, const uint64_t&, ContractHost* host)>& createFunc ,boost::unordered_flat_map, SafeHash>& contracts_, + boost::unordered_flat_map, SafeHash, SafeCompare>& contracts_, const uint64_t&, ContractHost*)>,SafeHash>& createContractFuncs ) { @@ -170,7 +169,7 @@ namespace ContractFactory { boost::unordered_flat_map, SafeHash>& contracts_, + boost::unordered_flat_map, SafeHash, SafeCompare>& contracts_, const uint64_t&, ContractHost* )>, SafeHash>& createContractFuncs, @@ -179,7 +178,7 @@ namespace ContractFactory { ((addContractFuncs>([]( const evmc_message &callInfo, const Address &derivedAddress, - boost::unordered_flat_map, SafeHash> &contracts, + boost::unordered_flat_map, SafeHash, SafeCompare> &contracts, const uint64_t &chainId, ContractHost* host ) { @@ -194,7 +193,7 @@ namespace ContractFactory { template requires Utils::is_tuple::value void addAllContractFuncs( boost::unordered_flat_map, SafeHash>& contracts_, + boost::unordered_flat_map, SafeHash, SafeCompare>& contracts_, const uint64_t&, ContractHost*)>,SafeHash>& createContractFuncs) { addAllContractFuncsHelper(createContractFuncs, std::make_index_sequence::value>{}); diff --git a/src/contract/contracthost.cpp b/src/contract/contracthost.cpp index 52300b93..35aa3399 100644 --- a/src/contract/contracthost.cpp +++ b/src/contract/contracthost.cpp @@ -1,840 +1,27 @@ #include "contracthost.h" -#include "dynamiccontract.h" - -static inline bool isCall(const evmc_message& msg) { - return msg.kind == EVMC_CALL || msg.kind == EVMC_DELEGATECALL; -} - -void ContractHost::transfer(const Address& from, const Address& to, const uint256_t& value) { - // the from account **Must exist** on the unordered_map. - // unordered_map references to values are valid **until** you insert a new element - // So we can safely take a reference from it and create a reference from the to account. - auto& toAccount = *accounts_[to]; - auto& fromAccount = *accounts_[from]; - auto& toBalance = toAccount.balance; - auto& fromBalance = fromAccount.balance; - if (fromBalance < value) { - throw DynamicException("ContractHost transfer: insufficient funds"); - } - this->stack_.registerBalance(from, fromBalance); - this->stack_.registerBalance(to, toBalance); - fromBalance -= value; - toBalance += value; -} ContractHost::~ContractHost() { - if (!this->mustRevert_) { - // When the execution is complete and we need to commit the changes to the storage we must do the following steps: - // - Commit all the SafeBase variables - // - Commit all the emitted events to the storage - // We only need to commit the C++ stack, EVM operates directly on the storage (only requiring reverts) - // There is no permanent temporary storage for the EVM stack like on the SaveBase variables - // Instead, we use a journaling system with ContractStack to store the original values of the this->storage_ - // TODO: Maybe we should apply the same logic to the C++ stack as well somehow - for (auto& var : this->stack_.getUsedVars()) { - var.get().commit(); - } - for (const auto& event : this->stack_.getEvents()) { - this->storage_.putEvent(event); - } - for (const auto& contractPair : this->stack_.getContracts()) { - const auto& [address, contract] = contractPair; - if (contract != nullptr) { - this->manager_.pushBack(dynamic_cast(contract)); - } else { - // If the contract is nullptr, it means that it was a EVM contract, we need to link txHash and txIndex - this->addTxData_.contractAddress = address; - } - } - } else { - // When reverting, we must revert all the changes, that means: - // - Revert all the SafeBase variables - // - Remove newly created contracts (if any) - // - Revert all the storage changes - // - Revert all balance changes - // - Revert all nonce changes (contracts creating contracts) - // First, lets revert all the SafeBase variables - for (auto& var : this->stack_.getUsedVars()) { + if (mustRevert_) { + for (auto& var : this->stack_.getUsedVars()) var.get().revert(); - } - // Then lets clear off newly created contracts - for (const auto& contractPair : this->stack_.getContracts()) { - const auto& address = std::get<0>(contractPair); - this->accounts_.erase(address); - this->contracts_.erase(address); - } - // Thirdly, revert all storage changes, erasing them if the key was (0x00) - for (const auto& [key, value] : this->stack_.getStorage()) { - if (value == Hash()) { - // If the storage key was empty, we must erase it. - this->vmStorage_.erase(key); - } else { - this->vmStorage_[key] = value; - } - } - // Fourtly, revert all balance changes - for (const auto& [address, balance] : this->stack_.getBalance()) { - // Make sure we don't create a new account if it doesn't exist (deleted as it was a newly created contract) - auto accountIt = this->accounts_.find(address); - if (accountIt != this->accounts_.end()) { - accountIt->second->balance = balance; - } - } - // Finally, revert nonce changes - for (const auto& [address, nonce] : this->stack_.getNonce()) { - // Make sure we don't create a new account if it doesn't exist (deleted as it was a newly created contract) - auto accountIt = this->accounts_.find(address); - if (accountIt != this->accounts_.end()) { - accountIt->second->nonce = nonce; - } - } - } - - saveTxAdditionalData(); - saveCallTrace(); -} - -Address ContractHost::deriveContractAddress(const uint64_t& nonce, - const Address& address) { - // Contract address is last 20 bytes of sha3 - // ( rlp ( tx from address + tx nonce ) ) - uint8_t rlpSize = 0xc0; - rlpSize += 20; - rlpSize += (nonce < 0x80) ? 1 : 1 + Utils::bytesRequired(nonce); - Bytes rlp; - rlp.insert(rlp.end(), rlpSize); - rlp.insert(rlp.end(), address.cbegin(), address.cend()); - rlp.insert(rlp.end(), (nonce < 0x80) ? (char)nonce : (char)0x80 + Utils::bytesRequired(nonce)); - - return Address(Utils::sha3(rlp).view(12)); -} -Address ContractHost::deriveContractAddress(const Address& fromAddress, - const Hash& salt, - const View& code) -{ - const auto code_hash = Utils::sha3(code); - Bytes buffer(1 + sizeof(fromAddress) + sizeof(salt) + sizeof(code_hash)); - assert(std::size(buffer) == 85); - - buffer[0] = 0xff; - buffer.insert(buffer.end(), fromAddress.cbegin(), fromAddress.cend()); - buffer.insert(buffer.end(), salt.cbegin(), salt.cend()); - buffer.insert(buffer.end(), code_hash.cbegin(), code_hash.cend()); - - return Address(Utils::sha3(buffer).view(12)); -} - -evmc::Result ContractHost::createEVMContract(const evmc_message& msg, - const Address& contractAddress, - const evmc_call_kind& kind) { - assert (kind == evmc_call_kind::EVMC_CREATE || kind == evmc_call_kind::EVMC_CREATE2); - // Create a new contract - auto createMsg = msg; - createMsg.recipient = bytes::cast(contractAddress); - createMsg.kind = kind; - createMsg.input_data = nullptr; - createMsg.input_size = 0; - auto result = evmc::Result( - evmc_execute(this->vm_, - &this->get_interface(), - this->to_context(), - evmc_revision::EVMC_LATEST_STABLE_REVISION, - &createMsg, - msg.input_data, - msg.input_size)); - // gas_left is not linked with leftoverGas_, we need to link it. - this->leftoverGas_ = result.gas_left; - this->deduceGas(100000); - if (result.status_code) { - // Set the leftOverGas_ to the gas left after the execution - throw DynamicException("Error when creating EVM contract, EVM status code: " + - std::string(evmc_status_code_to_string(result.status_code)) + " bytes: " + - Hex::fromBytes(Utils::cArrayToBytes(result.output_data, result.output_size)).get()); - } - if (result.output_size > 50000) { - throw DynamicException("ContractHost createEVMContract: contract code too large"); - } - this->registerNewEVMContract(contractAddress, - result.output_data, - result.output_size); - return evmc::Result{result.status_code, this->leftoverGas_, 0, createMsg.recipient}; -} - -bool ContractHost::isTracingCalls() const noexcept { - return bool(txHash_) && storage_.getIndexingMode() == IndexingMode::RPC_TRACE; -} - - -void ContractHost::traceCallStarted(const evmc_message& msg) noexcept { - if (this->isTracingCalls()) { - callTracer_.callStarted(trace::Call(msg)); - } -} - -void ContractHost::traceCallSucceeded(Bytes output, uint64_t gasUsed) noexcept { - if (this->isTracingCalls() && callTracer_.hasCalls()) { - callTracer_.callSucceeded(std::move(output), gasUsed); - } -} - -void ContractHost::traceCallFinished(const evmc_result& res) noexcept { - if (!this->isTracingCalls() || !callTracer_.hasCalls()) { - return; - } - - const uint64_t gasUsed = callTracer_.current().gas - res.gas_left; - Bytes output = Utils::makeBytes(View(res.output_data, res.output_size)); - - if (res.status_code == EVMC_SUCCESS) { - callTracer_.callSucceeded(std::move(output), gasUsed); + context_.revert(); } else { - callTracer_.callReverted(std::move(output), gasUsed); - } -} - -void ContractHost::traceCallReverted(Bytes output, uint64_t gasUsed) noexcept { - if (this->isTracingCalls() && callTracer_.hasCalls()) { - callTracer_.callReverted(std::move(output), gasUsed); - } -} - -void ContractHost::traceCallReverted(uint64_t gasUsed) noexcept { - this->traceCallReverted(Bytes(), gasUsed); -} - -void ContractHost::traceCallOutOfGas() noexcept { - if (this->isTracingCalls() && callTracer_.hasCalls()) { - callTracer_.callOutOfGas(); - } -} - -void ContractHost::saveCallTrace() noexcept { - if (!this->isTracingCalls() || !callTracer_.hasCalls()) - return; - - if (!callTracer_.isFinished()) { - LOGERROR(std::string("Attempt to persist unfinished call trace, hash: ") + txHash_.hex(true).get()); - return; - } - - try { - storage_.putCallTrace(txHash_, callTracer_.root()); - } catch (const std::exception& err) { - LOGERROR(std::string("Fail to persist call trace: ") + err.what()); - } -} - -void ContractHost::saveTxAdditionalData() noexcept { - if (!this->addTxData_.hash || this->storage_.getIndexingMode() == IndexingMode::DISABLED) { - return; - } - - try { - this->storage_.putTxAdditionalData(this->addTxData_); - } catch (const std::exception& err) { - LOGERROR(std::string("Fail to persist additional tx data: ") + err.what()); - } -} - -evmc::Result ContractHost::processBDKPrecompile(const evmc_message& msg) { - /** - * interface BDKPrecompile { - * function getRandom() external view returns (uint256); - * } - */ - try { - this->leftoverGas_ = msg.gas; - this->deduceGas(1000); // CPP contract call is 1000 gas - if (msg.input_size < 4) { - throw DynamicException("ContractHost processBDKPrecompile: invalid input size"); - } - if (Utils::getFunctor(msg).value != 2865519127) { - // we only have one function on the BDKD precompile - // getRandom() == 0xaacc5a17 == 2865519127 - throw DynamicException("ContractHost processBDKPrecompile: invalid function selector"); - } - auto ret = Utils::uint256ToBytes(this->randomGen_.operator()()); - return evmc::Result(EVMC_SUCCESS, this->leftoverGas_, 0, ret.data(), ret.size()); - } catch (std::exception &e) { - this->evmcThrows_.emplace_back(e.what()); - this->evmcThrow_ = true; - } - return evmc::Result(EVMC_PRECOMPILE_FAILURE, this->leftoverGas_, 0, nullptr, 0); -} - -void ContractHost::execute(const evmc_message& msg, const ContractType& type) { - const Address from(msg.sender); - const Address to(msg.recipient); - const uint256_t value(Utils::evmcUint256ToUint256(msg.value)); - const bool isContractCall = isCall(msg); - - if (isContractCall) { - this->traceCallStarted(msg); - } - - Bytes output; - std::string error; - bool outOfGas = false; - - if (value) { - this->transfer(from, to, value); - } - try { - if (to == Address()) { - // If the destination address of the transaction is 0x00, - // it means that we are creating a new contract - auto contractAddress = this->deriveContractAddress(this->getNonce(from), from); - if (this->accounts_.contains(contractAddress)) { - throw DynamicException("ContractHost create/execute: contract already exists"); - } - this->createEVMContract(msg, contractAddress, EVMC_CREATE); - } else { - - switch (type) - { - case ContractType::CPP: { - auto contractIt = this->contracts_.find(to); - if (contractIt == this->contracts_.end()) { - throw DynamicException("contract not found"); - } - this->setContractVars(contractIt->second.get(), from, value); - contractIt->second->ethCall(msg, this); - break; - } - case ContractType::EVM: { - // Execute a EVM contract. - auto result = evmc::Result(evmc_execute(this->vm_, - &this->get_interface(), - this->to_context(), - evmc_revision::EVMC_LATEST_STABLE_REVISION, &msg, - this->accounts_[to]->code.data(), - this->accounts_[to]->code.size())); - - output = Utils::makeBytes(View(result.output_data, result.output_size)); - - this->leftoverGas_ = result.gas_left; // gas_left is not linked with leftoverGas_, we need to link it. - if (result.status_code) { - outOfGas = result.status_code == EVMC_OUT_OF_GAS; - - error = evmc_status_code_to_string(result.status_code); - // Set the leftOverGas_ to the gas left after the execution - throw DynamicException("Error when executing EVM contract, EVM status code: " + - std::string(evmc_status_code_to_string(result.status_code)) + " bytes: " + - Hex::fromBytes(Utils::cArrayToBytes(result.output_data, result.output_size)).get()); - } - break; - } - } - } - // Take out 21000 gas Limit from the tx - this->deduceGas(21000); - } catch (const std::exception &e) { - std::string what = std::string("ContractHost execution failed: ") + e.what(); - what += " OTHER INFO: "; - for (const auto& evmcError : this->evmcThrows_) { - what += evmcError; - what += " --- OTHER INFO: --- "; - } - - this->addTxData_.gasUsed = msg.gas - this->leftoverGas_; - this->addTxData_.succeeded = false; - - if (isContractCall) { - if (outOfGas) { - this->traceCallOutOfGas(); - } else { - this->traceCallReverted(std::move(output), this->addTxData_.gasUsed); - } - } - - throw DynamicException(what); - } - // We only set that we don't revert, if EVMC didn't throw a exception - if (this->evmcThrow_) { - std::string what = std::string("ContractHost execution failed: EVMC threw an exception: "); - for (const auto& evmcError : this->evmcThrows_) { - what += evmcError; - what += " --- OTHER INFO: --- "; - } - - this->addTxData_.gasUsed = msg.gas - this->leftoverGas_; - this->addTxData_.succeeded = false; - - if (isContractCall) { - this->traceCallReverted(std::move(output), this->addTxData_.gasUsed); - } - throw DynamicException(what); - } - - this->addTxData_.gasUsed = msg.gas - this->leftoverGas_; - this->addTxData_.succeeded = true; - this->mustRevert_ = false; - if (isContractCall) { - this->traceCallSucceeded(std::move(output), this->addTxData_.gasUsed); - } -} - -Bytes ContractHost::ethCallView(const evmc_message& msg, const ContractType& type) { - Gas gas(leftoverGas_); // TODO: or we can just set a very high value, why not? - - evm::Message evmcMsg{ - .from = Address(msg.sender), - .to = Address(msg.recipient), - .value = Utils::evmcUint256ToUint256(msg.value), - .depth = 0, - .input = View(msg.input_data, msg.input_size) - }; - // return callHandler_.onCall(kind::STATIC, gas, std::move(evmcMsg)); - - Bytes ret; - //const Address from(tx.sender); - const Address to(msg.recipient); - //const uint256_t value(Utils::evmcUint256ToUint256(tx.value)); - try { - switch (type) { - case ContractType::CPP: { - auto contractIt = this->contracts_.find(to); - if (contractIt == this->contracts_.end()) { - throw DynamicException("contract not found"); - } - ret = contractIt->second->ethCallView(msg, this); - break; - } - case ContractType::EVM: { - // Execute a EVM contract. - auto result = evmc::Result(evmc_execute(this->vm_, &this->get_interface(), this->to_context(), - evmc_revision::EVMC_LATEST_STABLE_REVISION, &msg, - this->accounts_[to]->code.data(), this->accounts_[to]->code.size())); - this->leftoverGas_ = result.gas_left; - if (result.status_code) { - // Set the leftOverGas_ to the gas left after the execution - throw DynamicException("Error when executing (view) EVM contract, EVM status code: " + - std::string(evmc_status_code_to_string(result.status_code)) + " bytes: " + - Hex::fromBytes(Utils::cArrayToBytes(result.output_data, result.output_size)).get()); - } - ret = Bytes(result.output_data, result.output_data + result.output_size); - break; - } - } - } catch (const std::exception &e) { - std::string what = std::string("ContractHost execution failed: ") + e.what(); - what += " OTHER INFO: "; - for (const auto& evmcError : this->evmcThrows_) { - what += evmcError; - what += " --- OTHER INFO: --- "; - } - throw DynamicException(what); - } - // We only set that we don't revert, if EVMC didn't throw a exception - if (this->evmcThrow_) { - std::string what = std::string("ContractHost execution failed: EVMC threw an exception: "); - for (const auto& evmcError : this->evmcThrows_) { - what += evmcError; - what += " --- OTHER INFO: --- "; - } - throw DynamicException(what); - } - return ret; -} - -void ContractHost::simulate(const evmc_message& msg, const ContractType& type) { - this->execute(msg, type); - // We should set the revert flag to true, as we are only simulating the execution - this->mustRevert_ = true; -} - -bool ContractHost::account_exists(const evmc::address& addr) const noexcept { - return accounts_.find(Address(addr)) != accounts_.end(); -} - -evmc::bytes32 ContractHost::get_storage(const evmc::address& addr, - const evmc::bytes32& key) const noexcept { - try { - auto it = vmStorage_.find(StorageKeyView{addr, key}); - if (it != vmStorage_.end()) - return bytes::cast(it->second); - } catch (const std::exception& e) { - this->evmcThrows_.emplace_back(e.what()); - this->evmcThrow_ = true; - } - return {}; -} - -// on SET_STORAGE, we can return multiple types of evmc_storage_status -// But we simply say that the storage was modified ;) -// TODO: Make it EIP-2200 compliant -evmc_storage_status ContractHost::set_storage(const evmc::address& addr, - const evmc::bytes32& key, - const evmc::bytes32& value) noexcept { - StorageKey storageKey(addr, key); - try { - Hash hashValue(value); - auto& storageValue = vmStorage_[storageKey]; - this->stack_.registerStorageChange(storageKey, storageValue); - storageValue = hashValue; - return EVMC_STORAGE_MODIFIED; - } catch (const std::exception& e) { - this->evmcThrows_.emplace_back(e.what()); - this->evmcThrow_ = true; - return EVMC_STORAGE_MODIFIED; - } -} - -evmc::uint256be ContractHost::get_balance(const evmc::address& addr) const noexcept { - try { - auto it = accounts_.find(Address(addr)); - if (it != accounts_.end()) { - return Utils::uint256ToEvmcUint256(it->second->balance); - } - } catch (const std::exception& e) { - this->evmcThrows_.emplace_back(e.what()); - this->evmcThrow_ = true; - } - return {}; -} - -size_t ContractHost::get_code_size(const evmc::address& addr) const noexcept { - try { - auto it = accounts_.find(Address(addr)); - if (it != accounts_.end()) { - return it->second->code.size(); - } - } catch (const std::exception& e) { - this->evmcThrows_.emplace_back(e.what()); - this->evmcThrow_ = true; - } - return 0; -} - -evmc::bytes32 ContractHost::get_code_hash(const evmc::address& addr) const noexcept { - try { - auto it = accounts_.find(Address(addr)); - if (it != accounts_.end()) { - return bytes::cast(it->second->codeHash); - } - } catch (const std::exception& e) { - this->evmcThrows_.emplace_back(e.what()); - this->evmcThrow_ = true; - } - return {}; -} + for (auto& var : this->stack_.getUsedVars()) + var.get().commit(); -size_t ContractHost::copy_code(const evmc::address& addr, - size_t code_offset, - uint8_t* buffer_data, - size_t buffer_size) const noexcept { - try { - const auto it = this->accounts_.find(Address(addr)); - if (it != this->accounts_.end()) { - const auto& code = it->second->code; - if (code_offset < code.size()) { - const auto n = std::min(buffer_size, code.size() - code_offset); - if (n > 0) - std::copy_n(&code[code_offset], n, buffer_data); - return n; + for (auto& [address, contract] : context_.getNewContracts()) { + if (contract == nullptr) { + continue; } - } - } catch (std::exception& e) { - this->evmcThrows_.emplace_back(e.what()); - std::cerr << e.what() << std::endl; - this->evmcThrow_ = true; - } - return 0; -} - -bool ContractHost::selfdestruct(const evmc::address& addr, - const evmc::address& beneficiary) noexcept { - // SELFDESTRUCT is not allowed in the current implementation - this->evmcThrow_ = true; - return false; -} - -evmc::Result ContractHost::callEVMCreate(const evmc_message& msg) { - try { - auto sender = Address(msg.sender); - auto& nonce = this->getNonce(sender); - auto contractAddress = this->deriveContractAddress(nonce, sender); - uint256_t value = Utils::evmcUint256ToUint256(msg.value); - - this->stack_.registerNonce(sender, nonce); - ++nonce; - if (value) { - this->transfer(sender, contractAddress, value); - } - return this->createEVMContract(msg, contractAddress, EVMC_CREATE); - } catch (const std::exception &e) { - this->evmcThrows_.emplace_back(e.what()); - this->evmcThrow_ = true; - } - return evmc::Result(EVMC_REVERT, this->leftoverGas_, 0, nullptr, 0); -} - -evmc::Result ContractHost::callEVMCreate2(const evmc_message& msg) { - try { - Bytes code(msg.input_data, msg.input_data + msg.input_size); - uint256_t value = Utils::evmcUint256ToUint256(msg.value); - auto salt = Hash(msg.create2_salt); - auto sender = Address(msg.sender); - auto contractAddress = this->deriveContractAddress(sender, salt, code); - - if (value) { - this->transfer(sender, contractAddress, value); - } - return this->createEVMContract(msg, contractAddress, EVMC_CREATE2); - } catch (const std::exception &e) { - this->evmcThrows_.emplace_back(e.what()); - this->evmcThrow_ = true; - } - return evmc::Result(EVMC_REVERT, this->leftoverGas_, 0, nullptr, 0); -} - -evmc::Result ContractHost::callCPPContract(const evmc_message& msg) { - Address recipient(msg.recipient); - try { - this->leftoverGas_ = msg.gas; - this->deduceGas(1000); // CPP contract call is 1000 gas - auto& contract = contracts_[recipient]; - if (contract == nullptr) { - throw DynamicException("ContractHost call: contract not found"); - } - this->setContractVars(contract.get(), - Address(msg.sender), - Utils::evmcUint256ToUint256(msg.value)); - Bytes ret = contract->evmEthCall(msg, this); - return evmc::Result(EVMC_SUCCESS, - this->leftoverGas_, - 0, - ret.data(), - ret.size()); - } catch (std::exception& e) { - this->evmcThrows_.emplace_back(e.what()); - this->evmcThrow_ = true; - return evmc::Result(EVMC_PRECOMPILE_FAILURE, this->leftoverGas_, 0, nullptr, 0); - } -} - -evmc::Result ContractHost::callEVMContract(const evmc_message& msg) { - Address recipient(msg.recipient); - auto &recipientAccount = *accounts_[recipient]; - evmc::Result result(evmc_execute(this->vm_, - &this->get_interface(), - this->to_context(), - evmc_revision::EVMC_LATEST_STABLE_REVISION, - &msg, - recipientAccount.code.data(), - recipientAccount.code.size())); - // gas_left is not linked with leftoverGas_, we need to link it. - this->leftoverGas_ = result.gas_left; - // EVM contract call is 5000 gas - this->deduceGas(5000); - // We need to set the gas left to the leftoverGas_ - result.gas_left = this->leftoverGas_; - return result; -} - -ContractType ContractHost::decodeContractCallType(const evmc_message& msg) const { - switch (msg.kind) - { - case evmc_call_kind::EVMC_CREATE: { - return ContractType::CREATE; - } - case evmc_call_kind::EVMC_CREATE2: { - return ContractType::CREATE2; - } - default: - if (msg.recipient == BDK_PRECOMPILE) - return ContractType::PRECOMPILED; - Address recipient(msg.recipient); - // we need to take a reference to the account, not a reference - // to the pointer - const auto& recipientAccount = *accounts_[recipient]; - if (recipientAccount.contractType == ContractType::CPP) - return ContractType::CPP; - // else EVM call - return ContractType::EVM; - } -} - -// EVM -> EVM calls don't need to use this->leftOverGas_ as the final -// evmc::Result will have the gas left after the execution -evmc::Result ContractHost::call(const evmc_message& msg) noexcept { - evmc::Result result; - const bool isContractCall = isCall(msg); - - if (isContractCall) { - this->traceCallStarted(msg); - } - - switch (this->decodeContractCallType(msg)) - { - case ContractType::CREATE: { - result = this->callEVMCreate(msg); - break; - } - case ContractType::CREATE2: { - result = this->callEVMCreate2(msg); - break; - } - case ContractType::PRECOMPILED: { - result = this->processBDKPrecompile(msg); - break; - } - case ContractType::CPP: { - result = this->callCPPContract(msg); - break; - } - default: - result = this->callEVMContract(msg); - break; - } - - if (isContractCall) { - this->traceCallFinished(result.raw()); - } - - return result; -} - -evmc_tx_context ContractHost::get_tx_context() const noexcept { - return this->currentTxContext_; -} - -evmc::bytes32 ContractHost::get_block_hash(int64_t number) const noexcept { - try { - return Utils::uint256ToEvmcUint256(number); - } catch (std::exception& e) { - this->evmcThrows_.emplace_back(e.what()); - this->evmcThrow_ = true; - } - return {}; -} -void ContractHost::emit_log(const evmc::address& addr, - const uint8_t* data, - size_t data_size, - const evmc::bytes32 topics[], - size_t topics_count) noexcept { - try { - // We need the following arguments to build a event: - // (std::string) name The event's name. - // (uint64_t) logIndex The event's position on the block. - // (Hash) txHash The hash of the transaction that emitted the event. - // (uint64_t) txIndex The position of the transaction in the block. - // (Hash) blockHash The hash of the block that emitted the event. - // (uint64_t) blockIndex The height of the block. - // (Address) address The address that emitted the event. - // (Bytes) data The event's arguments. - // (std::vector) topics The event's indexed arguments. - // (bool) anonymous Whether the event is anonymous or not. - std::vector topics_; - for (uint64_t i = 0; i < topics_count; i++) { - topics_.emplace_back(topics[i]); + this->manager_.pushBack(dynamic_cast(contract)); } - Event event("", // EVM events do not have names - this->eventIndex_, - this->txHash_, - this->txIndex_, - this->blockHash_, - this->currentTxContext_.block_number, - Address(addr), - Bytes(data, data + data_size), - topics_, - (topics_count == 0) - ); - ++this->eventIndex_; - this->stack_.registerEvent(std::move(event)); - } catch (std::exception& e) { - this->evmcThrows_.emplace_back(e.what()); - this->evmcThrow_ = true; - } -} - -// We always return warm because we are warm (storage is on ram) ;) -evmc_access_status ContractHost::access_account(const evmc::address& addr) noexcept { - return EVMC_ACCESS_WARM; -} -// Same as above -evmc_access_status ContractHost::access_storage(const evmc::address& addr, - const evmc::bytes32& key) noexcept { - return EVMC_ACCESS_WARM; -} - -evmc::bytes32 ContractHost::get_transient_storage(const evmc::address &addr, - const evmc::bytes32 &key) const noexcept { - StorageKey storageKey(addr, key); - try { - auto it = transientStorage_.find(StorageKeyView{addr, key}); - if (it != transientStorage_.end()) { - return bytes::cast(it->second); + for (const auto& event : context_.getEvents()) { + this->storage_.putEvent(event); } - } catch (const std::exception& e) { - this->evmcThrows_.emplace_back(e.what()); - this->evmcThrow_ = true; - } - return {}; -} -void ContractHost::set_transient_storage(const evmc::address &addr, - const evmc::bytes32 &key, - const evmc::bytes32 &value) noexcept { - try { - transientStorage_.emplace(StorageKeyView{addr, key}, value); - } catch (const std::exception& e) { - this->evmcThrows_.emplace_back(e.what()); - this->evmcThrow_ = true; + context_.commit(); } } - -void ContractHost::emitContractEvent(Event&& event) { - this->stack_.registerEvent(std::move(event)); -} - -uint256_t ContractHost::getBalanceFromAddress(const Address& address) const { - auto it = this->accounts_.find(address); - if (it != this->accounts_.end()) - return it->second->balance; - return 0; -} - -void ContractHost::sendTokens(const BaseContract* from, - const Address& to, - const uint256_t& amount) { - this->transfer(from->getContractAddress(), to, amount); -} - -uint64_t& ContractHost::getNonce(const Address& nonce) { - return this->accounts_[nonce]->nonce; -} - -void ContractHost::registerNewCPPContract(const Address& address, - BaseContract* contract) { - Account contractAcc; - contractAcc.contractType = ContractType::CPP; - contractAcc.nonce = 1; - auto emplace = this->accounts_.try_emplace(address, contractAcc); - if (!emplace.second) { - throw DynamicException("ContractHost registerNewCPPContract: account on address already exists"); - } - this->stack_.registerContract(address, contract); -} - -void ContractHost::registerNewEVMContract(const Address& address, - const uint8_t* code, - size_t codeSize) { - Account contractAcc; - contractAcc.contractType = ContractType::EVM; - contractAcc.nonce = 1; - contractAcc.code = Bytes(code, code + codeSize); - contractAcc.codeHash = Utils::sha3(contractAcc.code); - auto emplace = this->accounts_.try_emplace(address, contractAcc); - if (!emplace.second) { - throw DynamicException("ContractHost registerNewCPPContract: account on address already exists"); - } - this->stack_.registerContract(address, nullptr); -} - -void ContractHost::registerVariableUse(SafeBase& variable) { - this->stack_.registerVariableUse(variable); -} diff --git a/src/contract/contracthost.h b/src/contract/contracthost.h index c46bab3b..6a465886 100644 --- a/src/contract/contracthost.h +++ b/src/contract/contracthost.h @@ -19,8 +19,11 @@ #include "calltracer.h" #include "bytes/join.h" #include "bytes/cast.h" -#include "gas.h" -#include "calldispatcher.h" +#include "messages/gas.h" +#include "messages/concepts.h" +#include "messages/executioncontext.h" +#include "messages/messagedispatcher.h" +#include "messages/packedmessages.h" // TODO: EVMC Static Mode Handling // TODO: Contract creating other contracts (EVM Factories) @@ -51,192 +54,58 @@ using namespace evmc::literals; const auto ZERO_ADDRESS = 0x0000000000000000000000000000000000000000_address; const auto BDK_PRECOMPILE = 0x1000000000000000000000000000100000000001_address; -class ContractHost : public evmc::Host { +using MessageHandler = MessageDispatcher; + +class ContractHost { private: - // We need this because nested calls can call the same contract multiple times - // Potentially taking advantage of wrong context variables. - class NestedCallSafeGuard { - private: - const ContractLocals* contract_; - Address caller_; - uint256_t value_; - public: - NestedCallSafeGuard(const ContractLocals* contract, const Address& caller, const uint256_t& value) : - contract_(contract), caller_(contract->caller_), value_(contract->value_) { - } - ~NestedCallSafeGuard() { - contract_->caller_ = caller_; - contract_->value_ = value_; - } - }; - evmc_vm* vm_; DumpManager& manager_; Storage& storage_; mutable ContractStack stack_; mutable RandomGen randomGen_; // Random generator for the contract. - const evmc_tx_context& currentTxContext_; // MUST be initialized within the constructor. - boost::unordered_flat_map, SafeHash>& contracts_; - boost::unordered_flat_map, SafeHash>& accounts_; - boost::unordered_flat_map& vmStorage_; - boost::unordered_flat_map transientStorage_; bool mustRevert_ = true; // We always assume that we must revert until proven otherwise. - mutable bool evmcThrow_ = false; // Did the EVMC throw an exception? - mutable std::vector evmcThrows_; - std::vector evmcResults_; /// evmc_result has a uint8_t* and a size, but we need to allocate memory for it. - uint64_t eventIndex_ = 0; - const Hash& txHash_; - const uint64_t txIndex_; - const Hash& blockHash_; - int64_t& leftoverGas_; /// Reference to the leftover gas from the transaction. - /// The leftoverGas_ is a object given by the State - TxAdditionalData addTxData_; - trace::CallTracer callTracer_; - Gas *gas_ = nullptr; - - // Private as this is not available for contracts as it has safety checks - void transfer(const Address& from, const Address& to, const uint256_t& value); - - /** - * Set the local variables for a given contract (origin, caller, value). - * Used everytime *before* and *after* (if nested) a contract call. - * @param contract The contract to set the local variables for. - * @param caller The caller address to set. - * @param value The value to set. - */ - inline void setContractVars(const ContractLocals* contract, - const Address& caller, - const uint256_t& value) const { - contract->caller_ = caller; - contract->value_ = value; - } - - inline void deduceGas(const int64_t& gas) const { - this->leftoverGas_ -= gas; - if (this->leftoverGas_ < 0) { - throw DynamicException("ContractHost deduceGas: out of gas"); - } - } - - evmc::Result createEVMContract(const evmc_message& msg, - const Address& contractAddress, - const evmc_call_kind& kind); - - ContractType decodeContractCallType(const evmc_message& msg) const; - evmc::Result processBDKPrecompile(const evmc_message& msg); - evmc::Result callEVMCreate(const evmc_message& msg); - evmc::Result callEVMCreate2(const evmc_message& msg); - evmc::Result callEVMContract(const evmc_message& msg); - evmc::Result callCPPContract(const evmc_message& msg); - - Address computeNewAccountAddress(const Address& fromAddress, - const uint64_t& nonce, - const Hash& salt, - const View& init_code); - - bool isTracingCalls() const noexcept; - - void traceCallStarted(const evmc_message& msg) noexcept; - - void traceCallFinished(const evmc_result& res) noexcept; - - void traceCallSucceeded(Bytes output, uint64_t gasUsed) noexcept; - - void traceCallReverted(Bytes output, uint64_t gasUsed) noexcept; - - void traceCallReverted(uint64_t gasUsed) noexcept; - - void traceCallOutOfGas() noexcept; - - void saveCallTrace() noexcept; - - void saveTxAdditionalData() noexcept; + ExecutionContext& context_; + MessageHandler messageHandler_; public: ContractHost(evmc_vm* vm, DumpManager& manager, Storage& storage, const Hash& randomnessSeed, - const evmc_tx_context& currentTxContext, - boost::unordered_flat_map, SafeHash>& contracts, - boost::unordered_flat_map, SafeHash>& accounts, - boost::unordered_flat_map& vmStorage, - const Hash& txHash, - const uint64_t txIndex, - const Hash& blockHash, - int64_t& txGasLimit) : - vm_(vm), + ExecutionContext& context) : manager_(manager), storage_(storage), randomGen_(randomnessSeed), - currentTxContext_(currentTxContext), - contracts_(contracts), - accounts_(accounts), - vmStorage_(vmStorage), - txHash_(txHash), - txIndex_(txIndex), - blockHash_(blockHash), - leftoverGas_(txGasLimit), - addTxData_({.hash = txHash}), - stack_() {} - // callHandler_(cpp::CallExecutor(*this, contracts_), DummyHandler(), std::function(), accounts_){} - // callHandler_(vm_, accounts_, contracts_, nullptr) { - // callHandler_.setContractHost(this); - // } + stack_(), + context_(context), + messageHandler_( + context_, + CppContractExecutor(context_, *this), + EvmContractExecutor(AnyEncodedMessageHandler::from(messageHandler_), context_, vm) + ) {} // Rule of five, no copy/move allowed. ContractHost(const ContractHost&) = delete; ContractHost(ContractHost&&) = delete; ContractHost& operator=(const ContractHost&) = delete; ContractHost& operator=(ContractHost&&) = delete; - ~ContractHost() noexcept override; - - static Address deriveContractAddress(const uint64_t& nonce, - const Address& address); - - static Address deriveContractAddress(const Address& fromAddress, - const Hash& salt, - const View& init_code); - - /// Executes a call - void execute(const evmc_message& msg, const ContractType& type); - - decltype(auto) execute(auto&& msg) { - // gas.use(21000); + ~ContractHost(); - // try { - // return callHandler_.onCall(kind::NORMAL, gas, std::forward(msg)); - // } catch (const std::exception& err) { - // mustRevert_ = true; - // throw err; - // } + decltype(auto) simulate(concepts::Message auto&& msg) { + decltype(auto) result = execute(std::forward(msg)); + mustRevert_ = true; + return result; } - /// Executes a eth_call RPC method (view) - /// returns the result of the call - Bytes ethCallView(const evmc_message& msg, const ContractType& type); - - /// Simulates a call - void simulate(const evmc_message& msg, const ContractType& type); - - /// EVMC FUNCTIONS - bool account_exists(const evmc::address& addr) const noexcept final; - evmc::bytes32 get_storage(const evmc::address& addr, const evmc::bytes32& key) const noexcept final; - evmc_storage_status set_storage(const evmc::address& addr, const evmc::bytes32& key, const evmc::bytes32& value) noexcept final; - evmc::uint256be get_balance(const evmc::address& addr) const noexcept final; - size_t get_code_size(const evmc::address& addr) const noexcept final; - evmc::bytes32 get_code_hash(const evmc::address& addr) const noexcept final; - size_t copy_code(const evmc::address& addr, size_t code_offset, uint8_t* buffer_data, size_t buffer_size) const noexcept final; - bool selfdestruct(const evmc::address& addr, const evmc::address& beneficiary) noexcept final; - evmc::Result call(const evmc_message& msg) noexcept final; - evmc_tx_context get_tx_context() const noexcept final; - evmc::bytes32 get_block_hash(int64_t number) const noexcept final; - void emit_log(const evmc::address& addr, const uint8_t* data, size_t data_size, const evmc::bytes32 topics[], size_t topics_count) noexcept final; - evmc_access_status access_account(const evmc::address& addr) noexcept final; - evmc_access_status access_storage(const evmc::address& addr, const evmc::bytes32& key) noexcept final; - evmc::bytes32 get_transient_storage(const evmc::address &addr, const evmc::bytes32 &key) const noexcept final; - void set_transient_storage(const evmc::address &addr, const evmc::bytes32 &key, const evmc::bytes32 &value) noexcept final; - /// END OF EVMC FUNCTIONS - + decltype(auto) execute(concepts::Message auto&& msg) { + try { + mustRevert_ = false; + msg.gas().use(21000); + return messageHandler_.onMessage(std::forward(msg)); + } catch (const std::exception& err) { + mustRevert_ = true; + throw err; + } + } /// CONTRACT INTERFACING FUNCTIONS /** @@ -251,79 +120,15 @@ class ContractHost : public evmc::Host { */ template R callContractViewFunction(const BaseContract* caller, const Address& targetAddr, R(C::*func)(const Args&...) const, const Args&... args) { - - cpp::Message msg{ - .from = caller->getContractAddress(), - .to = targetAddr, - .value = 0, - .caller = caller, - .method = cpp::PackagedMethod(func, args...) - }; - - // return callHandler_.onCall(kind::STATIC, *gas_, std::move(msg)); - - const auto recipientAccIt = this->accounts_.find(targetAddr); - if (recipientAccIt == this->accounts_.end()) { - throw DynamicException(std::string(__func__) + ": Contract Account does not exist - Type: " - + Utils::getRealTypeName() + " at address: " + targetAddr.hex().get() - ); - } - const auto& recipientAcc = *recipientAccIt->second; - if (!recipientAcc.isContract()) { - throw DynamicException(std::string(__func__) + ": Contract does not exist - Type: " - + Utils::getRealTypeName() + " at address: " + targetAddr.hex().get() - ); - } - NestedCallSafeGuard guard(caller, caller->caller_, caller->value_); - switch (recipientAcc.contractType) { - case ContractType::EVM : { - this->deduceGas(5000); - evmc_message msg; - msg.kind = EVMC_CALL; - msg.flags = EVMC_STATIC; - msg.depth = 1; - msg.gas = this->leftoverGas_; - msg.recipient = bytes::cast(targetAddr); - msg.sender = bytes::cast(caller->getContractAddress()); - auto functionName = ContractReflectionInterface::getFunctionName(func); - if (functionName.empty()) { - throw DynamicException("ContractHost::callContractViewFunction: EVM contract function name is empty (contract not registered?)"); - } - auto functor = ABI::FunctorEncoder::encode(functionName); - Bytes fullData; - Utils::appendBytes(fullData, Utils::uint32ToBytes(functor.value)); - if constexpr (sizeof...(Args) > 0) { - Utils::appendBytes(fullData, ABI::Encoder::encodeData(args...)); - } - msg.input_data = fullData.data(); - msg.input_size = fullData.size(); - msg.value = {}; - msg.create2_salt = {}; - msg.code_address = bytes::cast(targetAddr); - /// TODO: OMG this is so ugly, we need to fix this. - /// A **CONST_CAST** is needed because we can't explicity tell the evmc_execute to do a view call. - /// Regardless of that, we set flag = 1 to indicate that this is a view/STATIC call. - evmc::Result result (evmc_execute(this->vm_, &this->get_interface(), const_cast(this)->to_context(), - evmc_revision::EVMC_LATEST_STABLE_REVISION, &msg, recipientAcc.code.data(), recipientAcc.code.size())); - this->leftoverGas_ = result.gas_left; - if (result.status_code) { - auto hexResult = Hex::fromBytes(View(result.output_data, result.output_data + result.output_size)); - throw DynamicException("ContractHost::callContractViewFunction: EVMC call failed - Type: " - + Utils::getRealTypeName() + " at address: " + targetAddr.hex().get() + " - Result: " + hexResult.get() - ); - } - return std::get<0>(ABI::Decoder::decodeData(View(result.output_data, result.output_data + result.output_size))); - } break; - case ContractType::CPP : { - this->deduceGas(1000); - const C* contract = this->getContract(targetAddr); - this->setContractVars(contract, caller->getContractAddress(), 0); - return (contract->*func)(args...); - } - default: { - throw DynamicException("PANIC! ContractHost::callContractViewFunction: Unknown contract type"); - } - } + PackedStaticCallMessage msg( + caller->getContractAddress(), + targetAddr, + messageHandler_.cppExecutor().currentGas(), + *caller, + func, + args...); + + return messageHandler_.onMessage(std::move(msg)); } template @@ -331,155 +136,17 @@ class ContractHost : public evmc::Host { BaseContract* caller, const Address& targetAddr, const uint256_t& value, R(C::*func)(const Args&...), const Args&... args) { - if (this->isTracingCalls()) [[unlikely]] { - trace::Call callData; - - const uint64_t gas = leftoverGas_; - - callData.type = trace::Call::Type::CALL; - callData.from = caller->getContractAddress(); - callData.to = targetAddr; - callData.gas = gas; - - const std::string functionName = ContractReflectionInterface::getFunctionName(func); - - const BytesArr<4> encodedFunctor = - Utils::uint32ToBytes(ABI::FunctorEncoder::encode(functionName).value); - - if constexpr (sizeof...(args) > 0) { - const Bytes encodedArgs = ABI::Encoder::encodeData(args...); - callData.input = Utils::makeBytes(bytes::join(encodedFunctor, encodedArgs)); - } else { - callData.input = Utils::makeBytes(encodedFunctor); - } - - callTracer_.callStarted(std::move(callData)); - - try { - if constexpr (std::same_as) { - callContractFunctionImpl(caller, targetAddr, value, func, args...); - callTracer_.callSucceeded(Bytes(), gas - leftoverGas_); - return; - } else { - R result = callContractFunctionImpl(caller, targetAddr, value, func, args...); - const uint64_t gasUsed = gas - leftoverGas_; - Bytes output = ABI::Encoder::encodeData(result); - callTracer_.callSucceeded(std::move(output), gasUsed); - return result; - } - } catch (const std::exception& err) { - Bytes output; - - if (err.what()) { - output = trace::encodeRevertReason(err.what()); - } - - callTracer_.callReverted(std::move(output), gas - leftoverGas_); - throw err; - } - } else [[likely]] { - return callContractFunctionImpl(caller, targetAddr, value, func, args...); - } - } - /** - * Call a contract function. Used by DynamicContract to call other contracts. - * A given DynamicContract will only call another contract if triggered by a transaction. - * This will only be called if callContract() or validateCallContractWithTx() was called before. - * Implementation for @tparam ReturnType NOT being void - * @tparam R The return type of the function. - * @tparam C The contract type. - * @tparam Args The arguments types. - * @param targetAddr The address of the contract to call. - * @param value Flag to indicate if the function is payable., - * @param func The function to call. - * @param args The arguments to pass to the function. - * @return The return value of the function. - */ - template - R callContractFunctionImpl( - BaseContract* caller, const Address& targetAddr, - const uint256_t& value, - R(C::*func)(const Args&...), const Args&... args - ) { + PackedCallMessage msg( + caller->getContractAddress(), + targetAddr, + messageHandler_.cppExecutor().currentGas(), + value, + *caller, + func, + args...); - cpp::Message msg{ - .from = caller->getContractAddress(), - .to = targetAddr, - .value = value, - .caller = caller, - .method = cpp::PackagedMethod(func, args...) - }; - - // return callHandler_.onCall(kind::NORMAL, *gas_, std::move(msg)); - - // 1000 Gas Limit for every C++ contract call! - auto& recipientAcc = *this->accounts_[targetAddr]; - if (!recipientAcc.isContract()) { - throw DynamicException(std::string(__func__) + ": Contract does not exist - Type: " - + Utils::getRealTypeName() + " at address: " + targetAddr.hex().get() - ); - } - if (value) { - this->sendTokens(caller, targetAddr, value); - } - NestedCallSafeGuard guard(caller, caller->caller_, caller->value_); - switch (recipientAcc.contractType) { - case ContractType::EVM : { - this->deduceGas(10000); - evmc_message msg; - msg.kind = EVMC_CALL; - msg.flags = 0; - msg.depth = 1; - msg.gas = this->leftoverGas_; - msg.recipient = bytes::cast(targetAddr); - msg.sender = bytes::cast(caller->getContractAddress()); - auto functionName = ContractReflectionInterface::getFunctionName(func); - if (functionName.empty()) { - throw DynamicException("ContractHost::callContractFunction: EVM contract function name is empty (contract not registered?)"); - } - auto functor = ABI::FunctorEncoder::encode(functionName); - Bytes fullData; - Utils::appendBytes(fullData, Utils::uint32ToBytes(functor.value)); - if constexpr (sizeof...(Args) > 0) { - Utils::appendBytes(fullData, ABI::Encoder::encodeData(args...)); - } - msg.input_data = fullData.data(); - msg.input_size = fullData.size(); - msg.value = Utils::uint256ToEvmcUint256(value); - msg.create2_salt = {}; - msg.code_address = bytes::cast(targetAddr); - evmc::Result result (evmc_execute(this->vm_, &this->get_interface(), this->to_context(), - evmc_revision::EVMC_LATEST_STABLE_REVISION, &msg, recipientAcc.code.data(), recipientAcc.code.size())); - this->leftoverGas_ = result.gas_left; - if (result.status_code) { - auto hexResult = Hex::fromBytes(View(result.output_data, result.output_data + result.output_size)); - throw DynamicException("ContractHost::callContractFunction: EVMC call failed - Type: " - + Utils::getRealTypeName() + " at address: " + targetAddr.hex().get() + " - Result: " + hexResult.get() - ); - } - if constexpr (std::same_as) { - return; - } else { - return std::get<0>(ABI::Decoder::decodeData(View(result.output_data, result.output_data + result.output_size))); - } - } break; - case ContractType::CPP : { - this->deduceGas(1000); - C* contract = this->getContract(targetAddr); - this->setContractVars(contract, caller->getContractAddress(), value); - try { - return contract->callContractFunction(this, func, args...); - } catch (const std::exception& e) { - throw DynamicException(e.what() + std::string(" - Type: ") - + Utils::getRealTypeName() + " at address: " + targetAddr.hex().get() - ); - } - } - default : { - throw DynamicException("PANIC! ContractHost::callContractFunction: Unknown contract type"); - } - } + return messageHandler_.onMessage(std::move(msg)); } /** @@ -490,50 +157,19 @@ class ContractHost : public evmc::Host { * @param encoder The ABI encoder. * @return The address of the new contract. */ - template Address callCreateContract( - BaseContract* caller, - const Bytes &fullData - ) { - // 100k gas limit for every contract creation! - this->deduceGas(50000); - evmc_message callInfo; - // evmc_message: - // struct evmc_message - // { - // enum evmc_call_kind kind; - // uint32_t flags; - // int32_t depth; - // int64_t gas; - // evmc_address recipient; - // evmc_address sender; - // const uint8_t* input_data; - // size_t input_size; - // evmc_uint256be value; - // evmc_bytes32 create2_salt; - // evmc_address code_address; - // }; - const auto& to = ProtocolContractAddresses.at("ContractManager"); - const auto& from = caller->getContractAddress(); - callInfo.flags = 0; - callInfo.depth = 1; - callInfo.gas = this->leftoverGas_; - callInfo.recipient = bytes::cast(to); - callInfo.sender = bytes::cast(from); - callInfo.input_data = fullData.data(); - callInfo.input_size = fullData.size(); - callInfo.value = {}; - callInfo.create2_salt = {}; - callInfo.code_address = bytes::cast(to); - // Get the ContractManager from the this->accounts_ map - ContractManager* contractManager = dynamic_cast(this->contracts_.at(to).get()); - this->setContractVars(contractManager, from, 0); - auto& callerNonce = this->accounts_[from]->nonce; - Address newContractAddress = ContractHost::deriveContractAddress(callerNonce, from); - this->stack_.registerNonce(from, callerNonce); - NestedCallSafeGuard guard(caller, caller->caller_, caller->value_); - contractManager->ethCall(callInfo, this); - ++callerNonce; - return newContractAddress; + template + Address callCreateContract(BaseContract& caller, Args&&... args) { + const uint256_t value = 0; + + PackedCreateMessage msg( + caller.getContractAddress(), + messageHandler_.cppExecutor().currentGas(), + value, + caller, + std::forward(args)... + ); + + return messageHandler_.onMessage(std::move(msg)); } /** @@ -544,18 +180,15 @@ class ContractHost : public evmc::Host { * @return A pointer to the contract. * @throw DynamicException if contract is not found or not of the requested type. */ - template const T* getContract(const Address &address) const { - auto it = this->contracts_.find(address); - if (it == this->contracts_.end()) throw DynamicException( - "ContractManager::getContract: contract at address " + - address.hex().get() + " not found." - ); - auto ptr = dynamic_cast(it->second.get()); - if (ptr == nullptr) throw DynamicException( - "ContractManager::getContract: Contract at address " + - address.hex().get() + " is not of the requested type: " + Utils::getRealTypeName() - ); - return ptr; + template + const T* getContract(View
address) const { + const T* pointer = dynamic_cast(&context_.getContract(address)); + + if (pointer == nullptr) { + throw DynamicException("Wrong contract type"); + } + + return pointer; } /** @@ -566,50 +199,16 @@ class ContractHost : public evmc::Host { * @return A pointer to the contract. * @throw DynamicException if contract is not found or not of the requested type. */ - template T* getContract(const Address& address) { - auto it = this->contracts_.find(address); - if (it == this->contracts_.end()) throw DynamicException( - "ContractManager::getContract: contract at address " + - address.hex().get() + " not found." - ); - auto ptr = dynamic_cast(it->second.get()); - if (ptr == nullptr) throw DynamicException( - "ContractManager::getContract: Contract at address " + - address.hex().get() + " is not of the requested type: " + Utils::getRealTypeName() - ); - return ptr; - } - - /** - * Emit an event from a contract. Called by DynamicContract's emitEvent(). - * @param event The event to emit. - * @throw DynamicException if there's an attempt to emit the event outside a contract call. - */ - void emitContractEvent(Event&& event); + template + T* getContract(const Address& address) { + T* pointer = dynamic_cast(&context_.getContract(address)); - /** - * Get the balance from a given address. Calls populateBalance(), so - * it's technically the same as getting the balance directly from State. - * Does NOT consider the current transaction being processed, if there is one. - * @param address The address to get the balance from. - * @return The balance of the address. - */ - uint256_t getBalanceFromAddress(const Address& address) const; - - /** - * Send tokens to a given address. Used by DynamicContract to send tokens to other contracts. - * @param from The address from which the tokens will be sent from. - * @param to The address to send the tokens to. - * @param amount The amount of tokens to send. - */ - void sendTokens(const BaseContract* from, const Address& to, const uint256_t& amount); + if (pointer == nullptr) { + throw DynamicException("Wrong contract type"); + } - /** - * Get the current nonce of a given Account - * Returns a REFERENCE to the nonce, so it can be modified. - * @param acc The address of the account to get the nonce from. - */ - uint64_t& getNonce(const Address& acc); + return pointer; + } template void emitEvent( @@ -618,53 +217,18 @@ class ContractHost : public evmc::Host { const std::tuple...>& args, bool anonymous ) { - Event event(name, // EVM events do not have names - this->eventIndex_, - this->txHash_, - this->txIndex_, - this->blockHash_, - this->currentTxContext_.block_number, - contract, - args, - anonymous - ); - this->eventIndex_++; - this->stack_.registerEvent(std::move(event)); + context_.addEvent(name, contract, args, anonymous); } - void registerNewCPPContract(const Address& addr, BaseContract* contract); - void registerNewEVMContract(const Address& addr, const uint8_t* code, size_t codeSize); - void registerVariableUse(SafeBase& variable); + ExecutionContext& context() { return context_; } - uint256_t getRandomValue() const { - return this->randomGen_.operator()(); - } - /// END OF CONTRACT INTERFACING FUNCTIONS - - // auto setGasContext(Gas& gas) { - // struct GasContextGuard { - // ~GasContextGuard() { - // *target = prevGas; - // } - - // Gas** target; - // Gas* prevGas; - // }; - - // const GasContextGuard guard{ .target = &gas_, .prevGas = gas_ }; + const ExecutionContext& context() const { return context_; } - // gas_ = &gas; + void registerVariableUse(SafeBase& var) { stack_.registerVariableUse(var); } - // return guard; - // } + uint256_t getRandomValue() const { return std::invoke(this->randomGen_); } - void setGas(Gas& gas) { - gas_ = &gas; - } - - Gas& getGas() { - return *gas_; // TODO: check null - } + /// END OF CONTRACT INTERFACING FUNCTIONS }; #endif // CONTRACT_HOST_H diff --git a/src/contract/contractmanager.cpp b/src/contract/contractmanager.cpp index 8c831213..e1ebc7dc 100644 --- a/src/contract/contractmanager.cpp +++ b/src/contract/contractmanager.cpp @@ -16,7 +16,7 @@ See the LICENSE.txt file in the project root for more information. #include "../utils/dynamicexception.h" ContractManager::ContractManager( - const DB& db, boost::unordered_flat_map, SafeHash>& contracts, + const DB& db, boost::unordered_flat_map, SafeHash, SafeCompare>& contracts, DumpManager& manager, const Options& options ) : BaseContract("ContractManager", ProtocolContractAddresses.at("ContractManager"), options.getChainOwner(), options.getChainID()), contracts_(contracts) @@ -80,7 +80,7 @@ void ContractManager::ethCall(const evmc_message& callInfo, ContractHost* host) throw DynamicException("ContractManager: Invalid function call"); } it->second(callInfo, - ContractHost::deriveContractAddress(this->host_->getNonce(caller), caller), + generateContractAddress(this->host_->context().getAccount(caller).nonce, caller), this->contracts_, this->getContractChainId(), this->host_); diff --git a/src/contract/contractmanager.h b/src/contract/contractmanager.h index 8cb7f23e..55c23ad1 100644 --- a/src/contract/contractmanager.h +++ b/src/contract/contractmanager.h @@ -34,7 +34,7 @@ class ContractManager : public BaseContract { private: /// Reference of currently deployed contracts. /// Owned by the State - boost::unordered_flat_map, SafeHash>& contracts_; + boost::unordered_flat_map, SafeHash, SafeCompare>& contracts_; /// Functions to create contracts. boost::unordered_flat_map< @@ -42,7 +42,7 @@ class ContractManager : public BaseContract { std::function< void(const evmc_message&, const Address&, - boost::unordered_flat_map, SafeHash>& contracts_, + boost::unordered_flat_map, SafeHash, SafeCompare>& contracts_, const uint64_t&, ContractHost* )>, @@ -119,7 +119,7 @@ class ContractManager : public BaseContract { * @throw DynamicException if contract address doesn't exist in the database. */ ContractManager(const DB& db, - boost::unordered_flat_map, SafeHash>& contracts, + boost::unordered_flat_map, SafeHash, SafeCompare>& contracts, DumpManager& manager, const Options& options); diff --git a/src/contract/contractstack.h b/src/contract/contractstack.h index f5acdc45..4aac9ba9 100644 --- a/src/contract/contractstack.h +++ b/src/contract/contractstack.h @@ -14,50 +14,13 @@ class ContractStack { private: - boost::unordered_flat_map code_; - boost::unordered_flat_map balance_; - boost::unordered_flat_map nonce_; - boost::unordered_flat_map storage_; - std::vector events_; - std::vector> contracts_; // Contracts that have been created during the execution of the call, we need to revert them if the call reverts. std::vector> usedVars_; public: - inline void registerCode(const Address& addr, const Bytes& code) { - this->code_.try_emplace(addr, code); - } - - inline void registerBalance(const Address& addr, const uint256_t& balance) { - this->balance_.try_emplace(addr, balance); - } - - inline void registerNonce(const Address& addr, const uint64_t& nonce) { - this->nonce_.try_emplace(addr, nonce); - } - - inline void registerStorageChange(const StorageKey& key, const Hash& value) { - this->storage_.try_emplace(key, value); - } - - inline void registerEvent(Event event) { - this->events_.emplace_back(std::move(event)); - } - - inline void registerContract(const Address& addr, BaseContract* contract) { - this->contracts_.emplace_back(addr, contract); - } - inline void registerVariableUse(SafeBase& var) { this->usedVars_.emplace_back(var); } - /// Getters - inline const boost::unordered_flat_map& getCode() const { return this->code_; } - inline const boost::unordered_flat_map& getBalance() const { return this->balance_; } - inline const boost::unordered_flat_map& getNonce() const { return this->nonce_; } - inline const boost::unordered_flat_map& getStorage() const { return this->storage_; } - inline std::vector& getEvents() { return this->events_; } - inline const std::vector>& getContracts() const { return this->contracts_; } inline const std::vector>& getUsedVars() const { return this->usedVars_; } }; diff --git a/src/contract/cpp/callexecutor.h b/src/contract/cpp/callexecutor.h index 463121c2..eedc1b79 100644 --- a/src/contract/cpp/callexecutor.h +++ b/src/contract/cpp/callexecutor.h @@ -39,7 +39,7 @@ class GasGuard { class CallExecutor { public: - using Contracts = boost::unordered_flat_map, SafeHash>&; + using Contracts = boost::unordered_flat_map, SafeHash, SafeCompare>&; CallExecutor(ContractHost& host, Contracts& contracts) : host_(host), contracts_(contracts) {} diff --git a/src/contract/dynamiccontract.h b/src/contract/dynamiccontract.h index 47b8becc..8819eb7e 100644 --- a/src/contract/dynamiccontract.h +++ b/src/contract/dynamiccontract.h @@ -639,17 +639,7 @@ class DynamicContract : public BaseContract { if (this->host_ == nullptr) { throw DynamicException("Contracts going haywire! trying to create a contract without a host!"); } - std::string createSignature = "createNew" + Utils::getRealTypeName() + "Contract("; - // Append args - createSignature += ContractReflectionInterface::getConstructorArgumentTypesString(); - createSignature += ")"; - Bytes fullData; - Utils::appendBytes(fullData, Utils::sha3(Utils::create_view_span(createSignature))); - fullData.resize(4); // We only need the first 4 bytes for the function signature - if constexpr (sizeof...(Args) > 0) { - Utils::appendBytes(fullData, ABI::Encoder::encodeData(std::forward(args)...)); - } - return this->host_->callCreateContract(this, fullData); + return this->host_->callCreateContract(*this, std::forward(args)...); } /** @@ -661,7 +651,7 @@ class DynamicContract : public BaseContract { if (this->host_ == nullptr) { throw DynamicException("Contracts going haywire! trying to get balance without a host!"); } - return host_->getBalanceFromAddress(address); + return host_->context().getAccount(address).balance; } /** @@ -673,7 +663,7 @@ class DynamicContract : public BaseContract { if (this->host_ == nullptr) { throw DynamicException("Contracts going haywire! trying to send tokens without a host!"); } - host_->sendTokens(this, to, amount); + host_->context().transferBalance(this->getContractAddress(), to, amount); } /** diff --git a/src/contract/evm/callexecutor.h b/src/contract/evm/callexecutor.h index 84220bdd..d56b2b57 100644 --- a/src/contract/evm/callexecutor.h +++ b/src/contract/evm/callexecutor.h @@ -13,7 +13,7 @@ namespace evm { class CallExecutor : public evmc::Host { public: using VmStorage = boost::unordered_flat_map; - using Accounts = boost::unordered_flat_map, SafeHash>; + using Accounts = boost::unordered_flat_map, SafeHash, SafeCompare>; CallExecutor(AnyCallHandler callHandler, evmc_vm* vm, VmStorage& vmStorage, Accounts& accounts, ContractStack& stack, const Hash& txHash, uint64_t txIndex, const Hash& blockHash, const evmc_tx_context& currentTxContext) : callHandler_(std::move(callHandler)), vm_(vm), vmStorage_(vmStorage), accounts_(accounts), stack_(stack), txHash_(txHash), txIndex_(txIndex), blockHash_(blockHash), currentTxContext_(currentTxContext) {} diff --git a/src/contract/messages/anyencodedmessagehandler.h b/src/contract/messages/anyencodedmessagehandler.h new file mode 100644 index 00000000..2f3aca77 --- /dev/null +++ b/src/contract/messages/anyencodedmessagehandler.h @@ -0,0 +1,59 @@ +#ifndef BDK_MESSAGES_ANYENCODEDMESSAGEHANDLER_H +#define BDK_MESSAGES_ANYENCODEDMESSAGEHANDLER_H + +#include "encodedmessages.h" + +class AnyEncodedMessageHandler { +public: + template + static AnyEncodedMessageHandler from(MessageHandler& handler) { + AnyEncodedMessageHandler anyHandler; + + const auto generic = [] (void *obj, auto& msg) { return static_cast(obj)->onMessage(msg); }; + + anyHandler.handler_ = &handler; + anyHandler.onCreate_ = static_cast(generic); + anyHandler.onSaltCreate_ = static_cast(generic); + anyHandler.onCall_ = static_cast(generic); + anyHandler.onStaticCall_ = static_cast(generic); + anyHandler.onDelegateCall_ = static_cast(generic); + + return anyHandler; + } + + Address onMessage(EncodedCreateMessage& msg) { return std::invoke(onCreate_, handler_, msg); } + + Address onMessage(EncodedSaltCreateMessage& msg) { return std::invoke(onSaltCreate_, handler_, msg); } + + Bytes onMessage(EncodedCallMessage& msg) { return std::invoke(onCall_, handler_, msg); } + + Bytes onMessage(EncodedStaticCallMessage& msg) { return std::invoke(onStaticCall_, handler_, msg); } + + Bytes onMessage(EncodedDelegateCallMessage& msg) { return std::invoke(onDelegateCall_, handler_, msg); } + +private: + void *handler_; + Address (*onCreate_)(void*, EncodedCreateMessage&); + Address (*onSaltCreate_)(void*, EncodedSaltCreateMessage&); + Bytes (*onCall_)(void*, EncodedCallMessage&); + Bytes (*onStaticCall_)(void*, EncodedStaticCallMessage&); + Bytes (*onDelegateCall_)(void*, EncodedDelegateCallMessage&); +}; + +template +AnyEncodedMessageHandler makeEncodedMessageHandler(MessageHandler& handler) { + AnyEncodedMessageHandler res; + + static const auto lambda = [] (void *obj, auto& msg) { return static_cast(obj)->onMessage(msg); }; // TODO: the static may be removed + + res.handler_ = &handler; + res.onCreate_ = static_cast(lambda); + res.onSaltCreate_ = static_cast(lambda); + res.onCall_ = static_cast(lambda); + res.onStaticCall_ = static_cast(lambda); + res.onDelegateCall_ = static_cast(lambda); + + return res; +} + +#endif // BDK_MESSAGES_ANYENCODEDMESSAGEHANDLER_H diff --git a/src/contract/messages/basemessage.h b/src/contract/messages/basemessage.h new file mode 100644 index 00000000..5229eb9c --- /dev/null +++ b/src/contract/messages/basemessage.h @@ -0,0 +1,149 @@ +#ifndef BDK_MESSAGES_BASEMESSAGE_H +#define BDK_MESSAGES_BASEMESSAGE_H + +#include "bytes/range.h" +#include "utils/address.h" +#include "utils/hash.h" +#include "gas.h" + +struct BaseContract; + +template +struct BaseMessage : BaseMessage, BaseMessage { + template + constexpr BaseMessage(U&& first, Us&&... others) + : BaseMessage(std::forward(first)), + BaseMessage(std::forward(others)...) {} +}; + +template +struct BaseMessage : T { + template + explicit constexpr BaseMessage(U&& first) : T(std::forward(first)) {} +}; + +class FromField { +public: + template + requires std::convertible_to> + explicit constexpr FromField(R&& range) : from_(range) {} + + constexpr View
from() const { return from_; } + +private: + View
from_; +}; + +class ToField { +public: + template + requires std::convertible_to> + explicit constexpr ToField(R&& range) : to_(range) {} + + constexpr View
to() const { return to_; } + +private: + View
to_; +}; + +class GasField { +public: + explicit constexpr GasField(messages::Gas& gas) : gas_(gas) {} + constexpr messages::Gas& gas() { return gas_; } + constexpr const messages::Gas& gas() const { return gas_; } + +private: + messages::Gas& gas_; +}; + +class ValueField { +public: + explicit constexpr ValueField(const uint256_t& value) : value_(value) {} + constexpr const uint256_t& value() const { return value_; } + +private: + const uint256_t& value_; +}; + +class InputField { +public: + explicit constexpr InputField(View input) : input_(input) {} + constexpr View input() const { return input_; } + +private: + View input_; +}; + +class CodeField { +public: + explicit constexpr CodeField(View code) : code_(code) {} + constexpr View code() const { return code_; } + +private: + View code_; +}; + +class SaltField { +public: + template + requires std::convertible_to> + explicit constexpr SaltField(R&& range) : salt_(range) {} + constexpr View salt() const { return salt_; } + +private: + View salt_; +}; + +class CodeAddressField { +public: + template + requires std::convertible_to> + explicit constexpr CodeAddressField(R&& range) : codeAddress_(range) {} + constexpr View
codeAddress() const { return codeAddress_; } + +private: + View
codeAddress_; +}; + +class CallerField { +public: + explicit constexpr CallerField(const BaseContract& caller) : caller_(caller) {} + constexpr const BaseContract& caller() const { return caller_; } + +private: + const BaseContract& caller_; +}; + +template +class MethodField { +public: + explicit constexpr MethodField(M method) : method_(method) {} + constexpr M& method() { return method_; } + constexpr const M& method() const { return method_; } + +private: + M method_; +}; + +template +class ArgsField { +public: + explicit constexpr ArgsField(Args&&... args) : args_(std::forward(args)...) {} + constexpr std::tuple& args() & { return args_; } + constexpr std::tuple&& args() && { return std::move(args_); } + constexpr const std::tuple& args() const& { return args_; } + +private: + std::tuple args_; +}; + +template +ArgsField(Args&&...) -> ArgsField; + +template +struct BaseMessage> : ArgsField { + explicit constexpr BaseMessage(auto&&... args) + : ArgsField(std::forward(args)...) {} +}; + +#endif // BDK_MESSAGES_BASEMESSAGE_H diff --git a/src/contract/messages/common.cpp b/src/contract/messages/common.cpp new file mode 100644 index 00000000..c7df74ca --- /dev/null +++ b/src/contract/messages/common.cpp @@ -0,0 +1,21 @@ +#include "common.h" + +Address generateContractAddress(uint64_t nonce, View
address) { + uint8_t rlpSize = 0xc0; + rlpSize += 20; + rlpSize += (nonce < 0x80) ? 1 : 1 + Utils::bytesRequired(nonce); + Bytes rlp; + rlp.insert(rlp.end(), rlpSize); + rlp.insert(rlp.end(), address.begin(), address.end()); + rlp.insert(rlp.end(), (nonce < 0x80) ? (char)nonce : (char)0x80 + Utils::bytesRequired(nonce)); + + return Address(Utils::sha3(rlp).view(12)); +} + +Address generateContractAddress(View
from, View salt, View code) { + const Hash codeHash = Utils::sha3(code); + Bytes buffer(from.size() + salt.size() + codeHash.size() + 1); // 85 + buffer[0] = 0xFF; + bytes::join(from, salt, codeHash).to(buffer | std::views::drop(1)); + return Address(Utils::sha3(buffer).view(12)); +} diff --git a/src/contract/messages/common.h b/src/contract/messages/common.h new file mode 100644 index 00000000..24cf9434 --- /dev/null +++ b/src/contract/messages/common.h @@ -0,0 +1,43 @@ +#ifndef BDK_MESSAGES_COMMON_H +#define BDK_MESSAGES_COMMON_H + +#include "utils/utils.h" +#include "concepts.h" + +Address generateContractAddress(uint64_t nonce, View
address); + +Address generateContractAddress(View
from, View salt, View code); + +constexpr uint256_t messageValueOrZero(const auto& msg) { + if constexpr (concepts::HasValueField) { + return msg.value(); + } else { + return uint256_t(0); + } +} + +constexpr View
messageCodeAddress(const auto& msg) { + if constexpr (concepts::DelegateCallMessage) { + return msg.codeAddress(); + } else { + return msg.to(); + } +} + +constexpr Address messageRecipientOrDefault(const auto& msg) { + if constexpr (concepts::CreateMessage) { + return Address{}; + } else { + return Address(msg.to()); + } +} + +constexpr Hash messageSaltOrDefault(const auto& msg) { + if constexpr (concepts::SaltMessage) { + return Hash(msg.salt()); + } else { + return Hash{}; + } +} + +#endif // BDK_MESSAGES_COMMON_H diff --git a/src/contract/messages/concepts.h b/src/contract/messages/concepts.h new file mode 100644 index 00000000..9ba836d3 --- /dev/null +++ b/src/contract/messages/concepts.h @@ -0,0 +1,107 @@ +#ifndef BDK_MESSAGES_CONCEPTS_H +#define BDK_MESSAGES_CONCEPTS_H + +#include "utils/strings.h" +#include "gas.h" + +namespace concepts { + +/** + * Basic message concept. Every message has as sender address and a gas reference to be consumed. + */ +template +concept Message = requires (M m) { + { m.from() } -> std::convertible_to>; + { m.gas() } -> std::convertible_to; +}; + +/** + * A message that also has value. Often used to describe composed concepts. + */ +template +concept HasValueField = requires (M m) { + { m.value() } -> std::convertible_to; +}; + +template +concept HasInputField = requires (M m) { + { m.input() } -> std::convertible_to>; +}; + +template +concept HasCodeField = requires (M m) { + { m.code() } -> std::convertible_to>; +}; + +/** + * A message that also has recipient address. Often used to describe composed concepts. + */ +template +concept HasToField = requires (M m) { + { m.to() } -> std::convertible_to>; +}; + +/** + * A message aimed to create an address. In general, contract creation messages + * do not have a target address (i.e. recipient) but can have value. + */ +template +concept CreateMessage = Message && HasValueField && !HasToField; + +/** + * Call messages can have value and must have a target address. + */ +template +concept CallMessage = Message && HasToField; + +/** + * Static call messages are messages that calls const/view functions. They + * can't have value but (as any call message) must have a recipient address + */ +template +concept StaticCallMessage = CallMessage && !HasValueField; + +/** + * Solution based on the C++ standard: std::ranges::enable_borrowed_range. + * In short, delegate calls have the exact same structure of a normal call, + * but with different intention. Thus, any message that represents a + * delegate message must specialize the EnableDelegate as a true_type, + * allowing the DelegateCallMessage concept to be chosen during overload + * resolution. + */ +template +constexpr bool EnableDelegate = false; + +template +constexpr bool EnableCallCode = false; + +/** + * Concept of delegate call messages. + */ +template +concept DelegateCallMessage = CallMessage && requires (M m) { + { m.codeAddress() } -> std::convertible_to>; +}; + +/** + * Concept of delegate call messages. + */ +template +concept CallCodeMessage = CallMessage && EnableCallCode>; + +template +concept SaltMessage = CreateMessage && requires (M m) { + { m.salt() } -> std::convertible_to>; +}; + +template +concept EncodedMessage = (CallMessage && HasInputField) || (CreateMessage && HasCodeField); + +template +concept PackedMessage = Message && requires (M m) { + m.args(); +}; + +} // namespace concepts + +#endif // BDK_MESSAGES_CONCEPTS_H diff --git a/src/contract/messages/cppcontractexecutor.h b/src/contract/messages/cppcontractexecutor.h new file mode 100644 index 00000000..f08b0996 --- /dev/null +++ b/src/contract/messages/cppcontractexecutor.h @@ -0,0 +1,142 @@ +#ifndef BDK_MESSAGES_CPPCONTRACTEXECUTOR_H +#define BDK_MESSAGES_CPPCONTRACTEXECUTOR_H + +#include "executioncontext.h" +#include "traits.h" +#include "bytes/cast.h" +#include "utils/contractreflectioninterface.h" + +struct ContractHost; + +class CppContractExecutor { +public: + CppContractExecutor(ExecutionContext& context, ContractHost& host) + : context_(context), host_(host) {} + + template + auto execute(M&& msg) -> traits::MessageResult { + if constexpr (concepts::DelegateCallMessage) { + throw DynamicException("Delegate call not supported for C++ contracts"); + } + + auto guard = transactional::checkpoint(currentGas_); + currentGas_ = &msg.gas(); + + if constexpr (concepts::CreateMessage) { + return createContract(std::forward(msg)); + } else { + return callContract(std::forward(msg)); + } + } + + messages::Gas& currentGas() { return *currentGas_; } + +private: + decltype(auto) callContract(concepts::PackedMessage auto&& msg) { + transactional::Group guard = { + transactional::checkpoint(msg.caller().caller_), + transactional::checkpoint(msg.caller().value_) + }; + + auto& contract = getContractAs>(msg.to()); + + return std::apply([&] (auto&&... args) { + if constexpr (concepts::StaticCallMessage) { + return std::invoke(msg.method(), contract, std::forward(args)...); + } else { + return contract.callContractFunction(&host_, msg.method(), std::forward(args)...); + } + }, std::forward(msg).args()); + } + + decltype(auto) callContract(concepts::EncodedMessage auto&& msg) { + const evmc_message evmcMsg{ + .kind = EVMC_CALL, + .flags = (concepts::StaticCallMessage ? EVMC_STATIC : evmc_flags(0)), + .depth = 0, + .gas = msg.gas().value(), + .recipient = bytes::cast(msg.to()), + .sender = bytes::cast(msg.from()), + .input_data = msg.input().data(), + .input_size = msg.input().size(), + .value = evmc_uint256be{}, + .create2_salt = evmc_bytes32{}, + .code_address = bytes::cast(msg.to()) + }; + + auto& contract = context_.getContract(msg.to()); + contract.caller_ = Address(msg.from()); + contract.value_ = messageValueOrZero(msg); + + return contract.evmEthCall(evmcMsg, &host_); + } + + Address createContract(concepts::CreateMessage auto&& msg) { + using ContractType = traits::MessageContract; + + const std::string createSignature = "createNew" + + Utils::getRealTypeName() + + "Contract(" + + ContractReflectionInterface::getConstructorArgumentTypesString() + + ")"; + + // We only need the first 4 bytes for the function signature + Bytes fullData = Utils::makeBytes(Utils::sha3(bytes::view(createSignature)) | std::views::take(4)); + + std::apply(Utils::Overloaded{ + [] () {}, + [&fullData] (const auto&... args) { + Utils::appendBytes(fullData, ABI::Encoder::encodeData(args...)); + } + }, msg.args()); + + const Address& to = ProtocolContractAddresses.at("ContractManager"); + + const evmc_message evmcMsg{ + .kind = EVMC_CREATE, + .flags = 0, + .depth = 1, + .gas = msg.gas().value(), + .recipient = bytes::cast(to), + .sender = bytes::cast(msg.from()), + .input_data = fullData.data(), + .input_size = fullData.size(), + .value = Utils::uint256ToEvmcUint256(msg.value()), + .create2_salt{}, + .code_address = bytes::cast(to) + }; + + auto& contract = context_.getContract(to); + + transactional::Group guard = { + transactional::checkpoint(contract.caller_), + transactional::checkpoint(contract.value_) + }; + + contract.caller_ = Address(msg.from()); // TODO: these copies can be avoided + contract.value_ = 0; + + const Address contractAddress = generateContractAddress(context_.getAccount(msg.from()).nonce, msg.from()); + contract.ethCall(evmcMsg, &host_); + context_.incrementNonce(msg.from()); + + return contractAddress; + } + + template + C& getContractAs(View
address) { + C* ptr = dynamic_cast(&context_.getContract(address)); + + if (ptr == nullptr) { + throw DynamicException("Wrong contract type"); // TODO: add more info + } + + return *ptr; + } + + ExecutionContext& context_; + ContractHost& host_; + messages::Gas *currentGas_ = nullptr; +}; + +#endif // BDK_MESSAGES_CPPCONTRACTEXECUTOR_H diff --git a/src/contract/messages/encodedmessages.h b/src/contract/messages/encodedmessages.h new file mode 100644 index 00000000..bdc519dc --- /dev/null +++ b/src/contract/messages/encodedmessages.h @@ -0,0 +1,45 @@ +#ifndef BDK_ENCODEDMESSAGES_H +#define BDK_ENCODEDMESSAGES_H + +#include "contract/messages/concepts.h" +#include "contract/messages/basemessage.h" + +struct EncodedCallMessage : BaseMessage { + using BaseMessage::BaseMessage; +}; + +struct EncodedStaticCallMessage : BaseMessage { + using BaseMessage::BaseMessage; +}; + +struct EncodedCreateMessage : BaseMessage { + using BaseMessage::BaseMessage; +}; + +struct EncodedSaltCreateMessage : BaseMessage { + using BaseMessage::BaseMessage; +}; + +struct EncodedDelegateCallMessage : BaseMessage { + using BaseMessage::BaseMessage; +}; + +struct EncodedCallCodeMessage : EncodedCallMessage { + using EncodedCallMessage::EncodedCallMessage; +}; + +template<> +constexpr bool concepts::EnableDelegate = true; + +template<> +constexpr bool concepts::EnableCallCode = true; + +using EncodedMessageVariant = std::variant< + EncodedCreateMessage, + EncodedSaltCreateMessage, + EncodedCallMessage, + EncodedStaticCallMessage, + EncodedDelegateCallMessage +>; + +#endif // BDK_ENCODEDMESSAGES_H diff --git a/src/contract/messages/evmcontractexecutor.cpp b/src/contract/messages/evmcontractexecutor.cpp new file mode 100644 index 00000000..40fe8342 --- /dev/null +++ b/src/contract/messages/evmcontractexecutor.cpp @@ -0,0 +1,359 @@ +#include "evmcontractexecutor.h" +#include "bytes/cast.h" +#include "common.h" +#include "outofgas.h" + +constexpr decltype(auto) getAndThen(auto&& map, const auto& key, auto&& andThen, auto&& orElse) { + const auto it = map.find(key); + + if (it == map.end()) { + return std::invoke(orElse); + } else { + return std::invoke(andThen, it->second); + } +} + +constexpr auto getAndThen(auto&& map, const auto& key, auto&& andThen) { + using Result = std::invoke_result_tsecond)>; + return getAndThen(map, key, andThen, [] () { return Result{}; }); +} + + +template +constexpr evmc_call_kind getEvmcKind(const M& msg) { + if constexpr (concepts::SaltMessage) { + return EVMC_CREATE2; + } else if (concepts::CreateMessage) { + return EVMC_CREATE; + } else if (concepts::DelegateCallMessage) { + return EVMC_DELEGATECALL; + } else { + return EVMC_CALL; + } + + // TODO: CALL CODE! +} + +template +constexpr evmc_flags getEvmcFlags(const M& msg) { + if constexpr (concepts::StaticCallMessage) { + return EVMC_STATIC; + } else { + return evmc_flags{}; + } +} + +template +constexpr evmc_message makeEvmcMessage(const M& msg, uint64_t depth) { + return evmc_message{ + .kind = getEvmcKind(msg), + .flags = getEvmcFlags(msg), + .depth = depth, + .gas = msg.gas().value(), + .recipient = bytes::cast(messageRecipientOrDefault(msg)), + .sender = bytes::cast(msg.from()), + .input_data = msg.input().data(), + .input_size = msg.input().size(), + .value = Utils::uint256ToEvmcUint256(messageValueOrZero(msg)), + .create2_salt = evmc_bytes32{}, + .code_address = evmc_address{} // TODO: CALL CODE? + }; +} + +template +constexpr evmc_message makeEvmcMessage(const M& msg, uint64_t depth, View
contractAddress) { + return evmc_message{ + .kind = getEvmcKind(msg), + .flags = 0, + .depth = depth, + .gas = msg.gas().value(), + .recipient = bytes::cast(contractAddress), + .sender = bytes::cast(msg.from()), + .input_data = nullptr, + .input_size = 0, + .value = Utils::uint256ToEvmcUint256(messageValueOrZero(msg)), + .create2_salt = bytes::cast(messageSaltOrDefault(msg)), + .code_address = evmc_address{} // TODO: CALL CODE? + }; +} + +static Bytes executeEvmcMessage(evmc_vm* vm, const evmc_host_interface* host, evmc_host_context* context, const evmc_message& msg, messages::Gas& gas, View code) { + evmc::Result result(::evmc_execute( + vm, + host, + context, + evmc_revision::EVMC_LATEST_STABLE_REVISION, + &msg, + code.data(), + code.size())); + + gas = messages::Gas(result.gas_left); + + if (result.status_code == EVMC_SUCCESS) { + return Bytes(result.output_data, result.output_data + result.output_size); + } else if (result.status_code == EVMC_OUT_OF_GAS) { + throw OutOfGas(); + } else { + std::string reason = ""; + + if (result.output_size > 0) { + reason = ABI::Decoder::decodeError(View(result.output_data, result.output_size)); + } + + throw DynamicException(reason); + } +} + +static void createContractImpl(auto& msg, ExecutionContext& context, View
contractAddress, evmc_vm *vm, evmc::Host& host, uint64_t depth) { + Bytes code = executeEvmcMessage(vm, &evmc::Host::get_interface(), host.to_context(), + makeEvmcMessage(msg, depth, contractAddress), msg.gas(), msg.code()); + + Account newAccount; + newAccount.nonce = 1; // TODO: starting as 1? + newAccount.codeHash = Utils::sha3(code); + newAccount.code = std::move(code); + newAccount.contractType = ContractType::EVM; + + context.addAccount(contractAddress, std::move(newAccount)); + context.notifyNewContract(contractAddress, nullptr); + context.incrementNonce(msg.from()); +} + +Bytes EvmContractExecutor::execute(EncodedCallMessage& msg) { + auto checkpoint = context_.checkpoint(); + auto depthGuard = transactional::copy(depth_); // TODO: checkpoint (and deprecate copy) + ++depth_; + + if (msg.value() > 0) { + context_.transferBalance(msg.from(), msg.to(), msg.value()); + } + + const Bytes output = executeEvmcMessage(this->vm_, &this->get_interface(), this->to_context(), + makeEvmcMessage(msg, depth_), msg.gas(), context_.getAccount(msg.to()).code); + + checkpoint.commit(); + + return output; +} + +Bytes EvmContractExecutor::execute(EncodedStaticCallMessage& msg) { + View code = context_.getAccount(msg.to()).code; + + auto depthGuard = transactional::copy(depth_); + ++depth_; + + return executeEvmcMessage(this->vm_, &this->get_interface(), this->to_context(), + makeEvmcMessage(msg, depth_), msg.gas(), code); +} + +Bytes EvmContractExecutor::execute(EncodedDelegateCallMessage& msg) { + auto checkpoint = context_.checkpoint(); + auto depthGuard = transactional::copy(depth_); + ++depth_; + + // TODO: is the value transfer correct for delegate calls? + if (msg.value() > 0) { + context_.transferBalance(msg.from(), msg.to(), msg.value()); + } + + const Bytes output = executeEvmcMessage(this->vm_, &this->get_interface(), this->to_context(), + makeEvmcMessage(msg, depth_), msg.gas(), context_.getAccount(msg.codeAddress()).code); + + checkpoint.commit(); + + return output; +} + +Address EvmContractExecutor::execute(EncodedCreateMessage& msg) { + auto checkpoint = context_.checkpoint(); + auto depthGuard = transactional::copy(depth_); + const Address contractAddress = generateContractAddress(context_.getAccount(msg.from()).nonce, msg.from()); + createContractImpl(msg, context_, contractAddress, vm_, *this, ++depth_); + checkpoint.commit(); + return contractAddress; +} + +Address EvmContractExecutor::execute(EncodedSaltCreateMessage& msg) { + auto checkpoint = context_.checkpoint(); + auto depthGuard = transactional::copy(depth_); + const Address contractAddress = generateContractAddress(msg.from(), msg.salt(), msg.code()); + createContractImpl(msg, context_, contractAddress, vm_, *this, ++depth_); + checkpoint.commit(); + return contractAddress; +} + +bool EvmContractExecutor::account_exists(const evmc::address& addr) const noexcept { + return context_.accountExists(addr); +} + +evmc::bytes32 EvmContractExecutor::get_storage(const evmc::address& addr, const evmc::bytes32& key) const noexcept { + return bytes::cast(context_.retrieve(addr, key)); +} + +evmc_storage_status EvmContractExecutor::set_storage(const evmc::address& addr, const evmc::bytes32& key, const evmc::bytes32& value) noexcept { + context_.store(addr, key, value); + return EVMC_STORAGE_MODIFIED; +} + +evmc::uint256be EvmContractExecutor::get_balance(const evmc::address& addr) const noexcept { + try { + return Utils::uint256ToEvmcUint256(context_.getAccount(addr).balance); + } catch (const std::exception&) { + return evmc::uint256be{}; + } +} + +size_t EvmContractExecutor::get_code_size(const evmc::address& addr) const noexcept { + try { + return context_.getAccount(addr).code.size(); + } catch (const std::exception&) { + return 0; + } +} + +evmc::bytes32 EvmContractExecutor::get_code_hash(const evmc::address& addr) const noexcept { + try { + return bytes::cast(context_.getAccount(addr).codeHash); + } catch (const std::exception&) { + return evmc::bytes32{}; + } +} + +size_t EvmContractExecutor::copy_code(const evmc::address& addr, size_t code_offset, uint8_t* buffer_data, size_t buffer_size) const noexcept { + + try { + View code = context_.getAccount(addr).code; + + if (code_offset < code.size()) { + const auto n = std::min(buffer_size, code.size() - code_offset); + if (n > 0) + std::copy_n(&code[code_offset], n, buffer_data); + return n; + } + + } catch (const std::exception&) {} + + return 0; +} + +bool EvmContractExecutor::selfdestruct(const evmc::address& addr, const evmc::address& beneficiary) noexcept { + // SELFDESTRUCT is not allowed in the current implementation + return false; +} + +evmc_tx_context EvmContractExecutor::get_tx_context() const noexcept { + return evmc_tx_context{ + .tx_gas_price = Utils::uint256ToEvmcUint256(context_.getTxGasPrice()), + .tx_origin = bytes::cast(context_.getTxOrigin()), + .block_coinbase = bytes::cast(context_.getBlockCoinbase()), + .block_number = context_.getBlockNumber(), + .block_timestamp = context_.getBlockTimestamp(), + .block_gas_limit = context_.getBlockGasLimit(), + .block_prev_randao = {}, + .chain_id = Utils::uint256ToEvmcUint256(context_.getChainId()), + .block_base_fee = {}, + .blob_base_fee = {}, + .blob_hashes = nullptr, + .blob_hashes_count = 0 + }; +} + +evmc::bytes32 EvmContractExecutor::get_block_hash(int64_t number) const noexcept { + return Utils::uint256ToEvmcUint256(number); // TODO: ??? +} + +void EvmContractExecutor::emit_log(const evmc::address& addr, const uint8_t* data, size_t dataSize, const evmc::bytes32 topics[], size_t topicsCount) noexcept { + try { + // We need the following arguments to build a event: + // (std::string) name The event's name. + // (uint64_t) logIndex The event's position on the block. + // (Hash) txHash The hash of the transaction that emitted the event. + // (uint64_t) txIndex The position of the transaction in the block. + // (Hash) blockHash The hash of the block that emitted the event. + // (uint64_t) blockIndex The height of the block. + // (Address) address The address that emitted the event. + // (Bytes) data The event's arguments. + // (std::vector) topics The event's indexed arguments. + // (bool) anonymous Whether the event is anonymous or not. + std::vector topicsVec; + topicsVec.reserve(topicsCount); + for (uint64_t i = 0; i < topicsCount; i++) { + topicsVec.emplace_back(topics[i]); + } + + context_.addEvent(addr, View(data, dataSize), std::move(topicsVec)); + + } catch (const std::exception& ignored) { + // TODO: log errors + } +} + +evmc_access_status EvmContractExecutor::access_account(const evmc::address& addr) noexcept { + return EVMC_ACCESS_WARM; +} + +evmc_access_status EvmContractExecutor::access_storage(const evmc::address& addr, const evmc::bytes32& key) noexcept { + return EVMC_ACCESS_WARM; +} + +evmc::bytes32 EvmContractExecutor::get_transient_storage(const evmc::address &addr, const evmc::bytes32 &key) const noexcept { + return getAndThen(transientStorage_, StorageKeyView(addr, key), [] (const auto& result) { return bytes::cast(result); }); +} + +void EvmContractExecutor::set_transient_storage(const evmc::address &addr, const evmc::bytes32 &key, const evmc::bytes32 &value) noexcept { + // TODO: This also must be controlled by transactions + transientStorage_.emplace(StorageKeyView{addr, key}, value); +} + +evmc::Result EvmContractExecutor::call(const evmc_message& msg) noexcept { + messages::Gas gas(msg.gas); + const uint256_t value = Utils::evmcUint256ToUint256(msg.value); + + const auto process = [&] (auto& msg) { + try { + const auto output = messageHandler_.onMessage(msg); + + if constexpr (concepts::CreateMessage) { + return evmc::Result(EVMC_SUCCESS, gas.value(), 0, bytes::cast(output)); + } else { + return evmc::Result(EVMC_SUCCESS, gas.value(), 0, output.data(), output.size()); + } + } catch (const OutOfGas&) { // TODO: ExecutionReverted exception is important + return evmc::Result(EVMC_OUT_OF_GAS); + } catch (const std::exception& err) { + Bytes output; + + if (err.what() != nullptr) { + output = ABI::Encoder::encodeError(err.what()); // TODO: this may throw... + } + + return evmc::Result(EVMC_REVERT, gas.value(), 0, output.data(), output.size()); + } + }; + + if (msg.kind == EVMC_DELEGATECALL) { + std::cout << "from: " << Hex::fromBytes(msg.sender, true) << "\n"; + std::cout << "to: " << Hex::fromBytes(msg.recipient, true) << "\n"; + std::cout << "code address: " << Hex::fromBytes(msg.code_address, true) << "\n"; + + EncodedDelegateCallMessage encodedMessage(msg.sender, msg.recipient, gas, value, View(msg.input_data, msg.input_size), msg.code_address); + return process(encodedMessage); + } else if (msg.kind == EVMC_CALL && msg.flags == EVMC_STATIC) { + EncodedStaticCallMessage encodedMessage(msg.sender, msg.recipient, gas, View(msg.input_data, msg.input_size)); + return process(encodedMessage); + } else if (msg.kind == EVMC_CALL) { + EncodedCallMessage encodedMessage(msg.sender, msg.recipient, gas, value, View(msg.input_data, msg.input_size)); + return process(encodedMessage); + } else if (msg.kind == EVMC_CREATE) { + EncodedCreateMessage encodedMessage(msg.sender, gas, value, View(msg.input_data, msg.input_size)); + return process(encodedMessage); + } else if (msg.kind == EVMC_CREATE2) { + EncodedSaltCreateMessage encodedMessage(msg.sender, gas, value, View(msg.input_data, msg.input_size), msg.create2_salt); + return process(encodedMessage); + } else if (msg.kind == EVMC_CALLCODE) { + return evmc::Result{}; // TODO: CALL CODE!!! + } + + // TODO: proper error result with proper reason (encoded) + return evmc::Result{}; +} diff --git a/src/contract/messages/evmcontractexecutor.h b/src/contract/messages/evmcontractexecutor.h new file mode 100644 index 00000000..d2ed5761 --- /dev/null +++ b/src/contract/messages/evmcontractexecutor.h @@ -0,0 +1,111 @@ +#ifndef BDK_MESSAGES_EVMCONTRACTEXECUTOR_H +#define BDK_MESSAGES_EVMCONTRACTEXECUTOR_H + +#include +#include "utils/hash.h" +#include "utils/contractreflectioninterface.h" +#include "contract/contractstack.h" +#include "anyencodedmessagehandler.h" +#include "executioncontext.h" +#include "traits.h" + +class EvmContractExecutor : public evmc::Host { +public: + EvmContractExecutor( + AnyEncodedMessageHandler messageHandler, ExecutionContext& context, evmc_vm *vm) + : messageHandler_(messageHandler), context_(context), vm_(vm), transientStorage_(), depth_(0) {} + + Bytes execute(EncodedCallMessage& msg); + + Bytes execute(EncodedStaticCallMessage& msg); + + Bytes execute(EncodedDelegateCallMessage& msg); + + template + requires concepts::CallMessage + auto execute(M&& msg) -> traits::MessageResult { + // TODO: can this apply be a free common function for encoding not encoded messages? + // TODO: what about the whole process of converting a packed msg to a encoded msg? + const Bytes encodedInput = std::apply([&] (const Args&... args) { + const std::string functionName = ContractReflectionInterface::getFunctionName(msg.method()); + + if (functionName.empty()) { + throw DynamicException("EVM contract function name is empty (contract not registered?)"); + } + + Bytes res = Utils::makeBytes(Utils::uint32ToBytes(ABI::FunctorEncoder::encode(functionName).value)); + + if constexpr (sizeof...(Args) > 0) { + Utils::appendBytes(res, ABI::Encoder::encodeData(args...)); + } + + return res; + }, msg.args()); + + Bytes output; + + if constexpr (concepts::StaticCallMessage) { + EncodedStaticCallMessage encodedMessage(msg.from(), msg.to(), msg.gas(), encodedInput); + output = this->execute(encodedMessage); + } else if constexpr (concepts::DelegateCallMessage) { + EncodedDelegateCallMessage encodedMessage(msg.from(), msg.to(), msg.gas(), msg.value(), encodedInput, msg.codeAddress()); + output = this->execute(encodedMessage); + } else { + EncodedCallMessage encodedMessage(msg.from(), msg.to(), msg.gas(), msg.value(), encodedInput); + output = this->execute(encodedMessage); + } + + if constexpr (not std::same_as, void>) { + return std::get<0>(ABI::Decoder::decodeData>(output)); + } + } + + Address execute(EncodedCreateMessage& msg); + + Address execute(EncodedSaltCreateMessage& msg); + + decltype(auto) execute(auto&& msg) { + return execute(msg); + } + + bool account_exists(const evmc::address& addr) const noexcept override final; + + evmc::bytes32 get_storage(const evmc::address& addr, const evmc::bytes32& key) const noexcept override final; + + evmc_storage_status set_storage(const evmc::address& addr, const evmc::bytes32& key, const evmc::bytes32& value) noexcept override final; + + evmc::uint256be get_balance(const evmc::address& addr) const noexcept override final; + + size_t get_code_size(const evmc::address& addr) const noexcept override final; + + evmc::bytes32 get_code_hash(const evmc::address& addr) const noexcept override final; + + size_t copy_code(const evmc::address& addr, size_t code_offset, uint8_t* buffer_data, size_t buffer_size) const noexcept override final; + + bool selfdestruct(const evmc::address& addr, const evmc::address& beneficiary) noexcept override final; + + evmc_tx_context get_tx_context() const noexcept override final; + + evmc::bytes32 get_block_hash(int64_t number) const noexcept override final; + + void emit_log(const evmc::address& addr, const uint8_t* data, size_t data_size, const evmc::bytes32 topics[], size_t topics_count) noexcept override final; + + evmc_access_status access_account(const evmc::address& addr) noexcept override final; + + evmc_access_status access_storage(const evmc::address& addr, const evmc::bytes32& key) noexcept override final; + + evmc::bytes32 get_transient_storage(const evmc::address &addr, const evmc::bytes32 &key) const noexcept override final; + + void set_transient_storage(const evmc::address &addr, const evmc::bytes32 &key, const evmc::bytes32 &value) noexcept override final; + + evmc::Result call(const evmc_message& msg) noexcept override final; + +private: + AnyEncodedMessageHandler messageHandler_; + ExecutionContext& context_; + evmc_vm *vm_; + boost::unordered_flat_map transientStorage_; + uint64_t depth_; +}; + +#endif // BDK_MESSAGES_EVMCONTRACTEXECUTOR_H diff --git a/src/contract/messages/executioncontext.cpp b/src/contract/messages/executioncontext.cpp new file mode 100644 index 00000000..161ae7cc --- /dev/null +++ b/src/contract/messages/executioncontext.cpp @@ -0,0 +1,179 @@ +#include "executioncontext.h" + +void ExecutionContext::addEvent(Event event) { + transactional::AnyTransactional emplaceTransaction(transactional::emplaceBack(events_, std::move(event))); + transactions_.push(std::move(emplaceTransaction)); +} + +void ExecutionContext::addEvent(View
address, View data, std::vector topics) { + this->addEvent(Event("", events_.size(), txHash_, txIndex_, blockHash_, blockNumber_, Address(address), Bytes(data), std::move(topics), !topics.empty())); +} + + +const Account& ExecutionContext::getAccount(View
accountAddress) const { + const auto iterator = accounts_.find(accountAddress); + + if (iterator == accounts_.end()) { + throw DynamicException("account not found"); // TODO: dynamic exception + } + + return *iterator->second; +} + +BaseContract& ExecutionContext::getContract(View
contractAddress) { + const auto it = contracts_.find(contractAddress); + + if (it == contracts_.end()) { + throw DynamicException("contract not found"); // TODO: dynamic exception + } + + if (it->second == nullptr) { + throw DynamicException("not a C++ contract"); // TODO: dynamic exception + } + + return *it->second; +} + +const BaseContract& ExecutionContext::getContract(View
contractAddress) const { + const auto it = contracts_.find(contractAddress); + + if (it == contracts_.end()) { + throw DynamicException("contract not found"); // TODO: dynamic exception + } + + if (it->second == nullptr) { + throw DynamicException("not a C++ contract"); // TODO: dynamic exception + } + + return *it->second; +} + +Account& ExecutionContext::getMutableAccount(View
accountAddress) { + const auto iterator = accounts_.find(accountAddress); + + if (iterator == accounts_.end()) { + throw DynamicException("account not found"); // TODO: dynamic exception + } + + return *iterator->second; +} + +bool ExecutionContext::accountExists(View
accountAddress) const { + return accounts_.contains(accountAddress); +} + +void ExecutionContext::addAccount(View
address, Account account) { + auto [transaction, inserted] = transactional::emplace(accounts_, address, std::move(account)); + + if (!inserted) { + throw DynamicException("account already exist"); // TODO: dynamic exception + } + + transactions_.push(transactional::AnyTransactional(std::move(transaction))); +} + +void ExecutionContext::addContract(View
address, std::unique_ptr contract) { + const auto [iterator, inserted] = contracts_.emplace(address, std::move(contract)); + + if (!inserted) { + throw DynamicException("contract already exists"); + } + + addAccount(address, Account::makeCppContract()); + notifyNewContract(iterator->first, iterator->second.get()); +} + +void ExecutionContext::notifyNewContract(View
address, BaseContract* contract) { + using ContractsTuple = std::tuple>&>; + + newContracts_.emplace_back(address, contract); + + transactions_.push(transactional::AnyTransactional(transactional::BasicTransactional(contracts_, [contractAddress = Address(address)] (auto& contracts) { + contracts.erase(contractAddress); + }))); + + transactions_.push(transactional::AnyTransactional(transactional::BasicTransactional(newContracts_, [] (auto& newContracts) { + newContracts.pop_back(); + }))); +} + +void addContract(View
address, BaseContract* contract) { + // TODO: ??? + // TODO: take as std::unique_ptr and save the contract? + // TODO: analyse ContractHost and see what is done when a C++ contract is created + // TODO: remember that this function must also be called for EVM contracts (nullptr in this case) +} + +void ExecutionContext::transferBalance(View
fromAddress, View
toAddress, const uint256_t& amount) { + Account& sender = getMutableAccount(fromAddress); + Account& recipient = getMutableAccount(toAddress); + + if (sender.balance < amount) { + throw DynamicException("insufficient founds"); + } + + transactional::AnyTransactional senderTransaction(transactional::copy(sender.balance)); + transactional::AnyTransactional recipientTransaction(transactional::copy(recipient.balance)); + + sender.balance -= amount; + recipient.balance += amount; + + transactions_.push(std::move(senderTransaction)); + transactions_.push(std::move(recipientTransaction)); +} + +void ExecutionContext::incrementNonce(View
accountAddress) { + Account& account = getMutableAccount(accountAddress); + transactions_.push(transactional::AnyTransactional(transactional::copy(account.nonce))); + ++account.nonce; +} + +void ExecutionContext::store(View
addr, View slot, View data) { + transactional::AnyTransactional transaction(transactional::emplaceOrAssign(storage_, StorageKeyView(addr, slot), data)); + transactions_.push(std::move(transaction)); +} + +Hash ExecutionContext::retrieve(View
addr, View slot) const { + const auto iterator = storage_.find(StorageKeyView(addr, slot)); + return (iterator == storage_.end()) ? Hash() : iterator->second; +} + +void ExecutionContext::commit() { + while (!transactions_.empty()) { + transactions_.top().commit(); + transactions_.pop(); + } + + events_.clear(); + newContracts_.clear(); +} + +void ExecutionContext::revert() { + while (!transactions_.empty()) { + transactions_.pop(); // transactions revert on destructor (by default) + } + + events_.clear(); + newContracts_.clear(); +} + +ExecutionContext::Checkpoint ExecutionContext::checkpoint() { + return ExecutionContext::Checkpoint(transactions_); +} + +ExecutionContext::Checkpoint::Checkpoint(std::stack& transactions) + : transactions_(&transactions), checkpoint_(transactions.size()) {} + +void ExecutionContext::Checkpoint::commit() { + transactions_ = nullptr; +} + +void ExecutionContext::Checkpoint::revert() { + if (transactions_ == nullptr) + return; + + while (transactions_->size() > checkpoint_) + transactions_->pop(); + + transactions_ = nullptr; +} diff --git a/src/contract/messages/executioncontext.h b/src/contract/messages/executioncontext.h new file mode 100644 index 00000000..410ba10c --- /dev/null +++ b/src/contract/messages/executioncontext.h @@ -0,0 +1,201 @@ +#ifndef BDK_EXECUTIONCONTEXT_H +#define BDK_EXECUTIONCONTEXT_H + +#include +#include "utils/hash.h" +#include "utils/address.h" +#include "utils/utils.h" +#include "utils/transactional.h" +#include "utils/safehash.h" +#include "contract/contract.h" +#include "contract/event.h" + +class ExecutionContext { +public: + using Storage = boost::unordered_flat_map; + using Accounts = boost::unordered_flat_map, SafeHash, SafeCompare>; + using Contracts = boost::unordered_flat_map, SafeHash, SafeCompare>; + + class Checkpoint; + + class Builder; + + ExecutionContext( + Accounts& accounts, Storage& storage, Contracts& contracts, + int64_t blockGasLimit, int64_t blockNumber, int64_t blockTimestamp, int64_t txIndex, + View
blockCoinbase, View
txOrigin, View blockHash, View txHash, + const uint256_t& chainId, const uint256_t& txGasPrice) : + accounts_(accounts), storage_(storage), contracts_(contracts), newContracts_(), + blockGasLimit_(blockGasLimit), blockNumber_(blockNumber), blockTimestamp_(blockTimestamp), txIndex_(txIndex), + blockCoinbase_(blockCoinbase), txOrigin_(txOrigin), blockHash_(blockHash), txHash_(txHash), + chainId_(chainId), txGasPrice_(txGasPrice) {} + + ~ExecutionContext() { revert(); } + + ExecutionContext(const ExecutionContext&) = delete; + + ExecutionContext(ExecutionContext&&) = delete; + + ExecutionContext& operator=(const ExecutionContext&) = delete; + + ExecutionContext& operator=(ExecutionContext&&) = delete; + + View getBlockHash() const { return blockHash_; } + + View getTxHash() const { return txHash_; } + + View
getTxOrigin() const { return txOrigin_; } + + View
getBlockCoinbase() const { return blockCoinbase_; } + + int64_t getBlockGasLimit() const { return blockGasLimit_; } + + int64_t getBlockNumber() const { return blockNumber_; } + + int64_t getBlockTimestamp() const { return blockTimestamp_; } + + int64_t getTxIndex() const { return txIndex_; } + + const uint256_t& getTxGasPrice() const { return txGasPrice_; } + + const uint256_t& getChainId() const { return chainId_; } + + void addEvent(Event event); + + void addEvent(std::string_view name, View
address, const auto& args, bool anonymous) { + Event event(std::string(name), events_.size(), Hash(this->getTxHash()), this->getTxIndex(), Hash(this->getBlockHash()), + this->getBlockNumber(), Address(address), args, anonymous); + + this->addEvent(std::move(event)); + } + + void addEvent(View
address, View data, std::vector topics); + + bool accountExists(View
accountAddress) const; + + const Account& getAccount(View
accountAddress) const; + + BaseContract& getContract(View
contractAddress); + + const BaseContract& getContract(View
contractAddress) const; + + const auto& getEvents() const { return events_; } + + const auto& getNewContracts() const { return newContracts_; } + + void addAccount(View
address, Account account); + + void addContract(View
address, std::unique_ptr contract); + + void notifyNewContract(View
address, BaseContract* contract); + + void transferBalance(View
fromAddress, View
toAddress, const uint256_t& amount); + + void incrementNonce(View
accountAddress); + + void store(View
addr, View slot, View data); + + Hash retrieve(View
addr, View slot) const; + + void commit(); + + void revert(); + + Checkpoint checkpoint(); + +private: + Account& getMutableAccount(View
accountAddress); + + Accounts& accounts_; + Storage& storage_; + Contracts& contracts_; + int64_t blockGasLimit_; + int64_t blockNumber_; + int64_t blockTimestamp_; + int64_t txIndex_; + Address blockCoinbase_; + Address txOrigin_; + Hash blockHash_; + Hash txHash_; + uint256_t chainId_; + uint256_t txGasPrice_; + size_t eventIndex_ = 0; + std::vector events_; + std::vector> newContracts_; + std::stack transactions_; +}; + +class ExecutionContext::Checkpoint { +public: + explicit Checkpoint(std::stack& transactions); + + Checkpoint(const Checkpoint&) = delete; + Checkpoint(Checkpoint&&) noexcept = delete; + Checkpoint& operator=(const Checkpoint&) = delete; + Checkpoint& operator=(Checkpoint&&) noexcept = delete; + + ~Checkpoint() { revert(); } + + void commit(); + + void revert(); + +private: + std::stack* transactions_; + size_t checkpoint_; +}; + +class ExecutionContext::Builder { +public: + + Builder() {} + + Builder& storage(ExecutionContext::Storage& storage) { storage_ = &storage; return *this; } + + Builder& accounts(ExecutionContext::Accounts& accounts) { accounts_ = &accounts; return *this; } + + Builder& contracts(ExecutionContext::Contracts& contracts) { contracts_ = &contracts; return *this; } + + Builder& blockHash(View blockHash) { blockHash_ = Hash(blockHash); return *this; } + + Builder& txHash(View txHash) { txHash_ = Hash(txHash); return *this; } + + Builder& txOrigin(View
txOrigin) { txOrigin_ = Address(txOrigin); return *this; } + + Builder& blockCoinbase(View
blockCoinbase) { blockCoinbase_ = Address(blockCoinbase); return *this; } + + Builder& txIndex(int64_t txIndex) { txIndex_ = txIndex; return *this; } + + Builder& blockNumber(int64_t blockNumber) { blockNumber_ = blockNumber; return *this; } + + Builder& blockTimestamp(int64_t blockTimestamp) { blockTimestamp_ = blockTimestamp; return *this; } + + Builder& blockGasLimit(int64_t blockGasLimit) { blockGasLimit_ = blockGasLimit; return *this; } + + Builder& txGasPrice(const uint256_t& txGasPrice) { txGasPrice_ = txGasPrice; return *this; } + + Builder& chainId(const uint256_t& chainId) { chainId_ = chainId; return *this; } + + ExecutionContext build() { + return ExecutionContext( + *accounts_, *storage_, *contracts_, blockGasLimit_, blockNumber_, blockTimestamp_, txIndex_, + blockCoinbase_, txOrigin_, blockHash_, txHash_, chainId_, txGasPrice_); + } + +private: + ExecutionContext::Accounts* accounts_; + ExecutionContext::Storage* storage_; + ExecutionContext::Contracts* contracts_; + int64_t blockGasLimit_; + int64_t blockNumber_; + int64_t blockTimestamp_; + int64_t txIndex_; + Address blockCoinbase_; + Address txOrigin_; + Hash blockHash_; + Hash txHash_; + uint256_t chainId_; + uint256_t txGasPrice_; +}; + +#endif // BDK_EXECUTIONCONTEXT_H diff --git a/src/contract/messages/executionreverted.h b/src/contract/messages/executionreverted.h new file mode 100644 index 00000000..05fce1e7 --- /dev/null +++ b/src/contract/messages/executionreverted.h @@ -0,0 +1,15 @@ +#ifndef BDK_MESSAGES_EXECUTIONREVERTED_H +#define BDK_MESSAGES_EXECUTIONREVERTED_H + +#include "utils/dynamicexception.h" + +namespace messages { + +struct ExecutionReverted : std::runtime_error { + explicit ExecutionReverted(std::string_view msg) + : std::runtime_error(str.data()) {} +}; + +} // namespace messages + +#endif // BDK_MESSAGES_EXECUTIONREVERTED_H diff --git a/src/contract/messages/gas.h b/src/contract/messages/gas.h new file mode 100644 index 00000000..ccf8711b --- /dev/null +++ b/src/contract/messages/gas.h @@ -0,0 +1,33 @@ +#ifndef BDK_MESSAGES_GAS_H +#define BDK_MESSAGES_GAS_H + +#include "outofgas.h" + +namespace messages { + +class Gas { +public: + constexpr explicit Gas(uint64_t value = 0) : value_(value) {} + + constexpr uint64_t value() const { return value_; } + + constexpr void use(uint64_t amount) { + if (amount > value_) { + value_ = 0; + throw OutOfGas(); + } + + value_ -= amount; + } + + constexpr void refund(uint64_t amount) { + value_ += amount; + } + +private: + uint64_t value_; +}; + +} // namespace messages + +#endif // BDK_MESSAGES_GAS_H diff --git a/src/contract/messages/messagedispatcher.h b/src/contract/messages/messagedispatcher.h new file mode 100644 index 00000000..d86219aa --- /dev/null +++ b/src/contract/messages/messagedispatcher.h @@ -0,0 +1,54 @@ +#ifndef BDK_MESSAGES_MESSAGEDISPATCHER_H +#define BDK_MESSAGES_MESSAGEDISPATCHER_H + +#include "common.h" +#include "utils/utils.h" +#include "concepts.h" +#include "executioncontext.h" +#include "evmcontractexecutor.h" +#include "cppcontractexecutor.h" + +class MessageDispatcher { +public: + MessageDispatcher(ExecutionContext& context, CppContractExecutor cppExecutor, EvmContractExecutor evmExecutor) + : context_(context), cppExecutor_(std::move(cppExecutor)), evmExecutor_(std::move(evmExecutor)) {} + + template + decltype(auto) onMessage(M&& msg) { + const Account& account = context_.getAccount(messageCodeAddress(msg)); + + if (!account.isContract()) { + throw DynamicException("Not a contract address"); + } + + if (account.contractType == ContractType::CPP) { + return cppExecutor_.execute(std::forward(msg)); + } else { + return evmExecutor_.execute(std::forward(msg)); + } + } + + template + Address onMessage(M&& msg) { + const Address result = std::invoke([&] () { + if constexpr (concepts::EncodedMessage) { + return evmExecutor_.execute(std::forward(msg)); + } else { + return cppExecutor_.execute(std::forward(msg)); + } + }); + + return result; + } + + CppContractExecutor& cppExecutor() { return cppExecutor_; } + + EvmContractExecutor& evmExecutor() { return evmExecutor_; } + +private: + ExecutionContext& context_; + CppContractExecutor cppExecutor_; + EvmContractExecutor evmExecutor_; +}; + +#endif // BDK_MESSAGES_MESSAGEDISPATCHER_H diff --git a/src/contract/messages/outofgas.h b/src/contract/messages/outofgas.h new file mode 100644 index 00000000..69b82de5 --- /dev/null +++ b/src/contract/messages/outofgas.h @@ -0,0 +1,8 @@ +#ifndef BDK_MESSAGES_OUTOFGAS_H +#define BDK_MESSAGES_OUTOFGAS_H + +struct OutOfGas : std::runtime_error { + OutOfGas() : std::runtime_error("Out of gas") {} +}; + +#endif // BDK_MESSAGES_OUTOFGAS_H diff --git a/src/contract/messages/packedmessages.h b/src/contract/messages/packedmessages.h new file mode 100644 index 00000000..c903a066 --- /dev/null +++ b/src/contract/messages/packedmessages.h @@ -0,0 +1,38 @@ +#ifndef BDK_PACKEDMESSAGES_H +#define BDK_PACKEDMESSAGES_H + +#include "contract/messages/concepts.h" +#include "contract/messages/basemessage.h" + +template +struct PackedCallMessage : BaseMessage< + FromField, + ToField, + GasField, + ValueField, + CallerField, + MethodField, + ArgsField> { + + using BaseMessage, ArgsField>::BaseMessage; +}; + +template +struct PackedStaticCallMessage : BaseMessage< + FromField, + ToField, + GasField, + CallerField, + MethodField, + ArgsField> { + + using BaseMessage, ArgsField>::BaseMessage; +}; + +template +struct PackedCreateMessage : BaseMessage> { + using BaseMessage>::BaseMessage; + using ContractType = C; +}; + +#endif // BDK_PACKEDMESSAGES_H diff --git a/src/contract/messages/traits.h b/src/contract/messages/traits.h new file mode 100644 index 00000000..eaa2b7bb --- /dev/null +++ b/src/contract/messages/traits.h @@ -0,0 +1,78 @@ +#ifndef BDK_MESSAGES_TRAITS_H +#define BDK_MESSAGES_TRAITS_H + +#include +#include "concepts.h" + +namespace traits { + +template +struct Methods {}; + +template +struct Methods { + using Return = R; + using Class = C; + static constexpr bool IS_VIEW = false; +}; + +template +struct Methods { + using Return = R; + using Class = C; + static constexpr bool IS_VIEW = true; +}; + +template +using MethodReturn = typename Methods>::Return; + +template +using MethodClass = typename Methods>::Class; + +template +constexpr bool IsViewMethod = Methods>::IS_VIEW; + +template +struct MessageResultHelper; + +template + requires concepts::PackedMessage +struct MessageResultHelper { + using Type = MethodReturn().method())>; +}; + +template + requires concepts::EncodedMessage +struct MessageResultHelper { + using Type = Bytes; +}; + +template +struct MessageResultHelper { + using Type = Address; +}; + +template +using MessageResult = typename MessageResultHelper::Type; + +template +struct MessageContractHelper; + +template + requires (concepts::PackedMessage) +struct MessageContractHelper { + using Type = MethodClass().method())>; +}; + +template + requires (concepts::PackedMessage) +struct MessageContractHelper { + using Type = typename std::remove_cvref_t::ContractType; +}; + +template +using MessageContract = MessageContractHelper::Type; + +} // namespace traits + +#endif // BDK_MESSAGES_TRAITS_H diff --git a/src/core/state.cpp b/src/core/state.cpp index e3176936..1f28d0e1 100644 --- a/src/core/state.cpp +++ b/src/core/state.cpp @@ -212,53 +212,45 @@ void State::processTransaction( throw DynamicException("Transaction nonce mismatch"); return; } + + messages::Gas gas(static_cast(tx.getGasLimit())); + try { - evmc_tx_context txContext; - txContext.tx_gas_price = Utils::uint256ToEvmcUint256(tx.getMaxFeePerGas()); - txContext.tx_origin = bytes::cast(tx.getFrom()); - txContext.block_coinbase = bytes::cast(ContractGlobals::getCoinbase()); - txContext.block_number = ContractGlobals::getBlockHeight(); - txContext.block_timestamp = ContractGlobals::getBlockTimestamp(); - txContext.block_gas_limit = 10000000; - txContext.block_prev_randao = {}; - txContext.chain_id = Utils::uint256ToEvmcUint256(this->options_.getChainID()); - txContext.block_base_fee = {}; - txContext.blob_base_fee = {}; - txContext.blob_hashes = nullptr; - txContext.blob_hashes_count = 0; - Hash randomSeed(Utils::uint256ToBytes((static_cast(randomnessHash) + txIndex))); + const Hash randomSeed(Utils::uint256ToBytes((static_cast(randomnessHash) + txIndex))); + + ExecutionContext context = ExecutionContext::Builder{} + .storage(this->vmStorage_) + .accounts(this->accounts_) + .contracts(this->contracts_) + .blockHash(blockHash) + .txHash(tx.hash()) + .txOrigin(tx.getFrom()) + .blockCoinbase(ContractGlobals::getCoinbase()) + .txIndex(txIndex) + .blockNumber(ContractGlobals::getBlockHeight()) + .blockTimestamp(ContractGlobals::getBlockTimestamp()) + .blockGasLimit(10'000'000) + .txGasPrice(tx.getMaxFeePerGas()) + .chainId(this->options_.getChainID()) + .build(); + ContractHost host( this->vm_, this->dumpManager_, this->storage_, randomSeed, - txContext, - this->contracts_, - this->accounts_, - this->vmStorage_, - tx.hash(), - txIndex, - blockHash, - leftOverGas - ); + context); - host.execute(tx.txToMessage(), accountTo.contractType); + std::visit([&host] (auto&& msg) { + host.execute(std::forward(msg)); + }, tx.toMessage(gas)); - // if (tx.getTo() == Address()) { - // CreateMessage msg{ tx.getTo(), tx.getFrom(), tx.getValue(), 0, tx.getData() }; - // host.execute(msg); - // } else { - // host.execute(tx.txToMessage(), accountTo.contractType); - // } - - } catch (std::exception& e) { + } catch (const std::exception& e) { LOGERRORP("Transaction: " + tx.hash().hex().get() + " failed to process, reason: " + e.what()); } - // if (leftOverGas < 0) { - // leftOverGas = 0; // We don't want to """refund""" gas due to negative gas - // } + ++fromNonce; - auto usedGas = tx.getGasLimit() - leftOverGas; + auto usedGas = tx.getGasLimit() - gas.value(); fromBalance -= (usedGas * tx.getMaxFeePerGas()); } @@ -446,84 +438,72 @@ void State::addBalance(const Address& addr) { this->accounts_[addr]->balance += uint256_t("1000000000000000000000"); } -Bytes State::ethCall(const evmc_message& callInfo) { +Bytes State::ethCall(EncodedStaticCallMessage& msg) { // We actually need to lock uniquely here // As the contract host will modify (reverting in the end) the state. std::unique_lock lock(this->stateMutex_); - const auto recipient(callInfo.recipient); - const auto& accIt = this->accounts_.find(Address(recipient)); + const auto& accIt = this->accounts_.find(msg.to()); if (accIt == this->accounts_.end()) { return {}; } const auto& acc = accIt->second; if (acc->isContract()) { - int64_t leftoverGas = callInfo.gas; - evmc_tx_context txContext; - txContext.tx_gas_price = {}; - txContext.tx_origin = callInfo.sender; - txContext.block_coinbase = bytes::cast(ContractGlobals::getCoinbase()); - txContext.block_number = static_cast(ContractGlobals::getBlockHeight()); - txContext.block_timestamp = static_cast(ContractGlobals::getBlockTimestamp()); - txContext.block_gas_limit = 10000000; - txContext.block_prev_randao = {}; - txContext.chain_id = Utils::uint256ToEvmcUint256(this->options_.getChainID()); - txContext.block_base_fee = {}; - txContext.blob_base_fee = {}; - txContext.blob_hashes = nullptr; - txContext.blob_hashes_count = 0; + ExecutionContext context = ExecutionContext::Builder{} + .storage(this->vmStorage_) + .accounts(this->accounts_) + .contracts(this->contracts_) + .blockHash(Hash()) + .txHash(Hash()) + .txOrigin(msg.from()) + .blockCoinbase(ContractGlobals::getCoinbase()) + .txIndex(0) + .blockNumber(ContractGlobals::getBlockHeight()) + .blockTimestamp(ContractGlobals::getBlockTimestamp()) + .blockGasLimit(10'000'000) + .txGasPrice(0) + .chainId(this->options_.getChainID()) + .build(); + // As we are simulating, the randomSeed can be anything - Hash randomSeed = bytes::random(); + const Hash randomSeed = bytes::random(); + return ContractHost( this->vm_, this->dumpManager_, this->storage_, randomSeed, - txContext, - this->contracts_, - this->accounts_, - this->vmStorage_, - Hash(), - 0, - Hash(), - leftoverGas - ).ethCallView(callInfo, acc->contractType); + context + ).execute(msg); } else { return {}; } } -int64_t State::estimateGas(const evmc_message& callInfo) { +int64_t State::estimateGas(EncodedMessageVariant msg) { std::unique_lock lock(this->stateMutex_); - const Address to(callInfo.recipient); - // ContractHost simulate already do all necessary checks - // We just need to execute and get the leftOverGas - ContractType type = ContractType::NOT_A_CONTRACT; - if (auto accIt = this->accounts_.find(to); accIt != this->accounts_.end()) { - type = accIt->second->contractType; - } - int64_t leftoverGas = callInfo.gas; - Hash randomSeed = bytes::random(); - ContractHost( + ExecutionContext context = ExecutionContext::Builder{} + .storage(this->vmStorage_) + .accounts(this->accounts_) + .contracts(this->contracts_) + .build(); + + const Hash randomSeed = bytes::random(); + + ContractHost host( this->vm_, this->dumpManager_, this->storage_, randomSeed, - evmc_tx_context(), - this->contracts_, - this->accounts_, - this->vmStorage_, - Hash(), - 0, - Hash(), - leftoverGas - ).simulate(callInfo, type); - // return gasLeft.value(); - auto used = callInfo.gas - leftoverGas; - if (used < 0) { - used = 0; - } - return used; + context + ); + + return std::visit([&host] (auto&& msg) { + const messages::Gas& gas = msg.gas(); + const uint64_t initialGas = gas.value(); + host.simulate(std::forward(msg)); + return initialGas - gas.value(); + }, std::move(msg)); } std::vector> State::getCppContracts() const { diff --git a/src/core/state.h b/src/core/state.h index fe531492..6a8351ca 100644 --- a/src/core/state.h +++ b/src/core/state.h @@ -38,9 +38,9 @@ class State : public Dumpable, public Log::LogicalLocationProvider { DumpWorker dumpWorker_; ///< Dump Manager object P2P::ManagerNormal& p2pManager_; ///< Reference to the P2P connection manager. rdPoS rdpos_; ///< rdPoS object (consensus). - boost::unordered_flat_map, SafeHash> contracts_; ///< Map with information about blockchain contracts (Address -> Contract). + boost::unordered_flat_map, SafeHash, SafeCompare> contracts_; ///< Map with information about blockchain contracts (Address -> Contract). boost::unordered_flat_map vmStorage_; ///< Map with the storage of the EVM. - boost::unordered_flat_map, SafeHash> accounts_; ///< Map with information about blockchain accounts (Address -> Account). + boost::unordered_flat_map, SafeHash, SafeCompare> accounts_; ///< Map with information about blockchain accounts (Address -> Account). boost::unordered_flat_map mempool_; ///< TxBlock mempool. /** @@ -240,7 +240,7 @@ class State : public Dumpable, public Log::LogicalLocationProvider { * @param callInfo Tuple with info about the call (from, to, gasLimit, gasPrice, value, data). * @return The return of the called function as a data string. */ - Bytes ethCall(const evmc_message& callInfo); + Bytes ethCall(EncodedStaticCallMessage& msg); /** * Estimate gas for callInfo in RPC. @@ -248,7 +248,7 @@ class State : public Dumpable, public Log::LogicalLocationProvider { * @param callInfo Tuple with info about the call (from, to, gasLimit, gasPrice, value, data). * @return The used gas limit of the transaction. */ - int64_t estimateGas(const evmc_message& callInfo); + int64_t estimateGas(EncodedMessageVariant msg); /// Get a list of the C++ contract addresses and names. std::vector> getCppContracts() const; diff --git a/src/net/http/jsonrpc/methods.cpp b/src/net/http/jsonrpc/methods.cpp index edf6be07..06c45f36 100644 --- a/src/net/http/jsonrpc/methods.cpp +++ b/src/net/http/jsonrpc/methods.cpp @@ -95,41 +95,30 @@ static json getBlockJson(const FinalizedBlock *block, bool includeTransactions) return ret; } -static std::pair parseEvmcMessage(const json& request, const Storage& storage, bool recipientRequired) { - std::pair res{}; +static std::tuple parseMessage(const json& request, const Storage& storage, bool recipientRequired) { + std::tuple result; - Bytes& buffer = res.first; - evmc_message& msg = res.second; + auto& [from, to, gas, value, data] = result; const auto [txJson, optionalBlockNumber] = parseAllParams>(request); - if (optionalBlockNumber.has_value() && !optionalBlockNumber->isLatest(storage)) + if (optionalBlockNumber.has_value() && !optionalBlockNumber->isLatest(storage)) { throw Error(-32601, "Only latest block is supported"); + } - msg.sender = parseIfExists
(txJson, "from") - .transform([] (const Address& addr) { return bytes::cast(addr); }) - .value_or(evmc::address{}); - - if (recipientRequired) - msg.recipient = bytes::cast(parse
(txJson.at("to"))); - else - msg.recipient = parseIfExists
(txJson, "to") - .transform([] (const Address& addr) { return bytes::cast(addr); }) - .value_or(evmc::address{}); + from = parseIfExists
(txJson, "from").value_or(Address{}); - msg.gas = parseIfExists(txJson, "gas").value_or(10000000); - parseIfExists(txJson, "gasPrice"); // gas price ignored as chain is fixed at 1 GWEI + to = recipientRequired + ? parse
(txJson.at("to")) + : parseIfExists
(txJson, "to").value_or(Address{}); - msg.value = parseIfExists(txJson, "value") - .transform([] (uint64_t val) { return Utils::uint256ToEvmcUint256(uint256_t(val)); }) - .value_or(evmc::uint256be{}); + gas = messages::Gas(parseIfExists(txJson, "gas").value_or(10000000)); - buffer = parseIfExists(txJson, "data").value_or(Bytes{}); + value = uint256_t(parseIfExists(txJson, "value").value_or(0)); - msg.input_size = buffer.size(); - msg.input_data = buffer.empty() ? nullptr : buffer.data(); + data = parseIfExists(txJson, "data").value_or(Bytes{}); - return res; + return result; } json web3_clientVersion(const json& request, const Options& options) { @@ -218,25 +207,25 @@ json eth_blockNumber(const json& request, const Storage& storage) { } json eth_call(const json& request, const Storage& storage, State& state) { - auto [buffer, callInfo] = parseEvmcMessage(request, storage, true); - callInfo.kind = EVMC_CALL; - callInfo.flags = 0; - callInfo.depth = 0; - return Hex::fromBytes(state.ethCall(callInfo), true); + auto [from, to, gas, value, data] = parseMessage(request, storage, true); + EncodedStaticCallMessage msg(from, to, gas, data); + return Hex::fromBytes(state.ethCall(msg)); } json eth_estimateGas(const json& request, const Storage& storage, State& state) { - auto [buffer, callInfo] = parseEvmcMessage(request, storage, false); - callInfo.flags = 0; - callInfo.depth = 0; + auto [from, to, gas, value, data] = parseMessage(request, storage, false); - // TODO: "kind" is uninitialized if recipient is not zeroes - if (evmc::is_zero(callInfo.recipient)) - callInfo.kind = EVMC_CREATE; + uint64_t gasUsed; - const auto usedGas = state.estimateGas(callInfo); + if (to == Address()) { + EncodedCreateMessage msg(from, gas, value, data); + gasUsed = state.estimateGas(msg); + } else { + EncodedCallMessage msg(from, to, gas, value, data); + gasUsed = state.estimateGas(msg); + } - return Hex::fromBytes(Utils::uintToBytes(static_cast(usedGas)), true).forRPC(); + return Hex::fromBytes(Utils::uintToBytes(static_cast(gasUsed)), true).forRPC(); } json eth_gasPrice(const json& request) { diff --git a/src/utils/transactional.h b/src/utils/transactional.h new file mode 100644 index 00000000..b989c7ed --- /dev/null +++ b/src/utils/transactional.h @@ -0,0 +1,150 @@ +#ifndef BDK_TRANSACTIONAL_H +#define BDK_TRANSACTIONAL_H + +#include +#include + +namespace transactional { + +template Revert> +class BasicTransactional { +public: + BasicTransactional(T& target, Revert revert) + : target_(&target), revert_(std::move(revert)) {} + + ~BasicTransactional() { revert(); } + + BasicTransactional(const BasicTransactional&) = delete; + + BasicTransactional(BasicTransactional&& other) noexcept + : target_(other.target_), revert_(std::move(other.revert_)) { + other.target_ = nullptr; + } + + BasicTransactional& operator=(const BasicTransactional&) = delete; + + BasicTransactional& operator=(BasicTransactional&& other) noexcept { + using std::swap; + swap(target_, other.target_); + swap(revert_, other.revert_); + return *this; + } + + void commit() { target_ = nullptr; } + + void revert() { + if (target_ == nullptr) + return; + + std::invoke(revert_, *target_); + target_ = nullptr; + } + +private: + T *target_; + Revert revert_; +}; + +class AnyTransactional { +public: + template + explicit AnyTransactional(T transactional) : impl_(std::make_unique>(std::move(transactional))) {} + + void commit() { impl_->commit(); } + + void revert() { impl_->revert(); } + +private: + struct Base { + virtual ~Base() = default; + virtual void commit() = 0; + virtual void revert() = 0; + }; + + template + struct Derived : Base { + explicit Derived(T impl) : impl_(std::move(impl)) {} + void commit() override { impl_.commit(); } + void revert() override { impl_.revert(); } + T impl_; + }; + + std::unique_ptr impl_; +}; + +template +class Group { +public: + constexpr Group(Ts... transactions) : transactions_(std::forward(transactions)...) {} + + constexpr ~Group() { revert(); } + + constexpr void commit() { + std::invoke([this] (std::index_sequence) { + (void) std::initializer_list({(std::get(transactions_).commit(), 0)...}); + }, std::make_index_sequence{}); + } + + constexpr void revert() { + std::invoke([this] (std::index_sequence) { + (void) std::initializer_list({(std::get(transactions_).revert(), 0)...}); + }, std::make_index_sequence{}); + } + +private: + std::tuple transactions_; +}; + +template +concept Checkpointable = requires (T t) { t.checkpoint(); }; + +auto checkpoint(auto& any) { + return BasicTransactional(any, [any] (auto& ref) mutable { ref = std::move(any); }); +} + +auto checkpoint(Checkpointable auto& checkpointable) { + return checkpointable.checkpoint(); +} + +auto copy(auto& target) { + return BasicTransactional(target, [target] (auto& ref) mutable { ref = std::move(target); }); +} + +auto emplace(auto& container, auto&&... args) { + auto [iterator, inserted] = container.emplace(std::forward(args)...); + + return std::make_tuple(BasicTransactional(container, [key = iterator->first, inserted] (auto& container) { + if (inserted) { + container.erase(key); + } + }), inserted); +} + +auto emplaceOrAssign(auto& container, const auto& key, auto&&... args) { + auto [iterator, inserted] = container.try_emplace(key, std::forward(args)...); + + using Mapped = std::remove_cvref_tsecond)>; + std::optional previousValue; + + if (!inserted) { + previousValue = std::move(iterator->second); + iterator->second = Mapped(std::forward(args)...); + } + + return BasicTransactional(container, [key = iterator->first, mapped = std::move(previousValue)] (auto& container) { + if (mapped.has_value()) { + container.at(key) = std::move(mapped).value(); + } else { + container.erase(key); + } + }); +} + +auto emplaceBack(auto& container, auto&&... args) { + container.emplace_back(std::forward(args)...); + return BasicTransactional(container, [] (auto& container) { container.pop_back(); }); +} + +} // namespace transactional + +#endif // BDK_TRANSACTIONAL_H diff --git a/src/utils/tx.cpp b/src/utils/tx.cpp index 072f34ce..9be95aa4 100644 --- a/src/utils/tx.cpp +++ b/src/utils/tx.cpp @@ -489,6 +489,14 @@ evmc_message TxBlock::txToMessage() const { return msg; } +EncodedMessageVariant TxBlock::toMessage(messages::Gas& gas) const { + if (this->to_ == Address()) { + return EncodedCreateMessage(this->from_, gas, this->value_, this->data_); + } else { + return EncodedCallMessage(this->from_, this->to_, gas, this->value_, this->data_); + } +} + TxValidator::TxValidator(const View bytes, const uint64_t&) { uint64_t index = 0; diff --git a/src/utils/tx.h b/src/utils/tx.h index c8a00386..4ddf90b7 100644 --- a/src/utils/tx.h +++ b/src/utils/tx.h @@ -9,6 +9,7 @@ See the LICENSE.txt file in the project root for more information. #define TX_H #include "../bytes/view.h" +#include "contract/messages/encodedmessages.h" #include "ecdsa.h" #include "strings.h" @@ -165,6 +166,8 @@ class TxBlock { */ evmc_message txToMessage() const; + EncodedMessageVariant toMessage(messages::Gas& gas) const; + /// Copy assignment operator. TxBlock& operator=(const TxBlock& other) { this->to_ = other.to_; diff --git a/src/utils/utils.h b/src/utils/utils.h index cfafe0c9..dfc0d4a3 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -267,6 +267,14 @@ struct Account { Bytes code = Bytes(); ///< Account code (if any) ContractType contractType = ContractType::NOT_A_CONTRACT; ///< Account contract type. + static Account makeCppContract(const uint256_t& balance = 0) { + Account account; + account.balance = balance; + account.nonce = 1; + account.contractType = ContractType::CPP; + return account; + } + /// Default constructor. Account() = default; @@ -345,7 +353,7 @@ template struct EventParam { /// Namespace for utility functions. namespace Utils { - template + template struct Overloaded : Ts... { using Ts::operator()...; }; std::string getTestDumpPath(); ///< Get the path to the test dump folder. diff --git a/src/utils/view.h b/src/utils/view.h index b7087c53..13da3387 100644 --- a/src/utils/view.h +++ b/src/utils/view.h @@ -51,6 +51,10 @@ struct View : std::span { */ template S> constexpr View(I begin, S end) : span(begin, end) {} + + explicit operator Bytes() const { + return Bytes(begin(), end()); + } }; /** diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9cc4419f..deb23f0c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -34,6 +34,8 @@ set (TESTS_SOURCES ${CMAKE_SOURCE_DIR}/tests/contract/calltracer.cpp ${CMAKE_SOURCE_DIR}/tests/contract/createcontract.cpp ${CMAKE_SOURCE_DIR}/tests/contract/pebble.cpp + ${CMAKE_SOURCE_DIR}/tests/contract/messages/executioncontext.cpp + ${CMAKE_SOURCE_DIR}/tests/contract/messages/evmcontractexecutor.cpp ${CMAKE_SOURCE_DIR}/tests/contract/variables/safeuint_t.cpp ${CMAKE_SOURCE_DIR}/tests/contract/variables/safeint_t.cpp ${CMAKE_SOURCE_DIR}/tests/contract/variables/safestring.cpp diff --git a/tests/contract/createcontract.cpp b/tests/contract/createcontract.cpp index 42050f76..2d66c041 100644 --- a/tests/contract/createcontract.cpp +++ b/tests/contract/createcontract.cpp @@ -111,9 +111,9 @@ namespace TContractRandomness { } REQUIRE(sdk.getState().getEvmContracts().size() == 3); REQUIRE(sdk.getNativeNonce(createContractAddress) == 2); // 1 when contract was created + 1 when contract called CREATE, CREATE2 does **NOT** increment nonce - REQUIRE(newContractAddress == ContractHost::deriveContractAddress(1, createContractAddress)); + REQUIRE(newContractAddress == generateContractAddress(1, createContractAddress)); Bytes init_code = Hex::toBytes("6080604052348015600e575f80fd5b506040516101493803806101498339818101604052810190602e9190606b565b805f81905550506091565b5f80fd5b5f819050919050565b604d81603d565b81146056575f80fd5b50565b5f815190506065816046565b92915050565b5f60208284031215607d57607c6039565b5b5f6088848285016059565b91505092915050565b60ac8061009d5f395ff3fe6080604052348015600e575f80fd5b50600436106026575f3560e01c80633fa4f24514602a575b5f80fd5b60306044565b604051603b9190605f565b60405180910390f35b5f5481565b5f819050919050565b6059816049565b82525050565b5f60208201905060705f8301846052565b9291505056fea2646970667358221220800668e87144b8625a7e59ac82528e013d51d6ed08562ba8b641f0a2e66c0f3764736f6c634300081a00330000000000000000000000000000000000000000000000000000000000000064"); - REQUIRE(newContractAddressCreate2 == ContractHost::deriveContractAddress(createContractAddress, salt, init_code)); + REQUIRE(newContractAddressCreate2 == generateContractAddress(createContractAddress, salt, init_code)); } } } diff --git a/tests/contract/evm.cpp b/tests/contract/evm.cpp index fe5704fa..a4fac915 100644 --- a/tests/contract/evm.cpp +++ b/tests/contract/evm.cpp @@ -199,7 +199,7 @@ namespace TEVM { REQUIRE (sdk.callViewFunction(erc20Address, &ERC20::balanceOf, sdk.getChainOwnerAccount().address) == uint256_t("10000000000000000000000")); REQUIRE (sdk.callViewFunction(erc20Address, &ERC20::balanceOf, erc20WrapperAddress) == uint256_t("0")); - auto deposit = sdk.callFunction(erc20WrapperAddress, &SolERC20Wrapper::deposit, erc20Address, uint256_t("10000000000000000000000")); // TODO: THIS ONE ACTUALLY + auto deposit = sdk.callFunction(erc20WrapperAddress, &SolERC20Wrapper::deposit, erc20Address, uint256_t("10000000000000000000000")); REQUIRE(sdk.callViewFunction(erc20WrapperAddress, &SolERC20Wrapper::balanceOf, sdk.getChainOwnerAccount().address, erc20Address) == uint256_t("10000000000000000000000")); REQUIRE(sdk.callViewFunction(erc20WrapperAddress, &SolERC20Wrapper::contractBalance, erc20Address) == uint256_t("10000000000000000000000")); REQUIRE(sdk.callViewFunction(erc20Address, &ERC20::balanceOf, sdk.getChainOwnerAccount().address) == uint256_t("0")); diff --git a/tests/contract/messages/evmcontractexecutor.cpp b/tests/contract/messages/evmcontractexecutor.cpp new file mode 100644 index 00000000..42634191 --- /dev/null +++ b/tests/contract/messages/evmcontractexecutor.cpp @@ -0,0 +1,182 @@ +/* +Copyright (c) [2023-2024] [AppLayer Developers] + +This software is distributed under the MIT License. +See the LICENSE.txt file in the project root for more information. +*/ + +#include "evmone/evmone.h" +#include "catch2/catch_amalgamated.hpp" +#include "contract/messages/evmcontractexecutor.h" +#include "bytes/random.h" +#include "bytes/hex.h" +#include "bytes/cast.h" +#include "utils/utils.h" + +/** + * Test cases + * + * 1. Simple function call, run some math code, whatever + * 2. Nested function call + * 3. Call with value + * 4. Out of gas call? + * 5. Delegate Call + * 6. Attempt to make mutable call from static call (error expected) + * 7. Any other error call? + * + * 8... Corner cases? + */ + +constexpr Address ORIGIN_ADDRESS = bytes::hex("0x00dead00665771855a34155f5e7405489df2c3c6"); +constexpr FixedBytes<585> SIMPLE_CONTRACT_BYTECODE = bytes::hex("0x608060405234801561001057600080fd5b50610229806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806306661abd1461003b57806311a1d3e814610059575b600080fd5b610043610089565b60405161005091906100d5565b60405180910390f35b610073600480360381019061006e9190610121565b610092565b60405161008091906100d5565b60405180910390f35b60008054905090565b600060016000546100a3919061017d565b60008190555081826100b591906101b1565b9050919050565b6000819050919050565b6100cf816100bc565b82525050565b60006020820190506100ea60008301846100c6565b92915050565b600080fd5b6100fe816100bc565b811461010957600080fd5b50565b60008135905061011b816100f5565b92915050565b600060208284031215610137576101366100f0565b5b60006101458482850161010c565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610188826100bc565b9150610193836100bc565b92508282019050808211156101ab576101aa61014e565b5b92915050565b60006101bc826100bc565b91506101c7836100bc565b92508282026101d5816100bc565b915082820484148315176101ec576101eb61014e565b5b509291505056fea26469706673582212200803e42b1acf7991b8d0f02907360f1e72660bcb1a223e3e5b29bdcf8b283b2664736f6c63430008130033"); +constexpr FixedBytes<477> FUNDS_CONTRACT_BYTECODE = bytes::hex("0x608060405234801561001057600080fd5b506101bd806100206000396000f3fe6080604052600436106100295760003560e01c8063d0e30db01461002e578063f3fef3a314610038575b600080fd5b610036610061565b005b34801561004457600080fd5b5061005f600480360381019061005a9190610147565b610063565b005b565b8173ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501580156100a9573d6000803e3d6000fd5b505050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006100de826100b3565b9050919050565b6100ee816100d3565b81146100f957600080fd5b50565b60008135905061010b816100e5565b92915050565b6000819050919050565b61012481610111565b811461012f57600080fd5b50565b6000813590506101418161011b565b92915050565b6000806040838503121561015e5761015d6100ae565b5b600061016c858286016100fc565b925050602061017d85828601610132565b915050925092905056fea2646970667358221220f033d70d7f542aeddd138086a36a545f06b35e7a56f135b907fa8a98160682b064736f6c63430008130033"); +const uint256_t ONE_ETHER{"1000000000000000000"}; +constexpr Hash EMPTY_HASH{}; + +struct NoOpObserver { + constexpr void operator()(auto&&) const {} +}; + +template +struct MockedEnvironment { + MockedEnvironment(Observer observer) + : vm(evmc_create_evmone(), evmc_destroy), storage(), accounts(), context(ExecutionContext::Builder().storage(storage).accounts(accounts).build()), + executor(AnyEncodedMessageHandler::from(*this), context, vm.get()) {} + + MockedEnvironment() : MockedEnvironment(NoOpObserver{}) {} + + ~MockedEnvironment() = default; + MockedEnvironment(const MockedEnvironment&) = delete; + MockedEnvironment(MockedEnvironment&&) = delete; + MockedEnvironment& operator=(const MockedEnvironment&) = delete; + MockedEnvironment& operator=(MockedEnvironment&&) = delete; + + Bytes onMessage(concepts::CallMessage auto&& msg) { + std::invoke(observer_, msg); + return executor.execute(std::forward(msg)); + } + + Address onMessage(concepts::CreateMessage auto&& msg) { + std::invoke(observer_, msg); + return executor.execute(std::forward(msg)); + } + + std::unique_ptr vm; + ExecutionContext::Storage storage; + ExecutionContext::Accounts accounts; + ExecutionContext context; + EvmContractExecutor executor; + Observer observer_; +}; + +MockedEnvironment() -> MockedEnvironment; + +TEST_CASE("Evm Message Executor Tests", "[evmcontractexecutor]") { + SECTION("Simple calls") { + MockedEnvironment env; + + env.accounts.emplace(ORIGIN_ADDRESS, Account(1000000, 0)); + + messages::Gas gas(1000000); + uint256_t value = 0; + const Bytes getCountInput = Utils::makeBytes(bytes::hex("0x06661abd")); + const Bytes getSquaredInput = Utils::makeBytes(bytes::hex("0x11a1d3e80000000000000000000000000000000000000000000000000000000000000019")); + const Address expectedContractAddress = bytes::hex("0x5b41cef7f46a4a147e31150c3c5ffd077e54d0e1"); + + EncodedCreateMessage createMessage(ORIGIN_ADDRESS, gas, value, SIMPLE_CONTRACT_BYTECODE); + EncodedStaticCallMessage getCountMessage(ORIGIN_ADDRESS, expectedContractAddress, gas, getCountInput); + EncodedCallMessage getSquaredMessage(ORIGIN_ADDRESS, expectedContractAddress, gas, value, getSquaredInput); + + const Address contractAddress = env.executor.execute(createMessage); + REQUIRE(contractAddress == expectedContractAddress); + + uint256_t count = std::get<0>(ABI::Decoder::decodeData(env.executor.execute(getCountMessage))); + REQUIRE(count == uint256_t(0)); + + uint256_t squared = std::get<0>(ABI::Decoder::decodeData(env.executor.execute(getSquaredMessage))); + REQUIRE(squared == uint256_t(625)); + + count = std::get<0>(ABI::Decoder::decodeData(env.executor.execute(getCountMessage))); + REQUIRE(count == uint256_t(1)); + + value = 200; + REQUIRE_THROWS(env.executor.execute(getSquaredMessage)); + } + + SECTION("Calls with value") { + MockedEnvironment env([] (const auto& msg) { + printf("foo!\n"); + }); + + const Address firstAccountAddress = bytes::hex("0x05ed5b0cec75408bb57ab2a5413b9d6b0d6c756f"); + const Address secondAccountAddress = bytes::hex("0xf11abf1f64dbcc256599b57e82f8d6654b0aba40"); + + Account& firstAccount = *env.accounts.emplace(firstAccountAddress, Account(ONE_ETHER, 1)).first->second; + Account& secondAccount = *env.accounts.emplace(secondAccountAddress, Account(0, 1)).first->second; + + const Bytes fundsBytecode = Utils::makeBytes(bytes::hex("0x608060405234801561001057600080fd5b50610225806100206000396000f3fe6080604052600436106100345760003560e01c8063b69ef8a814610039578063d0e30db014610064578063f3fef3a31461006e575b600080fd5b34801561004557600080fd5b5061004e610097565b60405161005b9190610105565b60405180910390f35b61006c61009f565b005b34801561007a57600080fd5b50610095600480360381019061009091906101af565b6100a1565b005b600047905090565b565b8173ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501580156100e7573d6000803e3d6000fd5b505050565b6000819050919050565b6100ff816100ec565b82525050565b600060208201905061011a60008301846100f6565b92915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061015082610125565b9050919050565b61016081610145565b811461016b57600080fd5b50565b60008135905061017d81610157565b92915050565b61018c816100ec565b811461019757600080fd5b50565b6000813590506101a981610183565b92915050565b600080604083850312156101c6576101c5610120565b5b60006101d48582860161016e565b92505060206101e58582860161019a565b915050925092905056fea26469706673582212206df27ae5d2764317da225f7c3853c25e3786f13b2f78691a76f215a48ab6955a64736f6c63430008130033")); + const Bytes casinoBytecode = Utils::makeBytes(bytes::hex("0x608060405260016000806101000a81548160ff02191690831515021790555034801561002a57600080fd5b506103ad8061003a6000396000f3fe6080604052600436106100555760003560e01c806311610c251461005a5780632ad957861461006457806343d726d61461008d57806347535d7b146100a4578063b69ef8a8146100cf578063fcfff16f146100fa575b600080fd5b610062610111565b005b34801561007057600080fd5b5061008b60048036038101906100869190610263565b610160565b005b34801561009957600080fd5b506100a26101aa565b005b3480156100b057600080fd5b506100b96101c6565b6040516100c691906102ab565b60405180910390f35b3480156100db57600080fd5b506100e46101dc565b6040516100f191906102df565b60405180910390f35b34801561010657600080fd5b5061010f6101e4565b005b60008054906101000a900460ff1661015e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161015590610357565b60405180910390fd5b565b8073ffffffffffffffffffffffffffffffffffffffff166108fc479081150290604051600060405180830381858888f193505050501580156101a6573d6000803e3d6000fd5b5050565b60008060006101000a81548160ff021916908315150217905550565b60008060009054906101000a900460ff16905090565b600047905090565b60016000806101000a81548160ff021916908315150217905550565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061023082610205565b9050919050565b61024081610225565b811461024b57600080fd5b50565b60008135905061025d81610237565b92915050565b60006020828403121561027957610278610200565b5b60006102878482850161024e565b91505092915050565b60008115159050919050565b6102a581610290565b82525050565b60006020820190506102c0600083018461029c565b92915050565b6000819050919050565b6102d9816102c6565b82525050565b60006020820190506102f460008301846102d0565b92915050565b600082825260208201905092915050565b7f6265747320636c6f736564000000000000000000000000000000000000000000600082015250565b6000610341600b836102fa565b915061034c8261030b565b602082019050919050565b6000602082019050818103600083015261037081610334565b905091905056fea26469706673582212207757c95d521b5892342b02350317f2c90bcb46f0e30b16a13de45e34c659e21764736f6c63430008130033")); + const Bytes gamblerBytecode = Utils::makeBytes(bytes::hex("0x608060405234801561001057600080fd5b50610292806100206000396000f3fe6080604052600436106100295760003560e01c80632c5433051461002e578063b69ef8a81461004a575b600080fd5b610048600480360381019061004391906101e8565b610075565b005b34801561005657600080fd5b5061005f61017d565b60405161006c9190610241565b60405180910390f35b600082905060008290508173ffffffffffffffffffffffffffffffffffffffff166311610c25346040518263ffffffff1660e01b81526004016000604051808303818588803b1580156100c757600080fd5b505af1935050505080156100d9575060015b610176573d8060008114610109576040519150601f19603f3d011682016040523d82523d6000602084013e61010e565b606091505b508173ffffffffffffffffffffffffffffffffffffffff1663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b15801561015757600080fd5b505af115801561016b573d6000803e3d6000fd5b505050505050610177565b5b50505050565b600047905090565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101b58261018a565b9050919050565b6101c5816101aa565b81146101d057600080fd5b50565b6000813590506101e2816101bc565b92915050565b600080604083850312156101ff576101fe610185565b5b600061020d858286016101d3565b925050602061021e858286016101d3565b9150509250929050565b6000819050919050565b61023b81610228565b82525050565b60006020820190506102566000830184610232565b9291505056fea2646970667358221220ce419e18421bafa55bf72c1e46652f2e313a338b574ece304a4044cb4cc9c05164736f6c63430008130033")); + + messages::Gas gas(1'000'000); + uint256_t value = 0; + Bytes input; + + const Address fundsAddress = env.executor.execute(EncodedCreateMessage(firstAccountAddress, gas, value, fundsBytecode)); + firstAccount.nonce++; + const Address casinoAddress = env.executor.execute(EncodedCreateMessage(firstAccountAddress, gas, value, casinoBytecode)); + firstAccount.nonce++; + const Address gamblerAddress = env.executor.execute(EncodedCreateMessage(firstAccountAddress, gas, value, gamblerBytecode)); + firstAccount.nonce++; + + // TODO: with the call bellow, things doesn't work + input.clear(); + auto functor1 = ABI::FunctorEncoder::encode<>("close"); + Utils::appendBytes(input, Utils::uint32ToBytes(functor1.value)); + env.executor.execute(EncodedCallMessage(firstAccountAddress, casinoAddress, gas, value, input)); + + input.clear(); + auto functor2 = ABI::FunctorEncoder::encode("gamble"); + Utils::appendBytes(input, Utils::uint32ToBytes(functor2.value)); + Utils::appendBytes(input, ABI::Encoder::encodeData(casinoAddress, fundsAddress)); + + value = ONE_ETHER / 2; + env.executor.execute(EncodedCallMessage(firstAccountAddress, gamblerAddress, gas, value, input)); + + std::cout << "gambler balance: " << env.accounts.at(gamblerAddress)->balance << "\n"; + std::cout << "casino balance: " << env.accounts.at(casinoAddress)->balance << "\n"; + std::cout << "funds balance: " << env.accounts.at(fundsAddress)->balance << "\n"; + } + + SECTION("Delegate calls") { + // TODO: the code field from the create message also has the constructor arguments + + MockedEnvironment env; + messages::Gas gas(1'000'000); + uint256_t value = 0; + + const Address accountAddress = bytes::hex("0x05ed5b0cec75408bb57ab2a5413b9d6b0d6c756f"); + Account& account = *env.accounts.emplace(accountAddress, Account(ONE_ETHER, 1)).first->second; + + const Bytes delegatorBytecode = Utils::makeBytes(bytes::hex("0x60806040526040518060400160405280600681526020017f416c666163650000000000000000000000000000000000000000000000000000815250600090816200004a91906200032e565b5073ab8483f64d9c6d1ecf9b849ae677dd3315835cb2600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550348015620000ad57600080fd5b5062000415565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200013657607f821691505b6020821081036200014c576200014b620000ee565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620001b67fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000177565b620001c2868362000177565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006200020f620002096200020384620001da565b620001e4565b620001da565b9050919050565b6000819050919050565b6200022b83620001ee565b620002436200023a8262000216565b84845462000184565b825550505050565b600090565b6200025a6200024b565b6200026781848462000220565b505050565b5b818110156200028f576200028360008262000250565b6001810190506200026d565b5050565b601f821115620002de57620002a88162000152565b620002b38462000167565b81016020851015620002c3578190505b620002db620002d28562000167565b8301826200026c565b50505b505050565b600082821c905092915050565b60006200030360001984600802620002e3565b1980831691505092915050565b60006200031e8383620002f0565b9150826002028217905092915050565b6200033982620000b4565b67ffffffffffffffff811115620003555762000354620000bf565b5b6200036182546200011d565b6200036e82828562000293565b600060209050601f831160018114620003a6576000841562000391578287015190505b6200039d858262000310565b8655506200040d565b601f198416620003b68662000152565b60005b82811015620003e057848901518255600182019150602085019450602081019050620003b9565b86831015620004005784890151620003fc601f891682620002f0565b8355505b6001600288020188555050505b505050505050565b61076e80620004256000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c8063893d20e81461005c578063ce6d41de1461007a578063d129b79a14610098578063d6026296146100b6578063e7663079146100d2575b600080fd5b6100646100f0565b60405161007191906103e6565b60405180910390f35b61008261011a565b60405161008f9190610491565b60405180910390f35b6100a06101ac565b6040516100ad9190610491565b60405180910390f35b6100d060048036038101906100cb919061054e565b61023a565b005b6100da61037f565b6040516100e791906103e6565b60405180910390f35b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b606060008054610129906105dd565b80601f0160208091040260200160405190810160405280929190818152602001828054610155906105dd565b80156101a25780601f10610177576101008083540402835291602001916101a2565b820191906000526020600020905b81548152906001019060200180831161018557829003601f168201915b5050505050905090565b600080546101b9906105dd565b80601f01602080910402602001604051908101604052809291908181526020018280546101e5906105dd565b80156102325780601f1061020757610100808354040283529160200191610232565b820191906000526020600020905b81548152906001019060200180831161021557829003601f168201915b505050505081565b6000808473ffffffffffffffffffffffffffffffffffffffff1663368b877260e01b858560405160240161026f92919061064a565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516102d991906106b5565b600060405180830381855af49150503d8060008114610314576040519150601f19603f3d011682016040523d82523d6000602084013e610319565b606091505b509150915060001515821515036103785760008151111561033d5780518082602001fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161036f90610718565b60405180910390fd5b5050505050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006103d0826103a5565b9050919050565b6103e0816103c5565b82525050565b60006020820190506103fb60008301846103d7565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561043b578082015181840152602081019050610420565b60008484015250505050565b6000601f19601f8301169050919050565b600061046382610401565b61046d818561040c565b935061047d81856020860161041d565b61048681610447565b840191505092915050565b600060208201905081810360008301526104ab8184610458565b905092915050565b600080fd5b600080fd5b6104c6816103c5565b81146104d157600080fd5b50565b6000813590506104e3816104bd565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f84011261050e5761050d6104e9565b5b8235905067ffffffffffffffff81111561052b5761052a6104ee565b5b602083019150836001820283011115610547576105466104f3565b5b9250929050565b600080600060408486031215610567576105666104b3565b5b6000610575868287016104d4565b935050602084013567ffffffffffffffff811115610596576105956104b8565b5b6105a2868287016104f8565b92509250509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806105f557607f821691505b602082108103610608576106076105ae565b5b50919050565b82818337600083830152505050565b6000610629838561040c565b935061063683858461060e565b61063f83610447565b840190509392505050565b6000602082019050818103600083015261066581848661061d565b90509392505050565b600081519050919050565b600081905092915050565b600061068f8261066e565b6106998185610679565b93506106a981856020860161041d565b80840191505092915050565b60006106c18284610684565b915081905092915050565b7f46756e6374696f6e2063616c6c20726576657274656400000000000000000000600082015250565b600061070260168361040c565b915061070d826106cc565b602082019050919050565b60006020820190508181036000830152610731816106f5565b905091905056fea26469706673582212207bb9c9ffbef5fbdb6a396f9c4e1962581051cafee1ecbf4367476185b9bdaadd64736f6c63430008130033")); + const Bytes delegatedBytecode = Utils::makeBytes(bytes::hex("0x60806040526040518060400160405280600681526020017f416c666163650000000000000000000000000000000000000000000000000000815250600090816200004a919062000340565b503480156200005857600080fd5b5060405162000caa38038062000caa83398181016040528101906200007e919062000491565b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050620004c3565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200014857607f821691505b6020821081036200015e576200015d62000100565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620001c87fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000189565b620001d4868362000189565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b6000620002216200021b6200021584620001ec565b620001f6565b620001ec565b9050919050565b6000819050919050565b6200023d8362000200565b620002556200024c8262000228565b84845462000196565b825550505050565b600090565b6200026c6200025d565b6200027981848462000232565b505050565b5b81811015620002a1576200029560008262000262565b6001810190506200027f565b5050565b601f821115620002f057620002ba8162000164565b620002c58462000179565b81016020851015620002d5578190505b620002ed620002e48562000179565b8301826200027e565b50505b505050565b600082821c905092915050565b60006200031560001984600802620002f5565b1980831691505092915050565b600062000330838362000302565b9150826002028217905092915050565b6200034b82620000c6565b67ffffffffffffffff811115620003675762000366620000d1565b5b6200037382546200012f565b62000380828285620002a5565b600060209050601f831160018114620003b85760008415620003a3578287015190505b620003af858262000322565b8655506200041f565b601f198416620003c88662000164565b60005b82811015620003f257848901518255600182019150602085019450602081019050620003cb565b868310156200041257848901516200040e601f89168262000302565b8355505b6001600288020188555050505b505050505050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600062000459826200042c565b9050919050565b6200046b816200044c565b81146200047757600080fd5b50565b6000815190506200048b8162000460565b92915050565b600060208284031215620004aa57620004a962000427565b5b6000620004ba848285016200047a565b91505092915050565b6107d780620004d36000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c8063368b87721461005c578063893d20e814610078578063ce6d41de14610096578063d129b79a146100b4578063e7663079146100d2575b600080fd5b61007660048036038101906100719190610326565b6100f0565b005b610080610147565b60405161008d91906103b4565b60405180910390f35b61009e610171565b6040516100ab919061045f565b60405180910390f35b6100bc610203565b6040516100c9919061045f565b60405180910390f35b6100da610291565b6040516100e791906103b4565b60405180910390f35b8181600091826101019291906106d1565b5033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b606060008054610180906104ea565b80601f01602080910402602001604051908101604052809291908181526020018280546101ac906104ea565b80156101f95780601f106101ce576101008083540402835291602001916101f9565b820191906000526020600020905b8154815290600101906020018083116101dc57829003601f168201915b5050505050905090565b60008054610210906104ea565b80601f016020809104026020016040519081016040528092919081815260200182805461023c906104ea565b80156102895780601f1061025e57610100808354040283529160200191610289565b820191906000526020600020905b81548152906001019060200180831161026c57829003601f168201915b505050505081565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b60008083601f8401126102e6576102e56102c1565b5b8235905067ffffffffffffffff811115610303576103026102c6565b5b60208301915083600182028301111561031f5761031e6102cb565b5b9250929050565b6000806020838503121561033d5761033c6102b7565b5b600083013567ffffffffffffffff81111561035b5761035a6102bc565b5b610367858286016102d0565b92509250509250929050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061039e82610373565b9050919050565b6103ae81610393565b82525050565b60006020820190506103c960008301846103a5565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156104095780820151818401526020810190506103ee565b60008484015250505050565b6000601f19601f8301169050919050565b6000610431826103cf565b61043b81856103da565b935061044b8185602086016103eb565b61045481610415565b840191505092915050565b600060208201905081810360008301526104798184610426565b905092915050565b600082905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061050257607f821691505b602082108103610515576105146104bb565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b60006008830261057d7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82610540565b6105878683610540565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006105ce6105c96105c48461059f565b6105a9565b61059f565b9050919050565b6000819050919050565b6105e8836105b3565b6105fc6105f4826105d5565b84845461054d565b825550505050565b600090565b610611610604565b61061c8184846105df565b505050565b5b8181101561064057610635600082610609565b600181019050610622565b5050565b601f821115610685576106568161051b565b61065f84610530565b8101602085101561066e578190505b61068261067a85610530565b830182610621565b50505b505050565b600082821c905092915050565b60006106a86000198460080261068a565b1980831691505092915050565b60006106c18383610697565b9150826002028217905092915050565b6106db8383610481565b67ffffffffffffffff8111156106f4576106f361048c565b5b6106fe82546104ea565b610709828285610644565b6000601f8311600181146107385760008415610726578287013590505b61073085826106b5565b865550610798565b601f1984166107468661051b565b60005b8281101561076e57848901358255600182019150602085019450602081019050610749565b8683101561078b5784890135610787601f891682610697565b8355505b6001600288020188555050505b5050505050505056fea26469706673582212204d0495ba5050deed7ead9ed3191f798cb26ec86deac8246cd32b297fec3664cb64736f6c634300081300330000000000000000000000009fc12574abe3e595c73b6d4380762f232507aeeb")); + + const Address delegatorAddress = env.executor.execute(EncodedCreateMessage(accountAddress, gas, value, delegatorBytecode)); + account.nonce++; + + const Address delegatedAddress = env.executor.execute(EncodedCreateMessage(accountAddress, gas, value, delegatedBytecode)); + account.nonce++; + + Bytes input; + auto functor = ABI::FunctorEncoder::encode("delegateTo"); + Utils::appendBytes(input, Utils::uint32ToBytes(functor.value)); + Utils::appendBytes(input, ABI::Encoder::encodeData(delegatedAddress, std::string("cometa"))); + + std::cout << "\n\n"; + std::cout << "account address: " << accountAddress.hex(true) << "\n"; + std::cout << "delegator address: " << delegatorAddress.hex(true) << "\n"; + std::cout << "delegated address: " << delegatedAddress.hex(true) << "\n\n"; + + env.executor.execute(EncodedCallMessage(accountAddress, delegatorAddress, gas, value, input)); + } +} diff --git a/tests/contract/messages/executioncontext.cpp b/tests/contract/messages/executioncontext.cpp new file mode 100644 index 00000000..6f6f21ed --- /dev/null +++ b/tests/contract/messages/executioncontext.cpp @@ -0,0 +1,345 @@ +/* +Copyright (c) [2023-2024] [AppLayer Developers] + +This software is distributed under the MIT License. +See the LICENSE.txt file in the project root for more information. +*/ + +#include "catch2/catch_amalgamated.hpp" +#include "bytes/hex.h" +#include "contract/messages/executioncontext.h" + +TEST_CASE("Execution Context Test Cases", "[executioncontext]") { + SECTION("Building correctly") { + ExecutionContext::Accounts accounts; + ExecutionContext::Storage storage; + + const Address accountAddress1 = bytes::hex("0xa29F7649159DBF66daaa6D03F9ed5733c85BDc27"); + const Address accountAddress2 = bytes::hex("0x87e42c3307c79334e4A22EF406BDe0A004D9c8C7"); + + accounts.emplace(accountAddress1, Account(1000, 5)); + accounts.emplace(accountAddress2, Account(666, 3)); + + const Hash slot1 = bytes::hex("0x0000000000000000000000000000000000000000000000000000000000000001"); + const Hash slot2 = bytes::hex("0x0000000000000000000000000000000000000000000000000000000000000002"); + const Hash data = bytes::hex("0xc89a747ae61fb49aeefadaa8d6fce73ab2f61b444a196c026d46cbe550b90b5b"); + + storage.emplace(StorageKeyView(accountAddress1, slot1), data); + + const int64_t blockGasLimit = 5000; + const int64_t blockNumber = 123; + const int64_t blockTimestamp = 31987233180528; + const int64_t txIndex = 2; + const Address blockCoinbase = bytes::hex("0xe4d327487dd563e93dd09743d1aad131f89e1866"); + const Address txOrigin = bytes::hex("0x35b5758593b3da41a2cc5574d1c4a9aa7cc994f4"); + const Hash blockHash = bytes::hex("0x044475f2cb0876a477b9f7fb401162317ea6ae98c5a7fc84b84cda820c864541"); + const Hash txHash = bytes::hex("0xac7dbb9fd2bf03c58b61664bf453bf7760de39edd91cf812f4b8d49763d29a03"); + const uint256_t chainId = 45; + const uint256_t txGasPrice = 1234753453245; + + ExecutionContext context = ExecutionContext::Builder() + .storage(storage) + .accounts(accounts) + .blockHash(blockHash) + .txHash(txHash) + .txOrigin(txOrigin) + .blockCoinbase(blockCoinbase) + .txIndex(txIndex) + .blockNumber(blockNumber) + .blockTimestamp(blockTimestamp) + .blockGasLimit(blockGasLimit) + .txGasPrice(txGasPrice) + .chainId(chainId) + .build(); + + REQUIRE(context.getBlockHash() == blockHash); + REQUIRE(context.getTxHash() == txHash); + REQUIRE(context.getTxOrigin() == txOrigin); + REQUIRE(context.getBlockCoinbase() == blockCoinbase); + REQUIRE(context.getTxIndex() == txIndex); + REQUIRE(context.getBlockNumber() == blockNumber); + REQUIRE(context.getBlockTimestamp() == blockTimestamp); + REQUIRE(context.getBlockGasLimit() == blockGasLimit); + REQUIRE(context.getTxGasPrice() == txGasPrice); + REQUIRE(context.getChainId() == chainId); + REQUIRE(context.retrieve(accountAddress1, slot1) == data); + REQUIRE(context.retrieve(accountAddress1, slot2) == Hash()); + REQUIRE(context.retrieve(accountAddress2, slot1) == Hash()); + REQUIRE(context.getAccount(accountAddress1).balance == 1000); + REQUIRE(context.getAccount(accountAddress2).balance == 666); + REQUIRE_THROWS(context.getAccount(blockCoinbase)); + } + + SECTION("Checkpoint revert to accounts") { + ExecutionContext::Accounts accounts; + ExecutionContext::Storage storage; + + const std::array accountsArr = { + Account(200, 3), Account(100, 2), Account(150, 1), + Account(800, 2), Account(100, 4), Account(666, 0), + Account(111, 0), Account(222, 3), Account(0, 0) + }; + + const std::array addresses = { + bytes::hex("0xbb1fe74127ed514da3715ea15f567095215dbf9c"), + bytes::hex("0x0eb51dd6be2169a9a0696a34034a31886647dc3f"), + bytes::hex("0x2a2baa3d4772ed5f2fa34758e4f23633c70fcc9b"), + bytes::hex("0x349dbcc860b78cdeac089bace1eb6a212d8460c4"), + bytes::hex("0xaba088c6004908ab1eee30598ffe528472c15f0f"), + bytes::hex("0x6efa98097f6b6b8f4e6347af82220eb969e1f509"), + bytes::hex("0xaa8b62e65fb53029d46de33e0073a4e813249ab2"), + bytes::hex("0x2f6ea6302015283fe13a38e8d8ba813426acacf2"), + bytes::hex("0x9a6cabc2cd5f053961bbc2dae59f0bde2d5daa76"), + bytes::hex("0xa0ca021de6ee33fd980129ce06af50764a461771") + }; + + accounts.emplace(addresses[0], accountsArr[0]); + + ExecutionContext context = ExecutionContext::Builder() + .storage(storage) + .accounts(accounts) + .build(); + + context.addAccount(addresses[1], accountsArr[1]); + context.addAccount(addresses[2], accountsArr[2]); + + REQUIRE(context.getAccount(addresses[0]).balance == accountsArr[0].balance); + REQUIRE(context.getAccount(addresses[1]).balance == accountsArr[1].balance); + REQUIRE(context.getAccount(addresses[2]).balance == accountsArr[2].balance); + + auto checkpoint = context.checkpoint(); + + context.addAccount(addresses[3], accountsArr[3]); + context.addAccount(addresses[4], accountsArr[4]); + + REQUIRE(context.getAccount(addresses[0]).balance == accountsArr[0].balance); + REQUIRE(context.getAccount(addresses[1]).balance == accountsArr[1].balance); + REQUIRE(context.getAccount(addresses[2]).balance == accountsArr[2].balance); + REQUIRE(context.getAccount(addresses[3]).balance == accountsArr[3].balance); + REQUIRE(context.getAccount(addresses[4]).balance == accountsArr[4].balance); + + context.transferBalance(addresses[0], addresses[1], 100); + REQUIRE(context.getAccount(addresses[0]).balance == accountsArr[0].balance - 100); + REQUIRE(context.getAccount(addresses[1]).balance == accountsArr[1].balance + 100); + REQUIRE_THROWS(context.transferBalance(addresses[0], addresses[1], 101)); + + checkpoint.revert(); + + REQUIRE(context.getAccount(addresses[0]).balance == accountsArr[0].balance); + REQUIRE(context.getAccount(addresses[1]).balance == accountsArr[1].balance); + REQUIRE(context.getAccount(addresses[2]).balance == accountsArr[2].balance); + REQUIRE_THROWS(context.getAccount(addresses[3])); + REQUIRE_THROWS(context.getAccount(addresses[4])); + + { + auto checkpoint2 = context.checkpoint(); + + context.addAccount(addresses[5], accountsArr[5]); + context.addAccount(addresses[6], accountsArr[6]); + context.transferBalance(addresses[1], addresses[2], 50); + + checkpoint2.commit(); + } + + REQUIRE(context.getAccount(addresses[1]).balance == accountsArr[1].balance - 50); + REQUIRE(context.getAccount(addresses[2]).balance == accountsArr[2].balance + 50); + REQUIRE(context.getAccount(addresses[5]).balance == accountsArr[5].balance); + REQUIRE(context.getAccount(addresses[6]).balance == accountsArr[6].balance); + } + + SECTION("Nested: revert, revert, revert") { + + std::array addr = { + bytes::hex("5f6e2d4d9d847b820362bf62e1e8b4d4897ce760"), + bytes::hex("c84c5ecbcc4d5932dfdb4034ab2b8e2b246aef41"), + bytes::hex("69ca633ac018da9f3356148717b6818b8b37c379"), + bytes::hex("cc75db077372b3dfcfbf0f3bb37b995aaf8ef155"), + bytes::hex("0625070d23c0228e75c08c2aac6beba86d349f76"), + bytes::hex("9c1d041e67b9fb6367a857a98e7a22097f02b28d"), + bytes::hex("671a7c172bd07caf482c6b6a0e8f74740bf0b2b4"), + bytes::hex("404e19eca6b9ce9012f6959650a7d19cea4cfb39"), + bytes::hex("47212cde146e973b8291038e4b7da890e128d2d9"), + bytes::hex("061e32675637d8accd4a23e32cb6d31dd082f344") + }; + + std::array slots = { + Hash(uint256_t(0)), Hash(uint256_t(1)), Hash(uint256_t(2)), + Hash(uint256_t(3)), Hash(uint256_t(4)), Hash(uint256_t(5)), + Hash(uint256_t(6)), Hash(uint256_t(7)), Hash(uint256_t(8)), + Hash(uint256_t(9)) + }; + + std::array data = { + bytes::hex("a2122ba388a9bf54565d31a711a1a863cb0c7472433253122f8307f8edca73a8"), + bytes::hex("4a84e91e23c4872751210b7088ba266cbdf515e0a053e95f567aac5e1a45b537"), + bytes::hex("432b7b1c5c00d2bd3b37d72672328468271884b311cd094fa5759cbd66310048"), + bytes::hex("b80d1fcfb14cc5b8a01f7264f326786cefe5db0fc6b1a75d3b7dd0730120678f"), + bytes::hex("a559ea3e765536ab3610fcf455e44320a5ce221275eb3ed7f84470a370ac2aee"), + bytes::hex("78886f68e66985f4e7813dc929e6ad0e427ae577ca0a6336442e18426f5a80a1"), + bytes::hex("66db09b48ea9a16269f07cbd6442abdf19a11d442fe5697b833c99e6cf9e3794"), + bytes::hex("41047dcdfb2e6dd03a61511beaabc0be7c7f72a74bb999622566bb2e1d0628ca"), + bytes::hex("6f9f1638daa7e7a8bd2bfaab03db43e29f78f1c920414638cfcebaff41c70ffa"), + bytes::hex("7bc77af21a9f554fb5d8e5f12255191b96b830c953420072f62b1e507aeed638") + }; + + ExecutionContext::Accounts accounts; + ExecutionContext::Storage storage; + + storage.emplace(StorageKeyView(addr[0], slots[0]), data[0]); + + ExecutionContext context = ExecutionContext::Builder() + .storage(storage) + .accounts(accounts) + .build(); + + context.store(addr[1], slots[1], data[1]); + context.store(addr[2], slots[2], data[2]); + + { + auto cp1 = context.checkpoint(); + + context.store(addr[3], slots[3], data[3]); + context.store(addr[4], slots[4], data[4]); + + { + auto cp2 = context.checkpoint(); + + context.store(addr[5], slots[5], data[5]); + context.store(addr[6], slots[6], data[6]); + + REQUIRE(context.retrieve(addr[0], slots[0]) == data[0]); + REQUIRE(context.retrieve(addr[1], slots[1]) == data[1]); + REQUIRE(context.retrieve(addr[2], slots[2]) == data[2]); + REQUIRE(context.retrieve(addr[3], slots[3]) == data[3]); + REQUIRE(context.retrieve(addr[4], slots[4]) == data[4]); + REQUIRE(context.retrieve(addr[5], slots[5]) == data[5]); + REQUIRE(context.retrieve(addr[6], slots[6]) == data[6]); + } + + REQUIRE(context.retrieve(addr[0], slots[0]) == data[0]); + REQUIRE(context.retrieve(addr[1], slots[1]) == data[1]); + REQUIRE(context.retrieve(addr[2], slots[2]) == data[2]); + REQUIRE(context.retrieve(addr[3], slots[3]) == data[3]); + REQUIRE(context.retrieve(addr[4], slots[4]) == data[4]); + REQUIRE(context.retrieve(addr[5], slots[5]) == Hash()); + REQUIRE(context.retrieve(addr[6], slots[6]) == Hash()); + } + + REQUIRE(context.retrieve(addr[0], slots[0]) == data[0]); + REQUIRE(context.retrieve(addr[1], slots[1]) == data[1]); + REQUIRE(context.retrieve(addr[2], slots[2]) == data[2]); + REQUIRE(context.retrieve(addr[3], slots[3]) == Hash()); + REQUIRE(context.retrieve(addr[4], slots[4]) == Hash()); + REQUIRE(context.retrieve(addr[5], slots[5]) == Hash()); + REQUIRE(context.retrieve(addr[6], slots[6]) == Hash()); + + context.revert(); + + REQUIRE(context.retrieve(addr[0], slots[0]) == data[0]); + REQUIRE(context.retrieve(addr[1], slots[1]) == Hash()); + REQUIRE(context.retrieve(addr[2], slots[2]) == Hash()); + REQUIRE(context.retrieve(addr[3], slots[3]) == Hash()); + REQUIRE(context.retrieve(addr[4], slots[4]) == Hash()); + REQUIRE(context.retrieve(addr[5], slots[5]) == Hash()); + REQUIRE(context.retrieve(addr[6], slots[6]) == Hash()); + } + + SECTION("Nested: commit, revert, commit") { + + std::array addr = { + bytes::hex("5f6e2d4d9d847b820362bf62e1e8b4d4897ce760"), + bytes::hex("c84c5ecbcc4d5932dfdb4034ab2b8e2b246aef41"), + bytes::hex("69ca633ac018da9f3356148717b6818b8b37c379"), + bytes::hex("cc75db077372b3dfcfbf0f3bb37b995aaf8ef155"), + bytes::hex("0625070d23c0228e75c08c2aac6beba86d349f76"), + bytes::hex("9c1d041e67b9fb6367a857a98e7a22097f02b28d"), + bytes::hex("671a7c172bd07caf482c6b6a0e8f74740bf0b2b4"), + bytes::hex("404e19eca6b9ce9012f6959650a7d19cea4cfb39"), + bytes::hex("47212cde146e973b8291038e4b7da890e128d2d9"), + bytes::hex("061e32675637d8accd4a23e32cb6d31dd082f344") + }; + + std::array slots = { + Hash(uint256_t(0)), Hash(uint256_t(1)), Hash(uint256_t(2)), + Hash(uint256_t(3)), Hash(uint256_t(4)), Hash(uint256_t(5)), + Hash(uint256_t(6)), Hash(uint256_t(7)), Hash(uint256_t(8)), + Hash(uint256_t(9)) + }; + + std::array data = { + bytes::hex("a2122ba388a9bf54565d31a711a1a863cb0c7472433253122f8307f8edca73a8"), + bytes::hex("4a84e91e23c4872751210b7088ba266cbdf515e0a053e95f567aac5e1a45b537"), + bytes::hex("432b7b1c5c00d2bd3b37d72672328468271884b311cd094fa5759cbd66310048"), + bytes::hex("b80d1fcfb14cc5b8a01f7264f326786cefe5db0fc6b1a75d3b7dd0730120678f"), + bytes::hex("a559ea3e765536ab3610fcf455e44320a5ce221275eb3ed7f84470a370ac2aee"), + bytes::hex("78886f68e66985f4e7813dc929e6ad0e427ae577ca0a6336442e18426f5a80a1"), + bytes::hex("66db09b48ea9a16269f07cbd6442abdf19a11d442fe5697b833c99e6cf9e3794"), + bytes::hex("41047dcdfb2e6dd03a61511beaabc0be7c7f72a74bb999622566bb2e1d0628ca"), + bytes::hex("6f9f1638daa7e7a8bd2bfaab03db43e29f78f1c920414638cfcebaff41c70ffa"), + bytes::hex("7bc77af21a9f554fb5d8e5f12255191b96b830c953420072f62b1e507aeed638") + }; + + ExecutionContext::Accounts accounts; + ExecutionContext::Storage storage; + + storage.emplace(StorageKeyView(addr[0], slots[0]), data[0]); + + ExecutionContext context = ExecutionContext::Builder() + .storage(storage) + .accounts(accounts) + .build(); + + context.store(addr[1], slots[1], data[1]); + context.store(addr[2], slots[2], data[2]); + + { + auto cp1 = context.checkpoint(); + + context.store(addr[3], slots[3], data[3]); + context.store(addr[4], slots[4], data[4]); + + { + auto cp2 = context.checkpoint(); + + context.store(addr[5], slots[5], data[5]); + context.store(addr[6], slots[6], data[6]); + + REQUIRE(context.retrieve(addr[0], slots[0]) == data[0]); + REQUIRE(context.retrieve(addr[1], slots[1]) == data[1]); + REQUIRE(context.retrieve(addr[2], slots[2]) == data[2]); + REQUIRE(context.retrieve(addr[3], slots[3]) == data[3]); + REQUIRE(context.retrieve(addr[4], slots[4]) == data[4]); + REQUIRE(context.retrieve(addr[5], slots[5]) == data[5]); + REQUIRE(context.retrieve(addr[6], slots[6]) == data[6]); + + cp2.commit(); + } + + REQUIRE(context.retrieve(addr[0], slots[0]) == data[0]); + REQUIRE(context.retrieve(addr[1], slots[1]) == data[1]); + REQUIRE(context.retrieve(addr[2], slots[2]) == data[2]); + REQUIRE(context.retrieve(addr[3], slots[3]) == data[3]); + REQUIRE(context.retrieve(addr[4], slots[4]) == data[4]); + REQUIRE(context.retrieve(addr[5], slots[5]) == data[5]); + REQUIRE(context.retrieve(addr[6], slots[6]) == data[6]); + } + + REQUIRE(context.retrieve(addr[0], slots[0]) == data[0]); + REQUIRE(context.retrieve(addr[1], slots[1]) == data[1]); + REQUIRE(context.retrieve(addr[2], slots[2]) == data[2]); + REQUIRE(context.retrieve(addr[3], slots[3]) == Hash()); + REQUIRE(context.retrieve(addr[4], slots[4]) == Hash()); + REQUIRE(context.retrieve(addr[5], slots[5]) == Hash()); + REQUIRE(context.retrieve(addr[6], slots[6]) == Hash()); + + context.commit(); + + REQUIRE(context.retrieve(addr[0], slots[0]) == data[0]); + REQUIRE(context.retrieve(addr[1], slots[1]) == data[1]); + REQUIRE(context.retrieve(addr[2], slots[2]) == data[2]); + REQUIRE(context.retrieve(addr[3], slots[3]) == Hash()); + REQUIRE(context.retrieve(addr[4], slots[4]) == Hash()); + REQUIRE(context.retrieve(addr[5], slots[5]) == Hash()); + REQUIRE(context.retrieve(addr[6], slots[6]) == Hash()); + } +} diff --git a/tests/core/state.cpp b/tests/core/state.cpp index b2e90053..3067f2a7 100644 --- a/tests/core/state.cpp +++ b/tests/core/state.cpp @@ -63,6 +63,13 @@ std::pair buildCallInfo(const Address& addressToCall, const return callInfo; } +static Bytes buildMessageData(const Functor& function, View callData) { + Bytes messageData; + Utils::appendBytes(messageData, Utils::uint32ToBytes(function.value)); + Utils::appendBytes(messageData, callData); + return messageData; +} + namespace TState { std::string testDumpPath = Utils::getTestDumpPath(); TEST_CASE("State Class", "[core][state]") { @@ -1415,7 +1422,7 @@ namespace TState { // We are doing 10 ERC20 txs, one per block std::vector txs; Hash creationHash = Hash(); - Address ERC20ContractAddress = ContractHost::deriveContractAddress(blockchainWrapper1.state.getNativeNonce(owner), owner); + Address ERC20ContractAddress = generateContractAddress(blockchainWrapper1.state.getNativeNonce(owner), owner); uint64_t nonce = 0; for (uint64_t i = 0; i < 10; ++i) { if (i == 0) { @@ -1497,50 +1504,49 @@ namespace TState { Bytes getBalanceMeEncoder = ABI::Encoder::encodeData(targetOfTransactions); Functor getBalanceMeFunctor = ABI::FunctorEncoder::encode
("balanceOf"); - Bytes getBalanceMeNode1Result = blockchainWrapper1.state.ethCall( - buildCallInfo(ERC20ContractAddress, getBalanceMeFunctor, getBalanceMeEncoder).first); + + + const Address from{}; + messages::Gas gas(10000000); + const Bytes data = buildMessageData(getBalanceMeFunctor, getBalanceMeEncoder); + EncodedStaticCallMessage msg(from, ERC20ContractAddress, gas, data); + + Bytes getBalanceMeNode1Result = blockchainWrapper1.state.ethCall(msg); auto getBalanceMeNode1Decoder = ABI::Decoder::decodeData(getBalanceMeNode1Result); REQUIRE(std::get<0>(getBalanceMeNode1Decoder) == targetExpectedValue); - Bytes getBalanceMeNode2Result = blockchainWrapper2.state.ethCall( - buildCallInfo(ERC20ContractAddress, getBalanceMeFunctor, getBalanceMeEncoder).first); + Bytes getBalanceMeNode2Result = blockchainWrapper2.state.ethCall(msg); auto getBalanceMeNode2Decoder = ABI::Decoder::decodeData(getBalanceMeNode2Result); REQUIRE(std::get<0>(getBalanceMeNode2Decoder) == targetExpectedValue); - Bytes getBalanceMeNode3Result = blockchainWrapper3.state.ethCall( - buildCallInfo(ERC20ContractAddress, getBalanceMeFunctor, getBalanceMeEncoder).first); + Bytes getBalanceMeNode3Result = blockchainWrapper3.state.ethCall(msg); auto getBalanceMeNode3Decoder = ABI::Decoder::decodeData(getBalanceMeNode3Result); REQUIRE(std::get<0>(getBalanceMeNode3Decoder) == targetExpectedValue); - Bytes getBalanceMeNode4Result = blockchainWrapper4.state.ethCall( - buildCallInfo(ERC20ContractAddress, getBalanceMeFunctor, getBalanceMeEncoder).first); + Bytes getBalanceMeNode4Result = blockchainWrapper4.state.ethCall(msg); auto getBalanceMeNode4Decoder = ABI::Decoder::decodeData(getBalanceMeNode4Result); REQUIRE(std::get<0>(getBalanceMeNode4Decoder) == targetExpectedValue); - Bytes getBalanceMeNode5Result = blockchainWrapper5.state.ethCall( - buildCallInfo(ERC20ContractAddress, getBalanceMeFunctor, getBalanceMeEncoder).first); + Bytes getBalanceMeNode5Result = blockchainWrapper5.state.ethCall(msg); auto getBalanceMeNode5Decoder = ABI::Decoder::decodeData(getBalanceMeNode5Result); REQUIRE(std::get<0>(getBalanceMeNode5Decoder) == targetExpectedValue); - Bytes getBalanceMeNode6Result = blockchainWrapper6.state.ethCall( - buildCallInfo(ERC20ContractAddress, getBalanceMeFunctor, getBalanceMeEncoder).first); + Bytes getBalanceMeNode6Result = blockchainWrapper6.state.ethCall(msg); auto getBalanceMeNode6Decoder = ABI::Decoder::decodeData(getBalanceMeNode6Result); REQUIRE(std::get<0>(getBalanceMeNode6Decoder) == targetExpectedValue); - Bytes getBalanceMeNode7Result = blockchainWrapper7.state.ethCall( - buildCallInfo(ERC20ContractAddress, getBalanceMeFunctor, getBalanceMeEncoder).first); + Bytes getBalanceMeNode7Result = blockchainWrapper7.state.ethCall(msg); auto getBalanceMeNode7Decoder = ABI::Decoder::decodeData(getBalanceMeNode7Result); REQUIRE(std::get<0>(getBalanceMeNode7Decoder) == targetExpectedValue); - Bytes getBalanceMeNode8Result = blockchainWrapper8.state.ethCall( - buildCallInfo(ERC20ContractAddress, getBalanceMeFunctor, getBalanceMeEncoder).first); + Bytes getBalanceMeNode8Result = blockchainWrapper8.state.ethCall(msg); auto getBalanceMeNode8Decoder = ABI::Decoder::decodeData(getBalanceMeNode8Result); REQUIRE(std::get<0>(getBalanceMeNode8Decoder) == targetExpectedValue); diff --git a/tests/sdktestsuite.hpp b/tests/sdktestsuite.hpp index 7872c3b5..c302c5ee 100644 --- a/tests/sdktestsuite.hpp +++ b/tests/sdktestsuite.hpp @@ -318,39 +318,23 @@ class SDKTestSuite { TxBlock createNewTx( const TestAccount& from, const Address& to, const uint256_t& value, Bytes data = Bytes() ) { - evmc_message callInfo; - auto& [callKind, - callFlags, - callDepth, - callGas, - callRecipient, - callSender, - callInputData, - callInputSize, - callValue, - callCreate2Salt, - callCodeAddress] = callInfo; - - callKind = (to == Address()) ? EVMC_CREATE : EVMC_CALL; - callFlags = 0; - callDepth = 1; - callGas = 1000000000; - callRecipient = bytes::cast(to); - callSender = bytes::cast(from.address); - callInputData = data.data(); - callInputSize = data.size(); - callValue = Utils::uint256ToEvmcUint256(value); - callCreate2Salt = {}; - callCodeAddress = {}; - auto usedGas = this->state_.estimateGas(callInfo); - usedGas += 10000; // Add some extra gas for the transaction itself - /// Estimate the gas to see how much gaslimit we should give to the tx itself + + messages::Gas gas(1000000000); + + const uint64_t gasUsed = 10000 + std::invoke([&] () { + if (to) { + return this->state_.estimateGas(EncodedCallMessage(from.address, to, gas, value, data)); + } else { + return this->state_.estimateGas(EncodedCreateMessage(from.address, gas, value, data)); + } + }); + return TxBlock(to, from.address, data, this->options_.getChainID(), this->state_.getNativeNonce(from.address), value, 1000000000, 1000000000, - usedGas, + gasUsed, from.privKey ); } @@ -403,7 +387,7 @@ class SDKTestSuite { * @return Address of the deployed contract. */ Address deployBytecode(const Bytes& bytecode) { - Address newContractAddress = ContractHost::deriveContractAddress(this->getNativeNonce(this->getChainOwnerAccount().address), this->getChainOwnerAccount().address); + Address newContractAddress = generateContractAddress(this->getNativeNonce(this->getChainOwnerAccount().address), this->getChainOwnerAccount().address); auto createTx = this->createNewTx(this->getChainOwnerAccount(), Address(), 0, bytecode); this->advanceChain(0, {createTx}); return newContractAddress; @@ -761,38 +745,19 @@ class SDKTestSuite { const Address& contractAddress, ReturnType(TContract::*func)() const ) { TContract::registerContract(); - evmc_message callData; - auto& [callKind, - callFlags, - callDepth, - callGas, - callRecipient, - callSender, - callInputData, - callInputSize, - callValue, - callCreate2Salt, - callCodeAddress] = callData; auto functor = ABI::FunctorEncoder::encode<>(ContractReflectionInterface::getFunctionName(func)); Bytes fullData; Utils::appendBytes(fullData, Utils::uint32ToBytes(functor.value)); - callKind = EVMC_CALL; - callFlags = 0; - callDepth = 1; - callGas = 10000000; - callRecipient = bytes::cast(contractAddress); - callSender = bytes::cast(this->getChainOwnerAccount().address); - callInputData = fullData.data(); - callInputSize = fullData.size(); - callValue = Utils::uint256ToEvmcUint256(0); - callCreate2Salt = {}; - callCodeAddress = bytes::cast(contractAddress); - return std::get<0>(ABI::Decoder::decodeData(this->state_.ethCall(callData))); + messages::Gas gas(10000000); + const Address from = this->getChainOwnerAccount().address; + EncodedStaticCallMessage msg(from, contractAddress, gas, fullData); + + return std::get<0>(ABI::Decoder::decodeData(this->state_.ethCall(msg))); } - /** + /** * Call a contract view function with args and return the result. * @tparam ReturnType Return type of the function. * @tparam TContract Contract type to call. @@ -809,36 +774,16 @@ class SDKTestSuite { const Args&... args ) { TContract::registerContract(); - evmc_message callData; - auto& [callKind, - callFlags, - callDepth, - callGas, - callRecipient, - callSender, - callInputData, - callInputSize, - callValue, - callCreate2Salt, - callCodeAddress] = callData; auto functor = ABI::FunctorEncoder::encode(ContractReflectionInterface::getFunctionName(func)); Bytes fullData; Utils::appendBytes(fullData, Utils::uint32ToBytes(functor.value)); Utils::appendBytes(fullData, ABI::Encoder::encodeData(std::forward(args)...)); - callKind = EVMC_CALL; - callFlags = 0; - callDepth = 1; - callGas = 10000000; - callRecipient = bytes::cast(contractAddress); - callSender = bytes::cast(this->getChainOwnerAccount().address); - callInputData = fullData.data(); - callInputSize = fullData.size(); - callValue = Utils::uint256ToEvmcUint256(0); - callCreate2Salt = {}; - callCodeAddress = {}; - - return std::get<0>(ABI::Decoder::decodeData(this->state_.ethCall(callData))); + messages::Gas gas(10000000); + const Address from = this->getChainOwnerAccount().address; + EncodedStaticCallMessage msg(from, contractAddress, gas, fullData); + + return std::get<0>(ABI::Decoder::decodeData(this->state_.ethCall(msg))); } diff --git a/tests/statetest.hpp b/tests/statetest.hpp index 5fa3de25..91387598 100644 --- a/tests/statetest.hpp +++ b/tests/statetest.hpp @@ -13,31 +13,4 @@ class StateTest : public State { // StateTest has the same constructor as State StateTest(const DB& db, Storage& storage, P2P::ManagerNormal& p2pManager, const uint64_t& snapshotHeight, const Options& options) : State(db, storage, p2pManager, snapshotHeight, options) {}; - - /** - * Force a contract call, regardless of the current state. - */ - inline void call(const evmc_message& callInfo, - const evmc_tx_context& txContext, - const ContractType& type, - const Hash& randomness, - const Hash& txHash, - const Hash& blockHash, - int64_t& leftoverGas) { - ContractHost host( - this->vm_, - this->dumpManager_, - this->storage_, - randomness, - txContext, - this->contracts_, - this->accounts_, - this->vmStorage_, - txHash, - 0, - blockHash, - leftoverGas - ); - host.execute(callInfo, type); - } }; \ No newline at end of file From 4c50bcf6da311e9cde9bc42af6040859325f02e1 Mon Sep 17 00:00:00 2001 From: Leonardo Amalaral Date: Thu, 19 Dec 2024 15:18:26 -0300 Subject: [PATCH 11/37] cursed bug fixed --- src/contract/contracthost.cpp | 3 +- src/contract/dynamiccontract.h | 60 +++++++++------------ src/contract/messages/cppcontractexecutor.h | 30 ++++++++--- src/contract/messages/executioncontext.cpp | 11 ++-- src/contract/messages/traits.h | 2 +- 5 files changed, 55 insertions(+), 51 deletions(-) diff --git a/src/contract/contracthost.cpp b/src/contract/contracthost.cpp index 35aa3399..0b5e4fd8 100644 --- a/src/contract/contracthost.cpp +++ b/src/contract/contracthost.cpp @@ -2,8 +2,9 @@ ContractHost::~ContractHost() { if (mustRevert_) { - for (auto& var : this->stack_.getUsedVars()) + for (auto& var : this->stack_.getUsedVars()) { var.get().revert(); + } context_.revert(); } else { diff --git a/src/contract/dynamiccontract.h b/src/contract/dynamiccontract.h index 8819eb7e..2595526f 100644 --- a/src/contract/dynamiccontract.h +++ b/src/contract/dynamiccontract.h @@ -394,24 +394,20 @@ class DynamicContract : public BaseContract { Bytes evmEthCall(const evmc_message& callInfo, ContractHost* host) final { this->host_ = host; PointerNullifier nullifier(this->host_); - try { - Functor funcName = Utils::getFunctor(callInfo); - if (this->isPayableFunction(funcName)) { - auto func = this->evmFunctions_.find(funcName); - if (func == this->evmFunctions_.end()) throw DynamicException("Functor not found for payable function"); - return func->second(callInfo); - } else { - // value is a uint8_t[32] C array, we need to check if it's zero in modern C++ - if (!evmc::is_zero(callInfo.value)) { - // If the value is not zero, we need to throw an exception - throw DynamicException("Non-payable function called with value"); - } - auto func = this->evmFunctions_.find(funcName); - if (func == this->evmFunctions_.end()) throw DynamicException("Functor not found for non-payable function"); - return func->second(callInfo); + Functor funcName = Utils::getFunctor(callInfo); + if (this->isPayableFunction(funcName)) { + auto func = this->evmFunctions_.find(funcName); + if (func == this->evmFunctions_.end()) throw DynamicException("Functor not found for payable function"); + return func->second(callInfo); + } else { + // value is a uint8_t[32] C array, we need to check if it's zero in modern C++ + if (!evmc::is_zero(callInfo.value)) { + // If the value is not zero, we need to throw an exception + throw DynamicException("Non-payable function called with value"); } - } catch (const std::exception& e) { - throw DynamicException(e.what()); + auto func = this->evmFunctions_.find(funcName); + if (func == this->evmFunctions_.end()) throw DynamicException("Functor not found for non-payable function"); + return func->second(callInfo); } } @@ -424,14 +420,11 @@ class DynamicContract : public BaseContract { Bytes ethCallView(const evmc_message& data, ContractHost* host) const override { this->host_ = host; PointerNullifier nullifier(this->host_); - try { - Functor funcName = Utils::getFunctor(data); - auto func = this->viewFunctions_.find(funcName); - if (func == this->viewFunctions_.end()) throw DynamicException("Functor not found"); - return func->second(data); - } catch (std::exception& e) { - throw DynamicException(e.what()); - } + Functor funcName = Utils::getFunctor(data); + auto func = this->viewFunctions_.find(funcName); + if (func == this->viewFunctions_.end()) + throw DynamicException("Functor not found"); + return func->second(data); } /** @@ -588,17 +581,14 @@ class DynamicContract : public BaseContract { template R callContractFunction( ContractHost* contractHost, R (C::*func)(const Args&...), const Args&... args ) { - try { - // We don't want to ever overwrite the host_ pointer if it's already set (nested calls) - if (this->host_ == nullptr) { - this->host_ = contractHost; - PointerNullifier nullifier(this->host_); - return (static_cast(this)->*func)(args...); - } - return (static_cast(this)->*func)(args...); - } catch (const std::exception& e) { - throw DynamicException(e.what()); + // We don't want to ever overwrite the host_ pointer if it's already set (nested calls) + PointerNullifier nullifier(this->host_); + + if (this->host_ == nullptr) { + this->host_ = contractHost; } + + return (static_cast(this)->*func)(args...); } /** diff --git a/src/contract/messages/cppcontractexecutor.h b/src/contract/messages/cppcontractexecutor.h index f08b0996..92e333b5 100644 --- a/src/contract/messages/cppcontractexecutor.h +++ b/src/contract/messages/cppcontractexecutor.h @@ -33,12 +33,18 @@ class CppContractExecutor { private: decltype(auto) callContract(concepts::PackedMessage auto&& msg) { + auto& contract = getContractAs>(msg.to()); + transactional::Group guard = { - transactional::checkpoint(msg.caller().caller_), - transactional::checkpoint(msg.caller().value_) + transactional::checkpoint(contract.caller_), + transactional::checkpoint(contract.value_) }; - auto& contract = getContractAs>(msg.to()); + const Address caller(msg.from()); + const uint256_t value = messageValueOrZero(msg); + + contract.caller_ = caller; + contract.value_ = value; return std::apply([&] (auto&&... args) { if constexpr (concepts::StaticCallMessage) { @@ -65,8 +71,15 @@ class CppContractExecutor { }; auto& contract = context_.getContract(msg.to()); - contract.caller_ = Address(msg.from()); - contract.value_ = messageValueOrZero(msg); + transactional::Group guard = { + transactional::checkpoint(contract.caller_), + transactional::checkpoint(contract.value_) + }; + Address caller(msg.from()); + uint256_t value = messageValueOrZero(msg); + + contract.caller_ = caller; + contract.value_ = value; return contract.evmEthCall(evmcMsg, &host_); } @@ -113,8 +126,11 @@ class CppContractExecutor { transactional::checkpoint(contract.value_) }; - contract.caller_ = Address(msg.from()); // TODO: these copies can be avoided - contract.value_ = 0; + Address caller(msg.from()); + uint256_t value = 0; + + contract.caller_ = caller; + contract.value_ = value; // TODO: value 0 ALWAYS? const Address contractAddress = generateContractAddress(context_.getAccount(msg.from()).nonce, msg.from()); contract.ethCall(evmcMsg, &host_); diff --git a/src/contract/messages/executioncontext.cpp b/src/contract/messages/executioncontext.cpp index 161ae7cc..74e197c1 100644 --- a/src/contract/messages/executioncontext.cpp +++ b/src/contract/messages/executioncontext.cpp @@ -73,6 +73,10 @@ void ExecutionContext::addAccount(View
address, Account account) { } void ExecutionContext::addContract(View
address, std::unique_ptr contract) { + if (contract == nullptr) { + throw DynamicException("attempt to insert null contract"); + } + const auto [iterator, inserted] = contracts_.emplace(address, std::move(contract)); if (!inserted) { @@ -97,13 +101,6 @@ void ExecutionContext::notifyNewContract(View
address, BaseContract* co }))); } -void addContract(View
address, BaseContract* contract) { - // TODO: ??? - // TODO: take as std::unique_ptr and save the contract? - // TODO: analyse ContractHost and see what is done when a C++ contract is created - // TODO: remember that this function must also be called for EVM contracts (nullptr in this case) -} - void ExecutionContext::transferBalance(View
fromAddress, View
toAddress, const uint256_t& amount) { Account& sender = getMutableAccount(fromAddress); Account& recipient = getMutableAccount(toAddress); diff --git a/src/contract/messages/traits.h b/src/contract/messages/traits.h index eaa2b7bb..a430bf58 100644 --- a/src/contract/messages/traits.h +++ b/src/contract/messages/traits.h @@ -71,7 +71,7 @@ struct MessageContractHelper { }; template -using MessageContract = MessageContractHelper::Type; +using MessageContract = std::remove_cvref_t::Type>; } // namespace traits From af87fe37b2eded461065ac9267e3a1d62921a160 Mon Sep 17 00:00:00 2001 From: Leonardo Amalaral Date: Thu, 19 Dec 2024 16:12:39 -0300 Subject: [PATCH 12/37] more bugs fixed --- src/contract/messages/evmcontractexecutor.cpp | 19 ------------ src/contract/messages/messagedispatcher.h | 31 +++++++++++++++++-- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/contract/messages/evmcontractexecutor.cpp b/src/contract/messages/evmcontractexecutor.cpp index 40fe8342..a028bee1 100644 --- a/src/contract/messages/evmcontractexecutor.cpp +++ b/src/contract/messages/evmcontractexecutor.cpp @@ -120,19 +120,12 @@ static void createContractImpl(auto& msg, ExecutionContext& context, View 0) { - context_.transferBalance(msg.from(), msg.to(), msg.value()); - } - const Bytes output = executeEvmcMessage(this->vm_, &this->get_interface(), this->to_context(), makeEvmcMessage(msg, depth_), msg.gas(), context_.getAccount(msg.to()).code); - checkpoint.commit(); - return output; } @@ -147,38 +140,26 @@ Bytes EvmContractExecutor::execute(EncodedStaticCallMessage& msg) { } Bytes EvmContractExecutor::execute(EncodedDelegateCallMessage& msg) { - auto checkpoint = context_.checkpoint(); auto depthGuard = transactional::copy(depth_); ++depth_; - // TODO: is the value transfer correct for delegate calls? - if (msg.value() > 0) { - context_.transferBalance(msg.from(), msg.to(), msg.value()); - } - const Bytes output = executeEvmcMessage(this->vm_, &this->get_interface(), this->to_context(), makeEvmcMessage(msg, depth_), msg.gas(), context_.getAccount(msg.codeAddress()).code); - checkpoint.commit(); - return output; } Address EvmContractExecutor::execute(EncodedCreateMessage& msg) { - auto checkpoint = context_.checkpoint(); auto depthGuard = transactional::copy(depth_); const Address contractAddress = generateContractAddress(context_.getAccount(msg.from()).nonce, msg.from()); createContractImpl(msg, context_, contractAddress, vm_, *this, ++depth_); - checkpoint.commit(); return contractAddress; } Address EvmContractExecutor::execute(EncodedSaltCreateMessage& msg) { - auto checkpoint = context_.checkpoint(); auto depthGuard = transactional::copy(depth_); const Address contractAddress = generateContractAddress(msg.from(), msg.salt(), msg.code()); createContractImpl(msg, context_, contractAddress, vm_, *this, ++depth_); - checkpoint.commit(); return contractAddress; } diff --git a/src/contract/messages/messagedispatcher.h b/src/contract/messages/messagedispatcher.h index d86219aa..43b37209 100644 --- a/src/contract/messages/messagedispatcher.h +++ b/src/contract/messages/messagedispatcher.h @@ -15,16 +15,43 @@ class MessageDispatcher { template decltype(auto) onMessage(M&& msg) { + using Result = traits::MessageResult; + const Account& account = context_.getAccount(messageCodeAddress(msg)); if (!account.isContract()) { throw DynamicException("Not a contract address"); } + auto checkpoint = context_.checkpoint(); + + if constexpr (concepts::HasValueField) { + if (msg.value() > 0) { + context_.transferBalance(msg.from(), msg.to(), msg.value()); + } + } + + // TODO: to much code repetition, you can do better than this. if (account.contractType == ContractType::CPP) { - return cppExecutor_.execute(std::forward(msg)); + if constexpr (std::same_as) { + cppExecutor_.execute(std::forward(msg)); + checkpoint.commit(); + return; + } else { + decltype(auto) result = cppExecutor_.execute(std::forward(msg)); + checkpoint.commit(); + return result; + } } else { - return evmExecutor_.execute(std::forward(msg)); + if constexpr (std::same_as) { + evmExecutor_.execute(std::forward(msg)); + checkpoint.commit(); + return; + } else { + decltype(auto) result = evmExecutor_.execute(std::forward(msg)); + checkpoint.commit(); + return result; + } } } From ae2be6f81e9a14f74ffe71f553bbda8b4fec72cf Mon Sep 17 00:00:00 2001 From: Leonardo Amalaral Date: Sat, 21 Dec 2024 11:25:10 -0300 Subject: [PATCH 13/37] call tracer unfinished (not compilable) --- src/contract/CMakeLists.txt | 2 +- src/contract/calltracer.cpp | 180 ----------------- src/contract/calltracer.h | 121 ++++++----- src/contract/contracthost.cpp | 10 + src/contract/contracthost.h | 48 +++-- src/contract/messages/common.h | 19 ++ src/contract/messages/evmcontractexecutor.h | 5 + src/contract/trace/calltrace.cpp | 48 +++++ src/contract/trace/calltrace.h | 70 +++++++ tests/contract/calltracer.cpp | 42 ++-- .../contract/messages/evmcontractexecutor.cpp | 188 +++++++++--------- 11 files changed, 358 insertions(+), 375 deletions(-) delete mode 100644 src/contract/calltracer.cpp create mode 100644 src/contract/trace/calltrace.cpp create mode 100644 src/contract/trace/calltrace.h diff --git a/src/contract/CMakeLists.txt b/src/contract/CMakeLists.txt index 0177e2b8..585f418e 100644 --- a/src/contract/CMakeLists.txt +++ b/src/contract/CMakeLists.txt @@ -52,7 +52,7 @@ set(CONTRACT_SOURCES ${CMAKE_SOURCE_DIR}/src/contract/contracthost.cpp ${CMAKE_SOURCE_DIR}/src/contract/contractmanager.cpp ${CMAKE_SOURCE_DIR}/src/contract/dynamiccontract.cpp - ${CMAKE_SOURCE_DIR}/src/contract/calltracer.cpp + ${CMAKE_SOURCE_DIR}/src/contract/trace/calltrace.cpp ${CMAKE_SOURCE_DIR}/src/contract/event.cpp ${CMAKE_SOURCE_DIR}/src/contract/messages/common.cpp ${CMAKE_SOURCE_DIR}/src/contract/messages/executioncontext.cpp diff --git a/src/contract/calltracer.cpp b/src/contract/calltracer.cpp deleted file mode 100644 index 4f2cb557..00000000 --- a/src/contract/calltracer.cpp +++ /dev/null @@ -1,180 +0,0 @@ -#include "calltracer.h" -#include "bytes/join.h" - -namespace trace { - -static Call::Type getCallType(const evmc_message& msg) { - using enum Call::Type; - - if (msg.kind == EVMC_CALL) - return (msg.flags == EVMC_STATIC) ? STATICCALL : CALL; - - if (msg.kind == EVMC_DELEGATECALL) - return DELEGATECALL; - - throw DynamicException("evmc_message is not from a function call"); -} - -Bytes encodeRevertReason(std::string_view reason) { - FixedBytes<32> reasonEncoded{}; - - const size_t count = std::min(reason.size(), reasonEncoded.size()); - std::copy_n(reason.begin(), count, reasonEncoded.begin()); - - const uint256_t size(reason.size()); - const FixedBytes<32> sizeEncoded(Utils::uint256ToBytes(size)); - - return Utils::makeBytes(bytes::join( - Hex::toBytes("0x08c379a0"), - Hex::toBytes("0x0000000000000000000000000000000000000000000000000000000000000020"), - sizeEncoded, - reasonEncoded - )); -} - -std::string decodeRevertReason(View data) { - if (data.size() != 100) { - throw DynamicException("Encoded revert reason is expected to have exactly 100 bytes"); - } - - const size_t size = Utils::bytesToUint256(data.subspan(36, 32)).convert_to(); - - std::string res; - res.reserve(size); - std::ranges::copy(data.subspan(68, size), std::back_inserter(res)); - return res; -} - -Call::Call(const evmc_message& msg) - : type(getCallType(msg)), - from(msg.sender), - to(msg.recipient), - value(msg.value.bytes), - gas(msg.gas), - gasUsed(0), - input(Utils::makeBytes(View(msg.input_data, msg.input_size))) {} - -json Call::toJson() const { - using enum Call::Type; - - json res; - - switch (this->type) { - case CALL: - res["type"] = "CALL"; - break; - - case STATICCALL: - res["type"] = "STATICCALL"; - break; - - case DELEGATECALL: - res["type"] = "DELEGATECALL"; - break; - } - - res["from"] = this->from.hex(true); - res["to"] = this->to.hex(true); - - const uint256_t value = Utils::bytesToUint256(this->value); - res["value"] = Hex::fromBytes(Utils::uintToBytes(value), true).forRPC(); - - res["gas"] = Hex::fromBytes(Utils::uintToBytes(this->gas), true).forRPC(); - res["gasUsed"] = Hex::fromBytes(Utils::uintToBytes(this->gasUsed), true).forRPC(); - res["input"] = Hex::fromBytes(this->input, true); - - if (!this->output.empty()) { - res["output"] = Hex::fromBytes(this->output, true); - } - - switch (this->status) { - case Status::EXECUTION_REVERTED: { - res["error"] = "execution reverted"; - try { - std::string revertReason = decodeRevertReason(this->output); - res["revertReason"] = std::move(revertReason); - } catch (const std::exception& ignored) {} - break; - } - - case Status::OUT_OF_GAS: - res["error"] = "out of gas"; - break; - } - - if (!this->calls.empty()) { - res["calls"] = json::array(); - - for (const auto& subcall : this->calls) - res["calls"].push_back(subcall.toJson()); - } - - return res; -} - -CallTracer::CallTracer(Call rootCall) : root_(std::make_unique(std::move(rootCall))) { - stack_.emplace_back(root_.get()); -} - -const Call& CallTracer::root() const { - if (!hasCalls()) - throw DynamicException("root call does not exists since no call was traced"); - - return *root_; -} - -const Call& CallTracer::current() const { - if (!hasCalls()) - throw DynamicException("current call does not exists since no call was traced"); - - if (isFinished()) - throw DynamicException("call tracer is already finished, no call currently opened"); - - return *stack_.back(); -} - -void CallTracer::push(Call call) { - if (stack_.empty()) [[unlikely]] { - root_ = std::make_unique(std::move(call)); - stack_.emplace_back(root_.get()); - return; - } - - Call& currCall = *stack_.back(); - Call& newCall = currCall.calls.emplace_back(std::move(call)); - stack_.emplace_back(&newCall); -} - -void CallTracer::pop(Bytes output, Status status, uint64_t gasUsed) { - if (stack_.empty()) [[unlikely]] { - throw DynamicException("No function start was traced yet"); - } - - Call& curr = *stack_.back(); - curr.output = std::move(output); - curr.status = status; - curr.gasUsed = gasUsed; - stack_.pop_back(); -} - -void CallTracer::callStarted(Call call) { - this->push(std::move(call)); -} - -void CallTracer::callOutOfGas() { - this->pop(Bytes(), Status::OUT_OF_GAS, this->current().gas); -} - -void CallTracer::callReverted(uint64_t gasUsed) { - this->pop(Bytes(), Status::EXECUTION_REVERTED, gasUsed); -} - -void CallTracer::callReverted(Bytes output, uint64_t gasUsed) { - this->pop(std::move(output), Status::EXECUTION_REVERTED, gasUsed); -} - -void CallTracer::callSucceeded(Bytes output, uint64_t gasUsed) { - this->pop(std::move(output), Status::SUCCEEDED, gasUsed); -} - -} // namespace trace diff --git a/src/contract/calltracer.h b/src/contract/calltracer.h index 3017d0b3..1e2d8105 100644 --- a/src/contract/calltracer.h +++ b/src/contract/calltracer.h @@ -1,85 +1,80 @@ #ifndef CONTRACT_CALLTRACER_H #define CONTRACT_CALLTRACER_H -#include -#include -#include -#include "../utils/utils.h" -#include "../utils/strings.h" -#include "../libs/zpp_bits.h" - -namespace trace { - -Bytes encodeRevertReason(std::string_view reason); - -std::string decodeRevertReason(View data); - -enum class Status { - SUCCEEDED, - EXECUTION_REVERTED, - OUT_OF_GAS -}; - -struct Call { - enum class Type { - CALL, - STATICCALL, - DELEGATECALL - }; - - Call() = default; - - explicit Call(const evmc_message& msg); - - json toJson() const; - - using serialize = zpp::bits::members<10>; - - Type type; - Status status; - Address from; - Address to; - FixedBytes<32> value; - uint64_t gas; - uint64_t gasUsed; - Bytes input; - Bytes output; - boost::container::stable_vector calls; +#include "utils/utils.h" +#include "contract/messages/concepts.h" +#include "contract/messages/outofgas.h" +#include "contract/trace/calltrace.h" +#include "contract/messages/traits.h" +#include "contract/abi.h" + +template +class Defer { +public: + explicit constexpr Defer(F func) : func_(std::move(func)) {} + constexpr ~Defer() { std::invoke(func_); } +private: + F func_; }; +template class CallTracer { -private: - std::unique_ptr root_; - std::deque stack_; +public: + explicit CallTracer(MessageHandler handler) : handler_(std::move(handler)), rootCall_(), callStack_() {} - void push(Call call); + template + decltype(auto) onMessage(Message&& msg) { + using Result = traits::MessageResult; - void pop(Bytes output, Status status, uint64_t gasUsed); + trace::Call& callTrace = callStack_.empty() + ? *(rootCall_ = std::make_unique(msg)) + : callStack_.top()->calls.emplace_back(msg); -public: - CallTracer() = default; + callStack_.push(&callTrace); - explicit CallTracer(Call rootCall); + Defer finally([this, &gas = msg.gas(), &callTrace] () { + callTrace.gasUsed = callTrace.gas - gas.value(); + callStack_.pop(); + }); - bool hasCalls() const noexcept { return bool(root_); } + try { + if constexpr (not std::same_as) { + Result result = handler_.onMessage(std::forward(msg)); + callTrace.output = ABI::Encoder::encodeData(result); + return result; + } - bool isFinished() const noexcept { return root_ != nullptr && stack_.empty(); } + handler_.onMessage(std::forward(msg)); + } catch (const OutOfGas& outOfGas) { + callTrace.status = trace::CallStatus::OUT_OF_GAS; + throw outOfGas; + } catch (const std::exception& error) { + callTrace.status = trace::CallStatus::EXECUTION_REVERTED; - const Call& root() const; + if (error.what()) { + callTrace.output = ABI::Encoder::encodeError(error.what()); + } - const Call& current() const; + throw error; + } + } - void callStarted(Call call); + decltype(auto) onMessage(concepts::CreateMessage auto&& msg) { + return handler_.onMessage(std::forward(msg)); // TODO: are call messages really just forwarded? + } - void callOutOfGas(); + const MessageHandler& getHandler() const { return handler_; } - void callReverted(uint64_t gasUsed); + MessageHandler& getHandler() { return handler_; } - void callReverted(Bytes output, uint64_t gasUsed); + bool hasCallTrace() const { return rootCall_ != nullptr; } - void callSucceeded(Bytes output, uint64_t gasUsed); -}; + const trace::Call& getCallTrace() const { return *rootCall_; } -} // namespace trace +private: + MessageHandler handler_; + std::unique_ptr rootCall_; + std::stack callStack_; +}; #endif // CONTRACT_CALLTRACER_H diff --git a/src/contract/contracthost.cpp b/src/contract/contracthost.cpp index 0b5e4fd8..b27c8d67 100644 --- a/src/contract/contracthost.cpp +++ b/src/contract/contracthost.cpp @@ -1,5 +1,15 @@ #include "contracthost.h" +MessageHandler makeMessageHandler(ContractHost& host, ExecutionContext& context, evmc_vm *vm, Storage& storage) { + MessageDispatcher dispatcher(context, CppContractExecutor(context, host), EvmContractExecutor(context, vm)); + + if (context.getTxHash() && storage.getIndexingMode() == IndexingMode::RPC_TRACE) { + return CallTracer(std::move(dispatcher)); + } + + return dispatcher; +} + ContractHost::~ContractHost() { if (mustRevert_) { for (auto& var : this->stack_.getUsedVars()) { diff --git a/src/contract/contracthost.h b/src/contract/contracthost.h index 6a465886..61e66d77 100644 --- a/src/contract/contracthost.h +++ b/src/contract/contracthost.h @@ -24,6 +24,7 @@ #include "messages/executioncontext.h" #include "messages/messagedispatcher.h" #include "messages/packedmessages.h" +#include "calltracer.h" // TODO: EVMC Static Mode Handling // TODO: Contract creating other contracts (EVM Factories) @@ -54,7 +55,11 @@ using namespace evmc::literals; const auto ZERO_ADDRESS = 0x0000000000000000000000000000000000000000_address; const auto BDK_PRECOMPILE = 0x1000000000000000000000000000100000000001_address; -using MessageHandler = MessageDispatcher; +// std::unique_ptr makeMessageHandler(ContractHost& host, evmc_vm *vm, ExecutionContext& context, Storage& storage); + +using MessageHandler = std::variant>; + +MessageHandler makeMessageHandler(ContractHost& host, ExecutionContext& context, evmc_vm *vm, Storage& storage); class ContractHost { private: @@ -77,11 +82,12 @@ class ContractHost { randomGen_(randomnessSeed), stack_(), context_(context), - messageHandler_( - context_, - CppContractExecutor(context_, *this), - EvmContractExecutor(AnyEncodedMessageHandler::from(messageHandler_), context_, vm) - ) {} + messageHandler_(makeMessageHandler(*this, context, vm, storage)) { + std::visit(Utils::Overloaded{ + [] (MessageDispatcher& handler) { handler.evmExecutor().setMessageHandler(AnyEncodedMessageHandler::from(handler)); }, + [] (CallTracer& tracer) { tracer.getHandler().evmExecutor().setMessageHandler(AnyEncodedMessageHandler::from(tracer)); } + }, messageHandler_); + } // Rule of five, no copy/move allowed. ContractHost(const ContractHost&) = delete; @@ -100,7 +106,7 @@ class ContractHost { try { mustRevert_ = false; msg.gas().use(21000); - return messageHandler_.onMessage(std::forward(msg)); + return dispatchMessage(std::forward(msg)); } catch (const std::exception& err) { mustRevert_ = true; throw err; @@ -123,12 +129,12 @@ class ContractHost { PackedStaticCallMessage msg( caller->getContractAddress(), targetAddr, - messageHandler_.cppExecutor().currentGas(), + this->getCurrentGas(), *caller, func, args...); - return messageHandler_.onMessage(std::move(msg)); + return this->dispatchMessage(std::move(msg)); } template @@ -136,17 +142,16 @@ class ContractHost { BaseContract* caller, const Address& targetAddr, const uint256_t& value, R(C::*func)(const Args&...), const Args&... args) { - PackedCallMessage msg( caller->getContractAddress(), targetAddr, - messageHandler_.cppExecutor().currentGas(), + this->getCurrentGas(), value, *caller, func, args...); - return messageHandler_.onMessage(std::move(msg)); + return this->dispatchMessage(std::move(msg)); } /** @@ -160,16 +165,15 @@ class ContractHost { template Address callCreateContract(BaseContract& caller, Args&&... args) { const uint256_t value = 0; - PackedCreateMessage msg( caller.getContractAddress(), - messageHandler_.cppExecutor().currentGas(), + this->getCurrentGas(), value, caller, std::forward(args)... ); - return messageHandler_.onMessage(std::move(msg)); + return this->dispatchMessage(std::move(msg)); } /** @@ -228,7 +232,19 @@ class ContractHost { uint256_t getRandomValue() const { return std::invoke(this->randomGen_); } - /// END OF CONTRACT INTERFACING FUNCTIONS +private: + decltype(auto) dispatchMessage(auto&& msg) { + return std::visit([&msg] (auto& handler) { + return handler.onMessage(std::forward(msg)); + }, messageHandler_); + } + + messages::Gas& getCurrentGas() { + return std::visit(Utils::Overloaded{ + [] (MessageDispatcher& handler) -> messages::Gas& { return handler.cppExecutor().currentGas(); }, + [] (CallTracer& tracer) -> messages::Gas& { return tracer.getHandler().cppExecutor().currentGas(); } + }, messageHandler_); + } }; #endif // CONTRACT_HOST_H diff --git a/src/contract/messages/common.h b/src/contract/messages/common.h index 24cf9434..538e8364 100644 --- a/src/contract/messages/common.h +++ b/src/contract/messages/common.h @@ -2,6 +2,7 @@ #define BDK_MESSAGES_COMMON_H #include "utils/utils.h" +#include "utils/contractreflectioninterface.h" #include "concepts.h" Address generateContractAddress(uint64_t nonce, View
address); @@ -40,4 +41,22 @@ constexpr Hash messageSaltOrDefault(const auto& msg) { } } +Bytes messageInputEncoded(const concepts::EncodedMessage auto& msg) { + return Bytes(msg.input()); +} + +Bytes messageInputEncoded(const concepts::PackedMessage auto& msg) { + return std::apply([&] (const auto&... args) -> Bytes { + const std::string functionName = ContractReflectionInterface::getFunctionName(msg.method()); + const BytesArr<4> encodedFunctor = Utils::uint32ToBytes(ABI::FunctorEncoder::encode(functionName).value); + + if constexpr (sizeof...(args) > 0) { + const Bytes encodedArgs = ABI::Encoder::encodeData(args...); + return Utils::makeBytes(bytes::join(encodedFunctor, encodedArgs)); + } + + return Utils::makeBytes(encodedFunctor); + }, msg.args()); +} + #endif // BDK_MESSAGES_COMMON_H diff --git a/src/contract/messages/evmcontractexecutor.h b/src/contract/messages/evmcontractexecutor.h index d2ed5761..bac40cf8 100644 --- a/src/contract/messages/evmcontractexecutor.h +++ b/src/contract/messages/evmcontractexecutor.h @@ -15,6 +15,11 @@ class EvmContractExecutor : public evmc::Host { AnyEncodedMessageHandler messageHandler, ExecutionContext& context, evmc_vm *vm) : messageHandler_(messageHandler), context_(context), vm_(vm), transientStorage_(), depth_(0) {} + EvmContractExecutor(ExecutionContext& context, evmc_vm *vm) + : context_(context), vm_(vm), transientStorage_(), depth_(0) {} + + void setMessageHandler(AnyEncodedMessageHandler messageHandler) { messageHandler_ = messageHandler; } + Bytes execute(EncodedCallMessage& msg); Bytes execute(EncodedStaticCallMessage& msg); diff --git a/src/contract/trace/calltrace.cpp b/src/contract/trace/calltrace.cpp new file mode 100644 index 00000000..b464940c --- /dev/null +++ b/src/contract/trace/calltrace.cpp @@ -0,0 +1,48 @@ +#include "calltrace.h" +#include "contract/abi.h" + +namespace trace { + +json Call::toJson() const { + using enum CallType; + json res; + + switch (this->type) { + case CALL: res["type"] = "CALL"; break; + case STATICCALL: res["type"] = "STATICCALL"; break; + case DELEGATECALL: res["type"] = "DELEGATECALL"; break; + } + + res["from"] = this->from.hex(true); + res["to"] = this->to.hex(true); + + const uint256_t value = Utils::bytesToUint256(this->value); + res["value"] = Hex::fromBytes(Utils::uintToBytes(value), true).forRPC(); + + res["gas"] = Hex::fromBytes(Utils::uintToBytes(this->gas), true).forRPC(); + res["gasUsed"] = Hex::fromBytes(Utils::uintToBytes(this->gasUsed), true).forRPC(); + res["input"] = Hex::fromBytes(this->input, true); + + if (!this->output.empty()) res["output"] = Hex::fromBytes(this->output, true); + + switch (this->status) { + case CallStatus::EXECUTION_REVERTED: { + res["error"] = "execution reverted"; + try { + std::string revertReason = ABI::Decoder::decodeError(this->output); + res["revertReason"] = std::move(revertReason); + } catch (const std::exception& ignored) {} + break; + } + case CallStatus::OUT_OF_GAS: res["error"] = "out of gas"; break; + } + + if (!this->calls.empty()) { + res["calls"] = json::array(); + for (const auto& subcall : this->calls) res["calls"].push_back(subcall.toJson()); + } + + return res; +} + +} // namespace trace diff --git a/src/contract/trace/calltrace.h b/src/contract/trace/calltrace.h new file mode 100644 index 00000000..8ac28003 --- /dev/null +++ b/src/contract/trace/calltrace.h @@ -0,0 +1,70 @@ +#ifndef BDK_CONTRACT_CALLTRACE_H +#define BDK_CONTRACT_CALLTRACE_H + +#include +#include "libs/zpp_bits.h" +#include "utils/bytes.h" +#include "utils/fixedbytes.h" +#include "utils/address.h" +#include "utils/utils.h" +#include "contract/messages/concepts.h" + +namespace trace { + +enum class CallType { + CALL, + STATICCALL, + DELEGATECALL +}; + +template +constexpr CallType getMessageCallType(const M& msg) { + if constexpr (concepts::DelegateCallMessage) { + return CallType::DELEGATECALL; + } else if (concepts::StaticCallMessage) { + return CallType::STATICCALL; + } else { + return CallType::CALL; + } +} + +enum class CallStatus { + SUCCEEDED, + EXECUTION_REVERTED, + OUT_OF_GAS +}; + +struct Call { + Call() = default; + + explicit Call(const concepts::CallMessage auto& msg) + : type(getMessageCallType(msg)), + status(CallStatus::SUCCEEDED), + from(msg.from()), + to(msg.to()), + value(Utils::uint256ToBytes(messageValueOrZero(msg))), + gas(msg.gas().value()), + gasUsed(0), + input(messageInputEncoded(msg)), + output(), + calls() {} + + json toJson() const; + + using serialize = zpp::bits::members<10>; + + CallType type; + CallStatus status; + Address from; + Address to; + FixedBytes<32> value; + uint64_t gas; + uint64_t gasUsed; + Bytes input; + Bytes output; + boost::container::stable_vector calls; +}; + +} // namespace trace + +#endif // BDK_CONTRACT_CALLTRACE_H diff --git a/tests/contract/calltracer.cpp b/tests/contract/calltracer.cpp index 7224a9f6..52ecc646 100644 --- a/tests/contract/calltracer.cpp +++ b/tests/contract/calltracer.cpp @@ -98,7 +98,7 @@ TEST_CASE("CallTracer Tests", "[trace]") { std::optional callTrace = sdk.getStorage().getCallTrace(txHash); REQUIRE(callTrace); - REQUIRE(callTrace->type == trace::Call::Type::CALL); + REQUIRE(callTrace->type == trace::CallType::CALL); REQUIRE(callTrace->from == sdk.getOptions().getChainOwner()); REQUIRE(callTrace->to == contractAddress); REQUIRE(callTrace->value == FixedBytes<32>()); @@ -118,7 +118,7 @@ TEST_CASE("CallTracer Tests", "[trace]") { callTrace = sdk.getStorage().getCallTrace(txHash); REQUIRE(callTrace); - REQUIRE(callTrace->type == trace::Call::Type::CALL); + REQUIRE(callTrace->type == trace::CallType::CALL); REQUIRE(callTrace->from == sdk.getOptions().getChainOwner()); REQUIRE(callTrace->to == contractAddress); REQUIRE(callTrace->value == FixedBytes<32>()); @@ -154,7 +154,7 @@ TEST_CASE("CallTracer Tests", "[trace]") { std::optional callTrace = sdk.getStorage().getCallTrace(txHash); REQUIRE(callTrace); - REQUIRE(callTrace->type == trace::Call::Type::CALL); + REQUIRE(callTrace->type == trace::CallType::CALL); REQUIRE(callTrace->from == sdk.getOptions().getChainOwner()); REQUIRE(callTrace->to == testProxyContractAddress); REQUIRE(callTrace->value == FixedBytes<32>()); @@ -167,7 +167,7 @@ TEST_CASE("CallTracer Tests", "[trace]") { const trace::Call& nestedCall = callTrace->calls.front(); - REQUIRE(nestedCall.type == trace::Call::Type::CALL); + REQUIRE(nestedCall.type == trace::CallType::CALL); REQUIRE(nestedCall.from == testProxyContractAddress); REQUIRE(nestedCall.to == testContractAddress); REQUIRE(nestedCall.value == FixedBytes<32>()); @@ -204,8 +204,8 @@ TEST_CASE("CallTracer Tests", "[trace]") { const auto depositCallTrace = sdk.getStorage().getCallTrace(depositTx); REQUIRE(approveCallTrace); - REQUIRE(approveCallTrace->type == trace::Call::Type::CALL); - REQUIRE(approveCallTrace->status == trace::Status::SUCCEEDED); + REQUIRE(approveCallTrace->type == trace::CallType::CALL); + REQUIRE(approveCallTrace->status == trace::CallStatus::SUCCEEDED); REQUIRE(approveCallTrace->from == sdk.getOptions().getChainOwner()); REQUIRE(approveCallTrace->to == erc20); REQUIRE(approveCallTrace->value == FixedBytes<32>()); @@ -214,16 +214,16 @@ TEST_CASE("CallTracer Tests", "[trace]") { REQUIRE(approveCallTrace->calls.empty()); REQUIRE(depositCallTrace); - REQUIRE(depositCallTrace->type == trace::Call::Type::CALL); - REQUIRE(depositCallTrace->status == trace::Status::SUCCEEDED); + REQUIRE(depositCallTrace->type == trace::CallType::CALL); + REQUIRE(depositCallTrace->status == trace::CallStatus::SUCCEEDED); REQUIRE(depositCallTrace->from == sdk.getOptions().getChainOwner()); REQUIRE(depositCallTrace->to == erc20Wrapper); REQUIRE(depositCallTrace->value == FixedBytes<32>()); REQUIRE(depositCallTrace->input == Hex::toBytes("0x47e7ef240000000000000000000000005b41cef7f46a4a147e31150c3c5ffd077e54d0e100000000000000000000000000000000000000000000000006f05b59d3b20000")); REQUIRE(depositCallTrace->output == Bytes()); REQUIRE(!depositCallTrace->calls.empty()); - REQUIRE(depositCallTrace->calls[0].type == trace::Call::Type::CALL); - REQUIRE(depositCallTrace->calls[0].status == trace::Status::SUCCEEDED); + REQUIRE(depositCallTrace->calls[0].type == trace::CallType::CALL); + REQUIRE(depositCallTrace->calls[0].status == trace::CallStatus::SUCCEEDED); REQUIRE(depositCallTrace->calls[0].from == erc20Wrapper); REQUIRE(depositCallTrace->calls[0].to == erc20); REQUIRE(depositCallTrace->calls[0].value == FixedBytes<32>()); @@ -254,34 +254,34 @@ TEST_CASE("CallTracer Tests", "[trace]") { const auto payCallTrace = sdk.getStorage().getCallTrace(payTxHash); REQUIRE(errorCallTrace); - REQUIRE(errorCallTrace->type == trace::Call::Type::CALL); - REQUIRE(errorCallTrace->status == trace::Status::SUCCEEDED); + REQUIRE(errorCallTrace->type == trace::CallType::CALL); + REQUIRE(errorCallTrace->status == trace::CallStatus::SUCCEEDED); REQUIRE(errorCallTrace->from == sdk.getOptions().getChainOwner()); REQUIRE(errorCallTrace->to == userAddress); REQUIRE(errorCallTrace->value == FixedBytes<32>()); REQUIRE(errorCallTrace->input == Hex::toBytes("0x7f3358bc0000000000000000000000005b41cef7f46a4a147e31150c3c5ffd077e54d0e100000000000000000000000000000000000000000000000000000000000001f5")); REQUIRE(errorCallTrace->output == Bytes(32)); REQUIRE(!errorCallTrace->calls.empty()); - REQUIRE(errorCallTrace->calls[0].type == trace::Call::Type::CALL); - REQUIRE(errorCallTrace->calls[0].status == trace::Status::EXECUTION_REVERTED); + REQUIRE(errorCallTrace->calls[0].type == trace::CallType::CALL); + REQUIRE(errorCallTrace->calls[0].status == trace::CallStatus::EXECUTION_REVERTED); REQUIRE(errorCallTrace->calls[0].from == userAddress); REQUIRE(errorCallTrace->calls[0].to == bankAddress); REQUIRE(errorCallTrace->calls[0].value == FixedBytes<32>()); REQUIRE(errorCallTrace->calls[0].input == Hex::toBytes("0x2e1a7d4d00000000000000000000000000000000000000000000000000000000000001f5")); - REQUIRE(errorCallTrace->calls[0].output == trace::encodeRevertReason("Insufficient funds")); + REQUIRE(errorCallTrace->calls[0].output == ABI::Encoder::encodeError("Insufficient funds")); REQUIRE(errorCallTrace->calls[0].calls.empty()); REQUIRE(successCallTrace); - REQUIRE(successCallTrace->type == trace::Call::Type::CALL); - REQUIRE(successCallTrace->status == trace::Status::SUCCEEDED); + REQUIRE(successCallTrace->type == trace::CallType::CALL); + REQUIRE(successCallTrace->status == trace::CallStatus::SUCCEEDED); REQUIRE(successCallTrace->from == sdk.getOptions().getChainOwner()); REQUIRE(successCallTrace->to == userAddress); REQUIRE(successCallTrace->value == FixedBytes<32>()); REQUIRE(successCallTrace->input == Hex::toBytes("0x7f3358bc0000000000000000000000005b41cef7f46a4a147e31150c3c5ffd077e54d0e1000000000000000000000000000000000000000000000000000000000000012c")); REQUIRE(successCallTrace->output == Hex::toBytes("0x0000000000000000000000000000000000000000000000000000000000000001")); REQUIRE(!successCallTrace->calls.empty()); - REQUIRE(successCallTrace->calls[0].type == trace::Call::Type::CALL); - REQUIRE(successCallTrace->calls[0].status == trace::Status::SUCCEEDED); + REQUIRE(successCallTrace->calls[0].type == trace::CallType::CALL); + REQUIRE(successCallTrace->calls[0].status == trace::CallStatus::SUCCEEDED); REQUIRE(successCallTrace->calls[0].from == userAddress); REQUIRE(successCallTrace->calls[0].to == bankAddress); REQUIRE(successCallTrace->calls[0].value == FixedBytes<32>()); @@ -290,8 +290,8 @@ TEST_CASE("CallTracer Tests", "[trace]") { REQUIRE(successCallTrace->calls[0].calls.empty()); REQUIRE(payCallTrace); - REQUIRE(payCallTrace->type == trace::Call::Type::CALL); - REQUIRE(payCallTrace->status == trace::Status::SUCCEEDED); + REQUIRE(payCallTrace->type == trace::CallType::CALL); + REQUIRE(payCallTrace->status == trace::CallStatus::SUCCEEDED); REQUIRE(payCallTrace->from == sdk.getOptions().getChainOwner()); REQUIRE(payCallTrace->to == bankAddress); REQUIRE(payCallTrace->value == FixedBytes<32>(Utils::uint256ToBytes(uint256_t(4568)))); diff --git a/tests/contract/messages/evmcontractexecutor.cpp b/tests/contract/messages/evmcontractexecutor.cpp index 42634191..ff988318 100644 --- a/tests/contract/messages/evmcontractexecutor.cpp +++ b/tests/contract/messages/evmcontractexecutor.cpp @@ -72,111 +72,111 @@ struct MockedEnvironment { MockedEnvironment() -> MockedEnvironment; TEST_CASE("Evm Message Executor Tests", "[evmcontractexecutor]") { - SECTION("Simple calls") { - MockedEnvironment env; + // SECTION("Simple calls") { + // MockedEnvironment env; - env.accounts.emplace(ORIGIN_ADDRESS, Account(1000000, 0)); + // env.accounts.emplace(ORIGIN_ADDRESS, Account(1000000, 0)); - messages::Gas gas(1000000); - uint256_t value = 0; - const Bytes getCountInput = Utils::makeBytes(bytes::hex("0x06661abd")); - const Bytes getSquaredInput = Utils::makeBytes(bytes::hex("0x11a1d3e80000000000000000000000000000000000000000000000000000000000000019")); - const Address expectedContractAddress = bytes::hex("0x5b41cef7f46a4a147e31150c3c5ffd077e54d0e1"); + // messages::Gas gas(1000000); + // uint256_t value = 0; + // const Bytes getCountInput = Utils::makeBytes(bytes::hex("0x06661abd")); + // const Bytes getSquaredInput = Utils::makeBytes(bytes::hex("0x11a1d3e80000000000000000000000000000000000000000000000000000000000000019")); + // const Address expectedContractAddress = bytes::hex("0x5b41cef7f46a4a147e31150c3c5ffd077e54d0e1"); - EncodedCreateMessage createMessage(ORIGIN_ADDRESS, gas, value, SIMPLE_CONTRACT_BYTECODE); - EncodedStaticCallMessage getCountMessage(ORIGIN_ADDRESS, expectedContractAddress, gas, getCountInput); - EncodedCallMessage getSquaredMessage(ORIGIN_ADDRESS, expectedContractAddress, gas, value, getSquaredInput); + // EncodedCreateMessage createMessage(ORIGIN_ADDRESS, gas, value, SIMPLE_CONTRACT_BYTECODE); + // EncodedStaticCallMessage getCountMessage(ORIGIN_ADDRESS, expectedContractAddress, gas, getCountInput); + // EncodedCallMessage getSquaredMessage(ORIGIN_ADDRESS, expectedContractAddress, gas, value, getSquaredInput); - const Address contractAddress = env.executor.execute(createMessage); - REQUIRE(contractAddress == expectedContractAddress); + // const Address contractAddress = env.executor.execute(createMessage); + // REQUIRE(contractAddress == expectedContractAddress); - uint256_t count = std::get<0>(ABI::Decoder::decodeData(env.executor.execute(getCountMessage))); - REQUIRE(count == uint256_t(0)); - - uint256_t squared = std::get<0>(ABI::Decoder::decodeData(env.executor.execute(getSquaredMessage))); - REQUIRE(squared == uint256_t(625)); - - count = std::get<0>(ABI::Decoder::decodeData(env.executor.execute(getCountMessage))); - REQUIRE(count == uint256_t(1)); - - value = 200; - REQUIRE_THROWS(env.executor.execute(getSquaredMessage)); - } - - SECTION("Calls with value") { - MockedEnvironment env([] (const auto& msg) { - printf("foo!\n"); - }); - - const Address firstAccountAddress = bytes::hex("0x05ed5b0cec75408bb57ab2a5413b9d6b0d6c756f"); - const Address secondAccountAddress = bytes::hex("0xf11abf1f64dbcc256599b57e82f8d6654b0aba40"); - - Account& firstAccount = *env.accounts.emplace(firstAccountAddress, Account(ONE_ETHER, 1)).first->second; - Account& secondAccount = *env.accounts.emplace(secondAccountAddress, Account(0, 1)).first->second; - - const Bytes fundsBytecode = Utils::makeBytes(bytes::hex("0x608060405234801561001057600080fd5b50610225806100206000396000f3fe6080604052600436106100345760003560e01c8063b69ef8a814610039578063d0e30db014610064578063f3fef3a31461006e575b600080fd5b34801561004557600080fd5b5061004e610097565b60405161005b9190610105565b60405180910390f35b61006c61009f565b005b34801561007a57600080fd5b50610095600480360381019061009091906101af565b6100a1565b005b600047905090565b565b8173ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501580156100e7573d6000803e3d6000fd5b505050565b6000819050919050565b6100ff816100ec565b82525050565b600060208201905061011a60008301846100f6565b92915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061015082610125565b9050919050565b61016081610145565b811461016b57600080fd5b50565b60008135905061017d81610157565b92915050565b61018c816100ec565b811461019757600080fd5b50565b6000813590506101a981610183565b92915050565b600080604083850312156101c6576101c5610120565b5b60006101d48582860161016e565b92505060206101e58582860161019a565b915050925092905056fea26469706673582212206df27ae5d2764317da225f7c3853c25e3786f13b2f78691a76f215a48ab6955a64736f6c63430008130033")); - const Bytes casinoBytecode = Utils::makeBytes(bytes::hex("0x608060405260016000806101000a81548160ff02191690831515021790555034801561002a57600080fd5b506103ad8061003a6000396000f3fe6080604052600436106100555760003560e01c806311610c251461005a5780632ad957861461006457806343d726d61461008d57806347535d7b146100a4578063b69ef8a8146100cf578063fcfff16f146100fa575b600080fd5b610062610111565b005b34801561007057600080fd5b5061008b60048036038101906100869190610263565b610160565b005b34801561009957600080fd5b506100a26101aa565b005b3480156100b057600080fd5b506100b96101c6565b6040516100c691906102ab565b60405180910390f35b3480156100db57600080fd5b506100e46101dc565b6040516100f191906102df565b60405180910390f35b34801561010657600080fd5b5061010f6101e4565b005b60008054906101000a900460ff1661015e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161015590610357565b60405180910390fd5b565b8073ffffffffffffffffffffffffffffffffffffffff166108fc479081150290604051600060405180830381858888f193505050501580156101a6573d6000803e3d6000fd5b5050565b60008060006101000a81548160ff021916908315150217905550565b60008060009054906101000a900460ff16905090565b600047905090565b60016000806101000a81548160ff021916908315150217905550565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061023082610205565b9050919050565b61024081610225565b811461024b57600080fd5b50565b60008135905061025d81610237565b92915050565b60006020828403121561027957610278610200565b5b60006102878482850161024e565b91505092915050565b60008115159050919050565b6102a581610290565b82525050565b60006020820190506102c0600083018461029c565b92915050565b6000819050919050565b6102d9816102c6565b82525050565b60006020820190506102f460008301846102d0565b92915050565b600082825260208201905092915050565b7f6265747320636c6f736564000000000000000000000000000000000000000000600082015250565b6000610341600b836102fa565b915061034c8261030b565b602082019050919050565b6000602082019050818103600083015261037081610334565b905091905056fea26469706673582212207757c95d521b5892342b02350317f2c90bcb46f0e30b16a13de45e34c659e21764736f6c63430008130033")); - const Bytes gamblerBytecode = Utils::makeBytes(bytes::hex("0x608060405234801561001057600080fd5b50610292806100206000396000f3fe6080604052600436106100295760003560e01c80632c5433051461002e578063b69ef8a81461004a575b600080fd5b610048600480360381019061004391906101e8565b610075565b005b34801561005657600080fd5b5061005f61017d565b60405161006c9190610241565b60405180910390f35b600082905060008290508173ffffffffffffffffffffffffffffffffffffffff166311610c25346040518263ffffffff1660e01b81526004016000604051808303818588803b1580156100c757600080fd5b505af1935050505080156100d9575060015b610176573d8060008114610109576040519150601f19603f3d011682016040523d82523d6000602084013e61010e565b606091505b508173ffffffffffffffffffffffffffffffffffffffff1663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b15801561015757600080fd5b505af115801561016b573d6000803e3d6000fd5b505050505050610177565b5b50505050565b600047905090565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101b58261018a565b9050919050565b6101c5816101aa565b81146101d057600080fd5b50565b6000813590506101e2816101bc565b92915050565b600080604083850312156101ff576101fe610185565b5b600061020d858286016101d3565b925050602061021e858286016101d3565b9150509250929050565b6000819050919050565b61023b81610228565b82525050565b60006020820190506102566000830184610232565b9291505056fea2646970667358221220ce419e18421bafa55bf72c1e46652f2e313a338b574ece304a4044cb4cc9c05164736f6c63430008130033")); - - messages::Gas gas(1'000'000); - uint256_t value = 0; - Bytes input; - - const Address fundsAddress = env.executor.execute(EncodedCreateMessage(firstAccountAddress, gas, value, fundsBytecode)); - firstAccount.nonce++; - const Address casinoAddress = env.executor.execute(EncodedCreateMessage(firstAccountAddress, gas, value, casinoBytecode)); - firstAccount.nonce++; - const Address gamblerAddress = env.executor.execute(EncodedCreateMessage(firstAccountAddress, gas, value, gamblerBytecode)); - firstAccount.nonce++; - - // TODO: with the call bellow, things doesn't work - input.clear(); - auto functor1 = ABI::FunctorEncoder::encode<>("close"); - Utils::appendBytes(input, Utils::uint32ToBytes(functor1.value)); - env.executor.execute(EncodedCallMessage(firstAccountAddress, casinoAddress, gas, value, input)); - - input.clear(); - auto functor2 = ABI::FunctorEncoder::encode("gamble"); - Utils::appendBytes(input, Utils::uint32ToBytes(functor2.value)); - Utils::appendBytes(input, ABI::Encoder::encodeData(casinoAddress, fundsAddress)); - - value = ONE_ETHER / 2; - env.executor.execute(EncodedCallMessage(firstAccountAddress, gamblerAddress, gas, value, input)); - - std::cout << "gambler balance: " << env.accounts.at(gamblerAddress)->balance << "\n"; - std::cout << "casino balance: " << env.accounts.at(casinoAddress)->balance << "\n"; - std::cout << "funds balance: " << env.accounts.at(fundsAddress)->balance << "\n"; - } - - SECTION("Delegate calls") { - // TODO: the code field from the create message also has the constructor arguments + // uint256_t count = std::get<0>(ABI::Decoder::decodeData(env.executor.execute(getCountMessage))); + // REQUIRE(count == uint256_t(0)); + + // uint256_t squared = std::get<0>(ABI::Decoder::decodeData(env.executor.execute(getSquaredMessage))); + // REQUIRE(squared == uint256_t(625)); + + // count = std::get<0>(ABI::Decoder::decodeData(env.executor.execute(getCountMessage))); + // REQUIRE(count == uint256_t(1)); + + // value = 200; + // REQUIRE_THROWS(env.executor.execute(getSquaredMessage)); + // } + + // SECTION("Calls with value") { + // MockedEnvironment env([] (const auto& msg) { + // printf("foo!\n"); + // }); + + // const Address firstAccountAddress = bytes::hex("0x05ed5b0cec75408bb57ab2a5413b9d6b0d6c756f"); + // const Address secondAccountAddress = bytes::hex("0xf11abf1f64dbcc256599b57e82f8d6654b0aba40"); + + // Account& firstAccount = *env.accounts.emplace(firstAccountAddress, Account(ONE_ETHER, 1)).first->second; + // Account& secondAccount = *env.accounts.emplace(secondAccountAddress, Account(0, 1)).first->second; + + // const Bytes fundsBytecode = Utils::makeBytes(bytes::hex("0x608060405234801561001057600080fd5b50610225806100206000396000f3fe6080604052600436106100345760003560e01c8063b69ef8a814610039578063d0e30db014610064578063f3fef3a31461006e575b600080fd5b34801561004557600080fd5b5061004e610097565b60405161005b9190610105565b60405180910390f35b61006c61009f565b005b34801561007a57600080fd5b50610095600480360381019061009091906101af565b6100a1565b005b600047905090565b565b8173ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501580156100e7573d6000803e3d6000fd5b505050565b6000819050919050565b6100ff816100ec565b82525050565b600060208201905061011a60008301846100f6565b92915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061015082610125565b9050919050565b61016081610145565b811461016b57600080fd5b50565b60008135905061017d81610157565b92915050565b61018c816100ec565b811461019757600080fd5b50565b6000813590506101a981610183565b92915050565b600080604083850312156101c6576101c5610120565b5b60006101d48582860161016e565b92505060206101e58582860161019a565b915050925092905056fea26469706673582212206df27ae5d2764317da225f7c3853c25e3786f13b2f78691a76f215a48ab6955a64736f6c63430008130033")); + // const Bytes casinoBytecode = Utils::makeBytes(bytes::hex("0x608060405260016000806101000a81548160ff02191690831515021790555034801561002a57600080fd5b506103ad8061003a6000396000f3fe6080604052600436106100555760003560e01c806311610c251461005a5780632ad957861461006457806343d726d61461008d57806347535d7b146100a4578063b69ef8a8146100cf578063fcfff16f146100fa575b600080fd5b610062610111565b005b34801561007057600080fd5b5061008b60048036038101906100869190610263565b610160565b005b34801561009957600080fd5b506100a26101aa565b005b3480156100b057600080fd5b506100b96101c6565b6040516100c691906102ab565b60405180910390f35b3480156100db57600080fd5b506100e46101dc565b6040516100f191906102df565b60405180910390f35b34801561010657600080fd5b5061010f6101e4565b005b60008054906101000a900460ff1661015e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161015590610357565b60405180910390fd5b565b8073ffffffffffffffffffffffffffffffffffffffff166108fc479081150290604051600060405180830381858888f193505050501580156101a6573d6000803e3d6000fd5b5050565b60008060006101000a81548160ff021916908315150217905550565b60008060009054906101000a900460ff16905090565b600047905090565b60016000806101000a81548160ff021916908315150217905550565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061023082610205565b9050919050565b61024081610225565b811461024b57600080fd5b50565b60008135905061025d81610237565b92915050565b60006020828403121561027957610278610200565b5b60006102878482850161024e565b91505092915050565b60008115159050919050565b6102a581610290565b82525050565b60006020820190506102c0600083018461029c565b92915050565b6000819050919050565b6102d9816102c6565b82525050565b60006020820190506102f460008301846102d0565b92915050565b600082825260208201905092915050565b7f6265747320636c6f736564000000000000000000000000000000000000000000600082015250565b6000610341600b836102fa565b915061034c8261030b565b602082019050919050565b6000602082019050818103600083015261037081610334565b905091905056fea26469706673582212207757c95d521b5892342b02350317f2c90bcb46f0e30b16a13de45e34c659e21764736f6c63430008130033")); + // const Bytes gamblerBytecode = Utils::makeBytes(bytes::hex("0x608060405234801561001057600080fd5b50610292806100206000396000f3fe6080604052600436106100295760003560e01c80632c5433051461002e578063b69ef8a81461004a575b600080fd5b610048600480360381019061004391906101e8565b610075565b005b34801561005657600080fd5b5061005f61017d565b60405161006c9190610241565b60405180910390f35b600082905060008290508173ffffffffffffffffffffffffffffffffffffffff166311610c25346040518263ffffffff1660e01b81526004016000604051808303818588803b1580156100c757600080fd5b505af1935050505080156100d9575060015b610176573d8060008114610109576040519150601f19603f3d011682016040523d82523d6000602084013e61010e565b606091505b508173ffffffffffffffffffffffffffffffffffffffff1663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b15801561015757600080fd5b505af115801561016b573d6000803e3d6000fd5b505050505050610177565b5b50505050565b600047905090565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101b58261018a565b9050919050565b6101c5816101aa565b81146101d057600080fd5b50565b6000813590506101e2816101bc565b92915050565b600080604083850312156101ff576101fe610185565b5b600061020d858286016101d3565b925050602061021e858286016101d3565b9150509250929050565b6000819050919050565b61023b81610228565b82525050565b60006020820190506102566000830184610232565b9291505056fea2646970667358221220ce419e18421bafa55bf72c1e46652f2e313a338b574ece304a4044cb4cc9c05164736f6c63430008130033")); + + // messages::Gas gas(1'000'000); + // uint256_t value = 0; + // Bytes input; + + // const Address fundsAddress = env.executor.execute(EncodedCreateMessage(firstAccountAddress, gas, value, fundsBytecode)); + // firstAccount.nonce++; + // const Address casinoAddress = env.executor.execute(EncodedCreateMessage(firstAccountAddress, gas, value, casinoBytecode)); + // firstAccount.nonce++; + // const Address gamblerAddress = env.executor.execute(EncodedCreateMessage(firstAccountAddress, gas, value, gamblerBytecode)); + // firstAccount.nonce++; + + // // TODO: with the call bellow, things doesn't work + // input.clear(); + // auto functor1 = ABI::FunctorEncoder::encode<>("close"); + // Utils::appendBytes(input, Utils::uint32ToBytes(functor1.value)); + // env.executor.execute(EncodedCallMessage(firstAccountAddress, casinoAddress, gas, value, input)); + + // input.clear(); + // auto functor2 = ABI::FunctorEncoder::encode("gamble"); + // Utils::appendBytes(input, Utils::uint32ToBytes(functor2.value)); + // Utils::appendBytes(input, ABI::Encoder::encodeData(casinoAddress, fundsAddress)); + + // value = ONE_ETHER / 2; + // env.executor.execute(EncodedCallMessage(firstAccountAddress, gamblerAddress, gas, value, input)); + + // std::cout << "gambler balance: " << env.accounts.at(gamblerAddress)->balance << "\n"; + // std::cout << "casino balance: " << env.accounts.at(casinoAddress)->balance << "\n"; + // std::cout << "funds balance: " << env.accounts.at(fundsAddress)->balance << "\n"; + // } + + // SECTION("Delegate calls") { + // // TODO: the code field from the create message also has the constructor arguments - MockedEnvironment env; - messages::Gas gas(1'000'000); - uint256_t value = 0; + // MockedEnvironment env; + // messages::Gas gas(1'000'000); + // uint256_t value = 0; - const Address accountAddress = bytes::hex("0x05ed5b0cec75408bb57ab2a5413b9d6b0d6c756f"); - Account& account = *env.accounts.emplace(accountAddress, Account(ONE_ETHER, 1)).first->second; + // const Address accountAddress = bytes::hex("0x05ed5b0cec75408bb57ab2a5413b9d6b0d6c756f"); + // Account& account = *env.accounts.emplace(accountAddress, Account(ONE_ETHER, 1)).first->second; - const Bytes delegatorBytecode = Utils::makeBytes(bytes::hex("0x60806040526040518060400160405280600681526020017f416c666163650000000000000000000000000000000000000000000000000000815250600090816200004a91906200032e565b5073ab8483f64d9c6d1ecf9b849ae677dd3315835cb2600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550348015620000ad57600080fd5b5062000415565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200013657607f821691505b6020821081036200014c576200014b620000ee565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620001b67fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000177565b620001c2868362000177565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006200020f620002096200020384620001da565b620001e4565b620001da565b9050919050565b6000819050919050565b6200022b83620001ee565b620002436200023a8262000216565b84845462000184565b825550505050565b600090565b6200025a6200024b565b6200026781848462000220565b505050565b5b818110156200028f576200028360008262000250565b6001810190506200026d565b5050565b601f821115620002de57620002a88162000152565b620002b38462000167565b81016020851015620002c3578190505b620002db620002d28562000167565b8301826200026c565b50505b505050565b600082821c905092915050565b60006200030360001984600802620002e3565b1980831691505092915050565b60006200031e8383620002f0565b9150826002028217905092915050565b6200033982620000b4565b67ffffffffffffffff811115620003555762000354620000bf565b5b6200036182546200011d565b6200036e82828562000293565b600060209050601f831160018114620003a6576000841562000391578287015190505b6200039d858262000310565b8655506200040d565b601f198416620003b68662000152565b60005b82811015620003e057848901518255600182019150602085019450602081019050620003b9565b86831015620004005784890151620003fc601f891682620002f0565b8355505b6001600288020188555050505b505050505050565b61076e80620004256000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c8063893d20e81461005c578063ce6d41de1461007a578063d129b79a14610098578063d6026296146100b6578063e7663079146100d2575b600080fd5b6100646100f0565b60405161007191906103e6565b60405180910390f35b61008261011a565b60405161008f9190610491565b60405180910390f35b6100a06101ac565b6040516100ad9190610491565b60405180910390f35b6100d060048036038101906100cb919061054e565b61023a565b005b6100da61037f565b6040516100e791906103e6565b60405180910390f35b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b606060008054610129906105dd565b80601f0160208091040260200160405190810160405280929190818152602001828054610155906105dd565b80156101a25780601f10610177576101008083540402835291602001916101a2565b820191906000526020600020905b81548152906001019060200180831161018557829003601f168201915b5050505050905090565b600080546101b9906105dd565b80601f01602080910402602001604051908101604052809291908181526020018280546101e5906105dd565b80156102325780601f1061020757610100808354040283529160200191610232565b820191906000526020600020905b81548152906001019060200180831161021557829003601f168201915b505050505081565b6000808473ffffffffffffffffffffffffffffffffffffffff1663368b877260e01b858560405160240161026f92919061064a565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516102d991906106b5565b600060405180830381855af49150503d8060008114610314576040519150601f19603f3d011682016040523d82523d6000602084013e610319565b606091505b509150915060001515821515036103785760008151111561033d5780518082602001fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161036f90610718565b60405180910390fd5b5050505050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006103d0826103a5565b9050919050565b6103e0816103c5565b82525050565b60006020820190506103fb60008301846103d7565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561043b578082015181840152602081019050610420565b60008484015250505050565b6000601f19601f8301169050919050565b600061046382610401565b61046d818561040c565b935061047d81856020860161041d565b61048681610447565b840191505092915050565b600060208201905081810360008301526104ab8184610458565b905092915050565b600080fd5b600080fd5b6104c6816103c5565b81146104d157600080fd5b50565b6000813590506104e3816104bd565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f84011261050e5761050d6104e9565b5b8235905067ffffffffffffffff81111561052b5761052a6104ee565b5b602083019150836001820283011115610547576105466104f3565b5b9250929050565b600080600060408486031215610567576105666104b3565b5b6000610575868287016104d4565b935050602084013567ffffffffffffffff811115610596576105956104b8565b5b6105a2868287016104f8565b92509250509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806105f557607f821691505b602082108103610608576106076105ae565b5b50919050565b82818337600083830152505050565b6000610629838561040c565b935061063683858461060e565b61063f83610447565b840190509392505050565b6000602082019050818103600083015261066581848661061d565b90509392505050565b600081519050919050565b600081905092915050565b600061068f8261066e565b6106998185610679565b93506106a981856020860161041d565b80840191505092915050565b60006106c18284610684565b915081905092915050565b7f46756e6374696f6e2063616c6c20726576657274656400000000000000000000600082015250565b600061070260168361040c565b915061070d826106cc565b602082019050919050565b60006020820190508181036000830152610731816106f5565b905091905056fea26469706673582212207bb9c9ffbef5fbdb6a396f9c4e1962581051cafee1ecbf4367476185b9bdaadd64736f6c63430008130033")); - const Bytes delegatedBytecode = Utils::makeBytes(bytes::hex("0x60806040526040518060400160405280600681526020017f416c666163650000000000000000000000000000000000000000000000000000815250600090816200004a919062000340565b503480156200005857600080fd5b5060405162000caa38038062000caa83398181016040528101906200007e919062000491565b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050620004c3565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200014857607f821691505b6020821081036200015e576200015d62000100565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620001c87fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000189565b620001d4868362000189565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b6000620002216200021b6200021584620001ec565b620001f6565b620001ec565b9050919050565b6000819050919050565b6200023d8362000200565b620002556200024c8262000228565b84845462000196565b825550505050565b600090565b6200026c6200025d565b6200027981848462000232565b505050565b5b81811015620002a1576200029560008262000262565b6001810190506200027f565b5050565b601f821115620002f057620002ba8162000164565b620002c58462000179565b81016020851015620002d5578190505b620002ed620002e48562000179565b8301826200027e565b50505b505050565b600082821c905092915050565b60006200031560001984600802620002f5565b1980831691505092915050565b600062000330838362000302565b9150826002028217905092915050565b6200034b82620000c6565b67ffffffffffffffff811115620003675762000366620000d1565b5b6200037382546200012f565b62000380828285620002a5565b600060209050601f831160018114620003b85760008415620003a3578287015190505b620003af858262000322565b8655506200041f565b601f198416620003c88662000164565b60005b82811015620003f257848901518255600182019150602085019450602081019050620003cb565b868310156200041257848901516200040e601f89168262000302565b8355505b6001600288020188555050505b505050505050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600062000459826200042c565b9050919050565b6200046b816200044c565b81146200047757600080fd5b50565b6000815190506200048b8162000460565b92915050565b600060208284031215620004aa57620004a962000427565b5b6000620004ba848285016200047a565b91505092915050565b6107d780620004d36000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c8063368b87721461005c578063893d20e814610078578063ce6d41de14610096578063d129b79a146100b4578063e7663079146100d2575b600080fd5b61007660048036038101906100719190610326565b6100f0565b005b610080610147565b60405161008d91906103b4565b60405180910390f35b61009e610171565b6040516100ab919061045f565b60405180910390f35b6100bc610203565b6040516100c9919061045f565b60405180910390f35b6100da610291565b6040516100e791906103b4565b60405180910390f35b8181600091826101019291906106d1565b5033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b606060008054610180906104ea565b80601f01602080910402602001604051908101604052809291908181526020018280546101ac906104ea565b80156101f95780601f106101ce576101008083540402835291602001916101f9565b820191906000526020600020905b8154815290600101906020018083116101dc57829003601f168201915b5050505050905090565b60008054610210906104ea565b80601f016020809104026020016040519081016040528092919081815260200182805461023c906104ea565b80156102895780601f1061025e57610100808354040283529160200191610289565b820191906000526020600020905b81548152906001019060200180831161026c57829003601f168201915b505050505081565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b60008083601f8401126102e6576102e56102c1565b5b8235905067ffffffffffffffff811115610303576103026102c6565b5b60208301915083600182028301111561031f5761031e6102cb565b5b9250929050565b6000806020838503121561033d5761033c6102b7565b5b600083013567ffffffffffffffff81111561035b5761035a6102bc565b5b610367858286016102d0565b92509250509250929050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061039e82610373565b9050919050565b6103ae81610393565b82525050565b60006020820190506103c960008301846103a5565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156104095780820151818401526020810190506103ee565b60008484015250505050565b6000601f19601f8301169050919050565b6000610431826103cf565b61043b81856103da565b935061044b8185602086016103eb565b61045481610415565b840191505092915050565b600060208201905081810360008301526104798184610426565b905092915050565b600082905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061050257607f821691505b602082108103610515576105146104bb565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b60006008830261057d7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82610540565b6105878683610540565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006105ce6105c96105c48461059f565b6105a9565b61059f565b9050919050565b6000819050919050565b6105e8836105b3565b6105fc6105f4826105d5565b84845461054d565b825550505050565b600090565b610611610604565b61061c8184846105df565b505050565b5b8181101561064057610635600082610609565b600181019050610622565b5050565b601f821115610685576106568161051b565b61065f84610530565b8101602085101561066e578190505b61068261067a85610530565b830182610621565b50505b505050565b600082821c905092915050565b60006106a86000198460080261068a565b1980831691505092915050565b60006106c18383610697565b9150826002028217905092915050565b6106db8383610481565b67ffffffffffffffff8111156106f4576106f361048c565b5b6106fe82546104ea565b610709828285610644565b6000601f8311600181146107385760008415610726578287013590505b61073085826106b5565b865550610798565b601f1984166107468661051b565b60005b8281101561076e57848901358255600182019150602085019450602081019050610749565b8683101561078b5784890135610787601f891682610697565b8355505b6001600288020188555050505b5050505050505056fea26469706673582212204d0495ba5050deed7ead9ed3191f798cb26ec86deac8246cd32b297fec3664cb64736f6c634300081300330000000000000000000000009fc12574abe3e595c73b6d4380762f232507aeeb")); + // const Bytes delegatorBytecode = Utils::makeBytes(bytes::hex("0x60806040526040518060400160405280600681526020017f416c666163650000000000000000000000000000000000000000000000000000815250600090816200004a91906200032e565b5073ab8483f64d9c6d1ecf9b849ae677dd3315835cb2600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550348015620000ad57600080fd5b5062000415565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200013657607f821691505b6020821081036200014c576200014b620000ee565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620001b67fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000177565b620001c2868362000177565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006200020f620002096200020384620001da565b620001e4565b620001da565b9050919050565b6000819050919050565b6200022b83620001ee565b620002436200023a8262000216565b84845462000184565b825550505050565b600090565b6200025a6200024b565b6200026781848462000220565b505050565b5b818110156200028f576200028360008262000250565b6001810190506200026d565b5050565b601f821115620002de57620002a88162000152565b620002b38462000167565b81016020851015620002c3578190505b620002db620002d28562000167565b8301826200026c565b50505b505050565b600082821c905092915050565b60006200030360001984600802620002e3565b1980831691505092915050565b60006200031e8383620002f0565b9150826002028217905092915050565b6200033982620000b4565b67ffffffffffffffff811115620003555762000354620000bf565b5b6200036182546200011d565b6200036e82828562000293565b600060209050601f831160018114620003a6576000841562000391578287015190505b6200039d858262000310565b8655506200040d565b601f198416620003b68662000152565b60005b82811015620003e057848901518255600182019150602085019450602081019050620003b9565b86831015620004005784890151620003fc601f891682620002f0565b8355505b6001600288020188555050505b505050505050565b61076e80620004256000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c8063893d20e81461005c578063ce6d41de1461007a578063d129b79a14610098578063d6026296146100b6578063e7663079146100d2575b600080fd5b6100646100f0565b60405161007191906103e6565b60405180910390f35b61008261011a565b60405161008f9190610491565b60405180910390f35b6100a06101ac565b6040516100ad9190610491565b60405180910390f35b6100d060048036038101906100cb919061054e565b61023a565b005b6100da61037f565b6040516100e791906103e6565b60405180910390f35b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b606060008054610129906105dd565b80601f0160208091040260200160405190810160405280929190818152602001828054610155906105dd565b80156101a25780601f10610177576101008083540402835291602001916101a2565b820191906000526020600020905b81548152906001019060200180831161018557829003601f168201915b5050505050905090565b600080546101b9906105dd565b80601f01602080910402602001604051908101604052809291908181526020018280546101e5906105dd565b80156102325780601f1061020757610100808354040283529160200191610232565b820191906000526020600020905b81548152906001019060200180831161021557829003601f168201915b505050505081565b6000808473ffffffffffffffffffffffffffffffffffffffff1663368b877260e01b858560405160240161026f92919061064a565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516102d991906106b5565b600060405180830381855af49150503d8060008114610314576040519150601f19603f3d011682016040523d82523d6000602084013e610319565b606091505b509150915060001515821515036103785760008151111561033d5780518082602001fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161036f90610718565b60405180910390fd5b5050505050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006103d0826103a5565b9050919050565b6103e0816103c5565b82525050565b60006020820190506103fb60008301846103d7565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561043b578082015181840152602081019050610420565b60008484015250505050565b6000601f19601f8301169050919050565b600061046382610401565b61046d818561040c565b935061047d81856020860161041d565b61048681610447565b840191505092915050565b600060208201905081810360008301526104ab8184610458565b905092915050565b600080fd5b600080fd5b6104c6816103c5565b81146104d157600080fd5b50565b6000813590506104e3816104bd565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f84011261050e5761050d6104e9565b5b8235905067ffffffffffffffff81111561052b5761052a6104ee565b5b602083019150836001820283011115610547576105466104f3565b5b9250929050565b600080600060408486031215610567576105666104b3565b5b6000610575868287016104d4565b935050602084013567ffffffffffffffff811115610596576105956104b8565b5b6105a2868287016104f8565b92509250509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806105f557607f821691505b602082108103610608576106076105ae565b5b50919050565b82818337600083830152505050565b6000610629838561040c565b935061063683858461060e565b61063f83610447565b840190509392505050565b6000602082019050818103600083015261066581848661061d565b90509392505050565b600081519050919050565b600081905092915050565b600061068f8261066e565b6106998185610679565b93506106a981856020860161041d565b80840191505092915050565b60006106c18284610684565b915081905092915050565b7f46756e6374696f6e2063616c6c20726576657274656400000000000000000000600082015250565b600061070260168361040c565b915061070d826106cc565b602082019050919050565b60006020820190508181036000830152610731816106f5565b905091905056fea26469706673582212207bb9c9ffbef5fbdb6a396f9c4e1962581051cafee1ecbf4367476185b9bdaadd64736f6c63430008130033")); + // const Bytes delegatedBytecode = Utils::makeBytes(bytes::hex("0x60806040526040518060400160405280600681526020017f416c666163650000000000000000000000000000000000000000000000000000815250600090816200004a919062000340565b503480156200005857600080fd5b5060405162000caa38038062000caa83398181016040528101906200007e919062000491565b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050620004c3565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200014857607f821691505b6020821081036200015e576200015d62000100565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620001c87fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000189565b620001d4868362000189565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b6000620002216200021b6200021584620001ec565b620001f6565b620001ec565b9050919050565b6000819050919050565b6200023d8362000200565b620002556200024c8262000228565b84845462000196565b825550505050565b600090565b6200026c6200025d565b6200027981848462000232565b505050565b5b81811015620002a1576200029560008262000262565b6001810190506200027f565b5050565b601f821115620002f057620002ba8162000164565b620002c58462000179565b81016020851015620002d5578190505b620002ed620002e48562000179565b8301826200027e565b50505b505050565b600082821c905092915050565b60006200031560001984600802620002f5565b1980831691505092915050565b600062000330838362000302565b9150826002028217905092915050565b6200034b82620000c6565b67ffffffffffffffff811115620003675762000366620000d1565b5b6200037382546200012f565b62000380828285620002a5565b600060209050601f831160018114620003b85760008415620003a3578287015190505b620003af858262000322565b8655506200041f565b601f198416620003c88662000164565b60005b82811015620003f257848901518255600182019150602085019450602081019050620003cb565b868310156200041257848901516200040e601f89168262000302565b8355505b6001600288020188555050505b505050505050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600062000459826200042c565b9050919050565b6200046b816200044c565b81146200047757600080fd5b50565b6000815190506200048b8162000460565b92915050565b600060208284031215620004aa57620004a962000427565b5b6000620004ba848285016200047a565b91505092915050565b6107d780620004d36000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c8063368b87721461005c578063893d20e814610078578063ce6d41de14610096578063d129b79a146100b4578063e7663079146100d2575b600080fd5b61007660048036038101906100719190610326565b6100f0565b005b610080610147565b60405161008d91906103b4565b60405180910390f35b61009e610171565b6040516100ab919061045f565b60405180910390f35b6100bc610203565b6040516100c9919061045f565b60405180910390f35b6100da610291565b6040516100e791906103b4565b60405180910390f35b8181600091826101019291906106d1565b5033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b606060008054610180906104ea565b80601f01602080910402602001604051908101604052809291908181526020018280546101ac906104ea565b80156101f95780601f106101ce576101008083540402835291602001916101f9565b820191906000526020600020905b8154815290600101906020018083116101dc57829003601f168201915b5050505050905090565b60008054610210906104ea565b80601f016020809104026020016040519081016040528092919081815260200182805461023c906104ea565b80156102895780601f1061025e57610100808354040283529160200191610289565b820191906000526020600020905b81548152906001019060200180831161026c57829003601f168201915b505050505081565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b60008083601f8401126102e6576102e56102c1565b5b8235905067ffffffffffffffff811115610303576103026102c6565b5b60208301915083600182028301111561031f5761031e6102cb565b5b9250929050565b6000806020838503121561033d5761033c6102b7565b5b600083013567ffffffffffffffff81111561035b5761035a6102bc565b5b610367858286016102d0565b92509250509250929050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061039e82610373565b9050919050565b6103ae81610393565b82525050565b60006020820190506103c960008301846103a5565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156104095780820151818401526020810190506103ee565b60008484015250505050565b6000601f19601f8301169050919050565b6000610431826103cf565b61043b81856103da565b935061044b8185602086016103eb565b61045481610415565b840191505092915050565b600060208201905081810360008301526104798184610426565b905092915050565b600082905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061050257607f821691505b602082108103610515576105146104bb565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b60006008830261057d7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82610540565b6105878683610540565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006105ce6105c96105c48461059f565b6105a9565b61059f565b9050919050565b6000819050919050565b6105e8836105b3565b6105fc6105f4826105d5565b84845461054d565b825550505050565b600090565b610611610604565b61061c8184846105df565b505050565b5b8181101561064057610635600082610609565b600181019050610622565b5050565b601f821115610685576106568161051b565b61065f84610530565b8101602085101561066e578190505b61068261067a85610530565b830182610621565b50505b505050565b600082821c905092915050565b60006106a86000198460080261068a565b1980831691505092915050565b60006106c18383610697565b9150826002028217905092915050565b6106db8383610481565b67ffffffffffffffff8111156106f4576106f361048c565b5b6106fe82546104ea565b610709828285610644565b6000601f8311600181146107385760008415610726578287013590505b61073085826106b5565b865550610798565b601f1984166107468661051b565b60005b8281101561076e57848901358255600182019150602085019450602081019050610749565b8683101561078b5784890135610787601f891682610697565b8355505b6001600288020188555050505b5050505050505056fea26469706673582212204d0495ba5050deed7ead9ed3191f798cb26ec86deac8246cd32b297fec3664cb64736f6c634300081300330000000000000000000000009fc12574abe3e595c73b6d4380762f232507aeeb")); - const Address delegatorAddress = env.executor.execute(EncodedCreateMessage(accountAddress, gas, value, delegatorBytecode)); - account.nonce++; + // const Address delegatorAddress = env.executor.execute(EncodedCreateMessage(accountAddress, gas, value, delegatorBytecode)); + // account.nonce++; - const Address delegatedAddress = env.executor.execute(EncodedCreateMessage(accountAddress, gas, value, delegatedBytecode)); - account.nonce++; + // const Address delegatedAddress = env.executor.execute(EncodedCreateMessage(accountAddress, gas, value, delegatedBytecode)); + // account.nonce++; - Bytes input; - auto functor = ABI::FunctorEncoder::encode("delegateTo"); - Utils::appendBytes(input, Utils::uint32ToBytes(functor.value)); - Utils::appendBytes(input, ABI::Encoder::encodeData(delegatedAddress, std::string("cometa"))); + // Bytes input; + // auto functor = ABI::FunctorEncoder::encode("delegateTo"); + // Utils::appendBytes(input, Utils::uint32ToBytes(functor.value)); + // Utils::appendBytes(input, ABI::Encoder::encodeData(delegatedAddress, std::string("cometa"))); - std::cout << "\n\n"; - std::cout << "account address: " << accountAddress.hex(true) << "\n"; - std::cout << "delegator address: " << delegatorAddress.hex(true) << "\n"; - std::cout << "delegated address: " << delegatedAddress.hex(true) << "\n\n"; + // std::cout << "\n\n"; + // std::cout << "account address: " << accountAddress.hex(true) << "\n"; + // std::cout << "delegator address: " << delegatorAddress.hex(true) << "\n"; + // std::cout << "delegated address: " << delegatedAddress.hex(true) << "\n\n"; - env.executor.execute(EncodedCallMessage(accountAddress, delegatorAddress, gas, value, input)); - } + // env.executor.execute(EncodedCallMessage(accountAddress, delegatorAddress, gas, value, input)); + // } } From de6d72a8e6d9a9cffebb72559c11889fbcd856c0 Mon Sep 17 00:00:00 2001 From: Leonardo Amaral Date: Thu, 26 Dec 2024 13:28:56 -0300 Subject: [PATCH 14/37] call tracer tests fixed --- src/contract/abi.h | 9 +++++++++ src/contract/calltracer.h | 8 +++++++- src/contract/contracthost.cpp | 11 +++++++++++ src/contract/messages/traits.h | 8 ++++---- tests/contract/calltracer.cpp | 5 ++--- 5 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/contract/abi.h b/src/contract/abi.h index 47171d2e..2d6cde42 100644 --- a/src/contract/abi.h +++ b/src/contract/abi.h @@ -507,6 +507,15 @@ namespace ABI { } }; + // Specialization for std::pair + template + struct TypeEncoder> { + static Bytes encode(const std::pair& p) { + using Tuple = std::tuple; + return TypeEncoder::encode(Tuple(p.first, p.second)); + } + }; + // Specialization for std::vector template Bytes TypeEncoder>::encode(const std::vector& v) { diff --git a/src/contract/calltracer.h b/src/contract/calltracer.h index 1e2d8105..908ee9dc 100644 --- a/src/contract/calltracer.h +++ b/src/contract/calltracer.h @@ -40,7 +40,13 @@ class CallTracer { try { if constexpr (not std::same_as) { Result result = handler_.onMessage(std::forward(msg)); - callTrace.output = ABI::Encoder::encodeData(result); + + if constexpr (concepts::PackedMessage) { + callTrace.output = ABI::Encoder::encodeData(result); + } else { + callTrace.output = result; + } + return result; } diff --git a/src/contract/contracthost.cpp b/src/contract/contracthost.cpp index b27c8d67..1ad39f79 100644 --- a/src/contract/contracthost.cpp +++ b/src/contract/contracthost.cpp @@ -35,4 +35,15 @@ ContractHost::~ContractHost() { context_.commit(); } + + std::visit(Utils::Overloaded{ + [] (MessageDispatcher& handler) {}, + [this] (CallTracer& tracer) { + if (tracer.hasCallTrace()) { + storage_.putCallTrace(Hash(context_.getTxHash()), tracer.getCallTrace()); // TODO: do not create a hash + } + } + }, messageHandler_); + + // TODO: save transaction additional data } diff --git a/src/contract/messages/traits.h b/src/contract/messages/traits.h index a430bf58..1baa9ab0 100644 --- a/src/contract/messages/traits.h +++ b/src/contract/messages/traits.h @@ -24,10 +24,10 @@ struct Methods { }; template -using MethodReturn = typename Methods>::Return; +using MethodReturn = Methods>::Return; template -using MethodClass = typename Methods>::Class; +using MethodClass = Methods>::Class; template constexpr bool IsViewMethod = Methods>::IS_VIEW; @@ -53,7 +53,7 @@ struct MessageResultHelper { }; template -using MessageResult = typename MessageResultHelper::Type; +using MessageResult = MessageResultHelper::Type; template struct MessageContractHelper; @@ -67,7 +67,7 @@ struct MessageContractHelper { template requires (concepts::PackedMessage) struct MessageContractHelper { - using Type = typename std::remove_cvref_t::ContractType; + using Type = std::remove_cvref_t::ContractType; }; template diff --git a/tests/contract/calltracer.cpp b/tests/contract/calltracer.cpp index 52ecc646..f1d76913 100644 --- a/tests/contract/calltracer.cpp +++ b/tests/contract/calltracer.cpp @@ -12,6 +12,7 @@ See the LICENSE.txt file in the project root for more information. #include "../sdktestsuite.hpp" #include "../blockchainwrapper.hpp" #include "../src/contract/templates/simplecontract.h" +#include "bytes/hex.h" static const Bytes testBytecode = Hex::toBytes("0x608060405234801561001057600080fd5b50610219806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80631003e2d2146100465780634fa522db14610062578063853255cc14610092575b600080fd5b610060600480360381019061005b9190610129565b6100b0565b005b61007c60048036038101906100779190610129565b6100cb565b6040516100899190610165565b60405180910390f35b61009a6100e5565b6040516100a79190610165565b60405180910390f35b806000808282546100c191906101af565b9250508190555050565b60006100d6826100b0565b6100de6100e5565b9050919050565b60008054905090565b600080fd5b6000819050919050565b610106816100f3565b811461011157600080fd5b50565b600081359050610123816100fd565b92915050565b60006020828403121561013f5761013e6100ee565b5b600061014d84828501610114565b91505092915050565b61015f816100f3565b82525050565b600060208201905061017a6000830184610156565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006101ba826100f3565b91506101c5836100f3565b92508282019050808211156101dd576101dc610180565b5b9291505056fea264697066735822122010806f8bd0eb78dd8bf1e05d0621ad54dfe78cd22c6a67e02decd89cd4a2208064736f6c63430008130033"); static const Bytes testProxyBytecode = Hex::toBytes("0x608060405234801561001057600080fd5b50610341806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80637714eaca1461003b5780638e6113211461006b575b600080fd5b61005560048036038101906100509190610232565b61009b565b6040516100629190610281565b60405180910390f35b6100856004803603810190610080919061029c565b610121565b6040516100929190610281565b60405180910390f35b60008273ffffffffffffffffffffffffffffffffffffffff16634fa522db836040518263ffffffff1660e01b81526004016100d69190610281565b6020604051808303816000875af11580156100f5573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061011991906102de565b905092915050565b60008173ffffffffffffffffffffffffffffffffffffffff1663853255cc6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561016e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061019291906102de565b9050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101c98261019e565b9050919050565b6101d9816101be565b81146101e457600080fd5b50565b6000813590506101f6816101d0565b92915050565b6000819050919050565b61020f816101fc565b811461021a57600080fd5b50565b60008135905061022c81610206565b92915050565b6000806040838503121561024957610248610199565b5b6000610257858286016101e7565b92505060206102688582860161021d565b9150509250929050565b61027b816101fc565b82525050565b60006020820190506102966000830184610272565b92915050565b6000602082840312156102b2576102b1610199565b5b60006102c0848285016101e7565b91505092915050565b6000815190506102d881610206565b92915050565b6000602082840312156102f4576102f3610199565b5b6000610302848285016102c9565b9150509291505056fea26469706673582212201c63da23a5ecb525a33d51b61ad178576a9dbc8733cc98401d2be1db76021bbf64736f6c63430008130033"); @@ -103,7 +104,6 @@ TEST_CASE("CallTracer Tests", "[trace]") { REQUIRE(callTrace->to == contractAddress); REQUIRE(callTrace->value == FixedBytes<32>()); // TODO: gas and gasUsed? - // TODO: what are these 4 bytes prefix? REQUIRE(FixedBytes<32>(callTrace->input | std::views::drop(4)) == FixedBytes<32>(Utils::uint256ToBytes(uint256_t(33)))); REQUIRE(callTrace->output == Bytes()); @@ -123,7 +123,6 @@ TEST_CASE("CallTracer Tests", "[trace]") { REQUIRE(callTrace->to == contractAddress); REQUIRE(callTrace->value == FixedBytes<32>()); // TODO: gas and gasUsed? - // TODO: what are these 4 bytes prefix? REQUIRE(FixedBytes<32>(callTrace->input | std::views::drop(4)) == FixedBytes<32>(Utils::uint256ToBytes(uint256_t(66)))); REQUIRE(callTrace->output == Utils::makeBytes(Utils::uint256ToBytes(uint256_t(99)))); @@ -210,7 +209,7 @@ TEST_CASE("CallTracer Tests", "[trace]") { REQUIRE(approveCallTrace->to == erc20); REQUIRE(approveCallTrace->value == FixedBytes<32>()); REQUIRE(approveCallTrace->input == Hex::toBytes("0x095ea7b30000000000000000000000006d48fdfe009e309dd5c4e69dec87365bfa0c811900000000000000000000000000000000000000000000000006f05b59d3b20000")); - REQUIRE(approveCallTrace->output == Bytes()); + REQUIRE(approveCallTrace->output == Utils::makeBytes(bytes::hex("0x0000000000000000000000000000000000000000000000000000000000000001"))); REQUIRE(approveCallTrace->calls.empty()); REQUIRE(depositCallTrace); From ddaff63a8ce7d8d338b04568708454bcc51fe1a0 Mon Sep 17 00:00:00 2001 From: Leonardo Amaral Date: Thu, 26 Dec 2024 14:05:19 -0300 Subject: [PATCH 15/37] call tracer decoupled --- src/contract/CMakeLists.txt | 2 +- src/contract/calltracer.h | 16 ++---- .../trace/{calltrace.cpp => call.cpp} | 2 +- src/contract/trace/call.h | 48 +++++++++++++++++ src/contract/trace/callstatus.h | 14 +++++ src/contract/trace/calltrace.h | 54 ------------------- src/contract/trace/calltype.h | 28 ++++++++++ src/utils/finally.h | 16 ++++++ 8 files changed, 112 insertions(+), 68 deletions(-) rename src/contract/trace/{calltrace.cpp => call.cpp} (98%) create mode 100644 src/contract/trace/call.h create mode 100644 src/contract/trace/callstatus.h create mode 100644 src/contract/trace/calltype.h create mode 100644 src/utils/finally.h diff --git a/src/contract/CMakeLists.txt b/src/contract/CMakeLists.txt index 585f418e..9244e982 100644 --- a/src/contract/CMakeLists.txt +++ b/src/contract/CMakeLists.txt @@ -52,7 +52,7 @@ set(CONTRACT_SOURCES ${CMAKE_SOURCE_DIR}/src/contract/contracthost.cpp ${CMAKE_SOURCE_DIR}/src/contract/contractmanager.cpp ${CMAKE_SOURCE_DIR}/src/contract/dynamiccontract.cpp - ${CMAKE_SOURCE_DIR}/src/contract/trace/calltrace.cpp + ${CMAKE_SOURCE_DIR}/src/contract/trace/call.cpp ${CMAKE_SOURCE_DIR}/src/contract/event.cpp ${CMAKE_SOURCE_DIR}/src/contract/messages/common.cpp ${CMAKE_SOURCE_DIR}/src/contract/messages/executioncontext.cpp diff --git a/src/contract/calltracer.h b/src/contract/calltracer.h index 908ee9dc..ca2e6e70 100644 --- a/src/contract/calltracer.h +++ b/src/contract/calltracer.h @@ -4,18 +4,10 @@ #include "utils/utils.h" #include "contract/messages/concepts.h" #include "contract/messages/outofgas.h" -#include "contract/trace/calltrace.h" +#include "contract/trace/call.h" #include "contract/messages/traits.h" #include "contract/abi.h" - -template -class Defer { -public: - explicit constexpr Defer(F func) : func_(std::move(func)) {} - constexpr ~Defer() { std::invoke(func_); } -private: - F func_; -}; +#include "utils/finally.h" template class CallTracer { @@ -32,7 +24,7 @@ class CallTracer { callStack_.push(&callTrace); - Defer finally([this, &gas = msg.gas(), &callTrace] () { + Finally finally([this, &gas = msg.gas(), &callTrace] () { callTrace.gasUsed = callTrace.gas - gas.value(); callStack_.pop(); }); @@ -66,7 +58,7 @@ class CallTracer { } decltype(auto) onMessage(concepts::CreateMessage auto&& msg) { - return handler_.onMessage(std::forward(msg)); // TODO: are call messages really just forwarded? + return handler_.onMessage(std::forward(msg)); } const MessageHandler& getHandler() const { return handler_; } diff --git a/src/contract/trace/calltrace.cpp b/src/contract/trace/call.cpp similarity index 98% rename from src/contract/trace/calltrace.cpp rename to src/contract/trace/call.cpp index b464940c..094ca432 100644 --- a/src/contract/trace/calltrace.cpp +++ b/src/contract/trace/call.cpp @@ -1,4 +1,4 @@ -#include "calltrace.h" +#include "call.h" #include "contract/abi.h" namespace trace { diff --git a/src/contract/trace/call.h b/src/contract/trace/call.h new file mode 100644 index 00000000..af3851b6 --- /dev/null +++ b/src/contract/trace/call.h @@ -0,0 +1,48 @@ +#ifndef BDK_CONTRACT_TRACE_CALL_H +#define BDK_CONTRACT_TRACE_CALL_H + +#include +#include "libs/zpp_bits.h" +#include "utils/bytes.h" +#include "utils/fixedbytes.h" +#include "utils/address.h" +#include "utils/utils.h" +#include "callstatus.h" +#include "calltype.h" + +namespace trace { + +struct Call { + Call() = default; + + explicit Call(const concepts::CallMessage auto& msg) + : type(getMessageCallType(msg)), + status(CallStatus::SUCCEEDED), + from(msg.from()), + to(msg.to()), + value(Utils::uint256ToBytes(messageValueOrZero(msg))), + gas(msg.gas().value()), + gasUsed(0), + input(messageInputEncoded(msg)), + output(), + calls() {} + + json toJson() const; + + using serialize = zpp::bits::members<10>; + + CallType type; + CallStatus status; + Address from; + Address to; + FixedBytes<32> value; + uint64_t gas; + uint64_t gasUsed; + Bytes input; + Bytes output; + boost::container::stable_vector calls; +}; + +} // namespace trace + +#endif // BDK_CONTRACT_TRACE_CALL_H diff --git a/src/contract/trace/callstatus.h b/src/contract/trace/callstatus.h new file mode 100644 index 00000000..c8debb47 --- /dev/null +++ b/src/contract/trace/callstatus.h @@ -0,0 +1,14 @@ +#ifndef BDK_CONTRACT_TRACE_CALLSTATUS_H +#define BDK_CONTRACT_TRACE_CALLSTATUS_H + +namespace trace { + +enum class CallStatus { + SUCCEEDED, + EXECUTION_REVERTED, + OUT_OF_GAS +}; + +} // namespace trace + +#endif // BDK_CONTRACT_TRACE_CALLSTATUS_H diff --git a/src/contract/trace/calltrace.h b/src/contract/trace/calltrace.h index 8ac28003..f9f34fb6 100644 --- a/src/contract/trace/calltrace.h +++ b/src/contract/trace/calltrace.h @@ -11,60 +11,6 @@ namespace trace { -enum class CallType { - CALL, - STATICCALL, - DELEGATECALL -}; - -template -constexpr CallType getMessageCallType(const M& msg) { - if constexpr (concepts::DelegateCallMessage) { - return CallType::DELEGATECALL; - } else if (concepts::StaticCallMessage) { - return CallType::STATICCALL; - } else { - return CallType::CALL; - } -} - -enum class CallStatus { - SUCCEEDED, - EXECUTION_REVERTED, - OUT_OF_GAS -}; - -struct Call { - Call() = default; - - explicit Call(const concepts::CallMessage auto& msg) - : type(getMessageCallType(msg)), - status(CallStatus::SUCCEEDED), - from(msg.from()), - to(msg.to()), - value(Utils::uint256ToBytes(messageValueOrZero(msg))), - gas(msg.gas().value()), - gasUsed(0), - input(messageInputEncoded(msg)), - output(), - calls() {} - - json toJson() const; - - using serialize = zpp::bits::members<10>; - - CallType type; - CallStatus status; - Address from; - Address to; - FixedBytes<32> value; - uint64_t gas; - uint64_t gasUsed; - Bytes input; - Bytes output; - boost::container::stable_vector calls; -}; - } // namespace trace #endif // BDK_CONTRACT_CALLTRACE_H diff --git a/src/contract/trace/calltype.h b/src/contract/trace/calltype.h new file mode 100644 index 00000000..e8f6f2d7 --- /dev/null +++ b/src/contract/trace/calltype.h @@ -0,0 +1,28 @@ +#ifndef BDK_CONTRACT_TRACE_CALLTYPE_H +#define BDK_CONTRACT_TRACE_CALLTYPE_H + +#include "contract/messages/concepts.h" + +namespace trace { + +enum class CallType { + CALL, + STATICCALL, + DELEGATECALL +}; + +template +constexpr CallType getMessageCallType(const M& msg) { + if constexpr (concepts::DelegateCallMessage) { + return CallType::DELEGATECALL; + } else if (concepts::StaticCallMessage) { + return CallType::STATICCALL; + } else { + return CallType::CALL; + } +} + +} // namespace trace + + +#endif // BDK_CONTRACT_TRACE_CALLTYPE_H diff --git a/src/utils/finally.h b/src/utils/finally.h new file mode 100644 index 00000000..89ee6833 --- /dev/null +++ b/src/utils/finally.h @@ -0,0 +1,16 @@ +#ifndef BDK_UTILS_FINALLY_H +#define BDK_UTILS_FINALLY_H + +#include +#include + +template +class Finally { +public: + explicit constexpr Finally(F func) : func_(std::move(func)) {} + constexpr ~Finally() { std::invoke(func_); } +private: + F func_; +}; + +#endif // BDK_UTILS_FINALLY_H From a6e3ce810f1e5f7d76249b28ccb6170804c25db2 Mon Sep 17 00:00:00 2001 From: Leonardo Amaral Date: Fri, 27 Dec 2024 16:49:51 -0300 Subject: [PATCH 16/37] call tracer UB fixed --- src/contract/calltracer.h | 38 ++++++++++++++++++++++++++-------- src/contract/trace/call.h | 14 ------------- src/contract/trace/calltrace.h | 16 -------------- src/utils/finally.h | 16 -------------- 4 files changed, 29 insertions(+), 55 deletions(-) delete mode 100644 src/contract/trace/calltrace.h delete mode 100644 src/utils/finally.h diff --git a/src/contract/calltracer.h b/src/contract/calltracer.h index ca2e6e70..46fa087a 100644 --- a/src/contract/calltracer.h +++ b/src/contract/calltracer.h @@ -7,7 +7,6 @@ #include "contract/trace/call.h" #include "contract/messages/traits.h" #include "contract/abi.h" -#include "utils/finally.h" template class CallTracer { @@ -18,16 +17,26 @@ class CallTracer { decltype(auto) onMessage(Message&& msg) { using Result = traits::MessageResult; + trace::Call& callTrace = callStack_.empty() - ? *(rootCall_ = std::make_unique(msg)) - : callStack_.top()->calls.emplace_back(msg); + ? *(rootCall_ = std::make_unique()) + : callStack_.top()->calls.emplace_back(); + + messages::Gas& gas = msg.gas(); + + callTrace.type = trace::getMessageCallType(msg); + callTrace.status = trace::CallStatus::SUCCEEDED; + callTrace.from = Address(msg.from()); + callTrace.to = Address(msg.to()); + callTrace.value = FixedBytes<32>(Utils::uint256ToBytes(messageValueOrZero(msg))); + callTrace.gas = msg.gas().value(); - callStack_.push(&callTrace); + try { + callTrace.input = messageInputEncoded(msg); + } catch (const std::exception& ignored) {} - Finally finally([this, &gas = msg.gas(), &callTrace] () { - callTrace.gasUsed = callTrace.gas - gas.value(); - callStack_.pop(); - }); + + callStack_.push(&callTrace); try { if constexpr (not std::same_as) { @@ -39,20 +48,31 @@ class CallTracer { callTrace.output = result; } + callTrace.gasUsed = callTrace.gas - gas.value(); + callStack_.pop(); + return result; } handler_.onMessage(std::forward(msg)); } catch (const OutOfGas& outOfGas) { callTrace.status = trace::CallStatus::OUT_OF_GAS; + callTrace.gasUsed = callTrace.gas - gas.value(); + callStack_.pop(); + throw outOfGas; } catch (const std::exception& error) { callTrace.status = trace::CallStatus::EXECUTION_REVERTED; if (error.what()) { - callTrace.output = ABI::Encoder::encodeError(error.what()); + try { + callTrace.output = ABI::Encoder::encodeError(error.what()); + } catch (const std::exception& ignored) {} } + callTrace.gasUsed = callTrace.gas - gas.value(); + callStack_.pop(); + throw error; } } diff --git a/src/contract/trace/call.h b/src/contract/trace/call.h index af3851b6..1194dfc5 100644 --- a/src/contract/trace/call.h +++ b/src/contract/trace/call.h @@ -13,20 +13,6 @@ namespace trace { struct Call { - Call() = default; - - explicit Call(const concepts::CallMessage auto& msg) - : type(getMessageCallType(msg)), - status(CallStatus::SUCCEEDED), - from(msg.from()), - to(msg.to()), - value(Utils::uint256ToBytes(messageValueOrZero(msg))), - gas(msg.gas().value()), - gasUsed(0), - input(messageInputEncoded(msg)), - output(), - calls() {} - json toJson() const; using serialize = zpp::bits::members<10>; diff --git a/src/contract/trace/calltrace.h b/src/contract/trace/calltrace.h deleted file mode 100644 index f9f34fb6..00000000 --- a/src/contract/trace/calltrace.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef BDK_CONTRACT_CALLTRACE_H -#define BDK_CONTRACT_CALLTRACE_H - -#include -#include "libs/zpp_bits.h" -#include "utils/bytes.h" -#include "utils/fixedbytes.h" -#include "utils/address.h" -#include "utils/utils.h" -#include "contract/messages/concepts.h" - -namespace trace { - -} // namespace trace - -#endif // BDK_CONTRACT_CALLTRACE_H diff --git a/src/utils/finally.h b/src/utils/finally.h deleted file mode 100644 index 89ee6833..00000000 --- a/src/utils/finally.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef BDK_UTILS_FINALLY_H -#define BDK_UTILS_FINALLY_H - -#include -#include - -template -class Finally { -public: - explicit constexpr Finally(F func) : func_(std::move(func)) {} - constexpr ~Finally() { std::invoke(func_); } -private: - F func_; -}; - -#endif // BDK_UTILS_FINALLY_H From 0fba68b31df58f1546af3cf91d73a651523bba17 Mon Sep 17 00:00:00 2001 From: Leonardo Date: Mon, 6 Jan 2025 14:34:35 -0300 Subject: [PATCH 17/37] precompiled contract added and errors fixed --- src/contract/CMakeLists.txt | 1 + src/contract/calltracer.h | 4 +- src/contract/contracthost.cpp | 15 ++++++- src/contract/contracthost.h | 12 +++--- src/contract/messages/common.h | 2 +- src/contract/messages/evmcontractexecutor.cpp | 5 ++- src/contract/messages/messagedispatcher.h | 22 ++++++++-- .../messages/precompiledcontractexecutor.cpp | 6 +++ .../messages/precompiledcontractexecutor.h | 42 +++++++++++++++++++ src/utils/randomgen.cpp | 22 ++++++++++ src/utils/randomgen.h | 12 +++++- src/utils/view.h | 4 ++ 12 files changed, 129 insertions(+), 18 deletions(-) create mode 100644 src/contract/messages/precompiledcontractexecutor.cpp create mode 100644 src/contract/messages/precompiledcontractexecutor.h diff --git a/src/contract/CMakeLists.txt b/src/contract/CMakeLists.txt index 9244e982..4251e5e3 100644 --- a/src/contract/CMakeLists.txt +++ b/src/contract/CMakeLists.txt @@ -57,6 +57,7 @@ set(CONTRACT_SOURCES ${CMAKE_SOURCE_DIR}/src/contract/messages/common.cpp ${CMAKE_SOURCE_DIR}/src/contract/messages/executioncontext.cpp ${CMAKE_SOURCE_DIR}/src/contract/messages/evmcontractexecutor.cpp + ${CMAKE_SOURCE_DIR}/src/contract/messages/precompiledcontractexecutor.cpp ${CMAKE_SOURCE_DIR}/src/contract/templates/ownable.cpp ${CMAKE_SOURCE_DIR}/src/contract/templates/erc20.cpp ${CMAKE_SOURCE_DIR}/src/contract/templates/erc721.cpp diff --git a/src/contract/calltracer.h b/src/contract/calltracer.h index 46fa087a..251f8f81 100644 --- a/src/contract/calltracer.h +++ b/src/contract/calltracer.h @@ -81,9 +81,9 @@ class CallTracer { return handler_.onMessage(std::forward(msg)); } - const MessageHandler& getHandler() const { return handler_; } + const MessageHandler& handler() const { return handler_; } - MessageHandler& getHandler() { return handler_; } + MessageHandler& handler() { return handler_; } bool hasCallTrace() const { return rootCall_ != nullptr; } diff --git a/src/contract/contracthost.cpp b/src/contract/contracthost.cpp index 1ad39f79..005d71b5 100644 --- a/src/contract/contracthost.cpp +++ b/src/contract/contracthost.cpp @@ -1,7 +1,7 @@ #include "contracthost.h" -MessageHandler makeMessageHandler(ContractHost& host, ExecutionContext& context, evmc_vm *vm, Storage& storage) { - MessageDispatcher dispatcher(context, CppContractExecutor(context, host), EvmContractExecutor(context, vm)); +MessageHandler makeMessageHandler(ContractHost& host, ExecutionContext& context, evmc_vm *vm, Storage& storage, const Hash& randomSeed) { + MessageDispatcher dispatcher(context, CppContractExecutor(context, host), EvmContractExecutor(context, vm), PrecompiledContractExecutor(RandomGen(randomSeed))); if (context.getTxHash() && storage.getIndexingMode() == IndexingMode::RPC_TRACE) { return CallTracer(std::move(dispatcher)); @@ -10,6 +10,17 @@ MessageHandler makeMessageHandler(ContractHost& host, ExecutionContext& context, return dispatcher; } +uint256_t ContractHost::getRandomValue() { + return std::visit(Utils::Overloaded{ + [] (MessageDispatcher& handler) { + return std::invoke(handler.precompiledExecutor().randomGenerator()); + }, + [] (CallTracer& tracer) { + return std::invoke(tracer.handler().precompiledExecutor().randomGenerator()); + } + }, messageHandler_); +} + ContractHost::~ContractHost() { if (mustRevert_) { for (auto& var : this->stack_.getUsedVars()) { diff --git a/src/contract/contracthost.h b/src/contract/contracthost.h index 61e66d77..248b4599 100644 --- a/src/contract/contracthost.h +++ b/src/contract/contracthost.h @@ -59,14 +59,13 @@ const auto BDK_PRECOMPILE = 0x1000000000000000000000000000100000000001_address; using MessageHandler = std::variant>; -MessageHandler makeMessageHandler(ContractHost& host, ExecutionContext& context, evmc_vm *vm, Storage& storage); +MessageHandler makeMessageHandler(ContractHost& host, ExecutionContext& context, evmc_vm *vm, Storage& storage, const Hash& randomSeed); class ContractHost { private: DumpManager& manager_; Storage& storage_; mutable ContractStack stack_; - mutable RandomGen randomGen_; // Random generator for the contract. bool mustRevert_ = true; // We always assume that we must revert until proven otherwise. ExecutionContext& context_; MessageHandler messageHandler_; @@ -79,13 +78,12 @@ class ContractHost { ExecutionContext& context) : manager_(manager), storage_(storage), - randomGen_(randomnessSeed), stack_(), context_(context), - messageHandler_(makeMessageHandler(*this, context, vm, storage)) { + messageHandler_(makeMessageHandler(*this, context, vm, storage, randomnessSeed)) { std::visit(Utils::Overloaded{ [] (MessageDispatcher& handler) { handler.evmExecutor().setMessageHandler(AnyEncodedMessageHandler::from(handler)); }, - [] (CallTracer& tracer) { tracer.getHandler().evmExecutor().setMessageHandler(AnyEncodedMessageHandler::from(tracer)); } + [] (CallTracer& tracer) { tracer.handler().evmExecutor().setMessageHandler(AnyEncodedMessageHandler::from(tracer)); } }, messageHandler_); } @@ -230,7 +228,7 @@ class ContractHost { void registerVariableUse(SafeBase& var) { stack_.registerVariableUse(var); } - uint256_t getRandomValue() const { return std::invoke(this->randomGen_); } + uint256_t getRandomValue(); private: decltype(auto) dispatchMessage(auto&& msg) { @@ -242,7 +240,7 @@ class ContractHost { messages::Gas& getCurrentGas() { return std::visit(Utils::Overloaded{ [] (MessageDispatcher& handler) -> messages::Gas& { return handler.cppExecutor().currentGas(); }, - [] (CallTracer& tracer) -> messages::Gas& { return tracer.getHandler().cppExecutor().currentGas(); } + [] (CallTracer& tracer) -> messages::Gas& { return tracer.handler().cppExecutor().currentGas(); } }, messageHandler_); } }; diff --git a/src/contract/messages/common.h b/src/contract/messages/common.h index 538e8364..a8b8ba1d 100644 --- a/src/contract/messages/common.h +++ b/src/contract/messages/common.h @@ -48,7 +48,7 @@ Bytes messageInputEncoded(const concepts::EncodedMessage auto& msg) { Bytes messageInputEncoded(const concepts::PackedMessage auto& msg) { return std::apply([&] (const auto&... args) -> Bytes { const std::string functionName = ContractReflectionInterface::getFunctionName(msg.method()); - const BytesArr<4> encodedFunctor = Utils::uint32ToBytes(ABI::FunctorEncoder::encode(functionName).value); + const BytesArr<4> encodedFunctor = Utils::uint32ToBytes(ABI::FunctorEncoder::encode(std::string(functionName)).value); if constexpr (sizeof...(args) > 0) { const Bytes encodedArgs = ABI::Encoder::encodeData(args...); diff --git a/src/contract/messages/evmcontractexecutor.cpp b/src/contract/messages/evmcontractexecutor.cpp index a028bee1..1b459fad 100644 --- a/src/contract/messages/evmcontractexecutor.cpp +++ b/src/contract/messages/evmcontractexecutor.cpp @@ -1,5 +1,6 @@ #include "evmcontractexecutor.h" #include "bytes/cast.h" +#include "bytes/hex.h" #include "common.h" #include "outofgas.h" @@ -56,7 +57,7 @@ constexpr evmc_message makeEvmcMessage(const M& msg, uint64_t depth) { .input_size = msg.input().size(), .value = Utils::uint256ToEvmcUint256(messageValueOrZero(msg)), .create2_salt = evmc_bytes32{}, - .code_address = evmc_address{} // TODO: CALL CODE? + .code_address = bytes::cast(messageCodeAddress(msg)) }; } @@ -116,7 +117,6 @@ static void createContractImpl(auto& msg, ExecutionContext& context, View decltype(auto) onMessage(M&& msg) { using Result = traits::MessageResult; - const Account& account = context_.getAccount(messageCodeAddress(msg)); + View
codeAddress = messageCodeAddress(msg); + + if (isPrecompiled(codeAddress)) { + return precompiledExecutor_.execute(std::forward(msg)); + } + + const Account& account = context_.getAccount(codeAddress); if (!account.isContract()) { throw DynamicException("Not a contract address"); @@ -72,10 +80,18 @@ class MessageDispatcher { EvmContractExecutor& evmExecutor() { return evmExecutor_; } + PrecompiledContractExecutor& precompiledExecutor() { return precompiledExecutor_; } + private: + bool isPrecompiled(View
address) const { + constexpr Address randomGeneratorAddress = bytes::hex("0x1000000000000000000000000000100000000001"); + return address == randomGeneratorAddress; + } + ExecutionContext& context_; CppContractExecutor cppExecutor_; EvmContractExecutor evmExecutor_; + PrecompiledContractExecutor precompiledExecutor_; }; #endif // BDK_MESSAGES_MESSAGEDISPATCHER_H diff --git a/src/contract/messages/precompiledcontractexecutor.cpp b/src/contract/messages/precompiledcontractexecutor.cpp new file mode 100644 index 00000000..9c578dc0 --- /dev/null +++ b/src/contract/messages/precompiledcontractexecutor.cpp @@ -0,0 +1,6 @@ +#include "precompiledcontractexecutor.h" + +Bytes PrecompiledContractExecutor::execute(EncodedStaticCallMessage& msg) { + const uint256_t randomValue = std::invoke(randomGen_); + return Utils::makeBytes(Utils::uint256ToBytes(randomValue)); +} diff --git a/src/contract/messages/precompiledcontractexecutor.h b/src/contract/messages/precompiledcontractexecutor.h new file mode 100644 index 00000000..a056699d --- /dev/null +++ b/src/contract/messages/precompiledcontractexecutor.h @@ -0,0 +1,42 @@ +#ifndef BDK_CONTRACT_PRECOMPILEDCONTRACTEXECUTOR_H +#define BDK_CONTRACT_PRECOMPILEDCONTRACTEXECUTOR_H + +#include "utils/randomgen.h" +#include "traits.h" +#include "contract/messages/encodedmessages.h" +#include "contract/abi.h" + +class PrecompiledContractExecutor { +public: + explicit PrecompiledContractExecutor(RandomGen randomGen) : randomGen_(std::move(randomGen)) {} + + Bytes execute(EncodedStaticCallMessage& msg); + + template> + Result execute(Message&& msg) { + + if constexpr (concepts::DelegateCallMessage) { + throw DynamicException("Delegate calls not allowed for precompiled contracts"); + } + + const Bytes input = messageInputEncoded(msg); + EncodedStaticCallMessage encodedMessage(msg.from(), msg.to(), msg.gas(), input); + + if constexpr (concepts::PackedMessage) { + if constexpr (std::same_as) { + this->execute(encodedMessage); + } else { + return std::get<0>(ABI::Decoder::decodeData(this->execute(encodedMessage))); + } + } else { + return this->execute(encodedMessage); + } + } + + RandomGen& randomGenerator() { return randomGen_; } + +private: + RandomGen randomGen_; +}; + +#endif // BDK_CONTRACT_PRECOMPILEDCONTRACTEXECUTOR_H diff --git a/src/utils/randomgen.cpp b/src/utils/randomgen.cpp index 231f54b4..e513f51d 100644 --- a/src/utils/randomgen.cpp +++ b/src/utils/randomgen.cpp @@ -7,6 +7,28 @@ See the LICENSE.txt file in the project root for more information. #include "randomgen.h" +RandomGen::RandomGen(const RandomGen& other) { + std::unique_lock lock(other.seedLock_); + seed_ = other.seed_; +} + +RandomGen& RandomGen::operator=(const RandomGen& other) { + std::unique_lock lockA(seedLock_, std::defer_lock); + std::unique_lock lockB(other.seedLock_, std::defer_lock); + + std::lock(lockA, lockB); + + seed_ = other.seed_; + + return *this; +} + +RandomGen& RandomGen::operator=(RandomGen&& other) noexcept { + std::unique_lock lock(seedLock_); + seed_ = other.seed_; + return *this; +} + uint256_t RandomGen::operator()() { std::lock_guard lock(this->seedLock_); this->seed_ = Utils::sha3(this->seed_); diff --git a/src/utils/randomgen.h b/src/utils/randomgen.h index 5563f380..c1d87f68 100644 --- a/src/utils/randomgen.h +++ b/src/utils/randomgen.h @@ -34,7 +34,17 @@ class RandomGen { * Constructor. * @param seed A random seed for initialization. */ - explicit RandomGen(const Hash& seed) : seed_(seed) {}; + explicit RandomGen(const Hash& seed) : seed_(seed) {} + + ~RandomGen() = default; + + RandomGen(const RandomGen&); + + RandomGen(RandomGen&& other) noexcept : seed_(other.seed_) {} + + RandomGen& operator=(const RandomGen&); + + RandomGen& operator=(RandomGen&&) noexcept; /// Getter for `seed_`. inline const Hash& getSeed() const { std::lock_guard lock(seedLock_); return this->seed_; } diff --git a/src/utils/view.h b/src/utils/view.h index 13da3387..5b9ccc11 100644 --- a/src/utils/view.h +++ b/src/utils/view.h @@ -52,6 +52,10 @@ struct View : std::span { template S> constexpr View(I begin, S end) : span(begin, end) {} + constexpr auto cbegin() const { return begin(); } + + constexpr auto cend() const { return end(); } + explicit operator Bytes() const { return Bytes(begin(), end()); } From ecc20f4e8bebfd79731c3c159d3c46facc90a710 Mon Sep 17 00:00:00 2001 From: Leonardo Amaral Date: Wed, 8 Jan 2025 15:16:00 -0300 Subject: [PATCH 18/37] many improvements and fixes related to gas --- src/contract/calltracer.h | 11 ++-- src/contract/contracthost.h | 9 +-- src/contract/costs.h | 12 ++++ src/contract/cpp/callexecutor.cpp | 4 +- src/contract/evm/callexecutor.cpp | 6 +- src/contract/gas.h | 56 ------------------- src/contract/messages/basemessage.h | 8 +-- src/contract/messages/concepts.h | 2 +- src/contract/messages/cppcontractexecutor.h | 18 ++++-- src/contract/messages/evmcontractexecutor.cpp | 28 +++++----- src/contract/messages/gas.h | 20 +++---- .../messages/precompiledcontractexecutor.h | 4 +- src/core/state.cpp | 10 ++-- src/net/http/jsonrpc/methods.cpp | 6 +- src/utils/tx.cpp | 2 +- src/utils/tx.h | 2 +- .../contract/messages/evmcontractexecutor.cpp | 6 +- tests/core/state.cpp | 2 +- tests/sdktestsuite.hpp | 8 +-- 19 files changed, 92 insertions(+), 122 deletions(-) create mode 100644 src/contract/costs.h delete mode 100644 src/contract/gas.h diff --git a/src/contract/calltracer.h b/src/contract/calltracer.h index 251f8f81..bab50a4f 100644 --- a/src/contract/calltracer.h +++ b/src/contract/calltracer.h @@ -22,20 +22,19 @@ class CallTracer { ? *(rootCall_ = std::make_unique()) : callStack_.top()->calls.emplace_back(); - messages::Gas& gas = msg.gas(); + Gas& gas = msg.gas(); callTrace.type = trace::getMessageCallType(msg); callTrace.status = trace::CallStatus::SUCCEEDED; callTrace.from = Address(msg.from()); callTrace.to = Address(msg.to()); callTrace.value = FixedBytes<32>(Utils::uint256ToBytes(messageValueOrZero(msg))); - callTrace.gas = msg.gas().value(); + callTrace.gas = uint64_t(gas); try { callTrace.input = messageInputEncoded(msg); } catch (const std::exception& ignored) {} - callStack_.push(&callTrace); try { @@ -48,7 +47,7 @@ class CallTracer { callTrace.output = result; } - callTrace.gasUsed = callTrace.gas - gas.value(); + callTrace.gasUsed = callTrace.gas - uint64_t(gas); callStack_.pop(); return result; @@ -57,7 +56,7 @@ class CallTracer { handler_.onMessage(std::forward(msg)); } catch (const OutOfGas& outOfGas) { callTrace.status = trace::CallStatus::OUT_OF_GAS; - callTrace.gasUsed = callTrace.gas - gas.value(); + callTrace.gasUsed = callTrace.gas - uint64_t(gas); callStack_.pop(); throw outOfGas; @@ -70,7 +69,7 @@ class CallTracer { } catch (const std::exception& ignored) {} } - callTrace.gasUsed = callTrace.gas - gas.value(); + callTrace.gasUsed = callTrace.gas - uint64_t(gas); callStack_.pop(); throw error; diff --git a/src/contract/contracthost.h b/src/contract/contracthost.h index 248b4599..1e3cb0bc 100644 --- a/src/contract/contracthost.h +++ b/src/contract/contracthost.h @@ -25,6 +25,7 @@ #include "messages/messagedispatcher.h" #include "messages/packedmessages.h" #include "calltracer.h" +#include "costs.h" // TODO: EVMC Static Mode Handling // TODO: Contract creating other contracts (EVM Factories) @@ -103,7 +104,7 @@ class ContractHost { decltype(auto) execute(concepts::Message auto&& msg) { try { mustRevert_ = false; - msg.gas().use(21000); + msg.gas().use(CONTRACT_EXECUTION_COST); return dispatchMessage(std::forward(msg)); } catch (const std::exception& err) { mustRevert_ = true; @@ -237,10 +238,10 @@ class ContractHost { }, messageHandler_); } - messages::Gas& getCurrentGas() { + Gas& getCurrentGas() { return std::visit(Utils::Overloaded{ - [] (MessageDispatcher& handler) -> messages::Gas& { return handler.cppExecutor().currentGas(); }, - [] (CallTracer& tracer) -> messages::Gas& { return tracer.handler().cppExecutor().currentGas(); } + [] (MessageDispatcher& handler) -> Gas& { return handler.cppExecutor().currentGas(); }, + [] (CallTracer& tracer) -> Gas& { return tracer.handler().cppExecutor().currentGas(); } }, messageHandler_); } }; diff --git a/src/contract/costs.h b/src/contract/costs.h new file mode 100644 index 00000000..bd93afa7 --- /dev/null +++ b/src/contract/costs.h @@ -0,0 +1,12 @@ +#ifndef BDK_CONTRACT_COSTS_H +#define BDK_CONTRACT_COSTS_H + +#include + +constexpr uint64_t CONTRACT_EXECUTION_COST = 21'000; +constexpr uint64_t EVM_CONTRACT_CREATION_COST = 100'000; +constexpr uint64_t CPP_CONTRACT_CREATION_COST = 50'000; +constexpr uint64_t CPP_CONTRACT_CALL_COST = 1'000; +constexpr uint64_t EVM_CONTRACT_CALL_COST = 5'000; + +#endif // BDK_CONTRACT_COSTS_H diff --git a/src/contract/cpp/callexecutor.cpp b/src/contract/cpp/callexecutor.cpp index 115bca6e..3e25894b 100644 --- a/src/contract/cpp/callexecutor.cpp +++ b/src/contract/cpp/callexecutor.cpp @@ -15,7 +15,7 @@ Bytes CallExecutor::executeCall(kind::Normal, Gas& gas, const evm::Message& msg) .kind = EVMC_CALL, .flags = 0, .depth = msg.depth, - .gas = gas.value(), + .gas = int64_t(gas), .recipient = bytes::cast(msg.to), .sender = bytes::cast(msg.from), .input_data = msg.input.data(), @@ -32,7 +32,7 @@ Bytes CallExecutor::executeCall(kind::Static, Gas& gas, const evm::Message& msg) .kind = EVMC_CALL, .flags = EVMC_STATIC, .depth = msg.depth, - .gas = gas.value(), + .gas = int64_t(gas), .recipient = bytes::cast(msg.to), .sender = bytes::cast(msg.from), .input_data = msg.input.data(), diff --git a/src/contract/evm/callexecutor.cpp b/src/contract/evm/callexecutor.cpp index 7b96f0e9..2d99fe41 100644 --- a/src/contract/evm/callexecutor.cpp +++ b/src/contract/evm/callexecutor.cpp @@ -45,7 +45,7 @@ Bytes CallExecutor::executeCall(kind::Any callKind, Gas& gas, const Message& msg .kind = getCallKind(callKind), .flags = getCallFlags(callKind), .depth = msg.depth, - .gas = gas.value(), + .gas = int64_t(gas), .recipient = bytes::cast(msg.to), .sender = bytes::cast(msg.from), .input_data = msg.input.data(), @@ -241,8 +241,8 @@ evmc::Result CallExecutor::call(const evmc_message& msg) noexcept { } return std::visit(Utils::Overloaded{ - [&] (Bytes output) { return evmc::Result(status, gas.value(), 0, output.data(), output.size()); }, - [&] (const Address& createAddress) { return evmc::Result(status, gas.value(), 0, createAddress.toEvmcAddress()); }, + [&] (Bytes output) { return evmc::Result(status, int64_t(gas), 0, output.data(), output.size()); }, + [&] (const Address& createAddress) { return evmc::Result(status, int64_t(gas), 0, createAddress.toEvmcAddress()); }, }, std::move(result)); } diff --git a/src/contract/gas.h b/src/contract/gas.h deleted file mode 100644 index a7f1dcbe..00000000 --- a/src/contract/gas.h +++ /dev/null @@ -1,56 +0,0 @@ - -#pragma once - -struct ExecutionFailure : std::runtime_error { - explicit ExecutionFailure(std::string_view str) : std::runtime_error(str.data()) {} -}; - -struct OutOfGas : ExecutionFailure { - OutOfGas() : ExecutionFailure("out of gas") {} -}; - -struct ExecutionReverted : ExecutionFailure { - explicit ExecutionReverted(std::string_view msg) : ExecutionFailure(msg) {} - explicit ExecutionReverted() : ExecutionReverted("execution reverted") {} -}; - -namespace kind { - -struct Normal{}; -struct Static{}; -struct Delegate{}; - -using Any = std::variant; - -inline constexpr Normal NORMAL{}; -inline constexpr Static STATIC{}; -inline constexpr Delegate DELEGATE{}; - -}; - -class Gas { -public: - constexpr explicit Gas(uint64_t value) : value_(value) {} - - constexpr uint64_t value() const { return value_; } - - constexpr void use(uint64_t amount) { - if (amount > value_) { - value_ = 0; - throw OutOfGas(); - } - - value_ -= amount; - } - - constexpr void refund(uint64_t amount) { - value_ += amount; // TODO: check overflow? - } - - constexpr void set(uint64_t amount) { - value_ = amount; - } - -// private: - uint64_t value_; -}; \ No newline at end of file diff --git a/src/contract/messages/basemessage.h b/src/contract/messages/basemessage.h index 5229eb9c..0818a0fd 100644 --- a/src/contract/messages/basemessage.h +++ b/src/contract/messages/basemessage.h @@ -48,12 +48,12 @@ class ToField { class GasField { public: - explicit constexpr GasField(messages::Gas& gas) : gas_(gas) {} - constexpr messages::Gas& gas() { return gas_; } - constexpr const messages::Gas& gas() const { return gas_; } + explicit constexpr GasField(Gas& gas) : gas_(gas) {} + constexpr Gas& gas() { return gas_; } + constexpr const Gas& gas() const { return gas_; } private: - messages::Gas& gas_; + Gas& gas_; }; class ValueField { diff --git a/src/contract/messages/concepts.h b/src/contract/messages/concepts.h index 9ba836d3..4cbdb2e9 100644 --- a/src/contract/messages/concepts.h +++ b/src/contract/messages/concepts.h @@ -12,7 +12,7 @@ namespace concepts { template concept Message = requires (M m) { { m.from() } -> std::convertible_to>; - { m.gas() } -> std::convertible_to; + { m.gas() } -> std::convertible_to; }; /** diff --git a/src/contract/messages/cppcontractexecutor.h b/src/contract/messages/cppcontractexecutor.h index 92e333b5..60c16a6b 100644 --- a/src/contract/messages/cppcontractexecutor.h +++ b/src/contract/messages/cppcontractexecutor.h @@ -5,6 +5,7 @@ #include "traits.h" #include "bytes/cast.h" #include "utils/contractreflectioninterface.h" +#include "contract/costs.h" struct ContractHost; @@ -29,10 +30,11 @@ class CppContractExecutor { } } - messages::Gas& currentGas() { return *currentGas_; } + Gas& currentGas() { return *currentGas_; } private: decltype(auto) callContract(concepts::PackedMessage auto&& msg) { + msg.gas().use(CPP_CONTRACT_CALL_COST); auto& contract = getContractAs>(msg.to()); transactional::Group guard = { @@ -56,11 +58,17 @@ class CppContractExecutor { } decltype(auto) callContract(concepts::EncodedMessage auto&& msg) { + if (msg.to() == ProtocolContractAddresses.at("ContractManager")) [[unlikely]] { + msg.gas().use(CPP_CONTRACT_CREATION_COST); + } else [[likely]] { + msg.gas().use(CPP_CONTRACT_CALL_COST); + } + const evmc_message evmcMsg{ .kind = EVMC_CALL, .flags = (concepts::StaticCallMessage ? EVMC_STATIC : evmc_flags(0)), .depth = 0, - .gas = msg.gas().value(), + .gas = int64_t(msg.gas()), .recipient = bytes::cast(msg.to()), .sender = bytes::cast(msg.from()), .input_data = msg.input().data(), @@ -85,6 +93,8 @@ class CppContractExecutor { } Address createContract(concepts::CreateMessage auto&& msg) { + msg.gas().use(CPP_CONTRACT_CREATION_COST); + using ContractType = traits::MessageContract; const std::string createSignature = "createNew" @@ -109,7 +119,7 @@ class CppContractExecutor { .kind = EVMC_CREATE, .flags = 0, .depth = 1, - .gas = msg.gas().value(), + .gas = int64_t(msg.gas()), .recipient = bytes::cast(to), .sender = bytes::cast(msg.from()), .input_data = fullData.data(), @@ -152,7 +162,7 @@ class CppContractExecutor { ExecutionContext& context_; ContractHost& host_; - messages::Gas *currentGas_ = nullptr; + Gas *currentGas_ = nullptr; }; #endif // BDK_MESSAGES_CPPCONTRACTEXECUTOR_H diff --git a/src/contract/messages/evmcontractexecutor.cpp b/src/contract/messages/evmcontractexecutor.cpp index 1b459fad..768580cb 100644 --- a/src/contract/messages/evmcontractexecutor.cpp +++ b/src/contract/messages/evmcontractexecutor.cpp @@ -3,6 +3,7 @@ #include "bytes/hex.h" #include "common.h" #include "outofgas.h" +#include "contract/costs.h" constexpr decltype(auto) getAndThen(auto&& map, const auto& key, auto&& andThen, auto&& orElse) { const auto it = map.find(key); @@ -50,7 +51,7 @@ constexpr evmc_message makeEvmcMessage(const M& msg, uint64_t depth) { .kind = getEvmcKind(msg), .flags = getEvmcFlags(msg), .depth = depth, - .gas = msg.gas().value(), + .gas = int64_t(msg.gas()), .recipient = bytes::cast(messageRecipientOrDefault(msg)), .sender = bytes::cast(msg.from()), .input_data = msg.input().data(), @@ -67,7 +68,7 @@ constexpr evmc_message makeEvmcMessage(const M& msg, uint64_t depth, View(contractAddress), .sender = bytes::cast(msg.from()), .input_data = nullptr, @@ -78,7 +79,7 @@ constexpr evmc_message makeEvmcMessage(const M& msg, uint64_t depth, View code) { +static Bytes executeEvmcMessage(evmc_vm* vm, const evmc_host_interface* host, evmc_host_context* context, const evmc_message& msg, Gas& gas, View code) { evmc::Result result(::evmc_execute( vm, host, @@ -88,7 +89,7 @@ static Bytes executeEvmcMessage(evmc_vm* vm, const evmc_host_interface* host, ev code.data(), code.size())); - gas = messages::Gas(result.gas_left); + gas = Gas(result.gas_left); if (result.status_code == EVMC_SUCCESS) { return Bytes(result.output_data, result.output_data + result.output_size); @@ -110,7 +111,7 @@ static void createContractImpl(auto& msg, ExecutionContext& context, View code = context_.getAccount(msg.to()).code; auto depthGuard = transactional::copy(depth_); @@ -140,6 +143,7 @@ Bytes EvmContractExecutor::execute(EncodedStaticCallMessage& msg) { } Bytes EvmContractExecutor::execute(EncodedDelegateCallMessage& msg) { + msg.gas().use(EVM_CONTRACT_CALL_COST); auto depthGuard = transactional::copy(depth_); ++depth_; @@ -150,6 +154,7 @@ Bytes EvmContractExecutor::execute(EncodedDelegateCallMessage& msg) { } Address EvmContractExecutor::execute(EncodedCreateMessage& msg) { + msg.gas().use(EVM_CONTRACT_CREATION_COST); auto depthGuard = transactional::copy(depth_); const Address contractAddress = generateContractAddress(context_.getAccount(msg.from()).nonce, msg.from()); createContractImpl(msg, context_, contractAddress, vm_, *this, ++depth_); @@ -158,6 +163,7 @@ Address EvmContractExecutor::execute(EncodedCreateMessage& msg) { } Address EvmContractExecutor::execute(EncodedSaltCreateMessage& msg) { + msg.gas().use(EVM_CONTRACT_CREATION_COST); auto depthGuard = transactional::copy(depth_); const Address contractAddress = generateContractAddress(msg.from(), msg.salt(), msg.code()); createContractImpl(msg, context_, contractAddress, vm_, *this, ++depth_); @@ -288,7 +294,7 @@ void EvmContractExecutor::set_transient_storage(const evmc::address &addr, const } evmc::Result EvmContractExecutor::call(const evmc_message& msg) noexcept { - messages::Gas gas(msg.gas); + Gas gas(msg.gas); const uint256_t value = Utils::evmcUint256ToUint256(msg.value); const auto process = [&] (auto& msg) { @@ -296,9 +302,9 @@ evmc::Result EvmContractExecutor::call(const evmc_message& msg) noexcept { const auto output = messageHandler_.onMessage(msg); if constexpr (concepts::CreateMessage) { - return evmc::Result(EVMC_SUCCESS, gas.value(), 0, bytes::cast(output)); + return evmc::Result(EVMC_SUCCESS, int64_t(gas), 0, bytes::cast(output)); } else { - return evmc::Result(EVMC_SUCCESS, gas.value(), 0, output.data(), output.size()); + return evmc::Result(EVMC_SUCCESS, int64_t(gas), 0, output.data(), output.size()); } } catch (const OutOfGas&) { // TODO: ExecutionReverted exception is important return evmc::Result(EVMC_OUT_OF_GAS); @@ -309,15 +315,11 @@ evmc::Result EvmContractExecutor::call(const evmc_message& msg) noexcept { output = ABI::Encoder::encodeError(err.what()); // TODO: this may throw... } - return evmc::Result(EVMC_REVERT, gas.value(), 0, output.data(), output.size()); + return evmc::Result(EVMC_REVERT, int64_t(gas), 0, output.data(), output.size()); } }; if (msg.kind == EVMC_DELEGATECALL) { - std::cout << "from: " << Hex::fromBytes(msg.sender, true) << "\n"; - std::cout << "to: " << Hex::fromBytes(msg.recipient, true) << "\n"; - std::cout << "code address: " << Hex::fromBytes(msg.code_address, true) << "\n"; - EncodedDelegateCallMessage encodedMessage(msg.sender, msg.recipient, gas, value, View(msg.input_data, msg.input_size), msg.code_address); return process(encodedMessage); } else if (msg.kind == EVMC_CALL && msg.flags == EVMC_STATIC) { diff --git a/src/contract/messages/gas.h b/src/contract/messages/gas.h index ccf8711b..a98ce3de 100644 --- a/src/contract/messages/gas.h +++ b/src/contract/messages/gas.h @@ -3,15 +3,11 @@ #include "outofgas.h" -namespace messages { - class Gas { public: - constexpr explicit Gas(uint64_t value = 0) : value_(value) {} - - constexpr uint64_t value() const { return value_; } + constexpr explicit Gas(uint256_t value = 0) : value_(value) {} - constexpr void use(uint64_t amount) { + constexpr void use(const uint256_t& amount) { if (amount > value_) { value_ = 0; throw OutOfGas(); @@ -20,14 +16,18 @@ class Gas { value_ -= amount; } - constexpr void refund(uint64_t amount) { + constexpr void refund(const uint256_t& amount) { value_ += amount; } + template + requires std::constructible_from + explicit constexpr operator T() const { + return static_cast(value_); + } + private: - uint64_t value_; + uint256_t value_; }; -} // namespace messages - #endif // BDK_MESSAGES_GAS_H diff --git a/src/contract/messages/precompiledcontractexecutor.h b/src/contract/messages/precompiledcontractexecutor.h index a056699d..09ff7eb6 100644 --- a/src/contract/messages/precompiledcontractexecutor.h +++ b/src/contract/messages/precompiledcontractexecutor.h @@ -5,6 +5,7 @@ #include "traits.h" #include "contract/messages/encodedmessages.h" #include "contract/abi.h" +#include "contract/costs.h" class PrecompiledContractExecutor { public: @@ -14,11 +15,12 @@ class PrecompiledContractExecutor { template> Result execute(Message&& msg) { - if constexpr (concepts::DelegateCallMessage) { throw DynamicException("Delegate calls not allowed for precompiled contracts"); } + msg.gas().use(CPP_CONTRACT_CALL_COST); + const Bytes input = messageInputEncoded(msg); EncodedStaticCallMessage encodedMessage(msg.from(), msg.to(), msg.gas(), input); diff --git a/src/core/state.cpp b/src/core/state.cpp index 1f28d0e1..430e9c76 100644 --- a/src/core/state.cpp +++ b/src/core/state.cpp @@ -213,7 +213,7 @@ void State::processTransaction( return; } - messages::Gas gas(static_cast(tx.getGasLimit())); + Gas gas(uint64_t(tx.getGasLimit())); try { const Hash randomSeed(Utils::uint256ToBytes((static_cast(randomnessHash) + txIndex))); @@ -250,7 +250,7 @@ void State::processTransaction( } ++fromNonce; - auto usedGas = tx.getGasLimit() - gas.value(); + auto usedGas = tx.getGasLimit() - uint256_t(gas); fromBalance -= (usedGas * tx.getMaxFeePerGas()); } @@ -499,10 +499,10 @@ int64_t State::estimateGas(EncodedMessageVariant msg) { ); return std::visit([&host] (auto&& msg) { - const messages::Gas& gas = msg.gas(); - const uint64_t initialGas = gas.value(); + const Gas& gas = msg.gas(); + const int64_t initialGas(gas); host.simulate(std::forward(msg)); - return initialGas - gas.value(); + return initialGas - int64_t(gas); }, std::move(msg)); } diff --git a/src/net/http/jsonrpc/methods.cpp b/src/net/http/jsonrpc/methods.cpp index 06c45f36..4bac1c7e 100644 --- a/src/net/http/jsonrpc/methods.cpp +++ b/src/net/http/jsonrpc/methods.cpp @@ -95,8 +95,8 @@ static json getBlockJson(const FinalizedBlock *block, bool includeTransactions) return ret; } -static std::tuple parseMessage(const json& request, const Storage& storage, bool recipientRequired) { - std::tuple result; +static std::tuple parseMessage(const json& request, const Storage& storage, bool recipientRequired) { + std::tuple result; auto& [from, to, gas, value, data] = result; @@ -112,7 +112,7 @@ static std::tuple parseMessag ? parse
(txJson.at("to")) : parseIfExists
(txJson, "to").value_or(Address{}); - gas = messages::Gas(parseIfExists(txJson, "gas").value_or(10000000)); + gas = Gas(parseIfExists(txJson, "gas").value_or(10'000'000)); value = uint256_t(parseIfExists(txJson, "value").value_or(0)); diff --git a/src/utils/tx.cpp b/src/utils/tx.cpp index 9be95aa4..087d1571 100644 --- a/src/utils/tx.cpp +++ b/src/utils/tx.cpp @@ -489,7 +489,7 @@ evmc_message TxBlock::txToMessage() const { return msg; } -EncodedMessageVariant TxBlock::toMessage(messages::Gas& gas) const { +EncodedMessageVariant TxBlock::toMessage(Gas& gas) const { if (this->to_ == Address()) { return EncodedCreateMessage(this->from_, gas, this->value_, this->data_); } else { diff --git a/src/utils/tx.h b/src/utils/tx.h index 4ddf90b7..c81ee925 100644 --- a/src/utils/tx.h +++ b/src/utils/tx.h @@ -166,7 +166,7 @@ class TxBlock { */ evmc_message txToMessage() const; - EncodedMessageVariant toMessage(messages::Gas& gas) const; + EncodedMessageVariant toMessage(Gas& gas) const; /// Copy assignment operator. TxBlock& operator=(const TxBlock& other) { diff --git a/tests/contract/messages/evmcontractexecutor.cpp b/tests/contract/messages/evmcontractexecutor.cpp index ff988318..9e678ab9 100644 --- a/tests/contract/messages/evmcontractexecutor.cpp +++ b/tests/contract/messages/evmcontractexecutor.cpp @@ -77,7 +77,7 @@ TEST_CASE("Evm Message Executor Tests", "[evmcontractexecutor]") { // env.accounts.emplace(ORIGIN_ADDRESS, Account(1000000, 0)); - // messages::Gas gas(1000000); + // Gas gas(1000000); // uint256_t value = 0; // const Bytes getCountInput = Utils::makeBytes(bytes::hex("0x06661abd")); // const Bytes getSquaredInput = Utils::makeBytes(bytes::hex("0x11a1d3e80000000000000000000000000000000000000000000000000000000000000019")); @@ -118,7 +118,7 @@ TEST_CASE("Evm Message Executor Tests", "[evmcontractexecutor]") { // const Bytes casinoBytecode = Utils::makeBytes(bytes::hex("0x608060405260016000806101000a81548160ff02191690831515021790555034801561002a57600080fd5b506103ad8061003a6000396000f3fe6080604052600436106100555760003560e01c806311610c251461005a5780632ad957861461006457806343d726d61461008d57806347535d7b146100a4578063b69ef8a8146100cf578063fcfff16f146100fa575b600080fd5b610062610111565b005b34801561007057600080fd5b5061008b60048036038101906100869190610263565b610160565b005b34801561009957600080fd5b506100a26101aa565b005b3480156100b057600080fd5b506100b96101c6565b6040516100c691906102ab565b60405180910390f35b3480156100db57600080fd5b506100e46101dc565b6040516100f191906102df565b60405180910390f35b34801561010657600080fd5b5061010f6101e4565b005b60008054906101000a900460ff1661015e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161015590610357565b60405180910390fd5b565b8073ffffffffffffffffffffffffffffffffffffffff166108fc479081150290604051600060405180830381858888f193505050501580156101a6573d6000803e3d6000fd5b5050565b60008060006101000a81548160ff021916908315150217905550565b60008060009054906101000a900460ff16905090565b600047905090565b60016000806101000a81548160ff021916908315150217905550565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061023082610205565b9050919050565b61024081610225565b811461024b57600080fd5b50565b60008135905061025d81610237565b92915050565b60006020828403121561027957610278610200565b5b60006102878482850161024e565b91505092915050565b60008115159050919050565b6102a581610290565b82525050565b60006020820190506102c0600083018461029c565b92915050565b6000819050919050565b6102d9816102c6565b82525050565b60006020820190506102f460008301846102d0565b92915050565b600082825260208201905092915050565b7f6265747320636c6f736564000000000000000000000000000000000000000000600082015250565b6000610341600b836102fa565b915061034c8261030b565b602082019050919050565b6000602082019050818103600083015261037081610334565b905091905056fea26469706673582212207757c95d521b5892342b02350317f2c90bcb46f0e30b16a13de45e34c659e21764736f6c63430008130033")); // const Bytes gamblerBytecode = Utils::makeBytes(bytes::hex("0x608060405234801561001057600080fd5b50610292806100206000396000f3fe6080604052600436106100295760003560e01c80632c5433051461002e578063b69ef8a81461004a575b600080fd5b610048600480360381019061004391906101e8565b610075565b005b34801561005657600080fd5b5061005f61017d565b60405161006c9190610241565b60405180910390f35b600082905060008290508173ffffffffffffffffffffffffffffffffffffffff166311610c25346040518263ffffffff1660e01b81526004016000604051808303818588803b1580156100c757600080fd5b505af1935050505080156100d9575060015b610176573d8060008114610109576040519150601f19603f3d011682016040523d82523d6000602084013e61010e565b606091505b508173ffffffffffffffffffffffffffffffffffffffff1663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b15801561015757600080fd5b505af115801561016b573d6000803e3d6000fd5b505050505050610177565b5b50505050565b600047905090565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101b58261018a565b9050919050565b6101c5816101aa565b81146101d057600080fd5b50565b6000813590506101e2816101bc565b92915050565b600080604083850312156101ff576101fe610185565b5b600061020d858286016101d3565b925050602061021e858286016101d3565b9150509250929050565b6000819050919050565b61023b81610228565b82525050565b60006020820190506102566000830184610232565b9291505056fea2646970667358221220ce419e18421bafa55bf72c1e46652f2e313a338b574ece304a4044cb4cc9c05164736f6c63430008130033")); - // messages::Gas gas(1'000'000); + // Gas gas(1'000'000); // uint256_t value = 0; // Bytes input; @@ -152,7 +152,7 @@ TEST_CASE("Evm Message Executor Tests", "[evmcontractexecutor]") { // // TODO: the code field from the create message also has the constructor arguments // MockedEnvironment env; - // messages::Gas gas(1'000'000); + // Gas gas(1'000'000); // uint256_t value = 0; // const Address accountAddress = bytes::hex("0x05ed5b0cec75408bb57ab2a5413b9d6b0d6c756f"); diff --git a/tests/core/state.cpp b/tests/core/state.cpp index 3067f2a7..fb5f5763 100644 --- a/tests/core/state.cpp +++ b/tests/core/state.cpp @@ -1507,7 +1507,7 @@ namespace TState { const Address from{}; - messages::Gas gas(10000000); + Gas gas(10'000'000); const Bytes data = buildMessageData(getBalanceMeFunctor, getBalanceMeEncoder); EncodedStaticCallMessage msg(from, ERC20ContractAddress, gas, data); diff --git a/tests/sdktestsuite.hpp b/tests/sdktestsuite.hpp index c302c5ee..d491b00f 100644 --- a/tests/sdktestsuite.hpp +++ b/tests/sdktestsuite.hpp @@ -319,9 +319,9 @@ class SDKTestSuite { const TestAccount& from, const Address& to, const uint256_t& value, Bytes data = Bytes() ) { - messages::Gas gas(1000000000); + Gas gas(1'000'000'000); - const uint64_t gasUsed = 10000 + std::invoke([&] () { + const uint64_t gasUsed = 10'000 + std::invoke([&] () { if (to) { return this->state_.estimateGas(EncodedCallMessage(from.address, to, gas, value, data)); } else { @@ -750,7 +750,7 @@ class SDKTestSuite { Bytes fullData; Utils::appendBytes(fullData, Utils::uint32ToBytes(functor.value)); - messages::Gas gas(10000000); + Gas gas(10'000'000); const Address from = this->getChainOwnerAccount().address; EncodedStaticCallMessage msg(from, contractAddress, gas, fullData); @@ -779,7 +779,7 @@ class SDKTestSuite { Utils::appendBytes(fullData, Utils::uint32ToBytes(functor.value)); Utils::appendBytes(fullData, ABI::Encoder::encodeData(std::forward(args)...)); - messages::Gas gas(10000000); + Gas gas(10'000'000); const Address from = this->getChainOwnerAccount().address; EncodedStaticCallMessage msg(from, contractAddress, gas, fullData); From f823be8d0d60c8700670dbcb1a8a7aad19433c26 Mon Sep 17 00:00:00 2001 From: Leonardo Amaral Date: Thu, 9 Jan 2025 11:52:05 -0300 Subject: [PATCH 19/37] native wrapper tests fixed --- tests/contract/nativewrapper.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/tests/contract/nativewrapper.cpp b/tests/contract/nativewrapper.cpp index d3f7ab33..c6c4cc54 100644 --- a/tests/contract/nativewrapper.cpp +++ b/tests/contract/nativewrapper.cpp @@ -36,22 +36,31 @@ namespace TNativeWrapper { SECTION("NativeWrapper deposit() and withdraw()") { SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testNativeWrapperDepositAndWithdraw"); + + const uint256_t gasPrice = 1'000'000'000; + Address nativeWrapper = sdk.deployContract( std::string("WrappedToken"), std::string("WTKN"), uint8_t(18) ); Address owner = sdk.getChainOwnerAccount().address; uint256_t amountToTransfer = uint256_t("192838158112259"); uint256_t amountToWithdraw = amountToTransfer / 3; + Hash depositTx = sdk.callFunction(nativeWrapper, &NativeWrapper::deposit, amountToTransfer); + REQUIRE(sdk.getNativeBalance(nativeWrapper) == amountToTransfer); - // SDKTestSuite::createNewTx() is adding a tx fee of 21000 gwei, thus * 2 (or * 3 in the test below) - REQUIRE(sdk.getNativeBalance(owner) == uint256_t("1000000000000000000000") - amountToTransfer - (uint256_t(1000000000) * 21000 * 2)); + + uint256_t expectedGasUsed = gasPrice * (uint256_t(CONTRACT_EXECUTION_COST) * 2 + CPP_CONTRACT_CREATION_COST + CPP_CONTRACT_CALL_COST); + + REQUIRE(sdk.getNativeBalance(owner) == uint256_t("1000000000000000000000") - amountToTransfer - expectedGasUsed); REQUIRE(sdk.callViewFunction(nativeWrapper, &NativeWrapper::balanceOf, owner) == amountToTransfer); + Hash withdrawTx = sdk.callFunction(nativeWrapper, &NativeWrapper::withdraw, amountToWithdraw); + expectedGasUsed += gasPrice * (CONTRACT_EXECUTION_COST + CPP_CONTRACT_CALL_COST); + REQUIRE(sdk.getNativeBalance(nativeWrapper) == amountToTransfer - amountToWithdraw); - REQUIRE(sdk.getNativeBalance(owner) == uint256_t("1000000000000000000000") - amountToTransfer + amountToWithdraw - (uint256_t(1000000000) * 21000 * 3)); + REQUIRE(sdk.getNativeBalance(owner) == uint256_t("1000000000000000000000") - amountToTransfer + amountToWithdraw - expectedGasUsed); REQUIRE(sdk.callViewFunction(nativeWrapper, &NativeWrapper::balanceOf, owner) == amountToTransfer - amountToWithdraw); } } } - From 79b4f6ba4c527ee8a0882c1ade74e4273cce0638 Mon Sep 17 00:00:00 2001 From: Leonardo Amaral Date: Mon, 13 Jan 2025 12:38:33 -0300 Subject: [PATCH 20/37] fixed accounts behavior when not previously populated, and added proper support for payment (i.e. non-invocation) transactions --- src/contract/calltracer.h | 1 - src/contract/contract.cpp | 4 +- src/contract/contractmanager.cpp | 2 +- src/contract/dynamiccontract.h | 2 +- src/contract/messages/cppcontractexecutor.h | 5 +- src/contract/messages/evmcontractexecutor.cpp | 31 +++---- src/contract/messages/executioncontext.cpp | 92 +++++++++++-------- src/contract/messages/executioncontext.h | 35 ++++++- src/contract/messages/messagedispatcher.h | 21 +++-- src/core/state.cpp | 2 +- src/utils/utils.h | 8 -- tests/contract/messages/executioncontext.cpp | 63 +++++++------ tests/net/http/httpjsonrpc.cpp | 3 +- 13 files changed, 159 insertions(+), 110 deletions(-) diff --git a/src/contract/calltracer.h b/src/contract/calltracer.h index bab50a4f..b732c5a0 100644 --- a/src/contract/calltracer.h +++ b/src/contract/calltracer.h @@ -17,7 +17,6 @@ class CallTracer { decltype(auto) onMessage(Message&& msg) { using Result = traits::MessageResult; - trace::Call& callTrace = callStack_.empty() ? *(rootCall_ = std::make_unique()) : callStack_.top()->calls.emplace_back(); diff --git a/src/contract/contract.cpp b/src/contract/contract.cpp index 25f9484e..3a9bc66a 100644 --- a/src/contract/contract.cpp +++ b/src/contract/contract.cpp @@ -24,5 +24,5 @@ uint64_t BaseContract::getNonce(const Address& address) const { if (this->host_ == nullptr) { throw DynamicException("Contracts going haywire! trying to get nonce without a host!"); } - return this->host_->context().getAccount(address).nonce; -} \ No newline at end of file + return this->host_->context().getAccount(address).getNonce(); +} diff --git a/src/contract/contractmanager.cpp b/src/contract/contractmanager.cpp index e1ebc7dc..8fc5c6b4 100644 --- a/src/contract/contractmanager.cpp +++ b/src/contract/contractmanager.cpp @@ -80,7 +80,7 @@ void ContractManager::ethCall(const evmc_message& callInfo, ContractHost* host) throw DynamicException("ContractManager: Invalid function call"); } it->second(callInfo, - generateContractAddress(this->host_->context().getAccount(caller).nonce, caller), + generateContractAddress(this->host_->context().getAccount(caller).getNonce(), caller), this->contracts_, this->getContractChainId(), this->host_); diff --git a/src/contract/dynamiccontract.h b/src/contract/dynamiccontract.h index 2595526f..d2811cef 100644 --- a/src/contract/dynamiccontract.h +++ b/src/contract/dynamiccontract.h @@ -641,7 +641,7 @@ class DynamicContract : public BaseContract { if (this->host_ == nullptr) { throw DynamicException("Contracts going haywire! trying to get balance without a host!"); } - return host_->context().getAccount(address).balance; + return host_->context().getAccount(address).getBalance(); } /** diff --git a/src/contract/messages/cppcontractexecutor.h b/src/contract/messages/cppcontractexecutor.h index 60c16a6b..0c7382c9 100644 --- a/src/contract/messages/cppcontractexecutor.h +++ b/src/contract/messages/cppcontractexecutor.h @@ -142,9 +142,10 @@ class CppContractExecutor { contract.caller_ = caller; contract.value_ = value; // TODO: value 0 ALWAYS? - const Address contractAddress = generateContractAddress(context_.getAccount(msg.from()).nonce, msg.from()); + auto account = context_.getAccount(msg.from()); + const Address contractAddress = generateContractAddress(account.getNonce(), msg.from()); contract.ethCall(evmcMsg, &host_); - context_.incrementNonce(msg.from()); + account.setNonce(account.getNonce() + 1); return contractAddress; } diff --git a/src/contract/messages/evmcontractexecutor.cpp b/src/contract/messages/evmcontractexecutor.cpp index 768580cb..b8b7b3cc 100644 --- a/src/contract/messages/evmcontractexecutor.cpp +++ b/src/contract/messages/evmcontractexecutor.cpp @@ -110,13 +110,11 @@ static void createContractImpl(auto& msg, ExecutionContext& context, Viewvm_, &this->get_interface(), this->to_context(), - makeEvmcMessage(msg, depth_), msg.gas(), context_.getAccount(msg.to()).code); + makeEvmcMessage(msg, depth_), msg.gas(), context_.getAccount(msg.to()).getCode()); return output; } Bytes EvmContractExecutor::execute(EncodedStaticCallMessage& msg) { msg.gas().use(EVM_CONTRACT_CALL_COST); - View code = context_.getAccount(msg.to()).code; + View code = context_.getAccount(msg.to()).getCode(); auto depthGuard = transactional::copy(depth_); ++depth_; @@ -148,7 +146,7 @@ Bytes EvmContractExecutor::execute(EncodedDelegateCallMessage& msg) { ++depth_; const Bytes output = executeEvmcMessage(this->vm_, &this->get_interface(), this->to_context(), - makeEvmcMessage(msg, depth_), msg.gas(), context_.getAccount(msg.codeAddress()).code); + makeEvmcMessage(msg, depth_), msg.gas(), context_.getAccount(msg.codeAddress()).getCode()); return output; } @@ -156,9 +154,10 @@ Bytes EvmContractExecutor::execute(EncodedDelegateCallMessage& msg) { Address EvmContractExecutor::execute(EncodedCreateMessage& msg) { msg.gas().use(EVM_CONTRACT_CREATION_COST); auto depthGuard = transactional::copy(depth_); - const Address contractAddress = generateContractAddress(context_.getAccount(msg.from()).nonce, msg.from()); + auto account = context_.getAccount(msg.from()); + const Address contractAddress = generateContractAddress(account.getNonce(), msg.from()); createContractImpl(msg, context_, contractAddress, vm_, *this, ++depth_); - context_.incrementNonce(msg.from()); + account.setNonce(account.getNonce() + 1); return contractAddress; } @@ -185,7 +184,7 @@ evmc_storage_status EvmContractExecutor::set_storage(const evmc::address& addr, evmc::uint256be EvmContractExecutor::get_balance(const evmc::address& addr) const noexcept { try { - return Utils::uint256ToEvmcUint256(context_.getAccount(addr).balance); + return Utils::uint256ToEvmcUint256(context_.getAccount(addr).getBalance()); } catch (const std::exception&) { return evmc::uint256be{}; } @@ -193,7 +192,7 @@ evmc::uint256be EvmContractExecutor::get_balance(const evmc::address& addr) cons size_t EvmContractExecutor::get_code_size(const evmc::address& addr) const noexcept { try { - return context_.getAccount(addr).code.size(); + return context_.getAccount(addr).getCode().size(); } catch (const std::exception&) { return 0; } @@ -201,7 +200,7 @@ size_t EvmContractExecutor::get_code_size(const evmc::address& addr) const noexc evmc::bytes32 EvmContractExecutor::get_code_hash(const evmc::address& addr) const noexcept { try { - return bytes::cast(context_.getAccount(addr).codeHash); + return bytes::cast(context_.getAccount(addr).getCodeHash()); } catch (const std::exception&) { return evmc::bytes32{}; } @@ -210,7 +209,7 @@ evmc::bytes32 EvmContractExecutor::get_code_hash(const evmc::address& addr) cons size_t EvmContractExecutor::copy_code(const evmc::address& addr, size_t code_offset, uint8_t* buffer_data, size_t buffer_size) const noexcept { try { - View code = context_.getAccount(addr).code; + View code = context_.getAccount(addr).getCode(); if (code_offset < code.size()) { const auto n = std::min(buffer_size, code.size() - code_offset); diff --git a/src/contract/messages/executioncontext.cpp b/src/contract/messages/executioncontext.cpp index 74e197c1..9a4e21e5 100644 --- a/src/contract/messages/executioncontext.cpp +++ b/src/contract/messages/executioncontext.cpp @@ -10,14 +10,8 @@ void ExecutionContext::addEvent(View
address, View data, std::ve } -const Account& ExecutionContext::getAccount(View
accountAddress) const { - const auto iterator = accounts_.find(accountAddress); - - if (iterator == accounts_.end()) { - throw DynamicException("account not found"); // TODO: dynamic exception - } - - return *iterator->second; +ExecutionContext::AccountPointer ExecutionContext::getAccount(View
accountAddress) { + return ExecutionContext::AccountPointer(*accounts_[accountAddress], transactions_); } BaseContract& ExecutionContext::getContract(View
contractAddress) { @@ -62,16 +56,6 @@ bool ExecutionContext::accountExists(View
accountAddress) const { return accounts_.contains(accountAddress); } -void ExecutionContext::addAccount(View
address, Account account) { - auto [transaction, inserted] = transactional::emplace(accounts_, address, std::move(account)); - - if (!inserted) { - throw DynamicException("account already exist"); // TODO: dynamic exception - } - - transactions_.push(transactional::AnyTransactional(std::move(transaction))); -} - void ExecutionContext::addContract(View
address, std::unique_ptr contract) { if (contract == nullptr) { throw DynamicException("attempt to insert null contract"); @@ -83,7 +67,10 @@ void ExecutionContext::addContract(View
address, std::unique_ptrfirst, iterator->second.get()); } @@ -102,27 +89,15 @@ void ExecutionContext::notifyNewContract(View
address, BaseContract* co } void ExecutionContext::transferBalance(View
fromAddress, View
toAddress, const uint256_t& amount) { - Account& sender = getMutableAccount(fromAddress); - Account& recipient = getMutableAccount(toAddress); + auto sender = getAccount(fromAddress); + auto recipient = getAccount(toAddress); - if (sender.balance < amount) { + if (sender.getBalance() < amount) { throw DynamicException("insufficient founds"); } - transactional::AnyTransactional senderTransaction(transactional::copy(sender.balance)); - transactional::AnyTransactional recipientTransaction(transactional::copy(recipient.balance)); - - sender.balance -= amount; - recipient.balance += amount; - - transactions_.push(std::move(senderTransaction)); - transactions_.push(std::move(recipientTransaction)); -} - -void ExecutionContext::incrementNonce(View
accountAddress) { - Account& account = getMutableAccount(accountAddress); - transactions_.push(transactional::AnyTransactional(transactional::copy(account.nonce))); - ++account.nonce; + sender.setBalance(sender.getBalance() - amount); + recipient.setBalance(recipient.getBalance() + amount); } void ExecutionContext::store(View
addr, View slot, View data) { @@ -158,6 +133,51 @@ ExecutionContext::Checkpoint ExecutionContext::checkpoint() { return ExecutionContext::Checkpoint(transactions_); } +ExecutionContext::AccountPointer::AccountPointer(Account& account, std::stack& transactions) + : account_(account), transactions_(transactions) {} + +const uint256_t& ExecutionContext::AccountPointer::getBalance() const{ + return account_.balance; +} + +uint64_t ExecutionContext::AccountPointer::getNonce() const { + return account_.nonce; +} + +View ExecutionContext::AccountPointer::getCodeHash() const { + return account_.codeHash; +} + +View ExecutionContext::AccountPointer::getCode() const { + return account_.code; +} + +ContractType ExecutionContext::AccountPointer::getContractType() const { + return account_.contractType; +} + +void ExecutionContext::AccountPointer::setBalance(const uint256_t& amount) { + transactions_.push(transactional::AnyTransactional(transactional::copy(account_.balance))); + account_.balance = amount; +} + +void ExecutionContext::AccountPointer::setNonce(uint64_t nonce) { + transactions_.push(transactional::AnyTransactional(transactional::copy(account_.nonce))); + account_.nonce = nonce; +} + +void ExecutionContext::AccountPointer::setCode(Bytes code) { + transactions_.push(transactional::AnyTransactional(transactional::copy(account_.codeHash))); + transactions_.push(transactional::AnyTransactional(transactional::copy(account_.code))); + account_.codeHash = Utils::sha3(code); + account_.code = std::move(code); +} + +void ExecutionContext::AccountPointer::setContractType(ContractType type) { + transactions_.push(transactional::AnyTransactional(transactional::copy(account_.contractType))); + account_.contractType = type; +} + ExecutionContext::Checkpoint::Checkpoint(std::stack& transactions) : transactions_(&transactions), checkpoint_(transactions.size()) {} diff --git a/src/contract/messages/executioncontext.h b/src/contract/messages/executioncontext.h index 410ba10c..6b3b94d6 100644 --- a/src/contract/messages/executioncontext.h +++ b/src/contract/messages/executioncontext.h @@ -20,6 +20,8 @@ class ExecutionContext { class Builder; + class AccountPointer; + ExecutionContext( Accounts& accounts, Storage& storage, Contracts& contracts, int64_t blockGasLimit, int64_t blockNumber, int64_t blockTimestamp, int64_t txIndex, @@ -73,7 +75,7 @@ class ExecutionContext { bool accountExists(View
accountAddress) const; - const Account& getAccount(View
accountAddress) const; + AccountPointer getAccount(View
accountAddress); BaseContract& getContract(View
contractAddress); @@ -83,16 +85,12 @@ class ExecutionContext { const auto& getNewContracts() const { return newContracts_; } - void addAccount(View
address, Account account); - void addContract(View
address, std::unique_ptr contract); void notifyNewContract(View
address, BaseContract* contract); void transferBalance(View
fromAddress, View
toAddress, const uint256_t& amount); - void incrementNonce(View
accountAddress); - void store(View
addr, View slot, View data); Hash retrieve(View
addr, View slot) const; @@ -125,6 +123,33 @@ class ExecutionContext { std::stack transactions_; }; +class ExecutionContext::AccountPointer { +public: + AccountPointer(Account& account, std::stack& transactions); + + const uint256_t& getBalance() const; + + uint64_t getNonce() const; + + View getCodeHash() const; + + View getCode() const; + + ContractType getContractType() const; + + void setBalance(const uint256_t& amount); + + void setNonce(uint64_t nonce); + + void setCode(Bytes code); + + void setContractType(ContractType type); + +private: + Account& account_; + std::stack& transactions_; +}; + class ExecutionContext::Checkpoint { public: explicit Checkpoint(std::stack& transactions); diff --git a/src/contract/messages/messagedispatcher.h b/src/contract/messages/messagedispatcher.h index 7ab38463..ac6cdcdf 100644 --- a/src/contract/messages/messagedispatcher.h +++ b/src/contract/messages/messagedispatcher.h @@ -25,11 +25,7 @@ class MessageDispatcher { return precompiledExecutor_.execute(std::forward(msg)); } - const Account& account = context_.getAccount(codeAddress); - - if (!account.isContract()) { - throw DynamicException("Not a contract address"); - } + auto account = context_.getAccount(codeAddress); auto checkpoint = context_.checkpoint(); @@ -39,8 +35,14 @@ class MessageDispatcher { } } + if constexpr (concepts::EncodedMessage) { + if (msg.input().size() == 0) { + return Bytes(); + } + } + // TODO: to much code repetition, you can do better than this. - if (account.contractType == ContractType::CPP) { + if (account.getContractType() == ContractType::CPP) { if constexpr (std::same_as) { cppExecutor_.execute(std::forward(msg)); checkpoint.commit(); @@ -50,7 +52,7 @@ class MessageDispatcher { checkpoint.commit(); return result; } - } else { + } else if (account.getContractType() == ContractType::EVM) { if constexpr (std::same_as) { evmExecutor_.execute(std::forward(msg)); checkpoint.commit(); @@ -60,11 +62,15 @@ class MessageDispatcher { checkpoint.commit(); return result; } + } else { + throw DynamicException("Attempt to invoke account that is not a contract"); } } template Address onMessage(M&& msg) { + auto checkpoint = context_.checkpoint(); + const Address result = std::invoke([&] () { if constexpr (concepts::EncodedMessage) { return evmExecutor_.execute(std::forward(msg)); @@ -73,6 +79,7 @@ class MessageDispatcher { } }); + checkpoint.commit(); return result; } diff --git a/src/core/state.cpp b/src/core/state.cpp index 430e9c76..730f5033 100644 --- a/src/core/state.cpp +++ b/src/core/state.cpp @@ -501,7 +501,7 @@ int64_t State::estimateGas(EncodedMessageVariant msg) { return std::visit([&host] (auto&& msg) { const Gas& gas = msg.gas(); const int64_t initialGas(gas); - host.simulate(std::forward(msg)); + host.simulate(std::forward(msg)); return initialGas - int64_t(gas); }, std::move(msg)); } diff --git a/src/utils/utils.h b/src/utils/utils.h index dfc0d4a3..b8bb5657 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -267,14 +267,6 @@ struct Account { Bytes code = Bytes(); ///< Account code (if any) ContractType contractType = ContractType::NOT_A_CONTRACT; ///< Account contract type. - static Account makeCppContract(const uint256_t& balance = 0) { - Account account; - account.balance = balance; - account.nonce = 1; - account.contractType = ContractType::CPP; - return account; - } - /// Default constructor. Account() = default; diff --git a/tests/contract/messages/executioncontext.cpp b/tests/contract/messages/executioncontext.cpp index 6f6f21ed..34725b51 100644 --- a/tests/contract/messages/executioncontext.cpp +++ b/tests/contract/messages/executioncontext.cpp @@ -9,6 +9,14 @@ See the LICENSE.txt file in the project root for more information. #include "bytes/hex.h" #include "contract/messages/executioncontext.h" +static inline void addAccount(ExecutionContext& context, View
address, const Account& account) { + auto pointer = context.getAccount(address); + pointer.setBalance(account.balance); + pointer.setNonce(account.nonce); + pointer.setCode(account.code); + pointer.setContractType(account.contractType); +} + TEST_CASE("Execution Context Test Cases", "[executioncontext]") { SECTION("Building correctly") { ExecutionContext::Accounts accounts; @@ -65,9 +73,8 @@ TEST_CASE("Execution Context Test Cases", "[executioncontext]") { REQUIRE(context.retrieve(accountAddress1, slot1) == data); REQUIRE(context.retrieve(accountAddress1, slot2) == Hash()); REQUIRE(context.retrieve(accountAddress2, slot1) == Hash()); - REQUIRE(context.getAccount(accountAddress1).balance == 1000); - REQUIRE(context.getAccount(accountAddress2).balance == 666); - REQUIRE_THROWS(context.getAccount(blockCoinbase)); + REQUIRE(context.getAccount(accountAddress1).getBalance() == 1000); + REQUIRE(context.getAccount(accountAddress2).getBalance() == 666); } SECTION("Checkpoint revert to accounts") { @@ -100,51 +107,51 @@ TEST_CASE("Execution Context Test Cases", "[executioncontext]") { .accounts(accounts) .build(); - context.addAccount(addresses[1], accountsArr[1]); - context.addAccount(addresses[2], accountsArr[2]); + addAccount(context, addresses[1], accountsArr[1]); + addAccount(context, addresses[2], accountsArr[2]); - REQUIRE(context.getAccount(addresses[0]).balance == accountsArr[0].balance); - REQUIRE(context.getAccount(addresses[1]).balance == accountsArr[1].balance); - REQUIRE(context.getAccount(addresses[2]).balance == accountsArr[2].balance); + REQUIRE(context.getAccount(addresses[0]).getBalance() == accountsArr[0].balance); + REQUIRE(context.getAccount(addresses[1]).getBalance() == accountsArr[1].balance); + REQUIRE(context.getAccount(addresses[2]).getBalance() == accountsArr[2].balance); auto checkpoint = context.checkpoint(); - context.addAccount(addresses[3], accountsArr[3]); - context.addAccount(addresses[4], accountsArr[4]); + addAccount(context, addresses[3], accountsArr[3]); + addAccount(context, addresses[4], accountsArr[4]); - REQUIRE(context.getAccount(addresses[0]).balance == accountsArr[0].balance); - REQUIRE(context.getAccount(addresses[1]).balance == accountsArr[1].balance); - REQUIRE(context.getAccount(addresses[2]).balance == accountsArr[2].balance); - REQUIRE(context.getAccount(addresses[3]).balance == accountsArr[3].balance); - REQUIRE(context.getAccount(addresses[4]).balance == accountsArr[4].balance); + REQUIRE(context.getAccount(addresses[0]).getBalance() == accountsArr[0].balance); + REQUIRE(context.getAccount(addresses[1]).getBalance() == accountsArr[1].balance); + REQUIRE(context.getAccount(addresses[2]).getBalance() == accountsArr[2].balance); + REQUIRE(context.getAccount(addresses[3]).getBalance() == accountsArr[3].balance); + REQUIRE(context.getAccount(addresses[4]).getBalance() == accountsArr[4].balance); context.transferBalance(addresses[0], addresses[1], 100); - REQUIRE(context.getAccount(addresses[0]).balance == accountsArr[0].balance - 100); - REQUIRE(context.getAccount(addresses[1]).balance == accountsArr[1].balance + 100); + REQUIRE(context.getAccount(addresses[0]).getBalance() == accountsArr[0].balance - 100); + REQUIRE(context.getAccount(addresses[1]).getBalance() == accountsArr[1].balance + 100); REQUIRE_THROWS(context.transferBalance(addresses[0], addresses[1], 101)); checkpoint.revert(); - REQUIRE(context.getAccount(addresses[0]).balance == accountsArr[0].balance); - REQUIRE(context.getAccount(addresses[1]).balance == accountsArr[1].balance); - REQUIRE(context.getAccount(addresses[2]).balance == accountsArr[2].balance); - REQUIRE_THROWS(context.getAccount(addresses[3])); - REQUIRE_THROWS(context.getAccount(addresses[4])); + REQUIRE(context.getAccount(addresses[0]).getBalance() == accountsArr[0].balance); + REQUIRE(context.getAccount(addresses[1]).getBalance() == accountsArr[1].balance); + REQUIRE(context.getAccount(addresses[2]).getBalance() == accountsArr[2].balance); + REQUIRE(context.getAccount(addresses[3]).getBalance() == uint256_t(0)); + REQUIRE(context.getAccount(addresses[4]).getBalance() == uint256_t(0)); { auto checkpoint2 = context.checkpoint(); - context.addAccount(addresses[5], accountsArr[5]); - context.addAccount(addresses[6], accountsArr[6]); + addAccount(context, addresses[5], accountsArr[5]); + addAccount(context, addresses[6], accountsArr[6]); context.transferBalance(addresses[1], addresses[2], 50); checkpoint2.commit(); } - REQUIRE(context.getAccount(addresses[1]).balance == accountsArr[1].balance - 50); - REQUIRE(context.getAccount(addresses[2]).balance == accountsArr[2].balance + 50); - REQUIRE(context.getAccount(addresses[5]).balance == accountsArr[5].balance); - REQUIRE(context.getAccount(addresses[6]).balance == accountsArr[6].balance); + REQUIRE(context.getAccount(addresses[1]).getBalance() == accountsArr[1].balance - 50); + REQUIRE(context.getAccount(addresses[2]).getBalance() == accountsArr[2].balance + 50); + REQUIRE(context.getAccount(addresses[5]).getBalance() == accountsArr[5].balance); + REQUIRE(context.getAccount(addresses[6]).getBalance() == accountsArr[6].balance); } SECTION("Nested: revert, revert, revert") { diff --git a/tests/net/http/httpjsonrpc.cpp b/tests/net/http/httpjsonrpc.cpp index d106868d..2b0e2244 100644 --- a/tests/net/http/httpjsonrpc.cpp +++ b/tests/net/http/httpjsonrpc.cpp @@ -282,8 +282,7 @@ namespace THTTPJsonRPC{ {"to", "0xaaA85B2B2bD0bFdF6Bc5D0d61B6192c53818567b"}, {"gas", "0xffffff"}, {"gasPrice", "0x1"}, - {"value", "0x1"}, - {"data", "0x1"}, + {"value", "0x1"} }), "latest"})); REQUIRE(eth_estimateGasResponse["result"] == "0x5208"); From 155fed94072911132b60b5179968a15c17558f95 Mon Sep 17 00:00:00 2001 From: Leonardo Amaral Date: Mon, 13 Jan 2025 15:41:41 -0300 Subject: [PATCH 21/37] added missing checkpoint commit --- src/contract/messages/messagedispatcher.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/contract/messages/messagedispatcher.h b/src/contract/messages/messagedispatcher.h index ac6cdcdf..d491fddd 100644 --- a/src/contract/messages/messagedispatcher.h +++ b/src/contract/messages/messagedispatcher.h @@ -37,6 +37,7 @@ class MessageDispatcher { if constexpr (concepts::EncodedMessage) { if (msg.input().size() == 0) { + checkpoint.commit(); return Bytes(); } } From cac0ba877ba27d152edcef03906d4725ac57321b Mon Sep 17 00:00:00 2001 From: Leonardo Amaral Date: Mon, 13 Jan 2025 16:54:05 -0300 Subject: [PATCH 22/37] message dispatcher reorganized --- src/contract/messages/messagedispatcher.h | 108 +++++++++++----------- 1 file changed, 55 insertions(+), 53 deletions(-) diff --git a/src/contract/messages/messagedispatcher.h b/src/contract/messages/messagedispatcher.h index d491fddd..f515eff9 100644 --- a/src/contract/messages/messagedispatcher.h +++ b/src/contract/messages/messagedispatcher.h @@ -15,73 +15,59 @@ class MessageDispatcher { MessageDispatcher(ExecutionContext& context, CppContractExecutor cppExecutor, EvmContractExecutor evmExecutor, PrecompiledContractExecutor precompiledExecutor) : context_(context), cppExecutor_(std::move(cppExecutor)), evmExecutor_(std::move(evmExecutor)), precompiledExecutor_(std::move(precompiledExecutor)) {} - template - decltype(auto) onMessage(M&& msg) { - using Result = traits::MessageResult; - - View
codeAddress = messageCodeAddress(msg); + template> + R onMessage(M&& msg) { + auto checkpoint = context_.checkpoint(); - if (isPrecompiled(codeAddress)) { - return precompiledExecutor_.execute(std::forward(msg)); + if constexpr (std::same_as) { + dispatchMessage(std::forward(msg)); + checkpoint.commit(); + } else { + R result = dispatchMessage(std::forward(msg)); + checkpoint.commit(); + return result; } + } - auto account = context_.getAccount(codeAddress); + Address dispatchMessage(concepts::CreateMessage auto&& msg) requires concepts::EncodedMessage { + return evmExecutor_.execute(std::forward(msg)); + } - auto checkpoint = context_.checkpoint(); + Address dispatchMessage(concepts::CreateMessage auto&& msg) requires concepts::PackedMessage { + return cppExecutor_.execute(std::forward(msg)); + } - if constexpr (concepts::HasValueField) { - if (msg.value() > 0) { - context_.transferBalance(msg.from(), msg.to(), msg.value()); - } - } + decltype(auto) dispatchMessage(concepts::CallMessage auto&& msg) { + transferFunds(msg); - if constexpr (concepts::EncodedMessage) { - if (msg.input().size() == 0) { - checkpoint.commit(); + if constexpr (concepts::EncodedMessage) { + if (isPayment(msg)) { return Bytes(); } } - // TODO: to much code repetition, you can do better than this. - if (account.getContractType() == ContractType::CPP) { - if constexpr (std::same_as) { - cppExecutor_.execute(std::forward(msg)); - checkpoint.commit(); - return; - } else { - decltype(auto) result = cppExecutor_.execute(std::forward(msg)); - checkpoint.commit(); - return result; - } - } else if (account.getContractType() == ContractType::EVM) { - if constexpr (std::same_as) { - evmExecutor_.execute(std::forward(msg)); - checkpoint.commit(); - return; - } else { - decltype(auto) result = evmExecutor_.execute(std::forward(msg)); - checkpoint.commit(); - return result; - } - } else { - throw DynamicException("Attempt to invoke account that is not a contract"); + if (isPrecompiled(msg.to())) { + return precompiledExecutor_.execute(std::forward(msg)); } - } - template - Address onMessage(M&& msg) { - auto checkpoint = context_.checkpoint(); + auto account = context_.getAccount(messageCodeAddress(msg)); - const Address result = std::invoke([&] () { - if constexpr (concepts::EncodedMessage) { - return evmExecutor_.execute(std::forward(msg)); - } else { - return cppExecutor_.execute(std::forward(msg)); - } - }); + switch (account.getContractType()) { + case ContractType::CPP: + return dispatchCall(cppExecutor_, std::forward(msg)); + break; + + case ContractType::EVM: + return dispatchCall(evmExecutor_, std::forward(msg)); + break; - checkpoint.commit(); - return result; + default: + throw DynamicException("Attempt to invoke non-contract or inexistent address"); + } + } + + decltype(auto) dispatchCall(auto& executor, auto&& msg) { + return executor.execute(std::forward(msg)); } CppContractExecutor& cppExecutor() { return cppExecutor_; } @@ -91,6 +77,22 @@ class MessageDispatcher { PrecompiledContractExecutor& precompiledExecutor() { return precompiledExecutor_; } private: + void transferFunds(const concepts::Message auto& msg) { + const uint256_t value = messageValueOrZero(msg); + + if (value > 0) { + context_.transferBalance(msg.from(), msg.to(), value); + } + } + + bool isPayment(const concepts::EncodedMessage auto& msg) { + return msg.input().size() == 0; + } + + bool isPayment(const concepts::PackedMessage auto& msg) { + return false; + } + bool isPrecompiled(View
address) const { constexpr Address randomGeneratorAddress = bytes::hex("0x1000000000000000000000000000100000000001"); return address == randomGeneratorAddress; From ce7054139fb5d29e7953896d20d6861e71e3b3ee Mon Sep 17 00:00:00 2001 From: Leonardo Amaral Date: Tue, 14 Jan 2025 13:13:35 -0300 Subject: [PATCH 23/37] out of gas error fixed --- tests/core/state.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/state.cpp b/tests/core/state.cpp index fb5f5763..c2b8199a 100644 --- a/tests/core/state.cpp +++ b/tests/core/state.cpp @@ -1446,7 +1446,7 @@ namespace TState { 0, 1000000000, 1000000000, - 21000, + 71000, ownerPrivKey ); creationHash = createNewERC2OTx.hash(); From 511371b09f85242cc2d1fe8cb222579d8f6f49db Mon Sep 17 00:00:00 2001 From: Leonardo Amaral Date: Tue, 14 Jan 2025 16:56:56 -0300 Subject: [PATCH 24/37] simplified call tracer --- src/contract/calltracer.h | 9 ++++++++- src/contract/contracthost.cpp | 30 ++++-------------------------- src/contract/contracthost.h | 29 +++++------------------------ 3 files changed, 17 insertions(+), 51 deletions(-) diff --git a/src/contract/calltracer.h b/src/contract/calltracer.h index b732c5a0..c13ecf4d 100644 --- a/src/contract/calltracer.h +++ b/src/contract/calltracer.h @@ -2,6 +2,7 @@ #define CONTRACT_CALLTRACER_H #include "utils/utils.h" +#include "utils/options.h" #include "contract/messages/concepts.h" #include "contract/messages/outofgas.h" #include "contract/trace/call.h" @@ -11,12 +12,17 @@ template class CallTracer { public: - explicit CallTracer(MessageHandler handler) : handler_(std::move(handler)), rootCall_(), callStack_() {} + CallTracer(MessageHandler handler, IndexingMode indexingMode) + : handler_(std::move(handler)), rootCall_(), callStack_(), indexingMode_(indexingMode) {} template decltype(auto) onMessage(Message&& msg) { using Result = traits::MessageResult; + if (indexingMode_ != IndexingMode::RPC_TRACE) { + return handler_.onMessage(std::forward(msg)); + } + trace::Call& callTrace = callStack_.empty() ? *(rootCall_ = std::make_unique()) : callStack_.top()->calls.emplace_back(); @@ -91,6 +97,7 @@ class CallTracer { MessageHandler handler_; std::unique_ptr rootCall_; std::stack callStack_; + IndexingMode indexingMode_; }; #endif // CONTRACT_CALLTRACER_H diff --git a/src/contract/contracthost.cpp b/src/contract/contracthost.cpp index 005d71b5..2f74dc35 100644 --- a/src/contract/contracthost.cpp +++ b/src/contract/contracthost.cpp @@ -1,24 +1,7 @@ #include "contracthost.h" -MessageHandler makeMessageHandler(ContractHost& host, ExecutionContext& context, evmc_vm *vm, Storage& storage, const Hash& randomSeed) { - MessageDispatcher dispatcher(context, CppContractExecutor(context, host), EvmContractExecutor(context, vm), PrecompiledContractExecutor(RandomGen(randomSeed))); - - if (context.getTxHash() && storage.getIndexingMode() == IndexingMode::RPC_TRACE) { - return CallTracer(std::move(dispatcher)); - } - - return dispatcher; -} - uint256_t ContractHost::getRandomValue() { - return std::visit(Utils::Overloaded{ - [] (MessageDispatcher& handler) { - return std::invoke(handler.precompiledExecutor().randomGenerator()); - }, - [] (CallTracer& tracer) { - return std::invoke(tracer.handler().precompiledExecutor().randomGenerator()); - } - }, messageHandler_); + return std::invoke(messageHandler_.handler().precompiledExecutor().randomGenerator()); } ContractHost::~ContractHost() { @@ -47,14 +30,9 @@ ContractHost::~ContractHost() { context_.commit(); } - std::visit(Utils::Overloaded{ - [] (MessageDispatcher& handler) {}, - [this] (CallTracer& tracer) { - if (tracer.hasCallTrace()) { - storage_.putCallTrace(Hash(context_.getTxHash()), tracer.getCallTrace()); // TODO: do not create a hash - } - } - }, messageHandler_); + if (messageHandler_.hasCallTrace()) { + storage_.putCallTrace(Hash(context_.getTxHash()), messageHandler_.getCallTrace()); + } // TODO: save transaction additional data } diff --git a/src/contract/contracthost.h b/src/contract/contracthost.h index 1e3cb0bc..342a359e 100644 --- a/src/contract/contracthost.h +++ b/src/contract/contracthost.h @@ -51,17 +51,6 @@ * Any CPP Contract: 50000 */ -// Address for static BDKD precompile contracts. -using namespace evmc::literals; -const auto ZERO_ADDRESS = 0x0000000000000000000000000000000000000000_address; -const auto BDK_PRECOMPILE = 0x1000000000000000000000000000100000000001_address; - -// std::unique_ptr makeMessageHandler(ContractHost& host, evmc_vm *vm, ExecutionContext& context, Storage& storage); - -using MessageHandler = std::variant>; - -MessageHandler makeMessageHandler(ContractHost& host, ExecutionContext& context, evmc_vm *vm, Storage& storage, const Hash& randomSeed); - class ContractHost { private: DumpManager& manager_; @@ -69,7 +58,7 @@ class ContractHost { mutable ContractStack stack_; bool mustRevert_ = true; // We always assume that we must revert until proven otherwise. ExecutionContext& context_; - MessageHandler messageHandler_; + CallTracer messageHandler_; public: ContractHost(evmc_vm* vm, @@ -81,11 +70,8 @@ class ContractHost { storage_(storage), stack_(), context_(context), - messageHandler_(makeMessageHandler(*this, context, vm, storage, randomnessSeed)) { - std::visit(Utils::Overloaded{ - [] (MessageDispatcher& handler) { handler.evmExecutor().setMessageHandler(AnyEncodedMessageHandler::from(handler)); }, - [] (CallTracer& tracer) { tracer.handler().evmExecutor().setMessageHandler(AnyEncodedMessageHandler::from(tracer)); } - }, messageHandler_); + messageHandler_(MessageDispatcher(context_, CppContractExecutor(context_, *this), EvmContractExecutor(context_, vm), PrecompiledContractExecutor(RandomGen(randomnessSeed))), storage.getIndexingMode()) { + messageHandler_.handler().evmExecutor().setMessageHandler(AnyEncodedMessageHandler::from(messageHandler_)); // TODO: is this really required? } // Rule of five, no copy/move allowed. @@ -233,16 +219,11 @@ class ContractHost { private: decltype(auto) dispatchMessage(auto&& msg) { - return std::visit([&msg] (auto& handler) { - return handler.onMessage(std::forward(msg)); - }, messageHandler_); + return messageHandler_.onMessage(std::forward(msg)); } Gas& getCurrentGas() { - return std::visit(Utils::Overloaded{ - [] (MessageDispatcher& handler) -> Gas& { return handler.cppExecutor().currentGas(); }, - [] (CallTracer& tracer) -> Gas& { return tracer.handler().cppExecutor().currentGas(); } - }, messageHandler_); + return messageHandler_.handler().cppExecutor().currentGas(); } }; From 054306d0ed9147a6f37c1e47ecbe062a07948ff8 Mon Sep 17 00:00:00 2001 From: Leonardo Amaral Date: Tue, 14 Jan 2025 17:40:34 -0300 Subject: [PATCH 25/37] tx additional data added back --- src/core/state.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/core/state.cpp b/src/core/state.cpp index 730f5033..291007a9 100644 --- a/src/core/state.cpp +++ b/src/core/state.cpp @@ -214,6 +214,7 @@ void State::processTransaction( } Gas gas(uint64_t(tx.getGasLimit())); + TxAdditionalData txData{.hash = tx.hash()}; try { const Hash randomSeed(Utils::uint256ToBytes((static_cast(randomnessHash) + txIndex))); @@ -241,17 +242,28 @@ void State::processTransaction( randomSeed, context); - std::visit([&host] (auto&& msg) { - host.execute(std::forward(msg)); + std::visit([&] (auto&& msg) { + if constexpr (concepts::CreateMessage) { + txData.contractAddress = host.execute(std::forward(msg)); + } else { + host.execute(std::forward(msg)); + } }, tx.toMessage(gas)); + txData.succeeded = true; } catch (const std::exception& e) { + txData.succeeded = false; LOGERRORP("Transaction: " + tx.hash().hex().get() + " failed to process, reason: " + e.what()); } ++fromNonce; - auto usedGas = tx.getGasLimit() - uint256_t(gas); - fromBalance -= (usedGas * tx.getMaxFeePerGas()); + txData.gasUsed = uint64_t(tx.getGasLimit() - uint256_t(gas)); + + if (storage_.getIndexingMode() != IndexingMode::DISABLED) { + storage_.putTxAdditionalData(txData); + } + + fromBalance -= (txData.gasUsed * tx.getMaxFeePerGas()); } void State::refreshMempool(const FinalizedBlock& block) { From 8b93a0327f278a7234cbac6e7395eaaa0c0c5f68 Mon Sep 17 00:00:00 2001 From: Leonardo Amaral Date: Wed, 15 Jan 2025 14:54:36 -0300 Subject: [PATCH 26/37] fixing merging errors --- src/bytes/initializer.h | 1 + src/contract/abi.cpp | 4 +- src/contract/calltracer.h | 2 +- src/contract/dynamiccontract.h | 4 +- src/contract/evm/callexecutor.cpp | 14 ++-- src/contract/evm/callexecutor.h | 2 +- src/contract/messages/common.h | 2 +- src/contract/messages/cppcontractexecutor.h | 5 +- src/contract/messages/evmcontractexecutor.cpp | 16 +++-- src/contract/messages/evmcontractexecutor.h | 2 +- .../messages/precompiledcontractexecutor.cpp | 2 +- src/contract/trace/call.cpp | 2 +- src/core/state.cpp | 2 +- src/core/storage.cpp | 2 +- src/net/p2p/encoding.cpp | 8 +-- src/net/p2p/encoding.h | 6 +- src/utils/bytesinterface.h | 2 +- src/utils/dynamicexception.h | 1 + src/utils/evmcconv.cpp | 12 ++-- src/utils/evmcconv.h | 8 +-- src/utils/hash.cpp | 6 +- src/utils/intconv.cpp | 6 +- src/utils/intconv.h | 6 +- src/utils/strconv.cpp | 4 +- src/utils/strconv.h | 4 +- src/utils/uintconv.cpp | 64 +++++++++---------- src/utils/uintconv.h | 64 +++++++++---------- src/utils/utils.cpp | 8 +-- src/utils/utils.h | 4 +- tests/contract/calltracer.cpp | 28 ++------ tests/contract/createcontract.cpp | 2 +- tests/contract/dexv2.cpp | 4 +- tests/contract/erc20.cpp | 4 +- tests/contract/erc721test.cpp | 6 +- tests/contract/event.cpp | 26 ++++---- .../contract/messages/evmcontractexecutor.cpp | 6 +- tests/contract/pebble.cpp | 6 +- tests/core/rdpos.cpp | 2 +- tests/core/state.cpp | 2 +- tests/core/storage.cpp | 19 +++--- tests/net/http/parser.cpp | 13 ++-- tests/net/p2p/nodeinfo.cpp | 8 ++- tests/sdktestsuite.hpp | 12 +++- tests/utils/evmcconv.cpp | 8 +-- tests/utils/safehash.cpp | 13 ++-- tests/utils/strings.cpp | 48 ++------------ tests/utils/utils.cpp | 4 +- 47 files changed, 223 insertions(+), 251 deletions(-) diff --git a/src/bytes/initializer.h b/src/bytes/initializer.h index f3439580..507eed05 100644 --- a/src/bytes/initializer.h +++ b/src/bytes/initializer.h @@ -8,6 +8,7 @@ See the LICENSE.txt file in the project root for more information. #ifndef BYTES_INITIALIZER_H #define BYTES_INITIALIZER_H +#include #include "view.h" // range.h -> ranges -> concepts /// Namespace for bytes-related functionalities. diff --git a/src/contract/abi.cpp b/src/contract/abi.cpp index 9000e632..f8f3cd94 100644 --- a/src/contract/abi.cpp +++ b/src/contract/abi.cpp @@ -52,7 +52,7 @@ Bytes ABI::Encoder::encodeError(std::string_view reason) { std::copy_n(reason.begin(), count, reasonEncoded.begin()); const uint256_t size(reason.size()); - const FixedBytes<32> sizeEncoded(Utils::uint256ToBytes(size)); + const FixedBytes<32> sizeEncoded(UintConv::uint256ToBytes(size)); return Utils::makeBytes(bytes::join( Hex::toBytes("0x08c379a0"), @@ -67,7 +67,7 @@ std::string ABI::Decoder::decodeError(View data) { throw DynamicException("Encoded revert reason is expected to have exactly 100 bytes"); } - const size_t size = Utils::bytesToUint256(data.subspan(36, 32)).convert_to(); + const size_t size = UintConv::bytesToUint256(data.subspan(36, 32)).convert_to(); std::string res; res.reserve(size); diff --git a/src/contract/calltracer.h b/src/contract/calltracer.h index 9ed4e497..38cdaba4 100644 --- a/src/contract/calltracer.h +++ b/src/contract/calltracer.h @@ -40,7 +40,7 @@ class CallTracer { callTrace.status = trace::CallStatus::SUCCEEDED; callTrace.from = Address(msg.from()); callTrace.to = Address(msg.to()); - callTrace.value = FixedBytes<32>(Utils::uint256ToBytes(messageValueOrZero(msg))); + callTrace.value = FixedBytes<32>(UintConv::uint256ToBytes(messageValueOrZero(msg))); callTrace.gas = uint64_t(gas); try { diff --git a/src/contract/dynamiccontract.h b/src/contract/dynamiccontract.h index 70dc398c..680d89c8 100644 --- a/src/contract/dynamiccontract.h +++ b/src/contract/dynamiccontract.h @@ -391,7 +391,7 @@ class DynamicContract : public BaseContract { Bytes evmEthCall(const evmc_message& callInfo, ContractHost* host) final { this->host_ = host; PointerNullifier nullifier(this->host_); - Functor funcName = Utils::getFunctor(callInfo); + Functor funcName = EVMCConv::getFunctor(callInfo); if (this->isPayableFunction(funcName)) { auto func = this->evmFunctions_.find(funcName); if (func == this->evmFunctions_.end()) throw DynamicException("Functor not found for payable function"); @@ -418,7 +418,7 @@ class DynamicContract : public BaseContract { Bytes ethCallView(const evmc_message& data, ContractHost* host) const override { this->host_ = host; PointerNullifier nullifier(this->host_); - Functor funcName = Utils::getFunctor(data); + Functor funcName = EVMCConv::getFunctor(data); auto func = this->viewFunctions_.find(funcName); if (func == this->viewFunctions_.end()) throw DynamicException("Functor not found"); diff --git a/src/contract/evm/callexecutor.cpp b/src/contract/evm/callexecutor.cpp index 2d99fe41..d603e799 100644 --- a/src/contract/evm/callexecutor.cpp +++ b/src/contract/evm/callexecutor.cpp @@ -50,7 +50,7 @@ Bytes CallExecutor::executeCall(kind::Any callKind, Gas& gas, const Message& msg .sender = bytes::cast(msg.from), .input_data = msg.input.data(), .input_size = msg.input.size(), - .value = Utils::uint256ToEvmcUint256(msg.value), + .value = EVMCConv::uint256ToEvmcUint256(msg.value), .create2_salt = {}, .code_address = {} }; @@ -88,7 +88,7 @@ bool CallExecutor::account_exists(const evmc::address& addr) const noexcept { evmc::bytes32 CallExecutor::get_storage(const evmc::address& addr, const evmc::bytes32& key) const noexcept { return get(vmStorage_, StorageKey(addr, key)) - .transform([] (const Hash& hash) { return hash.toEvmcBytes32(); }) + .transform([] (const Hash& hash) { return bytes::cast(hash); }) .value_or(evmc::bytes32{}); } @@ -102,7 +102,7 @@ evmc_storage_status CallExecutor::set_storage(const evmc::address& addr, const e evmc::uint256be CallExecutor::get_balance(const evmc::address& addr) const noexcept { return get(accounts_, addr) - .transform([] (const auto& account) { return Utils::uint256ToEvmcUint256(account.get()->balance); }) + .transform([] (const auto& account) { return EVMCConv::uint256ToEvmcUint256(account.get()->balance); }) .value_or(evmc::uint256be{}); } @@ -114,7 +114,7 @@ size_t CallExecutor::get_code_size(const evmc::address& addr) const noexcept { evmc::bytes32 CallExecutor::get_code_hash(const evmc::address& addr) const noexcept { return get(accounts_, addr) - .transform([] (const auto& account) { return account.get()->codeHash.toEvmcBytes32(); }) + .transform([] (const auto& account) { return bytes::cast(account.get()->codeHash); }) .value_or(evmc::bytes32{}); } @@ -146,7 +146,7 @@ evmc_tx_context CallExecutor::get_tx_context() const noexcept { } evmc::bytes32 CallExecutor::get_block_hash(int64_t number) const noexcept { - return Utils::uint256ToEvmcUint256(number); + return EVMCConv::uint256ToEvmcUint256(number); } void CallExecutor::emit_log(const evmc::address& addr, const uint8_t* data, size_t data_size, const evmc::bytes32 topics[], size_t topics_count) noexcept { @@ -181,7 +181,7 @@ evmc_access_status CallExecutor::access_storage(const evmc::address& addr, const evmc::bytes32 CallExecutor::get_transient_storage(const evmc::address &addr, const evmc::bytes32 &key) const noexcept { return get(transientStorage_, StorageKey(addr, key)) - .transform([] (const Hash& hash) { return hash.toEvmcBytes32(); }) + .transform([] (const Hash& hash) { return bytes::cast(hash); }) .value_or(evmc::bytes32{}); } @@ -212,7 +212,7 @@ evmc::Result CallExecutor::call(const evmc_message& msg) noexcept { case EVMC_DELEGATECALL: callMsg.from = Address(msg.sender); callMsg.to = Address(msg.recipient); - callMsg.value = Utils::evmcUint256ToUint256(msg.value); + callMsg.value = EVMCConv::evmcUint256ToUint256(msg.value); callMsg.depth = msg.depth; callMsg.input = View(msg.input_data, msg.input_size); result = callHandler_.onCall(callKind, gas, callMsg); diff --git a/src/contract/evm/callexecutor.h b/src/contract/evm/callexecutor.h index d56b2b57..04d6dff9 100644 --- a/src/contract/evm/callexecutor.h +++ b/src/contract/evm/callexecutor.h @@ -29,7 +29,7 @@ class CallExecutor : public evmc::Host { throw DynamicException("EVM contract function name is empty (contract not registered?)"); } - Bytes res = Utils::makeBytes(Utils::uint32ToBytes(ABI::FunctorEncoder::encode(functionName).value)); + Bytes res = Utils::makeBytes(UintConv::uint32ToBytes(ABI::FunctorEncoder::encode(functionName).value)); if constexpr (sizeof...(Args) > 0) { Utils::appendBytes(res, ABI::Encoder::encodeData(args...)); diff --git a/src/contract/messages/common.h b/src/contract/messages/common.h index a8b8ba1d..f981b7d7 100644 --- a/src/contract/messages/common.h +++ b/src/contract/messages/common.h @@ -48,7 +48,7 @@ Bytes messageInputEncoded(const concepts::EncodedMessage auto& msg) { Bytes messageInputEncoded(const concepts::PackedMessage auto& msg) { return std::apply([&] (const auto&... args) -> Bytes { const std::string functionName = ContractReflectionInterface::getFunctionName(msg.method()); - const BytesArr<4> encodedFunctor = Utils::uint32ToBytes(ABI::FunctorEncoder::encode(std::string(functionName)).value); + const BytesArr<4> encodedFunctor = UintConv::uint32ToBytes(ABI::FunctorEncoder::encode(std::string(functionName)).value); if constexpr (sizeof...(args) > 0) { const Bytes encodedArgs = ABI::Encoder::encodeData(args...); diff --git a/src/contract/messages/cppcontractexecutor.h b/src/contract/messages/cppcontractexecutor.h index 0c7382c9..c6d75a29 100644 --- a/src/contract/messages/cppcontractexecutor.h +++ b/src/contract/messages/cppcontractexecutor.h @@ -4,6 +4,7 @@ #include "executioncontext.h" #include "traits.h" #include "bytes/cast.h" +#include "utils/evmcconv.h" #include "utils/contractreflectioninterface.h" #include "contract/costs.h" @@ -104,7 +105,7 @@ class CppContractExecutor { + ")"; // We only need the first 4 bytes for the function signature - Bytes fullData = Utils::makeBytes(Utils::sha3(bytes::view(createSignature)) | std::views::take(4)); + Bytes fullData = Utils::makeBytes(Utils::sha3(View(bytes::view(createSignature))) | std::views::take(4)); std::apply(Utils::Overloaded{ [] () {}, @@ -124,7 +125,7 @@ class CppContractExecutor { .sender = bytes::cast(msg.from()), .input_data = fullData.data(), .input_size = fullData.size(), - .value = Utils::uint256ToEvmcUint256(msg.value()), + .value = EVMCConv::uint256ToEvmcUint256(msg.value()), .create2_salt{}, .code_address = bytes::cast(to) }; diff --git a/src/contract/messages/evmcontractexecutor.cpp b/src/contract/messages/evmcontractexecutor.cpp index b8b7b3cc..3ff26de0 100644 --- a/src/contract/messages/evmcontractexecutor.cpp +++ b/src/contract/messages/evmcontractexecutor.cpp @@ -1,8 +1,10 @@ #include "evmcontractexecutor.h" + #include "bytes/cast.h" #include "bytes/hex.h" #include "common.h" #include "outofgas.h" +#include "utils/evmcconv.h" #include "contract/costs.h" constexpr decltype(auto) getAndThen(auto&& map, const auto& key, auto&& andThen, auto&& orElse) { @@ -56,7 +58,7 @@ constexpr evmc_message makeEvmcMessage(const M& msg, uint64_t depth) { .sender = bytes::cast(msg.from()), .input_data = msg.input().data(), .input_size = msg.input().size(), - .value = Utils::uint256ToEvmcUint256(messageValueOrZero(msg)), + .value = EVMCConv::uint256ToEvmcUint256(messageValueOrZero(msg)), .create2_salt = evmc_bytes32{}, .code_address = bytes::cast(messageCodeAddress(msg)) }; @@ -73,7 +75,7 @@ constexpr evmc_message makeEvmcMessage(const M& msg, uint64_t depth, View(msg.from()), .input_data = nullptr, .input_size = 0, - .value = Utils::uint256ToEvmcUint256(messageValueOrZero(msg)), + .value = EVMCConv::uint256ToEvmcUint256(messageValueOrZero(msg)), .create2_salt = bytes::cast(messageSaltOrDefault(msg)), .code_address = evmc_address{} // TODO: CALL CODE? }; @@ -184,7 +186,7 @@ evmc_storage_status EvmContractExecutor::set_storage(const evmc::address& addr, evmc::uint256be EvmContractExecutor::get_balance(const evmc::address& addr) const noexcept { try { - return Utils::uint256ToEvmcUint256(context_.getAccount(addr).getBalance()); + return EVMCConv::uint256ToEvmcUint256(context_.getAccount(addr).getBalance()); } catch (const std::exception&) { return evmc::uint256be{}; } @@ -230,14 +232,14 @@ bool EvmContractExecutor::selfdestruct(const evmc::address& addr, const evmc::ad evmc_tx_context EvmContractExecutor::get_tx_context() const noexcept { return evmc_tx_context{ - .tx_gas_price = Utils::uint256ToEvmcUint256(context_.getTxGasPrice()), + .tx_gas_price = EVMCConv::uint256ToEvmcUint256(context_.getTxGasPrice()), .tx_origin = bytes::cast(context_.getTxOrigin()), .block_coinbase = bytes::cast(context_.getBlockCoinbase()), .block_number = context_.getBlockNumber(), .block_timestamp = context_.getBlockTimestamp(), .block_gas_limit = context_.getBlockGasLimit(), .block_prev_randao = {}, - .chain_id = Utils::uint256ToEvmcUint256(context_.getChainId()), + .chain_id = EVMCConv::uint256ToEvmcUint256(context_.getChainId()), .block_base_fee = {}, .blob_base_fee = {}, .blob_hashes = nullptr, @@ -246,7 +248,7 @@ evmc_tx_context EvmContractExecutor::get_tx_context() const noexcept { } evmc::bytes32 EvmContractExecutor::get_block_hash(int64_t number) const noexcept { - return Utils::uint256ToEvmcUint256(number); // TODO: ??? + return EVMCConv::uint256ToEvmcUint256(number); // TODO: ??? } void EvmContractExecutor::emit_log(const evmc::address& addr, const uint8_t* data, size_t dataSize, const evmc::bytes32 topics[], size_t topicsCount) noexcept { @@ -294,7 +296,7 @@ void EvmContractExecutor::set_transient_storage(const evmc::address &addr, const evmc::Result EvmContractExecutor::call(const evmc_message& msg) noexcept { Gas gas(msg.gas); - const uint256_t value = Utils::evmcUint256ToUint256(msg.value); + const uint256_t value = EVMCConv::evmcUint256ToUint256(msg.value); const auto process = [&] (auto& msg) { try { diff --git a/src/contract/messages/evmcontractexecutor.h b/src/contract/messages/evmcontractexecutor.h index bac40cf8..164d0c10 100644 --- a/src/contract/messages/evmcontractexecutor.h +++ b/src/contract/messages/evmcontractexecutor.h @@ -38,7 +38,7 @@ class EvmContractExecutor : public evmc::Host { throw DynamicException("EVM contract function name is empty (contract not registered?)"); } - Bytes res = Utils::makeBytes(Utils::uint32ToBytes(ABI::FunctorEncoder::encode(functionName).value)); + Bytes res = Utils::makeBytes(UintConv::uint32ToBytes(ABI::FunctorEncoder::encode(functionName).value)); if constexpr (sizeof...(Args) > 0) { Utils::appendBytes(res, ABI::Encoder::encodeData(args...)); diff --git a/src/contract/messages/precompiledcontractexecutor.cpp b/src/contract/messages/precompiledcontractexecutor.cpp index 9c578dc0..edb513fd 100644 --- a/src/contract/messages/precompiledcontractexecutor.cpp +++ b/src/contract/messages/precompiledcontractexecutor.cpp @@ -2,5 +2,5 @@ Bytes PrecompiledContractExecutor::execute(EncodedStaticCallMessage& msg) { const uint256_t randomValue = std::invoke(randomGen_); - return Utils::makeBytes(Utils::uint256ToBytes(randomValue)); + return Utils::makeBytes(UintConv::uint256ToBytes(randomValue)); } diff --git a/src/contract/trace/call.cpp b/src/contract/trace/call.cpp index 094ca432..57312db6 100644 --- a/src/contract/trace/call.cpp +++ b/src/contract/trace/call.cpp @@ -16,7 +16,7 @@ json Call::toJson() const { res["from"] = this->from.hex(true); res["to"] = this->to.hex(true); - const uint256_t value = Utils::bytesToUint256(this->value); + const uint256_t value = UintConv::bytesToUint256(this->value); res["value"] = Hex::fromBytes(Utils::uintToBytes(value), true).forRPC(); res["gas"] = Hex::fromBytes(Utils::uintToBytes(this->gas), true).forRPC(); diff --git a/src/core/state.cpp b/src/core/state.cpp index 232a6792..b0400cfc 100644 --- a/src/core/state.cpp +++ b/src/core/state.cpp @@ -220,7 +220,7 @@ void State::processTransaction( TxAdditionalData txData{.hash = tx.hash()}; try { - const Hash randomSeed(Utils::uint256ToBytes((static_cast(randomnessHash) + txIndex))); + const Hash randomSeed(UintConv::uint256ToBytes((static_cast(randomnessHash) + txIndex))); ExecutionContext context = ExecutionContext::Builder{} .storage(this->vmStorage_) diff --git a/src/core/storage.cpp b/src/core/storage.cpp index 9e449660..c446cb59 100644 --- a/src/core/storage.cpp +++ b/src/core/storage.cpp @@ -150,7 +150,7 @@ std::tuple< const Bytes blockData = blocksDb_.get(blockHash, DBPrefix::blocks); if (blockData.empty()) std::make_tuple(nullptr, Hash(), 0u, 0u); - const uint64_t blockHeight = Utils::bytesToUint64(View(blockData).subspan(201, 8)); + const uint64_t blockHeight = UintConv::bytesToUint64(View(blockData).subspan(201, 8)); return std::make_tuple( std::make_shared(getTxFromBlockWithIndex(blockData, blockIndex)), blockHash, blockIndex, blockHeight diff --git a/src/net/p2p/encoding.cpp b/src/net/p2p/encoding.cpp index d46ce9e1..cf820d2d 100644 --- a/src/net/p2p/encoding.cpp +++ b/src/net/p2p/encoding.cpp @@ -65,7 +65,7 @@ namespace P2P { } } - NodeInfo nodeInfoFromMessage(const bytes::View& data) { + NodeInfo nodeInfoFromMessage(const View& data) { uint64_t nodeVersion = UintConv::bytesToUint64(data.subspan(0, 8)); uint64_t nodeEpoch = UintConv::bytesToUint64(data.subspan(8, 8)); uint64_t nodeHeight = UintConv::bytesToUint64(data.subspan(16, 8)); @@ -94,7 +94,7 @@ namespace P2P { nodesToMessage(message, nodes); } - std::vector blocksFromMessage(const bytes::View& data, const uint64_t& requiredChainId) { + std::vector blocksFromMessage(const View& data, const uint64_t& requiredChainId) { std::vector blocks; size_t index = 0; while (index < data.size()) { @@ -102,7 +102,7 @@ namespace P2P { uint64_t blockSize = UintConv::bytesToUint64(data.subspan(index, 8)); index += 8; if (data.size() - index < blockSize) { throw DynamicException("Invalid data size (block too small)"); } - bytes::View blockData = data.subspan(index, blockSize); + View blockData = data.subspan(index, blockSize); index += blockSize; blocks.emplace_back(FinalizedBlock::fromBytes(blockData, requiredChainId)); } @@ -133,7 +133,7 @@ namespace P2P { uint32_t txSize = UintConv::bytesToUint32(data.subspan(index, 4)); index += 4; if (data.size() - index < txSize) { throw DynamicException("Invalid data size (tx too small)"); } - bytes::View txData = data.subspan(index, txSize); + View txData = data.subspan(index, txSize); index += txSize; // Assuming requiredChainId is declared elsewhere txs.emplace_back(txData, requiredChainId); diff --git a/src/net/p2p/encoding.h b/src/net/p2p/encoding.h index d1d7ffed..ad6b5176 100644 --- a/src/net/p2p/encoding.h +++ b/src/net/p2p/encoding.h @@ -660,7 +660,7 @@ namespace P2P { * @return A map of the nodes and their respective IDs. * @throw DynamicException if data size or IP version is invalid. */ - boost::unordered_flat_map nodesFromMessage(bytes::View data); + boost::unordered_flat_map nodesFromMessage(View data); /** * Helper function for converting nodes to a message. Conversion is done in-place. @@ -674,7 +674,7 @@ namespace P2P { * @param data The raw bytes string to parse. * @return A struct with the node's information. */ - NodeInfo nodeInfoFromMessage(const bytes::View& data); + NodeInfo nodeInfoFromMessage(const View& data); /** * Helper function for converting node information to a message. Conversion is done in-place. @@ -697,7 +697,7 @@ namespace P2P { * @return A list of blocks. * @throw DynamicException if data size is invalid. */ - std::vector blocksFromMessage(const bytes::View& data, const uint64_t& requiredChainId); + std::vector blocksFromMessage(const View& data, const uint64_t& requiredChainId); /** * Helper function for converting block data to a message. Conversion is done in-place. diff --git a/src/utils/bytesinterface.h b/src/utils/bytesinterface.h index 2fb81488..ca2d8b15 100644 --- a/src/utils/bytesinterface.h +++ b/src/utils/bytesinterface.h @@ -37,7 +37,7 @@ class BytesInterface { explicit constexpr BytesInterface(const bytes::Range auto& data) requires (N != std::dynamic_extent) { if (const size_t size = std::ranges::size(data); size != N) - throw DynamicException("Given bytes range of size " + std::to_string(size) + + throw std::invalid_argument("Given bytes range of size " + std::to_string(size) + " is not suitable for initializing a FixedBytes<" + std::to_string(N) + ">"); std::ranges::copy(data, self().begin()); diff --git a/src/utils/dynamicexception.h b/src/utils/dynamicexception.h index 9a29bf6d..6bec165d 100644 --- a/src/utils/dynamicexception.h +++ b/src/utils/dynamicexception.h @@ -8,6 +8,7 @@ See the LICENSE.txt file in the project root for more information. #ifndef DYNAMIC_EXCEPTION_H #define DYNAMIC_EXCEPTION_H +#include // put_time #include // ctime #include // ostringstream diff --git a/src/utils/evmcconv.cpp b/src/utils/evmcconv.cpp index 370807ed..4675ec95 100644 --- a/src/utils/evmcconv.cpp +++ b/src/utils/evmcconv.cpp @@ -10,7 +10,7 @@ See the LICENSE.txt file in the project root for more information. uint256_t EVMCConv::evmcUint256ToUint256(const evmc::uint256be& x) { // We can use the uint256ToBytes directly as it is std::span and we can create a span from an array - return UintConv::bytesToUint256(bytes::View(x.bytes, 32)); + return UintConv::bytesToUint256(View(x.bytes, 32)); } evmc::uint256be EVMCConv::uint256ToEvmcUint256(const uint256_t& x) { @@ -28,7 +28,7 @@ BytesArr<32> EVMCConv::evmcUint256ToBytes(const evmc::uint256be& x) { return ret; } -evmc::uint256be EVMCConv::bytesToEvmcUint256(const bytes::View x) { +evmc::uint256be EVMCConv::bytesToEvmcUint256(const View x) { evmc::uint256be ret; std::copy(x.begin(), x.end(), ret.bytes); return ret; @@ -38,12 +38,12 @@ Functor EVMCConv::getFunctor(const evmc_message& msg) { Functor ret; if (msg.input_size < 4) return ret; // Memcpy the first 4 bytes from the input data to the function signature - ret.value = UintConv::bytesToUint32(bytes::View(msg.input_data, 4)); + ret.value = UintConv::bytesToUint32(View(msg.input_data, 4)); return ret; } -bytes::View EVMCConv::getFunctionArgs(const evmc_message& msg) { - if (msg.input_size < 4) return bytes::View(); - return bytes::View(msg.input_data + 4, msg.input_size - 4); +View EVMCConv::getFunctionArgs(const evmc_message& msg) { + if (msg.input_size < 4) return View(); + return View(msg.input_data + 4, msg.input_size - 4); } diff --git a/src/utils/evmcconv.h b/src/utils/evmcconv.h index ef62c32e..bdfc0113 100644 --- a/src/utils/evmcconv.h +++ b/src/utils/evmcconv.h @@ -42,7 +42,7 @@ namespace EVMCConv { uint256_t evmcUint256ToUint256(const evmc::uint256be& x); evmc::uint256be uint256ToEvmcUint256(const uint256_t& x); BytesArr<32> evmcUint256ToBytes(const evmc::uint256be& x); - evmc::uint256be bytesToEvmcUint256(const bytes::View x); + evmc::uint256be bytesToEvmcUint256(const View x); ///@} /** @@ -53,11 +53,11 @@ namespace EVMCConv { Functor getFunctor(const evmc_message& msg); /** - * Get the bytes::View representing the function arguments of a given evmc_message. + * Get the View representing the function arguments of a given evmc_message. * @param msg The evmc_message to get the function arguments from. - * @return The bytes::View representing the function arguments. + * @return The View representing the function arguments. */ - bytes::View getFunctionArgs(const evmc_message& msg); + View getFunctionArgs(const evmc_message& msg); }; #endif // EVMCCONV_H diff --git a/src/utils/hash.cpp b/src/utils/hash.cpp index 1f6db3b3..436c5d7b 100644 --- a/src/utils/hash.cpp +++ b/src/utils/hash.cpp @@ -1,12 +1,12 @@ #include "hash.h" #include "utils.h" -Hash::Hash(const uint256_t& value) : Hash(Utils::uint256ToBytes(value)) {} +Hash::Hash(const uint256_t& value) : Hash(UintConv::uint256ToBytes(value)) {} Hash::operator uint256_t() const { - return Utils::bytesToUint256(*this); + return UintConv::bytesToUint256(*this); } View::operator uint256_t() const { - return Utils::bytesToUint256(*this); + return UintConv::bytesToUint256(*this); } \ No newline at end of file diff --git a/src/utils/intconv.cpp b/src/utils/intconv.cpp index 976960d2..dc993042 100644 --- a/src/utils/intconv.cpp +++ b/src/utils/intconv.cpp @@ -64,7 +64,7 @@ BytesArr<8> IntConv::int64ToBytes(const int64_t& i) { // BYTES TO INT // ========================================================================== -int256_t IntConv::bytesToInt256(const bytes::View b) { +int256_t IntConv::bytesToInt256(const View b) { if (b.size() != 32) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 32, got " + std::to_string(b.size()) ); @@ -87,7 +87,7 @@ int256_t IntConv::bytesToInt256(const bytes::View b) { } } -int136_t IntConv::bytesToInt136(const bytes::View b) { +int136_t IntConv::bytesToInt136(const View b) { if (b.size() != 17) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 17, got " + std::to_string(b.size()) ); @@ -96,7 +96,7 @@ int136_t IntConv::bytesToInt136(const bytes::View b) { return ret; } -int64_t IntConv::bytesToInt64(const bytes::View b) { +int64_t IntConv::bytesToInt64(const View b) { if (b.size() != 8) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 8, got " + std::to_string(b.size()) ); diff --git a/src/utils/intconv.h b/src/utils/intconv.h index f8e38b93..ad65914a 100644 --- a/src/utils/intconv.h +++ b/src/utils/intconv.h @@ -73,9 +73,9 @@ namespace IntConv { * @return The converted integer. * @throw DynamicException if string size is invalid. */ - int256_t bytesToInt256(const bytes::View b); - int136_t bytesToInt136(const bytes::View b); - int64_t bytesToInt64(const bytes::View b); + int256_t bytesToInt256(const View b); + int136_t bytesToInt136(const View b); + int64_t bytesToInt64(const View b); ///@} }; diff --git a/src/utils/strconv.cpp b/src/utils/strconv.cpp index 754243bf..dc92a250 100644 --- a/src/utils/strconv.cpp +++ b/src/utils/strconv.cpp @@ -16,7 +16,7 @@ Bytes StrConv::cArrayToBytes(const uint8_t* arr, size_t size) { return ret; } -Bytes StrConv::padLeftBytes(const bytes::View bytes, unsigned int charAmount, uint8_t sign) { +Bytes StrConv::padLeftBytes(const View bytes, unsigned int charAmount, uint8_t sign) { size_t padding = (charAmount > bytes.size()) ? (charAmount - bytes.size()) : 0; Bytes padded = (padding != 0) ? Bytes(padding, sign) : Bytes(0, 0x00); padded.reserve(bytes.size() + padded.size()); @@ -24,7 +24,7 @@ Bytes StrConv::padLeftBytes(const bytes::View bytes, unsigned int charAmount, ui return padded; } -Bytes StrConv::padRightBytes(const bytes::View bytes, unsigned int charAmount, uint8_t sign) { +Bytes StrConv::padRightBytes(const View bytes, unsigned int charAmount, uint8_t sign) { size_t padding = (charAmount > bytes.size()) ? (charAmount - bytes.size()) : 0; Bytes padded = (padding != 0) ? Bytes(padding, sign) : Bytes(0, 0x00); Bytes ret; diff --git a/src/utils/strconv.h b/src/utils/strconv.h index 231f24e7..718ae182 100644 --- a/src/utils/strconv.h +++ b/src/utils/strconv.h @@ -37,7 +37,7 @@ namespace StrConv { * @param sign (optional) The character to use as padding. Defaults to '0'. * @return The padded vector. */ - Bytes padLeftBytes(const bytes::View bytes, unsigned int charAmount, uint8_t sign = 0x00); + Bytes padLeftBytes(const View bytes, unsigned int charAmount, uint8_t sign = 0x00); /** * Add padding to the right of a byte vector. @@ -49,7 +49,7 @@ namespace StrConv { * @param sign (optional) The character to use as padding. Defaults to '0'. * @return The padded vector. */ - Bytes padRightBytes(const bytes::View bytes, unsigned int charAmount, uint8_t sign = 0x00); + Bytes padRightBytes(const View bytes, unsigned int charAmount, uint8_t sign = 0x00); /// Overload of padLeftBytes() that works with UTF-8 strings. std::string padLeft(std::string str, unsigned int charAmount, char sign = '\x00'); diff --git a/src/utils/uintconv.cpp b/src/utils/uintconv.cpp index 08467f29..a2223f0a 100644 --- a/src/utils/uintconv.cpp +++ b/src/utils/uintconv.cpp @@ -304,7 +304,7 @@ BytesArr<1> UintConv::uint8ToBytes(const uint8_t& i) { // BYTES TO UINT // ========================================================================== -uint256_t UintConv::bytesToUint256(const bytes::View b) { +uint256_t UintConv::bytesToUint256(const View b) { if (b.size() != 32) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 32, got " + std::to_string(b.size()) ); @@ -313,7 +313,7 @@ uint256_t UintConv::bytesToUint256(const bytes::View b) { return ret; } -uint248_t UintConv::bytesToUint248(const bytes::View b) { +uint248_t UintConv::bytesToUint248(const View b) { if (b.size() != 31) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 31, got " + std::to_string(b.size()) ); @@ -322,7 +322,7 @@ uint248_t UintConv::bytesToUint248(const bytes::View b) { return ret; } -uint240_t UintConv::bytesToUint240(const bytes::View b) { +uint240_t UintConv::bytesToUint240(const View b) { if (b.size() != 30) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 30, got " + std::to_string(b.size()) ); @@ -331,7 +331,7 @@ uint240_t UintConv::bytesToUint240(const bytes::View b) { return ret; } -uint232_t UintConv::bytesToUint232(const bytes::View b) { +uint232_t UintConv::bytesToUint232(const View b) { if (b.size() != 29) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 29, got " + std::to_string(b.size()) ); @@ -340,7 +340,7 @@ uint232_t UintConv::bytesToUint232(const bytes::View b) { return ret; } -uint224_t UintConv::bytesToUint224(const bytes::View b) { +uint224_t UintConv::bytesToUint224(const View b) { if (b.size() != 28) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 28, got " + std::to_string(b.size()) ); @@ -349,7 +349,7 @@ uint224_t UintConv::bytesToUint224(const bytes::View b) { return ret; } -uint216_t UintConv::bytesToUint216(const bytes::View b) { +uint216_t UintConv::bytesToUint216(const View b) { if (b.size() != 27) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 27, got " + std::to_string(b.size()) ); @@ -358,7 +358,7 @@ uint216_t UintConv::bytesToUint216(const bytes::View b) { return ret; } -uint208_t UintConv::bytesToUint208(const bytes::View b) { +uint208_t UintConv::bytesToUint208(const View b) { if (b.size() != 26) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 26, got " + std::to_string(b.size()) ); @@ -367,7 +367,7 @@ uint208_t UintConv::bytesToUint208(const bytes::View b) { return ret; } -uint200_t UintConv::bytesToUint200(const bytes::View b) { +uint200_t UintConv::bytesToUint200(const View b) { if (b.size() != 25) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 25, got " + std::to_string(b.size()) ); @@ -376,7 +376,7 @@ uint200_t UintConv::bytesToUint200(const bytes::View b) { return ret; } -uint192_t UintConv::bytesToUint192(const bytes::View b) { +uint192_t UintConv::bytesToUint192(const View b) { if (b.size() != 24) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 24, got " + std::to_string(b.size()) ); @@ -385,7 +385,7 @@ uint192_t UintConv::bytesToUint192(const bytes::View b) { return ret; } -uint184_t UintConv::bytesToUint184(const bytes::View b) { +uint184_t UintConv::bytesToUint184(const View b) { if (b.size() != 23) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 23, got " + std::to_string(b.size()) ); @@ -394,7 +394,7 @@ uint184_t UintConv::bytesToUint184(const bytes::View b) { return ret; } -uint176_t UintConv::bytesToUint176(const bytes::View b) { +uint176_t UintConv::bytesToUint176(const View b) { if (b.size() != 22) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 22, got " + std::to_string(b.size()) ); @@ -403,7 +403,7 @@ uint176_t UintConv::bytesToUint176(const bytes::View b) { return ret; } -uint168_t UintConv::bytesToUint168(const bytes::View b) { +uint168_t UintConv::bytesToUint168(const View b) { if (b.size() != 21) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 21, got " + std::to_string(b.size()) ); @@ -412,7 +412,7 @@ uint168_t UintConv::bytesToUint168(const bytes::View b) { return ret; } -uint160_t UintConv::bytesToUint160(const bytes::View b) { +uint160_t UintConv::bytesToUint160(const View b) { if (b.size() != 20) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 20, got " + std::to_string(b.size()) ); @@ -421,7 +421,7 @@ uint160_t UintConv::bytesToUint160(const bytes::View b) { return ret; } -uint152_t UintConv::bytesToUint152(const bytes::View b) { +uint152_t UintConv::bytesToUint152(const View b) { if (b.size() != 19) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 19, got " + std::to_string(b.size()) ); @@ -430,7 +430,7 @@ uint152_t UintConv::bytesToUint152(const bytes::View b) { return ret; } -uint144_t UintConv::bytesToUint144(const bytes::View b) { +uint144_t UintConv::bytesToUint144(const View b) { if (b.size() != 18) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 18, got " + std::to_string(b.size()) ); @@ -439,7 +439,7 @@ uint144_t UintConv::bytesToUint144(const bytes::View b) { return ret; } -uint136_t UintConv::bytesToUint136(const bytes::View b) { +uint136_t UintConv::bytesToUint136(const View b) { if (b.size() != 17) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 17, got " + std::to_string(b.size()) ); @@ -448,7 +448,7 @@ uint136_t UintConv::bytesToUint136(const bytes::View b) { return ret; } -uint128_t UintConv::bytesToUint128(const bytes::View b) { +uint128_t UintConv::bytesToUint128(const View b) { if (b.size() != 16) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 16, got " + std::to_string(b.size()) ); @@ -457,7 +457,7 @@ uint128_t UintConv::bytesToUint128(const bytes::View b) { return ret; } -uint120_t UintConv::bytesToUint120(const bytes::View b) { +uint120_t UintConv::bytesToUint120(const View b) { if (b.size() != 15) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 15, got " + std::to_string(b.size()) ); @@ -466,7 +466,7 @@ uint120_t UintConv::bytesToUint120(const bytes::View b) { return ret; } -uint112_t UintConv::bytesToUint112(const bytes::View b) { +uint112_t UintConv::bytesToUint112(const View b) { if (b.size() != 14) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 16, got " + std::to_string(b.size()) ); @@ -475,7 +475,7 @@ uint112_t UintConv::bytesToUint112(const bytes::View b) { return ret; } -uint104_t UintConv::bytesToUint104(const bytes::View b) { +uint104_t UintConv::bytesToUint104(const View b) { if (b.size() != 13) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 13, got " + std::to_string(b.size()) ); @@ -484,7 +484,7 @@ uint104_t UintConv::bytesToUint104(const bytes::View b) { return ret; } -uint96_t UintConv::bytesToUint96(const bytes::View b) { +uint96_t UintConv::bytesToUint96(const View b) { if (b.size() != 12) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 12, got " + std::to_string(b.size()) ); @@ -493,7 +493,7 @@ uint96_t UintConv::bytesToUint96(const bytes::View b) { return ret; } -uint88_t UintConv::bytesToUint88(const bytes::View b) { +uint88_t UintConv::bytesToUint88(const View b) { if (b.size() != 11) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 11, got " + std::to_string(b.size()) ); @@ -502,7 +502,7 @@ uint88_t UintConv::bytesToUint88(const bytes::View b) { return ret; } -uint80_t UintConv::bytesToUint80(const bytes::View b) { +uint80_t UintConv::bytesToUint80(const View b) { if (b.size() != 10) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 10, got " + std::to_string(b.size()) ); @@ -511,7 +511,7 @@ uint80_t UintConv::bytesToUint80(const bytes::View b) { return ret; } -uint72_t UintConv::bytesToUint72(const bytes::View b) { +uint72_t UintConv::bytesToUint72(const View b) { if (b.size() != 9) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 9, got " + std::to_string(b.size()) ); @@ -520,7 +520,7 @@ uint72_t UintConv::bytesToUint72(const bytes::View b) { return ret; } -uint56_t UintConv::bytesToUint56(const bytes::View b) { +uint56_t UintConv::bytesToUint56(const View b) { if (b.size() != 7) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 7, got " + std::to_string(b.size()) ); @@ -529,7 +529,7 @@ uint56_t UintConv::bytesToUint56(const bytes::View b) { return ret; } -uint48_t UintConv::bytesToUint48(const bytes::View b) { +uint48_t UintConv::bytesToUint48(const View b) { if (b.size() != 6) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 6, got " + std::to_string(b.size()) ); @@ -538,7 +538,7 @@ uint48_t UintConv::bytesToUint48(const bytes::View b) { return ret; } -uint40_t UintConv::bytesToUint40(const bytes::View b) { +uint40_t UintConv::bytesToUint40(const View b) { if (b.size() != 5) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 5, got " + std::to_string(b.size()) ); @@ -547,7 +547,7 @@ uint40_t UintConv::bytesToUint40(const bytes::View b) { return ret; } -uint24_t UintConv::bytesToUint24(const bytes::View b) { +uint24_t UintConv::bytesToUint24(const View b) { if (b.size() != 3) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 3, got " + std::to_string(b.size()) ); @@ -556,7 +556,7 @@ uint24_t UintConv::bytesToUint24(const bytes::View b) { return ret; } -uint64_t UintConv::bytesToUint64(const bytes::View b) { +uint64_t UintConv::bytesToUint64(const View b) { if (b.size() != 8) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 8, got " + std::to_string(b.size()) ); @@ -568,7 +568,7 @@ uint64_t UintConv::bytesToUint64(const bytes::View b) { return ret; } -uint32_t UintConv::bytesToUint32(const bytes::View b) { +uint32_t UintConv::bytesToUint32(const View b) { if (b.size() != 4) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 4, got " + std::to_string(b.size()) ); @@ -580,7 +580,7 @@ uint32_t UintConv::bytesToUint32(const bytes::View b) { return ret; } -uint16_t UintConv::bytesToUint16(const bytes::View b) { +uint16_t UintConv::bytesToUint16(const View b) { if (b.size() != 2) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 2, got " + std::to_string(b.size()) ); @@ -592,7 +592,7 @@ uint16_t UintConv::bytesToUint16(const bytes::View b) { return ret; } -uint8_t UintConv::bytesToUint8(const bytes::View b) { +uint8_t UintConv::bytesToUint8(const View b) { if (b.size() != 1) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 1, got " + std::to_string(b.size()) ); diff --git a/src/utils/uintconv.h b/src/utils/uintconv.h index 017dec17..09de7399 100644 --- a/src/utils/uintconv.h +++ b/src/utils/uintconv.h @@ -98,39 +98,39 @@ namespace UintConv { * @return The converted integer. * @throw DynamicException if string size is invalid. */ - uint256_t bytesToUint256(const bytes::View b); - uint248_t bytesToUint248(const bytes::View b); - uint240_t bytesToUint240(const bytes::View b); - uint232_t bytesToUint232(const bytes::View b); - uint224_t bytesToUint224(const bytes::View b); - uint216_t bytesToUint216(const bytes::View b); - uint208_t bytesToUint208(const bytes::View b); - uint200_t bytesToUint200(const bytes::View b); - uint192_t bytesToUint192(const bytes::View b); - uint184_t bytesToUint184(const bytes::View b); - uint176_t bytesToUint176(const bytes::View b); - uint168_t bytesToUint168(const bytes::View b); - uint160_t bytesToUint160(const bytes::View b); - uint152_t bytesToUint152(const bytes::View b); - uint144_t bytesToUint144(const bytes::View b); - uint136_t bytesToUint136(const bytes::View b); - uint128_t bytesToUint128(const bytes::View b); - uint120_t bytesToUint120(const bytes::View b); - uint112_t bytesToUint112(const bytes::View b); - uint104_t bytesToUint104(const bytes::View b); - uint96_t bytesToUint96(const bytes::View b); - uint88_t bytesToUint88(const bytes::View b); - uint80_t bytesToUint80(const bytes::View b); - uint72_t bytesToUint72(const bytes::View b); - uint56_t bytesToUint56(const bytes::View b); - uint48_t bytesToUint48(const bytes::View b); - uint40_t bytesToUint40(const bytes::View b); - uint24_t bytesToUint24(const bytes::View b); + uint256_t bytesToUint256(const View b); + uint248_t bytesToUint248(const View b); + uint240_t bytesToUint240(const View b); + uint232_t bytesToUint232(const View b); + uint224_t bytesToUint224(const View b); + uint216_t bytesToUint216(const View b); + uint208_t bytesToUint208(const View b); + uint200_t bytesToUint200(const View b); + uint192_t bytesToUint192(const View b); + uint184_t bytesToUint184(const View b); + uint176_t bytesToUint176(const View b); + uint168_t bytesToUint168(const View b); + uint160_t bytesToUint160(const View b); + uint152_t bytesToUint152(const View b); + uint144_t bytesToUint144(const View b); + uint136_t bytesToUint136(const View b); + uint128_t bytesToUint128(const View b); + uint120_t bytesToUint120(const View b); + uint112_t bytesToUint112(const View b); + uint104_t bytesToUint104(const View b); + uint96_t bytesToUint96(const View b); + uint88_t bytesToUint88(const View b); + uint80_t bytesToUint80(const View b); + uint72_t bytesToUint72(const View b); + uint56_t bytesToUint56(const View b); + uint48_t bytesToUint48(const View b); + uint40_t bytesToUint40(const View b); + uint24_t bytesToUint24(const View b); - uint64_t bytesToUint64(const bytes::View b); - uint32_t bytesToUint32(const bytes::View b); - uint16_t bytesToUint16(const bytes::View b); - uint8_t bytesToUint8(const bytes::View b); + uint64_t bytesToUint64(const View b); + uint32_t bytesToUint32(const View b); + uint16_t bytesToUint16(const View b); + uint8_t bytesToUint8(const View b); ///@} }; diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp index e5fedd81..108b15af 100644 --- a/src/utils/utils.cpp +++ b/src/utils/utils.cpp @@ -105,13 +105,13 @@ std::string Utils::getSignalName(int signum) { return n; } -bytes::View Utils::create_view_span(const Bytes& vec, size_t start, size_t size) { +View Utils::create_view_span(const Bytes& vec, size_t start, size_t size) { if (start + size > vec.size()) throw DynamicException("Invalid range for span"); - return bytes::View(vec.data() + start, size); + return View(vec.data() + start, size); } -bytes::View Utils::create_view_span(const std::string_view str, size_t start, size_t size) { +View Utils::create_view_span(const std::string_view str, size_t start, size_t size) { if (start + size > str.size()) throw DynamicException("Invalid range for span"); - return bytes::View(reinterpret_cast(str.data()) + start, size); + return View(reinterpret_cast(str.data()) + start, size); } diff --git a/src/utils/utils.h b/src/utils/utils.h index 2e7ce5d7..b79a3c1e 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -438,7 +438,7 @@ namespace Utils { * @return The converted span. * @throw DynamicException if range is invalid. */ - bytes::View create_view_span(const Bytes& vec, size_t start, size_t size); + View create_view_span(const Bytes& vec, size_t start, size_t size); /** * Convert an array to const span. @@ -482,7 +482,7 @@ namespace Utils { * @return The converted span. * @throw DynamicException if range is invalid. */ - bytes::View create_view_span(const std::string_view str, size_t start, size_t size); + View create_view_span(const std::string_view str, size_t start, size_t size); /** * Append a vector to another. diff --git a/tests/contract/calltracer.cpp b/tests/contract/calltracer.cpp index cd960581..a37ce096 100644 --- a/tests/contract/calltracer.cpp +++ b/tests/contract/calltracer.cpp @@ -87,22 +87,6 @@ struct UserWrapper { namespace TCallTracer { TEST_CASE("CallTracer Tests", "[trace]") { - SECTION("Call Type Parsing") { - evmc_message msgCall; - evmc_message msgStaticCall; - evmc_message msgDelegateCall; - evmc_message msgInvalidCall; - msgCall.kind = EVMC_CALL; - msgStaticCall.kind = EVMC_CALL; - msgDelegateCall.kind = EVMC_DELEGATECALL; - msgInvalidCall.kind = evmc_call_kind(-1); - msgStaticCall.flags = EVMC_STATIC; - REQUIRE(trace::getCallType(msgCall) == trace::Call::Type::CALL); - REQUIRE(trace::getCallType(msgStaticCall) == trace::Call::Type::STATICCALL); - REQUIRE(trace::getCallType(msgDelegateCall) == trace::Call::Type::DELEGATECALL); - REQUIRE_THROWS(trace::getCallType(msgInvalidCall)); - } - SECTION("EVM Single Call") { SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("TestTraceContracts"); @@ -124,7 +108,7 @@ namespace TCallTracer { REQUIRE(callTrace->to == contractAddress); REQUIRE(callTrace->value == FixedBytes<32>()); // TODO: gas and gasUsed? - REQUIRE(FixedBytes<32>(callTrace->input | std::views::drop(4)) == FixedBytes<32>(Utils::uint256ToBytes(uint256_t(33)))); + REQUIRE(FixedBytes<32>(callTrace->input | std::views::drop(4)) == FixedBytes<32>(UintConv::uint256ToBytes(uint256_t(33)))); REQUIRE(callTrace->output == Bytes()); REQUIRE(callTrace->calls.empty()); @@ -143,9 +127,9 @@ namespace TCallTracer { REQUIRE(callTrace->to == contractAddress); REQUIRE(callTrace->value == FixedBytes<32>()); // TODO: gas and gasUsed? - REQUIRE(FixedBytes<32>(callTrace->input | std::views::drop(4)) == FixedBytes<32>(Utils::uint256ToBytes(uint256_t(66)))); + REQUIRE(FixedBytes<32>(callTrace->input | std::views::drop(4)) == FixedBytes<32>(UintConv::uint256ToBytes(uint256_t(66)))); - REQUIRE(callTrace->output == Utils::makeBytes(Utils::uint256ToBytes(uint256_t(99)))); + REQUIRE(callTrace->output == Utils::makeBytes(UintConv::uint256ToBytes(uint256_t(99)))); REQUIRE(callTrace->calls.empty()); res = sdk.callViewFunction(contractAddress, &TestWrapper::sum); @@ -179,7 +163,7 @@ namespace TCallTracer { REQUIRE(callTrace->value == FixedBytes<32>()); // TODO: gas and gasUsed REQUIRE(Address(callTrace->input | std::views::drop(16) | std::views::take(20)) == testContractAddress); - REQUIRE(FixedBytes<32>(callTrace->input | std::views::drop(36)) == FixedBytes<32>(Utils::uint256ToBytes(uint256_t(55)))); + REQUIRE(FixedBytes<32>(callTrace->input | std::views::drop(36)) == FixedBytes<32>(UintConv::uint256ToBytes(uint256_t(55)))); REQUIRE(callTrace->output == Utils::makeBytes(UintConv::uint256ToBytes(uint256_t(100)))); REQUIRE(callTrace->calls.size() == 1); @@ -190,7 +174,7 @@ namespace TCallTracer { REQUIRE(nestedCall.from == testProxyContractAddress); REQUIRE(nestedCall.to == testContractAddress); REQUIRE(nestedCall.value == FixedBytes<32>()); - REQUIRE(FixedBytes<32>(nestedCall.input | std::views::drop(4)) == FixedBytes<32>(Utils::uint256ToBytes(uint256_t(55)))); + REQUIRE(FixedBytes<32>(nestedCall.input | std::views::drop(4)) == FixedBytes<32>(UintConv::uint256ToBytes(uint256_t(55)))); REQUIRE(nestedCall.output == Utils::makeBytes(UintConv::uint256ToBytes(uint256_t(100)))); REQUIRE(nestedCall.calls.empty()); @@ -313,7 +297,7 @@ namespace TCallTracer { REQUIRE(payCallTrace->status == trace::CallStatus::SUCCEEDED); REQUIRE(payCallTrace->from == sdk.getOptions().getChainOwner()); REQUIRE(payCallTrace->to == bankAddress); - REQUIRE(payCallTrace->value == FixedBytes<32>(Utils::uint256ToBytes(uint256_t(4568)))); + REQUIRE(payCallTrace->value == FixedBytes<32>(UintConv::uint256ToBytes(uint256_t(4568)))); REQUIRE(payCallTrace->input == Hex::toBytes("0x1b9265b8")); REQUIRE(payCallTrace->output == Bytes()); REQUIRE(payCallTrace->calls.empty()); diff --git a/tests/contract/createcontract.cpp b/tests/contract/createcontract.cpp index fd76b34f..42177bfc 100644 --- a/tests/contract/createcontract.cpp +++ b/tests/contract/createcontract.cpp @@ -117,7 +117,7 @@ namespace TContractRandomness { // For coverage std::string contractCode = "6080604052348015600e575f80fd5b50600436106026575f3560e01c80633fa4f24514602a575b5f80fd5b60306044565b604051603b9190605f565b60405180910390f35b5f5481565b5f819050919050565b6059816049565b82525050565b5f60208201905060705f8301846052565b9291505056fea2646970667358221220800668e87144b8625a7e59ac82528e013d51d6ed08562ba8b641f0a2e66c0f3764736f6c634300081a0033"; REQUIRE(Hex::fromBytes(sdk.getState().getContractCode(newContractAddressCreate2)).get() == contractCode); - Address randAdd("0x1234567890123456789012345678901234567890", false); + Address randAdd(bytes::hex("0x1234567890123456789012345678901234567890")); REQUIRE(Hex::fromBytes(sdk.getState().getContractCode(randAdd)).get() == ""); } } diff --git a/tests/contract/dexv2.cpp b/tests/contract/dexv2.cpp index 261afc3b..91c50dab 100644 --- a/tests/contract/dexv2.cpp +++ b/tests/contract/dexv2.cpp @@ -39,7 +39,7 @@ namespace TDEXV2 { Address pair; Address tokenA; Address tokenB; - Address chainOwner("0x00dead00665771855a34155f5e7405489df2c3c6", false); + Address chainOwner(bytes::hex("0x00dead00665771855a34155f5e7405489df2c3c6")); std::unique_ptr options; { SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testDEXV2Pair"); @@ -133,7 +133,7 @@ namespace TDEXV2 { std::vector
allPairs = sdk.callViewFunction(factory, &DEXV2Factory::allPairs); REQUIRE(allPairs.size() == 1); REQUIRE(allPairs[0] == pair); - Address add("0x1234567890123456789012345678901234567890", false); + Address add(bytes::hex("0x1234567890123456789012345678901234567890")); sdk.callFunction(factory, &DEXV2Factory::setFeeTo, add); sdk.callFunction(factory, &DEXV2Factory::setFeeToSetter, add); REQUIRE(sdk.callViewFunction(factory, &DEXV2Factory::feeTo) == add); diff --git a/tests/contract/erc20.cpp b/tests/contract/erc20.cpp index 77c917a4..c518ba95 100644 --- a/tests/contract/erc20.cpp +++ b/tests/contract/erc20.cpp @@ -11,6 +11,8 @@ See the LICENSE.txt file in the project root for more information. #include "../sdktestsuite.hpp" +#include "bytes/hex.h" + // TODO: test events if/when implemented namespace TERC20 { @@ -80,7 +82,7 @@ namespace TERC20 { REQUIRE(std::get<0>(ABI::Decoder::decodeData(approveEvents[0].getData())) == uint256_t("500000000000000000")); // Search for a non-existing spender (for coverage) - Address ghost("0x1234567890123456789012345678901234567890", false); + Address ghost(bytes::hex("0x1234567890123456789012345678901234567890")); REQUIRE(sdk.callViewFunction(erc20, &ERC20::allowance, owner, ghost) == uint256_t(0)); } diff --git a/tests/contract/erc721test.cpp b/tests/contract/erc721test.cpp index 5c58491e..8e31688c 100644 --- a/tests/contract/erc721test.cpp +++ b/tests/contract/erc721test.cpp @@ -10,6 +10,8 @@ #include "../sdktestsuite.hpp" +#include "bytes/hex.h" + namespace TERC721Test { TEST_CASE("ERC721Test Class", "[contract][erc721test]") { SECTION("ERC721Test Creation + Dump") { @@ -70,8 +72,8 @@ namespace TERC721Test { REQUIRE_THROWS(sdk.callFunction(ERC721Address, &ERC721Test::mint, Address())); // Try transferring to zero address and from wrong owner - Address add1("0x1234567890123456789012345678901234567890", false); - Address add2("0x0987654321098765432109876543210987654321", false); + Address add1(bytes::hex("0x1234567890123456789012345678901234567890")); + Address add2(bytes::hex("0x0987654321098765432109876543210987654321")); REQUIRE_THROWS(sdk.callFunction(ERC721Address, &ERC721Test::transferFrom, sdk.getChainOwnerAccount().address, Address(), uint256_t(0))); REQUIRE_THROWS(sdk.callFunction(ERC721Address, &ERC721Test::transferFrom, add1, add2, uint256_t(0))); diff --git a/tests/contract/event.cpp b/tests/contract/event.cpp index 02f1dda3..dde1561f 100644 --- a/tests/contract/event.cpp +++ b/tests/contract/event.cpp @@ -5,19 +5,19 @@ This software is distributed under the MIT License. See the LICENSE.txt file in the project root for more information. */ -#include "../../src/libs/catch2/catch_amalgamated.hpp" - -#include "../../src/contract/event.h" - -#include "../../src/bytes/view.h" +#include "libs/catch2/catch_amalgamated.hpp" +#include "contract/event.h" +#include "bytes/view.h" +#include "bytes/random.h" +#include "bytes/hex.h" namespace TEvent { TEST_CASE("Event Class", "[contract][event]") { SECTION("Event Constructor (EVM)") { - Hash txHash = Hash::random(); - Hash blockHash = Hash::random(); - std::vector topics = {Hash::random(), Hash::random(), Hash::random(), Hash::random(), Hash::random()}; - Address add("0x1234567890123456789012345678901234567890", false); + Hash txHash = bytes::random(); + Hash blockHash = bytes::random(); + std::vector topics = {bytes::random(), bytes::random(), bytes::random(), bytes::random(), bytes::random()}; + Address add(bytes::hex("0x1234567890123456789012345678901234567890")); Bytes data{0xDE, 0xAD, 0xBE, 0xEF}; Event e("myEvent", 0, txHash, 1, blockHash, 2, add, data, topics, false); @@ -40,9 +40,9 @@ namespace TEvent { } SECTION("Event Constructor (CPP)") { - Hash txHash = Hash::random(); - Hash blockHash = Hash::random(); - Address add("0x1234567890123456789012345678901234567890", false); + Hash txHash = bytes::random(); + Hash blockHash = bytes::random(); + Address add(bytes::hex("0x1234567890123456789012345678901234567890")); // Anonymous event Event e1("myEvent", 0, txHash, 1, blockHash, 2, add, std::make_tuple( @@ -129,7 +129,7 @@ namespace TEvent { SECTION("Event Serialization (Normal + RPC)") { Hash txHash(Hex::toBytes("0x53472c61f1db8612fcdd17f24b78986bfa111ea3e323522456b1a78560f2215a")); Hash blockHash(Hex::toBytes("0x2b9b8644330d50ffb90c5fea02b73b562dfc550ec7f8c85f643b20391a972d5f")); - Address add("0x1234567890123456789012345678901234567890", false); + Address add(bytes::hex("0x1234567890123456789012345678901234567890")); Event e1("myEvent", 0, txHash, 1, blockHash, 2, add, std::make_tuple( EventParam("p1"), EventParam("p2"), diff --git a/tests/contract/messages/evmcontractexecutor.cpp b/tests/contract/messages/evmcontractexecutor.cpp index 9e678ab9..bf101ab2 100644 --- a/tests/contract/messages/evmcontractexecutor.cpp +++ b/tests/contract/messages/evmcontractexecutor.cpp @@ -132,12 +132,12 @@ TEST_CASE("Evm Message Executor Tests", "[evmcontractexecutor]") { // // TODO: with the call bellow, things doesn't work // input.clear(); // auto functor1 = ABI::FunctorEncoder::encode<>("close"); - // Utils::appendBytes(input, Utils::uint32ToBytes(functor1.value)); + // Utils::appendBytes(input, UintConv::uint32ToBytes(functor1.value)); // env.executor.execute(EncodedCallMessage(firstAccountAddress, casinoAddress, gas, value, input)); // input.clear(); // auto functor2 = ABI::FunctorEncoder::encode("gamble"); - // Utils::appendBytes(input, Utils::uint32ToBytes(functor2.value)); + // Utils::appendBytes(input, UintConv::uint32ToBytes(functor2.value)); // Utils::appendBytes(input, ABI::Encoder::encodeData(casinoAddress, fundsAddress)); // value = ONE_ETHER / 2; @@ -169,7 +169,7 @@ TEST_CASE("Evm Message Executor Tests", "[evmcontractexecutor]") { // Bytes input; // auto functor = ABI::FunctorEncoder::encode("delegateTo"); - // Utils::appendBytes(input, Utils::uint32ToBytes(functor.value)); + // Utils::appendBytes(input, UintConv::uint32ToBytes(functor.value)); // Utils::appendBytes(input, ABI::Encoder::encodeData(delegatedAddress, std::string("cometa"))); // std::cout << "\n\n"; diff --git a/tests/contract/pebble.cpp b/tests/contract/pebble.cpp index e650b695..b0aa3dd0 100644 --- a/tests/contract/pebble.cpp +++ b/tests/contract/pebble.cpp @@ -57,8 +57,8 @@ namespace TPEBBLE { SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testPebbleOwnershipTransfer"); Address pebbleAddr = sdk.deployContract(uint256_t(100000)); REQUIRE_THROWS(sdk.callFunction(pebbleAddr, &Pebble::transferOwnership, Address())); // cannot transfer to zero address - REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::owner) == Address("0x00dead00665771855a34155f5e7405489df2c3c6", false)); - Address newOwner("0x1234567890123456789012345678901234567890", false); + REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::owner) == Address(bytes::hex("0x00dead00665771855a34155f5e7405489df2c3c6"))); + Address newOwner(bytes::hex("0x1234567890123456789012345678901234567890")); sdk.callFunction(pebbleAddr, &Pebble::transferOwnership, newOwner); REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::owner) == newOwner); } @@ -66,7 +66,7 @@ namespace TPEBBLE { SECTION("Pebble ownership renounce (Ownable coverage)") { SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testPebbleOwnershipTransfer"); Address pebbleAddr = sdk.deployContract(uint256_t(100000)); - REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::owner) == Address("0x00dead00665771855a34155f5e7405489df2c3c6", false)); + REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::owner) == Address(bytes::hex("0x00dead00665771855a34155f5e7405489df2c3c6"))); sdk.callFunction(pebbleAddr, &Pebble::renounceOwnership); REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::owner) == Address()); } diff --git a/tests/core/rdpos.cpp b/tests/core/rdpos.cpp index 017e70a1..e0138458 100644 --- a/tests/core/rdpos.cpp +++ b/tests/core/rdpos.cpp @@ -127,7 +127,7 @@ namespace TRdPoS { auto blockchainWrapper = initialize(validatorPrivKeysRdpos, validatorKey, SDKTestSuite::getTestPort(), true, testDumpPath + "/rdPoSValidateBlockCoverage"); // Wrong signature (not randomList_[0]) - FinalizedBlock b1(Signature(), UPubKey(), Hash::random(), Hash::random(), Hash::random(), Hash::random(), 0, 0, {}, {}, Hash::random(), 1); + FinalizedBlock b1(Signature(), UPubKey(), bytes::random(), bytes::random(), bytes::random(), bytes::random(), 0, 0, {}, {}, bytes::random(), 1); REQUIRE_FALSE(blockchainWrapper.state.rdposValidateBlock(b1)); // TODO: this should be covered further, but faking block contents is too much hassle as it is (same goes for addValidatorTx and maybe getTxValidatorFunction which is not exposed by State) diff --git a/tests/core/state.cpp b/tests/core/state.cpp index 41c218d4..355c002f 100644 --- a/tests/core/state.cpp +++ b/tests/core/state.cpp @@ -58,7 +58,7 @@ std::pair buildCallInfo(const Address& addressToCall, const static Bytes buildMessageData(const Functor& function, View callData) { Bytes messageData; - Utils::appendBytes(messageData, Utils::uint32ToBytes(function.value)); + Utils::appendBytes(messageData, UintConv::uint32ToBytes(function.value)); Utils::appendBytes(messageData, callData); return messageData; } diff --git a/tests/core/storage.cpp b/tests/core/storage.cpp index 5dd76c4b..0d6ffe4e 100644 --- a/tests/core/storage.cpp +++ b/tests/core/storage.cpp @@ -11,6 +11,7 @@ See the LICENSE.txt file in the project root for more information. #include "../blockchainwrapper.hpp" // blockchain.h -> consensus.h -> state.h -> dump.h -> (storage.h -> utils/options.h), utils/db.h #include "bytes/random.h" +#include "bytes/hex.h" const std::vector validatorPrivKeysStorage { Hash(Hex::toBytes("0x0a0415d68a5ec2df57aab65efc2a7231b59b029bae7ff1bd2e40df9af96418c8")), @@ -117,33 +118,33 @@ namespace TStorage { } SECTION("Storage topicsMatch") { - Hash txHash = Hash::random(); - Hash blockHash = Hash::random(); - std::vector topics = {Hash::random(), Hash::random(), Hash::random(), Hash::random(), Hash::random()}; - Address add("0x1234567890123456789012345678901234567890", false); + Hash txHash = bytes::random(); + Hash blockHash = bytes::random(); + std::vector topics = {bytes::random(), bytes::random(), bytes::random(), bytes::random(), bytes::random()}; + Address add(bytes::hex("0x1234567890123456789012345678901234567890")); Bytes data{0xDE, 0xAD, 0xBE, 0xEF}; Event e("myEvent", 0, txHash, 1, blockHash, 2, add, data, topics, false); REQUIRE(Storage::topicsMatch(e, topics)); // For coverage REQUIRE(Storage::topicsMatch(e, {})); // Empty topics - topics.push_back(Hash::random()); + topics.push_back(bytes::random()); REQUIRE_FALSE(Storage::topicsMatch(e, topics)); // Event has fewer topics than required topics.pop_back(); - topics[0] = Hash::random(); + topics[0] = bytes::random(); REQUIRE_FALSE(Storage::topicsMatch(e, topics)); // Event has wrong topics } SECTION("Storage getEvents") { auto blockchainWrapper = initialize(validatorPrivKeysStorage, PrivKey(), 8080, true, "StorageGetEvents"); - Address add("0x1234567890123456789012345678901234567890", false); + Address add(bytes::hex("0x1234567890123456789012345678901234567890")); Bytes data{0xDE, 0xAD, 0xBE, 0xEF}; std::vector> topics; std::vector events; for (int i = 0; i < 5; i++) { - topics.push_back({Hash::random(), Hash::random(), Hash::random()}); + topics.push_back({bytes::random(), bytes::random(), bytes::random()}); events.push_back(Event( - "event" + std::to_string(i), 0, Hash::random(), 0, Hash::random(), i, add, data, topics[i], false + "event" + std::to_string(i), 0, bytes::random(), 0, bytes::random(), i, add, data, topics[i], false )); blockchainWrapper.storage.putEvent(events[i]); } diff --git a/tests/net/http/parser.cpp b/tests/net/http/parser.cpp index 48395e7c..94dd9d31 100644 --- a/tests/net/http/parser.cpp +++ b/tests/net/http/parser.cpp @@ -9,15 +9,18 @@ See the LICENSE.txt file in the project root for more information. #include "../../src/net/http/jsonrpc/blocktag.h" // parser.h +#include "bytes/random.h" +#include "bytes/hex.h" + namespace THTTPJSONRPCParser { TEST_CASE("HTTP JSON RPC Parser Tests", "[net][http][jsonrpc][parser]") { SECTION("Parser operator()") { - Hash h = Hash::random(); + Hash h = bytes::random(); std::vector v = {10, 20, 30, 40, 50}; // Parser regex REQUIRES hex prefix (0x) json jsonHash = h.hex(true).get(); - json jsonAdd = Address("0x0000111122223333444455556666777788889999", false).hex(true).get(); + json jsonAdd = Address(bytes::hex("0x0000111122223333444455556666777788889999")).hex(true).get(); json jsonBytes = Hex::fromBytes(Bytes{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, true).get(); json jsonBool = true; json jsonFloat = 13.37f; @@ -48,7 +51,7 @@ namespace THTTPJSONRPCParser { std::vector resVectorObj = jsonrpc::parse>(jsonVectorObj); REQUIRE(resHash == h); - REQUIRE(resAdd == Address("0x0000111122223333444455556666777788889999", false)); + REQUIRE(resAdd == Address(bytes::hex("0x0000111122223333444455556666777788889999"))); REQUIRE(resBytes == Bytes{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}); REQUIRE(resBool == true); REQUIRE(resFloat == 13.37f); @@ -66,9 +69,9 @@ namespace THTTPJSONRPCParser { SECTION("Parser operator() (throws)") { // Same thing but everything is wrong on purpose to cover throw cases json hashWrongType = json::array(); // Type is not string (or the required type) - json hashWrongFormat = Hash::random().hex().get(); // No "0x" + json hashWrongFormat = Hash(bytes::random()).hex().get(); // No "0x" json addWrongType = json::array(); - json addWrongFormat = Address("0x0000111122223333444455556666777788889999", false).hex().get(); + json addWrongFormat = Address(bytes::hex("0x0000111122223333444455556666777788889999")).hex().get(); json bytesWrongType = json::array(); json bytesWrongFormat = "0x000g"; // Invalid hex (0-9a-fA-F) json boolWrongType = json::array(); diff --git a/tests/net/p2p/nodeinfo.cpp b/tests/net/p2p/nodeinfo.cpp index 39a5647d..11b6f813 100644 --- a/tests/net/p2p/nodeinfo.cpp +++ b/tests/net/p2p/nodeinfo.cpp @@ -9,11 +9,13 @@ See the LICENSE.txt file in the project root for more information. #include "../../src/net/p2p/encoding.h" +#include "bytes/random.h" + // For coverage namespace TP2PNodeInfo { TEST_CASE("P2P NodeInfo", "[p2p][nodeinfo]") { SECTION("NodeInfo Constructor") { - Hash randomBlockHash = Hash::random(); + Hash randomBlockHash = bytes::random(); P2P::NodeID randomId(boost::asio::ip::address::from_string("127.0.0.1"), uint16_t(8000)); P2P::NodeInfo emptyNode; P2P::NodeInfo node(uint64_t(1), uint64_t(15000), uint64_t(30000), uint64_t(5), uint64_t(12345), randomBlockHash, {randomId}); @@ -34,12 +36,12 @@ namespace TP2PNodeInfo { } SECTION("NodeInfo operator==") { - Hash randomBlockHash = Hash::random(); + Hash randomBlockHash = bytes::random(); P2P::NodeID randomId(boost::asio::ip::address::from_string("127.0.0.1"), uint16_t(8000)); P2P::NodeID randomId2(boost::asio::ip::address::from_string("127.0.0.2"), uint16_t(8001)); P2P::NodeInfo node1(uint64_t(1), uint64_t(15000), uint64_t(30000), uint64_t(5), uint64_t(12345), randomBlockHash, {randomId}); P2P::NodeInfo node2(uint64_t(1), uint64_t(15000), uint64_t(30000), uint64_t(5), uint64_t(12345), randomBlockHash, {randomId}); - P2P::NodeInfo node3(uint64_t(2), uint64_t(1000), uint64_t(3000), uint64_t(4), uint64_t(54321), Hash::random(), {randomId2}); + P2P::NodeInfo node3(uint64_t(2), uint64_t(1000), uint64_t(3000), uint64_t(4), uint64_t(54321), bytes::random(), {randomId2}); REQUIRE(node1 == node2); REQUIRE_FALSE(node1 == node3); } diff --git a/tests/sdktestsuite.hpp b/tests/sdktestsuite.hpp index c0b501a3..d1ceae8c 100644 --- a/tests/sdktestsuite.hpp +++ b/tests/sdktestsuite.hpp @@ -749,7 +749,11 @@ class SDKTestSuite { const Address from = this->getChainOwnerAccount().address; EncodedStaticCallMessage msg(from, contractAddress, gas, fullData); - return std::get<0>(ABI::Decoder::decodeData(this->state_.ethCall(msg))); + const Bytes result = this->state_.ethCall(msg); + + if constexpr (not std::same_as) { + return std::get<0>(ABI::Decoder::decodeData(result)); + } } /** @@ -778,7 +782,11 @@ class SDKTestSuite { const Address from = this->getChainOwnerAccount().address; EncodedStaticCallMessage msg(from, contractAddress, gas, fullData); - return std::get<0>(ABI::Decoder::decodeData(this->state_.ethCall(msg))); + const Bytes result = this->state_.ethCall(msg); + + if constexpr (not std::same_as) { + return std::get<0>(ABI::Decoder::decodeData(result)); + } } diff --git a/tests/utils/evmcconv.cpp b/tests/utils/evmcconv.cpp index 8981ad8e..3b3166bb 100644 --- a/tests/utils/evmcconv.cpp +++ b/tests/utils/evmcconv.cpp @@ -16,7 +16,7 @@ namespace TJsonAbi { SECTION("EVMCConv uint256 <-> evmcUint256") { uint256_t i = 12345678; evmc::uint256be resEVMC = EVMCConv::uint256ToEvmcUint256(i); - REQUIRE(UintConv::bytesToUint256(bytes::View(resEVMC.bytes, 32)) == i); + REQUIRE(UintConv::bytesToUint256(View(resEVMC.bytes, 32)) == i); uint256_t resUINT = EVMCConv::evmcUint256ToUint256(resEVMC); REQUIRE(resUINT == i); } @@ -27,7 +27,7 @@ namespace TJsonAbi { BytesArr<32> resBYTES = EVMCConv::evmcUint256ToBytes(iEVMC); REQUIRE(UintConv::bytesToUint256(resBYTES) == i); evmc::uint256be resEVMC = EVMCConv::bytesToEvmcUint256(resBYTES); - REQUIRE(UintConv::bytesToUint256(bytes::View(resEVMC.bytes, 32)) == i); + REQUIRE(UintConv::bytesToUint256(View(resEVMC.bytes, 32)) == i); } SECTION("EVMCConv getFunctor") { @@ -53,8 +53,8 @@ namespace TJsonAbi { msg1.input_data = msg1Data.data(); msg2.input_size = 16; msg2.input_data = msg2Data.data(); - bytes::View get1 = EVMCConv::getFunctionArgs(msg1); - bytes::View get2 = EVMCConv::getFunctionArgs(msg2); + View get1 = EVMCConv::getFunctionArgs(msg1); + View get2 = EVMCConv::getFunctionArgs(msg2); REQUIRE(Hex::fromBytes(get1).get() == ""); REQUIRE(Hex::fromBytes(get2).get() == "0405060708090a0b0c0d0e0f"); } diff --git a/tests/utils/safehash.cpp b/tests/utils/safehash.cpp index c8616f76..eef2dc46 100644 --- a/tests/utils/safehash.cpp +++ b/tests/utils/safehash.cpp @@ -5,9 +5,10 @@ This software is distributed under the MIT License. See the LICENSE.txt file in the project root for more information. */ -#include "../../src/libs/catch2/catch_amalgamated.hpp" - -#include "../../src/utils/safehash.h" +#include "libs/catch2/catch_amalgamated.hpp" +#include "utils/safehash.h" +#include "bytes/hex.h" +#include "bytes/random.h" namespace TSafeHash { TEST_CASE("SafeHash Struct", "[utils][safehash]") { @@ -17,10 +18,10 @@ namespace TSafeHash { const std::string_view strView = "Goodbye Planet"; const Bytes bytes = Bytes{0xDE, 0xAD, 0xBE, 0xEF}; const BytesArr<4> bytesArr = {0xDE, 0xAD, 0xBE, 0xEF}; - const bytes::View bytesView = Utils::create_view_span(str); - const Address add(std::string("0x1234567890123456789012345678901234567890"), false); + const View bytesView = Utils::create_view_span(str); + const Address add(bytes::hex("0x1234567890123456789012345678901234567890")); const Functor func{83892529}; - const Hash hash = Hash::random(); + const Hash hash = bytes::random(); const TxValidator tx(Hex::toBytes("f845808026a08a4591f48d6307bb4cb8a0b0088b544d923d00bc1f264c3fdf16f946fdee0b34a077a6f6e8b3e78b45478827604f070d03060f413d823eae7fab9b139be7a41d81"), 1); const std::shared_ptr ptr = std::make_shared(str); const FixedBytes<4> fixed{0xDE, 0xAD, 0xBE, 0xEF}; diff --git a/tests/utils/strings.cpp b/tests/utils/strings.cpp index ce1cad98..d104a535 100644 --- a/tests/utils/strings.cpp +++ b/tests/utils/strings.cpp @@ -12,6 +12,7 @@ See the LICENSE.txt file in the project root for more information. #include "bytes/view.h" #include "bytes/random.h" #include "bytes/hex.h" +#include "bytes/cast.h" using Catch::Matchers::Equals; @@ -203,7 +204,7 @@ namespace THash { Bytes b = Hex::toBytes("9be83ea08b549e7c77644c451b55a674bb12e4668d018183ff9723b1de493818"); for (int i = 0; i < 32; i++) num.bytes[i] = b[i]; Hash hash(num); - REQUIRE(hash.toEvmcBytes32() == num); + REQUIRE(bytes::cast(hash) == num); } SECTION("Hash random()") { @@ -236,14 +237,14 @@ namespace TSignature { namespace TAddress { TEST_CASE("Address Class", "[utils][strings][address]") { SECTION("Address String View Constructor") { - Address addStr(std::string_view("0x71c7656ec7ab88b098defb751b7401b5f6d8976f"), false); - Address addBytes(std::string_view("\x71\xc7\x65\x6e\xc7\xab\x88\xb0\x98\xde\xfb\x75\x1b\x74\x01\xb5\xf6\xd8\x97\x6f"), true); + Address addStr(bytes::hex("0x71c7656ec7ab88b098defb751b7401b5f6d8976f")); + Address addBytes(bytes::view("\x71\xc7\x65\x6e\xc7\xab\x88\xb0\x98\xde\xfb\x75\x1b\x74\x01\xb5\xf6\xd8\x97\x6f")); REQUIRE(addStr == addBytes); REQUIRE_THAT(addStr.hex(), Equals("71c7656ec7ab88b098defb751b7401b5f6d8976f")); REQUIRE_THAT(addBytes.hex(), Equals("71c7656ec7ab88b098defb751b7401b5f6d8976f")); // For coverage - REQUIRE_THROWS(Address(std::string_view("0x71c7656ec7ab88b098defb751b7401b5f6d8976h"), false)); // last char is "h" - REQUIRE_THROWS(Address("\x71\xc7\x65\x6e\xc7\xab\x88\xb0\x98\xde\xfb\x75\x1b\x74\x01\xb5\xf6\xd8\x97", true)); // missing last byte "\x6f" + REQUIRE_THROWS(Address(bytes::hex("0x71c7656ec7ab88b098defb751b7401b5f6d8976h"))); // last char is "h" + REQUIRE_THROWS(Address(bytes::view("\x71\xc7\x65\x6e\xc7\xab\x88\xb0\x98\xde\xfb\x75\x1b\x74\x01\xb5\xf6\xd8\x97"))); // missing last byte "\x6f" } SECTION("Address Copy Constructor") { @@ -305,40 +306,3 @@ namespace TAddress { } } } - -namespace TStorageKey { - TEST_CASE("StorageKey Class", "[utils][strings][storagekey]") { - SECTION("StorageKey Constructors") { - evmc::address addr1; - evmc_address addr2; - evmc::bytes32 slot1; - evmc_bytes32 slot2; - Address addr3(std::string("0x1234567890123456789012345678901234567890"), false); - Hash slot3(Hex::toBytes("aaaaaaaabbbbbbbbccccccccddddddddeeeeeeeeffffffff0000000099999999")); - - // TODO: replace this with one of the std::ranges alternatives after ContractHost refactor is merged: - // std::ranges::fill(addr, 0xFF); // <--- preferred - // std::ranges::fill(addr.bytes, 0xFF); - // std::fill(addr.bytes, addr.bytes + sizeof(addr.bytes), 0xFF); - for (int i = 0; i < 20; i++) { addr1.bytes[i] = 0xAA; addr2.bytes[i] = 0xFF; } - for (int i = 0; i < 32; i++) { slot1.bytes[i] = 0xAA; slot2.bytes[i] = 0xFF; } - - StorageKey key1(addr1, slot1); - StorageKey key2(addr2, slot2); - StorageKey key3(addr2, slot1); - StorageKey key4(addr1, slot2); - StorageKey key5(addr3, slot3); - - REQUIRE_THAT(Hex::fromBytes(key1.asBytes()).get(), Equals("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); - REQUIRE_THAT(Hex::fromBytes(key2.asBytes()).get(), Equals("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); - REQUIRE_THAT(Hex::fromBytes(key3.asBytes()).get(), Equals("ffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); - REQUIRE_THAT(Hex::fromBytes(key4.asBytes()).get(), Equals("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); - REQUIRE_THAT(Hex::fromBytes(key5.asBytes()).get(), Equals("1234567890123456789012345678901234567890aaaaaaaabbbbbbbbccccccccddddddddeeeeeeeeffffffff0000000099999999")); - - // Testing throw for coverage - FixedBytes<5> keyWrongSize({0x00, 0x00, 0x00, 0x00, 0x00}); - REQUIRE_THROWS(StorageKey(keyWrongSize.view())); - } - } -} - diff --git a/tests/utils/utils.cpp b/tests/utils/utils.cpp index 8d88a73e..9ae24166 100644 --- a/tests/utils/utils.cpp +++ b/tests/utils/utils.cpp @@ -106,8 +106,8 @@ namespace TUtils { SECTION("create_view_span") { Bytes b{0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; std::string_view sv("abcdef"); - bytes::View v1 = Utils::create_view_span(b, 0, 6); - bytes::View v2 = Utils::create_view_span(sv, 0, 6); + View v1 = Utils::create_view_span(b, 0, 6); + View v2 = Utils::create_view_span(sv, 0, 6); REQUIRE(Hex::fromBytes(v1).get() == "0a0b0c0d0e0f"); REQUIRE(Hex::fromBytes(v2).get() == "616263646566"); REQUIRE_THROWS(Utils::create_view_span(b, 0, 12)); From 3e1c265f0ca978125a6924c00231ebb07b652d8c Mon Sep 17 00:00:00 2001 From: Leonardo Amaral Date: Wed, 15 Jan 2025 14:56:45 -0300 Subject: [PATCH 27/37] removing unused files --- src/contract/cpp/callexecutor.cpp | 72 --------- src/contract/cpp/callexecutor.h | 110 ------------- src/contract/cpp/message.h | 43 ------ src/contract/evm/anycallhandler.h | 26 ---- src/contract/evm/callexecutor.cpp | 249 ------------------------------ src/contract/evm/callexecutor.h | 87 ----------- src/contract/evm/message.h | 23 --- 7 files changed, 610 deletions(-) delete mode 100644 src/contract/cpp/callexecutor.cpp delete mode 100644 src/contract/cpp/callexecutor.h delete mode 100644 src/contract/cpp/message.h delete mode 100644 src/contract/evm/anycallhandler.h delete mode 100644 src/contract/evm/callexecutor.cpp delete mode 100644 src/contract/evm/callexecutor.h delete mode 100644 src/contract/evm/message.h diff --git a/src/contract/cpp/callexecutor.cpp b/src/contract/cpp/callexecutor.cpp deleted file mode 100644 index 3e25894b..00000000 --- a/src/contract/cpp/callexecutor.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "callexecutor.h" -#include "../contracthost.h" - -namespace cpp { - -GasGuard::GasGuard(ContractHost& host) - : host_(host), prevGas_(host.getGas()) {} - -GasGuard::~GasGuard() { - host_.setGas(prevGas_); -} - -Bytes CallExecutor::executeCall(kind::Normal, Gas& gas, const evm::Message& msg) { - const evmc_message evmcMsg{ - .kind = EVMC_CALL, - .flags = 0, - .depth = msg.depth, - .gas = int64_t(gas), - .recipient = bytes::cast(msg.to), - .sender = bytes::cast(msg.from), - .input_data = msg.input.data(), - .input_size = msg.input.size() - }; - - const GasGuard gasContextGuard = setGasContext(gas); - - return prepareCall(gas, msg).evmEthCall(evmcMsg, &host_); -} - -Bytes CallExecutor::executeCall(kind::Static, Gas& gas, const evm::Message& msg) { - const evmc_message evmcMsg{ - .kind = EVMC_CALL, - .flags = EVMC_STATIC, - .depth = msg.depth, - .gas = int64_t(gas), - .recipient = bytes::cast(msg.to), - .sender = bytes::cast(msg.from), - .input_data = msg.input.data(), - .input_size = msg.input.size() - }; - - // TODO: construct evmc_message only after call is prepared, so gas will be correct - - const GasGuard gasContextGuard = setGasContext(gas); - - return prepareCall(gas, msg).ethCallView(evmcMsg, &host_); -} - -BaseContract& CallExecutor::prepareCall(Gas& gas, const evm::Message& msg) { - gas.use(1000); - - const auto it = contracts_.find(msg.to); - - if (it == contracts_.end()) { - throw ExecutionFailure("Contract not found"); // TODO: proper error + more info - } - - auto& contract = *it->second; - - contract.caller_ = msg.from; - contract.value_ = msg.value; - - return contract; -} - -GasGuard CallExecutor::setGasContext(Gas& gas) { - GasGuard guard(host_); - host_.setGas(gas); - return guard; -} - -} // namespace cpp diff --git a/src/contract/cpp/callexecutor.h b/src/contract/cpp/callexecutor.h deleted file mode 100644 index eedc1b79..00000000 --- a/src/contract/cpp/callexecutor.h +++ /dev/null @@ -1,110 +0,0 @@ -#pragma once - -#include "message.h" -#include "../gas.h" -#include "../traits/method.h" -#include "../evm/message.h" -// #include "../dynamiccontract.h" - -struct ContractHost; - -namespace cpp { - -class NestedCallSafeGuard { -public: - NestedCallSafeGuard(const ContractLocals* contract, const Address& caller, const uint256_t& value) : - contract_(contract), caller_(contract->caller_), value_(contract->value_) { - } - - ~NestedCallSafeGuard() { - contract_->caller_ = caller_; - contract_->value_ = value_; - } -private: - const ContractLocals* contract_; - Address caller_; - uint256_t value_; -}; - -class GasGuard { -public: - explicit GasGuard(ContractHost& host); - - ~GasGuard(); - -private: - ContractHost& host_; - Gas& prevGas_; -}; - -class CallExecutor { -public: - using Contracts = boost::unordered_flat_map, SafeHash, SafeCompare>&; - - CallExecutor(ContractHost& host, Contracts& contracts) - : host_(host), contracts_(contracts) {} - - template - decltype(auto) executeCall(kind::Delegate, Gas& gas, Message msg) { - throw ExecutionFailure("Delegate call not supported for C++ contracts"); - } - - Bytes executeCall(kind::Delegate, Gas& gas, const evm::Message& msg) { - throw ExecutionFailure("Delegate call not supported for C++ contracts"); - } - - template - decltype(auto) executeCall(auto callKind, Gas& gas, Message msg) { - gas.use(1000); - - NestedCallSafeGuard guard(msg.caller, msg.caller->caller_, msg.caller->value_); - - auto& contract = getContract(msg.to); - - const Utils::Overloaded invokeContractFunction{ - [&] (kind::Static, auto&&... args) { - return std::invoke(msg.method.func, contract, std::forward(args)...); - }, - [&] (kind::Normal, auto&&... args) { - return contract.callContractFunction(&host_, msg.method.func, std::forward(args)...); - } - }; - - const GasGuard gasContextGuard = setGasContext(gas); - - return std::apply([&] (auto&&... args) { - return invokeContractFunction(callKind, std::forward(args)...); - }, std::move(msg.method.args)); - } - - Bytes executeCall(kind::Static, Gas& gas, const evm::Message& msg); - - Bytes executeCall(kind::Normal, Gas& gas, const evm::Message& msg); - -private: - BaseContract& prepareCall(Gas& gas, const evm::Message& msg); - - GasGuard setGasContext(Gas& gas); - - template - C& getContract(const Address& address) { - const auto it = contracts_.find(address); - - if (it == contracts_.end()) { - throw ExecutionFailure("Contract not found"); // TODO: add more error info - } - - C* ptr = dynamic_cast(it->second.get()); - - if (ptr == nullptr) { - throw ExecutionFailure("Contract is not of the requested type"); // TODO: add more error info - } - - return *ptr; - } - - ContractHost& host_; - Contracts& contracts_; -}; - -} // namespace cpp diff --git a/src/contract/cpp/message.h b/src/contract/cpp/message.h deleted file mode 100644 index 02f82a84..00000000 --- a/src/contract/cpp/message.h +++ /dev/null @@ -1,43 +0,0 @@ - -#pragma once - -#include "../traits/method.h" -#include "../contract.h" -#include "../../utils/strings.h" - -namespace cpp { - -template -struct PackagedMethod { - using ClassType = traits::Method::ClassType; - using ReturnType = traits::Method::ReturnType; - static constexpr bool IsView = traits::Method::IsView; - - explicit PackagedMethod(M func, Args&&... args) - : func(std::move(func)), args(std::forward(args)...) {} - - M func; - std::tuple args; -}; - -template -explicit PackagedMethod(M, Ts&&...) -> PackagedMethod; - -template -struct Message { - Address from; - Address to; - uint256_t value; - uint32_t depth; - const BaseContract *caller; - MethodType method; -}; - -template -struct CreateMessage { - const BaseContract *caller; - Address from; - std::tuple args; -}; - -} // namespace cpp diff --git a/src/contract/evm/anycallhandler.h b/src/contract/evm/anycallhandler.h deleted file mode 100644 index c356a9d6..00000000 --- a/src/contract/evm/anycallhandler.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "../gas.h" -#include "message.h" - -namespace evm { - -class AnyCallHandler { -public: - template - AnyCallHandler(T& callHandler) - : object_(&callHandler), - func_([] (void *object, kind::Any callKind, Gas& gas, const Message& msg) { - return std::visit([&] (auto callKind) { return static_cast(object)->onCall(callKind, gas, msg); }, callKind); - }) {} - - Bytes onCall(kind::Any callKind, Gas& gas, const Message& msg) { - return std::invoke(func_, object_, callKind, gas, msg); - } - -private: - void *object_; - Bytes (*func_)(void*, kind::Any, Gas&, const Message&); -}; - -} // namespace evm diff --git a/src/contract/evm/callexecutor.cpp b/src/contract/evm/callexecutor.cpp deleted file mode 100644 index d603e799..00000000 --- a/src/contract/evm/callexecutor.cpp +++ /dev/null @@ -1,249 +0,0 @@ -#include "callexecutor.h" -#include "bytes/cast.h" - -template -using map_value_t = decltype(std::declval>().second); - -template -using map_value_reference_t = std::add_lvalue_reference_t< - std::conditional_t< - std::is_const_v>, - std::add_const_t>, - map_value_t>>; - -template -constexpr std::optional>>> get(M&& map, const auto& key) noexcept { - const auto it = map.find(key); - - if (it == map.end()) { - return std::nullopt; - } - - return it->second; -} - -constexpr evmc_call_kind getCallKind(kind::Any callKind) noexcept { - return std::visit(Utils::Overloaded{ - [] (kind::Delegate) { return EVMC_DELEGATECALL; }, - [] (kind::Normal) { return EVMC_CALL; }, - [] (kind::Static) { return EVMC_CALL; } - }, callKind); -} - -constexpr uint32_t getCallFlags(kind::Any callKind) noexcept { - return std::visit(Utils::Overloaded{ - [] (kind::Delegate) -> uint32_t { return 0; }, - [] (kind::Normal) -> uint32_t { return 0; }, - [] (kind::Static) -> uint32_t { return EVMC_STATIC; } - }, callKind); -} - -namespace evm { - -Bytes CallExecutor::executeCall(kind::Any callKind, Gas& gas, const Message& msg, View code) { - const evmc_message evmcMsg{ - .kind = getCallKind(callKind), - .flags = getCallFlags(callKind), - .depth = msg.depth, - .gas = int64_t(gas), - .recipient = bytes::cast(msg.to), - .sender = bytes::cast(msg.from), - .input_data = msg.input.data(), - .input_size = msg.input.size(), - .value = EVMCConv::uint256ToEvmcUint256(msg.value), - .create2_salt = {}, - .code_address = {} - }; - - evmc::Result result(::evmc_execute(this->vm_, - &this->get_interface(), - this->to_context(), - evmc_revision::EVMC_LATEST_STABLE_REVISION, - &evmcMsg, - code.data(), - code.size())); - - gas.use(evmcMsg.gas - result.gas_left); - - View output(result.output_data, result.output_size); - - switch (result.status_code) { - case EVMC_SUCCESS: - return Utils::makeBytes(output); - - case EVMC_REVERT: - throw ExecutionReverted("TODO"); - - case EVMC_OUT_OF_GAS: - throw OutOfGas(); - - default: - throw ExecutionFailure("TODO"); - } -} - -bool CallExecutor::account_exists(const evmc::address& addr) const noexcept { - return accounts_.find(addr) != accounts_.end(); -} - -evmc::bytes32 CallExecutor::get_storage(const evmc::address& addr, const evmc::bytes32& key) const noexcept { - return get(vmStorage_, StorageKey(addr, key)) - .transform([] (const Hash& hash) { return bytes::cast(hash); }) - .value_or(evmc::bytes32{}); -} - -evmc_storage_status CallExecutor::set_storage(const evmc::address& addr, const evmc::bytes32& key, const evmc::bytes32& value) noexcept { - const StorageKey storageKey(addr, key); - auto& storageValue = vmStorage_[storageKey]; - stack_.registerStorageChange(storageKey, storageValue); - storageValue = Hash(value); - return EVMC_STORAGE_MODIFIED; -} - -evmc::uint256be CallExecutor::get_balance(const evmc::address& addr) const noexcept { - return get(accounts_, addr) - .transform([] (const auto& account) { return EVMCConv::uint256ToEvmcUint256(account.get()->balance); }) - .value_or(evmc::uint256be{}); -} - -size_t CallExecutor::get_code_size(const evmc::address& addr) const noexcept { - return get(accounts_, addr) - .transform([] (const auto& account) { return account.get()->code.size(); }) - .value_or(0); -} - -evmc::bytes32 CallExecutor::get_code_hash(const evmc::address& addr) const noexcept { - return get(accounts_, addr) - .transform([] (const auto& account) { return bytes::cast(account.get()->codeHash); }) - .value_or(evmc::bytes32{}); -} - -size_t CallExecutor::copy_code(const evmc::address& addr, size_t code_offset, uint8_t* buffer_data, size_t buffer_size) const noexcept { - const auto thenCopyCode = [&] (const auto& account) -> size_t { - View code = account.get()->code; - - if (code_offset < code.size()) { - const size_t n = std::min(buffer_size, code.size() - code_offset); - if (n > 0) - std::copy_n(&code[code_offset], n, buffer_data); - return n; - } - - return 0; - }; - - return get(accounts_, addr) - .transform(thenCopyCode) - .value_or(0); -} - -bool CallExecutor::selfdestruct(const evmc::address& addr, const evmc::address& beneficiary) noexcept { - return false; -} - -evmc_tx_context CallExecutor::get_tx_context() const noexcept { - return currentTxContext_; -} - -evmc::bytes32 CallExecutor::get_block_hash(int64_t number) const noexcept { - return EVMCConv::uint256ToEvmcUint256(number); -} - -void CallExecutor::emit_log(const evmc::address& addr, const uint8_t* data, size_t data_size, const evmc::bytes32 topics[], size_t topics_count) noexcept { - try { - std::vector topics_; - for (uint64_t i = 0; i < topics_count; i++) { - topics_.emplace_back(topics[i]); - } - Event event("", // EVM events do not have names - this->eventIndex_, - this->txHash_, - this->txIndex_, - this->blockHash_, - this->currentTxContext_.block_number, - addr, - Bytes(data, data + data_size), - topics_, - (topics_count == 0) - ); - ++this->eventIndex_; - this->stack_.registerEvent(std::move(event)); - } catch (const std::exception& ignored) {} -} - -evmc_access_status CallExecutor::access_account(const evmc::address& addr) noexcept { - return EVMC_ACCESS_WARM; -} - -evmc_access_status CallExecutor::access_storage(const evmc::address& addr, const evmc::bytes32& key) noexcept { - return EVMC_ACCESS_WARM; -} - -evmc::bytes32 CallExecutor::get_transient_storage(const evmc::address &addr, const evmc::bytes32 &key) const noexcept { - return get(transientStorage_, StorageKey(addr, key)) - .transform([] (const Hash& hash) { return bytes::cast(hash); }) - .value_or(evmc::bytes32{}); -} - -void CallExecutor::set_transient_storage(const evmc::address &addr, const evmc::bytes32 &key, const evmc::bytes32 &value) noexcept { - transientStorage_.emplace(StorageKey(addr, key), value); -} - -evmc::Result CallExecutor::call(const evmc_message& msg) noexcept { - Message callMsg; - CreateMessage createMsg; - evmc_status_code status; - std::variant result = Bytes(); - kind::Any callKind; - - Gas gas(msg.gas); - - if (msg.kind == EVMC_DELEGATECALL) { - callKind = kind::DELEGATE; - } else if (msg.flags == EVMC_STATIC) { - callKind = kind::STATIC; - } else { - callKind = kind::NORMAL; - } - - try { - switch (msg.kind) { - case EVMC_CALL: - case EVMC_DELEGATECALL: - callMsg.from = Address(msg.sender); - callMsg.to = Address(msg.recipient); - callMsg.value = EVMCConv::evmcUint256ToUint256(msg.value); - callMsg.depth = msg.depth; - callMsg.input = View(msg.input_data, msg.input_size); - result = callHandler_.onCall(callKind, gas, callMsg); - break; - - case EVMC_CREATE2: - [[fallthrough]]; - - case EVMC_CREATE: - [[fallthrough]]; - - case EVMC_CALLCODE: - assert(false); // TODO - } - - status = EVMC_SUCCESS; - - } catch (const OutOfGas&) { - status = EVMC_OUT_OF_GAS; - } catch (const ExecutionReverted&) { - // TODO: encode error if exists - status = EVMC_REVERT; - } catch (const std::exception&) { - // TODO: encode error if exists - status = EVMC_FAILURE; - } - - return std::visit(Utils::Overloaded{ - [&] (Bytes output) { return evmc::Result(status, int64_t(gas), 0, output.data(), output.size()); }, - [&] (const Address& createAddress) { return evmc::Result(status, int64_t(gas), 0, createAddress.toEvmcAddress()); }, - }, std::move(result)); -} - -} // namespace evm diff --git a/src/contract/evm/callexecutor.h b/src/contract/evm/callexecutor.h deleted file mode 100644 index 04d6dff9..00000000 --- a/src/contract/evm/callexecutor.h +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once - -#include "message.h" -#include "anycallhandler.h" -#include "../gas.h" -#include "../traits/method.h" -#include "../contractstack.h" -#include "../cpp/message.h" -#include "../../utils/contractreflectioninterface.h" - -namespace evm { - -class CallExecutor : public evmc::Host { -public: - using VmStorage = boost::unordered_flat_map; - using Accounts = boost::unordered_flat_map, SafeHash, SafeCompare>; - - CallExecutor(AnyCallHandler callHandler, evmc_vm* vm, VmStorage& vmStorage, Accounts& accounts, ContractStack& stack, const Hash& txHash, uint64_t txIndex, const Hash& blockHash, const evmc_tx_context& currentTxContext) - : callHandler_(std::move(callHandler)), vm_(vm), vmStorage_(vmStorage), accounts_(accounts), stack_(stack), txHash_(txHash), txIndex_(txIndex), blockHash_(blockHash), currentTxContext_(currentTxContext) {} - - Bytes executeCall(kind::Any callKind, Gas& gas, const Message& msg, View code); - - template - M::ReturnType executeCall(auto callKind, Gas& gas, cpp::Message msg, View code) { - const Bytes input = std::apply([&] (const Args&... args) { - const std::string functionName = ContractReflectionInterface::getFunctionName(msg.method.func); - - if (functionName.empty()) { - throw DynamicException("EVM contract function name is empty (contract not registered?)"); - } - - Bytes res = Utils::makeBytes(UintConv::uint32ToBytes(ABI::FunctorEncoder::encode(functionName).value)); - - if constexpr (sizeof...(Args) > 0) { - Utils::appendBytes(res, ABI::Encoder::encodeData(args...)); - } - - return res; - }, msg.method.args); - - const Message newMsg { - .from = msg.from, - .to = msg.to, - .value = msg.value, - .depth = msg.depth, - .input = input - }; - - const Bytes output = executeCall(callKind, gas, newMsg, code); - - if constexpr (not std::same_as) { - return std::get<0>(ABI::Decoder::decodeData(output)); - } - } - - bool account_exists(const evmc::address& addr) const noexcept override final; - evmc::bytes32 get_storage(const evmc::address& addr, const evmc::bytes32& key) const noexcept override final; - evmc_storage_status set_storage(const evmc::address& addr, const evmc::bytes32& key, const evmc::bytes32& value) noexcept override final; - evmc::uint256be get_balance(const evmc::address& addr) const noexcept override final; - size_t get_code_size(const evmc::address& addr) const noexcept override final; - evmc::bytes32 get_code_hash(const evmc::address& addr) const noexcept override final; - size_t copy_code(const evmc::address& addr, size_t code_offset, uint8_t* buffer_data, size_t buffer_size) const noexcept override final; - bool selfdestruct(const evmc::address& addr, const evmc::address& beneficiary) noexcept override final; - evmc_tx_context get_tx_context() const noexcept override final; - evmc::bytes32 get_block_hash(int64_t number) const noexcept override final; - void emit_log(const evmc::address& addr, const uint8_t* data, size_t data_size, const evmc::bytes32 topics[], size_t topics_count) noexcept override final; - evmc_access_status access_account(const evmc::address& addr) noexcept override final; - evmc_access_status access_storage(const evmc::address& addr, const evmc::bytes32& key) noexcept override final; - evmc::bytes32 get_transient_storage(const evmc::address &addr, const evmc::bytes32 &key) const noexcept override final; - void set_transient_storage(const evmc::address &addr, const evmc::bytes32 &key, const evmc::bytes32 &value) noexcept override final; - evmc::Result call(const evmc_message& msg) noexcept override final; - -private: - AnyCallHandler callHandler_; - evmc_vm* vm_; - VmStorage& vmStorage_; - Accounts& accounts_; - boost::unordered_flat_map transientStorage_; - ContractStack& stack_; - uint64_t eventIndex_ = 0; - const Hash& txHash_; - const uint64_t txIndex_; - const Hash& blockHash_; - const evmc_tx_context& currentTxContext_; -}; - -} // namespace evm diff --git a/src/contract/evm/message.h b/src/contract/evm/message.h deleted file mode 100644 index 0f4ef824..00000000 --- a/src/contract/evm/message.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "../../utils/strings.h" - -namespace evm { - -struct Message { - Address from; - Address to; - uint256_t value; - uint32_t depth; - View input; -}; - -struct CreateMessage { - Address from; - uint256_t value; - uint32_t depth; - View code; - std::optional salt; -}; - -} // namespace evm From 929debddae7581a8dbd43fb574c8bc33f53d2bde Mon Sep 17 00:00:00 2001 From: Leonardo Amaral Date: Wed, 15 Jan 2025 15:22:41 -0300 Subject: [PATCH 28/37] small style and warnings adjustments --- .../messages/anyencodedmessagehandler.h | 2 +- src/contract/messages/common.h | 5 ++++ src/contract/messages/evmcontractexecutor.cpp | 17 ++++++------- src/contract/messages/evmcontractexecutor.h | 25 +++---------------- src/contract/messages/messagedispatcher.h | 4 +-- 5 files changed, 20 insertions(+), 33 deletions(-) diff --git a/src/contract/messages/anyencodedmessagehandler.h b/src/contract/messages/anyencodedmessagehandler.h index 2f3aca77..32c7cdb2 100644 --- a/src/contract/messages/anyencodedmessagehandler.h +++ b/src/contract/messages/anyencodedmessagehandler.h @@ -44,7 +44,7 @@ template AnyEncodedMessageHandler makeEncodedMessageHandler(MessageHandler& handler) { AnyEncodedMessageHandler res; - static const auto lambda = [] (void *obj, auto& msg) { return static_cast(obj)->onMessage(msg); }; // TODO: the static may be removed + const auto lambda = [] (void *obj, auto& msg) { return static_cast(obj)->onMessage(msg); }; res.handler_ = &handler; res.onCreate_ = static_cast(lambda); diff --git a/src/contract/messages/common.h b/src/contract/messages/common.h index f981b7d7..68ce0241 100644 --- a/src/contract/messages/common.h +++ b/src/contract/messages/common.h @@ -48,6 +48,11 @@ Bytes messageInputEncoded(const concepts::EncodedMessage auto& msg) { Bytes messageInputEncoded(const concepts::PackedMessage auto& msg) { return std::apply([&] (const auto&... args) -> Bytes { const std::string functionName = ContractReflectionInterface::getFunctionName(msg.method()); + + if (functionName.empty()) { + throw DynamicException("Contract fuction not found (contract not registered?)"); + } + const BytesArr<4> encodedFunctor = UintConv::uint32ToBytes(ABI::FunctorEncoder::encode(std::string(functionName)).value); if constexpr (sizeof...(args) > 0) { diff --git a/src/contract/messages/evmcontractexecutor.cpp b/src/contract/messages/evmcontractexecutor.cpp index 3ff26de0..7b21a63f 100644 --- a/src/contract/messages/evmcontractexecutor.cpp +++ b/src/contract/messages/evmcontractexecutor.cpp @@ -35,7 +35,7 @@ constexpr evmc_call_kind getEvmcKind(const M& msg) { return EVMC_CALL; } - // TODO: CALL CODE! + std::unreachable(); } template @@ -52,7 +52,7 @@ constexpr evmc_message makeEvmcMessage(const M& msg, uint64_t depth) { return evmc_message{ .kind = getEvmcKind(msg), .flags = getEvmcFlags(msg), - .depth = depth, + .depth = int32_t(depth), .gas = int64_t(msg.gas()), .recipient = bytes::cast(messageRecipientOrDefault(msg)), .sender = bytes::cast(msg.from()), @@ -69,7 +69,7 @@ constexpr evmc_message makeEvmcMessage(const M& msg, uint64_t depth, View(contractAddress), .sender = bytes::cast(msg.from()), @@ -77,7 +77,7 @@ constexpr evmc_message makeEvmcMessage(const M& msg, uint64_t depth, View(messageSaltOrDefault(msg)), - .code_address = evmc_address{} // TODO: CALL CODE? + .code_address = evmc_address{} }; } @@ -220,7 +220,9 @@ size_t EvmContractExecutor::copy_code(const evmc::address& addr, size_t code_off return n; } - } catch (const std::exception&) {} + } catch (const std::exception&) { + // TODO: makes sense to just ignore? + } return 0; } @@ -335,10 +337,7 @@ evmc::Result EvmContractExecutor::call(const evmc_message& msg) noexcept { } else if (msg.kind == EVMC_CREATE2) { EncodedSaltCreateMessage encodedMessage(msg.sender, gas, value, View(msg.input_data, msg.input_size), msg.create2_salt); return process(encodedMessage); - } else if (msg.kind == EVMC_CALLCODE) { - return evmc::Result{}; // TODO: CALL CODE!!! } - // TODO: proper error result with proper reason (encoded) - return evmc::Result{}; + std::unreachable(); } diff --git a/src/contract/messages/evmcontractexecutor.h b/src/contract/messages/evmcontractexecutor.h index 164d0c10..2c8b0fff 100644 --- a/src/contract/messages/evmcontractexecutor.h +++ b/src/contract/messages/evmcontractexecutor.h @@ -29,34 +29,17 @@ class EvmContractExecutor : public evmc::Host { template requires concepts::CallMessage auto execute(M&& msg) -> traits::MessageResult { - // TODO: can this apply be a free common function for encoding not encoded messages? - // TODO: what about the whole process of converting a packed msg to a encoded msg? - const Bytes encodedInput = std::apply([&] (const Args&... args) { - const std::string functionName = ContractReflectionInterface::getFunctionName(msg.method()); - - if (functionName.empty()) { - throw DynamicException("EVM contract function name is empty (contract not registered?)"); - } - - Bytes res = Utils::makeBytes(UintConv::uint32ToBytes(ABI::FunctorEncoder::encode(functionName).value)); - - if constexpr (sizeof...(Args) > 0) { - Utils::appendBytes(res, ABI::Encoder::encodeData(args...)); - } - - return res; - }, msg.args()); - + const Bytes input = messageInputEncoded(msg); Bytes output; if constexpr (concepts::StaticCallMessage) { - EncodedStaticCallMessage encodedMessage(msg.from(), msg.to(), msg.gas(), encodedInput); + EncodedStaticCallMessage encodedMessage(msg.from(), msg.to(), msg.gas(), input); output = this->execute(encodedMessage); } else if constexpr (concepts::DelegateCallMessage) { - EncodedDelegateCallMessage encodedMessage(msg.from(), msg.to(), msg.gas(), msg.value(), encodedInput, msg.codeAddress()); + EncodedDelegateCallMessage encodedMessage(msg.from(), msg.to(), msg.gas(), msg.value(), input, msg.codeAddress()); output = this->execute(encodedMessage); } else { - EncodedCallMessage encodedMessage(msg.from(), msg.to(), msg.gas(), msg.value(), encodedInput); + EncodedCallMessage encodedMessage(msg.from(), msg.to(), msg.gas(), msg.value(), input); output = this->execute(encodedMessage); } diff --git a/src/contract/messages/messagedispatcher.h b/src/contract/messages/messagedispatcher.h index f515eff9..33baa3ca 100644 --- a/src/contract/messages/messagedispatcher.h +++ b/src/contract/messages/messagedispatcher.h @@ -85,11 +85,11 @@ class MessageDispatcher { } } - bool isPayment(const concepts::EncodedMessage auto& msg) { + bool isPayment(const concepts::EncodedMessage auto& msg) const { return msg.input().size() == 0; } - bool isPayment(const concepts::PackedMessage auto& msg) { + bool isPayment(const concepts::PackedMessage auto& msg) const { return false; } From 78a3c4cc7b2da2394985146ac5af30c07ac8d815 Mon Sep 17 00:00:00 2001 From: Leonardo Amaral Date: Wed, 15 Jan 2025 15:31:38 -0300 Subject: [PATCH 29/37] fixing failing unit test --- tests/net/http/httpjsonrpc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/net/http/httpjsonrpc.cpp b/tests/net/http/httpjsonrpc.cpp index 88903569..3ed1d178 100644 --- a/tests/net/http/httpjsonrpc.cpp +++ b/tests/net/http/httpjsonrpc.cpp @@ -303,7 +303,7 @@ namespace THTTPJsonRPC { {"value", "0x1"} }), "latest"})); - REQUIRE(eth_estimateGasResponse["result"] == "0x5e56"); + REQUIRE(eth_estimateGasResponse["result"] == "0x5208"); json eth_gasPriceResponse = requestMethod("eth_gasPrice", json::array()); REQUIRE(eth_gasPriceResponse["result"] == "0x9502f900"); From 6d3b86d28983617c24eaa0fa9e381a78b41a1760 Mon Sep 17 00:00:00 2001 From: Leonardo Amaral Date: Wed, 15 Jan 2025 15:41:00 -0300 Subject: [PATCH 30/37] removed unused stuff --- src/contract/messages/anyencodedmessagehandler.h | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/contract/messages/anyencodedmessagehandler.h b/src/contract/messages/anyencodedmessagehandler.h index 32c7cdb2..aa5f380f 100644 --- a/src/contract/messages/anyencodedmessagehandler.h +++ b/src/contract/messages/anyencodedmessagehandler.h @@ -40,20 +40,4 @@ class AnyEncodedMessageHandler { Bytes (*onDelegateCall_)(void*, EncodedDelegateCallMessage&); }; -template -AnyEncodedMessageHandler makeEncodedMessageHandler(MessageHandler& handler) { - AnyEncodedMessageHandler res; - - const auto lambda = [] (void *obj, auto& msg) { return static_cast(obj)->onMessage(msg); }; - - res.handler_ = &handler; - res.onCreate_ = static_cast(lambda); - res.onSaltCreate_ = static_cast(lambda); - res.onCall_ = static_cast(lambda); - res.onStaticCall_ = static_cast(lambda); - res.onDelegateCall_ = static_cast(lambda); - - return res; -} - #endif // BDK_MESSAGES_ANYENCODEDMESSAGEHANDLER_H From bdf354c37672b12bebe70d0c80cf445962133861 Mon Sep 17 00:00:00 2001 From: Leonardo Amaral Date: Wed, 15 Jan 2025 15:43:27 -0300 Subject: [PATCH 31/37] messages directory removed --- src/contract/{messages => }/anyencodedmessagehandler.h | 0 src/contract/{messages => }/basemessage.h | 0 src/contract/{messages => }/common.cpp | 0 src/contract/{messages => }/common.h | 0 src/contract/{messages => }/concepts.h | 0 src/contract/{messages => }/cppcontractexecutor.h | 0 src/contract/{messages => }/encodedmessages.h | 0 src/contract/{messages => }/evmcontractexecutor.cpp | 0 src/contract/{messages => }/evmcontractexecutor.h | 0 src/contract/{messages => }/executioncontext.cpp | 0 src/contract/{messages => }/executioncontext.h | 0 src/contract/{messages => }/executionreverted.h | 0 src/contract/{messages => }/gas.h | 0 src/contract/{messages => }/messagedispatcher.h | 0 src/contract/{messages => }/outofgas.h | 0 src/contract/{messages => }/packedmessages.h | 0 src/contract/{messages => }/precompiledcontractexecutor.cpp | 0 src/contract/{messages => }/precompiledcontractexecutor.h | 0 src/contract/{messages => }/traits.h | 0 19 files changed, 0 insertions(+), 0 deletions(-) rename src/contract/{messages => }/anyencodedmessagehandler.h (100%) rename src/contract/{messages => }/basemessage.h (100%) rename src/contract/{messages => }/common.cpp (100%) rename src/contract/{messages => }/common.h (100%) rename src/contract/{messages => }/concepts.h (100%) rename src/contract/{messages => }/cppcontractexecutor.h (100%) rename src/contract/{messages => }/encodedmessages.h (100%) rename src/contract/{messages => }/evmcontractexecutor.cpp (100%) rename src/contract/{messages => }/evmcontractexecutor.h (100%) rename src/contract/{messages => }/executioncontext.cpp (100%) rename src/contract/{messages => }/executioncontext.h (100%) rename src/contract/{messages => }/executionreverted.h (100%) rename src/contract/{messages => }/gas.h (100%) rename src/contract/{messages => }/messagedispatcher.h (100%) rename src/contract/{messages => }/outofgas.h (100%) rename src/contract/{messages => }/packedmessages.h (100%) rename src/contract/{messages => }/precompiledcontractexecutor.cpp (100%) rename src/contract/{messages => }/precompiledcontractexecutor.h (100%) rename src/contract/{messages => }/traits.h (100%) diff --git a/src/contract/messages/anyencodedmessagehandler.h b/src/contract/anyencodedmessagehandler.h similarity index 100% rename from src/contract/messages/anyencodedmessagehandler.h rename to src/contract/anyencodedmessagehandler.h diff --git a/src/contract/messages/basemessage.h b/src/contract/basemessage.h similarity index 100% rename from src/contract/messages/basemessage.h rename to src/contract/basemessage.h diff --git a/src/contract/messages/common.cpp b/src/contract/common.cpp similarity index 100% rename from src/contract/messages/common.cpp rename to src/contract/common.cpp diff --git a/src/contract/messages/common.h b/src/contract/common.h similarity index 100% rename from src/contract/messages/common.h rename to src/contract/common.h diff --git a/src/contract/messages/concepts.h b/src/contract/concepts.h similarity index 100% rename from src/contract/messages/concepts.h rename to src/contract/concepts.h diff --git a/src/contract/messages/cppcontractexecutor.h b/src/contract/cppcontractexecutor.h similarity index 100% rename from src/contract/messages/cppcontractexecutor.h rename to src/contract/cppcontractexecutor.h diff --git a/src/contract/messages/encodedmessages.h b/src/contract/encodedmessages.h similarity index 100% rename from src/contract/messages/encodedmessages.h rename to src/contract/encodedmessages.h diff --git a/src/contract/messages/evmcontractexecutor.cpp b/src/contract/evmcontractexecutor.cpp similarity index 100% rename from src/contract/messages/evmcontractexecutor.cpp rename to src/contract/evmcontractexecutor.cpp diff --git a/src/contract/messages/evmcontractexecutor.h b/src/contract/evmcontractexecutor.h similarity index 100% rename from src/contract/messages/evmcontractexecutor.h rename to src/contract/evmcontractexecutor.h diff --git a/src/contract/messages/executioncontext.cpp b/src/contract/executioncontext.cpp similarity index 100% rename from src/contract/messages/executioncontext.cpp rename to src/contract/executioncontext.cpp diff --git a/src/contract/messages/executioncontext.h b/src/contract/executioncontext.h similarity index 100% rename from src/contract/messages/executioncontext.h rename to src/contract/executioncontext.h diff --git a/src/contract/messages/executionreverted.h b/src/contract/executionreverted.h similarity index 100% rename from src/contract/messages/executionreverted.h rename to src/contract/executionreverted.h diff --git a/src/contract/messages/gas.h b/src/contract/gas.h similarity index 100% rename from src/contract/messages/gas.h rename to src/contract/gas.h diff --git a/src/contract/messages/messagedispatcher.h b/src/contract/messagedispatcher.h similarity index 100% rename from src/contract/messages/messagedispatcher.h rename to src/contract/messagedispatcher.h diff --git a/src/contract/messages/outofgas.h b/src/contract/outofgas.h similarity index 100% rename from src/contract/messages/outofgas.h rename to src/contract/outofgas.h diff --git a/src/contract/messages/packedmessages.h b/src/contract/packedmessages.h similarity index 100% rename from src/contract/messages/packedmessages.h rename to src/contract/packedmessages.h diff --git a/src/contract/messages/precompiledcontractexecutor.cpp b/src/contract/precompiledcontractexecutor.cpp similarity index 100% rename from src/contract/messages/precompiledcontractexecutor.cpp rename to src/contract/precompiledcontractexecutor.cpp diff --git a/src/contract/messages/precompiledcontractexecutor.h b/src/contract/precompiledcontractexecutor.h similarity index 100% rename from src/contract/messages/precompiledcontractexecutor.h rename to src/contract/precompiledcontractexecutor.h diff --git a/src/contract/messages/traits.h b/src/contract/traits.h similarity index 100% rename from src/contract/messages/traits.h rename to src/contract/traits.h From 88cbb72b2027c88021e2c15ce4eb879dcc3941a7 Mon Sep 17 00:00:00 2001 From: Leonardo Amaral Date: Wed, 15 Jan 2025 15:57:03 -0300 Subject: [PATCH 32/37] fixed include names --- src/contract/CMakeLists.txt | 8 ++++---- src/contract/calltracer.h | 6 +++--- src/contract/contracthost.h | 10 +++++----- src/contract/encodedmessages.h | 4 ++-- src/contract/packedmessages.h | 4 ++-- src/contract/precompiledcontractexecutor.h | 2 +- src/contract/trace/calltype.h | 2 +- src/utils/tx.h | 2 +- tests/CMakeLists.txt | 4 ++-- tests/contract/{messages => }/evmcontractexecutor.cpp | 2 +- tests/contract/{messages => }/executioncontext.cpp | 2 +- 11 files changed, 23 insertions(+), 23 deletions(-) rename tests/contract/{messages => }/evmcontractexecutor.cpp (99%) rename tests/contract/{messages => }/executioncontext.cpp (99%) diff --git a/src/contract/CMakeLists.txt b/src/contract/CMakeLists.txt index 4251e5e3..57a5971f 100644 --- a/src/contract/CMakeLists.txt +++ b/src/contract/CMakeLists.txt @@ -54,10 +54,10 @@ set(CONTRACT_SOURCES ${CMAKE_SOURCE_DIR}/src/contract/dynamiccontract.cpp ${CMAKE_SOURCE_DIR}/src/contract/trace/call.cpp ${CMAKE_SOURCE_DIR}/src/contract/event.cpp - ${CMAKE_SOURCE_DIR}/src/contract/messages/common.cpp - ${CMAKE_SOURCE_DIR}/src/contract/messages/executioncontext.cpp - ${CMAKE_SOURCE_DIR}/src/contract/messages/evmcontractexecutor.cpp - ${CMAKE_SOURCE_DIR}/src/contract/messages/precompiledcontractexecutor.cpp + ${CMAKE_SOURCE_DIR}/src/contract/common.cpp + ${CMAKE_SOURCE_DIR}/src/contract/executioncontext.cpp + ${CMAKE_SOURCE_DIR}/src/contract/evmcontractexecutor.cpp + ${CMAKE_SOURCE_DIR}/src/contract/precompiledcontractexecutor.cpp ${CMAKE_SOURCE_DIR}/src/contract/templates/ownable.cpp ${CMAKE_SOURCE_DIR}/src/contract/templates/erc20.cpp ${CMAKE_SOURCE_DIR}/src/contract/templates/erc721.cpp diff --git a/src/contract/calltracer.h b/src/contract/calltracer.h index 38cdaba4..dda7f843 100644 --- a/src/contract/calltracer.h +++ b/src/contract/calltracer.h @@ -10,10 +10,10 @@ See the LICENSE.txt file in the project root for more information. #include "utils/utils.h" #include "utils/options.h" -#include "contract/messages/concepts.h" -#include "contract/messages/outofgas.h" +#include "contract/concepts.h" +#include "contract/outofgas.h" #include "contract/trace/call.h" -#include "contract/messages/traits.h" +#include "contract/traits.h" #include "contract/abi.h" template diff --git a/src/contract/contracthost.h b/src/contract/contracthost.h index eb308b49..bfb82039 100644 --- a/src/contract/contracthost.h +++ b/src/contract/contracthost.h @@ -9,11 +9,11 @@ #include "calltracer.h" #include "bytes/join.h" #include "bytes/cast.h" -#include "messages/gas.h" -#include "messages/concepts.h" -#include "messages/executioncontext.h" -#include "messages/messagedispatcher.h" -#include "messages/packedmessages.h" +#include "gas.h" +#include "concepts.h" +#include "executioncontext.h" +#include "messagedispatcher.h" +#include "packedmessages.h" #include "calltracer.h" #include "costs.h" diff --git a/src/contract/encodedmessages.h b/src/contract/encodedmessages.h index bdc519dc..a3a3ed86 100644 --- a/src/contract/encodedmessages.h +++ b/src/contract/encodedmessages.h @@ -1,8 +1,8 @@ #ifndef BDK_ENCODEDMESSAGES_H #define BDK_ENCODEDMESSAGES_H -#include "contract/messages/concepts.h" -#include "contract/messages/basemessage.h" +#include "contract/concepts.h" +#include "contract/basemessage.h" struct EncodedCallMessage : BaseMessage { using BaseMessage::BaseMessage; diff --git a/src/contract/packedmessages.h b/src/contract/packedmessages.h index c903a066..81f40726 100644 --- a/src/contract/packedmessages.h +++ b/src/contract/packedmessages.h @@ -1,8 +1,8 @@ #ifndef BDK_PACKEDMESSAGES_H #define BDK_PACKEDMESSAGES_H -#include "contract/messages/concepts.h" -#include "contract/messages/basemessage.h" +#include "contract/concepts.h" +#include "contract/basemessage.h" template struct PackedCallMessage : BaseMessage< diff --git a/src/contract/precompiledcontractexecutor.h b/src/contract/precompiledcontractexecutor.h index 09ff7eb6..b9010812 100644 --- a/src/contract/precompiledcontractexecutor.h +++ b/src/contract/precompiledcontractexecutor.h @@ -3,7 +3,7 @@ #include "utils/randomgen.h" #include "traits.h" -#include "contract/messages/encodedmessages.h" +#include "contract/encodedmessages.h" #include "contract/abi.h" #include "contract/costs.h" diff --git a/src/contract/trace/calltype.h b/src/contract/trace/calltype.h index e8f6f2d7..affbee2f 100644 --- a/src/contract/trace/calltype.h +++ b/src/contract/trace/calltype.h @@ -1,7 +1,7 @@ #ifndef BDK_CONTRACT_TRACE_CALLTYPE_H #define BDK_CONTRACT_TRACE_CALLTYPE_H -#include "contract/messages/concepts.h" +#include "contract/concepts.h" namespace trace { diff --git a/src/utils/tx.h b/src/utils/tx.h index 6f13e21d..fdf6ec19 100644 --- a/src/utils/tx.h +++ b/src/utils/tx.h @@ -10,7 +10,7 @@ See the LICENSE.txt file in the project root for more information. #include "ecdsa.h" // utils.h -> strings.h, (bytes/join.h -> bytes/view.h) #include "uintconv.h" -#include "contract/messages/encodedmessages.h" +#include "contract/encodedmessages.h" /** * Abstraction of a block transaction. diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7d0a0aed..22700b1f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -42,8 +42,8 @@ set (TESTS_SOURCES ${CMAKE_SOURCE_DIR}/tests/contract/calltracer.cpp ${CMAKE_SOURCE_DIR}/tests/contract/createcontract.cpp ${CMAKE_SOURCE_DIR}/tests/contract/pebble.cpp - ${CMAKE_SOURCE_DIR}/tests/contract/messages/executioncontext.cpp - ${CMAKE_SOURCE_DIR}/tests/contract/messages/evmcontractexecutor.cpp + ${CMAKE_SOURCE_DIR}/tests/contract/executioncontext.cpp + ${CMAKE_SOURCE_DIR}/tests/contract/evmcontractexecutor.cpp ${CMAKE_SOURCE_DIR}/tests/contract/throwtest.cpp ${CMAKE_SOURCE_DIR}/tests/core/rdpos.cpp ${CMAKE_SOURCE_DIR}/tests/core/storage.cpp diff --git a/tests/contract/messages/evmcontractexecutor.cpp b/tests/contract/evmcontractexecutor.cpp similarity index 99% rename from tests/contract/messages/evmcontractexecutor.cpp rename to tests/contract/evmcontractexecutor.cpp index bf101ab2..42b679df 100644 --- a/tests/contract/messages/evmcontractexecutor.cpp +++ b/tests/contract/evmcontractexecutor.cpp @@ -7,7 +7,7 @@ See the LICENSE.txt file in the project root for more information. #include "evmone/evmone.h" #include "catch2/catch_amalgamated.hpp" -#include "contract/messages/evmcontractexecutor.h" +#include "contract/evmcontractexecutor.h" #include "bytes/random.h" #include "bytes/hex.h" #include "bytes/cast.h" diff --git a/tests/contract/messages/executioncontext.cpp b/tests/contract/executioncontext.cpp similarity index 99% rename from tests/contract/messages/executioncontext.cpp rename to tests/contract/executioncontext.cpp index 34725b51..c2e76a31 100644 --- a/tests/contract/messages/executioncontext.cpp +++ b/tests/contract/executioncontext.cpp @@ -7,7 +7,7 @@ See the LICENSE.txt file in the project root for more information. #include "catch2/catch_amalgamated.hpp" #include "bytes/hex.h" -#include "contract/messages/executioncontext.h" +#include "contract/executioncontext.h" static inline void addAccount(ExecutionContext& context, View
address, const Account& account) { auto pointer = context.getAccount(address); From 075aa723716dfb3c2fe6d07cb981d63eb62cf1ad Mon Sep 17 00:00:00 2001 From: Leonardo Amaral Date: Wed, 15 Jan 2025 16:36:45 -0300 Subject: [PATCH 33/37] removed unused file --- src/contract/traits/method.h | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 src/contract/traits/method.h diff --git a/src/contract/traits/method.h b/src/contract/traits/method.h deleted file mode 100644 index cb8d2e7a..00000000 --- a/src/contract/traits/method.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -namespace traits { - -template -struct Method {}; - -template -struct Method { - using ReturnType = R; - using ClassType = C; - static constexpr bool IsView = false; -}; - -template -struct Method { - using ReturnType = R; - using ClassType = const C; - static constexpr bool IsView = true; -}; - -} // namespace traits From 0a4abbd8be373c64705050da65c4beb46deee88e Mon Sep 17 00:00:00 2001 From: Leonardo Amaral Date: Thu, 16 Jan 2025 11:04:26 -0300 Subject: [PATCH 34/37] added missing include on bytes.h --- src/utils/bytes.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/bytes.h b/src/utils/bytes.h index d7126eae..6b91716b 100644 --- a/src/utils/bytes.h +++ b/src/utils/bytes.h @@ -1,6 +1,7 @@ #ifndef BDK_UTILS_BYTES_H #define BDK_UTILS_BYTES_H +#include #include using Byte = uint8_t; From da24d43fa3b3816476426f5c8827a77a847faf90 Mon Sep 17 00:00:00 2001 From: Leonardo Amaral Date: Thu, 16 Jan 2025 14:19:56 -0300 Subject: [PATCH 35/37] implementing pull request review comments --- src/contract/cppcontractexecutor.h | 2 +- src/contract/evmcontractexecutor.cpp | 2 +- src/contract/executioncontext.cpp | 10 +++++----- src/utils/strings.h | 2 +- tests/utils/strings.cpp | 8 -------- 5 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/contract/cppcontractexecutor.h b/src/contract/cppcontractexecutor.h index c6d75a29..9242632b 100644 --- a/src/contract/cppcontractexecutor.h +++ b/src/contract/cppcontractexecutor.h @@ -141,7 +141,7 @@ class CppContractExecutor { uint256_t value = 0; contract.caller_ = caller; - contract.value_ = value; // TODO: value 0 ALWAYS? + contract.value_ = value; auto account = context_.getAccount(msg.from()); const Address contractAddress = generateContractAddress(account.getNonce(), msg.from()); diff --git a/src/contract/evmcontractexecutor.cpp b/src/contract/evmcontractexecutor.cpp index 7b21a63f..ade52a04 100644 --- a/src/contract/evmcontractexecutor.cpp +++ b/src/contract/evmcontractexecutor.cpp @@ -250,7 +250,7 @@ evmc_tx_context EvmContractExecutor::get_tx_context() const noexcept { } evmc::bytes32 EvmContractExecutor::get_block_hash(int64_t number) const noexcept { - return EVMCConv::uint256ToEvmcUint256(number); // TODO: ??? + return EVMCConv::uint256ToEvmcUint256(number); } void EvmContractExecutor::emit_log(const evmc::address& addr, const uint8_t* data, size_t dataSize, const evmc::bytes32 topics[], size_t topicsCount) noexcept { diff --git a/src/contract/executioncontext.cpp b/src/contract/executioncontext.cpp index 9a4e21e5..57e4201c 100644 --- a/src/contract/executioncontext.cpp +++ b/src/contract/executioncontext.cpp @@ -18,11 +18,11 @@ BaseContract& ExecutionContext::getContract(View
contractAddress) { const auto it = contracts_.find(contractAddress); if (it == contracts_.end()) { - throw DynamicException("contract not found"); // TODO: dynamic exception + throw DynamicException("contract not found"); } if (it->second == nullptr) { - throw DynamicException("not a C++ contract"); // TODO: dynamic exception + throw DynamicException("not a C++ contract"); } return *it->second; @@ -32,11 +32,11 @@ const BaseContract& ExecutionContext::getContract(View
contractAddress) const auto it = contracts_.find(contractAddress); if (it == contracts_.end()) { - throw DynamicException("contract not found"); // TODO: dynamic exception + throw DynamicException("contract not found"); } if (it->second == nullptr) { - throw DynamicException("not a C++ contract"); // TODO: dynamic exception + throw DynamicException("not a C++ contract"); } return *it->second; @@ -46,7 +46,7 @@ Account& ExecutionContext::getMutableAccount(View
accountAddress) { const auto iterator = accounts_.find(accountAddress); if (iterator == accounts_.end()) { - throw DynamicException("account not found"); // TODO: dynamic exception + throw DynamicException("account not found"); } return *iterator->second; diff --git a/src/utils/strings.h b/src/utils/strings.h index 0b087da6..1d1bd7e0 100644 --- a/src/utils/strings.h +++ b/src/utils/strings.h @@ -15,7 +15,7 @@ See the LICENSE.txt file in the project root for more information. #include "../bytes/initializer.h" // bytes/view.h -> bytes/range.h -> ranges -> span -#include "dynamicexception.h" // TODO: see the size todo below +#include "dynamicexception.h" #include "hex.h" #include "uintconv.h" diff --git a/tests/utils/strings.cpp b/tests/utils/strings.cpp index d104a535..c27dd599 100644 --- a/tests/utils/strings.cpp +++ b/tests/utils/strings.cpp @@ -199,14 +199,6 @@ namespace THash { REQUIRE(bytes::cast(hash) == num); } - SECTION("Hash toEvmcBytes32") { - evmc::bytes32 num; - Bytes b = Hex::toBytes("9be83ea08b549e7c77644c451b55a674bb12e4668d018183ff9723b1de493818"); - for (int i = 0; i < 32; i++) num.bytes[i] = b[i]; - Hash hash(num); - REQUIRE(bytes::cast(hash) == num); - } - SECTION("Hash random()") { Hash hash1 = bytes::random(); Hash hash2 = bytes::random(); From 2d226a8cdf855b32dc5672af2933320fb320b4a3 Mon Sep 17 00:00:00 2001 From: Leonardo Amaral Date: Mon, 20 Jan 2025 10:14:06 -0300 Subject: [PATCH 36/37] fixing error messages, implementing uint256_t parsing, and fixing UB on decoding errors longer than 32 chars --- src/contract/abi.cpp | 4 +++- src/contract/calltracer.h | 4 ++-- src/contract/contracthost.h | 2 +- src/core/state.cpp | 2 -- src/net/http/jsonrpc/methods.cpp | 2 +- src/net/http/jsonrpc/parser.cpp | 16 ++++++++++++++++ src/net/http/jsonrpc/parser.h | 5 +++++ 7 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/contract/abi.cpp b/src/contract/abi.cpp index f8f3cd94..af650f47 100644 --- a/src/contract/abi.cpp +++ b/src/contract/abi.cpp @@ -63,11 +63,13 @@ Bytes ABI::Encoder::encodeError(std::string_view reason) { } std::string ABI::Decoder::decodeError(View data) { + static constexpr size_t MAX_STR_SIZE = 32; + if (data.size() != 100) { throw DynamicException("Encoded revert reason is expected to have exactly 100 bytes"); } - const size_t size = UintConv::bytesToUint256(data.subspan(36, 32)).convert_to(); + const size_t size = std::min(size_t(UintConv::bytesToUint256(data.subspan(36, 32))), MAX_STR_SIZE); std::string res; res.reserve(size); diff --git a/src/contract/calltracer.h b/src/contract/calltracer.h index dda7f843..00a3e6e9 100644 --- a/src/contract/calltracer.h +++ b/src/contract/calltracer.h @@ -71,7 +71,7 @@ class CallTracer { callTrace.gasUsed = callTrace.gas - uint64_t(gas); callStack_.pop(); - throw outOfGas; + throw; } catch (const std::exception& error) { callTrace.status = trace::CallStatus::EXECUTION_REVERTED; @@ -84,7 +84,7 @@ class CallTracer { callTrace.gasUsed = callTrace.gas - uint64_t(gas); callStack_.pop(); - throw error; + throw; } } diff --git a/src/contract/contracthost.h b/src/contract/contracthost.h index bfb82039..2bf5b02b 100644 --- a/src/contract/contracthost.h +++ b/src/contract/contracthost.h @@ -84,7 +84,7 @@ class ContractHost { return dispatchMessage(std::forward(msg)); } catch (const std::exception& err) { mustRevert_ = true; - throw err; + throw; } } diff --git a/src/core/state.cpp b/src/core/state.cpp index b0400cfc..69401484 100644 --- a/src/core/state.cpp +++ b/src/core/state.cpp @@ -199,8 +199,6 @@ void State::processTransaction( // processNextBlock already calls validateTransaction in every tx, // as it calls validateNextBlock as a sanity check. Account& accountFrom = *this->accounts_[tx.getFrom()]; - Account& accountTo = *this->accounts_[tx.getTo()]; - auto leftOverGas = int64_t(tx.getGasLimit()); auto& fromNonce = accountFrom.nonce; auto& fromBalance = accountFrom.balance; if (fromBalance < (tx.getValue() + tx.getGasLimit() * tx.getMaxFeePerGas())) { diff --git a/src/net/http/jsonrpc/methods.cpp b/src/net/http/jsonrpc/methods.cpp index 70aa187d..a450b1ed 100644 --- a/src/net/http/jsonrpc/methods.cpp +++ b/src/net/http/jsonrpc/methods.cpp @@ -93,7 +93,7 @@ static std::tuple parseMessage(const js gas = Gas(parseIfExists(txJson, "gas").value_or(10'000'000)); - value = uint256_t(parseIfExists(txJson, "value").value_or(0)); + value = parseIfExists(txJson, "value").value_or(0); data = parseIfExists(txJson, "data").value_or(Bytes{}); diff --git a/src/net/http/jsonrpc/parser.cpp b/src/net/http/jsonrpc/parser.cpp index 1b911e2b..83fdc9a3 100644 --- a/src/net/http/jsonrpc/parser.cpp +++ b/src/net/http/jsonrpc/parser.cpp @@ -50,5 +50,21 @@ namespace jsonrpc { if (!std::regex_match(value, numberFormat)) throw Error::invalidFormat(value); return uint64_t(Hex(value).getUint()); } + + uint256_t Parser::operator()(const json& data) const { + if (data.is_number_unsigned()) + return uint256_t(data.get()); + + if (!data.is_string()) + throw Error::invalidType("string", data.type_name()); + + auto value = data.get(); + + if (!std::regex_match(value, numberFormat)) + throw Error::invalidFormat(value); + + return Hex(value).getUint(); + } + } // namespace jsonrpc diff --git a/src/net/http/jsonrpc/parser.h b/src/net/http/jsonrpc/parser.h index e0a0c2a4..ab3260d4 100644 --- a/src/net/http/jsonrpc/parser.h +++ b/src/net/http/jsonrpc/parser.h @@ -63,6 +63,11 @@ namespace jsonrpc { uint64_t operator()(const json& data) const; ///< Function call operator. }; + /// Specialization. + template<> struct Parser { + uint256_t operator()(const json& data) const; ///< Function call operator. + }; + /// Partial specialization to optionally parse a json field. template struct Parser> { /** From d8b68dd5da8aae1ffc26c17dfd898eb4019c5cc8 Mon Sep 17 00:00:00 2001 From: Leonardo Amaral Date: Mon, 20 Jan 2025 11:05:35 -0300 Subject: [PATCH 37/37] 15% increase in gas estimation workaround --- src/core/state.cpp | 6 ++---- tests/net/http/httpjsonrpc.cpp | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/core/state.cpp b/src/core/state.cpp index 69401484..e49179fb 100644 --- a/src/core/state.cpp +++ b/src/core/state.cpp @@ -511,14 +511,12 @@ int64_t State::estimateGas(EncodedMessageVariant msg) { context ); - return std::visit([&host] (auto&& msg) { + return int64_t(std::visit([&host] (auto&& msg) { const Gas& gas = msg.gas(); const int64_t initialGas(gas); host.simulate(std::forward(msg)); return initialGas - int64_t(gas); - }, std::move(msg)); - - // TODO: add 15% workaround + }, std::move(msg)) * 1.15); } std::vector> State::getCppContracts() const { diff --git a/tests/net/http/httpjsonrpc.cpp b/tests/net/http/httpjsonrpc.cpp index 3ed1d178..0b3dcb94 100644 --- a/tests/net/http/httpjsonrpc.cpp +++ b/tests/net/http/httpjsonrpc.cpp @@ -303,7 +303,7 @@ namespace THTTPJsonRPC { {"value", "0x1"} }), "latest"})); - REQUIRE(eth_estimateGasResponse["result"] == "0x5208"); + REQUIRE(eth_estimateGasResponse["result"] == "0x5e55"); json eth_gasPriceResponse = requestMethod("eth_gasPrice", json::array()); REQUIRE(eth_gasPriceResponse["result"] == "0x9502f900");