diff --git a/include/gradido_blockchain/interaction/toJson/Context.h b/include/gradido_blockchain/interaction/toJson/Context.h index d7adf60..10a1b97 100644 --- a/include/gradido_blockchain/interaction/toJson/Context.h +++ b/include/gradido_blockchain/interaction/toJson/Context.h @@ -4,6 +4,7 @@ #include "TransactionBodyRole.h" #include "GradidoTransactionRole.h" #include "ConfirmedTransactionRole.h" +#include "FilterRole.h" namespace gradido { namespace interaction { @@ -17,6 +18,8 @@ namespace gradido { : mRole(std::make_unique(gradidoTransaction, format)) {} Context(const data::ConfirmedTransaction& confirmedTransaction, BodyBytesType format = BodyBytesType::JSON) : mRole(std::make_unique(confirmedTransaction, format)) {} + Context(const blockchain::Filter& filter) + : mRole(std::make_unique(filter)) {} inline std::string run(bool pretty = false) { return mRole->run(pretty);} protected: diff --git a/include/gradido_blockchain/interaction/toJson/FilterRole.h b/include/gradido_blockchain/interaction/toJson/FilterRole.h new file mode 100644 index 0000000..bf384ac --- /dev/null +++ b/include/gradido_blockchain/interaction/toJson/FilterRole.h @@ -0,0 +1,25 @@ +#ifndef __GRADIDO_BLOCKCHAIN_INTERACTION_TO_JSON_FILTER_ROLE_H +#define __GRADIDO_BLOCKCHAIN_INTERACTION_TO_JSON_FILTER_ROLE_H + +#include "gradido_blockchain/blockchain/Filter.h" +#include "AbstractRole.h" + +namespace gradido { + namespace interaction { + namespace toJson { + class FilterRole : public AbstractRole + { + public: + FilterRole(const blockchain::Filter& filter) + : mFilter(filter) {} + + rapidjson::Value composeJson(rapidjson::Document& rootDocument) const; + + protected: + const blockchain::Filter& mFilter; + }; + } + } +} + +#endif //__GRADIDO_BLOCKCHAIN_INTERACTION_TO_JSON_FILTER_ROLE_H \ No newline at end of file diff --git a/src/blockchain/InMemory.cpp b/src/blockchain/InMemory.cpp index 751e926..b78e58a 100644 --- a/src/blockchain/InMemory.cpp +++ b/src/blockchain/InMemory.cpp @@ -6,6 +6,8 @@ #include "gradido_blockchain/const.h" #include "gradido_blockchain/blockchain/FilterBuilder.h" +#include "gradido_blockchain/interaction/toJson/Context.h" + #include namespace gradido { @@ -180,8 +182,9 @@ namespace gradido { // disable pagination for prefilter round partFilter.pagination = Pagination(); auto prefilteredTransactions = processEntry(range.first, range.second, notYetFiltered, partFilter); - // and if a filter function exist we sort and call it in correct order - if (filter.filterFunction && !prefilteredTransactions.empty()) { + + // we need to call processEntry again for filterFunction, searchDirection and/or pagination + if (!prefilteredTransactions.empty()) { std::map> sortedTransactions; for (std::shared_ptr entry : prefilteredTransactions) { sortedTransactions.insert({ entry->getTransactionNr(), entry }); @@ -331,17 +334,19 @@ namespace gradido { ); if (lastFromSameSender) { auto lastFromSameSenderBody = lastFromSameSender->getTransactionBody(); - auto lastFromSameSenderRecipient = lastFromSameSenderBody->getDeferredTransfer()->getTransfer().getRecipient(); + auto pubkey = body->getTransfer()->getSender().getPubkey(); - if (lastFromSameSenderBody->isDeferredTransfer() && - lastFromSameSenderRecipient->isTheSame(pubkey)) { - auto lastFromSameSenderTimeout = lastFromSameSenderBody->getDeferredTransfer()->getTimeout(); - // seems we found a matching deferred transfer transaction - auto byTimeoutDeferredRedeemedPairsIt = mTimeoutDeferredRedeemedTransferPairs.equal_range(lastFromSameSenderTimeout.getAsTimepoint()); - for (auto it = byTimeoutDeferredRedeemedPairsIt.first; it != byTimeoutDeferredRedeemedPairsIt.second; ++it) { - if (lastFromSameSender->getSerializedTransaction()->isTheSame(it->second.first->getSerializedTransaction())) { - it->second.second = transactionEntry; - break; + if (lastFromSameSenderBody->isDeferredTransfer()) { + auto lastFromSameSenderRecipient = lastFromSameSenderBody->getDeferredTransfer()->getTransfer().getRecipient(); + if (lastFromSameSenderRecipient->isTheSame(pubkey)) { + auto lastFromSameSenderTimeout = lastFromSameSenderBody->getDeferredTransfer()->getTimeout(); + // seems we found a matching deferred transfer transaction + auto byTimeoutDeferredRedeemedPairsIt = mTimeoutDeferredRedeemedTransferPairs.equal_range(lastFromSameSenderTimeout.getAsTimepoint()); + for (auto it = byTimeoutDeferredRedeemedPairsIt.first; it != byTimeoutDeferredRedeemedPairsIt.second; ++it) { + if (lastFromSameSender->getSerializedTransaction()->isTheSame(it->second.first->getSerializedTransaction())) { + it->second.second = transactionEntry; + break; + } } } } diff --git a/src/interaction/toJson/FilterRole.cpp b/src/interaction/toJson/FilterRole.cpp new file mode 100644 index 0000000..0a7c868 --- /dev/null +++ b/src/interaction/toJson/FilterRole.cpp @@ -0,0 +1,47 @@ +#include "gradido_blockchain/interaction/toJson/FilterRole.h" +#include "gradido_blockchain/lib/DataTypeConverter.h" + +#include "magic_enum/magic_enum.hpp" + +using namespace rapidjson; +using namespace magic_enum; + +namespace gradido { + namespace interaction { + namespace toJson { + Value FilterRole::composeJson(rapidjson::Document& rootDocument) const + { + Value d(kObjectType); + auto& alloc = rootDocument.GetAllocator(); + d.AddMember("minTransactionNr", mFilter.minTransactionNr, alloc); + d.AddMember("maxTransactionNr", mFilter.maxTransactionNr, alloc); + if(mFilter.involvedPublicKey) { + d.AddMember("involvedPublicKey", Value(mFilter.involvedPublicKey->convertToHex().data(), alloc), alloc); + } + d.AddMember("searchDirection", Value(enum_name(mFilter.searchDirection).data(), alloc), alloc); + Value pagination(kObjectType); + pagination.AddMember("size", mFilter.pagination.size, alloc); + pagination.AddMember("page", mFilter.pagination.page, alloc); + d.AddMember("pagination", pagination, alloc); + d.AddMember("coinCommunityId", Value(mFilter.coinCommunityId.data(), alloc), alloc); + + Value timepointInterval(kObjectType); + timepointInterval.AddMember( + "startDate", + Value(DataTypeConverter::timePointToString(mFilter.timepointInterval.getStartDate()).data(), alloc), + alloc + ); + timepointInterval.AddMember( + "endDate", + Value(DataTypeConverter::timePointToString(mFilter.timepointInterval.getEndDate()).data(), alloc), + alloc + ); + d.AddMember("timepointInterval", timepointInterval, alloc); + d.AddMember("transactionType", Value(enum_name(mFilter.transactionType).data(), alloc), alloc); + + return d; + } + + } + } +} \ No newline at end of file diff --git a/test/blockchain/InMemoryTest.cpp b/test/blockchain/InMemoryTest.cpp index 738802b..eb2c707 100644 --- a/test/blockchain/InMemoryTest.cpp +++ b/test/blockchain/InMemoryTest.cpp @@ -157,6 +157,44 @@ bool InMemoryTest::createGradidoCreation( return false; } +bool InMemoryTest::createGradidoTransfer( + int senderKeyPairIndex, + int recipientKeyPairIndex, + GradidoUnit amount, + Timepoint createdAt +) { + assert(senderKeyPairIndex > 0 && senderKeyPairIndex < g_KeyPairs.size()); + assert(recipientKeyPairIndex > 0 && recipientKeyPairIndex < g_KeyPairs.size()); + + TransactionBodyBuilder bodyBuilder; + bodyBuilder + .setMemo("dummy memo") + .setCreatedAt(createdAt) + .setVersionNumber(VERSION_STRING) + .setTransactionTransfer( + TransferAmount(g_KeyPairs[senderKeyPairIndex].publicKey, amount), + g_KeyPairs[recipientKeyPairIndex].publicKey + ) + ; + auto body = bodyBuilder.build(); + + serialize::Context c(*body); + auto bodyBytes = c.run(); + GradidoTransactionBuilder builder; + builder + .setTransactionBody(bodyBytes) + .addSignaturePair(g_KeyPairs[senderKeyPairIndex].publicKey, sign(bodyBytes, g_KeyPairs[senderKeyPairIndex])) + ; + if (mBlockchain->addGradidoTransaction(builder.build(), nullptr, createdAt + chrono::seconds{45})) { + auto lastTransaction = mBlockchain->findOne(Filter::LAST_TRANSACTION)->getConfirmedTransaction(); + auto accountIt = mKeyPairIndexAccountMap.find(senderKeyPairIndex); + accountIt->second.balance = lastTransaction->getAccountBalance(); + accountIt->second.balanceDate = lastTransaction->getConfirmedAt(); + return true; + } + return false; +} + void InMemoryTest::logBlockchain() { auto transactions = dynamic_cast(mBlockchain.get())->getSortedTransactions(); @@ -332,3 +370,27 @@ TEST_F(InMemoryTest, InvalidCreationTransactions) EXPECT_EQ(accountIt->second.balance, GradidoUnit(0.0)); } +TEST_F(InMemoryTest, ValidTransferTransaction) +{ + ASSERT_NO_THROW(createRegisterAddress(3)); + ASSERT_NO_THROW(createRegisterAddress(5)); + auto createdAt = generateNewCreatedAt(); + auto targetDate = getPreviousNMonth2(createdAt, 1); + try { + ASSERT_TRUE(createGradidoCreation(6, 4, 1000.0, createdAt, targetDate)); + } catch(GradidoBlockchainException& ex) { + logBlockchain(); + LOG_F(ERROR, ex.getFullString().data()); + } + auto accountIt = mKeyPairIndexAccountMap.find(6); + EXPECT_EQ(accountIt->second.balance, GradidoUnit(1000.0)); + EXPECT_GT(accountIt->second.balanceDate, createdAt); + + try { + ASSERT_TRUE(createGradidoTransfer(6, 4, 500.10, generateNewCreatedAt())); + } catch(GradidoBlockchainException& ex) { + logBlockchain(); + LOG_F(ERROR, ex.getFullString().data()); + } +} + diff --git a/test/blockchain/InMemoryTest.h b/test/blockchain/InMemoryTest.h index 4d7e2d8..3cfea69 100644 --- a/test/blockchain/InMemoryTest.h +++ b/test/blockchain/InMemoryTest.h @@ -26,6 +26,12 @@ class InMemoryTest : public ::testing::Test Timepoint createdAt, Timepoint targetDate ); + bool createGradidoTransfer( + int senderKeyPairIndex, + int recipientKeyPairIndex, + GradidoUnit amount, + Timepoint createdAt + ); void logBlockchain();