Browse Source

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.
philemon 10 years ago
parent
commit
45e921fc28
5 changed files with 54 additions and 53 deletions
  1. 21 20
      README.md
  2. 1 1
      mmgen/rpc/connection.py
  3. 7 8
      mmgen/tool.py
  4. 13 14
      mmgen/tx.py
  5. 12 10
      mmgen/util.py

+ 21 - 20
README.md

@@ -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.
-
-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.
+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 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]
 

+ 1 - 1
mmgen/rpc/connection.py

@@ -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:

+ 7 - 8
mmgen/tool.py

@@ -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)

+ 13 - 14
mmgen/tx.py

@@ -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"

+ 12 - 10
mmgen/util.py

@@ -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 = ""
-
-	import readline
-	def st_hook(): readline.insert_text(insert_txt)
-	readline.set_startup_hook(st_hook)
+	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 = ""
 
-	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):