TX scripts updates; import address feature added

new file:   mmgen-addrimport
	new file:   mmgen/connection.py
	new file:   mmgen/proxy.py
This commit is contained in:
The MMGen Project 2013-12-08 07:36:49 +04:00
commit aa66a44066
17 changed files with 1278 additions and 216 deletions

View file

@ -1,8 +1,10 @@
# file GENERATED by distutils, do NOT edit
mmgen-addrgen
mmgen-addrimport
mmgen-keygen
mmgen-passchg
mmgen-txcreate
mmgen-txsend
mmgen-txsign
mmgen-walletchk
mmgen-walletgen
@ -12,6 +14,7 @@ mmgen/__init__.py
mmgen/addr.py
mmgen/bitcoin.py
mmgen/config.py
mmgen/connection.py
mmgen/license.py
mmgen/mn_electrum.py
mmgen/mn_tirosh.py

View file

@ -1 +0,0 @@
mmgen-keygen

173
mmgen-addrgen Executable file
View file

@ -0,0 +1,173 @@
#!/usr/bin/env python
#
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
# Copyright (C) 2013 by philemon <mmgen-py@yandex.com>
#
# 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/>.
"""
mmgen-addrgen: Generate a range of addresses from a mmgen deterministic
wallet.
Call as 'btc-keygen' to allow key generation as well.
"""
import sys
from mmgen.Opts import *
from mmgen.config import *
from mmgen.license import *
from mmgen.utils import *
from mmgen.addr import *
invoked_as = sys.argv[0].split("-")[-1]
gen_what = "addresses" if invoked_as == "addrgen" else "keys"
if invoked_as == "keygen":
extra_help_data = (
"\n-A, --no-addresses Print only secret keys, no addresses",
"\n-x, --b16 Print secret keys in hexadecimal too",
".\nBy default, both addresses and secret keys are generated"
)
else: extra_help_data = ("","","")
help_data = {
'prog_name': sys.argv[0].split("/")[-1],
'desc': """Generate a range of {} from a {} wallet, mnemonic,
seed or password""".format(gen_what,proj_name),
'usage':"[opts] [infile] <address range>",
'options': """
-h, --help Print this help message{}
-d, --outdir d Specify an alternate directory 'd' for output
-e, --echo-passphrase Display passphrase or mnemonic on screen upon entry
-K, --no-keyconv Use internal libraries for address generation
instead of 'keyconv'
-l, --seed-len N Length of seed. Options: {}
(default: {})
-p, --hash-preset p Use scrypt.hash() parameters from preset 'p'
when hashing password (default: '{}')
-P, --show-hash-presets Show information on available hash presets
-q, --quiet Suppress warnings; overwrite files without asking
-S, --stdout Print {W} to stdout
-v, --verbose Produce more verbose output{}
-b, --from-brain l,p Generate {W} from a user-created password,
i.e. a "brainwallet", using seed length 'l' and
hash preset 'p' (comma-separated)
-m, --from-mnemonic Generate {W} from an electrum-like mnemonic
-s, --from-seed Generate {W} from a seed in .{S} format
Address range may be a single number or a range in the form XXX-YYY{}
If available, external 'keyconv' program will be used for address generation
Data for the --from-<what> options will be taken from <infile> if <infile>
is specified. Otherwise, the user will be prompted to enter the data. Note
that passphrase data in a file may be arranged in free-form fashion, using
any combination of spaces, tabs or newlines to separate words
BRAINWALLET NOTE:
As brainwallets require especially strong hashing to thwart dictionary
attacks, the brainwallet hash preset must be specified by the user, using
the 'p' parameter of the '--from-brain' option
The '--from-brain' option also requires the user to specify a seed length
(the 'l' parameter)
For a brainwallet passphrase to always generate the same keys and addresses,
the same 'l' and 'p' parameters to '--from-brain' must be used in all future
invocations with that passphrase
""".format(
extra_help_data[0],
", ".join([str(i) for i in seed_lens]),
seed_len,
hash_preset,
extra_help_data[1],
extra_help_data[2],
W=gen_what,
S=seed_ext
)
}
so = "h","A","d:","e","K","l:","p:","P","q","S","v","x","b:","m","s"
lo = "help","no_addresses","outdir=","echo_passphrase","no_keyconv",\
"seed_len=","hash_preset=","show_hash_presets","quiet","stdout",\
"verbose","b16","from_brain=","from_mnemonic","from_seed"
if invoked_as == "addrgen":
short_opts = so[0:1] + so[2:10] + so[11:]
long_opts = lo[0:1] + lo[2:10] + lo[11:]
else:
short_opts,long_opts = so,lo
opts,cmd_args = process_opts(sys.argv,help_data,"".join(short_opts),long_opts)
if 'show_hash_presets' in opts: show_hash_presets()
# Sanity checking and processing of command-line arguments:
opts['gen_what'] = gen_what
set_if_unset_and_typeconvert(opts,(
('hash_preset',hash_preset,'str'),
('seed_len',seed_len,'int')
))
# Exits on invalid input
check_opts(opts,('hash_preset','seed_len','outdir','from_brain'))
if debug:
print "Processed options: %s" % repr(opts)
print "Cmd args: %s" % repr(cmd_args)
if len(cmd_args) == 1 and (
'from_mnemonic' in opts or
'from_brain' in opts or
'from_seed' in opts
): infile,addr_range = "",cmd_args[0]
elif len(cmd_args) == 2:
infile,addr_range = cmd_args
check_infile(infile)
else: usage(help_data)
start,end = parse_address_range(addr_range)
if not 'quiet' in opts: do_license_msg()
# Interact with user:
if invoked_as == "keygen" and not 'quiet' in opts:
confirm_or_exit(cmessages['unencrypted_secret_keys'], 'continue')
# Generate data:
if invoked_as == "addrgen":
opts['print_addresses_only'] = True
else:
if not 'no_addresses' in opts: opts['print_secret'] = True
seed = get_seed(infile,opts)
seed_id = make_chksum_8(seed)
addr_data = generate_addrs(seed, start, end, opts)
addr_data_str = format_addr_data(addr_data, seed_id, opts)
# Output data:
if 'stdout' in opts:
if invoked_as == "keygen" and not 'quiet' in opts:
confirm = True
else: confirm = False
write_to_stdout(addr_data_str,"secret keys",confirm)
elif not sys.stdout.isatty():
write_to_stdout(addr_data_str,"secret keys",confirm=False)
else:
write_addr_data_to_file(seed, addr_data_str, start, end, opts)

69
mmgen-addrimport Executable file
View file

