don't use underscore in commands.
[electrum-nvc.git] / lib / commands.py
1 #!/usr/bin/env python
2 #
3 # Electrum - lightweight Bitcoin client
4 # Copyright (C) 2011 thomasv@gitorious
5 #
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19
20 from util import *
21 from bitcoin import *
22 from decimal import Decimal
23 import bitcoin
24
25 known_commands = {
26     'help':'Prints this help',
27     'validateaddress':'Check that the address is valid', 
28     'balance': "Display the balance of your wallet or of an address.\nSyntax: balance [<address>]", 
29     'contacts': "Show your list of contacts", 
30     'create':'Create a wallet', 
31     'restore':'Restore a wallet', 
32     'payto':"""Create and broadcast a transaction.
33 Syntax: payto <recipient> <amount> [label]
34 <recipient> can be a bitcoin address or a label
35 options:\n  --fee, -f: set transaction fee\n  --fromaddr, -s: send from address -\n  --changeaddr, -c: send change to address
36             """,
37     'sendrawtransaction':
38             'Broadcasts a transaction to the network. \nSyntax: sendrawtransaction <tx in hexadecimal>',
39     'password': 
40             "Changes your password",
41     'listaddresses':
42             """Shows your list of addresses.
43 options:
44   -a: show all addresses, including change addresses
45   -b: include balance in results
46   -l: include labels in results
47   """,
48
49     'history':"Shows the transaction history",
50     'setlabel':'Assign a label to an item\nSyntax: label <tx_hash> <label>',
51     'mktx':
52         """Create a signed transaction, password protected.
53 Syntax: mktx <recipient> <amount> [label]
54 options:\n  --fee, -f: set transaction fee\n  --fromaddr, -s: send from address -\n  --changeaddr, -c: send change to address
55         """,
56     'getseed':
57             "Print the generation seed of your wallet.",
58     'importprivkey': 
59             'Import a private key\nSyntax: importprivkey <privatekey>',
60     'signmessage':
61             'Signs a message with a key\nSyntax: signmessage <address> <message>\nIf you want to lead or end a message with spaces, or want double spaces inside the message make sure you quote the string. I.e. " Hello  This is a weird String "',
62     'verifymessage':
63              'Verifies a signature\nSyntax: verifymessage <address> <signature> <message>\nIf you want to lead or end a message with spaces, or want double spaces inside the message make sure you quote the string. I.e. " Hello  This is a weird String "',
64     'eval':  
65              "Run python eval() on an object\nSyntax: eval <expression>\nExample: eval \"wallet.aliases\"",
66     'get': 
67              "Get config parameter.",
68     'set': 
69              "Set config parameter.",
70     'deseed':
71             "Create a seedless, watching-only wallet.",
72     'freeze':'',
73     'unfreeze':'',
74     'prioritize':'',
75     'unprioritize':'',
76     'dumpprivkey':'similar to bitcoind\'s command',
77     'dumpprivkeys':'dump all private keys',
78     'listunspent':'similar to bitcoind\'s command',
79     'createmultisig':'similar to bitcoind\'s command',
80     'createrawtransaction':'similar to bitcoind\'s command',
81     'decoderawtransaction':'similar to bitcoind\'s command',
82     'signrawtransaction':'similar to bitcoind\'s command',
83     'get_history': 'get history for an address'
84     
85     }
86
87
88
89 offline_commands = [ 'password', 'mktx',
90                      'setlabel', 'contacts',
91                      'help', 'validateaddress',
92                      'signmessage', 'verifymessage',
93                      'eval', 'set', 'get', 'create', 'listaddresses',
94                      'importprivkey', 'getseed',
95                      'deseed',
96                      'freeze','unfreeze',
97                      'prioritize','unprioritize',
98                      'dumpprivkey','dumpprivkeys','listunspent',
99                      'createmultisig', 'createrawtransaction', 'decoderawtransaction', 'signrawtransaction'
100                      ]
101
102 protected_commands = ['payto', 'password', 'mktx', 'getseed', 'importprivkey','signmessage', 'signrawtransaction', 'dumpprivkey', 'dumpprivkeys' ]
103
104 class Commands:
105
106     def __init__(self, wallet, interface, callback = None):
107         self.wallet = wallet
108         self.interface = interface
109         self._callback = callback
110
111     def _run(self, method, args, password_getter):
112         if method in protected_commands:
113             self.password = apply(password_getter,())
114         f = eval('self.'+method)
115         result = apply(f,args)
116         self.password = None
117         if self._callback:
118             apply(self._callback, ())
119         return result
120
121     def get_history(self, addr):
122         h = self.wallet.get_history(addr)
123         if h is None: h = self.wallet.interface.synchronous_get([ ('blockchain.address.get_history',[addr]) ])[0]
124         return h
125
126     def listunspent(self):
127         import copy
128         l = copy.deepcopy(self.wallet.get_unspent_coins())
129         for i in l: i["value"] = str(Decimal(i["value"])/100000000)
130         return l
131
132     def createrawtransaction(self, inputs, outputs):
133         # convert to own format
134         for i in inputs:
135             i['tx_hash'] = i['txid']
136             i['index'] = i['vout']
137         outputs = map(lambda x: (x[0],int(1e8*x[1])), outputs.items())
138         tx = Transaction.from_io(inputs, outputs)
139         return tx.as_dict()
140
141     def signrawtransaction(self, raw_tx, input_info, private_keys):
142         tx = Transaction(raw_tx)
143         self.wallet.signrawtransaction(tx, input_info, private_keys, self.password)
144         return tx.as_dict()
145
146     def decoderawtransaction(self, raw):
147         tx = Transaction(raw)
148         return tx.deserialize()
149
150     def sendrawtransaction(self, raw):
151         tx = Transaction(raw)
152         r, h = wallet.sendtx( tx )
153         return h
154
155     def createmultisig(self, num, pubkeys):
156         assert isinstance(pubkeys, list)
157         return Transaction.multisig_script(pubkeys, num)
158     
159     def freeze(self,addr):
160         return self.wallet.freeze(addr)
161         
162     def unfreeze(self,addr):
163         return self.wallet.unfreeze(addr)
164
165     def prioritize(self, addr):
166         return self.wallet.prioritize(addr)
167
168     def unprioritize(self, addr):
169         return self.wallet.unprioritize(addr)
170
171     def dumpprivkey(self, addr):
172         return self.wallet.get_private_key(addr, self.password)
173
174     def dumpprivkeys(self, addresses = None):
175         if addresses is None:
176             addresses = self.wallet.all_addresses()
177         return self.wallet.get_private_keys(addresses, self.password)
178
179     def validateaddress(self,addr):
180         is_valid = self.wallet.is_valid(addr)
181         out = { 'isvalid':is_valid }
182         if is_valid:
183             is_mine = self.wallet.is_mine(addr)
184             out['address'] = addr
185             out['ismine'] = is_mine
186             if is_mine:
187                 out['pubkey'] = self.wallet.get_public_key(addr)
188             
189         return out
190
191         
192     def balance(self, addresses = []):
193         if addresses == []:
194             c, u = self.wallet.get_balance()
195         else:
196             c = u = 0
197             for addr in addresses:
198                 cc, uu = wallet.get_addr_balance(addr)
199                 c += cc
200                 u += uu
201
202         out = { "confirmed": str(Decimal(c)/100000000) }
203         if u: out["unconfirmed"] = str(Decimal(u)/100000000)
204         return out
205
206
207     def getseed(self):
208         import mnemonic
209         seed = self.wallet.decode_seed(self.password)
210         return { "hex":seed, "mnemonic": ' '.join(mnemonic.mn_encode(seed)) }
211
212     def importprivkey(self, sec):
213         try:
214             addr = wallet.import_key(sec,self.password)
215             wallet.save()
216             out = "Keypair imported: ", addr
217         except BaseException as e:
218             out = "Error: Keypair import failed: " + str(e)
219         return out
220
221
222     def signmessage(self, address, message):
223         return self.wallet.sign_message(address, message, self.password)
224
225
226     def verifymessage(self, address, signature, message):
227         try:
228             EC_KEY.verify_message(address, signature, message)
229             return True
230         except BaseException as e:
231             print_error("Verification error: {0}".format(e))
232             return False
233
234
235     def _mktx(self, to_address, amount, fee = None, change_addr = None, from_addr = None):
236         for k, v in self.wallet.labels.items():
237             if v == to_address:
238                 to_address = k
239                 print_msg("alias", to_address)
240                 break
241             if change_addr and v == change_addr:
242                 change_addr = k
243
244         amount = int(10000000*amount)
245         if fee: fee = int(10000000*fee)
246         return self.wallet.mktx( [(to_address, amount)], self.password, fee , change_addr, from_addr)
247
248
249     def mktx(self, to_address, amount, fee = None, change_addr = None, from_addr = None):
250         tx = self._mktx(to_address, amount, fee, change_addr, from_addr)
251         return tx.as_dict()
252
253
254     def payto(self, to_address, amount, fee = None, change_addr = None, from_addr = None):
255         tx = self._mktx(to_address, amount, fee, change_addr, from_addr)
256         r, h = wallet.sendtx( tx )
257         return h
258
259
260     def history(self):
261         import datetime
262         balance = 0
263         out = []
264         for item in self.wallet.get_tx_history():
265             tx_hash, conf, is_mine, value, fee, balance, timestamp = item
266             try:
267                 time_str = datetime.datetime.fromtimestamp( timestamp).isoformat(' ')[:-3]
268             except:
269                 time_str = "----"
270
271             label, is_default_label = self.wallet.get_label(tx_hash)
272             if not label: label = tx_hash
273             else: label = label + ' '*(64 - len(label) )
274
275             out.append( "%16s"%time_str + "  " + label + "  " + format_satoshis(value)+ "  "+ format_satoshis(balance) )
276         return out
277
278
279
280     def setlabel(self, tx, label):
281         self.wallet.labels[tx] = label
282         self.wallet.save()
283             
284
285     def contacts(self):
286         c = {}
287         for addr in self.wallet.addressbook:
288             c[addr] = self.wallet.labels.get(addr)
289         return c
290
291
292     def listaddresses(self, show_all = False, show_balance = False, show_label = False):
293         out = []
294         for addr in self.wallet.all_addresses():
295             if show_all or not self.wallet.is_change(addr):
296                 if show_balance or show_label:
297                     item = { 'address': addr }
298                     if show_balance:
299                         item['balance'] = str(Decimal(self.wallet.get_addr_balance(addr)[0])/100000000)
300                     if show_label:
301                         label = self.wallet.labels.get(addr,'')
302                         if label:
303                             item['label'] = label
304                 else:
305                     item = addr
306                 out.append( item )
307         return out
308                          
309