PK!~GAB00contractvmd/chainstarter.pyimport os import sys def main (): ARGS = '-server -rpcuser=test -rpcpassword=testpass -rpcport=8080 -txindex -debug -printtoconsole -rpcallowip=0.0.0.0/0' if len (sys.argv) == 1: os.system ('bitcoin-qt -testnet '+ARGS) elif len (sys.argv) > 1: cmd = '-qt' if len (sys.argv) == 3 and sys.argv[2] == 'daemon': ARGS += ' -daemon' cmd = 'd' if len (sys.argv) == 3 and sys.argv[2] == 'stop': cmd = '-cli' ARGS += ' stop' if sys.argv[1] == 'XLT': os.system ('litecoin'+cmd+' -testnet '+ARGS) elif sys.argv[1] == 'XTN': os.system ('bitcoin'+cmd+' -testnet '+ARGS) elif sys.argv[1] == 'XDT': os.system ('dogecoin'+cmd+' -testtest '+ARGS) elif sys.argv[1] == 'RXLT': os.system ('litecoin'+cmd+' -regtest '+ARGS) elif sys.argv[1] == 'RXTN': os.system ('bitcoin'+cmd+' -regtest '+ARGS) elif sys.argv[1] == 'RXDT': os.system ('dogecoin'+cmd+' -regtest '+ARGS) else: print ("unrecognized chain name", sys.argv[1]) else: print ('usage: python '+sys.argv[0]+' chaincode [daemon|stop]') if __name__ == "__main__": main () PKb,GÚMcontractvmd/__init__.py# Copyright (c) 2015 Davide Gessa # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. from . import contractvmd if __name__ == "__main__": contractvmd.main () PK EGU>&&contractvmd/contractvmd.py#!/usr/bin/python3 # Copyright (c) 2015 Davide Gessa # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. import time import json import sys import os import getopt import logging import signal from . import config, dht, database, pluginmanager, api from .chain import chain from .backend import daemonrpc, chainsoapi, node logger = logging.getLogger(config.APP_NAME) def signal_handler (sig, frame): logger.critical ('Exiting...') f = open (config.DATA_DIR + '/pid', 'r') cpid = f.read () f.close () os.kill (int (cpid), signal.SIGKILL) sys.exit (0) def usage (): print ('Usage:',sys.argv[0],'[OPTIONS]\n') print ('Mandatory arguments:') print ('\t-h,--help\t\t\tdisplay this help') print ('\t-V,--version\t\t\tdisplay the software version') print ('\t-v,--verbose=n\t\t\tset verbosity level to n=[1-5] (default: '+str(config.VERBOSE)+')') print ('\t-D,--data=path\t\t\tspecify a custom data directory path (default: '+config.DATA_DIR+')') print ('\t-d,--daemon\t\t\trun the software as daemon') print ('\t-c,--chain=chainname\t\tblock-chain', '['+(', '.join (map (lambda x: "'"+x+"'", config.CHAINS)))+']') print ('\t-b,--backend=protocol\t\tbackend protocol', str(config.BACKEND_PROTOCOLS)) print ('\t-p,--port=port\t\t\tdht port') print ('\t-a,--api=bool\t\t\tdisable or enable api framework') print ('\t--api-port=port\t\t\tset an api port') print ('\t-s,--seed=host:port,[host:port]\tset a contractvm seed nodes list') print ('\t--discard-old-blocks\t\tdiscard old blocks') print ('\nDaemon commands:') print ('\t--restart\t\t\trestart the contractvmd instance') print ('\t--stop\t\t\t\tstop the contractvmd instance') def core (opts, args): firstrun = False logger.info ('Starting %s %s', config.APP_NAME, config.APP_VERSION) # Set debug level logger.setLevel (60-config.VERBOSE*10) # Check if the data-dir exists if not os.path.isdir (config.DATA_DIR): logger.warning ('Directory %s not present', config.DATA_DIR) os.mkdir (config.DATA_DIR) logger.warning ('Directory %s created', config.DATA_DIR) firstrun = True # Check if temp dir exists if not os.path.isdir (config.DATA_DIR + config.TEMP_DIR_RELATIVE): logger.warning ('Directory %s not present', config.DATA_DIR + config.TEMP_DIR_RELATIVE) os.mkdir (config.DATA_DIR + config.TEMP_DIR_RELATIVE) logger.warning ('Directory %s created', config.DATA_DIR + config.TEMP_DIR_RELATIVE) config.TEMP_DIR = config.DATA_DIR + config.TEMP_DIR_RELATIVE # Check if dapps dir exists if not os.path.isdir (config.DATA_DIR + config.DAPPS_DIR_RELATIVE): logger.warning ('Directory %s not present', config.DATA_DIR + config.DAPPS_DIR_RELATIVE) os.mkdir (config.DATA_DIR + config.DAPPS_DIR_RELATIVE) logger.warning ('Directory %s created', config.DATA_DIR + config.DAPPS_DIR_RELATIVE) config.DAPPS_DIR = config.DATA_DIR + config.DAPPS_DIR_RELATIVE # Check if config file exits if not os.path.exists(config.DATA_DIR+'/'+config.APP_NAME+'.json'): logger.warning ('Configuration file %s not present', config.DATA_DIR+'/'+config.APP_NAME+'.json') f = open (config.DATA_DIR+'/'+config.APP_NAME+'.json', 'w') f.write (json.dumps (config.CONF, indent=4, separators=(',', ': '))) f.close () logger.warning ('Configuration file %s created', config.DATA_DIR+'/'+config.APP_NAME+'.json') firstrun = True try: os.mkdirs (config.DATA_DIR + '/dapps/') except: pass # Loading config file f = open (config.DATA_DIR+'/'+config.APP_NAME+'.json', 'r') conf = f.read () f.close () config.CONF = json.loads (conf) logger.info ('Configuration file %s loaded', config.DATA_DIR+'/'+config.APP_NAME+'.json') # Parse arguments that overrides config file for opt, arg in opts: if opt in ("-r", "--regtest"): config.CONF['regtest'] = True config.CHAINS[config.CONF['chain']]['genesis_height'] = 0 elif opt in ("-c", "--chain"): config.CONF['chain'] = arg elif opt in ("-b", "--backend"): config.CONF['backend']['protocol'] = [arg] elif opt in ("-a", "--api"): config.CONF['api']['enabled'] = bool (int (arg)) elif opt in ("-s", "--seed"): config.CONF['dht']['seeds'] = arg.split (',') elif opt in ("-p", "--port"): config.CONF['dht']['port'] = int (arg) elif opt in ("--api-port"): config.CONF['api']['port'] = int (arg) elif opt in ("--discard-old-blocks"): config.CONF['discard-old-blocks'] = True # Check for chain if not config.CONF['chain'] in config.CHAINS: logger.critical ('Unable to start %s on chain \'%s\'', config.APP_NAME, config.CONF['chain']) sys.exit (0) # Start the backend be = None fallbackends = config.CONF['backend']['protocol'] while be == None and len (fallbackends) > 0: cbe = fallbackends [0] fallbackends = fallbackends[1:] #print (fallbackends) if cbe == 'rpc': be = daemonrpc.DaemonRPC (config.CONF['chain'], config.CONF['backend']['rpc']['host'], config.CONF['backend']['rpc']['port'], config.CONF['backend']['rpc']['user'], config.CONF['backend']['rpc']['password'], bool(config.CONF['backend']['rpc']['ssl'])) if be.connect (): logger.info ('Backend protocol %s initialized', cbe) else: logger.critical ('Unable to connect to the rpc host, falling back') be = None elif cbe == 'chainsoapi': if chainsoapi.ChainSoAPI.isChainSupported(config.CONF['chain']): be = chainsoapi.ChainSoAPI (config.CONF['chain']) else: logger.critical ('Backend protocol %s is only available with %s networks, falling back', config.CONF['backend']['protocol'], str (chainsoapi.ChainSoAPI.getSupportedChains ())) be = None elif cbe == 'node': c = config.CHAINS[config.CONF['chain']] be = node.Node (config.CONF['chain'], config.DATA_DIR+'/node_'+config.CONF['chain']+'.dat', (c['genesis_block'], c['genesis_height'])) if be.connect (): logger.info ('Backend protocol %s initialized', cbe) else: logger.critical ('Unable to enstablish a bitpeer daemon, falling back') be = None else: logger.critical ('Unable to handle the backend protocol %s, falling back', cbe) be = None if be == None: logger.critical ('Cannot find a good backend protocol, exiting') sys.exit (0) # Start the DHT try: ddht = dht.DHT (int (config.CONF['dht']['port']), seedlist=config.CONF['dht']['seeds'], dhtfile=config.DATA_DIR+'/dht_'+config.CONF['chain']+'.dat', info=config.CONF['api']['port']) ddht.run () logger.info ('DHT initialized with identity: ' + str (ddht.identity ())) except Exception as e: logger.critical ('Exception while initializing kademlia DHT') logger.critical (e) sys.exit (0) # Load the state db db = database.Database (config.DATA_DIR+'/db_'+config.CONF['chain']+ ('_regtest' if config.CONF['regtest'] else '') + '.dat') logger.info ('Database %s initialized', 'db_'+config.CONF['chain']+ ('_regtest' if config.CONF['regtest'] else '') + '.dat') # Load the plugin engine pm = pluginmanager.PluginManager () # Create the chain engine ch = chain.Chain (pm, db, be, ddht, config.CHAINS[config.CONF['chain']]) # API if bool (int (config.CONF['api']['enabled'])): aapi = api.API (be, ch, ddht, config.CONF['api']['port'], config.CONF['api']['threads']) aapi.run () else: aapi = None # Load all dapps for dapp in config.CONF['dapps']['enabled']: try: pm.load (dapp, ch, db, ddht, aapi) except Exception as e: logger.critical ('Exception while loading dapp: ' + dapp) logger.critical (e) # Run the mainloop logger.info ('Chain initialized, starting the main loop') ch.run () def main (): try: opts, args = getopt.getopt(sys.argv[1:], "hv:VD:c:b:t:a:sp", ["stop", "restart", "discard-old-blocks", "help", "verbose=", "version", "data=", "daemon", "chain=", "backend=", "api-port=", "api=", "regtest", "seed=", "port="]) except getopt.GetoptError: usage() sys.exit(2) # Parse arguments for opt, arg in opts: if opt in ("-h", "--help"): usage () sys.exit () elif opt in ("-V", "--version"): print (config.APP_VERSION) sys.exit () elif opt in ("-D", "--data"): config.DATA_DIR = os.path.expanduser (arg) elif opt in ("-v", "--verbose"): config.VERBOSE = int (arg) elif opt in ("-d", "--daemon"): logger.critical ('Daemon is not yet implemented') sys.exit () elif opt in ("--restart"): print ('Restarting daemon...') f = open (config.DATA_DIR + '/pid', 'r') cpid = f.read () f.close () try: os.kill (int (cpid), signal.SIGUSR1) except: print ('No running instance.') sys.exit (0) elif opt in ("--stop"): print ('Stopping daemon...') f = open (config.DATA_DIR + '/pid', 'r') cpid = f.read () f.close () try: os.kill (int (cpid), signal.SIGKILL) except: print ('No running instance.') sys.exit (0) # Check if the data-dir exists if not os.path.isdir (config.DATA_DIR): logger.warning ('Directory %s not present', config.DATA_DIR) os.mkdir (config.DATA_DIR) logger.warning ('Directory %s created', config.DATA_DIR) try: f = open (config.DATA_DIR + '/pid', 'r') cpid = f.read () f.close () os.kill (int (cpid), signal.SIGKILL) logger.critical ('Already running, killed: ' + str (cpid)) except: pass #logger.addHandler (logging.FileHandler (config.DATA_DIR + '/contractvmd.log')) #logger.info ('Logging to', config.DATA_DIR + '/contractvmd.log') run = True while run: pid = os.fork () if pid != 0: signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGQUIT, signal_handler) logger.critical ('Started: ' + str (pid)) f = open (config.DATA_DIR + '/pid', 'w') f.write (str(pid)) f.close () r = os.waitpid (int (pid), 0) logger.critical ('Stopped: ' + str (r[0])) time.sleep (5) if r[1] == signal.SIGKILL: run = False else: core (opts, args) sys.exit (0) if __name__ == "__main__": main () PKl~G contractvmd/proto.py# Copyright (c) 2015 Davide Gessa # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. # Protocol metadata and definitions from . import config class Protocol: # Protocol related VERSION = 1 MAGIC_FLAG = 'CCHN' # Transaction limits STORAGE_OPRETURN_LIMIT = 40 DATA_HASH_SIZE = 64 # Time related TIME_UNIT_BLOCKS = 1 def timeUnit (chain): return TIME_UNIT_BLOCKS # Fee estimation def estimateFee (chain, weight = 1000): return config.CHAINS[chain]['base_fee'] + weight * 4 + 2 def estimateExpiryFromFee (chain, fee, weight): fee = int (fee) - config.CHAINS[chain]['base_fee'] - 2 return int (fee / weight) PKlALG.cccontractvmd/database.py# Copyright (c) 2015 Davide Gessa # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. import sys import logging import copy import shelve #import leveldb from threading import Lock from . import config from .chain.message import Message logger = logging.getLogger(config.APP_NAME) # Database abstraction layer class Database: def __init__ (self, f): self.shelve = shelve.open (f) def close (self): self.shelve.close () def sync (self): self.shelve.sync () def new (path): return Database (path) # Raw operations def _exists (self, key): return (key in self.shelve) #try: # self.db.Get(key) # return True #except: # return False def _delete (self, key): del (self.shelve[key]) #self.db.Delete(key) def _get (self, key): if key in self.shelve: return self.shelve [key] else: return None #try: # return self.db.Get(key) #except: # return None def _set (self, key, dictobj): self.shelve [key] = dictobj #self.db.Put(key, dictobj) # General operations def exists (self, key): return self._exists (key) def get (self, key): return self._get (key) def set (self, key, dictobj): self._set (key, dictobj) self.sync () def delete (self, key): if self.exists (key): self._delete (key) self.sync () # Integer object operations def intinc (self, key): self.set (key, int (self.get (key)) + 1) def intdec (self, key): self.set (key, int (self.get (key)) + 1) # List # List object operations def listappend (self, key, data): d = self.get (key) #print (d) d.append (data) #print (d) self.set (key, d) def listremove (self, key, data): li = self.get (key) li.remove (data) self.set (key, li) def listcontains (self, key, data): return (data in self.get (key)) # Get the object at 'key', or set 'key' to 'dictobj' def getinit (self, key, dictobj): if not self.exists (key): self.set (key, dictobj) return dictobj else: return self.get (key) def init (self, key, dictobj): if not self.exists (key): self.set (key, dictobj) PK!8G5xeecontractvmd/config.py# Copyright (c) 2015 Davide Gessa # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. from pycoin import networks from colorlog import ColoredFormatter import logging import os import platform import sys def app_data_path (appauthor, appname, roaming=True): if sys.platform.startswith('java'): os_name = platform.java_ver()[3][0] if os_name.startswith('Windows'): system = 'win32' elif os_name.startswith('Mac'): system = 'darwin' else: system = 'linux2' else: system = sys.platform if system == "win32": if appauthor is None: appauthor = appname const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" path = os.path.normpath(_get_win_folder(const)) if appname: if appauthor is not False: path = os.path.join(path, appauthor, appname) else: path = os.path.join(path, appname) elif system == 'darwin': path = os.path.expanduser('~/Library/Application Support/') if appname: path = os.path.join(path, appname) else: path = os.getenv('XDG_DATA_HOME', os.path.expanduser("~/")) if appname: path = os.path.join(path, '.'+appname) return path VERBOSE = 5 APP_VERSION = '0.6.9.14' APP_NAME = 'contractvm' APP_AUTHOR = 'Davide Gessa' DATA_DIR = app_data_path (appauthor=APP_AUTHOR, appname=APP_NAME) TEMP_DIR_RELATIVE = '/temp/' TEMP_DIR = DATA_DIR + TEMP_DIR_RELATIVE DAPPS_DIR_RELATIVE = '/dapps/' DAPPS_DIR = DATA_DIR + DAPPS_DIR_RELATIVE BACKEND_PROTOCOLS = ['rpc', 'chainsoapi', 'node'] CHAINS = { 'XTN' : { 'code': 'XTN', 'base_fee': 60000, 'genesis_block': "00000000004919bcc4a8d5d7d2f3842b6a32b63483a070fd7c0cc1585b72e504", 'genesis_height': 629480, 'name': networks.full_network_name_for_netcode ('XTN'), 'seeds': [ ] }, 'BTC' : { 'code': 'BTC', 'base_fee': 40000, 'genesis_block': "000000000000000002f214ea3bc2c195ed11f9195ab07229151befab03ada10b", 'genesis_height': 385720, 'name': networks.full_network_name_for_netcode ('BTC') }, 'XLT' : { 'code': 'XLT', 'base_fee': 450000, 'genesis_block': "22f9d7316645dc02cdd05c32db902ae4aca582c7f138b2b7cecbc58d269e58a6", 'genesis_height': 741198, 'name': networks.full_network_name_for_netcode ('XLT') }, 'LTC' : { 'code': 'LTC', 'base_fee': 100000, 'genesis_block': "", 'genesis_height': 329203, 'name': networks.full_network_name_for_netcode ('LTC') }, 'DOGE': { 'code': 'DOGE', 'base_fee': 100000000, 'genesis_block': "7a0d505875129fcf5f88b6bad6f083214d341130dde3053019d38a23258b4f1e", 'genesis_height': 981606, 'name': networks.full_network_name_for_netcode ('DOGE') } } CONF = { 'chain': 'XTN', 'regtest': False, 'discard-old-blocks': False, 'maxpeers': 25, 'dapps': { 'list': [], 'enabled': [] }, 'backend': { 'protocol': ['node', 'rpc'], "rpc": { "host": "51.254.215.160", "port": "8080", "user": "test", "password": "testpass", "ssl": False } }, 'api': { 'enabled': True, 'threads': 1, 'port': 8181 }, 'dht': { 'seeds': [ ], 'port': 5051 } } formatter = ColoredFormatter( '%(log_color)s[%(asctime)-8s] %(module)s: %(message_log_color)s%(message)s', datefmt=None, reset=True, log_colors = { 'DEBUG': 'blue', 'PLUGINFO': 'purple', 'INFO': 'green', 'WARNING': 'yellow', 'ERROR': 'red', 'CRITICAL': 'red', }, secondary_log_colors={ 'message': { 'DEBUG': 'purple', 'PLUGINFO': 'blue', 'INFO': 'yellow', 'WARNING': 'green', 'ERROR': 'yellow', 'CRITICAL': 'red', } }, style = '%' ) stream = logging.StreamHandler () stream.setFormatter (formatter) logger = logging.getLogger (APP_NAME) logger.addHandler (stream) logging.addLevelName (15, "PLUGINFO") logging.Logger.pluginfo = lambda self, message, *args, **kws: self._log(15, message, args, **kws) if self.isEnabledFor(15) else None PKxG 7XXcontractvmd/pluginmanager.py# Copyright (c) 2015 Davide Gessa # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. import logging import imp import sys from .database import Database from . import config logger = logging.getLogger(config.APP_NAME) class PluginManager: def __init__ (self): self.dapps = {} def load (self, pname, chain, db, dht, api): logger.pluginfo ('Plugging dapp "%s"', pname.lower ()) dapp = imp.load_source (pname, config.DATA_DIR + '/dapps/' + pname + '/dapp/__init__.py') pc = eval ('dapp.'+pname+'.'+pname) po = pc (chain, Database.new (config.DATA_DIR + '/dapps/state_' + pname + '_' + chain.getChainCode () + '.dat'), dht, api) # Register API methods rpcm = po.getAPI ().getRPCMethods () for m in rpcm: api.registerRPCMethod (pname.lower () + '.' + m, rpcm[m]) self.dapps[pname.lower ()] = po # Return true if the message should be handled def shouldBeHandled (self, m): for p in self.dapps: if m.DappCode == self.dapps[p].DappCode: return True return False # Handle message received from the DHT def handleMessage (self, m): for p in self.dapps: if m.DappCode == self.dapps[p].DappCode: logger.pluginfo ('Found handler %s for message %s from %s', p, m.Hash, m.Player) try: return self.dapps[p].handleMessage (m) except Exception as e: logger.critical ('Exception from dapp ' + p + ' while handling a message') logger.critical (e) return None logger.error ('Cannot handle message method %d for dapp %s', m.Method, str (m.DappCode)) return None PK&eGiccontractvmd/dapp.py# Copyright (c) 2015 Davide Gessa # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. from .proto import Protocol class Dapp: def __init__ (self, dapp_code, methods, chain, database, dht, api = None): self.DappCode = dapp_code self.Methods = methods self.Database = database self.Chain = chain self.DHT = dht self.API = api def getAPI (self): return self.API def handleMessage (self, message): return None class API: def __init__ (self, core, dht, rpcmethods, errors): self.core = core self.dht = dht self.errors = errors self.rpcmethods = rpcmethods def getRPCMethods (self): return self.rpcmethods def createTransactionResponse (self, message): [datahash, outscript, tempid] = message.toOutputScript (self.dht) return { "outscript": outscript, "datahash": datahash, "tempid": tempid, "fee": Protocol.estimateFee (self.core.getChainCode ()) } def createErrorResponse (self, error): if error in self.errors: return { 'error': self.errors[error]['code'], 'message': self.errors[error]['message'] } else: return { 'error': -1, 'message': 'General error ('+str(error)+')' } class Core: def __init__ (self, chain, database): self.chain = chain self.database = database # Get the current time, or time of an arbitrary block-height def getTime (self, height=None): if height != None: return int (int (height) / Protocol.TIME_UNIT_BLOCKS) return int (int (self.chain.getChainHeight ()) / Protocol.TIME_UNIT_BLOCKS) def getChainName (self): return self.chain.getChainName () def getChainCode (self): return self.chain.getChainCode () def getChainHeight (self): return int (self.chain.getChainHeight ()) PK6jfG-##contractvmd/dappman.py#!/usr/bin/python3 # Copyright (c) 2015 Davide Gessa # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. import shutil import json import sys import os import getopt import requests import urllib from zipfile import ZipFile from . import config DAPP_LIST_URL = "https://raw.githubusercontent.com/contractvm/dapp-list/master/list.json" def restart_daemon (): os.system ('contractvmd --restart') def usage (): print ('Usage:', sys.argv[0], '[option] action') print ('Actions:') #print ('\t-s, --search=query\t\tsearch for a dapp') print ('\t-i,\t--install=giturl\tinstall a dapp by its git repository') print ('\t\t--install=path\t\tinstall a dapp from a local directory') print ('\t\t--install=name\t\tinstall a dapp from its catalog name') #print ('\t-ii, --info=url\t\t\treturn informations about a dapp') print ('\t-r,\t--remove=name\t\tremove an installed dapp') print ('\t-u,\t--update=name\t\tupdate an installed dapp') print ('\t-U\t\t\t\tupdate all dapps') print ('\t-c,\t--reset=name\t\treset an installed dapp state') print ('\t-l,\t--list\t\t\tlist installed dapps') print ('\t-w,\t--create\t\tcreate a new empty dapp starting from a template') print ('\t-h,\t--help\t\t\tthis help') print ('\t-v,\t--version\t\tsoftware version') print ('') print ('Options:') print ('\t-d,\t--data=path\t\tspecify a custom data directory path \n\t\t\t\t\t(default: '+config.DATA_DIR+')') def save_conf (fpath, conf): f = open (fpath, 'w') f.write (json.dumps (conf)) f.close () def download_list (): print ('Downloading dapps catalog...') r = requests.get (DAPP_LIST_URL) d = r.json ()['dapps'] print ('Dapps catalog contains', len (d), 'dapps') return d def create_wizard (catalog, conf): name = input ('Dapp name: ').lower () description = input ('Description: ') authors = input ('Authors (comma separated): ').split (',') print ('Select a template:') i = 0 for dapp in catalog: print ('\t',i,'.',dapp['name'], '('+dapp['source']+')') i += 1 i = int (input ('Template: ')) if i < 0 or i >= len (catalog): print ('Invalid template') sys.exit (0) print ('Creating directory for dapp:', name) try: os.mkdir (name) except: print ('Directory',name,'exists') r = input ('Remove old directory (y/n)? ').lower() if r == 'y': shutil.rmtree (name) os.mkdir (name) else: print ('Exiting') sys.exit (0) print ('Downloading template:', catalog[i]['name']) testfile = urllib.request.urlretrieve(catalog[i]['source'] + '/archive/master.zip', catalog[i]['name']+'_template.zip') print ('Extracting template') with ZipFile(catalog[i]['name']+'_template.zip') as myzip: myzip.extractall (path=name) myzip.close () print ('Setting up directories') nd = os.listdir (name)[0] for f in os.listdir (name + '/' + nd): shutil.move (name + '/' + nd + '/' + f, name + '/') shutil.rmtree (name + '/' + nd) print ('String replace for dapp name') shutil.move (name + '/dapp/' + catalog[i]['name'] + '.py', name + '/dapp/' + name + '.py') f = open (name + '/dapp/' + name + '.py', 'r') d = f.read ().replace (catalog[i]['name'], name) f.close () f = open (name + '/dapp/' + name + '.py', 'w') f.write (d) f.close () f = open (name + '/dapp/__init__.py', 'r') d = f.read ().replace (catalog[i]['name'], name) f.close () f = open (name + '/dapp/__init__.py', 'w') f.write (d) f.close () shutil.move (name + '/library/' + catalog[i]['name'], name + '/library/' + name) f = open (name + '/setup.py', 'r') d = f.read ().replace (catalog[i]['name'], name) f.close () f = open (name + '/setup.py', 'w') f.write (d) f.close () print ('Creating manifest.json') manifest = { "name": name, "version": "0.0.1", "description": description, "authors": authors } f = open (name + '/manifest.json', 'w') f.write (json.dumps (manifest)) f.close () print ('Dapp', name, 'sucessfully created') print ('You can now install your local dapp by typing: dappman -i',os.getcwd()+'/'+name) def main (): try: opts, args = getopt.getopt(sys.argv[1:], "s:i:k:r:lwhd:vu:c:U", ["reset=", "update=", "update-all", "help", "version", "search=", "data=", "list", "install="]) except getopt.GetoptError: usage() sys.exit(2) # Parse options for opt, arg in opts: if opt in ("-D", "--data"): config.DATA_DIR = os.path.expanduser (arg) # Loading config file try: f = open (config.DATA_DIR+'/'+config.APP_NAME+'.json', 'r') except: print ('Cannot read configuration file:', config.DATA_DIR+'/'+config.APP_NAME+'.json') print ('You have to run contractvmd for the first time.') sys.exit (0) conf = json.loads (f.read ()) f.close () try: os.mkdirs (config.DATA_DIR + '/dapps/') except: pass # Parse actions for opt, arg in opts: if opt in ("-h", "--help"): usage () sys.exit () elif opt in ("-w", "--create"): catalog = download_list () create_wizard (catalog, conf) sys.exit () elif opt in ("-v", "--version"): print (config.APP_VERSION) sys.exit () elif opt in ("-c", "--reset"): dapp = arg.lower () print ('Resetting state of', dapp) r = input ('Are you sure (y/n)? ').lower () if r == 'y': for f in os.listdir (config.DATA_DIR + '/dapps/'): if f[0:6] == 'state_' and f[-3:] == 'dat' and f[6:6+len(dapp)] == dapp: os.remove (config.DATA_DIR + '/dapps/' + f) print ('Deleted', f) restart_daemon () print ('State of', dapp, 'successfully reset') sys.exit (0) elif opt in ("-U", "--update-all"): for dapp in conf['dapps']['list']: print ('Updating', dapp, '...') os.system ('cd ' + config.DATA_DIR + '/dapps/' + dapp + ' && git pull') os.system ('cd ' + config.DATA_DIR + '/dapps/' + dapp + ' && sudo pip3 install -r requirements.txt && sudo python3 setup.py install') print (dapp, 'updated') restart_daemon () sys.exit (0) elif opt in ("-u", "--update"): dapp = arg.lower () print ('Updating', dapp, '...') os.system ('cd ' + config.DATA_DIR + '/dapps/' + dapp + ' && git pull') os.system ('cd ' + config.DATA_DIR + '/dapps/' + dapp + ' && sudo pip3 install -r requirements.txt && sudo python3 setup.py install') restart_daemon () print (dapp, 'updated') sys.exit (0) elif opt in ("-r", "--remove"): dapp = arg.lower () print ('Removing', dapp, '...') try: # shutils.rmtree ( os.system ('sudo rm -r ' + config.DATA_DIR + '/dapps/' + dapp) except: print ('Dapp' + dapp + 'doesn\'t exists') if dapp in conf['dapps']['list']: conf['dapps']['list'].remove (dapp) if dapp in conf['dapps']['enabled']: conf['dapps']['enabled'].remove (dapp) save_conf (config.DATA_DIR + '/' + config.APP_NAME + '.json', conf) restart_daemon () print ('Dapp', dapp, 'successfully removed') print ('State is preserved') sys.exit (0) elif opt in ("-i", "--install"): catalog = download_list () print ('Installing', arg, '...') url = arg for dapp in catalog: if dapp['name'] == arg: url = dapp['source'] # Cleaning temp tree try: shutil.rmtree (config.DATA_DIR + '/dapps/temp/') except: pass # Local dapp if os.path.isdir (arg): shutil.copytree (arg, config.DATA_DIR + '/dapps/temp') # Git cloning else: os.system ('git clone ' + url + ' ' + config.DATA_DIR + '/dapps/temp') manifest = {} try: f = open (config.DATA_DIR + '/dapps/temp/manifest.json', 'r') manifest = json.loads (f.read ()) f.close () except: print ('No manifest.json or malformed manifest') sys.exit () if not 'name' in manifest: print ('No dapp name in manifest.json') sys.exit () # Move the cloned repository # Move the cloned repository if os.path.isdir (config.DATA_DIR + '/dapps/' + manifest['name'].lower ()): print ('Dapp already installed, reinstalling') os.system ('sudo rm -r ' + config.DATA_DIR + '/dapps/' + manifest['name'].lower ()) #shutil.rmtree ( os.rename (config.DATA_DIR + '/dapps/temp/', config.DATA_DIR + '/dapps/' + manifest['name'].lower ()) # Install os.system ('cd ' + config.DATA_DIR + '/dapps/' + manifest['name'].lower () + ' && sudo pip3 install -r requirements.txt && sudo python3 setup.py install') # Config update if not manifest['name'].lower () in conf['dapps']['list']: conf['dapps']['list'].append (manifest['name'].lower ()) if not manifest['name'].lower () in conf['dapps']['enabled']: conf['dapps']['enabled'].append (manifest['name'].lower ()) save_conf (config.DATA_DIR + '/' + config.APP_NAME + '.json', conf) restart_daemon () print (manifest['name'], 'is now installed') sys.exit () elif opt in ("-l", "--list"): catalog = download_list () print ('Installed dapps:') for dapp in conf['dapps']['list']: print ('\t', dapp) print ('Enabled dapps:') for dapp in conf['dapps']['enabled']: print ('\t', dapp) print ('Available:') for dapp in catalog: print ('\t', dapp['name'], '-', dapp['description']) sys.exit () usage () PKGcccontractvmd/api.py# Copyright (c) 2015 Davide Gessa # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. import time import logging import json import inspect import random from . import config from .proto import Protocol from .chain.message import * from threading import Thread from threading import Lock from werkzeug.wrappers import Request, Response from werkzeug.serving import run_simple from jsonrpc import JSONRPCResponseManager, dispatcher logger = logging.getLogger(config.APP_NAME) logging.getLogger('werkzeug').setLevel(logging.ERROR) class API: def __init__ (self, backend, chain, dht, port, threads): self.port = int (port) self.threads = int (threads) self.dht = dht self.backend = backend self.chain = chain self.RPCHelp = { "broadcast" : {"args": ["signed_transaction", "temp_id"], "return": {'txid': 'transaction_hash'}}, "info" : {"args": [], "return": { "chain": { "code": "XLT", "height": 561596, "name": "Litecoin testnet" }, "node": { "backend": [ "rpc", "chainsoapi" ], "dapps": { "list": [ "cotst" ], "enabled": [ "cotst" ] }, "version": "0.1" } }}, "net.peers": {"args": [], "return": {"list": [("host", "port", "id")]}}, "net.connections": {"args": [], "return": {"count": 'total_peers'}}, "help": {"args":[], "return": {}} } self.RPCDispatcher = {} self.RPCDispatcher["broadcast"] = self.method_broadcast self.RPCDispatcher["help"] = self.method_help self.RPCDispatcher["info"] = self.method_info self.RPCDispatcher["net.peers"] = self.method_net_peers self.RPCDispatcher["net.connections"] = self.method_net_connections def registerRPCMethod (self, name, method): self.RPCDispatcher [name] = method['call'] self.RPCHelp [name] = method['help'] def method_net_connections (self): return {'count': len (self.dht.peers ())} def method_net_peers (self): return self.dht.peers () # Broadcast a signed transaction def method_broadcast (self, thex, temp_id): # TODO check if temp_id is associated with the player who signed thex # Check the validity of the signed transaction # Use the backend to broadcast the transaction and get txid r = self.backend.broadcastTransaction (thex) # Publish the temp data on the DHT if r != None: self.dht.publish (temp_id, r) # Return the transaction id to the client return {'txid': r} def method_help (self): return self.RPCHelp def method_info (self): return {'chain': {'height': self.chain.getChainHeight (), 'regtest': config.CONF['regtest'], 'code': self.chain.getChainCode (), 'name': self.chain.getChainName ()}, 'node': { 'dapps': config.CONF['dapps'], 'backend': config.CONF['backend']['protocol'], 'version': config.APP_VERSION }} @Request.application def serveApplication (self, request): try: rjson = json.loads (request.data.decode('ascii')) except: apiresponse = Response({}, mimetype='application/json') apiresponse.headers.add('Access-Control-Allow-Origin', '*') apiresponse.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,PATCH') apiresponse.headers.add('Access-Control-Allow-Headers', 'Content-Type, Authorization') return apiresponse if rjson['method'] in self.RPCDispatcher: nargs = len (inspect.getargspec (self.RPCDispatcher[rjson['method']]).args) - 1 if len (rjson['params']) != nargs: logger.error ('Client invalid request arguments: "%s" %s', rjson['method'], str(rjson['params'])) else: if rjson['method'].find ('get') == -1 and rjson['method'].find ('info') == -1: logger.debug ('Client request: %s', rjson['method']) else: logger.error ('Client invalid request: "%s" %s', rjson['method'], str(rjson['params'])) response = JSONRPCResponseManager.handle (request.data, self.RPCDispatcher) apiresponse = Response(response.json, mimetype='application/json') apiresponse.headers.add('Access-Control-Allow-Origin', '*') apiresponse.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,PATCH') apiresponse.headers.add('Access-Control-Allow-Headers', 'Content-Type, Authorization') return apiresponse def serverThread (self): if self.threads > 1: run_simple('localhost', self.port, self.serveApplication, threaded=True, processes=self.threads, use_debugger=False) else: run_simple('localhost', self.port, self.serveApplication, use_debugger=False) def run (self): logger.info ('Starting jsonrpc api server at port %d (%d threads)', self.port, self.threads) # Start the serve thread self.servethread = Thread(target=self.serverThread, args=()) self.servethread.start() PK~GhŮ contractvmd/dht.py# Copyright (c) 2015 Davide Gessa # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. import time import logging import random import kad import threading from threading import Thread, Lock, Timer from . import config logger = logging.getLogger(config.APP_NAME) class DHT: def __init__ (self, port, seedlist = [], dhtfile = '', info = {}): self.dhtfile = dhtfile self.seeds = [] self.info = info self.port = port self.temp = {'index': 0} self.storage = kad.storage.Shelve (self.dhtfile) self.writelock = Lock () ##TODO Open the peers file and search for seed peers for peer in seedlist: peer = peer.split (':') if len (peer) == 2: self.seeds.append ((peer[0], int (peer[1]))) logger.debug ('Binding peer (%s:%d)', peer[0], int (peer[1])) #else: # self.seeds.append ((peer[0], port)) # logger.debug ('Binding peer (%s:%d)', peer[0], port) # Temp data def prepare (self, data): self.writelock.acquire () self.temp ['index'] += 1 tempid = str (self.temp['index']) self.temp [tempid] = data logger.debug ('Prepare temp data %s (%d in the buffer)', str (tempid), len (self.temp)) self.writelock.release () return tempid def publish (self, tempid, key): self.writelock.acquire () tempid = str (tempid) if not tempid in self.temp: self.writelock.release () return None logger.debug ('Publish temp data %s -> %s', str (tempid), key) data = self.temp[tempid] r = self.set (key, data) del self.temp[str (tempid)] self.writelock.release () return r def startServiceThread (self): self.thread = Thread(target=self.serviceThread, args=()) self.thread.start() def run (self): logger.info ('Bootstraping DHT from %d nodes, listening on port %d', len (self.seeds), self.port) self.bootstrap () def set (self, key, value): #self.storage.dump () #try: self.dht [key] = value logger.debug ("Storing %s", key) return True #except: # logger.error ('Error while setting data in the dht') # return False def identity (self): return self.dht.identity () def get (self, key, handler, handlerdata): #self.storage.dump () logger.info ('Waiting for %s', key) self.dht.get (key, lambda d: handler (handlerdata, d)) def bootstrap (self): self.dht = kad.DHT ('', int (self.port), storage=self.storage, info=str (self.info)) self.dht.bootstrap (self.seeds) self.startServiceThread () def peers (self): return self.dht.peers () def serviceThread (self): i = 0 while True: if i % 4 == 0: logger.debug ('Discovering nodes, %d total', len (self.peers ())) try: self.dht.bootstrap () except: pass time.sleep (60) i += 1 PK"c,Gὅcontractvmd/chain/__init__.py# Copyright (c) 2015 Davide Gessa # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. from . import * PKjtG;88contractvmd/chain/message.py# Copyright (c) 2015 Davide Gessa # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. import random import json import logging import binascii import hashlib from pycoin.tx.Tx import * from pycoin.tx.script import tools from pycoin import encoding from pycoin.networks import * from pycoin.serialize import b2h, b2h_rev, h2b, h2b_rev from .. import config from ..proto import Protocol logger = logging.getLogger(config.APP_NAME) class Message (): def __init__ (self): self.Transaction = None self.Method = None self.DappCode = None self.Protocol = Protocol.VERSION self.Data = None self.Hash = "" self.Player = "" self.Block = 0 self.DataHash = None def toJSON (self): data = {'method': self.Method, 'player': self.Player} return data def getDataHash (self, data): return hashlib.sha256 (data.encode ('ascii')).hexdigest ().encode ('ascii') # Serialize a message in a transaction output script def toOutputScript (self, dht): #while True: jdata = self.toJSON () data = json.dumps (self.toJSON (), (':', ',')) if data == None: assert ('Data is none') datahash = self.getDataHash (data) # Set tempdata on dht temp_id = dht.prepare (data) # Create opreturn opret = Protocol.MAGIC_FLAG + chr (self.Protocol) + chr (self.DappCode[0]) + chr (self.DappCode[1]) + chr (self.Method) + datahash.decode () retscript = "OP_RETURN " + str (binascii.hexlify (opret.encode ('ascii')))[2:-1] if len (retscript) > Protocol.STORAGE_OPRETURN_LIMIT: assert ('Transaction data is too big') # Save data in the DHT return [datahash.decode (), retscript, temp_id] def isMessage (txhex): tx = Tx.from_hex (txhex) oprets = [] for txout in tx.txs_out: ops = tools.opcode_list (txout.script) #print ('OPS:',ops) if len (ops) > 0 and ops[0] == 'OP_RETURN': oprets.append (ops[1]) if len (oprets) == 0: return False for opret in oprets: data = (''.join(chr(int(opret[i:i+2], 16)) for i in range(0, len(opret), 2))) if data[0:len (Protocol.MAGIC_FLAG)] != Protocol.MAGIC_FLAG: continue else: return True return False # Deserialize a transaction to a message def fromTransaction (blockn, txhex): tx = Tx.from_hex(txhex) #print (binascii.hexlify (tx.blanked_hash ())) # Get the opreturn oprets = [] for txout in tx.txs_out: ops = tools.opcode_list (txout.script) if len (ops) > 0 and ops[0] == 'OP_RETURN': oprets.append (ops[1]) # If there's no opreturn, return empty if len (oprets) == 0: return None # Check opret datas for opret in oprets: # Get the opreturn data try: data = (''.join(chr(int(opret[i:i+2], 16)) for i in range(0, len(opret), 2))) except: # Other oprets with invalid data continue # Check if data is a contractchain message if data[0:len (Protocol.MAGIC_FLAG)] != Protocol.MAGIC_FLAG: continue m = Message () # Grab metadata from transaction m.Protocol = ord (data[len(Protocol.MAGIC_FLAG):len(Protocol.MAGIC_FLAG)+1]) m.DappCode = [ ord (data[len(Protocol.MAGIC_FLAG)+1:len(Protocol.MAGIC_FLAG)+2]), ord (data[len(Protocol.MAGIC_FLAG)+2:len(Protocol.MAGIC_FLAG)+3]) ] m.Method = ord (data[len(Protocol.MAGIC_FLAG)+3:len(Protocol.MAGIC_FLAG)+4]) m.Hash = tx.id () m.Block = blockn m.DataHash = data[len(Protocol.MAGIC_FLAG)+4:len(Protocol.MAGIC_FLAG)+4+Protocol.DATA_HASH_SIZE].encode ('ascii') m.Player = tx.txs_in[0].bitcoin_address (address_prefix_for_netcode(config.CONF['chain'])) return m return None PKxGo&DDcontractvmd/chain/chain.py# Copyright (c) 2015 Davide Gessa # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. import datetime import time import logging import copy from threading import Thread from threading import Lock from .. import config from .blockwatch import * from .message import * logger = logging.getLogger(config.APP_NAME) class Chain: DATA_TIMEOUT = 15 DATA_HANDLER_TIMEOUT = 5 def __init__ (self, plugman, database, backend, dht, chain): self.plugman = plugman self.backend = backend self.database = database self.chainHeight = int (self.database.getinit ('ChainHeight', 0)) self.chain = chain self.queue = [] self.queuelock = Lock () self.dht = dht self.dhtrequests = {} self.dhtqueue = [] self.dhtdatalock = Lock () def getChainName (self): return self.chain['name'] def getChainCode (self): return self.chain['code'] def getChainHeight (self): return int (self.chainHeight) def updateChainHeight (self, h): self.chainHeight = int (h) self.database.set ('ChainHeight', int (h)) def newBlockHandler (self, i): logger.info ('New block found %d', i) # Insert the block into a queue self.queuelock.acquire () self.queue.append (i) self.queuelock.release () def onMessageDataReceived (self, message, data): if data == None: logger.error ('Cannot retrive data %s from DHT', message.Hash) return # Check hash validity if message.getDataHash (data) != message.DataHash: logger.error ('Invalid data hash (%s <> %s', message.getDataHash (data), message.DataHash) #return message.Data = json.loads (data) # Check data <=> metadata consistency #if message.Data['player'] != message.Player: # logger.error ('Different players between transaction and data (%s <> %s)', message.Data['player'], message.Player) # #return if int (message.Data['method']) != int (message.Method): logger.error ('Different method between transaction and data') #return self.dhtdatalock.acquire () # dhtrequests doesn't contain message.Block if the timer for data is expired if message.Block in self.dhtrequests: self.dhtrequests [message.Block]['list'][message.Hash] = message self.dhtrequests [message.Block]['pending'] -= 1 #print (message.Block, self.dhtrequests [message.Block]['pending'], message.Hash) self.dhtdatalock.release () #self.plugman.handleMessage (message) def parseBlock (self, blockn): block = self.backend.getBlock (blockn) if block == None: logger.debug ('Waiting for block...') time.sleep (2) return logger.debug ('Parsing block %s - %d %s', datetime.datetime.fromtimestamp(int(block['time'])).strftime('%Y-%m-%d %H:%M:%S'), block['height'], block['hash']) for txid in block['tx']: txhex = self.backend.getTransaction (txid) if txhex != None: #logger.debug ('Parsing transaction %s of block %d', txid, nblock) if True: #try: message = Message.fromTransaction (block['height'], txhex) if message != None: if self.plugman.shouldBeHandled (message): self.dhtdatalock.acquire () if not message.Block in self.dhtrequests: self.dhtrequests[message.Block] = {'pending': 0, 'timer': 0, 'list': {}} self.dhtrequests [message.Block]['pending'] += 1 self.dhtrequests [message.Block]['list'][message.Hash] = None if not message.Block in self.dhtqueue: self.dhtqueue.append (message.Block) self.dhtdatalock.release () # Get data from DHT self.dht.get (message.Hash, self.onMessageDataReceived, message) else: logger.debug ('Message %s should not be handled, skipped', txid) #except: # logger.error ('Failed to parse transaction %s of block %d', txid, nblock) # continue else: logger.debug ('Cannot retrive transaction %s of block %d', txid, block['height']) # If the block is empty, we need to updateChainHeight self.dhtdatalock.acquire () if len (self.dhtqueue) == 0: self.updateChainHeight (block['height']) self.dhtdatalock.release () def dataHandler (self): while True: self.dhtdatalock.acquire () if len (self.dhtqueue) > 0: logger.debug ("Data queue for block %d pending %d chunks with %d timer.", int (self.dhtqueue[0]), self.dhtrequests[self.dhtqueue[0]]['pending'], self.dhtrequests[self.dhtqueue[0]]['timer']) if len (self.dhtqueue) > 0 and (self.dhtrequests [self.dhtqueue[0]]['pending'] == 0 or self.dhtrequests [self.dhtqueue[0]]['timer'] > Chain.DATA_TIMEOUT): bn = self.dhtqueue.pop (0) logger.debug ('Data of block %d retrived',bn) mhashs = self.dhtrequests[bn]['list'] for m in mhashs: mdata = self.dhtrequests[bn]['list'][m] if mdata != None: self.plugman.handleMessage (mdata) else: logger.error ("Skipping message %s due to a timeout", m) del self.dhtrequests[bn] self.dhtdatalock.release () # Update here the chain height self.updateChainHeight (bn) elif len (self.dhtqueue) > 0 and self.dhtqueue[0] < self.dhtqueue[0] - 1: self.dhtdatalock.release () self.updateChainHeight (self.dhtqueue[0]-1) else: for q in self.dhtqueue: self.dhtrequests [q]['timer'] += Chain.DATA_HANDLER_TIMEOUT self.dhtdatalock.release () time.sleep (Chain.DATA_HANDLER_TIMEOUT) def run (self): # Start the blockwatch thread if self.getChainHeight () < self.chain['genesis_height']: cu = self.chain['genesis_height'] else: cu = self.getChainHeight () if config.CONF['discard-old-blocks']: logger.info ('Discarding old blocks') cu = self.backend.getLastBlockHeight () - 1 # Update the chain height to avoid a full-sync in the next start self.updateChainHeight (self.backend.getLastBlockHeight () - 1) logger.info ('Starting chain loop from block %d on %s', cu, self.chain['name']) self.blockwatch = BlockWatch (cu, self.backend, self.newBlockHandler) self.blockwatchthread = Thread(target=self.blockwatch.run, args=()) self.blockwatchthread.start() self.datawatchthread = Thread(target=self.dataHandler, args=()) self.datawatchthread.start() while True: # Check for new queue elements self.queuelock.acquire () if len (self.queue) > 0: nb = self.queue.pop (0) else: nb = None self.queuelock.release () # Parse new data if nb != None: self.parseBlock (nb) time.sleep (0.1) PKxGaUNllcontractvmd/chain/blockwatch.py# Copyright (c) 2015 Davide Gessa # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. import time from .. import config class BlockWatch: def __init__ (self, current, backend, notifyHandler): self.current_height = current self.backend = backend self.notify = notifyHandler def run (self): while True: h = self.backend.getLastBlockHeight () if (h != self.current_height): for i in range (self.current_height+1, h+1): self.notify (i) self.current_height = i time.sleep (0.1) time.sleep (5) PK3c,Gcontractvmd/backend/__init__.py# Copyright (c) 2015 Davide Gessa # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. from . import * PK%@rG󞱊contractvmd/backend/backend.py# Copyright (c) 2015 Davide Gessa # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. class ChainNotSupportedException (Exception): pass class WrongChainException (Exception): pass class Backend: def __init__ (self): raise ('This is an abstract method') def connect (self): raise ('This is an abstract method') def getLastBlockHeight (self): raise ('This is an abstract method') def getBlockHash (self, index): raise ('This is an abstract method') # Return a dict {"height": x, "hash": y, "time": unixtime, "tx": [listoftxids]} def getBlockByHash (self, bhash): raise ('This is an abstract method') def getTransaction (self, txid): raise ('This is an abstract method') def broadcastTransaction (self, transaction): raise ('This is an abstract method') def getBlock (self, index): return self.getBlockByHash (self.getBlockHash (index)) PKEG-DBh contractvmd/backend/node.py# Copyright (c) 2015 Davide Gessa # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. import requests import json import logging import time import shelve import socket import codecs import binascii from pycoin.block import Block from threading import Thread from threading import Lock from pycoin.serialize import b2h_rev, h2b from bitpeer import clients, networks, node, serializers from ..chain.message import Message from .backend import * from .. import config from io import BytesIO logger = logging.getLogger(config.APP_NAME) class Node (Backend): # Return a block with transactions that contains only the magicode def blockFilter (block): v = [] for tx in block.txs: tt = tx.as_hex () try: if Message.isMessage (tt): v.append (tx) except: pass block.txs = v return block def __init__ (self, chain, dbfile, genesisblock = None): if not networks.isSupported (chain): raise ChainNotSupportedException () self.genesis = genesisblock self.tx_cache = {} self.thread = None self.lastID = 0 self.node = node.Node (chain, dbfile, self.genesis[0], self.genesis[1], maxpeers=25, logger=logger) self.node.blockFilter = Node.blockFilter def getChainCode (self): return self.chain def connect (self): logger.info ('Waiting for bootstrap from seed servers') try: self.node.bootstrap () except: logger.critical ('No reachable seed server found') return False try: self.node.connect () except: logger.critical ('No peer available') return False logger.info ('Bootstraped from %d peers (%d nodes discovered)', len(self.node.clients), len (self.node.peers)) self.thread = Thread (target=self.node.loop, args=()) self.thread.start () return True def getLastBlockHeight (self): return self.node.getLastBlockHeight () def getBlockHash (self, index): self.lastID = index return self.node.getBlockHash (index) def getBlockByHash (self, bhash): if bhash == None: return None block = self.node.getBlockByHash (bhash) v = [] d = {} for tx in block.txs: v.append (tx.id ()) d [tx.id ()] = tx.as_hex () self.tx_cache = d block = { "height": self.lastID, "time": block.timestamp, "hash": bhash, "tx": v } return block def broadcastTransaction (self, transaction): return self.node.broadcastTransaction (transaction) def getTransaction (self, txid): return self.tx_cache [txid] PKxGoeY Y contractvmd/backend/daemonrpc.py# Copyright (c) 2015 Davide Gessa # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. import requests import json import logging import time from .backend import * from .. import config logger = logging.getLogger(config.APP_NAME) class DaemonRPC (Backend): SUPPORTED_CHAINS = ['BTC', 'XTN', 'DOGE', 'LTC', 'XLT', 'XDT'] def __init__ (self, chain, host, port, user, password, ssl): self.chain = chain self.host = host self.port = port self.user = user self.password = password self.ssl = ssl self.url = 'http' + ('s' if self.ssl else '') + '://' + self.user + ':' + self.password + '@' + self.host + ':' + self.port self.headers = {'content-type': 'application/json'} def _rpc (self, command, args = []): while True: try: payload = { "method": command, "params": args, "jsonrpc": "2.0", "id": 0, } response = requests.post(self.url, data=json.dumps(payload), headers=self.headers).json() if 'error' in response and response['error'] != None and response['error']['code'] == -28: logger.warning ('The rpc server is syncing. Retrying in 5 seconds') time.sleep (5) else: return response except Exception as e: logger.error ('Unable to connect. Retrying in 5 seconds...') time.sleep (5) def getChainCode (self): responseh = self._rpc ("help")['result'] response = self._rpc ("getinfo") tn = response['result']['testnet'] if responseh.find ('litecoin') != -1: if bool (tn): return 'XLT' else: return 'LTC' elif responseh.find ('bitcoin') != -1: if bool (tn): return 'XTN' else: return 'BTC' elif responseh.find ('dogecoin') != -1: if bool (tn): return 'XDT' else: return 'DOGE' return 'UNK' def connect (self): try: code = self.getChainCode () if code == self.chain: return True else: logger.critical ('Using rpc of wrong chain (%s <> %s)', code, self.chain) return False except: return False def getLastBlockHeight (self): response = self._rpc ("getblockcount") return int (response["result"]) def getBlockHash (self, index): response = self._rpc ("getblockhash", [index]) return response["result"] def getBlockByHash (self, bhash): response = self._rpc ("getblock", [bhash]) return response["result"] def broadcastTransaction (self, transaction): response = self._rpc ("sendrawtransaction", [transaction]) return response["result"] def getTransaction (self, txid): response = self._rpc ("getrawtransaction", [txid]) return response["result"] PK/c,G4]!contractvmd/backend/chainsoapi.py# Copyright (c) 2015 Davide Gessa # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. import requests import json from .backend import Backend class ChainSoAPI (Backend): SUPPORTED_CHAINS = ['BTC', 'XTN', 'DOGE', 'XDT', 'LTC', 'XLT'] def __init__ (self, chain): self.chain = chain if self.chain == 'BTC': self.chainstr = 'BTC' elif self.chain == 'XTN': self.chainstr = 'BTCTEST' elif self.chain == 'DOGE': self.chainstr = 'DOGE' elif self.chain == 'XDT': self.chainstr = 'DOGETEST' elif self.chain == 'LTC': self.chainstr = 'LTC' elif self.chain == 'XLT': self.chainstr = 'LTCTEST' else: #TODO fatal error pass def getSupportedChains (): return ChainSoAPI.SUPPORTED_CHAINS def isChainSupported (chain): return chain in ChainSoAPI.SUPPORTED_CHAINS def getJsonFromUrl (self, u): r = requests.get(u) return json.loads (r.text) def connect (self): pass def getLastBlockHeight (self): u = 'https://chain.so/api/v2/get_info/' + self.chainstr d = self.getJsonFromUrl (u) return int (d['data']['blocks']) def getBlockHash (self, index): u = 'https://chain.so/api/v2/get_blockhash/'+self.chainstr+'/' + str (index) d = self.getJsonFromUrl (u) return str (d['data']['blockhash']) def getBlockByHash (self, bhash): u = 'https://chain.so/api/v2/get_block/'+self.chainstr+'/' + str (bhash) d = self.getJsonFromUrl (u) block = {"height": d['data']['block_no'], "time": d['data']['time'], "hash": d['data']['blockhash'], "tx": d['data']['txs']} return block def getTransaction (self, txid): d = None try: u = 'https://chain.so/api/v2/get_tx/'+self.chainstr+'/' + str (txid) d = self.getJsonFromUrl (u) return d['data']['tx_hex'] except: print (u,d) def broadcastTransaction (self, transaction): raise ('This is an abstract method') PK,8G^- -contractvm-0.6.9.14.dist-info/DESCRIPTION.rstUNKNOWN PK,8G.contractvm-0.6.9.14.dist-info/entry_points.txt[console_scripts] chainstarter = contractvmd.chainstarter:main contractvmd = contractvmd.contractvmd:main dappman = contractvmd.dappman:main PK,8Gggg+contractvm-0.6.9.14.dist-info/metadata.json{"extensions": {"python.commands": {"wrap_console": {"chainstarter": "contractvmd.chainstarter:main", "contractvmd": "contractvmd.contractvmd:main", "dappman": "contractvmd.dappman:main"}}, "python.details": {"contacts": [{"email": "gessadavide@gmail.com", "name": "Davide Gessa", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}}, "python.exports": {"console_scripts": {"chainstarter": "contractvmd.chainstarter:main", "contractvmd": "contractvmd.contractvmd:main", "dappman": "contractvmd.dappman:main"}}}, "extras": [], "generator": "bdist_wheel (0.26.0)", "metadata_version": "2.0", "name": "contractvm", "run_requires": [{"requires": ["base58", "bitpeer.py", "colorlog", "json-rpc", "kad.py", "lxml", "pycoin (==0.62)", "requests", "werkzeug"]}], "summary": "A general-purpose framework for decentralized applications", "version": "0.6.9.14"}PK,8G+ +contractvm-0.6.9.14.dist-info/top_level.txtcontractvmd PK,8G}\\#contractvm-0.6.9.14.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.26.0) Root-Is-Purelib: true Tag: py3-none-any PK,8GM&contractvm-0.6.9.14.dist-info/METADATAMetadata-Version: 2.0 Name: contractvm Version: 0.6.9.14 Summary: A general-purpose framework for decentralized applications Home-page: UNKNOWN Author: Davide Gessa Author-email: gessadavide@gmail.com License: UNKNOWN Platform: UNKNOWN Requires-Dist: base58 Requires-Dist: bitpeer.py Requires-Dist: colorlog Requires-Dist: json-rpc Requires-Dist: kad.py Requires-Dist: lxml Requires-Dist: pycoin (==0.62) Requires-Dist: requests Requires-Dist: werkzeug UNKNOWN PK,8G h$contractvm-0.6.9.14.dist-info/RECORDcontractvm-0.6.9.14.dist-info/DESCRIPTION.rst,sha256=OCTuuN6LcWulhHS3d5rfjdsQtW22n7HENFRh6jC6ego,10 contractvm-0.6.9.14.dist-info/METADATA,sha256=cuiBxPpI-Md7YEsxhPdodb9k2Aq8FrJdvloTCmmsnfQ,464 contractvm-0.6.9.14.dist-info/RECORD,, contractvm-0.6.9.14.dist-info/WHEEL,sha256=zX7PHtH_7K-lEzyK75et0UBa3Bj8egCBMXe1M4gc6SU,92 contractvm-0.6.9.14.dist-info/entry_points.txt,sha256=6ZXoPQ8AzKxpcIydlBc8pDYRLZ-KF8VrGXwZz0FlHdk,142 contractvm-0.6.9.14.dist-info/metadata.json,sha256=kxh4e3sCypmVw47mKKbryrP91nSvj6CvjwomldzBMp0,871 contractvm-0.6.9.14.dist-info/top_level.txt,sha256=oVsSzaQgvymtjtF8hYDIRvSpU5E1aGj3K92UXK4ibhE,12 contractvmd/__init__.py,sha256=E4GAWfDDvA4u-tBSUdnoGgHqMUYlSsVsHzmWZ_p38Mo,247 contractvmd/api.py,sha256=9XwMeNqZJywM-BfnspqZQNK_or25RsJ86jEB_StVeTg,4707 contractvmd/chainstarter.py,sha256=N2aFjzdXBppjaXfHVHGBj4jzSsghTMbvB0rvJgjpZ8g,1072 contractvmd/config.py,sha256=RzvLAJvhAPCM39jrVkSYeerrQofx0nwkjCU7aEMvTjk,3941 contractvmd/contractvmd.py,sha256=rzeiv7tu-z2YUyAp6i2UYFI9mL842FaFxOxuyAkDGwo,9931 contractvmd/dapp.py,sha256=Qdfq83v6o8KPTUNcBBnsIsf4Aal06L1B5J28SG9phVw,1766 contractvmd/dappman.py,sha256=Ntw_tCfTrpjQ0J042zA2GE_ub9l5V8LUqBFEi8ETdd0,9103 contractvmd/database.py,sha256=9oN-I72O7WmQEm6zFV6WiKCEm6H3WoCQptu-zpNomVU,2147 contractvmd/dht.py,sha256=q5_JrluBZeVqsephiYq4mP1s5z6Z0OZIFScKwftTTgg,2759 contractvmd/pluginmanager.py,sha256=vsduKMTz4H4NVQGMlSft94nZgBx-hvTlMx3QhJD3SsY,1624 contractvmd/proto.py,sha256=g2X25nAYcEpU_Fi7Ud8vqn1o0dXGCWTpwl5TSUjMOyM,720 contractvmd/backend/__init__.py,sha256=mW3BQktEPcKJ00cCIx28MEVxM2bcgciKfA_xXxxA_ww,189 contractvmd/backend/backend.py,sha256=Og3Msu24KeluotgIgrkZmRfrl6XGekfuMfTZQDKoNYc,971 contractvmd/backend/chainsoapi.py,sha256=N32DLl6rLXGmjBExkh3qGjOxFUANHTpLGmDOj64jnhY,1927 contractvmd/backend/daemonrpc.py,sha256=T0KtvEZkOs_4PX-cmkwCdvB_ddaSkU7PJz38n4INHqk,2649 contractvmd/backend/node.py,sha256=liEsS1aM4ezGYioE3S2KLu5rMYnRSTH-fDvP4PgSAyA,2497 contractvmd/chain/__init__.py,sha256=tXy8kI-zczeOC1cO8If2HEcl8Y6GDCUFQXhVdFCaVjg,188 contractvmd/chain/blockwatch.py,sha256=w9XFVMqUr1MzPEEfMpAxudbbNf6c86pJixiM_rFGnWI,620 contractvmd/chain/chain.py,sha256=dCWes_MUKWHrzWq4OW0ifojAk9ZstnXH-YGu_LKoV_w,6468 contractvmd/chain/message.py,sha256=729SfoOPbsQrpnQo5thM5-oGA02VH_yfGrwTddzJPgY,3640 PK!~GAB00contractvmd/chainstarter.pyPKb,GÚMicontractvmd/__init__.pyPK EGU>&&contractvmd/contractvmd.pyPKl~G ,contractvmd/proto.pyPKlALG.cc/contractvmd/database.pyPK!8G5xee28contractvmd/config.pyPKxG 7XXGcontractvmd/pluginmanager.pyPK&eGic\Ncontractvmd/dapp.pyPK6jfG-##sUcontractvmd/dappman.pyPKGcc6ycontractvmd/api.pyPK~GhŮ ɋcontractvmd/dht.pyPK"c,Gὅcontractvmd/chain/__init__.pyPKjtG;88contractvmd/chain/message.pyPKxGo&DD)contractvmd/chain/chain.pyPKxGaUNllcontractvmd/chain/blockwatch.pyPK3c,GNcontractvmd/backend/__init__.pyPK%@rG󞱊Hcontractvmd/backend/backend.pyPKEG-DBh Ocontractvmd/backend/node.pyPKxGoeY Y Icontractvmd/backend/daemonrpc.pyPK/c,G4]!contractvmd/backend/chainsoapi.pyPK,8G^- -contractvm-0.6.9.14.dist-info/DESCRIPTION.rstPK,8G.contractvm-0.6.9.14.dist-info/entry_points.txtPK,8Gggg+contractvm-0.6.9.14.dist-info/metadata.jsonPK,8G+ +contractvm-0.6.9.14.dist-info/top_level.txtPK,8G}\\#contractvm-0.6.9.14.dist-info/WHEELPK,8GM&wcontractvm-0.6.9.14.dist-info/METADATAPK,8G h$contractvm-0.6.9.14.dist-info/RECORDPK