2020-05-19 17:39:21 +02:00
|
|
|
/*
|
|
|
|
|
* This file is part of the Flowee project
|
2021-06-20 22:44:44 +02:00
|
|
|
* Copyright (C) 2019-2020 Tom Zander <tom@flowee.org>
|
2020-05-19 17:39:21 +02:00
|
|
|
*
|
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
#include "TestTxIdMonitor.h"
|
2021-03-17 21:07:25 +01:00
|
|
|
#include "TestData.h"
|
2020-05-19 17:39:21 +02:00
|
|
|
|
|
|
|
|
#include <uint256.h>
|
|
|
|
|
#include <streaming/BufferPool.h>
|
|
|
|
|
#include <streaming/MessageBuilder.h>
|
|
|
|
|
#include <streaming/MessageParser.h>
|
2021-11-02 11:04:59 +01:00
|
|
|
#include <primitives/Tx.h>
|
2020-05-19 17:39:21 +02:00
|
|
|
|
|
|
|
|
void TestTxIdMonitor::testBasic()
|
|
|
|
|
{
|
|
|
|
|
startHubs();
|
|
|
|
|
|
2023-12-21 15:13:56 +01:00
|
|
|
auto pool = std::make_shared<Streaming::BufferPool>();
|
|
|
|
|
pool->reserve(50);
|
2020-05-19 17:39:21 +02:00
|
|
|
Streaming::MessageBuilder builder(pool);
|
|
|
|
|
|
|
|
|
|
// (coinbase @ 5)
|
|
|
|
|
builder.add(Api::TransactionMonitor::TxId, uint256S("29bae599098880a9e609418ea5dee1da154b214c3cad6f99d96596badfadefc1"));
|
|
|
|
|
// (last tx in block 111)
|
|
|
|
|
builder.add(Api::TransactionMonitor::TxId, uint256S("a3132383a40424c1ac9644be637c3bb471dffdfa4bf05139b9a0ce3b8d45db88"));
|
|
|
|
|
auto m = waitForReply(0, builder.message(Api::TransactionMonitorService,
|
|
|
|
|
Api::TransactionMonitor::Subscribe), Api::TransactionMonitor::SubscribeReply);
|
|
|
|
|
QVERIFY(m.messageId() == Api::TransactionMonitor::SubscribeReply);
|
|
|
|
|
feedDefaultBlocksToHub(0);
|
|
|
|
|
|
|
|
|
|
int total = 0;
|
|
|
|
|
for (auto message : m_hubs[0].messages) {
|
|
|
|
|
if (message.serviceId() == Api::TransactionMonitorService) {
|
|
|
|
|
QVERIFY(message.messageId() == Api::TransactionMonitor::SubscribeReply
|
|
|
|
|
|| message.messageId() == Api::TransactionMonitor::TransactionFound);
|
|
|
|
|
if (message.messageId() != Api::TransactionMonitor::TransactionFound)
|
|
|
|
|
continue;
|
|
|
|
|
++total;
|
|
|
|
|
bool seenBlockHeight = false, seenOffsetInBlock = false;
|
|
|
|
|
int seenTransactions = 0;
|
|
|
|
|
Streaming::MessageParser p(message.body());
|
|
|
|
|
QCOMPARE(p.next(), Streaming::FoundTag);
|
|
|
|
|
while (true) {
|
|
|
|
|
if (p.tag() == Api::TransactionMonitor::TxId) {
|
|
|
|
|
seenTransactions++;
|
|
|
|
|
QCOMPARE(p.isByteArray(), true);
|
|
|
|
|
QCOMPARE(p.dataLength(), 32);
|
|
|
|
|
}
|
|
|
|
|
else if (p.tag() == Api::TransactionMonitor::OffsetInBlock) {
|
|
|
|
|
seenOffsetInBlock = true;
|
|
|
|
|
QCOMPARE(p.isInt(), true);
|
|
|
|
|
QVERIFY(p.intData() > 80);
|
|
|
|
|
}
|
|
|
|
|
else if (p.tag() == Api::TransactionMonitor::BlockHeight) {
|
|
|
|
|
seenBlockHeight = true;
|
|
|
|
|
QCOMPARE(p.isInt(), true);
|
|
|
|
|
QVERIFY(p.intData() > 0);
|
|
|
|
|
}
|
|
|
|
|
auto type = p.next();
|
|
|
|
|
QVERIFY(type != Streaming::Error);
|
|
|
|
|
if (type != Streaming::FoundTag)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QVERIFY(seenTransactions > 0);
|
|
|
|
|
QVERIFY(seenTransactions <= 2);
|
|
|
|
|
QVERIFY(seenOffsetInBlock);
|
|
|
|
|
QVERIFY(seenBlockHeight);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
QCOMPARE(total, 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TestTxIdMonitor::testMempool()
|
|
|
|
|
{
|
|
|
|
|
startHubs(2);
|
|
|
|
|
feedDefaultBlocksToHub(0);
|
|
|
|
|
QVERIFY(waitForHeight(115)); // make sure all nodes are at the same tip.
|
|
|
|
|
|
2023-12-21 15:13:56 +01:00
|
|
|
auto pool = std::make_shared<Streaming::BufferPool>();
|
|
|
|
|
auto txs = TestData::createDoubleSpend(pool);
|
2021-03-17 21:07:25 +01:00
|
|
|
Tx tx1 = txs.first;
|
2023-12-21 15:13:56 +01:00
|
|
|
pool->reserve(50);
|
2020-05-19 17:39:21 +02:00
|
|
|
Streaming::MessageBuilder builder(pool);
|
|
|
|
|
builder.add(Api::TransactionMonitor::TxId, tx1.createHash());
|
|
|
|
|
auto m = waitForReply(0, builder.message(Api::TransactionMonitorService,
|
|
|
|
|
Api::TransactionMonitor::Subscribe), Api::TransactionMonitor::SubscribeReply);
|
|
|
|
|
QVERIFY(m.messageId() == Api::TransactionMonitor::SubscribeReply);
|
|
|
|
|
|
|
|
|
|
logDebug() << "Sending tx1 to hub0" << tx1.createHash();
|
2020-09-02 13:52:36 +02:00
|
|
|
Streaming::MessageBuilder builder2(pool);
|
|
|
|
|
builder2.add(Api::LiveTransactions::GenericByteData, tx1.data());
|
2020-05-19 17:39:21 +02:00
|
|
|
|
|
|
|
|
m = waitForReply(0, builder.message(Api::LiveTransactionService, Api::LiveTransactions::SendTransaction),
|
|
|
|
|
Api::TransactionMonitorService, Api::TransactionMonitor::TransactionFound);
|
|
|
|
|
QVERIFY(m.messageId() == Api::TransactionMonitor::TransactionFound);
|
|
|
|
|
bool seenBlockHeight = false, seenOffsetInBlock = false, seenTxId = false;
|
|
|
|
|
Streaming::MessageParser p(m.body());
|
|
|
|
|
while (p.next() == Streaming::FoundTag) {
|
|
|
|
|
if (p.tag() == Api::TransactionMonitor::TxId) {
|
|
|
|
|
QVERIFY(p.uint256Data() == tx1.createHash());
|
|
|
|
|
seenTxId = true;
|
|
|
|
|
}
|
|
|
|
|
else if (p.tag() == Api::TransactionMonitor::BlockHeight)
|
|
|
|
|
seenBlockHeight = true;
|
|
|
|
|
else if (p.tag() == Api::TransactionMonitor::OffsetInBlock)
|
|
|
|
|
seenOffsetInBlock = true;
|
|
|
|
|
else
|
|
|
|
|
QVERIFY(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QCOMPARE(seenTxId, true);
|
|
|
|
|
QCOMPARE(seenBlockHeight, false);
|
|
|
|
|
QCOMPARE(seenOffsetInBlock, false);
|
|
|
|
|
|
|
|
|
|
// second part; to connect to a peer that already has it in the mempool.
|
2020-09-02 13:52:36 +02:00
|
|
|
builder2.add(Api::TransactionMonitor::TxId, tx1.createHash());
|
|
|
|
|
m = waitForReply(1, builder2.message(Api::TransactionMonitorService, Api::TransactionMonitor::Subscribe),
|
2020-05-19 17:39:21 +02:00
|
|
|
Api::TransactionMonitorService, Api::TransactionMonitor::TransactionFound);
|
|
|
|
|
QVERIFY(m.messageId() == Api::TransactionMonitor::TransactionFound);
|
|
|
|
|
|
|
|
|
|
seenTxId = false;
|
|
|
|
|
p = Streaming::MessageParser(m.body());
|
|
|
|
|
while (p.next() == Streaming::FoundTag) {
|
|
|
|
|
if (p.tag() == Api::TransactionMonitor::TxId) {
|
|
|
|
|
QVERIFY(p.uint256Data() == tx1.createHash());
|
|
|
|
|
seenTxId = true;
|
|
|
|
|
}
|
|
|
|
|
else if (p.tag() == Api::TransactionMonitor::BlockHeight)
|
|
|
|
|
seenBlockHeight = true;
|
|
|
|
|
else if (p.tag() == Api::TransactionMonitor::OffsetInBlock)
|
|
|
|
|
seenOffsetInBlock = true;
|
|
|
|
|
else
|
|
|
|
|
QVERIFY(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QCOMPARE(seenTxId, true);
|
|
|
|
|
QCOMPARE(seenBlockHeight, false);
|
|
|
|
|
QCOMPARE(seenOffsetInBlock, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TestTxIdMonitor::testDoubleSpend()
|
|
|
|
|
{
|
|
|
|
|
startHubs(2);
|
|
|
|
|
feedDefaultBlocksToHub(0);
|
|
|
|
|
QVERIFY(waitForHeight(115)); // make sure all nodes are at the same tip.
|
|
|
|
|
|
2023-12-21 15:13:56 +01:00
|
|
|
auto pool = std::make_shared<Streaming::BufferPool>();
|
|
|
|
|
auto txs = TestData::createDoubleSpend(pool);
|
2021-03-17 21:07:25 +01:00
|
|
|
Tx tx1 = txs.first;
|
|
|
|
|
Tx tx2 = txs.second;
|
2020-05-19 17:39:21 +02:00
|
|
|
|
|
|
|
|
// subscribing
|
|
|
|
|
Streaming::MessageBuilder builder(pool);
|
|
|
|
|
builder.add(Api::TransactionMonitor::TxId, tx1.createHash());
|
|
|
|
|
auto subScribeMessage = builder.message(Api::TransactionMonitorService, Api::TransactionMonitor::Subscribe);
|
|
|
|
|
auto m = waitForReply(0, subScribeMessage, Api::TransactionMonitor::SubscribeReply);
|
|
|
|
|
QCOMPARE(m.messageId(), (int) Api::TransactionMonitor::SubscribeReply);
|
|
|
|
|
m = waitForReply(1, subScribeMessage, Api::TransactionMonitor::SubscribeReply);
|
|
|
|
|
QCOMPARE(m.messageId(), (int) Api::TransactionMonitor::SubscribeReply);
|
|
|
|
|
|
|
|
|
|
// I wills end the txs to peer zero, lets catch the double spend proof on peer 1
|
|
|
|
|
m_hubs[1].m_waitForMessageId = Api::TransactionMonitor::DoubleSpendFound;
|
|
|
|
|
m_hubs[1].m_waitForServiceId = Api::TransactionMonitorService;
|
|
|
|
|
m_hubs[1].m_waitForMessageId2 = -1;
|
2022-08-20 19:18:34 +02:00
|
|
|
m_hubs[1].m_foundMessage.storeRelaxed(nullptr);
|
2020-05-19 17:39:21 +02:00
|
|
|
|
|
|
|
|
logDebug() << "Sending tx1 to hub0" << tx1.createHash();
|
2020-09-02 13:52:36 +02:00
|
|
|
Streaming::MessageBuilder builder2(pool);
|
|
|
|
|
builder2.add(Api::LiveTransactions::GenericByteData, tx1.data());
|
|
|
|
|
m = waitForReply(0, builder2.message(Api::LiveTransactionService, Api::LiveTransactions::SendTransaction),
|
2020-05-19 17:39:21 +02:00
|
|
|
Api::TransactionMonitorService, Api::TransactionMonitor::TransactionFound);
|
|
|
|
|
QCOMPARE(m.messageId(), (int) Api::TransactionMonitor::TransactionFound);
|
|
|
|
|
logDebug() << "Sending tx2 to hub0" << tx2.createHash();
|
2020-09-02 13:52:36 +02:00
|
|
|
Streaming::MessageBuilder builder3(pool);
|
|
|
|
|
builder3.add(Api::LiveTransactions::GenericByteData, tx2.data());
|
|
|
|
|
m = waitForReply(0, builder3.message(Api::LiveTransactionService, Api::LiveTransactions::SendTransaction),
|
2020-05-19 17:39:21 +02:00
|
|
|
Api::TransactionMonitorService, Api::TransactionMonitor::DoubleSpendFound);
|
|
|
|
|
QCOMPARE(m.messageId(), (int) Api::TransactionMonitor::DoubleSpendFound);
|
|
|
|
|
|
|
|
|
|
// we should get a notification about the double spend attempt of the transaction I am interested in (tx1).
|
|
|
|
|
// Lets take a look at the message.
|
|
|
|
|
|
|
|
|
|
Streaming::MessageParser p(m);
|
|
|
|
|
p.next();
|
|
|
|
|
QCOMPARE(p.tag(), (uint32_t) Api::TransactionMonitor::TxId);
|
|
|
|
|
QCOMPARE(p.dataLength(), 32);
|
|
|
|
|
QVERIFY((p.uint256Data() == tx1.createHash()));
|
|
|
|
|
p.next();
|
|
|
|
|
QCOMPARE(p.tag(), (uint32_t) Api::TransactionMonitor::TransactionData); // the transaction.
|
|
|
|
|
QCOMPARE(p.dataLength(), tx2.size());
|
|
|
|
|
|
2020-06-01 19:58:38 +02:00
|
|
|
QTRY_VERIFY_WITH_TIMEOUT(m_hubs[1].m_foundMessage != nullptr, 30000); // this waits until its arrived.
|
2022-08-20 19:18:34 +02:00
|
|
|
Message *m2 = m_hubs[1].m_foundMessage.loadRelaxed();
|
2020-05-19 17:39:21 +02:00
|
|
|
assert(m2); // the qtry should have failed.
|
|
|
|
|
|
|
|
|
|
p = Streaming::MessageParser(*m2);
|
|
|
|
|
p.next();
|
|
|
|
|
QCOMPARE(p.tag(), (uint32_t) Api::TransactionMonitor::TxId);
|
|
|
|
|
QCOMPARE(p.dataLength(), 32);
|
|
|
|
|
QVERIFY((p.uint256Data() == tx1.createHash()));
|
|
|
|
|
p.next();
|
2020-07-05 16:31:49 +02:00
|
|
|
QVERIFY(p.tag() == Api::TransactionMonitor::DoubleSpendProofData
|
|
|
|
|
|| p.tag() == Api::TransactionMonitor::TransactionData);
|
2020-05-19 17:39:21 +02:00
|
|
|
}
|
2021-03-19 14:18:40 +01:00
|
|
|
|
|
|
|
|
QTEST_MAIN(TestTxIdMonitor)
|