mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-22 06:52:36 +01:00
coinselection: Use COutput instead of CInputCoin
Also rename setPresetCoins to preset_coins
This commit is contained in:
parent
14fbb57b79
commit
70f31f1a81
5 changed files with 72 additions and 71 deletions
|
@ -82,9 +82,9 @@ static void add_coin(const CAmount& nValue, int nInput, std::vector<OutputGroup>
|
||||||
CMutableTransaction tx;
|
CMutableTransaction tx;
|
||||||
tx.vout.resize(nInput + 1);
|
tx.vout.resize(nInput + 1);
|
||||||
tx.vout[nInput].nValue = nValue;
|
tx.vout[nInput].nValue = nValue;
|
||||||
CInputCoin coin(MakeTransactionRef(tx), nInput);
|
COutput output(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 0, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ true);
|
||||||
set.emplace_back();
|
set.emplace_back();
|
||||||
set.back().Insert(coin, 0, true, 0, 0, false);
|
set.back().Insert(output, /*ancestors=*/ 0, /*descendants=*/ 0, /*positive_only=*/ false);
|
||||||
}
|
}
|
||||||
// Copied from src/wallet/test/coinselector_tests.cpp
|
// Copied from src/wallet/test/coinselector_tests.cpp
|
||||||
static CAmount make_hard_case(int utxos, std::vector<OutputGroup>& utxo_pool)
|
static CAmount make_hard_case(int utxos, std::vector<OutputGroup>& utxo_pool)
|
||||||
|
|
|
@ -50,8 +50,8 @@ struct {
|
||||||
* The Branch and Bound algorithm is described in detail in Murch's Master Thesis:
|
* The Branch and Bound algorithm is described in detail in Murch's Master Thesis:
|
||||||
* https://murch.one/wp-content/uploads/2016/11/erhardt2016coinselection.pdf
|
* https://murch.one/wp-content/uploads/2016/11/erhardt2016coinselection.pdf
|
||||||
*
|
*
|
||||||
* @param const std::vector<CInputCoin>& utxo_pool The set of UTXOs that we are choosing from.
|
* @param const std::vector<OutputGroup>& utxo_pool The set of UTXO groups that we are choosing from.
|
||||||
* These UTXOs will be sorted in descending order by effective value and the CInputCoins'
|
* These UTXO groups will be sorted in descending order by effective value and the OutputGroups'
|
||||||
* values are their effective values.
|
* values are their effective values.
|
||||||
* @param const CAmount& selection_target This is the value that we want to select. It is the lower
|
* @param const CAmount& selection_target This is the value that we want to select. It is the lower
|
||||||
* bound of the range.
|
* bound of the range.
|
||||||
|
@ -315,29 +315,29 @@ std::optional<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups,
|
||||||
|
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
void OutputGroup::Insert(const CInputCoin& output, int depth, bool from_me, size_t ancestors, size_t descendants, bool positive_only) {
|
void OutputGroup::Insert(const COutput& output, size_t ancestors, size_t descendants, bool positive_only) {
|
||||||
// Compute the effective value first
|
// Compute the effective value first
|
||||||
const CAmount coin_fee = output.m_input_bytes < 0 ? 0 : m_effective_feerate.GetFee(output.m_input_bytes);
|
const CAmount coin_fee = output.input_bytes < 0 ? 0 : m_effective_feerate.GetFee(output.input_bytes);
|
||||||
const CAmount ev = output.txout.nValue - coin_fee;
|
const CAmount ev = output.txout.nValue - coin_fee;
|
||||||
|
|
||||||
// Filter for positive only here before adding the coin
|
// Filter for positive only here before adding the coin
|
||||||
if (positive_only && ev <= 0) return;
|
if (positive_only && ev <= 0) return;
|
||||||
|
|
||||||
m_outputs.push_back(output);
|
m_outputs.push_back(output);
|
||||||
CInputCoin& coin = m_outputs.back();
|
COutput& coin = m_outputs.back();
|
||||||
|
|
||||||
coin.m_fee = coin_fee;
|
coin.fee = coin_fee;
|
||||||
fee += coin.m_fee;
|
fee += coin.fee;
|
||||||
|
|
||||||
coin.m_long_term_fee = coin.m_input_bytes < 0 ? 0 : m_long_term_feerate.GetFee(coin.m_input_bytes);
|
coin.long_term_fee = coin.input_bytes < 0 ? 0 : m_long_term_feerate.GetFee(coin.input_bytes);
|
||||||
long_term_fee += coin.m_long_term_fee;
|
long_term_fee += coin.long_term_fee;
|
||||||
|
|
||||||
coin.effective_value = ev;
|
coin.effective_value = ev;
|
||||||
effective_value += coin.effective_value;
|
effective_value += coin.effective_value;
|
||||||
|
|
||||||
m_from_me &= from_me;
|
m_from_me &= coin.from_me;
|
||||||
m_value += output.txout.nValue;
|
m_value += coin.txout.nValue;
|
||||||
m_depth = std::min(m_depth, depth);
|
m_depth = std::min(m_depth, coin.depth);
|
||||||
// ancestors here express the number of ancestors the new coin will end up having, which is
|
// ancestors here express the number of ancestors the new coin will end up having, which is
|
||||||
// the sum, rather than the max; this will overestimate in the cases where multiple inputs
|
// the sum, rather than the max; this will overestimate in the cases where multiple inputs
|
||||||
// have common ancestors
|
// have common ancestors
|
||||||
|
@ -359,7 +359,7 @@ CAmount OutputGroup::GetSelectionAmount() const
|
||||||
return m_subtract_fee_outputs ? m_value : effective_value;
|
return m_subtract_fee_outputs ? m_value : effective_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
CAmount GetSelectionWaste(const std::set<CInputCoin>& inputs, CAmount change_cost, CAmount target, bool use_effective_value)
|
CAmount GetSelectionWaste(const std::set<COutput>& inputs, CAmount change_cost, CAmount target, bool use_effective_value)
|
||||||
{
|
{
|
||||||
// This function should not be called with empty inputs as that would mean the selection failed
|
// This function should not be called with empty inputs as that would mean the selection failed
|
||||||
assert(!inputs.empty());
|
assert(!inputs.empty());
|
||||||
|
@ -367,8 +367,8 @@ CAmount GetSelectionWaste(const std::set<CInputCoin>& inputs, CAmount change_cos
|
||||||
// Always consider the cost of spending an input now vs in the future.
|
// Always consider the cost of spending an input now vs in the future.
|
||||||
CAmount waste = 0;
|
CAmount waste = 0;
|
||||||
CAmount selected_effective_value = 0;
|
CAmount selected_effective_value = 0;
|
||||||
for (const CInputCoin& coin : inputs) {
|
for (const COutput& coin : inputs) {
|
||||||
waste += coin.m_fee - coin.m_long_term_fee;
|
waste += coin.fee - coin.long_term_fee;
|
||||||
selected_effective_value += use_effective_value ? coin.effective_value : coin.txout.nValue;
|
selected_effective_value += use_effective_value ? coin.effective_value : coin.txout.nValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,14 +413,14 @@ void SelectionResult::AddInput(const OutputGroup& group)
|
||||||
m_use_effective = !group.m_subtract_fee_outputs;
|
m_use_effective = !group.m_subtract_fee_outputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::set<CInputCoin>& SelectionResult::GetInputSet() const
|
const std::set<COutput>& SelectionResult::GetInputSet() const
|
||||||
{
|
{
|
||||||
return m_selected_inputs;
|
return m_selected_inputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CInputCoin> SelectionResult::GetShuffledInputVector() const
|
std::vector<COutput> SelectionResult::GetShuffledInputVector() const
|
||||||
{
|
{
|
||||||
std::vector<CInputCoin> coins(m_selected_inputs.begin(), m_selected_inputs.end());
|
std::vector<COutput> coins(m_selected_inputs.begin(), m_selected_inputs.end());
|
||||||
Shuffle(coins.begin(), coins.end(), FastRandomContext());
|
Shuffle(coins.begin(), coins.end(), FastRandomContext());
|
||||||
return coins;
|
return coins;
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,6 +138,18 @@ public:
|
||||||
{
|
{
|
||||||
return CInputCoin(outpoint, txout, input_bytes);
|
return CInputCoin(outpoint, txout, input_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool operator<(const COutput& rhs) const {
|
||||||
|
return outpoint < rhs.outpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const COutput& rhs) const {
|
||||||
|
return outpoint != rhs.outpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const COutput& rhs) const {
|
||||||
|
return outpoint == rhs.outpoint;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Parameters for one iteration of Coin Selection. */
|
/** Parameters for one iteration of Coin Selection. */
|
||||||
|
@ -207,7 +219,7 @@ struct CoinEligibilityFilter
|
||||||
struct OutputGroup
|
struct OutputGroup
|
||||||
{
|
{
|
||||||
/** The list of UTXOs contained in this output group. */
|
/** The list of UTXOs contained in this output group. */
|
||||||
std::vector<CInputCoin> m_outputs;
|
std::vector<COutput> m_outputs;
|
||||||
/** Whether the UTXOs were sent by the wallet to itself. This is relevant because we may want at
|
/** Whether the UTXOs were sent by the wallet to itself. This is relevant because we may want at
|
||||||
* least a certain number of confirmations on UTXOs received from outside wallets while trusting
|
* least a certain number of confirmations on UTXOs received from outside wallets while trusting
|
||||||
* our own UTXOs more. */
|
* our own UTXOs more. */
|
||||||
|
@ -244,7 +256,7 @@ struct OutputGroup
|
||||||
m_subtract_fee_outputs(params.m_subtract_fee_outputs)
|
m_subtract_fee_outputs(params.m_subtract_fee_outputs)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void Insert(const CInputCoin& output, int depth, bool from_me, size_t ancestors, size_t descendants, bool positive_only);
|
void Insert(const COutput& output, size_t ancestors, size_t descendants, bool positive_only);
|
||||||
bool EligibleForSpending(const CoinEligibilityFilter& eligibility_filter) const;
|
bool EligibleForSpending(const CoinEligibilityFilter& eligibility_filter) const;
|
||||||
CAmount GetSelectionAmount() const;
|
CAmount GetSelectionAmount() const;
|
||||||
};
|
};
|
||||||
|
@ -266,13 +278,13 @@ struct OutputGroup
|
||||||
* @param[in] use_effective_value Whether to use the input's effective value (when true) or the real value (when false).
|
* @param[in] use_effective_value Whether to use the input's effective value (when true) or the real value (when false).
|
||||||
* @return The waste
|
* @return The waste
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] CAmount GetSelectionWaste(const std::set<CInputCoin>& inputs, CAmount change_cost, CAmount target, bool use_effective_value = true);
|
[[nodiscard]] CAmount GetSelectionWaste(const std::set<COutput>& inputs, CAmount change_cost, CAmount target, bool use_effective_value = true);
|
||||||
|
|
||||||
struct SelectionResult
|
struct SelectionResult
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
/** Set of inputs selected by the algorithm to use in the transaction */
|
/** Set of inputs selected by the algorithm to use in the transaction */
|
||||||
std::set<CInputCoin> m_selected_inputs;
|
std::set<COutput> m_selected_inputs;
|
||||||
/** The target the algorithm selected for. Note that this may not be equal to the recipient amount as it can include non-input fees */
|
/** The target the algorithm selected for. Note that this may not be equal to the recipient amount as it can include non-input fees */
|
||||||
const CAmount m_target;
|
const CAmount m_target;
|
||||||
/** Whether the input values for calculations should be the effective value (true) or normal value (false) */
|
/** Whether the input values for calculations should be the effective value (true) or normal value (false) */
|
||||||
|
@ -298,9 +310,9 @@ public:
|
||||||
[[nodiscard]] CAmount GetWaste() const;
|
[[nodiscard]] CAmount GetWaste() const;
|
||||||
|
|
||||||
/** Get m_selected_inputs */
|
/** Get m_selected_inputs */
|
||||||
const std::set<CInputCoin>& GetInputSet() const;
|
const std::set<COutput>& GetInputSet() const;
|
||||||
/** Get the vector of CInputCoins that will be used to fill in a CTransaction's vin */
|
/** Get the vector of COutputs that will be used to fill in a CTransaction's vin */
|
||||||
std::vector<CInputCoin> GetShuffledInputVector() const;
|
std::vector<COutput> GetShuffledInputVector() const;
|
||||||
|
|
||||||
bool operator<(SelectionResult other) const;
|
bool operator<(SelectionResult other) const;
|
||||||
};
|
};
|
||||||
|
|
|
@ -301,11 +301,10 @@ std::vector<OutputGroup> GroupOutputs(const CWallet& wallet, const std::vector<C
|
||||||
|
|
||||||
size_t ancestors, descendants;
|
size_t ancestors, descendants;
|
||||||
wallet.chain().getTransactionAncestry(output.outpoint.hash, ancestors, descendants);
|
wallet.chain().getTransactionAncestry(output.outpoint.hash, ancestors, descendants);
|
||||||
CInputCoin input_coin = output.GetInputCoin();
|
|
||||||
|
|
||||||
// Make an OutputGroup containing just this output
|
// Make an OutputGroup containing just this output
|
||||||
OutputGroup group{coin_sel_params};
|
OutputGroup group{coin_sel_params};
|
||||||
group.Insert(input_coin, output.depth, output.from_me, ancestors, descendants, positive_only);
|
group.Insert(output, ancestors, descendants, positive_only);
|
||||||
|
|
||||||
// Check the OutputGroup's eligibility. Only add the eligible ones.
|
// Check the OutputGroup's eligibility. Only add the eligible ones.
|
||||||
if (positive_only && group.GetSelectionAmount() <= 0) continue;
|
if (positive_only && group.GetSelectionAmount() <= 0) continue;
|
||||||
|
@ -317,9 +316,9 @@ std::vector<OutputGroup> GroupOutputs(const CWallet& wallet, const std::vector<C
|
||||||
// We want to combine COutputs that have the same scriptPubKey into single OutputGroups
|
// We want to combine COutputs that have the same scriptPubKey into single OutputGroups
|
||||||
// except when there are more than OUTPUT_GROUP_MAX_ENTRIES COutputs grouped in an OutputGroup.
|
// except when there are more than OUTPUT_GROUP_MAX_ENTRIES COutputs grouped in an OutputGroup.
|
||||||
// To do this, we maintain a map where the key is the scriptPubKey and the value is a vector of OutputGroups.
|
// To do this, we maintain a map where the key is the scriptPubKey and the value is a vector of OutputGroups.
|
||||||
// For each COutput, we check if the scriptPubKey is in the map, and if it is, the COutput's CInputCoin is added
|
// For each COutput, we check if the scriptPubKey is in the map, and if it is, the COutput is added
|
||||||
// to the last OutputGroup in the vector for the scriptPubKey. When the last OutputGroup has
|
// to the last OutputGroup in the vector for the scriptPubKey. When the last OutputGroup has
|
||||||
// OUTPUT_GROUP_MAX_ENTRIES CInputCoins, a new OutputGroup is added to the end of the vector.
|
// OUTPUT_GROUP_MAX_ENTRIES COutputs, a new OutputGroup is added to the end of the vector.
|
||||||
std::map<CScript, std::vector<OutputGroup>> spk_to_groups_map;
|
std::map<CScript, std::vector<OutputGroup>> spk_to_groups_map;
|
||||||
for (const auto& output : outputs) {
|
for (const auto& output : outputs) {
|
||||||
// Skip outputs we cannot spend
|
// Skip outputs we cannot spend
|
||||||
|
@ -327,8 +326,7 @@ std::vector<OutputGroup> GroupOutputs(const CWallet& wallet, const std::vector<C
|
||||||
|
|
||||||
size_t ancestors, descendants;
|
size_t ancestors, descendants;
|
||||||
wallet.chain().getTransactionAncestry(output.outpoint.hash, ancestors, descendants);
|
wallet.chain().getTransactionAncestry(output.outpoint.hash, ancestors, descendants);
|
||||||
CInputCoin input_coin = output.GetInputCoin();
|
CScript spk = output.txout.scriptPubKey;
|
||||||
CScript spk = input_coin.txout.scriptPubKey;
|
|
||||||
|
|
||||||
std::vector<OutputGroup>& groups = spk_to_groups_map[spk];
|
std::vector<OutputGroup>& groups = spk_to_groups_map[spk];
|
||||||
|
|
||||||
|
@ -337,7 +335,7 @@ std::vector<OutputGroup> GroupOutputs(const CWallet& wallet, const std::vector<C
|
||||||
groups.emplace_back(coin_sel_params);
|
groups.emplace_back(coin_sel_params);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the last OutputGroup in the vector so that we can add the CInputCoin to it
|
// Get the last OutputGroup in the vector so that we can add the COutput to it
|
||||||
// A pointer is used here so that group can be reassigned later if it is full.
|
// A pointer is used here so that group can be reassigned later if it is full.
|
||||||
OutputGroup* group = &groups.back();
|
OutputGroup* group = &groups.back();
|
||||||
|
|
||||||
|
@ -349,8 +347,8 @@ std::vector<OutputGroup> GroupOutputs(const CWallet& wallet, const std::vector<C
|
||||||
group = &groups.back();
|
group = &groups.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the input_coin to group
|
// Add the output to group
|
||||||
group->Insert(input_coin, output.depth, output.from_me, ancestors, descendants, positive_only);
|
group->Insert(output, ancestors, descendants, positive_only);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we go through the entire map and pull out the OutputGroups
|
// Now we go through the entire map and pull out the OutputGroups
|
||||||
|
@ -427,10 +425,10 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vec
|
||||||
{
|
{
|
||||||
for (const COutput& out : vCoins) {
|
for (const COutput& out : vCoins) {
|
||||||
if (!out.spendable) continue;
|
if (!out.spendable) continue;
|
||||||
/* Set depth, from_me, ancestors, and descendants to 0 or false as these don't matter for preset inputs as no actual selection is being done.
|
/* Set ancestors and descendants to 0 as these don't matter for preset inputs as no actual selection is being done.
|
||||||
* positive_only is set to false because we want to include all preset inputs, even if they are dust.
|
* positive_only is set to false because we want to include all preset inputs, even if they are dust.
|
||||||
*/
|
*/
|
||||||
preset_inputs.Insert(out.GetInputCoin(), 0, false, 0, 0, false);
|
preset_inputs.Insert(out, /*ancestors=*/ 0, /*descendants=*/ 0, /*positive_only=*/ false);
|
||||||
}
|
}
|
||||||
SelectionResult result(nTargetValue);
|
SelectionResult result(nTargetValue);
|
||||||
result.AddInput(preset_inputs);
|
result.AddInput(preset_inputs);
|
||||||
|
@ -439,7 +437,7 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vec
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate value from preset inputs and store them
|
// calculate value from preset inputs and store them
|
||||||
std::set<CInputCoin> setPresetCoins;
|
std::set<COutPoint> preset_coins;
|
||||||
|
|
||||||
std::vector<COutPoint> vPresetInputs;
|
std::vector<COutPoint> vPresetInputs;
|
||||||
coin_control.ListSelected(vPresetInputs);
|
coin_control.ListSelected(vPresetInputs);
|
||||||
|
@ -467,27 +465,29 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vec
|
||||||
input_bytes = GetVirtualTransactionSize(coin_control.GetInputWeight(outpoint), 0, 0);
|
input_bytes = GetVirtualTransactionSize(coin_control.GetInputWeight(outpoint), 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
CInputCoin coin(outpoint, txout, input_bytes);
|
if (input_bytes == -1) {
|
||||||
if (coin.m_input_bytes == -1) {
|
|
||||||
return std::nullopt; // Not solvable, can't estimate size for fee
|
return std::nullopt; // Not solvable, can't estimate size for fee
|
||||||
}
|
}
|
||||||
coin.effective_value = coin.txout.nValue - coin_selection_params.m_effective_feerate.GetFee(coin.m_input_bytes);
|
|
||||||
|
/* Set some defaults for depth, spendable, solvable, safe, time, and from_me as these don't matter for preset inputs since no selection is being done. */
|
||||||
|
COutput output(outpoint, txout, /*depth=*/ 0, input_bytes, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false);
|
||||||
|
output.effective_value = output.txout.nValue - coin_selection_params.m_effective_feerate.GetFee(output.input_bytes);
|
||||||
if (coin_selection_params.m_subtract_fee_outputs) {
|
if (coin_selection_params.m_subtract_fee_outputs) {
|
||||||
value_to_select -= coin.txout.nValue;
|
value_to_select -= output.txout.nValue;
|
||||||
} else {
|
} else {
|
||||||
value_to_select -= coin.effective_value;
|
value_to_select -= output.effective_value;
|
||||||
}
|
}
|
||||||
setPresetCoins.insert(coin);
|
preset_coins.insert(outpoint);
|
||||||
/* Set depth, from_me, ancestors, and descendants to 0 or false as don't matter for preset inputs as no actual selection is being done.
|
/* Set ancestors and descendants to 0 as they don't matter for preset inputs since no actual selection is being done.
|
||||||
* positive_only is set to false because we want to include all preset inputs, even if they are dust.
|
* positive_only is set to false because we want to include all preset inputs, even if they are dust.
|
||||||
*/
|
*/
|
||||||
preset_inputs.Insert(coin, 0, false, 0, 0, false);
|
preset_inputs.Insert(output, /*ancestors=*/ 0, /*descendants=*/ 0, /*positive_only=*/ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove preset inputs from vCoins so that Coin Selection doesn't pick them.
|
// remove preset inputs from vCoins so that Coin Selection doesn't pick them.
|
||||||
for (std::vector<COutput>::iterator it = vCoins.begin(); it != vCoins.end() && coin_control.HasSelected();)
|
for (std::vector<COutput>::iterator it = vCoins.begin(); it != vCoins.end() && coin_control.HasSelected();)
|
||||||
{
|
{
|
||||||
if (setPresetCoins.count(it->GetInputCoin()))
|
if (preset_coins.count(it->outpoint))
|
||||||
it = vCoins.erase(it);
|
it = vCoins.erase(it);
|
||||||
else
|
else
|
||||||
++it;
|
++it;
|
||||||
|
@ -802,7 +802,7 @@ static bool CreateTransactionInternal(
|
||||||
auto change_position = txNew.vout.insert(txNew.vout.begin() + nChangePosInOut, newTxOut);
|
auto change_position = txNew.vout.insert(txNew.vout.begin() + nChangePosInOut, newTxOut);
|
||||||
|
|
||||||
// Shuffle selected coins and fill in final vin
|
// Shuffle selected coins and fill in final vin
|
||||||
std::vector<CInputCoin> selected_coins = result->GetShuffledInputVector();
|
std::vector<COutput> selected_coins = result->GetShuffledInputVector();
|
||||||
|
|
||||||
// The sequence number is set to non-maxint so that DiscourageFeeSniping
|
// The sequence number is set to non-maxint so that DiscourageFeeSniping
|
||||||
// works.
|
// works.
|
||||||
|
|
|
@ -28,20 +28,20 @@ BOOST_FIXTURE_TEST_SUITE(coinselector_tests, WalletTestingSetup)
|
||||||
// we repeat those tests this many times and only complain if all iterations of the test fail
|
// we repeat those tests this many times and only complain if all iterations of the test fail
|
||||||
#define RANDOM_REPEATS 5
|
#define RANDOM_REPEATS 5
|
||||||
|
|
||||||
typedef std::set<CInputCoin> CoinSet;
|
typedef std::set<COutput> CoinSet;
|
||||||
|
|
||||||
static const CoinEligibilityFilter filter_standard(1, 6, 0);
|
static const CoinEligibilityFilter filter_standard(1, 6, 0);
|
||||||
static const CoinEligibilityFilter filter_confirmed(1, 1, 0);
|
static const CoinEligibilityFilter filter_confirmed(1, 1, 0);
|
||||||
static const CoinEligibilityFilter filter_standard_extra(6, 6, 0);
|
static const CoinEligibilityFilter filter_standard_extra(6, 6, 0);
|
||||||
static int nextLockTime = 0;
|
static int nextLockTime = 0;
|
||||||
|
|
||||||
static void add_coin(const CAmount& nValue, int nInput, std::vector<CInputCoin>& set)
|
static void add_coin(const CAmount& nValue, int nInput, std::vector<COutput>& set)
|
||||||
{
|
{
|
||||||
CMutableTransaction tx;
|
CMutableTransaction tx;
|
||||||
tx.vout.resize(nInput + 1);
|
tx.vout.resize(nInput + 1);
|
||||||
tx.vout[nInput].nValue = nValue;
|
tx.vout[nInput].nValue = nValue;
|
||||||
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
|
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
|
||||||
set.emplace_back(MakeTransactionRef(tx), nInput);
|
set.emplace_back(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_coin(const CAmount& nValue, int nInput, SelectionResult& result)
|
static void add_coin(const CAmount& nValue, int nInput, SelectionResult& result)
|
||||||
|
@ -50,9 +50,9 @@ static void add_coin(const CAmount& nValue, int nInput, SelectionResult& result)
|
||||||
tx.vout.resize(nInput + 1);
|
tx.vout.resize(nInput + 1);
|
||||||
tx.vout[nInput].nValue = nValue;
|
tx.vout[nInput].nValue = nValue;
|
||||||
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
|
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
|
||||||
CInputCoin coin(MakeTransactionRef(tx), nInput);
|
COutput output(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false);
|
||||||
OutputGroup group;
|
OutputGroup group;
|
||||||
group.Insert(coin, 1, false, 0, 0, true);
|
group.Insert(output, /*ancestors=*/ 0, /*descendants=*/ 0, /*positive_only=*/ true);
|
||||||
result.AddInput(group);
|
result.AddInput(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,10 +62,10 @@ static void add_coin(const CAmount& nValue, int nInput, CoinSet& set, CAmount fe
|
||||||
tx.vout.resize(nInput + 1);
|
tx.vout.resize(nInput + 1);
|
||||||
tx.vout[nInput].nValue = nValue;
|
tx.vout[nInput].nValue = nValue;
|
||||||
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
|
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
|
||||||
CInputCoin coin(MakeTransactionRef(tx), nInput);
|
COutput coin(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false);
|
||||||
coin.effective_value = nValue - fee;
|
coin.effective_value = nValue - fee;
|
||||||
coin.m_fee = fee;
|
coin.fee = fee;
|
||||||
coin.m_long_term_fee = long_term_fee;
|
coin.long_term_fee = long_term_fee;
|
||||||
set.insert(coin);
|
set.insert(coin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ static bool EqualResult(const SelectionResult& a, const SelectionResult& b)
|
||||||
return ret.first == a.GetInputSet().end() && ret.second == b.GetInputSet().end();
|
return ret.first == a.GetInputSet().end() && ret.second == b.GetInputSet().end();
|
||||||
}
|
}
|
||||||
|
|
||||||
static CAmount make_hard_case(int utxos, std::vector<CInputCoin>& utxo_pool)
|
static CAmount make_hard_case(int utxos, std::vector<COutput>& utxo_pool)
|
||||||
{
|
{
|
||||||
utxo_pool.clear();
|
utxo_pool.clear();
|
||||||
CAmount target = 0;
|
CAmount target = 0;
|
||||||
|
@ -129,24 +129,13 @@ static CAmount make_hard_case(int utxos, std::vector<CInputCoin>& utxo_pool)
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::vector<OutputGroup>& GroupCoins(const std::vector<CInputCoin>& coins)
|
|
||||||
{
|
|
||||||
static std::vector<OutputGroup> static_groups;
|
|
||||||
static_groups.clear();
|
|
||||||
for (auto& coin : coins) {
|
|
||||||
static_groups.emplace_back();
|
|
||||||
static_groups.back().Insert(coin, 0, true, 0, 0, false);
|
|
||||||
}
|
|
||||||
return static_groups;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::vector<OutputGroup>& GroupCoins(const std::vector<COutput>& coins)
|
inline std::vector<OutputGroup>& GroupCoins(const std::vector<COutput>& coins)
|
||||||
{
|
{
|
||||||
static std::vector<OutputGroup> static_groups;
|
static std::vector<OutputGroup> static_groups;
|
||||||
static_groups.clear();
|
static_groups.clear();
|
||||||
for (auto& coin : coins) {
|
for (auto& coin : coins) {
|
||||||
static_groups.emplace_back();
|
static_groups.emplace_back();
|
||||||
static_groups.back().Insert(coin.GetInputCoin(), coin.depth, coin.from_me, 0, 0, false);
|
static_groups.back().Insert(coin, /*ancestors=*/ 0, /*descendants=*/ 0, /*positive_only=*/ false);
|
||||||
}
|
}
|
||||||
return static_groups;
|
return static_groups;
|
||||||
}
|
}
|
||||||
|
@ -166,7 +155,7 @@ inline std::vector<OutputGroup>& KnapsackGroupOutputs(const std::vector<COutput>
|
||||||
BOOST_AUTO_TEST_CASE(bnb_search_test)
|
BOOST_AUTO_TEST_CASE(bnb_search_test)
|
||||||
{
|
{
|
||||||
// Setup
|
// Setup
|
||||||
std::vector<CInputCoin> utxo_pool;
|
std::vector<COutput> utxo_pool;
|
||||||
SelectionResult expected_result(CAmount(0));
|
SelectionResult expected_result(CAmount(0));
|
||||||
|
|
||||||
/////////////////////////
|
/////////////////////////
|
||||||
|
|
Loading…
Add table
Reference in a new issue