electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit a1d7d39f687c2dd6e7a1c812a589bab708b326cc
parent 1bc73b3475285a1ab2a3fbbb506a3d495ef10bac
Author: SomberNight <somber.night@protonmail.com>
Date:   Sat, 21 Sep 2019 02:14:22 +0200

commands: add type hints for "wallet" param, and fix code rot found via

Diffstat:
Melectrum/commands.py | 106++++++++++++++++++++++++++++++++++++++++----------------------------------------
Melectrum/wallet.py | 3+++
2 files changed, 56 insertions(+), 53 deletions(-)

diff --git a/electrum/commands.py b/electrum/commands.py @@ -242,7 +242,7 @@ class Commands: } @command('wp') - async def password(self, password=None, new_password=None, wallet=None): + async def password(self, password=None, new_password=None, wallet: Abstract_Wallet = None): """Change wallet password. """ if wallet.storage.is_encrypted_with_hw_device() and new_password: raise Exception("Can't change the password of a wallet encrypted with a hw device.") @@ -252,7 +252,7 @@ class Commands: return {'password':wallet.has_password()} @command('w') - async def get(self, key, wallet=None): + async def get(self, key, wallet: Abstract_Wallet = None): """Return item from wallet storage""" return wallet.storage.get(key) @@ -294,7 +294,7 @@ class Commands: return await self.network.get_history_for_scripthash(sh) @command('w') - async def listunspent(self, wallet=None): + async def listunspent(self, wallet: Abstract_Wallet = None): """List unspent outputs. Returns the list of unspent transaction outputs in your wallet.""" l = copy.deepcopy(wallet.get_utxos()) @@ -342,7 +342,7 @@ class Commands: return tx.as_dict() @command('wp') - async def signtransaction(self, tx, privkey=None, password=None, wallet=None): + async def signtransaction(self, tx, privkey=None, password=None, wallet: Abstract_Wallet = None): """Sign a transaction. The wallet keys will be used unless a private key is provided.""" tx = Transaction(tx) if privkey: @@ -375,17 +375,17 @@ class Commands: return {'address':address, 'redeemScript':redeem_script} @command('w') - async def freeze(self, address, wallet=None): + async def freeze(self, address, wallet: Abstract_Wallet = None): """Freeze address. Freeze the funds at one of your wallet\'s addresses""" return wallet.set_frozen_state_of_addresses([address], True) @command('w') - async def unfreeze(self, address, wallet=None): + async def unfreeze(self, address, wallet: Abstract_Wallet = None): """Unfreeze address. Unfreeze the funds at one of your wallet\'s address""" return wallet.set_frozen_state_of_addresses([address], False) @command('wp') - async def getprivatekeys(self, address, password=None, wallet=None): + async def getprivatekeys(self, address, password=None, wallet: Abstract_Wallet = None): """Get private keys of addresses. You may pass a single wallet address, or a list of wallet addresses.""" if isinstance(address, str): address = address.strip() @@ -395,7 +395,7 @@ class Commands: return [wallet.export_private_key(address, password)[0] for address in domain] @command('w') - async def ismine(self, address, wallet=None): + async def ismine(self, address, wallet: Abstract_Wallet = None): """Check if address is in wallet. Return true if and only address is in wallet""" return wallet.is_mine(address) @@ -410,12 +410,12 @@ class Commands: return is_address(address) @command('w') - async def getpubkeys(self, address, wallet=None): + async def getpubkeys(self, address, wallet: Abstract_Wallet = None): """Return the public keys for a wallet address. """ return wallet.get_public_keys(address) @command('w') - async def getbalance(self, wallet=None): + async def getbalance(self, wallet: Abstract_Wallet = None): """Return the balance of your wallet. """ c, u, x = wallet.get_balance() l = wallet.lnworker.get_balance() if wallet.lnworker else None @@ -457,12 +457,12 @@ class Commands: return ELECTRUM_VERSION @command('w') - async def getmpk(self, wallet=None): + async def getmpk(self, wallet: Abstract_Wallet = None): """Get master public key. Return your wallet\'s master public key""" return wallet.get_master_public_key() @command('wp') - async def getmasterprivate(self, password=None, wallet=None): + async def getmasterprivate(self, password=None, wallet: Abstract_Wallet = None): """Get master private key. Return your wallet\'s master private key""" return str(wallet.keystore.get_master_private_key(password)) @@ -476,13 +476,13 @@ class Commands: return node._replace(xtype=xtype).to_xkey() @command('wp') - async def getseed(self, password=None, wallet=None): + async def getseed(self, password=None, wallet: Abstract_Wallet = None): """Get seed phrase. Print the generation seed of your wallet.""" s = wallet.get_seed(password) return s @command('wp') - async def importprivkey(self, privkey, password=None, wallet=None): + async def importprivkey(self, privkey, password=None, wallet: Abstract_Wallet = None): """Import a private key.""" if not wallet.can_import_privkey(): return "Error: This type of wallet cannot import private keys. Try to create a new wallet with that key." @@ -515,7 +515,7 @@ class Commands: return tx.as_dict() if tx else None @command('wp') - async def signmessage(self, address, message, password=None, wallet=None): + async def signmessage(self, address, message, password=None, wallet: Abstract_Wallet = None): """Sign a message with a key. Use quotes if your message contains whitespaces""" sig = wallet.sign_message(address, message, password) @@ -528,7 +528,7 @@ class Commands: message = util.to_bytes(message) return ecc.verify_message_with_address(address, sig, message) - def _mktx(self, wallet, outputs, *, fee=None, feerate=None, change_addr=None, domain=None, + def _mktx(self, wallet: Abstract_Wallet, outputs, *, fee=None, feerate=None, change_addr=None, domain=None, nocheck=False, unsigned=False, rbf=None, password=None, locktime=None): if fee is not None and feerate is not None: raise Exception("Cannot specify both 'fee' and 'feerate' at the same time!") @@ -541,7 +541,7 @@ class Commands: amount = satoshis(amount) final_outputs.append(TxOutput(TYPE_ADDRESS, address, amount)) - coins = wallet.get_spendable_coins(domain, self.config) + coins = wallet.get_spendable_coins(domain) if feerate is not None: fee_per_kb = 1000 * Decimal(feerate) fee_estimator = partial(SimpleConfig.estimate_fee_for_feerate, fee_per_kb) @@ -560,7 +560,7 @@ class Commands: @command('wp') async def payto(self, destination, amount, fee=None, feerate=None, from_addr=None, change_addr=None, - nocheck=False, unsigned=False, rbf=None, password=None, locktime=None, wallet=None): + nocheck=False, unsigned=False, rbf=None, password=None, locktime=None, wallet: Abstract_Wallet = None): """Create a transaction. """ tx_fee = satoshis(fee) domain = from_addr.split(',') if from_addr else None @@ -579,7 +579,7 @@ class Commands: @command('wp') async def paytomany(self, outputs, fee=None, feerate=None, from_addr=None, change_addr=None, - nocheck=False, unsigned=False, rbf=None, password=None, locktime=None, wallet=None): + nocheck=False, unsigned=False, rbf=None, password=None, locktime=None, wallet: Abstract_Wallet = None): """Create a multi-output transaction. """ tx_fee = satoshis(fee) domain = from_addr.split(',') if from_addr else None @@ -597,7 +597,7 @@ class Commands: return tx.as_dict() @command('w') - async def onchain_history(self, year=None, show_addresses=False, show_fiat=False, wallet=None): + async def onchain_history(self, year=None, show_addresses=False, show_fiat=False, wallet: Abstract_Wallet = None): """Wallet onchain history. Returns the transaction history of your wallet.""" kwargs = { 'show_addresses': show_addresses, @@ -615,29 +615,29 @@ class Commands: return json_encode(wallet.get_detailed_history(**kwargs)) @command('w') - async def lightning_history(self, show_fiat=False, wallet=None): + async def lightning_history(self, show_fiat=False, wallet: Abstract_Wallet = None): """ lightning history """ lightning_history = wallet.lnworker.get_history() if wallet.lnworker else [] return json_encode(lightning_history) @command('w') - async def setlabel(self, key, label, wallet=None): + async def setlabel(self, key, label, wallet: Abstract_Wallet = None): """Assign a label to an item. Item may be a bitcoin address or a transaction ID""" wallet.set_label(key, label) @command('w') - async def listcontacts(self, wallet=None): + async def listcontacts(self, wallet: Abstract_Wallet = None): """Show your list of contacts""" return wallet.contacts @command('w') - async def getalias(self, key, wallet=None): + async def getalias(self, key, wallet: Abstract_Wallet = None): """Retrieve alias. Lookup in your list of contacts, and for an OpenAlias DNS record.""" return wallet.contacts.resolve(key) @command('w') - async def searchcontacts(self, query, wallet=None): + async def searchcontacts(self, query, wallet: Abstract_Wallet = None): """Search through contacts, return matching entries. """ results = {} for key, value in wallet.contacts.items(): @@ -646,7 +646,7 @@ class Commands: return results @command('w') - async def listaddresses(self, receiving=False, change=False, labels=False, frozen=False, unused=False, funded=False, balance=False, wallet=None): + async def listaddresses(self, receiving=False, change=False, labels=False, frozen=False, unused=False, funded=False, balance=False, wallet: Abstract_Wallet = None): """List wallet addresses. Returns the list of all addresses in your wallet. Use optional arguments to filter the results.""" out = [] for addr in wallet.get_addresses(): @@ -671,7 +671,7 @@ class Commands: return out @command('n') - async def gettransaction(self, txid, wallet=None): + async def gettransaction(self, txid, wallet: Abstract_Wallet = None): """Retrieve a transaction. """ tx = None if wallet: @@ -698,7 +698,7 @@ class Commands: return encrypted.decode('utf-8') @command('wp') - async def decrypt(self, pubkey, encrypted, password=None, wallet=None) -> str: + async def decrypt(self, pubkey, encrypted, password=None, wallet: Abstract_Wallet = None) -> str: """Decrypt a message encrypted with a public key.""" if not is_hex_str(pubkey): raise Exception(f"pubkey must be a hex string instead of {repr(pubkey)}") @@ -714,7 +714,7 @@ class Commands: return out @command('w') - async def getrequest(self, key, wallet=None): + async def getrequest(self, key, wallet: Abstract_Wallet = None): """Return a payment request""" r = wallet.get_request(key) if not r: @@ -727,7 +727,7 @@ class Commands: # pass @command('w') - async def listrequests(self, pending=False, expired=False, paid=False, wallet=None): + async def listrequests(self, pending=False, expired=False, paid=False, wallet: Abstract_Wallet = None): """List the payment requests you made.""" out = wallet.get_sorted_requests() if pending: @@ -743,18 +743,18 @@ class Commands: return list(map(self._format_request, out)) @command('w') - async def createnewaddress(self, wallet=None): + async def createnewaddress(self, wallet: Abstract_Wallet = None): """Create a new receiving address, beyond the gap limit of the wallet""" return wallet.create_new_address(False) @command('w') - async def getunusedaddress(self, wallet=None): + async def getunusedaddress(self, wallet: Abstract_Wallet = None): """Returns the first unused address of the wallet, or None if all addresses are used. An address is considered as used if it has received a transaction, or if it is used in a payment request.""" return wallet.get_unused_address() @command('w') - async def addrequest(self, amount, memo='', expiration=None, force=False, wallet=None): + async def addrequest(self, amount, memo='', expiration=None, force=False, wallet: Abstract_Wallet = None): """Create a payment request, using the first unused address of the wallet. The address will be considered as used after this operation. If no payment is received, the address will be considered as unused if the payment request is deleted from the wallet.""" @@ -772,16 +772,16 @@ class Commands: return self._format_request(out) @command('w') - async def addtransaction(self, tx, wallet=None): + async def addtransaction(self, tx, wallet: Abstract_Wallet = None): """ Add a transaction to the wallet history """ tx = Transaction(tx) - if not wallet.add_transaction(tx.txid(), tx, wallet=None): + if not wallet.add_transaction(tx.txid(), tx): return False wallet.storage.write() return tx.txid() @command('wp') - async def signrequest(self, address, password=None, wallet=None): + async def signrequest(self, address, password=None, wallet: Abstract_Wallet = None): "Sign payment request with an OpenAlias" alias = self.config.get('alias') if not alias: @@ -790,18 +790,18 @@ class Commands: wallet.sign_payment_request(address, alias, alias_addr, password) @command('w') - async def rmrequest(self, address, wallet=None): + async def rmrequest(self, address, wallet: Abstract_Wallet = None): """Remove a payment request""" return wallet.remove_payment_request(address) @command('w') - async def clear_requests(self, wallet=None): + async def clear_requests(self, wallet: Abstract_Wallet = None): """Remove all payment requests""" for k in list(wallet.receive_requests.keys()): wallet.remove_payment_request(k) @command('w') - async def clear_invoices(self, wallet=None): + async def clear_invoices(self, wallet: Abstract_Wallet = None): """Remove all invoices""" wallet.clear_invoices() return True @@ -815,7 +815,7 @@ class Commands: return True @command('wn') - async def is_synchronized(self, wallet=None): + async def is_synchronized(self, wallet: Abstract_Wallet = None): """ return wallet synchronization status """ return wallet.is_up_to_date() @@ -839,7 +839,7 @@ class Commands: return self.config.fee_per_kb(dyn=dyn, mempool=mempool, fee_level=fee_level) @command('w') - async def removelocaltx(self, txid, wallet=None): + async def removelocaltx(self, txid, wallet: Abstract_Wallet = None): """Remove a 'local' transaction from the wallet, and its dependent transactions. """ @@ -856,7 +856,7 @@ class Commands: wallet.storage.write() @command('wn') - async def get_tx_status(self, txid, wallet=None): + async def get_tx_status(self, txid, wallet: Abstract_Wallet = None): """Returns some information regarding the tx. For now, only confirmations. The transaction must be related to the wallet. """ @@ -875,37 +875,37 @@ class Commands: # lightning network commands @command('wn') - async def add_peer(self, connection_string, timeout=20, wallet=None): + async def add_peer(self, connection_string, timeout=20, wallet: Abstract_Wallet = None): await wallet.lnworker.add_peer(connection_string) return True @command('wpn') - async def open_channel(self, connection_string, amount, channel_push=0, password=None, wallet=None): + async def open_channel(self, connection_string, amount, channel_push=0, password=None, wallet: Abstract_Wallet = None): chan = await wallet.lnworker._open_channel_coroutine(connection_string, satoshis(amount), satoshis(channel_push), password) return chan.funding_outpoint.to_str() @command('wn') - async def lnpay(self, invoice, attempts=1, timeout=10, wallet=None): + async def lnpay(self, invoice, attempts=1, timeout=10, wallet: Abstract_Wallet = None): return await wallet.lnworker._pay(invoice, attempts=attempts) @command('wn') - async def addinvoice(self, requested_amount, message, expiration=3600, wallet=None): + async def addinvoice(self, requested_amount, message, expiration=3600, wallet: Abstract_Wallet = None): # using requested_amount because it is documented in param_descriptions payment_hash = await wallet.lnworker._add_invoice_coro(satoshis(requested_amount), message, expiration) invoice, direction, is_paid = wallet.lnworker.invoices[bh2u(payment_hash)] return invoice @command('w') - async def nodeid(self, wallet=None): + async def nodeid(self, wallet: Abstract_Wallet = None): listen_addr = self.config.get('lightning_listen') return bh2u(wallet.lnworker.node_keypair.pubkey) + (('@' + listen_addr) if listen_addr else '') @command('w') - async def list_channels(self, wallet=None): + async def list_channels(self, wallet: Abstract_Wallet = None): return list(wallet.lnworker.list_channels()) @command('wn') - async def dumpgraph(self, wallet=None): + async def dumpgraph(self, wallet: Abstract_Wallet = None): return list(map(bh2u, wallet.lnworker.channel_db.nodes.keys())) @command('n') @@ -919,7 +919,7 @@ class Commands: self.network.path_finder.blacklist.clear() @command('w') - async def lightning_invoices(self, wallet=None): + async def lightning_invoices(self, wallet: Abstract_Wallet = None): from .util import pr_tooltips out = [] for payment_hash, (preimage, invoice, is_received, timestamp) in wallet.lnworker.invoices.items(): @@ -936,18 +936,18 @@ class Commands: return out @command('w') - async def lightning_history(self, wallet=None): + async def lightning_history(self, wallet: Abstract_Wallet = None): return wallet.lnworker.get_history() @command('wn') - async def close_channel(self, channel_point, force=False, wallet=None): + async def close_channel(self, channel_point, force=False, wallet: Abstract_Wallet = None): txid, index = channel_point.split(':') chan_id, _ = channel_id_from_funding_tx(txid, int(index)) coro = wallet.lnworker.force_close_channel(chan_id) if force else wallet.lnworker.close_channel(chan_id) return await coro @command('wn') - async def get_channel_ctx(self, channel_point, wallet=None): + async def get_channel_ctx(self, channel_point, wallet: Abstract_Wallet = None): """ return the current commitment transaction of a channel """ txid, index = channel_point.split(':') chan_id, _ = channel_id_from_funding_tx(txid, int(index)) diff --git a/electrum/wallet.py b/electrum/wallet.py @@ -1294,6 +1294,9 @@ class Abstract_Wallet(AddressSynchronizer): choice = addr return choice + def create_new_address(self, for_change=False): + raise Exception("this wallet cannot generate new addresses") + def get_payment_status(self, address, amount): local_height = self.get_local_height() received, sent = self.get_addr_io(address)