diff --git a/src/contract/abi.h b/src/contract/abi.h index 245fd509..5a2030de 100644 --- a/src/contract/abi.h +++ b/src/contract/abi.h @@ -11,7 +11,7 @@ See the LICENSE.txt file in the project root for more information. #include "../utils/intconv.h" #include "../utils/uintconv.h" #include "../utils/strconv.h" -#include "../utils/utils.h" // libs/json.hpp -> string +#include "../utils/utils.h" // FunctionTypes, libs/json.hpp -> string /// Namespace for Solidity ABI-related operations. namespace ABI { diff --git a/src/contract/contractmanager.cpp b/src/contract/contractmanager.cpp index a2c7e43b..8cbf1081 100644 --- a/src/contract/contractmanager.cpp +++ b/src/contract/contractmanager.cpp @@ -70,16 +70,15 @@ void ContractManager::ethCall(const evmc_message& callInfo, ContractHost* host) PointerNullifier nullifier(this->host_); const Address caller(callInfo.sender); const Functor functor = EVMCConv::getFunctor(callInfo); - /// Call the function on this->createContractFuncs_ + // Call the function on this->createContractFuncs_ auto it = this->createContractFuncs_.find(functor); if (it == this->createContractFuncs_.end()) { throw DynamicException("ContractManager: Invalid function call"); } it->second(callInfo, - ContractHost::deriveContractAddress(this->host_->getNonce(caller), caller), - this->contracts_, - this->getContractChainId(), - this->host_); + ContractHost::deriveContractAddress(this->host_->getNonce(caller), caller), + this->contracts_, this->getContractChainId(), this->host_ + ); } Bytes ContractManager::ethCallView(const evmc_message& callInfo, ContractHost* host) const { diff --git a/src/contract/contractmanager.h b/src/contract/contractmanager.h index 9232c976..92904b79 100644 --- a/src/contract/contractmanager.h +++ b/src/contract/contractmanager.h @@ -27,16 +27,16 @@ class ContractManager : public BaseContract { /// Functions to create contracts. boost::unordered_flat_map< - Functor, - std::function< - void(const evmc_message&, - const Address&, - boost::unordered_flat_map, SafeHash>& contracts_, - const uint64_t&, - ContractHost* - )>, - SafeHash - > createContractFuncs_; + Functor, + std::function, SafeHash>& contracts_, + const uint64_t&, + ContractHost* + )>, + SafeHash + > createContractFuncs_; /** * Get all deployed contracts. diff --git a/src/contract/templates/dexv2/dexv2pair.cpp b/src/contract/templates/dexv2/dexv2pair.cpp index ffc2f519..1fb9b04f 100644 --- a/src/contract/templates/dexv2/dexv2pair.cpp +++ b/src/contract/templates/dexv2/dexv2pair.cpp @@ -16,15 +16,38 @@ DEXV2Pair::DEXV2Pair(const Address& address, const DB& db reserve0_(this), reserve1_(this), blockTimestampLast_(this), price0CumulativeLast_(this), price1CumulativeLast_(this), kLast_(this) { - this->factory_ = Address(db.get(std::string("factory_"), this->getDBPrefix())); - this->token0_ = Address(db.get(std::string("token0_"), this->getDBPrefix())); - this->token1_ = Address(db.get(std::string("token1_"), this->getDBPrefix())); - this->reserve0_ = UintConv::bytesToUint112(db.get(std::string("reserve0_"), this->getDBPrefix())); - this->reserve1_ = UintConv::bytesToUint112(db.get(std::string("reserve1_"), this->getDBPrefix())); - this->blockTimestampLast_ = UintConv::bytesToUint32(db.get(std::string("blockTimestampLast_"), this->getDBPrefix())); - this->price0CumulativeLast_ = UintConv::bytesToUint256(db.get(std::string("price0CumulativeLast_"), this->getDBPrefix())); - this->price1CumulativeLast_ = UintConv::bytesToUint256(db.get(std::string("price1CumulativeLast_"), this->getDBPrefix())); - this->kLast_ = UintConv::bytesToUint256(db.get(std::string("kLast_"), this->getDBPrefix())); + Hex prefix = Hex::fromBytes(this->getDBPrefix()); + Utils::safePrintTest("Loading contract DEXV2Pair from DB with prefix: " + prefix.get()); + auto factoryBytes = db.get(std::string("factory_"), this->getDBPrefix()); + auto token0Bytes = db.get(std::string("token0_"), this->getDBPrefix()); + auto token1Bytes = db.get(std::string("token1_"), this->getDBPrefix()); + auto reserve0Bytes = db.get(std::string("reserve0_"), this->getDBPrefix()); + auto reserve1Bytes = db.get(std::string("reserve1_"), this->getDBPrefix()); + auto blockTimestampLastBytes = db.get(std::string("blockTimestampLast_"), this->getDBPrefix()); + auto price0CumulativeLastBytes = db.get(std::string("price0CumulativeLast_"), this->getDBPrefix()); + auto price1CumulativeLastBytes = db.get(std::string("price1CumulativeLast_"), this->getDBPrefix()); + auto kLastBytes = db.get(std::string("kLast_"), this->getDBPrefix()); + + Utils::safePrintTest("Factory Bytes: " + Hex::fromBytes(factoryBytes).get()); + Utils::safePrintTest("Token0 Bytes: " + Hex::fromBytes(token0Bytes).get()); + Utils::safePrintTest("Token1 Bytes: " + Hex::fromBytes(token1Bytes).get()); + Utils::safePrintTest("Reserve0 Bytes: " + Hex::fromBytes(reserve0Bytes).get()); + Utils::safePrintTest("Reserve1 Bytes: " + Hex::fromBytes(reserve1Bytes).get()); + Utils::safePrintTest("BlockTimestampLast Bytes: " + Hex::fromBytes(blockTimestampLastBytes).get()); + Utils::safePrintTest("Price0CumulativeLast Bytes: " + Hex::fromBytes(price0CumulativeLastBytes).get()); + Utils::safePrintTest("Price1CumulativeLast Bytes: " + Hex::fromBytes(price1CumulativeLastBytes).get()); + Utils::safePrintTest("KLast Bytes: " + Hex::fromBytes(kLastBytes).get()); + + + this->factory_ = Address(factoryBytes); + this->token0_ = Address(token0Bytes); + this->token1_ = Address(token1Bytes); + this->reserve0_ = UintConv::bytesToUint112(reserve0Bytes); + this->reserve1_ = UintConv::bytesToUint112(reserve1Bytes); + this->blockTimestampLast_ = UintConv::bytesToUint32(blockTimestampLastBytes); + this->price0CumulativeLast_ = UintConv::bytesToUint256(price0CumulativeLastBytes); + this->price1CumulativeLast_ = UintConv::bytesToUint256(price1CumulativeLastBytes); + this->kLast_ = UintConv::bytesToUint256(kLastBytes); this->factory_.commit(); this->token0_.commit(); @@ -47,6 +70,17 @@ DEXV2Pair::DEXV2Pair(const Address& address, const DB& db this->price0CumulativeLast_.enableRegister(); this->price1CumulativeLast_.enableRegister(); this->kLast_.enableRegister(); + Utils::safePrintTest("Loaded from DB DEXV2Pair contract"); + Utils::safePrintTest("Factory: " + this->factory_.get().hex().get()); + Utils::safePrintTest("Token0: " + this->token0_.get().hex().get()); + Utils::safePrintTest("Token1: " + this->token1_.get().hex().get()); + Utils::safePrintTest("Reserve0: " + this->reserve0_.get().str()); + Utils::safePrintTest("Reserve1: " + this->reserve1_.get().str()); + Utils::safePrintTest("BlockTimestampLast: " + std::to_string(this->blockTimestampLast_.get())); + Utils::safePrintTest("Price0CumulativeLast: " + this->price0CumulativeLast_.get().str()); + Utils::safePrintTest("Price1CumulativeLast: " + this->price1CumulativeLast_.get().str()); + Utils::safePrintTest("KLast: " + this->kLast_.get().str()); + } DEXV2Pair::DEXV2Pair( @@ -55,7 +89,14 @@ DEXV2Pair::DEXV2Pair( factory_(this), token0_(this), token1_(this), reserve0_(this), reserve1_(this), blockTimestampLast_(this), price0CumulativeLast_(this), price1CumulativeLast_(this), kLast_(this) { + // Explicitly initialize numbers to 0 to avoid junk values on DB load this->factory_ = creator; + this->reserve0_ = 0; + this->reserve1_ = 0; + this->blockTimestampLast_ = 0; + this->price0CumulativeLast_ = 0; + this->price1CumulativeLast_ = 0; + this->kLast_ = 0; this->factory_.commit(); this->token0_.commit(); @@ -78,6 +119,16 @@ DEXV2Pair::DEXV2Pair( this->price0CumulativeLast_.enableRegister(); this->price1CumulativeLast_.enableRegister(); this->kLast_.enableRegister(); + Utils::safePrintTest("Creating new DEXV2Pair contract"); + Utils::safePrintTest("Factory: " + this->factory_.get().hex().get()); + Utils::safePrintTest("Token0: " + this->token0_.get().hex().get()); + Utils::safePrintTest("Token1: " + this->token1_.get().hex().get()); + Utils::safePrintTest("Reserve0: " + this->reserve0_.get().str()); + Utils::safePrintTest("Reserve1: " + this->reserve1_.get().str()); + Utils::safePrintTest("BlockTimestampLast: " + std::to_string(this->blockTimestampLast_.get())); + Utils::safePrintTest("Price0CumulativeLast: " + this->price0CumulativeLast_.get().str()); + Utils::safePrintTest("Price1CumulativeLast: " + this->price1CumulativeLast_.get().str()); + Utils::safePrintTest("KLast: " + this->kLast_.get().str()); } DEXV2Pair::~DEXV2Pair() {}; @@ -141,6 +192,8 @@ void DEXV2Pair::initialize(const Address& token0, const Address& token1) { if (this->factory_ != this->getCaller()) throw DynamicException("DEXV2Pair: FORBIDDEN"); this->token0_ = token0; this->token1_ = token1; + Utils::safePrintTest("Initialized DEXV2Pair contract with token0: " + token0.hex().get() + " and token1: " + token1.hex().get()); + Utils::safePrintTest("Initialized Reserves 0: " + this->reserve0_.get().str() + " and Reserves 1: " + this->reserve1_.get().str()); } std::pair DEXV2Pair::getReservess() const { @@ -256,16 +309,67 @@ void DEXV2Pair::sync() { DBBatch DEXV2Pair::dump() const { + // We have to dump the tokens as well DBBatch dbBatch = BaseContract::dump(); + DBBatch erc20Batch = ERC20::dump(); + for (const auto& dbItem : erc20Batch.getPuts()) dbBatch.push_back(dbItem); + for (const auto& dbItem : erc20Batch.getDels()) dbBatch.delete_key(dbItem); + + Utils::safePrintTest("Dumping DEXV2Pair to DB"); + uint112_t zeroUint = 0; + uint112_t oneUint = 1; + auto zeroUintBytes = UintConv::uint112ToBytes(zeroUint); + auto oneUintBytes = UintConv::uint112ToBytes(oneUint); + Utils::safePrintTest("Zero Uint: " + zeroUint.str()); + Utils::safePrintTest("One Uint: " + oneUint.str()); + Utils::safePrintTest("Zero Uint Bytes: " + Hex::fromBytes(zeroUintBytes).get()); + Utils::safePrintTest("One Uint Bytes: " + Hex::fromBytes(oneUintBytes).get()); + + Bytes zeroUintRawBytes; + zeroUintRawBytes.reserve(14); + Bytes oneUintRawBytes; + oneUintRawBytes.reserve(14); + boost::multiprecision::export_bits(zeroUint, std::back_inserter(zeroUintRawBytes), 8); + boost::multiprecision::export_bits(oneUint, std::back_inserter(oneUintRawBytes), 8); + Utils::safePrintTest("Zero Uint Raw Bytes: " + Hex::fromBytes(zeroUintRawBytes).get()); + Utils::safePrintTest("One Uint Raw Bytes: " + Hex::fromBytes(oneUintRawBytes).get()); + + Utils::safePrintTest("Factory: " + this->factory_.get().hex().get()); + Utils::safePrintTest("Token0: " + this->token0_.get().hex().get()); + Utils::safePrintTest("Token1: " + this->token1_.get().hex().get()); + Utils::safePrintTest("Reserve0 Before: " + this->reserve0_.get().str()); + Utils::safePrintTest("Reserve1 Before: " + this->reserve1_.get().str()); + auto reserve0Bytes = UintConv::uint112ToBytes(this->reserve0_.get()); + auto reserve1Bytes = UintConv::uint112ToBytes(this->reserve1_.get()); + Utils::safePrintTest("Reserve0 After: " + this->reserve0_.get().str()); + Utils::safePrintTest("Reserve1 After: " + this->reserve1_.get().str()); + Utils::safePrintTest("reserve0 Bytes: " + Hex::fromBytes(reserve0Bytes).get()); + Utils::safePrintTest("reserve1 Bytes: " + Hex::fromBytes(reserve1Bytes).get()); + + Utils::safePrintTest("BlockTimestampLast: " + std::to_string(this->blockTimestampLast_.get())); + Utils::safePrintTest("Price0CumulativeLast: " + this->price0CumulativeLast_.get().str()); + Utils::safePrintTest("Price1CumulativeLast: " + this->price1CumulativeLast_.get().str()); + Utils::safePrintTest("KLast: " + this->kLast_.get().str()); + dbBatch.push_back(StrConv::stringToBytes("factory_"), this->factory_.get().view(), this->getDBPrefix()); dbBatch.push_back(StrConv::stringToBytes("token0_"), this->token0_.get().view(), this->getDBPrefix()); dbBatch.push_back(StrConv::stringToBytes("token1_"), this->token1_.get().view(), this->getDBPrefix()); - dbBatch.push_back(StrConv::stringToBytes("reserve0_"), UintConv::uint112ToBytes(this->reserve0_.get()), this->getDBPrefix()); - dbBatch.push_back(StrConv::stringToBytes("reserve1_"), UintConv::uint112ToBytes(this->reserve1_.get()), this->getDBPrefix()); + dbBatch.push_back(StrConv::stringToBytes("reserve0_"), reserve0Bytes, this->getDBPrefix()); + dbBatch.push_back(StrConv::stringToBytes("reserve1_"), reserve1Bytes, this->getDBPrefix()); dbBatch.push_back(StrConv::stringToBytes("blockTimestampLast_"), UintConv::uint32ToBytes(this->blockTimestampLast_.get()), this->getDBPrefix()); dbBatch.push_back(StrConv::stringToBytes("price0CumulativeLast_"), UintConv::uint256ToBytes(this->price0CumulativeLast_.get()), this->getDBPrefix()); dbBatch.push_back(StrConv::stringToBytes("price1CumulativeLast_"), UintConv::uint256ToBytes(this->price1CumulativeLast_.get()), this->getDBPrefix()); dbBatch.push_back(StrConv::stringToBytes("kLast_"), UintConv::uint256ToBytes(this->kLast_.get()), this->getDBPrefix()); + const auto& puts = dbBatch.getPuts(); + for (const auto& dbItem : puts) { + // Take of the prefix based on this->getDBPrefix().size() from the dbItem.key + Bytes prefixBytes(dbItem.key.begin(), dbItem.key.begin() + this->getDBPrefix().size()); + Hex prefix = Hex::fromBytes(prefixBytes); + // Then take dbItem.key + this->getDBPrefix().size() to the end + std::string key(dbItem.key.begin() + this->getDBPrefix().size(), dbItem.key.end()); + Hex value = Hex::fromBytes(dbItem.value); + Utils::safePrintTest("Dumping to DB with prefix : " + prefix.get() + " key: " + key + " value: " + value.get()); + } return dbBatch; } diff --git a/src/contract/templates/erc721test.cpp b/src/contract/templates/erc721test.cpp index ab40343e..7a0d8c85 100644 --- a/src/contract/templates/erc721test.cpp +++ b/src/contract/templates/erc721test.cpp @@ -33,7 +33,7 @@ ERC721Test::ERC721Test(const Address &address, const DB& db) ERC721Test::ERC721Test( const std::string &erc721name, const std::string &erc721symbol, const uint64_t& maxTokens, const Address &address, const Address &creator, const uint64_t &chainId -) : DynamicContract(erc721name, address, creator, chainId), +) : DynamicContract("ERC721Test", address, creator, chainId), ERC721(erc721name, erc721symbol, address, creator, chainId), tokenIdCounter_(this, 0), maxTokens_(this, maxTokens), totalSupply_(this, 0) { diff --git a/src/contract/templates/erc721uristorage.cpp b/src/contract/templates/erc721uristorage.cpp index 9098dd55..1f7a1b7f 100644 --- a/src/contract/templates/erc721uristorage.cpp +++ b/src/contract/templates/erc721uristorage.cpp @@ -13,7 +13,6 @@ ERC721URIStorage::ERC721URIStorage(const Address& address, const DB& db) : DynamicContract(address, db), ERC721(address, db), _tokenURIs(this) { - for (const auto& dbEntry : db.getBatch(this->getNewPrefix("tokenURIs_"))) { this->_tokenURIs[Utils::fromBigEndian(dbEntry.key)] = StrConv::bytesToString(dbEntry.value); } @@ -36,13 +35,11 @@ ERC721URIStorage::ERC721URIStorage( ) : DynamicContract(derivedTypeName, address, creator, chainId), ERC721(derivedTypeName, erc721_name, erc721_symbol, address, creator, chainId), _tokenURIs(this) { - ERC721URIStorage::registerContractFunctions(); } DBBatch ERC721URIStorage::dump() const { DBBatch batchedOperations = ERC721::dump(); - for (auto it = this->_tokenURIs.cbegin(); it != this->_tokenURIs.cend(); ++it) { batchedOperations.push_back( Utils::uintToBytes(it->first), @@ -50,7 +47,6 @@ DBBatch ERC721URIStorage::dump() const { this->getNewPrefix("tokenURIs_") ); } - return batchedOperations; } diff --git a/src/contract/templates/erc721uristorage.h b/src/contract/templates/erc721uristorage.h index d527ba59..585611be 100644 --- a/src/contract/templates/erc721uristorage.h +++ b/src/contract/templates/erc721uristorage.h @@ -1,9 +1,14 @@ +/* + 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. +*/ + #ifndef ERC721URISTORAGE_H #define ERC721URISTORAGE_H #include "erc721.h" - /// Template for an ERC721URIStorage contract. /// Roughly based on the OpenZeppelin implementation. class ERC721URIStorage : virtual public ERC721 { @@ -33,21 +38,17 @@ class ERC721URIStorage : virtual public ERC721 { void registerContractFunctions() override; public: - /** * ConstructorArguments is a tuple of the contract constructor arguments in the order they appear in the constructor. */ using ConstructorArguments = std::tuple; - /** * Constructor for loading contract from DB. * @param address The address where the contract will be deployed. * @param db Reference to the database object. */ - ERC721URIStorage( - const Address& address, const DB& db - ); + ERC721URIStorage(const Address& address, const DB& db); /** * Constructor to be used when creating a new contract. diff --git a/src/contract/templates/nativewrapper.cpp b/src/contract/templates/nativewrapper.cpp index 9827befd..b71e9a23 100644 --- a/src/contract/templates/nativewrapper.cpp +++ b/src/contract/templates/nativewrapper.cpp @@ -27,7 +27,12 @@ NativeWrapper::NativeWrapper( NativeWrapper::~NativeWrapper() = default; DBBatch NativeWrapper::dump() const { - return BaseContract::dump(); + // We need to dump all the data from the parent class as well + DBBatch batch = ERC20::dump(); + DBBatch baseDump = BaseContract::dump(); + for (const auto& dbItem : baseDump.getPuts()) batch.push_back(dbItem); + for (const auto& dbItem : baseDump.getDels()) batch.delete_key(dbItem); + return batch; } void NativeWrapper::registerContractFunctions() { diff --git a/src/contract/templates/pebble.cpp b/src/contract/templates/pebble.cpp index 1b2ff47d..5f5555d4 100644 --- a/src/contract/templates/pebble.cpp +++ b/src/contract/templates/pebble.cpp @@ -132,7 +132,7 @@ DBBatch Pebble::dump() const { for (auto it = this->minters_.cbegin(); it != this->minters_.cend(); ++it) { batch.push_back(it->first.asBytes(), UintConv::uint8ToBytes(static_cast(it->second)), this->getNewPrefix("minters_")); } - batch.push_back(StrConv::stringToBytes("authorized_"), this->authorizer_.get().asBytes(), this->getDBPrefix()); + batch.push_back(StrConv::stringToBytes("authorizer_"), this->authorizer_.get().asBytes(), this->getDBPrefix()); return batch; } diff --git a/src/contract/templates/throwtestA.cpp b/src/contract/templates/throwtestA.cpp index a12f4d81..aafb8159 100644 --- a/src/contract/templates/throwtestA.cpp +++ b/src/contract/templates/throwtestA.cpp @@ -8,24 +8,26 @@ See the LICENSE.txt file in the project root for more information. #include "throwtestA.h" ThrowTestA::ThrowTestA( - const Address& address, const Address& creator, - const uint64_t& chainId -) : DynamicContract("ThrowTestA", address, creator, chainId) { + const Address& address, const Address& creator, const uint64_t& chainId +) : DynamicContract("ThrowTestA", address, creator, chainId), num_(this) { + this->num_.commit(); registerContractFunctions(); + this->num_.enableRegister(); } -ThrowTestA::ThrowTestA( - const Address& address, - const DB& db -) : DynamicContract(address, db) { +ThrowTestA::ThrowTestA(const Address& address, const DB& db) : DynamicContract(address, db) { + this->num_ = UintConv::bytesToUint8(db.get(std::string("num_"), this->getDBPrefix())); + this->num_.commit(); registerContractFunctions(); + this->num_.enableRegister(); } ThrowTestA::~ThrowTestA() { return; } -DBBatch ThrowTestA::dump() const -{ - return BaseContract::dump(); +DBBatch ThrowTestA::dump() const { + DBBatch dbBatch = BaseContract::dump(); + dbBatch.push_back(StrConv::stringToBytes("num_"), UintConv::uint8ToBytes(this->num_.get()), this->getDBPrefix()); + return dbBatch; } uint8_t ThrowTestA::getNumA() const { return this->num_.get(); } diff --git a/src/contract/templates/throwtestB.cpp b/src/contract/templates/throwtestB.cpp index 489aee40..486f2da3 100644 --- a/src/contract/templates/throwtestB.cpp +++ b/src/contract/templates/throwtestB.cpp @@ -8,24 +8,26 @@ See the LICENSE.txt file in the project root for more information. #include "throwtestB.h" ThrowTestB::ThrowTestB( - const Address& address, const Address& creator, - const uint64_t& chainId -) : DynamicContract("ThrowTestB", address, creator, chainId) { + const Address& address, const Address& creator, const uint64_t& chainId +) : DynamicContract("ThrowTestB", address, creator, chainId), num_(this) { + this->num_.commit(); registerContractFunctions(); + this->num_.enableRegister(); } -ThrowTestB::ThrowTestB( - const Address& address, - const DB& db -) : DynamicContract(address, db) { +ThrowTestB::ThrowTestB(const Address& address, const DB& db) : DynamicContract(address, db) { + this->num_ = UintConv::bytesToUint8(db.get(std::string("num_"), this->getDBPrefix())); + this->num_.commit(); registerContractFunctions(); + this->num_.enableRegister(); } ThrowTestB::~ThrowTestB() { return; } -DBBatch ThrowTestB::dump() const -{ - return BaseContract::dump(); +DBBatch ThrowTestB::dump() const { + DBBatch dbBatch = BaseContract::dump(); + dbBatch.push_back(StrConv::stringToBytes("num_"), UintConv::uint8ToBytes(this->num_.get()), this->getDBPrefix()); + return dbBatch; } uint8_t ThrowTestB::getNumB() const { return this->num_.get(); } diff --git a/src/contract/templates/throwtestC.cpp b/src/contract/templates/throwtestC.cpp index 3a0b73eb..4a446295 100644 --- a/src/contract/templates/throwtestC.cpp +++ b/src/contract/templates/throwtestC.cpp @@ -8,24 +8,26 @@ See the LICENSE.txt file in the project root for more information. #include "throwtestC.h" ThrowTestC::ThrowTestC( - const Address& address, const Address& creator, - const uint64_t& chainId -) : DynamicContract("ThrowTestC", address, creator, chainId) { + const Address& address, const Address& creator, const uint64_t& chainId +) : DynamicContract("ThrowTestC", address, creator, chainId), num_(this) { + this->num_.commit(); registerContractFunctions(); + this->num_.enableRegister(); } -ThrowTestC::ThrowTestC( - const Address& address, - const DB& db -) : DynamicContract(address, db) { +ThrowTestC::ThrowTestC(const Address& address, const DB& db) : DynamicContract(address, db) { + this->num_ = UintConv::bytesToUint8(db.get(std::string("num_"), this->getDBPrefix())); + this->num_.commit(); registerContractFunctions(); + this->num_.enableRegister(); } ThrowTestC::~ThrowTestC() { return; } -DBBatch ThrowTestC::dump() const -{ - return BaseContract::dump(); +DBBatch ThrowTestC::dump() const { + DBBatch dbBatch = BaseContract::dump(); + dbBatch.push_back(StrConv::stringToBytes("num_"), UintConv::uint8ToBytes(this->num_.get()), this->getDBPrefix()); + return dbBatch; } uint8_t ThrowTestC::getNumC() const { return this->num_.get(); } diff --git a/src/contract/variables/safeunorderedmap.h b/src/contract/variables/safeunorderedmap.h index 6128afa1..296a985d 100644 --- a/src/contract/variables/safeunorderedmap.h +++ b/src/contract/variables/safeunorderedmap.h @@ -243,7 +243,7 @@ template class SafeUnorderedMap : public SafeBase { */ template void insert(InputIt first, InputIt last) { // On this insert, we copy everything because we cannot check the insert - // return to see what keys were insertted. + // return to see what keys were inserted. for (auto it = first; it != last; ++it) { auto valueIt = this->value_.find(it->first); if (valueIt != this->value_.end()) { @@ -483,7 +483,7 @@ template class SafeUnorderedMap : public SafeBase { } /** - * Erase a value from the map. + * Erase a value from the map. Segfaults if value is already non-existant. * @param pos The position of the value to erase. * @return An iterator to the next value. */ @@ -492,14 +492,12 @@ template class SafeUnorderedMap : public SafeBase { ) { if (auto itValue = this->value_.find((*pos).first); itValue != this->value_.end()) { this->copy_.try_emplace((*itValue).first, std::in_place, (*itValue).second); - } else { - this->copy_.try_emplace((*itValue).first, std::nullopt); } - markAsUsed(); return this->value_.erase(pos); + markAsUsed(); return this->value_.erase(pos); // segfaults if itValue == end() (doesn't exist), seems to be intended behaviour from std } /** - * Erase a range of values from the map. + * Erase a range of values from the map. Segfaults if any of the values are already non-existant. * @param first The first position to erase. * @param last The last position to erase. * @return An iterator to the next value. @@ -509,26 +507,21 @@ template class SafeUnorderedMap : public SafeBase { typename boost::unordered_flat_map::const_iterator last ) { for (auto it = first; it != last; ++it) { - auto itValue = this->value_.find((*it).first); - if (itValue != this->value_.end()) { + if (auto itValue = this->value_.find((*it).first); itValue != this->value_.end()) { this->copy_.try_emplace((*itValue).first, std::in_place, (*itValue).second); - } else { - this->copy_.try_emplace((*itValue).first, std::nullopt); } } markAsUsed(); return this->value_.erase(first, last); } /** - * Erase a value from the map, using a key. + * Erase a value from the map, using a key. Does nothing if the key is already non-existant. * @param key The key of the value to erase. * @return The number of values erased. */ typename boost::unordered_flat_map::size_type erase(const Key& key) { if (auto itValue = this->value_.find(key); itValue != this->value_.end()) { this->copy_.try_emplace((*itValue).first, std::in_place, (*itValue).second); - } else { - this->copy_.try_emplace((*itValue).first, std::nullopt); } markAsUsed(); return this->value_.erase(key); } diff --git a/src/core/state.cpp b/src/core/state.cpp index bab09491..2d53a602 100644 --- a/src/core/state.cpp +++ b/src/core/state.cpp @@ -51,7 +51,6 @@ State::State( } auto latestBlock = this->storage_.latest(); - // Insert the contract manager into the contracts_ map. this->contracts_[ProtocolContractAddresses.at("ContractManager")] = std::make_unique( db, this->contracts_, this->dumpManager_ ,this->options_ diff --git a/src/core/storage.cpp b/src/core/storage.cpp index 14e72150..903aa2d8 100644 --- a/src/core/storage.cpp +++ b/src/core/storage.cpp @@ -10,7 +10,7 @@ See the LICENSE.txt file in the project root for more information. #include "../utils/strconv.h" #include "../utils/uintconv.h" -static bool topicsMatch(const Event& event, const std::vector& topics) { +bool Storage::topicsMatch(const Event& event, const std::vector& topics) { if (topics.empty()) return true; // No topic filter applied const std::vector& eventTopics = event.getTopics(); if (eventTopics.size() < topics.size()) return false; @@ -18,7 +18,7 @@ static bool topicsMatch(const Event& event, const std::vector& topics) { return true; } -static void storeBlock(DB& db, const FinalizedBlock& block, bool indexingEnabled) { +void Storage::storeBlock(DB& db, const FinalizedBlock& block, bool indexingEnabled) { DBBatch batch; batch.push_back(block.getHash(), block.serializeBlock(), DBPrefix::blocks); batch.push_back(UintConv::uint64ToBytes(block.getNHeight()), block.getHash(), DBPrefix::heightToBlock); diff --git a/src/core/storage.h b/src/core/storage.h index 24c1748f..deaebda5 100644 --- a/src/core/storage.h +++ b/src/core/storage.h @@ -183,6 +183,22 @@ class Storage : public Log::LogicalLocationProvider { * @returns The indexing mode of the storage. */ inline IndexingMode getIndexingMode() const { return options_.getIndexingMode(); } + + /** + * Helper function for checking if an event has certain topics. + * @param event The event to check. + * @param topics A list of topics to check for. + * @return `true` if all topics match (or if no topics were provided), `false` otherwise. + */ + static bool topicsMatch(const Event& event, const std::vector& topics); + + /** + * Helper function for storing a block in the database. + * @param db Reference to the database. + * @param block The block to store. + * @param indexingEnabled Whether the node has indexing enabled or not. + */ + static void storeBlock(DB& db, const FinalizedBlock& block, bool indexingEnabled); }; #endif // STORAGE_H diff --git a/src/net/http/jsonrpc/parser.cpp b/src/net/http/jsonrpc/parser.cpp index 639da96f..1b911e2b 100644 --- a/src/net/http/jsonrpc/parser.cpp +++ b/src/net/http/jsonrpc/parser.cpp @@ -12,70 +12,43 @@ static inline const std::regex addressFormat{"^0x[0-9,a-f,A-F]{40}$"}; static inline const std::regex numberFormat{"^0x([1-9A-Fa-f]+[0-9A-Fa-f]*|0)$"}; namespace jsonrpc { - -Hash Parser::operator()(const json& data) const { - if (!data.is_string()) - throw Error::invalidType("string", data.type_name()); - - std::string rawData = data.get(); - - if (!std::regex_match(rawData, hashFormat)) - throw Error::invalidFormat(rawData); - - return Hash(Hex::toBytes(rawData)); -} - -Address Parser
::operator()(const json& data) const { - if (!data.is_string()) - throw Error::invalidType("string", data.type_name()); - - std::string rawData = data.get(); - - if (!std::regex_match(rawData, addressFormat)) - throw Error::invalidFormat(rawData); - - return Address(Hex::toBytes(rawData)); -} - -Bytes Parser::operator()(const json& data) const { - if (!data.is_string()) - throw Error::invalidType("string", data.type_name()); - - std::string rawData = data.get(); - - if (!Hex::isValid(rawData, true)) - throw Error::invalidFormat(rawData); - - return Hex::toBytes(rawData); -} - -bool Parser::operator()(const json& data) const { - if (!data.is_boolean()) - throw Error::invalidType("boolean", data.type_name()); - - return data.get(); -} - -float Parser::operator()(const json& data) const { - if (!data.is_number()) - throw Error::invalidType("number", data.type_name()); - - return data.get(); -} - -uint64_t Parser::operator()(const json& data) const { - if (data.is_number_unsigned()) - return data.get(); - - if (!data.is_string()) - throw Error::invalidType("string", data.type_name()); - - auto value = data.get(); - - if (!std::regex_match(value, numberFormat)) - throw Error::invalidFormat(value); - - return uint64_t(Hex(value).getUint()); -} - + Hash Parser::operator()(const json& data) const { + if (!data.is_string()) throw Error::invalidType("string", data.type_name()); + std::string rawData = data.get(); + if (!std::regex_match(rawData, hashFormat)) throw Error::invalidFormat(rawData); + return Hash(Hex::toBytes(rawData)); + } + + Address Parser
::operator()(const json& data) const { + if (!data.is_string()) throw Error::invalidType("string", data.type_name()); + std::string rawData = data.get(); + if (!std::regex_match(rawData, addressFormat)) throw Error::invalidFormat(rawData); + return Address(Hex::toBytes(rawData)); + } + + Bytes Parser::operator()(const json& data) const { + if (!data.is_string()) throw Error::invalidType("string", data.type_name()); + std::string rawData = data.get(); + if (!Hex::isValid(rawData, true)) throw Error::invalidFormat(rawData); + return Hex::toBytes(rawData); + } + + bool Parser::operator()(const json& data) const { + if (!data.is_boolean()) throw Error::invalidType("boolean", data.type_name()); + return data.get(); + } + + float Parser::operator()(const json& data) const { + if (!data.is_number()) throw Error::invalidType("number", data.type_name()); + return data.get(); + } + + uint64_t Parser::operator()(const json& data) const { + if (data.is_number_unsigned()) return data.get(); + if (!data.is_string()) throw Error::invalidType("string", data.type_name()); + auto value = data.get(); + if (!std::regex_match(value, numberFormat)) throw Error::invalidFormat(value); + return uint64_t(Hex(value).getUint()); + } } // namespace jsonrpc + diff --git a/src/net/http/jsonrpc/parser.h b/src/net/http/jsonrpc/parser.h index b060162f..e0a0c2a4 100644 --- a/src/net/http/jsonrpc/parser.h +++ b/src/net/http/jsonrpc/parser.h @@ -105,16 +105,19 @@ namespace jsonrpc { template struct Parser> { /** * Parse the JSON data as one of the containers. - * @param data The JSON object to be parsed. - * @return The containter with the variant type. + * @param data The JSON object to be parsed. Can be either an array (e.g. + * "[1,2,3]") or an object (e.g. "{{"a", 1}, {"b", 2}, {"c", 3}}"). + * Object keys are ignored altogether. + * @return The container with the variant type. */ std::vector operator()(const json& data) const { std::vector res; if (data.is_array()) { res.reserve(data.size()); - std::ranges::transform(data, std::back_inserter(res), [] (const json& elem) { return Parser{}(elem); }); + std::ranges::transform(data, std::back_inserter(res), [](const json& elem){ return Parser{}(elem); }); } else if (data.is_object()) { - res.emplace_back(Parser{}(data)); + res.reserve(data.size()); + for (auto& el : data.items()) res.emplace_back(Parser{}(el.value())); } else { throw Error::invalidType("array or object", data.type_name()); } diff --git a/src/net/p2p/encoding.cpp b/src/net/p2p/encoding.cpp index b43cd959..349fdd9b 100644 --- a/src/net/p2p/encoding.cpp +++ b/src/net/p2p/encoding.cpp @@ -21,27 +21,27 @@ namespace P2P { size_t index = 0; while (index < data.size()) { boost::asio::ip::address address; - if (data.size() - index < 2) { throw DynamicException("Invalid data size."); } + if (data.size() - index < 2) { throw DynamicException("Invalid data size (message too small)"); } auto nodeType = NodeType(UintConv::bytesToUint8(data.subspan(index, 1))); index += 1; uint8_t ipVersion = UintConv::bytesToUint8(data.subspan(index, 1)); index += 1; // Move index to IP address if (ipVersion == 0) { // V4 - if (data.size() - index < 4) { throw DynamicException("Invalid data size."); } + if (data.size() - index < 4) { throw DynamicException("Invalid data size (IPv4 too small)"); } BytesArr<4> ipBytes; std::copy(data.begin() + index, data.begin() + index + 4, ipBytes.begin()); address = boost::asio::ip::address_v4(ipBytes); index += 4; } else if (ipVersion == 1) { // V6 - if (data.size() - index < 16) { throw DynamicException("Invalid data size."); } + if (data.size() - index < 16) { throw DynamicException("Invalid data size (IPv6 too small)"); } BytesArr<16> ipBytes; std::copy(data.begin() + index, data.begin() + index + 16, ipBytes.begin()); address = boost::asio::ip::address_v6(ipBytes); index += 16; } else { - throw DynamicException("Invalid ip version."); + throw DynamicException("Invalid IP version (not v4 or v6)"); } - if (data.size() - index < 2) { throw DynamicException("Invalid data size."); } + if (data.size() - index < 2) { throw DynamicException("Invalid data size (missing port)"); } auto port = UintConv::bytesToUint16(data.subspan(index, 2)); nodes.insert({NodeID(address, port), nodeType}); index += 2; @@ -98,10 +98,10 @@ namespace P2P { std::vector blocks; size_t index = 0; while (index < data.size()) { - if (data.size() - index < 8) { throw DynamicException("Invalid data size."); } + if (data.size() - index < 8) { throw DynamicException("Invalid data size (data too small)"); } uint64_t blockSize = UintConv::bytesToUint64(data.subspan(index, 8)); index += 8; - if (data.size() - index < blockSize) { throw DynamicException("Invalid data size."); } + if (data.size() - index < blockSize) { throw DynamicException("Invalid data size (block too small)"); } bytes::View blockData = data.subspan(index, blockSize); index += blockSize; blocks.emplace_back(FinalizedBlock::fromBytes(blockData, requiredChainId)); @@ -129,10 +129,10 @@ namespace P2P { std::vector txs; size_t index = 0; while (index < data.size()) { - if (data.size() - index < 4) { throw DynamicException("Invalid data size."); } + if (data.size() - index < 4) { throw DynamicException("Invalid data size (msg too small)"); } uint32_t txSize = UintConv::bytesToUint32(data.subspan(index, 4)); index += 4; - if (data.size() - index < txSize) { throw DynamicException("Invalid data size."); } + if (data.size() - index < txSize) { throw DynamicException("Invalid data size (tx too small)"); } bytes::View txData = data.subspan(index, txSize); index += txSize; // Assuming requiredChainId is declared elsewhere diff --git a/src/utils/db.cpp b/src/utils/db.cpp index 0f84ad6d..109f3611 100644 --- a/src/utils/db.cpp +++ b/src/utils/db.cpp @@ -92,9 +92,7 @@ Bytes DB::getLastByPrefix(const Bytes& pfx) const { bool overflow; int i = static_cast(nextPfx.size()) - 1; // increment the given prefix by 1 - do { - overflow = (++nextPfx[i--]) == 0; - } while (overflow && i >= 0); + do { overflow = (++nextPfx[i--]) == 0; } while (overflow && i >= 0); if (overflow) [[unlikely]] { it->SeekToLast(); @@ -102,13 +100,10 @@ Bytes DB::getLastByPrefix(const Bytes& pfx) const { it->SeekForPrev(rocksdb::Slice(reinterpret_cast(nextPfx.data()), nextPfx.size())); } - if (!it->Valid()) { - return {}; - } + if (!it->Valid()) return {}; - if (!it->key().starts_with(rocksdb::Slice(reinterpret_cast(pfx.data()), pfx.size()))) { - return {}; - } + if (!it->key().starts_with(rocksdb::Slice(reinterpret_cast(pfx.data()), pfx.size()))) return {}; return Bytes(it->value().data(), it->value().data() + it->value().size()); } + diff --git a/src/utils/db.h b/src/utils/db.h index 61d00dff..399f65b7 100644 --- a/src/utils/db.h +++ b/src/utils/db.h @@ -264,7 +264,7 @@ class DB { /** * Get all entries from a given prefix. * @param bytesPfx The prefix to search for. - * @param keys (optional) A list of keys to search for. Defaults to an empty list. + * @param keys (optional) A list of keys to search for, WITHOUT the prefixes. Defaults to an empty list. * @return A list of found entries. */ std::vector getBatch( @@ -277,8 +277,8 @@ class DB { * (e.g. a query that returns millions of entries at once). * Prefix is automatically added to the queries themselves internally. * @param pfx The prefix to search keys from. - * @param start (optional) The first key to start searching from. Defaults to none. - * @param end (optional) The last key to end searching at. Defaults to none. + * @param start (optional) The first key to start searching from, WITHOUT the prefix. Defaults to none. + * @param end (optional) The last key to end searching at, WITHOUT the prefix. Defaults to none. * @return A list of found keys, WITHOUT their prefixes. */ std::vector getKeys(const Bytes& pfx, const Bytes& start = {}, const Bytes& end = {}) const; diff --git a/src/utils/intconv.cpp b/src/utils/intconv.cpp index 24b517a9..798afc21 100644 --- a/src/utils/intconv.cpp +++ b/src/utils/intconv.cpp @@ -88,8 +88,8 @@ int256_t IntConv::bytesToInt256(const bytes::View b) { } int136_t IntConv::bytesToInt136(const bytes::View b) { - if (b.size() != 18) throw DynamicException(std::string(__func__) - + ": Invalid bytes size - expected 18, got " + std::to_string(b.size()) + if (b.size() != 17) throw DynamicException(std::string(__func__) + + ": Invalid bytes size - expected 17, got " + std::to_string(b.size()) ); int136_t ret; boost::multiprecision::import_bits(ret, b.begin(), b.end(), 8); diff --git a/src/utils/jsonabi.cpp b/src/utils/jsonabi.cpp index 0ba8262e..aee36975 100644 --- a/src/utils/jsonabi.cpp +++ b/src/utils/jsonabi.cpp @@ -8,7 +8,7 @@ See the LICENSE.txt file in the project root for more information. #include "jsonabi.h" bool JsonAbi::isTuple(const std::string& type) { - // Tuples are always as "(type1, type2, ...)" (alternatively, "(type1,type2,...)[]"). + // Tuples are always as "(type1,type2,...)" (alternatively, "(type1,type2,...)[]"). // Check if first and last characters are "()" (not counting the "[]" at the end, if there are any). return (JsonAbi::isArray(type)) ? (type[0] == '(' && type[type.size() - 2 * JsonAbi::countTupleArrays(type) - 1] == ')') @@ -49,7 +49,7 @@ std::vector JsonAbi::getTupleTypes(const std::string& type) { types.push_back(tmp); tmp = ""; } else { - tmp += c; + if (c != ' ') tmp += c; // Prevent e.g. "(int, double)" second type coming out as " double" with the space in it } } // Push the last type and return. diff --git a/src/utils/jsonabi.h b/src/utils/jsonabi.h index a26e758a..598c0961 100644 --- a/src/utils/jsonabi.h +++ b/src/utils/jsonabi.h @@ -57,7 +57,7 @@ namespace JsonAbi { /** * Parse a given method output to a JSON object. - * @param outputDesc The output description of the method (std::pair). + * @param outputDesc The output description of the method (std::string). * Be aware that tuple types are concatenated into the string itself. * @return A JSON object containing the outputs of the method. */ diff --git a/src/utils/options.cpp b/src/utils/options.cpp index b9f07d75..532f1dce 100644 --- a/src/utils/options.cpp +++ b/src/utils/options.cpp @@ -23,13 +23,6 @@ IndexingMode::IndexingMode(std::string_view mode) { } } -constexpr std::string_view IndexingMode::toString() const { - if (*this == DISABLED) return "DISABLED"; - if (*this == RPC) return "RPC"; - if (*this == RPC_TRACE) return "RPC_TRACE"; - throw DynamicException("Unknown indexing mode"); -} - Options::Options( const std::string& rootPath, const std::string& web3clientVersion, const uint64_t& version, const uint64_t& chainID, const Address& chainOwner, diff --git a/src/utils/options.h.in b/src/utils/options.h.in index 36682253..daf0cc74 100644 --- a/src/utils/options.h.in +++ b/src/utils/options.h.in @@ -75,12 +75,13 @@ class IndexingMode { */ explicit IndexingMode(std::string_view mode); - /** - * Convert the internal value back to its string form. - * @return The setting as a string. - * @throw DynamicException if value doesn't match one of the values above. - */ - constexpr std::string_view toString() const; + /// Convert the internal value back to its string form. + constexpr std::string_view toString() const { + if (*this == DISABLED) return "DISABLED"; + if (*this == RPC) return "RPC"; + if (*this == RPC_TRACE) return "RPC_TRACE"; + std::unreachable(); + } /// Equality operator (default, as the internal value is just an int). constexpr bool operator==(const IndexingMode&) const = default; diff --git a/src/utils/strings.cpp b/src/utils/strings.cpp index ddc4f864..f4dda96b 100644 --- a/src/utils/strings.cpp +++ b/src/utils/strings.cpp @@ -10,11 +10,6 @@ See the LICENSE.txt file in the project root for more information. Hash::Hash(const uint256_t& data) : FixedBytes<32>(UintConv::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()); diff --git a/src/utils/strings.h b/src/utils/strings.h index e6a73bd8..c8557dea 100644 --- a/src/utils/strings.h +++ b/src/utils/strings.h @@ -174,12 +174,6 @@ class Hash : public FixedBytes<32> { */ 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. diff --git a/src/utils/tx.cpp b/src/utils/tx.cpp index ef3d318a..34030882 100644 --- a/src/utils/tx.cpp +++ b/src/utils/tx.cpp @@ -474,10 +474,7 @@ evmc_message TxBlock::txToMessage() const { // evmc_address code_address; // }; evmc_message msg; - if (this->to_ == Address()) - msg.kind = EVMC_CREATE; - else - msg.kind = EVMC_CALL; + msg.kind = (this->to_ == Address()) ? EVMC_CREATE : EVMC_CALL; msg.flags = 0; msg.depth = 1; msg.gas = static_cast(this->gasLimit_); diff --git a/src/utils/uintconv.cpp b/src/utils/uintconv.cpp index 713abb52..ad2a0f41 100644 --- a/src/utils/uintconv.cpp +++ b/src/utils/uintconv.cpp @@ -178,7 +178,7 @@ BytesArr<15> UintConv::uint120ToBytes(const uint120_t &i) { } BytesArr<14> UintConv::uint112ToBytes(const uint112_t &i) { - BytesArr<14> ret; + BytesArr<14> ret = {0}; Bytes tmp; tmp.reserve(14); boost::multiprecision::export_bits(i, std::back_inserter(tmp), 8); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fb55fd84..036dd100 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -12,21 +12,26 @@ set (TESTS_SOURCES ${CMAKE_SOURCE_DIR}/tests/utils/block_throw.cpp ${CMAKE_SOURCE_DIR}/tests/utils/db.cpp ${CMAKE_SOURCE_DIR}/tests/utils/ecdsa.cpp + ${CMAKE_SOURCE_DIR}/tests/utils/evmcconv.cpp ${CMAKE_SOURCE_DIR}/tests/utils/hex.cpp + ${CMAKE_SOURCE_DIR}/tests/utils/jsonabi.cpp ${CMAKE_SOURCE_DIR}/tests/utils/merkle.cpp ${CMAKE_SOURCE_DIR}/tests/utils/randomgen.cpp ${CMAKE_SOURCE_DIR}/tests/utils/strings.cpp ${CMAKE_SOURCE_DIR}/tests/utils/tx.cpp ${CMAKE_SOURCE_DIR}/tests/utils/tx_throw.cpp ${CMAKE_SOURCE_DIR}/tests/utils/utils.cpp + ${CMAKE_SOURCE_DIR}/tests/utils/safehash.cpp ${CMAKE_SOURCE_DIR}/tests/utils/options.cpp ${CMAKE_SOURCE_DIR}/tests/utils/dynamicexception.cpp ${CMAKE_SOURCE_DIR}/tests/utils/strconv.cpp ${CMAKE_SOURCE_DIR}/tests/utils/uintconv.cpp + ${CMAKE_SOURCE_DIR}/tests/utils/intconv.cpp ${CMAKE_SOURCE_DIR}/tests/contract/abi.cpp + ${CMAKE_SOURCE_DIR}/tests/contract/event.cpp ${CMAKE_SOURCE_DIR}/tests/contract/erc20.cpp ${CMAKE_SOURCE_DIR}/tests/contract/erc20wrapper.cpp - ${CMAKE_SOURCE_DIR}/tests/contract/erc721.cpp + ${CMAKE_SOURCE_DIR}/tests/contract/erc721test.cpp ${CMAKE_SOURCE_DIR}/tests/contract/nativewrapper.cpp ${CMAKE_SOURCE_DIR}/tests/contract/contractabigenerator.cpp ${CMAKE_SOURCE_DIR}/tests/contract/dexv2.cpp @@ -36,24 +41,18 @@ set (TESTS_SOURCES ${CMAKE_SOURCE_DIR}/tests/contract/calltracer.cpp ${CMAKE_SOURCE_DIR}/tests/contract/createcontract.cpp ${CMAKE_SOURCE_DIR}/tests/contract/pebble.cpp - ${CMAKE_SOURCE_DIR}/tests/contract/variables/safeuint_t.cpp - ${CMAKE_SOURCE_DIR}/tests/contract/variables/safeint_t.cpp - ${CMAKE_SOURCE_DIR}/tests/contract/variables/safestring.cpp - ${CMAKE_SOURCE_DIR}/tests/contract/variables/safeaddress.cpp - ${CMAKE_SOURCE_DIR}/tests/contract/variables/safeunorderedmap.cpp - ${CMAKE_SOURCE_DIR}/tests/contract/variables/safebool.cpp - ${CMAKE_SOURCE_DIR}/tests/contract/variables/safebytes.cpp - ${CMAKE_SOURCE_DIR}/tests/contract/variables/safevector.cpp - ${CMAKE_SOURCE_DIR}/tests/contract/variables/safearray.cpp - ${CMAKE_SOURCE_DIR}/tests/contract/variables/safetuple.cpp + ${CMAKE_SOURCE_DIR}/tests/contract/throwtest.cpp ${CMAKE_SOURCE_DIR}/tests/core/rdpos.cpp ${CMAKE_SOURCE_DIR}/tests/core/storage.cpp ${CMAKE_SOURCE_DIR}/tests/core/state.cpp ${CMAKE_SOURCE_DIR}/tests/core/dumpmanager.cpp - # ${CMAKE_SOURCE_DIR}/tests/core/blockchain.cpp # TODO: Blockchain is failing due to rdPoSWorker. + #${CMAKE_SOURCE_DIR}/tests/core/blockchain.cpp # TODO: Blockchain is failing due to rdPoSWorker. + ${CMAKE_SOURCE_DIR}/tests/net/p2p/encoding.cpp + ${CMAKE_SOURCE_DIR}/tests/net/p2p/nodeinfo.cpp ${CMAKE_SOURCE_DIR}/tests/net/p2p/p2p.cpp - ${CMAKE_SOURCE_DIR}/tests/net/http/httpjsonrpc.cpp ${CMAKE_SOURCE_DIR}/tests/net/http/error.cpp + ${CMAKE_SOURCE_DIR}/tests/net/http/httpjsonrpc.cpp + ${CMAKE_SOURCE_DIR}/tests/net/http/parser.cpp ${CMAKE_SOURCE_DIR}/tests/sdktestsuite.cpp ) @@ -62,16 +61,16 @@ set (TESTS_SOURCES # TODO: change this when we take out boost multiprecision from the project for C23 BigInt if (BUILD_VARIABLES_TESTS) list(APPEND TESTS_SOURCES - ${CMAKE_SOURCE_DIR}/tests/contract/variables/safeuint_t.cpp - ${CMAKE_SOURCE_DIR}/tests/contract/variables/safeint_t.cpp - ${CMAKE_SOURCE_DIR}/tests/contract/variables/safestring.cpp ${CMAKE_SOURCE_DIR}/tests/contract/variables/safeaddress.cpp - ${CMAKE_SOURCE_DIR}/tests/contract/variables/safeunorderedmap.cpp + ${CMAKE_SOURCE_DIR}/tests/contract/variables/safearray.cpp ${CMAKE_SOURCE_DIR}/tests/contract/variables/safebool.cpp ${CMAKE_SOURCE_DIR}/tests/contract/variables/safebytes.cpp - ${CMAKE_SOURCE_DIR}/tests/contract/variables/safevector.cpp - ${CMAKE_SOURCE_DIR}/tests/contract/variables/safearray.cpp + ${CMAKE_SOURCE_DIR}/tests/contract/variables/safeint_t.cpp + ${CMAKE_SOURCE_DIR}/tests/contract/variables/safestring.cpp ${CMAKE_SOURCE_DIR}/tests/contract/variables/safetuple.cpp + ${CMAKE_SOURCE_DIR}/tests/contract/variables/safeuint_t.cpp + ${CMAKE_SOURCE_DIR}/tests/contract/variables/safeunorderedmap.cpp + ${CMAKE_SOURCE_DIR}/tests/contract/variables/safevector.cpp ) endif() diff --git a/tests/contract/calltracer.cpp b/tests/contract/calltracer.cpp index 65b89527..ea0465d3 100644 --- a/tests/contract/calltracer.cpp +++ b/tests/contract/calltracer.cpp @@ -83,225 +83,280 @@ struct UserWrapper { }; namespace TCallTracer { + TEST_CASE("CallTracer Tests", "[trace]") { + SECTION("Call Type Parsing") { + evmc_message msgCall; + evmc_message msgStaticCall; + evmc_message msgDelegateCall; + evmc_message msgInvalidCall; + msgCall.kind = EVMC_CALL; + msgStaticCall.kind = EVMC_CALL; + msgDelegateCall.kind = EVMC_DELEGATECALL; + msgInvalidCall.kind = evmc_call_kind(-1); + msgStaticCall.flags = EVMC_STATIC; + REQUIRE(trace::getCallType(msgCall) == trace::Call::Type::CALL); + REQUIRE(trace::getCallType(msgStaticCall) == trace::Call::Type::STATICCALL); + REQUIRE(trace::getCallType(msgDelegateCall) == trace::Call::Type::DELEGATECALL); + REQUIRE_THROWS(trace::getCallType(msgInvalidCall)); + } -TEST_CASE("CallTracer Tests", "[trace]") { - SECTION("EVM Single Call") { - SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("TestTraceContracts"); - - const Address contractAddress = sdk.deployBytecode(testBytecode); - - uint256_t res; - - sdk.callViewFunction(contractAddress, &TestWrapper::sum); - REQUIRE(res == 0); - - sdk.callFunction(contractAddress, &TestWrapper::add, uint256_t(33)); - - Hash txHash = getLatestTransactionHash(sdk.getStorage()); - std::optional callTrace = sdk.getStorage().getCallTrace(txHash); - REQUIRE(callTrace); - - REQUIRE(callTrace->type == trace::Call::Type::CALL); - REQUIRE(callTrace->from == sdk.getOptions().getChainOwner()); - REQUIRE(callTrace->to == contractAddress); - REQUIRE(callTrace->value == FixedBytes<32>()); - // TODO: gas and gasUsed? - // TODO: what are these 4 bytes prefix? - REQUIRE(FixedBytes<32>(callTrace->input | std::views::drop(4)) == FixedBytes<32>(UintConv::uint256ToBytes(uint256_t(33)))); - - REQUIRE(callTrace->output == Bytes()); - REQUIRE(callTrace->calls.empty()); - - res = sdk.callViewFunction(contractAddress, &TestWrapper::sum); - REQUIRE(res == 33); - - sdk.callFunction(contractAddress, &TestWrapper::addAndReturn, uint256_t(66)); - - txHash = getLatestTransactionHash(sdk.getStorage()); - callTrace = sdk.getStorage().getCallTrace(txHash); - REQUIRE(callTrace); - - REQUIRE(callTrace->type == trace::Call::Type::CALL); - REQUIRE(callTrace->from == sdk.getOptions().getChainOwner()); - REQUIRE(callTrace->to == contractAddress); - REQUIRE(callTrace->value == FixedBytes<32>()); - // TODO: gas and gasUsed? - // TODO: what are these 4 bytes prefix? - REQUIRE(FixedBytes<32>(callTrace->input | std::views::drop(4)) == FixedBytes<32>(UintConv::uint256ToBytes(uint256_t(66)))); - - REQUIRE(callTrace->output == Utils::makeBytes(UintConv::uint256ToBytes(uint256_t(99)))); - REQUIRE(callTrace->calls.empty()); - - res = sdk.callViewFunction(contractAddress, &TestWrapper::sum); - REQUIRE(res == 99); - } - - SECTION("EVM Nested Calls") { - SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("TestTraceContracts"); - const Address testContractAddress = sdk.deployBytecode(testBytecode); - const Address testProxyContractAddress = sdk.deployBytecode(testProxyBytecode); - - uint256_t res; - - res = sdk.callViewFunction(testContractAddress, &TestWrapper::sum); - REQUIRE(res == 0); + SECTION("EVM Single Call") { + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("TestTraceContracts"); + + const Address contractAddress = sdk.deployBytecode(testBytecode); + + uint256_t res; + + sdk.callViewFunction(contractAddress, &TestWrapper::sum); + REQUIRE(res == 0); + + sdk.callFunction(contractAddress, &TestWrapper::add, uint256_t(33)); + + Hash txHash = getLatestTransactionHash(sdk.getStorage()); + std::optional callTrace = sdk.getStorage().getCallTrace(txHash); + REQUIRE(callTrace); + REQUIRE(callTrace->type == trace::Call::Type::CALL); + REQUIRE(callTrace->from == sdk.getOptions().getChainOwner()); + REQUIRE(callTrace->to == contractAddress); + REQUIRE(callTrace->value == FixedBytes<32>()); + // TODO: gas and gasUsed? + // TODO: what are these 4 bytes prefix? + REQUIRE(FixedBytes<32>(callTrace->input | std::views::drop(4)) == FixedBytes<32>(UintConv::uint256ToBytes(uint256_t(33)))); + REQUIRE(callTrace->output == Bytes()); + REQUIRE(callTrace->calls.empty()); + + json callJson = callTrace->toJson(); + REQUIRE(callJson["type"] == "CALL"); + REQUIRE(callJson["from"] == "0x00dead00665771855a34155f5e7405489df2c3c6"); + REQUIRE(callJson["to"] == "0x5b41cef7f46a4a147e31150c3c5ffd077e54d0e1"); + REQUIRE(callJson["value"] == "0x0"); + REQUIRE(callJson["gas"] == "0x8727"); + REQUIRE(callJson["gasUsed"] == "0x6017"); + REQUIRE(callJson["input"] == "0x1003e2d20000000000000000000000000000000000000000000000000000000000000021"); + + res = sdk.callViewFunction(contractAddress, &TestWrapper::sum); + REQUIRE(res == 33); + + sdk.callFunction(contractAddress, &TestWrapper::addAndReturn, uint256_t(66)); + + txHash = getLatestTransactionHash(sdk.getStorage()); + callTrace = sdk.getStorage().getCallTrace(txHash); + REQUIRE(callTrace); + REQUIRE(callTrace->type == trace::Call::Type::CALL); + REQUIRE(callTrace->from == sdk.getOptions().getChainOwner()); + REQUIRE(callTrace->to == contractAddress); + REQUIRE(callTrace->value == FixedBytes<32>()); + // TODO: gas and gasUsed? + // TODO: what are these 4 bytes prefix? + REQUIRE(FixedBytes<32>(callTrace->input | std::views::drop(4)) == FixedBytes<32>(UintConv::uint256ToBytes(uint256_t(66)))); + REQUIRE(callTrace->output == Utils::makeBytes(UintConv::uint256ToBytes(uint256_t(99)))); + REQUIRE(callTrace->calls.empty()); + + json callJson2 = callTrace->toJson(); + REQUIRE(callJson2["type"] == "CALL"); + REQUIRE(callJson2["from"] == "0x00dead00665771855a34155f5e7405489df2c3c6"); + REQUIRE(callJson2["to"] == "0x5b41cef7f46a4a147e31150c3c5ffd077e54d0e1"); + REQUIRE(callJson2["value"] == "0x0"); + REQUIRE(callJson2["gas"] == "0x88a5"); + REQUIRE(callJson2["gasUsed"] == "0x6195"); + REQUIRE(callJson2["input"] == "0x4fa522db0000000000000000000000000000000000000000000000000000000000000042"); + REQUIRE(callJson2["output"] == "0x0000000000000000000000000000000000000000000000000000000000000063"); + + res = sdk.callViewFunction(contractAddress, &TestWrapper::sum); + REQUIRE(res == 99); + } - sdk.callFunction(testContractAddress, &TestWrapper::add, uint256_t(45)); + SECTION("EVM Nested Calls") { + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("TestTraceContracts"); + const Address testContractAddress = sdk.deployBytecode(testBytecode); + const Address testProxyContractAddress = sdk.deployBytecode(testProxyBytecode); - res = sdk.callViewFunction(testProxyContractAddress, &TestProxyWrapper::sumOf, testContractAddress); - REQUIRE(res == 45); + uint256_t res; - sdk.callFunction(testProxyContractAddress, &TestProxyWrapper::addToAndReturn, testContractAddress, uint256_t(55)); + res = sdk.callViewFunction(testContractAddress, &TestWrapper::sum); + REQUIRE(res == 0); - const Hash txHash = getLatestTransactionHash(sdk.getStorage()); - std::optional callTrace = sdk.getStorage().getCallTrace(txHash); - REQUIRE(callTrace); + sdk.callFunction(testContractAddress, &TestWrapper::add, uint256_t(45)); - REQUIRE(callTrace->type == trace::Call::Type::CALL); - REQUIRE(callTrace->from == sdk.getOptions().getChainOwner()); - REQUIRE(callTrace->to == testProxyContractAddress); - REQUIRE(callTrace->value == FixedBytes<32>()); - // TODO: gas and gasUsed - REQUIRE(Address(callTrace->input | std::views::drop(16) | std::views::take(20)) == testContractAddress); - REQUIRE(FixedBytes<32>(callTrace->input | std::views::drop(36)) == FixedBytes<32>(UintConv::uint256ToBytes(uint256_t(55)))); + res = sdk.callViewFunction(testProxyContractAddress, &TestProxyWrapper::sumOf, testContractAddress); + REQUIRE(res == 45); - REQUIRE(callTrace->output == Utils::makeBytes(UintConv::uint256ToBytes(uint256_t(100)))); - REQUIRE(callTrace->calls.size() == 1); + sdk.callFunction(testProxyContractAddress, &TestProxyWrapper::addToAndReturn, testContractAddress, uint256_t(55)); - const trace::Call& nestedCall = callTrace->calls.front(); + const Hash txHash = getLatestTransactionHash(sdk.getStorage()); + std::optional callTrace = sdk.getStorage().getCallTrace(txHash); + REQUIRE(callTrace); - REQUIRE(nestedCall.type == trace::Call::Type::CALL); - REQUIRE(nestedCall.from == testProxyContractAddress); - REQUIRE(nestedCall.to == testContractAddress); - REQUIRE(nestedCall.value == FixedBytes<32>()); - REQUIRE(FixedBytes<32>(nestedCall.input | std::views::drop(4)) == FixedBytes<32>(UintConv::uint256ToBytes(uint256_t(55)))); + REQUIRE(callTrace->type == trace::Call::Type::CALL); + REQUIRE(callTrace->from == sdk.getOptions().getChainOwner()); + REQUIRE(callTrace->to == testProxyContractAddress); + REQUIRE(callTrace->value == FixedBytes<32>()); + // TODO: gas and gasUsed + REQUIRE(Address(callTrace->input | std::views::drop(16) | std::views::take(20)) == testContractAddress); + REQUIRE(FixedBytes<32>(callTrace->input | std::views::drop(36)) == FixedBytes<32>(UintConv::uint256ToBytes(uint256_t(55)))); - REQUIRE(nestedCall.output == Utils::makeBytes(UintConv::uint256ToBytes(uint256_t(100)))); - REQUIRE(nestedCall.calls.empty()); + REQUIRE(callTrace->output == Utils::makeBytes(UintConv::uint256ToBytes(uint256_t(100)))); + REQUIRE(callTrace->calls.size() == 1); - res = sdk.callViewFunction(testContractAddress, &TestWrapper::sum); - REQUIRE(res == 100); - } + const trace::Call& nestedCall = callTrace->calls.front(); - SECTION("Native Contracts") { - SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("TestCallTracerOfErc20Wrapper"); + REQUIRE(nestedCall.type == trace::Call::Type::CALL); + REQUIRE(nestedCall.from == testProxyContractAddress); + REQUIRE(nestedCall.to == testContractAddress); + REQUIRE(nestedCall.value == FixedBytes<32>()); + REQUIRE(FixedBytes<32>(nestedCall.input | std::views::drop(4)) == FixedBytes<32>(UintConv::uint256ToBytes(uint256_t(55)))); - const Address erc20 = sdk.deployContract( - std::string("TestToken"), std::string("TST"), uint8_t(18), uint256_t("1000000000000000000") - ); + REQUIRE(nestedCall.output == Utils::makeBytes(UintConv::uint256ToBytes(uint256_t(100)))); + REQUIRE(nestedCall.calls.empty()); - const Address erc20Wrapper = sdk.deployContract(); - const Address owner = sdk.getChainOwnerAccount().address; - - for (const auto& [name, address] : sdk.getState().getCppContracts()) { - if (name == "ERC20") - REQUIRE(address == erc20); - else if (name == "ERC20Wrapper") - REQUIRE(address == erc20Wrapper); + res = sdk.callViewFunction(testContractAddress, &TestWrapper::sum); + REQUIRE(res == 100); } - const Hash approveTx = sdk.callFunction(erc20, &ERC20::approve, erc20Wrapper, uint256_t("500000000000000000")); - const Hash depositTx = sdk.callFunction(erc20Wrapper, &ERC20Wrapper::deposit, erc20, uint256_t("500000000000000000")); - - const auto approveCallTrace = sdk.getStorage().getCallTrace(approveTx); - const auto depositCallTrace = sdk.getStorage().getCallTrace(depositTx); - - REQUIRE(approveCallTrace); - REQUIRE(approveCallTrace->type == trace::Call::Type::CALL); - REQUIRE(approveCallTrace->status == trace::Status::SUCCEEDED); - REQUIRE(approveCallTrace->from == sdk.getOptions().getChainOwner()); - REQUIRE(approveCallTrace->to == erc20); - REQUIRE(approveCallTrace->value == FixedBytes<32>()); - REQUIRE(approveCallTrace->input == Hex::toBytes("0x095ea7b30000000000000000000000006d48fdfe009e309dd5c4e69dec87365bfa0c811900000000000000000000000000000000000000000000000006f05b59d3b20000")); - REQUIRE(approveCallTrace->output == Bytes()); - REQUIRE(approveCallTrace->calls.empty()); - - REQUIRE(depositCallTrace); - REQUIRE(depositCallTrace->type == trace::Call::Type::CALL); - REQUIRE(depositCallTrace->status == trace::Status::SUCCEEDED); - REQUIRE(depositCallTrace->from == sdk.getOptions().getChainOwner()); - REQUIRE(depositCallTrace->to == erc20Wrapper); - REQUIRE(depositCallTrace->value == FixedBytes<32>()); - REQUIRE(depositCallTrace->input == Hex::toBytes("0x47e7ef240000000000000000000000005b41cef7f46a4a147e31150c3c5ffd077e54d0e100000000000000000000000000000000000000000000000006f05b59d3b20000")); - REQUIRE(depositCallTrace->output == Bytes()); - REQUIRE(!depositCallTrace->calls.empty()); - REQUIRE(depositCallTrace->calls[0].type == trace::Call::Type::CALL); - REQUIRE(depositCallTrace->calls[0].status == trace::Status::SUCCEEDED); - REQUIRE(depositCallTrace->calls[0].from == erc20Wrapper); - REQUIRE(depositCallTrace->calls[0].to == erc20); - REQUIRE(depositCallTrace->calls[0].value == FixedBytes<32>()); - REQUIRE(depositCallTrace->calls[0].input == Hex::toBytes("0x23b872dd00000000000000000000000000dead00665771855a34155f5e7405489df2c3c60000000000000000000000006d48fdfe009e309dd5c4e69dec87365bfa0c811900000000000000000000000000000000000000000000000006f05b59d3b20000")); - REQUIRE(depositCallTrace->calls[0].output == Hex::toBytes("0x0000000000000000000000000000000000000000000000000000000000000001")); - REQUIRE(depositCallTrace->calls[0].calls.empty()); - } + SECTION("Native Contracts") { + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("TestCallTracerOfErc20Wrapper"); + + const Address erc20 = sdk.deployContract( + std::string("TestToken"), std::string("TST"), uint8_t(18), uint256_t("1000000000000000000") + ); + + const Address erc20Wrapper = sdk.deployContract(); + const Address owner = sdk.getChainOwnerAccount().address; + + for (const auto& [name, address] : sdk.getState().getCppContracts()) { + if (name == "ERC20") + REQUIRE(address == erc20); + else if (name == "ERC20Wrapper") + REQUIRE(address == erc20Wrapper); + } + + const Hash approveTx = sdk.callFunction(erc20, &ERC20::approve, erc20Wrapper, uint256_t("500000000000000000")); + const Hash depositTx = sdk.callFunction(erc20Wrapper, &ERC20Wrapper::deposit, erc20, uint256_t("500000000000000000")); + + const auto approveCallTrace = sdk.getStorage().getCallTrace(approveTx); + const auto depositCallTrace = sdk.getStorage().getCallTrace(depositTx); + + REQUIRE(approveCallTrace); + REQUIRE(approveCallTrace->type == trace::Call::Type::CALL); + REQUIRE(approveCallTrace->status == trace::Status::SUCCEEDED); + REQUIRE(approveCallTrace->from == sdk.getOptions().getChainOwner()); + REQUIRE(approveCallTrace->to == erc20); + REQUIRE(approveCallTrace->value == FixedBytes<32>()); + REQUIRE(approveCallTrace->input == Hex::toBytes("0x095ea7b30000000000000000000000006d48fdfe009e309dd5c4e69dec87365bfa0c811900000000000000000000000000000000000000000000000006f05b59d3b20000")); + REQUIRE(approveCallTrace->output == Bytes()); + REQUIRE(approveCallTrace->calls.empty()); + + REQUIRE(depositCallTrace); + REQUIRE(depositCallTrace->type == trace::Call::Type::CALL); + REQUIRE(depositCallTrace->status == trace::Status::SUCCEEDED); + REQUIRE(depositCallTrace->from == sdk.getOptions().getChainOwner()); + REQUIRE(depositCallTrace->to == erc20Wrapper); + REQUIRE(depositCallTrace->value == FixedBytes<32>()); + REQUIRE(depositCallTrace->input == Hex::toBytes("0x47e7ef240000000000000000000000005b41cef7f46a4a147e31150c3c5ffd077e54d0e100000000000000000000000000000000000000000000000006f05b59d3b20000")); + REQUIRE(depositCallTrace->output == Bytes()); + REQUIRE(!depositCallTrace->calls.empty()); + REQUIRE(depositCallTrace->calls[0].type == trace::Call::Type::CALL); + REQUIRE(depositCallTrace->calls[0].status == trace::Status::SUCCEEDED); + REQUIRE(depositCallTrace->calls[0].from == erc20Wrapper); + REQUIRE(depositCallTrace->calls[0].to == erc20); + REQUIRE(depositCallTrace->calls[0].value == FixedBytes<32>()); + REQUIRE(depositCallTrace->calls[0].input == Hex::toBytes("0x23b872dd00000000000000000000000000dead00665771855a34155f5e7405489df2c3c60000000000000000000000006d48fdfe009e309dd5c4e69dec87365bfa0c811900000000000000000000000000000000000000000000000006f05b59d3b20000")); + REQUIRE(depositCallTrace->calls[0].output == Hex::toBytes("0x0000000000000000000000000000000000000000000000000000000000000001")); + REQUIRE(depositCallTrace->calls[0].calls.empty()); + } - SECTION("Errors and payable functions") { - SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("TestCallTracingErrosAndPays"); - - const Address bankAddress = sdk.deployBytecode(bankBytecode); - const Address userAddress = sdk.deployBytecode(userBytecode); - - uint256_t res = sdk.callViewFunction(bankAddress, &BankWrapper::balance); - REQUIRE(res == 0); - - const Hash depositTxHash = sdk.callFunction(bankAddress, &BankWrapper::deposit, uint256_t(500)); - res = sdk.callViewFunction(bankAddress, &BankWrapper::balance); - REQUIRE(res == 500); - - const Hash invalidWithdrawTxHash = sdk.callFunction(userAddress, &UserWrapper::tryWithdraw, bankAddress, uint256_t(501)); - const Hash validWithdrawTxHash = sdk.callFunction(userAddress, &UserWrapper::tryWithdraw, bankAddress, uint256_t(300)); - const Hash payTxHash = sdk.callFunction(bankAddress, &BankWrapper::pay, uint256_t(4568)); - - const auto errorCallTrace = sdk.getStorage().getCallTrace(invalidWithdrawTxHash); - const auto successCallTrace = sdk.getStorage().getCallTrace(validWithdrawTxHash); - const auto payCallTrace = sdk.getStorage().getCallTrace(payTxHash); - - REQUIRE(errorCallTrace); - REQUIRE(errorCallTrace->type == trace::Call::Type::CALL); - REQUIRE(errorCallTrace->status == trace::Status::SUCCEEDED); - REQUIRE(errorCallTrace->from == sdk.getOptions().getChainOwner()); - REQUIRE(errorCallTrace->to == userAddress); - REQUIRE(errorCallTrace->value == FixedBytes<32>()); - REQUIRE(errorCallTrace->input == Hex::toBytes("0x7f3358bc0000000000000000000000005b41cef7f46a4a147e31150c3c5ffd077e54d0e100000000000000000000000000000000000000000000000000000000000001f5")); - REQUIRE(errorCallTrace->output == Bytes(32)); - REQUIRE(!errorCallTrace->calls.empty()); - REQUIRE(errorCallTrace->calls[0].type == trace::Call::Type::CALL); - REQUIRE(errorCallTrace->calls[0].status == trace::Status::EXECUTION_REVERTED); - REQUIRE(errorCallTrace->calls[0].from == userAddress); - REQUIRE(errorCallTrace->calls[0].to == bankAddress); - REQUIRE(errorCallTrace->calls[0].value == FixedBytes<32>()); - REQUIRE(errorCallTrace->calls[0].input == Hex::toBytes("0x2e1a7d4d00000000000000000000000000000000000000000000000000000000000001f5")); - REQUIRE(errorCallTrace->calls[0].output == trace::encodeRevertReason("Insufficient funds")); - REQUIRE(errorCallTrace->calls[0].calls.empty()); - - REQUIRE(successCallTrace); - REQUIRE(successCallTrace->type == trace::Call::Type::CALL); - REQUIRE(successCallTrace->status == trace::Status::SUCCEEDED); - REQUIRE(successCallTrace->from == sdk.getOptions().getChainOwner()); - REQUIRE(successCallTrace->to == userAddress); - REQUIRE(successCallTrace->value == FixedBytes<32>()); - REQUIRE(successCallTrace->input == Hex::toBytes("0x7f3358bc0000000000000000000000005b41cef7f46a4a147e31150c3c5ffd077e54d0e1000000000000000000000000000000000000000000000000000000000000012c")); - REQUIRE(successCallTrace->output == Hex::toBytes("0x0000000000000000000000000000000000000000000000000000000000000001")); - REQUIRE(!successCallTrace->calls.empty()); - REQUIRE(successCallTrace->calls[0].type == trace::Call::Type::CALL); - REQUIRE(successCallTrace->calls[0].status == trace::Status::SUCCEEDED); - REQUIRE(successCallTrace->calls[0].from == userAddress); - REQUIRE(successCallTrace->calls[0].to == bankAddress); - REQUIRE(successCallTrace->calls[0].value == FixedBytes<32>()); - REQUIRE(successCallTrace->calls[0].input == Hex::toBytes("0x2e1a7d4d000000000000000000000000000000000000000000000000000000000000012c")); - REQUIRE(successCallTrace->calls[0].output == Bytes()); - REQUIRE(successCallTrace->calls[0].calls.empty()); - - REQUIRE(payCallTrace); - REQUIRE(payCallTrace->type == trace::Call::Type::CALL); - REQUIRE(payCallTrace->status == trace::Status::SUCCEEDED); - REQUIRE(payCallTrace->from == sdk.getOptions().getChainOwner()); - REQUIRE(payCallTrace->to == bankAddress); - REQUIRE(payCallTrace->value == FixedBytes<32>(UintConv::uint256ToBytes(uint256_t(4568)))); - REQUIRE(payCallTrace->input == Hex::toBytes("0x1b9265b8")); - REQUIRE(payCallTrace->output == Bytes()); - REQUIRE(payCallTrace->calls.empty()); + SECTION("Errors and payable functions") { + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("TestCallTracingErrosAndPays"); + + const Address bankAddress = sdk.deployBytecode(bankBytecode); + const Address userAddress = sdk.deployBytecode(userBytecode); + + uint256_t res = sdk.callViewFunction(bankAddress, &BankWrapper::balance); + REQUIRE(res == 0); + + const Hash depositTxHash = sdk.callFunction(bankAddress, &BankWrapper::deposit, uint256_t(500)); + res = sdk.callViewFunction(bankAddress, &BankWrapper::balance); + REQUIRE(res == 500); + + const Hash invalidWithdrawTxHash = sdk.callFunction(userAddress, &UserWrapper::tryWithdraw, bankAddress, uint256_t(501)); + const Hash validWithdrawTxHash = sdk.callFunction(userAddress, &UserWrapper::tryWithdraw, bankAddress, uint256_t(300)); + const Hash payTxHash = sdk.callFunction(bankAddress, &BankWrapper::pay, uint256_t(4568)); + + const auto errorCallTrace = sdk.getStorage().getCallTrace(invalidWithdrawTxHash); + const auto successCallTrace = sdk.getStorage().getCallTrace(validWithdrawTxHash); + const auto payCallTrace = sdk.getStorage().getCallTrace(payTxHash); + + Bytes reasonInsufficientFunds = trace::encodeRevertReason("Insufficient funds"); + REQUIRE(trace::decodeRevertReason(reasonInsufficientFunds) == "Insufficient funds"); + REQUIRE_THROWS(trace::decodeRevertReason(Bytes{0x00, 0x01, 0x02, 0x03, 0x04})); // Data has to be exactly 100 bytes + + REQUIRE(errorCallTrace); + REQUIRE(errorCallTrace->type == trace::Call::Type::CALL); + REQUIRE(errorCallTrace->status == trace::Status::SUCCEEDED); + REQUIRE(errorCallTrace->from == sdk.getOptions().getChainOwner()); + REQUIRE(errorCallTrace->to == userAddress); + REQUIRE(errorCallTrace->value == FixedBytes<32>()); + REQUIRE(errorCallTrace->input == Hex::toBytes("0x7f3358bc0000000000000000000000005b41cef7f46a4a147e31150c3c5ffd077e54d0e100000000000000000000000000000000000000000000000000000000000001f5")); + REQUIRE(errorCallTrace->output == Bytes(32)); + REQUIRE(!errorCallTrace->calls.empty()); + REQUIRE(errorCallTrace->calls[0].type == trace::Call::Type::CALL); + REQUIRE(errorCallTrace->calls[0].status == trace::Status::EXECUTION_REVERTED); + REQUIRE(errorCallTrace->calls[0].from == userAddress); + REQUIRE(errorCallTrace->calls[0].to == bankAddress); + REQUIRE(errorCallTrace->calls[0].value == FixedBytes<32>()); + REQUIRE(errorCallTrace->calls[0].input == Hex::toBytes("0x2e1a7d4d00000000000000000000000000000000000000000000000000000000000001f5")); + REQUIRE(errorCallTrace->calls[0].output == reasonInsufficientFunds); + REQUIRE(errorCallTrace->calls[0].calls.empty()); + + json errorJson = errorCallTrace->toJson(); + REQUIRE(errorJson["type"] == "CALL"); + REQUIRE(errorJson["from"] == "0x00dead00665771855a34155f5e7405489df2c3c6"); + REQUIRE(errorJson["to"] == "0x6d48fdfe009e309dd5c4e69dec87365bfa0c8119"); + REQUIRE(errorJson["value"] == "0x0"); + REQUIRE(errorJson["gas"] == "0x958b"); + REQUIRE(errorJson["gasUsed"] == "0x6e7b"); + REQUIRE(errorJson["input"] == "0x7f3358bc0000000000000000000000005b41cef7f46a4a147e31150c3c5ffd077e54d0e100000000000000000000000000000000000000000000000000000000000001f5"); + REQUIRE(errorJson.contains("calls")); + REQUIRE(!errorJson["calls"].empty()); + json errorJsonCall = errorJson["calls"][0]; + REQUIRE(errorJsonCall["type"] == "CALL"); + REQUIRE(errorJsonCall["from"] == "0x6d48fdfe009e309dd5c4e69dec87365bfa0c8119"); + REQUIRE(errorJsonCall["to"] == "0x5b41cef7f46a4a147e31150c3c5ffd077e54d0e1"); + REQUIRE(errorJsonCall["value"] == "0x0"); + REQUIRE(errorJsonCall["gas"] == "0x8f18"); + REQUIRE(errorJsonCall["gasUsed"] == "0x16bb"); + REQUIRE(errorJsonCall["input"] == "0x2e1a7d4d00000000000000000000000000000000000000000000000000000000000001f5"); + REQUIRE(errorJsonCall["output"] == "0x08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012496e73756666696369656e742066756e64730000000000000000000000000000"); + REQUIRE(errorJsonCall["error"] == "execution reverted"); + REQUIRE(errorJsonCall["revertReason"] == "Insufficient funds"); + + REQUIRE(successCallTrace); + REQUIRE(successCallTrace->type == trace::Call::Type::CALL); + REQUIRE(successCallTrace->status == trace::Status::SUCCEEDED); + REQUIRE(successCallTrace->from == sdk.getOptions().getChainOwner()); + REQUIRE(successCallTrace->to == userAddress); + REQUIRE(successCallTrace->value == FixedBytes<32>()); + REQUIRE(successCallTrace->input == Hex::toBytes("0x7f3358bc0000000000000000000000005b41cef7f46a4a147e31150c3c5ffd077e54d0e1000000000000000000000000000000000000000000000000000000000000012c")); + REQUIRE(successCallTrace->output == Hex::toBytes("0x0000000000000000000000000000000000000000000000000000000000000001")); + REQUIRE(!successCallTrace->calls.empty()); + REQUIRE(successCallTrace->calls[0].type == trace::Call::Type::CALL); + REQUIRE(successCallTrace->calls[0].status == trace::Status::SUCCEEDED); + REQUIRE(successCallTrace->calls[0].from == userAddress); + REQUIRE(successCallTrace->calls[0].to == bankAddress); + REQUIRE(successCallTrace->calls[0].value == FixedBytes<32>()); + REQUIRE(successCallTrace->calls[0].input == Hex::toBytes("0x2e1a7d4d000000000000000000000000000000000000000000000000000000000000012c")); + REQUIRE(successCallTrace->calls[0].output == Bytes()); + REQUIRE(successCallTrace->calls[0].calls.empty()); + + REQUIRE(payCallTrace); + REQUIRE(payCallTrace->type == trace::Call::Type::CALL); + REQUIRE(payCallTrace->status == trace::Status::SUCCEEDED); + REQUIRE(payCallTrace->from == sdk.getOptions().getChainOwner()); + REQUIRE(payCallTrace->to == bankAddress); + REQUIRE(payCallTrace->value == FixedBytes<32>(UintConv::uint256ToBytes(uint256_t(4568)))); + REQUIRE(payCallTrace->input == Hex::toBytes("0x1b9265b8")); + REQUIRE(payCallTrace->output == Bytes()); + REQUIRE(payCallTrace->calls.empty()); + } } -} - } // namespace TCallTracer diff --git a/tests/contract/dexv2.cpp b/tests/contract/dexv2.cpp index 7d18e3e9..5b191343 100644 --- a/tests/contract/dexv2.cpp +++ b/tests/contract/dexv2.cpp @@ -7,6 +7,7 @@ See the LICENSE.txt file in the project root for more information. #include "../../src/libs/catch2/catch_amalgamated.hpp" +#include "../../src/contract/templates/dexv2/dexv2pair.h" #include "../../src/contract/templates/dexv2/dexv2factory.h" #include "../../src/contract/templates/dexv2/dexv2router02.h" #include "../../src/contract/templates/nativewrapper.h" @@ -14,24 +15,112 @@ See the LICENSE.txt file in the project root for more information. #include "../sdktestsuite.hpp" // TODO: test events if/when implemented +// TODO: implement the rest of the coverage: +// swapExactTokensForTokens, swapTokensForExactTokens, +// swapExactNativeForTokens, swapTokensForExactNative, +// swapExactTokensForNative, swapNativeForExactTokens +// also whatever's left of DEXV2Pair if applicable + +namespace TUQ112x112 { + TEST_CASE("UQ112x112 Namespace Test", "[contract][dexv2][uq112x112]") { + SECTION("UQ112x112 Coverage") { + // Q112 = 5192296858534827628530496329220096 + uint224_t enc = UQ112x112::encode(1024); + REQUIRE(enc == uint224_t("5316911983139663491615228241121378304")); + uint224_t div = UQ112x112::uqdiv(uint224_t("123456789000"), uint112_t("1234567890")); + REQUIRE(div == uint224_t(100)); + } + } +} namespace TDEXV2 { - TEST_CASE("DEXV2 Test", "[contract][dexv2]") { - SECTION("Deploy DEXV2Router/Factory with a single pair") { - SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testDEXV2SinglePair"); - Address wrapped = sdk.deployContract(std::string("WSPARQ"), std::string("WSPARQ"), uint8_t(18)); - Address factory = sdk.deployContract(Address()); - Address router = sdk.deployContract(factory, wrapped); - Address owner = sdk.getChainOwnerAccount().address; + TEST_CASE("DEXV2 Pair Test", "[contract][dexv2][dexv2pair]") { + SECTION("Deploy + Dump DEXV2Pair") { + Address pair; + Address tokenA; + Address tokenB; + Address chainOwner("0x00dead00665771855a34155f5e7405489df2c3c6", false); + std::unique_ptr options; + { + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testDEXV2Pair"); + pair = sdk.deployContract(); + tokenA = sdk.deployContract( + std::string("TestTokenA"), std::string("TSTA"), uint8_t(18), uint256_t("1000000000000000000") + ); + tokenB = sdk.deployContract( + std::string("TestTokenB"), std::string("TSTB"), uint8_t(18), uint256_t("1000000000000000000") + ); + sdk.callFunction(pair, &DEXV2Pair::initialize, tokenA, tokenB); + REQUIRE(sdk.callViewFunction(pair, &DEXV2Pair::factory) == chainOwner); + REQUIRE(sdk.callViewFunction(pair, &DEXV2Pair::token0) == tokenA); + REQUIRE(sdk.callViewFunction(pair, &DEXV2Pair::token1) == tokenB); + REQUIRE(sdk.callViewFunction(pair, &DEXV2Pair::price0CumulativeLast) == 0); + REQUIRE(sdk.callViewFunction(pair, &DEXV2Pair::price1CumulativeLast) == 0); + REQUIRE(sdk.callViewFunction(pair, &DEXV2Pair::kLast) == 0); + std::tuple reservesOutput = sdk.callViewFunction(pair, &DEXV2Pair::getReserves); + REQUIRE(std::get<0>(reservesOutput) == 0); + REQUIRE(std::get<1>(reservesOutput) == 0); + REQUIRE(std::get<2>(reservesOutput) == 0); + + // Dump to database + options = std::make_unique(sdk.getOptions()); + sdk.getState().saveToDB(); + } + + // SDKTestSuite should automatically load the state from the DB if we construct it with an Options object + // (The createNewEnvironment DELETES the DB if any is found) + SDKTestSuite sdk(*options); + REQUIRE(sdk.callViewFunction(pair, &DEXV2Pair::factory) == chainOwner); + REQUIRE(sdk.callViewFunction(pair, &DEXV2Pair::token0) == tokenA); + REQUIRE(sdk.callViewFunction(pair, &DEXV2Pair::token1) == tokenB); + REQUIRE(sdk.callViewFunction(pair, &DEXV2Pair::price0CumulativeLast) == 0); + REQUIRE(sdk.callViewFunction(pair, &DEXV2Pair::price1CumulativeLast) == 0); + REQUIRE(sdk.callViewFunction(pair, &DEXV2Pair::kLast) == 0); + std::tuple reservesOutput = sdk.callViewFunction(pair, &DEXV2Pair::getReserves); + Utils::safePrintTest("DEXV2 Testing Reserves: " + std::get<0>(reservesOutput).str() + ", " + std::get<1>(reservesOutput).str() + ", " + std::get<2>(reservesOutput).str()); + REQUIRE(std::get<0>(reservesOutput) == 0); + REQUIRE(std::get<1>(reservesOutput) == 0); + REQUIRE(std::get<2>(reservesOutput) == 0); + } + } + + TEST_CASE("DEXV2 Router Test", "[contract][dexv2][dexv2router]") { + SECTION("Deploy + Dump DEXV2Router/Factory with a single pair") { + Address wrapped; + Address factory; + Address router; + std::unique_ptr options; + { + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testDEXV2RouterSinglePair"); + wrapped = sdk.deployContract(std::string("WSPARQ"), std::string("WSPARQ"), uint8_t(18)); + factory = sdk.deployContract(Address()); + router = sdk.deployContract(factory, wrapped); + for (const auto& contract : sdk.getState().getCppContracts()) { + if (contract.first == "NativeWrapper") REQUIRE(contract.second == wrapped); + if (contract.first == "DEXV2Factory") REQUIRE(contract.second == factory); + if (contract.first == "DEXV2Router02") REQUIRE(contract.second == router); + } + // Dump to database + options = std::make_unique(sdk.getOptions()); + sdk.getState().saveToDB(); + } + + // SDKTestSuite should automatically load the state from the DB if we construct it with an Options object + // (The createNewEnvironment DELETES the DB if any is found) + SDKTestSuite sdk(*options); for (const auto& contract : sdk.getState().getCppContracts()) { if (contract.first == "NativeWrapper") REQUIRE(contract.second == wrapped); if (contract.first == "DEXV2Factory") REQUIRE(contract.second == factory); if (contract.first == "DEXV2Router02") REQUIRE(contract.second == router); } + + // For coverage + REQUIRE(sdk.callViewFunction(router, &DEXV2Router02::factory) == factory); + REQUIRE(sdk.callViewFunction(router, &DEXV2Router02::wrappedNative) == wrapped); } - SECTION("Deploy DEXV2 and add liquidity to token/token pair") { - SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testDEXV2LiqTokenTokenPair"); + SECTION("Deploy DEXV2 and add/remove liquidity to token/token pair") { + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testDEXV2RouterLiqTokenTokenPair"); Address tokenA = sdk.deployContract(std::string("TokenA"), std::string("TKNA"), uint8_t(18), uint256_t("10000000000000000000000")); Address tokenB = sdk.deployContract(std::string("TokenB"), std::string("TKNB"), uint8_t(18), uint256_t("10000000000000000000000")); Address wrapped = sdk.deployContract(std::string("WSPARQ"), std::string("WSPARQ"), uint8_t(18)); @@ -45,7 +134,7 @@ namespace TDEXV2 { } // Approve "router" so it can spend up to 10000 tokens from both sides - // on behalf of "owner" (which already has the tokens) + // on behalf of "owner" (which already has the tokens). Hash approveATx = sdk.callFunction(tokenA, &ERC20::approve, router, uint256_t("10000000000000000000000")); Hash approveBTx = sdk.callFunction(tokenB, &ERC20::approve, router, uint256_t("10000000000000000000000")); REQUIRE(sdk.callViewFunction(tokenA, &ERC20::allowance, owner, router) == uint256_t("10000000000000000000000")); @@ -58,7 +147,7 @@ namespace TDEXV2 { std::chrono::system_clock::now().time_since_epoch() ).count() + 60000000; // 60 seconds // tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin, to, deadline - Hash addLiqudityTx = sdk.callFunction(router, &DEXV2Router02::addLiquidity, + Hash addLiquidityTx = sdk.callFunction(router, &DEXV2Router02::addLiquidity, tokenA, tokenB, uint256_t("100000000000000000000"), uint256_t("250000000000000000000"), uint256_t(0), uint256_t(0), owner, deadline ); @@ -73,10 +162,50 @@ namespace TDEXV2 { REQUIRE(ownerTknB == uint256_t("9750000000000000000000")); REQUIRE(pairTknA == uint256_t("100000000000000000000")); REQUIRE(pairTknB == uint256_t("250000000000000000000")); + + // Approve "pair" so it can allow up to 10000 liquidity tokens to be + // withdrawn by the "owner" (which has much less than that) + Hash approvePairTx = sdk.callFunction(pair, &ERC20::approve, router, uint256_t("10000000000000000000000")); + REQUIRE(sdk.callViewFunction(pair, &ERC20::allowance, owner, router) == uint256_t("10000000000000000000000")); + REQUIRE(sdk.callViewFunction(pair, &ERC20::balanceOf, owner) == uint256_t("158113883008418965599")); + + // Remove 50 liquidity tokens from the pair + deadline = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch() + ).count() + 60000000; // 60 seconds + // tokenA, tokenB, liquidity, amountAMin, amountBMin, to, deadline + Hash removeLiquidityTx = sdk.callFunction(router, &DEXV2Router02::removeLiquidity, + tokenA, tokenB, uint256_t("50000000000000000000"), uint256_t(0), uint256_t(0), owner, deadline + ); + + // Check if operation worked successfully + pair = sdk.callViewFunction(factory, &DEXV2Factory::getPair, tokenA, tokenB); + ownerTknA = sdk.callViewFunction(tokenA, &ERC20::balanceOf, owner); + ownerTknB = sdk.callViewFunction(tokenB, &ERC20::balanceOf, owner); + pairTknA = sdk.callViewFunction(tokenA, &ERC20::balanceOf, pair); + pairTknB = sdk.callViewFunction(tokenB, &ERC20::balanceOf, pair); + REQUIRE(ownerTknA == uint256_t("9931622776601683793320")); + REQUIRE(ownerTknB == uint256_t("9829056941504209483300")); + REQUIRE(pairTknA == uint256_t("68377223398316206680")); + REQUIRE(pairTknB == uint256_t("170943058495790516700")); + + // For coverage (ensure() and throws on removeLiquidity()) + REQUIRE_THROWS(sdk.callFunction(router, &DEXV2Router02::removeLiquidity, + tokenA, tokenB, uint256_t("5000000000000000000"), + uint256_t(0), uint256_t(0), owner, uint256_t(0) // deadline always expired + )); + REQUIRE_THROWS(sdk.callFunction(router, &DEXV2Router02::removeLiquidity, + tokenA, tokenB, uint256_t("5000000000000000000"), + uint256_t("500000000000000000000"), uint256_t(0), owner, deadline // insufficient amountA (500) + )); + REQUIRE_THROWS(sdk.callFunction(router, &DEXV2Router02::removeLiquidity, + tokenA, tokenB, uint256_t("5000000000000000000"), + uint256_t(0), uint256_t("500000000000000000000"), owner, deadline // insufficient amountB (500) + )); } - SECTION("Deploy DEXV2 and add liquidity to token/native pair") { - SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testDEXV2LiqTokenNativePair"); + SECTION("Deploy DEXV2 and add/remove liquidity to token/native pair") { + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testDEXV2RouterLiqTokenNativePair"); Address tokenA = sdk.deployContract(std::string("TokenA"), std::string("TKNA"), uint8_t(18), uint256_t("10000000000000000000000")); Address wrapped = sdk.deployContract(std::string("WSPARQ"), std::string("WSPARQ"), uint8_t(18)); Address factory = sdk.deployContract(Address()); @@ -99,14 +228,13 @@ namespace TDEXV2 { std::chrono::system_clock::now().time_since_epoch() ).count() + 60000000; // 60 seconds // token, amountTokenDesired, amountTokenMin, amountNativeMin, to, deadline - Hash addLiqudityTx = sdk.callFunction( + Hash addLiquidityTx = sdk.callFunction( router, uint256_t("100000000000000000000"), &DEXV2Router02::addLiquidityNative, tokenA, uint256_t("100000000000000000000"), uint256_t("100000000000000000000"), uint256_t("100000000000000000000"), owner, deadline ); // Check if operation worked successfully - // SDKTestSuite::createNewTx() is adding a tx fee of 21000 gwei, thus * 2 Address pair = sdk.callViewFunction(factory, &DEXV2Factory::getPair, tokenA, wrapped); uint256_t ownerTknA = sdk.callViewFunction(tokenA, &ERC20::balanceOf, owner); uint256_t ownerNative = sdk.getNativeBalance(owner); @@ -118,6 +246,37 @@ namespace TDEXV2 { REQUIRE(pairTknA == uint256_t("100000000000000000000")); REQUIRE(wrappedNative == uint256_t("100000000000000000000")); REQUIRE(pairNativeWrapped == uint256_t("100000000000000000000")); + + // Approve "pair" so it can allow up to 10000 liquidity tokens to be + // withdrawn by the "owner" (which has much less than that) + Hash approvePairTx = sdk.callFunction(pair, &ERC20::approve, router, uint256_t("10000000000000000000000")); + REQUIRE(sdk.callViewFunction(pair, &ERC20::allowance, owner, router) == uint256_t("10000000000000000000000")); + REQUIRE(sdk.callViewFunction(pair, &ERC20::balanceOf, owner) == uint256_t("99999999999999999000")); + + uint256_t ownerNativeBeforeSubLiq = sdk.getNativeBalance(owner); + // Remove 50 liquidity tokens + deadline = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch() + ).count() + 60000000; // 60 seconds + // token, liquidity, amountTokenMin, amountNativeMin, to, deadline + Hash removeLiquidityTx = sdk.callFunction( + router, uint256_t("100000000000000000000"), &DEXV2Router02::removeLiquidityNative, + tokenA, uint256_t("50000000000000000000"), uint256_t("10000000000000000000"), + uint256_t("10000000000000000000"), owner, deadline + ); + + // Check if operation worked successfully + pair = sdk.callViewFunction(factory, &DEXV2Factory::getPair, tokenA, wrapped); + ownerTknA = sdk.callViewFunction(tokenA, &ERC20::balanceOf, owner); + ownerNative = sdk.getNativeBalance(owner); + pairTknA = sdk.callViewFunction(tokenA, &ERC20::balanceOf, pair); + wrappedNative = sdk.getNativeBalance(wrapped); + pairNativeWrapped = sdk.callViewFunction(wrapped, &ERC20::balanceOf, pair); + REQUIRE(ownerTknA == uint256_t("9950000000000000000000")); + REQUIRE(ownerNative >= ownerNativeBeforeSubLiq - uint256_t("100000000000000000000") - (uint256_t(1000000000) * 21000)); + REQUIRE(pairTknA == uint256_t("50000000000000000000")); + REQUIRE(wrappedNative == uint256_t("50000000000000000000")); + REQUIRE(pairNativeWrapped == uint256_t("50000000000000000000")); } } } diff --git a/tests/contract/erc721.cpp b/tests/contract/erc721test.cpp similarity index 84% rename from tests/contract/erc721.cpp rename to tests/contract/erc721test.cpp index 9476cd63..203bc2d3 100644 --- a/tests/contract/erc721.cpp +++ b/tests/contract/erc721test.cpp @@ -10,18 +10,34 @@ #include "../sdktestsuite.hpp" -namespace TERC721 { - TEST_CASE("ERC721 Class", "[contract][erc721]") { - SECTION("ERC721 Creation") { - SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721Creation"); - auto ERC721Address = sdk.deployContract(std::string("My Test NFT!"), std::string("NFT"), uint64_t(100)); +namespace TERC721Test { + TEST_CASE("ERC721Test Class", "[contract][erc721test]") { + SECTION("ERC721Test Creation + Dump") { + Address ERC721Address; + std::unique_ptr options; + { + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721TestCreation"); + ERC721Address = sdk.deployContract(std::string("My Test NFT!"), std::string("NFT"), uint64_t(100)); + REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::name) == "My Test NFT!"); + REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::symbol) == "NFT"); + REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::maxTokens) == 100); + REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::tokenIdCounter) == 0); + // Dump to database + options = std::make_unique(sdk.getOptions()); + sdk.getState().saveToDB(); + } + + // SDKTestSuite should automatically load the state from the DB if we construct it with an Options object + // (The createNewEnvironment DELETES the DB if any is found) + SDKTestSuite sdk(*options); REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::name) == "My Test NFT!"); REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::symbol) == "NFT"); REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::maxTokens) == 100); + REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::tokenIdCounter) == 0); } - SECTION("ERC721 Mint 100 Token Same Address") { - SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721Mint100TokenSameAddress"); + SECTION("ERC721Test Mint 100 Token Same Address") { + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721TestMint100TokenSameAddress"); auto ERC721Address = sdk.deployContract(std::string("My Test NFT!"), std::string("NFT"), uint64_t(100)); for (uint64_t i = 0; i < 100; ++i) { sdk.callFunction(ERC721Address, &ERC721Test::mint, sdk.getChainOwnerAccount().address); @@ -35,13 +51,13 @@ namespace TERC721 { REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::totalSupply) == 100); } - SECTION("ERC721 Mint 100 Different Addresses") { + SECTION("ERC721Test Mint 100 Different Addresses") { // Generate 100 different TestAccounts std::vector accounts; for (int i = 0; i < 100; ++i) { accounts.emplace_back(TestAccount::newRandomAccount()); } - SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721Mint100DifferentAddresses", accounts); + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721TestMint100DifferentAddresses", accounts); auto ERC721Address = sdk.deployContract(std::string("My Test NFT!"), std::string("NFT"), uint64_t(100)); for (int i = 0; i < 100; ++i) { sdk.callFunction(ERC721Address, &ERC721Test::mint, accounts[i].address); @@ -54,13 +70,13 @@ namespace TERC721 { REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::totalSupply) == 100); } - SECTION("ERC721 Mint 100 Different Addresses reverse") { + SECTION("ERC721Test Mint 100 Different Addresses reverse") { // Same as before, but we mint from the test accounts list in reverse order std::vector accounts; for (int i = 0; i < 100; ++i) { accounts.emplace_back(TestAccount::newRandomAccount()); } - SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721Mint100DifferentAddressesReverse", accounts); + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721TestMint100DifferentAddressesReverse", accounts); auto ERC721Address = sdk.deployContract(std::string("My Test NFT!"), std::string("NFT"), uint64_t(100)); for (int i = 99; i >= 0; i--) { sdk.callFunction(ERC721Address, &ERC721Test::mint, accounts[i].address); @@ -74,8 +90,8 @@ namespace TERC721 { REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::totalSupply) == 100); } - SECTION("ERC721 Mint 100 and Burn 100 Same Address") { - SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721Mint100AndBurn100SameAddress"); + SECTION("ERC721Test Mint 100 and Burn 100 Same Address") { + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721TestMint100AndBurn100SameAddress"); auto ERC721Address = sdk.deployContract(std::string("My Test NFT!"), std::string("NFT"), uint64_t(100)); for (uint64_t i = 0; i < 100; ++i) { sdk.callFunction(ERC721Address, &ERC721Test::mint, sdk.getChainOwnerAccount().address); @@ -97,13 +113,13 @@ namespace TERC721 { REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::balanceOf, sdk.getChainOwnerAccount().address) == 0); } - SECTION("ERC721 Mint 100 Different Address Burn 100 Different Address") { + SECTION("ERC721Test Mint 100 Different Address Burn 100 Different Address") { // Generate 100 different TestAccounts std::vector accounts; for (int i = 0; i < 100; ++i) { accounts.emplace_back(TestAccount::newRandomAccount()); } - SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721Mint100DifferentAddressBurn100DifferentAddress", accounts); + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721TestMint100DifferentAddressBurn100DifferentAddress", accounts); auto ERC721Address = sdk.deployContract(std::string("My Test NFT!"), std::string("NFT"), uint64_t(100)); for (int i = 0; i < 100; ++i) { sdk.callFunction(ERC721Address, &ERC721Test::mint, accounts[i].address); @@ -125,13 +141,13 @@ namespace TERC721 { } } - SECTION("ERC721 Mint 100 Different Address Burn With Allowance") { + SECTION("ERC721Test Mint 100 Different Address Burn With Allowance") { // Generate 100 different TestAccounts std::vector accounts; for (int i = 0; i < 100; ++i) { accounts.emplace_back(TestAccount::newRandomAccount()); } - SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721Mint100DifferentAddressBurnWithAllowance", accounts); + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721TestMint100DifferentAddressBurnWithAllowance", accounts); auto ERC721Address = sdk.deployContract(std::string("My Test NFT!"), std::string("NFT"), uint64_t(100)); for (int i = 0; i < 100; ++i) { sdk.callFunction(ERC721Address, &ERC721Test::mint, accounts[i].address); @@ -173,14 +189,14 @@ namespace TERC721 { } } - SECTION("ERC721 transferFrom with allowance from 100 different accounts") { + SECTION("ERC721Test transferFrom with allowance from 100 different accounts") { // Generate 100 different TestAccounts std::vector accounts; for (int i = 0; i < 100; ++i) { accounts.emplace_back(TestAccount::newRandomAccount()); } TestAccount accountToSent = TestAccount::newRandomAccount(); - SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721Mint100DifferentAddressBurnWithAllowance", accounts); + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721TestMint100DifferentAddressBurnWithAllowance", accounts); auto ERC721Address = sdk.deployContract(std::string("My Test NFT!"), std::string("NFT"), uint64_t(100)); for (int i = 0; i < 100; ++i) { sdk.callFunction(ERC721Address, &ERC721Test::mint, accounts[i].address); diff --git a/tests/contract/event.cpp b/tests/contract/event.cpp new file mode 100644 index 00000000..02f1dda3 --- /dev/null +++ b/tests/contract/event.cpp @@ -0,0 +1,166 @@ +/* +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 "../../src/libs/catch2/catch_amalgamated.hpp" + +#include "../../src/contract/event.h" + +#include "../../src/bytes/view.h" + +namespace TEvent { + TEST_CASE("Event Class", "[contract][event]") { + SECTION("Event Constructor (EVM)") { + Hash txHash = Hash::random(); + Hash blockHash = Hash::random(); + std::vector topics = {Hash::random(), Hash::random(), Hash::random(), Hash::random(), Hash::random()}; + Address add("0x1234567890123456789012345678901234567890", false); + Bytes data{0xDE, 0xAD, 0xBE, 0xEF}; + + Event e("myEvent", 0, txHash, 1, blockHash, 2, add, data, topics, false); + REQUIRE(e.getName() == "myEvent"); + REQUIRE(e.getLogIndex() == 0); + REQUIRE(e.getTxHash() == txHash); + REQUIRE(e.getTxIndex() == 1); + REQUIRE(e.getBlockHash() == blockHash); + REQUIRE(e.getBlockIndex() == 2); + REQUIRE(e.getAddress() == add); + REQUIRE(e.getData() == data); + REQUIRE(e.getTopics().size() == 5); + REQUIRE(e.getTopics()[0] == topics[0]); + REQUIRE(e.getTopics()[1] == topics[1]); + REQUIRE(e.getTopics()[2] == topics[2]); + REQUIRE(e.getTopics()[3] == topics[3]); + REQUIRE(e.getTopics()[4] == topics[4]); + REQUIRE(e.isAnonymous() == false); + REQUIRE(e.getSelector() == topics[0]); + } + + SECTION("Event Constructor (CPP)") { + Hash txHash = Hash::random(); + Hash blockHash = Hash::random(); + Address add("0x1234567890123456789012345678901234567890", false); + + // Anonymous event + Event e1("myEvent", 0, txHash, 1, blockHash, 2, add, std::make_tuple( + EventParam("p1"), + EventParam("p2"), + EventParam("p3"), + EventParam("p4"), + EventParam("p5") + ), true); + REQUIRE(e1.getName() == "myEvent"); + REQUIRE(e1.getLogIndex() == 0); + REQUIRE(e1.getTxHash() == txHash); + REQUIRE(e1.getTxIndex() == 1); + REQUIRE(e1.getBlockHash() == blockHash); + REQUIRE(e1.getBlockIndex() == 2); + REQUIRE(e1.getAddress() == add); + REQUIRE(e1.getData() == Bytes()); + REQUIRE(e1.getTopics().size() == 4); // topics only goes up to 4 even though we put 5 + REQUIRE(e1.isAnonymous() == true); + REQUIRE(e1.getSelector() == Hash()); + + // Non-anonymous event + Event e2("myEvent", 0, txHash, 1, blockHash, 2, add, std::make_tuple( + EventParam("p1"), + EventParam("p2"), + EventParam("p3"), + EventParam("p4"), + EventParam("p5") + ), false); + REQUIRE(e2.getName() == "myEvent"); + REQUIRE(e2.getLogIndex() == 0); + REQUIRE(e2.getTxHash() == txHash); + REQUIRE(e2.getTxIndex() == 1); + REQUIRE(e2.getBlockHash() == blockHash); + REQUIRE(e2.getBlockIndex() == 2); + REQUIRE(e2.getAddress() == add); + REQUIRE(e2.getData() == Bytes()); + REQUIRE(e2.getTopics().size() == 4); // topics only goes up to 4 (event signature + 3) even though we put 5 + REQUIRE(e2.isAnonymous() == false); + REQUIRE(e2.getSelector() != Hash()); + } + + SECTION("Event Constructor (JSON String)") { + // Copied from above + std::string e1Str = "{\"name\":\"myEvent\",\"logIndex\":0,\"txHash\":\"0x05846d60d5b92b068c28a9017831e29827243bd4f642734977dcb111ccd40425\",\"txIndex\":1,\"blockHash\":\"0xec313458e29969850621411a36ab7b93c2b494ffb1ca77b5e62ea5b42100674d\",\"blockIndex\":2,\"address\":\"0x1234567890123456789012345678901234567890\",\"data\":[],\"topics\":[\"0x260e065801cba6ca065f28640c3d94ef235f67db5431448aae1a51af7214efaf\",\"0xc30a3ae685bfcb917dceb41e4afed5342f332572d5b3a8212679077685c494cb\",\"0xff05acda0d6ef15409d713cb0f124d2c3a3fd95b33af096109172229d5c8671a\",\"0x0c38459a0b5ed2a98afa2a407dc31ff1744c1c3159dbbbd3aef8736778a0e063\"],\"anonymous\":true}"; + std::string e2Str = "{\"name\":\"myEvent\",\"logIndex\":0,\"txHash\":\"0x05846d60d5b92b068c28a9017831e29827243bd4f642734977dcb111ccd40425\",\"txIndex\":1,\"blockHash\":\"0xec313458e29969850621411a36ab7b93c2b494ffb1ca77b5e62ea5b42100674d\",\"blockIndex\":2,\"address\":\"0x1234567890123456789012345678901234567890\",\"data\":[],\"topics\":[\"0x386cc2513b9e8b9e78a0792c33d6f69798774e0fa5424d3042fdd0fe7647420b\",\"0x260e065801cba6ca065f28640c3d94ef235f67db5431448aae1a51af7214efaf\",\"0xc30a3ae685bfcb917dceb41e4afed5342f332572d5b3a8212679077685c494cb\",\"0xff05acda0d6ef15409d713cb0f124d2c3a3fd95b33af096109172229d5c8671a\"],\"anonymous\":false}"; + + Event e1(e1Str); + Event e2(e2Str); + + REQUIRE(e1.getName() == "myEvent"); + REQUIRE(e1.getLogIndex() == 0); + REQUIRE(e1.getTxHash().hex(true).get() == "0x05846d60d5b92b068c28a9017831e29827243bd4f642734977dcb111ccd40425"); + REQUIRE(e1.getTxIndex() == 1); + REQUIRE(e1.getBlockHash().hex(true).get() == "0xec313458e29969850621411a36ab7b93c2b494ffb1ca77b5e62ea5b42100674d"); + REQUIRE(e1.getBlockIndex() == 2); + REQUIRE(e1.getAddress().hex(true).get() == "0x1234567890123456789012345678901234567890"); + REQUIRE(e1.getData() == Bytes()); + REQUIRE(e1.getTopics().size() == 4); + REQUIRE(e1.getTopics()[0].hex(true).get() == "0x260e065801cba6ca065f28640c3d94ef235f67db5431448aae1a51af7214efaf"); + REQUIRE(e1.getTopics()[1].hex(true).get() == "0xc30a3ae685bfcb917dceb41e4afed5342f332572d5b3a8212679077685c494cb"); + REQUIRE(e1.getTopics()[2].hex(true).get() == "0xff05acda0d6ef15409d713cb0f124d2c3a3fd95b33af096109172229d5c8671a"); + REQUIRE(e1.getTopics()[3].hex(true).get() == "0x0c38459a0b5ed2a98afa2a407dc31ff1744c1c3159dbbbd3aef8736778a0e063"); + REQUIRE(e1.isAnonymous() == true); + REQUIRE(e1.getSelector() == Hash()); + + REQUIRE(e2.getName() == "myEvent"); + REQUIRE(e2.getLogIndex() == 0); + REQUIRE(e2.getTxHash().hex(true).get() == "0x05846d60d5b92b068c28a9017831e29827243bd4f642734977dcb111ccd40425"); + REQUIRE(e2.getTxIndex() == 1); + REQUIRE(e2.getBlockHash().hex(true).get() == "0xec313458e29969850621411a36ab7b93c2b494ffb1ca77b5e62ea5b42100674d"); + REQUIRE(e2.getBlockIndex() == 2); + REQUIRE(e2.getAddress().hex(true).get() == "0x1234567890123456789012345678901234567890"); + REQUIRE(e2.getData() == Bytes()); + REQUIRE(e2.getTopics().size() == 4); + REQUIRE(e2.getTopics()[0].hex(true).get() == "0x386cc2513b9e8b9e78a0792c33d6f69798774e0fa5424d3042fdd0fe7647420b"); + REQUIRE(e2.getTopics()[1].hex(true).get() == "0x260e065801cba6ca065f28640c3d94ef235f67db5431448aae1a51af7214efaf"); + REQUIRE(e2.getTopics()[2].hex(true).get() == "0xc30a3ae685bfcb917dceb41e4afed5342f332572d5b3a8212679077685c494cb"); + REQUIRE(e2.getTopics()[3].hex(true).get() == "0xff05acda0d6ef15409d713cb0f124d2c3a3fd95b33af096109172229d5c8671a"); + REQUIRE(e2.isAnonymous() == false); + REQUIRE(e2.getSelector() != Hash()); + } + + SECTION("Event Serialization (Normal + RPC)") { + Hash txHash(Hex::toBytes("0x53472c61f1db8612fcdd17f24b78986bfa111ea3e323522456b1a78560f2215a")); + Hash blockHash(Hex::toBytes("0x2b9b8644330d50ffb90c5fea02b73b562dfc550ec7f8c85f643b20391a972d5f")); + Address add("0x1234567890123456789012345678901234567890", false); + Event e1("myEvent", 0, txHash, 1, blockHash, 2, add, std::make_tuple( + EventParam("p1"), + EventParam("p2"), + EventParam("p3"), + EventParam("p4"), + EventParam("p5") + ), false); + + std::string e1Str = e1.serializeToJson(); + json e1Json = e1.serializeForRPC(); + + REQUIRE(e1Str == "{\"name\":\"myEvent\",\"logIndex\":0,\"txHash\":\"0x53472c61f1db8612fcdd17f24b78986bfa111ea3e323522456b1a78560f2215a\",\"txIndex\":1,\"blockHash\":\"0x2b9b8644330d50ffb90c5fea02b73b562dfc550ec7f8c85f643b20391a972d5f\",\"blockIndex\":2,\"address\":\"0x1234567890123456789012345678901234567890\",\"data\":[],\"topics\":[\"0x386cc2513b9e8b9e78a0792c33d6f69798774e0fa5424d3042fdd0fe7647420b\",\"0x260e065801cba6ca065f28640c3d94ef235f67db5431448aae1a51af7214efaf\",\"0xc30a3ae685bfcb917dceb41e4afed5342f332572d5b3a8212679077685c494cb\",\"0xff05acda0d6ef15409d713cb0f124d2c3a3fd95b33af096109172229d5c8671a\"],\"anonymous\":false}"); + REQUIRE(e1Json.dump() == "{\"address\":\"0x1234567890123456789012345678901234567890\",\"blockHash\":\"0x2b9b8644330d50ffb90c5fea02b73b562dfc550ec7f8c85f643b20391a972d5f\",\"blockNumber\":\"0x0000000000000002\",\"data\":\"0x\",\"logIndex\":\"0x0000000000000000\",\"removed\":false,\"topics\":[\"0x386cc2513b9e8b9e78a0792c33d6f69798774e0fa5424d3042fdd0fe7647420b\",\"0x260e065801cba6ca065f28640c3d94ef235f67db5431448aae1a51af7214efaf\",\"0xc30a3ae685bfcb917dceb41e4afed5342f332572d5b3a8212679077685c494cb\",\"0xff05acda0d6ef15409d713cb0f124d2c3a3fd95b33af096109172229d5c8671a\"],\"transactionHash\":\"0x53472c61f1db8612fcdd17f24b78986bfa111ea3e323522456b1a78560f2215a\",\"transactionIndex\":\"0x0000000000000001\"}"); + + Event e2(e1Str); + REQUIRE(e2.getName() == "myEvent"); + REQUIRE(e2.getLogIndex() == 0); + REQUIRE(e2.getTxHash() == txHash); + REQUIRE(e2.getTxIndex() == 1); + REQUIRE(e2.getBlockHash() == blockHash); + REQUIRE(e2.getBlockIndex() == 2); + REQUIRE(e2.getAddress() == add); + REQUIRE(e2.getData() == Bytes()); + REQUIRE(e2.getTopics().size() == 4); + REQUIRE(e2.getTopics()[0].hex(true).get() == "0x386cc2513b9e8b9e78a0792c33d6f69798774e0fa5424d3042fdd0fe7647420b"); + REQUIRE(e2.getTopics()[1].hex(true).get() == "0x260e065801cba6ca065f28640c3d94ef235f67db5431448aae1a51af7214efaf"); + REQUIRE(e2.getTopics()[2].hex(true).get() == "0xc30a3ae685bfcb917dceb41e4afed5342f332572d5b3a8212679077685c494cb"); + REQUIRE(e2.getTopics()[3].hex(true).get() == "0xff05acda0d6ef15409d713cb0f124d2c3a3fd95b33af096109172229d5c8671a"); + REQUIRE(e2.isAnonymous() == false); + REQUIRE(e2.getSelector() != Hash()); + } + } +} + diff --git a/tests/contract/evm.cpp b/tests/contract/evm.cpp index 712f198a..966d9178 100644 --- a/tests/contract/evm.cpp +++ b/tests/contract/evm.cpp @@ -121,9 +121,9 @@ namespace TEVM { sdk.getState().saveToDB(); } - /// SDKTestSuite should automatically load the state from the DB if we - /// Construct it with a options object - /// (The createNewEnvironment DELETES the DB if any is found) + // SDKTestSuite should automatically load the state from the DB if we + // Construct it with a options object + // (The createNewEnvironment DELETES the DB if any is found) SDKTestSuite sdk(*options); REQUIRE(sdk.callViewFunction(erc20Address, &ERC20::name) == "TestToken"); diff --git a/tests/contract/pebble.cpp b/tests/contract/pebble.cpp index cdd5c1f6..7d18afdf 100644 --- a/tests/contract/pebble.cpp +++ b/tests/contract/pebble.cpp @@ -13,9 +13,31 @@ See the LICENSE.txt file in the project root for more information. namespace TPEBBLE { TEST_CASE("Pebble Class", "[contract][pebble]") { - SECTION("Pebble creation") { - SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testPebbleCreation"); - Address pebbleAddr = sdk.deployContract(uint256_t(100000)); + SECTION("Pebble creation + dump") { + Address pebbleAddr; + std::unique_ptr options; + { + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testPebbleCreation"); + pebbleAddr = sdk.deployContract(uint256_t(100000)); + REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::name) == "Pebble"); + REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::symbol) == "PBL"); + REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::totalSupply) == uint256_t(0)); + REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::maxSupply) == uint256_t(100000)); + REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::totalNormal) == uint64_t(0)); + REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::totalGold) == uint64_t(0)); + REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::totalDiamond) == uint64_t(0)); + REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::raritySeed) == uint256_t(1000000)); + REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::diamondRarity) == uint256_t(1)); + REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::goldRarity) == uint256_t(10)); + REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::getAuthorizer) == Address()); + // Dump to database + options = std::make_unique(sdk.getOptions()); + sdk.getState().saveToDB(); + } + + // SDKTestSuite should automatically load the state from the DB if we construct it with an Options object + // (The createNewEnvironment DELETES the DB if any is found) + SDKTestSuite sdk(*options); REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::name) == "Pebble"); REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::symbol) == "PBL"); REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::totalSupply) == uint256_t(0)); @@ -28,6 +50,25 @@ namespace TPEBBLE { REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::goldRarity) == uint256_t(10)); REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::getAuthorizer) == Address()); } + + SECTION("Pebble ownership transfer (Ownable coverage)") { + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testPebbleOwnershipTransfer"); + Address pebbleAddr = sdk.deployContract(uint256_t(100000)); + REQUIRE_THROWS(sdk.callFunction(pebbleAddr, &Pebble::transferOwnership, Address())); // cannot transfer to zero address + REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::owner) == Address("0x00dead00665771855a34155f5e7405489df2c3c6", false)); + Address newOwner("0x1234567890123456789012345678901234567890", false); + sdk.callFunction(pebbleAddr, &Pebble::transferOwnership, newOwner); + REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::owner) == newOwner); + } + + SECTION("Pebble ownership renounce (Ownable coverage)") { + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testPebbleOwnershipTransfer"); + Address pebbleAddr = sdk.deployContract(uint256_t(100000)); + REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::owner) == Address("0x00dead00665771855a34155f5e7405489df2c3c6", false)); + sdk.callFunction(pebbleAddr, &Pebble::renounceOwnership); + REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::owner) == Address()); + } + SECTION("Pebble minting") { std::unique_ptr opts = nullptr; TestAccount authorizerAccount = TestAccount::newRandomAccount(); diff --git a/tests/contract/randomness.cpp b/tests/contract/randomness.cpp index f39eca37..f601cab2 100644 --- a/tests/contract/randomness.cpp +++ b/tests/contract/randomness.cpp @@ -36,35 +36,62 @@ namespace TContractRandomness { TEST_CASE("Contract Randomness", "[contract][randomness]") { SECTION("CPP Randomness Test") { auto sdk = SDKTestSuite::createNewEnvironment("CPPContractRandomness"); - auto randomnessContractAddr = sdk.deployContract(); - REQUIRE(sdk.callViewFunction(randomnessContractAddr, &RandomnessTest::getRandom) == 0); - auto setRandomTx = sdk.callFunction(randomnessContractAddr, &RandomnessTest::setRandom); // The random value should be the first RandomGen operator 0 // with the seed being Hash(blockRandomness + txIndex) // TxIndex is 0, so the seed should be the blockRandomness + auto setRandomTx = sdk.callFunction(randomnessContractAddr, &RandomnessTest::setRandom); Hash randomnessSeed = sdk.getStorage().latest()->getBlockRandomness(); RandomGen randomGen(randomnessSeed); - REQUIRE(sdk.callViewFunction(randomnessContractAddr, &RandomnessTest::getRandom) == randomGen.operator()()); - } + SECTION("EVM Randomness Test") { auto randomnessBytecode = Hex::toBytes("6080604052348015600e575f80fd5b506101b08061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c806353e7209e14610038578063aacc5a1714610042575b5f80fd5b610040610060565b005b61004a6100e8565b6040516100579190610108565b60405180910390f35b73100000000000000000000000000010000000000173ffffffffffffffffffffffffffffffffffffffff1663aacc5a176040518163ffffffff1660e01b8152600401602060405180830381865afa1580156100bd573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100e1919061014f565b5f81905550565b5f8054905090565b5f819050919050565b610102816100f0565b82525050565b5f60208201905061011b5f8301846100f9565b92915050565b5f80fd5b61012e816100f0565b8114610138575f80fd5b50565b5f8151905061014981610125565b92915050565b5f6020828403121561016457610163610121565b5b5f6101718482850161013b565b9150509291505056fea26469706673582212206ffd6a41e2097987a251d467fce209c21bde13b5a81c4123a0b5a0aa7f62153b64736f6c63430008190033"); auto sdk = SDKTestSuite::createNewEnvironment("EVMContractRandomness"); auto randomnessContractAddr = sdk.deployBytecode(randomnessBytecode); REQUIRE(sdk.callViewFunction(randomnessContractAddr, &RandomnessTest::getRandom) == 0); - auto setRandomTx = sdk.callFunction(randomnessContractAddr, &RandomnessTest::setRandom); // The random value should be the first RandomGen operator 0 // with the seed being Hash(blockRandomness + txIndex) // TxIndex is 0, so the seed should be the blockRandomness + auto setRandomTx = sdk.callFunction(randomnessContractAddr, &RandomnessTest::setRandom); Hash randomnessSeed = sdk.getStorage().latest()->getBlockRandomness(); RandomGen randomGen(randomnessSeed); - REQUIRE(sdk.callViewFunction(randomnessContractAddr, &RandomnessTest::getRandom) == randomGen.operator()()); } + + SECTION("Randomness Test DB Dump") { + Address randomnessContractAddr; + uint256_t randomNum; + std::unique_ptr options; + Hash randomnessSeed; + { + auto sdk = SDKTestSuite::createNewEnvironment("DumpContractRandomness"); + randomnessContractAddr = sdk.deployContract(); + REQUIRE(sdk.callViewFunction(randomnessContractAddr, &RandomnessTest::getRandom) == 0); + + // The random value should be the first RandomGen operator 0 + // with the seed being Hash(blockRandomness + txIndex) + // TxIndex is 0, so the seed should be the blockRandomness + auto setRandomTx = sdk.callFunction(randomnessContractAddr, &RandomnessTest::setRandom); + randomnessSeed = sdk.getStorage().latest()->getBlockRandomness(); + RandomGen randomGen(randomnessSeed); + randomNum = randomGen.operator()(); + REQUIRE(sdk.callViewFunction(randomnessContractAddr, &RandomnessTest::getRandom) == randomNum); + + // Dump to database + options = std::make_unique(sdk.getOptions()); + sdk.getState().saveToDB(); + } + + // SDKTestSuite should automatically load the state from the DB if we construct it with an Options object + // (The createNewEnvironment DELETES the DB if any is found) + SDKTestSuite sdk(*options); + REQUIRE(sdk.callViewFunction(randomnessContractAddr, &RandomnessTest::getRandom) == randomNum); + } } } + diff --git a/tests/contract/simplecontract.cpp b/tests/contract/simplecontract.cpp index c3440553..fd34d7d3 100644 --- a/tests/contract/simplecontract.cpp +++ b/tests/contract/simplecontract.cpp @@ -67,5 +67,38 @@ namespace TSimpleContract { REQUIRE(numberEvent.size() == 1); REQUIRE(tupleEvent.size() == 1); } + + SECTION("SimpleContract setNamesAndNumbers and setNamesAndNumbersInTuple") { + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testSimpleContractSetNamesAndNumbersInTuple"); + Address simpleContract = sdk.deployContract( + std::string("TestName"), uint256_t(19283187581), + std::make_tuple(std::string("TupleName"), uint256_t(987654321)) + ); + + std::string name = sdk.callViewFunction(simpleContract, &SimpleContract::getName); + uint256_t number = sdk.callViewFunction(simpleContract, &SimpleContract::getNumber); + std::tuple tuple = sdk.callViewFunction(simpleContract, &SimpleContract::getTuple); + REQUIRE(name == "TestName"); + REQUIRE(number == 19283187581); + REQUIRE(std::get<0>(tuple) == "TupleName"); + REQUIRE(std::get<1>(tuple) == 987654321); + + std::vector names = {"111", "222", "333"}; + std::vector numbers = {400, 300, 200, 100}; + Hash namesAndNumbersTx = sdk.callFunction(simpleContract, &SimpleContract::setNamesAndNumbers, names, numbers); + name = sdk.callViewFunction(simpleContract, &SimpleContract::getName); + number = sdk.callViewFunction(simpleContract, &SimpleContract::getNumber); + REQUIRE(name == "111222333"); + REQUIRE(number == 1000); + + std::vector> list = { + {"555", 1000}, {"444", 500}, {"333", 250}, {"222", 150}, {"111", 100} + }; + Hash namesAndNumbersInTupleTx = sdk.callFunction(simpleContract, &SimpleContract::setNamesAndNumbersInTuple, list); + name = sdk.callViewFunction(simpleContract, &SimpleContract::getName); + number = sdk.callViewFunction(simpleContract, &SimpleContract::getNumber); + REQUIRE(name == "555444333222111"); + REQUIRE(number == 2000); + } } } diff --git a/tests/contract/throwtest.cpp b/tests/contract/throwtest.cpp new file mode 100644 index 00000000..9b71d27b --- /dev/null +++ b/tests/contract/throwtest.cpp @@ -0,0 +1,61 @@ +/* +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 "../../src/libs/catch2/catch_amalgamated.hpp" + +#include "../../src/contract/templates/throwtestA.h" +#include "../../src/contract/templates/throwtestB.h" +#include "../../src/contract/templates/throwtestC.h" + +#include "../sdktestsuite.hpp" + +namespace TThrowTest { + TEST_CASE("ThrowTest Contracts", "[contract][throwtest]") { + SECTION("ThrowTest Coverage") { + // Everything in one section because yes + Address throwA; + Address throwB; + Address throwC; + std::unique_ptr options; + { + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testThrowTestCreation"); + throwA = sdk.deployContract(); + throwB = sdk.deployContract(); + throwC = sdk.deployContract(); + REQUIRE(sdk.callViewFunction(throwA, &ThrowTestA::getNumA) == 0); + REQUIRE(sdk.callViewFunction(throwB, &ThrowTestB::getNumB) == 0); + REQUIRE(sdk.callViewFunction(throwC, &ThrowTestC::getNumC) == 0); + + // A and B always throw, C is the only one that keeps the value + REQUIRE_THROWS(sdk.callFunction(throwA, &ThrowTestA::setNumA, uint8_t(1), throwB, uint8_t(2), throwC, uint8_t(3))); + REQUIRE(sdk.callViewFunction(throwA, &ThrowTestA::getNumA) == 0); + REQUIRE(sdk.callViewFunction(throwB, &ThrowTestB::getNumB) == 0); + REQUIRE(sdk.callViewFunction(throwC, &ThrowTestC::getNumC) == 0); + REQUIRE_THROWS(sdk.callFunction(throwB, &ThrowTestB::setNumB, uint8_t(4), throwC, uint8_t(5))); + REQUIRE(sdk.callViewFunction(throwA, &ThrowTestA::getNumA) == 0); + REQUIRE(sdk.callViewFunction(throwB, &ThrowTestB::getNumB) == 0); + REQUIRE(sdk.callViewFunction(throwC, &ThrowTestC::getNumC) == 0); + sdk.callFunction(throwC, &ThrowTestC::setNumC, uint8_t(6)); + REQUIRE(sdk.callViewFunction(throwA, &ThrowTestA::getNumA) == 0); + REQUIRE(sdk.callViewFunction(throwB, &ThrowTestB::getNumB) == 0); + REQUIRE(sdk.callViewFunction(throwC, &ThrowTestC::getNumC) == 6); + + // Dump to database + options = std::make_unique(sdk.getOptions()); + sdk.getState().saveToDB(); + } + + // SDKTestSuite should automatically load the state from the DB if we construct it with an Options object + // (The createNewEnvironment DELETES the DB if any is found) + SDKTestSuite sdk(*options); + REQUIRE(sdk.callViewFunction(throwA, &ThrowTestA::getNumA) == 0); + REQUIRE(sdk.callViewFunction(throwB, &ThrowTestB::getNumB) == 0); + REQUIRE(sdk.callViewFunction(throwC, &ThrowTestC::getNumC) == 6); + } + } +} + diff --git a/tests/contract/variables/safeint_t.cpp b/tests/contract/variables/safeint_t.cpp index 74a43eb2..5df3f0c3 100644 --- a/tests/contract/variables/safeint_t.cpp +++ b/tests/contract/variables/safeint_t.cpp @@ -70,13 +70,19 @@ template struct SafeIntTester { SafeInt val(UnderlyingType(-42)); SafeInt valOver(std::numeric_limits::max()); SafeInt valUnder(std::numeric_limits::min()); - bool hadOver = false; - bool hadUnder = false; + bool hadOver1 = false; + bool hadOver2 = false; + bool hadUnder1 = false; + bool hadUnder2 = false; // catch over/underflow - try { valOver = valOver + UnderlyingType(1); } catch (std::overflow_error& e) { hadOver = true; } - try { valUnder = valUnder + UnderlyingType(-1); } catch (std::underflow_error& e) { hadUnder = true; } - REQUIRE(hadOver); - REQUIRE(hadUnder); + try { valOver = valOver + UnderlyingType(1); } catch (std::overflow_error& e) { hadOver1 = true; } + try { valOver = valOver + valOver; } catch (std::overflow_error& e) { hadOver2 = true; } + try { valUnder = valUnder + UnderlyingType(-1); } catch (std::underflow_error& e) { hadUnder1 = true; } + try { valUnder = valUnder + valUnder; } catch (std::underflow_error& e) { hadUnder2 = true; } + REQUIRE(hadOver1); + REQUIRE(hadOver2); + REQUIRE(hadUnder1); + REQUIRE(hadUnder2); // operate with int val = val + UnderlyingType(5); val.revert(); @@ -98,13 +104,21 @@ template struct SafeIntTester { SafeInt val(UnderlyingType(-42)); SafeInt valOver(std::numeric_limits::max()); SafeInt valUnder(std::numeric_limits::min()); - bool hadOver = false; - bool hadUnder = false; + SafeInt valOverMinus(UnderlyingType(-1)); + SafeInt valUnderMinus(UnderlyingType(1)); + bool hadOver1 = false; + bool hadOver2 = false; + bool hadUnder1 = false; + bool hadUnder2 = false; // catch over/underflow - try { valOver = valOver - UnderlyingType(-1); } catch (std::overflow_error& e) { hadOver = true; } - try { valUnder = valUnder - UnderlyingType(1); } catch (std::underflow_error& e) { hadUnder = true; } - REQUIRE(hadOver); - REQUIRE(hadUnder); + try { valOver = valOver - UnderlyingType(-1); } catch (std::overflow_error& e) { hadOver1 = true; } + try { valOver = valOver - valOverMinus; } catch (std::overflow_error& e) { hadOver2 = true; } + try { valUnder = valUnder - UnderlyingType(1); } catch (std::underflow_error& e) { hadUnder1 = true; } + try { valUnder = valUnder - valUnderMinus; } catch (std::underflow_error& e) { hadUnder2 = true; } + REQUIRE(hadOver1); + REQUIRE(hadOver2); + REQUIRE(hadUnder1); + REQUIRE(hadUnder2); // operate with int val = val - UnderlyingType(5); val.revert(); @@ -130,16 +144,19 @@ template struct SafeIntTester { SafeInt valUnder(std::numeric_limits::min()); bool hadZero1 = false; bool hadZero2 = false; - bool hadOver = false; + bool hadOver1 = false; + bool hadOver2 = false; bool hadUnder = false; // catch over/underflow and mul by zero try { valZero1 = valZero1 * UnderlyingType(0); } catch (std::domain_error& e) { hadZero1 = true; } try { valZero2 = valZero2 * UnderlyingType(10); } catch (std::domain_error& e) { hadZero2 = true; } - try { valOver = valOver * UnderlyingType(2); } catch (std::overflow_error& e) { hadOver = true; } + try { valOver = valOver * UnderlyingType(2); } catch (std::overflow_error& e) { hadOver1 = true; } + try { valOver = valOver * valOver; } catch (std::overflow_error& e) { hadOver2 = true; } try { valUnder = valUnder * UnderlyingType(2); } catch (std::underflow_error& e) { hadUnder = true; } REQUIRE(hadZero1); REQUIRE(hadZero2); - REQUIRE(hadOver); + REQUIRE(hadOver1); + REQUIRE(hadOver2); REQUIRE(hadUnder); // operate with int val = val * UnderlyingType(2); @@ -162,14 +179,18 @@ template struct SafeIntTester { SECTION(std::string("SafeInt_t<") + std::to_string(Size) + "> operator/") { SafeInt val(UnderlyingType(-42)); SafeInt valZero(UnderlyingType(-42)); + SafeInt valMinusOne(UnderlyingType(-1)); SafeInt valOver(std::numeric_limits::min()); bool hadZero = false; - bool hadOver = false; + bool hadOver1 = false; + bool hadOver2 = false; // catch overflow and div by zero try { valZero = valZero / UnderlyingType(0); } catch (std::domain_error& e) { hadZero = true; } - try { valOver = valOver / UnderlyingType(-1); } catch (std::overflow_error& e) { hadOver = true; } + try { valOver = valOver / UnderlyingType(-1); } catch (std::overflow_error& e) { hadOver1 = true; } + try { valOver = valOver / valMinusOne; } catch (std::overflow_error& e) { hadOver2 = true; } REQUIRE(hadZero); - REQUIRE(hadOver); + REQUIRE(hadOver1); + REQUIRE(hadOver2); // operate with int val = val / UnderlyingType(2); val.revert(); @@ -423,13 +444,19 @@ template struct SafeIntTester { SafeInt val(UnderlyingType(-42)); SafeInt valOver(std::numeric_limits::max()); SafeInt valUnder(std::numeric_limits::min()); - bool hadOver = false; - bool hadUnder = false; + bool hadOver1 = false; + bool hadOver2 = false; + bool hadUnder1 = false; + bool hadUnder2 = false; // catch over/underflow - try { valOver += UnderlyingType(1); } catch (std::overflow_error& e) { hadOver = true; } - try { valUnder += UnderlyingType(-1); } catch (std::underflow_error& e) { hadUnder = true; } - REQUIRE(hadOver); - REQUIRE(hadUnder); + try { valOver += UnderlyingType(1); } catch (std::overflow_error& e) { hadOver1 = true; } + try { valOver += valOver; } catch (std::overflow_error& e) { hadOver2 = true; } + try { valUnder += UnderlyingType(-1); } catch (std::underflow_error& e) { hadUnder1 = true; } + try { valUnder += valUnder; } catch (std::underflow_error& e) { hadUnder2 = true; } + REQUIRE(hadOver1); + REQUIRE(hadOver2); + REQUIRE(hadUnder1); + REQUIRE(hadUnder2); // operate with int val = val += UnderlyingType(5); val.revert(); @@ -451,13 +478,21 @@ template struct SafeIntTester { SafeInt val(UnderlyingType(-42)); SafeInt valOver(std::numeric_limits::max()); SafeInt valUnder(std::numeric_limits::min()); - bool hadOver = false; - bool hadUnder = false; + SafeInt valOverMinus(UnderlyingType(-1)); + SafeInt valUnderMinus(UnderlyingType(1)); + bool hadOver1 = false; + bool hadOver2 = false; + bool hadUnder1 = false; + bool hadUnder2 = false; // catch over/underflow - try { valOver -= UnderlyingType(-1); } catch (std::overflow_error& e) { hadOver = true; } - try { valUnder -= UnderlyingType(1); } catch (std::underflow_error& e) { hadUnder = true; } - REQUIRE(hadOver); - REQUIRE(hadUnder); + try { valOver -= valOver - UnderlyingType(-1); } catch (std::overflow_error& e) { hadOver1 = true; } + try { valOver -= valOver - valOverMinus; } catch (std::overflow_error& e) { hadOver2 = true; } + try { valUnder -= valUnder - UnderlyingType(1); } catch (std::underflow_error& e) { hadUnder1 = true; } + try { valUnder -= valUnder - valUnderMinus; } catch (std::underflow_error& e) { hadUnder2 = true; } + REQUIRE(hadOver1); + REQUIRE(hadOver2); + REQUIRE(hadUnder1); + REQUIRE(hadUnder2); // operate with int val -= UnderlyingType(5); val.revert(); @@ -483,16 +518,19 @@ template struct SafeIntTester { SafeInt valUnder(std::numeric_limits::min()); bool hadZero1 = false; bool hadZero2 = false; - bool hadOver = false; + bool hadOver1 = false; + bool hadOver2 = false; bool hadUnder = false; // catch over/underflow and mul by zero try { valZero1 *= UnderlyingType(0); } catch (std::domain_error& e) { hadZero1 = true; } try { valZero2 *= UnderlyingType(10); } catch (std::domain_error& e) { hadZero2 = true; } - try { valOver *= UnderlyingType(2); } catch (std::overflow_error& e) { hadOver = true; } + try { valOver *= UnderlyingType(2); } catch (std::overflow_error& e) { hadOver1 = true; } + try { valOver *= valOver; } catch (std::overflow_error& e) { hadOver2 = true; } try { valUnder *= UnderlyingType(2); } catch (std::underflow_error& e) { hadUnder = true; } REQUIRE(hadZero1); REQUIRE(hadZero2); - REQUIRE(hadOver); + REQUIRE(hadOver1); + REQUIRE(hadOver2); REQUIRE(hadUnder); // operate with int val *= UnderlyingType(2); @@ -515,14 +553,18 @@ template struct SafeIntTester { SECTION(std::string("SafeInt_t<") + std::to_string(Size) + "> operator/=") { SafeInt val(UnderlyingType(-42)); SafeInt valZero(UnderlyingType(-42)); + SafeInt valMinusOne(UnderlyingType(-1)); SafeInt valOver(std::numeric_limits::min()); bool hadZero = false; - bool hadOver = false; + bool hadOver1 = false; + bool hadOver2 = false; // catch overflow and div by zero try { valZero /= UnderlyingType(0); } catch (std::domain_error& e) { hadZero = true; } - try { valOver /= UnderlyingType(-1); } catch (std::overflow_error& e) { hadOver = true; } + try { valOver /= UnderlyingType(-1); } catch (std::overflow_error& e) { hadOver1 = true; } + try { valOver /= valMinusOne; } catch (std::overflow_error& e) { hadOver2 = true; } REQUIRE(hadZero); - REQUIRE(hadOver); + REQUIRE(hadOver1); + REQUIRE(hadOver2); // operate with int val /= UnderlyingType(2); val.revert(); diff --git a/tests/contract/variables/safestring.cpp b/tests/contract/variables/safestring.cpp index 4b3f9f6b..e2d6c3eb 100644 --- a/tests/contract/variables/safestring.cpp +++ b/tests/contract/variables/safestring.cpp @@ -8,6 +8,8 @@ See the LICENSE.txt file in the project root for more information. #include "../../src/libs/catch2/catch_amalgamated.hpp" #include "../../src/contract/variables/safestring.h" +using Catch::Matchers::Equals; + namespace TSafeString { TEST_CASE("SafeString class", "[contract][variables][safestring]") { SECTION("SafeString constructor") { @@ -1100,6 +1102,13 @@ namespace TSafeString { REQUIRE(!(strA1 >= cstrB)); REQUIRE(!(strB <= cstrA)); } + + SECTION("SafeString operator<<") { + SafeString str("ABCDE"); + std::stringstream ss; + ss << str; + REQUIRE_THAT(ss.str(), Equals("ABCDE")); + } } } diff --git a/tests/contract/variables/safetuple.cpp b/tests/contract/variables/safetuple.cpp index ad4fcb4e..4ba65dc3 100644 --- a/tests/contract/variables/safetuple.cpp +++ b/tests/contract/variables/safetuple.cpp @@ -68,6 +68,14 @@ namespace TSafeTuple { REQUIRE(get<2>(std::as_const(tup)) == "ccc"); } + SECTION("SafeTuple raw") { + SafeTuple tup(std::make_tuple(10, 1.0, "aaa")); + std::tuple tupRaw = tup.raw(); + REQUIRE(std::get<0>(tupRaw) == 10); + REQUIRE(std::get<1>(tupRaw) == 1.0); + REQUIRE(std::get<2>(tupRaw) == "aaa"); + } + SECTION("SafeTuple operator=") { SafeTuple tup(std::make_tuple(10, 1.0, "aaa")); // assign by copy diff --git a/tests/contract/variables/safeuint_t.cpp b/tests/contract/variables/safeuint_t.cpp index b3ade250..382ce087 100644 --- a/tests/contract/variables/safeuint_t.cpp +++ b/tests/contract/variables/safeuint_t.cpp @@ -156,16 +156,19 @@ template struct SafeUintTester { SafeUint valUnder(1); bool hadZero1 = false; bool hadZero2 = false; - bool hadOver = false; + bool hadOver1 = false; + bool hadOver2 = false; bool hadUnder = false; // catch over/underflow and mul by zero try { valZero1 = valZero1 * UnderlyingType(0); } catch (std::domain_error& e) { hadZero1 = true; } try { valZero2 = valZero2 * UnderlyingType(10); } catch (std::domain_error& e) { hadZero2 = true; } - try { valOver = valOver * UnderlyingType(2); } catch (std::overflow_error& e) { hadOver = true; } + try { valOver = valOver * UnderlyingType(2); } catch (std::overflow_error& e) { hadOver1 = true; } + try { valOver = valOver * valOver; } catch (std::overflow_error& e) { hadOver2 = true; } try { valUnder = valUnder * int(-1); } catch (std::underflow_error& e) { hadUnder = true; } REQUIRE(hadZero1); REQUIRE(hadZero2); - REQUIRE(hadOver); + REQUIRE(hadOver1); + REQUIRE(hadOver2); REQUIRE(hadUnder); // operate with uint val = val * UnderlyingType(2); @@ -651,16 +654,19 @@ template struct SafeUintTester { SafeUint valUnder(1); bool hadZero1 = false; bool hadZero2 = false; - bool hadOver = false; + bool hadOver1 = false; + bool hadOver2 = false; bool hadUnder = false; // catch over/underflow and mul by zero try { valZero1 *= UnderlyingType(0); } catch (std::domain_error& e) { hadZero1 = true; } try { valZero2 *= UnderlyingType(10); } catch (std::domain_error& e) { hadZero2 = true; } - try { valOver *= UnderlyingType(2); } catch (std::overflow_error& e) { hadOver = true; } - try { valUnder = valUnder * int(-1); } catch (std::underflow_error& e) { hadUnder = true; } + try { valOver *= UnderlyingType(2); } catch (std::overflow_error& e) { hadOver1 = true; } + try { valOver *= valOver; } catch (std::overflow_error& e) { hadOver2 = true; } + try { valUnder *= int(-1); } catch (std::underflow_error& e) { hadUnder = true; } REQUIRE(hadZero1); REQUIRE(hadZero2); - REQUIRE(hadOver); + REQUIRE(hadOver1); + REQUIRE(hadOver2); REQUIRE(hadUnder); // operate with uint val *= UnderlyingType(2); diff --git a/tests/contract/variables/safeunorderedmap.cpp b/tests/contract/variables/safeunorderedmap.cpp index 53bafb62..65d3b791 100644 --- a/tests/contract/variables/safeunorderedmap.cpp +++ b/tests/contract/variables/safeunorderedmap.cpp @@ -259,10 +259,10 @@ namespace TSafeUnorderedMap { // Same as insert (simple), but we use iterators to the vector instead (also an ilist) std::vector> values; - for (uint8_t i = 0; i < 100; i++) { // inserting 100 values + for (uint8_t i = 0; i < 100; i++) { // Inserting 100 values values.emplace_back(std::make_pair(Address(Utils::randBytes(20)), bal0)); } - std::initializer_list> ilist { // creating 10 values + std::initializer_list> ilist { // Creating 10 values std::make_pair(Address(Utils::randBytes(20)), bal0), std::make_pair(Address(Utils::randBytes(20)), bal0), std::make_pair(Address(Utils::randBytes(20)), bal0), @@ -287,7 +287,9 @@ namespace TSafeUnorderedMap { REQUIRE(map.size() == 101); for (std::pair val : values) REQUIRE(map.at(val.first) == val.second); - map.clear(); map.insert(std::make_pair(add0, bal0)); map.commit(); // revert to starting map + map.insert(values.cbegin(), values.cend()); // "Re-insert" for coverage + + map.clear(); map.insert(std::make_pair(add0, bal0)); map.commit(); // Revert to starting map // Mass insert by ilist, then revert map.insert(ilist); @@ -300,6 +302,8 @@ namespace TSafeUnorderedMap { map.commit(); REQUIRE(map.size() == 11); for (auto it = ilist.begin(); it != ilist.end(); it++) REQUIRE(map.at(it->first) == it->second); + + map.insert(ilist); // "Re-insert" for coverage } SECTION("SafeUnorderedMap insert_or_assign (copy)") { @@ -647,7 +651,7 @@ namespace TSafeUnorderedMap { map.commit(); REQUIRE(map.size() == 100); REQUIRE(!map.contains(firstVal.first)); - map[firstVal.first] = firstVal.second; map.commit(); REQUIRE(map.size() == 101); // re-add the key for next test + map[firstVal.first] = firstVal.second; map.commit(); REQUIRE(map.size() == 101); // Re-add the key for next test // Erase a single key using a value map.erase(add0); @@ -662,11 +666,11 @@ namespace TSafeUnorderedMap { // Erase a range of keys using iterators auto itB = map.cbegin(); auto itE = map.cbegin(); - std::advance(itE, map.size() / 2); // thanos snap, half the map is gone + std::advance(itE, map.size() / 2); // Thanos snap, half the map is gone map.erase(itB, itE); map.revert(); REQUIRE(map.size() == 100); - itB = map.cbegin(); // refresh iterators just in case + itB = map.cbegin(); // Refresh iterators just in case itE = map.cbegin(); std::advance(itE, map.size() / 2); map.erase(itB, itE); diff --git a/tests/contract/variables/safevector.cpp b/tests/contract/variables/safevector.cpp index 474f75e1..dceb25d2 100644 --- a/tests/contract/variables/safevector.cpp +++ b/tests/contract/variables/safevector.cpp @@ -104,6 +104,9 @@ namespace TSafeVector { REQUIRE(vec[2] == "x"); REQUIRE(vec[3] == "x"); REQUIRE(vec[4] == "x"); + // For coverage + const std::string str = vec[0]; + REQUIRE(str == "x"); } SECTION("SafeVector front and back") { diff --git a/tests/core/blockchain.cpp b/tests/core/blockchain.cpp index c66f1aaf..3760fbe9 100644 --- a/tests/core/blockchain.cpp +++ b/tests/core/blockchain.cpp @@ -58,14 +58,13 @@ void initialize( blockchain = std::make_unique(blockchainPath); } - namespace TBlockchain { TEST_CASE("Blockchain Class", "[core][blockchain]") { std::string testDumpPath = Utils::getTestDumpPath(); SECTION("Initialize Blockchain Multiple Nodes") { std::shared_ptr bestBlock; { - /// Initialize ManagerDiscovery + // Initialize ManagerDiscovery std::vector> discoveryNodes; std::unique_ptr discoveryOptions = std::make_unique( testDumpPath + "/statedDiscoveryNodeNetworkCapabilitiesWithTxBlockBroadcast", @@ -79,7 +78,7 @@ namespace TBlockchain { std::unique_ptr p2pDiscovery = std::make_unique( LOCALHOST, discoveryOptions); - /// Initialize multiple blockchain nodes. + // Initialize multiple blockchain nodes. std::unique_ptr blockchain1; initialize("blockchainInitializeTestNode1", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8080, 8101, PrivKey(), {"127.0.0.1", 8100}, blockchain1); @@ -96,7 +95,7 @@ namespace TBlockchain { initialize("blockchainInitializeTestNode4", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8083, 8104, PrivKey(), {"127.0.0.1", 8100}, blockchain4); - /// Start the blockchain nodes. + // Start the blockchain nodes. p2pDiscovery->start(); p2pDiscovery->startDiscovery(); blockchain1->start(); @@ -109,7 +108,7 @@ namespace TBlockchain { REQUIRE(!blockchain3->getOptions()->getIsValidator()); REQUIRE(!blockchain4->getOptions()->getIsValidator()); - /// Wait everyone to connect. + // Wait everyone to connect. auto connectFuture = std::async(std::launch::async, [&]() { while (blockchain1->getP2P()->getSessionsIDs().size() != 4 || blockchain2->getP2P()->getSessionsIDs().size() != 4 || @@ -131,7 +130,7 @@ namespace TBlockchain { REQUIRE(blockchain1->getStorage()->latest()->hash() == blockchain3->getStorage()->latest()->hash()); REQUIRE(blockchain1->getStorage()->latest()->hash() == blockchain4->getStorage()->latest()->hash()); - /// Stop the blockchain nodes. + // Stop the blockchain nodes. p2pDiscovery->stop(); blockchain1->stop(); blockchain2->stop(); @@ -153,7 +152,7 @@ namespace TBlockchain { uint256_t myBalance("1000000000000000000000"); std::shared_ptr bestBlock; - /// Create the discovery node. + // Create the discovery node. { std::vector> discoveryNodes; std::unique_ptr discoveryOptions = std::make_unique( @@ -168,7 +167,7 @@ namespace TBlockchain { std::unique_ptr p2pDiscovery = std::make_unique( LOCALHOST, discoveryOptions); - /// Create the validator nodes (5 in total) + // Create the validator nodes (5 in total) std::unique_ptr blockchainValidator1; initialize("blockchainMove10BlocksTestValidator1", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8080, 8101, PrivKey(Hex::toBytes("0xba5e6e9dd9cbd263969b94ee385d885c2d303dfc181db2a09f6bf19a7ba26759")), @@ -194,7 +193,7 @@ namespace TBlockchain { PrivKey(Hex::toBytes("0x81f288dd776f4edfe256d34af1f7d719f511559f19115af3e3d692e741faadc6")), {"127.0.0.1", 8100}, blockchainValidator5); - /// Create the normal nodes (6 in total) + // Create the normal nodes (6 in total) std::unique_ptr blockchainNode1; initialize("blockchainMove10BlocksTestNode1", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8085, 8106, PrivKey(), {"127.0.0.1", 8100}, blockchainNode1); @@ -219,20 +218,20 @@ namespace TBlockchain { initialize("blockchainMove10BlocksTestNode6", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8090, 8111, PrivKey(), {"127.0.0.1", 8100}, blockchainNode6); - /// Start the discovery node. + // Start the discovery node. p2pDiscovery->start(); p2pDiscovery->startDiscovery(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); - /// Start the validator nodes. + // Start the validator nodes. blockchainValidator1->start(); blockchainValidator2->start(); blockchainValidator3->start(); blockchainValidator4->start(); blockchainValidator5->start(); - /// Start all nodes + // Start all nodes blockchainNode1->start(); blockchainNode2->start(); blockchainNode3->start(); @@ -240,7 +239,7 @@ namespace TBlockchain { blockchainNode5->start(); blockchainNode6->start(); - /// Wait everyone to sync + // Wait everyone to sync auto syncFuture = std::async(std::launch::async, [&]() { while (!blockchainValidator1->isSynced() || !blockchainValidator2->isSynced() || @@ -274,8 +273,8 @@ namespace TBlockchain { privKey ); - /// Commment this out and IT WILL NOT WORK. - /// Waiting for rdPoSWorker rework. + // Commment this out and IT WILL NOT WORK. + // Waiting for rdPoSWorker rework. auto rdPoSmempoolFuture = std::async(std::launch::async, [&]() { while (blockchainValidator1->getState().rdposGetMempool().size() != 8 || blockchainValidator2->getState().rdposGetMempool().size() != 8 || @@ -296,14 +295,14 @@ namespace TBlockchain { myBalance -= tx.getValue() + (tx.getMaxFeePerGas() * tx.getGasLimit()); targetBalance += tx.getValue(); - /// Send the transactions through HTTP + // Send the transactions through HTTP auto sendRawTxJson = json({ {"jsonrpc", "2.0"}, {"id", 1}, {"method", "eth_sendRawTransaction"}, {"params", json::array({Hex::fromBytes(tx.rlpSerialize(), true).forRPC()})}}); - /// Send the transaction to the first validator. + // Send the transaction to the first validator. auto sendRawTxResponse = json::parse(makeHTTPRequest(sendRawTxJson.dump(), "127.0.0.1", std::to_string(8101), @@ -314,7 +313,7 @@ namespace TBlockchain { REQUIRE(sendRawTxResponse["result"].get() == tx.hash().hex(true).get()); - /// Wait for the new best block to be broadcasted and accepted by all nodes + // Wait for the new best block to be broadcasted and accepted by all nodes ++blocks; auto blockFuture = std::async(std::launch::async, [&]() { while (blocks != blockchainNode1->getStorage()->latest()->getNHeight() || @@ -388,7 +387,7 @@ namespace TBlockchain { REQUIRE(wait != std::future_status::timeout); - /// Everyone should be on the same block + // Everyone should be on the same block REQUIRE(blockchainValidator1->getStorage()->latest()->getNHeight() == blocks); REQUIRE(blockchainValidator1->getStorage()->latest()->hash() == blockchainValidator2->getStorage()->latest()->hash()); REQUIRE(blockchainValidator1->getStorage()->latest()->hash() == blockchainValidator3->getStorage()->latest()->hash()); @@ -402,7 +401,7 @@ namespace TBlockchain { REQUIRE(blockchainValidator1->getStorage()->latest()->hash() == blockchainNode6->getStorage()->latest()->hash()); } bestBlock = blockchainValidator1->getStorage()->latest(); - /// Stop the nodes + // Stop the nodes p2pDiscovery->stop(); blockchainValidator1->stop(); blockchainValidator2->stop(); diff --git a/tests/core/state.cpp b/tests/core/state.cpp index 160c4584..ca51f4f3 100644 --- a/tests/core/state.cpp +++ b/tests/core/state.cpp @@ -69,7 +69,7 @@ namespace TState { } // Wait a little until everyone has been destructed. std::this_thread::sleep_for(std::chrono::milliseconds(100)); - //// Check if opening the state loads successfully from DB. + // Check if opening the state loads successfully from DB. auto blockchainWrapper = initialize(validatorPrivKeysState, validatorPrivKeysState[0], 8080, false, testDumpPath + "/stateConstructorTest"); REQUIRE(blockchainWrapper.state.getNativeBalance(Address(Hex::toBytes("0x00dead00665771855a34155f5e7405489df2c3c6"))) == uint256_t("1000000000000000000000")); @@ -109,7 +109,7 @@ namespace TState { } } - SECTION("Test Simple block on State (No Transactions only rdPoS") { + SECTION("Test Simple block on State (No Transactions only rdPoS)") { std::unique_ptr latestBlock = nullptr; { auto blockchainWrapper = initialize(validatorPrivKeysState, validatorPrivKeysState[0], 8080, true, testDumpPath + "/stateSimpleBlockTest"); @@ -135,7 +135,7 @@ namespace TState { { auto blockchainWrapper = initialize(validatorPrivKeysState, validatorPrivKeysState[0], 8080, true, testDumpPath + "/stateSimpleBlockTest"); - /// Add balance to the random Accounts and create random transactions + // Add balance to the random Accounts and create random transactions std::vector transactions; for (auto &[privkey, val]: randomAccounts) { Address me = Secp256k1::toAddress(Secp256k1::toUPub(privkey)); @@ -153,7 +153,7 @@ namespace TState { privkey ); - /// Take note of expected balance and nonce + // Take note of expected balance and nonce val.first = blockchainWrapper.state.getNativeBalance(me) - (transactions.back().getMaxFeePerGas() * transactions.back().getGasLimit()) - @@ -185,7 +185,7 @@ namespace TState { { auto blockchainWrapper = initialize(validatorPrivKeysState, validatorPrivKeysState[0], 8080, true, testDumpPath + "/stateSimpleBlockTest"); - /// Add balance to the random Accounts and add tx's to directly to mempool. + // Add balance to the random Accounts and add tx's to directly to mempool. for (auto &[privkey, val]: randomAccounts) { Address me = Secp256k1::toAddress(Secp256k1::toUPub(privkey)); blockchainWrapper.state.addBalance(me); @@ -202,15 +202,17 @@ namespace TState { privkey ); - /// Take note of expected balance and nonce + // Take note of expected balance and nonce val.first = blockchainWrapper.state.getNativeBalance(me) - (tx.getMaxFeePerGas() * tx.getGasLimit()) - tx.getValue(); val.second = blockchainWrapper.state.getNativeNonce(me) + 1; targetExpectedValue += tx.getValue(); blockchainWrapper.state.addTx(std::move(tx)); + REQUIRE(blockchainWrapper.state.isTxInMempool(tx.hash())); } auto txCopy = blockchainWrapper.state.getMempool(); REQUIRE(txCopy.size() == 500); + REQUIRE(blockchainWrapper.state.getPendingTxs().size() == 500); auto newBestBlock = createValidBlock(validatorPrivKeysState, blockchainWrapper.state, blockchainWrapper.storage, std::move(txCopy)); REQUIRE(blockchainWrapper.state.tryProcessNextBlock(std::move(newBestBlock)) == BlockValidationStatus::valid); @@ -225,9 +227,9 @@ namespace TState { } SECTION("Test State mempool refresh") { - /// The block included will only have transactions where the address starts with \x08 or lower - /// where the mempool will have 500 transactions, including the \x08 addresses txs. - /// Test if the mempool is refreshed correctly. + // The block included will only have transactions where the address starts with \x08 or lower + // where the mempool will have 500 transactions, including the \x08 addresses txs. + // Test if the mempool is refreshed correctly. std::unordered_map, SafeHash> randomAccounts; for (uint64_t i = 0; i < 500; ++i) { randomAccounts.insert({PrivKey(Utils::randBytes(32)), std::make_pair(0, 0)}); @@ -238,7 +240,7 @@ namespace TState { { auto blockchainWrapper = initialize(validatorPrivKeysState, validatorPrivKeysState[0], 8080, true, testDumpPath + "/stateSimpleBlockTest"); - /// Add balance to the random Accounts and add tx's to directly to mempool. + // Add balance to the random Accounts and add tx's to directly to mempool. std::vector txs; std::vector notOnBlock; for (auto &[privkey, val]: randomAccounts) { @@ -259,7 +261,7 @@ namespace TState { if (me[0] <= 0x08) { txs.emplace_back(tx); - /// Take note of expected balance and nonce + // Take note of expected balance and nonce val.first = blockchainWrapper.state.getNativeBalance(me) - (tx.getMaxFeePerGas() * tx.getGasLimit()) - tx.getValue(); val.second = blockchainWrapper.state.getNativeNonce(me) + 1; targetExpectedValue += tx.getValue(); @@ -302,14 +304,14 @@ namespace TState { std::unique_ptr latestBlock = nullptr; { auto blockchainWrapper = initialize(validatorPrivKeysState, validatorPrivKeysState[0], 8080, true, testDumpPath + "/state10BlocksTest"); - /// Add balance to the given addresses + // Add balance to the given addresses for (const auto &[privkey, account]: randomAccounts) { Address me = Secp256k1::toAddress(Secp256k1::toUPub(privkey)); blockchainWrapper.state.addBalance(me); } for (uint64_t index = 0; index < 10; ++index) { - /// Create random transactions + // Create random transactions std::vector txs; for (auto &[privkey, account]: randomAccounts) { Address me = Secp256k1::toAddress(Secp256k1::toUPub(privkey)); @@ -325,7 +327,7 @@ namespace TState { 21000, privkey ); - /// Take note of expected balance and nonce + // Take note of expected balance and nonce account.first = blockchainWrapper.state.getNativeBalance(me) - (txs.back().getMaxFeePerGas() * txs.back().getGasLimit()) - txs.back().getValue(); account.second = blockchainWrapper.state.getNativeNonce(me) + 1; @@ -575,7 +577,7 @@ namespace TState { } REQUIRE(blockchainWrapper1.state.getMempool().size() == 100); - /// Wait for the transactions to be broadcasted. + // Wait for the transactions to be broadcasted. auto broadcastFuture = std::async(std::launch::async, [&]() { while (blockchainWrapper1.state.getMempool().size() != 100 || blockchainWrapper2.state.getMempool().size() != 100 || @@ -854,7 +856,7 @@ namespace TState { } - /// TODO: This is done for the same reason as stopDiscovery. + // TODO: This is done for the same reason as stopDiscovery. blockchainWrapper1.consensus.stop(); blockchainWrapper2.consensus.stop(); blockchainWrapper3.consensus.stop(); @@ -1089,7 +1091,7 @@ namespace TState { for (uint64_t i = 0; i < 10; ++i) { std::vector txSet; - /// Add balance to the random Accounts and create random transactions + // Add balance to the random Accounts and create random transactions for (auto &[privkey, val]: randomAccounts) { Address me = Secp256k1::toAddress(Secp256k1::toUPub(privkey)); TxBlock tx( @@ -1105,7 +1107,7 @@ namespace TState { privkey ); - /// Take note of expected balance and nonce + // Take note of expected balance and nonce val.first = blockchainWrapper1.state.getNativeBalance(me) - (tx.getMaxFeePerGas() * tx.getGasLimit()) - tx.getValue(); val.second = blockchainWrapper1.state.getNativeNonce(me) + 1; txSet.push_back(tx); @@ -1113,7 +1115,7 @@ namespace TState { txs.emplace_back(std::move(txSet)); } - /// For each set of transactions, broadcast them and wait for them to be confirmed + // For each set of transactions, broadcast them and wait for them to be confirmed for (const auto &txSet: txs) { for (const auto &tx: txSet) { auto txStatus = blockchainWrapper1.state.addTx(TxBlock(tx)); @@ -1122,7 +1124,7 @@ namespace TState { targetExpectedValue += tx.getValue(); } - /// Wait for the transactions to be confirmed. + // Wait for the transactions to be confirmed. uint64_t loop = 0; auto confirmFuture = std::async(std::launch::async, [&]() { while (true) { @@ -1172,7 +1174,7 @@ namespace TState { REQUIRE(blockchainWrapper1.storage.getBlock(i)->getHash() == blockchainWrapper8.storage.getBlock(i)->getHash()); } - /// TODO: This is done for the same reason as stopDiscovery. + // TODO: This is done for the same reason as stopDiscovery. blockchainWrapper1.consensus.stop(); blockchainWrapper2.consensus.stop(); blockchainWrapper3.consensus.stop(); @@ -1457,8 +1459,8 @@ namespace TState { auto txStatus = blockchainWrapper1.state.addTx(TxBlock(tx)); REQUIRE(isTxStatusValid(txStatus)); blockchainWrapper1.p2p.getBroadcaster().broadcastTxBlock(tx); - /// Wait for the transactions to be confirmed. - /// + // Wait for the transactions to be confirmed. + // auto confirmFuture = std::async(std::launch::async, [&]() { while (true) { if (blockchainWrapper1.storage.txExists(tx.hash()) && @@ -1531,8 +1533,7 @@ namespace TState { // Sanity check: blocks uint64_t bestBlockHeight = blockchainWrapper1.storage.latest()->getNHeight(); - for (uint64_t i = 0; i <= bestBlockHeight; ++i) - { + for (uint64_t i = 0; i <= bestBlockHeight; i++) { REQUIRE(blockchainWrapper1.storage.getBlock(i)->getHash() == blockchainWrapper2.storage.getBlock(i)->getHash()); REQUIRE(blockchainWrapper1.storage.getBlock(i)->getHash() == blockchainWrapper3.storage.getBlock(i)->getHash()); REQUIRE(blockchainWrapper1.storage.getBlock(i)->getHash() == blockchainWrapper4.storage.getBlock(i)->getHash()); @@ -1542,7 +1543,7 @@ namespace TState { REQUIRE(blockchainWrapper1.storage.getBlock(i)->getHash() == blockchainWrapper8.storage.getBlock(i)->getHash()); } - /// TODO: This is done for the same reason as stopDiscovery. + // TODO: This is done for the same reason as stopDiscovery. blockchainWrapper1.consensus.stop(); blockchainWrapper2.consensus.stop(); blockchainWrapper3.consensus.stop(); @@ -1551,6 +1552,7 @@ namespace TState { blockchainWrapper6.consensus.stop(); blockchainWrapper7.consensus.stop(); blockchainWrapper8.consensus.stop(); + // Sleep so it can conclude the last operations. std::this_thread::sleep_for(std::chrono::seconds(1)); } diff --git a/tests/core/storage.cpp b/tests/core/storage.cpp index 5a6abb7b..74e98227 100644 --- a/tests/core/storage.cpp +++ b/tests/core/storage.cpp @@ -88,12 +88,9 @@ FinalizedBlock createRandomBlock(uint64_t txCount, uint64_t validatorCount, uint std::vector txValidators = randomnessResult.first; // Create a new block with the transactions. - FinalizedBlock finalBlock = FinalizedBlock::createNewValidBlock(std::move(txs), - std::move(txValidators), - prevHash, - timestamp, - nHeight, - blockValidatorPrivKey); + FinalizedBlock finalBlock = FinalizedBlock::createNewValidBlock( + std::move(txs), std::move(txValidators), prevHash, timestamp, nHeight, blockValidatorPrivKey + ); REQUIRE(finalBlock.getBlockRandomness() == Hash(Utils::sha3(randomSeed))); return finalBlock; } @@ -101,7 +98,6 @@ FinalizedBlock createRandomBlock(uint64_t txCount, uint64_t validatorCount, uint namespace TStorage { TEST_CASE("Storage Class", "[core][storage]") { SECTION("Simple Storage Startup") { - auto blockchainWrapper = initialize(validatorPrivKeysStorage, PrivKey(), 8080, true, "StorageConstructor"); // Chain should be filled with the genesis. REQUIRE(blockchainWrapper.storage.currentChainSize() == 1); @@ -150,10 +146,10 @@ namespace TStorage { REQUIRE(block->getTxs().size() == blocks[i].getTxs().size()); REQUIRE(block->getValidatorPubKey() == blocks[i].getValidatorPubKey()); } - /// We actually need to dump the state otherwise it WILL try to process the added blocks - /// in the constructor of the State class. - /// Dumping the state will say to the State class that there is no missing blocks and it will - /// not try to process the blocks in the constructor. + // We actually need to dump the state otherwise it WILL try to process the added blocks + // in the constructor of the State class. + // Dumping the state will say to the State class that there is no missing blocks and it will + // not try to process the blocks in the constructor. blockchainWrapper.state.saveToDB(); } @@ -213,7 +209,7 @@ namespace TStorage { REQUIRE(block->getTxs().size() == requiredBlock.getTxs().size()); REQUIRE(block->getValidatorPubKey() == requiredBlock.getValidatorPubKey()); } - /// Same as before, we need to dump the state to avoid processing the blocks in the constructor. + // Same as before, we need to dump the state to avoid processing the blocks in the constructor. blockchainWrapper.state.saveToDB(); } // Load DB again... diff --git a/tests/net/http/httpjsonrpc.cpp b/tests/net/http/httpjsonrpc.cpp index 96b44d65..5f7f1e78 100644 --- a/tests/net/http/httpjsonrpc.cpp +++ b/tests/net/http/httpjsonrpc.cpp @@ -9,6 +9,8 @@ See the LICENSE.txt file in the project root for more information. #include "../../blockchainwrapper.hpp" // blockchain.h -> (net/http/httpserver.h -> net/p2p/managernormal.h), consensus.h -> state.h -> dump.h -> (storage.h -> utils/options.h), utils/db.h -> utils.h +#include "../../src/net/http/jsonrpc/call.h" + std::string makeHTTPRequest( const std::string& reqBody, const std::string& host, const std::string& port, const std::string& target, const std::string& requestType, const std::string& contentType @@ -77,7 +79,6 @@ std::string makeHTTPRequest( return result; } - const std::vector validatorPrivKeysHttpJsonRpc { Hash(Hex::toBytes("0x0a0415d68a5ec2df57aab65efc2a7231b59b029bae7ff1bd2e40df9af96418c8")), Hash(Hex::toBytes("0xb254f12b4ca3f0120f305cabf1188fe74f0bd38e58c932a3df79c4c55df8fa66")), @@ -89,31 +90,44 @@ const std::vector validatorPrivKeysHttpJsonRpc { Hash(Hex::toBytes("0x426dc06373b694d8804d634a0fd133be18e4e9bcbdde099fce0ccf3cb965492f")) }; -template -json requestMethod(std::string method, T params) { +template json requestMethod(std::string method, T params) { return json::parse(makeHTTPRequest( json({ - {"jsonrpc", "2.0"}, - {"id", 1}, - {"method", method}, - {"params", params} - }).dump(), + {"jsonrpc", "2.0"}, + {"id", 1}, + {"method", method}, + {"params", params} + }).dump(), "127.0.0.1", std::to_string(9999), // Default port for HTTPJsonRPC "/", "POST", - "application/json")); + "application/json") + ); } namespace THTTPJsonRPC{ TEST_CASE("HTTPJsonRPC Tests", "[net][http][jsonrpc]") { + SECTION("checkJsonRPCSpec") { + json ok = {{"jsonrpc", "2.0"}, {"method", "myMethod"}, {"params", json::array()}}; + json noJsonRpc = {{"method", "myMethod"}, {"params", json::array()}}; + json wrongJsonRpc = {{"jsonrpc", "1.0"}, {"method", "myMethod"}, {"params", json::array()}}; + json noMethod = {{"jsonrpc", "2.0"}, {"params", json::array()}}; + json wrongParams = {{"jsonrpc", "2.0"}, {"method", "myMethod"}, {"params", 12345}}; + jsonrpc::checkJsonRPCSpec(ok); + REQUIRE_THROWS(jsonrpc::checkJsonRPCSpec(noJsonRpc)); + REQUIRE_THROWS(jsonrpc::checkJsonRPCSpec(wrongJsonRpc)); + REQUIRE_THROWS(jsonrpc::checkJsonRPCSpec(noMethod)); + REQUIRE_THROWS(jsonrpc::checkJsonRPCSpec(wrongParams)); + } + SECTION("HTTPJsonRPC") { - /// One section to lead it all - /// Reasoning: we don't want to keep opening and closing everything per Section, just initialize once and run. + // One section to lead it all + // Reasoning: we don't want to keep opening and closing everything per Section, just initialize once and run. std::string testDumpPath = Utils::getTestDumpPath(); auto blockchainWrapper = initialize(validatorPrivKeysHttpJsonRpc, validatorPrivKeysHttpJsonRpc[0], 8080, true, testDumpPath + "/HTTPjsonRPC"); - /// Make random transactions within a given block, we need to include requests for getting txs and blocks + // Make random transactions within a given block, we need to include requests for getting txs and blocks Address targetOfTransactions = Address(Utils::randBytes(20)); uint256_t targetExpectedValue = 0; std::unordered_map, SafeHash> randomAccounts; @@ -138,13 +152,22 @@ namespace THTTPJsonRPC{ privkey ); - /// Take note of expected balance and nonce + // Take note of expected balance and nonce val.first = blockchainWrapper.state.getNativeBalance(me) - (transactions.back().getMaxFeePerGas() * transactions.back().getGasLimit()) - transactions.back().getValue(); val.second = blockchainWrapper.state.getNativeNonce(me) + 1; targetExpectedValue += transactions.back().getValue(); } + // TODO: missing the following (as per coverage): + // * eth_call + // * eth_getLogs + // * eth_getCode + // * eth_getUncleByBlockHashAndIndex + // * txpool_content + // * debug_traceBlockByNumber + // * debug_traceTransaction + // We need to copy since createValidBlock will consume (move) the transactions auto transactionsCopy = transactions; @@ -271,7 +294,6 @@ namespace THTTPJsonRPC{ json eth_blockNumberResponse = requestMethod("eth_blockNumber", json::array()); REQUIRE(eth_blockNumberResponse["result"] == "0x1"); - /// TODO: eth_call json eth_estimateGasResponse = requestMethod("eth_estimateGas", json::array({json::object({ {"from", blockchainWrapper.options.getChainOwner().hex(true) }, {"to", "0xaaA85B2B2bD0bFdF6Bc5D0d61B6192c53818567b"}, @@ -394,6 +416,22 @@ namespace THTTPJsonRPC{ REQUIRE(eth_feeHistoryResponse["result"]["baseFeePerGas"][1] == "0x9502f900"); REQUIRE(eth_feeHistoryResponse["result"]["gasUsedRatio"][0] == 1.0); // TODO: properly compare float pointing values REQUIRE(eth_feeHistoryResponse["result"]["oldestBlock"] == "0x0"); + + // Last part - cover the catch cases + // Invalid JSON id type + json wrongId = {{"jsonrpc", "2.0"}, {"id", json::array()}, {"method", "web3_clientVersion"}, {"params", json::array()}}; + json idErr = json::parse(makeHTTPRequest( + wrongId.dump(), "127.0.0.1", std::to_string(9999), "/", "POST", "application/json" + )); + REQUIRE(idErr.contains("error")); + REQUIRE(idErr["error"]["code"] == -32603); + REQUIRE(idErr["error"]["message"] == "Internal error: Invalid id type"); + // Invalid method call + json methodErr = requestMethod("lololol", json::array()); + REQUIRE(methodErr.contains("error")); + REQUIRE(methodErr["error"]["code"] == -32601); + REQUIRE(methodErr["error"]["message"] == "Method \"lololol\" not found/available"); } } } + diff --git a/tests/net/http/parser.cpp b/tests/net/http/parser.cpp new file mode 100644 index 00000000..9b48acd7 --- /dev/null +++ b/tests/net/http/parser.cpp @@ -0,0 +1,93 @@ +/* +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 "../../src/libs/catch2/catch_amalgamated.hpp" + +#include "../../src/net/http/jsonrpc/blocktag.h" // parser.h + +namespace THTTPJSONRPCParser { + TEST_CASE("HTTP JSON RPC Parser Tests", "[net][http][jsonrpc][parser]") { + SECTION("Parser operator()") { + Hash h = Hash::random(); + std::vector v = {10, 20, 30, 40, 50}; + + // Parser regex REQUIRES hex prefix (0x) + json jsonHash = h.hex(true).get(); + json jsonAdd = Address("0x0000111122223333444455556666777788889999", false).hex(true).get(); + json jsonBytes = Hex::fromBytes(Bytes{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, true).get(); + json jsonBool = true; + json jsonFloat = 13.37f; + json jsonUint = uint64_t(3926591489); + json jsonUintStr = "0xea0b0801"; + json jsonBlockTagLatest = "latest"; + json jsonBlockTagEarliest = "earliest"; + json jsonBlockTagPending = "pending"; + json jsonVectorArr = v; + json jsonVectorObj = json::object(); + for (int i = 0; i < v.size(); i++) jsonVectorObj[std::to_string(i)] = v[i]; + + Hash resHash = jsonrpc::parse(jsonHash); + Address resAdd = jsonrpc::parse
(jsonAdd); + Bytes resBytes = jsonrpc::parse(jsonBytes); + bool resBool = jsonrpc::parse(jsonBool); + float resFloat = jsonrpc::parse(jsonFloat); + uint64_t resUint = jsonrpc::parse(jsonUint); + uint64_t resUintStr = jsonrpc::parse(jsonUintStr); + jsonrpc::BlockTag resBlockTagLatest = jsonrpc::parse(jsonBlockTagLatest); + jsonrpc::BlockTag resBlockTagEarliest = jsonrpc::parse(jsonBlockTagEarliest); + jsonrpc::BlockTag resBlockTagPending = jsonrpc::parse(jsonBlockTagPending); + std::vector resVectorArr = jsonrpc::parse>(jsonVectorArr); + std::vector resVectorObj = jsonrpc::parse>(jsonVectorObj); + + REQUIRE(resHash == h); + REQUIRE(resAdd == Address("0x0000111122223333444455556666777788889999", false)); + REQUIRE(resBytes == Bytes{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}); + REQUIRE(resBool == true); + REQUIRE(resFloat == 13.37f); + REQUIRE(resUint == uint64_t(3926591489)); + REQUIRE(resUintStr == uint64_t(3926591489)); + REQUIRE(resBlockTagLatest == jsonrpc::BlockTag::LATEST); + REQUIRE(resBlockTagEarliest == jsonrpc::BlockTag::EARLIEST); + REQUIRE(resBlockTagPending == jsonrpc::BlockTag::PENDING); + REQUIRE(resVectorArr == v); + REQUIRE(resVectorObj == v); + } + + SECTION("Parser operator() (throws)") { + // Same thing but everything is wrong on purpose to cover throw cases + json hashWrongType = json::array(); // Type is not string (or the required type) + json hashWrongFormat = Hash::random().hex().get(); // No "0x" + json addWrongType = json::array(); + json addWrongFormat = Address("0x0000111122223333444455556666777788889999", false).hex().get(); + json bytesWrongType = json::array(); + json bytesWrongFormat = "0x000g"; // Invalid hex (0-9a-fA-F) + json boolWrongType = json::array(); + json floatWrongType = json::array(); + json uintWrongType = json::array(); + json uintWrongFormat = "ea0b0801"; + json uintWrongNumber = "hhhh"; // Invalid number + json blockTagWrongType = json::array(); + json blockTagWrongFormat = "holding"; // Invalid tag ("latest", "earliest", "pending") + json vectorWrongType = -1; // Not array or object + REQUIRE_THROWS(jsonrpc::parse(hashWrongType)); + REQUIRE_THROWS(jsonrpc::parse(hashWrongFormat)); + REQUIRE_THROWS(jsonrpc::parse
(addWrongType)); + REQUIRE_THROWS(jsonrpc::parse
(addWrongFormat)); + REQUIRE_THROWS(jsonrpc::parse(bytesWrongType)); + REQUIRE_THROWS(jsonrpc::parse(bytesWrongFormat)); + REQUIRE_THROWS(jsonrpc::parse(boolWrongType)); + REQUIRE_THROWS(jsonrpc::parse(floatWrongType)); + REQUIRE_THROWS(jsonrpc::parse(uintWrongType)); + REQUIRE_THROWS(jsonrpc::parse(uintWrongFormat)); + REQUIRE_THROWS(jsonrpc::parse(uintWrongNumber)); + REQUIRE_THROWS(jsonrpc::parse(blockTagWrongType)); + REQUIRE_THROWS(jsonrpc::parse(blockTagWrongFormat)); + REQUIRE_THROWS(jsonrpc::parse>(vectorWrongType)); + } + } +} + diff --git a/tests/net/p2p/encoding.cpp b/tests/net/p2p/encoding.cpp new file mode 100644 index 00000000..2d874fdf --- /dev/null +++ b/tests/net/p2p/encoding.cpp @@ -0,0 +1,201 @@ +/* +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 "../../src/libs/catch2/catch_amalgamated.hpp" + +#include "../../src/net/p2p/encoding.cpp" // .cpp has extra functions that .h doesn't have (???) +#include "../../src/utils/strconv.h" + +namespace TP2PEncoding { + TEST_CASE("P2P Encoding (Helpers)", "[p2p][encoding]") { + SECTION("nodesToMessage <-> nodesFromMessage") { + Bytes msg; + P2P::NodeID node1{boost::asio::ip::address_v4::from_string("127.0.0.1"), 8000}; + P2P::NodeID node2{boost::asio::ip::address_v6::from_string("::1"), 8001}; + const boost::unordered_flat_map nodes = { + {node1, P2P::NodeType::NORMAL_NODE}, {node2, P2P::NodeType::DISCOVERY_NODE} + }; + + P2P::nodesToMessage(msg, nodes); + REQUIRE(Hex::fromBytes(msg).get() == "00007f0000011f400101000000000000000000000000000000011f41"); + boost::unordered_flat_map conv = P2P::nodesFromMessage(msg); + REQUIRE(conv.contains(node1)); + REQUIRE(conv[node1] == P2P::NodeType::NORMAL_NODE); + REQUIRE(conv.contains(node2)); + REQUIRE(conv[node2] == P2P::NodeType::DISCOVERY_NODE); + + // Throws for coverage + Bytes msgSmall{0x00}; + Bytes msgSmallV4{0x00, 0x00, 0x7f, 0x00}; // missing 0001 + Bytes msgSmallV6{0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // missing 0000000000000001 + Bytes msgInvalidIp{0x02}; // only 00 (v4) or 01 (v6) + Bytes msgNoPort{0x00, 0x00, 0x7f, 0x00, 0x00, 0x01}; // missing 14f0 (8000) + REQUIRE_THROWS(P2P::nodesFromMessage(msgSmall)); + REQUIRE_THROWS(P2P::nodesFromMessage(msgSmallV4)); + REQUIRE_THROWS(P2P::nodesFromMessage(msgSmallV6)); + REQUIRE_THROWS(P2P::nodesFromMessage(msgInvalidIp)); + REQUIRE_THROWS(P2P::nodesFromMessage(msgNoPort)); + } + + SECTION("nodeInfoToMessage <-> nodeInfoFromMessage") { + Bytes msg; + P2P::NodeID node1{boost::asio::ip::address_v4::from_string("127.0.0.1"), 8000}; + P2P::NodeID node2{boost::asio::ip::address_v6::from_string("::1"), 8001}; + const boost::unordered_flat_map nodes = { + {node1, P2P::NodeType::NORMAL_NODE}, {node2, P2P::NodeType::DISCOVERY_NODE} + }; + Options opts = Options::binaryDefaultOptions("options.json"); + + // Block w/ no txs, copied from block.cpp + PrivKey validatorPrivKey(Hex::toBytes("0x4d5db4107d237df6a3d58ee5f70ae63d73d765d8a1214214d8a13340d0f2750d")); + Hash nPrevBlockHash(Hex::toBytes("22143e16db549af9ccfd3b746ea4a74421847fa0fe7e0e278626a4e7307ac0f6")); + uint64_t timestamp = 1678400201859; + uint64_t nHeight = 92137812; + FinalizedBlock newBlock = FinalizedBlock::createNewValidBlock({},{}, nPrevBlockHash, timestamp, nHeight, validatorPrivKey); + std::shared_ptr latestBlock = std::make_shared(newBlock); + + P2P::nodeInfoToMessage(msg, latestBlock, nodes, opts); + Hex msgNodeVersion = Hex::fromBytes(Utils::create_view_span(msg).subspan(0, 8)); + Hex hexNodeVersion = Hex::fromBytes(UintConv::uint64ToBytes(opts.getVersion())); + Hex msgNHeight = Hex::fromBytes(Utils::create_view_span(msg).subspan(16, 8)); + Hex hexNHeight = Hex::fromBytes(UintConv::uint64ToBytes(nHeight)); + Hex msgHash = Hex::fromBytes(Utils::create_view_span(msg).subspan(24, 32)); + Hex hexHash = Hex::fromBytes(latestBlock->getHash().asBytes()); + Hex msgPeers = Hex::fromBytes(Utils::create_view_span(msg).subspan(56)); + Bytes bytesPeers; + P2P::nodesToMessage(bytesPeers, nodes); + Hex hexPeers = Hex::fromBytes(bytesPeers); + REQUIRE(msgNodeVersion.get() == hexNodeVersion.get()); + REQUIRE(msgNHeight.get() == hexNHeight.get()); + REQUIRE(msgHash.get() == hexHash.get()); + REQUIRE(msgPeers.get() == hexPeers.get()); + + P2P::NodeInfo conv = P2P::nodeInfoFromMessage(msg); + REQUIRE(conv.nodeVersion() == opts.getVersion()); + REQUIRE(conv.latestBlockHeight() == latestBlock->getNHeight()); + REQUIRE(conv.latestBlockHash() == latestBlock->getHash()); + REQUIRE(conv.peers().size() == 2); + REQUIRE(conv.peers()[0] == node1); + REQUIRE(conv.peers()[1] == node2); + } + + SECTION("blocksToMessage <-> blocksFromMessage") { + // Data copied from block.cpp, five equal txs for three "different" blocks + Bytes msg; + PrivKey validatorPrivKey(Hex::toBytes("0x4d5db4107d237df6a3d58ee5f70ae63d73d765d8a1214214d8a13340d0f2750d")); + Hash nPrevBlockHash(Hex::toBytes("97a5ebd9bbb5e330b0b3c74b9816d595ffb7a04d4a29fb117ea93f8a333b43be")); + uint64_t timestamp = 1678400843316; + uint64_t nHeight = 100; + TxBlock tx(Hex::toBytes("0x02f874821f9080849502f900849502f900825208942e951aa58c8b9b504a97f597bbb2765c011a8802880de0b6b3a764000080c001a0f56fe87778b4420d3b0f8eba91d28093abfdbea281a188b8516dd8411dc223d7a05c2d2d71ad3473571ff637907d72e6ac399fe4804641dbd9e2d863586c57717d"), 1); + std::vector txs1; + std::vector txs2; + std::vector txs3; + for (uint64_t i = 0; i < 5; i++) { + txs1.emplace_back(tx); + txs2.emplace_back(tx); + txs3.emplace_back(tx); + } + FinalizedBlock block1 = FinalizedBlock::createNewValidBlock(std::move(txs1), {}, nPrevBlockHash, timestamp, nHeight, validatorPrivKey); + FinalizedBlock block2 = FinalizedBlock::createNewValidBlock(std::move(txs1), {}, nPrevBlockHash, timestamp, nHeight, validatorPrivKey); + FinalizedBlock block3 = FinalizedBlock::createNewValidBlock(std::move(txs1), {}, nPrevBlockHash, timestamp, nHeight, validatorPrivKey); + std::vector> blocks = { + std::make_shared(block1), + std::make_shared(block2), + std::make_shared(block3) + }; + P2P::blocksToMessage(msg, blocks); + std::vector conv = P2P::blocksFromMessage(Utils::create_view_span(msg), 0); + + // For coverage purposes only, don't care enough to test this through + Bytes dataSmall = Hex::toBytes("0x02f87482"); + Bytes blockSmall = Hex::toBytes("0x02f874821f9080849502f900849502f9"); + REQUIRE_THROWS(P2P::blocksFromMessage(dataSmall, 0)); + REQUIRE_THROWS(P2P::blocksFromMessage(blockSmall, 0)); + } + + SECTION("txsToMessage <-> txsFromMessage") { + // 3 simple transactions copied from block.cpp + Bytes msg1; + Bytes msg2; + Bytes rawTx1 = Hex::toBytes("0x02f87301808405f5e100850b5b977f998252089495944f9d42e181d76bb2c7e428410533aa3fed4a88012386f1806fe51080c080a0102fc0316ef07a9be233a270cdeb692e1666710bbdb8be67bf7d896fa96c6bafa038b6cbfdeb433911da6958a9dd3ac24d4ff39f11d1b985efca6d6d79a96a62ce"); + Bytes rawTx2 = Hex::toBytes("0x02f87501820cfd8405f5e100850b67b98b6b8252089480c67432656d59144ceff962e8faf8926599bcf8880de27d72f9c7632e80c001a057180e5af9ecbac905b17a45b56f1d93626190f1ec8df6a4a8cbbf7c0b8704a9a0166f15ae0e192835e1bf405b82c9faaaf9c8918a9d702441daa5168514377a17"); + Bytes rawTx3 = Hex::toBytes("0x02f87501826e5c8402faf0808510b3a67cc282520894eb38eab2a8d5f448d7d47005b64697e159aa284e88078cbf1fc56d4f1080c080a0763458eaffb9745026fc6360443e7ff8d171824d0410d48fdf06c08c7d4a8306a031b3d8f1753acc4239ffe0584536f12095651f72a61c684ef221aaa97a315328"); + TxBlock tx1(rawTx1, 1); + TxBlock tx2(rawTx2, 1); + TxBlock tx3(rawTx3, 1); + const boost::unordered_flat_map txs = { + {tx1.hash(), tx1}, {tx2.hash(), tx2}, {tx3.hash(), tx3} + }; + std::vector txsVec = {tx1, tx2, tx3}; + + P2P::txsToMessage(msg1, txs); + REQUIRE(Hex::fromBytes(Utils::create_view_span(msg1).subspan(0, 4)).get() == Hex::fromBytes(Bytes{0x00, 0x00, 0x00, 0x76}).get()); + REQUIRE(Hex::fromBytes(Utils::create_view_span(msg1).subspan(4, 118)).get() == Hex::fromBytes(rawTx1).get()); + REQUIRE(Hex::fromBytes(Utils::create_view_span(msg1).subspan(122, 4)).get() == Hex::fromBytes(Bytes{0x00, 0x00, 0x00, 0x78}).get()); + REQUIRE(Hex::fromBytes(Utils::create_view_span(msg1).subspan(126, 120)).get() == Hex::fromBytes(rawTx2).get()); + REQUIRE(Hex::fromBytes(Utils::create_view_span(msg1).subspan(246, 4)).get() == Hex::fromBytes(Bytes{0x00, 0x00, 0x00, 0x78}).get()); + REQUIRE(Hex::fromBytes(Utils::create_view_span(msg1).subspan(250, 120)).get() == Hex::fromBytes(rawTx3).get()); + + P2P::txsToMessage(msg2, txsVec); + REQUIRE(Hex::fromBytes(Utils::create_view_span(msg2).subspan(0, 4)).get() == Hex::fromBytes(Bytes{0x00, 0x00, 0x00, 0x76}).get()); + REQUIRE(Hex::fromBytes(Utils::create_view_span(msg2).subspan(4, 118)).get() == Hex::fromBytes(rawTx1).get()); + REQUIRE(Hex::fromBytes(Utils::create_view_span(msg2).subspan(122, 4)).get() == Hex::fromBytes(Bytes{0x00, 0x00, 0x00, 0x78}).get()); + REQUIRE(Hex::fromBytes(Utils::create_view_span(msg2).subspan(126, 120)).get() == Hex::fromBytes(rawTx2).get()); + REQUIRE(Hex::fromBytes(Utils::create_view_span(msg2).subspan(246, 4)).get() == Hex::fromBytes(Bytes{0x00, 0x00, 0x00, 0x78}).get()); + REQUIRE(Hex::fromBytes(Utils::create_view_span(msg2).subspan(250, 120)).get() == Hex::fromBytes(rawTx3).get()); + + std::vector conv = P2P::txsFromMessage(Utils::create_view_span(msg1), uint64_t(1)); + REQUIRE(txs.find(conv[0].hash()) != txs.end()); + REQUIRE(txs.find(conv[1].hash()) != txs.end()); + REQUIRE(txs.find(conv[2].hash()) != txs.end()); + + // For coverage + Bytes dataSmall = Hex::toBytes("0x0000"); + Bytes txSmall = Hex::toBytes("0x0000007602f87301"); + REQUIRE_THROWS(P2P::txsFromMessage(Utils::create_view_span(dataSmall), uint64_t(1))); + REQUIRE_THROWS(P2P::txsFromMessage(Utils::create_view_span(txSmall), uint64_t(1))); + } + } + + TEST_CASE("P2P Encoding (Miscellaneous)", "[p2p][encoding]") { + SECTION("RequestID Conversions") { + P2P::RequestID id(uint64_t(793228721938748925)); + REQUIRE(id.hex().get() == "0b021d51e4cbd5fd"); + REQUIRE(id.toUint64() == uint64_t(793228721938748925)); + REQUIRE(P2P::RequestID::random().size() == 8); + } + + SECTION("getCommandType") { + Bytes cmd{0x00, 0x00}; // Ping (0x0000) + Bytes cmdSize{0x00}; + Bytes cmdInvalid{0xFF, 0xFF}; + REQUIRE(P2P::getCommandType(Utils::create_view_span(cmd)) == P2P::CommandType::Ping); + REQUIRE_THROWS(P2P::getCommandType(Utils::create_view_span(cmdSize))); + REQUIRE_THROWS(P2P::getCommandType(Utils::create_view_span(cmdInvalid))); + } + + SECTION("getRequestType") { + Bytes req{0x00}; // Requesting + Bytes reqSize{0x00, 0x00}; + Bytes reqInvalid{0xFF}; + REQUIRE(P2P::getRequestType(Utils::create_view_span(req)) == P2P::RequestType::Requesting); + REQUIRE_THROWS(P2P::getRequestType(Utils::create_view_span(reqSize))); + REQUIRE_THROWS(P2P::getRequestType(Utils::create_view_span(reqInvalid))); + } + + SECTION("NodeID operator<") { + P2P::NodeID node1{boost::asio::ip::address_v4::from_string("127.0.0.1"), 8000}; + P2P::NodeID node2{boost::asio::ip::address_v4::from_string("127.0.0.2"), 8000}; + P2P::NodeID node3{boost::asio::ip::address_v4::from_string("127.0.0.2"), 8001}; + P2P::NodeID node4{boost::asio::ip::address_v4::from_string("127.0.0.2"), 8001}; + REQUIRE(node1 < node2); + REQUIRE(node2 < node3); + REQUIRE_FALSE(node3 < node4); + } + } +} + diff --git a/tests/net/p2p/nodeinfo.cpp b/tests/net/p2p/nodeinfo.cpp new file mode 100644 index 00000000..39a5647d --- /dev/null +++ b/tests/net/p2p/nodeinfo.cpp @@ -0,0 +1,48 @@ +/* +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 "../../src/libs/catch2/catch_amalgamated.hpp" + +#include "../../src/net/p2p/encoding.h" + +// For coverage +namespace TP2PNodeInfo { + TEST_CASE("P2P NodeInfo", "[p2p][nodeinfo]") { + SECTION("NodeInfo Constructor") { + Hash randomBlockHash = Hash::random(); + P2P::NodeID randomId(boost::asio::ip::address::from_string("127.0.0.1"), uint16_t(8000)); + P2P::NodeInfo emptyNode; + P2P::NodeInfo node(uint64_t(1), uint64_t(15000), uint64_t(30000), uint64_t(5), uint64_t(12345), randomBlockHash, {randomId}); + REQUIRE(emptyNode.nodeVersion() == 0); + REQUIRE(emptyNode.currentNodeTimestamp() == 0); + REQUIRE(emptyNode.currentTimestamp() == 0); + REQUIRE(emptyNode.timeDifference() == 0); + REQUIRE(emptyNode.latestBlockHeight() == 0); + REQUIRE(emptyNode.latestBlockHash() == Hash()); + REQUIRE(emptyNode.peers().empty()); + REQUIRE(node.nodeVersion() == 1); + REQUIRE(node.currentNodeTimestamp() == 15000); + REQUIRE(node.currentTimestamp() == 30000); + REQUIRE(node.timeDifference() == 5); + REQUIRE(node.latestBlockHeight() == 12345); + REQUIRE(node.latestBlockHash() == randomBlockHash); + REQUIRE(node.peers()[0] == randomId); + } + + SECTION("NodeInfo operator==") { + Hash randomBlockHash = Hash::random(); + P2P::NodeID randomId(boost::asio::ip::address::from_string("127.0.0.1"), uint16_t(8000)); + P2P::NodeID randomId2(boost::asio::ip::address::from_string("127.0.0.2"), uint16_t(8001)); + P2P::NodeInfo node1(uint64_t(1), uint64_t(15000), uint64_t(30000), uint64_t(5), uint64_t(12345), randomBlockHash, {randomId}); + P2P::NodeInfo node2(uint64_t(1), uint64_t(15000), uint64_t(30000), uint64_t(5), uint64_t(12345), randomBlockHash, {randomId}); + P2P::NodeInfo node3(uint64_t(2), uint64_t(1000), uint64_t(3000), uint64_t(4), uint64_t(54321), Hash::random(), {randomId2}); + REQUIRE(node1 == node2); + REQUIRE_FALSE(node1 == node3); + } + } +} + diff --git a/tests/net/p2p/p2p.cpp b/tests/net/p2p/p2p.cpp index c9e89f17..b29c2f74 100644 --- a/tests/net/p2p/p2p.cpp +++ b/tests/net/p2p/p2p.cpp @@ -46,7 +46,7 @@ namespace TP2P { SECTION("2 Node Network, Syncer") { - /// Make blockchainWrapper be 10 blocks ahead + // Make blockchainWrapper be 10 blocks ahead auto blockchainWrapper = initialize(validatorPrivKeysP2P, validatorPrivKeysP2P[0], SDKTestSuite::getTestPort(), true, testDumpPath + "/p2pSyncerNode1"); for (uint64_t index = 0; index < 10; ++index) { std::vector txs; @@ -55,10 +55,10 @@ namespace TP2P { } REQUIRE(blockchainWrapper.storage.latest()->getNHeight() == 10); - /// Create a blockchaiNWrapper2 with zero blocks + // Create a blockchaiNWrapper2 with zero blocks auto blockchainWrapper2 = initialize(validatorPrivKeysP2P, PrivKey(), SDKTestSuite::getTestPort(), true, testDumpPath + "/p2pSyncerNode2"); - /// Start the servers and connect them + // Start the servers and connect them blockchainWrapper.p2p.start(); blockchainWrapper2.p2p.start(); blockchainWrapper.p2p.connectToServer(LOCALHOST, blockchainWrapper2.p2p.serverPort()); @@ -72,7 +72,7 @@ namespace TP2P { REQUIRE(blockchainWrapper.p2p.getSessionsIDs().size() == 1); REQUIRE(blockchainWrapper2.p2p.getSessionsIDs().size() == 1); - /// Run blockchainWrapper2's Syncer + // Run blockchainWrapper2's Syncer // - At most "3" blocks per block range request answer // - Limit to "2000" bytes per block range request answer // - Don't wait for connections ("0") @@ -260,11 +260,11 @@ namespace TP2P { auto blockchainWrapper2 = initialize(validatorPrivKeysP2P, PrivKey(), SDKTestSuite::getTestPort(), true, testDumpPath + "/p2pRequestInfoNode2"); - /// Start the servers + // Start the servers blockchainWrapper1.p2p.start(); blockchainWrapper2.p2p.start(); - /// Connect to each other + // Connect to each other blockchainWrapper1.p2p.connectToServer(LOCALHOST, blockchainWrapper2.p2p.serverPort()); auto futureConnect = std::async(std::launch::async, [&]() { while (blockchainWrapper1.p2p.getSessionsIDs().size() != 1 || diff --git a/tests/utils/block.cpp b/tests/utils/block.cpp index afc6d929..e9b9d9bf 100644 --- a/tests/utils/block.cpp +++ b/tests/utils/block.cpp @@ -22,7 +22,6 @@ namespace TBlock { uint64_t nHeight = 92137812; FinalizedBlock finalizedNewBlock = FinalizedBlock::createNewValidBlock({},{}, nPrevBlockHash, timestamp, nHeight, validatorPrivKey); - // Checking within finalized block REQUIRE(finalizedNewBlock.getValidatorSig() == Signature(Hex::toBytes("18395ff0c8ee38a250b9e7aeb5733c437fed8d6ca2135fa634367bb288a3830a3c624e33401a1798ce09f049fb6507adc52b085d0a83dacc43adfa519c1228e701"))); REQUIRE(Secp256k1::verifySig(finalizedNewBlock.getValidatorSig().r(), finalizedNewBlock.getValidatorSig().s(), finalizedNewBlock.getValidatorSig().v())); @@ -62,10 +61,8 @@ namespace TBlock { // Compare transactions for (uint64_t i = 0; i < 10; i++) REQUIRE(finalizedNewBlock.getTxs()[i] == tx); - } - SECTION("Block creation with 64 TxBlock transactions and 16 TxValidator transactions") { // There is 16 TxValidator transactions, but only 8 of them are used for block randomness. PrivKey blockValidatorPrivKey(Hex::toBytes("0x77ec0f8f28012de474dcd0b0a2317df22e188cec0a4cb0c9b760c845a23c9699")); @@ -311,10 +308,12 @@ namespace TBlock { txValidatorPrivKey ); } + // We need to calculate the merkle root BEFORE creating the block // because we MOVE the transactions to the block. Hash txMerkleRoot = Merkle(txs).getRoot(); Hash validatorMerkleRoot = Merkle(txValidators).getRoot(); + // Also make a copy of txValidators and txs for later comparison std::vector txValidatorsCopy = txValidators; std::vector txsCopy = txs; @@ -332,10 +331,14 @@ namespace TBlock { REQUIRE(finalizedNewBlock.getTxValidators().size() == 256); REQUIRE(finalizedNewBlock.getTxs().size() == 40000); - // Compare transactions for (uint64_t i = 0; i < 40000; ++i) REQUIRE(finalizedNewBlock.getTxs()[i] == txsCopy[i]); for (uint64_t i = 0; i < 256; ++i) REQUIRE(finalizedNewBlock.getTxValidators()[i] == txValidatorsCopy[i]); + + // Deserialize again with threading (for coverage) + Bytes serializedNewBlock = finalizedNewBlock.serializeBlock(); + FinalizedBlock finalizedNewBlock2 = FinalizedBlock::fromBytes(serializedNewBlock, 8080); + REQUIRE(finalizedNewBlock2 == finalizedNewBlock); } } } diff --git a/tests/utils/block_throw.cpp b/tests/utils/block_throw.cpp index 0ddf85ae..69810e8a 100644 --- a/tests/utils/block_throw.cpp +++ b/tests/utils/block_throw.cpp @@ -18,7 +18,7 @@ namespace TBlock { "0x9890a27da5231bd842529fa107a6e137e807fb8086f6c740d39a37681e1394317e2b38f540f3a9ed7f0b4f6835fc67613dcb52d2e8b3afa193840441902cc030f2febfaa0a1edd774318d1fe6e3bf1aec16082457f7a66f7fd4bef8ddded9b76d7b9da8a2d15d02eae1743ddcfb9e34fe0374ceaec6e96fb8489d16c6886441697610af9744109384ae774b20eb22cce3677a4c836f57ca30eafc308af2d04cf93ada88ad0fb6968ce6ea1556cc24af1234b8b2d93a0e37a417f53148662659ccdbaa2ed5233d712a2ea93ea0a08e360c72018fa10a8d7" )); REQUIRE(bytes.size() < 217); - REQUIRE_THROWS (FinalizedBlock::fromBytes(bytes, 8080)); + REQUIRE_THROWS(FinalizedBlock::fromBytes(bytes, 8080)); } SECTION("Block with invalid Validator tx height") { @@ -45,7 +45,7 @@ namespace TBlock { Bytes bytes = Hex::toBytes(byteData); - REQUIRE_THROWS (FinalizedBlock::fromBytes(bytes, 8080)); + REQUIRE_THROWS(FinalizedBlock::fromBytes(bytes, 8080)); } } } diff --git a/tests/utils/db.cpp b/tests/utils/db.cpp index 8b43491b..1ef248e9 100644 --- a/tests/utils/db.cpp +++ b/tests/utils/db.cpp @@ -15,6 +15,22 @@ using Catch::Matchers::Equals; namespace TDB { TEST_CASE("DB Tests", "[utils][db]") { + SECTION("DBBatch Manipulation") { + DBBatch batch; + batch.push_back(StrConv::stringToBytes("aaaa"), StrConv::stringToBytes("1234"), StrConv::stringToBytes("0000")); + batch.push_back(DBEntry(Bytes{0xbb, 0xbb}, Bytes{0x56, 0x78})); + batch.delete_key(StrConv::stringToBytes("aaaa"), StrConv::stringToBytes("0000")); + batch.delete_key(Bytes{0xbb, 0xbb}); + auto puts = batch.getPuts(); + auto dels = batch.getDels(); + REQUIRE(puts[0].key == StrConv::stringToBytes("0000aaaa")); + REQUIRE(puts[0].value == StrConv::stringToBytes("1234")); + REQUIRE(puts[1].key == Bytes{0xbb, 0xbb}); + REQUIRE(puts[1].value == Bytes{0x56, 0x78}); + REQUIRE(dels[0] == StrConv::stringToBytes("0000aaaa")); + REQUIRE(dels[1] == Bytes{0xbb, 0xbb}); + } + SECTION("Open and Close DB (w/ throw)") { bool catched = false; DB db("testDB"); @@ -55,45 +71,58 @@ namespace TDB { SECTION("Batched CRUD (Create + Read + Update + Delete)") { // Open DB db("testDB"); - Bytes pfx = DBPrefix::blocks; + Bytes pfx = DBPrefix::blocks; // 0001 DBBatch batchP; DBBatch batchD; - std::vector keys; for (int i = 0; i < 32; i++) { batchP.push_back(Hash::random().asBytes(), Hash::random().asBytes(), pfx); batchD.delete_key(batchP.getPuts()[i].key, pfx); } + std::vector keys; // Reference vector for read checks + for (const DBEntry& e : batchP.getPuts()) keys.push_back(e.key); + for (Bytes& k : keys) k.erase(k.begin(), k.begin() + 2); // Remove prefixes (2 bytes) + std::sort(keys.begin(), keys.end(), [&](Bytes a, Bytes b){ return a < b; }); // Sort the vector for querying key range // Create std::cout << "BatchPuts: " << batchP.getPuts().size() << std::endl; - REQUIRE(db.putBatch(batchP)); for (const DBEntry& entry : batchP.getPuts()) { - /// No need to pass prefix as entry.key already contains it - REQUIRE(db.has(entry.key)); + REQUIRE(db.has(entry.key)); // No need to pass prefix as entry.key already contains it } - // Read + // Read (all) std::vector getB = db.getBatch(pfx); REQUIRE(!getB.empty()); for (const DBEntry& getE : getB) { for (const DBEntry& putE : batchP.getPuts()) { - if (getE.key == putE.key) { - REQUIRE(getE.value == putE.value); - } + if (getE.key == putE.key) REQUIRE(getE.value == putE.value); } } + // Read (specific, for coverage) + std::vector keysToSearch = {keys[0], keys[8], keys[16], keys[24]}; + std::vector getBS = db.getBatch(pfx, keysToSearch); + REQUIRE(!getBS.empty()); + REQUIRE(getBS.size() == 4); + for (const DBEntry& getES : getBS) { + REQUIRE(std::find(keys.begin(), keys.end(), getES.key) != keys.end()); + } + + // Read (getKeys, for coverage) + std::vector getBK = db.getKeys(pfx, keys[0], keys[7]); + for (const Bytes& b : getBK) { + REQUIRE(std::find(keys.begin(), keys.end(), b) != keys.end()); + } + // Update DBBatch newPutB; for (int i = 0; i < 32; i++) { newPutB.push_back(batchP.getPuts()[i].key, Hash::random().asBytes(), pfx); } REQUIRE(db.putBatch(newPutB)); - /// No need to pass prefix as entry.key already contains it + // No need to pass prefix as entry.key already contains it for (const DBEntry& entry : newPutB.getPuts()) REQUIRE(db.has(entry.key)); std::vector newGetB = db.getBatch(pfx); - REQUIRE(!newGetB.empty()); for (const DBEntry& newGetE : newGetB) { for (const DBEntry& newPutE : newPutB.getPuts()) { @@ -105,7 +134,7 @@ namespace TDB { // Delete REQUIRE(db.putBatch(batchD)); - /// No need to pass prefix as key already contains it + // No need to pass prefix as key already contains it for (const Bytes& key : batchD.getDels()) REQUIRE(!db.has(key)); // Close diff --git a/tests/utils/dynamicexception.cpp b/tests/utils/dynamicexception.cpp index 5e929074..349f8b63 100644 --- a/tests/utils/dynamicexception.cpp +++ b/tests/utils/dynamicexception.cpp @@ -128,7 +128,6 @@ namespace TDynamicException { } } - SECTION("Exception with custom objects") { CustomObject obj(100); DynamicException exception("Encountered an issue with ", obj); diff --git a/tests/utils/evmcconv.cpp b/tests/utils/evmcconv.cpp new file mode 100644 index 00000000..8981ad8e --- /dev/null +++ b/tests/utils/evmcconv.cpp @@ -0,0 +1,63 @@ +/* +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 "../../src/libs/catch2/catch_amalgamated.hpp" + +#include "../../src/utils/evmcconv.h" +#include "../../src/utils/uintconv.h" +#include "../../src/utils/strconv.h" + +namespace TJsonAbi { + TEST_CASE("EVMCConv Namespace", "[utils][evmcconv]") { + SECTION("EVMCConv uint256 <-> evmcUint256") { + uint256_t i = 12345678; + evmc::uint256be resEVMC = EVMCConv::uint256ToEvmcUint256(i); + REQUIRE(UintConv::bytesToUint256(bytes::View(resEVMC.bytes, 32)) == i); + uint256_t resUINT = EVMCConv::evmcUint256ToUint256(resEVMC); + REQUIRE(resUINT == i); + } + + SECTION("EVMCConv bytes <-> evmcUint256") { + uint256_t i = 12345678; + evmc::uint256be iEVMC = EVMCConv::uint256ToEvmcUint256(i); + BytesArr<32> resBYTES = EVMCConv::evmcUint256ToBytes(iEVMC); + REQUIRE(UintConv::bytesToUint256(resBYTES) == i); + evmc::uint256be resEVMC = EVMCConv::bytesToEvmcUint256(resBYTES); + REQUIRE(UintConv::bytesToUint256(bytes::View(resEVMC.bytes, 32)) == i); + } + + SECTION("EVMCConv getFunctor") { + evmc_message msg1; + evmc_message msg2; + Bytes msg1Data = Bytes{0x00, 0x00}; + Bytes msg2Data = Bytes{0x01, 0x02, 0x03, 0x04, 0xab, 0xcd, 0xef, 0xff}; + msg1.input_size = 2; + msg1.input_data = msg1Data.data(); + msg2.input_size = 8; + msg2.input_data = msg2Data.data(); + REQUIRE(EVMCConv::getFunctor(msg1) == Functor()); + REQUIRE(EVMCConv::getFunctor(msg2) != Functor()); + REQUIRE(Hex::fromBytes(UintConv::uint32ToBytes(EVMCConv::getFunctor(msg2).value)).get() == "01020304"); + } + + SECTION("EVMCConv getFunctionArgs") { + evmc_message msg1; + evmc_message msg2; + Bytes msg1Data = Bytes{0x00, 0x00}; + Bytes msg2Data = Bytes{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; + msg1.input_size = 2; + msg1.input_data = msg1Data.data(); + msg2.input_size = 16; + msg2.input_data = msg2Data.data(); + bytes::View get1 = EVMCConv::getFunctionArgs(msg1); + bytes::View get2 = EVMCConv::getFunctionArgs(msg2); + REQUIRE(Hex::fromBytes(get1).get() == ""); + REQUIRE(Hex::fromBytes(get2).get() == "0405060708090a0b0c0d0e0f"); + } + } +} + diff --git a/tests/utils/hex.cpp b/tests/utils/hex.cpp index 895d9dad..5070a98b 100644 --- a/tests/utils/hex.cpp +++ b/tests/utils/hex.cpp @@ -110,6 +110,12 @@ namespace THex { Hex hexStrict(hexStr, true); REQUIRE(hex.bytes() == Bytes{0x12, 0x34}); REQUIRE(hexStrict.bytes() == Bytes{0x12, 0x34}); + // Odd-numbered hex for coverage + std::string hexStrOdd = "0x123"; + Hex hexOdd(hexStrOdd, false); + Hex hexStrictOdd(hexStrOdd, true); + REQUIRE(hexOdd.bytes() == Bytes{0x01, 0x23}); + REQUIRE(hexStrictOdd.bytes() == Bytes{0x01, 0x23}); } SECTION("Hex Get") { @@ -152,8 +158,17 @@ namespace THex { REQUIRE_THAT(hexStrict.substr(4, 2), Equals("34")); } - // Cannot test string_view::substr with Catch2 - // REQUIRE_THAT(hex.substr_view(0, 2), Equals("12")); throws template errors + // std::string() wrapper is required here because Catch2 can't discern overloaded types + SECTION("Hex Substr View") { + std::string hexStr = "0x1234"; + Hex hex(hexStr, false); + Hex hexStrict(hexStr, true); + REQUIRE_THAT(std::string(hex.substr_view(0, 2)), Equals("12")); + REQUIRE_THAT(std::string(hex.substr_view(2, 2)), Equals("34")); + REQUIRE_THAT(std::string(hexStrict.substr_view(0, 2)), Equals("0x")); + REQUIRE_THAT(std::string(hexStrict.substr_view(2, 2)), Equals("12")); + REQUIRE_THAT(std::string(hexStrict.substr_view(4, 2)), Equals("34")); + } SECTION("Hex ToInt") { // Yes I know this is overkill (it could be more tho) but you can never be too sure lol @@ -209,6 +224,18 @@ namespace THex { REQUIRE_THAT(hex.get(), Equals("12345678")); REQUIRE_THAT(hexStrict.get(), Equals("0x12345678")); } + + SECTION("Hex operator<<") { + std::string hexStr = "0x1234"; + Hex hex(hexStr, false); + Hex hexStrict(hexStr, true); + std::stringstream ss; + std::stringstream ssStrict; + ss << hex; + ssStrict << hexStrict; + REQUIRE_THAT(ss.str(), Equals("1234")); + REQUIRE_THAT(ssStrict.str(), Equals("0x1234")); + } } } diff --git a/tests/utils/intconv.cpp b/tests/utils/intconv.cpp new file mode 100644 index 00000000..97d514a8 --- /dev/null +++ b/tests/utils/intconv.cpp @@ -0,0 +1,88 @@ +/* +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 "../../src/libs/catch2/catch_amalgamated.hpp" + +#include "../../src/utils/intconv.h" + +#include "../../src/utils/utils.h" // strings.h -> hex.h + +using Catch::Matchers::Equals; + +namespace TUtils { + TEST_CASE("IntConv Namespace", "[utils][intconv]") { + SECTION("int256ToBytes Test") { + int256_t int256Input = int256_t("91830918212381802449294565349763096207758814059154440393436864477986483867239"); + auto int256Output = IntConv::int256ToBytes(int256Input); + BytesArr<32> int256ExpectedOutput = BytesArr<32> {0xcb, 0x06, 0x75, 0x32, 0x90, 0xff, 0xac, 0x16, 0x72, 0x05, 0xd0, 0xf5, 0x3b, 0x64, 0xac, 0xfd, 0x80, 0xbe, 0x11, 0xed, 0xbb, 0x26, 0xa2, 0x24, 0xbe, 0xd9, 0x23, 0x9a, 0xe6, 0x74, 0x0e, 0x67}; + REQUIRE(int256Output == int256ExpectedOutput); + } + + SECTION("int136ToBytes Test") { + int136_t int136Input = int136_t("87112285131760246616623899502532662132135"); + auto int136Output = IntConv::int136ToBytes(int136Input); + BytesArr<17> int136ExpectedOutput = BytesArr<17> {0xff, 0xff, 0xff, 0xd8, 0x8e, 0x94, 0x95, 0xee, 0xc9, 0x84, 0xf6, 0x26, 0xc7, 0xe9, 0x3f, 0xfd, 0xa7}; + REQUIRE(int136Output == int136ExpectedOutput); + } + + SECTION("int64ToBytes Test") { + int64_t int64Input = int64_t(1155010102558518614LL); + auto int64Output = IntConv::int64ToBytes(int64Input); + BytesArr<8> int64ExpectedOutput = BytesArr<8> {0x10, 0x07, 0x6b, 0x91, 0x9a, 0xfc, 0xed, 0x56}; + REQUIRE(int64Output == int64ExpectedOutput); + } + + SECTION("bytesToInt256 Test") { + FixedBytes<32> bytesStr(bytes::view("\xcb\x06\x75\x32\x90\xff\xac\x16\x72\x05\xd0\xf5\x3b\x64\xac\xfd\x80\xbe\x11\xed\xbb\x26\xa2\x24\xbe\xd9\x23\x9a\xe6\x74\x0e\x67")); + auto int256Output = IntConv::bytesToInt256(bytesStr); + int256_t int256ExpectedOutput = int256_t("-23961171024934392974276419658924811645511170606486123646020719529926645772697"); + REQUIRE(int256Output == int256ExpectedOutput); + + bool catchLo = false; + bool catchHi = false; + Bytes loStr = Hex::toBytes("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + Bytes hiStr = Hex::toBytes("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + try { IntConv::bytesToInt256(loStr); } catch (std::exception &e) { catchLo = true; } + try { IntConv::bytesToInt256(hiStr); } catch (std::exception &e) { catchHi = true; } + REQUIRE(catchLo == true); + REQUIRE(catchHi == true); + } + + SECTION("bytesToInt136 Test") { + BytesArr<17> bytesArr = BytesArr<17> {0xff, 0xff, 0xff, 0xd8, 0x8e, 0x94, 0x95, 0xee, 0xc9, 0x84, 0xf6, 0x26, 0xc7, 0xe9, 0x3f, 0xfd, 0xa7}; + auto int136Output = IntConv::bytesToInt136(bytesArr); + int136_t int136ExpectedOutput = int136_t("87112285131760246616623899502532662132135"); + REQUIRE(int136Output == int136ExpectedOutput); + + bool catchLo = false; + bool catchHi = false; + Bytes loStr = Hex::toBytes("0xffffffffffffffffffffffffffffffffffffffffffffffff"); + Bytes hiStr = Hex::toBytes("0xfffffffffffffffffffffffffffffffffffffffffffffffff"); + try { IntConv::bytesToInt136(loStr); } catch (std::exception &e) { catchLo = true; } + try { IntConv::bytesToInt136(hiStr); } catch (std::exception &e) { catchHi = true; } + REQUIRE(catchLo == true); + REQUIRE(catchHi == true); + } + + SECTION("bytesToInt64 Test") { + FixedBytes<8> bytesStr(bytes::view("\x10\x07\x6b\x91\x9a\xfc\xed\x56")); + auto int64Output = IntConv::bytesToInt64(bytesStr); + int64_t int64ExpectedOutput = int64_t(1155010102558518614ULL); + REQUIRE(int64Output == int64ExpectedOutput); + + bool catchLo = false; + bool catchHi = false; + Bytes loStr = Hex::toBytes("0xffffffffffffff"); + Bytes hiStr = Hex::toBytes("0xffffffffffffffffff"); + try { IntConv::bytesToInt64(loStr); } catch (std::exception &e) { catchLo = true; } + try { IntConv::bytesToInt64(hiStr); } catch (std::exception &e) { catchHi = true; } + REQUIRE(catchLo == true); + REQUIRE(catchHi == true); + } + } +} + diff --git a/tests/utils/jsonabi.cpp b/tests/utils/jsonabi.cpp new file mode 100644 index 00000000..8f4ab66b --- /dev/null +++ b/tests/utils/jsonabi.cpp @@ -0,0 +1,203 @@ +/* +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 "../../src/libs/catch2/catch_amalgamated.hpp" + +#include "../../src/utils/jsonabi.h" + +namespace TJsonAbi { + TEST_CASE("JsonAbi Namespace", "[utils][jsonabi]") { + SECTION("JsonAbi isArray") { + std::string type1 = "int[]"; + std::string type2 = "int"; + REQUIRE(JsonAbi::isArray(type1)); + REQUIRE_FALSE(JsonAbi::isArray(type2)); + } + + SECTION("JsonAbi isTuple") { + std::string type1 = "(int, double, float)"; + std::string type2 = "(int, double)[]"; + std::string type3 = "int"; + REQUIRE(JsonAbi::isTuple(type1)); + REQUIRE(JsonAbi::isTuple(type2)); + REQUIRE_FALSE(JsonAbi::isTuple(type3)); + } + + SECTION("JsonAbi countTupleArrays") { + std::string type0 = "(int, double)"; + std::string type1 = "(int, double)[]"; + std::string type2 = "(int, double)[][]"; + std::string type3 = "(int, double)[][][]"; + REQUIRE(JsonAbi::countTupleArrays(type0) == 0); + REQUIRE(JsonAbi::countTupleArrays(type1) == 1); + REQUIRE(JsonAbi::countTupleArrays(type2) == 2); + REQUIRE(JsonAbi::countTupleArrays(type3) == 3); + } + + SECTION("JsonAbi getTupleTypes") { + std::string type1 = "(int, double)"; + std::string type2 = "(bytes, string, address)[]"; + std::vector get1 = JsonAbi::getTupleTypes(type1); + std::vector get2 = JsonAbi::getTupleTypes(type2); + REQUIRE(get1.size() == 2); + REQUIRE(get2.size() == 3); + REQUIRE(get1[0] == "int"); + REQUIRE(get1[1] == "double"); + REQUIRE(get2[0] == "bytes"); + REQUIRE(get2[1] == "string"); + REQUIRE(get2[2] == "address"); + } + + SECTION("JsonAbi handleTupleComponents") { + std::vector comps = {"int", "(int, double)", "(bytes, string, address)[]"}; + json compsJson = JsonAbi::handleTupleComponents(comps); + json obj1 = compsJson[0]; + json obj2 = compsJson[1]; + json obj3 = compsJson[2]; + REQUIRE(obj1["internalType"] == "int"); + REQUIRE(obj1["type"] == "int"); + REQUIRE(obj2["components"].is_array()); + REQUIRE(obj2["type"] == "tuple"); + REQUIRE(obj2["components"][0]["internalType"] == "int"); + REQUIRE(obj2["components"][0]["type"] == "int"); + REQUIRE(obj2["components"][1]["internalType"] == "double"); + REQUIRE(obj2["components"][1]["type"] == "double"); + REQUIRE(obj3["components"].is_array()); + REQUIRE(obj3["type"] == "tuple[]"); + REQUIRE(obj3["components"][0]["internalType"] == "bytes"); + REQUIRE(obj3["components"][0]["type"] == "bytes"); + REQUIRE(obj3["components"][1]["internalType"] == "string"); + REQUIRE(obj3["components"][1]["type"] == "string"); + REQUIRE(obj3["components"][2]["internalType"] == "address"); + REQUIRE(obj3["components"][2]["type"] == "address"); + } + + SECTION("JsonAbi parseMethodInput") { + std::vector> inputs = { + {"int", "var1"}, {"(int, double)", "var2"}, {"(bytes, string, address)[]", "var3"} + }; + json inputsJson = JsonAbi::parseMethodInput(inputs); + json input1 = inputsJson[0]; + json input2 = inputsJson[1]; + json input3 = inputsJson[2]; + REQUIRE(input1["internalType"] == "int"); + REQUIRE(input1["name"] == "var1"); + REQUIRE(input1["type"] == "int"); + REQUIRE(input2["components"].is_array()); + REQUIRE(input2["name"] == "var2"); + REQUIRE(input2["type"] == "tuple"); + REQUIRE(input2["components"][0]["internalType"] == "int"); + REQUIRE(input2["components"][0]["type"] == "int"); + REQUIRE(input2["components"][1]["internalType"] == "double"); + REQUIRE(input2["components"][1]["type"] == "double"); + REQUIRE(input3["components"].is_array()); + REQUIRE(input3["name"] == "var3"); + REQUIRE(input3["type"] == "tuple[]"); + REQUIRE(input3["components"][0]["internalType"] == "bytes"); + REQUIRE(input3["components"][0]["type"] == "bytes"); + REQUIRE(input3["components"][1]["internalType"] == "string"); + REQUIRE(input3["components"][1]["type"] == "string"); + REQUIRE(input3["components"][2]["internalType"] == "address"); + REQUIRE(input3["components"][2]["type"] == "address"); + } + + SECTION("JsonAbi parseMethodOutput") { + std::vector outputs = {"int", "(int, double)", "(bytes, string, address)[]"}; + json outputsJson = JsonAbi::parseMethodOutput(outputs); + json output1 = outputsJson[0]; + json output2 = outputsJson[1]; + json output3 = outputsJson[2]; + REQUIRE(output1["internalType"] == "int"); + REQUIRE(output1["type"] == "int"); + REQUIRE(output2["components"].is_array()); + REQUIRE(output2["type"] == "tuple"); + REQUIRE(output2["components"][0]["internalType"] == "int"); + REQUIRE(output2["components"][0]["type"] == "int"); + REQUIRE(output2["components"][1]["internalType"] == "double"); + REQUIRE(output2["components"][1]["type"] == "double"); + REQUIRE(output3["components"].is_array()); + REQUIRE(output3["type"] == "tuple[]"); + REQUIRE(output3["components"][0]["internalType"] == "bytes"); + REQUIRE(output3["components"][0]["type"] == "bytes"); + REQUIRE(output3["components"][1]["internalType"] == "string"); + REQUIRE(output3["components"][1]["type"] == "string"); + REQUIRE(output3["components"][2]["internalType"] == "address"); + REQUIRE(output3["components"][2]["type"] == "address"); + } + + SECTION("JsonAbi parseEventArgs") { + std::vector> events = { + {"int", "arg1", true}, {"(int, double)", "arg2", false}, {"(bytes, string, address)[]", "arg3", true} + }; + json eventsJson = JsonAbi::parseEventArgs(events); + json event1 = eventsJson[0]; + json event2 = eventsJson[1]; + json event3 = eventsJson[2]; + REQUIRE(event1["indexed"] == true); + REQUIRE(event1["internalType"] == "int"); + REQUIRE(event1["name"] == "arg1"); + REQUIRE(event1["type"] == "int"); + REQUIRE(event2["components"].is_array()); + REQUIRE(event2["indexed"] == false); + REQUIRE(event2["name"] == "arg2"); + REQUIRE(event2["type"] == "tuple"); + REQUIRE(event2["components"][0]["internalType"] == "int"); + REQUIRE(event2["components"][0]["type"] == "int"); + REQUIRE(event2["components"][1]["internalType"] == "double"); + REQUIRE(event2["components"][1]["type"] == "double"); + REQUIRE(event3["components"].is_array()); + REQUIRE(event3["indexed"] == true); + REQUIRE(event3["name"] == "arg3"); + REQUIRE(event3["type"] == "tuple[]"); + REQUIRE(event3["components"][0]["internalType"] == "bytes"); + REQUIRE(event3["components"][0]["type"] == "bytes"); + REQUIRE(event3["components"][1]["internalType"] == "string"); + REQUIRE(event3["components"][1]["type"] == "string"); + REQUIRE(event3["components"][2]["internalType"] == "address"); + REQUIRE(event3["components"][2]["type"] == "address"); + } + + SECTION("JsonAbi methodToJSON") { + ABI::MethodDescription desc; + desc.name = "transfer"; + desc.inputs = {{"uint256", "amount"}, {"address", "to"}}; + desc.outputs = {"bool"}; + desc.stateMutability = FunctionTypes::NonPayable; + desc.type = "function"; + json descObj = JsonAbi::methodToJSON(desc); + REQUIRE(descObj["name"] == "transfer"); + REQUIRE(descObj["inputs"][0]["internalType"] == "uint256"); + REQUIRE(descObj["inputs"][0]["type"] == "uint256"); + REQUIRE(descObj["inputs"][1]["internalType"] == "address"); + REQUIRE(descObj["inputs"][1]["type"] == "address"); + REQUIRE(descObj["outputs"][0]["internalType"] == "bool"); + REQUIRE(descObj["outputs"][0]["type"] == "bool"); + REQUIRE(descObj["stateMutability"] == "nonpayable"); + REQUIRE(descObj["type"] == "function"); + } + + SECTION("JsonAbi eventToJSON") { + ABI::EventDescription desc; + desc.name = "transferred"; + desc.args = {{"uint256", "amount", true}, {"address", "to", false}}; + desc.anonymous = false; + json descObj = JsonAbi::eventToJSON(desc); + REQUIRE(descObj["anonymous"] == false); + REQUIRE(descObj["inputs"][0]["indexed"] == true); + REQUIRE(descObj["inputs"][0]["internalType"] == "uint256"); + REQUIRE(descObj["inputs"][0]["name"] == "amount"); + REQUIRE(descObj["inputs"][0]["type"] == "uint256"); + REQUIRE(descObj["inputs"][1]["indexed"] == false); + REQUIRE(descObj["inputs"][1]["internalType"] == "address"); + REQUIRE(descObj["inputs"][1]["name"] == "to"); + REQUIRE(descObj["inputs"][1]["type"] == "address"); + REQUIRE(descObj["name"] == "transferred"); + REQUIRE(descObj["type"] == "event"); + } + } +} + diff --git a/tests/utils/options.cpp b/tests/utils/options.cpp index 1a248cc4..a3026226 100644 --- a/tests/utils/options.cpp +++ b/tests/utils/options.cpp @@ -21,10 +21,10 @@ const std::vector validatorPrivKeys_ { }; namespace TOptions { - TEST_CASE("Option Class", "[core][options]") { + TEST_CASE("Options Class", "[utils][options]") { std::string testDumpPath = Utils::getTestDumpPath(); SECTION("Options from File (default)") { - if(std::filesystem::exists(testDumpPath + "/optionClassFromFileWithPrivKey")) { + if (std::filesystem::exists(testDumpPath + "/optionClassFromFileWithPrivKey")) { std::filesystem::remove_all(testDumpPath + "/optionClassFromFileWithPrivKey"); } PrivKey genesisPrivKey(Hex::toBytes("0xe89ef6409c467285bcae9f80ab1cfeb3487cfe61ab28fb7d36443e1daa0c2867")); @@ -52,7 +52,10 @@ namespace TOptions { 10000, 1000, 4, - {}, + { + {boost::asio::ip::address_v4::from_string("127.0.0.1"), uint64_t(8000)}, + {boost::asio::ip::address_v4::from_string("127.0.0.1"), uint64_t(8001)} + }, genesis, genesisTimestamp, genesisPrivKey, @@ -88,7 +91,29 @@ namespace TOptions { REQUIRE(optionsFromFileWithPrivKey.getGenesisBlock() == optionsWithPrivKey.getGenesisBlock()); REQUIRE(optionsFromFileWithPrivKey.getGenesisBalances() == optionsWithPrivKey.getGenesisBalances()); REQUIRE(optionsFromFileWithPrivKey.getGenesisValidators() == optionsWithPrivKey.getGenesisValidators()); + } + SECTION("IndexingMode Coverage") { + IndexingMode idx1(IndexingMode::DISABLED); + IndexingMode idx2(IndexingMode::RPC); + IndexingMode idx3(IndexingMode::RPC_TRACE); + IndexingMode idx1Str("DISABLED"); + IndexingMode idx2Str("RPC"); + IndexingMode idx3Str("RPC_TRACE"); + std::string_view idx1View = idx1.toString(); + std::string_view idx2View = idx2.toString(); + std::string_view idx3View = idx3.toString(); + REQUIRE(idx1 == IndexingMode::DISABLED); + REQUIRE(idx2 == IndexingMode::RPC); + REQUIRE(idx3 == IndexingMode::RPC_TRACE); + REQUIRE(idx1 == idx1Str); + REQUIRE(idx2 == idx2Str); + REQUIRE(idx3 == idx3Str); + REQUIRE(idx1View == "DISABLED"); + REQUIRE(idx2View == "RPC"); + REQUIRE(idx3View == "RPC_TRACE"); + REQUIRE_THROWS(IndexingMode("unknown")); } } } + diff --git a/tests/utils/randomgen.cpp b/tests/utils/randomgen.cpp index 75751ee8..a94d0456 100644 --- a/tests/utils/randomgen.cpp +++ b/tests/utils/randomgen.cpp @@ -28,7 +28,7 @@ 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(Hex::toBytes("a62a86472e5c224aa0a784eccaf794abb60302e2073d52ae0d095ac5d16f03a6")); RandomGen generator(seed); REQUIRE(generator.getSeed() == seed); auto newSeed = generator(); @@ -49,7 +49,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(Hex::toBytes("a4dd40261fbabe977ab6ff77a7ea9f76cd3b286aa66290b0d62bdf4303f4382b")); RandomGen generator(seed); generator.shuffle(vector); diff --git a/tests/utils/safehash.cpp b/tests/utils/safehash.cpp new file mode 100644 index 00000000..c8616f76 --- /dev/null +++ b/tests/utils/safehash.cpp @@ -0,0 +1,55 @@ +/* +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 "../../src/libs/catch2/catch_amalgamated.hpp" + +#include "../../src/utils/safehash.h" + +namespace TSafeHash { + TEST_CASE("SafeHash Struct", "[utils][safehash]") { + SECTION("SafeHash operator() (coverage)") { + const uint64_t i = 12345; + const std::string str = "Hello World"; + const std::string_view strView = "Goodbye Planet"; + const Bytes bytes = Bytes{0xDE, 0xAD, 0xBE, 0xEF}; + const BytesArr<4> bytesArr = {0xDE, 0xAD, 0xBE, 0xEF}; + const bytes::View bytesView = Utils::create_view_span(str); + const Address add(std::string("0x1234567890123456789012345678901234567890"), false); + const Functor func{83892529}; + const Hash hash = Hash::random(); + const TxValidator tx(Hex::toBytes("f845808026a08a4591f48d6307bb4cb8a0b0088b544d923d00bc1f264c3fdf16f946fdee0b34a077a6f6e8b3e78b45478827604f070d03060f413d823eae7fab9b139be7a41d81"), 1); + const std::shared_ptr ptr = std::make_shared(str); + const FixedBytes<4> fixed{0xDE, 0xAD, 0xBE, 0xEF}; + const boost::unordered_flat_map map = {{1, "aaa"}, {2, "bbb"}}; + const std::pair nodeIdV4 = std::make_pair( + boost::asio::ip::address_v4::from_string("127.0.0.1"), 8000 + ); + const std::pair nodeIdV6 = std::make_pair( + boost::asio::ip::address_v6::from_string("::1"), 8000 + ); + + SafeHash()(i); + SafeHash()(str); + SafeHash()(strView); + SafeHash()(bytes); + SafeHash()(bytesArr); + SafeHash()(bytesView); + SafeHash()(add); + SafeHash()(func); + SafeHash()(hash); + SafeHash()(tx); + //SafeHash()(ptr); // Not sure why those error out, but they're already covered anyway + SafeHash()(fixed); + //SafeHash()(map); + SafeHash()(nodeIdV4); + SafeHash()(nodeIdV6); + + REQUIRE(true); // No test cases, just calling functions for coverage purposes + } + } +} + diff --git a/tests/utils/strconv.cpp b/tests/utils/strconv.cpp index f5a4a010..1ebed7cf 100644 --- a/tests/utils/strconv.cpp +++ b/tests/utils/strconv.cpp @@ -13,10 +13,28 @@ See the LICENSE.txt file in the project root for more information. using Catch::Matchers::Equals; -// TODO: missing tests for padLeft/padRight (no bytes) and cArrayToBytes - namespace TUtils { TEST_CASE("StrConv Namespace", "[utils][strconv]") { + SECTION("padLeft Test") { + std::string input = "abcdef"; + std::string output = StrConv::padLeft(input, 10, '0'); + std::string output2 = StrConv::padLeft(input, 20, '1'); + std::string expectedOutput = "0000abcdef"; + std::string expectedOutput2 = "11111111111111abcdef"; + REQUIRE(output == expectedOutput); + REQUIRE(output2 == expectedOutput2); + } + + SECTION("padRight Test") { + std::string input = "abcdef"; + std::string output = StrConv::padRight(input, 10, '0'); + std::string output2 = StrConv::padRight(input, 20, '1'); + std::string expectedOutput = "abcdef0000"; + std::string expectedOutput2 = "abcdef11111111111111"; + REQUIRE(output == expectedOutput); + REQUIRE(output2 == expectedOutput2); + } + SECTION("padLeftBytes Test") { Bytes inputBytes = Hex::toBytes("0xabcdef"); Bytes outputBytes = StrConv::padLeftBytes(inputBytes, 10, 0x00); diff --git a/tests/utils/strings.cpp b/tests/utils/strings.cpp index 987b5846..c40a7352 100644 --- a/tests/utils/strings.cpp +++ b/tests/utils/strings.cpp @@ -12,7 +12,7 @@ See the LICENSE.txt file in the project root for more information. using Catch::Matchers::Equals; namespace TFixedStr { - TEST_CASE("FixedBytes Class", "[utils][strings]") { + TEST_CASE("FixedBytes Class", "[utils][strings][fixedbytes]") { SECTION("FixedBytes Default Constructor") { FixedBytes<10> str1; FixedBytes<20> str2; @@ -20,6 +20,13 @@ namespace TFixedStr { REQUIRE(str2.asBytes() == Bytes(20, 0x00)); } + SECTION("FixedBytes Initializer List Constructor") { + std::initializer_list ilist = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a}; + FixedBytes<10> str1(ilist); + REQUIRE(str1.asBytes() == Bytes({0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a})); + REQUIRE_THROWS(FixedBytes<20>(ilist)); + } + SECTION("FixedBytes Copy Bytes Constructor") { FixedBytes<10> str1(bytes::view("1234567890")); FixedBytes<10> str2(bytes::view("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a")); @@ -143,19 +150,35 @@ namespace TFixedStr { } namespace THash { - TEST_CASE("Hash Class", "[utils]") { + TEST_CASE("Hash Class", "[utils][strings][hash]") { SECTION("Hash uint256_t Constructor") { uint256_t i = uint256_t("70518832285973061936518038480459635341011381946952877582230426678885538674712"); Hash hash(i); REQUIRE_THAT(hash.hex(), Equals("9be83ea08b549e7c77644c451b55a674bb12e4668d018183ff9723b1de493818")); } + SECTION("Hash evmc::bytes32 Constructor") { + evmc::bytes32 num; + Bytes b = Hex::toBytes("9be83ea08b549e7c77644c451b55a674bb12e4668d018183ff9723b1de493818"); + for (int i = 0; i < 32; i++) num.bytes[i] = b[i]; + Hash hash(num); + REQUIRE_THAT(hash.hex(), Equals("9be83ea08b549e7c77644c451b55a674bb12e4668d018183ff9723b1de493818")); + } + SECTION("Hash toUint256") { uint256_t i = uint256_t("70518832285973061936518038480459635341011381946952877582230426678885538674712"); Hash hash(i); REQUIRE(hash.toUint256() == i); } + SECTION("Hash toEvmcBytes32") { + evmc::bytes32 num; + Bytes b = Hex::toBytes("9be83ea08b549e7c77644c451b55a674bb12e4668d018183ff9723b1de493818"); + for (int i = 0; i < 32; i++) num.bytes[i] = b[i]; + Hash hash(num); + REQUIRE(hash.toEvmcBytes32() == num); + } + SECTION("Hash random()") { Hash hash1 = Hash::random(); Hash hash2 = Hash::random(); @@ -165,7 +188,7 @@ namespace THash { } namespace TSignature { - TEST_CASE("Signature Class", "[utils]") { + TEST_CASE("Signature Class", "[utils][strings][signature]") { SECTION("Signature r()") { Signature sig(bytes::view("70518832285973061936518038480459635341011381946952877582230426678")); REQUIRE(sig.r() == uint256_t("24962382450703388064783334469112749050093133395427026078791530264393631937849")); @@ -184,7 +207,7 @@ namespace TSignature { } namespace TAddress { - TEST_CASE("Address Class", "[utils]") { + TEST_CASE("Address Class", "[utils][strings][address]") { 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); @@ -202,12 +225,14 @@ namespace TAddress { } SECTION("Address isValid") { - std::string inputHexAddress = "0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359"; - std::string inputBytesAddress = "\xfb\x69\x16\x09\x5c\xa1\xdf\x60\xbb\x79\xce\x92\xce\x3e\xa7\x4c\x37\xc5\xd3\x59"; - REQUIRE(Address::isValid(inputHexAddress, false)); - REQUIRE(Address::isValid(inputBytesAddress, true)); - REQUIRE(!Address::isValid(inputHexAddress, true)); - REQUIRE(!Address::isValid(inputBytesAddress, false)); + std::string addHex = "0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359"; + std::string addHexNoPrefix = "fb6916095ca1df60bb79ce92ce3ea74c37c5d359"; + std::string addBytes = "\xfb\x69\x16\x09\x5c\xa1\xdf\x60\xbb\x79\xce\x92\xce\x3e\xa7\x4c\x37\xc5\xd3\x59"; + REQUIRE(Address::isValid(addHex, false)); + REQUIRE(Address::isValid(addBytes, true)); + REQUIRE(Address::isValid(addHexNoPrefix, false)); + REQUIRE(!Address::isValid(addHex, true)); + REQUIRE(!Address::isValid(addBytes, false)); } SECTION("Address isChksum") { @@ -223,3 +248,35 @@ namespace TAddress { } } +namespace TStorageKey { + TEST_CASE("StorageKey Class", "[utils][strings][storagekey]") { + SECTION("StorageKey Constructors") { + evmc::address addr1; + evmc_address addr2; + evmc::bytes32 slot1; + evmc_bytes32 slot2; + Address addr3(std::string("0x1234567890123456789012345678901234567890"), false); + Hash slot3(Hex::toBytes("aaaaaaaabbbbbbbbccccccccddddddddeeeeeeeeffffffff0000000099999999")); + + // TODO: replace this with one of the std::ranges alternatives after ContractHost refactor is merged: + // std::ranges::fill(addr, 0xFF); // <--- preferred + // std::ranges::fill(addr.bytes, 0xFF); + // std::fill(addr.bytes, addr.bytes + sizeof(addr.bytes), 0xFF); + for (int i = 0; i < 20; i++) { addr1.bytes[i] = 0xAA; addr2.bytes[i] = 0xFF; } + for (int i = 0; i < 32; i++) { slot1.bytes[i] = 0xAA; slot2.bytes[i] = 0xFF; } + + StorageKey key1(addr1, slot1); + StorageKey key2(addr2, slot2); + StorageKey key3(addr2, slot1); + StorageKey key4(addr1, slot2); + StorageKey key5(addr3, slot3); + + REQUIRE_THAT(Hex::fromBytes(key1.asBytes()).get(), Equals("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); + REQUIRE_THAT(Hex::fromBytes(key2.asBytes()).get(), Equals("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); + REQUIRE_THAT(Hex::fromBytes(key3.asBytes()).get(), Equals("ffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); + REQUIRE_THAT(Hex::fromBytes(key4.asBytes()).get(), Equals("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); + REQUIRE_THAT(Hex::fromBytes(key5.asBytes()).get(), Equals("1234567890123456789012345678901234567890aaaaaaaabbbbbbbbccccccccddddddddeeeeeeeeffffffff0000000099999999")); + } + } +} + diff --git a/tests/utils/tx.cpp b/tests/utils/tx.cpp index b541154e..74e0925e 100644 --- a/tests/utils/tx.cpp +++ b/tests/utils/tx.cpp @@ -777,6 +777,28 @@ namespace TTX { REQUIRE_THAT(tx.rlpSerialize(), Equals(Hex::toBytes("02f8ed842a43649385a2789b185983e0d7c48543b85e663e830fb45394f3611f06e176b22a95e98aa4c1cae63caadff7a588e0000c1075ebc4a9b87002c3a9dd32683c6ceb4976dfa1529025747527baef0a25512c20d4dc9c099d69a32ba42e5e23d06f4d72d00faf83810d1198d7143df1cf0f1e3cefef9fb51caa7805e2562f56da8e51f7f187320a4ad42825eb7bb8dfaafd37acb153cafa113e9c385ef4a6ed2d50b4a3994171ccbd42c080a0df63f6f3b75909503fe72e01281cb973476b32b7b4c82096a85061b6b6fe2fe6a057e8cacc0d2a8a893a565d3f7d8fe05558c86fa42323c9ba38e0c33efa1c301c"))); REQUIRE(TxBlock(tx.rlpSerialize(), 709059731) == tx); } + + // For coverage + SECTION("TxBlock operator= (copy)") { + // Copied from Simple Transaction 1 + TxBlock tx(Hex::toBytes("0x02f87501826e5c8402faf0808510b3a67cc282520894eb38eab2a8d5f448d7d47005b64697e159aa284e88078cbf1fc56d4f1080c080a0763458eaffb9745026fc6360443e7ff8d171824d0410d48fdf06c08c7d4a8306a031b3d8f1753acc4239ffe0584536f12095651f72a61c684ef221aaa97a315328"), 1); + TxBlock txCopy = tx; + REQUIRE(txCopy.getNonce() == 28252); + REQUIRE(txCopy.getMaxFeePerGas() == uint256_t("71733509314")); + REQUIRE(txCopy.getMaxPriorityFeePerGas() == uint256_t("50000000")); + REQUIRE(txCopy.getGasLimit() == 21000); + REQUIRE(txCopy.getTo() == Address(Hex::toBytes("0xeb38eab2a8d5f448d7d47005b64697e159aa284e"))); + REQUIRE(txCopy.getValue() == uint256_t("544019798182154000")); + REQUIRE(txCopy.getData() == Hex::toBytes("")); + REQUIRE(txCopy.getFrom() == Address(Hex::toBytes("0x27899fface558bde9f284ba5c8c91ec79ee60fd6"))); + REQUIRE(txCopy.getR() == uint256_t("53465405869430873365248981118909670428386485581419461107751901704540234875654")); + REQUIRE(txCopy.getS() == uint256_t("22481092492079163542744806243083258891162648924785755153837119151879964676904")); + REQUIRE(txCopy.getV() == 0); + REQUIRE(txCopy.getChainId() == 1); + REQUIRE(txCopy.hash() == Hash(Hex::toBytes("0x06339421e7fb585e21c52e5c0a1ba1a55ac8691f6c085d344f444791dfec976c"))); + REQUIRE_THAT(txCopy.rlpSerialize(), Equals(Hex::toBytes("0x02f87501826e5c8402faf0808510b3a67cc282520894eb38eab2a8d5f448d7d47005b64697e159aa284e88078cbf1fc56d4f1080c080a0763458eaffb9745026fc6360443e7ff8d171824d0410d48fdf06c08c7d4a8306a031b3d8f1753acc4239ffe0584536f12095651f72a61c684ef221aaa97a315328"))); + REQUIRE(TxBlock(txCopy.rlpSerialize(), 1) == tx); + } } TEST_CASE("TxValidator", "[utils][txvalidator]") { diff --git a/tests/utils/tx_throw.cpp b/tests/utils/tx_throw.cpp index a07f77a4..db39fbf2 100644 --- a/tests/utils/tx_throw.cpp +++ b/tests/utils/tx_throw.cpp @@ -13,18 +13,38 @@ using Catch::Matchers::Equals; namespace TTX { TEST_CASE("TxBlock (Throw)", "[utils][tx][throw]") { + SECTION("Tx is not type 2") { + bool catched = false; + TxBlock tx( + Address(Hex::toBytes("0x13b5c424686de186bc5268d5cfe6aa4200ca9aee")), + Address(Hex::toBytes("0x31Af43C5E5924610a9c02B669c7980D9eBdB9719")), + Hex::toBytes("0xe426208f118c6c7db391b3391dda9b94bb0e5c6da9514ad74b63fd6d723b38be421a039136c0015ef0c6bff94109cb9bc4942031949016b85e919fdca81f59f0e417bd696cf6e8f9203d792edc223a59d24e"), + uint64_t(8080), // ChainID + uint256_t("42968208492763873"), // Nonce + uint256_t("166903214424643"), // Value + uint256_t("65612315125671"), // maxPriorityFeePerGas + uint256_t("712471569147246"), // maxFeePerGas + uint256_t("61182866117425671"), // gasLimit + PrivKey(Hex::toBytes("ce974dad85cf9593db9d5c3e89ca8c67ca0f841dc97f2c58c6ea2038e4fa6d8d")) + ); + Bytes txStr = tx.rlpSerialize(); + txStr[0] = 0x01; + try { TxBlock tx(txStr, 8080); } catch (std::exception &e) { catched = true; } + REQUIRE(catched == true); + } + SECTION("Tx is not a list") { bool catched = false; TxBlock tx( Address(Hex::toBytes("0x13b5c424686de186bc5268d5cfe6aa4200ca9aee")), Address(Hex::toBytes("0x31Af43C5E5924610a9c02B669c7980D9eBdB9719")), Hex::toBytes("0xe426208f118c6c7db391b3391dda9b94bb0e5c6da9514ad74b63fd6d723b38be421a039136c0015ef0c6bff94109cb9bc4942031949016b85e919fdca81f59f0e417bd696cf6e8f9203d792edc223a59d24e"), - uint64_t(8080), /// ChainID - uint256_t("42968208492763873"), /// Nonce - uint256_t("166903214424643"), /// Value - uint256_t("65612315125671"), /// maxPriorityFeePerGas - uint256_t("712471569147246"), /// maxFeePerGas - uint256_t("61182866117425671"), /// gasLimit + uint64_t(8080), // ChainID + uint256_t("42968208492763873"), // Nonce + uint256_t("166903214424643"), // Value + uint256_t("65612315125671"), // maxPriorityFeePerGas + uint256_t("712471569147246"), // maxFeePerGas + uint256_t("61182866117425671"), // gasLimit PrivKey(Hex::toBytes("ce974dad85cf9593db9d5c3e89ca8c67ca0f841dc97f2c58c6ea2038e4fa6d8d")) ); Bytes txStr = tx.rlpSerialize(); @@ -40,12 +60,12 @@ namespace TTX { Address(Hex::toBytes("0x13b5c424686de186bc5268d5cfe6aa4200ca9aee")), Address(Hex::toBytes("0x31Af43C5E5924610a9c02B669c7980D9eBdB9719")), Hex::toBytes("0xe426208f118c6c7db391b3391dda9b94bb0e5c6da9514ad74b63fd6d723b38be421a039136c0015ef0c6bff94109cb9bc4942031949016b85e919fdca81f59f0e417bd696cf6e8f9203d792edc223a59d24e"), - uint64_t(8080), /// ChainID - uint256_t("42968208492763873"), /// Nonce - uint256_t("166903214424643"), /// Value - uint256_t("65612315125671"), /// maxPriorityFeePerGas - uint256_t("712471569147246"), /// maxFeePerGas - uint256_t("61182866117425671"), /// gasLimit + uint64_t(8080), // ChainID + uint256_t("42968208492763873"), // Nonce + uint256_t("166903214424643"), // Value + uint256_t("65612315125671"), // maxPriorityFeePerGas + uint256_t("712471569147246"), // maxFeePerGas + uint256_t("61182866117425671"), // gasLimit PrivKey(Hex::toBytes("ce974dad85cf9593db9d5c3e89ca8c67ca0f841dc97f2c58c6ea2038e4fa6d8d")) ); Bytes txShortStr = tx.rlpSerialize(); @@ -64,7 +84,7 @@ namespace TTX { bool catched = false; Bytes txStr = Hex::toBytes( "02f8d6b71f908798a75ba3d89ae1863bac8ebc67a7870287fd36caa56e87d95d7e1944fa079413b5c424686de186bc5268d5cfe6aa4200ca9aee8697cc2ecec243b852e426208f118c6c7db391b3391dda9b94bb0e5c6da9514ad74b63fd6d723b38be421a039136c0015ef0c6bff94109cb9bc4942031949016b85e919fdca81f59f0e417bd696cf6e8f9203d792edc223a59d24ec001a03f40d77504fededf1c0f59b1b5aeecd6cb1c9bbca983352f10717441b4c39fdfa02d71eaeba43f0cbd3f9184afc4e0a907c0b1b69b6a78e6af38b7ddf6b6c0e2a7" - ); /// "0x...b7" -> not value anymore", thus throw + ); // "0x...b7" -> not value anymore", thus throw try { TxBlock tx(txStr, 8080); } catch (std::exception &e) { catched = true; } REQUIRE(catched == true); } @@ -73,7 +93,7 @@ namespace TTX { bool catched = false; Bytes txStr = Hex::toBytes( "02f8d6821f908798a75ba3d89ae1b73bac8ebc67a7870287fd36caa56e87d95d7e1944fa079413b5c424686de186bc5268d5cfe6aa4200ca9aee8697cc2ecec243b852e426208f118c6c7db391b3391dda9b94bb0e5c6da9514ad74b63fd6d723b38be421a039136c0015ef0c6bff94109cb9bc4942031949016b85e919fdca81f59f0e417bd696cf6e8f9203d792edc223a59d24ec001a03f40d77504fededf1c0f59b1b5aeecd6cb1c9bbca983352f10717441b4c39fdfa02d71eaeba43f0cbd3f9184afc4e0a907c0b1b69b6a78e6af38b7ddf6b6c0e2a7" - ); /// "0x...b7" -> not value anymore", thus throw + ); // "0x...b7" -> not value anymore", thus throw try { TxBlock tx(txStr, 8080); } catch (std::exception &e) { catched = true; } REQUIRE(catched == true); } @@ -82,7 +102,7 @@ namespace TTX { bool catched = false; Bytes txStr = Hex::toBytes( "02f8d6821f90b798a75ba3d89ae1863bac8ebc67a7870287fd36caa56e87d95d7e1944fa079413b5c424686de186bc5268d5cfe6aa4200ca9aee8697cc2ecec243b852e426208f118c6c7db391b3391dda9b94bb0e5c6da9514ad74b63fd6d723b38be421a039136c0015ef0c6bff94109cb9bc4942031949016b85e919fdca81f59f0e417bd696cf6e8f9203d792edc223a59d24ec001a03f40d77504fededf1c0f59b1b5aeecd6cb1c9bbca983352f10717441b4c39fdfa02d71eaeba43f0cbd3f9184afc4e0a907c0b1b69b6a78e6af38b7ddf6b6c0e2a7" - ); /// "0x...b7" -> not value anymore", thus throw + ); // "0x...b7" -> not value anymore", thus throw try { TxBlock tx(txStr, 8080); } catch (std::exception &e) { catched = true; } REQUIRE(catched == true); } @@ -91,7 +111,7 @@ namespace TTX { bool catched = false; Bytes txStr = Hex::toBytes( "02f8d6821f908798a75ba3d89ae1b73bac8ebc67a7870287fd36caa56e87d95d7e1944fa079413b5c424686de186bc5268d5cfe6aa4200ca9aee8697cc2ecec243b852e426208f118c6c7db391b3391dda9b94bb0e5c6da9514ad74b63fd6d723b38be421a039136c0015ef0c6bff94109cb9bc4942031949016b85e919fdca81f59f0e417bd696cf6e8f9203d792edc223a59d24ec001a03f40d77504fededf1c0f59b1b5aeecd6cb1c9bbca983352f10717441b4c39fdfa02d71eaeba43f0cbd3f9184afc4e0a907c0b1b69b6a78e6af38b7ddf6b6c0e2a7" - ); // /// "0x...b7" -> not value anymore", thus throw + ); // "0x...b7" -> not value anymore", thus throw try { TxBlock tx(txStr, 8080); } catch (std::exception &e) { catched = true; } REQUIRE(catched == true); } @@ -100,7 +120,7 @@ namespace TTX { bool catched = false; Bytes txStr = Hex::toBytes( "02f8d6821f908798a75ba3d89ae1863bac8ebc67a7b70287fd36caa56e87d95d7e1944fa079413b5c424686de186bc5268d5cfe6aa4200ca9aee8697cc2ecec243b852e426208f118c6c7db391b3391dda9b94bb0e5c6da9514ad74b63fd6d723b38be421a039136c0015ef0c6bff94109cb9bc4942031949016b85e919fdca81f59f0e417bd696cf6e8f9203d792edc223a59d24ec001a03f40d77504fededf1c0f59b1b5aeecd6cb1c9bbca983352f10717441b4c39fdfa02d71eaeba43f0cbd3f9184afc4e0a907c0b1b69b6a78e6af38b7ddf6b6c0e2a7" - ); // /// "0x...b7" -> not value anymore", thus throw + ); // "0x...b7" -> not value anymore", thus throw try { TxBlock tx(txStr, 8080); } catch (std::exception &e) { catched = true; } REQUIRE(catched == true); } @@ -110,7 +130,7 @@ namespace TTX { bool catched = false; Bytes txStr = Hex::toBytes( "02f8d6821f908798a75ba3d89ae1863bac8ebc67a7870287fd36caa56eb7d95d7e1944fa079413b5c424686de186bc5268d5cfe6aa4200ca9aee8697cc2ecec243b852e426208f118c6c7db391b3391dda9b94bb0e5c6da9514ad74b63fd6d723b38be421a039136c0015ef0c6bff94109cb9bc4942031949016b85e919fdca81f59f0e417bd696cf6e8f9203d792edc223a59d24ec001a03f40d77504fededf1c0f59b1b5aeecd6cb1c9bbca983352f10717441b4c39fdfa02d71eaeba43f0cbd3f9184afc4e0a907c0b1b69b6a78e6af38b7ddf6b6c0e2a7" - ); // /// "0x...b7" -> not value anymore", thus throw + ); // "0x...b7" -> not value anymore", thus throw try { TxBlock tx(txStr, 8080); } catch (std::exception &e) { catched = true; } REQUIRE(catched == true); } @@ -134,7 +154,7 @@ namespace TTX { bool catched = false; Bytes txStr = Hex::toBytes( "02f8d6821f908798a75ba3d89ae1863bac8ebc67a7870287fd36caa56e87d95d7e1944fa079413b5c424686de186bc5268d5cfe6aa4200ca9aeeb797cc2ecec243b852e426208f118c6c7db391b3391dda9b94bb0e5c6da9514ad74b63fd6d723b38be421a039136c0015ef0c6bff94109cb9bc4942031949016b85e919fdca81f59f0e417bd696cf6e8f9203d792edc223a59d24ec001a03f40d77504fededf1c0f59b1b5aeecd6cb1c9bbca983352f10717441b4c39fdfa02d71eaeba43f0cbd3f9184afc4e0a907c0b1b69b6a78e6af38b7ddf6b6c0e2a7" - ); /// "0x...b7" -> not value anymore", thus throw + ); // "0x...b7" -> not value anymore", thus throw try { TxBlock tx(txStr, 8080); } catch (std::exception &e) { catched = true; } REQUIRE(catched == true); } @@ -152,7 +172,7 @@ namespace TTX { bool catched = false; Bytes txStr = Hex::toBytes( "02f8d6821f908798a75ba3d89ae1863bac8ebc67a7870287fd36caa56e87d95d7e1944fa079413b5c424686de186bc5268d5cfe6aa4200ca9aee8697cc2ecec243b852e426208f118c6c7db391b3391dda9b94bb0e5c6da9514ad74b63fd6d723b38be421a039136c0015ef0c6bff94109cb9bc4942031949016b85e919fdca81f59f0e417bd696cf6e8f9203d792edc223a59d24ec001a13f40d77504fededf1c0f59b1b5aeecd6cb1c9bbca983352f10717441b4c39fdfa02d71eaeba43f0cbd3f9184afc4e0a907c0b1b69b6a78e6af38b7ddf6b6c0e2a702f8d6821f908798a75ba3d89ae1863bac8ebc67a7870287fd36caa56e87d95d7e1944fa079413b5c424686de186bc5268d5cfe6aa4200ca9aee8697cc2ecec243b852e426208f118c6c7db391b3391dda9b94bb0e5c6da9514ad74b63fd6d723b38be421a039136c0015ef0c6bff94109cb9bc4942031949016b85e919fdca81f59f0e417bd696cf6e8f9203d792edc223a59d24ec001a13f40d77504fededf1c0f59b1b5aeecd6cb1c9bbca983352f10717441b4c39fdfa02d71eaeba43f0cbd3f9184afc4e0a907c0b1b69b6a78e6af38b7ddf6b6c0e2a7" - ); /// "0x...a1 > 0xa0", thus throw + ); // "0x...a1 > 0xa0", thus throw try { TxBlock tx(txStr, 8080); } catch (std::exception &e) { catched = true; } REQUIRE(catched == true); } @@ -161,7 +181,7 @@ namespace TTX { bool catched = false; Bytes txStr = Hex::toBytes( "02f8d6821f908798a75ba3d89ae1863bac8ebc67a7870287fd36caa56e87d95d7e1944fa079413b5c424686de186bc5268d5cfe6aa4200ca9aee8697cc2ecec243b852e426208f118c6c7db391b3391dda9b94bb0e5c6da9514ad74b63fd6d723b38be421a039136c0015ef0c6bff94109cb9bc4942031949016b85e919fdca81f59f0e417bd696cf6e8f9203d792edc223a59d24ec001a03f40d77504fededf1c0f59b1b5aeecd6cb1c9bbca983352f10717441b4c39fdfa12d71eaeba43f0cbd3f9184afc4e0a907c0b1b69b6a78e6af38b7ddf6b6c0e2a7" - ); /// "0x...a1 > 0xa0", thus throw + ); // "0x...a1 > 0xa0", thus throw try { TxBlock tx(txStr, 8080); } catch (std::exception &e) { catched = true; } REQUIRE(catched == true); } diff --git a/tests/utils/uintconv.cpp b/tests/utils/uintconv.cpp index b2ea5c87..3cf74f7c 100644 --- a/tests/utils/uintconv.cpp +++ b/tests/utils/uintconv.cpp @@ -13,8 +13,6 @@ See the LICENSE.txt file in the project root for more information. using Catch::Matchers::Equals; -// TODO: theoretically missing int_t tests - namespace TUtils { TEST_CASE("UintConv Namespace", "[utils][uintconv]") { SECTION("uint256ToBytes Test") { diff --git a/tests/utils/utils.cpp b/tests/utils/utils.cpp index 64d3c9fa..981a2d65 100644 --- a/tests/utils/utils.cpp +++ b/tests/utils/utils.cpp @@ -11,25 +11,40 @@ See the LICENSE.txt file in the project root for more information. #include "../../src/utils/strconv.h" -using Catch::Matchers::Equals; - namespace TUtils { TEST_CASE("Utils Namespace", "[utils][utilsitself]") { - SECTION("Sha3 Test") { + SECTION("fail (HTTP Fail Message) + logToFile, for coverage)") { + boost::beast::error_code ec; + fail("Utils", "fail", ec, "testing fail log"); + REQUIRE(!ec); + Utils::logToFile("testing log to file"); + } + + SECTION("sha3") { std::string sha3Input = "My SHA3 Input"; auto sha3Output = Utils::sha3(StrConv::stringToBytes(sha3Input)); Bytes sha3ExpectedOutput = Bytes{0x10, 0x11, 0x40, 0xd6, 0xe7, 0x50, 0x6f, 0x80, 0x4c, 0xf7, 0xb0, 0x37, 0x0f, 0xa9, 0x0b, 0x04, 0xc5, 0xe9, 0x37, 0x4d, 0xdb, 0x0c, 0x8c, 0xbe, 0x12, 0xaf, 0x15, 0x0c, 0x8f, 0xf3, 0xee, 0x36}; REQUIRE(sha3Output == Hash(sha3ExpectedOutput)); } - SECTION("randBytes Test") { + SECTION("randBytes") { Bytes randBytesOutput = Utils::randBytes(32); REQUIRE(randBytesOutput.size() == 32); } - SECTION("fromBigEndian Test") { - std::string_view inputBytes("\x10\x11\x40\xd6\xe7\x50\x6f\x80\x4c\xf7\xb0\x37\x0f\xa9\x0b\x04\xc5\xe9\x37\x4d\xdb\x0c\x8c\xbe\x12\xaf\x15\x0c\x8f\xf3\xee\x36"); + SECTION("readConfigFile") { + json cfg = Utils::readConfigFile(); + REQUIRE(cfg.dump() == "{\"rpcport\":8080,\"p2pport\":8081,\"seedNodes\":[\"127.0.0.1:8086\",\"127.0.0.1:8087\",\"127.0.0.1:8088\",\"127.0.0.1:8089\"]}"); + } + SECTION("getSignalName") { + REQUIRE(Utils::getSignalName(0) == "Unknown signal (0)"); + REQUIRE(Utils::getSignalName(1) == "SIGHUP (1)"); + REQUIRE(Utils::getSignalName(2) == "SIGINT (2)"); + } + + SECTION("fromBigEndian") { + std::string_view inputBytes("\x10\x11\x40\xd6\xe7\x50\x6f\x80\x4c\xf7\xb0\x37\x0f\xa9\x0b\x04\xc5\xe9\x37\x4d\xdb\x0c\x8c\xbe\x12\xaf\x15\x0c\x8f\xf3\xee\x36"); auto uint256Output = Utils::fromBigEndian(inputBytes); auto uint64Ouput12To20 = Utils::fromBigEndian(inputBytes.substr(12, 8)); auto uint64Ouput20To28 = Utils::fromBigEndian(inputBytes.substr(20, 8)); @@ -50,13 +65,24 @@ namespace TUtils { REQUIRE(uint160Output5To25 == uint160ExpectedOutput5To25); } - SECTION("appendBytes Test") { + SECTION("appendBytes") { Bytes int1 = Bytes{0x78, 0xF0, 0xB2, 0x91}; Bytes int2 = Bytes{0xAC, 0x26, 0x0E, 0x43}; Bytes res = Bytes{0x78, 0xF0, 0xB2, 0x91, 0xAC, 0x26, 0x0E, 0x43}; Utils::appendBytes(int1, int2); REQUIRE(int1 == res); } + + SECTION("create_view_span") { + Bytes b{0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; + std::string_view sv("abcdef"); + bytes::View v1 = Utils::create_view_span(b, 0, 6); + bytes::View v2 = Utils::create_view_span(sv, 0, 6); + REQUIRE(Hex::fromBytes(v1).get() == "0a0b0c0d0e0f"); + REQUIRE(Hex::fromBytes(v2).get() == "616263646566"); + REQUIRE_THROWS(Utils::create_view_span(b, 0, 12)); + REQUIRE_THROWS(Utils::create_view_span(sv, 0, 12)); + } } }