|
6 | 6 | #include <key_io.h>
|
7 | 7 | #include <policy/packages.h>
|
8 | 8 | #include <policy/policy.h>
|
| 9 | +#include <policy/rbf.h> |
9 | 10 | #include <primitives/transaction.h>
|
10 | 11 | #include <script/script.h>
|
11 | 12 | #include <serialize.h>
|
@@ -938,4 +939,147 @@ BOOST_FIXTURE_TEST_CASE(package_cpfp_tests, TestChain100Setup)
|
938 | 939 | BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
|
939 | 940 | }
|
940 | 941 | }
|
| 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 | +} |
941 | 1085 | BOOST_AUTO_TEST_SUITE_END()
|
0 commit comments