@@ -307,6 +307,10 @@ class CoinChooserBase(Logger):
|
|||||||
total_input = input_value + bucket_value_sum
|
total_input = input_value + bucket_value_sum
|
||||||
if total_input < spent_amount: # shortcut for performance
|
if total_input < spent_amount: # shortcut for performance
|
||||||
return False
|
return False
|
||||||
|
# any bitcoin tx must have at least 1 input by consensus
|
||||||
|
# (check we add some new UTXOs now or already have some fixed inputs)
|
||||||
|
if not buckets and not inputs:
|
||||||
|
return False
|
||||||
# note re performance: so far this was constant time
|
# note re performance: so far this was constant time
|
||||||
# what follows is linear in len(buckets)
|
# what follows is linear in len(buckets)
|
||||||
total_weight = self._get_tx_weight(buckets, base_weight=base_weight)
|
total_weight = self._get_tx_weight(buckets, base_weight=base_weight)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from electrum import SimpleConfig
|
|||||||
from electrum.address_synchronizer import TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_UNCONF_PARENT
|
from electrum.address_synchronizer import TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_UNCONF_PARENT
|
||||||
from electrum.wallet import (sweep, Multisig_Wallet, Standard_Wallet, Imported_Wallet,
|
from electrum.wallet import (sweep, Multisig_Wallet, Standard_Wallet, Imported_Wallet,
|
||||||
restore_wallet_from_text, Abstract_Wallet, BumpFeeStrategy)
|
restore_wallet_from_text, Abstract_Wallet, BumpFeeStrategy)
|
||||||
from electrum.util import bfh, bh2u, create_and_start_event_loop
|
from electrum.util import bfh, bh2u, create_and_start_event_loop, NotEnoughFunds
|
||||||
from electrum.transaction import (TxOutput, Transaction, PartialTransaction, PartialTxOutput,
|
from electrum.transaction import (TxOutput, Transaction, PartialTransaction, PartialTxOutput,
|
||||||
PartialTxInput, tx_from_any, TxOutpoint)
|
PartialTxInput, tx_from_any, TxOutpoint)
|
||||||
from electrum.mnemonic import seed_type
|
from electrum.mnemonic import seed_type
|
||||||
@@ -2173,6 +2173,30 @@ class TestWalletSending(TestCaseForTestnet):
|
|||||||
self.assertEqual("bf08206effded4126a95fbed375cedc0452b5e16a5d2025ac645dfae81addbe4:0",
|
self.assertEqual("bf08206effded4126a95fbed375cedc0452b5e16a5d2025ac645dfae81addbe4:0",
|
||||||
coins[0].prevout.to_str())
|
coins[0].prevout.to_str())
|
||||||
|
|
||||||
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
|
def test_wallet_does_not_create_zero_input_tx(self, mock_save_db):
|
||||||
|
wallet = self.create_standard_wallet_from_seed('cross end slow expose giraffe fuel track awake turtle capital ranch pulp',
|
||||||
|
config=self.config, gap_limit=3)
|
||||||
|
|
||||||
|
with self.subTest(msg="no coins to use as inputs, max output value, zero fee"):
|
||||||
|
outputs = [PartialTxOutput.from_address_and_value('tb1qsfcddwf7yytl62e3catwv8hpl2hs9e36g2cqxl', '!')]
|
||||||
|
coins = wallet.get_spendable_coins(domain=None)
|
||||||
|
with self.assertRaises(NotEnoughFunds):
|
||||||
|
tx = wallet.make_unsigned_transaction(coins=coins, outputs=outputs, fee=0)
|
||||||
|
|
||||||
|
# bootstrap wallet
|
||||||
|
funding_tx = Transaction('0200000000010132515e6aade1b79ec7dd3bac0896d8b32c56195d23d07d48e21659cef24301560100000000fdffffff0112841e000000000016001477fe6d2a27e8860c278d4d2cd90bad716bb9521a02473044022041ed68ef7ef122813ac6a5e996b8284f645c53fbe6823b8e430604a8915a867802203233f5f4d347a687eb19b2aa570829ab12aeeb29a24cc6d6d20b8b3d79e971ae012102bee0ee043817e50ac1bb31132770f7c41e35946ccdcb771750fb9696bdd1b307ad951d00')
|
||||||
|
funding_txid = funding_tx.txid()
|
||||||
|
self.assertEqual('db949963c3787c90a40fb689ffdc3146c27a9874a970d1fd20921afbe79a7aa9', funding_txid)
|
||||||
|
wallet.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED)
|
||||||
|
|
||||||
|
with self.subTest(msg="funded wallet, zero output value, zero fee"):
|
||||||
|
outputs = [PartialTxOutput.from_address_and_value('tb1qsfcddwf7yytl62e3catwv8hpl2hs9e36g2cqxl', 0)]
|
||||||
|
coins = wallet.get_spendable_coins(domain=None)
|
||||||
|
tx = wallet.make_unsigned_transaction(coins=coins, outputs=outputs, fee=0)
|
||||||
|
self.assertEqual(1, len(tx.inputs()))
|
||||||
|
self.assertEqual(2, len(tx.outputs()))
|
||||||
|
|
||||||
|
|
||||||
class TestWalletOfflineSigning(TestCaseForTestnet):
|
class TestWalletOfflineSigning(TestCaseForTestnet):
|
||||||
|
|
||||||
|
|||||||
@@ -1317,6 +1317,8 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
|
|||||||
is_sweep=False,
|
is_sweep=False,
|
||||||
rbf=False) -> PartialTransaction:
|
rbf=False) -> PartialTransaction:
|
||||||
|
|
||||||
|
if not coins: # any bitcoin tx must have at least 1 input by consensus
|
||||||
|
raise NotEnoughFunds()
|
||||||
if any([c.already_has_some_signatures() for c in coins]):
|
if any([c.already_has_some_signatures() for c in coins]):
|
||||||
raise Exception("Some inputs already contain signatures!")
|
raise Exception("Some inputs already contain signatures!")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user