@ -0,0 +1,69 @@
#!/usr/bin/env python
#
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
# Copyright (C) 2013 by philemon <mmgen-py@yandex.com>
#
# 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/>.
"""
mmgen-addrimport: Import addresses generated by mmgen-addrgen into an
online bitcoind watching wallet.
"""
import sys
from mmgen.Opts import *
from mmgen.config import *
from mmgen.license import *
from mmgen.utils import check_infile,parse_addrs_file,confirm_or_exit
help_data = {
'prog_name': sys.argv[0].split("/")[-1],
'desc': """Import addresses generated by mmgen-addrgen into an
online bitcoind watching wallet""",
'usage':"[opts] [infile]",
'options': """
-h, --help Print this help message
-q, --quiet Suppress warnings
-v, --verbose Produce more verbose output
"""
}
short_opts = "hqv"
long_opts = "help", "quiet", "verbose"
opts,cmd_args = process_opts(sys.argv,help_data,"".join(short_opts),long_opts)
if len(cmd_args) != 1: usage(help_data)
check_infile(cmd_args[0])
seed_id,addr_data = parse_addrs_file(cmd_args[0])
from mmgen.tx import connect_to_bitcoind
c = connect_to_bitcoind(mmgen=True)
message = """
Importing addresses can take a long time, up to 30 min. per address on a
low-powered computer such as a netbook.
"""
confirm_or_exit(message, "continue", expect="YES")
for n,i in enumerate(addr_data):
comment = " " + " ".join(i[2:]) if len(i) > 2 else ""
label = "%s:%s%s" % (seed_id,str(i[0]),comment)
msg("Importing %-6s %-34s (%s)" % (
("%s/%s:" % (n+1,len(addr_data))),
i[1], label)
)
c.importaddress(i[1],label)

View file

@ -1,174 +0,0 @@
#!/usr/bin/env python
#
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
# Copyright (C) 2013 by philemon <mmgen-py@yandex.com>
#
# 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/>.
"""
mmgen-keygen: Generate addresses/secret keys from a mmgen deterministic
wallet for a range of addresses.
Call as 'btc-addrgen' for safe, reduced functionality
with no output of secret keys.
"""
import sys
from mmgen.Opts import *
from mmgen.config import *
from mmgen.license import *
from mmgen.utils import *
from mmgen.addr import *
invoked_as = sys.argv[0].split("-")[-1]
gen_what = "addresses" if invoked_as == "addrgen" else "keys"
if invoked_as == "keygen":
extra_help_data = (
"\n-A, --no-addresses Print only secret keys, no addresses",
"\n-x, --b16 Print secret keys in hexadecimal too",
".\nBy default, both addresses and secret keys are generated"
)
else: extra_help_data = ("","","")
help_data = {
'prog_name': sys.argv[0].split("/")[-1],
'desc': """Generate a range of {} from a {} wallet, mnemonic,
seed or password""".format(gen_what,proj_name),
'usage':"[opts] [infile] <address range>",
'options': """
-h, --help Print this help message{}
-d, --outdir d Specify an alternate directory 'd' for output
-e, --echo-passphrase Display passphrase or mnemonic on screen upon entry
-K, --no-keyconv Use internal libraries for address generation
instead of 'keyconv'
-l, --seed-len N Length of seed. Options: {}
(default: {})
-p, --hash-preset p Use scrypt.hash() parameters from preset 'p'
when hashing password (default: '{}')
-P, --show-hash-presets Show information on available hash presets
-q, --quiet Suppress warnings; overwrite files without asking
-S, --stdout Print {W} to stdout
-v, --verbose Produce more verbose output{}
-b, --from-brain l,p Generate {W} from a user-created password,
i.e. a "brainwallet", using seed length 'l' and
hash preset 'p' (comma-separated)
-m, --from-mnemonic Generate {W} from an electrum-like mnemonic
-s, --from-seed Generate {W} from a seed in .{S} format
Address range may be a single number or a range in the form XXX-YYY{}
If available, external 'keyconv' program will be used for address generation
Data for the --from-<what> options will be taken from <infile> if <infile>
is specified. Otherwise, the user will be prompted to enter the data. Note
that passphrase data in a file may be arranged in free-form fashion, using
any combination of spaces, tabs or newlines to separate words
BRAINWALLET NOTE:
As brainwallets require especially strong hashing to thwart dictionary
attacks, the brainwallet hash preset must be specified by the user, using
the 'p' parameter of the '--from-brain' option
The '--from-brain' option also requires the user to specify a seed length
(the 'l' parameter)
For a brainwallet passphrase to always generate the same keys and addresses,
the same 'l' and 'p' parameters to '--from-brain' must be used in all future
invocations with that passphrase
""".format(
extra_help_data[0],
", ".join([str(i) for i in seed_lens]),
seed_len,
hash_preset,
extra_help_data[1],
extra_help_data[2],
W=gen_what,
S=seed_ext
)
}
so = "h","A","d:","e","K","l:","p:","P","q","S","v","x","b:","m","s"
lo = "help","no_addresses","outdir=","echo_passphrase","no_keyconv",\
"seed_len=","hash_preset=","show_hash_presets","quiet","stdout",\
"verbose","b16","from_brain=","from_mnemonic","from_seed"
if invoked_as == "addrgen":
short_opts = so[0:1] + so[2:10] + so[11:]
long_opts = lo[0:1] + lo[2:10] + lo[11:]
else:
short_opts,long_opts = so,lo
opts,cmd_args = process_opts(sys.argv,help_data,"".join(short_opts),long_opts)
if 'show_hash_presets' in opts: show_hash_presets()
# Sanity checking and processing of command-line arguments:
opts['gen_what'] = gen_what
set_if_unset_and_typeconvert(opts,(
('hash_preset',hash_preset,'str'),
('seed_len',seed_len,'int')
))
# Exits on invalid input
check_opts(opts,('hash_preset','seed_len','outdir','from_brain'))
if debug:
print "Processed options: %s" % repr(opts)
print "Cmd args: %s" % repr(cmd_args)
if len(cmd_args) == 1 and (
'from_mnemonic' in opts or
'from_brain' in opts or
'from_seed' in opts
): infile,addr_range = "",cmd_args[0]
elif len(cmd_args) == 2:
infile,addr_range = cmd_args
check_infile(infile)
else: usage(help_data)
start,end = parse_address_range(addr_range)
if not 'quiet' in opts: do_license_msg()
# Interact with user:
if invoked_as == "keygen" and not 'quiet' in opts:
confirm_or_exit(cmessages['unencrypted_secret_keys'], 'continue')
# Generate data:
if invoked_as == "addrgen":
opts['print_addresses_only'] = True
else:
if not 'no_addresses' in opts: opts['print_secret'] = True
seed = get_seed(infile,opts)
seed_id = make_chksum_8(seed)
addr_data = generate_addrs(seed, start, end, opts)
addr_data_str = format_addr_data(addr_data, seed_id, opts)
# Output data:
if 'stdout' in opts:
if invoked_as == "keygen" and not 'quiet' in opts:
confirm = True
else: confirm = False
write_to_stdout(addr_data_str,"secret keys",confirm)
elif not sys.stdout.isatty():
write_to_stdout(addr_data_str,"secret keys",confirm=False)
else:
write_addr_data_to_file(seed, addr_data_str, start, end, opts)

