2013-02-26 13:56:48 +01:00
#!/usr/bin/env python
#
# Electrum - lightweight Bitcoin client
# Copyright (C) 2011 thomasv@gitorious
#
2016-02-23 11:36:42 +01:00
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
2013-02-26 13:56:48 +01:00
#
2016-02-23 11:36:42 +01:00
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
2013-02-26 13:56:48 +01:00
#
2016-02-23 11:36:42 +01:00
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
2013-02-26 13:56:48 +01:00
2015-05-28 15:22:30 +02:00
import sys
2014-06-24 16:12:43 +03:00
import datetime
import copy
2015-05-28 15:22:30 +02:00
import argparse
2015-05-30 18:49:58 +02:00
import json
2015-05-31 22:42:34 +02:00
import ast
2015-07-14 16:37:04 +02:00
import base64
2015-06-01 06:10:06 +02:00
from functools import wraps
from decimal import Decimal
2015-05-28 15:22:30 +02:00
2018-05-24 18:57:13 +02:00
from . import util , ecc
2018-04-05 12:22:01 +02:00
from . util import bfh , bh2u , format_satoshis , json_decode , print_error , json_encode
2017-01-22 21:25:24 +03:00
from . import bitcoin
from . bitcoin import is_address , hash_160 , COIN , TYPE_ADDRESS
2017-11-12 16:15:06 -06:00
from . i18n import _
2017-12-06 18:30:02 +01:00
from . transaction import Transaction , multisig_script
2017-01-22 21:25:24 +03:00
from . paymentrequest import PR_PAID , PR_UNPAID , PR_UNKNOWN , PR_EXPIRED
2017-10-10 13:44:25 +02:00
from . plugins import run_hook
2017-09-04 14:43:31 +02:00
2015-06-01 06:10:06 +02:00
known_commands = { }
2017-02-22 11:23:12 +01:00
def satoshis ( amount ) :
# satoshi conversion must not be performed by the parser
return int ( COIN * Decimal ( amount ) ) if amount not in [ ' ! ' , None ] else amount
2015-06-01 06:10:06 +02:00
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 . description = func . __doc__
2015-09-07 13:13:04 +02:00
self . help = self . description . split ( ' . ' ) [ 0 ] if self . description else None
2017-01-22 21:25:24 +03:00
varnames = func . __code__ . co_varnames [ 1 : func . __code__ . co_argcount ]
self . defaults = func . __defaults__
2015-06-01 06:10:06 +02:00
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 = [ ]
def command ( s ) :
def decorator ( func ) :
global known_commands
name = func . __name__
known_commands [ name ] = Command ( func , s )
@wraps ( func )
2015-09-11 11:54:00 +02:00
def func_wrapper ( * args , * * kwargs ) :
2017-08-17 11:06:53 +02:00
c = known_commands [ func . __name__ ]
2017-10-07 08:19:14 +02:00
wallet = args [ 0 ] . wallet
password = kwargs . get ( ' password ' )
if c . requires_wallet and wallet is None :
2018-04-07 17:10:30 +02:00
raise Exception ( " wallet not loaded. Use ' electrum daemon load_wallet ' " )
2017-12-07 11:35:10 +01:00
if c . requires_password and password is None and wallet . has_password ( ) :
2017-10-07 08:19:14 +02:00
return { ' error ' : ' Password required ' }
2015-09-11 11:54:00 +02:00
return func ( * args , * * kwargs )
2015-06-01 06:10:06 +02:00
return func_wrapper
return decorator
2013-02-26 13:56:48 +01:00
class Commands :
2017-07-02 11:44:48 +02:00
def __init__ ( self , config , wallet , network , callback = None ) :
2015-05-30 12:35:58 +02:00
self . config = config
2013-02-26 13:56:48 +01:00
self . wallet = wallet
2013-09-15 11:19:48 +02:00
self . network = network
2013-02-27 12:40:16 +01:00
self . _callback = callback
2013-02-26 13:56:48 +01:00
def _run ( self , method , args , password_getter ) :
2017-07-02 11:44:48 +02:00
# this wrapper is called from the python console
2013-10-03 12:39:42 +02:00
cmd = known_commands [ method ]
2016-11-08 16:39:04 +01:00
if cmd . requires_password and self . wallet . has_password ( ) :
2017-08-01 05:22:18 +02:00
password = password_getter ( )
2017-07-02 11:44:48 +02:00
if password is None :
2016-07-01 16:19:26 +02:00
return
2017-08-03 21:32:25 +03:00
else :
password = None
2013-11-11 22:03:20 -08:00
f = getattr ( self , method )
2017-07-28 08:04:32 -04:00
if cmd . requires_password :
result = f ( * args , * * { ' password ' : password } )
else :
result = f ( * args )
2017-08-01 05:22:18 +02:00
2013-02-27 12:40:16 +01:00
if self . _callback :
2017-08-01 05:22:18 +02:00
self . _callback ( )
2013-02-26 17:57:48 +01:00
return result
2013-02-26 13:56:48 +01:00
2015-06-01 06:10:06 +02:00
@command ( ' ' )
2015-08-06 15:52:38 +02:00
def commands ( self ) :
""" List of commands """
return ' ' . join ( sorted ( known_commands . keys ( ) ) )
2015-05-30 19:13:28 +02:00
2015-06-01 06:10:06 +02:00
@command ( ' ' )
2017-10-17 11:45:55 +02:00
def create ( self , segwit = False ) :
2015-05-31 23:17:44 +02:00
""" Create a new wallet """
2018-04-07 17:10:30 +02:00
raise Exception ( ' Not a JSON-RPC command ' )
2015-05-31 23:17:44 +02:00
2015-10-28 10:36:44 +01:00
@command ( ' wn ' )
2015-10-28 09:33:35 +01:00
def restore ( self , text ) :
""" 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 your
seed , type ' ? ' or ' : ' ( concealed ) """
2018-04-07 17:10:30 +02:00
raise Exception ( ' Not a JSON-RPC command ' )
2015-05-31 23:17:44 +02:00
2015-06-01 06:10:06 +02:00
@command ( ' wp ' )
2017-07-02 11:44:48 +02:00
def password ( self , password = None , new_password = None ) :
2015-05-31 23:17:44 +02:00
""" Change wallet password. """
2018-02-10 19:18:48 +01:00
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. " )
2017-10-15 09:18:14 +02:00
b = self . wallet . storage . is_encrypted ( )
self . wallet . update_password ( password , new_password , b )
2015-12-23 10:54:31 +01:00
self . wallet . storage . write ( )
2017-07-02 11:44:48 +02:00
return { ' password ' : self . wallet . has_password ( ) }
2015-05-31 22:42:34 +02:00
2015-06-01 06:10:06 +02:00
@command ( ' ' )
2015-05-31 22:42:34 +02:00
def getconfig ( self , key ) :
2015-05-31 23:17:44 +02:00
""" Return a configuration variable. """
2015-05-31 22:42:34 +02:00
return self . config . get ( key )
2018-04-06 18:53:13 +02:00
@classmethod
def _setconfig_normalize_value ( cls , key , value ) :
if key not in ( ' rpcuser ' , ' rpcpassword ' ) :
value = json_decode ( value )
try :
value = ast . literal_eval ( value )
except :
pass
return value
2015-06-01 06:10:06 +02:00
@command ( ' ' )
2015-05-31 22:42:34 +02:00
def setconfig ( self , key , value ) :
2015-06-03 10:02:12 +02:00
""" Set a configuration variable. ' value ' may be a string or a Python expression. """
2018-04-06 18:53:13 +02:00
value = self . _setconfig_normalize_value ( key , value )
2015-06-02 11:05:21 +02:00
self . config . set_key ( key , value )
2015-05-31 22:42:34 +02:00
return True
2015-06-01 06:10:06 +02:00
@command ( ' ' )
2018-03-14 14:59:27 +01:00
def make_seed ( self , nbits = 132 , language = None , segwit = False ) :
2015-05-31 23:17:44 +02:00
""" Create a seed """
2017-08-27 09:53:22 +02:00
from . mnemonic import Mnemonic
2017-09-17 16:34:22 +02:00
t = ' segwit ' if segwit else ' standard '
2018-03-14 14:59:27 +01:00
s = Mnemonic ( language ) . make_seed ( t , nbits )
2017-08-27 09:53:22 +02:00
return s
2014-08-28 15:37:42 +02:00
2015-06-01 06:10:06 +02:00
@command ( ' n ' )
2015-05-31 22:42:34 +02:00
def getaddresshistory ( self , address ) :
2015-06-03 09:12:38 +02:00
""" Return the transaction history of any address. Note: This is a
walletless server query , results are not checked by SPV .
"""
2018-02-02 23:26:25 +01:00
sh = bitcoin . address_to_scripthash ( address )
2018-06-06 15:06:04 +02:00
return self . network . get_history_for_scripthash ( sh )
2014-01-23 17:06:47 +01:00
2015-12-23 15:59:32 +01:00
@command ( ' w ' )
2013-02-26 13:56:48 +01:00
def listunspent ( self ) :
2015-06-03 09:12:38 +02:00
""" List unspent outputs. Returns the list of unspent transaction
outputs in your wallet . """
2017-01-14 23:39:58 +01:00
l = copy . deepcopy ( self . wallet . get_utxos ( exclude_frozen = False ) )
2015-08-19 11:10:55 +02:00
for i in l :
v = i [ " value " ]
2017-10-20 21:35:18 +03:00
i [ " value " ] = str ( Decimal ( v ) / COIN ) if v is not None else None
2013-02-26 17:57:48 +01:00
return l
2013-02-26 13:56:48 +01:00
2015-06-01 06:10:06 +02:00
@command ( ' n ' )
2015-05-31 22:42:34 +02:00
def getaddressunspent ( self , address ) :
2015-06-10 23:48:36 +02:00
""" Returns the UTXO list of any address. Note: This
2015-06-03 09:12:38 +02:00
is a walletless server query , results are not checked by SPV .
"""
2018-02-02 23:26:25 +01:00
sh = bitcoin . address_to_scripthash ( address )
2018-06-06 15:06:04 +02:00
return self . network . listunspent_for_scripthash ( sh )
2014-01-23 17:06:47 +01:00
2016-05-16 14:39:01 +02:00
@command ( ' ' )
2016-05-18 14:33:00 +02:00
def serialize ( self , jsontx ) :
2017-03-04 11:14:44 +01:00
""" Create a transaction from json inputs.
Inputs must have a redeemPubkey .
Outputs must be a list of { ' address ' : address , ' value ' : satoshi_amount } .
2016-05-16 14:39:01 +02:00
"""
keypairs = { }
2016-05-18 14:33:00 +02:00
inputs = jsontx . get ( ' inputs ' )
outputs = jsontx . get ( ' outputs ' )
2018-03-16 12:06:33 +08:00
locktime = jsontx . get ( ' lockTime ' , 0 )
2016-05-16 14:39:01 +02:00
for txin in inputs :
if txin . get ( ' output ' ) :
prevout_hash , prevout_n = txin [ ' output ' ] . split ( ' : ' )
txin [ ' prevout_n ' ] = int ( prevout_n )
txin [ ' prevout_hash ' ] = prevout_hash
2017-10-09 11:53:47 +02:00
sec = txin . get ( ' privkey ' )
if sec :
txin_type , privkey , compressed = bitcoin . deserialize_privkey ( sec )
2018-05-24 18:57:13 +02:00
pubkey = ecc . ECPrivkey ( privkey ) . get_public_key_hex ( compressed = compressed )
2017-10-09 11:53:47 +02:00
keypairs [ pubkey ] = privkey , compressed
txin [ ' type ' ] = txin_type
2016-05-16 14:39:01 +02:00
txin [ ' x_pubkeys ' ] = [ pubkey ]
txin [ ' signatures ' ] = [ None ]
txin [ ' num_sig ' ] = 1
2017-03-15 12:13:20 +01:00
outputs = [ ( TYPE_ADDRESS , x [ ' address ' ] , int ( x [ ' value ' ] ) ) for x in outputs ]
2016-05-18 14:33:00 +02:00
tx = Transaction . from_io ( inputs , outputs , locktime = locktime )
2016-05-16 14:39:01 +02:00
tx . sign ( keypairs )
2015-10-29 14:36:50 +01:00
return tx . as_dict ( )
2013-02-26 13:56:48 +01:00
2015-06-01 06:10:06 +02:00
@command ( ' wp ' )
2017-07-02 11:44:48 +02:00
def signtransaction ( self , tx , privkey = None , password = None ) :
2015-05-31 23:17:44 +02:00
""" Sign a transaction. The wallet keys will be used unless a private key is provided. """
2016-03-20 19:05:38 +01:00
tx = Transaction ( tx )
2015-05-31 09:28:47 +02:00
if privkey :
2017-10-09 11:53:47 +02:00
txin_type , privkey2 , compressed = bitcoin . deserialize_privkey ( privkey )
2018-05-24 18:57:13 +02:00
pubkey_bytes = ecc . ECPrivkey ( privkey2 ) . get_public_key_bytes ( compressed = compressed )
h160 = bitcoin . hash_160 ( pubkey_bytes )
2017-01-30 12:36:56 +03:00
x_pubkey = ' fd ' + bh2u ( b ' \x00 ' + h160 )
2017-10-09 11:53:47 +02:00
tx . sign ( { x_pubkey : ( privkey2 , compressed ) } )
2015-05-31 09:28:47 +02:00
else :
2017-07-02 11:44:48 +02:00
self . wallet . sign_transaction ( tx , password )
2016-03-16 10:31:33 +01:00
return tx . as_dict ( )
2013-02-26 13:56:48 +01:00
2015-06-01 06:10:06 +02:00
@command ( ' ' )
2015-06-10 23:21:25 +02:00
def deserialize ( self , tx ) :
""" Deserialize a serialized transaction """
2016-03-20 19:05:38 +01:00
tx = Transaction ( tx )
2016-03-16 10:31:33 +01:00
return tx . deserialize ( )
2013-02-26 13:56:48 +01:00
2015-06-01 06:10:06 +02:00
@command ( ' n ' )
2018-06-06 15:06:04 +02:00
def broadcast ( self , tx ) :
2015-05-31 23:17:44 +02:00
""" Broadcast a transaction to the network. """
2016-03-20 19:05:38 +01:00
tx = Transaction ( tx )
2018-06-06 15:06:04 +02:00
return self . network . broadcast_transaction ( tx )
2013-02-26 13:56:48 +01:00
2015-06-01 06:10:06 +02:00
@command ( ' ' )
2013-02-26 13:56:48 +01:00
def createmultisig ( self , num , pubkeys ) :
2015-05-31 23:17:44 +02:00
""" Create multisig address """
2015-05-30 18:49:58 +02:00
assert isinstance ( pubkeys , list ) , ( type ( num ) , type ( pubkeys ) )
2017-12-06 18:30:02 +01:00
redeem_script = multisig_script ( pubkeys , num )
2017-01-30 12:36:56 +03:00
address = bitcoin . hash160_to_p2sh ( hash_160 ( bfh ( redeem_script ) ) )
2013-08-17 09:53:46 +02:00
return { ' address ' : address , ' redeemScript ' : redeem_script }
2014-06-24 16:12:43 +03:00
2015-06-01 06:10:06 +02:00
@command ( ' w ' )
2015-05-31 22:42:34 +02:00
def freeze ( self , address ) :
2015-05-31 23:17:44 +02:00
""" Freeze address. Freeze the funds at one of your wallet \' s addresses """
2015-05-31 22:42:34 +02:00
return self . wallet . set_frozen_state ( [ address ] , True )
2014-06-24 16:12:43 +03:00
2015-06-01 06:10:06 +02:00
@command ( ' w ' )
2015-05-31 22:42:34 +02:00
def unfreeze ( self , address ) :
2015-05-31 23:17:44 +02:00
""" Unfreeze address. Unfreeze the funds at one of your wallet \' s address """
2015-05-31 22:42:34 +02:00
return self . wallet . set_frozen_state ( [ address ] , False )
2013-02-26 13:56:48 +01:00
2015-06-01 06:10:06 +02:00
@command ( ' wp ' )
2017-07-02 11:44:48 +02:00
def getprivatekeys ( self , address , password = None ) :
2015-08-16 16:30:55 +02:00
""" Get private keys of addresses. You may pass a single wallet address, or a list of wallet addresses. """
2017-11-19 23:46:59 +01:00
if isinstance ( address , str ) :
address = address . strip ( )
2016-06-11 16:55:19 +02:00
if is_address ( address ) :
2017-10-04 23:22:33 +02:00
return self . wallet . export_private_key ( address , password ) [ 0 ]
2017-03-15 06:12:26 +01:00
domain = address
2017-10-04 23:22:33 +02:00
return [ self . wallet . export_private_key ( address , password ) [ 0 ] for address in domain ]
2013-02-26 16:03:04 +01:00
2015-06-01 06:10:06 +02:00
@command ( ' w ' )
2015-05-31 22:42:34 +02:00
def ismine ( self , address ) :
2015-05-31 23:17:44 +02:00
""" Check if address is in wallet. Return true if and only address is in wallet """
2015-05-31 22:42:34 +02:00
return self . wallet . is_mine ( address )
2015-01-11 20:37:08 +01:00
2015-08-16 16:30:55 +02:00
@command ( ' ' )
2015-08-16 16:11:52 +02:00
def dumpprivkeys ( self ) :
2015-08-16 16:30:55 +02:00
""" Deprecated. """
return " This command is deprecated. Use a pipe instead: ' electrum listaddresses | electrum getprivatekeys - ' "
2013-02-26 13:56:48 +01:00
2015-06-01 06:10:06 +02:00
@command ( ' ' )
2015-05-31 22:42:34 +02:00
def validateaddress ( self , address ) :
2015-06-12 10:34:45 +02:00
""" Check that an address is valid. """
2015-05-31 22:42:34 +02:00
return is_address ( address )
2013-10-03 13:31:59 +02:00
2015-06-01 06:10:06 +02:00
@command ( ' w ' )
2015-05-31 22:42:34 +02:00
def getpubkeys ( self , address ) :
2015-05-31 23:17:44 +02:00
""" Return the public keys for a wallet address. """
2015-05-31 22:42:34 +02:00
return self . wallet . get_public_keys ( address )
2013-02-26 13:56:48 +01:00
2015-12-23 15:59:32 +01:00
@command ( ' w ' )
2016-07-02 08:58:56 +02:00
def getbalance ( self ) :
2015-12-23 15:59:32 +01:00
""" Return the balance of your wallet. """
2016-07-02 08:58:56 +02:00
c , u , x = self . wallet . get_balance ( )
2015-06-01 11:26:22 +09:00
out = { " confirmed " : str ( Decimal ( c ) / COIN ) }
2015-05-05 20:52:14 +02:00
if u :
2015-06-01 11:26:22 +09:00
out [ " unconfirmed " ] = str ( Decimal ( u ) / COIN )
2015-05-05 20:52:14 +02:00
if x :
2015-06-01 11:26:22 +09:00
out [ " unmatured " ] = str ( Decimal ( x ) / COIN )
2013-04-16 16:05:45 +02:00
return out
2013-02-27 10:24:53 +01:00
2015-06-01 06:10:06 +02:00
@command ( ' n ' )
2015-05-31 22:42:34 +02:00
def getaddressbalance ( self , address ) :
2015-06-03 09:12:38 +02:00
""" Return the balance of any address. Note: This is a walletless
server query , results are not checked by SPV .
"""
2018-02-02 23:26:25 +01:00
sh = bitcoin . address_to_scripthash ( address )
2018-06-06 15:06:04 +02:00
out = self . network . get_balance_for_scripthash ( sh )
2015-06-01 11:26:22 +09:00
out [ " confirmed " ] = str ( Decimal ( out [ " confirmed " ] ) / COIN )
out [ " unconfirmed " ] = str ( Decimal ( out [ " unconfirmed " ] ) / COIN )
2014-03-02 10:31:34 +01:00
return out
2015-06-01 06:10:06 +02:00
@command ( ' n ' )
2015-05-12 12:30:26 +02:00
def getmerkle ( self , txid , height ) :
2015-06-12 10:34:45 +02:00
""" Get Merkle branch of a transaction included in a block. Electrum
uses this to verify transactions ( Simple Payment Verification ) . """
2018-06-06 15:06:04 +02:00
return self . network . get_merkle_for_transaction ( txid , int ( height ) )
2015-05-12 12:30:26 +02:00
2015-06-01 06:10:06 +02:00
@command ( ' n ' )
2013-10-02 13:50:36 +02:00
def getservers ( self ) :
2015-05-31 23:17:44 +02:00
""" Return the list of available servers """
2013-10-02 13:50:36 +02:00
return self . network . get_servers ( )
2013-02-26 13:56:48 +01:00
2015-06-01 06:10:06 +02:00
@command ( ' ' )
2015-05-28 15:22:30 +02:00
def version ( self ) :
2018-04-15 20:45:30 +03:00
""" Return the version of Electrum. """
2017-10-05 17:10:30 +02:00
from . version import ELECTRUM_VERSION
2016-10-11 12:08:56 +02:00
return ELECTRUM_VERSION
2014-06-24 16:12:43 +03:00
2015-06-01 06:10:06 +02:00
@command ( ' w ' )
2013-11-03 14:14:35 +01:00
def getmpk ( self ) :
2016-06-11 16:10:45 +02:00
""" Get master public key. Return your wallet \' s master public key """
return self . wallet . get_master_public_key ( )
2013-11-03 14:14:35 +01:00
2015-08-14 15:23:50 +02:00
@command ( ' wp ' )
2017-07-02 11:44:48 +02:00
def getmasterprivate ( self , password = None ) :
2015-08-14 15:23:50 +02:00
""" Get master private key. Return your wallet \' s master private key """
2017-07-02 11:44:48 +02:00
return str ( self . wallet . keystore . get_master_private_key ( password ) )
2015-08-14 15:23:50 +02:00
2015-06-01 06:10:06 +02:00
@command ( ' wp ' )
2017-07-02 11:44:48 +02:00
def getseed ( self , password = None ) :
2015-05-31 23:17:44 +02:00
""" Get seed phrase. Print the generation seed of your wallet. """
2017-07-02 11:44:48 +02:00
s = self . wallet . get_seed ( password )
2017-08-27 09:53:22 +02:00
return s
2013-02-26 13:56:48 +01:00
2015-06-01 06:10:06 +02:00
@command ( ' wp ' )
2017-07-02 11:44:48 +02:00
def importprivkey ( self , privkey , password = None ) :
2017-09-25 21:35:14 +02:00
""" Import a private key. """
2017-06-30 12:20:19 +02:00
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. "
2013-02-26 13:56:48 +01:00
try :
2017-09-25 21:35:14 +02:00
addr = self . wallet . import_private_key ( privkey , password )
2015-06-15 10:52:03 +02:00
out = " Keypair imported: " + addr
2016-08-17 09:49:58 +02:00
except BaseException as e :
2015-06-15 10:52:03 +02:00
out = " Error: " + str ( e )
2013-02-27 10:24:53 +01:00
return out
2013-02-26 13:56:48 +01:00
2015-06-11 12:08:38 +02:00
def _resolver ( self , x ) :
if x is None :
return None
2017-03-06 17:12:27 +01:00
out = self . wallet . contacts . resolve ( x )
2015-06-11 12:08:38 +02:00
if out . get ( ' type ' ) == ' openalias ' and self . nocheck is False and out . get ( ' validated ' ) is False :
2018-04-07 17:10:30 +02:00
raise Exception ( ' cannot verify alias ' , x )
2015-06-11 12:08:38 +02:00
return out [ ' address ' ]
2017-11-29 13:45:02 +01:00
@command ( ' n ' )
2017-10-07 09:48:20 +02:00
def sweep ( self , privkey , destination , fee = None , nocheck = False , imax = 100 ) :
2015-08-16 16:11:52 +02:00
""" Sweep private keys. Returns a transaction that spends UTXOs from
2015-05-31 23:23:13 +02:00
privkey to a destination address . The transaction is not
broadcasted . """
2017-11-29 13:45:02 +01:00
from . wallet import sweep
2017-10-07 09:48:20 +02:00
tx_fee = satoshis ( fee )
2017-10-05 20:08:16 +02:00
privkeys = privkey . split ( )
2015-06-11 12:08:38 +02:00
self . nocheck = nocheck
2017-11-29 13:45:02 +01:00
#dest = self._resolver(destination)
tx = sweep ( privkeys , self . network , self . config , destination , tx_fee , imax )
2016-10-08 11:40:03 +02:00
return tx . as_dict ( ) if tx else None
2014-04-25 17:23:26 +02:00
2015-06-01 06:10:06 +02:00
@command ( ' wp ' )
2017-07-02 11:44:48 +02:00
def signmessage ( self , address , message , password = None ) :
2015-05-31 23:23:13 +02:00
""" Sign a message with a key. Use quotes if your message contains
whitespaces """
2017-07-02 11:44:48 +02:00
sig = self . wallet . sign_message ( address , message , password )
2017-10-17 20:15:33 +02:00
return base64 . b64encode ( sig ) . decode ( ' ascii ' )
2013-02-26 13:56:48 +01:00
2015-06-01 06:10:06 +02:00
@command ( ' ' )
2013-03-01 11:25:50 +01:00
def verifymessage ( self , address , signature , message ) :
2015-05-31 23:17:44 +02:00
""" Verify a signature. """
2015-07-14 16:37:04 +02:00
sig = base64 . b64decode ( signature )
2017-12-04 17:36:57 +01:00
message = util . to_bytes ( message )
2018-05-24 18:57:13 +02:00
return ecc . verify_message_with_address ( address , sig , message )
2013-02-26 13:56:48 +01:00
2017-07-31 09:25:10 +07:00
def _mktx ( self , outputs , fee , change_addr , domain , nocheck , unsigned , rbf , password , locktime = None ) :
2015-06-11 12:08:38 +02:00
self . nocheck = nocheck
change_addr = self . _resolver ( change_addr )
domain = None if domain is None else map ( self . _resolver , domain )
2013-04-08 23:36:26 +01:00
final_outputs = [ ]
2015-05-31 14:10:52 +02:00
for address , amount in outputs :
2015-06-11 12:08:38 +02:00
address = self . _resolver ( address )
2017-02-22 11:23:12 +01:00
amount = satoshis ( amount )
2016-01-14 17:15:50 +01:00
final_outputs . append ( ( TYPE_ADDRESS , address , amount ) )
2014-06-24 16:12:43 +03:00
2017-07-01 22:20:10 +02:00
coins = self . wallet . get_spendable_coins ( domain , self . config )
2015-08-04 07:15:54 +02:00
tx = self . wallet . make_unsigned_transaction ( coins , final_outputs , self . config , fee , change_addr )
2017-07-31 09:25:10 +07:00
if locktime != None :
tx . locktime = locktime
2018-04-02 05:30:58 +02:00
if rbf is None :
rbf = self . config . get ( ' use_rbf ' , True )
2016-07-29 12:54:47 +02:00
if rbf :
2017-03-13 11:02:25 +01:00
tx . set_rbf ( True )
2015-05-31 17:38:57 +02:00
if not unsigned :
2017-07-02 11:44:48 +02:00
self . wallet . sign_transaction ( tx , password )
2015-06-10 23:21:25 +02:00
return tx
2013-02-26 15:13:01 +01:00
2015-12-23 15:59:32 +01:00
@command ( ' wp ' )
2018-04-02 05:30:58 +02:00
def payto ( self , destination , amount , fee = None , from_addr = None , change_addr = None , nocheck = False , unsigned = False , rbf = None , password = None , locktime = None ) :
2015-05-31 23:17:44 +02:00
""" Create a transaction. """
2017-10-07 09:48:20 +02:00
tx_fee = satoshis ( fee )
2017-10-07 11:54:28 +02:00
domain = from_addr . split ( ' , ' ) if from_addr else None
2017-07-31 09:25:10 +07:00
tx = self . _mktx ( [ ( destination , amount ) ] , tx_fee , change_addr , domain , nocheck , unsigned , rbf , password , locktime )
2015-10-29 14:44:41 +01:00
return tx . as_dict ( )
2015-05-30 18:49:58 +02:00
2015-12-23 15:59:32 +01:00
@command ( ' wp ' )
2018-04-02 05:30:58 +02:00
def paytomany ( self , outputs , fee = None , from_addr = None , change_addr = None , nocheck = False , unsigned = False , rbf = None , password = None , locktime = None ) :
2015-05-31 23:17:44 +02:00
""" Create a multi-output transaction. """
2017-10-07 09:48:20 +02:00
tx_fee = satoshis ( fee )
2017-10-07 11:54:28 +02:00
domain = from_addr . split ( ' , ' ) if from_addr else None
2017-07-31 09:25:10 +07:00
tx = self . _mktx ( outputs , tx_fee , change_addr , domain , nocheck , unsigned , rbf , password , locktime )
2015-10-29 14:44:41 +01:00
return tx . as_dict ( )
2013-04-08 23:40:51 +01:00
2015-12-23 15:59:32 +01:00
@command ( ' w ' )
2018-02-09 15:28:28 +01:00
def history ( self , year = None , show_addresses = False , show_fiat = False ) :
2015-05-31 23:17:44 +02:00
""" Wallet history. Returns the transaction history of your wallet. """
2018-02-09 15:28:28 +01:00
kwargs = { ' show_addresses ' : show_addresses }
if year :
import time
start_date = datetime . datetime ( year , 1 , 1 )
end_date = datetime . datetime ( year + 1 , 1 , 1 )
kwargs [ ' from_timestamp ' ] = time . mktime ( start_date . timetuple ( ) )
kwargs [ ' to_timestamp ' ] = time . mktime ( end_date . timetuple ( ) )
2018-02-14 10:40:11 +01:00
if show_fiat :
from . exchange_rate import FxThread
fx = FxThread ( self . config , None )
kwargs [ ' fx ' ] = fx
2018-04-05 12:22:01 +02:00
return json_encode ( self . wallet . get_full_history ( * * kwargs ) )
2013-02-26 18:10:29 +01:00
2015-06-01 06:10:06 +02:00
@command ( ' w ' )
2013-09-08 20:10:43 +02:00
def setlabel ( self , key , label ) :
2015-05-31 23:23:13 +02:00
""" Assign a label to an item. Item may be a bitcoin address or a
transaction ID """
2013-09-08 20:12:03 +02:00
self . wallet . set_label ( key , label )
2013-09-08 20:10:43 +02:00
2017-03-06 17:12:27 +01:00
@command ( ' w ' )
2015-05-30 12:35:58 +02:00
def listcontacts ( self ) :
2015-05-31 23:17:44 +02:00
""" Show your list of contacts """
2017-03-06 17:12:27 +01:00
return self . wallet . contacts
2013-02-26 13:56:48 +01:00
2017-03-06 17:12:27 +01:00
@command ( ' w ' )
2015-06-11 12:08:38 +02:00
def getalias ( self , key ) :
2015-05-31 23:17:44 +02:00
""" Retrieve alias. Lookup in your list of contacts, and for an OpenAlias DNS record. """
2017-03-06 17:12:27 +01:00
return self . wallet . contacts . resolve ( key )
2015-05-31 15:06:52 +02:00
2017-03-06 17:12:27 +01:00
@command ( ' w ' )
2014-11-05 18:02:44 -05:00
def searchcontacts ( self , query ) :
2015-05-31 23:17:44 +02:00
""" Search through contacts, return matching entries. """
2014-11-05 18:02:44 -05:00
results = { }
2017-03-06 17:12:27 +01:00
for key , value in self . wallet . contacts . items ( ) :
2015-05-30 12:35:58 +02:00
if query . lower ( ) in key . lower ( ) :
results [ key ] = value
2014-11-05 18:02:44 -05:00
return results
2015-06-01 06:10:06 +02:00
@command ( ' w ' )
2017-10-07 09:48:20 +02:00
def listaddresses ( self , receiving = False , change = False , labels = False , frozen = False , unused = False , funded = False , balance = False ) :
2015-09-11 13:07:49 +02:00
""" List wallet addresses. Returns the list of all addresses in your wallet. Use optional arguments to filter the results. """
2013-02-26 18:10:29 +01:00
out = [ ]
2016-07-02 08:58:56 +02:00
for addr in self . wallet . get_addresses ( ) :
2015-05-30 18:49:58 +02:00
if frozen and not self . wallet . is_frozen ( addr ) :
continue
2015-06-11 12:49:14 +02:00
if receiving and self . wallet . is_change ( addr ) :
continue
if change and not self . wallet . is_change ( addr ) :
2015-05-30 18:49:58 +02:00
continue
if unused and self . wallet . is_used ( addr ) :
continue
if funded and self . wallet . is_empty ( addr ) :
continue
item = addr
2017-10-07 09:48:20 +02:00
if labels or balance :
item = ( item , )
if balance :
item + = ( format_satoshis ( sum ( self . wallet . get_addr_balance ( addr ) ) ) , )
if labels :
item + = ( repr ( self . wallet . labels . get ( addr , ' ' ) ) , )
2015-05-30 18:49:58 +02:00
out . append ( item )
2013-02-26 18:10:29 +01:00
return out
2014-06-24 16:12:43 +03:00
2017-03-12 12:33:52 +01:00
@command ( ' n ' )
2015-10-29 14:44:41 +01:00
def gettransaction ( self , txid ) :
2015-05-31 23:17:44 +02:00
""" Retrieve a transaction. """
2017-03-12 12:33:52 +01:00
if self . wallet and txid in self . wallet . transactions :
tx = self . wallet . transactions [ txid ]
else :
2018-06-06 15:06:04 +02:00
raw = self . network . get_transaction ( txid )
2015-05-31 08:20:09 +02:00
if raw :
tx = Transaction ( raw )
else :
2018-04-07 17:10:30 +02:00
raise Exception ( " Unknown transaction " )
2015-10-29 14:44:41 +01:00
return tx . as_dict ( )
2014-01-23 17:06:47 +01:00
2015-06-01 06:10:06 +02:00
@command ( ' ' )
2014-01-30 14:43:46 +01:00
def encrypt ( self , pubkey , message ) :
2015-05-31 23:17:44 +02:00
""" Encrypt a message with a public key. Use quotes if the message contains whitespaces. """
2018-05-24 18:57:13 +02:00
public_key = ecc . ECPubkey ( bfh ( pubkey ) )
encrypted = public_key . encrypt_message ( message )
return encrypted
2014-03-03 10:39:10 +01:00
2015-06-01 06:10:06 +02:00
@command ( ' wp ' )
2017-07-02 11:44:48 +02:00
def decrypt ( self , pubkey , encrypted , password = None ) :
2015-05-31 23:17:44 +02:00
""" Decrypt a message encrypted with a public key. """
2017-07-02 11:44:48 +02:00
return self . wallet . decrypt_message ( pubkey , encrypted , password )
2015-05-31 22:42:34 +02:00
2015-06-08 12:51:45 +02:00
def _format_request ( self , out ) :
2015-06-02 11:36:06 +02:00
pr_str = {
PR_UNKNOWN : ' Unknown ' ,
PR_UNPAID : ' Pending ' ,
PR_PAID : ' Paid ' ,
PR_EXPIRED : ' Expired ' ,
}
2015-10-29 00:09:50 +01:00
out [ ' amount (BTC) ' ] = format_satoshis ( out . get ( ' amount ' ) )
2015-06-08 12:51:45 +02:00
out [ ' status ' ] = pr_str [ out . get ( ' status ' , PR_UNKNOWN ) ]
2015-06-02 10:25:39 +02:00
return out
2015-12-23 15:59:32 +01:00
@command ( ' w ' )
2015-06-07 18:44:33 +02:00
def getrequest ( self , key ) :
""" Return a payment request """
2015-06-08 12:51:45 +02:00
r = self . wallet . get_payment_request ( key , self . config )
2015-06-07 18:44:33 +02:00
if not r :
2018-04-07 17:10:30 +02:00
raise Exception ( " Request not found " )
2015-06-07 18:44:33 +02:00
return self . _format_request ( r )
2015-06-11 10:50:25 +02:00
#@command('w')
#def ackrequest(self, serialized):
# """<Not implemented>"""
# pass
2015-06-07 18:44:33 +02:00
2015-12-23 15:59:32 +01:00
@command ( ' w ' )
2015-06-12 09:46:21 +02:00
def listrequests ( self , pending = False , expired = False , paid = False ) :
2015-06-10 23:48:36 +02:00
""" List the payment requests you made. """
2015-06-12 09:46:21 +02:00
out = self . wallet . get_sorted_requests ( self . config )
if pending :
f = PR_UNPAID
elif expired :
f = PR_EXPIRED
elif paid :
f = PR_PAID
else :
f = None
2015-06-12 20:15:53 +02:00
if f is not None :
2017-02-04 20:59:22 +03:00
out = list ( filter ( lambda x : x . get ( ' status ' ) == f , out ) )
return list ( map ( self . _format_request , out ) )
2015-05-31 22:42:34 +02:00
2017-01-21 08:04:37 +01:00
@command ( ' w ' )
2017-10-11 12:07:41 +02:00
def createnewaddress ( self ) :
""" Create a new receiving address, beyond the gap limit of the wallet """
return self . wallet . create_new_address ( False )
2017-01-21 08:04:37 +01:00
2017-10-11 12:07:41 +02:00
@command ( ' w ' )
def getunusedaddress ( self ) :
""" 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 self . wallet . get_unused_address ( )
2017-01-21 08:04:37 +01:00
2015-06-01 13:02:09 +02:00
@command ( ' w ' )
2016-02-19 13:58:05 +01:00
def addrequest ( self , amount , memo = ' ' , expiration = None , force = False ) :
2017-10-11 12:07:41 +02:00
""" Create a payment request, using the first unused address of the wallet.
2018-04-15 20:45:30 +03:00
The address will be considered as used after this operation .
2017-10-11 12:07:41 +02:00
If no payment is received , the address will be considered as unused if the payment request is deleted from the wallet . """
2016-08-16 12:41:15 +02:00
addr = self . wallet . get_unused_address ( )
2015-06-08 12:51:45 +02:00
if addr is None :
2015-06-11 20:44:38 +02:00
if force :
2016-08-16 12:41:15 +02:00
addr = self . wallet . create_new_address ( False )
2015-06-11 20:44:38 +02:00
else :
return False
2017-02-22 11:23:12 +01:00
amount = satoshis ( amount )
2016-02-19 13:58:05 +01:00
expiration = int ( expiration ) if expiration else None
2015-07-11 21:03:02 +02:00
req = self . wallet . make_payment_request ( addr , amount , memo , expiration )
self . wallet . add_payment_request ( req , self . config )
2015-10-29 00:09:50 +01:00
out = self . wallet . get_payment_request ( addr , self . config )
return self . _format_request ( out )
2015-06-01 13:02:09 +02:00
2018-01-18 11:56:21 +01:00
@command ( ' w ' )
def addtransaction ( self , tx ) :
""" Add a transaction to the wallet history """
tx = Transaction ( tx )
2018-02-06 05:39:26 +01:00
if not self . wallet . add_transaction ( tx . txid ( ) , tx ) :
return False
2018-01-18 11:56:21 +01:00
self . wallet . save_transactions ( )
return tx . txid ( )
2015-07-22 09:06:03 +02:00
@command ( ' wp ' )
2017-07-02 11:44:48 +02:00
def signrequest ( self , address , password = None ) :
2015-07-22 09:06:03 +02:00
" Sign payment request with an OpenAlias "
alias = self . config . get ( ' alias ' )
2015-08-07 09:00:00 +02:00
if not alias :
2018-04-07 17:10:30 +02:00
raise Exception ( ' No alias in your configuration ' )
2017-03-06 17:12:27 +01:00
alias_addr = self . wallet . contacts . resolve ( alias ) [ ' address ' ]
2017-07-02 11:44:48 +02:00
self . wallet . sign_payment_request ( address , alias , alias_addr , password )
2015-07-22 09:06:03 +02:00
2015-06-01 13:02:09 +02:00
@command ( ' w ' )
2015-07-22 09:24:44 +02:00
def rmrequest ( self , address ) :
2015-06-01 13:02:09 +02:00
""" Remove a payment request """
2015-07-22 09:24:44 +02:00
return self . wallet . remove_payment_request ( address , self . config )
2015-05-31 22:42:34 +02:00
2015-07-22 15:46:53 +02:00
@command ( ' w ' )
def clearrequests ( self ) :
""" Remove all payment requests """
2017-11-09 11:54:25 -05:00
for k in list ( self . wallet . receive_requests . keys ( ) ) :
2015-07-22 15:46:53 +02:00
self . wallet . remove_payment_request ( k , self . config )
2015-11-30 10:54:15 +01:00
@command ( ' n ' )
2015-12-01 08:58:00 +01:00
def notify ( self , address , URL ) :
2018-04-15 20:45:30 +03:00
""" Watch an address. Every time the address changes, a http POST is sent to the URL. """
2015-11-30 10:54:15 +01:00
def callback ( x ) :
2017-11-12 16:15:06 -06:00
import urllib . request
2015-11-30 10:54:15 +01:00
headers = { ' content-type ' : ' application/json ' }
data = { ' address ' : address , ' status ' : x . get ( ' result ' ) }
2018-01-16 16:35:29 +01:00
serialized_data = util . to_bytes ( json . dumps ( data ) )
2015-11-30 10:54:15 +01:00
try :
2018-01-16 16:35:29 +01:00
req = urllib . request . Request ( URL , serialized_data , headers )
2017-11-12 16:15:06 -06:00
response_stream = urllib . request . urlopen ( req , timeout = 5 )
2015-11-30 10:54:15 +01:00
util . print_error ( ' Got Response for %s ' % address )
except BaseException as e :
util . print_error ( str ( e ) )
2018-05-18 10:56:01 +02:00
self . network . subscribe_to_addresses ( [ address ] , callback )
2015-12-01 08:58:00 +01:00
return True
2015-07-22 15:46:53 +02:00
2016-04-08 16:14:39 +02:00
@command ( ' wn ' )
2016-04-08 20:29:43 +02:00
def is_synchronized ( self ) :
""" return wallet synchronization status """
return self . wallet . is_up_to_date ( )
2016-04-08 16:14:39 +02:00
2018-01-18 07:07:42 +07:00
@command ( ' n ' )
2018-06-15 17:02:44 +02:00
def getfeerate ( self , fee_method = None , fee_level = None ) :
""" Return current suggested fee rate (in sat/kvByte), according to config
settings or supplied parameters .
"""
if fee_method is None :
dyn , mempool = None , None
elif fee_method . lower ( ) == ' static ' :
dyn , mempool = False , False
elif fee_method . lower ( ) == ' eta ' :
dyn , mempool = True , False
elif fee_method . lower ( ) == ' mempool ' :
dyn , mempool = True , True
else :
raise Exception ( ' Invalid fee estimation method: {} ' . format ( fee_method ) )
if fee_level is not None :
fee_level = Decimal ( fee_level )
return self . config . fee_per_kb ( dyn = dyn , mempool = mempool , fee_level = fee_level )
2018-01-18 07:07:42 +07:00
2016-02-29 09:57:56 +01:00
@command ( ' ' )
def help ( self ) :
# for the python console
return sorted ( known_commands . keys ( ) )
2015-05-31 22:42:34 +02:00
param_descriptions = {
' privkey ' : ' Private key. Type \' ? \' to get a prompt. ' ,
' destination ' : ' Bitcoin address, contact or alias ' ,
' address ' : ' Bitcoin address ' ,
' seed ' : ' Seed phrase ' ,
' txid ' : ' Transaction ID ' ,
' pos ' : ' Position ' ,
2015-06-11 04:55:08 -04:00
' height ' : ' Block height ' ,
2015-05-31 22:42:34 +02:00
' tx ' : ' Serialized transaction (hexadecimal) ' ,
' key ' : ' Variable name ' ,
' pubkey ' : ' Public key ' ,
' message ' : ' Clear text message. Use quotes if it contains spaces. ' ,
' encrypted ' : ' Encrypted message ' ,
' amount ' : ' Amount to be sent (in BTC). Type \' ! \' to send the maximum available. ' ,
2015-06-03 11:34:52 +02:00
' requested_amount ' : ' Requested amount (in BTC). ' ,
2015-09-09 08:47:30 +02:00
' outputs ' : ' list of [ " address " , amount] ' ,
2017-09-25 21:35:14 +02:00
' redeem_script ' : ' redeem script (hexadecimal) ' ,
2015-05-31 22:42:34 +02:00
}
command_options = {
2017-10-07 09:48:20 +02:00
' password ' : ( " -W " , " Password " ) ,
' new_password ' : ( None , " New Password " ) ,
' receiving ' : ( None , " Show only receiving addresses " ) ,
' change ' : ( None , " Show only change addresses " ) ,
' frozen ' : ( None , " Show only frozen addresses " ) ,
' unused ' : ( None , " Show only unused addresses " ) ,
' funded ' : ( None , " Show only funded addresses " ) ,
' balance ' : ( " -b " , " Show the balances of listed addresses " ) ,
' labels ' : ( " -l " , " Show the labels of listed addresses " ) ,
' nocheck ' : ( None , " Do not verify aliases " ) ,
' imax ' : ( None , " Maximum number of inputs " ) ,
' fee ' : ( " -f " , " Transaction fee (in BTC) " ) ,
2017-11-29 13:45:02 +01:00
' from_addr ' : ( " -F " , " Source address (must be a wallet address; use sweep to spend from non-wallet address). " ) ,
2017-10-07 09:48:20 +02:00
' change_addr ' : ( " -c " , " Change address. Default is a spare address, or the source address if it ' s not in the wallet " ) ,
' nbits ' : ( None , " Number of bits of entropy " ) ,
' segwit ' : ( None , " Create segwit seed " ) ,
' language ' : ( " -L " , " Default language for wordlist " ) ,
' privkey ' : ( None , " Private key. Set to ' ? ' to get a prompt. " ) ,
' unsigned ' : ( " -u " , " Do not sign transaction " ) ,
' rbf ' : ( None , " Replace-by-fee transaction " ) ,
' locktime ' : ( None , " Set locktime block number " ) ,
' domain ' : ( " -D " , " List of addresses " ) ,
' memo ' : ( " -m " , " Description of the request " ) ,
' expiration ' : ( None , " Time in seconds " ) ,
' timeout ' : ( None , " Timeout in seconds " ) ,
' force ' : ( None , " Create new address beyond gap limit, if no more addresses are available. " ) ,
' pending ' : ( None , " Show only pending requests. " ) ,
' expired ' : ( None , " Show only expired requests. " ) ,
' paid ' : ( None , " Show only paid requests. " ) ,
2018-02-09 15:28:28 +01:00
' show_addresses ' : ( None , " Show input and output addresses " ) ,
' show_fiat ' : ( None , " Show fiat value of transactions " ) ,
' year ' : ( None , " Show history for a given year " ) ,
2018-06-15 17:02:44 +02:00
' fee_method ' : ( None , " Fee estimation method to use " ) ,
' fee_level ' : ( None , " Float between 0.0 and 1.0, representing fee slider position " )
2015-05-31 22:42:34 +02:00
}
2015-12-15 11:33:04 +01:00
# don't use floats because of rounding errors
2017-01-22 21:25:24 +03:00
from . transaction import tx_from_str
2015-12-16 20:17:20 +01:00
json_loads = lambda x : json . loads ( x , parse_float = lambda x : str ( Decimal ( x ) ) )
2015-05-31 22:42:34 +02:00
arg_types = {
2015-12-16 20:17:20 +01:00
' num ' : int ,
' nbits ' : int ,
2016-10-08 11:40:03 +02:00
' imax ' : int ,
2018-02-09 15:28:28 +01:00
' year ' : int ,
2016-03-16 10:31:33 +01:00
' tx ' : tx_from_str ,
2015-12-16 20:17:20 +01:00
' pubkeys ' : json_loads ,
2016-05-18 14:33:00 +02:00
' jsontx ' : json_loads ,
2015-12-16 20:17:20 +01:00
' inputs ' : json_loads ,
' outputs ' : json_loads ,
2017-10-07 09:48:20 +02:00
' fee ' : lambda x : str ( Decimal ( x ) ) if x is not None else None ,
2017-02-22 11:23:12 +01:00
' amount ' : lambda x : str ( Decimal ( x ) ) if x != ' ! ' else ' ! ' ,
2017-07-31 09:25:10 +07:00
' locktime ' : int ,
2018-06-15 17:02:44 +02:00
' fee_method ' : str ,
' fee_level ' : json_loads ,
2015-05-31 22:42:34 +02:00
}
2015-06-03 11:34:52 +02:00
config_variables = {
' addrequest ' : {
' requests_dir ' : ' directory where a bip70 file will be written. ' ,
' ssl_privkey ' : ' Path to your SSL private key, needed to sign the request. ' ,
' ssl_chain ' : ' Chain of SSL certificates, needed for signed requests. Put your certificate at the top and the root CA at the end ' ,
2015-06-09 09:58:40 +02:00
' url_rewrite ' : ' Parameters passed to str.replace(), in order to create the r= part of bitcoin: URIs. Example: \" ( \' file:///var/www/ \' , \' https://electrum.org/ \' ) \" ' ,
2015-06-03 11:34:52 +02:00
} ,
' listrequests ' : {
' url_rewrite ' : ' Parameters passed to str.replace(), in order to create the r= part of bitcoin: URIs. Example: \" ( \' file:///var/www/ \' , \' https://electrum.org/ \' ) \" ' ,
}
}
2015-05-31 22:42:34 +02:00
def set_default_subparser ( self , name , args = None ) :
""" see http://stackoverflow.com/questions/5176691/argparse-how-to-specify-a-default-subcommand """
subparser_found = False
for arg in sys . argv [ 1 : ] :
if arg in [ ' -h ' , ' --help ' ] : # global help if no subparser
break
else :
for x in self . _subparsers . _actions :
if not isinstance ( x , argparse . _SubParsersAction ) :
continue
for sp_name in x . _name_parser_map . keys ( ) :
if sp_name in sys . argv [ 1 : ] :
subparser_found = True
if not subparser_found :
# insert default in first position, this implies no
# global options without a sub_parsers specified
if args is None :
sys . argv . insert ( 1 , name )
else :
args . insert ( 0 , name )
argparse . ArgumentParser . set_default_subparser = set_default_subparser
2015-06-07 18:44:33 +02:00
2017-02-22 09:32:35 +01:00
# workaround https://bugs.python.org/issue23058
# see https://github.com/nickstenning/honcho/pull/121
def subparser_call ( self , parser , namespace , values , option_string = None ) :
from argparse import ArgumentError , SUPPRESS , _UNRECOGNIZED_ARGS_ATTR
parser_name = values [ 0 ]
arg_strings = values [ 1 : ]
# set the parser name if requested
if self . dest is not SUPPRESS :
setattr ( namespace , self . dest , parser_name )
# select the parser
try :
parser = self . _name_parser_map [ parser_name ]
except KeyError :
tup = parser_name , ' , ' . join ( self . _name_parser_map )
2018-02-04 07:26:55 +01:00
msg = _ ( ' unknown parser {!r} (choices: {} ) ' ) . format ( * tup )
2017-02-22 09:32:35 +01:00
raise ArgumentError ( self , msg )
# parse all the remaining options into the namespace
# store any unrecognized options on the object, so that the top
# level parser can decide what to do with them
namespace , arg_strings = parser . parse_known_args ( arg_strings , namespace )
if arg_strings :
vars ( namespace ) . setdefault ( _UNRECOGNIZED_ARGS_ATTR , [ ] )
getattr ( namespace , _UNRECOGNIZED_ARGS_ATTR ) . extend ( arg_strings )
argparse . _SubParsersAction . __call__ = subparser_call
2015-06-07 18:44:33 +02:00
2015-05-31 22:42:34 +02:00
def add_network_options ( parser ) :
2018-04-11 11:16:29 +02:00
parser . add_argument ( " -1 " , " --oneserver " , action = " store_true " , dest = " oneserver " , default = None , help = " connect to one server only " )
2015-05-31 22:42:34 +02:00
parser . add_argument ( " -s " , " --server " , dest = " server " , default = None , help = " set server host:port:protocol, where protocol is either t (tcp) or s (ssl) " )
parser . add_argument ( " -p " , " --proxy " , dest = " proxy " , default = None , help = " set proxy [type:]host[:port], where type is socks4,socks5 or http " )
2017-02-22 09:32:35 +01:00
def add_global_options ( parser ) :
group = parser . add_argument_group ( ' global options ' )
2015-06-12 09:58:29 +02:00
group . add_argument ( " -v " , " --verbose " , action = " store_true " , dest = " verbose " , default = False , help = " Show debugging information " )
2016-05-30 08:58:10 +02:00
group . add_argument ( " -D " , " --dir " , dest = " electrum_path " , help = " electrum directory " )
2015-06-12 09:58:29 +02:00
group . add_argument ( " -P " , " --portable " , action = " store_true " , dest = " portable " , default = False , help = " Use local ' electrum_data ' directory " )
group . add_argument ( " -w " , " --wallet " , dest = " wallet_path " , help = " wallet path " )
2017-01-07 16:58:23 +01:00
group . add_argument ( " --testnet " , action = " store_true " , dest = " testnet " , default = False , help = " Use Testnet " )
2018-04-11 20:10:14 +03:00
group . add_argument ( " --regtest " , action = " store_true " , dest = " regtest " , default = False , help = " Use Regtest " )
2018-06-22 17:07:07 +02:00
group . add_argument ( " --simnet " , action = " store_true " , dest = " simnet " , default = False , help = " Use Simnet " )
2017-02-22 09:32:35 +01:00
def get_parser ( ) :
2015-05-31 22:42:34 +02:00
# create main parser
parser = argparse . ArgumentParser (
epilog = " Run ' electrum help <command> ' to see the help for a command " )
2017-02-22 09:32:35 +01:00
add_global_options ( parser )
2015-05-31 22:42:34 +02:00
subparsers = parser . add_subparsers ( dest = ' cmd ' , metavar = ' <command> ' )
# gui
2017-02-21 12:41:24 +01:00
parser_gui = subparsers . add_parser ( ' gui ' , description = " Run Electrum ' s Graphical User Interface. " , help = " Run GUI (default) " )
2015-05-31 22:42:34 +02:00
parser_gui . add_argument ( " url " , nargs = ' ? ' , default = None , help = " bitcoin URI (or bip70 file) " )
2016-01-13 15:26:16 +01:00
parser_gui . add_argument ( " -g " , " --gui " , dest = " gui " , help = " select graphical user interface " , choices = [ ' qt ' , ' kivy ' , ' text ' , ' stdio ' ] )
2015-12-23 15:59:32 +01:00
parser_gui . add_argument ( " -o " , " --offline " , action = " store_true " , dest = " offline " , default = False , help = " Run offline " )
2015-05-31 22:42:34 +02:00
parser_gui . add_argument ( " -m " , action = " store_true " , dest = " hide_gui " , default = False , help = " hide GUI on startup " )
parser_gui . add_argument ( " -L " , " --lang " , dest = " language " , default = None , help = " default language used in GUI " )
add_network_options ( parser_gui )
2017-02-22 09:32:35 +01:00
add_global_options ( parser_gui )
2015-05-31 22:42:34 +02:00
# daemon
2017-02-21 12:41:24 +01:00
parser_daemon = subparsers . add_parser ( ' daemon ' , help = " Run Daemon " )
2017-03-05 20:25:42 +01:00
parser_daemon . add_argument ( " subcommand " , choices = [ ' start ' , ' status ' , ' stop ' , ' load_wallet ' , ' close_wallet ' ] , nargs = ' ? ' )
2015-08-26 17:44:19 +02:00
#parser_daemon.set_defaults(func=run_daemon)
2015-05-31 22:42:34 +02:00
add_network_options ( parser_daemon )
2017-02-22 09:32:35 +01:00
add_global_options ( parser_daemon )
2015-05-31 22:42:34 +02:00
# commands
for cmdname in sorted ( known_commands . keys ( ) ) :
cmd = known_commands [ cmdname ]
2017-02-21 12:41:24 +01:00
p = subparsers . add_parser ( cmdname , help = cmd . help , description = cmd . description )
2017-02-22 09:32:35 +01:00
add_global_options ( p )
2016-04-08 16:27:15 +02:00
if cmdname == ' restore ' :
p . add_argument ( " -o " , " --offline " , action = " store_true " , dest = " offline " , default = False , help = " Run offline " )
2015-06-01 00:17:50 +02:00
for optname , default in zip ( cmd . options , cmd . defaults ) :
2017-10-07 09:48:20 +02:00
a , help = command_options [ optname ]
b = ' -- ' + optname
2015-05-31 22:42:34 +02:00
action = " store_true " if type ( default ) is bool else ' store '
args = ( a , b ) if a else ( b , )
if action == ' store ' :
_type = arg_types . get ( optname , str )
p . add_argument ( * args , dest = optname , action = action , default = default , help = help , type = _type )
else :
p . add_argument ( * args , dest = optname , action = action , default = default , help = help )
for param in cmd . params :
h = param_descriptions . get ( param , ' ' )
_type = arg_types . get ( param , str )
p . add_argument ( param , help = h , type = _type )
2015-06-03 11:34:52 +02:00
cvh = config_variables . get ( cmdname )
if cvh :
group = p . add_argument_group ( ' configuration variables ' , ' (set with setconfig/getconfig) ' )
for k , v in cvh . items ( ) :
group . add_argument ( k , nargs = ' ? ' , help = v )
2015-05-31 22:42:34 +02:00
# 'gui' is the default command
parser . set_default_subparser ( ' gui ' )
return parser