PKMNG27@U U 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 () 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 ('localhost', 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): while True: time.sleep (20) logger.debug ('Discovering nodes, %d total', len (self.peers ())) self.dht.bootstrap () PK]WAG 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) 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 ()) PKUfG8٨11contractvmd/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 () PKmEG}contractvmd/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" ], "plugins": [ "tst" ], "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): rjson = json.loads (request.data.decode('ascii')) 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) return Response(response.json, mimetype='application/json') 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>NGlIߨ!!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-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 (): catalog = download_list () try: opts, args = getopt.getopt(sys.argv[1:], "s:i:k:r:lwhd:vu:c:", ["reset=", "update=", "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"): 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"): 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"): 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"): 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 () PKv[fG3mcontractvmd/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.2' 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'] DAPPS = { 'TST': 'TST', 'HW': 'HelloWorld', 'FIFO': 'FIFO', 'BS': 'BlockStore', 'ETH': 'Eth' } CHAINS = { 'XTN' : { 'code': 'XTN', 'base_fee': 60000, 'genesis_block': "", 'genesis_height': 329600, 'name': networks.full_network_name_for_netcode ('XTN') }, 'BTC' : { 'code': 'BTC', 'base_fee': 40000, 'genesis_block': "", 'genesis_height': 329203, 'name': networks.full_network_name_for_netcode ('BTC') }, 'XLT' : { 'code': 'XLT', 'base_fee': 450000, 'genesis_block': "", 'genesis_height': 706293, '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': "", 'genesis_height': 481000, 'name': networks.full_network_name_for_netcode ('DOGE') } } #'dapps': [ 'tst', 'hw', 'bs', 'fifo' ], CONF = { 'chain': 'XLT', 'regtest': False, 'discard-old-blocks': False, 'maxpeers': 25, 'dapps': { 'list': [], 'enabled': [] }, 'backend': { 'protocol': ['rpc', 'chainsoapi'], '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 PKD]bG˕$$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 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') 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) # If firstrun, discard old blocks if firstrun: config.CONF['discard-old-blocks'] = True # Start the backend be = None fallbackends = config.CONF['backend']['protocol'] while be == None and len (fallbackends) > 0: cbe = fallbackends [0] fallbackends = fallbackends[1:] 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 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 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])) if r[1] == signal.SIGKILL: run = False else: core (opts, args) sys.exit (0) 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 () PKHNG2contractvmd/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 colorlog import ColoredFormatter 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 # 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 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.c,G<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 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)) 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/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') PKsOfGe[ [ 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"])+1 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"] PK2KG*contractvmd/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) 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: 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) #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: # TODO sistema #try: self.plugman.handleMessage (mdata) #except: # logger.critical ("Message %s create an exception in plugin", m) 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) PKc,GM ppcontractvmd/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 () - 1 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) PKdMG,r contractvmd/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 .. 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] # Deserialize a transaction to a message def fromTransaction (blockn, txhex): tx = 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 data = (''.join(chr(int(opret[i:i+2], 16)) for i in range(0, len(opret), 2))) # 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 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 * PK]fG^- *contractvm-0.6.2.dist-info/DESCRIPTION.rstUNKNOWN PK]fG+contractvm-0.6.2.dist-info/entry_points.txt[console_scripts] chainstarter = contractvmd.chainstarter:main contractvmd = contractvmd.contractvmd:main dappman = contractvmd.dappman:main PK]fG5AA(contractvm-0.6.2.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", "colorlog", "json-rpc", "kad.py", "lxml", "pycoin", "werkzeug"]}], "summary": "A general-purpose framework for decentralized applications", "version": "0.6.2"}PK]fG+ (contractvm-0.6.2.dist-info/top_level.txtcontractvmd PK]fG}\\ contractvm-0.6.2.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.26.0) Root-Is-Purelib: true Tag: py3-none-any PK]fGkpՒ#contractvm-0.6.2.dist-info/METADATAMetadata-Version: 2.0 Name: contractvm Version: 0.6.2 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: colorlog Requires-Dist: json-rpc Requires-Dist: kad.py Requires-Dist: lxml Requires-Dist: pycoin Requires-Dist: werkzeug UNKNOWN PK]fG֍!contractvm-0.6.2.dist-info/RECORDcontractvm-0.6.2.dist-info/DESCRIPTION.rst,sha256=OCTuuN6LcWulhHS3d5rfjdsQtW22n7HENFRh6jC6ego,10 contractvm-0.6.2.dist-info/METADATA,sha256=u6a8PKdwI0LVFdU1m8Gcc8mIUSJysljTkrODxcdGHqA,402 contractvm-0.6.2.dist-info/RECORD,, contractvm-0.6.2.dist-info/WHEEL,sha256=zX7PHtH_7K-lEzyK75et0UBa3Bj8egCBMXe1M4gc6SU,92 contractvm-0.6.2.dist-info/entry_points.txt,sha256=6ZXoPQ8AzKxpcIydlBc8pDYRLZ-KF8VrGXwZz0FlHdk,142 contractvm-0.6.2.dist-info/metadata.json,sha256=ORhFEXQze4LvUV9ryOQVZvKYOaDcSnr_pCaBrIk1m40,833 contractvm-0.6.2.dist-info/top_level.txt,sha256=oVsSzaQgvymtjtF8hYDIRvSpU5E1aGj3K92UXK4ibhE,12 contractvmd/__init__.py,sha256=E4GAWfDDvA4u-tBSUdnoGgHqMUYlSsVsHzmWZ_p38Mo,247 contractvmd/api.py,sha256=ibk1_K3hzDuL88gDb4eocL5cYdJ1MPR1HPLb36DOE7Y,4067 contractvmd/chainstarter.py,sha256=1kn6ifQDYdqkPw_bu9sxwjt6uI-XzCi6tOkcb7WKHfo,1073 contractvmd/config.py,sha256=8-VaaDyqB_lNAGkQ3qqbnzGeC0qlBUyuTeYcvpIScRU,3748 contractvmd/contractvmd.py,sha256=Ye93NMP9dKxK_xHyDDYdLthJ52w9_RzvHnJ4ZiloeeE,9428 contractvmd/dapp.py,sha256=Qdfq83v6o8KPTUNcBBnsIsf4Aal06L1B5J28SG9phVw,1766 contractvmd/dappman.py,sha256=cbh7nK133rmJioPRHC1_-7xAFOQBqKOy8DbEm-NEebI,8616 contractvmd/database.py,sha256=9oN-I72O7WmQEm6zFV6WiKCEm6H3WoCQptu-zpNomVU,2147 contractvmd/dht.py,sha256=Im1ULt2tzUEHdFkPEWYtyKCDYToyZb4Rb86NyP80vQQ,2645 contractvmd/pluginmanager.py,sha256=pRYeDrJCGJq0dfzJfd-_iK5dK9GrZGKoCt5aHLKwZ-w,1485 contractvmd/proto.py,sha256=g2X25nAYcEpU_Fi7Ud8vqn1o0dXGCWTpwl5TSUjMOyM,720 contractvmd/backend/__init__.py,sha256=mW3BQktEPcKJ00cCIx28MEVxM2bcgciKfA_xXxxA_ww,189 contractvmd/backend/backend.py,sha256=aaCWmtIWPVE1_BDow4uW8ZCmpzr71D5XmuyzpD2qwi0,918 contractvmd/backend/chainsoapi.py,sha256=N32DLl6rLXGmjBExkh3qGjOxFUANHTpLGmDOj64jnhY,1927 contractvmd/backend/daemonrpc.py,sha256=0fIyyxH0QX3FdtyBhP3l3iH7_RQOOqHKlA8JFhl45uA,2651 contractvmd/chain/__init__.py,sha256=tXy8kI-zczeOC1cO8If2HEcl8Y6GDCUFQXhVdFCaVjg,188 contractvmd/chain/blockwatch.py,sha256=NlC5fTz3u79RXsz7Amw4bDAhNveDC7JlpDi4mVujdsI,624 contractvmd/chain/chain.py,sha256=3m9pY4ckwxFTVixThoWt93Omp_2slkSk-BL5tn_1vNc,6356 contractvmd/chain/message.py,sha256=DyrWHpmZf8PrrC-l_e3P1O8EaFet4_rnm_tHLeJXmBM,3016 PKMNG27@U U contractvmd/dht.pyPK]WAG  contractvmd/proto.pyPK&eGic contractvmd/dapp.pyPKUfG8٨11contractvmd/chainstarter.pyPKmEG}contractvmd/api.pyPK>NGlIߨ!!)contractvmd/dappman.pyPKv[fG3mJcontractvmd/config.pyPKD]bG˕$$Ycontractvmd/contractvmd.pyPKb,GÚM~contractvmd/__init__.pyPKHNG2contractvmd/pluginmanager.pyPKlALG.cc contractvmd/database.pyPK.c,G<contractvmd/backend/backend.pyPK3c,Gwcontractvmd/backend/__init__.pyPK/c,G4]!qcontractvmd/backend/chainsoapi.pyPKsOfGe[ [ 7contractvmd/backend/daemonrpc.pyPK2KG*Хcontractvmd/chain/chain.pyPKc,GM ppܾcontractvmd/chain/blockwatch.pyPKdMG,r contractvmd/chain/message.pyPK"c,Gὅcontractvmd/chain/__init__.pyPK]fG^- *contractvm-0.6.2.dist-info/DESCRIPTION.rstPK]fG+contractvm-0.6.2.dist-info/entry_points.txtPK]fG5AA(contractvm-0.6.2.dist-info/metadata.jsonPK]fG+ (2contractvm-0.6.2.dist-info/top_level.txtPK]fG}\\ contractvm-0.6.2.dist-info/WHEELPK]fGkpՒ#contractvm-0.6.2.dist-info/METADATAPK]fG֍!contractvm-0.6.2.dist-info/RECORDPK