diff --git a/src/contract/contract.h b/src/contract/contract.h index c1a19c86..3fceb4aa 100644 --- a/src/contract/contract.h +++ b/src/contract/contract.h @@ -17,6 +17,7 @@ See the LICENSE.txt file in the project root for more information. #include "../utils/strings.h" #include "../utils/tx.h" #include "../utils/utils.h" +#include "../utils/dynamicexception.h" #include "variables/safebase.h" // Forward declarations. @@ -138,10 +139,10 @@ class BaseContract : public ContractLocals { * Invoke a contract function using a tuple of (from, to, gasLimit, gasPrice, * value, data). Should be overriden by derived classes. * @param data The tuple of (from, to, gasLimit, gasPrice, value, data). - * @throw std::runtime_error if the derived class does not override this. + * @throw DynamicException if the derived class does not override this. */ virtual void ethCall(const ethCallInfo& data) { - throw std::runtime_error("Derived Class from Contract does not override ethCall()"); + throw DynamicException("Derived Class from Contract does not override ethCall()"); } /** @@ -149,10 +150,10 @@ class BaseContract : public ContractLocals { * Should be overriden by derived classes. * @param data The tuple of (from, to, gasLimit, gasPrice, value, data). * @return A string with the answer to the call. - * @throw std::runtime_error if the derived class does not override this. + * @throw DynamicException if the derived class does not override this. */ virtual const Bytes ethCallView(const ethCallInfo &data) const { - throw std::runtime_error("Derived Class from Contract does not override ethCall()"); + throw DynamicException("Derived Class from Contract does not override ethCallView()"); } /// Getter for `contractAddress`. diff --git a/src/contract/contractfactory.h b/src/contract/contractfactory.h index e42a6865..43ccd9a0 100644 --- a/src/contract/contractfactory.h +++ b/src/contract/contractfactory.h @@ -52,27 +52,27 @@ class ContractFactory { * Setup data for a new contract before creating/validating it. * @param callInfo The call info to process. * @return A pair containing the contract address and the ABI decoder. - * @throw std::runtime_error if non contract creator tries to create a contract. - * @throw std::runtime_error if contract already exists. + * @throw DynamicException if non contract creator tries to create a contract. + * @throw DynamicException if contract already exists. */ template auto setupNewContract(const ethCallInfo &callInfo) { // Check if caller is creator // TODO: Check if caller is creator of the contract, not the creator of the transaction // Allow contracts to create other contracts though. if (this->manager_.getOrigin() != this->manager_.getContractCreator()) { - throw std::runtime_error("Only contract creator can create new contracts"); + throw DynamicException("Only contract creator can create new contracts"); } // Check if contract address already exists on the Dynamic Contract list const Address derivedAddress = this->manager_.deriveContractAddress(); if (this->manager_.contracts_.contains(derivedAddress)) { - throw std::runtime_error("Contract already exists as a Dynamic Contract"); + throw DynamicException("Contract already exists as a Dynamic Contract"); } // Check if contract address already exists on the Protocol Contract list for (const auto &[name, address] : ProtocolContractAddresses) { if (address == derivedAddress) { - throw std::runtime_error("Contract already exists as a Protocol Contract"); + throw DynamicException("Contract already exists as a Protocol Contract"); } } @@ -96,7 +96,7 @@ class ContractFactory { using ConstructorArguments = typename TContract::ConstructorArguments; auto setupResult = this->setupNewContract(callInfo); if (!ContractReflectionInterface::isContractFunctionsRegistered()) { - throw std::runtime_error("Contract " + Utils::getRealTypeName() + " is not registered"); + throw DynamicException("Contract " + Utils::getRealTypeName() + " is not registered"); } Address derivedAddress = setupResult.first; @@ -141,7 +141,7 @@ class ContractFactory { /// But the variables owned by the contract were registered as used in the ContractCallLogger. /// Meaning: we throw here, the variables are freed (as TContract ceases from existing), but a reference to the variable is still /// in the ContractCallLogger. This causes a instant segfault when ContractCallLogger tries to revert the variable - throw std::runtime_error( + throw DynamicException( "Could not construct contract " + Utils::getRealTypeName() + ": " + ex.what() ); } diff --git a/src/contract/contractmanager.cpp b/src/contract/contractmanager.cpp index f2b171a2..c38677ca 100644 --- a/src/contract/contractmanager.cpp +++ b/src/contract/contractmanager.cpp @@ -12,6 +12,7 @@ See the LICENSE.txt file in the project root for more information. #include "customcontracts.h" #include "../core/rdpos.h" #include "../core/state.h" +#include "../utils/dynamicexception.h" ContractManager::ContractManager( const std::unique_ptr& db, State* state, @@ -32,7 +33,7 @@ ContractManager::ContractManager( for (const DBEntry& contract : contractsFromDB) { Address address(contract.key); if (!this->loadFromDB(contract, address)) { - throw std::runtime_error("Unknown contract: " + Utils::bytesToString(contract.value)); + throw DynamicException("Unknown contract: " + Utils::bytesToString(contract.value)); } } } @@ -82,7 +83,9 @@ void ContractManager::ethCall(const ethCallInfo& callInfo) { Functor functor = std::get<5>(callInfo); std::function f; f = this->factory_->getCreateContractFunc(functor.asBytes()); - if (!f) throw std::runtime_error("Invalid function call with functor: " + functor.hex().get()); + if (!f) { + throw DynamicException("Invalid function call with functor: ", functor.hex().get()); + } f(callInfo); } @@ -90,7 +93,7 @@ const Bytes ContractManager::ethCallView(const ethCallInfo& data) const { const auto& functor = std::get<5>(data); // This hash is equivalent to "function getDeployedContracts() public view returns (string[] memory, address[] memory) {}" if (functor == Hex::toBytes("0xaa9a068f")) return this->getDeployedContracts(); - throw std::runtime_error("Invalid function call"); + throw DynamicException("Invalid function call"); } void ContractManager::callContract(const TxBlock& tx, const Hash&, const uint64_t& txIndex) { @@ -104,7 +107,7 @@ void ContractManager::callContract(const TxBlock& tx, const Hash&, const uint64_ } catch (std::exception &e) { this->callLogger_.reset(); this->eventManager_->revertEvents(); - throw std::runtime_error(e.what()); + throw DynamicException(e.what()); } this->callLogger_->shouldCommit(); this->callLogger_.reset(); @@ -119,7 +122,7 @@ void ContractManager::callContract(const TxBlock& tx, const Hash&, const uint64_ } catch (std::exception &e) { this->callLogger_.reset(); this->eventManager_->revertEvents(); - throw std::runtime_error(e.what()); + throw DynamicException(e.what()); } this->callLogger_->shouldCommit(); this->callLogger_.reset(); @@ -132,7 +135,7 @@ void ContractManager::callContract(const TxBlock& tx, const Hash&, const uint64_ if (it == this->contracts_.end()) { this->callLogger_.reset(); this->eventManager_->revertEvents(); - throw std::runtime_error(std::string(__func__) + "(void): Contract does not exist"); + throw DynamicException(std::string(__func__) + "(void): Contract does not exist"); } const std::unique_ptr& contract = it->second; @@ -142,7 +145,7 @@ void ContractManager::callContract(const TxBlock& tx, const Hash&, const uint64_ } catch (std::exception &e) { this->callLogger_.reset(); this->eventManager_->revertEvents(); - throw std::runtime_error(e.what()); + throw DynamicException(e.what()); } if (contract->isPayableFunction(functor)) { @@ -159,7 +162,7 @@ const Bytes ContractManager::callContract(const ethCallInfo& callInfo) const { if (to == ProtocolContractAddresses.at("rdPoS")) return rdpos_->ethCallView(callInfo); std::shared_lock lock(this->contractsMutex_); if (!this->contracts_.contains(to)) { - throw std::runtime_error(std::string(__func__) + "(Bytes): Contract does not exist"); + throw DynamicException(std::string(__func__) + "(Bytes): Contract does not exist"); } return this->contracts_.at(to)->ethCallView(callInfo); } @@ -208,7 +211,7 @@ bool ContractManager::validateCallContractWithTx(const ethCallInfo& callInfo) { contract->ethCall(callInfo); } catch (std::exception &e) { this->callLogger_.reset(); - throw std::runtime_error(e.what()); + throw DynamicException(e.what()); } this->callLogger_.reset(); return true; @@ -268,7 +271,7 @@ void ContractManagerInterface::registerVariableUse(SafeBase& variable) { } void ContractManagerInterface::populateBalance(const Address &address) const { - if (!this->manager_.callLogger_) throw std::runtime_error( + if (!this->manager_.callLogger_) throw DynamicException( "Contracts going haywire! Trying to call ContractState without an active callContract" ); if (!this->manager_.callLogger_->hasBalance(address)) { @@ -280,7 +283,7 @@ void ContractManagerInterface::populateBalance(const Address &address) const { } uint256_t ContractManagerInterface::getBalanceFromAddress(const Address& address) const { - if (!this->manager_.callLogger_) throw std::runtime_error( + if (!this->manager_.callLogger_) throw DynamicException( "Contracts going haywire! Trying to call ContractState without an active callContract" ); this->populateBalance(address); @@ -290,13 +293,13 @@ uint256_t ContractManagerInterface::getBalanceFromAddress(const Address& address void ContractManagerInterface::sendTokens( const Address& from, const Address& to, const uint256_t& amount ) { - if (!this->manager_.callLogger_) throw std::runtime_error( + if (!this->manager_.callLogger_) throw DynamicException( "Contracts going haywire! Trying to call ContractState without an active callContract" ); this->populateBalance(from); this->populateBalance(to); if (this->manager_.callLogger_->getBalanceAt(from) < amount) { - throw std::runtime_error("ContractManager::sendTokens: Not enough balance"); + throw DynamicException("ContractManager::sendTokens: Not enough balance"); } this->manager_.callLogger_->subBalance(from, amount); this->manager_.callLogger_->addBalance(to, amount); diff --git a/src/contract/contractmanager.h b/src/contract/contractmanager.h index c8a08a63..abcbca06 100644 --- a/src/contract/contractmanager.h +++ b/src/contract/contractmanager.h @@ -181,7 +181,7 @@ class ContractManager : public BaseContract { * SafeVariables as contract creation actively writes to DB). * @param data The call info to process. * @return A string with the requested info. - * @throw std::runtime_error if the call is not valid. + * @throw DynamicException if the call is not valid. */ const Bytes ethCallView(const ethCallInfo& data) const override; @@ -190,7 +190,7 @@ class ContractManager : public BaseContract { * @param tx The transaction to process. * @param blockHash The hash of the block that called the contract. Defaults to an empty hash. * @param txIndex The index of the transaction inside the block that called the contract. Defaults to the first position. - * @throw std::runtime_error if the call to the ethCall function fails. + * @throw DynamicException if the call to the ethCall function fails. * TODO: it would be a good idea to revise tests that call this function, default values here only exist as a placeholder */ void callContract(const TxBlock& tx, const Hash& blockHash = Hash(), const uint64_t& txIndex = 0); @@ -199,7 +199,7 @@ class ContractManager : public BaseContract { * Make an eth_call to a view function from the contract. Used by RPC. * @param callInfo The call info to process. * @return A string with the requested info. - * @throw std::runtime_error if the call to the ethCall function fails + * @throw DynamicException if the call to the ethCall function fails * or if the contract does not exist. */ const Bytes callContract(const ethCallInfo& callInfo) const; @@ -215,7 +215,7 @@ class ContractManager : public BaseContract { * Validate a transaction that calls a function from a given contract. * @param callInfo The call info to validate. * @return `true` if the transaction is valid, `false` otherwise. - * @throw std::runtime_error if the validation fails. + * @throw DynamicException if the validation fails. */ bool validateCallContractWithTx(const ethCallInfo& callInfo); @@ -330,14 +330,14 @@ class ContractManagerInterface { const uint256_t& value, R(C::*func)(const Args&...), const Args&... args ) { - if (!this->manager_.callLogger_) throw std::runtime_error( + if (!this->manager_.callLogger_) throw DynamicException( "Contracts going haywire! Trying to call ContractState without an active callContract" ); if (value) { this->sendTokens(fromAddr, targetAddr, value); } if (!this->manager_.contracts_.contains(targetAddr)) { - throw std::runtime_error(std::string(__func__) + ": Contract does not exist - Type: " + throw DynamicException(std::string(__func__) + ": Contract does not exist - Type: " + Utils::getRealTypeName() + " at address: " + targetAddr.hex().get() ); } @@ -346,7 +346,7 @@ class ContractManagerInterface { try { return contract->callContractFunction(func, args...); } catch (const std::exception& e) { - throw std::runtime_error(e.what() + std::string(" - Type: ") + throw DynamicException(e.what() + std::string(" - Type: ") + Utils::getRealTypeName() + " at address: " + targetAddr.hex().get() ); } @@ -369,19 +369,19 @@ class ContractManagerInterface { const Address& txOrigin, const Address& fromAddr, const Address& targetAddr, const uint256_t& value, R(C::*func)() ) { - if (!this->manager_.callLogger_) throw std::runtime_error( + if (!this->manager_.callLogger_) throw DynamicException( "Contracts going haywire! Trying to call ContractState without an active callContract" ); if (value) this->sendTokens(fromAddr, targetAddr, value); if (!this->manager_.contracts_.contains(targetAddr)) { - throw std::runtime_error(std::string(__func__) + ": Contract does not exist"); + throw DynamicException(std::string(__func__) + ": Contract does not exist"); } C* contract = this->getContract(targetAddr); this->manager_.callLogger_->setContractVars(contract, txOrigin, fromAddr, value); try { return contract->callContractFunction(func); } catch (const std::exception& e) { - throw std::runtime_error(e.what()); + throw DynamicException(e.what()); } } @@ -402,7 +402,7 @@ class ContractManagerInterface { const uint256_t &gasPriceValue, const uint256_t &callValue, const Bytes &encoder ) { - if (!this->manager_.callLogger_) throw std::runtime_error( + if (!this->manager_.callLogger_) throw DynamicException( "Contracts going haywire! Trying to call ContractState without an active callContract" ); ethCallInfo callInfo; @@ -434,12 +434,12 @@ class ContractManagerInterface { */ template const T* getContract(const Address &address) const { auto it = this->manager_.contracts_.find(address); - if (it == this->manager_.contracts_.end()) throw std::runtime_error( + if (it == this->manager_.contracts_.end()) throw DynamicException( "ContractManager::getContract: contract at address " + address.hex().get() + " not found." ); auto ptr = dynamic_cast(it->second.get()); - if (ptr == nullptr) throw std::runtime_error( + if (ptr == nullptr) throw DynamicException( "ContractManager::getContract: Contract at address " + address.hex().get() + " is not of the requested type: " + Utils::getRealTypeName() ); @@ -456,12 +456,12 @@ class ContractManagerInterface { */ template T* getContract(const Address& address) { auto it = this->manager_.contracts_.find(address); - if (it == this->manager_.contracts_.end()) throw std::runtime_error( + if (it == this->manager_.contracts_.end()) throw DynamicException( "ContractManager::getContract: contract at address " + address.hex().get() + " not found." ); auto ptr = dynamic_cast(it->second.get()); - if (ptr == nullptr) throw std::runtime_error( + if (ptr == nullptr) throw DynamicException( "ContractManager::getContract: Contract at address " + address.hex().get() + " is not of the requested type: " + Utils::getRealTypeName() ); @@ -471,7 +471,7 @@ class ContractManagerInterface { /** * Emit an event from a contract. Called by DynamicContract's emitEvent(). * @param event The event to emit. - * @throw std::runtime_error if there's an attempt to emit the event outside a contract call. + * @throw DynamicException if there's an attempt to emit the event outside a contract call. */ void emitContractEvent(Event& event) { // Sanity check - events should only be emitted during successful contract @@ -481,7 +481,7 @@ class ContractManagerInterface { // C++ itself already takes care of events not being emitted on pure/view // functions due to its built-in const-correctness logic. // TODO: check later if events are really not emitted on transaction revert - if (!this->manager_.callLogger_) throw std::runtime_error( + if (!this->manager_.callLogger_) throw DynamicException( "Contracts going haywire! Trying to emit an event without an active contract call" ); this->manager_.eventManager_->registerEvent(std::move(event)); diff --git a/src/contract/dynamiccontract.h b/src/contract/dynamiccontract.h index 64e340f6..3b33112f 100644 --- a/src/contract/dynamiccontract.h +++ b/src/contract/dynamiccontract.h @@ -105,7 +105,7 @@ class DynamicContract : public BaseContract { break; } default: { - throw std::runtime_error("Invalid function signature."); + throw DynamicException("Invalid function signature."); } } } @@ -123,7 +123,7 @@ class DynamicContract : public BaseContract { std::string functStr = funcSignature + "()"; switch (methodMutability) { case FunctionTypes::View: { - throw std::runtime_error("View must be const because it does not modify the state."); + throw DynamicException("View must be const because it does not modify the state."); } case FunctionTypes::NonPayable: { this->registerFunction( @@ -146,7 +146,7 @@ class DynamicContract : public BaseContract { break; } default: { - throw std::runtime_error("Invalid function signature."); + throw DynamicException("Invalid function signature."); } } } @@ -171,7 +171,7 @@ class DynamicContract : public BaseContract { }; switch (methodMutability) { case FunctionTypes::View: - throw std::runtime_error("View must be const because it does not modify the state."); + throw DynamicException("View must be const because it does not modify the state."); case FunctionTypes::NonPayable: this->registerFunction(functor, registrationFunc); break; @@ -179,7 +179,7 @@ class DynamicContract : public BaseContract { this->registerPayableFunction(functor, registrationFunc); break; default: - throw std::runtime_error("Invalid function signature."); + throw DynamicException("Invalid function signature."); } } @@ -242,10 +242,10 @@ class DynamicContract : public BaseContract { /** * Template function for calling the register functions. * Should be called by the derived class. - * @throw std::runtime_error if the derived class does not override this. + * @throw DynamicException if the derived class does not override this. */ virtual void registerContractFunctions() { - throw std::runtime_error( + throw DynamicException( "Derived Class from Contract does not override registerContractFunctions()" ); } @@ -283,22 +283,22 @@ class DynamicContract : public BaseContract { * Automatically differs between payable and non-payable functions. * Used by State when calling `processNewBlock()/validateNewBlock()`. * @param callInfo Tuple of (from, to, gasLimit, gasPrice, value, data). - * @throw std::runtime_error if the functor is not found or the function throws an exception. + * @throw DynamicException if the functor is not found or the function throws an exception. */ void ethCall(const ethCallInfo& callInfo) override { try { Functor funcName = std::get<5>(callInfo); if (this->isPayableFunction(funcName)) { auto func = this->payableFunctions_.find(funcName); - if (func == this->payableFunctions_.end()) throw std::runtime_error("Functor not found for payable function"); + if (func == this->payableFunctions_.end()) throw DynamicException("Functor not found for payable function"); func->second(callInfo); } else { auto func = this->publicFunctions_.find(funcName); - if (func == this->publicFunctions_.end()) throw std::runtime_error("Functor not found for non-payable function"); + if (func == this->publicFunctions_.end()) throw DynamicException("Functor not found for non-payable function"); func->second(callInfo); } } catch (const std::exception& e) { - throw std::runtime_error(e.what()); + throw DynamicException(e.what()); } }; @@ -306,16 +306,16 @@ class DynamicContract : public BaseContract { * Do a contract call to a view function. * @param data Tuple of (from, to, gasLimit, gasPrice, value, data). * @return The result of the view function. - * @throw std::runtime_error if the functor is not found or the function throws an exception. + * @throw DynamicException if the functor is not found or the function throws an exception. */ const Bytes ethCallView(const ethCallInfo& data) const override { try { Functor funcName = std::get<5>(data); auto func = this->viewFunctions_.find(funcName); - if (func == this->viewFunctions_.end()) throw std::runtime_error("Functor not found"); + if (func == this->viewFunctions_.end()) throw DynamicException("Functor not found"); return func->second(data); } catch (std::exception& e) { - throw std::runtime_error(e.what()); + throw DynamicException(e.what()); } } @@ -485,7 +485,7 @@ class DynamicContract : public BaseContract { try { return (static_cast(this)->*func)(args...); } catch (const std::exception& e) { - throw std::runtime_error(e.what()); + throw DynamicException(e.what()); } } @@ -501,7 +501,7 @@ class DynamicContract : public BaseContract { try { return (static_cast(this)->*func)(); } catch (const std::exception& e) { - throw std::runtime_error(e.what()); + throw DynamicException(e.what()); } } diff --git a/src/contract/templates/dexv2/dexv2factory.cpp b/src/contract/templates/dexv2/dexv2factory.cpp index ac051512..a4735b23 100644 --- a/src/contract/templates/dexv2/dexv2factory.cpp +++ b/src/contract/templates/dexv2/dexv2factory.cpp @@ -99,11 +99,11 @@ Address DEXV2Factory::getPairByIndex(const uint64_t& index) const { } Address DEXV2Factory::createPair(const Address& tokenA, const Address& tokenB) { - if (tokenA == tokenB) throw std::runtime_error("DEXV2Factory::createPair: IDENTICAL_ADDRESSES"); + if (tokenA == tokenB) throw DynamicException("DEXV2Factory::createPair: IDENTICAL_ADDRESSES"); auto& token0 = (tokenA < tokenB) ? tokenA : tokenB; auto& token1 = (tokenA < tokenB) ? tokenB : tokenA; - if (token0 == Address()) throw std::runtime_error("DEXV2Factory::createPair: ZERO_ADDRESS"); - if (this->getPair(token0, token1) != Address()) throw std::runtime_error("DEXV2Factory::createPair: PAIR_EXISTS"); + if (token0 == Address()) throw DynamicException("DEXV2Factory::createPair: ZERO_ADDRESS"); + if (this->getPair(token0, token1) != Address()) throw DynamicException("DEXV2Factory::createPair: PAIR_EXISTS"); Utils::safePrint("DEXV2Factory: creating pair..."); auto pair = this->callCreateContract(0, 0, 0); Utils::safePrint("DEXV2Factory: pair created..."); diff --git a/src/contract/templates/dexv2/dexv2library.cpp b/src/contract/templates/dexv2/dexv2library.cpp index 03ee42be..4a04bb02 100644 --- a/src/contract/templates/dexv2/dexv2library.cpp +++ b/src/contract/templates/dexv2/dexv2library.cpp @@ -12,9 +12,9 @@ See the LICENSE.txt file in the project root for more information. namespace DEXV2Library { std::pair sortTokens(const Address& tokenA, const Address& tokenB) { - if (tokenA == tokenB) throw std::runtime_error("DEXV2Library: IDENTICAL_ADDRESSES"); + if (tokenA == tokenB) throw DynamicException("DEXV2Library: IDENTICAL_ADDRESSES"); auto ret = tokenA < tokenB ? std::make_pair(tokenA, tokenB) : std::make_pair(tokenB, tokenA); - if (ret.first == Address()) throw std::runtime_error("DEXV2Library: ZERO_ADDRESS"); + if (ret.first == Address()) throw DynamicException("DEXV2Library: ZERO_ADDRESS"); return ret; } @@ -34,14 +34,14 @@ namespace DEXV2Library { } uint256_t quote(const uint256_t& amountA, const uint256_t& reserveA, const uint256_t& reserveB) { - if (amountA == 0) throw std::runtime_error("DEXV2Library: INSUFFICIENT_AMOUNT"); - if (reserveA == 0 || reserveB == 0) throw std::runtime_error("DEXV2Library: INSUFFICIENT_LIQUIDITY"); + if (amountA == 0) throw DynamicException("DEXV2Library: INSUFFICIENT_AMOUNT"); + if (reserveA == 0 || reserveB == 0) throw DynamicException("DEXV2Library: INSUFFICIENT_LIQUIDITY"); return amountA * reserveB / reserveA; } uint256_t getAmountOut(const uint256_t& amountIn, const uint256_t& reserveIn, const uint256_t& reserveOut) { - if (amountIn == 0) throw std::runtime_error("DEXV2Library: INSUFFICIENT_INPUT_AMOUNT"); - if (reserveIn == 0 || reserveOut == 0) throw std::runtime_error("DEXV2Library: INSUFFICIENT_LIQUIDITY"); + if (amountIn == 0) throw DynamicException("DEXV2Library: INSUFFICIENT_INPUT_AMOUNT"); + if (reserveIn == 0 || reserveOut == 0) throw DynamicException("DEXV2Library: INSUFFICIENT_LIQUIDITY"); uint256_t amountInWithFee = amountIn * 997; uint256_t numerator = amountInWithFee * reserveOut; uint256_t denominator = reserveIn * 1000 + amountInWithFee; @@ -49,8 +49,8 @@ namespace DEXV2Library { } uint256_t getAmountIn(const uint256_t& amountOut, const uint256_t& reserveIn, const uint256_t& reserveOut) { - if (amountOut == 0) throw std::runtime_error("DEXV2Library: INSUFFICIENT_OUTPUT_AMOUNT"); - if (reserveIn == 0 || reserveOut == 0) throw std::runtime_error("DEXV2Library: INSUFFICIENT_LIQUIDITY"); + if (amountOut == 0) throw DynamicException("DEXV2Library: INSUFFICIENT_OUTPUT_AMOUNT"); + if (reserveIn == 0 || reserveOut == 0) throw DynamicException("DEXV2Library: INSUFFICIENT_LIQUIDITY"); uint256_t numerator = reserveIn * amountOut * 1000; uint256_t denominator = (reserveOut - amountOut) * 997; return (numerator / denominator) + 1; @@ -60,7 +60,7 @@ namespace DEXV2Library { const ContractManagerInterface& interface, const Address& factory, const uint256_t& amountIn, const std::vector
& path ) { - if (path.size() < 2) throw std::runtime_error("DEXV2Library: INVALID_PATH"); + if (path.size() < 2) throw DynamicException("DEXV2Library: INVALID_PATH"); std::vector amounts(path.size()); amounts[0] = amountIn; for (size_t i = 0; i < path.size() - 1; i++) { @@ -74,7 +74,7 @@ namespace DEXV2Library { const ContractManagerInterface& interface, const Address& factory, const uint256_t& amountOut, const std::vector
& path ) { - if (path.size() < 2) throw std::runtime_error("DEXV2Library: INVALID_PATH"); + if (path.size() < 2) throw DynamicException("DEXV2Library: INVALID_PATH"); std::vector amounts(path.size()); amounts[amounts.size() - 1] = amountOut; for (size_t i = path.size() - 1; i > 0; i--) { diff --git a/src/contract/templates/dexv2/dexv2pair.cpp b/src/contract/templates/dexv2/dexv2pair.cpp index 1d724125..2d880f56 100644 --- a/src/contract/templates/dexv2/dexv2pair.cpp +++ b/src/contract/templates/dexv2/dexv2pair.cpp @@ -118,7 +118,7 @@ bool DEXV2Pair::_mintFee(uint112_t reserve0, uint112_t reserve1) { } void DEXV2Pair::initialize(const Address& token0, const Address& token1) { - if (this->factory_ != this->getCaller()) throw std::runtime_error("DEXV2Pair: FORBIDDEN"); + if (this->factory_ != this->getCaller()) throw DynamicException("DEXV2Pair: FORBIDDEN"); this->token0_ = token0; this->token1_ = token1; } @@ -161,7 +161,7 @@ uint256_t DEXV2Pair::mint(const Address& to) { liquidity = std::min(amount0 * totalSupply / this->reserve0_.get(), amount1 * totalSupply / this->reserve1_.get()); } - if (liquidity == 0) throw std::runtime_error("DEXV2Pair: INSUFFICIENT_LIQUIDITY_MINTED"); + if (liquidity == 0) throw DynamicException("DEXV2Pair: INSUFFICIENT_LIQUIDITY_MINTED"); this->mintValue_(to, liquidity); this->_update(balance0, balance1, this->reserve0_.get(), this->reserve1_.get()); if (feeOn) this->kLast_ = uint256_t(this->reserve0_.get()) * uint256_t(this->reserve1_.get()); @@ -182,7 +182,7 @@ std::tuple DEXV2Pair::burn(const Address& to) { uint256_t totalSupply = this->totalSupply_.get(); uint256_t amount0 = liquidity * balance0 / totalSupply; uint256_t amount1 = liquidity * balance1 / totalSupply; - if (amount0 == 0 || amount1 == 0) throw std::runtime_error("DEXV2Pair: INSUFFICIENT_LIQUIDITY_BURNED"); + if (amount0 == 0 || amount1 == 0) throw DynamicException("DEXV2Pair: INSUFFICIENT_LIQUIDITY_BURNED"); this->burnValue_(this->getContractAddress(), liquidity); this->_safeTransfer(this->token0_.get(), to, amount0); this->_safeTransfer(this->token1_.get(), to, amount1); @@ -195,19 +195,19 @@ std::tuple DEXV2Pair::burn(const Address& to) { void DEXV2Pair::swap(const uint256_t& amount0Out, const uint256_t& amount1Out, const Address& to) { ReentrancyGuard reentrancyGuard(this->reentrancyLock_); - if (amount0Out == 0 && amount1Out == 0) throw std::runtime_error("DEXV2Pair: INSUFFICIENT_OUTPUT_AMOUNT"); - if (reserve0_ <= uint112_t(amount0Out) && reserve1_ <= uint112_t(amount1Out)) throw std::runtime_error("DEXV2Pair: INSUFFICIENT_LIQUIDITY"); - if (token0_ == to || token1_ == to) throw std::runtime_error("DEXV2Pair: INVALID_TO"); + if (amount0Out == 0 && amount1Out == 0) throw DynamicException("DEXV2Pair: INSUFFICIENT_OUTPUT_AMOUNT"); + if (reserve0_ <= uint112_t(amount0Out) && reserve1_ <= uint112_t(amount1Out)) throw DynamicException("DEXV2Pair: INSUFFICIENT_LIQUIDITY"); + if (token0_ == to || token1_ == to) throw DynamicException("DEXV2Pair: INVALID_TO"); if (amount0Out > 0) this->_safeTransfer(this->token0_.get(), to, amount0Out); if (amount1Out > 0) this->_safeTransfer(this->token1_.get(), to, amount1Out); uint256_t balance0 = this->callContractViewFunction(this->token0_.get(), &ERC20::balanceOf, this->getContractAddress()); uint256_t balance1 = this->callContractViewFunction(this->token1_.get(), &ERC20::balanceOf, this->getContractAddress()); uint256_t amount0In = balance0 > this->reserve0_.get() - uint112_t(amount0Out) ? balance0 - this->reserve0_.get() + uint112_t(amount0Out) : 0; uint256_t amount1In = balance1 > this->reserve1_.get() - uint112_t(amount1Out) ? balance1 - this->reserve1_.get() + uint112_t(amount1Out) : 0; - if (amount0In == 0 && amount1In == 0) throw std::runtime_error("DEXV2Pair: INSUFFICIENT_INPUT_AMOUNT"); + if (amount0In == 0 && amount1In == 0) throw DynamicException("DEXV2Pair: INSUFFICIENT_INPUT_AMOUNT"); uint256_t balance0Adjusted = balance0 * 1000 - amount0In * 3; uint256_t balance1Adjusted = balance1 * 1000 - amount1In * 3; - if (balance0Adjusted * balance1Adjusted < uint256_t(this->reserve0_.get()) * this->reserve1_.get() * 1000 * 1000) throw std::runtime_error("DEXV2Pair: K"); + if (balance0Adjusted * balance1Adjusted < uint256_t(this->reserve0_.get()) * this->reserve1_.get() * 1000 * 1000) throw DynamicException("DEXV2Pair: K"); this->_update(balance0, balance1, this->reserve0_.get(), this->reserve1_.get()); } diff --git a/src/contract/templates/dexv2/dexv2router02.cpp b/src/contract/templates/dexv2/dexv2router02.cpp index 4038df9c..5f230575 100644 --- a/src/contract/templates/dexv2/dexv2router02.cpp +++ b/src/contract/templates/dexv2/dexv2router02.cpp @@ -93,7 +93,7 @@ std::pair DEXV2Router02::_addLiquidity( } else { uint256_t amountBoptimal = DEXV2Library::quote(amountADesired, reserveA, reserveB); if (amountBoptimal <= amountBDesired) { - if (amountBoptimal < amountBMin) throw std::runtime_error( + if (amountBoptimal < amountBMin) throw DynamicException( "DEXV2Router02::_addLiquidity: INSUFFICIENT_B_AMOUNT" ); amountA = amountADesired; @@ -101,13 +101,13 @@ std::pair DEXV2Router02::_addLiquidity( } else { uint256_t amountAoptimal = DEXV2Library::quote(amountBDesired, reserveB, reserveA); if (amountAoptimal <= amountADesired) { - if (amountAoptimal < amountAMin) throw std::runtime_error( + if (amountAoptimal < amountAMin) throw DynamicException( "DEXV2Router02::_addLiquidity: INSUFFICIENT_A_AMOUNT" ); amountA = amountAoptimal; amountB = amountBDesired; } else { - throw std::runtime_error("DEXV2Router02::_addLiquidity: INSUFFICIENT_A_AMOUNT"); + throw DynamicException("DEXV2Router02::_addLiquidity: INSUFFICIENT_A_AMOUNT"); } } } @@ -124,7 +124,7 @@ void DEXV2Router02::_swap( auto pairAddress = this->callContractViewFunction( this->factory_.get(), &DEXV2Factory::getPair, input, output ); - if (!pairAddress) throw std::runtime_error("DEXV2Router02::_swap: PAIR_NOT_FOUND"); + if (!pairAddress) throw DynamicException("DEXV2Router02::_swap: PAIR_NOT_FOUND"); auto token0 = DEXV2Library::sortTokens(input, output).first; uint256_t amountOut = amounts[i + 1]; uint256_t amount0Out; @@ -147,7 +147,7 @@ void DEXV2Router02::_swap( bool DEXV2Router02::ensure(const uint256_t& deadline) const { if (deadline < ContractGlobals::getBlockTimestamp()) { - throw std::runtime_error("DEXV2Router02::ensure: EXPIRED"); + throw DynamicException("DEXV2Router02::ensure: EXPIRED"); } return true; } @@ -217,8 +217,8 @@ std::tuple DEXV2Router02::removeLiquidity( const auto& [amount0, amount1] = this->callContractFunction(pair, &DEXV2Pair::burn, to); auto amountA = tokenA == DEXV2Library::sortTokens(tokenA, tokenB).first ? amount0 : amount1; auto amountB = tokenA == DEXV2Library::sortTokens(tokenA, tokenB).first ? amount1 : amount0; - if (amountA < amountAMin) throw std::runtime_error("DEXV2Router02::removeLiquidity: INSUFFICIENT_A_AMOUNT"); - if (amountB < amountBMin) throw std::runtime_error("DEXV2Router02::removeLiquidity: INSUFFICIENT_B_AMOUNT"); + if (amountA < amountAMin) throw DynamicException("DEXV2Router02::removeLiquidity: INSUFFICIENT_A_AMOUNT"); + if (amountB < amountBMin) throw DynamicException("DEXV2Router02::removeLiquidity: INSUFFICIENT_B_AMOUNT"); return std::make_tuple(amountA, amountB); } @@ -251,7 +251,7 @@ std::vector DEXV2Router02::swapExactTokensForTokens( this->ensure(deadline); auto amounts = DEXV2Library::getAmountsOut(this->interface_, this->factory_.get(), amountIn, path); auto amountOut = amounts.back(); - if (amountOut < amountOutMin) throw std::runtime_error( + if (amountOut < amountOutMin) throw DynamicException( "DEXV2Router02::swapExactTokensForTokens: INSUFFICIENT_OUTPUT_AMOUNT" ); auto pair = DEXV2Library::pairFor(this->interface_, this->factory_.get(), path[0], path[1]); @@ -270,7 +270,7 @@ std::vector DEXV2Router02::swapTokensForExactTokens( this->ensure(deadline); auto amounts = DEXV2Library::getAmountsIn(this->interface_, this->factory_.get(), amountOut, path); auto amountIn = amounts.front(); - if (amountIn > amountInMax) throw std::runtime_error( + if (amountIn > amountInMax) throw DynamicException( "DEXV2Router02::swapTokensForExactTokens: EXCESSIVE_INPUT_AMOUNT" ); auto pair = DEXV2Library::pairFor(this->interface_, this->factory_.get(), path[0], path[1]); @@ -286,12 +286,12 @@ std::vector DEXV2Router02::swapExactNativeForTokens( const uint256_t& deadline ) { this->ensure(deadline); - if (path[0] != this->wrappedNative_.get()) throw std::runtime_error( + if (path[0] != this->wrappedNative_.get()) throw DynamicException( "DEXV2Router02::swapExactNativeForTokens: INVALID_PATH" ); auto amounts = DEXV2Library::getAmountsOut(this->interface_, this->factory_.get(), this->getValue(), path); auto amountOut = amounts.back(); - if (amountOut < amountOutMin) throw std::runtime_error( + if (amountOut < amountOutMin) throw DynamicException( "DEXV2Router02::swapExactNativeForTokens: INSUFFICIENT_OUTPUT_AMOUNT" ); this->callContractFunction(amounts[0], this->wrappedNative_.get(), &NativeWrapper::deposit); @@ -309,12 +309,12 @@ std::vector DEXV2Router02::swapTokensForExactNative( const uint256_t& deadline ) { this->ensure(deadline); - if (path.back() != this->wrappedNative_.get()) throw std::runtime_error( + if (path.back() != this->wrappedNative_.get()) throw DynamicException( "DEXV2Router02::swapTokensForExactNative: INVALID_PATH" ); auto amounts = DEXV2Library::getAmountsIn(this->interface_, this->factory_.get(), amountOut, path); auto amountIn = amounts.front(); - if (amountIn > amountInMax) throw std::runtime_error( + if (amountIn > amountInMax) throw DynamicException( "DEXV2Router02::swapTokensForExactNative: EXCESSIVE_INPUT_AMOUNT" ); auto pair = DEXV2Library::pairFor(this->interface_, this->factory_.get(), path[0], path[1]); @@ -333,12 +333,12 @@ std::vector DEXV2Router02::swapExactTokensForNative( const uint256_t& deadline ) { this->ensure(deadline); - if (path.back() != this->wrappedNative_.get()) throw std::runtime_error( + if (path.back() != this->wrappedNative_.get()) throw DynamicException( "DEXV2Router02::swapExactTokensForNative: INVALID_PATH" ); auto amounts = DEXV2Library::getAmountsOut(this->interface_, this->factory_.get(), amountIn, path); auto amountOut = amounts.back(); - if (amountOut < amountOutMin) throw std::runtime_error( + if (amountOut < amountOutMin) throw DynamicException( "DEXV2Router02::swapExactTokensForNative: INSUFFICIENT_OUTPUT_AMOUNT" ); auto pair = DEXV2Library::pairFor(this->interface_, this->factory_.get(), path[0], path[1]); @@ -357,12 +357,12 @@ std::vector DEXV2Router02::swapNativeForExactTokens( const uint256_t& deadline ) { this->ensure(deadline); - if (path[0] != this->wrappedNative_.get()) throw std::runtime_error( + if (path[0] != this->wrappedNative_.get()) throw DynamicException( "DEXV2Router02::swapNativeForExactTokens: INVALID_PATH" ); auto amounts = DEXV2Library::getAmountsIn(this->interface_, this->factory_.get(), amountOut, path); auto amountIn = amounts.front(); - if (amountIn > amountInMax) throw std::runtime_error( + if (amountIn > amountInMax) throw DynamicException( "DEXV2Router02::swapNativeForExactTokens: EXCESSIVE_INPUT_AMOUNT" ); this->callContractFunction(amounts[0], this->wrappedNative_.get(), &NativeWrapper::deposit); diff --git a/src/contract/templates/dexv2/dexv2router02.h b/src/contract/templates/dexv2/dexv2router02.h index ae3e7d72..7f8248f1 100644 --- a/src/contract/templates/dexv2/dexv2router02.h +++ b/src/contract/templates/dexv2/dexv2router02.h @@ -70,7 +70,7 @@ class DEXV2Router02 : public DynamicContract { * ensure() doesn't have to execute the anything after the function call, so we can make it a bool function. * @param deadline The timestamp to check against, in seconds. * @return `true` if deadline has not expired yet. - * @throw std::runtime_error if deadline has expired. + * @throw DynamicException if deadline has expired. */ bool ensure(const uint256_t& deadline) const; diff --git a/src/contract/templates/erc20wrapper.cpp b/src/contract/templates/erc20wrapper.cpp index 135e324a..13215462 100644 --- a/src/contract/templates/erc20wrapper.cpp +++ b/src/contract/templates/erc20wrapper.cpp @@ -61,20 +61,20 @@ uint256_t ERC20Wrapper::getUserBalance(const Address& token, const Address& user void ERC20Wrapper::withdraw(const Address& token, const uint256_t& value) { auto it = this->tokensAndBalances_.find(token); - if (it == this->tokensAndBalances_.end()) throw std::runtime_error("Token not found"); + if (it == this->tokensAndBalances_.end()) throw DynamicException("Token not found"); auto itUser = it->second.find(this->getCaller()); - if (itUser == it->second.end()) throw std::runtime_error("User not found"); - if (itUser->second <= value) throw std::runtime_error("ERC20Wrapper: Not enough balance"); + if (itUser == it->second.end()) throw DynamicException("User not found"); + if (itUser->second <= value) throw DynamicException("ERC20Wrapper: Not enough balance"); itUser->second -= value; this->callContractFunction(token, &ERC20::transfer, this->getCaller(), value); } void ERC20Wrapper::transferTo(const Address& token, const Address& to, const uint256_t& value) { auto it = this->tokensAndBalances_.find(token); - if (it == this->tokensAndBalances_.end()) throw std::runtime_error("Token not found"); + if (it == this->tokensAndBalances_.end()) throw DynamicException("Token not found"); auto itUser = it->second.find(this->getCaller()); - if (itUser == it->second.end()) throw std::runtime_error("User not found"); - if (itUser->second <= value) throw std::runtime_error("ERC20Wrapper: Not enough balance"); + if (itUser == it->second.end()) throw DynamicException("User not found"); + if (itUser->second <= value) throw DynamicException("ERC20Wrapper: Not enough balance"); itUser->second -= value; this->callContractFunction(token, &ERC20::transfer, to, value); } diff --git a/src/contract/templates/erc20wrapper.h b/src/contract/templates/erc20wrapper.h index fb557a97..3b3b2695 100644 --- a/src/contract/templates/erc20wrapper.h +++ b/src/contract/templates/erc20wrapper.h @@ -101,7 +101,7 @@ class ERC20Wrapper : public DynamicContract { * function withdraw (address _token, uint256 _value) public returns (bool) * @param token The address of the token. * @param value The amount of tokens to withdraw. - * @throw std::runtime_error if the contract does not have enough tokens, + * @throw DynamicException if the contract does not have enough tokens, * or if the token was not found. */ void withdraw(const Address& token, const uint256_t& value); @@ -112,7 +112,7 @@ class ERC20Wrapper : public DynamicContract { * @param token The address of the token. * @param to The address of the user to send tokens to. * @param value The amount of tokens to transfer. - * @throw std::runtime_error if the contract does not have enough tokens, + * @throw DynamicException if the contract does not have enough tokens, * or if either the token or the user were not found. */ void transferTo(const Address& token, const Address& to, const uint256_t& value); diff --git a/src/contract/templates/erc721.cpp b/src/contract/templates/erc721.cpp index 8a2fbd85..4d007000 100644 --- a/src/contract/templates/erc721.cpp +++ b/src/contract/templates/erc721.cpp @@ -145,9 +145,9 @@ Address ERC721::update_(const Address& to, const uint256_t& tokenId, const Addre void ERC721::checkAuthorized_(const Address& owner, const Address& spender, const uint256_t& tokenId) const { if (!this->isAuthorized_(owner, spender, tokenId)) { if (owner) { - throw std::runtime_error("ERC721::checkAuthorized_: Not authorized"); + throw DynamicException("ERC721::checkAuthorized_: Not authorized"); } - throw std::runtime_error("ERC721::checkAuthorized_: inexistent token"); + throw DynamicException("ERC721::checkAuthorized_: inexistent token"); } } @@ -161,7 +161,7 @@ bool ERC721::isAuthorized_(const Address& owner, const Address& spender, const u void ERC721::mint_(const Address& to, const uint256_t& tokenId) { if (to == Address()) { - throw std::runtime_error("ERC721::mint_: mint to the zero address"); + throw DynamicException("ERC721::mint_: mint to the zero address"); } Address prevOwner = this->update_(to, tokenId, Address()); } @@ -169,20 +169,20 @@ void ERC721::mint_(const Address& to, const uint256_t& tokenId) { void ERC721::burn_(const uint256_t& tokenId) { Address prevOwner = this->update_(Address(), tokenId, Address()); if (prevOwner == Address()) { - throw std::runtime_error("ERC721::burn_: inexistent token"); + throw DynamicException("ERC721::burn_: inexistent token"); } } void ERC721::transfer_(const Address& from, const Address& to, const uint256_t& tokenId) { if (to == Address()) { - throw std::runtime_error("ERC721::transfer_: transfer to the zero address"); + throw DynamicException("ERC721::transfer_: transfer to the zero address"); } Address prevOwner = this->update_(to, tokenId, Address()); if (prevOwner == Address()) { - throw std::runtime_error("ERC721::transfer_: inexistent token"); + throw DynamicException("ERC721::transfer_: inexistent token"); } else if (prevOwner != from) { - throw std::runtime_error("ERC721::transfer_: incorrect owner"); + throw DynamicException("ERC721::transfer_: incorrect owner"); } } @@ -190,7 +190,7 @@ Address ERC721::approve_(const Address& to, const uint256_t& tokenId, const Addr Address owner = this->ownerOf(tokenId); if (auth != Address() && owner != auth && !this->isApprovedForAll(owner, auth)) { - throw std::runtime_error("ERC721::approve_: Not authorized"); + throw DynamicException("ERC721::approve_: Not authorized"); } this->tokenApprovals_[tokenId] = to; @@ -208,7 +208,7 @@ std::string ERC721::symbol() const { uint256_t ERC721::balanceOf(const Address& owner) const { if (owner == Address()) { - throw std::runtime_error("ERC721::balanceOf: zero address"); + throw DynamicException("ERC721::balanceOf: zero address"); } auto it = this->balances_.find(owner); if (it == this->balances_.end()) { @@ -220,7 +220,7 @@ uint256_t ERC721::balanceOf(const Address& owner) const { Address ERC721::ownerOf(const uint256_t& tokenId) const { Address owner = this->ownerOf_(tokenId); if (owner == Address()) { - throw std::runtime_error("ERC721::ownerOf: inexistent token"); + throw DynamicException("ERC721::ownerOf: inexistent token"); } return owner; } @@ -245,14 +245,14 @@ void ERC721::setApprovalForAll(const Address& operatorAddress, const bool& appro void ERC721::setApprovalForAll_(const Address& owner, const Address& operatorAddress, bool approved) { if (operatorAddress == Address()) { - throw std::runtime_error("ERC721::setApprovalForAll_: zero address"); + throw DynamicException("ERC721::setApprovalForAll_: zero address"); } this->operatorAddressApprovals_[owner][operatorAddress] = approved; } void ERC721::requireMinted_(const uint256_t& tokenId) const { if (this->ownerOf_(tokenId) == Address()) { - throw std::runtime_error("ERC721::requireMinted_: inexistent token"); + throw DynamicException("ERC721::requireMinted_: inexistent token"); } } @@ -270,12 +270,12 @@ bool ERC721::isApprovedForAll(const Address& owner, const Address& operatorAddre void ERC721::transferFrom(const Address& from, const Address& to, const uint256_t& tokenId) { if (to == Address()) { - throw std::runtime_error("ERC721::transferFrom: transfer to the zero address"); + throw DynamicException("ERC721::transferFrom: transfer to the zero address"); } Address prevOwner = this->update_(to, tokenId, this->getCaller()); if (prevOwner == Address()) { - throw std::runtime_error("ERC721::transferFrom: inexistent token"); + throw DynamicException("ERC721::transferFrom: inexistent token"); } else if (prevOwner != from) { - throw std::runtime_error("ERC721::transferFrom: incorrect owner"); + throw DynamicException("ERC721::transferFrom: incorrect owner"); } } diff --git a/src/contract/templates/simplecontract.cpp b/src/contract/templates/simplecontract.cpp index 15a5fd3e..da63f3d5 100644 --- a/src/contract/templates/simplecontract.cpp +++ b/src/contract/templates/simplecontract.cpp @@ -48,7 +48,7 @@ SimpleContract::~SimpleContract() { void SimpleContract::setName(const std::string& argName) { if (this->getCaller() != this->getContractCreator()) { - throw std::runtime_error("Only contract creator can call this function."); + throw DynamicException("Only contract creator can call this function."); } this->name_ = argName; this->nameChanged(this->name_.get()); @@ -56,7 +56,7 @@ void SimpleContract::setName(const std::string& argName) { void SimpleContract::setNames(const std::vector& argName) { if (this->getCaller() != this->getContractCreator()) { - throw std::runtime_error("Only contract creator can call this function."); + throw DynamicException("Only contract creator can call this function."); } this->name_ = ""; for (const auto& name : argName) this->name_ += name; @@ -65,7 +65,7 @@ void SimpleContract::setNames(const std::vector& argName) { void SimpleContract::setNumber(const uint256_t& argNumber) { if (this->getCaller() != this->getContractCreator()) { - throw std::runtime_error("Only contract creator can call this function."); + throw DynamicException("Only contract creator can call this function."); } this->number_ = argNumber; this->numberChanged(this->number_.get()); @@ -81,7 +81,7 @@ void SimpleContract::setNamesAndNumbers( const std::vector& argName, const std::vector& argNumber ) { if (this->getCaller() != this->getContractCreator()) { - throw std::runtime_error("Only contract creator can call this function."); + throw DynamicException("Only contract creator can call this function."); } this->name_ = ""; this->number_ = 0; @@ -94,7 +94,7 @@ void SimpleContract::setNamesAndNumbersInTuple( const std::vector>& argNameAndNumber ) { if (this->getCaller() != this->getContractCreator()) { - throw std::runtime_error("Only contract creator can call this function."); + throw DynamicException("Only contract creator can call this function."); } this->name_ = ""; this->number_ = 0; @@ -106,7 +106,7 @@ void SimpleContract::setNamesAndNumbersInArrayOfArrays( const std::vector>> &argNameAndNumber ) { if (this->getCaller() != this->getContractCreator()) { - throw std::runtime_error("Only contract creator can call this function."); + throw DynamicException("Only contract creator can call this function."); } this->name_ = ""; this->number_ = 0; @@ -118,7 +118,7 @@ void SimpleContract::setNamesAndNumbersInArrayOfArrays( void SimpleContract::setTuple(const std::tuple& argTuple) { if (this->getCaller() != this->getContractCreator()) { - throw std::runtime_error("Only contract creator can call this function."); + throw DynamicException("Only contract creator can call this function."); } this->tuple_ = argTuple; this->tupleChanged(std::make_tuple(get<0>(this->tuple_), get<1>(this->tuple_))); diff --git a/src/contract/templates/throwtestB.cpp b/src/contract/templates/throwtestB.cpp index 12990711..fe79fa2f 100644 --- a/src/contract/templates/throwtestB.cpp +++ b/src/contract/templates/throwtestB.cpp @@ -31,7 +31,7 @@ uint8_t ThrowTestB::getNumB() const { return this->num_.get(); } const uint8_t& valB, const Address&, const uint8_t& ) { this->num_ = valB; - throw std::runtime_error("Intended throw in ThrowTestB"); + throw DynamicException("Intended throw in ThrowTestB"); } void ThrowTestB::registerContractFunctions() { diff --git a/src/contract/variables/reentrancyguard.h b/src/contract/variables/reentrancyguard.h index 643d6aaa..56e3cc8d 100644 --- a/src/contract/variables/reentrancyguard.h +++ b/src/contract/variables/reentrancyguard.h @@ -27,10 +27,10 @@ class ReentrancyGuard { /** * Constructor. * @param lock Reference to the mutex. - * @throw std::runtime_error if the mutex is already locked. + * @throw DynamicException if the mutex is already locked. */ explicit ReentrancyGuard(bool &lock) : lock_(lock) { - if (lock_) throw std::runtime_error("ReentrancyGuard: reentrancy attack detected"); + if (lock_) throw DynamicException("ReentrancyGuard: reentrancy attack detected"); lock_ = true; } diff --git a/src/contract/variables/safebase.h b/src/contract/variables/safebase.h index fbb301f1..4d446002 100644 --- a/src/contract/variables/safebase.h +++ b/src/contract/variables/safebase.h @@ -9,6 +9,7 @@ See the LICENSE.txt file in the project root for more information. #define SAFEBASE_H #include +#include "../utils/dynamicexception.h" // Forward declarations. class DynamicContract; @@ -54,10 +55,10 @@ class SafeBase { /** * Check if the variable is initialized (and initialize it if not). - * @throw std::runtime_error if not overridden by the child class. + * @throw DynamicException if not overridden by the child class. */ inline virtual void check() const { - throw std::runtime_error("Derived Class from SafeBase does not override check()"); + throw DynamicException("Derived Class from SafeBase does not override check()"); }; /** @@ -87,20 +88,20 @@ class SafeBase { * Commit a structure value to the contract. * Should always be overridden by the child class. * Child class should always do `this->registered = false;` at the end of commit(). - * @throw std::runtime_error if not overridden by the child class. + * @throw DynamicException if not overridden by the child class. */ inline virtual void commit() { - throw std::runtime_error("Derived Class from SafeBase does not override commit()"); + throw DynamicException("Derived Class from SafeBase does not override commit()"); }; /** * Revert a structure value (nullify). * Should always be overridden by the child class. * Child class should always do `this->registered = false;` at the end of revert(). - * @throw std::runtime_error if not overridden by the child class. + * @throw DynamicException if not overridden by the child class. */ inline virtual void revert() const { - throw std::runtime_error("Derived Class from SafeBase does not override revert()"); + throw DynamicException("Derived Class from SafeBase does not override revert()"); }; }; diff --git a/src/contract/variables/safeunorderedmap.h b/src/contract/variables/safeunorderedmap.h index eb6c3be8..eb95ffbd 100644 --- a/src/contract/variables/safeunorderedmap.h +++ b/src/contract/variables/safeunorderedmap.h @@ -76,7 +76,7 @@ template class SafeUnorderedMap : public SafeBase { /** * Check if a key exists, throw if it doesn't. * @param key The key to check. - * @throw std::runtime_error if key doesn't exist. + * @throw DynamicException if key doesn't exist. */ inline void checkKeyAndThrow(const Key& key) const { check(); @@ -84,13 +84,13 @@ template class SafeUnorderedMap : public SafeBase { if (itP == mapPtr_->end()) { auto itM = map_.find(key); if (itM == map_.end()) { - throw std::runtime_error("Key not found"); + throw DynamicException("Key not found"); } else { auto itD = erasedKeys_->find(key); if (itD == erasedKeys_->end()) { (*mapPtr_)[key] = itM->second; } else { - throw std::runtime_error("Key not found"); + throw DynamicException("Key not found"); } } } diff --git a/src/core/blockchain.cpp b/src/core/blockchain.cpp index 011453a9..ac8d9493 100644 --- a/src/core/blockchain.cpp +++ b/src/core/blockchain.cpp @@ -151,14 +151,14 @@ void Syncer::doValidatorBlock() { this->blockchain_.rdpos_->signBlock(block); if (!this->blockchain_.state_->validateNextBlock(block)) { Logger::logToDebug(LogType::ERROR, Log::syncer, __func__, "Block is not valid!"); - throw std::runtime_error("Block is not valid!"); + throw DynamicException("Block is not valid!"); } if (this->stopSyncer_) return; Hash latestBlockHash = block.hash(); this->blockchain_.state_->processNextBlock(std::move(block)); if (this->blockchain_.storage_->latest()->hash() != latestBlockHash) { Logger::logToDebug(LogType::ERROR, Log::syncer, __func__, "Block is not valid!"); - throw std::runtime_error("Block is not valid!"); + throw DynamicException("Block is not valid!"); } // Broadcast the block through P2P diff --git a/src/core/blockchain.h b/src/core/blockchain.h index cf23d960..4b0e99da 100644 --- a/src/core/blockchain.h +++ b/src/core/blockchain.h @@ -124,7 +124,7 @@ class Syncer { * If the node is a Validator and it has to create a new block, * this function will be called, the new block will be created based on the * current State and rdPoS objects, and then it will be broadcasted. - * @throw std::runtime_error if block is invalid. + * @throw DynamicException if block is invalid. */ void doValidatorBlock(); diff --git a/src/core/rdpos.cpp b/src/core/rdpos.cpp index 1c625e23..7cad62b4 100644 --- a/src/core/rdpos.cpp +++ b/src/core/rdpos.cpp @@ -41,7 +41,7 @@ rdPoS::rdPoS(const std::unique_ptr& db, if (validatorsDb.empty()) { // No rdPoS in DB, this should have been initialized by Storage. Logger::logToDebug(LogType::ERROR, Log::rdPoS, __func__, "No rdPoS in DB, cannot proceed."); - throw std::runtime_error("No rdPoS in DB."); + throw DynamicException("No rdPoS in DB."); } Logger::logToDebug(LogType::INFO, Log::rdPoS, __func__, "Found " + std::to_string(validatorsDb.size()) + " rdPoS in DB"); // TODO: check if no index is missing from DB. @@ -222,7 +222,7 @@ Hash rdPoS::processBlock(const Block& block) { std::unique_lock lock(this->mutex_); if (!block.isFinalized()) { Logger::logToDebug(LogType::ERROR, Log::rdPoS, __func__, "Block is not finalized."); - throw std::runtime_error("Block is not finalized."); + throw DynamicException("Block is not finalized."); } validatorMempool_.clear(); this->randomList_ = std::vector(this->validators_.begin(), this->validators_.end()); diff --git a/src/core/rdpos.h b/src/core/rdpos.h index 3d0feb39..0a42bf99 100644 --- a/src/core/rdpos.h +++ b/src/core/rdpos.h @@ -113,7 +113,7 @@ class rdPoS : public BaseContract { * @param p2p Pointer to the P2P connection manager. * @param options Pointer to the options singleton. * @param state Pointer to the blockchain's state. - * @throw std::runtime_error if there are no Validators registered in the database. + * @throw DynamicException if there are no Validators registered in the database. */ rdPoS( const std::unique_ptr& db, const std::unique_ptr& storage, @@ -170,7 +170,7 @@ class rdPoS : public BaseContract { * Should be called from State, after a block is validated and before it is added to Storage. * @param block The block to process. * @return The new randomness seed to be used for the next block. - * @throw std::runtime_error if block is not finalized. + * @throw DynamicException if block is not finalized. */ Hash processBlock(const Block& block); diff --git a/src/core/state.cpp b/src/core/state.cpp index 596c6a30..9a59de61 100644 --- a/src/core/state.cpp +++ b/src/core/state.cpp @@ -33,12 +33,12 @@ contractManager_(std::make_unique(db, this, rdpos, options)) BytesArrView data(dbEntry.value); if (dbEntry.key.size() != 20) { Logger::logToDebug(LogType::ERROR, Log::state, __func__, "Error when loading State from DB, address from DB size mismatch"); - throw std::runtime_error("Error when loading State from DB, address from DB size mismatch"); + throw DynamicException("Error when loading State from DB, address from DB size mismatch"); } uint8_t balanceSize = Utils::fromBigEndian(data.subspan(0,1)); if (data.size() + 1 < data.size()) { Logger::logToDebug(LogType::ERROR, Log::state, __func__, "Error when loading State from DB, value from DB doesn't size mismatch on balanceSize"); - throw std::runtime_error("Error when loading State from DB, value from DB size mismatch on balanceSize"); + throw DynamicException("Error when loading State from DB, value from DB size mismatch on balanceSize"); } uint256_t balance = Utils::fromBigEndian(data.subspan(1, balanceSize)); @@ -46,7 +46,7 @@ contractManager_(std::make_unique(db, this, rdpos, options)) if (2 + balanceSize + nonceSize != data.size()) { Logger::logToDebug(LogType::ERROR, Log::state, __func__, "Error when loading State from DB, value from DB doesn't size mismatch on nonceSize"); - throw std::runtime_error("Error when loading State from DB, value from DB size mismatch on nonceSize"); + throw DynamicException("Error when loading State from DB, value from DB size mismatch on nonceSize"); } uint64_t nonce = Utils::fromBigEndian(data.subspan(2 + balanceSize, nonceSize)); @@ -262,7 +262,7 @@ void State::processNextBlock(Block&& block) { Logger::logToDebug(LogType::ERROR, Log::state, __func__, "Sanity check failed - blockchain is trying to append a invalid block, throwing" ); - throw std::runtime_error("Invalid block detected during processNextBlock sanity check"); + throw DynamicException("Invalid block detected during processNextBlock sanity check"); } std::unique_lock lock(this->stateMutex_); @@ -369,7 +369,7 @@ bool State::estimateGas(const ethCallInfo& callInfo) { } void State::processContractPayable(const std::unordered_map& payableMap) { - if (!this->processingPayable_) throw std::runtime_error( + if (!this->processingPayable_) throw DynamicException( "Uh oh, contracts are going haywire! Cannot change State while not processing a payable contract." ); for (const auto& [address, amount] : payableMap) this->accounts_[address].balance = amount; diff --git a/src/core/state.h b/src/core/state.h index cd6b99cf..057cd736 100644 --- a/src/core/state.h +++ b/src/core/state.h @@ -92,7 +92,7 @@ class State { * @param rdpos Pointer to the rdPoS object. * @param p2pManager Pointer to the P2P connection manager. * @param options Pointer to the options singleton. - * @throw std::runtime_error on any database size mismatch. + * @throw DynamicException on any database size mismatch. */ State( const std::unique_ptr& db, @@ -146,7 +146,7 @@ class State { * DOES update the state. * Appends block to Storage after processing. * @param block The block to process. - * @throw std::runtime_error if block is invalid. + * @throw DynamicException if block is invalid. */ void processNextBlock(Block&& block); @@ -226,7 +226,7 @@ class State { /** * Update the State's account balances after a contract call. Called by ContractManager. * @param payableMap A map of the accounts to update and their respective new balances. - * @throw std::runtime_error on an attempt to change State while not processing a payable contract. + * @throw DynamicException on an attempt to change State while not processing a payable contract. */ void processContractPayable(const std::unordered_map& payableMap); diff --git a/src/core/storage.cpp b/src/core/storage.cpp index 036210de..38344e9f 100644 --- a/src/core/storage.cpp +++ b/src/core/storage.cpp @@ -94,7 +94,7 @@ void Storage::initializeBlockchain() { /// Genesis block comes from Options, not hardcoded const auto genesis = this->options_->getGenesisBlock(); if (genesis.getNHeight() != 0) { - throw std::runtime_error("Genesis block height is not 0"); + throw DynamicException("Genesis block height is not 0"); } this->db_->put(std::string("latest"), genesis.serializeBlock(), DBPrefix::blocks); this->db_->put(Utils::uint64ToBytes(genesis.getNHeight()), genesis.hash().get(), DBPrefix::blockHeightMaps); @@ -109,7 +109,7 @@ void Storage::initializeBlockchain() { const auto genesisInDB = Block(this->db_->get(genesisInDBHash, DBPrefix::blocks), this->options_->getChainID()); if (genesis != genesisInDB) { Logger::logToDebug(LogType::ERROR, Log::storage, __func__, "Sanity Check! Genesis block in DB does not match genesis block in Options"); - throw std::runtime_error("Sanity Check! Genesis block in DB does not match genesis block in Options"); + throw DynamicException("Sanity Check! Genesis block in DB does not match genesis block in Options"); } } @@ -171,12 +171,12 @@ void Storage::pushBackInternal(Block&& block) { // Push the new block and get a pointer to it if (!this->chain_.empty()) { if (this->chain_.back()->hash() != block.getPrevBlockHash()) { - throw std::runtime_error("Block " + block.hash().hex().get() + throw DynamicException("Block " + block.hash().hex().get() + " does not have the correct previous block hash." ); } if (block.getNHeight() != this->chain_.back()->getNHeight() + 1) { - throw std::runtime_error("Block " + block.hash().hex().get() + throw DynamicException("Block " + block.hash().hex().get() + " does not have the correct height." ); } @@ -198,12 +198,12 @@ void Storage::pushFrontInternal(Block&& block) { // Push the new block and get a pointer to it if (!this->chain_.empty()) { if (this->chain_.front()->getPrevBlockHash() != block.hash()) { - throw std::runtime_error("Block " + block.hash().hex().get() + throw DynamicException("Block " + block.hash().hex().get() + " does not have the correct previous block hash." ); } if (block.getNHeight() != this->chain_.front()->getNHeight() - 1) { - throw std::runtime_error("Block " + block.hash().hex().get() + throw DynamicException("Block " + block.hash().hex().get() + " does not have the correct height." ); } @@ -340,9 +340,9 @@ const std::tuple< const auto& [blockHash, blockIndex, blockHeight] = this->txByHash_.find(tx)->second; const auto& transactionList = blockByHash_.at(blockHash)->getTxs(); /// We can use at() because we know it exists /// Check if transactionList can be accessed at the index - if (transactionList.size() <= blockIndex) throw std::runtime_error("Tx index out of bounds"); + if (transactionList.size() <= blockIndex) throw DynamicException("Tx index out of bounds"); const auto& transaction = transactionList[blockIndex]; - if (transaction.hash() != tx) throw std::runtime_error("Tx hash mismatch"); + if (transaction.hash() != tx) throw DynamicException("Tx hash mismatch"); return {std::make_shared(transaction), blockHash, blockIndex, blockHeight}; } case StorageStatus::OnCache: { @@ -377,12 +377,12 @@ const std::tuple< } case StorageStatus::OnChain: { const auto& transactionList = this->blockByHash_.at(blockHash)->getTxs(); /// We can use at() because we know it exists - if (transactionList.size() <= blockIndex) throw std::runtime_error("Tx index out of bounds"); + if (transactionList.size() <= blockIndex) throw DynamicException("Tx index out of bounds"); const auto& transaction = transactionList[blockIndex]; const auto& txHash = transaction.hash(); /// We can use at() because we know it exists const auto& [txBlockHash, txBlockIndex, txBlockHeight] = this->txByHash_.at(txHash); if (txBlockHash != blockHash || txBlockIndex != blockIndex) { - throw std::runtime_error("Tx hash mismatch"); + throw DynamicException("Tx hash mismatch"); } return {std::make_shared(transaction), txBlockHash, txBlockIndex, txBlockHeight}; } @@ -416,7 +416,7 @@ const std::tuple< case StorageStatus::OnChain: { auto blockHash = this->blockHashByHeight_.find(blockHeight)->second; const auto& transactionList = this->blockByHash_.at(blockHash)->getTxs(); - if (transactionList.size() <= blockIndex) throw std::runtime_error("Tx index out of bounds"); + if (transactionList.size() <= blockIndex) throw DynamicException("Tx index out of bounds"); const auto& transaction = transactionList[blockIndex]; const auto& txHash = transaction.hash(); const auto& [txBlockHash, txBlockIndex, txBlockHeight] = this->txByHash_.at(txHash); diff --git a/src/core/storage.h b/src/core/storage.h index 480b8e42..77aa9a08 100644 --- a/src/core/storage.h +++ b/src/core/storage.h @@ -86,7 +86,7 @@ class Storage { * Add a block to the end of the chain. * Only call this function directly if absolutely sure that `chainLock_` is locked. * @param block The block to add. - * @throw std::runtime_error on incorrect previous hash or height. + * @throw DynamicException on incorrect previous hash or height. */ void pushBackInternal(Block&& block); @@ -94,7 +94,7 @@ class Storage { * Add a block to the start of the chain. * Only call this function directly if absolutely sure that `chainLock_` is locked. * @param block The block to add. - * @throw std::runtime_error on incorrect previous hash or height. + * @throw DynamicException on incorrect previous hash or height. */ void pushFrontInternal(Block&& block); @@ -204,7 +204,7 @@ class Storage { * Get a transaction from the chain using a given hash. * @param tx The transaction hash to get. * @return A tuple with the found transaction, block hash, index and height. - * @throw std::runtime_error on hash mismatch. + * @throw DynamicException on hash mismatch. */ const std::tuple< const std::shared_ptr, const Hash, const uint64_t, const uint64_t @@ -215,7 +215,7 @@ class Storage { * @param blockHash The block hash * @param blockIndex the index within the block * @return A tuple with the found transaction, block hash, index and height. - * @throw std::runtime_error on hash mismatch. + * @throw DynamicException on hash mismatch. */ const std::tuple< const std::shared_ptr, const Hash, const uint64_t, const uint64_t diff --git a/src/net/http/httpparser.cpp b/src/net/http/httpparser.cpp index 852f5a00..dfc170c1 100644 --- a/src/net/http/httpparser.cpp +++ b/src/net/http/httpparser.cpp @@ -166,7 +166,7 @@ std::string parseJsonRpcRequest( } else if(request["id"].is_null()) { ret["id"] = nullptr; } else { - throw std::runtime_error("Invalid id type"); + throw DynamicException("Invalid id type"); } } catch (std::exception &e) { json error; diff --git a/src/net/http/jsonrpc/decoding.cpp b/src/net/http/jsonrpc/decoding.cpp index 310973fc..3fac63e7 100644 --- a/src/net/http/jsonrpc/decoding.cpp +++ b/src/net/http/jsonrpc/decoding.cpp @@ -29,7 +29,7 @@ namespace JsonRPC::Decoding { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while checking json RPC spec: ") + e.what() ); - throw std::runtime_error("Error while checking json RPC spec: " + std::string(e.what())); + throw DynamicException("Error while checking json RPC spec: " + std::string(e.what())); } } @@ -43,21 +43,21 @@ namespace JsonRPC::Decoding { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while getting method: ") + e.what() ); - throw std::runtime_error("Error while checking json RPC spec: " + std::string(e.what())); + throw DynamicException("Error while checking json RPC spec: " + std::string(e.what())); } } void web3_clientVersion(const json& request) { try { // No params are needed. - if (!request["params"].empty()) throw std::runtime_error( + if (!request["params"].empty()) throw DynamicException( "web3_clientVersion does not need params" ); } catch (std::exception& e) { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while decoding web3_clientVersion: ") + e.what() ); - throw std::runtime_error( + throw DynamicException( "Error while decoding web3_clientVersion: " + std::string(e.what()) ); } @@ -66,73 +66,73 @@ namespace JsonRPC::Decoding { Bytes web3_sha3(const json& request) { try { // Data to hash will always be at index 0. - if (request["params"].size() != 1) throw std::runtime_error( + if (request["params"].size() != 1) throw DynamicException( "web3_sha3 needs 1 param" ); std::string data = request["params"].at(0).get(); - if (!Hex::isValid(data, true)) throw std::runtime_error("Invalid hex string"); + if (!Hex::isValid(data, true)) throw DynamicException("Invalid hex string"); return Hex::toBytes(data); } catch (std::exception& e) { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while decoding web3_sha3: ") + e.what() ); - throw std::runtime_error("Error while decoding web3_sha3: " + std::string(e.what())); + throw DynamicException("Error while decoding web3_sha3: " + std::string(e.what())); } } void net_version(const json& request) { try { // No params are needed. - if (!request["params"].empty()) throw std::runtime_error( + if (!request["params"].empty()) throw DynamicException( "net_version does not need params" ); } catch (std::exception& e) { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while decoding net_version: ") + e.what() ); - throw std::runtime_error("Error while decoding net_version: " + std::string(e.what())); + throw DynamicException("Error while decoding net_version: " + std::string(e.what())); } } void net_listening(const json& request) { try { // No params are needed. - if (!request["params"].empty()) throw std::runtime_error( + if (!request["params"].empty()) throw DynamicException( "net_listening does not need params" ); } catch (std::exception& e) { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while decoding net_listening: ") + e.what() ); - throw std::runtime_error("Error while decoding net_listening: " + std::string(e.what())); + throw DynamicException("Error while decoding net_listening: " + std::string(e.what())); } } void net_peerCount(const json& request) { try { // No params are needed. - if (!request["params"].empty()) throw std::runtime_error( + if (!request["params"].empty()) throw DynamicException( "net_peerCount does not need params" ); } catch (std::exception& e) { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while decoding net_peerCount: ") + e.what() ); - throw std::runtime_error("Error while decoding net_peerCount: " + std::string(e.what())); + throw DynamicException("Error while decoding net_peerCount: " + std::string(e.what())); } } void eth_protocolVersion(const json& request) { try { // No params are needed. - if (!request["params"].empty()) throw std::runtime_error( + if (!request["params"].empty()) throw DynamicException( "eth_protocolVersion does not need params" ); } catch (std::exception& e) { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while decoding eth_protocolVersion: ") + e.what() ); - throw std::runtime_error("Error while decoding eth_protocolVersion: " + std::string(e.what())); + throw DynamicException("Error while decoding eth_protocolVersion: " + std::string(e.what())); } } @@ -141,13 +141,13 @@ namespace JsonRPC::Decoding { try { bool includeTxs = (request["params"].size() == 2) ? request["params"].at(1).get() : false; std::string blockHash = request["params"].at(0).get(); - if (!std::regex_match(blockHash, hashFilter)) throw std::runtime_error("Invalid block hash hex"); + if (!std::regex_match(blockHash, hashFilter)) throw DynamicException("Invalid block hash hex"); return std::make_pair(Hash(Hex::toBytes(blockHash)), includeTxs); } catch (std::exception& e) { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while decoding eth_getBlockByHash: ") + e.what() ); - throw std::runtime_error("Error while decoding eth_getBlockByHash: " + std::string(e.what())); + throw DynamicException("Error while decoding eth_getBlockByHash: " + std::string(e.what())); } } @@ -161,14 +161,14 @@ namespace JsonRPC::Decoding { std::string blockNum = request["params"].at(0).get(); if (blockNum == "latest") return std::make_pair(storage->latest()->getNHeight(), includeTxs); if (blockNum == "earliest") return std::make_pair(0, includeTxs); - if (blockNum == "pending") throw std::runtime_error("Pending block is not supported"); - if (!std::regex_match(blockNum, numFilter)) throw std::runtime_error("Invalid block hash hex"); + if (blockNum == "pending") throw DynamicException("Pending block is not supported"); + if (!std::regex_match(blockNum, numFilter)) throw DynamicException("Invalid block hash hex"); return std::make_pair(uint64_t(Hex(blockNum).getUint()), includeTxs); } catch (std::exception& e) { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while decoding eth_getBlockByNumber: ") + e.what() ); - throw std::runtime_error("Error while decoding eth_getBlockByNumber: " + std::string(e.what())); + throw DynamicException("Error while decoding eth_getBlockByNumber: " + std::string(e.what())); } } @@ -177,13 +177,13 @@ namespace JsonRPC::Decoding { try { // Check block hash. std::string blockHash = request["params"].at(0).get(); - if (!std::regex_match(blockHash, hashFilter)) throw std::runtime_error("Invalid block hash hex"); + if (!std::regex_match(blockHash, hashFilter)) throw DynamicException("Invalid block hash hex"); return Hash(Hex::toBytes(blockHash)); } catch (std::exception& e) { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while decoding eth_getBlockTransactionCountByHash: ") + e.what() ); - throw std::runtime_error( + throw DynamicException( "Error while decoding eth_getBlockTransactionCountByHash: " + std::string(e.what()) ); } @@ -196,14 +196,14 @@ namespace JsonRPC::Decoding { std::string blockNum = request["params"].at(0).get(); if (blockNum == "latest") return storage->latest()->getNHeight(); if (blockNum == "earliest") return 0; - if (blockNum == "pending") throw std::runtime_error("Pending block is not supported"); - if (!std::regex_match(blockNum, numFilter)) throw std::runtime_error("Invalid block hash hex"); + if (blockNum == "pending") throw DynamicException("Pending block is not supported"); + if (!std::regex_match(blockNum, numFilter)) throw DynamicException("Invalid block hash hex"); return uint64_t(Hex(blockNum).getUint()); } catch (std::exception& e) { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while decoding eth_getBlockTransactionCountByNumber: ") + e.what() ); - throw std::runtime_error( + throw DynamicException( "Error while decoding eth_getBlockTransactionCountByNumber: " + std::string(e.what()) ); } @@ -212,47 +212,47 @@ namespace JsonRPC::Decoding { void eth_chainId(const json& request) { try { // No params are needed. - if (!request["params"].empty()) throw std::runtime_error("eth_chainId does not need params"); + if (!request["params"].empty()) throw DynamicException("eth_chainId does not need params"); } catch (std::exception& e) { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while decoding eth_chainId: ") + e.what()); - throw std::runtime_error("Error while decoding eth_chainId: " + std::string(e.what())); + throw DynamicException("Error while decoding eth_chainId: " + std::string(e.what())); } } void eth_syncing(const json& request) { try { // No params are needed. - if (!request["params"].empty()) throw std::runtime_error("eth_syncing does not need params"); + if (!request["params"].empty()) throw DynamicException("eth_syncing does not need params"); } catch (std::exception& e) { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while decoding eth_syncing: ") + e.what() ); - throw std::runtime_error("Error while decoding eth_syncing: " + std::string(e.what())); + throw DynamicException("Error while decoding eth_syncing: " + std::string(e.what())); } } void eth_coinbase(const json& request) { try { // No params are needed. - if (!request["params"].empty()) throw std::runtime_error("eth_coinbase does not need params"); + if (!request["params"].empty()) throw DynamicException("eth_coinbase does not need params"); } catch (std::exception& e) { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while decoding eth_coinbase: ") + e.what() ); - throw std::runtime_error("Error while decoding eth_coinbase: " + std::string(e.what())); + throw DynamicException("Error while decoding eth_coinbase: " + std::string(e.what())); } } void eth_blockNumber(const json& request) { try { // No params are needed. - if (!request["params"].empty()) throw std::runtime_error("eth_blockNumber does not need params"); + if (!request["params"].empty()) throw DynamicException("eth_blockNumber does not need params"); return; } catch (std::exception& e) { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while decoding eth_blockNumber: ") + e.what() ); - throw std::runtime_error("Error while decoding eth_blockNumber: " + std::string(e.what())); + throw DynamicException("Error while decoding eth_blockNumber: " + std::string(e.what())); } } @@ -268,11 +268,11 @@ namespace JsonRPC::Decoding { if (request["params"].size() > 1) { const auto block = request["params"].at(1).get(); if (block != "latest") { - if (!std::regex_match(block, numFilter)) throw std::runtime_error( + if (!std::regex_match(block, numFilter)) throw DynamicException( "Invalid block number" ); auto blockNum = uint64_t(Hex(block).getUint()); - if (blockNum != storage->latest()->getNHeight()) throw std::runtime_error( + if (blockNum != storage->latest()->getNHeight()) throw DynamicException( "Only latest block is supported" ); } @@ -280,41 +280,41 @@ namespace JsonRPC::Decoding { } else if (request["params"].is_object()) { txObj = request["params"]; } else { - throw std::runtime_error("Invalid params"); + throw DynamicException("Invalid params"); } // Optional: Check from address if (txObj.contains("from") && !txObj["from"].is_null()) { std::string fromAdd = txObj["from"].get(); - if (!std::regex_match(fromAdd, addFilter)) throw std::runtime_error("Invalid from address hex"); + if (!std::regex_match(fromAdd, addFilter)) throw DynamicException("Invalid from address hex"); from = Address(Hex::toBytes(fromAdd)); } // Check to address std::string toAdd = txObj["to"].get(); - if (!std::regex_match(toAdd, addFilter)) throw std::runtime_error("Invalid to address hex"); + if (!std::regex_match(toAdd, addFilter)) throw DynamicException("Invalid to address hex"); to = Address(Hex::toBytes(toAdd)); // Optional: Check gas if (txObj.contains("gas") && !txObj["gas"].is_null()) { std::string gasHex = txObj["gas"].get(); - if (!std::regex_match(gasHex, numFilter)) throw std::runtime_error("Invalid gas hex"); + if (!std::regex_match(gasHex, numFilter)) throw DynamicException("Invalid gas hex"); gas = uint64_t(Hex(gasHex).getUint()); } // Optional: Check gasPrice if (txObj.contains("gasPrice") && !txObj["gasPrice"].is_null()) { std::string gasPriceHex = txObj["gasPrice"].get(); - if (!std::regex_match(gasPriceHex, numFilter)) throw std::runtime_error("Invalid gasPrice hex"); + if (!std::regex_match(gasPriceHex, numFilter)) throw DynamicException("Invalid gasPrice hex"); gasPrice = uint256_t(Hex(gasPriceHex).getUint()); } // Optional: Check value if (txObj.contains("value") && !txObj["value"].is_null()) { std::string valueHex = txObj["value"].get(); - if (!std::regex_match(valueHex, numFilter)) throw std::runtime_error("Invalid value hex"); + if (!std::regex_match(valueHex, numFilter)) throw DynamicException("Invalid value hex"); value = uint256_t(Hex(valueHex).getUint()); } // Optional: Check data if (txObj.contains("data") && !txObj["data"].is_null()) { std::string dataHex = txObj["data"].get(); - if (!Hex::isValid(dataHex, true)) throw std::runtime_error("Invalid data hex"); + if (!Hex::isValid(dataHex, true)) throw DynamicException("Invalid data hex"); auto dataBytes = Hex::toBytes(dataHex); if (dataBytes.size() >= 4) { functor = Functor(Utils::create_view_span(dataBytes, 0, 4)); @@ -328,7 +328,7 @@ namespace JsonRPC::Decoding { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while decoding eth_call: ") + e.what() ); - throw std::runtime_error("Error while decoding eth_call: " + std::string(e.what())); + throw DynamicException("Error while decoding eth_call: " + std::string(e.what())); } } @@ -344,11 +344,11 @@ namespace JsonRPC::Decoding { if (request["params"].size() > 1) { const auto block = request["params"].at(1).get(); if (block != "latest") { - if (!std::regex_match(block, numFilter)) throw std::runtime_error( + if (!std::regex_match(block, numFilter)) throw DynamicException( "Invalid block number" ); auto blockNum = uint64_t(Hex(block).getUint()); - if (blockNum != storage->latest()->getNHeight()) throw std::runtime_error( + if (blockNum != storage->latest()->getNHeight()) throw DynamicException( "Only latest block is supported" ); } @@ -356,25 +356,25 @@ namespace JsonRPC::Decoding { } else if (request["params"].is_object()) { txObj = request["params"]; } else { - throw std::runtime_error("Invalid params"); + throw DynamicException("Invalid params"); } // Optional: Check from address if (txObj.contains("from") && !txObj["from"].is_null()) { std::string fromAdd = txObj["from"].get(); - if (!std::regex_match(fromAdd, addFilter)) throw std::runtime_error("Invalid from address hex"); + if (!std::regex_match(fromAdd, addFilter)) throw DynamicException("Invalid from address hex"); from = Address(Hex::toBytes(fromAdd)); } // Optional: Check to address if (txObj.contains("to") && !txObj["to"].is_null()) { std::string toAdd = txObj["to"].get(); - if (!std::regex_match(toAdd, addFilter)) throw std::runtime_error("Invalid to address hex"); + if (!std::regex_match(toAdd, addFilter)) throw DynamicException("Invalid to address hex"); to = Address(Hex::toBytes(toAdd)); } // Optional: Check gas if (txObj.contains("gas") && !txObj["gas"].is_null()) { std::string gasHex = txObj["gas"].get(); - if (!std::regex_match(gasHex, numFilter)) throw std::runtime_error("Invalid gas hex"); + if (!std::regex_match(gasHex, numFilter)) throw DynamicException("Invalid gas hex"); gas = uint64_t(Hex(gasHex).getUint()); } else { // eth_estimateGas set gas to max if not specified // TODO: Change this if we ever change gas dynamics with the chain @@ -383,19 +383,19 @@ namespace JsonRPC::Decoding { // Optional: Check gasPrice if (txObj.contains("gasPrice") && !txObj["gasPrice"].is_null()) { std::string gasPriceHex = txObj["gasPrice"].get(); - if (!std::regex_match(gasPriceHex, numFilter)) throw std::runtime_error("Invalid gasPrice hex"); + if (!std::regex_match(gasPriceHex, numFilter)) throw DynamicException("Invalid gasPrice hex"); gasPrice = uint256_t(Hex(gasPriceHex).getUint()); } // Optional: Check value if (txObj.contains("value") && !txObj["value"].is_null()) { std::string valueHex = txObj["value"].get(); - if (!std::regex_match(valueHex, numFilter)) throw std::runtime_error("Invalid value hex"); + if (!std::regex_match(valueHex, numFilter)) throw DynamicException("Invalid value hex"); value = uint256_t(Hex(valueHex).getUint()); } // Optional: Check data if (txObj.contains("data") && !txObj["data"].is_null()) { std::string dataHex = txObj["data"].get(); - if (!Hex::isValid(dataHex, true)) throw std::runtime_error("Invalid data hex"); + if (!Hex::isValid(dataHex, true)) throw DynamicException("Invalid data hex"); auto dataBytes = Hex::toBytes(dataHex); if (dataBytes.size() >= 4) { functor = Functor(Utils::create_view_span(dataBytes, 0, 4)); @@ -409,18 +409,18 @@ namespace JsonRPC::Decoding { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while decoding eth_estimateGas: ") + e.what() ); - throw std::runtime_error("Error while decoding eth_estimateGas: " + std::string(e.what())); + throw DynamicException("Error while decoding eth_estimateGas: " + std::string(e.what())); } } void eth_gasPrice(const json& request) { try { - if (!request["params"].empty()) throw std::runtime_error("eth_gasPrice does not need params"); + if (!request["params"].empty()) throw DynamicException("eth_gasPrice does not need params"); } catch (std::exception& e) { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while decoding eth_gasPrice: ") + e.what() ); - throw std::runtime_error("Error while decoding eth_gasPrice: " + std::string(e.what())); + throw DynamicException("Error while decoding eth_gasPrice: " + std::string(e.what())); } } @@ -439,7 +439,7 @@ namespace JsonRPC::Decoding { if (logsObject.contains("blockHash")) { std::string blockHashHex = logsObject["blockHash"].get(); - if (!std::regex_match(blockHashHex, hashFilter)) throw std::runtime_error("Invalid block hash hex"); + if (!std::regex_match(blockHashHex, hashFilter)) throw DynamicException("Invalid block hash hex"); const std::shared_ptr block = storage->getBlock(Hash(Hex::toBytes(blockHashHex))); fromBlock = toBlock = block->getNHeight(); } else { @@ -450,11 +450,11 @@ namespace JsonRPC::Decoding { } else if (fromBlockHex == "earliest") { fromBlock = 0; } else if (fromBlockHex == "pending") { - throw std::runtime_error("Pending block is not supported"); + throw DynamicException("Pending block is not supported"); } else if (std::regex_match(fromBlockHex, numFilter)) { fromBlock = uint64_t(Hex(fromBlockHex).getUint()); } else { - throw std::runtime_error("Invalid fromBlock hex"); + throw DynamicException("Invalid fromBlock hex"); } } if (logsObject.contains("toBlock")) { @@ -464,28 +464,28 @@ namespace JsonRPC::Decoding { } else if (toBlockHex == "earliest") { toBlock = 0; } else if (toBlockHex == "pending") { - throw std::runtime_error("Pending block is not supported"); + throw DynamicException("Pending block is not supported"); } else if (std::regex_match(toBlockHex, numFilter)) { toBlock = uint64_t(Hex(toBlockHex).getUint()); } else { - throw std::runtime_error("Invalid fromBlock hex"); + throw DynamicException("Invalid fromBlock hex"); } } } if (logsObject.contains("address")) { std::string addressHex = logsObject["address"].get(); - if (!std::regex_match(addressHex, addFilter)) throw std::runtime_error("Invalid address hex"); + if (!std::regex_match(addressHex, addFilter)) throw DynamicException("Invalid address hex"); address = Address(Hex::toBytes(addressHex)); } if (logsObject.contains("topics")) { if (!logsObject.at("topics").is_array()) { - throw std::runtime_error("topics is not an array"); + throw DynamicException("topics is not an array"); } auto topicsArray = logsObject.at("topics").get>(); for (const auto& topic : topicsArray) { - if (!std::regex_match(topic, hashFilter)) throw std::runtime_error("Invalid topic hex"); + if (!std::regex_match(topic, hashFilter)) throw DynamicException("Invalid topic hex"); topics.emplace_back(Hex::toBytes(topic)); } } @@ -495,7 +495,7 @@ namespace JsonRPC::Decoding { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while decoding eth_getLogs: ") + e.what() ); - throw std::runtime_error("Error while decoding eth_getLogs: " + std::string(e.what())); + throw DynamicException("Error while decoding eth_getLogs: " + std::string(e.what())); } } @@ -506,21 +506,21 @@ namespace JsonRPC::Decoding { const auto address = request["params"].at(0).get(); const auto block = request["params"].at(1).get(); if (block != "latest") { - if (!std::regex_match(block, numFilter)) throw std::runtime_error( + if (!std::regex_match(block, numFilter)) throw DynamicException( "Invalid block number" ); auto blockNum = uint64_t(Hex(block).getUint()); - if (blockNum != storage->latest()->getNHeight()) throw std::runtime_error( + if (blockNum != storage->latest()->getNHeight()) throw DynamicException( "Only latest block is supported" ); } - if (!std::regex_match(address, addFilter)) throw std::runtime_error("Invalid address hex"); + if (!std::regex_match(address, addFilter)) throw DynamicException("Invalid address hex"); return Address(Hex::toBytes(address)); } catch (std::exception& e) { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while decoding eth_getBalance: ") + e.what() ); - throw std::runtime_error("Error while decoding eth_getBalance: " + std::string(e.what())); + throw DynamicException("Error while decoding eth_getBalance: " + std::string(e.what())); } } @@ -531,21 +531,21 @@ namespace JsonRPC::Decoding { const auto address = request["params"].at(0).get(); const auto block = request["params"].at(1).get(); if (block != "latest") { - if (!std::regex_match(block, numFilter)) throw std::runtime_error( + if (!std::regex_match(block, numFilter)) throw DynamicException( "Invalid block number" ); auto blockNum = uint64_t(Hex(block).getUint()); - if (blockNum != storage->latest()->getNHeight()) throw std::runtime_error( + if (blockNum != storage->latest()->getNHeight()) throw DynamicException( "Only latest block is supported" ); } - if (!std::regex_match(address, addFilter)) throw std::runtime_error("Invalid address hex"); + if (!std::regex_match(address, addFilter)) throw DynamicException("Invalid address hex"); return Address(Hex::toBytes(address)); } catch (std::exception& e) { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while decoding eth_getTransactionCount: ") + e.what() ); - throw std::runtime_error("Error while decoding eth_getTransactionCount: " + std::string(e.what())); + throw DynamicException("Error while decoding eth_getTransactionCount: " + std::string(e.what())); } } @@ -556,34 +556,34 @@ namespace JsonRPC::Decoding { const auto address = request["params"].at(0).get(); const auto block = request["params"].at(1).get(); if (block != "latest") { - if (!std::regex_match(block, numFilter)) throw std::runtime_error( + if (!std::regex_match(block, numFilter)) throw DynamicException( "Invalid block number" ); auto blockNum = uint64_t(Hex(block).getUint()); - if (blockNum != storage->latest()->getNHeight()) throw std::runtime_error( + if (blockNum != storage->latest()->getNHeight()) throw DynamicException( "Only latest block is supported" ); } - if (!std::regex_match(address, addFilter)) throw std::runtime_error("Invalid address hex"); + if (!std::regex_match(address, addFilter)) throw DynamicException("Invalid address hex"); return Address(Hex::toBytes(address)); } catch (std::exception& e) { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while decoding eth_getCode: ") + e.what() ); - throw std::runtime_error("Error while decoding eth_getCode: " + std::string(e.what())); + throw DynamicException("Error while decoding eth_getCode: " + std::string(e.what())); } } TxBlock eth_sendRawTransaction(const json& request, const uint64_t& requiredChainId) { try { const auto txHex = request["params"].at(0).get(); - if (!Hex::isValid(txHex, true)) throw std::runtime_error("Invalid transaction hex"); + if (!Hex::isValid(txHex, true)) throw DynamicException("Invalid transaction hex"); return TxBlock(Hex::toBytes(txHex), requiredChainId); } catch (std::exception& e) { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while decoding eth_sendRawTransaction: ") + e.what() ); - throw std::runtime_error("Error while decoding eth_sendRawTransaction: " + std::string(e.what())); + throw DynamicException("Error while decoding eth_sendRawTransaction: " + std::string(e.what())); } } @@ -591,13 +591,13 @@ namespace JsonRPC::Decoding { static const std::regex hashFilter("^0x[0-9,a-f,A-F]{64}$"); try { const auto hash = request["params"].at(0).get(); - if (!std::regex_match(hash, hashFilter)) throw std::runtime_error("Invalid hash hex"); + if (!std::regex_match(hash, hashFilter)) throw DynamicException("Invalid hash hex"); return Hash(Hex::toBytes(hash)); } catch (std::exception& e) { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while decoding eth_getTransactionByHash: ") + e.what() ); - throw std::runtime_error("Error while decoding eth_getTransactionByHash: " + std::string(e.what())); + throw DynamicException("Error while decoding eth_getTransactionByHash: " + std::string(e.what())); } } @@ -607,14 +607,14 @@ namespace JsonRPC::Decoding { try { std::string blockHash = request["params"].at(0).get(); std::string index = request["params"].at(1).get(); - if (!std::regex_match(blockHash, hashFilter)) throw std::runtime_error("Invalid blockHash hex"); - if (!std::regex_match(index, numFilter)) throw std::runtime_error("Invalid index hex"); + if (!std::regex_match(blockHash, hashFilter)) throw DynamicException("Invalid blockHash hex"); + if (!std::regex_match(index, numFilter)) throw DynamicException("Invalid index hex"); return std::make_pair(Hash(Hex::toBytes(blockHash)), uint64_t(Hex(index).getUint())); } catch (std::exception& e) { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while decoding eth_getTransactionByBlockHashAndIndex: ") + e.what() ); - throw std::runtime_error( + throw DynamicException( "Error while decoding eth_getTransactionByBlockHashAndIndex: " + std::string(e.what()) ); } @@ -627,11 +627,11 @@ namespace JsonRPC::Decoding { try { std::string blockNum = request["params"].at(0).get(); std::string index = request["params"].at(1).get(); - if (!std::regex_match(index, numFilter)) throw std::runtime_error("Invalid index hex"); + if (!std::regex_match(index, numFilter)) throw DynamicException("Invalid index hex"); if (blockNum == "latest") return std::make_pair( storage->latest()->getNHeight(), uint64_t(Hex(index).getUint()) ); - if (!std::regex_match(blockNum, numFilter)) throw std::runtime_error("Invalid blockNumber hex"); + if (!std::regex_match(blockNum, numFilter)) throw DynamicException("Invalid blockNumber hex"); return std::make_pair( uint64_t(Hex(blockNum).getUint()), uint64_t(Hex(index).getUint()) ); @@ -639,7 +639,7 @@ namespace JsonRPC::Decoding { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while decoding eth_getTransactionByBlockNumberAndIndex: ") + e.what() ); - throw std::runtime_error( + throw DynamicException( "Error while decoding eth_getTransactionByBlockNumberAndIndex: " + std::string(e.what()) ); } @@ -649,13 +649,13 @@ namespace JsonRPC::Decoding { static const std::regex hashFilter("^0x[0-9,a-f,A-F]{64}$"); try { std::string txHash = request["params"].at(0).get(); - if (!std::regex_match(txHash, hashFilter)) throw std::runtime_error("Invalid Hex"); + if (!std::regex_match(txHash, hashFilter)) throw DynamicException("Invalid Hex"); return Hash(Hex::toBytes(txHash)); } catch (std::exception& e) { Logger::logToDebug(LogType::ERROR, Log::JsonRPCDecoding, __func__, std::string("Error while decoding eth_getTransactionReceipt: ") + e.what() ); - throw std::runtime_error( + throw DynamicException( "Error while decoding eth_getTransactionReceipt: " + std::string(e.what()) ); } diff --git a/src/net/p2p/encoding.cpp b/src/net/p2p/encoding.cpp index 93044304..251cea9e 100644 --- a/src/net/p2p/encoding.cpp +++ b/src/net/p2p/encoding.cpp @@ -15,18 +15,18 @@ namespace P2P { RequestID RequestID::random() { return RequestID(Utils::randBytes(8)); } CommandType getCommandType(const BytesArrView message) { - if (message.size() != 2) { throw std::runtime_error("Invalid Command Type size." + std::to_string(message.size())); } + if (message.size() != 2) { throw DynamicException("Invalid Command Type size." + std::to_string(message.size())); } uint16_t commandType = Utils::bytesToUint16(message); - if (commandType > commandPrefixes.size()) { throw std::runtime_error("Invalid command type."); } + if (commandType > commandPrefixes.size()) { throw DynamicException("Invalid command type."); } return static_cast(commandType); } const Bytes& getCommandPrefix(const CommandType& commType) { return commandPrefixes[commType]; } RequestType getRequestType(const BytesArrView message) { - if (message.size() != 1) { throw std::runtime_error("Invalid Request Type size. " + std::to_string(message.size())); } + if (message.size() != 1) { throw DynamicException("Invalid Request Type size. " + std::to_string(message.size())); } uint8_t requestType = Utils::bytesToUint8(message); - if (requestType > typePrefixes.size()) { throw std::runtime_error("Invalid request type."); } + if (requestType > typePrefixes.size()) { throw DynamicException("Invalid request type."); } return static_cast(requestType); } @@ -77,8 +77,8 @@ namespace P2P { } NodeInfo RequestDecoder::info(const Message& message) { - if (message.size() != 67) { throw std::runtime_error("Invalid Info message size."); } - if (message.command() != Info) { throw std::runtime_error("Invalid Info message command."); } + if (message.size() != 67) { throw DynamicException("Invalid Info message size."); } + if (message.command() != Info) { throw DynamicException("Invalid Info message command."); } uint64_t nodeVersion = Utils::bytesToUint64(message.message().subspan(0, 8)); uint64_t nodeEpoch = Utils::bytesToUint64(message.message().subspan(8, 8)); uint64_t nodeHeight = Utils::bytesToUint64(message.message().subspan(16, 8)); @@ -168,8 +168,8 @@ namespace P2P { } NodeInfo AnswerDecoder::info(const Message& message) { - if (message.type() != Answering) { throw std::runtime_error("Invalid message type."); } - if (message.command() != Info) { throw std::runtime_error("Invalid command."); } + if (message.type() != Answering) { throw DynamicException("Invalid message type."); } + if (message.command() != Info) { throw DynamicException("Invalid command."); } uint64_t nodeVersion = Utils::bytesToUint64(message.message().subspan(0, 8)); uint64_t nodeEpoch = Utils::bytesToUint64(message.message().subspan(8, 8)); uint64_t nodeHeight = Utils::bytesToUint64(message.message().subspan(16, 8)); @@ -180,15 +180,15 @@ namespace P2P { std::unordered_map< NodeID, NodeType, SafeHash > AnswerDecoder::requestNodes(const Message& message) { - if (message.type() != Answering) { throw std::runtime_error("Invalid message type."); } - if (message.command() != RequestNodes) { throw std::runtime_error("Invalid command."); } + if (message.type() != Answering) { throw DynamicException("Invalid message type."); } + if (message.command() != RequestNodes) { throw DynamicException("Invalid command."); } std::unordered_map nodes; BytesArrView data = message.message(); size_t index = 0; while (index < data.size()) { boost::asio::ip::address address; - if (data.size() < 8) { throw std::runtime_error("Invalid data size."); } + if (data.size() < 8) { throw DynamicException("Invalid data size."); } auto nodeType = NodeType(Utils::bytesToUint8(data.subspan(index, 1))); index += 1; uint8_t ipVersion = Utils::bytesToUint8(data.subspan(index, 1)); @@ -204,7 +204,7 @@ namespace P2P { address = boost::asio::ip::address_v6(ipBytes); index += 16; } else { - throw std::runtime_error("Invalid ip version."); + throw DynamicException("Invalid ip version."); } auto port = Utils::bytesToUint16(data.subspan(index, 2)); nodes.insert({NodeID(address, port), nodeType}); @@ -216,16 +216,16 @@ namespace P2P { std::vector AnswerDecoder::requestValidatorTxs( const Message& message, const uint64_t& requiredChainId ) { - if (message.type() != Answering) { throw std::runtime_error("Invalid message type."); } - if (message.command() != RequestValidatorTxs) { throw std::runtime_error("Invalid command."); } + if (message.type() != Answering) { throw DynamicException("Invalid message type."); } + if (message.command() != RequestValidatorTxs) { throw DynamicException("Invalid command."); } std::vector txs; BytesArrView data = message.message(); size_t index = 0; while (index < data.size()) { - if (data.size() < 4) { throw std::runtime_error("Invalid data size."); } + if (data.size() < 4) { throw DynamicException("Invalid data size."); } uint32_t txSize = Utils::bytesToUint32(data.subspan(index, 4)); index += 4; - if (data.size() < txSize) { throw std::runtime_error("Invalid data size."); } + if (data.size() < txSize) { throw DynamicException("Invalid data size."); } BytesArrView txData = data.subspan(index, txSize); index += txSize; txs.emplace_back(txData, requiredChainId); @@ -265,23 +265,23 @@ namespace P2P { } TxValidator BroadcastDecoder::broadcastValidatorTx(const Message& message, const uint64_t& requiredChainId) { - if (message.type() != Broadcasting) { throw std::runtime_error("Invalid message type."); } - if (message.id().toUint64() != FNVHash()(message.message())) { throw std::runtime_error("Invalid message id."); } - if (message.command() != BroadcastValidatorTx) { throw std::runtime_error("Invalid command."); } + if (message.type() != Broadcasting) { throw DynamicException("Invalid message type."); } + if (message.id().toUint64() != FNVHash()(message.message())) { throw DynamicException("Invalid message id."); } + if (message.command() != BroadcastValidatorTx) { throw DynamicException("Invalid command."); } return TxValidator(message.message(), requiredChainId); } TxBlock BroadcastDecoder::broadcastTx(const P2P::Message &message, const uint64_t &requiredChainId) { - if (message.type() != Broadcasting) { throw std::runtime_error("Invalid message type."); } - if (message.id().toUint64() != FNVHash()(message.message())) { throw std::runtime_error("Invalid message id."); } - if (message.command() != BroadcastTx) { throw std::runtime_error("Invalid command."); } + if (message.type() != Broadcasting) { throw DynamicException("Invalid message type."); } + if (message.id().toUint64() != FNVHash()(message.message())) { throw DynamicException("Invalid message id."); } + if (message.command() != BroadcastTx) { throw DynamicException("Invalid command."); } return TxBlock(message.message(), requiredChainId); } Block BroadcastDecoder::broadcastBlock(const P2P::Message &message, const uint64_t &requiredChainId) { - if (message.type() != Broadcasting) { throw std::runtime_error("Invalid message type."); } - if (message.id().toUint64() != FNVHash()(message.message())) { throw std::runtime_error("Invalid message id. "); } - if (message.command() != BroadcastBlock) { throw std::runtime_error("Invalid command."); } + if (message.type() != Broadcasting) { throw DynamicException("Invalid message type."); } + if (message.id().toUint64() != FNVHash()(message.message())) { throw DynamicException("Invalid message id. "); } + if (message.command() != BroadcastBlock) { throw DynamicException("Invalid command."); } return Block(message.message(), requiredChainId); } } diff --git a/src/net/p2p/encoding.h b/src/net/p2p/encoding.h index dc92e880..a46a689e 100644 --- a/src/net/p2p/encoding.h +++ b/src/net/p2p/encoding.h @@ -386,7 +386,7 @@ namespace P2P { /// Raw string move constructor. Throws on invalid size. explicit Message(Bytes&& raw) : rawMessage_(std::move(raw)) { - if (rawMessage_.size() < 11) throw std::runtime_error("Invalid message size."); + if (rawMessage_.size() < 11) throw DynamicException("Invalid message size."); } /// Assignment operator. diff --git a/src/net/p2p/managerbase.cpp b/src/net/p2p/managerbase.cpp index 3f52f58f..b9e3229e 100644 --- a/src/net/p2p/managerbase.cpp +++ b/src/net/p2p/managerbase.cpp @@ -147,7 +147,7 @@ namespace P2P { auto request = std::make_shared(RequestEncoder::ping()); Utils::logToFile("Pinging " + nodeId.first.to_string() + ":" + std::to_string(nodeId.second)); auto requestPtr = sendRequestTo(nodeId, request); - if (requestPtr == nullptr) throw std::runtime_error( + if (requestPtr == nullptr) throw DynamicException( "Failed to send ping to " + nodeId.first.to_string() + ":" + std::to_string(nodeId.second) ); requestPtr->answerFuture().wait(); diff --git a/src/net/p2p/session.h b/src/net/p2p/session.h index bd353f66..783b6eac 100644 --- a/src/net/p2p/session.h +++ b/src/net/p2p/session.h @@ -152,7 +152,7 @@ namespace P2P { { if (connectionType == ConnectionType::OUTBOUND) { /// Not a server, it will not call do_connect(). - throw std::runtime_error("Session: Invalid connection type."); + throw DynamicException("Session: Invalid connection type."); } } @@ -175,7 +175,7 @@ namespace P2P { { if (connectionType == ConnectionType::INBOUND) { /// Not a client, it will try to write handshake without connecting. - throw std::runtime_error("Session: Invalid connection type."); + throw DynamicException("Session: Invalid connection type."); } } diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index d50a515b..476a9c61 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -12,6 +12,7 @@ set(UTILS_HEADERS ${CMAKE_SOURCE_DIR}/src/utils/contractreflectioninterface.h ${CMAKE_SOURCE_DIR}/src/utils/jsonabi.h ${CMAKE_SOURCE_DIR}/src/utils/logger.h + ${CMAKE_SOURCE_DIR}/src/utils/dynamicexception.h PARENT_SCOPE ) diff --git a/src/utils/block.cpp b/src/utils/block.cpp index 89fbd34b..37bb3757 100644 --- a/src/utils/block.cpp +++ b/src/utils/block.cpp @@ -11,7 +11,7 @@ See the LICENSE.txt file in the project root for more information. Block::Block(const BytesArrView bytes, const uint64_t& requiredChainId) { try { // Split the bytes string - if (bytes.size() < 217) throw std::runtime_error("Invalid block size - too short"); + if (bytes.size() < 217) throw DynamicException("Invalid block size - too short"); this->validatorSig_ = Signature(bytes.subspan(0, 65)); this->prevBlockHash_ = Hash(bytes.subspan(65, 32)); this->blockRandomness_= Hash(bytes.subspan(97, 32)); @@ -106,7 +106,7 @@ Block::Block(const BytesArrView bytes, const uint64_t& requiredChainId) { index += 4; this->txValidators_.emplace_back(bytes.subspan(index, txSize), requiredChainId); if (this->txValidators_.back().getNHeight() != this->nHeight_) { - throw std::runtime_error("Invalid validator tx height"); + throw DynamicException("Invalid validator tx height"); } index += txSize; } @@ -115,19 +115,19 @@ Block::Block(const BytesArrView bytes, const uint64_t& requiredChainId) { auto expectedValidatorMerkleRoot = Merkle(this->txValidators_).getRoot(); auto expectedRandomness = rdPoS::parseTxSeedList(this->txValidators_); if (expectedTxMerkleRoot != this->txMerkleRoot_) { - throw std::runtime_error("Invalid tx merkle root"); + throw DynamicException("Invalid tx merkle root"); } if (expectedValidatorMerkleRoot != this->validatorMerkleRoot_) { - throw std::runtime_error("Invalid validator merkle root"); + throw DynamicException("Invalid validator merkle root"); } if (expectedRandomness != this->blockRandomness_) { - throw std::runtime_error("Invalid block randomness"); + throw DynamicException("Invalid block randomness"); } Hash msgHash = this->hash(); if (!Secp256k1::verifySig( this->validatorSig_.r(), this->validatorSig_.s(), this->validatorSig_.v() )) { - throw std::runtime_error("Invalid validator signature"); + throw DynamicException("Invalid validator signature"); } // Get the signature and finalize the block this->validatorPubKey_ = Secp256k1::recover(this->validatorSig_, msgHash); @@ -137,7 +137,7 @@ Block::Block(const BytesArrView bytes, const uint64_t& requiredChainId) { "Error when deserializing a block: " + std::string(e.what()) ); // Throw again because invalid blocks should not be created at all. - throw std::runtime_error(std::string(__func__) + ": " + e.what()); + throw DynamicException(std::string(__func__) + ": " + e.what()); } } diff --git a/src/utils/block.h b/src/utils/block.h index 0b492742..21af0294 100644 --- a/src/utils/block.h +++ b/src/utils/block.h @@ -92,7 +92,7 @@ class Block { * Constructor from network/RPC. * @param bytes The raw block data string to parse. * @param requiredChainId The chain ID that the block and its transactions belong to. - * @throw std::runtime_error on any invalid block parameter (size, signature, etc.). + * @throw DynamicException on any invalid block parameter (size, signature, etc.). */ Block(const BytesArrView bytes, const uint64_t& requiredChainId); diff --git a/src/utils/contractreflectioninterface.h b/src/utils/contractreflectioninterface.h index e6312142..0b32ef25 100644 --- a/src/utils/contractreflectioninterface.h +++ b/src/utils/contractreflectioninterface.h @@ -451,13 +451,13 @@ namespace ContractReflectionInterface { * @return The constructor ABI data structure. */ template ABI::MethodDescription inline getConstructorDataStructure() { - if (!isContractFunctionsRegistered()) throw std::runtime_error( + if (!isContractFunctionsRegistered()) throw DynamicException( "Contract " + Utils::getRealTypeName() + " not registered" ); // Derive from Contract::ConstructorArguments to get the constructor auto ctorArgs = ABI::FunctorEncoder::listArgumentTypesVFromTuple(); auto ctorArgsNames = ctorArgNamesMap[Utils::getRealTypeName()]; - if (ctorArgs.size() != ctorArgsNames.size()) throw std::runtime_error( + if (ctorArgs.size() != ctorArgsNames.size()) throw DynamicException( "Contract " + Utils::getRealTypeName() + " constructor argument names not registered, wanted: " + std::to_string(ctorArgs.size()) + " got: " + std::to_string(ctorArgsNames.size()) ); @@ -481,7 +481,7 @@ namespace ContractReflectionInterface { * @return The function ABI data structure. */ template std::vector inline getFunctionsDataStructure() { - if (!isContractFunctionsRegistered()) throw std::runtime_error( + if (!isContractFunctionsRegistered()) throw DynamicException( "Contract " + Utils::getRealTypeName() + " not registered" ); std::vector descriptions; @@ -498,7 +498,7 @@ namespace ContractReflectionInterface { */ template std::vector inline getEventsDataStructure() { - if (!isContractFunctionsRegistered()) throw std::runtime_error( + if (!isContractFunctionsRegistered()) throw DynamicException( "Contract " + Utils::getRealTypeName() + " not registered" ); std::vector descriptions; diff --git a/src/utils/db.cpp b/src/utils/db.cpp index 16063f8e..f14a9637 100644 --- a/src/utils/db.cpp +++ b/src/utils/db.cpp @@ -15,7 +15,7 @@ DB::DB(const std::filesystem::path& path) { auto status = rocksdb::DB::Open(this->opts_, path, &this->db_); if (!status.ok()) { Logger::logToDebug(LogType::ERROR, Log::db, __func__, "Failed to open DB: " + status.ToString()); - throw std::runtime_error("Failed to open DB: " + status.ToString()); + throw DynamicException("Failed to open DB: " + status.ToString()); } } diff --git a/src/utils/db.h b/src/utils/db.h index cc35385f..517003d3 100644 --- a/src/utils/db.h +++ b/src/utils/db.h @@ -18,6 +18,7 @@ See the LICENSE.txt file in the project root for more information. #include #include "utils.h" +#include "dynamicexception.h" /// Namespace for accessing database prefixes. namespace DBPrefix { @@ -164,7 +165,7 @@ class DB { /** * Constructor. Automatically creates the database if it doesn't exist. * @param path The database's filesystem path (relative to the binary's current working directory). - * @throw std::runtime_error if database opening fails. + * @throw DynamicException if database opening fails. */ explicit DB(const std::filesystem::path& path); diff --git a/src/utils/dynamicexception.h b/src/utils/dynamicexception.h new file mode 100644 index 00000000..1cf77d02 --- /dev/null +++ b/src/utils/dynamicexception.h @@ -0,0 +1,131 @@ +/* +Copyright (c) [2023-2024] [Sparq Network] + +This software is distributed under the MIT License. +See the LICENSE.txt file in the project root for more information. +*/ + +#ifndef DYNAMIC_EXCEPTION_H +#define DYNAMIC_EXCEPTION_H + +#include +#include +#include +#include +#include +#include + +/** +* @brief A dynamic exception class that allows for dynamic message building and timestamping. +*/ +class DynamicException : public std::exception { +public: + /** + * @brief Constructor for the DynamicException class with a dynamic message. + * @param args The message parts to be concatenated. + */ + template + explicit DynamicException(Args... args) + : file_(""), line_(0), function_(""), message_(buildMessage(args...)) { + setTimestamp(); + } + + /** + * @brief Constructor for the DynamicException class with a dynamic message and stacktrace information. + * @param firstArg The first part of the message. + * @param restArgs The rest of the message parts. + * @param file The file where the exception was thrown. + * @param line The line where the exception was thrown. + * @param func The function where the exception was thrown. + */ + template + DynamicException(FirstArg firstArg, RestArgs... restArgs, const std::string& file, int line, const std::string& func) + : file_(file), line_(line), function_(func), message_(buildMessage(firstArg, restArgs...)) { + setTimestamp(); + } + + /** + * @brief Returns the exception message. + * @return The exception message. + */ + const char* what() const noexcept override { + return message_.c_str(); + } + + /** + * @brief Returns the timestamp of the exception. + * @return The timestamp of the exception. + */ + std::string getTimestamp() const { + return timestamp_; + } + + /** + * @brief Returns the file where the exception was thrown. + * @return The file where the exception was thrown. + */ + const std::string& getFile() const { + return file_; + } + + /** + * @brief Returns the line where the exception was thrown. + * @return The line where the exception was thrown. + */ + int getLine() const { + return line_; + } + + /** + * @brief Returns the function where the exception was thrown. + * @return The function where the exception was thrown. + */ + const std::string& getFunction() const { + return function_; + } + +private: + + /// The exception message. + std::string message_; + /// The timestamp of the exception. + std::string timestamp_; + /// The file where the exception was thrown. + std::string file_; + /// The line where the exception was thrown. + int line_; + /// The function where the exception was thrown. + std::string function_; + + /** + * @brief Sets the timestamp of the exception. + */ + void setTimestamp() { + auto now = std::chrono::system_clock::now(); + auto now_c = std::chrono::system_clock::to_time_t(now); + std::tm tm_buf; + std::stringstream ss; + + // Using localtime_r for thread safety + if (localtime_r(&now_c, &tm_buf)) { + ss << std::put_time(&tm_buf, "%Y-%m-%d %H:%M:%S"); + timestamp_ = ss.str(); + } else { + timestamp_ = "Error: Unable to get local time"; + } + } + + /** + * @brief Builds the exception message from the given parts. + * @param args The message parts to be concatenated. + * @return The built exception message. + */ + template + std::string buildMessage(Args... args) const { + std::ostringstream stream; + (stream << ... << args); + return stream.str(); + } +}; + +#endif // DYNAMIC_EXCEPTION_H diff --git a/src/utils/hex.cpp b/src/utils/hex.cpp index 89f41a61..f483caee 100644 --- a/src/utils/hex.cpp +++ b/src/utils/hex.cpp @@ -14,7 +14,7 @@ Hex::Hex(const std::string_view value, bool strict) : strict_(strict) { } else { if (ret[0] == '0' && (ret[1] == 'x' || ret[1] == 'X')) ret.erase(0, 2); } - if (!Hex::isValid(ret, strict)) throw std::runtime_error("Invalid Hex string at constructor"); + if (!Hex::isValid(ret, strict)) throw DynamicException("Invalid Hex string at constructor"); this->hex_ = std::move(ret); } @@ -28,7 +28,7 @@ Hex::Hex(std::string&& value, bool strict) : hex_(std::move(value)), strict_(str this->hex_.erase(0, 2); } } - if (!Hex::isValid(this->hex_, strict)) throw std::runtime_error("Invalid Hex string at constructor"); + if (!Hex::isValid(this->hex_, strict)) throw DynamicException("Invalid Hex string at constructor"); } bool Hex::isValid(const std::string_view hex, bool strict) { @@ -93,7 +93,7 @@ Bytes Hex::toBytes(const std::string_view hex) { const static std::string_view filter("0123456789abcdefABCDEF"); auto pos = hex.find_first_not_of(filter, i); if (pos != std::string::npos) { - throw std::runtime_error(std::string(__func__) + ": Invalid hex string: " + throw DynamicException(std::string(__func__) + ": Invalid hex string: " + std::string(hex) + " filter: " + std::string(filter) + " at pos: " + std::to_string(pos)); } if (hex.size() % 2) { diff --git a/src/utils/hex.h b/src/utils/hex.h index ff433bd9..67212118 100644 --- a/src/utils/hex.h +++ b/src/utils/hex.h @@ -16,8 +16,8 @@ See the LICENSE.txt file in the project root for more information. #include #include #include - #include +#include "dynamicexception.h" using Byte = uint8_t; using Bytes = std::vector; @@ -45,7 +45,7 @@ class Hex { * Move constructor. * @param value The hex string. * @param strict (optional) If `true`, includes "0x". Defaults to `false`. - * @throw std::runtime_error if hex string is invalid. + * @throw DynamicException if hex string is invalid. */ Hex(std::string&& value, bool strict = false); @@ -53,7 +53,7 @@ class Hex { * Copy constructor. * @param value The hex string. * @param strict (optional) If `true`, includes "0x". Defaults to `false`. - * @throw std::runtime_error if hex string is invalid. + * @throw DynamicException if hex string is invalid. */ Hex(const std::string_view value, bool strict = false); @@ -103,7 +103,7 @@ class Hex { * Static overload of bytes(). * @param hex The hex string to convert to bytes. * @return The converted bytes string. - * @throw std::runtime_error if hex string is invalid. + * @throw DynamicException if hex string is invalid. */ static Bytes toBytes(const std::string_view hex); @@ -173,7 +173,7 @@ class Hex { hex[0] == '0' && (hex[1] == 'x' || hex[1] == 'X') ) ? hex.substr(2) : hex; } else { - throw std::runtime_error("Invalid Hex concat operation"); + throw DynamicException("Invalid Hex concat operation"); } return *this; } diff --git a/src/utils/options.cpp b/src/utils/options.cpp index d2681857..c822ae41 100644 --- a/src/utils/options.cpp +++ b/src/utils/options.cpp @@ -204,7 +204,7 @@ Options Options::fromFile(const std::string& rootPath) { genesisValidators ); } catch (std::exception &e) { - throw std::runtime_error("Could not create blockchain directory: " + std::string(e.what())); + throw DynamicException("Could not create blockchain directory: " + std::string(e.what())); } } diff --git a/src/utils/options.h.in b/src/utils/options.h.in index 42d6840a..3b6eab53 100644 --- a/src/utils/options.h.in +++ b/src/utils/options.h.in @@ -266,7 +266,7 @@ class Options { * Defaults to this->binaryDefaultOptions() if no file is found. * @param rootPath Path to data root folder. * @return The constructed options object. - * @throw std::runtime_error on failure. + * @throw DynamicException on failure. */ static Options fromFile(const std::string& rootPath); diff --git a/src/utils/strings.h b/src/utils/strings.h index 640e9933..f76803d7 100644 --- a/src/utils/strings.h +++ b/src/utils/strings.h @@ -245,7 +245,7 @@ class Address : public FixedBytes<20> { * Copy constructor. * @param add The address itself. * @param inBytes If `true`, treats the input as a raw bytes string. - * @throw std::runtime_error if address has wrong size or is invalid. + * @throw DynamicException if address has wrong size or is invalid. */ Address(const std::string_view add, bool inBytes); diff --git a/src/utils/tx.cpp b/src/utils/tx.cpp index 76fd5484..5dce8352 100644 --- a/src/utils/tx.cpp +++ b/src/utils/tx.cpp @@ -12,10 +12,10 @@ TxBlock::TxBlock(const BytesArrView bytes, const uint64_t&) { const auto txData = bytes.subspan(1); // Check if Tx is type 2 - if (bytes[0] != 0x02) throw std::runtime_error("Tx is not type 2"); + if (bytes[0] != 0x02) throw DynamicException("Tx is not type 2"); // Check if first byte is equal or higher than 0xf7, meaning it is a list - if (txData[0] < 0xf7) throw std::runtime_error("Tx is not a list"); + if (txData[0] < 0xf7) throw DynamicException("Tx is not a list"); // Get list length uint8_t listLengthSize = txData[index] - 0xf7; @@ -27,9 +27,9 @@ TxBlock::TxBlock(const BytesArrView bytes, const uint64_t&) { // Size sanity check if (listLength < txData.size() - listLengthSize - 1) { - throw std::runtime_error("Tx RLP returns smaller size than reported"); + throw DynamicException("Tx RLP returns smaller size than reported"); } else if (listLength > txData.size() - listLengthSize - 1) { - throw std::runtime_error("Tx RLP returns larger size than reported"); + throw DynamicException("Tx RLP returns larger size than reported"); } // If chainId > 0, get chainId from string. @@ -37,7 +37,7 @@ TxBlock::TxBlock(const BytesArrView bytes, const uint64_t&) { const uint8_t chainIdLength = (txData[index] >= 0x80) ? txData[index] - 0x80 : 0; if (chainIdLength != 0) { - if (chainIdLength > 0x37) throw std::runtime_error("ChainId is too large"); + if (chainIdLength > 0x37) throw DynamicException("ChainId is too large"); index++; // Index at rlp[0] payload this->chainId_ = Utils::fromBigEndian( txData.subspan(index, chainIdLength) @@ -54,7 +54,7 @@ TxBlock::TxBlock(const BytesArrView bytes, const uint64_t&) { const uint8_t nonceLength = (txData[index] >= 0x80) ? txData[index] - 0x80 : 0; if (nonceLength != 0) { - if (nonceLength > 0x37) throw std::runtime_error("Nonce is too large"); + if (nonceLength > 0x37) throw DynamicException("Nonce is too large"); index++; // Index at rlp[1] payload this->nonce_ = Utils::fromBigEndian( txData.subspan(index, nonceLength) @@ -71,7 +71,7 @@ TxBlock::TxBlock(const BytesArrView bytes, const uint64_t&) { const uint8_t maxPriorityFeePerGasLength = txData[index] >= 0x80 ? txData[index] - 0x80 : 0; if (maxPriorityFeePerGasLength != 0) { - if (maxPriorityFeePerGasLength > 0x37) throw std::runtime_error("MaxPriorityFeePerGas is too large"); + if (maxPriorityFeePerGasLength > 0x37) throw DynamicException("MaxPriorityFeePerGas is too large"); index++; // Index at rlp[2] payload this->maxPriorityFeePerGas_ = Utils::fromBigEndian( txData.subspan(index, maxPriorityFeePerGasLength) @@ -88,7 +88,7 @@ TxBlock::TxBlock(const BytesArrView bytes, const uint64_t&) { const uint8_t maxFeePerGasLength = txData[index] >= 0x80 ? txData[index] - 0x80 : 0; if (maxFeePerGasLength != 0) { - if (maxFeePerGasLength > 0x37) throw std::runtime_error("MaxFeePerGas is too large"); + if (maxFeePerGasLength > 0x37) throw DynamicException("MaxFeePerGas is too large"); index++; // Index at rlp[3] payload this->maxFeePerGas_ = Utils::fromBigEndian( txData.subspan(index, maxFeePerGasLength) @@ -105,7 +105,7 @@ TxBlock::TxBlock(const BytesArrView bytes, const uint64_t&) { const uint8_t gasLimitLength = txData[index] >= 0x80 ? txData[index] - 0x80 : 0; if (gasLimitLength != 0) { - if (gasLimitLength > 0x37) throw std::runtime_error("GasLimit is too large"); + if (gasLimitLength > 0x37) throw DynamicException("GasLimit is too large"); index++; // Index at rlp[0] payload this->gasLimit_ = Utils::fromBigEndian( txData.subspan(index, gasLimitLength) @@ -119,7 +119,7 @@ TxBlock::TxBlock(const BytesArrView bytes, const uint64_t&) { // Get receiver addess (to) - small string. // We don't actually need to get the size, because to/from has a size of 20 - if (txData[index] != 0x94) throw std::runtime_error( + if (txData[index] != 0x94) throw DynamicException( "Receiver address (to) is not a 20 byte string (address)" ); index++; // Index at rlp[5] payload @@ -129,7 +129,7 @@ TxBlock::TxBlock(const BytesArrView bytes, const uint64_t&) { // Get value - small string or byte itself. uint8_t valueLength = txData[index] >= 0x80 ? txData[index] - 0x80 : 0; if (valueLength != 0) { - if (valueLength > 0x37) throw std::runtime_error("Value is not a small string"); + if (valueLength > 0x37) throw DynamicException("Value is not a small string"); index++; // Index at rlp[6] payload this->value_ = Utils::fromBigEndian( txData.subspan(index, valueLength) @@ -162,12 +162,12 @@ TxBlock::TxBlock(const BytesArrView bytes, const uint64_t&) { this->data_.assign(txData.begin() + index, txData.begin() + index + dataLength); index += dataLength; // Index at rlp[8] size } else { - throw std::runtime_error("Data is too large"); + throw DynamicException("Data is too large"); } // Get access list // ALWAYS 0xc0 (empty list) - if (txData[index] != 0xc0) throw std::runtime_error("Access list is not empty"); + if (txData[index] != 0xc0) throw DynamicException("Access list is not empty"); index++; // Index at rlp[9] size // Get v - always byte itself (1 byte) @@ -175,31 +175,31 @@ TxBlock::TxBlock(const BytesArrView bytes, const uint64_t&) { this->v_ = 0; } else { this->v_ = Utils::fromBigEndian(txData.subspan(index, 1)); - if (this->v_ > 0x01) throw std::runtime_error("V is not 0 or 1"); + if (this->v_ > 0x01) throw DynamicException("V is not 0 or 1"); } ++index; // Index at rlp[10] size // Get r - small string uint8_t rLength = txData[index] - 0x80; - if (rLength > 0x20) throw std::runtime_error("R is bigger than 32 bytes"); + if (rLength > 0x20) throw DynamicException("R is bigger than 32 bytes"); index++; // Index at rlp[10] payload this->r_ = Utils::fromBigEndian(txData.subspan(index, rLength)); index += rLength; // Index at rlp[11] size // Get s - small string uint8_t sLength = txData[index] - 0x80; - if (sLength > 0x20) throw std::runtime_error("S is bigger than 32 bytes"); + if (sLength > 0x20) throw DynamicException("S is bigger than 32 bytes"); index++; // Index at rlp[11] payload this->s_ = Utils::fromBigEndian(txData.subspan(index, sLength)); index += sLength; // Index at rlp[12] size if (!Secp256k1::verifySig(this->r_, this->s_, this->v_)) { - throw std::runtime_error("Invalid tx signature - doesn't fit elliptic curve verification"); + throw DynamicException("Invalid tx signature - doesn't fit elliptic curve verification"); } Signature sig = Secp256k1::makeSig(this->r_, this->s_, this->v_); Hash msgHash = Utils::sha3(this->rlpSerialize(false)); // Do not include signature UPubKey key = Secp256k1::recover(sig, msgHash); - if (!key) throw std::runtime_error("Invalid tx signature - cannot recover public key"); + if (!key) throw DynamicException("Invalid tx signature - cannot recover public key"); this->from_ = Secp256k1::toAddress(key); this->hash_ = Utils::sha3(this->rlpSerialize(true)); // Include signature @@ -214,7 +214,7 @@ TxBlock::TxBlock( { UPubKey pubKey = Secp256k1::toUPub(privKey); Address add = Secp256k1::toAddress(pubKey); - if (add != this->from_) throw std::runtime_error("Private key does not match sender address (from)"); + if (add != this->from_) throw DynamicException("Private key does not match sender address (from)"); Hash msgHash = Utils::sha3(this->rlpSerialize(false)); // Do not include signature Signature sig = Secp256k1::sign(msgHash, privKey); @@ -223,10 +223,10 @@ TxBlock::TxBlock( this->v_ = sig[64]; if (pubKey != Secp256k1::recover(sig, msgHash)) { - throw std::runtime_error("Invalid tx signature - derived key doesn't match public key"); + throw DynamicException("Invalid tx signature - derived key doesn't match public key"); } if (!Secp256k1::verifySig(this->r_, this->s_, this->v_)) { - throw std::runtime_error("Invalid tx signature - doesn't fit elliptic curve verification"); + throw DynamicException("Invalid tx signature - doesn't fit elliptic curve verification"); } this->hash_ = Utils::sha3(this->rlpSerialize(true)); // Include signature } @@ -402,7 +402,7 @@ TxValidator::TxValidator(const BytesArrView bytes, const uint64_t&) { uint64_t index = 0; // Check if first byte is equal or higher than 0xf7, meaning it is a list - if (bytes[0] < 0xf7) throw std::runtime_error("Tx is not a list"); + if (bytes[0] < 0xf7) throw DynamicException("Tx is not a list"); // Get list length uint8_t listLengthSize = bytes[index] - 0xf7; @@ -414,9 +414,9 @@ TxValidator::TxValidator(const BytesArrView bytes, const uint64_t&) { // Size sanity check if (listLength < bytes.size() - listLengthSize - 1) { - throw std::runtime_error("Tx RLP returns smaller size than reported"); + throw DynamicException("Tx RLP returns smaller size than reported"); } else if (listLength > bytes.size() - listLengthSize - 1) { - throw std::runtime_error("Tx RLP returns larger size than reported"); + throw DynamicException("Tx RLP returns larger size than reported"); } // Get data - it can be anything really, from nothing (0x80) to a big string (0xb7) @@ -458,7 +458,7 @@ TxValidator::TxValidator(const BytesArrView bytes, const uint64_t&) { // Get v - small string or the byte itself uint8_t vLength = (bytes[index] >= 0x80) ? bytes[index] - 0x80 : 0; if (vLength != 0) { - if (vLength > 0x37) throw std::runtime_error("V is not a small string"); + if (vLength > 0x37) throw DynamicException("V is not a small string"); index++; // Index at rlp[2] payload this->v_ = Utils::fromBigEndian( bytes.subspan(index, vLength) @@ -472,14 +472,14 @@ TxValidator::TxValidator(const BytesArrView bytes, const uint64_t&) { // Get r - small string uint8_t rLength = bytes[index] - 0x80; - if (rLength > 0x37) throw std::runtime_error("R is not a small string"); + if (rLength > 0x37) throw DynamicException("R is not a small string"); index++; // Index at rlp[3] payload this->r_ = Utils::fromBigEndian(bytes.subspan(index, rLength)); index += rLength; // Index at rlp[4] size // Get s - small string uint8_t sLength = bytes[index] - 0x80; - if (sLength > 0x37) throw std::runtime_error("S is not a small string"); + if (sLength > 0x37) throw DynamicException("S is not a small string"); index++; // Index at rlp[4] payload this->s_ = Utils::fromBigEndian(bytes.subspan(index, sLength)); index += sLength; // Index at rlp[5] size. rlp[5] doesn't exist on Validator txs @@ -488,22 +488,22 @@ TxValidator::TxValidator(const BytesArrView bytes, const uint64_t&) { if (this->v_ > 36) { this->chainId_ = static_cast((this->v_ - 35) / 2); if (this->chainId_ > std::numeric_limits::max()) { - throw std::runtime_error("chainId too high"); + throw DynamicException("chainId too high"); } } else if (this->v_ != 27 && this->v_ != 28) { - throw std::runtime_error("Invalid tx signature - v is not 27 or 28, v is " + throw DynamicException("Invalid tx signature - v is not 27 or 28, v is " + boost::lexical_cast(this->v_)); } // Get recoveryId, verify the signature and derive sender address (from) auto recoveryId = uint8_t{this->v_ - (uint256_t(this->chainId_) * 2 + 35)}; if (!Secp256k1::verifySig(this->r_, this->s_, recoveryId)) { - throw std::runtime_error("Invalid tx signature - doesn't fit elliptic curve verification"); + throw DynamicException("Invalid tx signature - doesn't fit elliptic curve verification"); } Signature sig = Secp256k1::makeSig(this->r_, this->s_, recoveryId); Hash msgHash = Utils::sha3(this->rlpSerialize(false)); // Do not include signature UPubKey key = Secp256k1::recover(sig, msgHash); - if (key == UPubKey()) throw std::runtime_error("Invalid tx signature - cannot recover public key"); + if (key == UPubKey()) throw DynamicException("Invalid tx signature - cannot recover public key"); this->from_ = Secp256k1::toAddress(key); this->hash_ = Utils::sha3(this->rlpSerialize(true)); // Include signature } @@ -515,7 +515,7 @@ TxValidator::TxValidator( UPubKey pubKey = Secp256k1::toUPub(privKey); Address add = Secp256k1::toAddress(pubKey); Hash msgHash = Utils::sha3(this->rlpSerialize(false)); // Do not include signature - if (add != this->from_) throw std::runtime_error("Private key does not match sender address (from)"); + if (add != this->from_) throw DynamicException("Private key does not match sender address (from)"); Signature sig = Secp256k1::sign(msgHash, privKey); this->r_ = Utils::bytesToUint256(sig.view_const(0, 32)); @@ -524,10 +524,10 @@ TxValidator::TxValidator( this->v_ = recoveryIds + (this->chainId_ * 2 + 35); if (!Secp256k1::verifySig(this->r_, this->s_, recoveryIds)) { - throw std::runtime_error("Invalid tx signature - doesn't fit elliptic curve verification"); + throw DynamicException("Invalid tx signature - doesn't fit elliptic curve verification"); } if (pubKey != Secp256k1::recover(sig, msgHash)) { - throw std::runtime_error("Invalid transaction signature, signature derived key doens't match public key"); + throw DynamicException("Invalid transaction signature, signature derived key doens't match public key"); } this->hash_ = Utils::sha3(this->rlpSerialize(true)); // Include signature } diff --git a/src/utils/tx.h b/src/utils/tx.h index 192ff798..8541f998 100644 --- a/src/utils/tx.h +++ b/src/utils/tx.h @@ -41,7 +41,7 @@ class TxBlock { * Raw constructor. * @param bytes The raw tx bytes to parse. * @param requiredChainId The chain ID of the transaction. - * @throw std::runtime_error on any parsing failure. + * @throw DynamicException on any parsing failure. */ TxBlock(const BytesArrView bytes, const uint64_t& requiredChainId); @@ -57,7 +57,7 @@ class TxBlock { * @param maxFeePerGas The maximum fee per gas of the transaction. * @param gasLimit The gas limit of the transaction. * @param privKey The private key used to sign the transaction. - * @throw std::runtime_error on signing failure or sender mismatch. + * @throw DynamicException on signing failure or sender mismatch. */ TxBlock( const Address& to, const Address& from, const Bytes& data, @@ -209,7 +209,7 @@ class TxValidator { * Raw constructor. * @param bytes The raw tx bytes to parse. * @param requiredChainId The chain ID of the transaction. - * @throw std::runtime_error on any parsing failure. + * @throw DynamicException on any parsing failure. */ TxValidator(const BytesArrView bytes, const uint64_t& requiredChainId); @@ -220,7 +220,7 @@ class TxValidator { * @param chainId The chain ID of the transaction. * @param nHeight The block height of the transaction. * @param privKey The private key used to sign the transaction. - * @throw std::runtime_error on signing failure or sender mismatch. + * @throw DynamicException on signing failure or sender mismatch. */ TxValidator( const Address& from, const Bytes& data, const uint64_t& chainId, diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp index 7ac526bf..34309540 100644 --- a/src/utils/utils.cpp +++ b/src/utils/utils.cpp @@ -322,7 +322,7 @@ BytesArr<32> Utils::uint256ToBytes(const uint256_t& i) { } uint256_t Utils::bytesToUint256(const BytesArrView b) { - if (b.size() != 32) throw std::runtime_error(std::string(__func__) + if (b.size() != 32) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 32, got " + std::to_string(b.size()) ); uint256_t ret; @@ -331,7 +331,7 @@ uint256_t Utils::bytesToUint256(const BytesArrView b) { } uint248_t Utils::bytesToUint248(const BytesArrView b) { - if (b.size() != 31) throw std::runtime_error(std::string(__func__) + if (b.size() != 31) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 31, got " + std::to_string(b.size()) ); uint248_t ret; @@ -340,7 +340,7 @@ uint248_t Utils::bytesToUint248(const BytesArrView b) { } uint240_t Utils::bytesToUint240(const BytesArrView b) { - if (b.size() != 30) throw std::runtime_error(std::string(__func__) + if (b.size() != 30) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 30, got " + std::to_string(b.size()) ); uint240_t ret; @@ -349,7 +349,7 @@ uint240_t Utils::bytesToUint240(const BytesArrView b) { } uint232_t Utils::bytesToUint232(const BytesArrView b) { - if (b.size() != 29) throw std::runtime_error(std::string(__func__) + if (b.size() != 29) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 29, got " + std::to_string(b.size()) ); uint232_t ret; @@ -358,7 +358,7 @@ uint232_t Utils::bytesToUint232(const BytesArrView b) { } uint224_t Utils::bytesToUint224(const BytesArrView b) { - if (b.size() != 28) throw std::runtime_error(std::string(__func__) + if (b.size() != 28) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 28, got " + std::to_string(b.size()) ); uint224_t ret; @@ -367,7 +367,7 @@ uint224_t Utils::bytesToUint224(const BytesArrView b) { } uint216_t Utils::bytesToUint216(const BytesArrView b) { - if (b.size() != 27) throw std::runtime_error(std::string(__func__) + if (b.size() != 27) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 27, got " + std::to_string(b.size()) ); uint216_t ret; @@ -376,7 +376,7 @@ uint216_t Utils::bytesToUint216(const BytesArrView b) { } uint208_t Utils::bytesToUint208(const BytesArrView b) { - if (b.size() != 26) throw std::runtime_error(std::string(__func__) + if (b.size() != 26) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 26, got " + std::to_string(b.size()) ); uint208_t ret; @@ -385,7 +385,7 @@ uint208_t Utils::bytesToUint208(const BytesArrView b) { } uint200_t Utils::bytesToUint200(const BytesArrView b) { - if (b.size() != 25) throw std::runtime_error(std::string(__func__) + if (b.size() != 25) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 25, got " + std::to_string(b.size()) ); uint200_t ret; @@ -394,7 +394,7 @@ uint200_t Utils::bytesToUint200(const BytesArrView b) { } uint192_t Utils::bytesToUint192(const BytesArrView b) { - if (b.size() != 24) throw std::runtime_error(std::string(__func__) + if (b.size() != 24) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 24, got " + std::to_string(b.size()) ); uint192_t ret; @@ -403,7 +403,7 @@ uint192_t Utils::bytesToUint192(const BytesArrView b) { } uint184_t Utils::bytesToUint184(const BytesArrView b) { - if (b.size() != 23) throw std::runtime_error(std::string(__func__) + if (b.size() != 23) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 23, got " + std::to_string(b.size()) ); uint184_t ret; @@ -412,7 +412,7 @@ uint184_t Utils::bytesToUint184(const BytesArrView b) { } uint176_t Utils::bytesToUint176(const BytesArrView b) { - if (b.size() != 22) throw std::runtime_error(std::string(__func__) + if (b.size() != 22) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 22, got " + std::to_string(b.size()) ); uint176_t ret; @@ -421,7 +421,7 @@ uint176_t Utils::bytesToUint176(const BytesArrView b) { } uint168_t Utils::bytesToUint168(const BytesArrView b) { - if (b.size() != 21) throw std::runtime_error(std::string(__func__) + if (b.size() != 21) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 21, got " + std::to_string(b.size()) ); uint168_t ret; @@ -430,7 +430,7 @@ uint168_t Utils::bytesToUint168(const BytesArrView b) { } uint160_t Utils::bytesToUint160(const BytesArrView b) { - if (b.size() != 20) throw std::runtime_error(std::string(__func__) + if (b.size() != 20) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 20, got " + std::to_string(b.size()) ); uint160_t ret; @@ -439,7 +439,7 @@ uint160_t Utils::bytesToUint160(const BytesArrView b) { } uint152_t Utils::bytesToUint152(const BytesArrView b) { - if (b.size() != 19) throw std::runtime_error(std::string(__func__) + if (b.size() != 19) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 19, got " + std::to_string(b.size()) ); uint152_t ret; @@ -448,7 +448,7 @@ uint152_t Utils::bytesToUint152(const BytesArrView b) { } uint144_t Utils::bytesToUint144(const BytesArrView b) { - if (b.size() != 18) throw std::runtime_error(std::string(__func__) + if (b.size() != 18) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 18, got " + std::to_string(b.size()) ); uint144_t ret; @@ -457,7 +457,7 @@ uint144_t Utils::bytesToUint144(const BytesArrView b) { } uint136_t Utils::bytesToUint136(const BytesArrView b) { - if (b.size() != 17) throw std::runtime_error(std::string(__func__) + if (b.size() != 17) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 17, got " + std::to_string(b.size()) ); uint136_t ret; @@ -466,7 +466,7 @@ uint136_t Utils::bytesToUint136(const BytesArrView b) { } uint128_t Utils::bytesToUint128(const BytesArrView b) { - if (b.size() != 16) throw std::runtime_error(std::string(__func__) + if (b.size() != 16) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 16, got " + std::to_string(b.size()) ); uint128_t ret; @@ -475,7 +475,7 @@ uint128_t Utils::bytesToUint128(const BytesArrView b) { } uint120_t Utils::bytesToUint120(const BytesArrView b) { - if (b.size() != 15) throw std::runtime_error(std::string(__func__) + if (b.size() != 15) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 15, got " + std::to_string(b.size()) ); uint120_t ret; @@ -484,7 +484,7 @@ uint120_t Utils::bytesToUint120(const BytesArrView b) { } uint112_t Utils::bytesToUint112(const BytesArrView b) { - if (b.size() != 14) throw std::runtime_error(std::string(__func__) + if (b.size() != 14) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 16, got " + std::to_string(b.size()) ); uint112_t ret; @@ -493,7 +493,7 @@ uint112_t Utils::bytesToUint112(const BytesArrView b) { } uint104_t Utils::bytesToUint104(const BytesArrView b) { - if (b.size() != 13) throw std::runtime_error(std::string(__func__) + if (b.size() != 13) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 13, got " + std::to_string(b.size()) ); uint104_t ret; @@ -502,7 +502,7 @@ uint104_t Utils::bytesToUint104(const BytesArrView b) { } uint96_t Utils::bytesToUint96(const BytesArrView b) { - if (b.size() != 12) throw std::runtime_error(std::string(__func__) + if (b.size() != 12) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 12, got " + std::to_string(b.size()) ); uint96_t ret; @@ -511,7 +511,7 @@ uint96_t Utils::bytesToUint96(const BytesArrView b) { } uint88_t Utils::bytesToUint88(const BytesArrView b) { - if (b.size() != 11) throw std::runtime_error(std::string(__func__) + if (b.size() != 11) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 11, got " + std::to_string(b.size()) ); uint88_t ret; @@ -520,7 +520,7 @@ uint88_t Utils::bytesToUint88(const BytesArrView b) { } uint80_t Utils::bytesToUint80(const BytesArrView b) { - if (b.size() != 10) throw std::runtime_error(std::string(__func__) + if (b.size() != 10) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 10, got " + std::to_string(b.size()) ); uint80_t ret; @@ -529,7 +529,7 @@ uint80_t Utils::bytesToUint80(const BytesArrView b) { } uint72_t Utils::bytesToUint72(const BytesArrView b) { - if (b.size() != 9) throw std::runtime_error(std::string(__func__) + if (b.size() != 9) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 9, got " + std::to_string(b.size()) ); uint72_t ret; @@ -538,7 +538,7 @@ uint72_t Utils::bytesToUint72(const BytesArrView b) { } uint56_t Utils::bytesToUint56(const BytesArrView b) { - if (b.size() != 7) throw std::runtime_error(std::string(__func__) + if (b.size() != 7) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 7, got " + std::to_string(b.size()) ); uint56_t ret; @@ -547,7 +547,7 @@ uint56_t Utils::bytesToUint56(const BytesArrView b) { } uint48_t Utils::bytesToUint48(const BytesArrView b) { - if (b.size() != 6) throw std::runtime_error(std::string(__func__) + if (b.size() != 6) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 6, got " + std::to_string(b.size()) ); uint48_t ret; @@ -556,7 +556,7 @@ uint48_t Utils::bytesToUint48(const BytesArrView b) { } uint40_t Utils::bytesToUint40(const BytesArrView b) { - if (b.size() != 5) throw std::runtime_error(std::string(__func__) + if (b.size() != 5) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 5, got " + std::to_string(b.size()) ); uint40_t ret; @@ -565,7 +565,7 @@ uint40_t Utils::bytesToUint40(const BytesArrView b) { } uint24_t Utils::bytesToUint24(const BytesArrView b) { - if (b.size() != 3) throw std::runtime_error(std::string(__func__) + if (b.size() != 3) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 3, got " + std::to_string(b.size()) ); uint24_t ret; @@ -583,7 +583,7 @@ BytesArr<8> Utils::uint64ToBytes(const uint64_t& i) { } uint64_t Utils::bytesToUint64(const BytesArrView b) { - if (b.size() != 8) throw std::runtime_error(std::string(__func__) + if (b.size() != 8) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 8, got " + std::to_string(b.size()) ); uint64_t ret = 0; @@ -604,7 +604,7 @@ BytesArr<4> Utils::uint32ToBytes(const uint32_t& i) { } uint32_t Utils::bytesToUint32(const BytesArrView b) { - if (b.size() != 4) throw std::runtime_error(std::string(__func__) + if (b.size() != 4) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 4, got " + std::to_string(b.size()) ); uint32_t ret = 0; @@ -625,7 +625,7 @@ BytesArr<2> Utils::uint16ToBytes(const uint16_t& i) { } uint16_t Utils::bytesToUint16(const BytesArrView b) { - if (b.size() != 2) throw std::runtime_error(std::string(__func__) + if (b.size() != 2) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 2, got " + std::to_string(b.size()) ); uint16_t ret = 0; @@ -643,7 +643,7 @@ BytesArr<1> Utils::uint8ToBytes(const uint8_t& i) { } uint8_t Utils::bytesToUint8(const BytesArrView b) { - if (b.size() != 1) throw std::runtime_error(std::string(__func__) + if (b.size() != 1) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 1, got " + std::to_string(b.size()) ); uint8_t ret; @@ -652,7 +652,7 @@ uint8_t Utils::bytesToUint8(const BytesArrView b) { } int256_t Utils::bytesToInt256(const BytesArrView b) { - if (b.size() != 32) throw std::runtime_error(std::string(__func__) + if (b.size() != 32) throw DynamicException(std::string(__func__) + ": Invalid bytes size - expected 32, got " + std::to_string(b.size()) ); uint256_t ret; diff --git a/src/utils/utils.h b/src/utils/utils.h index 5cd54e8f..40412ab6 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -680,7 +680,7 @@ namespace Utils { * Convert a bytes string to a 256-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 256-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint256_t bytesToUint256(const BytesArrView b); @@ -688,7 +688,7 @@ namespace Utils { * Convert a bytes string to a 248-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 248-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint248_t bytesToUint248(const BytesArrView b); @@ -696,7 +696,7 @@ namespace Utils { * Convert a bytes string to a 240-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 240-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint240_t bytesToUint240(const BytesArrView b); @@ -704,7 +704,7 @@ namespace Utils { * Convert a bytes string to a 232-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 232-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint232_t bytesToUint232(const BytesArrView b); @@ -712,7 +712,7 @@ namespace Utils { * Convert a bytes string to a 224-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 224-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint224_t bytesToUint224(const BytesArrView b); @@ -720,7 +720,7 @@ namespace Utils { * Convert a bytes string to a 216-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 216-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint216_t bytesToUint216(const BytesArrView b); @@ -728,7 +728,7 @@ namespace Utils { * Convert a bytes string to a 208-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 208-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint208_t bytesToUint208(const BytesArrView b); @@ -736,7 +736,7 @@ namespace Utils { * Convert a bytes string to a 200-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 200-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint200_t bytesToUint200(const BytesArrView b); @@ -744,7 +744,7 @@ namespace Utils { * Convert a bytes string to a 192-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 192-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint192_t bytesToUint192(const BytesArrView b); @@ -752,7 +752,7 @@ namespace Utils { * Convert a bytes string to a 184-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 184-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint184_t bytesToUint184(const BytesArrView b); @@ -760,7 +760,7 @@ namespace Utils { * Convert a bytes string to a 176-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 176-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint176_t bytesToUint176(const BytesArrView b); @@ -768,7 +768,7 @@ namespace Utils { * Convert a bytes string to a 168-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 168-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint168_t bytesToUint168(const BytesArrView b); @@ -776,7 +776,7 @@ namespace Utils { * Convert a bytes string to a 160-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 160-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint160_t bytesToUint160(const BytesArrView b); @@ -784,7 +784,7 @@ namespace Utils { * Convert a bytes string to a 152-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 152-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint152_t bytesToUint152(const BytesArrView b); @@ -792,7 +792,7 @@ namespace Utils { * Convert a bytes string to a 144-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 144-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint144_t bytesToUint144(const BytesArrView b); @@ -800,7 +800,7 @@ namespace Utils { * Convert a bytes string to a 136-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 136-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint136_t bytesToUint136(const BytesArrView b); @@ -808,7 +808,7 @@ namespace Utils { * Convert a bytes string to a 128-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 128-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint128_t bytesToUint128(const BytesArrView b); @@ -816,7 +816,7 @@ namespace Utils { * Convert a bytes string to a 120-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 120-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint120_t bytesToUint120(const BytesArrView b); @@ -824,7 +824,7 @@ namespace Utils { * Convert a bytes string to a 112-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 112-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint112_t bytesToUint112(const BytesArrView b); @@ -832,7 +832,7 @@ namespace Utils { * Convert a bytes string to a 104-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 104-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint104_t bytesToUint104(const BytesArrView b); @@ -840,7 +840,7 @@ namespace Utils { * Convert a bytes string to a 96-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 96-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint96_t bytesToUint96(const BytesArrView b); @@ -848,7 +848,7 @@ namespace Utils { * Convert a bytes string to a 88-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 88-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint88_t bytesToUint88(const BytesArrView b); @@ -856,7 +856,7 @@ namespace Utils { * Convert a bytes string to a 80-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 80-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint80_t bytesToUint80(const BytesArrView b); @@ -864,7 +864,7 @@ namespace Utils { * Convert a bytes string to a 72-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 72-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint72_t bytesToUint72(const BytesArrView b); @@ -872,7 +872,7 @@ namespace Utils { * Convert a bytes string to a 112-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 112-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint112_t bytesToUint112(const BytesArrView b); @@ -880,7 +880,7 @@ namespace Utils { * Convert a bytes string to a 64-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 64-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint64_t bytesToUint64(const BytesArrView b); @@ -888,7 +888,7 @@ namespace Utils { * Convert a bytes string to a 56-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 56-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint56_t bytesToUint56(const BytesArrView b); @@ -896,7 +896,7 @@ namespace Utils { * Convert a bytes string to a 48-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 48-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint48_t bytesToUint48(const BytesArrView b); @@ -905,7 +905,7 @@ namespace Utils { * Convert a bytes string to a 40-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 40-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint40_t bytesToUint40(const BytesArrView b); @@ -913,7 +913,7 @@ namespace Utils { * Convert a bytes string to a 32-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 32-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint32_t bytesToUint32(const BytesArrView b); @@ -921,7 +921,7 @@ namespace Utils { * Convert a bytes string to a 24-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 24-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint24_t bytesToUint24(const BytesArrView b); @@ -929,7 +929,7 @@ namespace Utils { * Convert a bytes string to a 16-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 16-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint16_t bytesToUint16(const BytesArrView b); @@ -937,7 +937,7 @@ namespace Utils { * Convert a bytes string to a 8-bit unsigned integer. * @param b The bytes string to convert. * @return The converted 8-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ uint8_t bytesToUint8(const BytesArrView b); @@ -945,7 +945,7 @@ namespace Utils { * Convert a bytes string to a 256-bit signed integer. * @param b The bytes string to convert. * @return The converted 256-bit integer. - * @throw std::runtime_error if string size is invalid. + * @throw DynamicException if string size is invalid. */ int256_t bytesToInt256(const BytesArrView b); @@ -1084,7 +1084,7 @@ namespace Utils { * @return The converted span. */ inline BytesArrMutableView create_span(Bytes& vec, size_t start, size_t size) { - if (start + size > vec.size()) throw std::runtime_error("Invalid range for span"); + if (start + size > vec.size()) throw DynamicException("Invalid range for span"); return BytesArrMutableView(vec.data() + start, size); } @@ -1105,7 +1105,7 @@ namespace Utils { * @return The converted span. */ inline BytesArrView create_view_span(const Bytes& vec, size_t start, size_t size) { - if (start + size > vec.size()) throw std::runtime_error("Invalid range for span"); + if (start + size > vec.size()) throw DynamicException("Invalid range for span"); return BytesArrView(vec.data() + start, size); } @@ -1128,7 +1128,7 @@ namespace Utils { template inline BytesArrMutableView create_span( BytesArr& arr, size_t start, size_t size ) { - if (start + size > arr.size()) throw std::runtime_error("Invalid range for span"); + if (start + size > arr.size()) throw DynamicException("Invalid range for span"); return BytesArrMutableView(arr.data() + start, size); } @@ -1151,7 +1151,7 @@ namespace Utils { template inline BytesArrView create_view_span( const BytesArr& arr, size_t start, size_t size ) { - if (start + size > arr.size()) throw std::runtime_error("Invalid range for span"); + if (start + size > arr.size()) throw DynamicException("Invalid range for span"); return BytesArrView(arr.data() + start, size); } @@ -1173,7 +1173,7 @@ namespace Utils { */ inline BytesArrView create_view_span(const std::string_view str, size_t start, size_t size) { if (start + size > str.size()) { - throw std::runtime_error("Invalid range for span"); + throw DynamicException("Invalid range for span"); } return BytesArrView(reinterpret_cast(str.data()) + start, size); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3779b23f..ded41112 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -18,6 +18,7 @@ set (TESTS_SOURCES ${CMAKE_SOURCE_DIR}/tests/utils/tx_throw.cpp ${CMAKE_SOURCE_DIR}/tests/utils/utils.cpp ${CMAKE_SOURCE_DIR}/tests/utils/options.cpp + ${CMAKE_SOURCE_DIR}/tests/utils/dynamicexception.cpp ${CMAKE_SOURCE_DIR}/tests/contract/abi.cpp ${CMAKE_SOURCE_DIR}/tests/contract/erc20.cpp ${CMAKE_SOURCE_DIR}/tests/contract/contractmanager.cpp diff --git a/tests/contract/contractmanager.cpp b/tests/contract/contractmanager.cpp index 60e8da3e..b3f1b73c 100644 --- a/tests/contract/contractmanager.cpp +++ b/tests/contract/contractmanager.cpp @@ -11,6 +11,7 @@ See the LICENSE.txt file in the project root for more information. #include "../../src/core/rdpos.h" #include "../../src/utils/db.h" #include "../../src/utils/options.h" +#include "../../src/utils/dynamicexception.h" #include #include @@ -299,6 +300,40 @@ namespace TContractManager { REQUIRE(std::get<0>(decB) == 0); REQUIRE(std::get<0>(decC) == 0); } + + SECTION("ContractManager ethCall() throws") { + if (std::filesystem::exists(testDumpPath + "/ContractManagerTestCreateNew")) { + std::filesystem::remove_all(testDumpPath + "/ContractManagerTestCreateNew"); + } + if (!std::filesystem::exists(testDumpPath)) { // Ensure the testdump folder actually exists + std::filesystem::create_directories(testDumpPath); + } + + PrivKey privKey(Hex::toBytes("0xe89ef6409c467285bcae9f80ab1cfeb3487cfe61ab28fb7d36443e1daa0c2867")); + Address owner = Secp256k1::toAddress(Secp256k1::toUPub(privKey)); + + { + std::unique_ptr options = std::make_unique(Options::fromFile(testDumpPath + "/ContractManagerTestCreateNew")); + std::unique_ptr db = std::make_unique(testDumpPath + "/ContractManagerTestCreateNew"); + std::unique_ptr rdpos; + ethCallInfo callInfo; + ContractManager contractManager(db, nullptr, rdpos, options); + + try { + contractManager.ethCall(callInfo); + FAIL("Expected DynamicException was not thrown."); // This line will fail the test if no exception is thrown + } catch (const DynamicException& e) { + // Check that the exception message matches the expected message + std::string expectedMessage = "Invalid function call with functor: 00000000"; + REQUIRE(std::string(e.what()) == expectedMessage); + } catch (...) { + FAIL("An unexpected exception type was thrown."); + } + + } + + } + } } diff --git a/tests/sdktestsuite.hpp b/tests/sdktestsuite.hpp index 8f5ce2fb..b6f62fdc 100644 --- a/tests/sdktestsuite.hpp +++ b/tests/sdktestsuite.hpp @@ -232,7 +232,7 @@ class SDKTestSuite { } // After finalization, the block should be valid. If it is, process the next one. - if (!this->state_->validateNextBlock(newBlock)) throw std::runtime_error( + if (!this->state_->validateNextBlock(newBlock)) throw DynamicException( "SDKTestSuite::advanceBlock: Block is not valid" ); state_->processNextBlock(std::move(newBlock)); @@ -291,7 +291,7 @@ class SDKTestSuite { * Get a transaction from the chain using a given hash. * @param tx The transaction hash to get. * @return A tuple with the found transaction, block hash, index and height, nullptr if the tx was not found - * @throw std::runtime_error on hash mismatch (should never happen). + * @throw DynamicException on hash mismatch (should never happen). */ const std::tuple< const std::shared_ptr, const Hash, const uint64_t, const uint64_t @@ -850,7 +850,7 @@ class SDKTestSuite { tuples.push_back(tuple); } } else { - throw std::runtime_error("Attempted to decode an event with only indexed parameters (empty tuple)."); + throw DynamicException("Attempted to decode an event with only indexed parameters (empty tuple)."); } return tuples; diff --git a/tests/utils/dynamicexception.cpp b/tests/utils/dynamicexception.cpp new file mode 100644 index 00000000..471244c2 --- /dev/null +++ b/tests/utils/dynamicexception.cpp @@ -0,0 +1,141 @@ +#include "../../src/libs/catch2/catch_amalgamated.hpp" +#include "../../src/utils/dynamicexception.h" +#include + +namespace TDynamicException { + + class CustomObject { + public: + int value; + CustomObject(int v) : value(v) {} + friend std::ostream& operator<<(std::ostream& os, const CustomObject& obj) { + return os << "CustomObject(value=" << obj.value << ")"; + } + }; + + TEST_CASE("DynamicException Class", "[utils][dynamicexception]") { + SECTION("Exception message is set and retrieved correctly") { + const std::string filename = "test.cpp"; + const int line = 42; + const std::string function = "testFunction"; + const std::string message = "Test message"; + DynamicException exception("Function " + function + " failed: " + message + " at " + filename + ":", line); + + try { + throw exception; + } catch (const DynamicException& e) { + REQUIRE(e.what() == "Function " + function + " failed: " + message + " at " + filename + ":" + std::to_string(line)); + REQUIRE(e.getFile() == ""); + REQUIRE(e.getLine() == 0); + REQUIRE(e.getFunction() == ""); + } + } + + SECTION("Exception with file, line, and function information") { + const std::string filename = "test.cpp"; + const int line = 42; + const std::string function = "testFunction"; + const std::string message = "Error in file " + filename + " at line " + std::to_string(line) + " in function " + function; + DynamicException exception(message, filename, line, function); + + try { + throw exception; + } catch (const DynamicException& e) { + REQUIRE(e.what() == message); + REQUIRE(e.getFile() == filename); + REQUIRE(e.getLine() == line); + REQUIRE(e.getFunction() == function); + } + } + + + SECTION("Timestamp is correctly formatted") { + const std::string message = "Error with timestamp"; + DynamicException exception(message); + + try { + throw exception; + } catch (const DynamicException& e) { + std::string timestamp = e.getTimestamp(); + REQUIRE(!timestamp.empty()); + REQUIRE(timestamp.length() == 19); // Checking the length of the timestamp + REQUIRE(timestamp[4] == '-'); + REQUIRE(timestamp[7] == '-'); + REQUIRE(timestamp[10] == ' '); + REQUIRE(timestamp[13] == ':'); + REQUIRE(timestamp[16] == ':'); + } + } + + SECTION("Exception with single message") { + const std::string message = "Error with single string message"; + DynamicException exception(message); + + try { + throw exception; + } catch (const DynamicException& e) { + REQUIRE(e.what() == message); + REQUIRE(e.getFile() == ""); + REQUIRE(e.getLine() == 0); + REQUIRE(e.getFunction() == ""); + } + } + + SECTION("Exception with multiple messages") { + int a = 5; + int b = 10; + const std::string message = "Error with multiple messages: " + std::to_string(a) + " and " + std::to_string(b); + DynamicException exception("Error with multiple messages: ", a, " and ", b); + + try { + throw exception; + } catch (const DynamicException& e) { + REQUIRE(e.what() == message); + REQUIRE(e.getFile() == ""); + REQUIRE(e.getLine() == 0); + REQUIRE(e.getFunction() == ""); + } + } + + SECTION("Exception with various basic types") { + int intValue = 42; + double doubleValue = 3.14; + const std::string message = "Error occurred with values: "; + DynamicException exception(message, intValue, " and ", doubleValue); + + try { + throw exception; + } catch (const DynamicException& e) { + REQUIRE_THAT(e.what(), Catch::Matchers::Equals("Error occurred with values: 42 and 3.14")); + } + } + + SECTION("Exception with strings and literals") { + const std::string part1 = "Error: "; + const char* part2 = "Invalid operation"; + DynamicException exception(part1, part2); + + try { + throw exception; + } catch (const DynamicException& e) { + REQUIRE(e.what() == std::string("Error: Invalid operation")); + } + } + + + SECTION("Exception with custom objects") { + CustomObject obj(100); + DynamicException exception("Encountered an issue with ", obj); + + try { + throw exception; + } catch (const DynamicException& e) { + REQUIRE_THAT(e.what(), Catch::Matchers::Equals("Encountered an issue with CustomObject(value=100)")); + } + } + + + + + } +}