Skip to content

Commit

Permalink
add failure test and final changes
Browse files Browse the repository at this point in the history
  • Loading branch information
jack60612 committed Apr 13, 2022
1 parent 7e5a21b commit 5ebc1ac
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 11 deletions.
19 changes: 8 additions & 11 deletions chia/wallet/coin_selection.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ async def select_coins(
log.debug(f"Selected all smaller coins because they equate to an exact match of the target.: {smaller_coins}")
return set(smaller_coins)
elif smaller_coin_sum < amount:
smallest_coin = select_smallest_coin_over_target(len(smaller_coins), valid_spendable_coins, amount)
smallest_coin = select_smallest_coin_over_target(len(smaller_coins), valid_spendable_coins)
log.debug(f"Selected closest greater coin: {smallest_coin.name()}")
return {smallest_coin}
elif smaller_coin_sum > amount:
Expand All @@ -87,7 +87,7 @@ async def select_coins(
if coin_set is None:
raise ValueError("Knapsack algorithm failed to find a solution.")
if len(coin_set) > max_num_coins:
coin = select_smallest_coin_over_target(len(smaller_coins), valid_spendable_coins, amount)
coin = select_smallest_coin_over_target(len(smaller_coins), valid_spendable_coins)
if coin is None or coin.amount < amount:
raise ValueError(
"Can't make this transaction because the transaction would use over 500 "
Expand All @@ -97,13 +97,8 @@ async def select_coins(
return coin_set
else:
# if we == amount and len(smaller_coins) >= max_num_coins.
coin = select_smallest_coin_over_target(len(smaller_coins), valid_spendable_coins, amount)
coin = select_smallest_coin_over_target(len(smaller_coins), valid_spendable_coins)
log.debug(f"Resorted to selecting smallest coin over target due to dust.: {coin}")
if coin is None or coin.amount < amount:
raise ValueError(
"Can't make this transaction because the transaction would use over 500 "
"coins which is unlikely to get confirmed. Try making a smaller transaction to condense the dust."
)
return {coin}


Expand All @@ -119,9 +114,11 @@ def check_for_exact_match(coin_list: List[Coin], target: uint64) -> Optional[Coi


# amount of coins smaller than target, followed by a list of all valid spendable coins sorted in descending order.
def select_smallest_coin_over_target(
smaller_coin_amount: int, valid_spendable_coin_list: List[Coin], target_amount: uint128
) -> Coin:
def select_smallest_coin_over_target(smaller_coin_amount: int, valid_spendable_coin_list: List[Coin]) -> Coin:
if smaller_coin_amount >= len(valid_spendable_coin_list):
raise ValueError(
"There are no coins greater then the target. This is caused by having too many dust coins. Try making a smaller transaction to condense the dust."
)
if smaller_coin_amount > 0: # in case we only have bigger coins.
greater_coins = valid_spendable_coin_list[:-smaller_coin_amount]
else:
Expand Down
34 changes: 34 additions & 0 deletions tests/wallet/test_coin_selection.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,40 @@ async def test_coin_selection_with_dust(self, a_hash: bytes32) -> None:
assert sum([coin.amount for coin in result]) >= target_amount
assert len(result) <= 500

@pytest.mark.asyncio
async def test_coin_selection_failure(self, a_hash: bytes32) -> None:
spendable_amount = uint128(10000)
coin_list: List[WalletCoinRecord] = []
for i in range(10000):
coin_list.append(
WalletCoinRecord(
Coin(a_hash, std_hash(i), uint64(1)), uint32(1), uint32(1), False, True, WalletType(0), 1
)
)
# make sure coins are not identical.
# test for failure
with pytest.raises(ValueError):
for target_amount in [10000, 9999]:
result: Set[Coin] = await select_coins(
spendable_amount,
DEFAULT_CONSTANTS.MAX_COIN_AMOUNT,
coin_list,
{},
logging.getLogger("test"),
uint128(target_amount),
)
# test not enough coin failure.
with pytest.raises(ValueError):
for target_amount in [10001, 20000]:
result: Set[Coin] = await select_coins(
spendable_amount,
DEFAULT_CONSTANTS.MAX_COIN_AMOUNT,
coin_list,
{},
logging.getLogger("test"),
uint128(target_amount),
)

@pytest.mark.asyncio
async def test_coin_selection(self, a_hash: bytes32) -> None:
coin_amounts = [3, 6, 20, 40, 80, 150, 160, 203, 202, 201, 320]
Expand Down

0 comments on commit 5ebc1ac

Please sign in to comment.