1
mmgen-keygen Symbolic link
View file

@ -0,0 +1 @@
mmgen-addrgen

View file

@ -35,7 +35,7 @@ help_data = {
-d, --outdir d Specify an alternate directory 'd' for output
-k, --keep-old-passphrase Keep old passphrase (use when changing hash
strength or label only)
-L, --label Change the wallet's label
-L, --label l Change the wallet's label to 'l'
-p, --hash-preset p Change scrypt.hash() parameters to preset 'p'
(default: '{}')
-P, --show-hash-presets Show information on available hash presets
@ -65,7 +65,8 @@ infile = cmd_args[0]
# Old key:
label,metadata,hash_preset,salt,enc_seed = get_data_from_wallet(infile,opts)
seed_id,key_id = metadata[:2]
passwd = " ".join(get_words("","","Enter old passphrase: ",opts))
oldp = "" if 'keep_old_passphrase' in opts else "old "
passwd = " ".join(get_words("","",("Enter %spassphrase: " % oldp),opts))
key = make_key(passwd, salt, hash_preset)
seed = decrypt_seed(enc_seed, key, seed_id, key_id)

View file

@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
mmgen-txcreate: Send BTC from specified outputs to specified addresses
mmgen-txcreate: Send BTC to specified addresses
"""
import sys
@ -33,8 +33,8 @@ prog_name = sys.argv[0].split("/")[-1]
help_data = {
'prog_name': prog_name,
'desc': "Send BTC from specified outputs to specified addresses",
'usage': "[opts] <recipient address> <amount> <transaction fee> <change address>",
'desc': "Send BTC to specified addresses",
'usage': "[opts] <address:amount>[,...] <transaction fee> <change address>",
'options': """
-h, --help Print this help message
-d, --outdir d Specify an alternate directory 'd' for output
@ -42,6 +42,8 @@ help_data = {
-i, --info Display unspent outputs and exit
-q, --quiet Suppress warnings; overwrite files without asking
Outputs to spend are chosen by the user via a menu.
Ages of transactions are approximate based on an estimated block discovery
time of %s minutes.
""" % mins_per_block
@ -59,19 +61,20 @@ if debug:
print "Processed options: %s" % repr(opts)
print "Cmd args: %s" % repr(cmd_args)
if len(cmd_args) == 4:
rcpt_addr,send_amt,tx_fee,change_addr = cmd_args
if len(cmd_args) == 3:
rcpt_arg,tx_fee,change_addr = cmd_args
check_address(change_addr)
elif len(cmd_args) == 3:
rcpt_addr,send_amt,tx_fee = cmd_args
elif len(cmd_args) == 2:
rcpt_arg,tx_fee = cmd_args
change_addr = ""
elif len(cmd_args) == 0 and 'info' in opts:
pass
else: usage(help_data)
if not 'info' in opts:
check_address(rcpt_addr)
send_amt = check_btc_amt(send_amt)
tx_out = make_tx_out(rcpt_arg)
for i in tx_out.keys(): check_address(i)
for i in tx_out.values(): check_btc_amt(i)
tx_fee = check_btc_amt(tx_fee)
# Begin execution
@ -86,10 +89,10 @@ total = trim_exponent(sum([i.amount for i in unspent]))
msg("Total unspent: %s BTC" % total)
if 'info' in opts: sys.exit(0)
msg("Amount to spend: %s BTC" % send_amt)
send_amt = sum(tx_out.values())
msg("Total amount to spend: %s BTC" % send_amt)
msg("%s unspent outputs total" % len(unspent))
while True:
sel_unspent = select_outputs(unspent,"Choose the outputs to spend: ")
total_in = trim_exponent(sum([o.amount for o in sel_unspent]))
@ -107,7 +110,8 @@ if change > 0 and not change_addr:
sys.exit(2)
tx_in = [{"txid":i.txid, "vout":i.vout} for i in sel_unspent]
tx_out = {rcpt_addr:float(send_amt), change_addr:float(change)}
for i in tx_out.keys(): tx_out[i] = float(tx_out[i])
if change: tx_out[change_addr] = float(change)
tx_hex = c.createrawtransaction(tx_in,tx_out)
msg("Transaction successfully created")

View file

@ -83,7 +83,7 @@ confirm_or_exit(warn, what, expect)
msg("Sending transaction")
c = connect_to_bitcoind()
c = connect_to_bitcoind(mmgen=True)
try:
tx = c.sendrawtransaction(tx_sig)

View file

@ -118,7 +118,7 @@ if wallet_enc:
if sig_tx['complete']:
msg("Signing completed")
else:
msg("signrawtransaction() returned failure")
msg("Signing failed: 'complete=%s'" % sig_tx['complete'])
sys.exit(3)
prompt = "Save signed transaction?"

View file

@ -49,9 +49,9 @@ opts,cmd_args = Opts.process_opts(sys.argv,help_data,short_opts,long_opts)
check_opts(opts, ('outdir',))
if len(cmd_args) != 1:
msg("One input file must be specified")
sys.exit(2)
if len(cmd_args) != 1: usage(help_data)
check_infile(cmd_args[0])
if 'export_mnemonic' in opts:
msg("Exporting mnemonic data to file by user request")

View file

@ -51,7 +51,7 @@ b58a='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
# The "zero address":
# 1111111111111111111114oLvT2 (use step2 = ("0" * 40) to generate)
#
def _pubhex2addr(pubhex):
def pubhex2addr(pubhex):
step1 = sha256(unhexlify(pubhex)).digest()
step2 = hashlib_new('ripemd160',step1).hexdigest()
# See above:
@ -64,28 +64,32 @@ def _pubhex2addr(pubhex):
def privnum2addr(numpriv):
pko = ecdsa.SigningKey.from_secret_exponent(numpriv,secp256k1)
pubkey = hexlify(pko.get_verifying_key().to_string())
return _pubhex2addr('04'+pubkey)
return pubhex2addr('04'+pubkey)
def verify_addr(addr):
if addr[0] != "1":
print "%s Invalid address" % addr
print "%s: Invalid address" % addr
return False
addr,lz = addr[1:],0
while addr[0] == "1": addr = addr[1:]; lz += 1
addr_hex = lz * "00" + hex(_b58tonum(addr))[2:].rstrip("L")
# addr,lz = addr[1:],0
# while addr[0] == "1": addr = addr[1:]; lz += 1
#
# addr_hex = lz * "00" + hex(_b58tonum(addr))[2:].rstrip("L")
if len(addr_hex) != 48:
print "%s Invalid address" % addr
return False
# if len(addr_hex) != 48:
# print "%s: Invalid address hex length: %s" % ("1"+addr, len(addr_hex))
# return False
num = _b58tonum(addr[1:])
if num == False: return False
addr_hex = hex(num)[2:].rstrip("L").zfill(48)
step1 = sha256(unhexlify('00'+addr_hex[:40])).digest()
step2 = sha256(step1).hexdigest()
if step2[:8] != addr_hex[40:]:
print "Invalid checksum in address %s" % addr
print "Invalid checksum in address %s" % ("1" + addr)
return False
return True
@ -104,7 +108,7 @@ def _b58tonum(b58num):
for i in b58num:
if not i in b58a:
print "Invalid symbol in b58 number: '%s'" % i
sys.exit(9)
return False
b58deconv = []
b58num_r = b58num[::-1]
@ -135,6 +139,7 @@ def b58decode(b58num):
if b58num == "": return ""
# Zap all spaces:
num = _b58tonum(b58num.translate(None,' \t\n\r'))
if num == False: return False
out = hex(num)[2:].rstrip('L')
return unhexlify("0" + out if len(out) % 2 else out)
@ -149,9 +154,10 @@ def _b58_pad(s,a,b,pad,f,w):
except:
print "_b58_pad() accepts only %s %s bytes long "\
"(input was %s bytes)" % (w,",".join([str(i) for i in a]),len(s))
sys.exit(9)
return False
out = f(s)
if out == False: return False
return "%s%s" % (pad * (outlen - len(out)), out)
def b58encode_pad(s):
@ -168,10 +174,13 @@ def b58decode_pad(s):
# To check validity, recode with numtowif()
def wiftonum(wifpriv):
num = _b58tonum(wifpriv)
if num == False: return False
return (num % (1<<288)) >> 32
def wiftohex(wifpriv):
key = hex(_b58tonum(wifpriv))[2:].rstrip('L')
num = _b58tonum(wifpriv)
if num == False: return False
key = hex(num)[2:].rstrip('L')
round1 = sha256(unhexlify(key[:66])).digest()
round2 = sha256(round1).hexdigest()
return key[2:66] if (key[:2] == '80' and key[66:] == round2[:8]) else False

737
mmgen/connection.py Executable file
View file

@ -0,0 +1,737 @@
# Copyright (c) 2010 Witchspace <witchspace81@gmail.com>
#
# 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:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# 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.
"""
Connect to Bitcoin server via JSON-RPC.
"""
from mmgen.proxy import JSONRPCException, AuthServiceProxy
from bitcoinrpc.exceptions import (_wrap_exception, WalletPassphraseIncorrect,
WalletAlreadyUnlocked)
from bitcoinrpc.data import (ServerInfo, AccountInfo, AddressInfo,
TransactionInfo, AddressValidation, WorkItem, MiningInfo)
class MMGenBitcoinConnection(object):
"""
A BitcoinConnection object defines a connection to a bitcoin server.
It is a thin wrapper around a JSON-RPC API connection.
Up-to-date for SVN revision 198.
Arguments to constructor:
- *user* -- Authenticate as user.
- *password* -- Authentication password.
- *host* -- Bitcoin JSON-RPC host.
- *port* -- Bitcoin JSON-RPC port.
"""
def __init__(self, user, password, host='localhost', port=8332,
use_https=False):
"""
Create a new bitcoin server connection.
"""
url = 'http{s}://{user}:{password}@{host}:{port}/'.format(
s='s' if use_https else '',
user=user, password=password, host=host, port=port)
self.url = url
try:
self.proxy = AuthServiceProxy(url)
except JSONRPCException as e:
raise _wrap_exception(e.error)
# importaddress <address> [label] [rescan=true]
def importaddress(self,address,label=None):
"""
"""
try:
return self.proxy.importaddress(address,label)
except JSONRPCException as e:
raise _wrap_exception(e.error)
# sendrawtransaction <hex string> [allowhighfees=false]
def sendrawtransaction(self,tx):
"""
"""
try:
return self.proxy.sendrawtransaction(tx)
except JSONRPCException as e:
raise _wrap_exception(e.error)
# def getbalance(self):
# """
# Stop bitcoin server.
# """
# try:
# self.proxy.stop()
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
# def getblock(self, hash):
# """
# Returns information about the given block hash.
# """
# try:
# return self.proxy.getblock(hash)
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def getblockcount(self):
# """
# Returns the number of blocks in the longest block chain.
# """
# try:
# return self.proxy.getblockcount()
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def getblockhash(self, index):
# """
# Returns hash of block in best-block-chain at index.
#
# :param index: index ob the block
#
# """
# try:
# return self.proxy.getblockhash(index)
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def getblocknumber(self):
# """
# Returns the block number of the latest block in the longest block chain.
# Deprecated. Use getblockcount instead.
# """
# return self.getblockcount()
#
# def getconnectioncount(self):
# """
# Returns the number of connections to other nodes.
# """
# try:
# return self.proxy.getconnectioncount()
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def getdifficulty(self):
# """
# Returns the proof-of-work difficulty as a multiple of the minimum difficulty.
# """
# try:
# return self.proxy.getdifficulty()
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def getgenerate(self):
# """
# Returns :const:`True` or :const:`False`, depending on whether generation is enabled.
# """
# try:
# return self.proxy.getgenerate()
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def setgenerate(self, generate, genproclimit=None):
# """
# Enable or disable generation (mining) of coins.
#
# Arguments:
#
# - *generate* -- is :const:`True` or :const:`False` to turn generation on or off.
# - *genproclimit* -- Number of processors that are used for generation, -1 is unlimited.
#
# """
# try:
# if genproclimit is None:
# return self.proxy.setgenerate(generate)
# else:
# return self.proxy.setgenerate(generate, genproclimit)
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def gethashespersec(self):
# """
# Returns a recent hashes per second performance measurement while generating.
# """
# try:
# return self.proxy.gethashespersec()
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def getinfo(self):
# """
# Returns an :class:`~bitcoinrpc.data.ServerInfo` object containing various state info.
# """
# try:
# return ServerInfo(**self.proxy.getinfo())
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def getmininginfo(self):
# """
# Returns an :class:`~bitcoinrpc.data.MiningInfo` object containing various
# mining state info.
# """
# try:
# return MiningInfo(**self.proxy.getmininginfo())
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def getnewaddress(self, account=None):
# """
# Returns a new bitcoin address for receiving payments.
#
# Arguments:
#
# - *account* -- If account is specified (recommended), it is added to the address book
# so that payments received with the address will be credited to it.
#
# """
# try:
# if account is None:
# return self.proxy.getnewaddress()
# else:
# return self.proxy.getnewaddress(account)
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def getaccountaddress(self, account):
# """
# Returns the current bitcoin address for receiving payments to an account.
#
# Arguments:
#
# - *account* -- Account for which the address should be returned.
#
# """
# try:
# return self.proxy.getaccountaddress(account)
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def setaccount(self, bitcoinaddress, account):
# """
# Sets the account associated with the given address.
#
# Arguments:
#
# - *bitcoinaddress* -- Bitcoin address to associate.
# - *account* -- Account to associate the address to.
#
# """
# try:
# return self.proxy.setaccount(bitcoinaddress, account)
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def getaccount(self, bitcoinaddress):
# """
# Returns the account associated with the given address.
#
# Arguments:
#
# - *bitcoinaddress* -- Bitcoin address to get account for.
# """
# try:
# return self.proxy.getaccount(bitcoinaddress)
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def getaddressesbyaccount(self, account):
# """
# Returns the list of addresses for the given account.
#
# Arguments:
#
# - *account* -- Account to get list of addresses for.
# """
# try:
# return self.proxy.getaddressesbyaccount(account)
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def sendtoaddress(self, bitcoinaddress, amount, comment=None, comment_to=None):
# """
# Sends *amount* from the server's available balance to *bitcoinaddress*.
#
# Arguments:
#
# - *bitcoinaddress* -- Bitcoin address to send to.
# - *amount* -- Amount to send (float, rounded to the nearest 0.01).
# - *minconf* -- Minimum number of confirmations required for transferred balance.
# - *comment* -- Comment for transaction.
# - *comment_to* -- Comment for to-address.
#
# """
# try:
# if comment is None:
# return self.proxy.sendtoaddress(bitcoinaddress, amount)
# elif comment_to is None:
# return self.proxy.sendtoaddress(bitcoinaddress, amount, comment)
# else:
# return self.proxy.sendtoaddress(bitcoinaddress, amount, comment, comment_to)
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def getreceivedbyaddress(self, bitcoinaddress, minconf=1):
# """
# Returns the total amount received by a bitcoin address in transactions with at least a
# certain number of confirmations.
#
# Arguments:
#
# - *bitcoinaddress* -- Address to query for total amount.
#
# - *minconf* -- Number of confirmations to require, defaults to 1.
# """
# try:
# return self.proxy.getreceivedbyaddress(bitcoinaddress, minconf)
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def getreceivedbyaccount(self, account, minconf=1):
# """
# Returns the total amount received by addresses with an account in transactions with
# at least a certain number of confirmations.
#
# Arguments:
#
# - *account* -- Account to query for total amount.
# - *minconf* -- Number of confirmations to require, defaults to 1.
#
# """
# try:
# return self.proxy.getreceivedbyaccount(account, minconf)
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def gettransaction(self, txid):
# """
# Get detailed information about transaction
#
# Arguments:
#
# - *txid* -- Transactiond id for which the info should be returned
#
# """
# try:
# return TransactionInfo(**self.proxy.gettransaction(txid))
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def getrawtransaction(self, txid, verbose=True):
# """
# Get transaction raw info
#
# Arguments:
#
# - *txid* -- Transactiond id for which the info should be returned.
# - *verbose* -- If False, return only the "hex" of the transaction.
#
# """
# try:
# if verbose:
# return TransactionInfo(**self.proxy.getrawtransaction(txid, 1))
# return self.proxy.getrawtransaction(txid, 0)
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def createrawtransaction(self, inputs, outputs):
# """
# Creates a raw transaction spending given inputs
# (a list of dictionaries, each containing a transaction id and an output number),
# sending to given address(es).
#
# Returns hex-encoded raw transaction.
#
# Example usage:
# >>> conn.createrawtransaction(
# [{"txid": "a9d4599e15b53f3eb531608ddb31f48c695c3d0b3538a6bda871e8b34f2f430c",
# "vout": 0}],
# {"mkZBYBiq6DNoQEKakpMJegyDbw2YiNQnHT":50})
#
#
# Arguments:
#
# - *inputs* -- A list of {"txid": txid, "vout": n} dictionaries.
# - *outputs* -- A dictionary mapping (public) addresses to the amount
# they are to be paid.
# """
# try:
# return self.proxy.createrawtransaction(inputs, outputs)
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def signrawtransaction(self, hexstring, previous_transactions=None, private_keys=None):
# """
# Sign inputs for raw transaction (serialized, hex-encoded).
#
# Returns a dictionary with the keys:
# "hex": raw transaction with signature(s) (hex-encoded string)
# "complete": 1 if transaction has a complete set of signature(s), 0 if not
#
# Arguments:
#
# - *hexstring* -- A hex string of the transaction to sign.
# - *previous_transactions* -- A (possibly empty) list of dictionaries of the form:
# {"txid": txid, "vout": n, "scriptPubKey": hex, "redeemScript": hex}, representing
# previous transaction outputs that this transaction depends on but may not yet be
# in the block chain.
# - *private_keys* -- A (possibly empty) list of base58-encoded private
# keys that, if given, will be the only keys used to sign the transaction.
# """
# try:
# return dict(self.proxy.signrawtransaction(hexstring, previous_transactions, private_keys))
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def decoderawtransaction(self, hexstring):
# """
# Produces a human-readable JSON object for a raw transaction.
#
# Arguments:
#
# - *hexstring* -- A hex string of the transaction to be decoded.
# """
# try:
# return dict(self.proxy.decoderawtransaction(hexstring))
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def listsinceblock(self, block_hash):
# try:
# res = self.proxy.listsinceblock(block_hash)
# res['transactions'] = [TransactionInfo(**x) for x in res['transactions']]
# return res
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def listreceivedbyaddress(self, minconf=1, includeempty=False):
# """
# Returns a list of addresses.
#
# Each address is represented with a :class:`~bitcoinrpc.data.AddressInfo` object.
#
# Arguments:
#
# - *minconf* -- Minimum number of confirmations before payments are included.
# - *includeempty* -- Whether to include addresses that haven't received any payments.
#
# """
# try:
# return [AddressInfo(**x) for x in
# self.proxy.listreceivedbyaddress(minconf, includeempty)]
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def listaccounts(self, minconf=1, as_dict=False):
# """
# Returns a list of account names.
#
# Arguments:
#
# - *minconf* -- Minimum number of confirmations before payments are included.
# - *as_dict* -- Returns a dictionary of account names, with their balance as values.
# """
# try:
# if as_dict:
# return dict(self.proxy.listaccounts(minconf))
# else:
# return self.proxy.listaccounts(minconf).keys()
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def listreceivedbyaccount(self, minconf=1, includeempty=False):
# """
# Returns a list of accounts.
#
# Each account is represented with a :class:`~bitcoinrpc.data.AccountInfo` object.
#
# Arguments:
#
# - *minconf* -- Minimum number of confirmations before payments are included.
#
# - *includeempty* -- Whether to include addresses that haven't received any payments.
# """
# try:
# return [AccountInfo(**x) for x in
# self.proxy.listreceivedbyaccount(minconf, includeempty)]
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def listtransactions(self, account=None, count=10, from_=0, address=None):
# """
# Returns a list of the last transactions for an account.
#
# Each transaction is represented with a :class:`~bitcoinrpc.data.TransactionInfo` object.
#
# Arguments:
#
# - *account* -- Account to list transactions from. Return transactions from
# all accounts if None.
# - *count* -- Number of transactions to return.
# - *from_* -- Skip the first <from_> transactions.
# - *address* -- Receive address to consider
# """
# accounts = [account] if account is not None else self.listaccounts(as_dict=True).iterkeys()
# try:
# return [TransactionInfo(**tx) for acc in accounts for
# tx in self.proxy.listtransactions(acc, count, from_) if
# address is None or tx["address"] == address]
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def backupwallet(self, destination):
# """
# Safely copies ``wallet.dat`` to *destination*, which can be a directory or a path
# with filename.
#
# Arguments:
# - *destination* -- directory or path with filename to backup wallet to.
#
# """
# try:
# return self.proxy.backupwallet(destination)
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def validateaddress(self, validateaddress):
# """
# Validate a bitcoin address and return information for it.
#
# The information is represented by a :class:`~bitcoinrpc.data.AddressValidation` object.
#
# Arguments: -- Address to validate.
#
#
# - *validateaddress*
# """
# try:
# return AddressValidation(**self.proxy.validateaddress(validateaddress))
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def getbalance(self, account=None, minconf=None):
# """
# Get the current balance, either for an account or the total server balance.
#
# Arguments:
# - *account* -- If this parameter is specified, returns the balance in the account.
# - *minconf* -- Minimum number of confirmations required for transferred balance.
#
# """
# args = []
# if account:
# args.append(account)
# if minconf is not None:
# args.append(minconf)
# try:
# return self.proxy.getbalance(*args)
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def move(self, fromaccount, toaccount, amount, minconf=1, comment=None):
# """
# Move from one account in your wallet to another.
#
# Arguments:
#
# - *fromaccount* -- Source account name.
# - *toaccount* -- Destination account name.
# - *amount* -- Amount to transfer.
# - *minconf* -- Minimum number of confirmations required for transferred balance.
# - *comment* -- Comment to add to transaction log.
#
# """
# try:
# if comment is None:
# return self.proxy.move(fromaccount, toaccount, amount, minconf)
# else:
# return self.proxy.move(fromaccount, toaccount, amount, minconf, comment)
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def sendfrom(self, fromaccount, tobitcoinaddress, amount, minconf=1, comment=None,
# comment_to=None):
# """
# Sends amount from account's balance to bitcoinaddress. This method will fail
# if there is less than amount bitcoins with minconf confirmations in the account's
# balance (unless account is the empty-string-named default account; it
# behaves like the sendtoaddress method). Returns transaction ID on success.
#
# Arguments:
#
# - *fromaccount* -- Account to send from.
# - *tobitcoinaddress* -- Bitcoin address to send to.
# - *amount* -- Amount to send (float, rounded to the nearest 0.01).
# - *minconf* -- Minimum number of confirmations required for transferred balance.
# - *comment* -- Comment for transaction.
# - *comment_to* -- Comment for to-address.
#
# """
# try:
# if comment is None:
# return self.proxy.sendfrom(fromaccount, tobitcoinaddress, amount, minconf)
# elif comment_to is None:
# return self.proxy.sendfrom(fromaccount, tobitcoinaddress, amount, minconf, comment)
# else:
# return self.proxy.sendfrom(fromaccount, tobitcoinaddress, amount, minconf,
# comment, comment_to)
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def sendmany(self, fromaccount, todict, minconf=1, comment=None):
# """
# Sends specified amounts from account's balance to bitcoinaddresses. This method will fail
# if there is less than total amount bitcoins with minconf confirmations in the account's
# balance (unless account is the empty-string-named default account; Returns transaction ID
# on success.
#
# Arguments:
#
# - *fromaccount* -- Account to send from.
# - *todict* -- Dictionary with Bitcoin addresses as keys and amounts as values.
# - *minconf* -- Minimum number of confirmations required for transferred balance.
# - *comment* -- Comment for transaction.
#
# """
# try:
# if comment is None:
# return self.proxy.sendmany(fromaccount, todict, minconf)
# else:
# return self.proxy.sendmany(fromaccount, todict, minconf, comment)
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def verifymessage(self, bitcoinaddress, signature, message):
# """
# Verifies a signature given the bitcoinaddress used to sign,
# the signature itself, and the message that was signed.
# Returns :const:`True` if the signature is valid, and :const:`False` if it is invalid.
#
# Arguments:
#
# - *bitcoinaddress* -- the bitcoinaddress used to sign the message
# - *signature* -- the signature to be verified
# - *message* -- the message that was originally signed
#
# """
# try:
# return self.proxy.verifymessage(bitcoinaddress, signature, message)
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def getwork(self, data=None):
# """
# Get work for remote mining, or submit result.
# If data is specified, the server tries to solve the block
# using the provided data and returns :const:`True` if it was successful.
# If not, the function returns formatted hash data (:class:`~bitcoinrpc.data.WorkItem`)
# to work on.
#
# Arguments:
#
# - *data* -- Result from remote mining.
#
# """
# try:
# if data is None:
# # Only if no data provided, it returns a WorkItem
# return WorkItem(**self.proxy.getwork())
# else:
# return self.proxy.getwork(data)
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def listunspent(self, minconf=1, maxconf=999999):
# """
# Returns a list of unspent transaction inputs in the wallet.
#
# Arguments:
#
# - *minconf* -- Minimum number of confirmations required to be listed.
#
# - *maxconf* -- Maximal number of confirmations allowed to be listed.
#
#
# """
# try:
# return [TransactionInfo(**tx) for tx in
# self.proxy.listunspent(minconf, maxconf)]
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def keypoolrefill(self):
# "Fills the keypool, requires wallet passphrase to be set."
# try:
# self.proxy.keypoolrefill()
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def walletpassphrase(self, passphrase, timeout, dont_raise=False):
# """
# Stores the wallet decryption key in memory for <timeout> seconds.
#
# - *passphrase* -- The wallet passphrase.
#
# - *timeout* -- Time in seconds to keep the wallet unlocked
# (by keeping the passphrase in memory).
#
# - *dont_raise* -- instead of raising `~bitcoinrpc.exceptions.WalletPassphraseIncorrect`
# return False.
# """
# try:
# self.proxy.walletpassphrase(passphrase, timeout)
# return True
# except JSONRPCException as e:
# json_exception = _wrap_exception(e.error)
# if dont_raise:
# if isinstance(json_exception, WalletPassphraseIncorrect):
# return False
# elif isinstance(json_exception, WalletAlreadyUnlocked):
# return True
# raise json_exception
#
# def walletlock(self):
# """
# Removes the wallet encryption key from memory, locking the wallet.
# After calling this method, you will need to call walletpassphrase
# again before being able to call any methods which require the wallet
# to be unlocked.
# """
# try:
# return self.proxy.walletlock()
# except JSONRPCException as e:
# raise _wrap_exception(e.error)
#
# def walletpassphrasechange(self, oldpassphrase, newpassphrase, dont_raise=False):
# """
# Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.
#
# Arguments:
#
# - *dont_raise* -- instead of raising `~bitcoinrpc.exceptions.WalletPassphraseIncorrect`
# return False.
# """
# try:
# self.proxy.walletpassphrasechange(oldpassphrase, newpassphrase)
# return True
# except JSONRPCException as e:
# json_exception = _wrap_exception(e.error)
# if dont_raise and isinstance(json_exception, WalletPassphraseIncorrect):
# return False
# raise json_exception

130
mmgen/proxy.py Executable file
View file

@ -0,0 +1,130 @@
"""
Copyright 2011 Jeff Garzik
AuthServiceProxy has the following improvements over python-jsonrpc's
ServiceProxy class:
- HTTP connections persist for the life of the AuthServiceProxy object
(if server supports HTTP/1.1)
- sends protocol 'version', per JSON-RPC 1.1
- sends proper, incrementing 'id'
- sends Basic HTTP authentication headers
- parses all JSON numbers that look like floats as Decimal
- uses standard Python json lib
Previous copyright, from python-jsonrpc/jsonrpc/proxy.py:
Copyright (c) 2007 Jan-Klaas Kollhof
This file is part of jsonrpc.
jsonrpc is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This software 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this software; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""
try:
import http.client as httplib
except ImportError:
import httplib
import base64
import json
import decimal
try:
import urllib.parse as urlparse
except ImportError:
import urlparse
USER_AGENT = "AuthServiceProxy/0.1"
HTTP_TIMEOUT = 7200
class JSONRPCException(Exception):
def __init__(self, rpcError):
Exception.__init__(self)
self.error = rpcError
class AuthServiceProxy(object):
def __init__(self, serviceURL, serviceName=None):
self.__serviceURL = serviceURL
self.__serviceName = serviceName
self.__url = urlparse.urlparse(serviceURL)
if self.__url.port is None:
port = 80
else:
port = self.__url.port
self.__idcnt = 0
authpair = "%s:%s" % (self.__url.username, self.__url.password)
authpair = authpair.encode('utf8')
self.__authhdr = "Basic ".encode('utf8') + base64.b64encode(authpair)
if self.__url.scheme == 'https':
self.__conn = httplib.HTTPSConnection(self.__url.hostname, port, None, None,False,
HTTP_TIMEOUT)
else:
self.__conn = httplib.HTTPConnection(self.__url.hostname, port, False,
HTTP_TIMEOUT)
def __getattr__(self, name):
if self.__serviceName != None:
name = "%s.%s" % (self.__serviceName, name)
return AuthServiceProxy(self.__serviceURL, name)
def __call__(self, *args):
self.__idcnt += 1
postdata = json.dumps({
'version': '1.1',
'method': self.__serviceName,
'params': args,
'id': self.__idcnt})
self.__conn.request('POST', self.__url.path, postdata,
{ 'Host' : self.__url.hostname,
'User-Agent' : USER_AGENT,
'Authorization' : self.__authhdr,
'Content-type' : 'application/json' })
httpresp = self.__conn.getresponse()
if httpresp is None:
raise JSONRPCException({
'code' : -342, 'message' : 'missing HTTP response from server'})
resp = httpresp.read()
resp = resp.decode('utf8')
resp = json.loads(resp, parse_float=decimal.Decimal)
if 'error' in resp and resp['error'] != None:
raise JSONRPCException(resp['error'])
elif 'result' not in resp:
raise JSONRPCException({
'code' : -343, 'message' : 'missing JSON-RPC result'})
else:
return resp['result']
def _batch(self, rpc_call_list):
postdata = json.dumps(list(rpc_call_list))
self.__conn.request('POST', self.__url.path, postdata,
{ 'Host' : self.__url.hostname,
'User-Agent' : USER_AGENT,
'Authorization' : self.__authhdr,
'Content-type' : 'application/json' })
httpresp = self.__conn.getresponse()
if httpresp is None:
raise JSONRPCException({
'code' : -342, 'message' : 'missing HTTP response from server'})
resp = httpresp.read()
resp = resp.decode('utf8')
resp = json.loads(resp, parse_float=decimal.Decimal)
return resp

View file

@ -22,7 +22,6 @@ tx.py: Bitcoin transaction routines
from binascii import unhexlify
from mmgen.utils import msg,msg_r,write_to_file,my_raw_input,get_char,make_chksum_8,make_timestamp
import sys, os
from bitcoinrpc.connection import *
from decimal import Decimal
from mmgen.config import *
@ -36,13 +35,21 @@ specified recipient address.
""".strip()
}
def connect_to_bitcoind():
def connect_to_bitcoind(mmgen=False):
host,port,user,passwd = "localhost",8332,"rpcuser","rpcpassword"
cfg = get_cfg_options((user,passwd))
if mmgen:
import mmgen.connection
f = mmgen.connection.MMGenBitcoinConnection
else:
import bitcoinrpc.connection
f = bitcoinrpc.connection.BitcoinConnection
try:
c = BitcoinConnection(cfg[user],cfg[passwd],host,port)
c = f(cfg[user],cfg[passwd],host,port)
except:
msg("Unable to establish RPC connection with bitcoind")
sys.exit(2)
@ -55,11 +62,13 @@ def trim_exponent(d):
'''
return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()
def check_address(rcpt_address):
from mmgen.bitcoin import verify_addr
if not verify_addr(rcpt_address):
sys.exit(3)
def check_btc_amt(send_amt):
from decimal import Decimal
@ -298,3 +307,23 @@ def select_outputs(unspent,prompt):
msg("'%s': Invalid input" % reply)
return [unspent[i] for i in selected]
def make_tx_out(rcpt_arg):
import decimal
try:
tx_out = dict([(i.split(":")[0],i.split(":")[1])
for i in rcpt_arg.split(",")])
except:
msg("Invalid format: %s" % rcpt_arg)
sys.exit(3)
try:
for i in tx_out.keys():
tx_out[i] = trim_exponent(Decimal(tx_out[i]))
except decimal.InvalidOperation:
msg("Decimal conversion error in suboption '%s:%s'" % (i,tx_out[i]))
sys.exit(3)
return tx_out

View file

@ -22,6 +22,7 @@ utils.py: Shared routines for the mmgen suite
import sys
from mmgen.config import *
from binascii import hexlify,unhexlify
from mmgen.bitcoin import b58decode_pad
def msg(s): sys.stderr.write(s + "\n")
def msg_r(s): sys.stderr.write(s)
@ -628,12 +629,14 @@ def get_data_from_wallet(infile,opts):
sys.exit(9)
res = {}
from mmgen.bitcoin import b58decode_pad
for i,key in (4,"salt"),(5,"enc_seed"):
l = lines[i].split()
val = "".join(l[1:])
_check_chksum_6(l[0], val, key, infile)
res[key] = b58decode_pad(val)
if res[key] == False:
msg("Invalid b58 number: %s" % val)
sys.exit(9)
_check_chksum_6(lines[0], " ".join(lines[1:]), "Master", infile)
@ -683,8 +686,11 @@ def get_seed_from_seed_data(words):
msg_r("Validating %s checksum..." % seed_ext)
if compare_checksums(chk, "from seed", stored_chk, "from input"):
from mmgen.bitcoin import b58decode_pad
seed = b58decode_pad(seed_b58)
if seed == False:
msg("Invalid b58 number: %s" % val)
sys.exit(9)
msg("%s data produces seed ID: %s" % (seed_ext,make_chksum_8(seed)))
return seed
else:
@ -778,5 +784,60 @@ def get_seed(infile,opts,no_wallet=False):
else:
return get_seed_from_wallet(infile, opts)
def remove_blanks_comments(lines):
import re
# re.sub(pattern, repl, string, count=0, flags=0)
ret = []
for i in lines:
i = re.sub('#.*','',i,1)
i = re.sub('\s+$','',i)
if i: ret.append(i)
return ret
def parse_addrs_file(f):
lines = get_lines_from_file(f,"address data")
lines = remove_blanks_comments(lines)
seed_id,obrace = lines[0].split()
cbrace = lines[-1]
if obrace != '{':
msg("'%s': invalid first line" % lines[0])
elif cbrace != '}':
msg("'%s': invalid last line" % cbrace)
elif len(seed_id) != 8:
msg("'%s': invalid Seed ID" % seed_id)
else:
try:
unhexlify(seed_id)
except:
msg("'%s': invalid Seed ID" % seed_id)
sys.exit(3)
ret = []
for i in lines[1:-1]:
d = i.split()
try: d[0] = int(d[0])
except:
msg("'%s': invalid address num. in line: %s" % (d[0],d))
sys.exit(3)
from mmgen.bitcoin import verify_addr
if not verify_addr(d[1]):
msg("'%s': invalid address" % d[1])
sys.exit(3)
ret.append(d)
return seed_id,ret
sys.exit(3)
if __name__ == "__main__":
print get_lines_from_file("/tmp/lines","test file")

3
scripts/deinstall.sh Executable file
View file

@ -0,0 +1,3 @@
#!/bin/bash
set -x
sudo rm -r /usr/local/bin/mmgen-* /usr/local/lib/python2.7/dist-packages/mmgen*

View file

@ -3,7 +3,7 @@ from distutils.core import setup
setup(
name = 'mmgen',
version = '0.6.1',
version = '0.6.2',
author = 'Philemon',
author_email = 'mmgen-py@yandex.com',
url = 'https://github.com/mmgen/mmgen',
@ -19,15 +19,27 @@ setup(
'mmgen.Opts',
'mmgen.tx',
'mmgen.utils',
'mmgen.walletgen'
'mmgen.walletgen',
'mmgen.connection',
'mmgen.proxy',
'tests.addr',
'tests.bitcoin',
'tests.mn_electrum',
'tests.mnemonic',
'tests.mn_tirosh',
'tests.test',
'tests.utils',
'tests.walletgen'
],
data_files=[('/usr/local/bin', [
'mmgen-addrgen',
'mmgen-addrimport',
'mmgen-keygen',
'mmgen-passchg',
'mmgen-walletchk',
'mmgen-walletgen',
'mmgen-txcreate',
'mmgen-txsign'
'mmgen-txsign',
'mmgen-txsend'
])]
)

View file

@ -168,6 +168,10 @@ def hextosha256(s_in):
s_enc = sha256(unhexlify(s_in)).hexdigest()
print "Encoded data: %s" % s_enc
def pubhextoaddr(s_in):
print "Entered data: %s" % s_in
s_enc = pubhex2addr(s_in)
print "Encoded data: %s" % s_enc
tests = {
"keyconv_compare": ['wif [str]','quiet [bool=False]'],
@ -183,6 +187,7 @@ tests = {
"numtowif_rand": ['quiet [bool=False]'],
"hextosha256": ['hexnum [str]','quiet [bool=False]'],
"hextowiftopubkey": ['hexnum [str]','quiet [bool=False]'],
"pubhextoaddr": ['hexnum [str]','quiet [bool=False]'],
}