2019-05-29 22:13:32 +02:00
|
|
|
/*
|
|
|
|
|
* This file is part of the Flowee project
|
2024-09-16 23:15:57 +02:00
|
|
|
* Copyright (C) 2019-2024 Tom Zander <tom@flowee.org>
|
2019-05-29 22:13:32 +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 "TestBlockchain.h"
|
|
|
|
|
|
|
|
|
|
#include <Message.h>
|
2021-03-24 18:55:36 +01:00
|
|
|
#include <TransactionBuilder.h>
|
2019-10-16 22:42:44 +02:00
|
|
|
#include <sha256.h>
|
2019-05-29 22:13:32 +02:00
|
|
|
#include <utilstrencodings.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>
|
2021-03-24 18:55:36 +01:00
|
|
|
#include <primitives/transaction.h>
|
|
|
|
|
|
2019-05-29 22:13:32 +02:00
|
|
|
void TestApiBlockchain::testChainInfo()
|
|
|
|
|
{
|
|
|
|
|
startHubs();
|
2019-06-16 00:23:09 +02:00
|
|
|
Message m = waitForReply(0, Message(Api::BlockChainService, Api::BlockChain::GetBlockChainInfo),
|
|
|
|
|
Api::BlockChain::GetBlockChainInfoReply);
|
2019-05-29 22:13:32 +02:00
|
|
|
QCOMPARE(m.serviceId(), (int) Api::BlockChainService);
|
|
|
|
|
QCOMPARE(m.messageId(), (int) Api::BlockChain::GetBlockChainInfoReply);
|
|
|
|
|
Streaming::MessageParser parser(m.body());
|
|
|
|
|
bool seenTitle = false;
|
2021-03-24 18:55:36 +01:00
|
|
|
while (parser.next() == Streaming::FoundTag) {
|
2019-10-21 14:18:19 +02:00
|
|
|
if (parser.tag() == 67) {
|
2019-05-29 22:13:32 +02:00
|
|
|
seenTitle = true;
|
|
|
|
|
QVERIFY(parser.isString());
|
|
|
|
|
QCOMPARE(parser.stringData(), std::string("regtest"));
|
|
|
|
|
}
|
2019-10-21 14:18:19 +02:00
|
|
|
else if (parser.tag() == 68) { // block-height
|
2019-05-29 22:13:32 +02:00
|
|
|
QVERIFY(parser.isInt());
|
|
|
|
|
QCOMPARE(parser.intData(), 0);
|
|
|
|
|
}
|
2019-10-21 14:18:19 +02:00
|
|
|
else if (parser.tag() == 69) { // header-height
|
2019-05-29 22:13:32 +02:00
|
|
|
QVERIFY(parser.isInt());
|
|
|
|
|
QCOMPARE(parser.intData(), 0);
|
|
|
|
|
}
|
2019-10-21 14:18:19 +02:00
|
|
|
else if (parser.tag() == 70) { // last blockhash
|
2019-05-29 22:13:32 +02:00
|
|
|
QVERIFY(parser.isByteArray());
|
|
|
|
|
QCOMPARE(parser.dataLength(), 32);
|
|
|
|
|
QByteArray parsedData(parser.bytesData().data(), 32);
|
|
|
|
|
QCOMPARE(parsedData, QByteArray::fromHex("06226E46111A0B59CAAF126043EB5BBF28C34F3A5E332A1FC7B2B73CF188910F"));
|
|
|
|
|
}
|
2019-10-21 14:18:19 +02:00
|
|
|
else if (parser.tag() == 64) { // difficulty
|
2019-05-29 22:13:32 +02:00
|
|
|
QVERIFY(parser.isDouble());
|
|
|
|
|
}
|
2019-10-21 14:18:19 +02:00
|
|
|
else if (parser.tag() == 63) { // time
|
2019-05-29 22:13:32 +02:00
|
|
|
QVERIFY(parser.isLong());
|
|
|
|
|
QCOMPARE(parser.longData(), (uint64_t) 1296688602);
|
|
|
|
|
}
|
2019-10-21 14:18:19 +02:00
|
|
|
else if (parser.tag() == 71) { // progress
|
2019-05-29 22:13:32 +02:00
|
|
|
QVERIFY(parser.isDouble());
|
|
|
|
|
QCOMPARE(parser.doubleData(), 1.);
|
|
|
|
|
}
|
2019-10-21 14:18:19 +02:00
|
|
|
else if (parser.tag() == 66) { // chain work
|
2019-05-29 22:13:32 +02:00
|
|
|
QVERIFY(parser.isByteArray());
|
|
|
|
|
QCOMPARE(parser.dataLength(), 32);
|
|
|
|
|
QVERIFY(parser.uint256Data() == uint256S("0000000000000000000000000000000000000000000000000000000000000000"));
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-24 18:55:36 +01:00
|
|
|
QVERIFY(seenTitle);
|
2019-05-29 22:13:32 +02:00
|
|
|
}
|
2019-06-16 00:23:09 +02:00
|
|
|
|
|
|
|
|
void TestApiBlockchain::testGetTransaction()
|
|
|
|
|
{
|
|
|
|
|
startHubs();
|
|
|
|
|
feedDefaultBlocksToHub(0);
|
|
|
|
|
|
2023-12-21 15:13:56 +01:00
|
|
|
auto pool = std::make_shared<Streaming::BufferPool>();
|
2019-06-16 00:23:09 +02:00
|
|
|
Streaming::MessageBuilder builder(pool);
|
|
|
|
|
builder.add(Api::BlockChain::BlockHeight, 112);
|
|
|
|
|
builder.add(Api::BlockChain::Tx_OffsetInBlock, 1019);
|
|
|
|
|
|
|
|
|
|
Message m = waitForReply(0, builder.message(Api::BlockChainService, Api::BlockChain::GetTransaction), Api::BlockChain::GetTransactionReply);
|
|
|
|
|
QCOMPARE(m.serviceId(), (int) Api::BlockChainService);
|
2019-08-03 16:50:22 +02:00
|
|
|
QCOMPARE(m.body().size(), 841); // the whole, raw, transaction plus 3 bytes overhead
|
2019-06-16 00:23:09 +02:00
|
|
|
|
|
|
|
|
builder.add(Api::BlockChain::BlockHeight, 112);
|
|
|
|
|
builder.add(Api::BlockChain::Tx_OffsetInBlock, 1019);
|
|
|
|
|
builder.add(Api::BlockChain::Include_TxId, true);
|
|
|
|
|
m = waitForReply(0, builder.message(Api::BlockChainService, Api::BlockChain::GetTransaction), Api::BlockChain::GetTransactionReply);
|
|
|
|
|
QCOMPARE(m.serviceId(), (int) Api::BlockChainService);
|
|
|
|
|
Streaming::MessageParser p(m.body());
|
|
|
|
|
p.next();
|
2019-08-03 16:50:22 +02:00
|
|
|
QCOMPARE(p.uint256Data(), uint256S("0xe455fc2cb76d11a015fe120c18cb590203b6a217640afcf7b3be898db7527a44"));
|
2019-06-16 00:23:09 +02:00
|
|
|
QCOMPARE(p.next(), Streaming::EndOfDocument);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
builder.add(Api::BlockChain::BlockHeight, 112);
|
|
|
|
|
builder.add(Api::BlockChain::Tx_OffsetInBlock, 1019);
|
|
|
|
|
builder.add(Api::BlockChain::Include_Inputs, true);
|
|
|
|
|
m = waitForReply(0, builder.message(Api::BlockChainService, Api::BlockChain::GetTransaction), Api::BlockChain::GetTransactionReply);
|
|
|
|
|
p = Streaming::MessageParser(m.body());
|
|
|
|
|
p.next();
|
2019-10-16 22:42:44 +02:00
|
|
|
QCOMPARE(p.tag(), (uint32_t) 20);
|
2019-08-03 16:50:22 +02:00
|
|
|
QCOMPARE(p.uint256Data(), uint256S("0x5256291727342b4cbd0d09bb09c745f4054d40618d19d2c037c9143d9e7399a4"));
|
2019-06-16 00:23:09 +02:00
|
|
|
p.next();
|
2019-10-16 22:42:44 +02:00
|
|
|
QCOMPARE(p.tag(), (uint32_t) 21);
|
2019-08-03 16:50:22 +02:00
|
|
|
QCOMPARE(p.intData(), 0);
|
2019-06-16 00:23:09 +02:00
|
|
|
p.next();
|
2019-10-16 22:42:44 +02:00
|
|
|
QCOMPARE(p.tag(), (uint32_t) 22);
|
2019-08-03 16:50:22 +02:00
|
|
|
QCOMPARE(p.dataLength(), 107);
|
2019-06-16 00:23:09 +02:00
|
|
|
QCOMPARE(p.next(), Streaming::EndOfDocument);
|
|
|
|
|
|
|
|
|
|
builder.add(Api::BlockChain::BlockHeight, 112);
|
|
|
|
|
builder.add(Api::BlockChain::Tx_OffsetInBlock, 1019);
|
|
|
|
|
builder.add(Api::BlockChain::Include_OutputAmounts, true);
|
|
|
|
|
m = waitForReply(0, builder.message(Api::BlockChainService, Api::BlockChain::GetTransaction), Api::BlockChain::GetTransactionReply);
|
|
|
|
|
p = Streaming::MessageParser(m.body());
|
|
|
|
|
for (int i = 0; i < 20; ++i) {
|
|
|
|
|
QCOMPARE(p.next(), Streaming::FoundTag);
|
|
|
|
|
QCOMPARE(p.tag(), (uint32_t) Api::BlockChain::Tx_Out_Index);
|
|
|
|
|
QCOMPARE(p.intData(), i);
|
|
|
|
|
QCOMPARE(p.next(), Streaming::FoundTag);
|
|
|
|
|
QCOMPARE(p.tag(), (uint32_t) Api::Amount);
|
2019-08-03 16:50:22 +02:00
|
|
|
QCOMPARE(p.longData(), (uint64_t) 249999850);
|
2019-06-16 00:23:09 +02:00
|
|
|
}
|
|
|
|
|
QCOMPARE(p.next(), Streaming::EndOfDocument);
|
|
|
|
|
|
|
|
|
|
builder.add(Api::BlockChain::BlockHeight, 112);
|
|
|
|
|
builder.add(Api::BlockChain::Tx_OffsetInBlock, 1019);
|
|
|
|
|
builder.add(Api::BlockChain::Include_OutputAmounts, true);
|
|
|
|
|
builder.add(Api::BlockChain::FilterOutputIndex, 1);
|
|
|
|
|
m = waitForReply(0, builder.message(Api::BlockChainService, Api::BlockChain::GetTransaction), Api::BlockChain::GetTransactionReply);
|
|
|
|
|
p = Streaming::MessageParser(m.body());
|
|
|
|
|
QCOMPARE(p.next(), Streaming::FoundTag);
|
|
|
|
|
QCOMPARE(p.tag(), (uint32_t) Api::BlockChain::Tx_Out_Index);
|
|
|
|
|
QCOMPARE(p.intData(), 1);
|
|
|
|
|
QCOMPARE(p.next(), Streaming::FoundTag);
|
|
|
|
|
QCOMPARE(p.tag(), (uint32_t) Api::Amount);
|
2019-08-03 16:50:22 +02:00
|
|
|
QCOMPARE(p.longData(), (uint64_t) 249999850);
|
2019-06-16 00:23:09 +02:00
|
|
|
QCOMPARE(p.next(), Streaming::EndOfDocument);
|
|
|
|
|
}
|
2019-10-16 22:42:44 +02:00
|
|
|
|
|
|
|
|
void TestApiBlockchain::testGetScript()
|
|
|
|
|
{
|
|
|
|
|
startHubs();
|
|
|
|
|
feedDefaultBlocksToHub(0);
|
|
|
|
|
|
2023-12-21 15:13:56 +01:00
|
|
|
auto pool = std::make_shared<Streaming::BufferPool>();
|
2019-10-16 22:42:44 +02:00
|
|
|
Streaming::MessageBuilder builder(pool);
|
|
|
|
|
builder.add(Api::BlockChain::BlockHeight, 112);
|
|
|
|
|
builder.add(Api::BlockChain::Tx_OffsetInBlock, 1019);
|
|
|
|
|
// Include_Outputs only gets the unmodified, actual data from the outputs.
|
|
|
|
|
// This currently means the Amount and the Script, and the index
|
|
|
|
|
// Nothing else should be sent
|
|
|
|
|
builder.add(Api::BlockChain::Include_Outputs, true);
|
|
|
|
|
|
|
|
|
|
auto m = waitForReply(0, builder.message(Api::BlockChainService, Api::BlockChain::GetTransaction), Api::BlockChain::GetTransactionReply);
|
|
|
|
|
QCOMPARE(m.serviceId(), (int) Api::BlockChainService);
|
|
|
|
|
Streaming::MessageParser p(m.body());
|
2021-03-24 18:55:36 +01:00
|
|
|
QVector<Streaming::ConstBuffer> scripts;
|
2019-10-16 22:42:44 +02:00
|
|
|
while (p.next() == Streaming::FoundTag) {
|
|
|
|
|
const auto tag = p.tag();
|
|
|
|
|
QVERIFY(tag == Api::BlockChain::Tx_OutputScript
|
|
|
|
|
|| tag == Api::BlockChain::Tx_Out_Amount
|
|
|
|
|
|| tag == Api::BlockChain::Tx_Out_Index);
|
|
|
|
|
if (tag == Api::BlockChain::Tx_OutputScript) // remember for a next test
|
|
|
|
|
scripts.append(p.bytesDataBuffer());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
builder.add(Api::BlockChain::BlockHeight, 112);
|
|
|
|
|
builder.add(Api::BlockChain::Tx_OffsetInBlock, 1019);
|
|
|
|
|
// Include_OutputsAddressHash hashes the output script (once) and includes the hash.
|
|
|
|
|
// This can be used as a unique ID instead of addresses, of which there are too many types.
|
|
|
|
|
builder.add(Api::BlockChain::Include_OutputScriptHash, true);
|
|
|
|
|
m = waitForReply(0, builder.message(Api::BlockChainService, Api::BlockChain::GetTransaction), Api::BlockChain::GetTransactionReply);
|
|
|
|
|
QCOMPARE(m.serviceId(), (int) Api::BlockChainService);
|
|
|
|
|
|
|
|
|
|
p = Streaming::MessageParser(m.body());
|
|
|
|
|
int index = -1;
|
|
|
|
|
while (p.next() == Streaming::FoundTag) {
|
|
|
|
|
const auto tag = p.tag();
|
|
|
|
|
QVERIFY(tag == Api::BlockChain::Tx_Out_ScriptHash
|
|
|
|
|
|| tag == Api::BlockChain::Tx_Out_Index);
|
|
|
|
|
if (tag == Api::BlockChain::Tx_Out_Index) {
|
|
|
|
|
QVERIFY(index == -1);
|
|
|
|
|
QVERIFY(p.isInt());
|
|
|
|
|
index = p.intData();
|
|
|
|
|
QVERIFY(index >= 0);
|
|
|
|
|
QVERIFY(index < scripts.size());
|
|
|
|
|
}
|
|
|
|
|
if (tag == Api::BlockChain::Tx_Out_ScriptHash) {
|
|
|
|
|
QVERIFY(p.isByteArray());
|
|
|
|
|
QCOMPARE(p.dataLength(), 32);
|
|
|
|
|
QVERIFY(index >= 0);
|
|
|
|
|
QVERIFY(index < scripts.size());
|
|
|
|
|
|
|
|
|
|
CSHA256 hasher;
|
|
|
|
|
const auto script = scripts.at(index);
|
2022-05-17 00:46:54 +02:00
|
|
|
hasher.write(script.begin(), script.size());
|
2019-10-16 22:42:44 +02:00
|
|
|
char buf[32];
|
2022-05-17 00:46:54 +02:00
|
|
|
hasher.finalize(buf);
|
2019-10-16 22:42:44 +02:00
|
|
|
QVERIFY(memcmp(buf, p.bytesDataBuffer().begin(), 32) == 0);
|
|
|
|
|
if (index == 5) // lets at least hardcode one comparison. Another test should catch that, but wth
|
|
|
|
|
QVERIFY(p.uint256Data() == uint256S("8CE6447F1046F208F00B68EDF06F3EE974395F795ABAF60732CB6B2B500D53FE"));
|
|
|
|
|
|
|
|
|
|
index = -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
builder.add(Api::BlockChain::BlockHeight, 112);
|
|
|
|
|
builder.add(Api::BlockChain::Tx_OffsetInBlock, 1019);
|
|
|
|
|
// Include_OutputAddresses interprets the output script and returns something if it is a P2PKH address.
|
|
|
|
|
builder.add(Api::BlockChain::Include_OutputAddresses, true);
|
|
|
|
|
m = waitForReply(0, builder.message(Api::BlockChainService, Api::BlockChain::GetTransaction), Api::BlockChain::GetTransactionReply);
|
|
|
|
|
QCOMPARE(m.serviceId(), (int) Api::BlockChainService);
|
|
|
|
|
|
|
|
|
|
p = Streaming::MessageParser(m.body());
|
|
|
|
|
while (p.next() == Streaming::FoundTag) {
|
|
|
|
|
const auto tag = p.tag();
|
|
|
|
|
QVERIFY(tag ==Api::BlockChain::Tx_Out_Address
|
|
|
|
|
|| tag == Api::BlockChain::Tx_Out_Index);
|
|
|
|
|
if (tag == Api::BlockChain::Tx_Out_Address) {
|
|
|
|
|
QVERIFY(p.isByteArray());
|
|
|
|
|
QCOMPARE(p.dataLength(), 20);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-10-20 21:02:17 +02:00
|
|
|
|
|
|
|
|
void TestApiBlockchain::testFilterOnScriptHash()
|
|
|
|
|
{
|
|
|
|
|
startHubs();
|
|
|
|
|
feedDefaultBlocksToHub(0);
|
|
|
|
|
|
2023-12-21 15:13:56 +01:00
|
|
|
auto pool = std::make_shared<Streaming::BufferPool>();
|
2019-10-20 21:02:17 +02:00
|
|
|
Streaming::MessageBuilder builder(pool);
|
|
|
|
|
builder.add(Api::BlockChain::BlockHeight, 115);
|
|
|
|
|
builder.add(Api::BlockChain::AddFilterScriptHash, uint256S("1111111111111111111111111111111111111111111111111111111111111111"));
|
|
|
|
|
builder.add(Api::BlockChain::FullTransactionData, false);
|
|
|
|
|
|
|
|
|
|
auto m = waitForReply(0, builder.message(Api::BlockChainService,
|
|
|
|
|
Api::BlockChain::GetBlock), Api::BlockChain::GetBlockReply);
|
|
|
|
|
|
|
|
|
|
// make sure that we return only transactions matching. Even if nothing matches
|
|
|
|
|
Streaming::MessageParser p(m.body());
|
|
|
|
|
QCOMPARE(p.next(), Streaming::FoundTag);
|
|
|
|
|
QCOMPARE(p.tag(), (uint32_t) Api::BlockChain::BlockHeight);
|
|
|
|
|
QCOMPARE(p.intData(), 115);
|
|
|
|
|
QCOMPARE(p.next(), Streaming::FoundTag);
|
|
|
|
|
QCOMPARE(p.tag(), (uint32_t) Api::BlockChain::BlockHash);
|
|
|
|
|
QCOMPARE(p.dataLength(), 32);
|
|
|
|
|
QCOMPARE(p.next(), Streaming::EndOfDocument);
|
|
|
|
|
|
|
|
|
|
// now use a filter that hits approx 80% of the transactions.
|
|
|
|
|
builder.add(Api::BlockChain::BlockHeight, 115);
|
|
|
|
|
builder.add(Api::BlockChain::SetFilterScriptHash, uint256S("00a7a0e144e7050ef5622b098faf19026631401fa46e68a93fe5e5630b94dcea"));
|
|
|
|
|
builder.add(Api::BlockChain::FullTransactionData, false);
|
|
|
|
|
|
|
|
|
|
m = waitForReply(0, builder.message(Api::BlockChainService,
|
|
|
|
|
Api::BlockChain::GetBlock), Api::BlockChain::GetBlockReply);
|
|
|
|
|
|
|
|
|
|
p = Streaming::MessageParser(m.body());
|
|
|
|
|
QCOMPARE(p.next(), Streaming::FoundTag);
|
|
|
|
|
QCOMPARE(p.tag(), (uint32_t) Api::BlockChain::BlockHeight);
|
|
|
|
|
QCOMPARE(p.intData(), 115);
|
|
|
|
|
QCOMPARE(p.next(), Streaming::FoundTag);
|
|
|
|
|
QCOMPARE(p.tag(), (uint32_t) Api::BlockChain::BlockHash);
|
|
|
|
|
QCOMPARE(p.dataLength(), 32);
|
|
|
|
|
// not the coinbase.
|
|
|
|
|
std::deque<int> positions = {181, 1018, 1855, 2692, 3529, 4366, 5203, 6040, 6877, 7714, 8551,
|
|
|
|
|
9388, 10225, 11063, 11901, 12739, 13577, 14415, 15253, 16091, 16929};
|
|
|
|
|
for (int pos : positions) {
|
|
|
|
|
QCOMPARE(p.next(), Streaming::FoundTag);
|
|
|
|
|
QCOMPARE(p.tag(), (uint32_t) Api::BlockChain::Tx_OffsetInBlock);
|
|
|
|
|
QCOMPARE(p.intData(), pos);
|
|
|
|
|
QCOMPARE(p.next(), Streaming::FoundTag);
|
|
|
|
|
QCOMPARE(p.tag(), (uint32_t) Api::BlockChain::Separator);
|
|
|
|
|
}
|
|
|
|
|
QCOMPARE(p.next(), Streaming::EndOfDocument);
|
|
|
|
|
}
|
2021-02-16 18:59:48 +01:00
|
|
|
|
|
|
|
|
void TestApiBlockchain::fetchTransaction()
|
|
|
|
|
{
|
|
|
|
|
startHubs();
|
|
|
|
|
feedDefaultBlocksToHub(0);
|
|
|
|
|
|
2023-12-21 15:13:56 +01:00
|
|
|
auto pool = std::make_shared<Streaming::BufferPool>();
|
2021-02-16 18:59:48 +01:00
|
|
|
Streaming::MessageBuilder builder(pool);
|
|
|
|
|
|
|
|
|
|
const int BlockSize = 17759;
|
2021-02-16 19:30:11 +01:00
|
|
|
for (int i = -1; i < BlockSize + 10; ++i) {
|
2021-02-16 18:59:48 +01:00
|
|
|
builder.add(Api::BlockChain::BlockHeight, 113);
|
|
|
|
|
builder.add(Api::BlockChain::Tx_OffsetInBlock, i);
|
|
|
|
|
auto m = waitForReply(0, builder.message(Api::BlockChainService,
|
|
|
|
|
Api::BlockChain::GetTransaction), Api::BlockChain::GetTransactionReply);
|
|
|
|
|
if (i > 100 && m.serviceId() == Api::APIService)
|
|
|
|
|
break;
|
|
|
|
|
}
|
2021-02-16 19:30:11 +01:00
|
|
|
|
|
|
|
|
// block height out of range.
|
|
|
|
|
builder.add(Api::BlockChain::BlockHeight, 200);
|
|
|
|
|
builder.add(Api::BlockChain::Tx_OffsetInBlock, 81);
|
|
|
|
|
auto m = waitForReply(0, builder.message(Api::BlockChainService,
|
|
|
|
|
Api::BlockChain::GetTransaction), Api::BlockChain::GetTransactionReply);
|
|
|
|
|
|
|
|
|
|
// negative block height
|
|
|
|
|
builder.add(Api::BlockChain::BlockHeight, -10);
|
|
|
|
|
builder.add(Api::BlockChain::Tx_OffsetInBlock, 81);
|
|
|
|
|
m = waitForReply(0, builder.message(Api::BlockChainService,
|
|
|
|
|
Api::BlockChain::GetTransaction), Api::BlockChain::GetTransactionReply);
|
|
|
|
|
|
|
|
|
|
// and finish with a known good one.
|
|
|
|
|
builder.add(Api::BlockChain::BlockHeight, 113);
|
|
|
|
|
builder.add(Api::BlockChain::Tx_OffsetInBlock, 81);
|
|
|
|
|
m = waitForReply(0, builder.message(Api::BlockChainService,
|
|
|
|
|
Api::BlockChain::GetTransaction), Api::BlockChain::GetTransactionReply);
|
|
|
|
|
QCOMPARE(m.messageId(), Api::BlockChain::GetTransactionReply);
|
2021-02-16 18:59:48 +01:00
|
|
|
}
|
2021-03-19 14:18:40 +01:00
|
|
|
|
2021-03-24 18:55:36 +01:00
|
|
|
void TestApiBlockchain::filterBlock()
|
|
|
|
|
{
|
|
|
|
|
startHubs();
|
|
|
|
|
feedDefaultBlocksToHub(0);
|
|
|
|
|
|
2023-12-21 15:13:56 +01:00
|
|
|
auto pool = std::make_shared<Streaming::BufferPool>();
|
2021-03-24 18:55:36 +01:00
|
|
|
Streaming::MessageBuilder builder(pool);
|
|
|
|
|
// pretty block with a nice op_return and a 3->1 checksig tx.
|
|
|
|
|
const auto blockData = ParseHex("00000020b435cf812ef738b33c7869a56d2e2565d367ae706c46756db1661390393c714fbfaff736e0b5059c54cf4871cae0c57b2e9961f8246e4389217f9c2f6a843fc147505b60ffff7f20000000000302000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0f017401010545423132380400000000ffffffff014cf5052a010000001976a914ec9cc6eb660a07f74e3c785d622e9ca2d951044688ac00000000020000000383f8ea2fa00e72dac2772be53a9648a8ae2deefb276ec0382cb3b99fa7bc1f98020000006a473044022052e514bacbbace82678727ab7127f11e3a4fe890e7a12a7017191231436eabd702207082915ce32db61d199c4e902d5cf32ae9a538dac76799a4e8695576669ea6c5412103a5f3ef29279b67d5369383516e5010a468c685021eb48b0576bd3366fed91ce3ffffffff9290d0b912464a953fbba24315030a2672f99092d5ce9d8bca18ebc7585220d6020000006a47304402202e9c31adf0bf2610193749bead883aca74915eb132fdb5281136f746b71fc9a502202cd746aef5f6b832633406f0747b949832df8e4b9f96ce36d9210e9f28e0bd46412103a5f3ef29279b67d5369383516e5010a468c685021eb48b0576bd3366fed91ce3ffffffff447a52b78d89beb3f7fc0a6417a2b6030259cb180c12fe15a0116db72cfc55e4020000006b483045022100b4307f90a08eea0512405c15dd8fa0519604e6039d66527d8fb4b115072bdf38022077ad2af5d03af82e04b19fd321c0fa88a44307026db05a38b149c094cfa72f33412103a5f3ef29279b67d5369383516e5010a468c685021eb48b0576bd3366fed91ce3ffffffff01d813b42c000000001976a9147d7ea8e0ae26260d310d8f485a80ba37c128d21c88ac000000000200000002407e8798680f76b0b38597f08ab9520d6f4e4c520f73fc1a470eae469e2654c9070000006b483045022100faeb903384c7144c4354cd405d6443e3cbd589eb9eac19223e80c2ae10b5060e022012cedbd780b0d74cffdc74beecac12b5c89a8eb093542d3a865e24a29b4098e64121034abe888b6be024a55356215502a094d4f040bd5f216cc7c4ea6f924cdc0456cfffffffff4570a84bbf31e7e5fd2154a2c330938fc67605434760061371bae29a35b0950d100000006a4730440220331958a8abd7799274eed69a7b5136e61a2b3d0f212627b9b9b1ee06ca1cb4df022037bf04155dab616db91ea5441adf3ce762a9a9ebef85eaf38f20c89352ccb9e0412103dea00a05e04ee8637756a284b1d38c075bda7f722d496a187501f72c21595692ffffffff0200000000000000000a6a08198278900982309a9e757d01000000001976a9140d77abf49d3a286154d15e31b1a7bd9898c3566988ac00000000");
|
|
|
|
|
QCOMPARE(Api::Mining::GenericByteData, 1);
|
|
|
|
|
QCOMPARE(Api::Mining::SubmitBlock, 0);
|
|
|
|
|
QCOMPARE(Api::Mining::SubmitBlockReply, 1);
|
|
|
|
|
QCOMPARE(Api::Mining::BlockHash, 5);
|
|
|
|
|
builder.add(Api::Mining::GenericByteData, blockData);
|
|
|
|
|
auto m = waitForReply(0, builder.message(Api::MiningService, Api::Mining::SubmitBlock), Api::Mining::SubmitBlockReply);
|
|
|
|
|
QCOMPARE(m.messageId(), Api::Mining::SubmitBlockReply);
|
|
|
|
|
{
|
|
|
|
|
Streaming::MessageParser parser(m);
|
|
|
|
|
bool checkedHash = false;
|
|
|
|
|
while (parser.next() == Streaming::FoundTag) {
|
|
|
|
|
if (parser.tag() == Api::Mining::BlockHash) {
|
|
|
|
|
QVERIFY(parser.isByteArray());
|
|
|
|
|
QCOMPARE(parser.dataLength(), 32);
|
|
|
|
|
QVERIFY(parser.uint256Data() == uint256S("0f820cace2f02b8cc475bb46e03172cf4eb09874ad2ef1ef1e8c91ca62bdceef"));
|
|
|
|
|
checkedHash = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
QVERIFY(checkedHash);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// filter on a script-type that does not exist in any of our transactions.
|
|
|
|
|
QCOMPARE(Api::BlockChain::BlockHeight, 7);
|
|
|
|
|
QCOMPARE(Api::BlockChain::FilterOnScriptType, 39);
|
|
|
|
|
builder.add(Api::BlockChain::BlockHeight, 116);
|
|
|
|
|
builder.add(Api::BlockChain::FilterOnScriptType, Api::ScriptTag::OpCheckmultisig);
|
|
|
|
|
m = waitForReply(0, builder.message(Api::BlockChainService, Api::BlockChain::GetBlock), Api::BlockChain::GetBlockReply);
|
|
|
|
|
{
|
|
|
|
|
Streaming::MessageParser parser(m);
|
|
|
|
|
while (parser.next() == Streaming::FoundTag) {
|
|
|
|
|
QVERIFY(parser.tag() != Api::Separator); // the above request should give us no results.
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// filter on op_return which should give us 2 transactions.
|
|
|
|
|
QCOMPARE(Api::BlockChain::FilterOnScriptType, 39);
|
|
|
|
|
QCOMPARE(Api::BlockChain::Tx_OffsetInBlock, 8);
|
|
|
|
|
QCOMPARE(Api::BlockChain::FullTransactionData, 45);
|
|
|
|
|
builder.add(Api::BlockChain::BlockHeight, 116);
|
|
|
|
|
builder.add(Api::BlockChain::FilterOnScriptType, Api::ScriptTag::OpReturn);
|
|
|
|
|
builder.add(Api::BlockChain::FullTransactionData, false);
|
|
|
|
|
m = waitForReply(0, builder.message(Api::BlockChainService, Api::BlockChain::GetBlock), Api::BlockChain::GetBlockReply);
|
|
|
|
|
{
|
|
|
|
|
Streaming::MessageParser parser(m);
|
|
|
|
|
bool checkedTx = false;
|
|
|
|
|
while (parser.next() == Streaming::FoundTag) {
|
|
|
|
|
if (parser.tag() == Api::BlockChain::Tx_OffsetInBlock) {
|
|
|
|
|
QCOMPARE(parser.intData(), 667);
|
|
|
|
|
checkedTx = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
QVERIFY(checkedTx);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-16 23:15:57 +02:00
|
|
|
void TestApiBlockchain::getBlockHeader()
|
|
|
|
|
{
|
|
|
|
|
startHubs();
|
|
|
|
|
feedDefaultBlocksToHub(0);
|
|
|
|
|
|
|
|
|
|
auto pool = std::make_shared<Streaming::BufferPool>();
|
|
|
|
|
Streaming::MessageBuilder builder(pool);
|
|
|
|
|
builder.add(Api::BlockChain::BlockHeight, 1);
|
|
|
|
|
auto m = waitForReply(0, builder.message(Api::BlockChainService, Api::BlockChain::GetBlockHeader), Api::BlockChain::GetBlockHeaderReply);
|
|
|
|
|
QCOMPARE(m.messageId(), Api::BlockChain::GetBlockHeaderReply);
|
|
|
|
|
Streaming::MessageParser parser(m);
|
|
|
|
|
bool hash = false;
|
|
|
|
|
bool confirmations = false;
|
|
|
|
|
bool height = false;
|
|
|
|
|
bool version = false;
|
|
|
|
|
bool merkleRoot = false;
|
|
|
|
|
bool time = false;
|
|
|
|
|
bool medianTime = false;
|
|
|
|
|
bool nonce = false;
|
|
|
|
|
bool target = false;
|
|
|
|
|
bool difficulty = false;
|
|
|
|
|
while (parser.next() == Streaming::FoundTag) {
|
|
|
|
|
if (parser.tag() == Api::BlockChain::BlockHash) {
|
|
|
|
|
hash = true;
|
|
|
|
|
QVERIFY(parser.isByteArray());
|
|
|
|
|
QCOMPARE(parser.dataLength(), 32);
|
|
|
|
|
QVERIFY(parser.uint256Data() == uint256S("34acac5e517599a2e2a0dc66a1742960ab02c493fec0c609d9bef035453e1b5f"));
|
|
|
|
|
}
|
|
|
|
|
else if (parser.tag() == Api::BlockChain::Confirmations) {
|
|
|
|
|
confirmations = true;
|
|
|
|
|
QVERIFY(parser.isInt());
|
|
|
|
|
QCOMPARE(parser.intData(), 115);
|
|
|
|
|
}
|
|
|
|
|
else if (parser.tag() == Api::BlockChain::BlockHeight) {
|
|
|
|
|
height = true;
|
|
|
|
|
QVERIFY(parser.isInt());
|
|
|
|
|
QCOMPARE(parser.intData(), 1);
|
|
|
|
|
}
|
|
|
|
|
else if (parser.tag() == Api::BlockChain::Version) {
|
|
|
|
|
version = true;
|
|
|
|
|
QVERIFY(parser.isInt());
|
|
|
|
|
QCOMPARE(parser.intData(), 0x20000000);
|
|
|
|
|
}
|
|
|
|
|
else if (parser.tag() == Api::BlockChain::MerkleRoot) {
|
|
|
|
|
merkleRoot = true;
|
|
|
|
|
QVERIFY(parser.isByteArray());
|
|
|
|
|
QCOMPARE(parser.dataLength(), 32);
|
|
|
|
|
QVERIFY(parser.uint256Data() == uint256S("6baef8b71bb304caf1b9c8b8ca7a791b7cd219d7d0207675680ff227c875cd1f"));
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else if (parser.tag() == Api::BlockChain::Time) {
|
|
|
|
|
QVERIFY(parser.isLong());
|
|
|
|
|
QCOMPARE(parser.longData(), 1564842809);
|
|
|
|
|
time = true;
|
|
|
|
|
}
|
|
|
|
|
else if (parser.tag() == Api::BlockChain::MedianTime) {
|
|
|
|
|
medianTime = true;
|
|
|
|
|
QVERIFY(parser.isLong());
|
|
|
|
|
QCOMPARE(parser.longData(), 1564842809);
|
|
|
|
|
}
|
|
|
|
|
else if (parser.tag() == Api::BlockChain::Nonce) {
|
|
|
|
|
nonce = true;
|
|
|
|
|
QVERIFY(parser.isLong());
|
|
|
|
|
QCOMPARE(parser.longData(), 0);
|
|
|
|
|
}
|
|
|
|
|
else if (parser.tag() == Api::BlockChain::BlockTarget) {
|
|
|
|
|
target = true;
|
|
|
|
|
QVERIFY(parser.isByteArray());
|
|
|
|
|
QCOMPARE(parser.dataLength(), 4);
|
|
|
|
|
// should be 207fffff
|
|
|
|
|
auto bytes = parser.bytesDataBuffer();
|
|
|
|
|
QCOMPARE(bytes[0], 0x20);
|
|
|
|
|
QCOMPARE(bytes[1], 0x7f);
|
|
|
|
|
QCOMPARE((uint8_t) bytes[2], 0xff);
|
|
|
|
|
QCOMPARE((uint8_t) bytes[3], 0xff);
|
|
|
|
|
}
|
|
|
|
|
else if (parser.tag() == Api::BlockChain::Difficulty) {
|
|
|
|
|
difficulty = true;
|
|
|
|
|
QVERIFY(parser.isDouble());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// nothing should be missing.
|
|
|
|
|
QVERIFY(hash);
|
|
|
|
|
QVERIFY(confirmations);
|
|
|
|
|
QVERIFY(height);
|
|
|
|
|
QVERIFY(version);
|
|
|
|
|
QVERIFY(merkleRoot);
|
|
|
|
|
QVERIFY(time);
|
|
|
|
|
QVERIFY(medianTime);
|
|
|
|
|
QVERIFY(nonce);
|
|
|
|
|
QVERIFY(target);
|
|
|
|
|
QVERIFY(difficulty);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-19 14:18:40 +01:00
|
|
|
QTEST_MAIN(TestApiBlockchain)
|