@ -31,6 +31,7 @@ import json
import ast
import ast
import base64
import base64
import operator
import operator
import asyncio
from functools import wraps
from functools import wraps
from decimal import Decimal
from decimal import Decimal
from typing import Optional , TYPE_CHECKING
from typing import Optional , TYPE_CHECKING
@ -112,8 +113,8 @@ class Commands:
self . _callback = callback
self . _callback = callback
self . lnworker = self . wallet . lnworker if self . wallet else None
self . lnworker = self . wallet . lnworker if self . wallet else None
def _run ( self , method , args , password_getter , * * kwargs ) :
def _run ( self , method , args , password_getter = None , * * kwargs ) :
""" This wrapper is called from the Qt python console. """
""" This wrapper is called from unit tests and the Qt python console. """
cmd = known_commands [ method ]
cmd = known_commands [ method ]
password = kwargs . get ( ' password ' , None )
password = kwargs . get ( ' password ' , None )
if ( cmd . requires_password and self . wallet . has_password ( )
if ( cmd . requires_password and self . wallet . has_password ( )
@ -125,19 +126,22 @@ class Commands:
f = getattr ( self , method )
f = getattr ( self , method )
if cmd . requires_password :
if cmd . requires_password :
kwargs [ ' password ' ] = password
kwargs [ ' password ' ] = password
result = f ( * args , * * kwargs )
coro = f ( * args , * * kwargs )
fut = asyncio . run_coroutine_threadsafe ( coro , asyncio . get_event_loop ( ) )
result = fut . result ( )
if self . _callback :
if self . _callback :
self . _callback ( )
self . _callback ( )
return result
return result
@command ( ' ' )
@command ( ' ' )
def commands ( self ) :
async def commands ( self ) :
""" List of commands """
""" List of commands """
return ' ' . join ( sorted ( known_commands . keys ( ) ) )
return ' ' . join ( sorted ( known_commands . keys ( ) ) )
@command ( ' ' )
@command ( ' ' )
def create ( self , passphrase = None , password = None , encrypt_file = True , seed_type = None ) :
async def create ( self , passphrase = None , password = None , encrypt_file = True , seed_type = None ) :
""" Create a new wallet.
""" Create a new wallet.
If you want to be prompted for an argument , type ' ? ' or ' : ' ( concealed )
If you want to be prompted for an argument , type ' ? ' or ' : ' ( concealed )
"""
"""
@ -153,7 +157,7 @@ class Commands:
}
}
@command ( ' ' )
@command ( ' ' )
def restore ( self , text , passphrase = None , password = None , encrypt_file = True ) :
async def restore ( self , text , passphrase = None , password = None , encrypt_file = True ) :
""" Restore a wallet from text. Text can be a seed phrase, a master
""" Restore a wallet from text. Text can be a seed phrase, a master
public key , a master private key , a list of bitcoin addresses
public key , a master private key , a list of bitcoin addresses
or bitcoin private keys .
or bitcoin private keys .
@ -171,7 +175,7 @@ class Commands:
}
}
@command ( ' wp ' )
@command ( ' wp ' )
def password ( self , password = None , new_password = None ) :
async def password ( self , password = None , new_password = None ) :
""" Change wallet password. """
""" Change wallet password. """
if self . wallet . storage . is_encrypted_with_hw_device ( ) and new_password :
if self . 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. " )
raise Exception ( " Can ' t change the password of a wallet encrypted with a hw device. " )
@ -181,12 +185,12 @@ class Commands:
return { ' password ' : self . wallet . has_password ( ) }
return { ' password ' : self . wallet . has_password ( ) }
@command ( ' w ' )
@command ( ' w ' )
def get ( self , key ) :
async def get ( self , key ) :
""" Return item from wallet storage """
""" Return item from wallet storage """
return self . wallet . storage . get ( key )
return self . wallet . storage . get ( key )
@command ( ' ' )
@command ( ' ' )
def getconfig ( self , key ) :
async def getconfig ( self , key ) :
""" Return a configuration variable. """
""" Return a configuration variable. """
return self . config . get ( key )
return self . config . get ( key )
@ -201,29 +205,29 @@ class Commands:
return value
return value
@command ( ' ' )
@command ( ' ' )
def setconfig ( self , key , value ) :
async def setconfig ( self , key , value ) :
""" Set a configuration variable. ' value ' may be a string or a Python expression. """
""" Set a configuration variable. ' value ' may be a string or a Python expression. """
value = self . _setconfig_normalize_value ( key , value )
value = self . _setconfig_normalize_value ( key , value )
self . config . set_key ( key , value )
self . config . set_key ( key , value )
return True
return True
@command ( ' ' )
@command ( ' ' )
def make_seed ( self , nbits = 132 , language = None , seed_type = None ) :
async def make_seed ( self , nbits = 132 , language = None , seed_type = None ) :
""" Create a seed """
""" Create a seed """
from . mnemonic import Mnemonic
from . mnemonic import Mnemonic
s = Mnemonic ( language ) . make_seed ( seed_type , num_bits = nbits )
s = Mnemonic ( language ) . make_seed ( seed_type , num_bits = nbits )
return s
return s
@command ( ' n ' )
@command ( ' n ' )
def getaddresshistory ( self , address ) :
async def getaddresshistory ( self , address ) :
""" Return the transaction history of any address. Note: This is a
""" Return the transaction history of any address. Note: This is a
walletless server query , results are not checked by SPV .
walletless server query , results are not checked by SPV .
"""
"""
sh = bitcoin . address_to_scripthash ( address )
sh = bitcoin . address_to_scripthash ( address )
return self . network . run_from_another_thread ( self . network . get_history_for_scripthash ( sh ) )
return await self . network . get_history_for_scripthash ( sh )
@command ( ' w ' )
@command ( ' w ' )
def listunspent ( self ) :
async def listunspent ( self ) :
""" List unspent outputs. Returns the list of unspent transaction
""" List unspent outputs. Returns the list of unspent transaction
outputs in your wallet . """
outputs in your wallet . """
l = copy . deepcopy ( self . wallet . get_utxos ( ) )
l = copy . deepcopy ( self . wallet . get_utxos ( ) )
@ -233,15 +237,15 @@ class Commands:
return l
return l
@command ( ' n ' )
@command ( ' n ' )
def getaddressunspent ( self , address ) :
async def getaddressunspent ( self , address ) :
""" Returns the UTXO list of any address. Note: This
""" Returns the UTXO list of any address. Note: This
is a walletless server query , results are not checked by SPV .
is a walletless server query , results are not checked by SPV .
"""
"""
sh = bitcoin . address_to_scripthash ( address )
sh = bitcoin . address_to_scripthash ( address )
return self . network . run_from_another_thread ( self . network . listunspent_for_scripthash ( sh ) )
return await self . network . listunspent_for_scripthash ( sh )
@command ( ' ' )
@command ( ' ' )
def serialize ( self , jsontx ) :
async def serialize ( self , jsontx ) :
""" Create a transaction from json inputs.
""" Create a transaction from json inputs.
Inputs must have a redeemPubkey .
Inputs must have a redeemPubkey .
Outputs must be a list of { ' address ' : address , ' value ' : satoshi_amount } .
Outputs must be a list of { ' address ' : address , ' value ' : satoshi_amount } .
@ -271,7 +275,7 @@ class Commands:
return tx . as_dict ( )
return tx . as_dict ( )
@command ( ' wp ' )
@command ( ' wp ' )
def signtransaction ( self , tx , privkey = None , password = None ) :
async def signtransaction ( self , tx , privkey = None , password = None ) :
""" Sign a transaction. The wallet keys will be used unless a private key is provided. """
""" Sign a transaction. The wallet keys will be used unless a private key is provided. """
tx = Transaction ( tx )
tx = Transaction ( tx )
if privkey :
if privkey :
@ -283,20 +287,20 @@ class Commands:
return tx . as_dict ( )
return tx . as_dict ( )
@command ( ' ' )
@command ( ' ' )
def deserialize ( self , tx ) :
async def deserialize ( self , tx ) :
""" Deserialize a serialized transaction """
""" Deserialize a serialized transaction """
tx = Transaction ( tx )
tx = Transaction ( tx )
return tx . deserialize ( force_full_parse = True )
return tx . deserialize ( force_full_parse = True )
@command ( ' n ' )
@command ( ' n ' )
def broadcast ( self , tx ) :
async def broadcast ( self , tx ) :
""" Broadcast a transaction to the network. """
""" Broadcast a transaction to the network. """
tx = Transaction ( tx )
tx = Transaction ( tx )
self . network . run_from_another_thread ( self . network . broadcast_transaction ( tx ) )
await self . network . broadcast_transaction ( tx )
return tx . txid ( )
return tx . txid ( )
@command ( ' ' )
@command ( ' ' )
def createmultisig ( self , num , pubkeys ) :
async def createmultisig ( self , num , pubkeys ) :
""" Create multisig address """
""" Create multisig address """
assert isinstance ( pubkeys , list ) , ( type ( num ) , type ( pubkeys ) )
assert isinstance ( pubkeys , list ) , ( type ( num ) , type ( pubkeys ) )
redeem_script = multisig_script ( pubkeys , num )
redeem_script = multisig_script ( pubkeys , num )
@ -304,17 +308,17 @@ class Commands:
return { ' address ' : address , ' redeemScript ' : redeem_script }
return { ' address ' : address , ' redeemScript ' : redeem_script }
@command ( ' w ' )
@command ( ' w ' )
def freeze ( self , address ) :
async def freeze ( self , address ) :
""" Freeze address. Freeze the funds at one of your wallet \' s addresses """
""" Freeze address. Freeze the funds at one of your wallet \' s addresses """
return self . wallet . set_frozen_state_of_addresses ( [ address ] , True )
return self . wallet . set_frozen_state_of_addresses ( [ address ] , True )
@command ( ' w ' )
@command ( ' w ' )
def unfreeze ( self , address ) :
async def unfreeze ( self , address ) :
""" Unfreeze address. Unfreeze the funds at one of your wallet \' s address """
""" Unfreeze address. Unfreeze the funds at one of your wallet \' s address """
return self . wallet . set_frozen_state_of_addresses ( [ address ] , False )
return self . wallet . set_frozen_state_of_addresses ( [ address ] , False )
@command ( ' wp ' )
@command ( ' wp ' )
def getprivatekeys ( self , address , password = None ) :
async def getprivatekeys ( self , address , password = None ) :
""" Get private keys of addresses. You may pass a single wallet address, or a list of wallet addresses. """
""" Get private keys of addresses. You may pass a single wallet address, or a list of wallet addresses. """
if isinstance ( address , str ) :
if isinstance ( address , str ) :
address = address . strip ( )
address = address . strip ( )
@ -324,27 +328,27 @@ class Commands:
return [ self . wallet . export_private_key ( address , password ) [ 0 ] for address in domain ]
return [ self . wallet . export_private_key ( address , password ) [ 0 ] for address in domain ]
@command ( ' w ' )
@command ( ' w ' )
def ismine ( self , address ) :
async def ismine ( self , address ) :
""" Check if address is in wallet. Return true if and only address is in wallet """
""" Check if address is in wallet. Return true if and only address is in wallet """
return self . wallet . is_mine ( address )
return self . wallet . is_mine ( address )
@command ( ' ' )
@command ( ' ' )
def dumpprivkeys ( self ) :
async def dumpprivkeys ( self ) :
""" Deprecated. """
""" Deprecated. """
return " This command is deprecated. Use a pipe instead: ' electrum listaddresses | electrum getprivatekeys - ' "
return " This command is deprecated. Use a pipe instead: ' electrum listaddresses | electrum getprivatekeys - ' "
@command ( ' ' )
@command ( ' ' )
def validateaddress ( self , address ) :
async def validateaddress ( self , address ) :
""" Check that an address is valid. """
""" Check that an address is valid. """
return is_address ( address )
return is_address ( address )
@command ( ' w ' )
@command ( ' w ' )
def getpubkeys ( self , address ) :
async def getpubkeys ( self , address ) :
""" Return the public keys for a wallet address. """
""" Return the public keys for a wallet address. """
return self . wallet . get_public_keys ( address )
return self . wallet . get_public_keys ( address )
@command ( ' w ' )
@command ( ' w ' )
def getbalance ( self ) :
async def getbalance ( self ) :
""" Return the balance of your wallet. """
""" Return the balance of your wallet. """
c , u , x = self . wallet . get_balance ( )
c , u , x = self . wallet . get_balance ( )
l = self . lnworker . get_balance ( ) if self . lnworker else None
l = self . lnworker . get_balance ( ) if self . lnworker else None
@ -358,45 +362,45 @@ class Commands:
return out
return out
@command ( ' n ' )
@command ( ' n ' )
def getaddressbalance ( self , address ) :
async def getaddressbalance ( self , address ) :
""" Return the balance of any address. Note: This is a walletless
""" Return the balance of any address. Note: This is a walletless
server query , results are not checked by SPV .
server query , results are not checked by SPV .
"""
"""
sh = bitcoin . address_to_scripthash ( address )
sh = bitcoin . address_to_scripthash ( address )
out = self . network . run_from_another_thread ( self . network . get_balance_for_scripthash ( sh ) )
out = await self . network . get_balance_for_scripthash ( sh )
out [ " confirmed " ] = str ( Decimal ( out [ " confirmed " ] ) / COIN )
out [ " confirmed " ] = str ( Decimal ( out [ " confirmed " ] ) / COIN )
out [ " unconfirmed " ] = str ( Decimal ( out [ " unconfirmed " ] ) / COIN )
out [ " unconfirmed " ] = str ( Decimal ( out [ " unconfirmed " ] ) / COIN )
return out
return out
@command ( ' n ' )
@command ( ' n ' )
def getmerkle ( self , txid , height ) :
async def getmerkle ( self , txid , height ) :
""" Get Merkle branch of a transaction included in a block. Electrum
""" Get Merkle branch of a transaction included in a block. Electrum
uses this to verify transactions ( Simple Payment Verification ) . """
uses this to verify transactions ( Simple Payment Verification ) . """
return self . network . run_from_another_thread ( self . network . get_merkle_for_transaction ( txid , int ( height ) ) )
return await self . network . get_merkle_for_transaction ( txid , int ( height ) )
@command ( ' n ' )
@command ( ' n ' )
def getservers ( self ) :
async def getservers ( self ) :
""" Return the list of available servers """
""" Return the list of available servers """
return self . network . get_servers ( )
return self . network . get_servers ( )
@command ( ' ' )
@command ( ' ' )
def version ( self ) :
async def version ( self ) :
""" Return the version of Electrum. """
""" Return the version of Electrum. """
from . version import ELECTRUM_VERSION
from . version import ELECTRUM_VERSION
return ELECTRUM_VERSION
return ELECTRUM_VERSION
@command ( ' w ' )
@command ( ' w ' )
def getmpk ( self ) :
async def getmpk ( self ) :
""" Get master public key. Return your wallet \' s master public key """
""" Get master public key. Return your wallet \' s master public key """
return self . wallet . get_master_public_key ( )
return self . wallet . get_master_public_key ( )
@command ( ' wp ' )
@command ( ' wp ' )
def getmasterprivate ( self , password = None ) :
async def getmasterprivate ( self , password = None ) :
""" Get master private key. Return your wallet \' s master private key """
""" Get master private key. Return your wallet \' s master private key """
return str ( self . wallet . keystore . get_master_private_key ( password ) )
return str ( self . wallet . keystore . get_master_private_key ( password ) )
@command ( ' ' )
@command ( ' ' )
def convert_xkey ( self , xkey , xtype ) :
async def convert_xkey ( self , xkey , xtype ) :
""" Convert xtype of a master key. e.g. xpub -> ypub """
""" Convert xtype of a master key. e.g. xpub -> ypub """
try :
try :
node = BIP32Node . from_xkey ( xkey )
node = BIP32Node . from_xkey ( xkey )
@ -405,13 +409,13 @@ class Commands:
return node . _replace ( xtype = xtype ) . to_xkey ( )
return node . _replace ( xtype = xtype ) . to_xkey ( )
@command ( ' wp ' )
@command ( ' wp ' )
def getseed ( self , password = None ) :
async def getseed ( self , password = None ) :
""" Get seed phrase. Print the generation seed of your wallet. """
""" Get seed phrase. Print the generation seed of your wallet. """
s = self . wallet . get_seed ( password )
s = self . wallet . get_seed ( password )
return s
return s
@command ( ' wp ' )
@command ( ' wp ' )
def importprivkey ( self , privkey , password = None ) :
async def importprivkey ( self , privkey , password = None ) :
""" Import a private key. """
""" Import a private key. """
if not self . wallet . can_import_privkey ( ) :
if not self . wallet . can_import_privkey ( ) :
return " Error: This type of wallet cannot import private keys. Try to create a new wallet with that key. "
return " Error: This type of wallet cannot import private keys. Try to create a new wallet with that key. "
@ -431,7 +435,7 @@ class Commands:
return out [ ' address ' ]
return out [ ' address ' ]
@command ( ' n ' )
@command ( ' n ' )
def sweep ( self , privkey , destination , fee = None , nocheck = False , imax = 100 ) :
async def sweep ( self , privkey , destination , fee = None , nocheck = False , imax = 100 ) :
""" Sweep private keys. Returns a transaction that spends UTXOs from
""" Sweep private keys. Returns a transaction that spends UTXOs from
privkey to a destination address . The transaction is not
privkey to a destination address . The transaction is not
broadcasted . """
broadcasted . """
@ -444,14 +448,14 @@ class Commands:
return tx . as_dict ( ) if tx else None
return tx . as_dict ( ) if tx else None
@command ( ' wp ' )
@command ( ' wp ' )
def signmessage ( self , address , message , password = None ) :
async def signmessage ( self , address , message , password = None ) :
""" Sign a message with a key. Use quotes if your message contains
""" Sign a message with a key. Use quotes if your message contains
whitespaces """
whitespaces """
sig = self . wallet . sign_message ( address , message , password )
sig = self . wallet . sign_message ( address , message , password )
return base64 . b64encode ( sig ) . decode ( ' ascii ' )
return base64 . b64encode ( sig ) . decode ( ' ascii ' )
@command ( ' ' )
@command ( ' ' )
def verifymessage ( self , address , signature , message ) :
async def verifymessage ( self , address , signature , message ) :
""" Verify a signature. """
""" Verify a signature. """
sig = base64 . b64decode ( signature )
sig = base64 . b64decode ( signature )
message = util . to_bytes ( message )
message = util . to_bytes ( message )
@ -480,7 +484,7 @@ class Commands:
return tx
return tx
@command ( ' wp ' )
@command ( ' wp ' )
def payto ( self , destination , amount , fee = None , from_addr = None , change_addr = None , nocheck = False , unsigned = False , rbf = None , password = None , locktime = None ) :
async def payto ( self , destination , amount , fee = None , from_addr = None , change_addr = None , nocheck = False , unsigned = False , rbf = None , password = None , locktime = None ) :
""" Create a transaction. """
""" Create a transaction. """
tx_fee = satoshis ( fee )
tx_fee = satoshis ( fee )
domain = from_addr . split ( ' , ' ) if from_addr else None
domain = from_addr . split ( ' , ' ) if from_addr else None
@ -488,7 +492,7 @@ class Commands:
return tx . as_dict ( )
return tx . as_dict ( )
@command ( ' wp ' )
@command ( ' wp ' )
def paytomany ( self , outputs , fee = None , from_addr = None , change_addr = None , nocheck = False , unsigned = False , rbf = None , password = None , locktime = None ) :
async def paytomany ( self , outputs , fee = None , from_addr = None , change_addr = None , nocheck = False , unsigned = False , rbf = None , password = None , locktime = None ) :
""" Create a multi-output transaction. """
""" Create a multi-output transaction. """
tx_fee = satoshis ( fee )
tx_fee = satoshis ( fee )
domain = from_addr . split ( ' , ' ) if from_addr else None
domain = from_addr . split ( ' , ' ) if from_addr else None
@ -496,7 +500,7 @@ class Commands:
return tx . as_dict ( )
return tx . as_dict ( )
@command ( ' w ' )
@command ( ' w ' )
def onchain_history ( self , year = None , show_addresses = False , show_fiat = False , show_fees = False ) :
async def onchain_history ( self , year = None , show_addresses = False , show_fiat = False , show_fees = False ) :
""" Wallet onchain history. Returns the transaction history of your wallet. """
""" Wallet onchain history. Returns the transaction history of your wallet. """
kwargs = {
kwargs = {
' show_addresses ' : show_addresses ,
' show_addresses ' : show_addresses ,
@ -515,29 +519,29 @@ class Commands:
return json_encode ( self . wallet . get_detailed_history ( * * kwargs ) )
return json_encode ( self . wallet . get_detailed_history ( * * kwargs ) )
@command ( ' w ' )
@command ( ' w ' )
def lightning_history ( self , show_fiat = False ) :
async def lightning_history ( self , show_fiat = False ) :
""" lightning history """
""" lightning history """
lightning_history = self . wallet . lnworker . get_history ( ) if self . wallet . lnworker else [ ]
lightning_history = self . wallet . lnworker . get_history ( ) if self . wallet . lnworker else [ ]
return json_encode ( lightning_history )
return json_encode ( lightning_history )
@command ( ' w ' )
@command ( ' w ' )
def setlabel ( self , key , label ) :
async def setlabel ( self , key , label ) :
""" Assign a label to an item. Item may be a bitcoin address or a
""" Assign a label to an item. Item may be a bitcoin address or a
transaction ID """
transaction ID """
self . wallet . set_label ( key , label )
self . wallet . set_label ( key , label )
@command ( ' w ' )
@command ( ' w ' )
def listcontacts ( self ) :
async def listcontacts ( self ) :
""" Show your list of contacts """
""" Show your list of contacts """
return self . wallet . contacts
return self . wallet . contacts
@command ( ' w ' )
@command ( ' w ' )
def getalias ( self , key ) :
async def getalias ( self , key ) :
""" Retrieve alias. Lookup in your list of contacts, and for an OpenAlias DNS record. """
""" Retrieve alias. Lookup in your list of contacts, and for an OpenAlias DNS record. """
return self . wallet . contacts . resolve ( key )
return self . wallet . contacts . resolve ( key )
@command ( ' w ' )
@command ( ' w ' )
def searchcontacts ( self , query ) :
async def searchcontacts ( self , query ) :
""" Search through contacts, return matching entries. """
""" Search through contacts, return matching entries. """
results = { }
results = { }
for key , value in self . wallet . contacts . items ( ) :
for key , value in self . wallet . contacts . items ( ) :
@ -546,7 +550,7 @@ class Commands:
return results
return results
@command ( ' w ' )
@command ( ' w ' )
def listaddresses ( self , receiving = False , change = False , labels = False , frozen = False , unused = False , funded = False , balance = False ) :
async def listaddresses ( self , receiving = False , change = False , labels = False , frozen = False , unused = False , funded = False , balance = False ) :
""" List wallet addresses. Returns the list of all addresses in your wallet. Use optional arguments to filter the results. """
""" List wallet addresses. Returns the list of all addresses in your wallet. Use optional arguments to filter the results. """
out = [ ]
out = [ ]
for addr in self . wallet . get_addresses ( ) :
for addr in self . wallet . get_addresses ( ) :
@ -571,13 +575,13 @@ class Commands:
return out
return out
@command ( ' n ' )
@command ( ' n ' )
def gettransaction ( self , txid ) :
async def gettransaction ( self , txid ) :
""" Retrieve a transaction. """
""" Retrieve a transaction. """
tx = None
tx = None
if self . wallet :
if self . wallet :
tx = self . wallet . db . get_transaction ( txid )
tx = self . wallet . db . get_transaction ( txid )
if tx is None :
if tx is None :
raw = self . network . run_from_another_thread ( self . network . get_transaction ( txid ) )
raw = await self . network . get_transaction ( txid )
if raw :
if raw :
tx = Transaction ( raw )
tx = Transaction ( raw )
else :
else :
@ -585,7 +589,7 @@ class Commands:
return tx . as_dict ( )
return tx . as_dict ( )
@command ( ' ' )
@command ( ' ' )
def encrypt ( self , pubkey , message ) - > str :
async def encrypt ( self , pubkey , message ) - > str :
""" Encrypt a message with a public key. Use quotes if the message contains whitespaces. """
""" Encrypt a message with a public key. Use quotes if the message contains whitespaces. """
if not is_hex_str ( pubkey ) :
if not is_hex_str ( pubkey ) :
raise Exception ( f " pubkey must be a hex string instead of { repr ( pubkey ) } " )
raise Exception ( f " pubkey must be a hex string instead of { repr ( pubkey ) } " )
@ -598,7 +602,7 @@ class Commands:
return encrypted . decode ( ' utf-8 ' )
return encrypted . decode ( ' utf-8 ' )
@command ( ' wp ' )
@command ( ' wp ' )
def decrypt ( self , pubkey , encrypted , password = None ) - > str :
async def decrypt ( self , pubkey , encrypted , password = None ) - > str :
""" Decrypt a message encrypted with a public key. """
""" Decrypt a message encrypted with a public key. """
if not is_hex_str ( pubkey ) :
if not is_hex_str ( pubkey ) :
raise Exception ( f " pubkey must be a hex string instead of { repr ( pubkey ) } " )
raise Exception ( f " pubkey must be a hex string instead of { repr ( pubkey ) } " )
@ -619,7 +623,7 @@ class Commands:
return out
return out
@command ( ' w ' )
@command ( ' w ' )
def getrequest ( self , key ) :
async def getrequest ( self , key ) :
""" Return a payment request """
""" Return a payment request """
r = self . wallet . get_payment_request ( key , self . config )
r = self . wallet . get_payment_request ( key , self . config )
if not r :
if not r :
@ -627,12 +631,12 @@ class Commands:
return self . _format_request ( r )
return self . _format_request ( r )
#@command('w')
#@command('w')
#def ackrequest(self, serialized):
#async def ackrequest(self, serialized):
# """<Not implemented>"""
# """<Not implemented>"""
# pass
# pass
@command ( ' w ' )
@command ( ' w ' )
def listrequests ( self , pending = False , expired = False , paid = False ) :
async def listrequests ( self , pending = False , expired = False , paid = False ) :
""" List the payment requests you made. """
""" List the payment requests you made. """
out = self . wallet . get_sorted_requests ( self . config )
out = self . wallet . get_sorted_requests ( self . config )
if pending :
if pending :
@ -648,18 +652,18 @@ class Commands:
return list ( map ( self . _format_request , out ) )
return list ( map ( self . _format_request , out ) )
@command ( ' w ' )
@command ( ' w ' )
def createnewaddress ( self ) :
async def createnewaddress ( self ) :
""" Create a new receiving address, beyond the gap limit of the wallet """
""" Create a new receiving address, beyond the gap limit of the wallet """
return self . wallet . create_new_address ( False )
return self . wallet . create_new_address ( False )
@command ( ' w ' )
@command ( ' w ' )
def getunusedaddress ( self ) :
async def getunusedaddress ( self ) :
""" Returns the first unused address of the wallet, or None if all addresses are used.
""" 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 . """
An address is considered as used if it has received a transaction , or if it is used in a payment request . """
return self . wallet . get_unused_address ( )
return self . wallet . get_unused_address ( )
@command ( ' w ' )
@command ( ' w ' )
def addrequest ( self , amount , memo = ' ' , expiration = None , force = False ) :
async def addrequest ( self , amount , memo = ' ' , expiration = None , force = False ) :
""" Create a payment request, using the first unused address of the wallet.
""" Create a payment request, using the first unused address of the wallet.
The address will be considered as used after this operation .
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 . """
If no payment is received , the address will be considered as unused if the payment request is deleted from the wallet . """
@ -677,7 +681,7 @@ class Commands:
return self . _format_request ( out )
return self . _format_request ( out )
@command ( ' w ' )
@command ( ' w ' )
def addtransaction ( self , tx ) :
async def addtransaction ( self , tx ) :
""" Add a transaction to the wallet history """
""" Add a transaction to the wallet history """
tx = Transaction ( tx )
tx = Transaction ( tx )
if not self . wallet . add_transaction ( tx . txid ( ) , tx ) :
if not self . wallet . add_transaction ( tx . txid ( ) , tx ) :
@ -686,7 +690,7 @@ class Commands:
return tx . txid ( )
return tx . txid ( )
@command ( ' wp ' )
@command ( ' wp ' )
def signrequest ( self , address , password = None ) :
async def signrequest ( self , address , password = None ) :
" Sign payment request with an OpenAlias "
" Sign payment request with an OpenAlias "
alias = self . config . get ( ' alias ' )
alias = self . config . get ( ' alias ' )
if not alias :
if not alias :
@ -695,31 +699,31 @@ class Commands:
self . wallet . sign_payment_request ( address , alias , alias_addr , password )
self . wallet . sign_payment_request ( address , alias , alias_addr , password )
@command ( ' w ' )
@command ( ' w ' )
def rmrequest ( self , address ) :
async def rmrequest ( self , address ) :
""" Remove a payment request """
""" Remove a payment request """
return self . wallet . remove_payment_request ( address , self . config )
return self . wallet . remove_payment_request ( address , self . config )
@command ( ' w ' )
@command ( ' w ' )
def clearrequests ( self ) :
async def clearrequests ( self ) :
""" Remove all payment requests """
""" Remove all payment requests """
for k in list ( self . wallet . receive_requests . keys ( ) ) :
for k in list ( self . wallet . receive_requests . keys ( ) ) :
self . wallet . remove_payment_request ( k , self . config )
self . wallet . remove_payment_request ( k , self . config )
@command ( ' n ' )
@command ( ' n ' )
def notify ( self , address : str , URL : str ) :
async def notify ( self , address : str , URL : str ) :
""" Watch an address. Every time the address changes, a http POST is sent to the URL. """
""" Watch an address. Every time the address changes, a http POST is sent to the URL. """
if not hasattr ( self , " _notifier " ) :
if not hasattr ( self , " _notifier " ) :
self . _notifier = Notifier ( self . network )
self . _notifier = Notifier ( self . network )
self . network . run_from_another_thread ( self . _notifier . start_watching_queue . put ( ( address , URL ) ) )
await self . _notifier . start_watching_queue . put ( ( address , URL ) )
return True
return True
@command ( ' wn ' )
@command ( ' wn ' )
def is_synchronized ( self ) :
async def is_synchronized ( self ) :
""" return wallet synchronization status """
""" return wallet synchronization status """
return self . wallet . is_up_to_date ( )
return self . wallet . is_up_to_date ( )
@command ( ' n ' )
@command ( ' n ' )
def getfeerate ( self , fee_method = None , fee_level = None ) :
async def getfeerate ( self , fee_method = None , fee_level = None ) :
""" Return current suggested fee rate (in sat/kvByte), according to config
""" Return current suggested fee rate (in sat/kvByte), according to config
settings or supplied parameters .
settings or supplied parameters .
"""
"""
@ -738,7 +742,7 @@ class Commands:
return self . config . fee_per_kb ( dyn = dyn , mempool = mempool , fee_level = fee_level )
return self . config . fee_per_kb ( dyn = dyn , mempool = mempool , fee_level = fee_level )
@command ( ' w ' )
@command ( ' w ' )
def removelocaltx ( self , txid ) :
async def removelocaltx ( self , txid ) :
""" Remove a ' local ' transaction from the wallet, and its dependent
""" Remove a ' local ' transaction from the wallet, and its dependent
transactions .
transactions .
"""
"""
@ -755,7 +759,7 @@ class Commands:
self . wallet . storage . write ( )
self . wallet . storage . write ( )
@command ( ' wn ' )
@command ( ' wn ' )
def get_tx_status ( self , txid ) :
async def get_tx_status ( self , txid ) :
""" Returns some information regarding the tx. For now, only confirmations.
""" Returns some information regarding the tx. For now, only confirmations.
The transaction must be related to the wallet .
The transaction must be related to the wallet .
"""
"""
@ -768,58 +772,57 @@ class Commands:
}
}
@command ( ' ' )
@command ( ' ' )
def help ( self ) :
async def help ( self ) :
# for the python console
# for the python console
return sorted ( known_commands . keys ( ) )
return sorted ( known_commands . keys ( ) )
# lightning network commands
# lightning network commands
@command ( ' wn ' )
@command ( ' wn ' )
def add_peer ( self , connection_string , timeout = 20 ) :
async def add_peer ( self , connection_string , timeout = 20 ) :
coro = self . lnworker . add_peer ( connection_string )
await self . lnworker . add_peer ( connection_string )
self . network . run_from_another_thread ( coro , timeout = timeout )
return True
return True
@command ( ' wpn ' )
@command ( ' wpn ' )
def open_channel ( self , connection_string , amount , channel_push = 0 , password = None ) :
async def open_channel ( self , connection_string , amount , channel_push = 0 , password = None ) :
chan = self . lnworker . open_channel ( connection_string , satoshis ( amount ) , satoshis ( channel_push ) , password )
chan = await self . lnworker . _ open_channel_coroutine ( connection_string , satoshis ( amount ) , satoshis ( channel_push ) , password )
return chan . funding_outpoint . to_str ( )
return chan . funding_outpoint . to_str ( )
@command ( ' wn ' )
@command ( ' wn ' )
def lnpay ( self , invoice , attempts = 1 , timeout = 10 ) :
async def lnpay ( self , invoice , attempts = 1 , timeout = 10 ) :
return self . lnworker . pay ( invoice , attempts = attempts , timeout = timeout )
return await self . lnworker . _ pay( invoice , attempts = attempts )
@command ( ' wn ' )
@command ( ' wn ' )
def addinvoice ( self , requested_amount , message ) :
async def addinvoice ( self , requested_amount , message ) :
# using requested_amount because it is documented in param_descriptions
# using requested_amount because it is documented in param_descriptions
payment_hash = self . lnworker . add_invoice ( satoshis ( requested_amount ) , message )
payment_hash = await self . lnworker . _ add_invoice_coro ( satoshis ( requested_amount ) , message )
invoice , direction , is_paid = self . lnworker . invoices [ bh2u ( payment_hash ) ]
invoice , direction , is_paid = self . lnworker . invoices [ bh2u ( payment_hash ) ]
return invoice
return invoice
@command ( ' w ' )
@command ( ' w ' )
def nodeid ( self ) :
async def nodeid ( self ) :
listen_addr = self . config . get ( ' lightning_listen ' )
listen_addr = self . config . get ( ' lightning_listen ' )
return bh2u ( self . lnworker . node_keypair . pubkey ) + ( ( ' @ ' + listen_addr ) if listen_addr else ' ' )
return bh2u ( self . lnworker . node_keypair . pubkey ) + ( ( ' @ ' + listen_addr ) if listen_addr else ' ' )
@command ( ' w ' )
@command ( ' w ' )
def list_channels ( self ) :
async def list_channels ( self ) :
return list ( self . lnworker . list_channels ( ) )
return list ( self . lnworker . list_channels ( ) )
@command ( ' wn ' )
@command ( ' wn ' )
def dumpgraph ( self ) :
async def dumpgraph ( self ) :
return list ( map ( bh2u , self . lnworker . channel_db . nodes . keys ( ) ) )
return list ( map ( bh2u , self . lnworker . channel_db . nodes . keys ( ) ) )
@command ( ' n ' )
@command ( ' n ' )
def inject_fees ( self , fees ) :
async def inject_fees ( self , fees ) :
import ast
import ast
self . network . config . fee_estimates = ast . literal_eval ( fees )
self . network . config . fee_estimates = ast . literal_eval ( fees )
self . network . notify ( ' fee ' )
self . network . notify ( ' fee ' )
@command ( ' n ' )
@command ( ' n ' )
def clear_ln_blacklist ( self ) :
async def clear_ln_blacklist ( self ) :
self . network . path_finder . blacklist . clear ( )
self . network . path_finder . blacklist . clear ( )
@command ( ' w ' )
@command ( ' w ' )
def lightning_invoices ( self ) :
async def lightning_invoices ( self ) :
from . util import pr_tooltips
from . util import pr_tooltips
out = [ ]
out = [ ]
for payment_hash , ( preimage , invoice , is_received , timestamp ) in self . lnworker . invoices . items ( ) :
for payment_hash , ( preimage , invoice , is_received , timestamp ) in self . lnworker . invoices . items ( ) :
@ -836,18 +839,18 @@ class Commands:
return out
return out
@command ( ' w ' )
@command ( ' w ' )
def lightning_history ( self ) :
async def lightning_history ( self ) :
return self . lnworker . get_history ( )
return self . lnworker . get_history ( )
@command ( ' wn ' )
@command ( ' wn ' )
def close_channel ( self , channel_point , force = False ) :
async def close_channel ( self , channel_point , force = False ) :
txid , index = channel_point . split ( ' : ' )
txid , index = channel_point . split ( ' : ' )
chan_id , _ = channel_id_from_funding_tx ( txid , int ( index ) )
chan_id , _ = channel_id_from_funding_tx ( txid , int ( index ) )
coro = self . lnworker . force_close_channel ( chan_id ) if force else self . lnworker . close_channel ( chan_id )
coro = self . lnworker . force_close_channel ( chan_id ) if force else self . lnworker . close_channel ( chan_id )
return self . network . run_from_another_thread ( coro )
return await coro
@command ( ' wn ' )
@command ( ' wn ' )
def get_channel_ctx ( self , channel_point ) :
async def get_channel_ctx ( self , channel_point ) :
""" return the current commitment transaction of a channel """
""" return the current commitment transaction of a channel """
txid , index = channel_point . split ( ' : ' )
txid , index = channel_point . split ( ' : ' )
chan_id , _ = channel_id_from_funding_tx ( txid , int ( index ) )
chan_id , _ = channel_id_from_funding_tx ( txid , int ( index ) )