PageRenderTime 34ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/Zeroname/updater/zeroname_updater.py

https://gitlab.com/cyrixhero/ZeroNet
Python | 210 lines | 187 code | 17 blank | 6 comment | 23 complexity | 3d9da5007861d36bb7de561e1a4fe8e7 MD5 | raw file
  1. import time
  2. import json
  3. import os
  4. import sys
  5. import re
  6. import socket
  7. from subprocess import call
  8. from bitcoinrpc.authproxy import AuthServiceProxy
  9. def publish():
  10. print "* Signing and Publishing..."
  11. call(" ".join(command_sign_publish), shell=True)
  12. def processNameOp(domain, value, test=False):
  13. if not value.strip().startswith("{"):
  14. return False
  15. try:
  16. data = json.loads(value)
  17. except Exception, err:
  18. print "Json load error: %s" % err
  19. return False
  20. if "zeronet" not in data:
  21. print "No zeronet in ", data.keys()
  22. return False
  23. if not isinstance(data["zeronet"], dict):
  24. print "Not dict: ", data["zeronet"]
  25. return False
  26. if not re.match("^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$", domain):
  27. print "Invalid domain: ", domain
  28. return False
  29. if test:
  30. return True
  31. if "slave" in sys.argv:
  32. print "Waiting for master update arrive"
  33. time.sleep(30) # Wait 30 sec to allow master updater
  34. # Note: Requires the file data/names.json to exist and contain "{}" to work
  35. names_raw = open(names_path, "rb").read()
  36. names = json.loads(names_raw)
  37. for subdomain, address in data["zeronet"].items():
  38. subdomain = subdomain.lower()
  39. address = re.sub("[^A-Za-z0-9]", "", address)
  40. print subdomain, domain, "->", address
  41. if subdomain:
  42. if re.match("^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$", subdomain):
  43. names["%s.%s.bit" % (subdomain, domain)] = address
  44. else:
  45. print "Invalid subdomain:", domain, subdomain
  46. else:
  47. names["%s.bit" % domain] = address
  48. new_names_raw = json.dumps(names, indent=2, sort_keys=True)
  49. if new_names_raw != names_raw:
  50. open(names_path, "wb").write(new_names_raw)
  51. print "-", domain, "Changed"
  52. return True
  53. else:
  54. print "-", domain, "Not changed"
  55. return False
  56. def processBlock(block_id, test=False):
  57. print "Processing block #%s..." % block_id
  58. s = time.time()
  59. block_hash = rpc.getblockhash(block_id)
  60. block = rpc.getblock(block_hash)
  61. print "Checking %s tx" % len(block["tx"])
  62. updated = 0
  63. for tx in block["tx"]:
  64. try:
  65. transaction = rpc.getrawtransaction(tx, 1)
  66. for vout in transaction.get("vout", []):
  67. if "scriptPubKey" in vout and "nameOp" in vout["scriptPubKey"] and "name" in vout["scriptPubKey"]["nameOp"]:
  68. name_op = vout["scriptPubKey"]["nameOp"]
  69. updated += processNameOp(name_op["name"].replace("d/", ""), name_op["value"], test)
  70. except Exception, err:
  71. print "Error processing tx #%s %s" % (tx, err)
  72. print "Done in %.3fs (updated %s)." % (time.time() - s, updated)
  73. return updated
  74. # Connecting to RPC
  75. def initRpc(config):
  76. """Initialize Namecoin RPC"""
  77. rpc_data = {
  78. 'connect': '127.0.0.1',
  79. 'port': '8336',
  80. 'user': 'PLACEHOLDER',
  81. 'password': 'PLACEHOLDER',
  82. 'clienttimeout': '900'
  83. }
  84. try:
  85. fptr = open(config, 'r')
  86. lines = fptr.readlines()
  87. fptr.close()
  88. except:
  89. return None # Or take some other appropriate action
  90. for line in lines:
  91. if not line.startswith('rpc'):
  92. continue
  93. key_val = line.split(None, 1)[0]
  94. (key, val) = key_val.split('=', 1)
  95. if not key or not val:
  96. continue
  97. rpc_data[key[3:]] = val
  98. url = 'http://%(user)s:%(password)s@%(connect)s:%(port)s' % rpc_data
  99. return url, int(rpc_data['clienttimeout'])
  100. # Loading config...
  101. # Check whether platform is on windows or linux
  102. # On linux namecoin is installed under ~/.namecoin, while on on windows it is in %appdata%/Namecoin
  103. if sys.platform == "win32":
  104. namecoin_location = os.getenv('APPDATA') + "/Namecoin/"
  105. else:
  106. namecoin_location = os.path.expanduser("~/.namecoin/")
  107. config_path = namecoin_location + 'zeroname_config.json'
  108. if not os.path.isfile(config_path): # Create sample config
  109. open(config_path, "w").write(
  110. json.dumps({'site': 'site', 'zeronet_path': '/home/zeronet', 'privatekey': '', 'lastprocessed': 223910}, indent=2)
  111. )
  112. print "* Example config written to %s" % config_path
  113. sys.exit(0)
  114. config = json.load(open(config_path))
  115. names_path = "%s/data/%s/data/names.json" % (config["zeronet_path"], config["site"])
  116. os.chdir(config["zeronet_path"]) # Change working dir - tells script where Zeronet install is.
  117. # Parameters to sign and publish
  118. command_sign_publish = [sys.executable, "zeronet.py", "siteSign", config["site"], config["privatekey"], "--publish"]
  119. if sys.platform == 'win32':
  120. command_sign_publish = ['"%s"' % param for param in command_sign_publish]
  121. # Initialize rpc connection
  122. rpc_auth, rpc_timeout = initRpc(namecoin_location + "namecoin.conf")
  123. rpc = AuthServiceProxy(rpc_auth, timeout=rpc_timeout)
  124. while 1:
  125. try:
  126. time.sleep(1)
  127. last_block = int(rpc.getinfo()["blocks"])
  128. break # Connection succeeded
  129. except socket.timeout: # Timeout
  130. print ".",
  131. sys.stdout.flush()
  132. except Exception, err:
  133. print "Exception", err.__class__, err
  134. time.sleep(5)
  135. rpc = AuthServiceProxy(rpc_auth, timeout=rpc_timeout)
  136. if not config["lastprocessed"]: # First startup: Start processing from last block
  137. config["lastprocessed"] = last_block
  138. print "- Testing domain parsing..."
  139. assert processBlock(223911, test=True) # Testing zeronetwork.bit
  140. assert processBlock(227052, test=True) # Testing brainwallets.bit
  141. assert not processBlock(236824, test=True) # Utf8 domain name (invalid should skip)
  142. assert not processBlock(236752, test=True) # Uppercase domain (invalid should skip)
  143. assert processBlock(236870, test=True) # Encoded domain (should pass)
  144. # sys.exit(0)
  145. print "- Parsing skipped blocks..."
  146. should_publish = False
  147. for block_id in range(config["lastprocessed"], last_block + 1):
  148. if processBlock(block_id):
  149. should_publish = True
  150. config["lastprocessed"] = last_block
  151. if should_publish:
  152. publish()
  153. while 1:
  154. print "- Waiting for new block"
  155. sys.stdout.flush()
  156. while 1:
  157. try:
  158. time.sleep(1)
  159. rpc.waitforblock()
  160. print "Found"
  161. break # Block found
  162. except socket.timeout: # Timeout
  163. print ".",
  164. sys.stdout.flush()
  165. except Exception, err:
  166. print "Exception", err.__class__, err
  167. time.sleep(5)
  168. rpc = AuthServiceProxy(rpc_auth, timeout=rpc_timeout)
  169. last_block = int(rpc.getinfo()["blocks"])
  170. should_publish = False
  171. for block_id in range(config["lastprocessed"] + 1, last_block + 1):
  172. if processBlock(block_id):
  173. should_publish = True
  174. config["lastprocessed"] = last_block
  175. open(config_path, "w").write(json.dumps(config, indent=2))
  176. if should_publish:
  177. publish()