diff --git a/electrum/commands.py b/electrum/commands.py index 8046bf168..805cedadd 100644 --- a/electrum/commands.py +++ b/electrum/commands.py @@ -50,7 +50,7 @@ from .transaction import (Transaction, multisig_script, TxOutput, PartialTransac tx_from_any, PartialTxInput, TxOutpoint) from .invoices import PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED from .synchronizer import Notifier -from .wallet import Abstract_Wallet, create_new_wallet, restore_wallet_from_text, Deterministic_Wallet +from .wallet import Abstract_Wallet, create_new_wallet, restore_wallet_from_text, Deterministic_Wallet, BumpFeeStrategy from .address_synchronizer import TX_HEIGHT_LOCAL from .mnemonic import Mnemonic from .lnutil import SENT, RECEIVED @@ -710,6 +710,37 @@ class Commands: return json_normalize(wallet.get_detailed_history(**kwargs)) + @command('wp') + async def bumpfee(self, tx, new_fee_rate, from_coins=None, strategies=None, password=None, unsigned=False, wallet: Abstract_Wallet = None): + """ Bump the Fee for an unconfirmed Transaction """ + tx = Transaction(tx) + domain_coins = from_coins.split(',') if from_coins else None + coins = wallet.get_spendable_coins(None) + if domain_coins is not None: + coins = [coin for coin in coins if (coin.prevout.to_str() in domain_coins)] + strategies = strategies.split(',') if strategies else None + bumpfee_strategies = None + if strategies is not None: + bumpfee_strategies = [] + for strategy in strategies: + if strategy == 'CoinChooser': + bumpfee_strategies.append(BumpFeeStrategy.COINCHOOSER) + elif strategy == 'DecreaseChange': + bumpfee_strategies.append(BumpFeeStrategy.DECREASE_CHANGE) + elif strategy == 'DecreasePayment': + bumpfee_strategies.append(BumpFeeStrategy.DECREASE_PAYMENT) + else: + raise Exception("Invalid Choice of Strategies") + new_tx = wallet.bump_fee( + tx=tx, + txid=tx.txid(), + coins=coins, + strategies=bumpfee_strategies, + new_fee_rate=new_fee_rate) + if not unsigned: + wallet.sign_transaction(new_tx, password) + return new_tx.serialize() + @command('wl') async def lightning_history(self, show_fiat=False, wallet: Abstract_Wallet = None): """ lightning history """ @@ -1302,6 +1333,8 @@ command_options = { 'iknowwhatimdoing': (None, "Acknowledge that I understand the full implications of what I am about to do"), 'gossip': (None, "Apply command to gossip node instead of wallet"), 'connection_string': (None, "Lightning network node ID or network address"), + 'new_fee_rate': (None, "The Updated/Increased Transaction fee rate (in sat/byte)"), + 'strategies': (None, "Select RBF any one or multiple RBF strategies in any order, separated by ','; Options : 'CoinChooser','DecreaseChange','DecreasePayment' "), } diff --git a/electrum/tests/test_commands.py b/electrum/tests/test_commands.py index 4909bce62..d0652d7de 100644 --- a/electrum/tests/test_commands.py +++ b/electrum/tests/test_commands.py @@ -276,3 +276,22 @@ class TestCommandsTestnet(TestCaseForTestnet): self.assertEqual("020000000001010392c1940e2ec9f2372919ca3887327fe5b98b866022cc79bab5cbed5a53d2ad0000000000feffffff022823000000000000160014690b59a8140602fb23cc2904ece9cc4daf361052301b0f0000000000160014ac0e2d229200bffb2167ed6fd196aef9d687d8bb02473044022027e1e37172e52b2d84106663cff5bcf6e447dcb41f6483f99584cfb4de2785f4022005c72f6324ad130c78fca43fe5fc565526d1723f2c9dc3efea78f66d7ae9d4360121030faee9b4a25b7db82023ca989192712cdd4cb53d3d9338591c7909e581ae1c0c00000000", cmds._run('signtransaction', (), tx=unsigned_tx, wallet=wallet)) + + @mock.patch.object(wallet.Abstract_Wallet, 'save_db') + def test_bumpfee(self, mock_save_db): + wallet = restore_wallet_from_text('right nominee cheese afford exotic pilot mask illness rug fringe degree pottery', + gap_limit=2, + path='if_this_exists_mocking_failed_648151893', + config=self.config)['wallet'] + + funding_tx = Transaction("02000000000102789e8aa8caa79d87241ff9df0e3fd757a07c85a30195d76e8efced1d57c56b670000000000fdffffff7ee2b6abd52b332f797718ae582f8d3b979b83b1799e0a3bfb2c90c6e070c29e0100000000fdffffff020820000000000000160014c0eb720c93a61615d2d66542d381be8943ca553950c3000000000000160014d7dbd0196a2cbd76420f14a19377096cf6cddb75024730440220485b491ad8d3ce3b4da034a851882da84a06ec9800edff0d3fd6aa42eeba3b440220359ea85d32a05932ac417125e133fa54e54e7e9cd20ebc54b883576b8603fd65012103860f1fbf8a482b9d35d7d4d04be8fb33d856a514117cd8b73e372d36895feec60247304402206c2ca56cc030853fa59b4b3cb293f69a3378ead0f10cb76f640f8c2888773461022079b7055d0f6af6952a48e5b97218015b0723462d667765c142b41bd35e3d9c0a01210359e303f57647094a668d69e8ff0bd46c356d00aa7da6dc533c438e71c057f0793e721f00") + funding_txid = funding_tx.txid() + wallet.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED) + + cmds = Commands(config=self.config) + tx = "02000000000101b9723dfc69af058ef6613539a000d2cd098a2c8a74e802b6d8739db708ba8c9a0100000000fdffffff02a00f00000000000016001429e1fd187f0cac845946ae1b11dc136c536bfc0fe8b2000000000000160014100611bcb3aee7aad176936cf4ed56ade03027aa02473044022063c05e2347f16251922830ccc757231247b3c2970c225f988e9204844a1ab7b802204652d2c4816707e3d3bea2609b83b079001a435bad2a99cc2e730f276d07070c012102ee3f00141178006c78b0b458aab21588388335078c655459afe544211f15aee050721f00" + self.assertEqual("02000000000101b9723dfc69af058ef6613539a000d2cd098a2c8a74e802b6d8739db708ba8c9a0100000000fdffffff02a00f00000000000016001429e1fd187f0cac845946ae1b11dc136c536bfc0f84b2000000000000160014100611bcb3aee7aad176936cf4ed56ade03027aa0247304402203aa63539b673a3bd70a76482b17f35f8843974fab28f84143a00450789010bc40220779c2ce2d0217f973f1f6c9f718e19fc7ebd14dd8821a962f002437cda3082ec012102ee3f00141178006c78b0b458aab21588388335078c655459afe544211f15aee000000000", + cmds._run('bumpfee', (), tx=tx, new_fee_rate='1.6', wallet=wallet)) + + self.assertEqual("02000000000101b9723dfc69af058ef6613539a000d2cd098a2c8a74e802b6d8739db708ba8c9a0100000000fdffffff02a00f00000000000016001429e1fd187f0cac845946ae1b11dc136c536bfc0f84b2000000000000160014100611bcb3aee7aad176936cf4ed56ade03027aa0247304402203aa63539b673a3bd70a76482b17f35f8843974fab28f84143a00450789010bc40220779c2ce2d0217f973f1f6c9f718e19fc7ebd14dd8821a962f002437cda3082ec012102ee3f00141178006c78b0b458aab21588388335078c655459afe544211f15aee000000000", + cmds._run('bumpfee', (), tx=tx, new_fee_rate='1.6', from_coins="9a8cba08b79d73d8b602e8748a2c8a09cdd200a0393561f68e05af69fc3d72b9:1", wallet=wallet))