Files
thehub/testing/utils/TestTransactionBuilder.cpp
T

380 lines
21 KiB
C++
Raw Permalink Normal View History

2022-04-15 17:33:55 +02:00
/*
* This file is part of the Flowee project
* Copyright (C) 2022 Tom Zander <tom@flowee.org>
*
* 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 "TestTransactionBuilder.h"
2024-10-04 12:22:09 +02:00
#include "streaming/BufferPools.h"
2022-04-15 17:33:55 +02:00
#include <primitives/transaction.h>
#include <streaming/streams.h>
#include <TransactionBuilder.h>
#include <utilstrencodings.h>
TestTransactionBuilder::TestTransactionBuilder()
{
// make sure that createTransaction can be called which signs.
ECC_Start();
m_handle = new ECCVerifyHandle();
}
TestTransactionBuilder::~TestTransactionBuilder()
{
delete m_handle;
ECC_Stop();
}
2022-04-15 17:33:55 +02:00
void TestTransactionBuilder::sortTx()
{
// Tx 0a6a357e2f7796444e02638749d9611c008b253fb55f5dc88b739b230ed0c4c3
const char *tx1 = "0100000011aad553bb1650007e9982a8ac79d227cd8c831e1573b11f25573a37664e5f3e64000000006a47304402205438cedd30ee828b0938a863e08d810526123746c1f4abee5b7bc2312373450c02207f26914f4275f8f0040ab3375bacc8c5d610c095db8ed0785de5dc57456591a601210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffffc26f3eb7932f7acddc5ddd26602b77e7516079b03090a16e2c2f5485d1fde028000000006b483045022100f81d98c1de9bb61063a5e6671d191b400fda3a07d886e663799760393405439d0220234303c9af4bad3d665f00277fe70cdd26cd56679f114a40d9107249d29c979401210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff456a9e597129f5df2e11b842833fc19a94c563f57449281d3cd01249a830a1f0000000006a47304402202310b00924794ef68a8f09564fd0bb128838c66bc45d1a3f95c5cab52680f166022039fc99138c29f6c434012b14aca651b1c02d97324d6bd9dd0ffced0782c7e3bd01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff571fb3e02278217852dd5d299947e2b7354a639adc32ec1fa7b82cfb5dec530e000000006b483045022100d276251f1f4479d8521269ec8b1b45c6f0e779fcf1658ec627689fa8a55a9ca50220212a1e307e6182479818c543e1b47d62e4fc3ce6cc7fc78183c7071d245839df01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff5d8de50362ff33d3526ac3602e9ee25c1a349def086a7fc1d9941aaeb9e91d38010000006b4830450221008768eeb1240451c127b88d89047dd387d13357ce5496726fc7813edc6acd55ac022015187451c3fb66629af38fdb061dfb39899244b15c45e4a7ccc31064a059730d01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff60ad3408b89ea19caf3abd5e74e7a084344987c64b1563af52242e9d2a8320f3000000006b4830450221009be4261ec050ebf33fa3d47248c7086e4c247cafbb100ea7cee4aa81cd1383f5022008a70d6402b153560096c849d7da6fe61c771a60e41ff457aac30673ceceafee01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffffe9b483a8ac4129780c88d1babe41e89dc10a26dedbf14f80a28474e9a11104de010000006b4830450221009bc40eee321b39b5dc26883f79cd1f5a226fc6eed9e79e21d828f4c23190c57e022078182fd6086e265589105023d9efa4cba83f38c674a499481bd54eee196b033f01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffffe28db9462d3004e21e765e03a45ecb147f136a20ba8bca78ba60ebfc8e2f8b3b000000006a47304402200fb572b7c6916515452e370c2b6f97fcae54abe0793d804a5a53e419983fae1602205191984b6928bf4a1e25b00e5b5569a0ce1ecb82db2dea75fe4378673b53b9e801210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff7a1ef65ff1b7b7740c662ab6c9735ace4a16279c23a1db5709ed652918ffff54010000006a47304402206bc218a925f7280d615c8ea4f0131a9f26e7fc64cff6eeeb44edb88aba14f1910220779d5d67231bc2d2d93c3c5ab74dcd193dd3d04023e58709ad7ffbf95161be6201210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff850cecf958468ca7ffa6a490afe13b8c271b1326b0ddc1fdfdf9f3c7e365fdba000000006a473044022047df98cc26bd2bfdc5b2b97c27aead78a214810ff023e721339292d5ce50823d02205fe99dc5f667908974dae40cc7a9475af7fa6671ba44f64a00fcd01fa12ab523012102ca46fa75454650afba1784bc7b079d687e808634411e4beff1f70e44596308a1ffffffff8640e312040e476cf6727c60ca3f4a3ad51623500aacdda96e7728dbdd99e8a5000000006a47304402205566aa84d3d84226d5ab93e6f253b57b3ef37eb09bb73441dae35de86271352a02206ee0b7f800f73695a2073a2967c9ad99e19f6ddf18ce877adf822e408ba9291e01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff91c1889c5c24b93b56e643121f7a05a34c10c5495c450504c7b5afcb37e11d7a000000006b483045022100df61d45bbaa4571cdd6c5c822cba458cdc55285cdf7ba9cd5bb9fc18096deb9102201caf8c771204df7fd7c920c4489da7bc3a60e1d23c1a97e237c63afe53250b4a01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff2470947216eb81ea0eeeb4fe19362ec05767db01c3aa3006bb499e8b6d6eaa26010000006a473044022031501a0b2846b8822a32b9947b058d89d32fc758e009fc2130c2e5effc925af70220574ef3c9e350cef726c75114f0701fd8b188c6ec5f84adce0ed5c393828a5ae001210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff0abcd77d65cc14363f8262898335f184d6da5ad060ff9e40bf201741022c2b40010000006b483045022100a6ac110802b699f9a2bff0eea252d32e3d572b19214d49d8bb7405efa2af28f1022033b7563eb595f6d7ed7ec01734e17b505214fe0851352ed9c3c8120d53268e9a0121039106
const auto vector = ParseHex(tx1);
CTransaction inputTx;
CDataStream buf(vector, 0 , 0);
inputTx.Unserialize(buf, 0, 0);
TransactionBuilder builder(inputTx);
builder.setAnonimize(false); // lets not assume default value in the test
{
auto unchanged(builder.createTransaction());
auto transaction = unchanged.createOldTransaction();
QCOMPARE(transaction.vin.size(), inputTx.vin.size());
QCOMPARE(transaction.vout.size(), inputTx.vout.size());
QVERIFY(inputTx.GetHash() == unchanged.createHash());
}
builder.setAnonimize(true);
{
auto unchanged(builder.createTransaction());
auto transaction = unchanged.createOldTransaction();
QCOMPARE(transaction.vin.size(), inputTx.vin.size());
QCOMPARE(transaction.vin.size(), 17);
QCOMPARE(transaction.vout.size(), inputTx.vout.size());
QCOMPARE(transaction.vout.size(), 2);
QVERIFY(inputTx.GetHash() != unchanged.createHash()); // inverted from above
QCOMPARE(transaction.vin.at(0).prevout.hash, uint256S("0e53ec5dfb2cb8a71fec32dc9a634a35b7e24799295ddd5278217822e0b31f57"));
QCOMPARE(transaction.vin.at(1).prevout.hash, uint256S("26aa6e6d8b9e49bb0630aac301db6757c02e3619feb4ee0eea81eb1672947024"));
QCOMPARE(transaction.vin.at(2).prevout.hash, uint256S("28e0fdd185542f2c6ea19030b0796051e7772b6026dd5ddccd7a2f93b73e6fc2"));
QCOMPARE(transaction.vin.at(3).prevout.hash, uint256S("381de9b9ae1a94d9c17f6a08ef9d341a5ce29e2e60c36a52d333ff6203e58d5d"));
QCOMPARE(transaction.vin.at(4).prevout.hash, uint256S("3b8b2f8efceb60ba78ca8bba206a137f14cb5ea4035e761ee204302d46b98de2"));
QCOMPARE(transaction.vin.at(5).prevout.hash, uint256S("402b2c02411720bf409eff60d05adad684f135838962823f3614cc657dd7bc0a"));
QCOMPARE(transaction.vin.at(6).prevout.hash, uint256S("54ffff182965ed0957dba1239c27164ace5a73c9b62a660c74b7b7f15ff61e7a"));
QCOMPARE(transaction.vin.at(7).prevout.hash, uint256S("643e5f4e66373a57251fb173151e838ccd27d279aca882997e005016bb53d5aa"));
QCOMPARE(transaction.vin.at(8).prevout.hash, uint256S("6c1d56f31b2de4bfc6aaea28396b333102b1f600da9c6d6149e96ca43f1102b1"));
QCOMPARE(transaction.vin.at(9).prevout.hash, uint256S("7a1de137cbafb5c70405455c49c5104ca3057a1f1243e6563bb9245c9c88c191"));
QCOMPARE(transaction.vin.at(10).prevout.hash, uint256S("7d037ceb2ee0dc03e82f17be7935d238b35d1deabf953a892a4507bfbeeb3ba4"));
QCOMPARE(transaction.vin.at(11).prevout.hash, uint256S("a5e899dddb28776ea9ddac0a502316d53a4a3fca607c72f66c470e0412e34086"));
QCOMPARE(transaction.vin.at(12).prevout.hash, uint256S("b4112b8f900a7ca0c8b0e7c4dfad35c6be5f6be46b3458974988e1cdb2fa61b8"));
QCOMPARE(transaction.vin.at(13).prevout.hash, uint256S("bafd65e3c7f3f9fdfdc1ddb026131b278c3be1af90a4a6ffa78c4658f9ec0c85"));
QCOMPARE(transaction.vin.at(14).prevout.hash, uint256S("de0411a1e97484a2804ff1dbde260ac19de841bebad1880c782941aca883b4e9"));
QCOMPARE(transaction.vin.at(15).prevout.hash, uint256S("f0a130a84912d03c1d284974f563c5949ac13f8342b8112edff52971599e6a45"));
QCOMPARE(transaction.vin.at(16).prevout.hash, uint256S("f320832a9d2e2452af63154bc687493484a0e7745ebd3aaf9ca19eb80834ad60"));
QCOMPARE(transaction.vout.at(0).nValue, 400057456);
QCOMPARE(transaction.vout.at(1).nValue, 40000000000);
}
}
void TestTransactionBuilder::sortTx2()
{
// Tx 28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f
const char *txk = "010000000255605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d28350000000049483045022100aa46504baa86df8a33b1192b1b9367b4d729dc41e389f2c04f3e5c7f0559aae702205e82253a54bf5c4f65b7428551554b2045167d6d206dfe6a2e198127d3f7df1501ffffffff55605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d2835010000004847304402202329484c35fa9d6bb32a55a70c0982f606ce0e3634b69006138683bcd12cbb6602200c28feb1e2555c3210f1dddb299738b4ff8bbe9667b68cb8764b5ac17b7adf0001ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac00000000";
const auto vector = ParseHex(txk);
CTransaction inputTx;
CDataStream buf(vector, 0 , 0);
inputTx.Unserialize(buf, 0, 0);
TransactionBuilder builder(inputTx);
builder.setAnonimize(true);
auto unchanged(builder.createTransaction());
auto transaction = unchanged.createOldTransaction();
QCOMPARE(transaction.vin.size(), inputTx.vin.size());
QCOMPARE(transaction.vin.size(), 2);
QCOMPARE(transaction.vout.size(), inputTx.vout.size());
QCOMPARE(transaction.vout.size(), 2);
QCOMPARE(transaction.vin.at(0).prevout.hash, uint256S("35288d269cee1941eaebb2ea85e32b42cdb2b04284a56d8b14dcc3f5c65d6055"));
QCOMPARE(transaction.vin.at(1).prevout.hash, uint256S("35288d269cee1941eaebb2ea85e32b42cdb2b04284a56d8b14dcc3f5c65d6055"));
QCOMPARE(transaction.vout.at(0).nValue, 100000000);
QCOMPARE(transaction.vout.at(1).nValue, 2400000000);
}
void TestTransactionBuilder::testFeeAdjustment()
{
PrivateKey key;
key.makeNewKey();
auto id = key.getPubKey().getKeyId();
const int InputAmount = 100000; // one input with this amount of sats.
TransactionBuilder builder;
builder.setAnonimize(false);
// make sure the higher output comes first, otherwise sort doesn't need to do anything.
builder.appendOutput(65433);
builder.pushOutputPay2Address(id);
builder.appendOutput(34567);
builder.pushOutputPay2Address(id);
auto prevTx = uint256S("35288d269cee1941eaebb2ea85e32b42cdb2b04284a56d8b14dcc3f5c65d6055");
builder.appendInput(prevTx, 0);
builder.pushInputSignature(key, CScript() << OP_NOP, InputAmount, TransactionBuilder::Schnorr);
builder.setOutputFeeSource(-10); // none
auto tx = builder.createTransaction();
auto i = Tx::Iterator(tx);
QCOMPARE(i.next(Tx::OutputValue), Tx::OutputValue);
QCOMPARE(i.longData(), 65433);
QCOMPARE(i.next(Tx::OutputValue), Tx::OutputValue);
QCOMPARE(i.longData(), 34567);
builder.setFeeTarget(1000);
builder.setOutputFeeSource(0);
tx = builder.createTransaction();
auto i2 = Tx::Iterator(tx);
QCOMPARE(i2.next(Tx::OutputValue), Tx::OutputValue);
QCOMPARE(i2.longData(), 65433 - tx.size());
QCOMPARE(i2.next(Tx::OutputValue), Tx::OutputValue);
QCOMPARE(i2.longData(), 34567);
// Get it from another output now.
builder.setOutputFeeSource(1);
tx = builder.createTransaction();
auto i3 = Tx::Iterator(tx);
QCOMPARE(i3.next(Tx::OutputValue), Tx::OutputValue);
QCOMPARE(i3.longData(), 65433);
QCOMPARE(i3.next(Tx::OutputValue), Tx::OutputValue);
QCOMPARE(i3.longData(), 34567 - tx.size());
// Double the fee.
builder.setOutputFeeSource(0);
builder.setFeeTarget(2000);
tx = builder.createTransaction();
auto i4 = Tx::Iterator(tx);
QCOMPARE(i4.next(Tx::OutputValue), Tx::OutputValue);
QCOMPARE(i4.longData(), 65433 - tx.size() * 2);
QCOMPARE(i4.next(Tx::OutputValue), Tx::OutputValue);
QCOMPARE(i4.longData(), 34567);
// combine this with sorting.
builder.setAnonimize(true);
builder.setOutputFeeSource(0);
builder.setFeeTarget(2000);
tx = builder.createTransaction();
auto i5 = Tx::Iterator(tx);
QCOMPARE(i5.next(Tx::OutputValue), Tx::OutputValue);
QCOMPARE(i5.longData(), 34567);
QCOMPARE(i5.next(Tx::OutputValue), Tx::OutputValue);
QCOMPARE(i5.longData(), 65433 - tx.size() * 2); // our initial input 0 is now input 1
}
2024-10-04 12:22:09 +02:00
void TestTransactionBuilder::txBuildWithTokens()
{
PrivateKey key;
key.makeNewKey();
auto id = key.getPubKey().getKeyId();
auto prevTx = uint256S("35288d269cee1941eaebb2ea85e32b42cdb2b04284a56d8b14dcc3f5c65d6055");
TransactionBuilder builder;
builder.setAnonimize(false);
2024-10-04 12:22:09 +02:00
builder.appendInput(prevTx, 1);
builder.pushInputSignature(key, CScript() << OP_NOP, 50 * CENT, TransactionBuilder::Schnorr);
builder.appendOutput(20 * CENT);
builder.pushOutputPay2Address(id);
uint256 category = uint256S("0x3a18a863818aa63a8686123455681209042ab4561b123455681209042ab4561b");
builder.startOutputToken(category, Tx::HasFtAmount);
const uint64_t ftAmount = 98327489728;
builder.pushOutputFtAmount(ftAmount);
builder.appendOutput(30 * CENT);
builder.pushOutputPay2Address(id);
auto tx1 = builder.createTransaction(Streaming::pool(1000));
Tx::Iterator iter1(tx1);
auto type = iter1.next();
QCOMPARE(type, Tx::TxVersion);
type = iter1.next();
QCOMPARE(type, Tx::PrevTxHash);
QCOMPARE(iter1.byteData().size(), 32);
auto prev = iter1.uint256Data();
QVERIFY(prev == prevTx);
type = iter1.next();
QCOMPARE(type, Tx::PrevTxIndex);
QCOMPARE(iter1.intData(), 1);
type = iter1.next();
QCOMPARE(type, Tx::TxInScript);
QVERIFY(iter1.byteData().size() >= 100);
type = iter1.next();
QCOMPARE(type, Tx::Sequence);
// output 1
type = iter1.next();
QCOMPARE(type, Tx::OutputValue);
QCOMPARE(iter1.longData(), (uint64_t) 20 * CENT);
type = iter1.next();
QCOMPARE(type, Tx::CashTokenCategory);
QCOMPARE(iter1.uint256Data(), category);
type = iter1.next();
QCOMPARE(type, Tx::CashTokenBitfield);
QCOMPARE(iter1.bitfieldData(), Tx::HasFtAmount);
type = iter1.next();
QCOMPARE(type, Tx::CashTokenAmount);
QCOMPARE(iter1.longData(), ftAmount);
type = iter1.next();
QCOMPARE(type, Tx::OutputScript);
auto outScript = iter1.byteData();
QCOMPARE(outScript.size(), 25);
QCOMPARE(outScript[0], OP_DUP);
QVERIFY(static_cast<uint8_t>(outScript[outScript.size() - 1]) == OP_CHECKSIG);
// output 2
type = iter1.next();
QCOMPARE(type, Tx::OutputValue);
QCOMPARE(iter1.longData(), (uint64_t) 30 * CENT);
type = iter1.next();
QCOMPARE(type, Tx::OutputScript);
outScript = iter1.byteData();
QCOMPARE(outScript.size(), 25);
QCOMPARE(outScript[0], OP_DUP);
QVERIFY(static_cast<uint8_t>(outScript[outScript.size() - 1]) == OP_CHECKSIG);
type = iter1.next();
QCOMPARE(type, Tx::LockTime);
type = iter1.next();
QCOMPARE(type, Tx::End);
// ----
builder.selectOutput(0);
builder.startOutputToken(category, Tx::HasNft | Tx::HasCommitment | Tx::NftMintBaton);
builder.pushNftCommitment({1, 2, 3});
auto tx2 = builder.createTransaction(Streaming::pool(1000));
Tx::Iterator iter2(tx2);
type = iter2.next(Tx::OutputValue);
QCOMPARE(type, Tx::OutputValue);
QCOMPARE(iter2.longData(), (uint64_t) 20 * CENT);
type = iter2.next();
QCOMPARE(type, Tx::CashTokenCategory);
QCOMPARE(iter2.uint256Data(), category);
type = iter2.next();
QCOMPARE(type, Tx::CashTokenBitfield);
QCOMPARE(iter2.bitfieldData(), Tx::HasNft | Tx::HasCommitment | Tx::NftMintBaton);
type = iter2.next();
QCOMPARE(type, Tx::CashTokenCommitment);
QCOMPARE(iter2.dataLength(), 3);
auto commitment = iter2.byteData();
QCOMPARE(commitment[0], 1);
QCOMPARE(commitment[1], 2);
QCOMPARE(commitment[2], 3);
type = iter2.next();
QCOMPARE(type, Tx::OutputScript);
outScript = iter2.byteData();
QCOMPARE(outScript.size(), 25);
QCOMPARE(outScript[0], OP_DUP);
QVERIFY(static_cast<uint8_t>(outScript[outScript.size() - 1]) == OP_CHECKSIG);
const std::vector<uint8_t> longCommitment(128, 0x7e);
builder.pushNftCommitment(longCommitment);
auto tx2b = builder.createTransaction(Streaming::pool(1000));
Tx::Iterator iter2b(tx2b);
type = iter2b.next(Tx::CashTokenCommitment);
QCOMPARE(type, Tx::CashTokenCommitment);
QCOMPARE(iter2b.dataLength(), 128);
auto longRead = iter2b.byteData();
QCOMPARE(longRead.size(), static_cast<int>(longCommitment.size()));
for (int i = 0; i < longRead.size(); ++i)
QCOMPARE(static_cast<uint8_t>(longRead[i]), longCommitment.at(static_cast<size_t>(i)));
try {
builder.pushNftCommitment(std::vector<uint8_t>(129, 0x7e));
QFAIL("commitment larger than 128 bytes should be rejected");
} catch (const std::runtime_error&) {
}
2024-10-04 12:22:09 +02:00
// ----
builder.selectOutput(0);
builder.startOutputToken(category, Tx::HasNft | Tx::HasCommitment | Tx::HasFtAmount);
builder.pushNftCommitment({1, 2, 3});
2024-10-04 12:22:09 +02:00
builder.pushOutputFtAmount(ftAmount);
auto tx3 = builder.createTransaction(Streaming::pool(1000));
Tx::Iterator iter3(tx3);
type = iter3.next(Tx::OutputValue);
QCOMPARE(type, Tx::OutputValue);
QCOMPARE(iter3.longData(), (uint64_t) 20 * CENT);
type = iter3.next();
QCOMPARE(type, Tx::CashTokenCategory);
QCOMPARE(iter3.uint256Data(), category);
type = iter3.next();
QCOMPARE(type, Tx::CashTokenBitfield);
QCOMPARE(iter3.bitfieldData(), Tx::HasNft | Tx::HasCommitment | Tx::HasFtAmount);
type = iter3.next();
QCOMPARE(type, Tx::CashTokenCommitment);
QCOMPARE(iter3.dataLength(), 3);
commitment = iter3.byteData();
QCOMPARE(commitment[0], 1);
QCOMPARE(commitment[1], 2);
QCOMPARE(commitment[2], 3);
type = iter3.next();
QCOMPARE(type, Tx::CashTokenAmount);
QCOMPARE(iter3.longData(), ftAmount);
type = iter3.next();
QCOMPARE(type, Tx::OutputScript);
outScript = iter3.byteData();
QCOMPARE(outScript.size(), 25);
QCOMPARE(outScript[0], OP_DUP);
QVERIFY(static_cast<uint8_t>(outScript[outScript.size() - 1]) == OP_CHECKSIG);
// ----
builder.selectOutput(0);
builder.clearOutputToken();
auto tx4 = builder.createTransaction(Streaming::pool(1000));
Tx::Iterator iter4(tx4);
type = iter4.next(Tx::CashTokenCategory);
QCOMPARE(type, Tx::End);
// combine it with sorting of the transaction.
// outputs are sorted by amount first.
builder.setAnonimize(true);
builder.selectOutput(0);
builder.setOutputValue(32 * CENT); // higher than the second output
builder.startOutputToken(category, Tx::HasNft | Tx::HasFtAmount);
builder.pushOutputFtAmount(ftAmount);
auto tx5 = builder.createTransaction();
// so, the token output is now output 1, due to anonimity sorting
Tx::Iterator iter5(tx5);
type = iter5.next(Tx::OutputValue);
QCOMPARE(type, Tx::OutputValue);
QCOMPARE(iter5.longData(), (uint64_t) 30 * CENT);
type = iter5.next();
QCOMPARE(type, Tx::OutputScript); // no token seen.
type = iter5.next(Tx::OutputValue); // next output
QCOMPARE(type, Tx::OutputValue);
QCOMPARE(iter5.longData(), (uint64_t) 32 * CENT);
type = iter5.next();
QCOMPARE(type, Tx::CashTokenCategory);
QCOMPARE(iter5.uint256Data(), category);
type = iter5.next();
QCOMPARE(type, Tx::CashTokenBitfield);
QCOMPARE(iter5.bitfieldData(), Tx::HasNft | Tx::HasFtAmount);
type = iter5.next();
QCOMPARE(type, Tx::CashTokenAmount);
QCOMPARE(iter5.longData(), ftAmount);
type = iter5.next();
QCOMPARE(type, Tx::OutputScript);
outScript = iter5.byteData();
QCOMPARE(outScript.size(), 25);
QCOMPARE(outScript[0], OP_DUP);
QVERIFY(static_cast<uint8_t>(outScript[outScript.size() - 1]) == OP_CHECKSIG);
2024-10-04 12:22:09 +02:00
}