Skip to content

Commit

Permalink
make validation exception messages better understable and more detailed
Browse files Browse the repository at this point in the history
  • Loading branch information
einhornimmond committed Aug 27, 2024
1 parent 835df0a commit e9adc51
Show file tree
Hide file tree
Showing 13 changed files with 261 additions and 65 deletions.
11 changes: 0 additions & 11 deletions include/gradido_blockchain/blockchain/InMemory.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,6 @@ namespace gradido {
//! \return false if transaction already exist
bool addGradidoTransaction(data::ConstGradidoTransactionPtr gradidoTransaction, memory::ConstBlockPtr messageId, Timepoint confirmedAt);

struct UserBalance
{
UserBalance(memory::ConstBlockPtr _userPubkey, GradidoUnit _balance, Timepoint _balanceDate)
: userPubkey(_userPubkey), balance(_balance), balanceDate(_balanceDate) {}
memory::ConstBlockPtr userPubkey;
GradidoUnit balance;
Timepoint balanceDate;
};

//std::string getUserTransactionsDebugString(const std::string& groupAlias, const std::string& pubkeyHex);

// get all transactions sorted by id
const TransactionEntries& getSortedTransactions();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
namespace gradido {
namespace interaction {
namespace validate {

class AbstractRole
{
public:
Expand Down Expand Up @@ -38,6 +38,7 @@ namespace gradido {

void isPublicKeyForbidden(memory::ConstBlockPtr pubkey) const;

const static std::string mCommunityIdRegexString;
data::TimestampSeconds mConfirmedAt;
data::Timestamp mCreatedAt;
uint32_t mMinSignatureCount;
Expand Down
10 changes: 9 additions & 1 deletion include/gradido_blockchain/interaction/validate/Exceptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,13 @@ namespace gradido {
class GRADIDOBLOCKCHAIN_EXPORT TransactionValidationInvalidInputException : public TransactionValidationException
{
public:
explicit TransactionValidationInvalidInputException(const char* what, const char* fieldname, const char* fieldType = nullptr) noexcept;
explicit TransactionValidationInvalidInputException(
const char* what,
const char* fieldName,
const char* fieldType = nullptr,
const char* expected = nullptr,
const char* actual = nullptr
) noexcept;
std::string getFullString() const noexcept;
//! mainly for return as details for json requests
rapidjson::Value getDetails(rapidjson::Document::AllocatorType& alloc) const;
Expand All @@ -48,6 +54,8 @@ namespace gradido {
protected:
std::string mFieldName;
std::string mFieldType;
std::string mExpected;
std::string mActual;
};

class GRADIDOBLOCKCHAIN_EXPORT TransactionValidationInvalidSignatureException : public TransactionValidationException
Expand Down
4 changes: 3 additions & 1 deletion src/interaction/validate/AbstractRole.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ namespace gradido {
namespace interaction {
namespace validate {

std::regex g_RegExCommunityAlias("^[a-z0-9-]{3,120}$");
#define COMMUNITY_ID_REGEX_STRING "^[a-z0-9-]{3,120}$"
const std::string AbstractRole::mCommunityIdRegexString = COMMUNITY_ID_REGEX_STRING;
std::regex g_RegExCommunityAlias(COMMUNITY_ID_REGEX_STRING);

bool AbstractRole::isValidCommunityAlias(std::string_view communityAlias) const
{
Expand Down
69 changes: 56 additions & 13 deletions src/interaction/validate/ConfirmedTransactionRole.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#include "gradido_blockchain/interaction/validate/ConfirmedTransactionRole.h"
#include "gradido_blockchain/const.h"
#include "gradido_blockchain/interaction/validate/ConfirmedTransactionRole.h"
#include "gradido_blockchain/interaction/validate/Exceptions.h"
#include "gradido_blockchain/interaction/validate/GradidoTransactionRole.h"
#include "gradido_blockchain/lib/DataTypeConverter.h"

using namespace std::chrono;

Expand All @@ -21,23 +22,38 @@ namespace gradido {

if ((type & Type::SINGLE) == Type::SINGLE) {
if (mConfirmedTransaction.getVersionNumber() != GRADIDO_CONFIRMED_TRANSACTION_V3_3_VERSION_STRING) {
TransactionValidationInvalidInputException exception("wrong version in gradido block", "version_number", "uint64");
TransactionValidationInvalidInputException exception(
"wrong version",
"version_number",
"string",
GRADIDO_CONFIRMED_TRANSACTION_V3_3_VERSION_STRING,
mConfirmedTransaction.getVersionNumber().data()
);
exception.setTransactionBody(*body);
throw exception;
}
auto messageId = mConfirmedTransaction.getMessageId();
// with iota it is a BLAKE2b-256 hash with 256 Bits or 32 Bytes
if (messageId && messageId->size() != 32) {
TransactionValidationInvalidInputException exception("wrong size", "message_id", "binary");
TransactionValidationInvalidInputException exception(
"wrong size",
"message_id",
"bytes",
"32",
std::to_string(messageId->size()).data()
);
exception.setTransactionBody(*body);
throw exception;
}

if (confirmedAt - createdAt < duration<int>::zero()) {
std::string expected = ">= " + DataTypeConverter::timePointToString(createdAt);
TransactionValidationInvalidInputException exception(
"timespan between created and received are negative",
"iota milestone timestamp",
std::to_string(mConfirmedTransaction.getConfirmedAt().getSeconds()).data()
"confirmed_at",
"TimestampSeconds",
expected.data(),
DataTypeConverter::timePointToString(confirmedAt).data()
);
exception.setTransactionBody(*body);
throw exception;
Expand Down Expand Up @@ -65,21 +81,48 @@ namespace gradido {
if (previousCreated > createdAt) {
auto timespanBetweenCreatedAndReceived = duration_cast<seconds>(confirmedAt - createdAt);
if (timespanBetweenCreatedAndReceived > MAGIC_NUMBER_MAX_TIMESPAN_BETWEEN_CREATING_AND_RECEIVING_TRANSACTION) {
std::string message = "timespan between created and received are more than " + DataTypeConverter::timespanToString(MAGIC_NUMBER_MAX_TIMESPAN_BETWEEN_CREATING_AND_RECEIVING_TRANSACTION);
std::string expected = "<= (" + DataTypeConverter::timePointToString(createdAt) + " + " + DataTypeConverter::timespanToString(MAGIC_NUMBER_MAX_TIMESPAN_BETWEEN_CREATING_AND_RECEIVING_TRANSACTION) + ")";
TransactionValidationInvalidInputException exception(
"timespan between created and received are more than 2 minutes",
"received/iota milestone timestamp",
"int64"
message.data(),
"confirmed_at",
"TimestampSeconds",
expected.data(),
DataTypeConverter::timePointToString(confirmedAt).data()
);
exception.setTransactionBody(*body);
throw exception;
}
}
auto txHash = mConfirmedTransaction.calculateRunningHash(previousConfirmedTransaction);
if (!mConfirmedTransaction.getRunningHash() || txHash->size() != mConfirmedTransaction.getRunningHash()->size()) {
throw TransactionValidationException("tx hash size isn't equal");
auto runningHash = mConfirmedTransaction.calculateRunningHash(previousConfirmedTransaction);
if (!mConfirmedTransaction.getRunningHash() || runningHash->size() != mConfirmedTransaction.getRunningHash()->size()) {
std::string fieldTypeWithSize = "binary[" + std::to_string(crypto_generichash_BYTES) + "]";
std::string actual = "0";
if(mConfirmedTransaction.getRunningHash()) {
actual = std::to_string(mConfirmedTransaction.getRunningHash()->size());
}
throw TransactionValidationInvalidInputException(
"stored running hash size isn't equal to calculated running hash size",
"running_hash",
fieldTypeWithSize.data(),
std::to_string(runningHash->size()).data(),
actual.data()
);
}
if(!txHash->isTheSame(mConfirmedTransaction.getRunningHash())) {
throw TransactionValidationInvalidInputException("stored tx hash isn't equal to calculated txHash", "txHash", "binary");
if(!runningHash->isTheSame(mConfirmedTransaction.getRunningHash())) {
std::string fieldTypeWithSize = "binary[" + std::to_string(crypto_generichash_BYTES) + "]";
std::string actual = "";
if(mConfirmedTransaction.getRunningHash()) {
actual = mConfirmedTransaction.getRunningHash()->convertToHex();
}

throw TransactionValidationInvalidInputException(
"stored tx hash isn't equal to calculated txHash",
"running_hash",
fieldTypeWithSize.data(),
runningHash->convertToHex().data(),
actual.data()
);
}
}
}
Expand Down
56 changes: 51 additions & 5 deletions src/interaction/validate/Exceptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,30 +33,73 @@ namespace gradido {
//************* Invalid Input *******************

TransactionValidationInvalidInputException::TransactionValidationInvalidInputException(
const char* what, const char* fieldname, const char* fieldType/* = nullptr*/
const char* what,
const char* fieldName,
const char* fieldType/* = nullptr*/,
const char* expected/* = nullptr*/,
const char* actual/* = nullptr*/
) noexcept
: TransactionValidationException(what), mFieldName(fieldname)
: TransactionValidationException(what), mFieldName(fieldName)
{
if (fieldType) {
mFieldType = fieldType;
}
if(expected) {
mExpected = expected;
}
if(actual) {
mActual = actual;
}
}

std::string TransactionValidationInvalidInputException::getFullString() const noexcept
{
std::string result;
auto whatString = what();
result.reserve(mFieldName.size() + mFieldType.size() + strlen(whatString) + 10);
size_t stringSize = mFieldName.size() + mFieldType.size() + strlen(whatString) + 10;
if(!mTransactionMemo.empty()) {
stringSize += mTransactionMemo.size() + 17;
}
if(!mExpected.empty()) {
stringSize += mExpected.size() + 10 + 2;
}
if(!mActual.empty()) {
stringSize += mActual.size() + 8 + 2;
}
result.reserve(stringSize);
result = whatString;
if(!mTransactionMemo.empty()) {
result += " with memo: " + mTransactionMemo + " and ";
}
result += " with " + mFieldName + ": " + mFieldType;
if(!mExpected.empty()) {
if(!result.empty()) result += ", ";
result += "expected: " + mExpected;
}
if(!mActual.empty()) {
if(!result.empty()) result += ", ";
result += "actual: " + mActual;
}
return result;
}
Value TransactionValidationInvalidInputException::getDetails(Document::AllocatorType& alloc) const
{
Value detailsObjs(kObjectType);
detailsObjs.AddMember("what", Value(what(), alloc), alloc);
if(!mTransactionMemo.empty()) {
detailsObjs.AddMember("memo", Value(mTransactionMemo.data(), alloc), alloc);
}
detailsObjs.AddMember("fieldName", Value(mFieldName.data(), alloc), alloc);
detailsObjs.AddMember("fieldType", Value(mFieldType.data(), alloc), alloc);
if(!mFieldType.empty()) {
detailsObjs.AddMember("fieldType", Value(mFieldType.data(), alloc), alloc);
}
if(!mExpected.empty()) {
detailsObjs.AddMember("expected", Value(mExpected.data(), alloc), alloc);
}
if(!mActual.empty()) {
detailsObjs.AddMember("actual", Value(mActual.data(), alloc), alloc);
}

return std::move(detailsObjs);
}

Expand Down Expand Up @@ -137,7 +180,10 @@ namespace gradido {
result += "transaction with memo: " + mTransactionMemo;
}
if (forbiddenPubkeyHex.size()) {
result += ", this forbidden pubkey was used for signing: " + forbiddenPubkeyHex;
if(result.size()) {
result += ", ";
}
result += "this forbidden pubkey was used for signing: " + forbiddenPubkeyHex;
}
return result;
}
Expand Down
78 changes: 64 additions & 14 deletions src/interaction/validate/GradidoCreationRole.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#include "gradido_blockchain/blockchain/FilterBuilder.h"
#include "gradido_blockchain/interaction/validate/GradidoCreationRole.h"
#include "gradido_blockchain/interaction/validate/Exceptions.h"
#include "gradido_blockchain/interaction/validate/TransferAmountRole.h"
#include "gradido_blockchain/blockchain/FilterBuilder.h"
#include "gradido_blockchain/lib/DataTypeConverter.h"

#include "date/date.h"

Expand Down Expand Up @@ -33,10 +34,22 @@ namespace gradido {
validateEd25519PublicKey(recipient.getPubkey(), "recipient pubkey");
auto recipientAmount = recipient.getAmount();
if (recipientAmount > GradidoUnit(1000.0)) {
throw TransactionValidationInvalidInputException("creation amount to high, max 1000 per month", "amount", "string");
throw TransactionValidationInvalidInputException(
"creation amount to high, max 1000 per month",
"amount",
"string",
"<= 10000",
recipientAmount.toString().data()
);
}
if (recipientAmount < GradidoUnit(1.0)) {
throw TransactionValidationInvalidInputException("creation amount to low, min 1 GDD", "amount", "string");
throw TransactionValidationInvalidInputException(
"creation amount to low, min 1 GDD",
"amount",
"string",
">= 1",
recipientAmount.toString().data()
);
}
}

Expand Down Expand Up @@ -144,33 +157,70 @@ namespace gradido {
if (target_date.year() == received.year())
{
if (static_cast<unsigned>(target_date.month()) + targetDateReceivedDistanceMonth < static_cast<unsigned>(received.month())) {
std::string errorMessage =
"year is the same, target date month is more than "
std::string expected = ">= "
+ DataTypeConverter::timePointToString(createdAtTimePoint)
+ " - "
+ std::to_string(static_cast<unsigned>(targetDateReceivedDistanceMonth))
+ " month in past";
throw TransactionValidationInvalidInputException(errorMessage.data(), "target_date", "date time");
+ " months"
;
throw TransactionValidationInvalidInputException(
"year is the same, target date month is invalid",
"target_date",
"TimestampSeconds",
expected.data(),
DataTypeConverter::timePointToString(mGradidoCreation->getTargetDate().getAsTimepoint()).data()
);
}
if (target_date.month() > received.month()) {
throw TransactionValidationInvalidInputException("year is the same, target date month is in future", "target_date", "date time");
std::string expected = "<= " + std::to_string(static_cast<unsigned>(received.month()));
throw TransactionValidationInvalidInputException(
"year is the same, target date month is invalid",
"target_date",
"TimestampSeconds",
expected.data(),
std::to_string(static_cast<unsigned>(target_date.month())).data()
);
}
}
else if (target_date.year() > received.year())
{
throw TransactionValidationInvalidInputException("target date year is in future", "target_date", "date time");
std::string expected = "<= " + std::to_string(static_cast<int>(received.year()));
throw TransactionValidationInvalidInputException(
"target date year is in future",
"target_date",
"TimestampSeconds",
expected.data(),
std::to_string(static_cast<int>(target_date.year())).data()
);
}
else if (static_cast<int>(target_date.year()) + 1 < static_cast<int>(received.year()))
{
throw TransactionValidationInvalidInputException("target date year is in past", "target_date", "date time");
std::string expected = " >= " + std::to_string(static_cast<int>(received.year())) + " - 1 year";
throw TransactionValidationInvalidInputException(
"target date year is in past",
"target_date",
"TimestampSeconds",
expected.data(),
std::to_string(static_cast<int>(target_date.year())).data()
);
}
else
{
// target_date.year +1 == now.year
if (static_cast<unsigned>(target_date.month()) + targetDateReceivedDistanceMonth < static_cast<unsigned>(received.month()) + 12) {
std::string errorMessage =
"target date month is more than "
std::string expected = ">= "
+ DataTypeConverter::timePointToString(createdAtTimePoint)
+ " - "
+ std::to_string(static_cast<unsigned>(targetDateReceivedDistanceMonth))
+ " month in past";
throw TransactionValidationInvalidInputException(errorMessage.data(), "target_date", "date time");
+ " months"
;
throw TransactionValidationInvalidInputException(
"target date month is invalid",
"target_date",
"TimestampSeconds",
expected.data(),
DataTypeConverter::timePointToString(mGradidoCreation->getTargetDate().getAsTimepoint()).data()
);
}
}
}
Expand Down
Loading

0 comments on commit e9adc51

Please sign in to comment.