From def4de8184f7a725fa397a89d7174a11e05bdfd6 Mon Sep 17 00:00:00 2001 From: DiThi Date: Wed, 8 Feb 2012 04:22:18 +0100 Subject: [PATCH 1/2] Added two cli commands: payfromto and paytoch. payfromto makes a payment from a keypair given as argument without saving the keypair in the wallet or using any other address in it. paytoch is the same as payto but specifying a custom change address (useful for imported keys). --- client/electrum | 58 ++++++++++++++++++++++++++++++++++++++++-------- client/wallet.py | 13 ++++++----- 2 files changed, 57 insertions(+), 14 deletions(-) diff --git a/client/electrum b/client/electrum index 29ff64514..d373de005 100755 --- a/client/electrum +++ b/client/electrum @@ -31,7 +31,7 @@ urldecode = lambda x: _ud.sub(lambda m: chr(int(m.group(1), 16)), x) from wallet import format_satoshis if __name__ == '__main__': - known_commands = ['help', 'validateaddress', 'balance', 'contacts', 'create', 'payto', 'sendtx', 'password', 'newaddress', 'addresses', 'history', 'label', 'gui', 'mktx','seed','import','signmessage','verifymessage','eval'] + known_commands = ['help', 'validateaddress', 'balance', 'contacts', 'create', 'payto', 'paytoch', 'payfromto', 'sendtx', 'password', 'newaddress', 'addresses', 'history', 'label', 'gui', 'mktx','seed','import','signmessage','verifymessage','eval'] usage = "usage: %prog [options] command args\nCommands: "+ (', '.join(known_commands)) @@ -137,11 +137,19 @@ if __name__ == '__main__': wallet.synchronize() # check syntax - if cmd in ['payto', 'mktx']: + if cmd in ['payto', 'mktx', 'paytoch', 'payfromto']: + offset = 0 + if cmd == 'payfromto': + offset = 1 try: - to_address = args[1] - amount = int( 100000000 * Decimal(args[2]) ) - label = ' '.join(args[3:]) + to_address = args[offset+1] + amount = int( 100000000 * Decimal(args[offset+2]) ) + change_addr = None + if cmd == 'paytoch': + change_addr = args[offset+3] + label = ' '.join(args[offset+4:]) + else: + label = ' '.join(args[offset+3:]) if options.tx_fee: options.tx_fee = int( 100000000 * Decimal(options.tx_fee) ) except: @@ -155,7 +163,7 @@ if __name__ == '__main__': wallet.save() # commands needing password - if cmd in ['payto', 'password', 'mktx', 'seed', 'import','signmessage' ] or ( cmd=='addresses' and options.show_keys): + if cmd in ['payto', 'paytoch', 'payfromto', 'password', 'mktx', 'seed', 'import','signmessage' ] or ( cmd=='addresses' and options.show_keys): password = getpass.getpass('Password:') if wallet.use_encryption else None # check password try: @@ -186,6 +194,16 @@ if __name__ == '__main__': print "payto [label]" print "create and broadcast a transaction." print " can be a bitcoin address or a label" + elif cmd2 == 'paytoch': + print "paytoch [label]" + print "create and broadcast a transaction, returning change to a custom address." + print " and can be a bitcoin address or a label" + elif cmd2 == 'payfromto': + print "payfromto [:] " + print "create and broadcast a transaction, from the address to , without saving the address to the wallet." + print "it sends back the change to the same source address." + print " can be a bitcoin address or a label." + print "if you omit , it will ask you for the key." elif cmd2== 'sendtx': print "sendtx " print "broadcast a transaction to the network. must be in hexadecimal" @@ -295,19 +313,41 @@ if __name__ == '__main__': wallet.labels[tx] = label wallet.save() - elif cmd in ['payto', 'mktx']: + elif cmd in ['payto', 'mktx', 'paytoch', 'payfromto']: + if cmd == 'payfromto': + #temporally import key and remove the other addresses + keypair = args[1] + wallet.imported_keys = {} + if keypair.find(":") == -1: + keypair = keypair + ":" + getpass.getpass('Private key:') + if not wallet.import_key(keypair,password): + print "invalid key pair" + exit(1) + addr = wallet.imported_keys.keys()[0] + wallet.history[addr] = wallet.interface.retrieve_history(addr) + wallet.synchronize() + wallet.update_tx_history() + wallet.addresses = [] + wallet.change_addresses = [] + change_addr = addr + save = False + else: + save = True for k, v in wallet.labels.items(): if v == to_address: to_address = k print "alias", to_address break + if change_addr and v == change_addr: + change_addr = k try: - tx = wallet.mktx( to_address, amount, label, password, fee = options.tx_fee ) + tx = wallet.mktx( to_address, amount, label, password, + fee = options.tx_fee, change_addr = change_addr, save = save ) except BaseException, e: print e tx = None - if tx and cmd=='payto': + if tx and cmd in ['payto', 'paytoch', 'payfromto']: r, h = wallet.sendtx( tx ) print h else: diff --git a/client/wallet.py b/client/wallet.py index ea133b615..9123e5028 100644 --- a/client/wallet.py +++ b/client/wallet.py @@ -618,12 +618,14 @@ class Wallet: inputs = [] return inputs, total, fee - def choose_tx_outputs( self, to_addr, amount, fee, total ): + def choose_tx_outputs( self, to_addr, amount, fee, total, change_addr=None ): outputs = [ (to_addr, amount) ] change_amount = total - ( amount + fee ) if change_amount != 0: # normally, the update thread should ensure that the last change address is unused - outputs.append( ( self.change_addresses[-1], change_amount) ) + if not change_addr: + change_addr = self.change_addresses[-1] + outputs.append( ( change_addr, change_amount) ) return outputs def sign_inputs( self, inputs, outputs, password ): @@ -702,13 +704,13 @@ class Wallet: default_label = 'at: ' + o_addr tx['default_label'] = default_label - def mktx(self, to_address, amount, label, password, fee=None): + def mktx(self, to_address, amount, label, password, fee=None, change_addr=None, save=True): if not self.is_valid(to_address): raise BaseException("Invalid address") inputs, total, fee = self.choose_tx_inputs( amount, fee ) if not inputs: raise BaseException("Not enough funds") - outputs = self.choose_tx_outputs( to_address, amount, fee, total ) + outputs = self.choose_tx_outputs( to_address, amount, fee, total, change_addr ) s_inputs = self.sign_inputs( inputs, outputs, password ) tx = filter( raw_tx( s_inputs, outputs ) ) @@ -717,7 +719,8 @@ class Wallet: if label: tx_hash = Hash(tx.decode('hex') )[::-1].encode('hex') self.labels[tx_hash] = label - self.save() + if save: + self.save() return tx def sendtx(self, tx): From a00b1e73a2c83f1528b9ee674ecef1a57f534e82 Mon Sep 17 00:00:00 2001 From: DiThi Date: Wed, 8 Feb 2012 09:36:19 +0100 Subject: [PATCH 2/2] Changed the new commands to be options, as suggested by ThomasV --- client/electrum | 49 +++++++++++++++++++------------------------------ 1 file changed, 19 insertions(+), 30 deletions(-) diff --git a/client/electrum b/client/electrum index d373de005..fbee56ac5 100755 --- a/client/electrum +++ b/client/electrum @@ -31,7 +31,7 @@ urldecode = lambda x: _ud.sub(lambda m: chr(int(m.group(1), 16)), x) from wallet import format_satoshis if __name__ == '__main__': - known_commands = ['help', 'validateaddress', 'balance', 'contacts', 'create', 'payto', 'paytoch', 'payfromto', 'sendtx', 'password', 'newaddress', 'addresses', 'history', 'label', 'gui', 'mktx','seed','import','signmessage','verifymessage','eval'] + known_commands = ['help', 'validateaddress', 'balance', 'contacts', 'create', 'payto', 'sendtx', 'password', 'newaddress', 'addresses', 'history', 'label', 'gui', 'mktx','seed','import','signmessage','verifymessage','eval'] usage = "usage: %prog [options] command args\nCommands: "+ (', '.join(known_commands)) @@ -41,6 +41,8 @@ if __name__ == '__main__': parser.add_option("-b", "--balance", action="store_true", dest="show_balance", default=False, help="show the balance at listed addresses") parser.add_option("-k", "--keys",action="store_true", dest="show_keys",default=False, help="show the private keys of listed addresses") parser.add_option("-f", "--fee", dest="tx_fee", default="0.005", help="set tx fee") + parser.add_option("-s", "--fromaddr", dest="from_addr", default=None, help="set source address for payto/mktx. 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.") + parser.add_option("-c", "--changeaddr", dest="change_addr", default=None, help="set the change address for payto/mktx. default is a spare address, or the source address if it's not in the wallet") options, args = parser.parse_args() try: cmd = args[0] @@ -137,19 +139,12 @@ if __name__ == '__main__': wallet.synchronize() # check syntax - if cmd in ['payto', 'mktx', 'paytoch', 'payfromto']: - offset = 0 - if cmd == 'payfromto': - offset = 1 + if cmd in ['payto', 'mktx']: try: - to_address = args[offset+1] - amount = int( 100000000 * Decimal(args[offset+2]) ) + to_address = args[1] + amount = int( 100000000 * Decimal(args[2]) ) change_addr = None - if cmd == 'paytoch': - change_addr = args[offset+3] - label = ' '.join(args[offset+4:]) - else: - label = ' '.join(args[offset+3:]) + label = ' '.join(args[3:]) if options.tx_fee: options.tx_fee = int( 100000000 * Decimal(options.tx_fee) ) except: @@ -163,7 +158,7 @@ if __name__ == '__main__': wallet.save() # commands needing password - if cmd in ['payto', 'paytoch', 'payfromto', 'password', 'mktx', 'seed', 'import','signmessage' ] or ( cmd=='addresses' and options.show_keys): + if cmd in ['payto', 'password', 'mktx', 'seed', 'import','signmessage' ] or ( cmd=='addresses' and options.show_keys): password = getpass.getpass('Password:') if wallet.use_encryption else None # check password try: @@ -194,16 +189,7 @@ if __name__ == '__main__': print "payto [label]" print "create and broadcast a transaction." print " can be a bitcoin address or a label" - elif cmd2 == 'paytoch': - print "paytoch [label]" - print "create and broadcast a transaction, returning change to a custom address." - print " and can be a bitcoin address or a label" - elif cmd2 == 'payfromto': - print "payfromto [:] " - print "create and broadcast a transaction, from the address to , without saving the address to the wallet." - print "it sends back the change to the same source address." - print " can be a bitcoin address or a label." - print "if you omit , it will ask you for the key." + print "options: --fromaddr, --changeaddr" elif cmd2== 'sendtx': print "sendtx " print "broadcast a transaction to the network. must be in hexadecimal" @@ -222,6 +208,7 @@ if __name__ == '__main__': elif cmd2 == 'mktx': print "create a signed transaction. password protected" print "syntax: mktx [label]" + print "options: --fromaddr, --changeaddr" elif cmd2 == 'seed': print "show generation seed of your wallet. password protected." elif cmd2 == 'eval': @@ -313,14 +300,14 @@ if __name__ == '__main__': wallet.labels[tx] = label wallet.save() - elif cmd in ['payto', 'mktx', 'paytoch', 'payfromto']: - if cmd == 'payfromto': + elif cmd in ['payto', 'mktx']: + if options.from_addr: #temporally import key and remove the other addresses - keypair = args[1] + addr = options.from_addr + if addr.find(":") == -1: + addr = addr + ":" + getpass.getpass('Private key:') wallet.imported_keys = {} - if keypair.find(":") == -1: - keypair = keypair + ":" + getpass.getpass('Private key:') - if not wallet.import_key(keypair,password): + if not wallet.import_key(options.from_addr,password): print "invalid key pair" exit(1) addr = wallet.imported_keys.keys()[0] @@ -333,6 +320,8 @@ if __name__ == '__main__': save = False else: save = True + if options.change_addr: + change_addr = options.change_addr for k, v in wallet.labels.items(): if v == to_address: to_address = k @@ -347,7 +336,7 @@ if __name__ == '__main__': print e tx = None - if tx and cmd in ['payto', 'paytoch', 'payfromto']: + if tx and cmd=='payto': r, h = wallet.sendtx( tx ) print h else: