Updated for the new watch-only address support in mainline bitcoind.

WARNING: As of this commit, the old sipa watchonly build is NO LONGER SUPPORTED
due to wallet incompatibility.

With the next release of Bitcoin Core, MMGen will work with the binary build of
bitcoind and building the daemon from source will become unnecessary.  For those
who don't wish to wait, updated build instructions are provided in the
documentation wiki.
This commit is contained in:
The MMGen Project 2014-08-09 23:08:18 +04:00
commit 45e921fc28
5 changed files with 54 additions and 53 deletions

View file

@ -5,32 +5,33 @@ MMGen = Multi-Mode GENerator
### Description
MMGen is a Bitcoin cold-storage system implemented as a suite of Python
command-line scripts requiring only a bare minimum of system resources. The
scripts work in tandem with a modified Satoshi bitcoind running on an online
computer and a stock bitcoind running offline to provide a robust solution for
command-line scripts that require only a bare minimum of system resources. The
scripts work in tandem with the reference Bitcoin Core daemon (bitcoind) running
on both an online and an offline computer to provide a robust solution for
securely storing, tracking, sending and receiving Bitcoins. "Non-MMGen"
addresses can be tracked and spent as well, creating an easy migration path from
other wallets.
To track address balances, MMGen relies on a bitcoin daemon modified by
Bitcoin core developers to support watch-only addresses. This feature
will one day be included in the mainline Satoshi build. In the meantime,
instructions are provided below for compiling the modified bitcoind. Under
Linux, this is a trivial task for even a casual user. Unfortunately, the same
can't be said for Windows, but the thoroughly tested, step-by-step Windows build
instructions provided here should guarantee success if followed carefully and
make the process as painless as possible.
To track address balances, MMGen relies on Bitcoin Core's newly included support
for watch-only addresses. Binary builds with this feature will become available
with the next release of Bitcoin Core. In the meantime, users can download the
Bitcoin source from the project's official repository on Github and compile it,
a trivial task on Linux. Compilation instructions for Windows are also
included, though Windows users may find it easier to wait for the binary from
the upcoming release.
MMGen is designed for reliability by having the reference Satoshi daemon, rather
than less-tested third-party software, do all the "heavy lifting" of tracking
and signing transactions. It's also designed for privacy: unlike some other
online/offline wallet solutions, the MMGen system is completely self-contained,
requiring no external server to do its work, which means no third party will
know which addresses you're tracking.
MMGen is designed for reliability by having the reference Bitcoin Core daemon,
rather than less-tested third-party software, do all the "heavy lifting" of
tracking and signing transactions. It's also designed for privacy: unlike some
other online/offline wallet solutions, MMGen plus Bitcoin Core is a **completely
self-contained system** requiring no external Internet resources except for the
Bitcoin network itself to do its work: no third parties are involved, and thus
no information regarding which addresses you're tracking is leaked to the
outside world.
Like all deterministic wallets, MMGen can generate a virtually unlimited number
of address/key pairs from a single seed. Your wallet never changes, so you need
to back it up only once. Transactions are signed offline: your seed and private
back it up only once. Transactions are signed offline: your seed and private
keys never touch an online computer.
At the heart of the MMGen system is the seed, the "master key" providing access
@ -65,7 +66,7 @@ mnemonic or seed or a lost seed from the wallet or mnemonic.
### Using MMGen
> #### See [Getting Started with MMGen][3]
> #### [Getting Started with MMGen][3]
> #### [MMGen command help][6]

View file

