Skip to content

Commit 4d15bcf

Browse files
glozowinstagibbs
authored andcommitted
[test] package rbf
1 parent dc21f61 commit 4d15bcf

File tree

3 files changed

+732
-0
lines changed

3 files changed

+732
-0
lines changed

src/test/txpackage_tests.cpp

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <key_io.h>
77
#include <policy/packages.h>
88
#include <policy/policy.h>
9+
#include <policy/rbf.h>
910
#include <primitives/transaction.h>
1011
#include <script/script.h>
1112
#include <serialize.h>
@@ -938,4 +939,147 @@ BOOST_FIXTURE_TEST_CASE(package_cpfp_tests, TestChain100Setup)
938939
BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
939940
}
940941
}
942+
943+
BOOST_FIXTURE_TEST_CASE(package_rbf_tests, TestChain100Setup)
944+
{
945+
mineBlocks(5);
946+
LOCK(::cs_main);
947+
size_t expected_pool_size = m_node.mempool->size();
948+
CKey child_key{GenerateRandomKey()};
949+
CScript parent_spk = GetScriptForDestination(WitnessV0KeyHash(child_key.GetPubKey()));
950+
CKey grandchild_key{GenerateRandomKey()};
951+
CScript child_spk = GetScriptForDestination(WitnessV0KeyHash(grandchild_key.GetPubKey()));
952+
953+
const CAmount coinbase_value{50 * COIN};
954+
// Test that de-duplication works. This is not actually package rbf.
955+
{
956+
// 1 parent paying 200sat, 1 child paying 300sat
957+
Package package1;
958+
// 1 parent paying 200sat, 1 child paying 500sat
959+
Package package2;
960+
// Package1 and package2 have the same parent. The children conflict.
961+
auto mtx_parent = CreateValidMempoolTransaction(/*input_transaction=*/m_coinbase_txns[0], /*input_vout=*/0,
962+
/*input_height=*/0, /*input_signing_key=*/coinbaseKey,
963+
/*output_destination=*/parent_spk,
964+
/*output_amount=*/coinbase_value - low_fee_amt, /*submit=*/false);
965+
CTransactionRef tx_parent = MakeTransactionRef(mtx_parent);
966+
package1.push_back(tx_parent);
967+
package2.push_back(tx_parent);
968+
969+
CTransactionRef tx_child_1 = MakeTransactionRef(CreateValidMempoolTransaction(tx_parent, 0, 101, child_key, child_spk, coinbase_value - low_fee_amt - 300, false));
970+
package1.push_back(tx_child_1);
971+
CTransactionRef tx_child_2 = MakeTransactionRef(CreateValidMempoolTransaction(tx_parent, 0, 101, child_key, child_spk, coinbase_value - low_fee_amt - 500, false));
972+
package2.push_back(tx_child_2);
973+
974+
LOCK(m_node.mempool->cs);
975+
const auto submit1 = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package1, /*test_accept=*/false, std::nullopt);
976+
if (auto err_1{CheckPackageMempoolAcceptResult(package1, submit1, /*expect_valid=*/true, m_node.mempool.get())}) {
977+
BOOST_ERROR(err_1.value());
978+
}
979+
980+
// Check precise ResultTypes and mempool size. We know it_parent_1 and it_child_1 exist from above call
981+
auto it_parent_1 = submit1.m_tx_results.find(tx_parent->GetWitnessHash());
982+
auto it_child_1 = submit1.m_tx_results.find(tx_child_1->GetWitnessHash());
983+
BOOST_CHECK_EQUAL(it_parent_1->second.m_result_type, MempoolAcceptResult::ResultType::VALID);
984+
BOOST_CHECK_EQUAL(it_child_1->second.m_result_type, MempoolAcceptResult::ResultType::VALID);
985+
expected_pool_size += 2;
986+
BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
987+
988+
const auto submit2 = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package2, /*test_accept=*/false, std::nullopt);
989+
if (auto err_2{CheckPackageMempoolAcceptResult(package2, submit2, /*expect_valid=*/true, m_node.mempool.get())}) {
990+
BOOST_ERROR(err_2.value());
991+
}
992+
993+
// Check precise ResultTypes and mempool size. We know it_parent_2 and it_child_2 exist from above call
994+
auto it_parent_2 = submit2.m_tx_results.find(tx_parent->GetWitnessHash());
995+
auto it_child_2 = submit2.m_tx_results.find(tx_child_2->GetWitnessHash());
996+
BOOST_CHECK_EQUAL(it_parent_2->second.m_result_type, MempoolAcceptResult::ResultType::MEMPOOL_ENTRY);
997+
BOOST_CHECK_EQUAL(it_child_2->second.m_result_type, MempoolAcceptResult::ResultType::VALID);
998+
BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
999+
1000+
// child1 has been replaced
1001+
BOOST_CHECK(!m_node.mempool->exists(GenTxid::Txid(tx_child_1->GetHash())));
1002+
}
1003+
1004+
// Test package rbf.
1005+
{
1006+
CTransactionRef tx_parent_1 = MakeTransactionRef(CreateValidMempoolTransaction(
1007+
m_coinbase_txns[1], /*input_vout=*/0, /*input_height=*/0,
1008+
coinbaseKey, parent_spk, coinbase_value - 200, /*submit=*/false));
1009+
CTransactionRef tx_child_1 = MakeTransactionRef(CreateValidMempoolTransaction(
1010+
tx_parent_1, /*input_vout=*/0, /*input_height=*/101,
1011+
child_key, child_spk, coinbase_value - 400, /*submit=*/false));
1012+
1013+
CTransactionRef tx_parent_2 = MakeTransactionRef(CreateValidMempoolTransaction(
1014+
m_coinbase_txns[1], /*input_vout=*/0, /*input_height=*/0,
1015+
coinbaseKey, parent_spk, coinbase_value - 800, /*submit=*/false));
1016+
CTransactionRef tx_child_2 = MakeTransactionRef(CreateValidMempoolTransaction(
1017+
tx_parent_2, /*input_vout=*/0, /*input_height=*/101,
1018+
child_key, child_spk, coinbase_value - 800 - 200, /*submit=*/false));
1019+
1020+
CTransactionRef tx_parent_3 = MakeTransactionRef(CreateValidMempoolTransaction(
1021+
m_coinbase_txns[1], /*input_vout=*/0, /*input_height=*/0,
1022+
coinbaseKey, parent_spk, coinbase_value - 199, /*submit=*/false));
1023+
CTransactionRef tx_child_3 = MakeTransactionRef(CreateValidMempoolTransaction(
1024+
tx_parent_3, /*input_vout=*/0, /*input_height=*/101,
1025+
child_key, child_spk, coinbase_value - 199 - 1300, /*submit=*/false));
1026+
1027+
// In all packages, the parents conflict with each other
1028+
BOOST_CHECK(tx_parent_1->GetHash() != tx_parent_2->GetHash() && tx_parent_2->GetHash() != tx_parent_3->GetHash());
1029+
1030+
// 1 parent paying 200sat, 1 child paying 200sat.
1031+
Package package1{tx_parent_1, tx_child_1};
1032+
// 1 parent paying 800sat, 1 child paying 200sat.
1033+
Package package2{tx_parent_2, tx_child_2};
1034+
// 1 parent paying 199sat, 1 child paying 1300sat.
1035+
Package package3{tx_parent_3, tx_child_3};
1036+
1037+
const auto submit1 = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package1, false, std::nullopt);
1038+
if (auto err_1{CheckPackageMempoolAcceptResult(package1, submit1, /*expect_valid=*/true, m_node.mempool.get())}) {
1039+
BOOST_ERROR(err_1.value());
1040+
}
1041+
auto it_parent_1 = submit1.m_tx_results.find(tx_parent_1->GetWitnessHash());
1042+
auto it_child_1 = submit1.m_tx_results.find(tx_child_1->GetWitnessHash());
1043+
BOOST_CHECK_EQUAL(it_parent_1->second.m_result_type, MempoolAcceptResult::ResultType::VALID);
1044+
BOOST_CHECK_EQUAL(it_child_1->second.m_result_type, MempoolAcceptResult::ResultType::VALID);
1045+
expected_pool_size += 2;
1046+
BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
1047+
1048+
// This replacement is actually not package rbf; the parent carries enough fees
1049+
// to replace the entire package on its own.
1050+
const auto submit2 = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package2, false, std::nullopt);
1051+
if (auto err_2{CheckPackageMempoolAcceptResult(package2, submit2, /*expect_valid=*/true, m_node.mempool.get())}) {
1052+
BOOST_ERROR(err_2.value());
1053+
}
1054+
auto it_parent_2 = submit2.m_tx_results.find(tx_parent_2->GetWitnessHash());
1055+
auto it_child_2 = submit2.m_tx_results.find(tx_child_2->GetWitnessHash());
1056+
BOOST_CHECK_EQUAL(it_parent_2->second.m_result_type, MempoolAcceptResult::ResultType::VALID);
1057+
BOOST_CHECK_EQUAL(it_child_2->second.m_result_type, MempoolAcceptResult::ResultType::VALID);
1058+
BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
1059+
1060+
// Package RBF, in which the replacement transaction's child sponsors the fees to meet RBF feerate rules
1061+
const auto submit3 = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package3, false, std::nullopt);
1062+
if (auto err_3{CheckPackageMempoolAcceptResult(package3, submit3, /*expect_valid=*/true, m_node.mempool.get())}) {
1063+
BOOST_ERROR(err_3.value());
1064+
}
1065+
auto it_parent_3 = submit3.m_tx_results.find(tx_parent_3->GetWitnessHash());
1066+
auto it_child_3 = submit3.m_tx_results.find(tx_child_3->GetWitnessHash());
1067+
BOOST_CHECK_EQUAL(it_parent_3->second.m_result_type, MempoolAcceptResult::ResultType::VALID);
1068+
BOOST_CHECK_EQUAL(it_child_3->second.m_result_type, MempoolAcceptResult::ResultType::VALID);
1069+
1070+
// package3 was considered as a package to replace both package2 transactions
1071+
BOOST_CHECK(it_parent_3->second.m_replaced_transactions.size() == 2);
1072+
BOOST_CHECK(it_child_3->second.m_replaced_transactions.empty());
1073+
1074+
std::vector<Wtxid> expected_package3_wtxids({tx_parent_3->GetWitnessHash(), tx_child_3->GetWitnessHash()});
1075+
const auto package3_total_vsize{GetVirtualTransactionSize(*tx_parent_3) + GetVirtualTransactionSize(*tx_child_3)};
1076+
BOOST_CHECK(it_parent_3->second.m_wtxids_fee_calculations.value() == expected_package3_wtxids);
1077+
BOOST_CHECK(it_child_3->second.m_wtxids_fee_calculations.value() == expected_package3_wtxids);
1078+
BOOST_CHECK_EQUAL(it_parent_3->second.m_effective_feerate.value().GetFee(package3_total_vsize), 199 + 1300);
1079+
BOOST_CHECK_EQUAL(it_child_3->second.m_effective_feerate.value().GetFee(package3_total_vsize), 199 + 1300);
1080+
1081+
BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
1082+
}
1083+
1084+
}
9411085
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)