def satoshis(amount):
# satoshi conversion must not be performed by the parser
return int(COIN*Decimal(amount)) if amount is not None else None
@command('')
async def create(self, passphrase=None, password=None, encrypt_file=True, seed_type=None, wallet_path=None):
"""Create a new wallet.
If you want to be prompted for an argument, type '?' or ':' (concealed)
"""
d = create_new_wallet(path=wallet_path,
passphrase=passphrase,
password=password,
encrypt_file=encrypt_file,
seed_type=seed_type,
config=self.config)
return {
'seed': d['seed'],
'path': d['wallet'].storage.path,
'msg': d['msg'],
}
@command('')
async def restore(self, text, passphrase=None, password=None, encrypt_file=True, wallet_path=None):
"""Restore a wallet from text. Text can be a seed phrase, a master
public key, a master private key, a list of bitcoin addresses
or bitcoin private keys.
If you want to be prompted for an argument, type '?' or ':' (concealed)
"""
# TODO create a separate command that blocks until wallet is synced
d = restore_wallet_from_text(text,
path=wallet_path,
passphrase=passphrase,
password=password,
encrypt_file=encrypt_file,
config=self.config)
return {
'path': d['wallet'].storage.path,
'msg': d['msg'],
}
@command('wp')
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.")
b = wallet.storage.is_encrypted()
wallet.update_password(password, new_password, encrypt_storage=b)
wallet.save_db()
return {'password':wallet.has_password()}
@command('')
async def setconfig(self, key, value):
"""Set a configuration variable. 'value' may be a string or a Python expression."""
value = self._setconfig_normalize_value(key, value)
if self.daemon and key == 'rpcuser':
self.daemon.commands_server.rpc_user = value
if self.daemon and key == 'rpcpassword':
self.daemon.commands_server.rpc_password = value
self.config.set_key(key, value)
return True
@command('')
async def get_ssl_domain(self):
"""Check and return the SSL domain set in ssl_keyfile and ssl_certfile
"""
return self.config.get_ssl_domain()
@command('n')
async def getaddresshistory(self, address):
"""Return the transaction history of any address. Note: This is a
walletless server query, results are not checked by SPV.
"""
sh = bitcoin.address_to_scripthash(address)
return await self.network.get_history_for_scripthash(sh)
@command('n')
async def getaddressunspent(self, address):
"""Returns the UTXO list of any address. Note: This
is a walletless server query, results are not checked by SPV.
"""
sh = bitcoin.address_to_scripthash(address)
return await self.network.listunspent_for_scripthash(sh)
@command('')
async def signtransaction_with_privkey(self, tx, privkey):
"""Sign a transaction. The provided list of private keys will be used to sign the transaction."""
tx = tx_from_any(tx)
@command('w')
async def freeze(self, address: str, 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: str, 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('w')
async def freeze_utxo(self, coin: str, wallet: Abstract_Wallet = None): ( [{"Show unspent outputs/auto/tx_hash_big_endian":"5da8f74aa4d6612c58c0a740b4740579db57c95828e4cdf04e7d18c23b66cb4a","tx_hash":"4acb663bc2187d4ef0cde42858c957db790574b440a7c0582c61d6a44af7a85d","tx_output_n":1,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":20100,"value_hex":"4e84","confirmations":4597,"tx_index":2631598528283407},{"tx_hash_big_endian":"354c4488e0aa4ebb787667f3560ab827ac337b9be60b3ef315e469c42fb71add","tx_hash":"dd1ab72fc469e415f33e0be69b7b33ac27b80a56f3677678bb4eaae088444c35","tx_output_n":73,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":17526,"value_hex":"4476","confirmations":5496,"tx_index":7779417991974204},{"tx_hash_big_endian":"21790d71d2581afdc456dbf22e1c95b77e2c0bd527aba199481cc0a4ed94d4f3","tx_hash":"f3d494eda4c01c4899a1ab27d50b2c7eb7951c2ef2db56c4fd1a58d2710d7921","tx_output_n":24,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":291612,"value_hex":"04731c","confirmations":25684,"tx_index":8579019430991875},{"tx_hash_big_endian":"9fed8adf37c5a496f080266105f823e2162c030c271a56e3f223391e7d8560ec","tx_hash":"ec60857d1e3923f2e3561a270c032c16e223f805612680f096a4c537df8aed9f","tx_output_n":25,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":6000,"value_hex":"1770","confirmations":26575,"tx_index":8316777618720548},{"tx_hash_big_endian":"9378ed7213ad98077ecf16a7a5b55a553ae63d0eeff2def979571e5030ff7447","tx_hash":"4774ff30501e5779f9def2ef0e3de63a555ab5a5a716cf7e0798ad1372ed7893","tx_output_n":60,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":10000,"value_hex":"2710","confirmations":39674,"tx_index":2514170340312010},{"tx_hash_big_endian":"7b2a671cec84301314fe1b27c0c993c5850fb3463da6c479f97820cd7f08a967","tx_hash":"67a9087fcd2078f979c4a63d46b30f85c593c9c0271bfe14133084ec1c672a7b","tx_output_n":1,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":16325,"value_hex":"3fc5","confirmations":46427,"tx_index":3647222071272463},{"tx_hash_big_endian":"d91b8365dd3e0d3d7611fc270baf6685697f1c4037c9dec48f19447322f088c4","tx_hash":"c488f0227344198fc4dec937401c7f698566af0b27fc11763d0d3edd65831bd9","tx_output_n":2,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":50000,"value_hex":"00c350","confirmations":56293,"tx_index":6914957548349571},{"tx_hash_big_endian":"c48d85744f5762d3d02a8daff2dfdb15d1952dbe778e737da218c60a1ca34187","tx_hash":"8741a31c0ac618a27d738e77be2d95d115dbdff2af8d2ad0d362574f74858dc4","tx_output_n":1,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":596677,"value_hex":"091ac5","confirmations":56435,"tx_index":4758911332735171},{"tx_hash_big_endian":"fd2abfa58670e8d811aedb029c60d117f25186e00351f581ecf6e3063cc281f8","tx_hash":"f881c23c06e3f6ec81f55103e08651f217d1609c02dbae11d8e87086a5bf2afd","tx_output_n":6,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":50000,"value_hex":"00c350","confirmations":56487,"tx_index":8743558181870718},{"tx_hash_big_endian":"2d596683c9c843cf7c1339a202c6c0c30259be361a55339ee932663ebf07527f","tx_hash":"7f5207bf3e6632e99e33551a36be5902c3c0c602a239137ccf43c8c98366592d","tx_output_n":3,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":5433,"value_hex":"1539","confirmations":57336,"tx_index":4479689408629958},{"tx_hash_big_endian":"d67a35f492510b9f3f0819771d45440b2b1bd498bbc81d4b31e360fa9ad333f4","tx_hash":"f433d39afa60e3314b1dc8bb98d41b2b0b44451d7719083f9f0b5192f4357ad6","tx_output_n":0,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":1000,"value_hex":"03e8","confirmations":57412,"tx_index":8592109781077020},{"tx_hash_big_endian":"d1c131aeaf16fe81087e811c0acba292c0f139ad86d154ac037004187d848ad9","tx_hash":"d98a847d18047003ac54d186ad39f1c092a2cb0a1c817e0881fe16afae31c1d1","tx_output_n":98,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":547,"value_hex":"0223","confirmations":57472,"tx_index":7654046448156814},{"tx_hash_big_endian":"6f014b3265de8c840d340e3a7651e44fd9e9ed9e172b44905fcff29a5859ffd6","tx_hash":"d6ff59589af2cf5f90442b179eede9d94fe451763a0e340d848cde65324b016f","tx_output_n":1,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":100000,"value_hex":"0186a0","confirmations":57652,"tx_index":7564550527475289},{"tx_hash_big_endian":"ed5f4bf0ecebffaff1e7a81b537349a8133da58db771c40f4ad86fcf0549cef9","tx_hash":"f9ce4905cf6fd84a0fc471b78da53d13a84973531ba8e7f1afffebecf04b5fed","tx_output_n":0,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":6501,"value_hex":"1965","confirmations":57910,"tx_index":8789260278296059},{"tx_hash_big_endian":"388a95a32b75e5291c3160b5f2eef3dbcfcf482f5575f3b6aea1c7a80873812f","tx_hash":"2f817308a8c7a1aeb6f375552f48cfcfdbf3eef2b560311c29e5752ba3958a38","tx_output_n":0,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":6125,"value_hex":"17ed","confirmations":58184,"tx_index":1671456871487732},{"tx_hash_big_endian":"263a47f985c5a3c5
"""Freeze a UTXO so that the wallet will not spend it."""
wallet.set_frozen_state_of_coins([coin], True)
return True
@command('w')
async def unfreeze_utxo(self, coin: str, wallet: Abstract_Wallet = None):
"""Unfreeze a UTXO so that the wallet might spend it."""
wallet.set_frozen_state_of_coins([coin], False)
return True
@command('wp')
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()
if is_address(address):
return wallet.export_private_key(address, password)
domain = address
return [wallet.export_private_key(address, password) for address in domain]
@command('wp')
async def getprivatekeyforpath(self, path, password=None, wallet: Abstract_Wallet = None):
"""Get private key corresponding to derivation path (address index).
'path' can be either a str such as "m/0/50", or a list of ints such as [0, 50].
"""
return wallet.export_private_key_for_path(path, password)
@command('w')
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)
@command('')
async def dumpprivkeys(self):
"""Deprecated."""
return "This command is deprecated. Use a pipe instead: 'electrum listaddresses | electrum getprivatekeys - '"
@command('')
async def validateaddress(self, address):
"""Check that an address is valid. """
return is_address(address)
@command('w')
async def getpubkeys(self, address, wallet: Abstract_Wallet = None):
"""Return the public keys for a wallet address. """
return wallet.get_public_keys(address)
@command('n')
async def getaddressbalance(self, address):
"""Return the balance of any address. Note: This is a walletless
server query, results are not checked by SPV.
"""
sh = bitcoin.address_to_scripthash(address)
out = await self.network.get_balance_for_scripthash(sh)
out["confirmed"] = str(Decimal(out["confirmed"])/COIN)
out["unconfirmed"] = str(Decimal(out["unconfirmed"])/COIN)
return out
@command('n')
async def getmerkle(self, txid, height):
"""Get Merkle branch of a transaction included in a block. Electrum
uses this to verify transactions (Simple Payment Verification)."""
return await self.network.get_merkle_for_transaction(txid, int(height))
@command('n')
async def getservers(self):
"""Return the list of known servers (candidates for connecting)."""
return self.network.get_servers()
@command('')
async def version(self):
"""Return the version of Electrum."""
from .version import ELECTRUM_VERSION
return ELECTRUM_VERSION
@command('w')
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()
[ import sys import datetime import copy import argparse import json import ast import base64 import operator import asyncio import inspect from collections import defaultdict from functools import wraps, partial from itertools import repeat from decimal import Decimal from typing import Optional, TYPE_CHECKING, Dict, List
from .import util, ecc from .util import (bfh, bh2u, format_satoshis, json_decode, json_normalize, is_hash256_str, is_hex_str, to_bytes, parse_max_spend) from . import bitcoin from .bitcoin import is_address, hash160, COIN from .bip32 import BIP32Node from .i18n import from .transaction import (Transaction, multisig_script, TxOutput, PartialTransaction, PartialTxOutput, 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, BumpFeeStrategy from .address_synchronizer import TX_HEIGHT_LOCAL from .mnemonic import Mnemonic from .lnutil import SENT, RECEIVED from .lnutil import LnFeatures from .lnutil import extract_nodeid from .lnpeer import channel_id_from_funding_tx from .plugin import run_hook from .version import ELECTRUM_VERSION from .simple_config import SimpleConfig from .invoices import LNInvoice from . import submarine_swaps
if TYPE_CHECKING: from .network import Network from .daemon import Daemon
known_commands = {} # type: Dict[str, Command]
class NotSynchronizedException(Exception): pass
def satoshis_or_max(amount): return satoshis(amount) if not parse_max_spend(amount) else amount
def satoshis(amount): # satoshi conversion must not be performed by the parser return int(COIN*Decimal(amount)) if amount is not None else None
def format_satoshis(x): return str(Decimal(x)/COIN) if x is not None else None
class Command: def init(self, func, s): self.name = func.name self.requires_network = 'n' in s self.requires_wallet = 'w' in s self.requires_password = 'p' in s self.requires_lightning = 'l' in s self.description = func.doc self.help = self.description.split('.')[0] if self.description else None varnames = func.code.co_varnames[1:func.code.co_argcount] self.defaults = func.defaults if self.defaults: n = len(self.defaults) self.params = list(varnames[:-n]) self.options = list(varnames[-n:]) else: self.params = list(varnames) self.options = [] self.defaults = []
# sanity checks if self.requires_password: assert self.requires_wallet for varname in ('wallet_path', 'wallet'): if varname in varnames: assert varname in self.options assert not ('wallet_path' in varnames and 'wallet' in varnames) if self.requires_wallet: assert 'wallet' in varnames
def command(s): def decorator(func): global known_commands name = func.name known_commands[name] = Command(func, s) @wraps(func) async def func_wrapper(*args, *kwargs): cmd_runner = args[0] # type: Commands cmd = known_commands[func.name] # type: Command password = kwargs.get('password') daemon = cmd_runner.daemon if daemon: if 'wallet_path' in cmd.options and kwargs.get('wallet_path') is None: kwargs['wallet_path'] = daemon.config.get_wallet_path() if cmd.requires_wallet and kwargs.get('wallet') is None: kwargs['wallet'] = daemon.config.get_wallet_path() if 'wallet' in cmd.options: wallet_path = kwargs.get('wallet', None) if isinstance(wallet_path, str): wallet = daemon.get_wallet(wallet_path) if wallet is None: raise Exception('wallet not loaded') kwargs['wallet'] = wallet wallet = kwargs.get('wallet') # type: Optional[Abstract_Wallet] if cmd.requires_wallet and not wallet: raise Exception('wallet not loaded') if cmd.requires_password and password is None and wallet.has_password(): raise Exception('Password required') if cmd.requires_lightning and (not wallet or not wallet.has_lightning()): raise Exception('Lightning not enabled in this wallet') return await func(args, **kwargs) return func_wrapper return decorator
class Commands:
def init(self, *, config: 'SimpleConfig', network: 'Network' = None, daemon: 'Daemon' = None, callback=None): self.config = config self.daemon = daemon self.network = network self._callback = callback
def _run(self, method, args, password_getter=None, **kwargs): """This wrapper is called from unit tests and the Qt python console.""" cmd = known_commands[method] password = kwargs.get('password', None) wallet = kwargs.get('wallet', None) if (cmd.requires_password and wallet and wallet.has_password() and password is None): password = password_getter() if password is None: return
f = getattr(self, method) if cmd.requires_password: kwargs['password'] = password
if 'wallet' in kwargs: sig = inspect.signature(f) if 'wallet' not in sig.parameters: kwargs.pop('wallet')
coro = f(*args, **kwargs) fut = asyncio.run_coroutine_threadsafe(coro, asyncio.get_event_loop()) result = fut.result()
if self._callback: self._callback() return result
@command('') async def commands(self): """List of commands""" return ' '.join(sorted(known_commands.keys()))
@command('n') async def getinfo(self): """ network info """ net_params = self.network.get_parameters() response = { 'path': self.network.config.path, 'server': net_params.server.host, 'blockchain_height': self.network.get_local_height(), 'server_height': self.network.get_server_height(), 'spv_nodes': len(self.network.get_interfaces()), 'connected': self.network.is_connected(), 'auto_connect': net_params.auto_connect, 'version': ELECTRUM_VERSION, 'default_wallet': self.config.get_wallet_path(), 'fee_per_kb': self.config.fee_per_kb(), } return response
@command('n') async def stop(self): """Stop daemon""" await self.daemon.stop() return "Daemon stopped"
@command('n') async def list_wallets(self): """List wallets open in daemon""" return [{'path': path, 'synchronized': w.is_up_to_date()} for path, w in self.daemon.get_wallets().items()]
@command('n') async def load_wallet(self, wallet_path=None, password=None): """Open wallet in daemon""" wallet = self.daemon.load_wallet(wallet_path, password, manual_upgrades=False) if wallet is not None: run_hook('load_wallet', wallet, None) response = wallet is not None return response
@command('n') async def close_wallet(self, wallet_path=None): """Close wallet""" return await self.daemon._stop_wallet(wallet_path)
@command('') async def create(self, passphrase=None, password=None, encrypt_file=True, seed_type=None, wallet_path=None): """Create a new wallet. If you want to be prompted for an argument, type '?' or ':' (concealed) """ d = create_new_wallet(path=wallet_path, passphrase=passphrase, password=password, encrypt_file=encrypt_file, seed_type=seed_type, config=self.config) return { 'seed': d['seed'], 'path': d['wallet'].storage.path, 'msg': d['msg'], }
@command('') async def restore(self, text, passphrase=None, password=None, encrypt_file=True, wallet_path=None): """Restore a wallet from text. Text can be a seed phrase, a master public key, a master private key, a list of bitcoin addresses or bitcoin private keys. If you want to be prompted for an argument, type '?' or ':' (concealed) """ # TODO create a separate command that blocks until wallet is synced d = restore_wallet_from_text(text, path=wallet_path, passphrase=passphrase, password=password, encrypt_file=encrypt_file, config=self.config) return { 'path': d['wallet'].storage.path, 'msg': d['msg'], }
@command('wp') 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.") b = wallet.storage.is_encrypted() wallet.update_password(password, new_password, encrypt_storage=b) wallet.save_db() return {'password':wallet.has_password()}
@command('w') async def get(self, key, wallet: Abstract_Wallet = None): """Return item from wallet storage""" return wallet.db.get(key)
@command('') async def getconfig(self, key): """Return a configuration variable. """ return self.config.get(key)
@classmethod def _setconfig_normalize_value(cls, key, value): if key not in ('rpcuser', 'rpcpassword'): value = json_decode(value) # call literal_eval for backward compatibility (see #4225) try: value = ast.literal_eval(value) except: pass return value
@command('') async def setconfig(self, key, value): """Set a configuration variable. 'value' may be a string or a Python expression.""" value = self._setconfig_normalize_value(key, value) if self.daemon and key == 'rpcuser': self.daemon.commands_server.rpc_user = value if self.daemon and key == 'rpcpassword': self.daemon.commands_server.rpc_password = value self.config.set_key(key, value) return True
@command('') async def get_ssl_domain(self): """Check and return the SSL domain set in ssl_keyfile and ssl_certfile """ return self.config.get_ssl_domain()
@command('') async def make_seed(self, nbits=None, language=None, seed_type=None): """Create a seed""" from .mnemonic import Mnemonic s = Mnemonic(language).make_seed(seed_type=seed_type, num_bits=nbits) return s
@command('n') async def getaddresshistory(self, address): """Return the transaction history of any address. Note: This is a walletless server query, results are not checked by SPV. """ sh = bitcoin.address_to_scripthash(address) return await self.network.get_history_for_scripthash(sh)
@command('w') async def listunspent(self, wallet: Abstract_Wallet = None): """List unspent outputs. Returns the list of unspent transaction outputs in your wallet.""" coins = [] for txin in wallet.get_utxos(): d = txin.to_json() v = d.pop("value_sats") d["value"] = str(Decimal(v)/COIN) if v is not None else None coins.append(d) return coins
@command('n') async def getaddressunspent(self, address): """Returns the UTXO list of any address. Note: This is a walletless server query, results are not checked by SPV. """ sh = bitcoin.address_to_scripthash(address) return await self.network.listunspent_for_scripthash(sh)
@command('') async def serialize(self, jsontx): """Create a transaction from json inputs. Inputs must have a redeemPubkey. Outputs must be a list of {'address':address, 'value':satoshi_amount}. """ keypairs = {} inputs = [] # type: List[PartialTxInput] locktime = jsontx.get('locktime', 0) for txin_dict in jsontx.get('inputs'): if txin_dict.get('prevout_hash') is not None and txin_dict.get('prevout_n') is not None: prevout = TxOutpoint(txid=bfh(txin_dict['prevout_hash']), out_idx=int(txin_dict['prevout_n'])) elif txin_dict.get('output'): prevout = TxOutpoint.from_str(txin_dict['output']) else: raise Exception("missing prevout for txin") txin = PartialTxInput(prevout=prevout) txin._trusted_value_sats = int(txin_dict.get('value', txin_dict['value_sats'])) nsequence = txin_dict.get('nsequence', None) if nsequence is not None: txin.nsequence = nsequence sec = txin_dict.get('privkey') if sec: txin_type, privkey, compressed = bitcoin.deserialize_privkey(sec) pubkey = ecc.ECPrivkey(privkey).get_public_key_hex(compressed=compressed) keypairs[pubkey] = privkey, compressed txin.script_type = txin_type txin.pubkeys = [bfh(pubkey)] txin.num_sig = 1 inputs.append(txin)
outputs = [PartialTxOutput.from_address_and_value(txout['address'], int(txout.get('value', txout['value_sats']))) for txout in jsontx.get('outputs')] tx = PartialTransaction.from_io(inputs, outputs, locktime=locktime) tx.sign(keypairs) return tx.serialize()
@command('') async def signtransaction_with_privkey(self, tx, privkey): """Sign a transaction. The provided list of private keys will be used to sign the transaction.""" tx = tx_from_any(tx)
txins_dict = defaultdict(list) for txin in tx.inputs(): txins_dict[txin.address].append(txin)
if not isinstance(privkey, list): privkey = [privkey]
for priv in privkey: txin_type, priv2, compressed = bitcoin.deserialize_privkey(priv) pubkey = ecc.ECPrivkey(priv2).get_public_key_bytes(compressed=compressed) address = bitcoin.pubkey_to_address(txin_type, pubkey.hex()) if address in txins_dict.keys(): for txin in txins_dict[address]: txin.pubkeys = [pubkey] txin.script_type = txin_type tx.sign({pubkey.hex(): (priv2, compressed)})
return tx.serialize()
@command('wp') async def signtransaction(self, tx, password=None, wallet: Abstract_Wallet = None): ]) """Sign a transaction. The wallet keys will be used to sign the transaction.""" tx = tx_from_any(tx) wallet.sign_transaction(tx, password) return tx.serialize()
@command('') async def deserialize(self, tx): """Deserialize a serialized transaction""" tx = tx_from_any(tx) return tx.to_json()
@command('n') async def broadcast(self, tx): """Broadcast a transaction to the network. """ tx = Transaction(tx) await self.network.broadcast_transaction(tx) return tx.txid()
@command('') async def createmultisig(self, num, pubkeys): """Create multisig address""" assert isinstance(pubkeys, list), (type(num), type(pubkeys)) redeem_script = multisig_script(pubkeys, num) address = bitcoin.hash160_to_p2sh(hash_160(bfh(redeem_script))) return {'address':address, 'redeemScript':redeem_script}
@command('w') async def freeze(self, address: str, 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: str, 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('w') async def freeze_utxo(self, coin: str, wallet: Abstract_Wallet = None): ( [{"Show unspent outputs/auto/tx_hash_big_endian":"5da8f74aa4d6612c58c0a740b4740579db57c95828e4cdf04e7d18c23b66cb4a","tx_hash":"4acb663bc2187d4ef0cde42858c957db790574b440a7c0582c61d6a44af7a85d","tx_output_n":1,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":20100,"value_hex":"4e84","confirmations":4597,"tx_index":2631598528283407},{"tx_hash_big_endian":"354c4488e0aa4ebb787667f3560ab827ac337b9be60b3ef315e469c42fb71add","tx_hash":"dd1ab72fc469e415f33e0be69b7b33ac27b80a56f3677678bb4eaae088444c35","tx_output_n":73,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":17526,"value_hex":"4476","confirmations":5496,"tx_index":7779417991974204},{"tx_hash_big_endian":"21790d71d2581afdc456dbf22e1c95b77e2c0bd527aba199481cc0a4ed94d4f3","tx_hash":"f3d494eda4c01c4899a1ab27d50b2c7eb7951c2ef2db56c4fd1a58d2710d7921","tx_output_n":24,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":291612,"value_hex":"04731c","confirmations":25684,"tx_index":8579019430991875},{"tx_hash_big_endian":"9fed8adf37c5a496f080266105f823e2162c030c271a56e3f223391e7d8560ec","tx_hash":"ec60857d1e3923f2e3561a270c032c16e223f805612680f096a4c537df8aed9f","tx_output_n":25,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":6000,"value_hex":"1770","confirmations":26575,"tx_index":8316777618720548},{"tx_hash_big_endian":"9378ed7213ad98077ecf16a7a5b55a553ae63d0eeff2def979571e5030ff7447","tx_hash":"4774ff30501e5779f9def2ef0e3de63a555ab5a5a716cf7e0798ad1372ed7893","tx_output_n":60,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":10000,"value_hex":"2710","confirmations":39674,"tx_index":2514170340312010},{"tx_hash_big_endian":"7b2a671cec84301314fe1b27c0c993c5850fb3463da6c479f97820cd7f08a967","tx_hash":"67a9087fcd2078f979c4a63d46b30f85c593c9c0271bfe14133084ec1c672a7b","tx_output_n":1,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":16325,"value_hex":"3fc5","confirmations":46427,"tx_index":3647222071272463},{"tx_hash_big_endian":"d91b8365dd3e0d3d7611fc270baf6685697f1c4037c9dec48f19447322f088c4","tx_hash":"c488f0227344198fc4dec937401c7f698566af0b27fc11763d0d3edd65831bd9","tx_output_n":2,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":50000,"value_hex":"00c350","confirmations":56293,"tx_index":6914957548349571},{"tx_hash_big_endian":"c48d85744f5762d3d02a8daff2dfdb15d1952dbe778e737da218c60a1ca34187","tx_hash":"8741a31c0ac618a27d738e77be2d95d115dbdff2af8d2ad0d362574f74858dc4","tx_output_n":1,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":596677,"value_hex":"091ac5","confirmations":56435,"tx_index":4758911332735171},{"tx_hash_big_endian":"fd2abfa58670e8d811aedb029c60d117f25186e00351f581ecf6e3063cc281f8","tx_hash":"f881c23c06e3f6ec81f55103e08651f217d1609c02dbae11d8e87086a5bf2afd","tx_output_n":6,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":50000,"value_hex":"00c350","confirmations":56487,"tx_index":8743558181870718},{"tx_hash_big_endian":"2d596683c9c843cf7c1339a202c6c0c30259be361a55339ee932663ebf07527f","tx_hash":"7f5207bf3e6632e99e33551a36be5902c3c0c602a239137ccf43c8c98366592d","tx_output_n":3,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":5433,"value_hex":"1539","confirmations":57336,"tx_index":4479689408629958},{"tx_hash_big_endian":"d67a35f492510b9f3f0819771d45440b2b1bd498bbc81d4b31e360fa9ad333f4","tx_hash":"f433d39afa60e3314b1dc8bb98d41b2b0b44451d7719083f9f0b5192f4357ad6","tx_output_n":0,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":1000,"value_hex":"03e8","confirmations":57412,"tx_index":8592109781077020},{"tx_hash_big_endian":"d1c131aeaf16fe81087e811c0acba292c0f139ad86d154ac037004187d848ad9","tx_hash":"d98a847d18047003ac54d186ad39f1c092a2cb0a1c817e0881fe16afae31c1d1","tx_output_n":98,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":547,"value_hex":"0223","confirmations":57472,"tx_index":7654046448156814},{"tx_hash_big_endian":"6f014b3265de8c840d340e3a7651e44fd9e9ed9e172b44905fcff29a5859ffd6","tx_hash":"d6ff59589af2cf5f90442b179eede9d94fe451763a0e340d848cde65324b016f","tx_output_n":1,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":100000,"value_hex":"0186a0","confirmations":57652,"tx_index":7564550527475289},{"tx_hash_big_endian":"ed5f4bf0ecebffaff1e7a81b537349a8133da58db771c40f4ad86fcf0549cef9","tx_hash":"f9ce4905cf6fd84a0fc471b78da53d13a84973531ba8e7f1afffebecf04b5fed","tx_output_n":0,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":6501,"value_hex":"1965","confirmations":57910,"tx_index":8789260278296059},{"tx_hash_big_endian":"388a95a32b75e5291c3160b5f2eef3dbcfcf482f5575f3b6aea1c7a80873812f","tx_hash":"2f817308a8c7a1aeb6f375552f48cfcfdbf3eef2b560311c29e5752ba3958a38","tx_output_n":0,"script":"76a914b3dd79fb3460c7b0d0bbb8d2ed93436b88b6d89c88ac","value":6125,"value_hex":"17ed","confirmations":58184,"tx_index":1671456871487732},{"tx_hash_big_endian":"263a47f985c5a3c5 """Freeze a UTXO so that the wallet will not spend it.""" wallet.set_frozen_state_of_coins([coin], True) return True
@command('w') async def unfreeze_utxo(self, coin: str, wallet: Abstract_Wallet = None): """Unfreeze a UTXO so that the wallet might spend it.""" wallet.set_frozen_state_of_coins([coin], False) return True
@command('wp') 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() if is_address(address): return wallet.export_private_key(address, password) domain = address return [wallet.export_private_key(address, password) for address in domain]
@command('wp') async def getprivatekeyforpath(self, path, password=None, wallet: Abstract_Wallet = None): """Get private key corresponding to derivation path (address index). 'path' can be either a str such as "m/0/50", or a list of ints such as [0, 50]. """ return wallet.export_private_key_for_path(path, password)
@command('w') 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)
@command('') async def dumpprivkeys(self): """Deprecated.""" return "This command is deprecated. Use a pipe instead: 'electrum listaddresses | electrum getprivatekeys - '"
@command('') async def validateaddress(self, address): """Check that an address is valid. """ return is_address(address)
@command('w') 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: 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 out = {"confirmed": str(Decimal(c)/COIN)} if u: out["unconfirmed"] = str(Decimal(u)/COIN) if x: out["unmatured"] = str(Decimal(x)/COIN) if l: out["lightning"] = str(Decimal(l)/COIN) return out
@command('n') async def getaddressbalance(self, address): """Return the balance of any address. Note: This is a walletless server query, results are not checked by SPV. """ sh = bitcoin.address_to_scripthash(address) out = await self.network.get_balance_for_scripthash(sh) out["confirmed"] = str(Decimal(out["confirmed"])/COIN) out["unconfirmed"] = str(Decimal(out["unconfirmed"])/COIN) return out
@command('n') async def getmerkle(self, txid, height): """Get Merkle branch of a transaction included in a block. Electrum uses this to verify transactions (Simple Payment Verification).""" return await self.network.get_merkle_for_transaction(txid, int(height))
@command('n') async def getservers(self): """Return the list of known servers (candidates for connecting).""" return self.network.get_servers()
@command('') async def version(self): """Return the version of Electrum.""" from .version import ELECTRUM_VERSION return ELECTRUM_VERSION
@command('w') 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: Abstract_Wallet = None): """Get master private key. Return your wallet\'s master private key""" return str(wallet.keystore.get_master_private_key(password))
@command('') async def convert_xkey(self, xkey, xtype): """Convert xtype of a master key. e.g. xpub -> ypub""" try: node = BIP32Node.from_xkey(xkey) except: