diff --git a/src/contract/abi.h b/src/contract/abi.h
index 245fd509..5a2030de 100644
--- a/src/contract/abi.h
+++ b/src/contract/abi.h
@@ -11,7 +11,7 @@ See the LICENSE.txt file in the project root for more information.
#include "../utils/intconv.h"
#include "../utils/uintconv.h"
#include "../utils/strconv.h"
-#include "../utils/utils.h" // libs/json.hpp -> string
+#include "../utils/utils.h" // FunctionTypes, libs/json.hpp -> string
/// Namespace for Solidity ABI-related operations.
namespace ABI {
diff --git a/src/contract/contractmanager.cpp b/src/contract/contractmanager.cpp
index a2c7e43b..8cbf1081 100644
--- a/src/contract/contractmanager.cpp
+++ b/src/contract/contractmanager.cpp
@@ -70,16 +70,15 @@ void ContractManager::ethCall(const evmc_message& callInfo, ContractHost* host)
PointerNullifier nullifier(this->host_);
const Address caller(callInfo.sender);
const Functor functor = EVMCConv::getFunctor(callInfo);
- /// Call the function on this->createContractFuncs_
+ // Call the function on this->createContractFuncs_
auto it = this->createContractFuncs_.find(functor);
if (it == this->createContractFuncs_.end()) {
throw DynamicException("ContractManager: Invalid function call");
}
it->second(callInfo,
- ContractHost::deriveContractAddress(this->host_->getNonce(caller), caller),
- this->contracts_,
- this->getContractChainId(),
- this->host_);
+ ContractHost::deriveContractAddress(this->host_->getNonce(caller), caller),
+ this->contracts_, this->getContractChainId(), this->host_
+ );
}
Bytes ContractManager::ethCallView(const evmc_message& callInfo, ContractHost* host) const {
diff --git a/src/contract/contractmanager.h b/src/contract/contractmanager.h
index 9232c976..92904b79 100644
--- a/src/contract/contractmanager.h
+++ b/src/contract/contractmanager.h
@@ -27,16 +27,16 @@ class ContractManager : public BaseContract {
/// Functions to create contracts.
boost::unordered_flat_map<
- Functor,
- std::function<
- void(const evmc_message&,
- const Address&,
- boost::unordered_flat_map
, SafeHash>& contracts_,
- const uint64_t&,
- ContractHost*
- )>,
- SafeHash
- > createContractFuncs_;
+ Functor,
+ std::function, SafeHash>& contracts_,
+ const uint64_t&,
+ ContractHost*
+ )>,
+ SafeHash
+ > createContractFuncs_;
/**
* Get all deployed contracts.
diff --git a/src/contract/templates/dexv2/dexv2pair.cpp b/src/contract/templates/dexv2/dexv2pair.cpp
index ffc2f519..1fb9b04f 100644
--- a/src/contract/templates/dexv2/dexv2pair.cpp
+++ b/src/contract/templates/dexv2/dexv2pair.cpp
@@ -16,15 +16,38 @@ DEXV2Pair::DEXV2Pair(const Address& address, const DB& db
reserve0_(this), reserve1_(this), blockTimestampLast_(this),
price0CumulativeLast_(this), price1CumulativeLast_(this), kLast_(this)
{
- this->factory_ = Address(db.get(std::string("factory_"), this->getDBPrefix()));
- this->token0_ = Address(db.get(std::string("token0_"), this->getDBPrefix()));
- this->token1_ = Address(db.get(std::string("token1_"), this->getDBPrefix()));
- this->reserve0_ = UintConv::bytesToUint112(db.get(std::string("reserve0_"), this->getDBPrefix()));
- this->reserve1_ = UintConv::bytesToUint112(db.get(std::string("reserve1_"), this->getDBPrefix()));
- this->blockTimestampLast_ = UintConv::bytesToUint32(db.get(std::string("blockTimestampLast_"), this->getDBPrefix()));
- this->price0CumulativeLast_ = UintConv::bytesToUint256(db.get(std::string("price0CumulativeLast_"), this->getDBPrefix()));
- this->price1CumulativeLast_ = UintConv::bytesToUint256(db.get(std::string("price1CumulativeLast_"), this->getDBPrefix()));
- this->kLast_ = UintConv::bytesToUint256(db.get(std::string("kLast_"), this->getDBPrefix()));
+ Hex prefix = Hex::fromBytes(this->getDBPrefix());
+ Utils::safePrintTest("Loading contract DEXV2Pair from DB with prefix: " + prefix.get());
+ auto factoryBytes = db.get(std::string("factory_"), this->getDBPrefix());
+ auto token0Bytes = db.get(std::string("token0_"), this->getDBPrefix());
+ auto token1Bytes = db.get(std::string("token1_"), this->getDBPrefix());
+ auto reserve0Bytes = db.get(std::string("reserve0_"), this->getDBPrefix());
+ auto reserve1Bytes = db.get(std::string("reserve1_"), this->getDBPrefix());
+ auto blockTimestampLastBytes = db.get(std::string("blockTimestampLast_"), this->getDBPrefix());
+ auto price0CumulativeLastBytes = db.get(std::string("price0CumulativeLast_"), this->getDBPrefix());
+ auto price1CumulativeLastBytes = db.get(std::string("price1CumulativeLast_"), this->getDBPrefix());
+ auto kLastBytes = db.get(std::string("kLast_"), this->getDBPrefix());
+
+ Utils::safePrintTest("Factory Bytes: " + Hex::fromBytes(factoryBytes).get());
+ Utils::safePrintTest("Token0 Bytes: " + Hex::fromBytes(token0Bytes).get());
+ Utils::safePrintTest("Token1 Bytes: " + Hex::fromBytes(token1Bytes).get());
+ Utils::safePrintTest("Reserve0 Bytes: " + Hex::fromBytes(reserve0Bytes).get());
+ Utils::safePrintTest("Reserve1 Bytes: " + Hex::fromBytes(reserve1Bytes).get());
+ Utils::safePrintTest("BlockTimestampLast Bytes: " + Hex::fromBytes(blockTimestampLastBytes).get());
+ Utils::safePrintTest("Price0CumulativeLast Bytes: " + Hex::fromBytes(price0CumulativeLastBytes).get());
+ Utils::safePrintTest("Price1CumulativeLast Bytes: " + Hex::fromBytes(price1CumulativeLastBytes).get());
+ Utils::safePrintTest("KLast Bytes: " + Hex::fromBytes(kLastBytes).get());
+
+
+ this->factory_ = Address(factoryBytes);
+ this->token0_ = Address(token0Bytes);
+ this->token1_ = Address(token1Bytes);
+ this->reserve0_ = UintConv::bytesToUint112(reserve0Bytes);
+ this->reserve1_ = UintConv::bytesToUint112(reserve1Bytes);
+ this->blockTimestampLast_ = UintConv::bytesToUint32(blockTimestampLastBytes);
+ this->price0CumulativeLast_ = UintConv::bytesToUint256(price0CumulativeLastBytes);
+ this->price1CumulativeLast_ = UintConv::bytesToUint256(price1CumulativeLastBytes);
+ this->kLast_ = UintConv::bytesToUint256(kLastBytes);
this->factory_.commit();
this->token0_.commit();
@@ -47,6 +70,17 @@ DEXV2Pair::DEXV2Pair(const Address& address, const DB& db
this->price0CumulativeLast_.enableRegister();
this->price1CumulativeLast_.enableRegister();
this->kLast_.enableRegister();
+ Utils::safePrintTest("Loaded from DB DEXV2Pair contract");
+ Utils::safePrintTest("Factory: " + this->factory_.get().hex().get());
+ Utils::safePrintTest("Token0: " + this->token0_.get().hex().get());
+ Utils::safePrintTest("Token1: " + this->token1_.get().hex().get());
+ Utils::safePrintTest("Reserve0: " + this->reserve0_.get().str());
+ Utils::safePrintTest("Reserve1: " + this->reserve1_.get().str());
+ Utils::safePrintTest("BlockTimestampLast: " + std::to_string(this->blockTimestampLast_.get()));
+ Utils::safePrintTest("Price0CumulativeLast: " + this->price0CumulativeLast_.get().str());
+ Utils::safePrintTest("Price1CumulativeLast: " + this->price1CumulativeLast_.get().str());
+ Utils::safePrintTest("KLast: " + this->kLast_.get().str());
+
}
DEXV2Pair::DEXV2Pair(
@@ -55,7 +89,14 @@ DEXV2Pair::DEXV2Pair(
factory_(this), token0_(this), token1_(this), reserve0_(this), reserve1_(this),
blockTimestampLast_(this), price0CumulativeLast_(this), price1CumulativeLast_(this), kLast_(this)
{
+ // Explicitly initialize numbers to 0 to avoid junk values on DB load
this->factory_ = creator;
+ this->reserve0_ = 0;
+ this->reserve1_ = 0;
+ this->blockTimestampLast_ = 0;
+ this->price0CumulativeLast_ = 0;
+ this->price1CumulativeLast_ = 0;
+ this->kLast_ = 0;
this->factory_.commit();
this->token0_.commit();
@@ -78,6 +119,16 @@ DEXV2Pair::DEXV2Pair(
this->price0CumulativeLast_.enableRegister();
this->price1CumulativeLast_.enableRegister();
this->kLast_.enableRegister();
+ Utils::safePrintTest("Creating new DEXV2Pair contract");
+ Utils::safePrintTest("Factory: " + this->factory_.get().hex().get());
+ Utils::safePrintTest("Token0: " + this->token0_.get().hex().get());
+ Utils::safePrintTest("Token1: " + this->token1_.get().hex().get());
+ Utils::safePrintTest("Reserve0: " + this->reserve0_.get().str());
+ Utils::safePrintTest("Reserve1: " + this->reserve1_.get().str());
+ Utils::safePrintTest("BlockTimestampLast: " + std::to_string(this->blockTimestampLast_.get()));
+ Utils::safePrintTest("Price0CumulativeLast: " + this->price0CumulativeLast_.get().str());
+ Utils::safePrintTest("Price1CumulativeLast: " + this->price1CumulativeLast_.get().str());
+ Utils::safePrintTest("KLast: " + this->kLast_.get().str());
}
DEXV2Pair::~DEXV2Pair() {};
@@ -141,6 +192,8 @@ void DEXV2Pair::initialize(const Address& token0, const Address& token1) {
if (this->factory_ != this->getCaller()) throw DynamicException("DEXV2Pair: FORBIDDEN");
this->token0_ = token0;
this->token1_ = token1;
+ Utils::safePrintTest("Initialized DEXV2Pair contract with token0: " + token0.hex().get() + " and token1: " + token1.hex().get());
+ Utils::safePrintTest("Initialized Reserves 0: " + this->reserve0_.get().str() + " and Reserves 1: " + this->reserve1_.get().str());
}
std::pair DEXV2Pair::getReservess() const {
@@ -256,16 +309,67 @@ void DEXV2Pair::sync() {
DBBatch DEXV2Pair::dump() const {
+ // We have to dump the tokens as well
DBBatch dbBatch = BaseContract::dump();
+ DBBatch erc20Batch = ERC20::dump();
+ for (const auto& dbItem : erc20Batch.getPuts()) dbBatch.push_back(dbItem);
+ for (const auto& dbItem : erc20Batch.getDels()) dbBatch.delete_key(dbItem);
+
+ Utils::safePrintTest("Dumping DEXV2Pair to DB");
+ uint112_t zeroUint = 0;
+ uint112_t oneUint = 1;
+ auto zeroUintBytes = UintConv::uint112ToBytes(zeroUint);
+ auto oneUintBytes = UintConv::uint112ToBytes(oneUint);
+ Utils::safePrintTest("Zero Uint: " + zeroUint.str());
+ Utils::safePrintTest("One Uint: " + oneUint.str());
+ Utils::safePrintTest("Zero Uint Bytes: " + Hex::fromBytes(zeroUintBytes).get());
+ Utils::safePrintTest("One Uint Bytes: " + Hex::fromBytes(oneUintBytes).get());
+
+ Bytes zeroUintRawBytes;
+ zeroUintRawBytes.reserve(14);
+ Bytes oneUintRawBytes;
+ oneUintRawBytes.reserve(14);
+ boost::multiprecision::export_bits(zeroUint, std::back_inserter(zeroUintRawBytes), 8);
+ boost::multiprecision::export_bits(oneUint, std::back_inserter(oneUintRawBytes), 8);
+ Utils::safePrintTest("Zero Uint Raw Bytes: " + Hex::fromBytes(zeroUintRawBytes).get());
+ Utils::safePrintTest("One Uint Raw Bytes: " + Hex::fromBytes(oneUintRawBytes).get());
+
+ Utils::safePrintTest("Factory: " + this->factory_.get().hex().get());
+ Utils::safePrintTest("Token0: " + this->token0_.get().hex().get());
+ Utils::safePrintTest("Token1: " + this->token1_.get().hex().get());
+ Utils::safePrintTest("Reserve0 Before: " + this->reserve0_.get().str());
+ Utils::safePrintTest("Reserve1 Before: " + this->reserve1_.get().str());
+ auto reserve0Bytes = UintConv::uint112ToBytes(this->reserve0_.get());
+ auto reserve1Bytes = UintConv::uint112ToBytes(this->reserve1_.get());
+ Utils::safePrintTest("Reserve0 After: " + this->reserve0_.get().str());
+ Utils::safePrintTest("Reserve1 After: " + this->reserve1_.get().str());
+ Utils::safePrintTest("reserve0 Bytes: " + Hex::fromBytes(reserve0Bytes).get());
+ Utils::safePrintTest("reserve1 Bytes: " + Hex::fromBytes(reserve1Bytes).get());
+
+ Utils::safePrintTest("BlockTimestampLast: " + std::to_string(this->blockTimestampLast_.get()));
+ Utils::safePrintTest("Price0CumulativeLast: " + this->price0CumulativeLast_.get().str());
+ Utils::safePrintTest("Price1CumulativeLast: " + this->price1CumulativeLast_.get().str());
+ Utils::safePrintTest("KLast: " + this->kLast_.get().str());
+
dbBatch.push_back(StrConv::stringToBytes("factory_"), this->factory_.get().view(), this->getDBPrefix());
dbBatch.push_back(StrConv::stringToBytes("token0_"), this->token0_.get().view(), this->getDBPrefix());
dbBatch.push_back(StrConv::stringToBytes("token1_"), this->token1_.get().view(), this->getDBPrefix());
- dbBatch.push_back(StrConv::stringToBytes("reserve0_"), UintConv::uint112ToBytes(this->reserve0_.get()), this->getDBPrefix());
- dbBatch.push_back(StrConv::stringToBytes("reserve1_"), UintConv::uint112ToBytes(this->reserve1_.get()), this->getDBPrefix());
+ dbBatch.push_back(StrConv::stringToBytes("reserve0_"), reserve0Bytes, this->getDBPrefix());
+ dbBatch.push_back(StrConv::stringToBytes("reserve1_"), reserve1Bytes, this->getDBPrefix());
dbBatch.push_back(StrConv::stringToBytes("blockTimestampLast_"), UintConv::uint32ToBytes(this->blockTimestampLast_.get()), this->getDBPrefix());
dbBatch.push_back(StrConv::stringToBytes("price0CumulativeLast_"), UintConv::uint256ToBytes(this->price0CumulativeLast_.get()), this->getDBPrefix());
dbBatch.push_back(StrConv::stringToBytes("price1CumulativeLast_"), UintConv::uint256ToBytes(this->price1CumulativeLast_.get()), this->getDBPrefix());
dbBatch.push_back(StrConv::stringToBytes("kLast_"), UintConv::uint256ToBytes(this->kLast_.get()), this->getDBPrefix());
+ const auto& puts = dbBatch.getPuts();
+ for (const auto& dbItem : puts) {
+ // Take of the prefix based on this->getDBPrefix().size() from the dbItem.key
+ Bytes prefixBytes(dbItem.key.begin(), dbItem.key.begin() + this->getDBPrefix().size());
+ Hex prefix = Hex::fromBytes(prefixBytes);
+ // Then take dbItem.key + this->getDBPrefix().size() to the end
+ std::string key(dbItem.key.begin() + this->getDBPrefix().size(), dbItem.key.end());
+ Hex value = Hex::fromBytes(dbItem.value);
+ Utils::safePrintTest("Dumping to DB with prefix : " + prefix.get() + " key: " + key + " value: " + value.get());
+ }
return dbBatch;
}
diff --git a/src/contract/templates/erc721test.cpp b/src/contract/templates/erc721test.cpp
index ab40343e..7a0d8c85 100644
--- a/src/contract/templates/erc721test.cpp
+++ b/src/contract/templates/erc721test.cpp
@@ -33,7 +33,7 @@ ERC721Test::ERC721Test(const Address &address, const DB& db)
ERC721Test::ERC721Test(
const std::string &erc721name, const std::string &erc721symbol, const uint64_t& maxTokens,
const Address &address, const Address &creator, const uint64_t &chainId
-) : DynamicContract(erc721name, address, creator, chainId),
+) : DynamicContract("ERC721Test", address, creator, chainId),
ERC721(erc721name, erc721symbol, address, creator, chainId),
tokenIdCounter_(this, 0), maxTokens_(this, maxTokens), totalSupply_(this, 0)
{
diff --git a/src/contract/templates/erc721uristorage.cpp b/src/contract/templates/erc721uristorage.cpp
index 9098dd55..1f7a1b7f 100644
--- a/src/contract/templates/erc721uristorage.cpp
+++ b/src/contract/templates/erc721uristorage.cpp
@@ -13,7 +13,6 @@ ERC721URIStorage::ERC721URIStorage(const Address& address, const DB& db)
: DynamicContract(address, db),
ERC721(address, db),
_tokenURIs(this) {
-
for (const auto& dbEntry : db.getBatch(this->getNewPrefix("tokenURIs_"))) {
this->_tokenURIs[Utils::fromBigEndian(dbEntry.key)] = StrConv::bytesToString(dbEntry.value);
}
@@ -36,13 +35,11 @@ ERC721URIStorage::ERC721URIStorage(
) : DynamicContract(derivedTypeName, address, creator, chainId),
ERC721(derivedTypeName, erc721_name, erc721_symbol, address, creator, chainId),
_tokenURIs(this) {
-
ERC721URIStorage::registerContractFunctions();
}
DBBatch ERC721URIStorage::dump() const {
DBBatch batchedOperations = ERC721::dump();
-
for (auto it = this->_tokenURIs.cbegin(); it != this->_tokenURIs.cend(); ++it) {
batchedOperations.push_back(
Utils::uintToBytes(it->first),
@@ -50,7 +47,6 @@ DBBatch ERC721URIStorage::dump() const {
this->getNewPrefix("tokenURIs_")
);
}
-
return batchedOperations;
}
diff --git a/src/contract/templates/erc721uristorage.h b/src/contract/templates/erc721uristorage.h
index d527ba59..585611be 100644
--- a/src/contract/templates/erc721uristorage.h
+++ b/src/contract/templates/erc721uristorage.h
@@ -1,9 +1,14 @@
+/*
+ Copyright (c) [2023-2024] [AppLayer Developers]
+ This software is distributed under the MIT License.
+ See the LICENSE.txt file in the project root for more information.
+*/
+
#ifndef ERC721URISTORAGE_H
#define ERC721URISTORAGE_H
#include "erc721.h"
-
/// Template for an ERC721URIStorage contract.
/// Roughly based on the OpenZeppelin implementation.
class ERC721URIStorage : virtual public ERC721 {
@@ -33,21 +38,17 @@ class ERC721URIStorage : virtual public ERC721 {
void registerContractFunctions() override;
public:
-
/**
* ConstructorArguments is a tuple of the contract constructor arguments in the order they appear in the constructor.
*/
using ConstructorArguments = std::tuple;
-
/**
* Constructor for loading contract from DB.
* @param address The address where the contract will be deployed.
* @param db Reference to the database object.
*/
- ERC721URIStorage(
- const Address& address, const DB& db
- );
+ ERC721URIStorage(const Address& address, const DB& db);
/**
* Constructor to be used when creating a new contract.
diff --git a/src/contract/templates/nativewrapper.cpp b/src/contract/templates/nativewrapper.cpp
index 9827befd..b71e9a23 100644
--- a/src/contract/templates/nativewrapper.cpp
+++ b/src/contract/templates/nativewrapper.cpp
@@ -27,7 +27,12 @@ NativeWrapper::NativeWrapper(
NativeWrapper::~NativeWrapper() = default;
DBBatch NativeWrapper::dump() const {
- return BaseContract::dump();
+ // We need to dump all the data from the parent class as well
+ DBBatch batch = ERC20::dump();
+ DBBatch baseDump = BaseContract::dump();
+ for (const auto& dbItem : baseDump.getPuts()) batch.push_back(dbItem);
+ for (const auto& dbItem : baseDump.getDels()) batch.delete_key(dbItem);
+ return batch;
}
void NativeWrapper::registerContractFunctions() {
diff --git a/src/contract/templates/pebble.cpp b/src/contract/templates/pebble.cpp
index 1b2ff47d..5f5555d4 100644
--- a/src/contract/templates/pebble.cpp
+++ b/src/contract/templates/pebble.cpp
@@ -132,7 +132,7 @@ DBBatch Pebble::dump() const {
for (auto it = this->minters_.cbegin(); it != this->minters_.cend(); ++it) {
batch.push_back(it->first.asBytes(), UintConv::uint8ToBytes(static_cast(it->second)), this->getNewPrefix("minters_"));
}
- batch.push_back(StrConv::stringToBytes("authorized_"), this->authorizer_.get().asBytes(), this->getDBPrefix());
+ batch.push_back(StrConv::stringToBytes("authorizer_"), this->authorizer_.get().asBytes(), this->getDBPrefix());
return batch;
}
diff --git a/src/contract/templates/throwtestA.cpp b/src/contract/templates/throwtestA.cpp
index a12f4d81..aafb8159 100644
--- a/src/contract/templates/throwtestA.cpp
+++ b/src/contract/templates/throwtestA.cpp
@@ -8,24 +8,26 @@ See the LICENSE.txt file in the project root for more information.
#include "throwtestA.h"
ThrowTestA::ThrowTestA(
- const Address& address, const Address& creator,
- const uint64_t& chainId
-) : DynamicContract("ThrowTestA", address, creator, chainId) {
+ const Address& address, const Address& creator, const uint64_t& chainId
+) : DynamicContract("ThrowTestA", address, creator, chainId), num_(this) {
+ this->num_.commit();
registerContractFunctions();
+ this->num_.enableRegister();
}
-ThrowTestA::ThrowTestA(
- const Address& address,
- const DB& db
-) : DynamicContract(address, db) {
+ThrowTestA::ThrowTestA(const Address& address, const DB& db) : DynamicContract(address, db) {
+ this->num_ = UintConv::bytesToUint8(db.get(std::string("num_"), this->getDBPrefix()));
+ this->num_.commit();
registerContractFunctions();
+ this->num_.enableRegister();
}
ThrowTestA::~ThrowTestA() { return; }
-DBBatch ThrowTestA::dump() const
-{
- return BaseContract::dump();
+DBBatch ThrowTestA::dump() const {
+ DBBatch dbBatch = BaseContract::dump();
+ dbBatch.push_back(StrConv::stringToBytes("num_"), UintConv::uint8ToBytes(this->num_.get()), this->getDBPrefix());
+ return dbBatch;
}
uint8_t ThrowTestA::getNumA() const { return this->num_.get(); }
diff --git a/src/contract/templates/throwtestB.cpp b/src/contract/templates/throwtestB.cpp
index 489aee40..486f2da3 100644
--- a/src/contract/templates/throwtestB.cpp
+++ b/src/contract/templates/throwtestB.cpp
@@ -8,24 +8,26 @@ See the LICENSE.txt file in the project root for more information.
#include "throwtestB.h"
ThrowTestB::ThrowTestB(
- const Address& address, const Address& creator,
- const uint64_t& chainId
-) : DynamicContract("ThrowTestB", address, creator, chainId) {
+ const Address& address, const Address& creator, const uint64_t& chainId
+) : DynamicContract("ThrowTestB", address, creator, chainId), num_(this) {
+ this->num_.commit();
registerContractFunctions();
+ this->num_.enableRegister();
}
-ThrowTestB::ThrowTestB(
- const Address& address,
- const DB& db
-) : DynamicContract(address, db) {
+ThrowTestB::ThrowTestB(const Address& address, const DB& db) : DynamicContract(address, db) {
+ this->num_ = UintConv::bytesToUint8(db.get(std::string("num_"), this->getDBPrefix()));
+ this->num_.commit();
registerContractFunctions();
+ this->num_.enableRegister();
}
ThrowTestB::~ThrowTestB() { return; }
-DBBatch ThrowTestB::dump() const
-{
- return BaseContract::dump();
+DBBatch ThrowTestB::dump() const {
+ DBBatch dbBatch = BaseContract::dump();
+ dbBatch.push_back(StrConv::stringToBytes("num_"), UintConv::uint8ToBytes(this->num_.get()), this->getDBPrefix());
+ return dbBatch;
}
uint8_t ThrowTestB::getNumB() const { return this->num_.get(); }
diff --git a/src/contract/templates/throwtestC.cpp b/src/contract/templates/throwtestC.cpp
index 3a0b73eb..4a446295 100644
--- a/src/contract/templates/throwtestC.cpp
+++ b/src/contract/templates/throwtestC.cpp
@@ -8,24 +8,26 @@ See the LICENSE.txt file in the project root for more information.
#include "throwtestC.h"
ThrowTestC::ThrowTestC(
- const Address& address, const Address& creator,
- const uint64_t& chainId
-) : DynamicContract("ThrowTestC", address, creator, chainId) {
+ const Address& address, const Address& creator, const uint64_t& chainId
+) : DynamicContract("ThrowTestC", address, creator, chainId), num_(this) {
+ this->num_.commit();
registerContractFunctions();
+ this->num_.enableRegister();
}
-ThrowTestC::ThrowTestC(
- const Address& address,
- const DB& db
-) : DynamicContract(address, db) {
+ThrowTestC::ThrowTestC(const Address& address, const DB& db) : DynamicContract(address, db) {
+ this->num_ = UintConv::bytesToUint8(db.get(std::string("num_"), this->getDBPrefix()));
+ this->num_.commit();
registerContractFunctions();
+ this->num_.enableRegister();
}
ThrowTestC::~ThrowTestC() { return; }
-DBBatch ThrowTestC::dump() const
-{
- return BaseContract::dump();
+DBBatch ThrowTestC::dump() const {
+ DBBatch dbBatch = BaseContract::dump();
+ dbBatch.push_back(StrConv::stringToBytes("num_"), UintConv::uint8ToBytes(this->num_.get()), this->getDBPrefix());
+ return dbBatch;
}
uint8_t ThrowTestC::getNumC() const { return this->num_.get(); }
diff --git a/src/contract/variables/safeunorderedmap.h b/src/contract/variables/safeunorderedmap.h
index 6128afa1..296a985d 100644
--- a/src/contract/variables/safeunorderedmap.h
+++ b/src/contract/variables/safeunorderedmap.h
@@ -243,7 +243,7 @@ template class SafeUnorderedMap : public SafeBase {
*/
template void insert(InputIt first, InputIt last) {
// On this insert, we copy everything because we cannot check the insert
- // return to see what keys were insertted.
+ // return to see what keys were inserted.
for (auto it = first; it != last; ++it) {
auto valueIt = this->value_.find(it->first);
if (valueIt != this->value_.end()) {
@@ -483,7 +483,7 @@ template class SafeUnorderedMap : public SafeBase {
}
/**
- * Erase a value from the map.
+ * Erase a value from the map. Segfaults if value is already non-existant.
* @param pos The position of the value to erase.
* @return An iterator to the next value.
*/
@@ -492,14 +492,12 @@ template class SafeUnorderedMap : public SafeBase {
) {
if (auto itValue = this->value_.find((*pos).first); itValue != this->value_.end()) {
this->copy_.try_emplace((*itValue).first, std::in_place, (*itValue).second);
- } else {
- this->copy_.try_emplace((*itValue).first, std::nullopt);
}
- markAsUsed(); return this->value_.erase(pos);
+ markAsUsed(); return this->value_.erase(pos); // segfaults if itValue == end() (doesn't exist), seems to be intended behaviour from std
}
/**
- * Erase a range of values from the map.
+ * Erase a range of values from the map. Segfaults if any of the values are already non-existant.
* @param first The first position to erase.
* @param last The last position to erase.
* @return An iterator to the next value.
@@ -509,26 +507,21 @@ template class SafeUnorderedMap : public SafeBase {
typename boost::unordered_flat_map::const_iterator last
) {
for (auto it = first; it != last; ++it) {
- auto itValue = this->value_.find((*it).first);
- if (itValue != this->value_.end()) {
+ if (auto itValue = this->value_.find((*it).first); itValue != this->value_.end()) {
this->copy_.try_emplace((*itValue).first, std::in_place, (*itValue).second);
- } else {
- this->copy_.try_emplace((*itValue).first, std::nullopt);
}
}
markAsUsed(); return this->value_.erase(first, last);
}
/**
- * Erase a value from the map, using a key.
+ * Erase a value from the map, using a key. Does nothing if the key is already non-existant.
* @param key The key of the value to erase.
* @return The number of values erased.
*/
typename boost::unordered_flat_map::size_type erase(const Key& key) {
if (auto itValue = this->value_.find(key); itValue != this->value_.end()) {
this->copy_.try_emplace((*itValue).first, std::in_place, (*itValue).second);
- } else {
- this->copy_.try_emplace((*itValue).first, std::nullopt);
}
markAsUsed(); return this->value_.erase(key);
}
diff --git a/src/core/state.cpp b/src/core/state.cpp
index bab09491..2d53a602 100644
--- a/src/core/state.cpp
+++ b/src/core/state.cpp
@@ -51,7 +51,6 @@ State::State(
}
auto latestBlock = this->storage_.latest();
-
// Insert the contract manager into the contracts_ map.
this->contracts_[ProtocolContractAddresses.at("ContractManager")] = std::make_unique(
db, this->contracts_, this->dumpManager_ ,this->options_
diff --git a/src/core/storage.cpp b/src/core/storage.cpp
index 14e72150..903aa2d8 100644
--- a/src/core/storage.cpp
+++ b/src/core/storage.cpp
@@ -10,7 +10,7 @@ See the LICENSE.txt file in the project root for more information.
#include "../utils/strconv.h"
#include "../utils/uintconv.h"
-static bool topicsMatch(const Event& event, const std::vector& topics) {
+bool Storage::topicsMatch(const Event& event, const std::vector& topics) {
if (topics.empty()) return true; // No topic filter applied
const std::vector& eventTopics = event.getTopics();
if (eventTopics.size() < topics.size()) return false;
@@ -18,7 +18,7 @@ static bool topicsMatch(const Event& event, const std::vector& topics) {
return true;
}
-static void storeBlock(DB& db, const FinalizedBlock& block, bool indexingEnabled) {
+void Storage::storeBlock(DB& db, const FinalizedBlock& block, bool indexingEnabled) {
DBBatch batch;
batch.push_back(block.getHash(), block.serializeBlock(), DBPrefix::blocks);
batch.push_back(UintConv::uint64ToBytes(block.getNHeight()), block.getHash(), DBPrefix::heightToBlock);
diff --git a/src/core/storage.h b/src/core/storage.h
index 24c1748f..deaebda5 100644
--- a/src/core/storage.h
+++ b/src/core/storage.h
@@ -183,6 +183,22 @@ class Storage : public Log::LogicalLocationProvider {
* @returns The indexing mode of the storage.
*/
inline IndexingMode getIndexingMode() const { return options_.getIndexingMode(); }
+
+ /**
+ * Helper function for checking if an event has certain topics.
+ * @param event The event to check.
+ * @param topics A list of topics to check for.
+ * @return `true` if all topics match (or if no topics were provided), `false` otherwise.
+ */
+ static bool topicsMatch(const Event& event, const std::vector& topics);
+
+ /**
+ * Helper function for storing a block in the database.
+ * @param db Reference to the database.
+ * @param block The block to store.
+ * @param indexingEnabled Whether the node has indexing enabled or not.
+ */
+ static void storeBlock(DB& db, const FinalizedBlock& block, bool indexingEnabled);
};
#endif // STORAGE_H
diff --git a/src/net/http/jsonrpc/parser.cpp b/src/net/http/jsonrpc/parser.cpp
index 639da96f..1b911e2b 100644
--- a/src/net/http/jsonrpc/parser.cpp
+++ b/src/net/http/jsonrpc/parser.cpp
@@ -12,70 +12,43 @@ static inline const std::regex addressFormat{"^0x[0-9,a-f,A-F]{40}$"};
static inline const std::regex numberFormat{"^0x([1-9A-Fa-f]+[0-9A-Fa-f]*|0)$"};
namespace jsonrpc {
-
-Hash Parser::operator()(const json& data) const {
- if (!data.is_string())
- throw Error::invalidType("string", data.type_name());
-
- std::string rawData = data.get();
-
- if (!std::regex_match(rawData, hashFormat))
- throw Error::invalidFormat(rawData);
-
- return Hash(Hex::toBytes(rawData));
-}
-
-Address Parser::operator()(const json& data) const {
- if (!data.is_string())
- throw Error::invalidType("string", data.type_name());
-
- std::string rawData = data.get();
-
- if (!std::regex_match(rawData, addressFormat))
- throw Error::invalidFormat(rawData);
-
- return Address(Hex::toBytes(rawData));
-}
-
-Bytes Parser::operator()(const json& data) const {
- if (!data.is_string())
- throw Error::invalidType("string", data.type_name());
-
- std::string rawData = data.get();
-
- if (!Hex::isValid(rawData, true))
- throw Error::invalidFormat(rawData);
-
- return Hex::toBytes(rawData);
-}
-
-bool Parser::operator()(const json& data) const {
- if (!data.is_boolean())
- throw Error::invalidType("boolean", data.type_name());
-
- return data.get();
-}
-
-float Parser::operator()(const json& data) const {
- if (!data.is_number())
- throw Error::invalidType("number", data.type_name());
-
- return data.get();
-}
-
-uint64_t Parser::operator()(const json& data) const {
- if (data.is_number_unsigned())
- return data.get();
-
- if (!data.is_string())
- throw Error::invalidType("string", data.type_name());
-
- auto value = data.get();
-
- if (!std::regex_match(value, numberFormat))
- throw Error::invalidFormat(value);
-
- return uint64_t(Hex(value).getUint());
-}
-
+ Hash Parser::operator()(const json& data) const {
+ if (!data.is_string()) throw Error::invalidType("string", data.type_name());
+ std::string rawData = data.get();
+ if (!std::regex_match(rawData, hashFormat)) throw Error::invalidFormat(rawData);
+ return Hash(Hex::toBytes(rawData));
+ }
+
+ Address Parser::operator()(const json& data) const {
+ if (!data.is_string()) throw Error::invalidType("string", data.type_name());
+ std::string rawData = data.get();
+ if (!std::regex_match(rawData, addressFormat)) throw Error::invalidFormat(rawData);
+ return Address(Hex::toBytes(rawData));
+ }
+
+ Bytes Parser::operator()(const json& data) const {
+ if (!data.is_string()) throw Error::invalidType("string", data.type_name());
+ std::string rawData = data.get();
+ if (!Hex::isValid(rawData, true)) throw Error::invalidFormat(rawData);
+ return Hex::toBytes(rawData);
+ }
+
+ bool Parser::operator()(const json& data) const {
+ if (!data.is_boolean()) throw Error::invalidType("boolean", data.type_name());
+ return data.get();
+ }
+
+ float Parser::operator()(const json& data) const {
+ if (!data.is_number()) throw Error::invalidType("number", data.type_name());
+ return data.get();
+ }
+
+ uint64_t Parser::operator()(const json& data) const {
+ if (data.is_number_unsigned()) return data.get();
+ if (!data.is_string()) throw Error::invalidType("string", data.type_name());
+ auto value = data.get();
+ if (!std::regex_match(value, numberFormat)) throw Error::invalidFormat(value);
+ return uint64_t(Hex(value).getUint());
+ }
} // namespace jsonrpc
+
diff --git a/src/net/http/jsonrpc/parser.h b/src/net/http/jsonrpc/parser.h
index b060162f..e0a0c2a4 100644
--- a/src/net/http/jsonrpc/parser.h
+++ b/src/net/http/jsonrpc/parser.h
@@ -105,16 +105,19 @@ namespace jsonrpc {
template struct Parser> {
/**
* Parse the JSON data as one of the containers.
- * @param data The JSON object to be parsed.
- * @return The containter with the variant type.
+ * @param data The JSON object to be parsed. Can be either an array (e.g.
+ * "[1,2,3]") or an object (e.g. "{{"a", 1}, {"b", 2}, {"c", 3}}").
+ * Object keys are ignored altogether.
+ * @return The container with the variant type.
*/
std::vector operator()(const json& data) const {
std::vector res;
if (data.is_array()) {
res.reserve(data.size());
- std::ranges::transform(data, std::back_inserter(res), [] (const json& elem) { return Parser{}(elem); });
+ std::ranges::transform(data, std::back_inserter(res), [](const json& elem){ return Parser{}(elem); });
} else if (data.is_object()) {
- res.emplace_back(Parser{}(data));
+ res.reserve(data.size());
+ for (auto& el : data.items()) res.emplace_back(Parser{}(el.value()));
} else {
throw Error::invalidType("array or object", data.type_name());
}
diff --git a/src/net/p2p/encoding.cpp b/src/net/p2p/encoding.cpp
index b43cd959..349fdd9b 100644
--- a/src/net/p2p/encoding.cpp
+++ b/src/net/p2p/encoding.cpp
@@ -21,27 +21,27 @@ namespace P2P {
size_t index = 0;
while (index < data.size()) {
boost::asio::ip::address address;
- if (data.size() - index < 2) { throw DynamicException("Invalid data size."); }
+ if (data.size() - index < 2) { throw DynamicException("Invalid data size (message too small)"); }
auto nodeType = NodeType(UintConv::bytesToUint8(data.subspan(index, 1)));
index += 1;
uint8_t ipVersion = UintConv::bytesToUint8(data.subspan(index, 1));
index += 1; // Move index to IP address
if (ipVersion == 0) { // V4
- if (data.size() - index < 4) { throw DynamicException("Invalid data size."); }
+ if (data.size() - index < 4) { throw DynamicException("Invalid data size (IPv4 too small)"); }
BytesArr<4> ipBytes;
std::copy(data.begin() + index, data.begin() + index + 4, ipBytes.begin());
address = boost::asio::ip::address_v4(ipBytes);
index += 4;
} else if (ipVersion == 1) { // V6
- if (data.size() - index < 16) { throw DynamicException("Invalid data size."); }
+ if (data.size() - index < 16) { throw DynamicException("Invalid data size (IPv6 too small)"); }
BytesArr<16> ipBytes;
std::copy(data.begin() + index, data.begin() + index + 16, ipBytes.begin());
address = boost::asio::ip::address_v6(ipBytes);
index += 16;
} else {
- throw DynamicException("Invalid ip version.");
+ throw DynamicException("Invalid IP version (not v4 or v6)");
}
- if (data.size() - index < 2) { throw DynamicException("Invalid data size."); }
+ if (data.size() - index < 2) { throw DynamicException("Invalid data size (missing port)"); }
auto port = UintConv::bytesToUint16(data.subspan(index, 2));
nodes.insert({NodeID(address, port), nodeType});
index += 2;
@@ -98,10 +98,10 @@ namespace P2P {
std::vector blocks;
size_t index = 0;
while (index < data.size()) {
- if (data.size() - index < 8) { throw DynamicException("Invalid data size."); }
+ if (data.size() - index < 8) { throw DynamicException("Invalid data size (data too small)"); }
uint64_t blockSize = UintConv::bytesToUint64(data.subspan(index, 8));
index += 8;
- if (data.size() - index < blockSize) { throw DynamicException("Invalid data size."); }
+ if (data.size() - index < blockSize) { throw DynamicException("Invalid data size (block too small)"); }
bytes::View blockData = data.subspan(index, blockSize);
index += blockSize;
blocks.emplace_back(FinalizedBlock::fromBytes(blockData, requiredChainId));
@@ -129,10 +129,10 @@ namespace P2P {
std::vector txs;
size_t index = 0;
while (index < data.size()) {
- if (data.size() - index < 4) { throw DynamicException("Invalid data size."); }
+ if (data.size() - index < 4) { throw DynamicException("Invalid data size (msg too small)"); }
uint32_t txSize = UintConv::bytesToUint32(data.subspan(index, 4));
index += 4;
- if (data.size() - index < txSize) { throw DynamicException("Invalid data size."); }
+ if (data.size() - index < txSize) { throw DynamicException("Invalid data size (tx too small)"); }
bytes::View txData = data.subspan(index, txSize);
index += txSize;
// Assuming requiredChainId is declared elsewhere
diff --git a/src/utils/db.cpp b/src/utils/db.cpp
index 0f84ad6d..109f3611 100644
--- a/src/utils/db.cpp
+++ b/src/utils/db.cpp
@@ -92,9 +92,7 @@ Bytes DB::getLastByPrefix(const Bytes& pfx) const {
bool overflow;
int i = static_cast(nextPfx.size()) - 1;
// increment the given prefix by 1
- do {
- overflow = (++nextPfx[i--]) == 0;
- } while (overflow && i >= 0);
+ do { overflow = (++nextPfx[i--]) == 0; } while (overflow && i >= 0);
if (overflow) [[unlikely]] {
it->SeekToLast();
@@ -102,13 +100,10 @@ Bytes DB::getLastByPrefix(const Bytes& pfx) const {
it->SeekForPrev(rocksdb::Slice(reinterpret_cast(nextPfx.data()), nextPfx.size()));
}
- if (!it->Valid()) {
- return {};
- }
+ if (!it->Valid()) return {};
- if (!it->key().starts_with(rocksdb::Slice(reinterpret_cast(pfx.data()), pfx.size()))) {
- return {};
- }
+ if (!it->key().starts_with(rocksdb::Slice(reinterpret_cast(pfx.data()), pfx.size()))) return {};
return Bytes(it->value().data(), it->value().data() + it->value().size());
}
+
diff --git a/src/utils/db.h b/src/utils/db.h
index 61d00dff..399f65b7 100644
--- a/src/utils/db.h
+++ b/src/utils/db.h
@@ -264,7 +264,7 @@ class DB {
/**
* Get all entries from a given prefix.
* @param bytesPfx The prefix to search for.
- * @param keys (optional) A list of keys to search for. Defaults to an empty list.
+ * @param keys (optional) A list of keys to search for, WITHOUT the prefixes. Defaults to an empty list.
* @return A list of found entries.
*/
std::vector getBatch(
@@ -277,8 +277,8 @@ class DB {
* (e.g. a query that returns millions of entries at once).
* Prefix is automatically added to the queries themselves internally.
* @param pfx The prefix to search keys from.
- * @param start (optional) The first key to start searching from. Defaults to none.
- * @param end (optional) The last key to end searching at. Defaults to none.
+ * @param start (optional) The first key to start searching from, WITHOUT the prefix. Defaults to none.
+ * @param end (optional) The last key to end searching at, WITHOUT the prefix. Defaults to none.
* @return A list of found keys, WITHOUT their prefixes.
*/
std::vector getKeys(const Bytes& pfx, const Bytes& start = {}, const Bytes& end = {}) const;
diff --git a/src/utils/intconv.cpp b/src/utils/intconv.cpp
index 24b517a9..798afc21 100644
--- a/src/utils/intconv.cpp
+++ b/src/utils/intconv.cpp
@@ -88,8 +88,8 @@ int256_t IntConv::bytesToInt256(const bytes::View b) {
}
int136_t IntConv::bytesToInt136(const bytes::View b) {
- if (b.size() != 18) throw DynamicException(std::string(__func__)
- + ": Invalid bytes size - expected 18, got " + std::to_string(b.size())
+ if (b.size() != 17) throw DynamicException(std::string(__func__)
+ + ": Invalid bytes size - expected 17, got " + std::to_string(b.size())
);
int136_t ret;
boost::multiprecision::import_bits(ret, b.begin(), b.end(), 8);
diff --git a/src/utils/jsonabi.cpp b/src/utils/jsonabi.cpp
index 0ba8262e..aee36975 100644
--- a/src/utils/jsonabi.cpp
+++ b/src/utils/jsonabi.cpp
@@ -8,7 +8,7 @@ See the LICENSE.txt file in the project root for more information.
#include "jsonabi.h"
bool JsonAbi::isTuple(const std::string& type) {
- // Tuples are always as "(type1, type2, ...)" (alternatively, "(type1,type2,...)[]").
+ // Tuples are always as "(type1,type2,...)" (alternatively, "(type1,type2,...)[]").
// Check if first and last characters are "()" (not counting the "[]" at the end, if there are any).
return (JsonAbi::isArray(type))
? (type[0] == '(' && type[type.size() - 2 * JsonAbi::countTupleArrays(type) - 1] == ')')
@@ -49,7 +49,7 @@ std::vector JsonAbi::getTupleTypes(const std::string& type) {
types.push_back(tmp);
tmp = "";
} else {
- tmp += c;
+ if (c != ' ') tmp += c; // Prevent e.g. "(int, double)" second type coming out as " double" with the space in it
}
}
// Push the last type and return.
diff --git a/src/utils/jsonabi.h b/src/utils/jsonabi.h
index a26e758a..598c0961 100644
--- a/src/utils/jsonabi.h
+++ b/src/utils/jsonabi.h
@@ -57,7 +57,7 @@ namespace JsonAbi {
/**
* Parse a given method output to a JSON object.
- * @param outputDesc The output description of the method (std::pair).
+ * @param outputDesc The output description of the method (std::string).
* Be aware that tuple types are concatenated into the string itself.
* @return A JSON object containing the outputs of the method.
*/
diff --git a/src/utils/options.cpp b/src/utils/options.cpp
index b9f07d75..532f1dce 100644
--- a/src/utils/options.cpp
+++ b/src/utils/options.cpp
@@ -23,13 +23,6 @@ IndexingMode::IndexingMode(std::string_view mode) {
}
}
-constexpr std::string_view IndexingMode::toString() const {
- if (*this == DISABLED) return "DISABLED";
- if (*this == RPC) return "RPC";
- if (*this == RPC_TRACE) return "RPC_TRACE";
- throw DynamicException("Unknown indexing mode");
-}
-
Options::Options(
const std::string& rootPath, const std::string& web3clientVersion,
const uint64_t& version, const uint64_t& chainID, const Address& chainOwner,
diff --git a/src/utils/options.h.in b/src/utils/options.h.in
index 36682253..daf0cc74 100644
--- a/src/utils/options.h.in
+++ b/src/utils/options.h.in
@@ -75,12 +75,13 @@ class IndexingMode {
*/
explicit IndexingMode(std::string_view mode);
- /**
- * Convert the internal value back to its string form.
- * @return The setting as a string.
- * @throw DynamicException if value doesn't match one of the values above.
- */
- constexpr std::string_view toString() const;
+ /// Convert the internal value back to its string form.
+ constexpr std::string_view toString() const {
+ if (*this == DISABLED) return "DISABLED";
+ if (*this == RPC) return "RPC";
+ if (*this == RPC_TRACE) return "RPC_TRACE";
+ std::unreachable();
+ }
/// Equality operator (default, as the internal value is just an int).
constexpr bool operator==(const IndexingMode&) const = default;
diff --git a/src/utils/strings.cpp b/src/utils/strings.cpp
index ddc4f864..f4dda96b 100644
--- a/src/utils/strings.cpp
+++ b/src/utils/strings.cpp
@@ -10,11 +10,6 @@ See the LICENSE.txt file in the project root for more information.
Hash::Hash(const uint256_t& data) : FixedBytes<32>(UintConv::uint256ToBytes(data)) {};
-Hash::Hash(const std::string_view sv) {
- if (sv.size() != 32) throw std::invalid_argument("Hash must be 32 bytes long.");
- std::ranges::copy(sv, this->begin());
-}
-
Hash::Hash(const evmc::bytes32& data) {
// Copy the data from the evmc::bytes32 struct to this->data_
std::copy(data.bytes, data.bytes + 32, this->begin());
diff --git a/src/utils/strings.h b/src/utils/strings.h
index e6a73bd8..c8557dea 100644
--- a/src/utils/strings.h
+++ b/src/utils/strings.h
@@ -174,12 +174,6 @@ class Hash : public FixedBytes<32> {
*/
Hash(const evmc::bytes32& data);
- /**
- * Constructor using string_view.
- * @param sv The string view to convert into a hash string.
- */
- Hash(const std::string_view sv);
-
uint256_t toUint256() const; ///< Convert the hash string back to an unsigned 256-bit number.
evmc::bytes32 toEvmcBytes32() const; ///< Convert the hash string back to an evmc::bytes32 pointer.
diff --git a/src/utils/tx.cpp b/src/utils/tx.cpp
index ef3d318a..34030882 100644
--- a/src/utils/tx.cpp
+++ b/src/utils/tx.cpp
@@ -474,10 +474,7 @@ evmc_message TxBlock::txToMessage() const {
// evmc_address code_address;
// };
evmc_message msg;
- if (this->to_ == Address())
- msg.kind = EVMC_CREATE;
- else
- msg.kind = EVMC_CALL;
+ msg.kind = (this->to_ == Address()) ? EVMC_CREATE : EVMC_CALL;
msg.flags = 0;
msg.depth = 1;
msg.gas = static_cast(this->gasLimit_);
diff --git a/src/utils/uintconv.cpp b/src/utils/uintconv.cpp
index 713abb52..ad2a0f41 100644
--- a/src/utils/uintconv.cpp
+++ b/src/utils/uintconv.cpp
@@ -178,7 +178,7 @@ BytesArr<15> UintConv::uint120ToBytes(const uint120_t &i) {
}
BytesArr<14> UintConv::uint112ToBytes(const uint112_t &i) {
- BytesArr<14> ret;
+ BytesArr<14> ret = {0};
Bytes tmp;
tmp.reserve(14);
boost::multiprecision::export_bits(i, std::back_inserter(tmp), 8);
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index fb55fd84..036dd100 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -12,21 +12,26 @@ set (TESTS_SOURCES
${CMAKE_SOURCE_DIR}/tests/utils/block_throw.cpp
${CMAKE_SOURCE_DIR}/tests/utils/db.cpp
${CMAKE_SOURCE_DIR}/tests/utils/ecdsa.cpp
+ ${CMAKE_SOURCE_DIR}/tests/utils/evmcconv.cpp
${CMAKE_SOURCE_DIR}/tests/utils/hex.cpp
+ ${CMAKE_SOURCE_DIR}/tests/utils/jsonabi.cpp
${CMAKE_SOURCE_DIR}/tests/utils/merkle.cpp
${CMAKE_SOURCE_DIR}/tests/utils/randomgen.cpp
${CMAKE_SOURCE_DIR}/tests/utils/strings.cpp
${CMAKE_SOURCE_DIR}/tests/utils/tx.cpp
${CMAKE_SOURCE_DIR}/tests/utils/tx_throw.cpp
${CMAKE_SOURCE_DIR}/tests/utils/utils.cpp
+ ${CMAKE_SOURCE_DIR}/tests/utils/safehash.cpp
${CMAKE_SOURCE_DIR}/tests/utils/options.cpp
${CMAKE_SOURCE_DIR}/tests/utils/dynamicexception.cpp
${CMAKE_SOURCE_DIR}/tests/utils/strconv.cpp
${CMAKE_SOURCE_DIR}/tests/utils/uintconv.cpp
+ ${CMAKE_SOURCE_DIR}/tests/utils/intconv.cpp
${CMAKE_SOURCE_DIR}/tests/contract/abi.cpp
+ ${CMAKE_SOURCE_DIR}/tests/contract/event.cpp
${CMAKE_SOURCE_DIR}/tests/contract/erc20.cpp
${CMAKE_SOURCE_DIR}/tests/contract/erc20wrapper.cpp
- ${CMAKE_SOURCE_DIR}/tests/contract/erc721.cpp
+ ${CMAKE_SOURCE_DIR}/tests/contract/erc721test.cpp
${CMAKE_SOURCE_DIR}/tests/contract/nativewrapper.cpp
${CMAKE_SOURCE_DIR}/tests/contract/contractabigenerator.cpp
${CMAKE_SOURCE_DIR}/tests/contract/dexv2.cpp
@@ -36,24 +41,18 @@ set (TESTS_SOURCES
${CMAKE_SOURCE_DIR}/tests/contract/calltracer.cpp
${CMAKE_SOURCE_DIR}/tests/contract/createcontract.cpp
${CMAKE_SOURCE_DIR}/tests/contract/pebble.cpp
- ${CMAKE_SOURCE_DIR}/tests/contract/variables/safeuint_t.cpp
- ${CMAKE_SOURCE_DIR}/tests/contract/variables/safeint_t.cpp
- ${CMAKE_SOURCE_DIR}/tests/contract/variables/safestring.cpp
- ${CMAKE_SOURCE_DIR}/tests/contract/variables/safeaddress.cpp
- ${CMAKE_SOURCE_DIR}/tests/contract/variables/safeunorderedmap.cpp
- ${CMAKE_SOURCE_DIR}/tests/contract/variables/safebool.cpp
- ${CMAKE_SOURCE_DIR}/tests/contract/variables/safebytes.cpp
- ${CMAKE_SOURCE_DIR}/tests/contract/variables/safevector.cpp
- ${CMAKE_SOURCE_DIR}/tests/contract/variables/safearray.cpp
- ${CMAKE_SOURCE_DIR}/tests/contract/variables/safetuple.cpp
+ ${CMAKE_SOURCE_DIR}/tests/contract/throwtest.cpp
${CMAKE_SOURCE_DIR}/tests/core/rdpos.cpp
${CMAKE_SOURCE_DIR}/tests/core/storage.cpp
${CMAKE_SOURCE_DIR}/tests/core/state.cpp
${CMAKE_SOURCE_DIR}/tests/core/dumpmanager.cpp
- # ${CMAKE_SOURCE_DIR}/tests/core/blockchain.cpp # TODO: Blockchain is failing due to rdPoSWorker.
+ #${CMAKE_SOURCE_DIR}/tests/core/blockchain.cpp # TODO: Blockchain is failing due to rdPoSWorker.
+ ${CMAKE_SOURCE_DIR}/tests/net/p2p/encoding.cpp
+ ${CMAKE_SOURCE_DIR}/tests/net/p2p/nodeinfo.cpp
${CMAKE_SOURCE_DIR}/tests/net/p2p/p2p.cpp
- ${CMAKE_SOURCE_DIR}/tests/net/http/httpjsonrpc.cpp
${CMAKE_SOURCE_DIR}/tests/net/http/error.cpp
+ ${CMAKE_SOURCE_DIR}/tests/net/http/httpjsonrpc.cpp
+ ${CMAKE_SOURCE_DIR}/tests/net/http/parser.cpp
${CMAKE_SOURCE_DIR}/tests/sdktestsuite.cpp
)
@@ -62,16 +61,16 @@ set (TESTS_SOURCES
# TODO: change this when we take out boost multiprecision from the project for C23 BigInt
if (BUILD_VARIABLES_TESTS)
list(APPEND TESTS_SOURCES
- ${CMAKE_SOURCE_DIR}/tests/contract/variables/safeuint_t.cpp
- ${CMAKE_SOURCE_DIR}/tests/contract/variables/safeint_t.cpp
- ${CMAKE_SOURCE_DIR}/tests/contract/variables/safestring.cpp
${CMAKE_SOURCE_DIR}/tests/contract/variables/safeaddress.cpp
- ${CMAKE_SOURCE_DIR}/tests/contract/variables/safeunorderedmap.cpp
+ ${CMAKE_SOURCE_DIR}/tests/contract/variables/safearray.cpp
${CMAKE_SOURCE_DIR}/tests/contract/variables/safebool.cpp
${CMAKE_SOURCE_DIR}/tests/contract/variables/safebytes.cpp
- ${CMAKE_SOURCE_DIR}/tests/contract/variables/safevector.cpp
- ${CMAKE_SOURCE_DIR}/tests/contract/variables/safearray.cpp
+ ${CMAKE_SOURCE_DIR}/tests/contract/variables/safeint_t.cpp
+ ${CMAKE_SOURCE_DIR}/tests/contract/variables/safestring.cpp
${CMAKE_SOURCE_DIR}/tests/contract/variables/safetuple.cpp
+ ${CMAKE_SOURCE_DIR}/tests/contract/variables/safeuint_t.cpp
+ ${CMAKE_SOURCE_DIR}/tests/contract/variables/safeunorderedmap.cpp
+ ${CMAKE_SOURCE_DIR}/tests/contract/variables/safevector.cpp
)
endif()
diff --git a/tests/contract/calltracer.cpp b/tests/contract/calltracer.cpp
index 65b89527..ea0465d3 100644
--- a/tests/contract/calltracer.cpp
+++ b/tests/contract/calltracer.cpp
@@ -83,225 +83,280 @@ struct UserWrapper {
};
namespace TCallTracer {
+ TEST_CASE("CallTracer Tests", "[trace]") {
+ SECTION("Call Type Parsing") {
+ evmc_message msgCall;
+ evmc_message msgStaticCall;
+ evmc_message msgDelegateCall;
+ evmc_message msgInvalidCall;
+ msgCall.kind = EVMC_CALL;
+ msgStaticCall.kind = EVMC_CALL;
+ msgDelegateCall.kind = EVMC_DELEGATECALL;
+ msgInvalidCall.kind = evmc_call_kind(-1);
+ msgStaticCall.flags = EVMC_STATIC;
+ REQUIRE(trace::getCallType(msgCall) == trace::Call::Type::CALL);
+ REQUIRE(trace::getCallType(msgStaticCall) == trace::Call::Type::STATICCALL);
+ REQUIRE(trace::getCallType(msgDelegateCall) == trace::Call::Type::DELEGATECALL);
+ REQUIRE_THROWS(trace::getCallType(msgInvalidCall));
+ }
-TEST_CASE("CallTracer Tests", "[trace]") {
- SECTION("EVM Single Call") {
- SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("TestTraceContracts");
-
- const Address contractAddress = sdk.deployBytecode(testBytecode);
-
- uint256_t res;
-
- sdk.callViewFunction(contractAddress, &TestWrapper::sum);
- REQUIRE(res == 0);
-
- sdk.callFunction(contractAddress, &TestWrapper::add, uint256_t(33));
-
- Hash txHash = getLatestTransactionHash(sdk.getStorage());
- std::optional callTrace = sdk.getStorage().getCallTrace(txHash);
- REQUIRE(callTrace);
-
- REQUIRE(callTrace->type == trace::Call::Type::CALL);
- REQUIRE(callTrace->from == sdk.getOptions().getChainOwner());
- REQUIRE(callTrace->to == contractAddress);
- REQUIRE(callTrace->value == FixedBytes<32>());
- // TODO: gas and gasUsed?
- // TODO: what are these 4 bytes prefix?
- REQUIRE(FixedBytes<32>(callTrace->input | std::views::drop(4)) == FixedBytes<32>(UintConv::uint256ToBytes(uint256_t(33))));
-
- REQUIRE(callTrace->output == Bytes());
- REQUIRE(callTrace->calls.empty());
-
- res = sdk.callViewFunction(contractAddress, &TestWrapper::sum);
- REQUIRE(res == 33);
-
- sdk.callFunction(contractAddress, &TestWrapper::addAndReturn, uint256_t(66));
-
- txHash = getLatestTransactionHash(sdk.getStorage());
- callTrace = sdk.getStorage().getCallTrace(txHash);
- REQUIRE(callTrace);
-
- REQUIRE(callTrace->type == trace::Call::Type::CALL);
- REQUIRE(callTrace->from == sdk.getOptions().getChainOwner());
- REQUIRE(callTrace->to == contractAddress);
- REQUIRE(callTrace->value == FixedBytes<32>());
- // TODO: gas and gasUsed?
- // TODO: what are these 4 bytes prefix?
- REQUIRE(FixedBytes<32>(callTrace->input | std::views::drop(4)) == FixedBytes<32>(UintConv::uint256ToBytes(uint256_t(66))));
-
- REQUIRE(callTrace->output == Utils::makeBytes(UintConv::uint256ToBytes(uint256_t(99))));
- REQUIRE(callTrace->calls.empty());
-
- res = sdk.callViewFunction(contractAddress, &TestWrapper::sum);
- REQUIRE(res == 99);
- }
-
- SECTION("EVM Nested Calls") {
- SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("TestTraceContracts");
- const Address testContractAddress = sdk.deployBytecode(testBytecode);
- const Address testProxyContractAddress = sdk.deployBytecode(testProxyBytecode);
-
- uint256_t res;
-
- res = sdk.callViewFunction(testContractAddress, &TestWrapper::sum);
- REQUIRE(res == 0);
+ SECTION("EVM Single Call") {
+ SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("TestTraceContracts");
+
+ const Address contractAddress = sdk.deployBytecode(testBytecode);
+
+ uint256_t res;
+
+ sdk.callViewFunction(contractAddress, &TestWrapper::sum);
+ REQUIRE(res == 0);
+
+ sdk.callFunction(contractAddress, &TestWrapper::add, uint256_t(33));
+
+ Hash txHash = getLatestTransactionHash(sdk.getStorage());
+ std::optional callTrace = sdk.getStorage().getCallTrace(txHash);
+ REQUIRE(callTrace);
+ REQUIRE(callTrace->type == trace::Call::Type::CALL);
+ REQUIRE(callTrace->from == sdk.getOptions().getChainOwner());
+ REQUIRE(callTrace->to == contractAddress);
+ REQUIRE(callTrace->value == FixedBytes<32>());
+ // TODO: gas and gasUsed?
+ // TODO: what are these 4 bytes prefix?
+ REQUIRE(FixedBytes<32>(callTrace->input | std::views::drop(4)) == FixedBytes<32>(UintConv::uint256ToBytes(uint256_t(33))));
+ REQUIRE(callTrace->output == Bytes());
+ REQUIRE(callTrace->calls.empty());
+
+ json callJson = callTrace->toJson();
+ REQUIRE(callJson["type"] == "CALL");
+ REQUIRE(callJson["from"] == "0x00dead00665771855a34155f5e7405489df2c3c6");
+ REQUIRE(callJson["to"] == "0x5b41cef7f46a4a147e31150c3c5ffd077e54d0e1");
+ REQUIRE(callJson["value"] == "0x0");
+ REQUIRE(callJson["gas"] == "0x8727");
+ REQUIRE(callJson["gasUsed"] == "0x6017");
+ REQUIRE(callJson["input"] == "0x1003e2d20000000000000000000000000000000000000000000000000000000000000021");
+
+ res = sdk.callViewFunction(contractAddress, &TestWrapper::sum);
+ REQUIRE(res == 33);
+
+ sdk.callFunction(contractAddress, &TestWrapper::addAndReturn, uint256_t(66));
+
+ txHash = getLatestTransactionHash(sdk.getStorage());
+ callTrace = sdk.getStorage().getCallTrace(txHash);
+ REQUIRE(callTrace);
+ REQUIRE(callTrace->type == trace::Call::Type::CALL);
+ REQUIRE(callTrace->from == sdk.getOptions().getChainOwner());
+ REQUIRE(callTrace->to == contractAddress);
+ REQUIRE(callTrace->value == FixedBytes<32>());
+ // TODO: gas and gasUsed?
+ // TODO: what are these 4 bytes prefix?
+ REQUIRE(FixedBytes<32>(callTrace->input | std::views::drop(4)) == FixedBytes<32>(UintConv::uint256ToBytes(uint256_t(66))));
+ REQUIRE(callTrace->output == Utils::makeBytes(UintConv::uint256ToBytes(uint256_t(99))));
+ REQUIRE(callTrace->calls.empty());
+
+ json callJson2 = callTrace->toJson();
+ REQUIRE(callJson2["type"] == "CALL");
+ REQUIRE(callJson2["from"] == "0x00dead00665771855a34155f5e7405489df2c3c6");
+ REQUIRE(callJson2["to"] == "0x5b41cef7f46a4a147e31150c3c5ffd077e54d0e1");
+ REQUIRE(callJson2["value"] == "0x0");
+ REQUIRE(callJson2["gas"] == "0x88a5");
+ REQUIRE(callJson2["gasUsed"] == "0x6195");
+ REQUIRE(callJson2["input"] == "0x4fa522db0000000000000000000000000000000000000000000000000000000000000042");
+ REQUIRE(callJson2["output"] == "0x0000000000000000000000000000000000000000000000000000000000000063");
+
+ res = sdk.callViewFunction(contractAddress, &TestWrapper::sum);
+ REQUIRE(res == 99);
+ }
- sdk.callFunction(testContractAddress, &TestWrapper::add, uint256_t(45));
+ SECTION("EVM Nested Calls") {
+ SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("TestTraceContracts");
+ const Address testContractAddress = sdk.deployBytecode(testBytecode);
+ const Address testProxyContractAddress = sdk.deployBytecode(testProxyBytecode);
- res = sdk.callViewFunction(testProxyContractAddress, &TestProxyWrapper::sumOf, testContractAddress);
- REQUIRE(res == 45);
+ uint256_t res;
- sdk.callFunction(testProxyContractAddress, &TestProxyWrapper::addToAndReturn, testContractAddress, uint256_t(55));
+ res = sdk.callViewFunction(testContractAddress, &TestWrapper::sum);
+ REQUIRE(res == 0);
- const Hash txHash = getLatestTransactionHash(sdk.getStorage());
- std::optional callTrace = sdk.getStorage().getCallTrace(txHash);
- REQUIRE(callTrace);
+ sdk.callFunction(testContractAddress, &TestWrapper::add, uint256_t(45));
- REQUIRE(callTrace->type == trace::Call::Type::CALL);
- REQUIRE(callTrace->from == sdk.getOptions().getChainOwner());
- REQUIRE(callTrace->to == testProxyContractAddress);
- REQUIRE(callTrace->value == FixedBytes<32>());
- // TODO: gas and gasUsed
- REQUIRE(Address(callTrace->input | std::views::drop(16) | std::views::take(20)) == testContractAddress);
- REQUIRE(FixedBytes<32>(callTrace->input | std::views::drop(36)) == FixedBytes<32>(UintConv::uint256ToBytes(uint256_t(55))));
+ res = sdk.callViewFunction(testProxyContractAddress, &TestProxyWrapper::sumOf, testContractAddress);
+ REQUIRE(res == 45);
- REQUIRE(callTrace->output == Utils::makeBytes(UintConv::uint256ToBytes(uint256_t(100))));
- REQUIRE(callTrace->calls.size() == 1);
+ sdk.callFunction(testProxyContractAddress, &TestProxyWrapper::addToAndReturn, testContractAddress, uint256_t(55));
- const trace::Call& nestedCall = callTrace->calls.front();
+ const Hash txHash = getLatestTransactionHash(sdk.getStorage());
+ std::optional callTrace = sdk.getStorage().getCallTrace(txHash);
+ REQUIRE(callTrace);
- REQUIRE(nestedCall.type == trace::Call::Type::CALL);
- REQUIRE(nestedCall.from == testProxyContractAddress);
- REQUIRE(nestedCall.to == testContractAddress);
- REQUIRE(nestedCall.value == FixedBytes<32>());
- REQUIRE(FixedBytes<32>(nestedCall.input | std::views::drop(4)) == FixedBytes<32>(UintConv::uint256ToBytes(uint256_t(55))));
+ REQUIRE(callTrace->type == trace::Call::Type::CALL);
+ REQUIRE(callTrace->from == sdk.getOptions().getChainOwner());
+ REQUIRE(callTrace->to == testProxyContractAddress);
+ REQUIRE(callTrace->value == FixedBytes<32>());
+ // TODO: gas and gasUsed
+ REQUIRE(Address(callTrace->input | std::views::drop(16) | std::views::take(20)) == testContractAddress);
+ REQUIRE(FixedBytes<32>(callTrace->input | std::views::drop(36)) == FixedBytes<32>(UintConv::uint256ToBytes(uint256_t(55))));
- REQUIRE(nestedCall.output == Utils::makeBytes(UintConv::uint256ToBytes(uint256_t(100))));
- REQUIRE(nestedCall.calls.empty());
+ REQUIRE(callTrace->output == Utils::makeBytes(UintConv::uint256ToBytes(uint256_t(100))));
+ REQUIRE(callTrace->calls.size() == 1);
- res = sdk.callViewFunction(testContractAddress, &TestWrapper::sum);
- REQUIRE(res == 100);
- }
+ const trace::Call& nestedCall = callTrace->calls.front();
- SECTION("Native Contracts") {
- SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("TestCallTracerOfErc20Wrapper");
+ REQUIRE(nestedCall.type == trace::Call::Type::CALL);
+ REQUIRE(nestedCall.from == testProxyContractAddress);
+ REQUIRE(nestedCall.to == testContractAddress);
+ REQUIRE(nestedCall.value == FixedBytes<32>());
+ REQUIRE(FixedBytes<32>(nestedCall.input | std::views::drop(4)) == FixedBytes<32>(UintConv::uint256ToBytes(uint256_t(55))));
- const Address erc20 = sdk.deployContract(
- std::string("TestToken"), std::string("TST"), uint8_t(18), uint256_t("1000000000000000000")
- );
+ REQUIRE(nestedCall.output == Utils::makeBytes(UintConv::uint256ToBytes(uint256_t(100))));
+ REQUIRE(nestedCall.calls.empty());
- const Address erc20Wrapper = sdk.deployContract();
- const Address owner = sdk.getChainOwnerAccount().address;
-
- for (const auto& [name, address] : sdk.getState().getCppContracts()) {
- if (name == "ERC20")
- REQUIRE(address == erc20);
- else if (name == "ERC20Wrapper")
- REQUIRE(address == erc20Wrapper);
+ res = sdk.callViewFunction(testContractAddress, &TestWrapper::sum);
+ REQUIRE(res == 100);
}
- const Hash approveTx = sdk.callFunction(erc20, &ERC20::approve, erc20Wrapper, uint256_t("500000000000000000"));
- const Hash depositTx = sdk.callFunction(erc20Wrapper, &ERC20Wrapper::deposit, erc20, uint256_t("500000000000000000"));
-
- const auto approveCallTrace = sdk.getStorage().getCallTrace(approveTx);
- const auto depositCallTrace = sdk.getStorage().getCallTrace(depositTx);
-
- REQUIRE(approveCallTrace);
- REQUIRE(approveCallTrace->type == trace::Call::Type::CALL);
- REQUIRE(approveCallTrace->status == trace::Status::SUCCEEDED);
- REQUIRE(approveCallTrace->from == sdk.getOptions().getChainOwner());
- REQUIRE(approveCallTrace->to == erc20);
- REQUIRE(approveCallTrace->value == FixedBytes<32>());
- REQUIRE(approveCallTrace->input == Hex::toBytes("0x095ea7b30000000000000000000000006d48fdfe009e309dd5c4e69dec87365bfa0c811900000000000000000000000000000000000000000000000006f05b59d3b20000"));
- REQUIRE(approveCallTrace->output == Bytes());
- REQUIRE(approveCallTrace->calls.empty());
-
- REQUIRE(depositCallTrace);
- REQUIRE(depositCallTrace->type == trace::Call::Type::CALL);
- REQUIRE(depositCallTrace->status == trace::Status::SUCCEEDED);
- REQUIRE(depositCallTrace->from == sdk.getOptions().getChainOwner());
- REQUIRE(depositCallTrace->to == erc20Wrapper);
- REQUIRE(depositCallTrace->value == FixedBytes<32>());
- REQUIRE(depositCallTrace->input == Hex::toBytes("0x47e7ef240000000000000000000000005b41cef7f46a4a147e31150c3c5ffd077e54d0e100000000000000000000000000000000000000000000000006f05b59d3b20000"));
- REQUIRE(depositCallTrace->output == Bytes());
- REQUIRE(!depositCallTrace->calls.empty());
- REQUIRE(depositCallTrace->calls[0].type == trace::Call::Type::CALL);
- REQUIRE(depositCallTrace->calls[0].status == trace::Status::SUCCEEDED);
- REQUIRE(depositCallTrace->calls[0].from == erc20Wrapper);
- REQUIRE(depositCallTrace->calls[0].to == erc20);
- REQUIRE(depositCallTrace->calls[0].value == FixedBytes<32>());
- REQUIRE(depositCallTrace->calls[0].input == Hex::toBytes("0x23b872dd00000000000000000000000000dead00665771855a34155f5e7405489df2c3c60000000000000000000000006d48fdfe009e309dd5c4e69dec87365bfa0c811900000000000000000000000000000000000000000000000006f05b59d3b20000"));
- REQUIRE(depositCallTrace->calls[0].output == Hex::toBytes("0x0000000000000000000000000000000000000000000000000000000000000001"));
- REQUIRE(depositCallTrace->calls[0].calls.empty());
- }
+ SECTION("Native Contracts") {
+ SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("TestCallTracerOfErc20Wrapper");
+
+ const Address erc20 = sdk.deployContract(
+ std::string("TestToken"), std::string("TST"), uint8_t(18), uint256_t("1000000000000000000")
+ );
+
+ const Address erc20Wrapper = sdk.deployContract();
+ const Address owner = sdk.getChainOwnerAccount().address;
+
+ for (const auto& [name, address] : sdk.getState().getCppContracts()) {
+ if (name == "ERC20")
+ REQUIRE(address == erc20);
+ else if (name == "ERC20Wrapper")
+ REQUIRE(address == erc20Wrapper);
+ }
+
+ const Hash approveTx = sdk.callFunction(erc20, &ERC20::approve, erc20Wrapper, uint256_t("500000000000000000"));
+ const Hash depositTx = sdk.callFunction(erc20Wrapper, &ERC20Wrapper::deposit, erc20, uint256_t("500000000000000000"));
+
+ const auto approveCallTrace = sdk.getStorage().getCallTrace(approveTx);
+ const auto depositCallTrace = sdk.getStorage().getCallTrace(depositTx);
+
+ REQUIRE(approveCallTrace);
+ REQUIRE(approveCallTrace->type == trace::Call::Type::CALL);
+ REQUIRE(approveCallTrace->status == trace::Status::SUCCEEDED);
+ REQUIRE(approveCallTrace->from == sdk.getOptions().getChainOwner());
+ REQUIRE(approveCallTrace->to == erc20);
+ REQUIRE(approveCallTrace->value == FixedBytes<32>());
+ REQUIRE(approveCallTrace->input == Hex::toBytes("0x095ea7b30000000000000000000000006d48fdfe009e309dd5c4e69dec87365bfa0c811900000000000000000000000000000000000000000000000006f05b59d3b20000"));
+ REQUIRE(approveCallTrace->output == Bytes());
+ REQUIRE(approveCallTrace->calls.empty());
+
+ REQUIRE(depositCallTrace);
+ REQUIRE(depositCallTrace->type == trace::Call::Type::CALL);
+ REQUIRE(depositCallTrace->status == trace::Status::SUCCEEDED);
+ REQUIRE(depositCallTrace->from == sdk.getOptions().getChainOwner());
+ REQUIRE(depositCallTrace->to == erc20Wrapper);
+ REQUIRE(depositCallTrace->value == FixedBytes<32>());
+ REQUIRE(depositCallTrace->input == Hex::toBytes("0x47e7ef240000000000000000000000005b41cef7f46a4a147e31150c3c5ffd077e54d0e100000000000000000000000000000000000000000000000006f05b59d3b20000"));
+ REQUIRE(depositCallTrace->output == Bytes());
+ REQUIRE(!depositCallTrace->calls.empty());
+ REQUIRE(depositCallTrace->calls[0].type == trace::Call::Type::CALL);
+ REQUIRE(depositCallTrace->calls[0].status == trace::Status::SUCCEEDED);
+ REQUIRE(depositCallTrace->calls[0].from == erc20Wrapper);
+ REQUIRE(depositCallTrace->calls[0].to == erc20);
+ REQUIRE(depositCallTrace->calls[0].value == FixedBytes<32>());
+ REQUIRE(depositCallTrace->calls[0].input == Hex::toBytes("0x23b872dd00000000000000000000000000dead00665771855a34155f5e7405489df2c3c60000000000000000000000006d48fdfe009e309dd5c4e69dec87365bfa0c811900000000000000000000000000000000000000000000000006f05b59d3b20000"));
+ REQUIRE(depositCallTrace->calls[0].output == Hex::toBytes("0x0000000000000000000000000000000000000000000000000000000000000001"));
+ REQUIRE(depositCallTrace->calls[0].calls.empty());
+ }
- SECTION("Errors and payable functions") {
- SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("TestCallTracingErrosAndPays");
-
- const Address bankAddress = sdk.deployBytecode(bankBytecode);
- const Address userAddress = sdk.deployBytecode(userBytecode);
-
- uint256_t res = sdk.callViewFunction(bankAddress, &BankWrapper::balance);
- REQUIRE(res == 0);
-
- const Hash depositTxHash = sdk.callFunction(bankAddress, &BankWrapper::deposit, uint256_t(500));
- res = sdk.callViewFunction(bankAddress, &BankWrapper::balance);
- REQUIRE(res == 500);
-
- const Hash invalidWithdrawTxHash = sdk.callFunction(userAddress, &UserWrapper::tryWithdraw, bankAddress, uint256_t(501));
- const Hash validWithdrawTxHash = sdk.callFunction(userAddress, &UserWrapper::tryWithdraw, bankAddress, uint256_t(300));
- const Hash payTxHash = sdk.callFunction(bankAddress, &BankWrapper::pay, uint256_t(4568));
-
- const auto errorCallTrace = sdk.getStorage().getCallTrace(invalidWithdrawTxHash);
- const auto successCallTrace = sdk.getStorage().getCallTrace(validWithdrawTxHash);
- const auto payCallTrace = sdk.getStorage().getCallTrace(payTxHash);
-
- REQUIRE(errorCallTrace);
- REQUIRE(errorCallTrace->type == trace::Call::Type::CALL);
- REQUIRE(errorCallTrace->status == trace::Status::SUCCEEDED);
- REQUIRE(errorCallTrace->from == sdk.getOptions().getChainOwner());
- REQUIRE(errorCallTrace->to == userAddress);
- REQUIRE(errorCallTrace->value == FixedBytes<32>());
- REQUIRE(errorCallTrace->input == Hex::toBytes("0x7f3358bc0000000000000000000000005b41cef7f46a4a147e31150c3c5ffd077e54d0e100000000000000000000000000000000000000000000000000000000000001f5"));
- REQUIRE(errorCallTrace->output == Bytes(32));
- REQUIRE(!errorCallTrace->calls.empty());
- REQUIRE(errorCallTrace->calls[0].type == trace::Call::Type::CALL);
- REQUIRE(errorCallTrace->calls[0].status == trace::Status::EXECUTION_REVERTED);
- REQUIRE(errorCallTrace->calls[0].from == userAddress);
- REQUIRE(errorCallTrace->calls[0].to == bankAddress);
- REQUIRE(errorCallTrace->calls[0].value == FixedBytes<32>());
- REQUIRE(errorCallTrace->calls[0].input == Hex::toBytes("0x2e1a7d4d00000000000000000000000000000000000000000000000000000000000001f5"));
- REQUIRE(errorCallTrace->calls[0].output == trace::encodeRevertReason("Insufficient funds"));
- REQUIRE(errorCallTrace->calls[0].calls.empty());
-
- REQUIRE(successCallTrace);
- REQUIRE(successCallTrace->type == trace::Call::Type::CALL);
- REQUIRE(successCallTrace->status == trace::Status::SUCCEEDED);
- REQUIRE(successCallTrace->from == sdk.getOptions().getChainOwner());
- REQUIRE(successCallTrace->to == userAddress);
- REQUIRE(successCallTrace->value == FixedBytes<32>());
- REQUIRE(successCallTrace->input == Hex::toBytes("0x7f3358bc0000000000000000000000005b41cef7f46a4a147e31150c3c5ffd077e54d0e1000000000000000000000000000000000000000000000000000000000000012c"));
- REQUIRE(successCallTrace->output == Hex::toBytes("0x0000000000000000000000000000000000000000000000000000000000000001"));
- REQUIRE(!successCallTrace->calls.empty());
- REQUIRE(successCallTrace->calls[0].type == trace::Call::Type::CALL);
- REQUIRE(successCallTrace->calls[0].status == trace::Status::SUCCEEDED);
- REQUIRE(successCallTrace->calls[0].from == userAddress);
- REQUIRE(successCallTrace->calls[0].to == bankAddress);
- REQUIRE(successCallTrace->calls[0].value == FixedBytes<32>());
- REQUIRE(successCallTrace->calls[0].input == Hex::toBytes("0x2e1a7d4d000000000000000000000000000000000000000000000000000000000000012c"));
- REQUIRE(successCallTrace->calls[0].output == Bytes());
- REQUIRE(successCallTrace->calls[0].calls.empty());
-
- REQUIRE(payCallTrace);
- REQUIRE(payCallTrace->type == trace::Call::Type::CALL);
- REQUIRE(payCallTrace->status == trace::Status::SUCCEEDED);
- REQUIRE(payCallTrace->from == sdk.getOptions().getChainOwner());
- REQUIRE(payCallTrace->to == bankAddress);
- REQUIRE(payCallTrace->value == FixedBytes<32>(UintConv::uint256ToBytes(uint256_t(4568))));
- REQUIRE(payCallTrace->input == Hex::toBytes("0x1b9265b8"));
- REQUIRE(payCallTrace->output == Bytes());
- REQUIRE(payCallTrace->calls.empty());
+ SECTION("Errors and payable functions") {
+ SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("TestCallTracingErrosAndPays");
+
+ const Address bankAddress = sdk.deployBytecode(bankBytecode);
+ const Address userAddress = sdk.deployBytecode(userBytecode);
+
+ uint256_t res = sdk.callViewFunction(bankAddress, &BankWrapper::balance);
+ REQUIRE(res == 0);
+
+ const Hash depositTxHash = sdk.callFunction(bankAddress, &BankWrapper::deposit, uint256_t(500));
+ res = sdk.callViewFunction(bankAddress, &BankWrapper::balance);
+ REQUIRE(res == 500);
+
+ const Hash invalidWithdrawTxHash = sdk.callFunction(userAddress, &UserWrapper::tryWithdraw, bankAddress, uint256_t(501));
+ const Hash validWithdrawTxHash = sdk.callFunction(userAddress, &UserWrapper::tryWithdraw, bankAddress, uint256_t(300));
+ const Hash payTxHash = sdk.callFunction(bankAddress, &BankWrapper::pay, uint256_t(4568));
+
+ const auto errorCallTrace = sdk.getStorage().getCallTrace(invalidWithdrawTxHash);
+ const auto successCallTrace = sdk.getStorage().getCallTrace(validWithdrawTxHash);
+ const auto payCallTrace = sdk.getStorage().getCallTrace(payTxHash);
+
+ Bytes reasonInsufficientFunds = trace::encodeRevertReason("Insufficient funds");
+ REQUIRE(trace::decodeRevertReason(reasonInsufficientFunds) == "Insufficient funds");
+ REQUIRE_THROWS(trace::decodeRevertReason(Bytes{0x00, 0x01, 0x02, 0x03, 0x04})); // Data has to be exactly 100 bytes
+
+ REQUIRE(errorCallTrace);
+ REQUIRE(errorCallTrace->type == trace::Call::Type::CALL);
+ REQUIRE(errorCallTrace->status == trace::Status::SUCCEEDED);
+ REQUIRE(errorCallTrace->from == sdk.getOptions().getChainOwner());
+ REQUIRE(errorCallTrace->to == userAddress);
+ REQUIRE(errorCallTrace->value == FixedBytes<32>());
+ REQUIRE(errorCallTrace->input == Hex::toBytes("0x7f3358bc0000000000000000000000005b41cef7f46a4a147e31150c3c5ffd077e54d0e100000000000000000000000000000000000000000000000000000000000001f5"));
+ REQUIRE(errorCallTrace->output == Bytes(32));
+ REQUIRE(!errorCallTrace->calls.empty());
+ REQUIRE(errorCallTrace->calls[0].type == trace::Call::Type::CALL);
+ REQUIRE(errorCallTrace->calls[0].status == trace::Status::EXECUTION_REVERTED);
+ REQUIRE(errorCallTrace->calls[0].from == userAddress);
+ REQUIRE(errorCallTrace->calls[0].to == bankAddress);
+ REQUIRE(errorCallTrace->calls[0].value == FixedBytes<32>());
+ REQUIRE(errorCallTrace->calls[0].input == Hex::toBytes("0x2e1a7d4d00000000000000000000000000000000000000000000000000000000000001f5"));
+ REQUIRE(errorCallTrace->calls[0].output == reasonInsufficientFunds);
+ REQUIRE(errorCallTrace->calls[0].calls.empty());
+
+ json errorJson = errorCallTrace->toJson();
+ REQUIRE(errorJson["type"] == "CALL");
+ REQUIRE(errorJson["from"] == "0x00dead00665771855a34155f5e7405489df2c3c6");
+ REQUIRE(errorJson["to"] == "0x6d48fdfe009e309dd5c4e69dec87365bfa0c8119");
+ REQUIRE(errorJson["value"] == "0x0");
+ REQUIRE(errorJson["gas"] == "0x958b");
+ REQUIRE(errorJson["gasUsed"] == "0x6e7b");
+ REQUIRE(errorJson["input"] == "0x7f3358bc0000000000000000000000005b41cef7f46a4a147e31150c3c5ffd077e54d0e100000000000000000000000000000000000000000000000000000000000001f5");
+ REQUIRE(errorJson.contains("calls"));
+ REQUIRE(!errorJson["calls"].empty());
+ json errorJsonCall = errorJson["calls"][0];
+ REQUIRE(errorJsonCall["type"] == "CALL");
+ REQUIRE(errorJsonCall["from"] == "0x6d48fdfe009e309dd5c4e69dec87365bfa0c8119");
+ REQUIRE(errorJsonCall["to"] == "0x5b41cef7f46a4a147e31150c3c5ffd077e54d0e1");
+ REQUIRE(errorJsonCall["value"] == "0x0");
+ REQUIRE(errorJsonCall["gas"] == "0x8f18");
+ REQUIRE(errorJsonCall["gasUsed"] == "0x16bb");
+ REQUIRE(errorJsonCall["input"] == "0x2e1a7d4d00000000000000000000000000000000000000000000000000000000000001f5");
+ REQUIRE(errorJsonCall["output"] == "0x08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012496e73756666696369656e742066756e64730000000000000000000000000000");
+ REQUIRE(errorJsonCall["error"] == "execution reverted");
+ REQUIRE(errorJsonCall["revertReason"] == "Insufficient funds");
+
+ REQUIRE(successCallTrace);
+ REQUIRE(successCallTrace->type == trace::Call::Type::CALL);
+ REQUIRE(successCallTrace->status == trace::Status::SUCCEEDED);
+ REQUIRE(successCallTrace->from == sdk.getOptions().getChainOwner());
+ REQUIRE(successCallTrace->to == userAddress);
+ REQUIRE(successCallTrace->value == FixedBytes<32>());
+ REQUIRE(successCallTrace->input == Hex::toBytes("0x7f3358bc0000000000000000000000005b41cef7f46a4a147e31150c3c5ffd077e54d0e1000000000000000000000000000000000000000000000000000000000000012c"));
+ REQUIRE(successCallTrace->output == Hex::toBytes("0x0000000000000000000000000000000000000000000000000000000000000001"));
+ REQUIRE(!successCallTrace->calls.empty());
+ REQUIRE(successCallTrace->calls[0].type == trace::Call::Type::CALL);
+ REQUIRE(successCallTrace->calls[0].status == trace::Status::SUCCEEDED);
+ REQUIRE(successCallTrace->calls[0].from == userAddress);
+ REQUIRE(successCallTrace->calls[0].to == bankAddress);
+ REQUIRE(successCallTrace->calls[0].value == FixedBytes<32>());
+ REQUIRE(successCallTrace->calls[0].input == Hex::toBytes("0x2e1a7d4d000000000000000000000000000000000000000000000000000000000000012c"));
+ REQUIRE(successCallTrace->calls[0].output == Bytes());
+ REQUIRE(successCallTrace->calls[0].calls.empty());
+
+ REQUIRE(payCallTrace);
+ REQUIRE(payCallTrace->type == trace::Call::Type::CALL);
+ REQUIRE(payCallTrace->status == trace::Status::SUCCEEDED);
+ REQUIRE(payCallTrace->from == sdk.getOptions().getChainOwner());
+ REQUIRE(payCallTrace->to == bankAddress);
+ REQUIRE(payCallTrace->value == FixedBytes<32>(UintConv::uint256ToBytes(uint256_t(4568))));
+ REQUIRE(payCallTrace->input == Hex::toBytes("0x1b9265b8"));
+ REQUIRE(payCallTrace->output == Bytes());
+ REQUIRE(payCallTrace->calls.empty());
+ }
}
-}
-
} // namespace TCallTracer
diff --git a/tests/contract/dexv2.cpp b/tests/contract/dexv2.cpp
index 7d18e3e9..5b191343 100644
--- a/tests/contract/dexv2.cpp
+++ b/tests/contract/dexv2.cpp
@@ -7,6 +7,7 @@ See the LICENSE.txt file in the project root for more information.
#include "../../src/libs/catch2/catch_amalgamated.hpp"
+#include "../../src/contract/templates/dexv2/dexv2pair.h"
#include "../../src/contract/templates/dexv2/dexv2factory.h"
#include "../../src/contract/templates/dexv2/dexv2router02.h"
#include "../../src/contract/templates/nativewrapper.h"
@@ -14,24 +15,112 @@ See the LICENSE.txt file in the project root for more information.
#include "../sdktestsuite.hpp"
// TODO: test events if/when implemented
+// TODO: implement the rest of the coverage:
+// swapExactTokensForTokens, swapTokensForExactTokens,
+// swapExactNativeForTokens, swapTokensForExactNative,
+// swapExactTokensForNative, swapNativeForExactTokens
+// also whatever's left of DEXV2Pair if applicable
+
+namespace TUQ112x112 {
+ TEST_CASE("UQ112x112 Namespace Test", "[contract][dexv2][uq112x112]") {
+ SECTION("UQ112x112 Coverage") {
+ // Q112 = 5192296858534827628530496329220096
+ uint224_t enc = UQ112x112::encode(1024);
+ REQUIRE(enc == uint224_t("5316911983139663491615228241121378304"));
+ uint224_t div = UQ112x112::uqdiv(uint224_t("123456789000"), uint112_t("1234567890"));
+ REQUIRE(div == uint224_t(100));
+ }
+ }
+}
namespace TDEXV2 {
- TEST_CASE("DEXV2 Test", "[contract][dexv2]") {
- SECTION("Deploy DEXV2Router/Factory with a single pair") {
- SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testDEXV2SinglePair");
- Address wrapped = sdk.deployContract(std::string("WSPARQ"), std::string("WSPARQ"), uint8_t(18));
- Address factory = sdk.deployContract(Address());
- Address router = sdk.deployContract(factory, wrapped);
- Address owner = sdk.getChainOwnerAccount().address;
+ TEST_CASE("DEXV2 Pair Test", "[contract][dexv2][dexv2pair]") {
+ SECTION("Deploy + Dump DEXV2Pair") {
+ Address pair;
+ Address tokenA;
+ Address tokenB;
+ Address chainOwner("0x00dead00665771855a34155f5e7405489df2c3c6", false);
+ std::unique_ptr options;
+ {
+ SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testDEXV2Pair");
+ pair = sdk.deployContract();
+ tokenA = sdk.deployContract(
+ std::string("TestTokenA"), std::string("TSTA"), uint8_t(18), uint256_t("1000000000000000000")
+ );
+ tokenB = sdk.deployContract(
+ std::string("TestTokenB"), std::string("TSTB"), uint8_t(18), uint256_t("1000000000000000000")
+ );
+ sdk.callFunction(pair, &DEXV2Pair::initialize, tokenA, tokenB);
+ REQUIRE(sdk.callViewFunction(pair, &DEXV2Pair::factory) == chainOwner);
+ REQUIRE(sdk.callViewFunction(pair, &DEXV2Pair::token0) == tokenA);
+ REQUIRE(sdk.callViewFunction(pair, &DEXV2Pair::token1) == tokenB);
+ REQUIRE(sdk.callViewFunction(pair, &DEXV2Pair::price0CumulativeLast) == 0);
+ REQUIRE(sdk.callViewFunction(pair, &DEXV2Pair::price1CumulativeLast) == 0);
+ REQUIRE(sdk.callViewFunction(pair, &DEXV2Pair::kLast) == 0);
+ std::tuple reservesOutput = sdk.callViewFunction(pair, &DEXV2Pair::getReserves);
+ REQUIRE(std::get<0>(reservesOutput) == 0);
+ REQUIRE(std::get<1>(reservesOutput) == 0);
+ REQUIRE(std::get<2>(reservesOutput) == 0);
+
+ // Dump to database
+ options = std::make_unique(sdk.getOptions());
+ sdk.getState().saveToDB();
+ }
+
+ // SDKTestSuite should automatically load the state from the DB if we construct it with an Options object
+ // (The createNewEnvironment DELETES the DB if any is found)
+ SDKTestSuite sdk(*options);
+ REQUIRE(sdk.callViewFunction(pair, &DEXV2Pair::factory) == chainOwner);
+ REQUIRE(sdk.callViewFunction(pair, &DEXV2Pair::token0) == tokenA);
+ REQUIRE(sdk.callViewFunction(pair, &DEXV2Pair::token1) == tokenB);
+ REQUIRE(sdk.callViewFunction(pair, &DEXV2Pair::price0CumulativeLast) == 0);
+ REQUIRE(sdk.callViewFunction(pair, &DEXV2Pair::price1CumulativeLast) == 0);
+ REQUIRE(sdk.callViewFunction(pair, &DEXV2Pair::kLast) == 0);
+ std::tuple reservesOutput = sdk.callViewFunction(pair, &DEXV2Pair::getReserves);
+ Utils::safePrintTest("DEXV2 Testing Reserves: " + std::get<0>(reservesOutput).str() + ", " + std::get<1>(reservesOutput).str() + ", " + std::get<2>(reservesOutput).str());
+ REQUIRE(std::get<0>(reservesOutput) == 0);
+ REQUIRE(std::get<1>(reservesOutput) == 0);
+ REQUIRE(std::get<2>(reservesOutput) == 0);
+ }
+ }
+
+ TEST_CASE("DEXV2 Router Test", "[contract][dexv2][dexv2router]") {
+ SECTION("Deploy + Dump DEXV2Router/Factory with a single pair") {
+ Address wrapped;
+ Address factory;
+ Address router;
+ std::unique_ptr options;
+ {
+ SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testDEXV2RouterSinglePair");
+ wrapped = sdk.deployContract(std::string("WSPARQ"), std::string("WSPARQ"), uint8_t(18));
+ factory = sdk.deployContract(Address());
+ router = sdk.deployContract(factory, wrapped);
+ for (const auto& contract : sdk.getState().getCppContracts()) {
+ if (contract.first == "NativeWrapper") REQUIRE(contract.second == wrapped);
+ if (contract.first == "DEXV2Factory") REQUIRE(contract.second == factory);
+ if (contract.first == "DEXV2Router02") REQUIRE(contract.second == router);
+ }
+ // Dump to database
+ options = std::make_unique(sdk.getOptions());
+ sdk.getState().saveToDB();
+ }
+
+ // SDKTestSuite should automatically load the state from the DB if we construct it with an Options object
+ // (The createNewEnvironment DELETES the DB if any is found)
+ SDKTestSuite sdk(*options);
for (const auto& contract : sdk.getState().getCppContracts()) {
if (contract.first == "NativeWrapper") REQUIRE(contract.second == wrapped);
if (contract.first == "DEXV2Factory") REQUIRE(contract.second == factory);
if (contract.first == "DEXV2Router02") REQUIRE(contract.second == router);
}
+
+ // For coverage
+ REQUIRE(sdk.callViewFunction(router, &DEXV2Router02::factory) == factory);
+ REQUIRE(sdk.callViewFunction(router, &DEXV2Router02::wrappedNative) == wrapped);
}
- SECTION("Deploy DEXV2 and add liquidity to token/token pair") {
- SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testDEXV2LiqTokenTokenPair");
+ SECTION("Deploy DEXV2 and add/remove liquidity to token/token pair") {
+ SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testDEXV2RouterLiqTokenTokenPair");
Address tokenA = sdk.deployContract(std::string("TokenA"), std::string("TKNA"), uint8_t(18), uint256_t("10000000000000000000000"));
Address tokenB = sdk.deployContract(std::string("TokenB"), std::string("TKNB"), uint8_t(18), uint256_t("10000000000000000000000"));
Address wrapped = sdk.deployContract(std::string("WSPARQ"), std::string("WSPARQ"), uint8_t(18));
@@ -45,7 +134,7 @@ namespace TDEXV2 {
}
// Approve "router" so it can spend up to 10000 tokens from both sides
- // on behalf of "owner" (which already has the tokens)
+ // on behalf of "owner" (which already has the tokens).
Hash approveATx = sdk.callFunction(tokenA, &ERC20::approve, router, uint256_t("10000000000000000000000"));
Hash approveBTx = sdk.callFunction(tokenB, &ERC20::approve, router, uint256_t("10000000000000000000000"));
REQUIRE(sdk.callViewFunction(tokenA, &ERC20::allowance, owner, router) == uint256_t("10000000000000000000000"));
@@ -58,7 +147,7 @@ namespace TDEXV2 {
std::chrono::system_clock::now().time_since_epoch()
).count() + 60000000; // 60 seconds
// tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin, to, deadline
- Hash addLiqudityTx = sdk.callFunction(router, &DEXV2Router02::addLiquidity,
+ Hash addLiquidityTx = sdk.callFunction(router, &DEXV2Router02::addLiquidity,
tokenA, tokenB, uint256_t("100000000000000000000"), uint256_t("250000000000000000000"),
uint256_t(0), uint256_t(0), owner, deadline
);
@@ -73,10 +162,50 @@ namespace TDEXV2 {
REQUIRE(ownerTknB == uint256_t("9750000000000000000000"));
REQUIRE(pairTknA == uint256_t("100000000000000000000"));
REQUIRE(pairTknB == uint256_t("250000000000000000000"));
+
+ // Approve "pair" so it can allow up to 10000 liquidity tokens to be
+ // withdrawn by the "owner" (which has much less than that)
+ Hash approvePairTx = sdk.callFunction(pair, &ERC20::approve, router, uint256_t("10000000000000000000000"));
+ REQUIRE(sdk.callViewFunction(pair, &ERC20::allowance, owner, router) == uint256_t("10000000000000000000000"));
+ REQUIRE(sdk.callViewFunction(pair, &ERC20::balanceOf, owner) == uint256_t("158113883008418965599"));
+
+ // Remove 50 liquidity tokens from the pair
+ deadline = std::chrono::duration_cast(
+ std::chrono::system_clock::now().time_since_epoch()
+ ).count() + 60000000; // 60 seconds
+ // tokenA, tokenB, liquidity, amountAMin, amountBMin, to, deadline
+ Hash removeLiquidityTx = sdk.callFunction(router, &DEXV2Router02::removeLiquidity,
+ tokenA, tokenB, uint256_t("50000000000000000000"), uint256_t(0), uint256_t(0), owner, deadline
+ );
+
+ // Check if operation worked successfully
+ pair = sdk.callViewFunction(factory, &DEXV2Factory::getPair, tokenA, tokenB);
+ ownerTknA = sdk.callViewFunction(tokenA, &ERC20::balanceOf, owner);
+ ownerTknB = sdk.callViewFunction(tokenB, &ERC20::balanceOf, owner);
+ pairTknA = sdk.callViewFunction(tokenA, &ERC20::balanceOf, pair);
+ pairTknB = sdk.callViewFunction(tokenB, &ERC20::balanceOf, pair);
+ REQUIRE(ownerTknA == uint256_t("9931622776601683793320"));
+ REQUIRE(ownerTknB == uint256_t("9829056941504209483300"));
+ REQUIRE(pairTknA == uint256_t("68377223398316206680"));
+ REQUIRE(pairTknB == uint256_t("170943058495790516700"));
+
+ // For coverage (ensure() and throws on removeLiquidity())
+ REQUIRE_THROWS(sdk.callFunction(router, &DEXV2Router02::removeLiquidity,
+ tokenA, tokenB, uint256_t("5000000000000000000"),
+ uint256_t(0), uint256_t(0), owner, uint256_t(0) // deadline always expired
+ ));
+ REQUIRE_THROWS(sdk.callFunction(router, &DEXV2Router02::removeLiquidity,
+ tokenA, tokenB, uint256_t("5000000000000000000"),
+ uint256_t("500000000000000000000"), uint256_t(0), owner, deadline // insufficient amountA (500)
+ ));
+ REQUIRE_THROWS(sdk.callFunction(router, &DEXV2Router02::removeLiquidity,
+ tokenA, tokenB, uint256_t("5000000000000000000"),
+ uint256_t(0), uint256_t("500000000000000000000"), owner, deadline // insufficient amountB (500)
+ ));
}
- SECTION("Deploy DEXV2 and add liquidity to token/native pair") {
- SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testDEXV2LiqTokenNativePair");
+ SECTION("Deploy DEXV2 and add/remove liquidity to token/native pair") {
+ SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testDEXV2RouterLiqTokenNativePair");
Address tokenA = sdk.deployContract(std::string("TokenA"), std::string("TKNA"), uint8_t(18), uint256_t("10000000000000000000000"));
Address wrapped = sdk.deployContract(std::string("WSPARQ"), std::string("WSPARQ"), uint8_t(18));
Address factory = sdk.deployContract(Address());
@@ -99,14 +228,13 @@ namespace TDEXV2 {
std::chrono::system_clock::now().time_since_epoch()
).count() + 60000000; // 60 seconds
// token, amountTokenDesired, amountTokenMin, amountNativeMin, to, deadline
- Hash addLiqudityTx = sdk.callFunction(
+ Hash addLiquidityTx = sdk.callFunction(
router, uint256_t("100000000000000000000"), &DEXV2Router02::addLiquidityNative,
tokenA, uint256_t("100000000000000000000"), uint256_t("100000000000000000000"),
uint256_t("100000000000000000000"), owner, deadline
);
// Check if operation worked successfully
- // SDKTestSuite::createNewTx() is adding a tx fee of 21000 gwei, thus * 2
Address pair = sdk.callViewFunction(factory, &DEXV2Factory::getPair, tokenA, wrapped);
uint256_t ownerTknA = sdk.callViewFunction(tokenA, &ERC20::balanceOf, owner);
uint256_t ownerNative = sdk.getNativeBalance(owner);
@@ -118,6 +246,37 @@ namespace TDEXV2 {
REQUIRE(pairTknA == uint256_t("100000000000000000000"));
REQUIRE(wrappedNative == uint256_t("100000000000000000000"));
REQUIRE(pairNativeWrapped == uint256_t("100000000000000000000"));
+
+ // Approve "pair" so it can allow up to 10000 liquidity tokens to be
+ // withdrawn by the "owner" (which has much less than that)
+ Hash approvePairTx = sdk.callFunction(pair, &ERC20::approve, router, uint256_t("10000000000000000000000"));
+ REQUIRE(sdk.callViewFunction(pair, &ERC20::allowance, owner, router) == uint256_t("10000000000000000000000"));
+ REQUIRE(sdk.callViewFunction(pair, &ERC20::balanceOf, owner) == uint256_t("99999999999999999000"));
+
+ uint256_t ownerNativeBeforeSubLiq = sdk.getNativeBalance(owner);
+ // Remove 50 liquidity tokens
+ deadline = std::chrono::duration_cast(
+ std::chrono::system_clock::now().time_since_epoch()
+ ).count() + 60000000; // 60 seconds
+ // token, liquidity, amountTokenMin, amountNativeMin, to, deadline
+ Hash removeLiquidityTx = sdk.callFunction(
+ router, uint256_t("100000000000000000000"), &DEXV2Router02::removeLiquidityNative,
+ tokenA, uint256_t("50000000000000000000"), uint256_t("10000000000000000000"),
+ uint256_t("10000000000000000000"), owner, deadline
+ );
+
+ // Check if operation worked successfully
+ pair = sdk.callViewFunction(factory, &DEXV2Factory::getPair, tokenA, wrapped);
+ ownerTknA = sdk.callViewFunction(tokenA, &ERC20::balanceOf, owner);
+ ownerNative = sdk.getNativeBalance(owner);
+ pairTknA = sdk.callViewFunction(tokenA, &ERC20::balanceOf, pair);
+ wrappedNative = sdk.getNativeBalance(wrapped);
+ pairNativeWrapped = sdk.callViewFunction(wrapped, &ERC20::balanceOf, pair);
+ REQUIRE(ownerTknA == uint256_t("9950000000000000000000"));
+ REQUIRE(ownerNative >= ownerNativeBeforeSubLiq - uint256_t("100000000000000000000") - (uint256_t(1000000000) * 21000));
+ REQUIRE(pairTknA == uint256_t("50000000000000000000"));
+ REQUIRE(wrappedNative == uint256_t("50000000000000000000"));
+ REQUIRE(pairNativeWrapped == uint256_t("50000000000000000000"));
}
}
}
diff --git a/tests/contract/erc721.cpp b/tests/contract/erc721test.cpp
similarity index 84%
rename from tests/contract/erc721.cpp
rename to tests/contract/erc721test.cpp
index 9476cd63..203bc2d3 100644
--- a/tests/contract/erc721.cpp
+++ b/tests/contract/erc721test.cpp
@@ -10,18 +10,34 @@
#include "../sdktestsuite.hpp"
-namespace TERC721 {
- TEST_CASE("ERC721 Class", "[contract][erc721]") {
- SECTION("ERC721 Creation") {
- SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721Creation");
- auto ERC721Address = sdk.deployContract(std::string("My Test NFT!"), std::string("NFT"), uint64_t(100));
+namespace TERC721Test {
+ TEST_CASE("ERC721Test Class", "[contract][erc721test]") {
+ SECTION("ERC721Test Creation + Dump") {
+ Address ERC721Address;
+ std::unique_ptr options;
+ {
+ SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721TestCreation");
+ ERC721Address = sdk.deployContract(std::string("My Test NFT!"), std::string("NFT"), uint64_t(100));
+ REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::name) == "My Test NFT!");
+ REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::symbol) == "NFT");
+ REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::maxTokens) == 100);
+ REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::tokenIdCounter) == 0);
+ // Dump to database
+ options = std::make_unique(sdk.getOptions());
+ sdk.getState().saveToDB();
+ }
+
+ // SDKTestSuite should automatically load the state from the DB if we construct it with an Options object
+ // (The createNewEnvironment DELETES the DB if any is found)
+ SDKTestSuite sdk(*options);
REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::name) == "My Test NFT!");
REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::symbol) == "NFT");
REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::maxTokens) == 100);
+ REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::tokenIdCounter) == 0);
}
- SECTION("ERC721 Mint 100 Token Same Address") {
- SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721Mint100TokenSameAddress");
+ SECTION("ERC721Test Mint 100 Token Same Address") {
+ SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721TestMint100TokenSameAddress");
auto ERC721Address = sdk.deployContract(std::string("My Test NFT!"), std::string("NFT"), uint64_t(100));
for (uint64_t i = 0; i < 100; ++i) {
sdk.callFunction(ERC721Address, &ERC721Test::mint, sdk.getChainOwnerAccount().address);
@@ -35,13 +51,13 @@ namespace TERC721 {
REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::totalSupply) == 100);
}
- SECTION("ERC721 Mint 100 Different Addresses") {
+ SECTION("ERC721Test Mint 100 Different Addresses") {
// Generate 100 different TestAccounts
std::vector accounts;
for (int i = 0; i < 100; ++i) {
accounts.emplace_back(TestAccount::newRandomAccount());
}
- SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721Mint100DifferentAddresses", accounts);
+ SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721TestMint100DifferentAddresses", accounts);
auto ERC721Address = sdk.deployContract(std::string("My Test NFT!"), std::string("NFT"), uint64_t(100));
for (int i = 0; i < 100; ++i) {
sdk.callFunction(ERC721Address, &ERC721Test::mint, accounts[i].address);
@@ -54,13 +70,13 @@ namespace TERC721 {
REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::totalSupply) == 100);
}
- SECTION("ERC721 Mint 100 Different Addresses reverse") {
+ SECTION("ERC721Test Mint 100 Different Addresses reverse") {
// Same as before, but we mint from the test accounts list in reverse order
std::vector accounts;
for (int i = 0; i < 100; ++i) {
accounts.emplace_back(TestAccount::newRandomAccount());
}
- SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721Mint100DifferentAddressesReverse", accounts);
+ SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721TestMint100DifferentAddressesReverse", accounts);
auto ERC721Address = sdk.deployContract(std::string("My Test NFT!"), std::string("NFT"), uint64_t(100));
for (int i = 99; i >= 0; i--) {
sdk.callFunction(ERC721Address, &ERC721Test::mint, accounts[i].address);
@@ -74,8 +90,8 @@ namespace TERC721 {
REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::totalSupply) == 100);
}
- SECTION("ERC721 Mint 100 and Burn 100 Same Address") {
- SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721Mint100AndBurn100SameAddress");
+ SECTION("ERC721Test Mint 100 and Burn 100 Same Address") {
+ SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721TestMint100AndBurn100SameAddress");
auto ERC721Address = sdk.deployContract(std::string("My Test NFT!"), std::string("NFT"), uint64_t(100));
for (uint64_t i = 0; i < 100; ++i) {
sdk.callFunction(ERC721Address, &ERC721Test::mint, sdk.getChainOwnerAccount().address);
@@ -97,13 +113,13 @@ namespace TERC721 {
REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::balanceOf, sdk.getChainOwnerAccount().address) == 0);
}
- SECTION("ERC721 Mint 100 Different Address Burn 100 Different Address") {
+ SECTION("ERC721Test Mint 100 Different Address Burn 100 Different Address") {
// Generate 100 different TestAccounts
std::vector accounts;
for (int i = 0; i < 100; ++i) {
accounts.emplace_back(TestAccount::newRandomAccount());
}
- SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721Mint100DifferentAddressBurn100DifferentAddress", accounts);
+ SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721TestMint100DifferentAddressBurn100DifferentAddress", accounts);
auto ERC721Address = sdk.deployContract(std::string("My Test NFT!"), std::string("NFT"), uint64_t(100));
for (int i = 0; i < 100; ++i) {
sdk.callFunction(ERC721Address, &ERC721Test::mint, accounts[i].address);
@@ -125,13 +141,13 @@ namespace TERC721 {
}
}
- SECTION("ERC721 Mint 100 Different Address Burn With Allowance") {
+ SECTION("ERC721Test Mint 100 Different Address Burn With Allowance") {
// Generate 100 different TestAccounts
std::vector accounts;
for (int i = 0; i < 100; ++i) {
accounts.emplace_back(TestAccount::newRandomAccount());
}
- SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721Mint100DifferentAddressBurnWithAllowance", accounts);
+ SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721TestMint100DifferentAddressBurnWithAllowance", accounts);
auto ERC721Address = sdk.deployContract(std::string("My Test NFT!"), std::string("NFT"), uint64_t(100));
for (int i = 0; i < 100; ++i) {
sdk.callFunction(ERC721Address, &ERC721Test::mint, accounts[i].address);
@@ -173,14 +189,14 @@ namespace TERC721 {
}
}
- SECTION("ERC721 transferFrom with allowance from 100 different accounts") {
+ SECTION("ERC721Test transferFrom with allowance from 100 different accounts") {
// Generate 100 different TestAccounts
std::vector accounts;
for (int i = 0; i < 100; ++i) {
accounts.emplace_back(TestAccount::newRandomAccount());
}
TestAccount accountToSent = TestAccount::newRandomAccount();
- SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721Mint100DifferentAddressBurnWithAllowance", accounts);
+ SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721TestMint100DifferentAddressBurnWithAllowance", accounts);
auto ERC721Address = sdk.deployContract(std::string("My Test NFT!"), std::string("NFT"), uint64_t(100));
for (int i = 0; i < 100; ++i) {
sdk.callFunction(ERC721Address, &ERC721Test::mint, accounts[i].address);
diff --git a/tests/contract/event.cpp b/tests/contract/event.cpp
new file mode 100644
index 00000000..02f1dda3
--- /dev/null
+++ b/tests/contract/event.cpp
@@ -0,0 +1,166 @@
+/*
+Copyright (c) [2023-2024] [AppLayer Developers]
+
+This software is distributed under the MIT License.
+See the LICENSE.txt file in the project root for more information.
+*/
+
+#include "../../src/libs/catch2/catch_amalgamated.hpp"
+
+#include "../../src/contract/event.h"
+
+#include "../../src/bytes/view.h"
+
+namespace TEvent {
+ TEST_CASE("Event Class", "[contract][event]") {
+ SECTION("Event Constructor (EVM)") {
+ Hash txHash = Hash::random();
+ Hash blockHash = Hash::random();
+ std::vector topics = {Hash::random(), Hash::random(), Hash::random(), Hash::random(), Hash::random()};
+ Address add("0x1234567890123456789012345678901234567890", false);
+ Bytes data{0xDE, 0xAD, 0xBE, 0xEF};
+
+ Event e("myEvent", 0, txHash, 1, blockHash, 2, add, data, topics, false);
+ REQUIRE(e.getName() == "myEvent");
+ REQUIRE(e.getLogIndex() == 0);
+ REQUIRE(e.getTxHash() == txHash);
+ REQUIRE(e.getTxIndex() == 1);
+ REQUIRE(e.getBlockHash() == blockHash);
+ REQUIRE(e.getBlockIndex() == 2);
+ REQUIRE(e.getAddress() == add);
+ REQUIRE(e.getData() == data);
+ REQUIRE(e.getTopics().size() == 5);
+ REQUIRE(e.getTopics()[0] == topics[0]);
+ REQUIRE(e.getTopics()[1] == topics[1]);
+ REQUIRE(e.getTopics()[2] == topics[2]);
+ REQUIRE(e.getTopics()[3] == topics[3]);
+ REQUIRE(e.getTopics()[4] == topics[4]);
+ REQUIRE(e.isAnonymous() == false);
+ REQUIRE(e.getSelector() == topics[0]);
+ }
+
+ SECTION("Event Constructor (CPP)") {
+ Hash txHash = Hash::random();
+ Hash blockHash = Hash::random();
+ Address add("0x1234567890123456789012345678901234567890", false);
+
+ // Anonymous event
+ Event e1("myEvent", 0, txHash, 1, blockHash, 2, add, std::make_tuple(
+ EventParam("p1"),
+ EventParam("p2"),
+ EventParam("p3"),
+ EventParam("p4"),
+ EventParam("p5")
+ ), true);
+ REQUIRE(e1.getName() == "myEvent");
+ REQUIRE(e1.getLogIndex() == 0);
+ REQUIRE(e1.getTxHash() == txHash);
+ REQUIRE(e1.getTxIndex() == 1);
+ REQUIRE(e1.getBlockHash() == blockHash);
+ REQUIRE(e1.getBlockIndex() == 2);
+ REQUIRE(e1.getAddress() == add);
+ REQUIRE(e1.getData() == Bytes());
+ REQUIRE(e1.getTopics().size() == 4); // topics only goes up to 4 even though we put 5
+ REQUIRE(e1.isAnonymous() == true);
+ REQUIRE(e1.getSelector() == Hash());
+
+ // Non-anonymous event
+ Event e2("myEvent", 0, txHash, 1, blockHash, 2, add, std::make_tuple(
+ EventParam("p1"),
+ EventParam("p2"),
+ EventParam("p3"),
+ EventParam("p4"),
+ EventParam("p5")
+ ), false);
+ REQUIRE(e2.getName() == "myEvent");
+ REQUIRE(e2.getLogIndex() == 0);
+ REQUIRE(e2.getTxHash() == txHash);
+ REQUIRE(e2.getTxIndex() == 1);
+ REQUIRE(e2.getBlockHash() == blockHash);
+ REQUIRE(e2.getBlockIndex() == 2);
+ REQUIRE(e2.getAddress() == add);
+ REQUIRE(e2.getData() == Bytes());
+ REQUIRE(e2.getTopics().size() == 4); // topics only goes up to 4 (event signature + 3) even though we put 5
+ REQUIRE(e2.isAnonymous() == false);
+ REQUIRE(e2.getSelector() != Hash());
+ }
+
+ SECTION("Event Constructor (JSON String)") {
+ // Copied from above
+ std::string e1Str = "{\"name\":\"myEvent\",\"logIndex\":0,\"txHash\":\"0x05846d60d5b92b068c28a9017831e29827243bd4f642734977dcb111ccd40425\",\"txIndex\":1,\"blockHash\":\"0xec313458e29969850621411a36ab7b93c2b494ffb1ca77b5e62ea5b42100674d\",\"blockIndex\":2,\"address\":\"0x1234567890123456789012345678901234567890\",\"data\":[],\"topics\":[\"0x260e065801cba6ca065f28640c3d94ef235f67db5431448aae1a51af7214efaf\",\"0xc30a3ae685bfcb917dceb41e4afed5342f332572d5b3a8212679077685c494cb\",\"0xff05acda0d6ef15409d713cb0f124d2c3a3fd95b33af096109172229d5c8671a\",\"0x0c38459a0b5ed2a98afa2a407dc31ff1744c1c3159dbbbd3aef8736778a0e063\"],\"anonymous\":true}";
+ std::string e2Str = "{\"name\":\"myEvent\",\"logIndex\":0,\"txHash\":\"0x05846d60d5b92b068c28a9017831e29827243bd4f642734977dcb111ccd40425\",\"txIndex\":1,\"blockHash\":\"0xec313458e29969850621411a36ab7b93c2b494ffb1ca77b5e62ea5b42100674d\",\"blockIndex\":2,\"address\":\"0x1234567890123456789012345678901234567890\",\"data\":[],\"topics\":[\"0x386cc2513b9e8b9e78a0792c33d6f69798774e0fa5424d3042fdd0fe7647420b\",\"0x260e065801cba6ca065f28640c3d94ef235f67db5431448aae1a51af7214efaf\",\"0xc30a3ae685bfcb917dceb41e4afed5342f332572d5b3a8212679077685c494cb\",\"0xff05acda0d6ef15409d713cb0f124d2c3a3fd95b33af096109172229d5c8671a\"],\"anonymous\":false}";
+
+ Event e1(e1Str);
+ Event e2(e2Str);
+
+ REQUIRE(e1.getName() == "myEvent");
+ REQUIRE(e1.getLogIndex() == 0);
+ REQUIRE(e1.getTxHash().hex(true).get() == "0x05846d60d5b92b068c28a9017831e29827243bd4f642734977dcb111ccd40425");
+ REQUIRE(e1.getTxIndex() == 1);
+ REQUIRE(e1.getBlockHash().hex(true).get() == "0xec313458e29969850621411a36ab7b93c2b494ffb1ca77b5e62ea5b42100674d");
+ REQUIRE(e1.getBlockIndex() == 2);
+ REQUIRE(e1.getAddress().hex(true).get() == "0x1234567890123456789012345678901234567890");
+ REQUIRE(e1.getData() == Bytes());
+ REQUIRE(e1.getTopics().size() == 4);
+ REQUIRE(e1.getTopics()[0].hex(true).get() == "0x260e065801cba6ca065f28640c3d94ef235f67db5431448aae1a51af7214efaf");
+ REQUIRE(e1.getTopics()[1].hex(true).get() == "0xc30a3ae685bfcb917dceb41e4afed5342f332572d5b3a8212679077685c494cb");
+ REQUIRE(e1.getTopics()[2].hex(true).get() == "0xff05acda0d6ef15409d713cb0f124d2c3a3fd95b33af096109172229d5c8671a");
+ REQUIRE(e1.getTopics()[3].hex(true).get() == "0x0c38459a0b5ed2a98afa2a407dc31ff1744c1c3159dbbbd3aef8736778a0e063");
+ REQUIRE(e1.isAnonymous() == true);
+ REQUIRE(e1.getSelector() == Hash());
+
+ REQUIRE(e2.getName() == "myEvent");
+ REQUIRE(e2.getLogIndex() == 0);
+ REQUIRE(e2.getTxHash().hex(true).get() == "0x05846d60d5b92b068c28a9017831e29827243bd4f642734977dcb111ccd40425");
+ REQUIRE(e2.getTxIndex() == 1);
+ REQUIRE(e2.getBlockHash().hex(true).get() == "0xec313458e29969850621411a36ab7b93c2b494ffb1ca77b5e62ea5b42100674d");
+ REQUIRE(e2.getBlockIndex() == 2);
+ REQUIRE(e2.getAddress().hex(true).get() == "0x1234567890123456789012345678901234567890");
+ REQUIRE(e2.getData() == Bytes());
+ REQUIRE(e2.getTopics().size() == 4);
+ REQUIRE(e2.getTopics()[0].hex(true).get() == "0x386cc2513b9e8b9e78a0792c33d6f69798774e0fa5424d3042fdd0fe7647420b");
+ REQUIRE(e2.getTopics()[1].hex(true).get() == "0x260e065801cba6ca065f28640c3d94ef235f67db5431448aae1a51af7214efaf");
+ REQUIRE(e2.getTopics()[2].hex(true).get() == "0xc30a3ae685bfcb917dceb41e4afed5342f332572d5b3a8212679077685c494cb");
+ REQUIRE(e2.getTopics()[3].hex(true).get() == "0xff05acda0d6ef15409d713cb0f124d2c3a3fd95b33af096109172229d5c8671a");
+ REQUIRE(e2.isAnonymous() == false);
+ REQUIRE(e2.getSelector() != Hash());
+ }
+
+ SECTION("Event Serialization (Normal + RPC)") {
+ Hash txHash(Hex::toBytes("0x53472c61f1db8612fcdd17f24b78986bfa111ea3e323522456b1a78560f2215a"));
+ Hash blockHash(Hex::toBytes("0x2b9b8644330d50ffb90c5fea02b73b562dfc550ec7f8c85f643b20391a972d5f"));
+ Address add("0x1234567890123456789012345678901234567890", false);
+ Event e1("myEvent", 0, txHash, 1, blockHash, 2, add, std::make_tuple(
+ EventParam("p1"),
+ EventParam("p2"),
+ EventParam("p3"),
+ EventParam("p4"),
+ EventParam("p5")
+ ), false);
+
+ std::string e1Str = e1.serializeToJson();
+ json e1Json = e1.serializeForRPC();
+
+ REQUIRE(e1Str == "{\"name\":\"myEvent\",\"logIndex\":0,\"txHash\":\"0x53472c61f1db8612fcdd17f24b78986bfa111ea3e323522456b1a78560f2215a\",\"txIndex\":1,\"blockHash\":\"0x2b9b8644330d50ffb90c5fea02b73b562dfc550ec7f8c85f643b20391a972d5f\",\"blockIndex\":2,\"address\":\"0x1234567890123456789012345678901234567890\",\"data\":[],\"topics\":[\"0x386cc2513b9e8b9e78a0792c33d6f69798774e0fa5424d3042fdd0fe7647420b\",\"0x260e065801cba6ca065f28640c3d94ef235f67db5431448aae1a51af7214efaf\",\"0xc30a3ae685bfcb917dceb41e4afed5342f332572d5b3a8212679077685c494cb\",\"0xff05acda0d6ef15409d713cb0f124d2c3a3fd95b33af096109172229d5c8671a\"],\"anonymous\":false}");
+ REQUIRE(e1Json.dump() == "{\"address\":\"0x1234567890123456789012345678901234567890\",\"blockHash\":\"0x2b9b8644330d50ffb90c5fea02b73b562dfc550ec7f8c85f643b20391a972d5f\",\"blockNumber\":\"0x0000000000000002\",\"data\":\"0x\",\"logIndex\":\"0x0000000000000000\",\"removed\":false,\"topics\":[\"0x386cc2513b9e8b9e78a0792c33d6f69798774e0fa5424d3042fdd0fe7647420b\",\"0x260e065801cba6ca065f28640c3d94ef235f67db5431448aae1a51af7214efaf\",\"0xc30a3ae685bfcb917dceb41e4afed5342f332572d5b3a8212679077685c494cb\",\"0xff05acda0d6ef15409d713cb0f124d2c3a3fd95b33af096109172229d5c8671a\"],\"transactionHash\":\"0x53472c61f1db8612fcdd17f24b78986bfa111ea3e323522456b1a78560f2215a\",\"transactionIndex\":\"0x0000000000000001\"}");
+
+ Event e2(e1Str);
+ REQUIRE(e2.getName() == "myEvent");
+ REQUIRE(e2.getLogIndex() == 0);
+ REQUIRE(e2.getTxHash() == txHash);
+ REQUIRE(e2.getTxIndex() == 1);
+ REQUIRE(e2.getBlockHash() == blockHash);
+ REQUIRE(e2.getBlockIndex() == 2);
+ REQUIRE(e2.getAddress() == add);
+ REQUIRE(e2.getData() == Bytes());
+ REQUIRE(e2.getTopics().size() == 4);
+ REQUIRE(e2.getTopics()[0].hex(true).get() == "0x386cc2513b9e8b9e78a0792c33d6f69798774e0fa5424d3042fdd0fe7647420b");
+ REQUIRE(e2.getTopics()[1].hex(true).get() == "0x260e065801cba6ca065f28640c3d94ef235f67db5431448aae1a51af7214efaf");
+ REQUIRE(e2.getTopics()[2].hex(true).get() == "0xc30a3ae685bfcb917dceb41e4afed5342f332572d5b3a8212679077685c494cb");
+ REQUIRE(e2.getTopics()[3].hex(true).get() == "0xff05acda0d6ef15409d713cb0f124d2c3a3fd95b33af096109172229d5c8671a");
+ REQUIRE(e2.isAnonymous() == false);
+ REQUIRE(e2.getSelector() != Hash());
+ }
+ }
+}
+
diff --git a/tests/contract/evm.cpp b/tests/contract/evm.cpp
index 712f198a..966d9178 100644
--- a/tests/contract/evm.cpp
+++ b/tests/contract/evm.cpp
@@ -121,9 +121,9 @@ namespace TEVM {
sdk.getState().saveToDB();
}
- /// SDKTestSuite should automatically load the state from the DB if we
- /// Construct it with a options object
- /// (The createNewEnvironment DELETES the DB if any is found)
+ // SDKTestSuite should automatically load the state from the DB if we
+ // Construct it with a options object
+ // (The createNewEnvironment DELETES the DB if any is found)
SDKTestSuite sdk(*options);
REQUIRE(sdk.callViewFunction(erc20Address, &ERC20::name) == "TestToken");
diff --git a/tests/contract/pebble.cpp b/tests/contract/pebble.cpp
index cdd5c1f6..7d18afdf 100644
--- a/tests/contract/pebble.cpp
+++ b/tests/contract/pebble.cpp
@@ -13,9 +13,31 @@ See the LICENSE.txt file in the project root for more information.
namespace TPEBBLE {
TEST_CASE("Pebble Class", "[contract][pebble]") {
- SECTION("Pebble creation") {
- SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testPebbleCreation");
- Address pebbleAddr = sdk.deployContract(uint256_t(100000));
+ SECTION("Pebble creation + dump") {
+ Address pebbleAddr;
+ std::unique_ptr options;
+ {
+ SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testPebbleCreation");
+ pebbleAddr = sdk.deployContract(uint256_t(100000));
+ REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::name) == "Pebble");
+ REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::symbol) == "PBL");
+ REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::totalSupply) == uint256_t(0));
+ REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::maxSupply) == uint256_t(100000));
+ REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::totalNormal) == uint64_t(0));
+ REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::totalGold) == uint64_t(0));
+ REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::totalDiamond) == uint64_t(0));
+ REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::raritySeed) == uint256_t(1000000));
+ REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::diamondRarity) == uint256_t(1));
+ REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::goldRarity) == uint256_t(10));
+ REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::getAuthorizer) == Address());
+ // Dump to database
+ options = std::make_unique(sdk.getOptions());
+ sdk.getState().saveToDB();
+ }
+
+ // SDKTestSuite should automatically load the state from the DB if we construct it with an Options object
+ // (The createNewEnvironment DELETES the DB if any is found)
+ SDKTestSuite sdk(*options);
REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::name) == "Pebble");
REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::symbol) == "PBL");
REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::totalSupply) == uint256_t(0));
@@ -28,6 +50,25 @@ namespace TPEBBLE {
REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::goldRarity) == uint256_t(10));
REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::getAuthorizer) == Address());
}
+
+ SECTION("Pebble ownership transfer (Ownable coverage)") {
+ SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testPebbleOwnershipTransfer");
+ Address pebbleAddr = sdk.deployContract(uint256_t(100000));
+ REQUIRE_THROWS(sdk.callFunction(pebbleAddr, &Pebble::transferOwnership, Address())); // cannot transfer to zero address
+ REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::owner) == Address("0x00dead00665771855a34155f5e7405489df2c3c6", false));
+ Address newOwner("0x1234567890123456789012345678901234567890", false);
+ sdk.callFunction(pebbleAddr, &Pebble::transferOwnership, newOwner);
+ REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::owner) == newOwner);
+ }
+
+ SECTION("Pebble ownership renounce (Ownable coverage)") {
+ SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testPebbleOwnershipTransfer");
+ Address pebbleAddr = sdk.deployContract(uint256_t(100000));
+ REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::owner) == Address("0x00dead00665771855a34155f5e7405489df2c3c6", false));
+ sdk.callFunction(pebbleAddr, &Pebble::renounceOwnership);
+ REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::owner) == Address());
+ }
+
SECTION("Pebble minting") {
std::unique_ptr opts = nullptr;
TestAccount authorizerAccount = TestAccount::newRandomAccount();
diff --git a/tests/contract/randomness.cpp b/tests/contract/randomness.cpp
index f39eca37..f601cab2 100644
--- a/tests/contract/randomness.cpp
+++ b/tests/contract/randomness.cpp
@@ -36,35 +36,62 @@ namespace TContractRandomness {
TEST_CASE("Contract Randomness", "[contract][randomness]") {
SECTION("CPP Randomness Test") {
auto sdk = SDKTestSuite::createNewEnvironment("CPPContractRandomness");
-
auto randomnessContractAddr = sdk.deployContract();
-
REQUIRE(sdk.callViewFunction(randomnessContractAddr, &RandomnessTest::getRandom) == 0);
- auto setRandomTx = sdk.callFunction(randomnessContractAddr, &RandomnessTest::setRandom);
// The random value should be the first RandomGen operator 0
// with the seed being Hash(blockRandomness + txIndex)
// TxIndex is 0, so the seed should be the blockRandomness
+ auto setRandomTx = sdk.callFunction(randomnessContractAddr, &RandomnessTest::setRandom);
Hash randomnessSeed = sdk.getStorage().latest()->getBlockRandomness();
RandomGen randomGen(randomnessSeed);
-
REQUIRE(sdk.callViewFunction(randomnessContractAddr, &RandomnessTest::getRandom) == randomGen.operator()());
-
}
+
SECTION("EVM Randomness Test") {
auto randomnessBytecode = Hex::toBytes("6080604052348015600e575f80fd5b506101b08061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c806353e7209e14610038578063aacc5a1714610042575b5f80fd5b610040610060565b005b61004a6100e8565b6040516100579190610108565b60405180910390f35b73100000000000000000000000000010000000000173ffffffffffffffffffffffffffffffffffffffff1663aacc5a176040518163ffffffff1660e01b8152600401602060405180830381865afa1580156100bd573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100e1919061014f565b5f81905550565b5f8054905090565b5f819050919050565b610102816100f0565b82525050565b5f60208201905061011b5f8301846100f9565b92915050565b5f80fd5b61012e816100f0565b8114610138575f80fd5b50565b5f8151905061014981610125565b92915050565b5f6020828403121561016457610163610121565b5b5f6101718482850161013b565b9150509291505056fea26469706673582212206ffd6a41e2097987a251d467fce209c21bde13b5a81c4123a0b5a0aa7f62153b64736f6c63430008190033");
auto sdk = SDKTestSuite::createNewEnvironment("EVMContractRandomness");
auto randomnessContractAddr = sdk.deployBytecode(randomnessBytecode);
REQUIRE(sdk.callViewFunction(randomnessContractAddr, &RandomnessTest::getRandom) == 0);
- auto setRandomTx = sdk.callFunction(randomnessContractAddr, &RandomnessTest::setRandom);
// The random value should be the first RandomGen operator 0
// with the seed being Hash(blockRandomness + txIndex)
// TxIndex is 0, so the seed should be the blockRandomness
+ auto setRandomTx = sdk.callFunction(randomnessContractAddr, &RandomnessTest::setRandom);
Hash randomnessSeed = sdk.getStorage().latest()->getBlockRandomness();
RandomGen randomGen(randomnessSeed);
-
REQUIRE(sdk.callViewFunction(randomnessContractAddr, &RandomnessTest::getRandom) == randomGen.operator()());
}
+
+ SECTION("Randomness Test DB Dump") {
+ Address randomnessContractAddr;
+ uint256_t randomNum;
+ std::unique_ptr options;
+ Hash randomnessSeed;
+ {
+ auto sdk = SDKTestSuite::createNewEnvironment("DumpContractRandomness");
+ randomnessContractAddr = sdk.deployContract();
+ REQUIRE(sdk.callViewFunction(randomnessContractAddr, &RandomnessTest::getRandom) == 0);
+
+ // The random value should be the first RandomGen operator 0
+ // with the seed being Hash(blockRandomness + txIndex)
+ // TxIndex is 0, so the seed should be the blockRandomness
+ auto setRandomTx = sdk.callFunction(randomnessContractAddr, &RandomnessTest::setRandom);
+ randomnessSeed = sdk.getStorage().latest()->getBlockRandomness();
+ RandomGen randomGen(randomnessSeed);
+ randomNum = randomGen.operator()();
+ REQUIRE(sdk.callViewFunction(randomnessContractAddr, &RandomnessTest::getRandom) == randomNum);
+
+ // Dump to database
+ options = std::make_unique(sdk.getOptions());
+ sdk.getState().saveToDB();
+ }
+
+ // SDKTestSuite should automatically load the state from the DB if we construct it with an Options object
+ // (The createNewEnvironment DELETES the DB if any is found)
+ SDKTestSuite sdk(*options);
+ REQUIRE(sdk.callViewFunction(randomnessContractAddr, &RandomnessTest::getRandom) == randomNum);
+ }
}
}
+
diff --git a/tests/contract/simplecontract.cpp b/tests/contract/simplecontract.cpp
index c3440553..fd34d7d3 100644
--- a/tests/contract/simplecontract.cpp
+++ b/tests/contract/simplecontract.cpp
@@ -67,5 +67,38 @@ namespace TSimpleContract {
REQUIRE(numberEvent.size() == 1);
REQUIRE(tupleEvent.size() == 1);
}
+
+ SECTION("SimpleContract setNamesAndNumbers and setNamesAndNumbersInTuple") {
+ SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testSimpleContractSetNamesAndNumbersInTuple");
+ Address simpleContract = sdk.deployContract(
+ std::string("TestName"), uint256_t(19283187581),
+ std::make_tuple(std::string("TupleName"), uint256_t(987654321))
+ );
+
+ std::string name = sdk.callViewFunction(simpleContract, &SimpleContract::getName);
+ uint256_t number = sdk.callViewFunction(simpleContract, &SimpleContract::getNumber);
+ std::tuple tuple = sdk.callViewFunction(simpleContract, &SimpleContract::getTuple);
+ REQUIRE(name == "TestName");
+ REQUIRE(number == 19283187581);
+ REQUIRE(std::get<0>(tuple) == "TupleName");
+ REQUIRE(std::get<1>(tuple) == 987654321);
+
+ std::vector names = {"111", "222", "333"};
+ std::vector numbers = {400, 300, 200, 100};
+ Hash namesAndNumbersTx = sdk.callFunction(simpleContract, &SimpleContract::setNamesAndNumbers, names, numbers);
+ name = sdk.callViewFunction(simpleContract, &SimpleContract::getName);
+ number = sdk.callViewFunction(simpleContract, &SimpleContract::getNumber);
+ REQUIRE(name == "111222333");
+ REQUIRE(number == 1000);
+
+ std::vector> list = {
+ {"555", 1000}, {"444", 500}, {"333", 250}, {"222", 150}, {"111", 100}
+ };
+ Hash namesAndNumbersInTupleTx = sdk.callFunction(simpleContract, &SimpleContract::setNamesAndNumbersInTuple, list);
+ name = sdk.callViewFunction(simpleContract, &SimpleContract::getName);
+ number = sdk.callViewFunction(simpleContract, &SimpleContract::getNumber);
+ REQUIRE(name == "555444333222111");
+ REQUIRE(number == 2000);
+ }
}
}
diff --git a/tests/contract/throwtest.cpp b/tests/contract/throwtest.cpp
new file mode 100644
index 00000000..9b71d27b
--- /dev/null
+++ b/tests/contract/throwtest.cpp
@@ -0,0 +1,61 @@
+/*
+Copyright (c) [2023-2024] [AppLayer Developers]
+
+This software is distributed under the MIT License.
+See the LICENSE.txt file in the project root for more information.
+*/
+
+#include "../../src/libs/catch2/catch_amalgamated.hpp"
+
+#include "../../src/contract/templates/throwtestA.h"
+#include "../../src/contract/templates/throwtestB.h"
+#include "../../src/contract/templates/throwtestC.h"
+
+#include "../sdktestsuite.hpp"
+
+namespace TThrowTest {
+ TEST_CASE("ThrowTest Contracts", "[contract][throwtest]") {
+ SECTION("ThrowTest Coverage") {
+ // Everything in one section because yes
+ Address throwA;
+ Address throwB;
+ Address throwC;
+ std::unique_ptr options;
+ {
+ SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testThrowTestCreation");
+ throwA = sdk.deployContract();
+ throwB = sdk.deployContract();
+ throwC = sdk.deployContract();
+ REQUIRE(sdk.callViewFunction(throwA, &ThrowTestA::getNumA) == 0);
+ REQUIRE(sdk.callViewFunction(throwB, &ThrowTestB::getNumB) == 0);
+ REQUIRE(sdk.callViewFunction(throwC, &ThrowTestC::getNumC) == 0);
+
+ // A and B always throw, C is the only one that keeps the value
+ REQUIRE_THROWS(sdk.callFunction(throwA, &ThrowTestA::setNumA, uint8_t(1), throwB, uint8_t(2), throwC, uint8_t(3)));
+ REQUIRE(sdk.callViewFunction(throwA, &ThrowTestA::getNumA) == 0);
+ REQUIRE(sdk.callViewFunction(throwB, &ThrowTestB::getNumB) == 0);
+ REQUIRE(sdk.callViewFunction(throwC, &ThrowTestC::getNumC) == 0);
+ REQUIRE_THROWS(sdk.callFunction(throwB, &ThrowTestB::setNumB, uint8_t(4), throwC, uint8_t(5)));
+ REQUIRE(sdk.callViewFunction(throwA, &ThrowTestA::getNumA) == 0);
+ REQUIRE(sdk.callViewFunction(throwB, &ThrowTestB::getNumB) == 0);
+ REQUIRE(sdk.callViewFunction(throwC, &ThrowTestC::getNumC) == 0);
+ sdk.callFunction(throwC, &ThrowTestC::setNumC, uint8_t(6));
+ REQUIRE(sdk.callViewFunction(throwA, &ThrowTestA::getNumA) == 0);
+ REQUIRE(sdk.callViewFunction(throwB, &ThrowTestB::getNumB) == 0);
+ REQUIRE(sdk.callViewFunction(throwC, &ThrowTestC::getNumC) == 6);
+
+ // Dump to database
+ options = std::make_unique(sdk.getOptions());
+ sdk.getState().saveToDB();
+ }
+
+ // SDKTestSuite should automatically load the state from the DB if we construct it with an Options object
+ // (The createNewEnvironment DELETES the DB if any is found)
+ SDKTestSuite sdk(*options);
+ REQUIRE(sdk.callViewFunction(throwA, &ThrowTestA::getNumA) == 0);
+ REQUIRE(sdk.callViewFunction(throwB, &ThrowTestB::getNumB) == 0);
+ REQUIRE(sdk.callViewFunction(throwC, &ThrowTestC::getNumC) == 6);
+ }
+ }
+}
+
diff --git a/tests/contract/variables/safeint_t.cpp b/tests/contract/variables/safeint_t.cpp
index 74a43eb2..5df3f0c3 100644
--- a/tests/contract/variables/safeint_t.cpp
+++ b/tests/contract/variables/safeint_t.cpp
@@ -70,13 +70,19 @@ template struct SafeIntTester {
SafeInt val(UnderlyingType(-42));
SafeInt valOver(std::numeric_limits::max());
SafeInt valUnder(std::numeric_limits::min());
- bool hadOver = false;
- bool hadUnder = false;
+ bool hadOver1 = false;
+ bool hadOver2 = false;
+ bool hadUnder1 = false;
+ bool hadUnder2 = false;
// catch over/underflow
- try { valOver = valOver + UnderlyingType(1); } catch (std::overflow_error& e) { hadOver = true; }
- try { valUnder = valUnder + UnderlyingType(-1); } catch (std::underflow_error& e) { hadUnder = true; }
- REQUIRE(hadOver);
- REQUIRE(hadUnder);
+ try { valOver = valOver + UnderlyingType(1); } catch (std::overflow_error& e) { hadOver1 = true; }
+ try { valOver = valOver + valOver; } catch (std::overflow_error& e) { hadOver2 = true; }
+ try { valUnder = valUnder + UnderlyingType(-1); } catch (std::underflow_error& e) { hadUnder1 = true; }
+ try { valUnder = valUnder + valUnder; } catch (std::underflow_error& e) { hadUnder2 = true; }
+ REQUIRE(hadOver1);
+ REQUIRE(hadOver2);
+ REQUIRE(hadUnder1);
+ REQUIRE(hadUnder2);
// operate with int
val = val + UnderlyingType(5);
val.revert();
@@ -98,13 +104,21 @@ template struct SafeIntTester {
SafeInt val(UnderlyingType(-42));
SafeInt valOver(std::numeric_limits::max());
SafeInt valUnder(std::numeric_limits::min());
- bool hadOver = false;
- bool hadUnder = false;
+ SafeInt valOverMinus(UnderlyingType(-1));
+ SafeInt valUnderMinus(UnderlyingType(1));
+ bool hadOver1 = false;
+ bool hadOver2 = false;
+ bool hadUnder1 = false;
+ bool hadUnder2 = false;
// catch over/underflow
- try { valOver = valOver - UnderlyingType(-1); } catch (std::overflow_error& e) { hadOver = true; }
- try { valUnder = valUnder - UnderlyingType(1); } catch (std::underflow_error& e) { hadUnder = true; }
- REQUIRE(hadOver);
- REQUIRE(hadUnder);
+ try { valOver = valOver - UnderlyingType(-1); } catch (std::overflow_error& e) { hadOver1 = true; }
+ try { valOver = valOver - valOverMinus; } catch (std::overflow_error& e) { hadOver2 = true; }
+ try { valUnder = valUnder - UnderlyingType(1); } catch (std::underflow_error& e) { hadUnder1 = true; }
+ try { valUnder = valUnder - valUnderMinus; } catch (std::underflow_error& e) { hadUnder2 = true; }
+ REQUIRE(hadOver1);
+ REQUIRE(hadOver2);
+ REQUIRE(hadUnder1);
+ REQUIRE(hadUnder2);
// operate with int
val = val - UnderlyingType(5);
val.revert();
@@ -130,16 +144,19 @@ template struct SafeIntTester {
SafeInt valUnder(std::numeric_limits::min());
bool hadZero1 = false;
bool hadZero2 = false;
- bool hadOver = false;
+ bool hadOver1 = false;
+ bool hadOver2 = false;
bool hadUnder = false;
// catch over/underflow and mul by zero
try { valZero1 = valZero1 * UnderlyingType(0); } catch (std::domain_error& e) { hadZero1 = true; }
try { valZero2 = valZero2 * UnderlyingType(10); } catch (std::domain_error& e) { hadZero2 = true; }
- try { valOver = valOver * UnderlyingType(2); } catch (std::overflow_error& e) { hadOver = true; }
+ try { valOver = valOver * UnderlyingType(2); } catch (std::overflow_error& e) { hadOver1 = true; }
+ try { valOver = valOver * valOver; } catch (std::overflow_error& e) { hadOver2 = true; }
try { valUnder = valUnder * UnderlyingType(2); } catch (std::underflow_error& e) { hadUnder = true; }
REQUIRE(hadZero1);
REQUIRE(hadZero2);
- REQUIRE(hadOver);
+ REQUIRE(hadOver1);
+ REQUIRE(hadOver2);
REQUIRE(hadUnder);
// operate with int
val = val * UnderlyingType(2);
@@ -162,14 +179,18 @@ template struct SafeIntTester {
SECTION(std::string("SafeInt_t<") + std::to_string(Size) + "> operator/") {
SafeInt val(UnderlyingType(-42));
SafeInt valZero(UnderlyingType(-42));
+ SafeInt valMinusOne(UnderlyingType(-1));
SafeInt valOver(std::numeric_limits::min());
bool hadZero = false;
- bool hadOver = false;
+ bool hadOver1 = false;
+ bool hadOver2 = false;
// catch overflow and div by zero
try { valZero = valZero / UnderlyingType(0); } catch (std::domain_error& e) { hadZero = true; }
- try { valOver = valOver / UnderlyingType(-1); } catch (std::overflow_error& e) { hadOver = true; }
+ try { valOver = valOver / UnderlyingType(-1); } catch (std::overflow_error& e) { hadOver1 = true; }
+ try { valOver = valOver / valMinusOne; } catch (std::overflow_error& e) { hadOver2 = true; }
REQUIRE(hadZero);
- REQUIRE(hadOver);
+ REQUIRE(hadOver1);
+ REQUIRE(hadOver2);
// operate with int
val = val / UnderlyingType(2);
val.revert();
@@ -423,13 +444,19 @@ template struct SafeIntTester {
SafeInt val(UnderlyingType(-42));
SafeInt valOver(std::numeric_limits::max());
SafeInt valUnder(std::numeric_limits::min());
- bool hadOver = false;
- bool hadUnder = false;
+ bool hadOver1 = false;
+ bool hadOver2 = false;
+ bool hadUnder1 = false;
+ bool hadUnder2 = false;
// catch over/underflow
- try { valOver += UnderlyingType(1); } catch (std::overflow_error& e) { hadOver = true; }
- try { valUnder += UnderlyingType(-1); } catch (std::underflow_error& e) { hadUnder = true; }
- REQUIRE(hadOver);
- REQUIRE(hadUnder);
+ try { valOver += UnderlyingType(1); } catch (std::overflow_error& e) { hadOver1 = true; }
+ try { valOver += valOver; } catch (std::overflow_error& e) { hadOver2 = true; }
+ try { valUnder += UnderlyingType(-1); } catch (std::underflow_error& e) { hadUnder1 = true; }
+ try { valUnder += valUnder; } catch (std::underflow_error& e) { hadUnder2 = true; }
+ REQUIRE(hadOver1);
+ REQUIRE(hadOver2);
+ REQUIRE(hadUnder1);
+ REQUIRE(hadUnder2);
// operate with int
val = val += UnderlyingType(5);
val.revert();
@@ -451,13 +478,21 @@ template struct SafeIntTester {
SafeInt val(UnderlyingType(-42));
SafeInt valOver(std::numeric_limits::max());
SafeInt valUnder(std::numeric_limits::min());
- bool hadOver = false;
- bool hadUnder = false;
+ SafeInt valOverMinus(UnderlyingType(-1));
+ SafeInt valUnderMinus(UnderlyingType(1));
+ bool hadOver1 = false;
+ bool hadOver2 = false;
+ bool hadUnder1 = false;
+ bool hadUnder2 = false;
// catch over/underflow
- try { valOver -= UnderlyingType(-1); } catch (std::overflow_error& e) { hadOver = true; }
- try { valUnder -= UnderlyingType(1); } catch (std::underflow_error& e) { hadUnder = true; }
- REQUIRE(hadOver);
- REQUIRE(hadUnder);
+ try { valOver -= valOver - UnderlyingType(-1); } catch (std::overflow_error& e) { hadOver1 = true; }
+ try { valOver -= valOver - valOverMinus; } catch (std::overflow_error& e) { hadOver2 = true; }
+ try { valUnder -= valUnder - UnderlyingType(1); } catch (std::underflow_error& e) { hadUnder1 = true; }
+ try { valUnder -= valUnder - valUnderMinus; } catch (std::underflow_error& e) { hadUnder2 = true; }
+ REQUIRE(hadOver1);
+ REQUIRE(hadOver2);
+ REQUIRE(hadUnder1);
+ REQUIRE(hadUnder2);
// operate with int
val -= UnderlyingType(5);
val.revert();
@@ -483,16 +518,19 @@ template struct SafeIntTester {
SafeInt valUnder(std::numeric_limits::min());
bool hadZero1 = false;
bool hadZero2 = false;
- bool hadOver = false;
+ bool hadOver1 = false;
+ bool hadOver2 = false;
bool hadUnder = false;
// catch over/underflow and mul by zero
try { valZero1 *= UnderlyingType(0); } catch (std::domain_error& e) { hadZero1 = true; }
try { valZero2 *= UnderlyingType(10); } catch (std::domain_error& e) { hadZero2 = true; }
- try { valOver *= UnderlyingType(2); } catch (std::overflow_error& e) { hadOver = true; }
+ try { valOver *= UnderlyingType(2); } catch (std::overflow_error& e) { hadOver1 = true; }
+ try { valOver *= valOver; } catch (std::overflow_error& e) { hadOver2 = true; }
try { valUnder *= UnderlyingType(2); } catch (std::underflow_error& e) { hadUnder = true; }
REQUIRE(hadZero1);
REQUIRE(hadZero2);
- REQUIRE(hadOver);
+ REQUIRE(hadOver1);
+ REQUIRE(hadOver2);
REQUIRE(hadUnder);
// operate with int
val *= UnderlyingType(2);
@@ -515,14 +553,18 @@ template struct SafeIntTester {
SECTION(std::string("SafeInt_t<") + std::to_string(Size) + "> operator/=") {
SafeInt val(UnderlyingType(-42));
SafeInt valZero(UnderlyingType(-42));
+ SafeInt valMinusOne(UnderlyingType(-1));
SafeInt valOver(std::numeric_limits::min());
bool hadZero = false;
- bool hadOver = false;
+ bool hadOver1 = false;
+ bool hadOver2 = false;
// catch overflow and div by zero
try { valZero /= UnderlyingType(0); } catch (std::domain_error& e) { hadZero = true; }
- try { valOver /= UnderlyingType(-1); } catch (std::overflow_error& e) { hadOver = true; }
+ try { valOver /= UnderlyingType(-1); } catch (std::overflow_error& e) { hadOver1 = true; }
+ try { valOver /= valMinusOne; } catch (std::overflow_error& e) { hadOver2 = true; }
REQUIRE(hadZero);
- REQUIRE(hadOver);
+ REQUIRE(hadOver1);
+ REQUIRE(hadOver2);
// operate with int
val /= UnderlyingType(2);
val.revert();
diff --git a/tests/contract/variables/safestring.cpp b/tests/contract/variables/safestring.cpp
index 4b3f9f6b..e2d6c3eb 100644
--- a/tests/contract/variables/safestring.cpp
+++ b/tests/contract/variables/safestring.cpp
@@ -8,6 +8,8 @@ See the LICENSE.txt file in the project root for more information.
#include "../../src/libs/catch2/catch_amalgamated.hpp"
#include "../../src/contract/variables/safestring.h"
+using Catch::Matchers::Equals;
+
namespace TSafeString {
TEST_CASE("SafeString class", "[contract][variables][safestring]") {
SECTION("SafeString constructor") {
@@ -1100,6 +1102,13 @@ namespace TSafeString {
REQUIRE(!(strA1 >= cstrB));
REQUIRE(!(strB <= cstrA));
}
+
+ SECTION("SafeString operator<<") {
+ SafeString str("ABCDE");
+ std::stringstream ss;
+ ss << str;
+ REQUIRE_THAT(ss.str(), Equals("ABCDE"));
+ }
}
}
diff --git a/tests/contract/variables/safetuple.cpp b/tests/contract/variables/safetuple.cpp
index ad4fcb4e..4ba65dc3 100644
--- a/tests/contract/variables/safetuple.cpp
+++ b/tests/contract/variables/safetuple.cpp
@@ -68,6 +68,14 @@ namespace TSafeTuple {
REQUIRE(get<2>(std::as_const(tup)) == "ccc");
}
+ SECTION("SafeTuple raw") {
+ SafeTuple tup(std::make_tuple(10, 1.0, "aaa"));
+ std::tuple tupRaw = tup.raw();
+ REQUIRE(std::get<0>(tupRaw) == 10);
+ REQUIRE(std::get<1>(tupRaw) == 1.0);
+ REQUIRE(std::get<2>(tupRaw) == "aaa");
+ }
+
SECTION("SafeTuple operator=") {
SafeTuple tup(std::make_tuple(10, 1.0, "aaa"));
// assign by copy
diff --git a/tests/contract/variables/safeuint_t.cpp b/tests/contract/variables/safeuint_t.cpp
index b3ade250..382ce087 100644
--- a/tests/contract/variables/safeuint_t.cpp
+++ b/tests/contract/variables/safeuint_t.cpp
@@ -156,16 +156,19 @@ template struct SafeUintTester {
SafeUint valUnder(1);
bool hadZero1 = false;
bool hadZero2 = false;
- bool hadOver = false;
+ bool hadOver1 = false;
+ bool hadOver2 = false;
bool hadUnder = false;
// catch over/underflow and mul by zero
try { valZero1 = valZero1 * UnderlyingType(0); } catch (std::domain_error& e) { hadZero1 = true; }
try { valZero2 = valZero2 * UnderlyingType(10); } catch (std::domain_error& e) { hadZero2 = true; }
- try { valOver = valOver * UnderlyingType(2); } catch (std::overflow_error& e) { hadOver = true; }
+ try { valOver = valOver * UnderlyingType(2); } catch (std::overflow_error& e) { hadOver1 = true; }
+ try { valOver = valOver * valOver; } catch (std::overflow_error& e) { hadOver2 = true; }
try { valUnder = valUnder * int(-1); } catch (std::underflow_error& e) { hadUnder = true; }
REQUIRE(hadZero1);
REQUIRE(hadZero2);
- REQUIRE(hadOver);
+ REQUIRE(hadOver1);
+ REQUIRE(hadOver2);
REQUIRE(hadUnder);
// operate with uint
val = val * UnderlyingType(2);
@@ -651,16 +654,19 @@ template struct SafeUintTester {
SafeUint valUnder(1);
bool hadZero1 = false;
bool hadZero2 = false;
- bool hadOver = false;
+ bool hadOver1 = false;
+ bool hadOver2 = false;
bool hadUnder = false;
// catch over/underflow and mul by zero
try { valZero1 *= UnderlyingType(0); } catch (std::domain_error& e) { hadZero1 = true; }
try { valZero2 *= UnderlyingType(10); } catch (std::domain_error& e) { hadZero2 = true; }
- try { valOver *= UnderlyingType(2); } catch (std::overflow_error& e) { hadOver = true; }
- try { valUnder = valUnder * int(-1); } catch (std::underflow_error& e) { hadUnder = true; }
+ try { valOver *= UnderlyingType(2); } catch (std::overflow_error& e) { hadOver1 = true; }
+ try { valOver *= valOver; } catch (std::overflow_error& e) { hadOver2 = true; }
+ try { valUnder *= int(-1); } catch (std::underflow_error& e) { hadUnder = true; }
REQUIRE(hadZero1);
REQUIRE(hadZero2);
- REQUIRE(hadOver);
+ REQUIRE(hadOver1);
+ REQUIRE(hadOver2);
REQUIRE(hadUnder);
// operate with uint
val *= UnderlyingType(2);
diff --git a/tests/contract/variables/safeunorderedmap.cpp b/tests/contract/variables/safeunorderedmap.cpp
index 53bafb62..65d3b791 100644
--- a/tests/contract/variables/safeunorderedmap.cpp
+++ b/tests/contract/variables/safeunorderedmap.cpp
@@ -259,10 +259,10 @@ namespace TSafeUnorderedMap {
// Same as insert (simple), but we use iterators to the vector instead (also an ilist)
std::vector> values;
- for (uint8_t i = 0; i < 100; i++) { // inserting 100 values
+ for (uint8_t i = 0; i < 100; i++) { // Inserting 100 values
values.emplace_back(std::make_pair(Address(Utils::randBytes(20)), bal0));
}
- std::initializer_list> ilist { // creating 10 values
+ std::initializer_list> ilist { // Creating 10 values
std::make_pair(Address(Utils::randBytes(20)), bal0),
std::make_pair(Address(Utils::randBytes(20)), bal0),
std::make_pair(Address(Utils::randBytes(20)), bal0),
@@ -287,7 +287,9 @@ namespace TSafeUnorderedMap {
REQUIRE(map.size() == 101);
for (std::pair val : values) REQUIRE(map.at(val.first) == val.second);
- map.clear(); map.insert(std::make_pair(add0, bal0)); map.commit(); // revert to starting map
+ map.insert(values.cbegin(), values.cend()); // "Re-insert" for coverage
+
+ map.clear(); map.insert(std::make_pair(add0, bal0)); map.commit(); // Revert to starting map
// Mass insert by ilist, then revert
map.insert(ilist);
@@ -300,6 +302,8 @@ namespace TSafeUnorderedMap {
map.commit();
REQUIRE(map.size() == 11);
for (auto it = ilist.begin(); it != ilist.end(); it++) REQUIRE(map.at(it->first) == it->second);
+
+ map.insert(ilist); // "Re-insert" for coverage
}
SECTION("SafeUnorderedMap insert_or_assign (copy)") {
@@ -647,7 +651,7 @@ namespace TSafeUnorderedMap {
map.commit();
REQUIRE(map.size() == 100);
REQUIRE(!map.contains(firstVal.first));
- map[firstVal.first] = firstVal.second; map.commit(); REQUIRE(map.size() == 101); // re-add the key for next test
+ map[firstVal.first] = firstVal.second; map.commit(); REQUIRE(map.size() == 101); // Re-add the key for next test
// Erase a single key using a value
map.erase(add0);
@@ -662,11 +666,11 @@ namespace TSafeUnorderedMap {
// Erase a range of keys using iterators
auto itB = map.cbegin();
auto itE = map.cbegin();
- std::advance(itE, map.size() / 2); // thanos snap, half the map is gone
+ std::advance(itE, map.size() / 2); // Thanos snap, half the map is gone
map.erase(itB, itE);
map.revert();
REQUIRE(map.size() == 100);
- itB = map.cbegin(); // refresh iterators just in case
+ itB = map.cbegin(); // Refresh iterators just in case
itE = map.cbegin();
std::advance(itE, map.size() / 2);
map.erase(itB, itE);
diff --git a/tests/contract/variables/safevector.cpp b/tests/contract/variables/safevector.cpp
index 474f75e1..dceb25d2 100644
--- a/tests/contract/variables/safevector.cpp
+++ b/tests/contract/variables/safevector.cpp
@@ -104,6 +104,9 @@ namespace TSafeVector {
REQUIRE(vec[2] == "x");
REQUIRE(vec[3] == "x");
REQUIRE(vec[4] == "x");
+ // For coverage
+ const std::string str = vec[0];
+ REQUIRE(str == "x");
}
SECTION("SafeVector front and back") {
diff --git a/tests/core/blockchain.cpp b/tests/core/blockchain.cpp
index c66f1aaf..3760fbe9 100644
--- a/tests/core/blockchain.cpp
+++ b/tests/core/blockchain.cpp
@@ -58,14 +58,13 @@ void initialize(
blockchain = std::make_unique(blockchainPath);
}
-
namespace TBlockchain {
TEST_CASE("Blockchain Class", "[core][blockchain]") {
std::string testDumpPath = Utils::getTestDumpPath();
SECTION("Initialize Blockchain Multiple Nodes") {
std::shared_ptr bestBlock;
{
- /// Initialize ManagerDiscovery
+ // Initialize ManagerDiscovery
std::vector> discoveryNodes;
std::unique_ptr discoveryOptions = std::make_unique(
testDumpPath + "/statedDiscoveryNodeNetworkCapabilitiesWithTxBlockBroadcast",
@@ -79,7 +78,7 @@ namespace TBlockchain {
std::unique_ptr p2pDiscovery = std::make_unique(
LOCALHOST, discoveryOptions);
- /// Initialize multiple blockchain nodes.
+ // Initialize multiple blockchain nodes.
std::unique_ptr blockchain1;
initialize("blockchainInitializeTestNode1", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8080, 8101,
PrivKey(), {"127.0.0.1", 8100}, blockchain1);
@@ -96,7 +95,7 @@ namespace TBlockchain {
initialize("blockchainInitializeTestNode4", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8083, 8104,
PrivKey(), {"127.0.0.1", 8100}, blockchain4);
- /// Start the blockchain nodes.
+ // Start the blockchain nodes.
p2pDiscovery->start();
p2pDiscovery->startDiscovery();
blockchain1->start();
@@ -109,7 +108,7 @@ namespace TBlockchain {
REQUIRE(!blockchain3->getOptions()->getIsValidator());
REQUIRE(!blockchain4->getOptions()->getIsValidator());
- /// Wait everyone to connect.
+ // Wait everyone to connect.
auto connectFuture = std::async(std::launch::async, [&]() {
while (blockchain1->getP2P()->getSessionsIDs().size() != 4 ||
blockchain2->getP2P()->getSessionsIDs().size() != 4 ||
@@ -131,7 +130,7 @@ namespace TBlockchain {
REQUIRE(blockchain1->getStorage()->latest()->hash() == blockchain3->getStorage()->latest()->hash());
REQUIRE(blockchain1->getStorage()->latest()->hash() == blockchain4->getStorage()->latest()->hash());
- /// Stop the blockchain nodes.
+ // Stop the blockchain nodes.
p2pDiscovery->stop();
blockchain1->stop();
blockchain2->stop();
@@ -153,7 +152,7 @@ namespace TBlockchain {
uint256_t myBalance("1000000000000000000000");
std::shared_ptr bestBlock;
- /// Create the discovery node.
+ // Create the discovery node.
{
std::vector> discoveryNodes;
std::unique_ptr discoveryOptions = std::make_unique(
@@ -168,7 +167,7 @@ namespace TBlockchain {
std::unique_ptr p2pDiscovery = std::make_unique(
LOCALHOST, discoveryOptions);
- /// Create the validator nodes (5 in total)
+ // Create the validator nodes (5 in total)
std::unique_ptr blockchainValidator1;
initialize("blockchainMove10BlocksTestValidator1", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8080, 8101,
PrivKey(Hex::toBytes("0xba5e6e9dd9cbd263969b94ee385d885c2d303dfc181db2a09f6bf19a7ba26759")),
@@ -194,7 +193,7 @@ namespace TBlockchain {
PrivKey(Hex::toBytes("0x81f288dd776f4edfe256d34af1f7d719f511559f19115af3e3d692e741faadc6")),
{"127.0.0.1", 8100}, blockchainValidator5);
- /// Create the normal nodes (6 in total)
+ // Create the normal nodes (6 in total)
std::unique_ptr blockchainNode1;
initialize("blockchainMove10BlocksTestNode1", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8085, 8106,
PrivKey(), {"127.0.0.1", 8100}, blockchainNode1);
@@ -219,20 +218,20 @@ namespace TBlockchain {
initialize("blockchainMove10BlocksTestNode6", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8090, 8111,
PrivKey(), {"127.0.0.1", 8100}, blockchainNode6);
- /// Start the discovery node.
+ // Start the discovery node.
p2pDiscovery->start();
p2pDiscovery->startDiscovery();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
- /// Start the validator nodes.
+ // Start the validator nodes.
blockchainValidator1->start();
blockchainValidator2->start();
blockchainValidator3->start();
blockchainValidator4->start();
blockchainValidator5->start();
- /// Start all nodes
+ // Start all nodes
blockchainNode1->start();
blockchainNode2->start();
blockchainNode3->start();
@@ -240,7 +239,7 @@ namespace TBlockchain {
blockchainNode5->start();
blockchainNode6->start();
- /// Wait everyone to sync
+ // Wait everyone to sync
auto syncFuture = std::async(std::launch::async, [&]() {
while (!blockchainValidator1->isSynced() ||
!blockchainValidator2->isSynced() ||
@@ -274,8 +273,8 @@ namespace TBlockchain {
privKey
);
- /// Commment this out and IT WILL NOT WORK.
- /// Waiting for rdPoSWorker rework.
+ // Commment this out and IT WILL NOT WORK.
+ // Waiting for rdPoSWorker rework.
auto rdPoSmempoolFuture = std::async(std::launch::async, [&]() {
while (blockchainValidator1->getState().rdposGetMempool().size() != 8 ||
blockchainValidator2->getState().rdposGetMempool().size() != 8 ||
@@ -296,14 +295,14 @@ namespace TBlockchain {
myBalance -= tx.getValue() + (tx.getMaxFeePerGas() * tx.getGasLimit());
targetBalance += tx.getValue();
- /// Send the transactions through HTTP
+ // Send the transactions through HTTP
auto sendRawTxJson = json({
{"jsonrpc", "2.0"},
{"id", 1},
{"method", "eth_sendRawTransaction"},
{"params", json::array({Hex::fromBytes(tx.rlpSerialize(), true).forRPC()})}});
- /// Send the transaction to the first validator.
+ // Send the transaction to the first validator.
auto sendRawTxResponse = json::parse(makeHTTPRequest(sendRawTxJson.dump(),
"127.0.0.1",
std::to_string(8101),
@@ -314,7 +313,7 @@ namespace TBlockchain {
REQUIRE(sendRawTxResponse["result"].get() == tx.hash().hex(true).get());
- /// Wait for the new best block to be broadcasted and accepted by all nodes
+ // Wait for the new best block to be broadcasted and accepted by all nodes
++blocks;
auto blockFuture = std::async(std::launch::async, [&]() {
while (blocks != blockchainNode1->getStorage()->latest()->getNHeight() ||
@@ -388,7 +387,7 @@ namespace TBlockchain {
REQUIRE(wait != std::future_status::timeout);
- /// Everyone should be on the same block
+ // Everyone should be on the same block
REQUIRE(blockchainValidator1->getStorage()->latest()->getNHeight() == blocks);
REQUIRE(blockchainValidator1->getStorage()->latest()->hash() == blockchainValidator2->getStorage()->latest()->hash());
REQUIRE(blockchainValidator1->getStorage()->latest()->hash() == blockchainValidator3->getStorage()->latest()->hash());
@@ -402,7 +401,7 @@ namespace TBlockchain {
REQUIRE(blockchainValidator1->getStorage()->latest()->hash() == blockchainNode6->getStorage()->latest()->hash());
}
bestBlock = blockchainValidator1->getStorage()->latest();
- /// Stop the nodes
+ // Stop the nodes
p2pDiscovery->stop();
blockchainValidator1->stop();
blockchainValidator2->stop();
diff --git a/tests/core/state.cpp b/tests/core/state.cpp
index 160c4584..ca51f4f3 100644
--- a/tests/core/state.cpp
+++ b/tests/core/state.cpp
@@ -69,7 +69,7 @@ namespace TState {
}
// Wait a little until everyone has been destructed.
std::this_thread::sleep_for(std::chrono::milliseconds(100));
- //// Check if opening the state loads successfully from DB.
+ // Check if opening the state loads successfully from DB.
auto blockchainWrapper = initialize(validatorPrivKeysState, validatorPrivKeysState[0], 8080, false, testDumpPath + "/stateConstructorTest");
REQUIRE(blockchainWrapper.state.getNativeBalance(Address(Hex::toBytes("0x00dead00665771855a34155f5e7405489df2c3c6"))) ==
uint256_t("1000000000000000000000"));
@@ -109,7 +109,7 @@ namespace TState {
}
}
- SECTION("Test Simple block on State (No Transactions only rdPoS") {
+ SECTION("Test Simple block on State (No Transactions only rdPoS)") {
std::unique_ptr latestBlock = nullptr;
{
auto blockchainWrapper = initialize(validatorPrivKeysState, validatorPrivKeysState[0], 8080, true, testDumpPath + "/stateSimpleBlockTest");
@@ -135,7 +135,7 @@ namespace TState {
{
auto blockchainWrapper = initialize(validatorPrivKeysState, validatorPrivKeysState[0], 8080, true, testDumpPath + "/stateSimpleBlockTest");
- /// Add balance to the random Accounts and create random transactions
+ // Add balance to the random Accounts and create random transactions
std::vector transactions;
for (auto &[privkey, val]: randomAccounts) {
Address me = Secp256k1::toAddress(Secp256k1::toUPub(privkey));
@@ -153,7 +153,7 @@ namespace TState {
privkey
);
- /// Take note of expected balance and nonce
+ // Take note of expected balance and nonce
val.first =
blockchainWrapper.state.getNativeBalance(me) -
(transactions.back().getMaxFeePerGas() * transactions.back().getGasLimit()) -
@@ -185,7 +185,7 @@ namespace TState {
{
auto blockchainWrapper = initialize(validatorPrivKeysState, validatorPrivKeysState[0], 8080, true, testDumpPath + "/stateSimpleBlockTest");
- /// Add balance to the random Accounts and add tx's to directly to mempool.
+ // Add balance to the random Accounts and add tx's to directly to mempool.
for (auto &[privkey, val]: randomAccounts) {
Address me = Secp256k1::toAddress(Secp256k1::toUPub(privkey));
blockchainWrapper.state.addBalance(me);
@@ -202,15 +202,17 @@ namespace TState {
privkey
);
- /// Take note of expected balance and nonce
+ // Take note of expected balance and nonce
val.first = blockchainWrapper.state.getNativeBalance(me) - (tx.getMaxFeePerGas() * tx.getGasLimit()) - tx.getValue();
val.second = blockchainWrapper.state.getNativeNonce(me) + 1;
targetExpectedValue += tx.getValue();
blockchainWrapper.state.addTx(std::move(tx));
+ REQUIRE(blockchainWrapper.state.isTxInMempool(tx.hash()));
}
auto txCopy = blockchainWrapper.state.getMempool();
REQUIRE(txCopy.size() == 500);
+ REQUIRE(blockchainWrapper.state.getPendingTxs().size() == 500);
auto newBestBlock = createValidBlock(validatorPrivKeysState, blockchainWrapper.state, blockchainWrapper.storage, std::move(txCopy));
REQUIRE(blockchainWrapper.state.tryProcessNextBlock(std::move(newBestBlock)) == BlockValidationStatus::valid);
@@ -225,9 +227,9 @@ namespace TState {
}
SECTION("Test State mempool refresh") {
- /// The block included will only have transactions where the address starts with \x08 or lower
- /// where the mempool will have 500 transactions, including the \x08 addresses txs.
- /// Test if the mempool is refreshed correctly.
+ // The block included will only have transactions where the address starts with \x08 or lower
+ // where the mempool will have 500 transactions, including the \x08 addresses txs.
+ // Test if the mempool is refreshed correctly.
std::unordered_map, SafeHash> randomAccounts;
for (uint64_t i = 0; i < 500; ++i) {
randomAccounts.insert({PrivKey(Utils::randBytes(32)), std::make_pair(0, 0)});
@@ -238,7 +240,7 @@ namespace TState {
{
auto blockchainWrapper = initialize(validatorPrivKeysState, validatorPrivKeysState[0], 8080, true, testDumpPath + "/stateSimpleBlockTest");
- /// Add balance to the random Accounts and add tx's to directly to mempool.
+ // Add balance to the random Accounts and add tx's to directly to mempool.
std::vector txs;
std::vector notOnBlock;
for (auto &[privkey, val]: randomAccounts) {
@@ -259,7 +261,7 @@ namespace TState {
if (me[0] <= 0x08) {
txs.emplace_back(tx);
- /// Take note of expected balance and nonce
+ // Take note of expected balance and nonce
val.first = blockchainWrapper.state.getNativeBalance(me) - (tx.getMaxFeePerGas() * tx.getGasLimit()) - tx.getValue();
val.second = blockchainWrapper.state.getNativeNonce(me) + 1;
targetExpectedValue += tx.getValue();
@@ -302,14 +304,14 @@ namespace TState {
std::unique_ptr latestBlock = nullptr;
{
auto blockchainWrapper = initialize(validatorPrivKeysState, validatorPrivKeysState[0], 8080, true, testDumpPath + "/state10BlocksTest");
- /// Add balance to the given addresses
+ // Add balance to the given addresses
for (const auto &[privkey, account]: randomAccounts) {
Address me = Secp256k1::toAddress(Secp256k1::toUPub(privkey));
blockchainWrapper.state.addBalance(me);
}
for (uint64_t index = 0; index < 10; ++index) {
- /// Create random transactions
+ // Create random transactions
std::vector txs;
for (auto &[privkey, account]: randomAccounts) {
Address me = Secp256k1::toAddress(Secp256k1::toUPub(privkey));
@@ -325,7 +327,7 @@ namespace TState {
21000,
privkey
);
- /// Take note of expected balance and nonce
+ // Take note of expected balance and nonce
account.first = blockchainWrapper.state.getNativeBalance(me) - (txs.back().getMaxFeePerGas() * txs.back().getGasLimit()) -
txs.back().getValue();
account.second = blockchainWrapper.state.getNativeNonce(me) + 1;
@@ -575,7 +577,7 @@ namespace TState {
}
REQUIRE(blockchainWrapper1.state.getMempool().size() == 100);
- /// Wait for the transactions to be broadcasted.
+ // Wait for the transactions to be broadcasted.
auto broadcastFuture = std::async(std::launch::async, [&]() {
while (blockchainWrapper1.state.getMempool().size() != 100 ||
blockchainWrapper2.state.getMempool().size() != 100 ||
@@ -854,7 +856,7 @@ namespace TState {
}
- /// TODO: This is done for the same reason as stopDiscovery.
+ // TODO: This is done for the same reason as stopDiscovery.
blockchainWrapper1.consensus.stop();
blockchainWrapper2.consensus.stop();
blockchainWrapper3.consensus.stop();
@@ -1089,7 +1091,7 @@ namespace TState {
for (uint64_t i = 0; i < 10; ++i) {
std::vector txSet;
- /// Add balance to the random Accounts and create random transactions
+ // Add balance to the random Accounts and create random transactions
for (auto &[privkey, val]: randomAccounts) {
Address me = Secp256k1::toAddress(Secp256k1::toUPub(privkey));
TxBlock tx(
@@ -1105,7 +1107,7 @@ namespace TState {
privkey
);
- /// Take note of expected balance and nonce
+ // Take note of expected balance and nonce
val.first = blockchainWrapper1.state.getNativeBalance(me) - (tx.getMaxFeePerGas() * tx.getGasLimit()) - tx.getValue();
val.second = blockchainWrapper1.state.getNativeNonce(me) + 1;
txSet.push_back(tx);
@@ -1113,7 +1115,7 @@ namespace TState {
txs.emplace_back(std::move(txSet));
}
- /// For each set of transactions, broadcast them and wait for them to be confirmed
+ // For each set of transactions, broadcast them and wait for them to be confirmed
for (const auto &txSet: txs) {
for (const auto &tx: txSet) {
auto txStatus = blockchainWrapper1.state.addTx(TxBlock(tx));
@@ -1122,7 +1124,7 @@ namespace TState {
targetExpectedValue += tx.getValue();
}
- /// Wait for the transactions to be confirmed.
+ // Wait for the transactions to be confirmed.
uint64_t loop = 0;
auto confirmFuture = std::async(std::launch::async, [&]() {
while (true) {
@@ -1172,7 +1174,7 @@ namespace TState {
REQUIRE(blockchainWrapper1.storage.getBlock(i)->getHash() == blockchainWrapper8.storage.getBlock(i)->getHash());
}
- /// TODO: This is done for the same reason as stopDiscovery.
+ // TODO: This is done for the same reason as stopDiscovery.
blockchainWrapper1.consensus.stop();
blockchainWrapper2.consensus.stop();
blockchainWrapper3.consensus.stop();
@@ -1457,8 +1459,8 @@ namespace TState {
auto txStatus = blockchainWrapper1.state.addTx(TxBlock(tx));
REQUIRE(isTxStatusValid(txStatus));
blockchainWrapper1.p2p.getBroadcaster().broadcastTxBlock(tx);
- /// Wait for the transactions to be confirmed.
- ///
+ // Wait for the transactions to be confirmed.
+ //
auto confirmFuture = std::async(std::launch::async, [&]() {
while (true) {
if (blockchainWrapper1.storage.txExists(tx.hash()) &&
@@ -1531,8 +1533,7 @@ namespace TState {
// Sanity check: blocks
uint64_t bestBlockHeight = blockchainWrapper1.storage.latest()->getNHeight();
- for (uint64_t i = 0; i <= bestBlockHeight; ++i)
- {
+ for (uint64_t i = 0; i <= bestBlockHeight; i++) {
REQUIRE(blockchainWrapper1.storage.getBlock(i)->getHash() == blockchainWrapper2.storage.getBlock(i)->getHash());
REQUIRE(blockchainWrapper1.storage.getBlock(i)->getHash() == blockchainWrapper3.storage.getBlock(i)->getHash());
REQUIRE(blockchainWrapper1.storage.getBlock(i)->getHash() == blockchainWrapper4.storage.getBlock(i)->getHash());
@@ -1542,7 +1543,7 @@ namespace TState {
REQUIRE(blockchainWrapper1.storage.getBlock(i)->getHash() == blockchainWrapper8.storage.getBlock(i)->getHash());
}
- /// TODO: This is done for the same reason as stopDiscovery.
+ // TODO: This is done for the same reason as stopDiscovery.
blockchainWrapper1.consensus.stop();
blockchainWrapper2.consensus.stop();
blockchainWrapper3.consensus.stop();
@@ -1551,6 +1552,7 @@ namespace TState {
blockchainWrapper6.consensus.stop();
blockchainWrapper7.consensus.stop();
blockchainWrapper8.consensus.stop();
+
// Sleep so it can conclude the last operations.
std::this_thread::sleep_for(std::chrono::seconds(1));
}
diff --git a/tests/core/storage.cpp b/tests/core/storage.cpp
index 5a6abb7b..74e98227 100644
--- a/tests/core/storage.cpp
+++ b/tests/core/storage.cpp
@@ -88,12 +88,9 @@ FinalizedBlock createRandomBlock(uint64_t txCount, uint64_t validatorCount, uint
std::vector txValidators = randomnessResult.first;
// Create a new block with the transactions.
- FinalizedBlock finalBlock = FinalizedBlock::createNewValidBlock(std::move(txs),
- std::move(txValidators),
- prevHash,
- timestamp,
- nHeight,
- blockValidatorPrivKey);
+ FinalizedBlock finalBlock = FinalizedBlock::createNewValidBlock(
+ std::move(txs), std::move(txValidators), prevHash, timestamp, nHeight, blockValidatorPrivKey
+ );
REQUIRE(finalBlock.getBlockRandomness() == Hash(Utils::sha3(randomSeed)));
return finalBlock;
}
@@ -101,7 +98,6 @@ FinalizedBlock createRandomBlock(uint64_t txCount, uint64_t validatorCount, uint
namespace TStorage {
TEST_CASE("Storage Class", "[core][storage]") {
SECTION("Simple Storage Startup") {
-
auto blockchainWrapper = initialize(validatorPrivKeysStorage, PrivKey(), 8080, true, "StorageConstructor");
// Chain should be filled with the genesis.
REQUIRE(blockchainWrapper.storage.currentChainSize() == 1);
@@ -150,10 +146,10 @@ namespace TStorage {
REQUIRE(block->getTxs().size() == blocks[i].getTxs().size());
REQUIRE(block->getValidatorPubKey() == blocks[i].getValidatorPubKey());
}
- /// We actually need to dump the state otherwise it WILL try to process the added blocks
- /// in the constructor of the State class.
- /// Dumping the state will say to the State class that there is no missing blocks and it will
- /// not try to process the blocks in the constructor.
+ // We actually need to dump the state otherwise it WILL try to process the added blocks
+ // in the constructor of the State class.
+ // Dumping the state will say to the State class that there is no missing blocks and it will
+ // not try to process the blocks in the constructor.
blockchainWrapper.state.saveToDB();
}
@@ -213,7 +209,7 @@ namespace TStorage {
REQUIRE(block->getTxs().size() == requiredBlock.getTxs().size());
REQUIRE(block->getValidatorPubKey() == requiredBlock.getValidatorPubKey());
}
- /// Same as before, we need to dump the state to avoid processing the blocks in the constructor.
+ // Same as before, we need to dump the state to avoid processing the blocks in the constructor.
blockchainWrapper.state.saveToDB();
}
// Load DB again...
diff --git a/tests/net/http/httpjsonrpc.cpp b/tests/net/http/httpjsonrpc.cpp
index 96b44d65..5f7f1e78 100644
--- a/tests/net/http/httpjsonrpc.cpp
+++ b/tests/net/http/httpjsonrpc.cpp
@@ -9,6 +9,8 @@ See the LICENSE.txt file in the project root for more information.
#include "../../blockchainwrapper.hpp" // blockchain.h -> (net/http/httpserver.h -> net/p2p/managernormal.h), consensus.h -> state.h -> dump.h -> (storage.h -> utils/options.h), utils/db.h -> utils.h
+#include "../../src/net/http/jsonrpc/call.h"
+
std::string makeHTTPRequest(
const std::string& reqBody, const std::string& host, const std::string& port,
const std::string& target, const std::string& requestType, const std::string& contentType
@@ -77,7 +79,6 @@ std::string makeHTTPRequest(
return result;
}
-
const std::vector validatorPrivKeysHttpJsonRpc {
Hash(Hex::toBytes("0x0a0415d68a5ec2df57aab65efc2a7231b59b029bae7ff1bd2e40df9af96418c8")),
Hash(Hex::toBytes("0xb254f12b4ca3f0120f305cabf1188fe74f0bd38e58c932a3df79c4c55df8fa66")),
@@ -89,31 +90,44 @@ const std::vector validatorPrivKeysHttpJsonRpc {
Hash(Hex::toBytes("0x426dc06373b694d8804d634a0fd133be18e4e9bcbdde099fce0ccf3cb965492f"))
};
-template
-json requestMethod(std::string method, T params) {
+template json requestMethod(std::string method, T params) {
return json::parse(makeHTTPRequest(
json({
- {"jsonrpc", "2.0"},
- {"id", 1},
- {"method", method},
- {"params", params}
- }).dump(),
+ {"jsonrpc", "2.0"},
+ {"id", 1},
+ {"method", method},
+ {"params", params}
+ }).dump(),
"127.0.0.1",
std::to_string(9999), // Default port for HTTPJsonRPC
"/",
"POST",
- "application/json"));
+ "application/json")
+ );
}
namespace THTTPJsonRPC{
TEST_CASE("HTTPJsonRPC Tests", "[net][http][jsonrpc]") {
+ SECTION("checkJsonRPCSpec") {
+ json ok = {{"jsonrpc", "2.0"}, {"method", "myMethod"}, {"params", json::array()}};
+ json noJsonRpc = {{"method", "myMethod"}, {"params", json::array()}};
+ json wrongJsonRpc = {{"jsonrpc", "1.0"}, {"method", "myMethod"}, {"params", json::array()}};
+ json noMethod = {{"jsonrpc", "2.0"}, {"params", json::array()}};
+ json wrongParams = {{"jsonrpc", "2.0"}, {"method", "myMethod"}, {"params", 12345}};
+ jsonrpc::checkJsonRPCSpec(ok);
+ REQUIRE_THROWS(jsonrpc::checkJsonRPCSpec(noJsonRpc));
+ REQUIRE_THROWS(jsonrpc::checkJsonRPCSpec(wrongJsonRpc));
+ REQUIRE_THROWS(jsonrpc::checkJsonRPCSpec(noMethod));
+ REQUIRE_THROWS(jsonrpc::checkJsonRPCSpec(wrongParams));
+ }
+
SECTION("HTTPJsonRPC") {
- /// One section to lead it all
- /// Reasoning: we don't want to keep opening and closing everything per Section, just initialize once and run.
+ // One section to lead it all
+ // Reasoning: we don't want to keep opening and closing everything per Section, just initialize once and run.
std::string testDumpPath = Utils::getTestDumpPath();
auto blockchainWrapper = initialize(validatorPrivKeysHttpJsonRpc, validatorPrivKeysHttpJsonRpc[0], 8080, true, testDumpPath + "/HTTPjsonRPC");
- /// Make random transactions within a given block, we need to include requests for getting txs and blocks
+ // Make random transactions within a given block, we need to include requests for getting txs and blocks
Address targetOfTransactions = Address(Utils::randBytes(20));
uint256_t targetExpectedValue = 0;
std::unordered_map, SafeHash> randomAccounts;
@@ -138,13 +152,22 @@ namespace THTTPJsonRPC{
privkey
);
- /// Take note of expected balance and nonce
+ // Take note of expected balance and nonce
val.first = blockchainWrapper.state.getNativeBalance(me) - (transactions.back().getMaxFeePerGas() * transactions.back().getGasLimit()) -
transactions.back().getValue();
val.second = blockchainWrapper.state.getNativeNonce(me) + 1;
targetExpectedValue += transactions.back().getValue();
}
+ // TODO: missing the following (as per coverage):
+ // * eth_call
+ // * eth_getLogs
+ // * eth_getCode
+ // * eth_getUncleByBlockHashAndIndex
+ // * txpool_content
+ // * debug_traceBlockByNumber
+ // * debug_traceTransaction
+
// We need to copy since createValidBlock will consume (move) the transactions
auto transactionsCopy = transactions;
@@ -271,7 +294,6 @@ namespace THTTPJsonRPC{
json eth_blockNumberResponse = requestMethod("eth_blockNumber", json::array());
REQUIRE(eth_blockNumberResponse["result"] == "0x1");
- /// TODO: eth_call
json eth_estimateGasResponse = requestMethod("eth_estimateGas", json::array({json::object({
{"from", blockchainWrapper.options.getChainOwner().hex(true) },
{"to", "0xaaA85B2B2bD0bFdF6Bc5D0d61B6192c53818567b"},
@@ -394,6 +416,22 @@ namespace THTTPJsonRPC{
REQUIRE(eth_feeHistoryResponse["result"]["baseFeePerGas"][1] == "0x9502f900");
REQUIRE(eth_feeHistoryResponse["result"]["gasUsedRatio"][0] == 1.0); // TODO: properly compare float pointing values
REQUIRE(eth_feeHistoryResponse["result"]["oldestBlock"] == "0x0");
+
+ // Last part - cover the catch cases
+ // Invalid JSON id type
+ json wrongId = {{"jsonrpc", "2.0"}, {"id", json::array()}, {"method", "web3_clientVersion"}, {"params", json::array()}};
+ json idErr = json::parse(makeHTTPRequest(
+ wrongId.dump(), "127.0.0.1", std::to_string(9999), "/", "POST", "application/json"
+ ));
+ REQUIRE(idErr.contains("error"));
+ REQUIRE(idErr["error"]["code"] == -32603);
+ REQUIRE(idErr["error"]["message"] == "Internal error: Invalid id type");
+ // Invalid method call
+ json methodErr = requestMethod("lololol", json::array());
+ REQUIRE(methodErr.contains("error"));
+ REQUIRE(methodErr["error"]["code"] == -32601);
+ REQUIRE(methodErr["error"]["message"] == "Method \"lololol\" not found/available");
}
}
}
+
diff --git a/tests/net/http/parser.cpp b/tests/net/http/parser.cpp
new file mode 100644
index 00000000..9b48acd7
--- /dev/null
+++ b/tests/net/http/parser.cpp
@@ -0,0 +1,93 @@
+/*
+Copyright (c) [2023-2024] [AppLayer Developers]
+
+This software is distributed under the MIT License.
+See the LICENSE.txt file in the project root for more information.
+*/
+
+#include "../../src/libs/catch2/catch_amalgamated.hpp"
+
+#include "../../src/net/http/jsonrpc/blocktag.h" // parser.h
+
+namespace THTTPJSONRPCParser {
+ TEST_CASE("HTTP JSON RPC Parser Tests", "[net][http][jsonrpc][parser]") {
+ SECTION("Parser operator()") {
+ Hash h = Hash::random();
+ std::vector v = {10, 20, 30, 40, 50};
+
+ // Parser regex REQUIRES hex prefix (0x)
+ json jsonHash = h.hex(true).get();
+ json jsonAdd = Address("0x0000111122223333444455556666777788889999", false).hex(true).get();
+ json jsonBytes = Hex::fromBytes(Bytes{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, true).get();
+ json jsonBool = true;
+ json jsonFloat = 13.37f;
+ json jsonUint = uint64_t(3926591489);
+ json jsonUintStr = "0xea0b0801";
+ json jsonBlockTagLatest = "latest";
+ json jsonBlockTagEarliest = "earliest";
+ json jsonBlockTagPending = "pending";
+ json jsonVectorArr = v;
+ json jsonVectorObj = json::object();
+ for (int i = 0; i < v.size(); i++) jsonVectorObj[std::to_string(i)] = v[i];
+
+ Hash resHash = jsonrpc::parse(jsonHash);
+ Address resAdd = jsonrpc::parse(jsonAdd);
+ Bytes resBytes = jsonrpc::parse(jsonBytes);
+ bool resBool = jsonrpc::parse(jsonBool);
+ float resFloat = jsonrpc::parse(jsonFloat);
+ uint64_t resUint = jsonrpc::parse(jsonUint);
+ uint64_t resUintStr = jsonrpc::parse(jsonUintStr);
+ jsonrpc::BlockTag resBlockTagLatest = jsonrpc::parse(jsonBlockTagLatest);
+ jsonrpc::BlockTag resBlockTagEarliest = jsonrpc::parse(jsonBlockTagEarliest);
+ jsonrpc::BlockTag resBlockTagPending = jsonrpc::parse(jsonBlockTagPending);
+ std::vector resVectorArr = jsonrpc::parse>(jsonVectorArr);
+ std::vector resVectorObj = jsonrpc::parse>(jsonVectorObj);
+
+ REQUIRE(resHash == h);
+ REQUIRE(resAdd == Address("0x0000111122223333444455556666777788889999", false));
+ REQUIRE(resBytes == Bytes{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF});
+ REQUIRE(resBool == true);
+ REQUIRE(resFloat == 13.37f);
+ REQUIRE(resUint == uint64_t(3926591489));
+ REQUIRE(resUintStr == uint64_t(3926591489));
+ REQUIRE(resBlockTagLatest == jsonrpc::BlockTag::LATEST);
+ REQUIRE(resBlockTagEarliest == jsonrpc::BlockTag::EARLIEST);
+ REQUIRE(resBlockTagPending == jsonrpc::BlockTag::PENDING);
+ REQUIRE(resVectorArr == v);
+ REQUIRE(resVectorObj == v);
+ }
+
+ SECTION("Parser operator() (throws)") {
+ // Same thing but everything is wrong on purpose to cover throw cases
+ json hashWrongType = json::array(); // Type is not string (or the required type)
+ json hashWrongFormat = Hash::random().hex().get(); // No "0x"
+ json addWrongType = json::array();
+ json addWrongFormat = Address("0x0000111122223333444455556666777788889999", false).hex().get();
+ json bytesWrongType = json::array();
+ json bytesWrongFormat = "0x000g"; // Invalid hex (0-9a-fA-F)
+ json boolWrongType = json::array();
+ json floatWrongType = json::array();
+ json uintWrongType = json::array();
+ json uintWrongFormat = "ea0b0801";
+ json uintWrongNumber = "hhhh"; // Invalid number
+ json blockTagWrongType = json::array();
+ json blockTagWrongFormat = "holding"; // Invalid tag ("latest", "earliest", "pending")
+ json vectorWrongType = -1; // Not array or object
+ REQUIRE_THROWS(jsonrpc::parse(hashWrongType));
+ REQUIRE_THROWS(jsonrpc::parse(hashWrongFormat));
+ REQUIRE_THROWS(jsonrpc::parse(addWrongType));
+ REQUIRE_THROWS(jsonrpc::parse(addWrongFormat));
+ REQUIRE_THROWS(jsonrpc::parse(bytesWrongType));
+ REQUIRE_THROWS(jsonrpc::parse(bytesWrongFormat));
+ REQUIRE_THROWS(jsonrpc::parse(boolWrongType));
+ REQUIRE_THROWS(jsonrpc::parse(floatWrongType));
+ REQUIRE_THROWS(jsonrpc::parse(uintWrongType));
+ REQUIRE_THROWS(jsonrpc::parse(uintWrongFormat));
+ REQUIRE_THROWS(jsonrpc::parse(uintWrongNumber));
+ REQUIRE_THROWS(jsonrpc::parse(blockTagWrongType));
+ REQUIRE_THROWS(jsonrpc::parse(blockTagWrongFormat));
+ REQUIRE_THROWS(jsonrpc::parse>(vectorWrongType));
+ }
+ }
+}
+
diff --git a/tests/net/p2p/encoding.cpp b/tests/net/p2p/encoding.cpp
new file mode 100644
index 00000000..2d874fdf
--- /dev/null
+++ b/tests/net/p2p/encoding.cpp
@@ -0,0 +1,201 @@
+/*
+Copyright (c) [2023-2024] [AppLayer Developers]
+
+This software is distributed under the MIT License.
+See the LICENSE.txt file in the project root for more information.
+*/
+
+#include "../../src/libs/catch2/catch_amalgamated.hpp"
+
+#include "../../src/net/p2p/encoding.cpp" // .cpp has extra functions that .h doesn't have (???)
+#include "../../src/utils/strconv.h"
+
+namespace TP2PEncoding {
+ TEST_CASE("P2P Encoding (Helpers)", "[p2p][encoding]") {
+ SECTION("nodesToMessage <-> nodesFromMessage") {
+ Bytes msg;
+ P2P::NodeID node1{boost::asio::ip::address_v4::from_string("127.0.0.1"), 8000};
+ P2P::NodeID node2{boost::asio::ip::address_v6::from_string("::1"), 8001};
+ const boost::unordered_flat_map nodes = {
+ {node1, P2P::NodeType::NORMAL_NODE}, {node2, P2P::NodeType::DISCOVERY_NODE}
+ };
+
+ P2P::nodesToMessage(msg, nodes);
+ REQUIRE(Hex::fromBytes(msg).get() == "00007f0000011f400101000000000000000000000000000000011f41");
+ boost::unordered_flat_map conv = P2P::nodesFromMessage(msg);
+ REQUIRE(conv.contains(node1));
+ REQUIRE(conv[node1] == P2P::NodeType::NORMAL_NODE);
+ REQUIRE(conv.contains(node2));
+ REQUIRE(conv[node2] == P2P::NodeType::DISCOVERY_NODE);
+
+ // Throws for coverage
+ Bytes msgSmall{0x00};
+ Bytes msgSmallV4{0x00, 0x00, 0x7f, 0x00}; // missing 0001
+ Bytes msgSmallV6{0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // missing 0000000000000001
+ Bytes msgInvalidIp{0x02}; // only 00 (v4) or 01 (v6)
+ Bytes msgNoPort{0x00, 0x00, 0x7f, 0x00, 0x00, 0x01}; // missing 14f0 (8000)
+ REQUIRE_THROWS(P2P::nodesFromMessage(msgSmall));
+ REQUIRE_THROWS(P2P::nodesFromMessage(msgSmallV4));
+ REQUIRE_THROWS(P2P::nodesFromMessage(msgSmallV6));
+ REQUIRE_THROWS(P2P::nodesFromMessage(msgInvalidIp));
+ REQUIRE_THROWS(P2P::nodesFromMessage(msgNoPort));
+ }
+
+ SECTION("nodeInfoToMessage <-> nodeInfoFromMessage") {
+ Bytes msg;
+ P2P::NodeID node1{boost::asio::ip::address_v4::from_string("127.0.0.1"), 8000};
+ P2P::NodeID node2{boost::asio::ip::address_v6::from_string("::1"), 8001};
+ const boost::unordered_flat_map nodes = {
+ {node1, P2P::NodeType::NORMAL_NODE}, {node2, P2P::NodeType::DISCOVERY_NODE}
+ };
+ Options opts = Options::binaryDefaultOptions("options.json");
+
+ // Block w/ no txs, copied from block.cpp
+ PrivKey validatorPrivKey(Hex::toBytes("0x4d5db4107d237df6a3d58ee5f70ae63d73d765d8a1214214d8a13340d0f2750d"));
+ Hash nPrevBlockHash(Hex::toBytes("22143e16db549af9ccfd3b746ea4a74421847fa0fe7e0e278626a4e7307ac0f6"));
+ uint64_t timestamp = 1678400201859;
+ uint64_t nHeight = 92137812;
+ FinalizedBlock newBlock = FinalizedBlock::createNewValidBlock({},{}, nPrevBlockHash, timestamp, nHeight, validatorPrivKey);
+ std::shared_ptr latestBlock = std::make_shared(newBlock);
+
+ P2P::nodeInfoToMessage(msg, latestBlock, nodes, opts);
+ Hex msgNodeVersion = Hex::fromBytes(Utils::create_view_span(msg).subspan(0, 8));
+ Hex hexNodeVersion = Hex::fromBytes(UintConv::uint64ToBytes(opts.getVersion()));
+ Hex msgNHeight = Hex::fromBytes(Utils::create_view_span(msg).subspan(16, 8));
+ Hex hexNHeight = Hex::fromBytes(UintConv::uint64ToBytes(nHeight));
+ Hex msgHash = Hex::fromBytes(Utils::create_view_span(msg).subspan(24, 32));
+ Hex hexHash = Hex::fromBytes(latestBlock->getHash().asBytes());
+ Hex msgPeers = Hex::fromBytes(Utils::create_view_span(msg).subspan(56));
+ Bytes bytesPeers;
+ P2P::nodesToMessage(bytesPeers, nodes);
+ Hex hexPeers = Hex::fromBytes(bytesPeers);
+ REQUIRE(msgNodeVersion.get() == hexNodeVersion.get());
+ REQUIRE(msgNHeight.get() == hexNHeight.get());
+ REQUIRE(msgHash.get() == hexHash.get());
+ REQUIRE(msgPeers.get() == hexPeers.get());
+
+ P2P::NodeInfo conv = P2P::nodeInfoFromMessage(msg);
+ REQUIRE(conv.nodeVersion() == opts.getVersion());
+ REQUIRE(conv.latestBlockHeight() == latestBlock->getNHeight());
+ REQUIRE(conv.latestBlockHash() == latestBlock->getHash());
+ REQUIRE(conv.peers().size() == 2);
+ REQUIRE(conv.peers()[0] == node1);
+ REQUIRE(conv.peers()[1] == node2);
+ }
+
+ SECTION("blocksToMessage <-> blocksFromMessage") {
+ // Data copied from block.cpp, five equal txs for three "different" blocks
+ Bytes msg;
+ PrivKey validatorPrivKey(Hex::toBytes("0x4d5db4107d237df6a3d58ee5f70ae63d73d765d8a1214214d8a13340d0f2750d"));
+ Hash nPrevBlockHash(Hex::toBytes("97a5ebd9bbb5e330b0b3c74b9816d595ffb7a04d4a29fb117ea93f8a333b43be"));
+ uint64_t timestamp = 1678400843316;
+ uint64_t nHeight = 100;
+ TxBlock tx(Hex::toBytes("0x02f874821f9080849502f900849502f900825208942e951aa58c8b9b504a97f597bbb2765c011a8802880de0b6b3a764000080c001a0f56fe87778b4420d3b0f8eba91d28093abfdbea281a188b8516dd8411dc223d7a05c2d2d71ad3473571ff637907d72e6ac399fe4804641dbd9e2d863586c57717d"), 1);
+ std::vector txs1;
+ std::vector txs2;
+ std::vector txs3;
+ for (uint64_t i = 0; i < 5; i++) {
+ txs1.emplace_back(tx);
+ txs2.emplace_back(tx);
+ txs3.emplace_back(tx);
+ }
+ FinalizedBlock block1 = FinalizedBlock::createNewValidBlock(std::move(txs1), {}, nPrevBlockHash, timestamp, nHeight, validatorPrivKey);
+ FinalizedBlock block2 = FinalizedBlock::createNewValidBlock(std::move(txs1), {}, nPrevBlockHash, timestamp, nHeight, validatorPrivKey);
+ FinalizedBlock block3 = FinalizedBlock::createNewValidBlock(std::move(txs1), {}, nPrevBlockHash, timestamp, nHeight, validatorPrivKey);
+ std::vector> blocks = {
+ std::make_shared(block1),
+ std::make_shared(block2),
+ std::make_shared(block3)
+ };
+ P2P::blocksToMessage(msg, blocks);
+ std::vector conv = P2P::blocksFromMessage(Utils::create_view_span(msg), 0);
+
+ // For coverage purposes only, don't care enough to test this through
+ Bytes dataSmall = Hex::toBytes("0x02f87482");
+ Bytes blockSmall = Hex::toBytes("0x02f874821f9080849502f900849502f9");
+ REQUIRE_THROWS(P2P::blocksFromMessage(dataSmall, 0));
+ REQUIRE_THROWS(P2P::blocksFromMessage(blockSmall, 0));
+ }
+
+ SECTION("txsToMessage <-> txsFromMessage") {
+ // 3 simple transactions copied from block.cpp
+ Bytes msg1;
+ Bytes msg2;
+ Bytes rawTx1 = Hex::toBytes("0x02f87301808405f5e100850b5b977f998252089495944f9d42e181d76bb2c7e428410533aa3fed4a88012386f1806fe51080c080a0102fc0316ef07a9be233a270cdeb692e1666710bbdb8be67bf7d896fa96c6bafa038b6cbfdeb433911da6958a9dd3ac24d4ff39f11d1b985efca6d6d79a96a62ce");
+ Bytes rawTx2 = Hex::toBytes("0x02f87501820cfd8405f5e100850b67b98b6b8252089480c67432656d59144ceff962e8faf8926599bcf8880de27d72f9c7632e80c001a057180e5af9ecbac905b17a45b56f1d93626190f1ec8df6a4a8cbbf7c0b8704a9a0166f15ae0e192835e1bf405b82c9faaaf9c8918a9d702441daa5168514377a17");
+ Bytes rawTx3 = Hex::toBytes("0x02f87501826e5c8402faf0808510b3a67cc282520894eb38eab2a8d5f448d7d47005b64697e159aa284e88078cbf1fc56d4f1080c080a0763458eaffb9745026fc6360443e7ff8d171824d0410d48fdf06c08c7d4a8306a031b3d8f1753acc4239ffe0584536f12095651f72a61c684ef221aaa97a315328");
+ TxBlock tx1(rawTx1, 1);
+ TxBlock tx2(rawTx2, 1);
+ TxBlock tx3(rawTx3, 1);
+ const boost::unordered_flat_map txs = {
+ {tx1.hash(), tx1}, {tx2.hash(), tx2}, {tx3.hash(), tx3}
+ };
+ std::vector txsVec = {tx1, tx2, tx3};
+
+ P2P::txsToMessage(msg1, txs);
+ REQUIRE(Hex::fromBytes(Utils::create_view_span(msg1).subspan(0, 4)).get() == Hex::fromBytes(Bytes{0x00, 0x00, 0x00, 0x76}).get());
+ REQUIRE(Hex::fromBytes(Utils::create_view_span(msg1).subspan(4, 118)).get() == Hex::fromBytes(rawTx1).get());
+ REQUIRE(Hex::fromBytes(Utils::create_view_span(msg1).subspan(122, 4)).get() == Hex::fromBytes(Bytes{0x00, 0x00, 0x00, 0x78}).get());
+ REQUIRE(Hex::fromBytes(Utils::create_view_span(msg1).subspan(126, 120)).get() == Hex::fromBytes(rawTx2).get());
+ REQUIRE(Hex::fromBytes(Utils::create_view_span(msg1).subspan(246, 4)).get() == Hex::fromBytes(Bytes{0x00, 0x00, 0x00, 0x78}).get());
+ REQUIRE(Hex::fromBytes(Utils::create_view_span(msg1).subspan(250, 120)).get() == Hex::fromBytes(rawTx3).get());
+
+ P2P::txsToMessage(msg2, txsVec);
+ REQUIRE(Hex::fromBytes(Utils::create_view_span(msg2).subspan(0, 4)).get() == Hex::fromBytes(Bytes{0x00, 0x00, 0x00, 0x76}).get());
+ REQUIRE(Hex::fromBytes(Utils::create_view_span(msg2).subspan(4, 118)).get() == Hex::fromBytes(rawTx1).get());
+ REQUIRE(Hex::fromBytes(Utils::create_view_span(msg2).subspan(122, 4)).get() == Hex::fromBytes(Bytes{0x00, 0x00, 0x00, 0x78}).get());
+ REQUIRE(Hex::fromBytes(Utils::create_view_span(msg2).subspan(126, 120)).get() == Hex::fromBytes(rawTx2).get());
+ REQUIRE(Hex::fromBytes(Utils::create_view_span(msg2).subspan(246, 4)).get() == Hex::fromBytes(Bytes{0x00, 0x00, 0x00, 0x78}).get());
+ REQUIRE(Hex::fromBytes(Utils::create_view_span(msg2).subspan(250, 120)).get() == Hex::fromBytes(rawTx3).get());
+
+ std::vector conv = P2P::txsFromMessage