2013-02-26 04:56:48 -08:00
#!/usr/bin/env python
#
# Electrum - lightweight Bitcoin client
# Copyright (C) 2011 thomasv@gitorious
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2015-06-07 09:44:33 -07:00
import os
2015-05-28 06:22:30 -07:00
import sys
2014-06-24 06:12:43 -07:00
import datetime
2014-03-15 03:04:11 -07:00
import time
2014-06-24 06:12:43 -07:00
import copy
2015-05-28 06:22:30 -07:00
import argparse
2015-05-30 09:49:58 -07:00
import json
2015-05-31 13:42:34 -07:00
import ast
2015-07-14 07:37:04 -07:00
import base64
2015-05-31 21:10:06 -07:00
from functools import wraps
from decimal import Decimal
2015-05-28 06:22:30 -07:00
2015-05-31 05:10:52 -07:00
import util
2014-07-24 01:59:13 -07:00
from util import print_msg , format_satoshis , print_stderr
2013-02-26 04:56:48 -08:00
import bitcoin
2015-05-31 19:26:22 -07:00
from bitcoin import is_address , hash_160_to_bc_address , hash_160 , COIN
2013-10-21 13:19:30 -07:00
from transaction import Transaction
2015-06-07 09:44:33 -07:00
import paymentrequest
2015-06-12 00:46:21 -07:00
from paymentrequest import PR_PAID , PR_UNPAID , PR_UNKNOWN , PR_EXPIRED
2015-07-02 03:44:53 -07:00
import contacts
2014-06-24 06:12:43 -07:00
2015-05-31 21:10:06 -07:00
known_commands = { }
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__
self . help = self . description . split ( ' . ' ) [ 0 ]
varnames = func . func_code . co_varnames [ 1 : func . func_code . co_argcount ]
self . defaults = func . 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 = [ ]
def command ( s ) :
def decorator ( func ) :
global known_commands
name = func . __name__
known_commands [ name ] = Command ( func , s )
@wraps ( func )
def func_wrapper ( * args ) :
return func ( * args )
return func_wrapper
return decorator
2013-02-26 04:56:48 -08:00
class Commands :
2015-05-30 03:35:58 -07:00
def __init__ ( self , config , wallet , network , callback = None ) :
self . config = config
2013-02-26 04:56:48 -08:00
self . wallet = wallet
2013-09-15 02:19:48 -07:00
self . network = network
2013-02-27 03:40:16 -08:00
self . _callback = callback
2013-03-13 06:29:50 -07:00
self . password = None
2015-07-02 03:44:53 -07:00
self . contacts = contacts . Contacts ( self . config )
2013-02-26 04:56:48 -08:00
def _run ( self , method , args , password_getter ) :
2013-10-03 03:39:42 -07:00
cmd = known_commands [ method ]
if cmd . requires_password and self . wallet . use_encryption :
2013-02-26 06:09:43 -08:00
self . password = apply ( password_getter , ( ) )
2013-11-11 22:03:20 -08:00
f = getattr ( self , method )
result = f ( * args )
2013-02-26 06:09:43 -08:00
self . password = None
2013-02-27 03:40:16 -08:00
if self . _callback :
apply ( self . _callback , ( ) )
2013-02-26 08:57:48 -08:00
return result
2013-02-26 04:56:48 -08:00
2015-05-31 21:10:06 -07:00
@command ( ' ' )
2015-08-06 06:52:38 -07:00
def commands ( self ) :
""" List of commands """
return ' ' . join ( sorted ( known_commands . keys ( ) ) )
2015-05-30 10:13:28 -07:00
2015-05-31 21:10:06 -07:00
@command ( ' ' )
2015-05-31 14:17:44 -07:00
def create ( self ) :
""" Create a new wallet """
2015-05-31 21:10:06 -07:00
@command ( ' ' )
2015-05-31 14:17:44 -07:00
def restore ( self ) :
""" Restore a wallet from seed. """
2015-07-31 05:51:48 -07:00
@command ( ' w ' )
2015-05-31 14:17:44 -07:00
def deseed ( self ) :
2015-05-31 15:17:50 -07:00
""" Remove seed from wallet. This creates a seedless, watching-only
wallet . """
2015-05-31 14:17:44 -07:00
2015-05-31 21:10:06 -07:00
@command ( ' wp ' )
2015-05-31 14:17:44 -07:00
def password ( self ) :
""" Change wallet password. """
2015-05-31 13:42:34 -07:00
2015-05-31 21:10:06 -07:00
@command ( ' ' )
2015-05-31 13:42:34 -07:00
def getconfig ( self , key ) :
2015-05-31 14:17:44 -07:00
""" Return a configuration variable. """
2015-05-31 13:42:34 -07:00
return self . config . get ( key )
2015-05-31 21:10:06 -07:00
@command ( ' ' )
2015-05-31 13:42:34 -07:00
def setconfig ( self , key , value ) :
2015-06-03 01:02:12 -07:00
""" Set a configuration variable. ' value ' may be a string or a Python expression. """
2015-05-31 13:42:34 -07:00
try :
value = ast . literal_eval ( value )
except :
2015-06-03 01:02:12 -07:00
pass
2015-06-02 02:05:21 -07:00
self . config . set_key ( key , value )
2015-05-31 13:42:34 -07:00
return True
2015-05-31 21:10:06 -07:00
@command ( ' ' )
2015-05-31 13:42:34 -07:00
def make_seed ( self , nbits = 128 , entropy = 1 , language = None ) :
2015-05-31 14:17:44 -07:00
""" Create a seed """
2014-08-28 06:37:42 -07:00
from mnemonic import Mnemonic
2015-06-10 23:56:07 -07:00
s = Mnemonic ( language ) . make_seed ( nbits , custom_entropy = entropy )
2014-09-03 08:21:43 -07:00
return s . encode ( ' utf8 ' )
2014-08-28 06:37:42 -07:00
2015-05-31 21:10:06 -07:00
@command ( ' ' )
2015-05-31 13:42:34 -07:00
def check_seed ( self , seed , entropy = 1 , language = None ) :
2015-05-31 14:17:44 -07:00
""" Check that a seed was generated with given entropy """
2014-08-28 06:37:42 -07:00
from mnemonic import Mnemonic
2015-05-31 13:42:34 -07:00
return Mnemonic ( language ) . check_seed ( seed , entropy )
2014-08-28 06:37:42 -07:00
2015-05-31 21:10:06 -07:00
@command ( ' n ' )
2015-05-31 13:42:34 -07:00
def getaddresshistory ( self , address ) :
2015-06-03 00:12:38 -07:00
""" Return the transaction history of any address. Note: This is a
walletless server query , results are not checked by SPV .
"""
2015-05-31 13:42:34 -07:00
return self . network . synchronous_get ( [ ( ' blockchain.address.get_history ' , [ address ] ) ] ) [ 0 ]
2014-01-23 08:06:47 -08:00
2015-06-03 00:12:38 -07:00
@command ( ' nw ' )
2013-02-26 04:56:48 -08:00
def listunspent ( self ) :
2015-06-03 00:12:38 -07:00
""" List unspent outputs. Returns the list of unspent transaction
outputs in your wallet . """
2015-05-30 21:58:39 -07:00
l = copy . deepcopy ( self . wallet . get_spendable_coins ( exclude_frozen = False ) )
2015-05-31 19:26:22 -07:00
for i in l : i [ " value " ] = str ( Decimal ( i [ " value " ] ) / COIN )
2013-02-26 08:57:48 -08:00
return l
2013-02-26 04:56:48 -08:00
2015-05-31 21:10:06 -07:00
@command ( ' n ' )
2015-05-31 13:42:34 -07:00
def getaddressunspent ( self , address ) :
2015-06-10 14:48:36 -07:00
""" Returns the UTXO list of any address. Note: This
2015-06-03 00:12:38 -07:00
is a walletless server query , results are not checked by SPV .
"""
2015-05-31 13:42:34 -07:00
return self . network . synchronous_get ( [ ( ' blockchain.address.listunspent ' , [ address ] ) ] ) [ 0 ]
2014-01-23 08:06:47 -08:00
2015-05-31 21:10:06 -07:00
@command ( ' n ' )
2015-05-31 13:42:34 -07:00
def getutxoaddress ( self , txid , pos ) :
2015-06-10 14:48:36 -07:00
""" Get the address of a UTXO. Note: This is a walletless server query, results are
2015-06-03 00:12:38 -07:00
not checked by SPV .
"""
2015-05-31 13:42:34 -07:00
r = self . network . synchronous_get ( [ ( ' blockchain.utxo.get_address ' , [ txid , pos ] ) ] )
2014-06-24 06:12:43 -07:00
if r :
2015-05-30 23:06:20 -07:00
return { ' address ' : r [ 0 ] }
2014-01-30 06:16:49 -08:00
2015-05-31 21:10:06 -07:00
@command ( ' wp ' )
2015-05-31 08:38:57 -07:00
def createrawtx ( self , inputs , outputs , unsigned = False ) :
2015-05-31 14:17:44 -07:00
""" Create a transaction from json inputs. The syntax is similar to bitcoind. """
2015-05-30 21:58:39 -07:00
coins = self . wallet . get_spendable_coins ( exclude_frozen = False )
2014-12-29 11:26:00 -08:00
tx_inputs = [ ]
2014-05-21 03:37:00 -07:00
for i in inputs :
2014-12-29 11:26:00 -08:00
prevout_hash = i [ ' txid ' ]
prevout_n = i [ ' vout ' ]
for c in coins :
if c [ ' prevout_hash ' ] == prevout_hash and c [ ' prevout_n ' ] == prevout_n :
self . wallet . add_input_info ( c )
tx_inputs . append ( c )
break
else :
raise BaseException ( ' Transaction output not in wallet ' , prevout_hash + " : %d " % prevout_n )
2015-05-31 19:26:22 -07:00
outputs = map ( lambda x : ( ' address ' , x [ 0 ] , int ( COIN * x [ 1 ] ) ) , outputs . items ( ) )
2015-05-13 05:57:34 -07:00
tx = Transaction . from_io ( tx_inputs , outputs )
2015-05-31 08:38:57 -07:00
if not unsigned :
self . wallet . sign_transaction ( tx , self . password )
2013-09-14 12:53:56 -07:00
return tx
2013-02-26 04:56:48 -08:00
2015-05-31 21:10:06 -07:00
@command ( ' wp ' )
2015-05-31 13:42:34 -07:00
def signtransaction ( self , tx , privkey = None ) :
2015-05-31 14:17:44 -07:00
""" Sign a transaction. The wallet keys will be used unless a private key is provided. """
t = Transaction ( tx )
t . deserialize ( )
2015-05-31 00:28:47 -07:00
if privkey :
2015-07-01 08:15:35 -07:00
pubkey = bitcoin . public_key_from_private_key ( privkey )
t . sign ( { pubkey : privkey } )
2015-05-31 00:28:47 -07:00
else :
2015-05-31 14:17:44 -07:00
self . wallet . sign_transaction ( t , self . password )
return t
2013-02-26 04:56:48 -08:00
2015-05-31 21:10:06 -07:00
@command ( ' ' )
2015-06-10 14:21:25 -07:00
def deserialize ( self , tx ) :
""" Deserialize a serialized transaction """
2015-05-31 14:17:44 -07:00
t = Transaction ( tx )
2015-05-31 13:42:34 -07:00
return t . deserialize ( )
2013-02-26 04:56:48 -08:00
2015-05-31 21:10:06 -07:00
@command ( ' n ' )
2015-06-10 14:21:25 -07:00
def broadcast ( self , tx ) :
2015-05-31 14:17:44 -07:00
""" Broadcast a transaction to the network. """
t = Transaction ( tx )
2015-05-31 13:42:34 -07:00
return self . network . synchronous_get ( [ ( ' blockchain.transaction.broadcast ' , [ str ( t ) ] ) ] ) [ 0 ]
2013-02-26 04:56:48 -08:00
2015-05-31 21:10:06 -07:00
@command ( ' ' )
2013-02-26 04:56:48 -08:00
def createmultisig ( self , num , pubkeys ) :
2015-05-31 14:17:44 -07:00
""" Create multisig address """
2015-05-30 09:49:58 -07:00
assert isinstance ( pubkeys , list ) , ( type ( num ) , type ( pubkeys ) )
2013-08-17 00:53:46 -07:00
redeem_script = Transaction . multisig_script ( pubkeys , num )
address = hash_160_to_bc_address ( hash_160 ( redeem_script . decode ( ' hex ' ) ) , 5 )
return { ' address ' : address , ' redeemScript ' : redeem_script }
2014-06-24 06:12:43 -07:00
2015-05-31 21:10:06 -07:00
@command ( ' w ' )
2015-05-31 13:42:34 -07:00
def freeze ( self , address ) :
2015-05-31 14:17:44 -07:00
""" Freeze address. Freeze the funds at one of your wallet \' s addresses """
2015-05-31 13:42:34 -07:00
return self . wallet . set_frozen_state ( [ address ] , True )
2014-06-24 06:12:43 -07:00
2015-05-31 21:10:06 -07:00
@command ( ' w ' )
2015-05-31 13:42:34 -07:00
def unfreeze ( self , address ) :
2015-05-31 14:17:44 -07:00
""" Unfreeze address. Unfreeze the funds at one of your wallet \' s address """
2015-05-31 13:42:34 -07:00
return self . wallet . set_frozen_state ( [ address ] , False )
2013-02-26 04:56:48 -08:00
2015-05-31 21:10:06 -07:00
@command ( ' wp ' )
2015-05-31 13:42:34 -07:00
def getprivatekeys ( self , address ) :
2015-05-31 14:17:44 -07:00
""" Get the private keys of an address. Address must be in wallet. """
2015-05-31 13:42:34 -07:00
return self . wallet . get_private_key ( address , self . password )
2013-02-26 07:03:04 -08:00
2015-05-31 21:10:06 -07:00
@command ( ' w ' )
2015-05-31 13:42:34 -07:00
def ismine ( self , address ) :
2015-05-31 14:17:44 -07:00
""" Check if address is in wallet. Return true if and only address is in wallet """
2015-05-31 13:42:34 -07:00
return self . wallet . is_mine ( address )
2015-01-11 11:37:08 -08:00
2015-05-31 21:10:06 -07:00
@command ( ' wp ' )
2015-05-31 13:42:34 -07:00
def dumpprivkeys ( self , domain = None ) :
2015-05-31 14:17:44 -07:00
""" Dump private keys from your wallet """
2015-05-31 13:42:34 -07:00
if domain is None :
domain = self . wallet . addresses ( True )
return [ self . wallet . get_private_key ( address , self . password ) for address in domain ]
2013-02-26 04:56:48 -08:00
2015-05-31 21:10:06 -07:00
@command ( ' ' )
2015-05-31 13:42:34 -07:00
def validateaddress ( self , address ) :
2015-06-12 01:34:45 -07:00
""" Check that an address is valid. """
2015-05-31 13:42:34 -07:00
return is_address ( address )
2013-10-03 04:31:59 -07:00
2015-05-31 21:10:06 -07:00
@command ( ' w ' )
2015-05-31 13:42:34 -07:00
def getpubkeys ( self , address ) :
2015-05-31 14:17:44 -07:00
""" Return the public keys for a wallet address. """
2015-05-31 13:42:34 -07:00
return self . wallet . get_public_keys ( address )
2013-02-26 04:56:48 -08:00
2015-05-31 21:10:06 -07:00
@command ( ' nw ' )
2015-05-31 13:42:34 -07:00
def getbalance ( self , account = None ) :
2015-06-12 01:34:45 -07:00
""" Return the balance of your wallet. If run with the --offline flag,
returns the last known balance . """
2013-04-16 07:05:45 -07:00
if account is None :
2015-05-05 11:52:14 -07:00
c , u , x = self . wallet . get_balance ( )
2013-02-26 04:56:48 -08:00
else :
2015-05-05 11:52:14 -07:00
c , u , x = self . wallet . get_account_balance ( account )
2015-05-31 19:26:22 -07:00
out = { " confirmed " : str ( Decimal ( c ) / COIN ) }
2015-05-05 11:52:14 -07:00
if u :
2015-05-31 19:26:22 -07:00
out [ " unconfirmed " ] = str ( Decimal ( u ) / COIN )
2015-05-05 11:52:14 -07:00
if x :
2015-05-31 19:26:22 -07:00
out [ " unmatured " ] = str ( Decimal ( x ) / COIN )
2013-04-16 07:05:45 -07:00
return out
2013-02-27 01:24:53 -08:00
2015-05-31 21:10:06 -07:00
@command ( ' n ' )
2015-05-31 13:42:34 -07:00
def getaddressbalance ( self , address ) :
2015-06-03 00:12:38 -07:00
""" Return the balance of any address. Note: This is a walletless
server query , results are not checked by SPV .
"""
2015-05-31 13:42:34 -07:00
out = self . network . synchronous_get ( [ ( ' blockchain.address.get_balance ' , [ address ] ) ] ) [ 0 ]
2015-05-31 19:26:22 -07:00
out [ " confirmed " ] = str ( Decimal ( out [ " confirmed " ] ) / COIN )
out [ " unconfirmed " ] = str ( Decimal ( out [ " unconfirmed " ] ) / COIN )
2014-03-02 01:31:34 -08:00
return out
2015-05-31 21:10:06 -07:00
@command ( ' n ' )
2015-05-31 13:42:34 -07:00
def getproof ( self , address ) :
2015-05-31 14:17:44 -07:00
""" Get Merkle branch of an address in the UTXO set """
2015-05-31 13:42:34 -07:00
p = self . network . synchronous_get ( [ ( ' blockchain.address.get_proof ' , [ address ] ) ] ) [ 0 ]
2014-01-29 07:48:24 -08:00
out = [ ]
for i , s in p :
out . append ( i )
return out
2015-05-31 21:10:06 -07:00
@command ( ' n ' )
2015-05-12 03:30:26 -07:00
def getmerkle ( self , txid , height ) :
2015-06-12 01:34:45 -07:00
""" Get Merkle branch of a transaction included in a block. Electrum
uses this to verify transactions ( Simple Payment Verification ) . """
2015-05-31 13:42:34 -07:00
return self . network . synchronous_get ( [ ( ' blockchain.transaction.get_merkle ' , [ txid , int ( height ) ] ) ] ) [ 0 ]
2015-05-12 03:30:26 -07:00
2015-05-31 21:10:06 -07:00
@command ( ' n ' )
2013-10-02 04:50:36 -07:00
def getservers ( self ) :
2015-05-31 14:17:44 -07:00
""" Return the list of available servers """
2014-03-15 03:04:11 -07:00
while not self . network . is_up_to_date ( ) :
time . sleep ( 0.1 )
2013-10-02 04:50:36 -07:00
return self . network . get_servers ( )
2013-02-26 04:56:48 -08:00
2015-05-31 21:10:06 -07:00
@command ( ' ' )
2015-05-28 06:22:30 -07:00
def version ( self ) :
2015-05-31 14:17:44 -07:00
""" Return the version of electrum. """
2014-06-24 06:12:43 -07:00
import electrum # Needs to stay here to prevent ciruclar imports
2013-12-01 00:42:22 -08:00
return electrum . ELECTRUM_VERSION
2014-06-24 06:12:43 -07:00
2015-05-31 21:10:06 -07:00
@command ( ' w ' )
2013-11-03 05:14:35 -08:00
def getmpk ( self ) :
2015-06-12 01:34:45 -07:00
""" Get master public key. Return your wallet \' s master public key(s) """
2014-06-21 23:06:37 -07:00
return self . wallet . get_master_public_keys ( )
2013-11-03 05:14:35 -08:00
2015-05-31 21:10:06 -07:00
@command ( ' wp ' )
2013-03-01 02:25:50 -08:00
def getseed ( self ) :
2015-05-31 14:17:44 -07:00
""" Get seed phrase. Print the generation seed of your wallet. """
2014-09-03 09:52:43 -07:00
s = self . wallet . get_mnemonic ( self . password )
return s . encode ( ' utf8 ' )
2013-02-26 04:56:48 -08:00
2015-05-31 21:10:06 -07:00
@command ( ' wp ' )
2015-05-31 13:42:34 -07:00
def importprivkey ( self , privkey ) :
2015-05-31 14:17:44 -07:00
""" Import a private key. """
2013-02-26 04:56:48 -08:00
try :
2015-05-31 13:42:34 -07:00
addr = self . wallet . import_key ( privkey , self . password )
2015-06-15 01:52:03 -07:00
out = " Keypair imported: " + addr
2013-11-09 20:21:02 -08:00
except Exception as e :
2015-06-15 01:52:03 -07:00
out = " Error: " + str ( e )
2013-02-27 01:24:53 -08:00
return out
2013-02-26 04:56:48 -08:00
2015-06-11 03:08:38 -07:00
def _resolver ( self , x ) :
if x is None :
return None
out = self . contacts . resolve ( x )
if out . get ( ' type ' ) == ' openalias ' and self . nocheck is False and out . get ( ' validated ' ) is False :
raise BaseException ( ' cannot verify alias ' , x )
return out [ ' address ' ]
2015-05-31 21:10:06 -07:00
@command ( ' n ' )
2015-05-31 13:42:34 -07:00
def sweep ( self , privkey , destination , tx_fee = None , nocheck = False ) :
2015-05-31 14:23:13 -07:00
""" Sweep private key. Returns a transaction that spends UTXOs from
privkey to a destination address . The transaction is not
broadcasted . """
2015-06-11 03:08:38 -07:00
self . nocheck = nocheck
dest = self . _resolver ( destination )
2015-05-31 13:42:34 -07:00
if tx_fee is None :
tx_fee = 0.0001
2015-05-31 19:26:22 -07:00
fee = int ( Decimal ( tx_fee ) * COIN )
2015-05-31 09:02:09 -07:00
return Transaction . sweep ( [ privkey ] , self . network , dest , fee )
2014-04-25 08:23:26 -07:00
2015-05-31 21:10:06 -07:00
@command ( ' wp ' )
2013-03-01 02:25:50 -08:00
def signmessage ( self , address , message ) :
2015-05-31 14:23:13 -07:00
""" Sign a message with a key. Use quotes if your message contains
whitespaces """
2015-07-14 07:37:04 -07:00
sig = self . wallet . sign_message ( address , message , self . password )
return base64 . b64encode ( sig )
2013-02-26 04:56:48 -08:00
2015-05-31 21:10:06 -07:00
@command ( ' ' )
2013-03-01 02:25:50 -08:00
def verifymessage ( self , address , signature , message ) :
2015-05-31 14:17:44 -07:00
""" Verify a signature. """
2015-07-14 07:37:04 -07:00
sig = base64 . b64decode ( signature )
return bitcoin . verify_message ( address , sig , message )
2013-02-26 04:56:48 -08:00
2015-06-10 14:21:25 -07:00
def _mktx ( self , outputs , fee , change_addr , domain , nocheck , unsigned ) :
2015-06-11 03:08:38 -07:00
self . nocheck = nocheck
change_addr = self . _resolver ( change_addr )
domain = None if domain is None else map ( self . _resolver , domain )
2015-05-31 19:26:22 -07:00
fee = None if fee is None else int ( COIN * Decimal ( fee ) )
2013-04-08 15:36:26 -07:00
final_outputs = [ ]
2015-05-31 05:10:52 -07:00
for address , amount in outputs :
2015-06-11 03:08:38 -07:00
address = self . _resolver ( address )
2015-05-31 05:10:52 -07:00
#assert self.wallet.is_mine(address)
2015-05-31 02:31:41 -07:00
if amount == ' ! ' :
assert len ( outputs ) == 1
inputs = self . wallet . get_spendable_coins ( domain )
amount = sum ( map ( lambda x : x [ ' value ' ] , inputs ) )
if fee is None :
for i in inputs :
self . wallet . add_input_info ( i )
2015-05-31 09:03:58 -07:00
output = ( ' address ' , address , amount )
2015-05-31 02:31:41 -07:00
dummy_tx = Transaction . from_io ( inputs , [ output ] )
2015-08-06 10:26:34 -07:00
fee_per_kb = self . wallet . fee_per_kb ( self . config )
fee = self . wallet . estimated_fee ( dummy_tx , fee_per_kb )
2015-05-31 02:31:41 -07:00
amount - = fee
else :
2015-05-31 19:26:22 -07:00
amount = int ( COIN * Decimal ( amount ) )
2015-05-31 05:10:52 -07:00
final_outputs . append ( ( ' address ' , address , amount ) )
2014-06-24 06:12:43 -07:00
2015-05-31 08:38:57 -07:00
coins = self . wallet . get_spendable_coins ( domain )
2015-08-03 22:15:54 -07:00
tx = self . wallet . make_unsigned_transaction ( coins , final_outputs , self . config , fee , change_addr )
2015-05-31 09:21:14 -07:00
str ( tx ) #this serializes
2015-05-31 08:38:57 -07:00
if not unsigned :
self . wallet . sign_transaction ( tx , self . password )
2015-06-10 14:21:25 -07:00
return tx
2013-02-26 06:13:01 -08:00
2015-05-29 10:23:49 -07:00
def _read_csv ( self , csvpath ) :
import csv
outputs = [ ]
with open ( csvpath , ' rb ' ) as csvfile :
csvReader = csv . reader ( csvfile , delimiter = ' , ' )
for row in csvReader :
address , amount = row
assert bitcoin . is_address ( address )
amount = Decimal ( amount )
outputs . append ( ( address , amount ) )
return outputs
2015-05-31 21:10:06 -07:00
@command ( ' wp ' )
2015-06-10 14:21:25 -07:00
def payto ( self , destination , amount , tx_fee = None , from_addr = None , change_addr = None , nocheck = False , unsigned = False , deserialized = False , broadcast = False ) :
2015-05-31 14:17:44 -07:00
""" Create a transaction. """
2015-05-30 09:49:58 -07:00
domain = [ from_addr ] if from_addr else None
2015-06-10 14:21:25 -07:00
tx = self . _mktx ( [ ( destination , amount ) ] , tx_fee , change_addr , domain , nocheck , unsigned )
if broadcast :
r , h = self . wallet . sendtx ( tx )
return h
else :
return tx . deserialize ( ) if deserialized else tx
2015-05-30 09:49:58 -07:00
2015-05-31 21:10:06 -07:00
@command ( ' wp ' )
2015-06-10 14:21:25 -07:00
def paytomany ( self , csv_file , tx_fee = None , from_addr = None , change_addr = None , nocheck = False , unsigned = False , deserialized = False , broadcast = False ) :
2015-05-31 14:17:44 -07:00
""" Create a multi-output transaction. """
2015-05-30 09:49:58 -07:00
domain = [ from_addr ] if from_addr else None
2015-05-31 13:42:34 -07:00
outputs = self . _read_csv ( csv_file )
2015-06-10 14:21:25 -07:00
tx = self . _mktx ( outputs , tx_fee , change_addr , domain , nocheck , unsigned )
if broadcast :
r , h = self . wallet . sendtx ( tx )
return h
else :
return tx . deserialize ( ) if deserialized else tx
2013-04-08 15:40:51 -07:00
2015-05-31 21:10:06 -07:00
@command ( ' wn ' )
2013-02-26 04:56:48 -08:00
def history ( self ) :
2015-05-31 14:17:44 -07:00
""" Wallet history. Returns the transaction history of your wallet. """
2013-02-26 04:56:48 -08:00
balance = 0
2013-02-26 09:10:29 -08:00
out = [ ]
2015-03-20 14:37:06 -07:00
for item in self . wallet . get_history ( ) :
2015-04-27 23:58:33 -07:00
tx_hash , conf , value , timestamp , balance = item
2013-02-26 04:56:48 -08:00
try :
time_str = datetime . datetime . fromtimestamp ( timestamp ) . isoformat ( ' ' ) [ : - 3 ]
2013-11-09 21:23:57 -08:00
except Exception :
2013-02-26 04:56:48 -08:00
time_str = " ---- "
label , is_default_label = self . wallet . get_label ( tx_hash )
2014-08-19 10:57:37 -07:00
out . append ( { ' txid ' : tx_hash , ' date ' : " %16s " % time_str , ' label ' : label , ' value ' : format_satoshis ( value ) , ' confirmations ' : conf } )
2013-02-26 09:10:29 -08:00
return out
2015-05-31 21:10:06 -07:00
@command ( ' w ' )
2013-09-08 11:10:43 -07:00
def setlabel ( self , key , label ) :
2015-05-31 14:23:13 -07:00
""" Assign a label to an item. Item may be a bitcoin address or a
transaction ID """
2013-09-08 11:12:03 -07:00
self . wallet . set_label ( key , label )
2013-09-08 11:10:43 -07:00
2015-05-31 21:10:06 -07:00
@command ( ' ' )
2015-05-30 03:35:58 -07:00
def listcontacts ( self ) :
2015-05-31 14:17:44 -07:00
""" Show your list of contacts """
2015-05-31 05:10:52 -07:00
return self . contacts
2013-02-26 04:56:48 -08:00
2015-05-31 21:10:06 -07:00
@command ( ' ' )
2015-06-11 03:08:38 -07:00
def getalias ( self , key ) :
2015-05-31 14:17:44 -07:00
""" Retrieve alias. Lookup in your list of contacts, and for an OpenAlias DNS record. """
2015-06-11 03:08:38 -07:00
return self . contacts . resolve ( key )
2015-05-31 06:06:52 -07:00
2015-05-31 21:10:06 -07:00
@command ( ' ' )
2014-11-05 15:02:44 -08:00
def searchcontacts ( self , query ) :
2015-05-31 14:17:44 -07:00
""" Search through contacts, return matching entries. """
2014-11-05 15:02:44 -08:00
results = { }
2015-05-31 05:10:52 -07:00
for key , value in self . contacts . items ( ) :
2015-05-30 03:35:58 -07:00
if query . lower ( ) in key . lower ( ) :
results [ key ] = value
2014-11-05 15:02:44 -08:00
return results
2015-05-31 21:10:06 -07:00
@command ( ' w ' )
2015-06-11 03:49:14 -07:00
def listaddresses ( self , receiving = False , change = False , show_labels = False , frozen = False , unused = False , funded = False , show_balance = False ) :
2015-05-31 14:17:44 -07:00
""" List wallet addresses. Returns your list of addresses. """
2013-02-26 09:10:29 -08:00
out = [ ]
2013-03-02 02:40:17 -08:00
for addr in self . wallet . addresses ( True ) :
2015-05-30 09:49:58 -07:00
if frozen and not self . wallet . is_frozen ( addr ) :
continue
2015-06-11 03:49:14 -07:00
if receiving and self . wallet . is_change ( addr ) :
continue
if change and not self . wallet . is_change ( addr ) :
2015-05-30 09:49:58 -07:00
continue
if unused and self . wallet . is_used ( addr ) :
continue
if funded and self . wallet . is_empty ( addr ) :
continue
item = addr
if show_balance :
item + = " , " + format_satoshis ( sum ( self . wallet . get_addr_balance ( addr ) ) )
2015-05-31 13:42:34 -07:00
if show_labels :
2015-06-08 03:51:45 -07:00
item + = ' , ' + repr ( self . wallet . labels . get ( addr , ' ' ) )
2015-05-30 09:49:58 -07:00
out . append ( item )
2013-02-26 09:10:29 -08:00
return out
2014-06-24 06:12:43 -07:00
2015-05-31 21:10:06 -07:00
@command ( ' nw ' )
2015-05-31 13:42:34 -07:00
def gettransaction ( self , txid , deserialized = False ) :
2015-05-31 14:17:44 -07:00
""" Retrieve a transaction. """
2015-05-31 13:42:34 -07:00
tx = self . wallet . transactions . get ( txid ) if self . wallet else None
2015-05-30 23:20:09 -07:00
if tx is None and self . network :
2015-05-31 13:42:34 -07:00
raw = self . network . synchronous_get ( [ ( ' blockchain.transaction.get ' , [ txid ] ) ] ) [ 0 ]
2015-05-30 23:20:09 -07:00
if raw :
tx = Transaction ( raw )
else :
raise BaseException ( " Unknown transaction " )
2015-05-31 09:21:14 -07:00
return tx . deserialize ( ) if deserialized else tx
2014-01-23 08:06:47 -08:00
2015-05-31 21:10:06 -07:00
@command ( ' ' )
2014-01-30 05:43:46 -08:00
def encrypt ( self , pubkey , message ) :
2015-05-31 14:17:44 -07:00
""" Encrypt a message with a public key. Use quotes if the message contains whitespaces. """
2014-03-03 01:39:10 -08:00
return bitcoin . encrypt_message ( message , pubkey )
2015-05-31 21:10:06 -07:00
@command ( ' wp ' )
2015-05-31 13:42:34 -07:00
def decrypt ( self , pubkey , encrypted ) :
2015-05-31 14:17:44 -07:00
""" Decrypt a message encrypted with a public key. """
2015-05-31 13:42:34 -07:00
return self . wallet . decrypt_message ( pubkey , encrypted , self . password )
2015-06-08 03:51:45 -07:00
def _format_request ( self , out ) :
2015-06-02 02:36:06 -07:00
pr_str = {
PR_UNKNOWN : ' Unknown ' ,
PR_UNPAID : ' Pending ' ,
PR_PAID : ' Paid ' ,
PR_EXPIRED : ' Expired ' ,
}
2015-06-09 00:58:40 -07:00
out [ ' amount ' ] = format_satoshis ( out . get ( ' amount ' ) ) + ' BTC '
2015-06-08 03:51:45 -07:00
out [ ' status ' ] = pr_str [ out . get ( ' status ' , PR_UNKNOWN ) ]
2015-06-02 01:25:39 -07:00
return out
2015-06-07 09:44:33 -07:00
@command ( ' wn ' )
def getrequest ( self , key ) :
""" Return a payment request """
2015-06-08 03:51:45 -07:00
r = self . wallet . get_payment_request ( key , self . config )
2015-06-07 09:44:33 -07:00
if not r :
raise BaseException ( " Request not found " )
return self . _format_request ( r )
2015-06-11 01:50:25 -07:00
#@command('w')
#def ackrequest(self, serialized):
# """<Not implemented>"""
# pass
2015-06-07 09:44:33 -07:00
2015-06-12 00:58:29 -07:00
@command ( ' wn ' )
2015-06-12 00:46:21 -07:00
def listrequests ( self , pending = False , expired = False , paid = False ) :
2015-06-10 14:48:36 -07:00
""" List the payment requests you made. """
2015-06-12 00:46:21 -07: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 11:15:53 -07:00
if f is not None :
2015-06-12 00:46:21 -07:00
out = filter ( lambda x : x . get ( ' status ' ) == f , out )
return map ( self . _format_request , out )
2015-05-31 13:42:34 -07:00
2015-06-01 04:02:09 -07:00
@command ( ' w ' )
2015-06-11 11:44:38 -07:00
def addrequest ( self , requested_amount , memo = ' ' , expiration = 60 * 60 , force = False ) :
2015-06-07 09:44:33 -07:00
""" Create a payment request. """
2015-06-08 03:51:45 -07:00
addr = self . wallet . get_unused_address ( None )
if addr is None :
2015-06-11 11:44:38 -07:00
if force :
addr = self . wallet . create_new_address ( None , False )
else :
return False
2015-06-03 02:39:44 -07:00
amount = int ( Decimal ( requested_amount ) * COIN )
2015-07-22 06:46:53 -07:00
expiration = int ( expiration )
2015-07-11 12:03:02 -07:00
req = self . wallet . make_payment_request ( addr , amount , memo , expiration )
self . wallet . add_payment_request ( req , self . config )
2015-06-08 03:51:45 -07:00
return self . _format_request ( req )
2015-06-01 04:02:09 -07:00
2015-07-22 00:06:03 -07:00
@command ( ' wp ' )
2015-07-22 00:24:44 -07:00
def signrequest ( self , address ) :
2015-07-22 00:06:03 -07:00
" Sign payment request with an OpenAlias "
alias = self . config . get ( ' alias ' )
2015-08-07 00:00:00 -07:00
if not alias :
raise BaseException ( ' No alias in your configuration ' )
2015-07-22 00:06:03 -07:00
alias_addr = self . contacts . resolve ( alias ) [ ' address ' ]
2015-07-22 00:24:44 -07:00
self . wallet . sign_payment_request ( address , alias , alias_addr , self . password )
2015-07-22 00:06:03 -07:00
2015-06-01 04:02:09 -07:00
@command ( ' w ' )
2015-07-22 00:24:44 -07:00
def rmrequest ( self , address ) :
2015-06-01 04:02:09 -07:00
""" Remove a payment request """
2015-07-22 00:24:44 -07:00
return self . wallet . remove_payment_request ( address , self . config )
2015-05-31 13:42:34 -07:00
2015-07-22 06:46:53 -07:00
@command ( ' w ' )
def clearrequests ( self ) :
""" Remove all payment requests """
for k in self . wallet . receive_requests . keys ( ) :
self . wallet . remove_payment_request ( k , self . config )
2015-05-31 13:42:34 -07: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 01:55:08 -07:00
' height ' : ' Block height ' ,
2015-05-31 13:42:34 -07: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 02:34:52 -07:00
' requested_amount ' : ' Requested amount (in BTC). ' ,
2015-05-31 13:42:34 -07:00
' csv_file ' : ' CSV file of recipient, amount ' ,
}
command_options = {
2015-06-10 14:21:25 -07:00
' broadcast ' : ( None , " --broadcast " , " Broadcast the transaction to the Bitcoin network " ) ,
2015-05-31 15:17:50 -07:00
' password ' : ( " -W " , " --password " , " Password " ) ,
' concealed ' : ( " -C " , " --concealed " , " Don ' t echo seed to console when restoring " ) ,
2015-06-11 03:49:14 -07:00
' receiving ' : ( None , " --receiving " , " Show only receiving addresses " ) ,
' change ' : ( None , " --change " , " Show only change addresses " ) ,
2015-05-31 15:17:50 -07:00
' frozen ' : ( None , " --frozen " , " Show only frozen addresses " ) ,
' unused ' : ( None , " --unused " , " Show only unused addresses " ) ,
' funded ' : ( None , " --funded " , " Show only funded addresses " ) ,
' show_balance ' : ( " -b " , " --balance " , " Show the balances of listed addresses " ) ,
' show_labels ' : ( " -l " , " --labels " , " Show the labels of listed addresses " ) ,
' nocheck ' : ( None , " --nocheck " , " Do not verify aliases " ) ,
' tx_fee ' : ( " -f " , " --fee " , " Transaction fee (in BTC) " ) ,
' from_addr ' : ( " -F " , " --from " , " Source address. If it isn ' t in the wallet, it will ask for the private key unless supplied in the format public_key:private_key. It ' s not saved in the wallet. " ) ,
' change_addr ' : ( " -c " , " --change " , " Change address. Default is a spare address, or the source address if it ' s not in the wallet " ) ,
' nbits ' : ( None , " --nbits " , " Number of bits of entropy " ) ,
' entropy ' : ( None , " --entropy " , " Custom entropy " ) ,
' language ' : ( " -L " , " --lang " , " Default language for wordlist " ) ,
' gap_limit ' : ( " -G " , " --gap " , " Gap limit " ) ,
' mpk ' : ( None , " --mpk " , " Restore from master public key " ) ,
' deserialized ' : ( " -d " , " --deserialized " , " Return deserialized transaction " ) ,
' privkey ' : ( None , " --privkey " , " Private key. Set to ' ? ' to get a prompt. " ) ,
' unsigned ' : ( " -u " , " --unsigned " , " Do not sign transaction " ) ,
' domain ' : ( " -D " , " --domain " , " List of addresses " ) ,
' account ' : ( None , " --account " , " Account " ) ,
2015-06-07 23:06:38 -07:00
' memo ' : ( " -m " , " --memo " , " Description of the request " ) ,
2015-06-01 04:02:09 -07:00
' expiration ' : ( None , " --expiration " , " Time in seconds " ) ,
2015-06-11 11:44:38 -07:00
' force ' : ( None , " --force " , " Create new address beyong gap limit, if no more address is available. " ) ,
2015-06-12 00:46:21 -07:00
' pending ' : ( None , " --pending " , " Show only pending requests. " ) ,
' expired ' : ( None , " --expired " , " Show only expired requests. " ) ,
' paid ' : ( None , " --paid " , " Show only paid requests. " ) ,
2015-05-31 13:42:34 -07:00
}
arg_types = {
' num ' : int ,
' nbits ' : int ,
' entropy ' : long ,
' pubkeys ' : json . loads ,
' inputs ' : json . loads ,
' outputs ' : json . loads ,
' tx_fee ' : lambda x : Decimal ( x ) if x is not None else None ,
' amount ' : lambda x : Decimal ( x ) if x != ' ! ' else ' ! ' ,
}
2015-06-03 02:34:52 -07: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 00:58:40 -07: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 02:34:52 -07: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 13:42:34 -07: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 09:44:33 -07:00
2015-05-31 13:42:34 -07:00
def add_network_options ( parser ) :
parser . add_argument ( " -1 " , " --oneserver " , action = " store_true " , dest = " oneserver " , default = False , help = " connect to one server only " )
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 " )
from util import profiler
@profiler
def get_parser ( run_gui , run_daemon , run_cmdline ) :
# parent parser, because set_default_subparser removes global options
parent_parser = argparse . ArgumentParser ( ' parent ' , add_help = False )
2015-06-12 00:58:29 -07:00
group = parent_parser . add_argument_group ( ' global options ' )
group . add_argument ( " -v " , " --verbose " , action = " store_true " , dest = " verbose " , default = False , help = " Show debugging information " )
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 " )
group . add_argument ( " -o " , " --offline " , action = " store_true " , dest = " offline " , default = False , help = " Run offline " )
2015-05-31 13:42:34 -07:00
# create main parser
parser = argparse . ArgumentParser (
parents = [ parent_parser ] ,
epilog = " Run ' electrum help <command> ' to see the help for a command " )
subparsers = parser . add_subparsers ( dest = ' cmd ' , metavar = ' <command> ' )
# gui
parser_gui = subparsers . add_parser ( ' gui ' , parents = [ parent_parser ] , description = " Run Electrum ' s Graphical User Interface. " , help = " Run GUI (default) " )
parser_gui . add_argument ( " url " , nargs = ' ? ' , default = None , help = " bitcoin URI (or bip70 file) " )
parser_gui . set_defaults ( func = run_gui )
2015-06-07 10:11:54 -07:00
parser_gui . add_argument ( " -g " , " --gui " , dest = " gui " , help = " select graphical user interface " , choices = [ ' qt ' , ' lite ' , ' gtk ' , ' text ' , ' stdio ' , ' jsonrpc ' ] )
2015-05-31 13:42:34 -07: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 )
# daemon
parser_daemon = subparsers . add_parser ( ' daemon ' , parents = [ parent_parser ] , help = " Run Daemon " )
parser_daemon . add_argument ( " subcommand " , choices = [ ' start ' , ' status ' , ' stop ' ] )
parser_daemon . set_defaults ( func = run_daemon )
add_network_options ( parser_daemon )
# commands
for cmdname in sorted ( known_commands . keys ( ) ) :
cmd = known_commands [ cmdname ]
2015-05-31 14:23:13 -07:00
p = subparsers . add_parser ( cmdname , parents = [ parent_parser ] , help = cmd . help , description = cmd . description )
2015-05-31 13:42:34 -07:00
p . set_defaults ( func = run_cmdline )
if cmd . requires_password :
p . add_argument ( " -W " , " --password " , dest = " password " , default = None , help = " password " )
2015-05-31 15:17:50 -07:00
for optname , default in zip ( cmd . options , cmd . defaults ) :
a , b , help = command_options [ optname ]
2015-05-31 13:42:34 -07: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 02:34:52 -07: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 13:42:34 -07:00
# 'gui' is the default command
parser . set_default_subparser ( ' gui ' )
return parser