@ -467,7 +467,7 @@ ERROR: 'importaddress' method not found. Is your bitcoind enabled for watch-onl
"""
try:
if as_dict:
return dict(self.proxy.listaccounts(minconf))
return dict(self.proxy.listaccounts(minconf,True))
else:
return self.proxy.listaccounts(minconf).keys()
except JSONRPCException as e:

View file

@ -328,14 +328,13 @@ def listaddresses(minconf=1,showempty=False):
if key not in addrs: addrs[key] = [0,comment]
addrs[key][0] += d.amount
# "bitcoind getbalance <account>" can produce a false balance
# (sipa watchonly bitcoind), so use only for empty accounts
if showempty:
# Show accts with not enough confirmations as empty!
# A feature, not a bug!
for (ma,comment),bal in [(split2(a),c.getbalance(a,minconf=minconf))
for a in c.listaccounts(0)]:
if is_mmgen_addr(ma) and bal == 0:
accts = c.listaccounts(minconf=0,as_dict=True)
for k in accts.keys():
ma,comment = split2(k)
if is_mmgen_addr(ma) and accts[k] == 0:
key = "_".join(ma.split(":"))
if key not in addrs: addrs[key] = [0,comment]
@ -372,12 +371,12 @@ def getbalance(minconf=1):
keys = ["TOTAL"]
if d.spendable: keys += ["SPENDABLE"]
if is_mmgen_addr(ma): keys += [ma.split(":")[0]]
c = d.confirmations
i = 2 if c >= minconf else 1
confs = d.confirmations
i = 2 if confs >= minconf else 1
for key in keys:
if key not in accts: accts[key] = [0,0,0]
for j in ([0] if c == 0 else []) + [i]:
for j in ([0] if confs == 0 else []) + [i]:
accts[key][j] += d.amount
fs = "{:12} {:<%s} {:<%s} {:<}" % (16,16)

View file

@ -475,7 +475,7 @@ def mmaddr2btcaddr_bitcoind(c,mmaddr,acct_data):
# Don't want to create a new object, so use append()
if not acct_data:
for i in c.listaccounts():
for i in c.listaccounts(minconf=0):
acct_data.append(i)
for acct in acct_data:
@ -773,21 +773,21 @@ def check_mmgen_to_btc_addr_mappings_addrfile(mmgen_inputs,b2m_map,addrfiles):
else: qmsg("Address mappings OK")
def is_valid_tx_comment(c, verbose=True):
if len(c) > g.max_tx_comment_len:
def is_valid_tx_comment(s, verbose=True):
if len(s) > g.max_tx_comment_len:
if verbose: msg("Invalid transaction comment (longer than %s characters)" %
g.max_tx_comment_len)
return False
try: c.decode("utf8")
try: s.decode("utf8")
except:
if verbose: msg("Invalid transaction comment (not UTF-8)")
return False
else: return True
def get_tx_comment_from_file(infile):
c = get_data_from_file(infile,"transaction comment")
if is_valid_tx_comment(c, verbose=True):
return c.decode("utf8").strip()
s = get_data_from_file(infile,"transaction comment")
if is_valid_tx_comment(s, verbose=True):
return s.decode("utf8").strip()
else: return False
@ -795,11 +795,10 @@ def get_tx_comment_from_user(comment=""):
try:
while True:
c = my_raw_input("Comment: ", echo=True,
insert_txt=comment.encode("utf8"))
if c == "": return False
if is_valid_tx_comment(c, verbose=True):
return c.decode("utf8")
s = my_raw_input("Comment: ",insert_txt=comment.encode("utf8"))
if s == "": return False
if is_valid_tx_comment(s, verbose=True):
return s.decode("utf8")
except KeyboardInterrupt:
msg("User interrupt")
return False
@ -807,6 +806,6 @@ def get_tx_comment_from_user(comment=""):
def make_tx_data(metadata_fmt, tx_hex, inputs_data, b2m_map, comment):
from mmgen.bitcoin import b58encode
c = (b58encode(comment.encode("utf8")),) if comment else ()
lines = (metadata_fmt, tx_hex, repr(inputs_data), repr(b2m_map)) + c
s = (b58encode(comment.encode("utf8")),) if comment else ()
lines = (metadata_fmt, tx_hex, repr(inputs_data), repr(b2m_map)) + s
return "\n".join(lines)+"\n"

View file

@ -712,23 +712,25 @@ def get_hash_preset_from_user(hp='3',what="data"):
else: return hp
def my_raw_input(prompt,echo=True,insert_txt=""):
def my_raw_input(prompt,echo=True,insert_txt="",use_readline=True):
if not sys.stdout.isatty(): insert_txt = ""
if use_readline and sys.stdout.isatty():
import readline
def st_hook(): readline.insert_text(insert_txt)
readline.set_startup_hook(st_hook)
else:
msg_r(prompt)
prompt = ""
import readline
def st_hook(): readline.insert_text(insert_txt)
readline.set_startup_hook(st_hook)
msg_r("" if insert_txt else prompt)
kb_hold_protect()
if echo:
reply = raw_input(prompt if insert_txt else "")
reply = raw_input(prompt)
else:
from getpass import getpass
reply = getpass(prompt if insert_txt else "")
reply = getpass(prompt)
kb_hold_protect()
return reply
return reply.strip()
def keypress_confirm(prompt,default_yes=False,verbose=False):