From 51fdcde96ca54562a2b37f638d22d972bea9b792 Mon Sep 17 00:00:00 2001 From: Leonardo Amalaral Date: Mon, 28 Oct 2024 20:05:46 -0300 Subject: [PATCH 1/6] 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 000000000..44a0b485f --- /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 6ca2b7fb0..14f135b25 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 cf44b8e60..d074a155e 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 6416bb14e..6199d0886 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 c5f69a17a..656d8319e 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 9ae8d1706..fafc91be9 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 0b5a07e53..e563c7c97 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 ab1876dca..edf6be07c 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 defa65b49..cc65f02a6 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 000000000..4f9a2306e --- /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 000000000..50ea905a9 --- /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 000000000..d7126eae0 --- /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 000000000..dd6dab1b6 --- /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 3ed30d413..05d938690 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 badc2a027..5d6f8f1bd 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 d7c3dcef1..efaf7c0b0 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 a8bb0b908..c13f80678 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 6e268fd35..5edc2fba9 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 000000000..f0f7a2e81 --- /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 676bb4349..b2e900537 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 8ef39714e..abb2d24ec 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 b980881b1..0292c1183 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 2/6] 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 cd5ed6acb..a6c7d37f5 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 000000000..2fde403ea --- /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 656d8319e..9fce1a083 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 d343af89b..2645ff606 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 e563c7c97..34a890c1a 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 cc65f02a6..52330730b 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 000000000..1f6db3b36 --- /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 000000000..4c190cac0 --- /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 7234bbc1b..231f54b41 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 f345355f0..5563f380f 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 a07ca48a2..9199e3c6e 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 5d6f8f1bd..1dcf3e292 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 efaf7c0b0..ea267305a 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 bf21ba1ed..3d5077515 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 3b5c21abd..9cfa01c97 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 3d76cd67a..7c0c60761 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 c2abc6620..d00ca372a 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 0a2f0eb67..8ed4ce481 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 c69ba79b7..52064248d 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 8d6ca6416..42050f768 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 ee6309c41..0970df0a3 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 7fbf73f42..640b0f453 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 db5faded3..fefb363f7 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 abb2d24ec..7872c3b51 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 c9a3e841f..902434a0e 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 43a5d5409..0bd67684e 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 155f55244..7c198198f 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 958ee2721..9662252bf 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 0292c1183..fb1d0c504 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 07e2af918..c1376db54 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 3/6] 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 000000000..548246e60 --- /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 1dcf3e292..51986c7da 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 ea267305a..c8c051fd2 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 4/6] 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 000000000..6f8985393 --- /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 2fde403ea..25f64fd0c 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 14f135b25..4005a41d6 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 a3bfacea1..1dc101023 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 e5a0f0afb..2c7ec6d21 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 06b7edbc5..268705cec 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 59c593e44..4f2cb5571 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 d55c8a8d7..3017d0b38 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 9fce1a083..53ea64d9a 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 fafc91be9..e1738db2c 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 eec548530..ac2a6a920 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 f5cf6eb95..15f7790b0 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 5615242a5..73883f571 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 c5938d87a..223a06f92 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 8e13f42fe..2eb7cae4c 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 2645ff606..fa1f15e99 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 7b7c49adb..c79a9dff1 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 34a890c1a..c0404715c 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 811b02f05..fe5314921 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 e36a1577f..e7e3ade41 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 fdd7e6217..63395e982 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 8659553d3..8eecbbebc 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 ec899005b..2b07d34f8 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 52330730b..490d5531f 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 4f9a2306e..b5c71f4db 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 50ea905a9..0ca06c566 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 dd6dab1b6..2fb81488b 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 a0aeb4cc0..f97c647cb 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 f7328a716..ed3cfe95a 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 32a4b6005..f40627f84 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 000000000..4f8a0fa37 --- /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 4c190cac0..68f3ef6a6 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 1cb45f3e2..df71f3f8c 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 05d938690..c5a28b756 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 9199e3c6e..d9950672f 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 51986c7da..000000000 --- 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 c8c051fd2..87ce7b689 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 c13f80678..072f34ce3 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 233c78fb1..c8a003863 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 de8636bdc..380c21eee 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 5edc2fba9..61d1802dd 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 f0f7a2e81..b7087c538 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 fb1d0c504..23531ecca 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 5/6] 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 4f8a0fa37..57ba63c61 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 548246e60..a985c194e 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 c7c085948a77765b16e97dadcc27efb89e1f23bd Mon Sep 17 00:00:00 2001 From: Leonardo Amalaral Date: Mon, 11 Nov 2024 16:12:08 -0300 Subject: [PATCH 6/6] moved bytes array definition --- src/utils/bytes.h | 4 ++++ src/utils/hex.h | 3 --- src/utils/utils.h | 2 -- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/utils/bytes.h b/src/utils/bytes.h index d7126eae0..2c634b0c6 100644 --- a/src/utils/bytes.h +++ b/src/utils/bytes.h @@ -1,10 +1,14 @@ #ifndef BDK_UTILS_BYTES_H #define BDK_UTILS_BYTES_H +#include #include using Byte = uint8_t; using Bytes = std::vector; +template +using BytesArr = std::array; + #endif // BDK_UTILS_BYTES_H diff --git a/src/utils/hex.h b/src/utils/hex.h index c5a28b756..11a4a01b1 100644 --- a/src/utils/hex.h +++ b/src/utils/hex.h @@ -23,9 +23,6 @@ See the LICENSE.txt file in the project root for more information. #include "bytes.h" #include "utils/view.h" -template -using BytesArr = std::array; - using uint256_t = boost::multiprecision::number>; /// Abstraction of a strictly hex-formatted string (`(0x)[1-9][a-f][A-F]`). diff --git a/src/utils/utils.h b/src/utils/utils.h index 61d1802dd..c9cbd0b82 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -59,8 +59,6 @@ using json = nlohmann::ordered_json; /// Typedef for bigint. using bigint = boost::multiprecision::number>; -template using BytesArr = std::array; ///< Typedef for BytesArr. - // Base case for the recursive helper - now using requires for an empty body function template requires (I == sizeof...(Tp))