@@ -58,15 +58,17 @@ static void add_coin(const CAmount& nValue, int nInput, SelectionResult& result)
58
58
result.AddInput (group);
59
59
}
60
60
61
- static void add_coin (const CAmount& nValue, int nInput, CoinSet& set , CAmount fee = 0 , CAmount long_term_fee = 0 )
61
+ static void add_coin (const CAmount& nValue, int nInput, SelectionResult& result , CAmount fee, CAmount long_term_fee)
62
62
{
63
63
CMutableTransaction tx;
64
64
tx.vout .resize (nInput + 1 );
65
65
tx.vout [nInput].nValue = nValue;
66
66
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
67
- COutput coin (COutPoint (tx.GetHash (), nInput), tx.vout .at (nInput), /* depth=*/ 1 , /* input_bytes=*/ 148 , /* spendable=*/ true , /* solvable=*/ true , /* safe=*/ true , /* time=*/ 0 , /* from_me=*/ false , fee);
68
- coin.long_term_fee = long_term_fee;
69
- set.insert (std::make_shared<COutput>(coin));
67
+ std::shared_ptr<COutput> coin = std::make_shared<COutput>(COutPoint (tx.GetHash (), nInput), tx.vout .at (nInput), /* depth=*/ 1 , /* input_bytes=*/ 148 , /* spendable=*/ true , /* solvable=*/ true , /* safe=*/ true , /* time=*/ 0 , /* from_me=*/ false , fee);
68
+ OutputGroup group;
69
+ group.Insert (coin, /* ancestors=*/ 0 , /* descendants=*/ 0 );
70
+ coin->long_term_fee = long_term_fee; // group.Insert() will modify long_term_fee, so we need to set it afterwards
71
+ result.AddInput (group);
70
72
}
71
73
72
74
static void add_coin (CoinsResult& available_coins, CWallet& wallet, const CAmount& nValue, CFeeRate feerate = CFeeRate(0 ), int nAge = 6*24, bool fIsFromMe = false, int nInput =0, bool spendable = false, int custom_size = 0)
@@ -827,100 +829,129 @@ BOOST_AUTO_TEST_CASE(SelectCoins_test)
827
829
828
830
BOOST_AUTO_TEST_CASE (waste_test)
829
831
{
830
- CoinSet selection;
831
832
const CAmount fee{100 };
832
833
const CAmount change_cost{125 };
833
834
const CAmount fee_diff{40 };
834
835
const CAmount in_amt{3 * COIN};
835
836
const CAmount target{2 * COIN};
836
837
const CAmount excess{in_amt - fee * 2 - target};
837
838
838
- // Waste with change is the change cost and difference between fee and long term fee
839
- add_coin (1 * COIN, 1 , selection, fee, fee - fee_diff);
840
- add_coin (2 * COIN, 2 , selection, fee, fee - fee_diff);
841
- const CAmount waste1 = GetSelectionWaste (selection, change_cost, target);
842
- BOOST_CHECK_EQUAL (fee_diff * 2 + change_cost, waste1);
843
- selection.clear ();
844
-
845
- // Waste without change is the excess and difference between fee and long term fee
846
- add_coin (1 * COIN, 1 , selection, fee, fee - fee_diff);
847
- add_coin (2 * COIN, 2 , selection, fee, fee - fee_diff);
848
- const CAmount waste_nochange1 = GetSelectionWaste (selection, 0 , target);
849
- BOOST_CHECK_EQUAL (fee_diff * 2 + excess, waste_nochange1);
850
- selection.clear ();
851
-
852
- // Waste with change and fee == long term fee is just cost of change
853
- add_coin (1 * COIN, 1 , selection, fee, fee);
854
- add_coin (2 * COIN, 2 , selection, fee, fee);
855
- BOOST_CHECK_EQUAL (change_cost, GetSelectionWaste (selection, change_cost, target));
856
- selection.clear ();
857
-
858
- // Waste without change and fee == long term fee is just the excess
859
- add_coin (1 * COIN, 1 , selection, fee, fee);
860
- add_coin (2 * COIN, 2 , selection, fee, fee);
861
- BOOST_CHECK_EQUAL (excess, GetSelectionWaste (selection, 0 , target));
862
- selection.clear ();
863
-
864
- // Waste will be greater when fee is greater, but long term fee is the same
865
- add_coin (1 * COIN, 1 , selection, fee * 2 , fee - fee_diff);
866
- add_coin (2 * COIN, 2 , selection, fee * 2 , fee - fee_diff);
867
- const CAmount waste2 = GetSelectionWaste (selection, change_cost, target);
868
- BOOST_CHECK_GT (waste2, waste1);
869
- selection.clear ();
870
-
871
- // Waste with change is the change cost and difference between fee and long term fee
872
- // With long term fee greater than fee, waste should be less than when long term fee is less than fee
873
- add_coin (1 * COIN, 1 , selection, fee, fee + fee_diff);
874
- add_coin (2 * COIN, 2 , selection, fee, fee + fee_diff);
875
- const CAmount waste3 = GetSelectionWaste (selection, change_cost, target);
876
- BOOST_CHECK_EQUAL (fee_diff * -2 + change_cost, waste3);
877
- BOOST_CHECK_LT (waste3, waste1);
878
- selection.clear ();
879
-
880
- // Waste without change is the excess and difference between fee and long term fee
881
- // With long term fee greater than fee, waste should be less than when long term fee is less than fee
882
- add_coin (1 * COIN, 1 , selection, fee, fee + fee_diff);
883
- add_coin (2 * COIN, 2 , selection, fee, fee + fee_diff);
884
- const CAmount waste_nochange2 = GetSelectionWaste (selection, 0 , target);
885
- BOOST_CHECK_EQUAL (fee_diff * -2 + excess, waste_nochange2);
886
- BOOST_CHECK_LT (waste_nochange2, waste_nochange1);
887
- selection.clear ();
888
-
889
- // No Waste when fee == long_term_fee, no change, and no excess
890
- add_coin (1 * COIN, 1 , selection, fee, fee);
891
- add_coin (2 * COIN, 2 , selection, fee, fee);
892
- const CAmount exact_target{in_amt - fee * 2 };
893
- BOOST_CHECK_EQUAL (0 , GetSelectionWaste (selection, /* change_cost=*/ 0 , exact_target));
894
- selection.clear ();
895
-
896
- // No Waste when (fee - long_term_fee) == (-cost_of_change), and no excess
897
- const CAmount new_change_cost{fee_diff * 2 };
898
- add_coin (1 * COIN, 1 , selection, fee, fee + fee_diff);
899
- add_coin (2 * COIN, 2 , selection, fee, fee + fee_diff);
900
- BOOST_CHECK_EQUAL (0 , GetSelectionWaste (selection, new_change_cost, target));
901
- selection.clear ();
902
-
903
- // No Waste when (fee - long_term_fee) == (-excess), no change cost
904
- const CAmount new_target{in_amt - fee * 2 - fee_diff * 2 };
905
- add_coin (1 * COIN, 1 , selection, fee, fee + fee_diff);
906
- add_coin (2 * COIN, 2 , selection, fee, fee + fee_diff);
907
- BOOST_CHECK_EQUAL (0 , GetSelectionWaste (selection, /* change_cost=*/ 0 , new_target));
908
- selection.clear ();
909
-
910
- // Negative waste when the long term fee is greater than the current fee and the selected value == target
911
- const CAmount exact_target1{3 * COIN - 2 * fee};
912
- const CAmount target_waste1{-2 * fee_diff}; // = (2 * fee) - (2 * (fee + fee_diff))
913
- add_coin (1 * COIN, 1 , selection, fee, fee + fee_diff);
914
- add_coin (2 * COIN, 2 , selection, fee, fee + fee_diff);
915
- BOOST_CHECK_EQUAL (target_waste1, GetSelectionWaste (selection, /* change_cost=*/ 0 , exact_target1));
916
- selection.clear ();
917
-
918
- // Negative waste when the long term fee is greater than the current fee and change_cost < - (inputs * (fee - long_term_fee))
919
- const CAmount large_fee_diff{90 };
920
- const CAmount target_waste2{-2 * large_fee_diff + change_cost}; // = (2 * fee) - (2 * (fee + large_fee_diff)) + change_cost
921
- add_coin (1 * COIN, 1 , selection, fee, fee + large_fee_diff);
922
- add_coin (2 * COIN, 2 , selection, fee, fee + large_fee_diff);
923
- BOOST_CHECK_EQUAL (target_waste2, GetSelectionWaste (selection, change_cost, target));
839
+ // The following tests that the waste is calculated correctly in various scenarios.
840
+ // ComputeAndSetWaste will first determine the size of the change output. We don't really
841
+ // care about the change and just want to use the variant that always includes the change_cost,
842
+ // so min_viable_change and change_fee are set to 0 to ensure that.
843
+ {
844
+ // Waste with change is the change cost and difference between fee and long term fee
845
+ SelectionResult selection1{target, SelectionAlgorithm::MANUAL};
846
+ add_coin (1 * COIN, 1 , selection1, fee, fee - fee_diff);
847
+ add_coin (2 * COIN, 2 , selection1, fee, fee - fee_diff);
848
+ selection1.ComputeAndSetWaste (/* min_viable_change=*/ 0 , change_cost, /* change_fee=*/ 0 );
849
+ BOOST_CHECK_EQUAL (fee_diff * 2 + change_cost, selection1.GetWaste ());
850
+
851
+ // Waste will be greater when fee is greater, but long term fee is the same
852
+ SelectionResult selection2{target, SelectionAlgorithm::MANUAL};
853
+ add_coin (1 * COIN, 1 , selection2, fee * 2 , fee - fee_diff);
854
+ add_coin (2 * COIN, 2 , selection2, fee * 2 , fee - fee_diff);
855
+ selection2.ComputeAndSetWaste (/* min_viable_change=*/ 0 , change_cost, /* change_fee=*/ 0 );
856
+ BOOST_CHECK_GT (selection2.GetWaste (), selection1.GetWaste ());
857
+
858
+ // Waste with change is the change cost and difference between fee and long term fee
859
+ // With long term fee greater than fee, waste should be less than when long term fee is less than fee
860
+ SelectionResult selection3{target, SelectionAlgorithm::MANUAL};
861
+ add_coin (1 * COIN, 1 , selection3, fee, fee + fee_diff);
862
+ add_coin (2 * COIN, 2 , selection3, fee, fee + fee_diff);
863
+ selection3.ComputeAndSetWaste (/* min_viable_change=*/ 0 , change_cost, /* change_fee=*/ 0 );
864
+ BOOST_CHECK_EQUAL (fee_diff * -2 + change_cost, selection3.GetWaste ());
865
+ BOOST_CHECK_LT (selection3.GetWaste (), selection1.GetWaste ());
866
+ }
867
+
868
+ {
869
+ // Waste without change is the excess and difference between fee and long term fee
870
+ SelectionResult selection_nochange1{target, SelectionAlgorithm::MANUAL};
871
+ add_coin (1 * COIN, 1 , selection_nochange1, fee, fee - fee_diff);
872
+ add_coin (2 * COIN, 2 , selection_nochange1, fee, fee - fee_diff);
873
+ selection_nochange1.ComputeAndSetWaste (/* min_viable_change=*/ 0 , /* change_cost=*/ 0 , /* change_fee=*/ 0 );
874
+ BOOST_CHECK_EQUAL (fee_diff * 2 + excess, selection_nochange1.GetWaste ());
875
+
876
+ // Waste without change is the excess and difference between fee and long term fee
877
+ // With long term fee greater than fee, waste should be less than when long term fee is less than fee
878
+ SelectionResult selection_nochange2{target, SelectionAlgorithm::MANUAL};
879
+ add_coin (1 * COIN, 1 , selection_nochange2, fee, fee + fee_diff);
880
+ add_coin (2 * COIN, 2 , selection_nochange2, fee, fee + fee_diff);
881
+ selection_nochange2.ComputeAndSetWaste (/* min_viable_change=*/ 0 , /* change_cost=*/ 0 , /* change_fee=*/ 0 );
882
+ BOOST_CHECK_EQUAL (fee_diff * -2 + excess, selection_nochange2.GetWaste ());
883
+ BOOST_CHECK_LT (selection_nochange2.GetWaste (), selection_nochange1.GetWaste ());
884
+ }
885
+
886
+ {
887
+ // Waste with change and fee == long term fee is just cost of change
888
+ SelectionResult selection{target, SelectionAlgorithm::MANUAL};
889
+ add_coin (1 * COIN, 1 , selection, fee, fee);
890
+ add_coin (2 * COIN, 2 , selection, fee, fee);
891
+ selection.ComputeAndSetWaste (/* min_viable_change=*/ 0 , change_cost, /* change_fee=*/ 0 );
892
+ BOOST_CHECK_EQUAL (change_cost, selection.GetWaste ());
893
+ }
894
+
895
+ {
896
+ // Waste without change and fee == long term fee is just the excess
897
+ SelectionResult selection{target, SelectionAlgorithm::MANUAL};
898
+ add_coin (1 * COIN, 1 , selection, fee, fee);
899
+ add_coin (2 * COIN, 2 , selection, fee, fee);
900
+ selection.ComputeAndSetWaste (/* min_viable_change=*/ 0 , /* change_cost=*/ 0 , /* change_fee=*/ 0 );
901
+ BOOST_CHECK_EQUAL (excess, selection.GetWaste ());
902
+ }
903
+
904
+ {
905
+ // No Waste when fee == long_term_fee, no change, and no excess
906
+ const CAmount exact_target{in_amt - fee * 2 };
907
+ SelectionResult selection{exact_target, SelectionAlgorithm::MANUAL};
908
+ add_coin (1 * COIN, 1 , selection, fee, fee);
909
+ add_coin (2 * COIN, 2 , selection, fee, fee);
910
+ selection.ComputeAndSetWaste (/* min_viable_change=*/ 0 , /* change_cost=*/ 0 , /* change_fee=*/ 0 );
911
+ BOOST_CHECK_EQUAL (0 , selection.GetWaste ());
912
+ }
913
+
914
+ {
915
+ // No Waste when (fee - long_term_fee) == (-cost_of_change), and no excess
916
+ SelectionResult selection{target, SelectionAlgorithm::MANUAL};
917
+ const CAmount new_change_cost{fee_diff * 2 };
918
+ add_coin (1 * COIN, 1 , selection, fee, fee + fee_diff);
919
+ add_coin (2 * COIN, 2 , selection, fee, fee + fee_diff);
920
+ selection.ComputeAndSetWaste (/* min_viable_change=*/ 0 , new_change_cost, /* change_fee=*/ 0 );
921
+ BOOST_CHECK_EQUAL (0 , selection.GetWaste ());
922
+ }
923
+
924
+ {
925
+ // No Waste when (fee - long_term_fee) == (-excess), no change cost
926
+ const CAmount new_target{in_amt - fee * 2 - fee_diff * 2 };
927
+ SelectionResult selection{new_target, SelectionAlgorithm::MANUAL};
928
+ add_coin (1 * COIN, 1 , selection, fee, fee + fee_diff);
929
+ add_coin (2 * COIN, 2 , selection, fee, fee + fee_diff);
930
+ selection.ComputeAndSetWaste (/* min_viable_change=*/ 0 , /* change_cost=*/ 0 , /* change_fee=*/ 0 );
931
+ BOOST_CHECK_EQUAL (0 , selection.GetWaste ());
932
+ }
933
+
934
+ {
935
+ // Negative waste when the long term fee is greater than the current fee and the selected value == target
936
+ const CAmount exact_target{3 * COIN - 2 * fee};
937
+ SelectionResult selection{exact_target, SelectionAlgorithm::MANUAL};
938
+ const CAmount target_waste1{-2 * fee_diff}; // = (2 * fee) - (2 * (fee + fee_diff))
939
+ add_coin (1 * COIN, 1 , selection, fee, fee + fee_diff);
940
+ add_coin (2 * COIN, 2 , selection, fee, fee + fee_diff);
941
+ selection.ComputeAndSetWaste (/* min_viable_change=*/ 0 , /* change_cost=*/ 0 , /* change_fee=*/ 0 );
942
+ BOOST_CHECK_EQUAL (target_waste1, selection.GetWaste ());
943
+ }
944
+
945
+ {
946
+ // Negative waste when the long term fee is greater than the current fee and change_cost < - (inputs * (fee - long_term_fee))
947
+ SelectionResult selection{target, SelectionAlgorithm::MANUAL};
948
+ const CAmount large_fee_diff{90 };
949
+ const CAmount target_waste2{-2 * large_fee_diff + change_cost}; // = (2 * fee) - (2 * (fee + large_fee_diff)) + change_cost
950
+ add_coin (1 * COIN, 1 , selection, fee, fee + large_fee_diff);
951
+ add_coin (2 * COIN, 2 , selection, fee, fee + large_fee_diff);
952
+ selection.ComputeAndSetWaste (/* min_viable_change=*/ 0 , change_cost, /* change_fee=*/ 0 );
953
+ BOOST_CHECK_EQUAL (target_waste2, selection.GetWaste ());
954
+ }
924
955
}
925
956
926
957
BOOST_AUTO_TEST_CASE (effective_value_test)
0 commit comments